// WL_GAME.C

#include <math.h>
#include "WL_DEF.H"
#pragma hdrstop

#ifdef MYPROFILE
#include <TIME.H>
#endif


/*
=============================================================================

                                                 LOCAL CONSTANTS

=============================================================================
*/


/*
=============================================================================

                                                 GLOBAL VARIABLES

=============================================================================
*/

boolean         ingame,fizzlein;
unsigned latchpics[NUMLATCHPICS];
gametype        gamestate;
byte            bordercol=VIEWCOLOR;        // color of the Change View/Ingame border

#ifdef SPEAR
long            spearx,speary;
unsigned spearangle;
boolean         spearflag;
#endif

//
// ELEVATOR BACK MAPS - REMEMBER (-1)!!
//
int ElevatorBackTo[]={1,1,7,3,5,3};

void ScanInfoPlane (void);
void SetupGameLevel (void);
void DrawPlayScreen (void);
void LoadLatchMem (void);
void GameLoop (void);

/*
=============================================================================

                                                 LOCAL VARIABLES

=============================================================================
*/



//===========================================================================
//===========================================================================


/*
==========================
=
= SetSoundLoc - Given the location of an object (in terms of global
=       coordinates, held in globalsoundx and globalsoundy), munges the values
=       for an approximate distance from the left and right ear, and puts
=       those values into leftchannel and rightchannel.
=
= JAB
=
==========================
*/

        fixed   globalsoundx,globalsoundy;
        int             leftchannel,rightchannel;
#define ATABLEMAX 15
byte righttable[ATABLEMAX][ATABLEMAX * 2] = {
{ 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 6, 0, 0, 0, 0, 0, 1, 3, 5, 8, 8, 8, 8, 8, 8, 8, 8},
{ 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 6, 4, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8},
{ 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 6, 6, 4, 1, 0, 0, 0, 1, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8},
{ 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 6, 5, 4, 2, 1, 0, 1, 2, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8},
{ 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 6, 5, 4, 3, 2, 2, 3, 3, 5, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8},
{ 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 6, 6, 5, 4, 4, 4, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8},
{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 6, 6, 5, 5, 5, 6, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 6, 6, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}
};
byte lefttable[ATABLEMAX][ATABLEMAX * 2] = {
{ 8, 8, 8, 8, 8, 8, 8, 8, 5, 3, 1, 0, 0, 0, 0, 0, 6, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8},
{ 8, 8, 8, 8, 8, 8, 8, 8, 6, 4, 2, 0, 0, 0, 0, 0, 4, 6, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8},
{ 8, 8, 8, 8, 8, 8, 8, 8, 6, 4, 2, 1, 0, 0, 0, 1, 4, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8},
{ 8, 8, 8, 8, 8, 8, 8, 8, 7, 5, 3, 2, 1, 0, 1, 2, 4, 5, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8},
{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 6, 5, 3, 3, 2, 2, 3, 4, 5, 6, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8},
{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 6, 5, 4, 4, 4, 4, 5, 6, 6, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8},
{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 6, 6, 5, 5, 5, 6, 6, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8},
{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 6, 6, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}
};

void
SetSoundLoc(fixed gx,fixed gy)
{
        fixed   xt,yt;
        int             x,y;

//
// translate point to view centered coordinates
//
        gx -= viewx;
        gy -= viewy;

//
// calculate newx
//
        xt = FixedMul(gx,viewcos);
        yt = FixedMul(gy,viewsin);
        x = (xt - yt) >> TILESHIFT;

//
// calculate newy
//
        xt = FixedMul(gx,viewsin);
        yt = FixedMul(gy,viewcos);
        y = (yt + xt) >> TILESHIFT;

        if (y >= ATABLEMAX)
                y = ATABLEMAX - 1;
        else if (y <= -ATABLEMAX)
                y = -ATABLEMAX;
        if (x < 0)
                x = -x;
        if (x >= ATABLEMAX)
                x = ATABLEMAX - 1;
        leftchannel  =  lefttable[x][y + ATABLEMAX];
        rightchannel = righttable[x][y + ATABLEMAX];

#if 0
        CenterWindow(8,1);
        US_PrintSigned(leftchannel);
        US_Print(",");
        US_PrintSigned(rightchannel);
        VW_UpdateScreen();
#endif
}

/*
==========================
=
= SetSoundLocGlobal - Sets up globalsoundx & globalsoundy and then calls
=       UpdateSoundLoc() to transform that into relative channel volumes. Those
=       values are then passed to the Sound Manager so that they'll be used for
=       the next sound played (if possible).
=
= JAB
=
==========================
*/
void PlaySoundLocGlobal(word s,fixed gx,fixed gy)
{
        SetSoundLoc(gx,gy);
        SD_PositionSound(leftchannel,rightchannel);
        if (SD_PlaySound((soundnames) s))
        {
                globalsoundx = gx;
                globalsoundy = gy;
        }
}

void UpdateSoundLoc(void)
{
        if (SoundPositioned)
        {
                SetSoundLoc(globalsoundx,globalsoundy);
                SD_SetPosition(leftchannel,rightchannel);
        }
}

/*
**      JAB End
*/

/*
==========================
=
= ScanInfoPlane
=
= Spawn all actors and mark down special places
=
==========================
*/

void ScanInfoPlane (void)
{
        unsigned x,y;
        int                     tile;
        word *start;

        start = mapsegs[1];
        for (y=0;y<mapheight;y++)
                for (x=0;x<mapwidth;x++)
                {
                        tile = *start++;
                        if (!tile)
                                continue;

                        switch (tile)
                        {
                        case 1:
                        case 2:
                        case 3:
                        case 4:
                        case 5:
                        case 6:
                        case 7:
                        case 8:
                        case 9:
                        case 10:
                        case 11:
                        case 12:
                        case 13:
                        case 14:
                        case 15:
                        case 16:
                        case 17:
                        case 18:
                                SpawnStatic(x,y,tile-1);
                                break;

                        case 19:
                        case 20:
                        case 21:
                        case 22:
                                SpawnPlayer(x,y,NORTH+tile-19);
                                break;

                        case 23:
                        case 24:
                        case 25:
                        case 26:
                        case 27:
                        case 28:
                        case 29:
                        case 30:

                        case 31:
                        case 32:
                        case 33:
                        case 34:
                        case 35:
                        case 36:
                        case 37:
                        case 38:

                        case 39:
                        case 40:
                        case 41:
                        case 42:
                        case 43:
                        case 44:
                        case 45:
                        case 46:

                        case 47:
                        case 48:
                        case 49:
                        case 50:
                        case 51:
                        case 52:
                        case 53:
                        case 54:

                        case 55:
                        case 56:
                        case 57:
                        case 58:
                        case 59:
                        case 60:
                        case 61:
                        case 62:

                        case 63:
                        case 64:
                        case 65:
                        case 66:
                        case 67:
                        case 68:
                        case 69:
                        case 70:
                        case 71:
                        case 72:
#ifdef SPEAR
                        case 73:                                                // TRUCK AND SPEAR!
                        case 74:
#endif
                                SpawnStatic(x,y,tile-5);
                                break;

                        case 75:
                        case 76:
                        case 77:
                        case 78:
                        case 79:
                        case 80:
                        case 81:
                        case 82:
                        case 83:
                        case 84:
                        case 85:
                        case 86:
                        case 87:
                        case 88:
                        case 89:
                                SpawnStatic(x,y,tile-8);
                                break;


//
// P wall
//
                        case 98:
                                if (!loadedgame)
                                  gamestate.secrettotal++;
                                break;

                        case 100:
                        case 101:
                        case 102:
                        case 103:
                        case 104:
                        case 105:
                        case 106:
                        case 107:
                                SpawnStatic(x,y,tile-18);
                                break;



//
// guard
//
                        case 180:
                        case 181:
                        case 182:
                        case 183:
                                if (gamestate.difficulty<gd_hard)
                                        break;
                                tile -= 36;
                        case 144:
                        case 145:
                        case 146:
                        case 147:
                                if (gamestate.difficulty<gd_medium)
                                        break;
                                tile -= 36;
                        case 108:
                        case 109:
                        case 110:
                        case 111:
                                SpawnStand(en_guard,x,y,tile-108);
                                break;


                        case 184:
                        case 185:
                        case 186:
                        case 187:
                                if (gamestate.difficulty<gd_hard)
                                        break;
                                tile -= 36;
                        case 148:
                        case 149:
                        case 150:
                        case 151:
                                if (gamestate.difficulty<gd_medium)
                                        break;
                                tile -= 36;
                        case 112:
                        case 113:
                        case 114:
                        case 115:
                                SpawnPatrol(en_guard,x,y,tile-112);
                                break;

                        case 124:
                                SpawnDeadGuard (x,y);
                                break;
//
// officer
//
                        case 188:
                        case 189:
                        case 190:
                        case 191:
                                if (gamestate.difficulty<gd_hard)
                                        break;
                                tile -= 36;
                        case 152:
                        case 153:
                        case 154:
                        case 155:
                                if (gamestate.difficulty<gd_medium)
                                        break;
                                tile -= 36;
                        case 116:
                        case 117:
                        case 118:
                        case 119:
                                SpawnStand(en_officer,x,y,tile-116);
                                break;


                        case 192:
                        case 193:
                        case 194:
                        case 195:
                                if (gamestate.difficulty<gd_hard)
                                        break;
                                tile -= 36;
                        case 156:
                        case 157:
                        case 158:
                        case 159:
                                if (gamestate.difficulty<gd_medium)
                                        break;
                                tile -= 36;
                        case 120:
                        case 121:
                        case 122:
                        case 123:
                                SpawnPatrol(en_officer,x,y,tile-120);
                                break;


//
// ss
//
                        case 198:
                        case 199:
                        case 200:
                        case 201:
                                if (gamestate.difficulty<gd_hard)
                                        break;
                                tile -= 36;
                        case 162:
                        case 163:
                        case 164:
                        case 165:
                                if (gamestate.difficulty<gd_medium)
                                        break;
                                tile -= 36;
                        case 126:
                        case 127:
                        case 128:
                        case 129:
                                SpawnStand(en_ss,x,y,tile-126);
                                break;


                        case 202:
                        case 203:
                        case 204:
                        case 205:
                                if (gamestate.difficulty<gd_hard)
                                        break;
                                tile -= 36;
                        case 166:
                        case 167:
                        case 168:
                        case 169:
                                if (gamestate.difficulty<gd_medium)
                                        break;
                                tile -= 36;
                        case 130:
                        case 131:
                        case 132:
                        case 133:
                                SpawnPatrol(en_ss,x,y,tile-130);
                                break;

//
// dogs
//
                        case 206:
                        case 207:
                        case 208:
                        case 209:
                                if (gamestate.difficulty<gd_hard)
                                        break;
                                tile -= 36;
                        case 170:
                        case 171:
                        case 172:
                        case 173:
                                if (gamestate.difficulty<gd_medium)
                                        break;
                                tile -= 36;
                        case 134:
                        case 135:
                        case 136:
                        case 137:
                                SpawnStand(en_dog,x,y,tile-134);
                                break;


                        case 210:
                        case 211:
                        case 212:
                        case 213:
                                if (gamestate.difficulty<gd_hard)
                                        break;
                                tile -= 36;
                        case 174:
                        case 175:
                        case 176:
                        case 177:
                                if (gamestate.difficulty<gd_medium)
                                        break;
                                tile -= 36;
                        case 138:
                        case 139:
                        case 140:
                        case 141:
                                SpawnPatrol(en_dog,x,y,tile-138);
                                break;

//
// boss
//
#ifndef SPEAR
                        case 214:
                                SpawnBoss (x,y);
                                break;
                        case 197:
                                SpawnGretel (x,y);
                                break;
                        case 215:
                                SpawnGift (x,y);
                                break;
                        case 179:
                                SpawnFat (x,y);
                                break;
                        case 196:
                                SpawnSchabbs (x,y);
                                break;
                        case 160:
                                SpawnFakeHitler (x,y);
                                break;
                        case 178:
                                SpawnHitler (x,y);
                                break;
#else
                        case 106:
                                SpawnSpectre (x,y);
                                break;
                        case 107:
                                SpawnAngel (x,y);
                                break;
                        case 125:
                                SpawnTrans (x,y);
                                break;
                        case 142:
                                SpawnUber (x,y);
                                break;
                        case 143:
                                SpawnWill (x,y);
                                break;
                        case 161:
                                SpawnDeath (x,y);
                                break;

#endif

//
// mutants
//
                        case 252:
                        case 253:
                        case 254:
                        case 255:
                                if (gamestate.difficulty<gd_hard)
                                        break;
                                tile -= 18;
                        case 234:
                        case 235:
                        case 236:
                        case 237:
                                if (gamestate.difficulty<gd_medium)
                                        break;
                                tile -= 18;
                        case 216:
                        case 217:
                        case 218:
                        case 219:
                                SpawnStand(en_mutant,x,y,tile-216);
                                break;

                        case 256:
                        case 257:
                        case 258:
                        case 259:
                                if (gamestate.difficulty<gd_hard)
                                        break;
                                tile -= 18;
                        case 238:
                        case 239:
                        case 240:
                        case 241:
                                if (gamestate.difficulty<gd_medium)
                                        break;
                                tile -= 18;
                        case 220:
                        case 221:
                        case 222:
                        case 223:
                                SpawnPatrol(en_mutant,x,y,tile-220);
                                break;

//
// ghosts
//
#ifndef SPEAR
                        case 224:
                                SpawnGhosts (en_blinky,x,y);
                                break;
                        case 225:
                                SpawnGhosts (en_clyde,x,y);
                                break;
                        case 226:
                                SpawnGhosts (en_pinky,x,y);
                                break;
                        case 227:
                                SpawnGhosts (en_inky,x,y);
                                break;
#endif
                        }

                }
}

//==========================================================================

/*
==================
=
= SetupGameLevel
=
==================
*/

void SetupGameLevel (void)
{
        int     x,y;
        word *map;
        word tile;


        if (!loadedgame)
        {
         gamestate.TimeCount=
         gamestate.secrettotal=
         gamestate.killtotal=
         gamestate.treasuretotal=
         gamestate.secretcount=
         gamestate.killcount=
         gamestate.treasurecount=
         pwallstate = pwallpos = 0;
        }

        if (demoplayback || demorecord)
                US_InitRndT (false);
        else
                US_InitRndT (true);

//
// load the level
//
        CA_CacheMap (gamestate.mapon+10*gamestate.episode);
        mapon-=gamestate.episode*10;

/*      mapwidth = mapheaderseg[mapon]->width;
        mapheight = mapheaderseg[mapon]->height;

        if (mapwidth != 64 || mapheight != 64)
                Quit ("Map not 64*64!");*/

//
// copy the wall data to a data segment array
//
        memset (tilemap,0,sizeof(tilemap));
        memset (actorat,0,sizeof(actorat));
        map = mapsegs[0];
        for (y=0;y<mapheight;y++)
                for (x=0;x<mapwidth;x++)
                {
                        tile = *map++;
                        if (tile<AREATILE)
                        {
                        // solid wall
                                tilemap[x][y] = (byte) tile;
                                actorat[x][y] = (objtype *) tile;
                        }
                        else
                        {
                        // area floor
                                tilemap[x][y] = 0;
                                actorat[x][y] = 0;
                        }
                }

//
// spawn doors
//
        InitActorList ();                       // start spawning things with a clean slate
        InitDoorList ();
        InitStaticList ();

        map = mapsegs[0];
        for (y=0;y<mapheight;y++)
                for (x=0;x<mapwidth;x++)
                {
                        tile = *map++;
                        if (tile >= 90 && tile <= 101)
                        {
                        // door
                                switch (tile)
                                {
                                case 90:
                                case 92:
                                case 94:
                                case 96:
                                case 98:
                                case 100:
                                        SpawnDoor (x,y,1,(tile-90)/2);
                                        break;
                                case 91:
                                case 93:
                                case 95:
                                case 97:
                                case 99:
                                case 101:
                                        SpawnDoor (x,y,0,(tile-91)/2);
                                        break;
                                }
                        }
                }

//
// spawn actors
//
        ScanInfoPlane ();
                
        
//
// take out the ambush markers
//
        map = mapsegs[0];
        for (y=0;y<mapheight;y++)
                for (x=0;x<mapwidth;x++)
                {
                        tile = *map++;
                        if (tile == AMBUSHTILE)
                        {
                                tilemap[x][y] = 0;
                                if ( (unsigned)actorat[x][y] == AMBUSHTILE)
                                        actorat[x][y] = NULL;

                                if (*map >= AREATILE)
                                        tile = *map;
                                if (*(map-1-mapwidth) >= AREATILE)
                                        tile = *(map-1-mapwidth);
                                if (*(map-1+mapwidth) >= AREATILE)
                                        tile = *(map-1+mapwidth);
                                if ( *(map-2) >= AREATILE)
                                        tile = *(map-2);

                                *(map-1) = tile;
                        }
                }


//
// have the caching manager load and purge stuff to make sure all marks
// are in memory
//
        CA_LoadAllSounds ();
}


//==========================================================================


/*
===================
=
= DrawPlayBorderSides
=
= To fix window overwrites
=
===================
*/

void DrawPlayBorderSides (void)
{
        int     xl,yl;

        xl = 160-viewwidth/2;
        yl = (200-STATUSLINES-viewheight)/2;

if(viewwidth!=320)
        {
        VWB_Bar (0,0,xl-1,200-STATUSLINES,bordercol);
        VWB_Bar (xl+viewwidth+1,0,xl-2,200-STATUSLINES,bordercol);

        VWB_Bar (0,0,320,yl-1,bordercol);
        VWB_Bar (0,yl+viewheight+1,320,yl-1,bordercol);

        VWB_Vlin (yl-1,yl+viewheight,xl-1,0);
        VWB_Vlin (yl-1,yl+viewheight,xl+viewwidth,bordercol-2);

        VWB_Hlin (xl-1,xl+viewwidth,yl-1,0);
        VWB_Hlin (xl-1,xl+viewwidth,yl+viewheight,bordercol-2);
        VWB_Plot (xl-1,yl+viewheight,bordercol-3);
        }

}


/*
===================
=
= DrawAllPlayBorderSides
=
===================
*/

void DrawAllPlayBorderSides (void)
{
        int i;
        byte *temp;

        temp = vbuf;
        for (i=0;i<3;i++)
        {
                vbuf = (byte *)(0xa0000+screenloc[i]);
                DrawPlayBorderSides ();
        }
        vbuf = temp;
}


/*
===================
=
= DrawStatusBorder
=
===================
*/

void DrawStatusBorder (byte color)
{
        VWB_Bar (0,0,320,163,color);
        VWB_Bar (0,163,8,36,color);
        VWB_Bar (0,198,320,2,color);
        VWB_Bar (312,163,8,36,color);
                                        
        VWB_Bar (9,197,97,1,color-1);
        VWB_Bar (106,197,161,1,color-2);
        VWB_Bar (267,197,44,1,color-3);
        VWB_Bar (311,164,1,20,color-2);
        VWB_Bar (311,184,1,14,color-3);
}


/*
===================
=
= DrawAllStatusBorder
=
===================
*/
void DrawAllStatusBorder (byte color)
{
        int i;
        byte *temp;

        temp = vbuf;
        for (i=0;i<3;i++)
        {
                vbuf = (byte *)(0xa0000+screenloc[i]);
                DrawStatusBorder (color);
        }
        vbuf = temp;
}


/*
===================
=
= DrawPlayBorder
=
===================
*/

void DrawPlayBorder (void)
{
        int     xl,yl;

        if (bordercol != VIEWCOLOR)
                DrawStatusBorder(bordercol);

        VWB_Bar (0,0,320,200-STATUSLINES,bordercol);

        xl = 160-viewwidth/2;
        yl = (200-STATUSLINES-viewheight)/2;
        VWB_Bar (xl,yl,viewwidth,viewheight,0);

if(viewwidth!=320)
        {
        VWB_Hlin (xl-1,xl+viewwidth,yl-1,0);
        VWB_Hlin (xl-1,xl+viewwidth,yl+viewheight,bordercol-2);
        VWB_Vlin (yl-1,yl+viewheight,xl-1,0);
        VWB_Vlin (yl-1,yl+viewheight,xl+viewwidth,bordercol-2);
        VWB_Plot (xl-1,yl+viewheight,bordercol-3);
        }

}

/*
===================
=
= DrawAllPlayBorder
=
===================
*/
void DrawAllPlayBorder (void)
{
        int i;
        byte *temp;

        temp = vbuf;
        for (i=0;i<3;i++)
        {
                vbuf = (byte *)(0xa0000+screenloc[i]);
                DrawPlayBorder ();
        }
        vbuf = temp;
}


/*
===================
=
= DrawPlayScreen
=
===================
*/

void DrawPlayScreen (void)
{
        int     i;
        byte *temp;

        VW_FadeOut ();

        temp = vbuf;

        CA_CacheGrChunk (STATUSBARPIC);

        for (i=0;i<3;i++)
        {
                vbuf = (byte *)(0xa0000+screenloc[i]);
                VWB_DrawPic (0,200-STATUSLINES,STATUSBARPIC);
                DrawPlayBorder ();
        }

        vbuf = temp;

        UNCACHEGRCHUNK (STATUSBARPIC);

        DrawFace ();
        DrawHealth ();
        DrawLives ();
        DrawLevel ();
        DrawAmmo ();
        DrawKeys ();
        DrawWeapon ();
        DrawScore ();
}



//==========================================================================

/*
==================
=
= StartDemoRecord
=
==================
*/

char    demoname[13] = "DEMO?.";

#ifndef REMDEBUG
#define MAXDEMOSIZE     8192

void StartDemoRecord (int levelnumber)
{
        demobuffer=malloc(MAXDEMOSIZE);
        demoptr = (char *) demobuffer;
        lastdemoptr = demoptr+MAXDEMOSIZE;

        *demoptr = levelnumber;
        demoptr += 4;                           // leave space for length
        demorecord = true;
}


/*
==================
=
= FinishDemoRecord
=
==================
*/

void FinishDemoRecord (void)
{
        long    length,level;

        demorecord = false;

        length = demoptr - (char *)demobuffer;

        demoptr = ((char *)demobuffer)+1;
        *(word *)demoptr = (word) length;

        VW_FadeIn();
        CenterWindow(24,3);
        PrintY+=6;
        fontnumber=0;
        SETFONTCOLOR(0,15);
        US_Print(" Demo number (0-9): ");
        VW_UpdateScreen();

        if (US_LineInput (px,py,str,NULL,true,1,0))
        {
                level = atoi (str);
                if (level>=0 && level<=9)
                {
                        demoname[4] = (char)('0'+level);
                        CA_WriteFile (demoname,demobuffer,length);
                }
        }

        free(demobuffer);
}

//==========================================================================

/*
==================
=
= RecordDemo
=
= Fades the screen out, then starts a demo.  Exits with the screen faded
=
==================
*/

void RecordDemo (void)
{
        int level,esc,maps;

        CenterWindow(26,3);
        PrintY+=6;
        CA_CacheGrChunk(STARTFONT);
        fontnumber=0;
        SETFONTCOLOR(0,15);
#ifndef SPEAR
#ifdef UPLOAD
        US_Print("  Demo which level(1-10): "); maps = 10;
#else
        US_Print("  Demo which level(1-60): "); maps = 60;
#endif
#else
        US_Print("  Demo which level(1-21): "); maps = 21;
#endif
        VW_UpdateScreen();
        VW_FadeIn ();
        esc = !US_LineInput (px,py,str,NULL,true,2,0);
        if (esc)
                return;

        level = atoi (str);
        level--;

        if (level >= maps || level < 0)
                return;

        VW_FadeOut ();

#ifndef SPEAR
        NewGame (gd_hard,level/10);
        gamestate.mapon = level%10;
#else
        NewGame (gd_hard,0);
        gamestate.mapon = level;
#endif

        StartDemoRecord (level);

        DrawPlayScreen ();
        VW_FadeIn ();

        startgame = false;
        demorecord = true;

        SetupGameLevel ();
        StartMusic ();
        
        fizzlein = true;

        PlayLoop ();

        demoplayback = false;

        StopMusic ();
        VW_FadeOut ();
        ClearMemory ();

        FinishDemoRecord ();
}
#else
void FinishDemoRecord (void) {return;}
void RecordDemo (void) {return;}
#endif



//==========================================================================

/*
==================
=
= PlayDemo
=
= Fades the screen out, then starts a demo.  Exits with the screen faded
=
==================
*/

void PlayDemo (int demonumber)
{
        int length;

#ifdef DEMOSEXTERN
// debug: load chunk
#ifndef SPEARDEMO
        int dems[4]={T_DEMO0,T_DEMO1,T_DEMO2,T_DEMO3};
#else
        int dems[1]={T_DEMO0};
#endif

        CA_CacheGrChunk(dems[demonumber]);
        demoptr = (char *) grsegs[dems[demonumber]];
#else
        demoname[4] = '0'+demonumber;
        CA_LoadFile (demoname,&demobuffer);
        MM_SetLock (&demobuffer,true);
        demoptr = (char far *)demobuffer;
#endif

        NewGame (1,0);
        gamestate.mapon = *demoptr++;
        gamestate.difficulty = gd_hard;
        length = *((word *)demoptr);
        demoptr+=2;
        demoptr++;
        lastdemoptr = demoptr-4+length;

        VW_FadeOut ();

        SETFONTCOLOR(0,15);
        DrawPlayScreen ();
        VW_FadeIn ();

        startgame = false;
        demoplayback = true;

        SetupGameLevel ();
        StartMusic ();
        
        fizzlein = true;

        PlayLoop ();

#ifdef DEMOSEXTERN
        UNCACHEGRCHUNK(dems[demonumber]);
#else
        MM_FreePtr (&demobuffer);
#endif

        demoplayback = false;

        StopMusic ();
        VW_FadeOut ();
        ClearMemory ();
}

//==========================================================================

/*
==================
=
= Died
=
==================
*/

#define DEATHROTATE 2

void Died (void)
{
        float fangle;
        long    dx,dy;
        int             iangle,curangle,clockwise,counter,change;

        if (screenfaded)
                VW_FadeIn ();

        gamestate.weapon = (weapontype) -1;                     // take away weapon
        SD_PlaySound (PLAYERDEATHSND);

//
// swing around to face attacker
//
        dx = killerobj->x - player->x;
        dy = player->y - killerobj->y;

        fangle = atan2(dy,dx);                  // returns -pi to pi
        if (fangle<0)
                fangle = M_PI*2+fangle;

        iangle = fangle/(M_PI*2)*ANGLES;

        if (player->angle > iangle)
        {
                counter = player->angle - iangle;
                clockwise = ANGLES-player->angle + iangle;
        }
        else
        {
                clockwise = iangle - player->angle;
                counter = player->angle + ANGLES-iangle;
        }

        curangle = player->angle;

        if (clockwise<counter)
        {
        //
        // rotate clockwise
        //
                if (curangle>iangle)
                        curangle -= ANGLES;
                do
                {
                        change = tics*DEATHROTATE;
                        if (curangle + change > iangle)
                                change = iangle-curangle;

                        curangle += change;
                        player->angle += change;
                        if (player->angle >= ANGLES)
                                player->angle -= ANGLES;

                        ThreeDRefresh ();
                        CalcTics ();
                } while (curangle != iangle);
        }
        else
        {
        //
        // rotate counterclockwise
        //
                if (curangle<iangle)
                        curangle += ANGLES;
                do
                {
                        change = -tics*DEATHROTATE;
                        if (curangle + change < iangle)
                                change = iangle-curangle;

                        curangle += change;
                        player->angle += change;
                        if (player->angle < 0)
                                player->angle += ANGLES;

                        ThreeDRefresh ();
                        CalcTics ();
                } while (curangle != iangle);
        }

//
// fade to red
//
        FinishPaletteShifts ();

        vbuf += screenofs;
        VW_Bar (0,0,viewwidth,viewheight,4);

        IN_ClearKeysDown ();

        FizzleFade((long)vbuf-0xa0000,(long)vdisp-0xa0000+screenofs,viewwidth,viewheight,70,false);
        
        vbuf -= screenofs;
        IN_UserInput(100);
        SD_WaitSoundDone ();
        ClearMemory();                  // Tricob's "Out of Memory" fix
        GetMessage(" ");

        if (tedlevel == false)  // SO'S YA DON'T GET KILLED WHILE LAUNCHING!
          gamestate.lives--;

        if (gamestate.lives > -1)
        {
                gamestate.health = 100;
                gamestate.weapon = gamestate.bestweapon
                        = gamestate.chosenweapon = wp_pistol;
                gamestate.ammo = STARTAMMO;
                gamestate.keys = 0;
                pwallstate = pwallpos = 0;
                gamestate.attackframe = gamestate.attackcount =
                gamestate.weaponframe = 0;

                DrawKeys ();
                DrawWeapon ();
                DrawAmmo ();
                DrawHealth ();
                DrawFace ();
                DrawLives ();
        }

}

//==========================================================================

/*
===================
=
= GameLoop
=
===================
*/

void GameLoop (void)
{
        boolean died;
#ifdef MYPROFILE
        clock_t start,end;
#endif

restartgame:
        ClearMemory ();
        SETFONTCOLOR(0,15);
        DrawPlayScreen ();
        died = false;
        do
        {
                if (!loadedgame)
                  gamestate.score = gamestate.oldscore;
                DrawScore();

                startgame = false;
                if (!loadedgame)
                        SetupGameLevel ();

#ifdef SPEAR
                if (gamestate.mapon == 20)      // give them the key allways
                {
                        gamestate.keys |= 1;
                        DrawKeys ();
                }
#endif

                ingame = true;
                if(loadedgame)
                {
                        ContinueMusic(lastgamemusicoffset);
                        loadedgame = false;
                }
                else StartMusic ();
                
                if (!died)
                        PreloadGraphics ();             // TODO: Let this do something useful!
                else
                        died = false;

                fizzlein = true;

                DrawLevel ();

//              VW_FadeIn();

#ifdef SPEAR
startplayloop:
#endif
                PlayLoop ();

#ifdef SPEAR
                if (spearflag)
                {
                        SD_StopSound();
                        SD_PlaySound(GETSPEARSND);
                        if (DigiMode != sds_Off)
                        {
                                long lasttimecount = TimeCount;

                                while(TimeCount < lasttimecount+150)
                                //while(DigiPlaying!=false)
                                        SD_Poll();
                        }
                        else
                                SD_WaitSoundDone();

                        ClearMemory ();
                        gamestate.oldscore = gamestate.score;
                        gamestate.mapon = 20;
                        SetupGameLevel ();
                        StartMusic ();
//                      PM_CheckMainMem ();
                        player->x = spearx;
                        player->y = speary;
                        player->angle = (short)spearangle;
                        spearflag = false;
                        Thrust (0,0);
                        goto startplayloop;
                }
#endif

                StopMusic ();
                ingame = false;

                if (demorecord && playstate != ex_warped)
                        FinishDemoRecord ();

                if (startgame || loadedgame)
                        goto restartgame;

                switch (playstate)
                {
                case ex_completed:
                case ex_secretlevel:
                        gamestate.keys = 0;
                        DrawKeys ();
                        VW_FadeOut ();

                        ClearMemory ();

                        LevelCompleted ();              // do the intermission

#ifdef SPEARDEMO
                        if (gamestate.mapon == 1)
                        {
                                died = true;                    // don't "get psyched!"

                                VW_FadeOut ();

                                ClearMemory ();

                                CheckHighScore (gamestate.score,gamestate.mapon+1);

                                #pragma warn -sus
                                #ifndef JAPAN
                                _fstrcpy(MainMenu[viewscores].string,STR_VS);
                                #endif
                                MainMenu[viewscores].routine = CP_ViewScores;
                                #pragma warn +sus

                                return;
                        }
#endif

#ifdef JAPDEMO
                        if (gamestate.mapon == 3)
                        {
                                died = true;                    // don't "get psyched!"

                                VW_FadeOut ();

                                ClearMemory ();

                                CheckHighScore (gamestate.score,gamestate.mapon+1);

                                #pragma warn -sus
                                #ifndef JAPAN
                                _fstrcpy(MainMenu[viewscores].string,STR_VS);
                                #endif
                                MainMenu[viewscores].routine = CP_ViewScores;
                                #pragma warn +sus

                                return;
                        }
#endif

                        gamestate.oldscore = gamestate.score;

#ifndef SPEAR
                        //
                        // COMING BACK FROM SECRET LEVEL
                        //
                        if (gamestate.mapon == 9)
                                gamestate.mapon = ElevatorBackTo[gamestate.episode];    // back from secret
                        else
                        //
                        // GOING TO SECRET LEVEL
                        //
                        if (playstate == ex_secretlevel)
                                gamestate.mapon = 9;
#else

#define FROMSECRET1             3
#define FROMSECRET2             11

                        //
                        // GOING TO SECRET LEVEL
                        //
                        if (playstate == ex_secretlevel)
                                switch(gamestate.mapon)
                                {
                                 case FROMSECRET1: gamestate.mapon = 18; break;
                                 case FROMSECRET2: gamestate.mapon = 19; break;
                                }
                        else
                        //
                        // COMING BACK FROM SECRET LEVEL
                        //
                        if (gamestate.mapon == 18 || gamestate.mapon == 19)
                                switch(gamestate.mapon)
                                {
                                 case 18: gamestate.mapon = FROMSECRET1+1; break;
                                 case 19: gamestate.mapon = FROMSECRET2+1; break;
                                }
#endif
                        else
                        //
                        // GOING TO NEXT LEVEL
                        //
                                gamestate.mapon++;


                        break;

                case ex_died:
                        Died ();
                        died = true;                    // don't "get psyched!"

                        if (gamestate.lives > -1)
                                break;                          // more lives left

                        VW_FadeOut ();

                        ClearMemory ();

                        CheckHighScore (gamestate.score,gamestate.mapon+1);

                        #pragma warn -sus
                        #ifndef JAPAN
                        _fstrcpy(MainMenu[viewscores].string,STR_VS);
                        #endif
                        MainMenu[viewscores].routine = CP_ViewScores;
                        #pragma warn +sus

                        return;

                case ex_victorious:

#ifndef SPEAR
                        VW_FadeOut ();
#else
                        VL_FadeOut (0,255,0,17,17,300);
#endif
                        ClearMemory ();

                        Victory ();

                        ClearMemory ();

                        CheckHighScore (gamestate.score,gamestate.mapon+1);

                        #pragma warn -sus
                        #ifndef JAPAN
                        strcpy(MainMenu[viewscores].string,STR_VS);
                        #endif
                        MainMenu[viewscores].routine = CP_ViewScores;
                        #pragma warn +sus

                        return;

                default:
                        ClearMemory ();
                        break;
                }

        } while (1);

}

