kolibrios-fun/contrib/games/wolf3d/wl_game.cpp

1605 lines
40 KiB
C++
Raw Normal View History

// WL_GAME.C
#include <math.h>
#include "wl_def.h"
//#include <SDL_mixer.h>
#pragma hdrstop
#ifdef MYPROFILE
#include <TIME.H>
#endif
/*
=============================================================================
LOCAL CONSTANTS
=============================================================================
*/
/*
=============================================================================
GLOBAL VARIABLES
=============================================================================
*/
boolean ingame,fizzlein;
gametype gamestate;
byte bordercol=VIEWCOLOR; // color of the Change View/Ingame border
#ifdef SPEAR
int32_t spearx,speary;
unsigned spearangle;
boolean spearflag;
#endif
#ifdef USE_FEATUREFLAGS
int ffDataTopLeft, ffDataTopRight, ffDataBottomLeft, ffDataBottomRight;
#endif
//
// ELEVATOR BACK MAPS - REMEMBER (-1)!!
//
int ElevatorBackTo[]={1,1,7,3,5,3};
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
=
==========================
*/
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);
int channel = SD_PlaySound((soundnames) s);
if(channel)
{
/* channelSoundPos[channel - 1].globalsoundx = gx;
channelSoundPos[channel - 1].globalsoundy = gy;
channelSoundPos[channel - 1].valid = 1;*/
}
}
void UpdateSoundLoc(void)
{
/* if (SoundPositioned)
{
SetSoundLoc(globalsoundx,globalsoundy);
SD_SetPosition(leftchannel,rightchannel);
}*/
/*
for(int i = 0; i < MIX_CHANNELS; i++)
{
if(channelSoundPos[i].valid)
{
SetSoundLoc(channelSoundPos[i].globalsoundx,
channelSoundPos[i].globalsoundy);
SD_SetPosition(i, leftchannel, rightchannel);
}
}*/
}
/*
** JAB End
*/
/*
==========================
=
= ScanInfoPlane
=
= Spawn all actors and mark down special places
=
==========================
*/
static 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 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:
#elif defined(USE_DIR3DSPR) // just for the example
case 73:
#endif
SpawnStatic(x,y,tile-23);
break;
//
// P wall
//
case 98:
if (!loadedgame)
gamestate.secrettotal++;
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 = facetimes = 0;
LastAttacker = NULL;
killerobj = NULL;
}
if (demoplayback || demorecord)
US_InitRndT (false);
else
US_InitRndT (true);
//
// load the level
//
CA_CacheMap (gamestate.mapon+10*gamestate.episode);
mapon-=gamestate.episode*10;
#ifdef USE_FEATUREFLAGS
// Temporary definition to make things clearer
#define MXX MAPSIZE - 1
// Read feature flags data from map corners and overwrite corners with adjacent tiles
ffDataTopLeft = MAPSPOT(0, 0, 0); MAPSPOT(0, 0, 0) = MAPSPOT(1, 0, 0);
ffDataTopRight = MAPSPOT(MXX, 0, 0); MAPSPOT(MXX, 0, 0) = MAPSPOT(MXX, 1, 0);
ffDataBottomRight = MAPSPOT(MXX, MXX, 0); MAPSPOT(MXX, MXX, 0) = MAPSPOT(MXX - 1, MXX, 0);
ffDataBottomLeft = MAPSPOT(0, MXX, 0); MAPSPOT(0, MXX, 0) = MAPSPOT(0, MXX - 1, 0);
#undef MXX
#endif
//
// 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 *)(uintptr_t) 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)(uintptr_t)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)
{
if(viewsize == 21) return;
const int sw = screenWidth;
const int sh = screenHeight;
const int vw = viewwidth;
const int vh = viewheight;
const int px = scaleFactor; // size of one "pixel"
const int h = sh - px * STATUSLINES;
const int xl = sw / 2 - vw / 2;
const int yl = (h - vh) / 2;
if(xl != 0)
{
VWB_BarScaledCoord(0, 0, xl - px, h, bordercol); // left side
VWB_BarScaledCoord(xl + vw + px, 0, xl - px * 2, h, bordercol); // right side
}
if(yl != 0)
{
VWB_BarScaledCoord(0, 0, sw, yl - px, bordercol); // upper side
VWB_BarScaledCoord(0, yl + vh + px, sw, yl - px, bordercol); // lower side
}
if(xl != 0)
{
// Paint game view border lines
VWB_BarScaledCoord(xl - px, yl - px, vw + px, px, 0); // upper border
VWB_BarScaledCoord(xl, yl + vh, vw + px, px, bordercol - 2); // lower border
VWB_BarScaledCoord(xl - px, yl - px, px, vh + px, 0); // left border
VWB_BarScaledCoord(xl + vw, yl - px, px, vh + px * 2, bordercol - 2); // right border
VWB_BarScaledCoord(xl - px, yl + vh, px, px, bordercol - 3); // lower left highlight
}
else
{
// Just paint a lower border line
VWB_BarScaledCoord(0, yl+vh, vw, px, bordercol-2); // lower border
}
}
/*
===================
=
= DrawStatusBorder
=
===================
*/
void DrawStatusBorder (byte color)
{
int statusborderw = (screenWidth-scaleFactor*320)/2;
VWB_BarScaledCoord (0,0,screenWidth,screenHeight-scaleFactor*(STATUSLINES-3),color);
VWB_BarScaledCoord (0,screenHeight-scaleFactor*(STATUSLINES-3),
statusborderw+scaleFactor*8,scaleFactor*(STATUSLINES-4),color);
VWB_BarScaledCoord (0,screenHeight-scaleFactor*2,screenWidth,scaleFactor*2,color);
VWB_BarScaledCoord (screenWidth-statusborderw-scaleFactor*8, screenHeight-scaleFactor*(STATUSLINES-3),
statusborderw+scaleFactor*8,scaleFactor*(STATUSLINES-4),color);
VWB_BarScaledCoord (statusborderw+scaleFactor*9, screenHeight-scaleFactor*3,
scaleFactor*97, scaleFactor*1, color-1);
VWB_BarScaledCoord (statusborderw+scaleFactor*106, screenHeight-scaleFactor*3,
scaleFactor*161, scaleFactor*1, color-2);
VWB_BarScaledCoord (statusborderw+scaleFactor*267, screenHeight-scaleFactor*3,
scaleFactor*44, scaleFactor*1, color-3);
VWB_BarScaledCoord (screenWidth-statusborderw-scaleFactor*9, screenHeight-scaleFactor*(STATUSLINES-4),
scaleFactor*1, scaleFactor*20, color-2);
VWB_BarScaledCoord (screenWidth-statusborderw-scaleFactor*9, screenHeight-scaleFactor*(STATUSLINES/2-4),
scaleFactor*1, scaleFactor*14, color-3);
}
/*
===================
=
= DrawPlayBorder
=
===================
*/
void DrawPlayBorder (void)
{
const int px = scaleFactor; // size of one "pixel"
if (bordercol != VIEWCOLOR)
DrawStatusBorder(bordercol);
else
{
const int statusborderw = (screenWidth-px*320)/2;
VWB_BarScaledCoord (0, screenHeight-px*STATUSLINES,
statusborderw+px*8, px*STATUSLINES, bordercol);
VWB_BarScaledCoord (screenWidth-statusborderw-px*8, screenHeight-px*STATUSLINES,
statusborderw+px*8, px*STATUSLINES, bordercol);
}
if((unsigned) viewheight == screenHeight) return;
VWB_BarScaledCoord (0,0,screenWidth,screenHeight-px*STATUSLINES,bordercol);
const int xl = screenWidth/2-viewwidth/2;
const int yl = (screenHeight-px*STATUSLINES-viewheight)/2;
VWB_BarScaledCoord (xl,yl,viewwidth,viewheight,0);
if(xl != 0)
{
// Paint game view border lines
VWB_BarScaledCoord(xl-px, yl-px, viewwidth+px, px, 0); // upper border
VWB_BarScaledCoord(xl, yl+viewheight, viewwidth+px, px, bordercol-2); // lower border
VWB_BarScaledCoord(xl-px, yl-px, px, viewheight+px, 0); // left border
VWB_BarScaledCoord(xl+viewwidth, yl-px, px, viewheight+2*px, bordercol-2); // right border
VWB_BarScaledCoord(xl-px, yl+viewheight, px, px, bordercol-3); // lower left highlight
}
else
{
// Just paint a lower border line
VWB_BarScaledCoord(0, yl+viewheight, viewwidth, px, bordercol-2); // lower border
}
}
/*
===================
=
= DrawPlayScreen
=
===================
*/
void DrawPlayScreen (void)
{
VWB_DrawPicScaledCoord ((screenWidth-scaleFactor*320)/2,screenHeight-scaleFactor*STATUSLINES,STATUSBARPIC);
DrawPlayBorder ();
DrawFace ();
DrawHealth ();
DrawLives ();
DrawLevel ();
DrawAmmo ();
DrawKeys ();
DrawWeapon ();
DrawScore ();
}
// Uses LatchDrawPic instead of StatusDrawPic
void LatchNumberHERE (int x, int y, unsigned width, int32_t number)
{
unsigned length,c;
char str[20];
ltoa (number,str,10);
length = (unsigned) strlen (str);
while (length<width)
{
LatchDrawPic (x,y,N_BLANKPIC);
x++;
width--;
}
c = length <= width ? 0 : length-width;
while (c<length)
{
LatchDrawPic (x,y,str[c]-'0'+ N_0PIC);
x++;
c++;
}
}
void ShowActStatus()
{
// Draw status bar without borders
byte *source = grsegs[STATUSBARPIC];
int picnum = STATUSBARPIC - STARTPICS;
int width = pictable[picnum].width;
int height = pictable[picnum].height;
int destx = (screenWidth-scaleFactor*320)/2 + 9 * scaleFactor;
int desty = screenHeight - (height - 4) * scaleFactor;
VL_MemToScreenScaledCoord(source, width, height, 9, 4, destx, desty, width - 18, height - 7);
ingame = false;
DrawFace ();
DrawHealth ();
DrawLives ();
DrawLevel ();
DrawAmmo ();
DrawKeys ();
DrawWeapon ();
DrawScore ();
ingame = true;
}
//==========================================================================
/*
==================
=
= StartDemoRecord
=
==================
*/
char demoname[13] = "DEMO?.";
#ifndef REMDEBUG
#define MAXDEMOSIZE 8192
void StartDemoRecord (int levelnumber)
{
demobuffer=malloc(MAXDEMOSIZE);
CHECKMALLOCRESULT(demobuffer);
demoptr = (int8_t *) demobuffer;
lastdemoptr = demoptr+MAXDEMOSIZE;
*demoptr = levelnumber;
demoptr += 4; // leave space for length
demorecord = true;
}
/*
==================
=
= FinishDemoRecord
=
==================
*/
void FinishDemoRecord (void)
{
int32_t length,level;
demorecord = false;
length = (int32_t) (demoptr - (int8_t *)demobuffer);
demoptr = ((int8_t *)demobuffer)+1;
demoptr[0] = (int8_t) length;
demoptr[1] = (int8_t) (length >> 8);
demoptr[2] = 0;
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 ();
if(usedoublebuffering) VH_UpdateScreen();
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 unfaded
=
==================
*/
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 = (int8_t *) grsegs[dems[demonumber]];
#else
demoname[4] = '0'+demonumber;
CA_LoadFile (demoname,&demobuffer);
demoptr = (int8_t *)demobuffer;
#endif
NewGame (1,0);
gamestate.mapon = *demoptr++;
gamestate.difficulty = gd_hard;
length = READWORD(*(uint8_t **)&demoptr);
// TODO: Seems like the original demo format supports 16 MB demos
// But T_DEM00 and T_DEM01 of Wolf have a 0xd8 as third length size...
demoptr++;
lastdemoptr = demoptr-4+length;
VW_FadeOut ();
SETFONTCOLOR(0,15);
DrawPlayScreen ();
startgame = false;
demoplayback = true;
SetupGameLevel ();
StartMusic ();
PlayLoop ();
#ifdef DEMOSEXTERN
UNCACHEGRCHUNK(dems[demonumber]);
#else
MM_FreePtr (&demobuffer);
#endif
demoplayback = false;
StopMusic ();
ClearMemory ();
}
//==========================================================================
/*
==================
=
= Died
=
==================
*/
#define DEATHROTATE 2
void Died (void)
{
float fangle;
int32_t dx,dy;
int iangle,curangle,clockwise,counter,change;
if (screenfaded)
{
ThreeDRefresh ();
VW_FadeIn ();
}
gamestate.weapon = (weapontype) -1; // take away weapon
SD_PlaySound (PLAYERDEATHSND);
//
// swing around to face attacker
//
if(killerobj)
{
dx = killerobj->x - player->x;
dy = player->y - killerobj->y;
fangle = (float) atan2((float) dy, (float) dx); // returns -pi to pi
if (fangle<0)
fangle = (float) (M_PI*2+fangle);
iangle = (int) (fangle/(M_PI*2)*ANGLES);
}
else
{
iangle = player->angle + ANGLES / 2;
if(iangle >= ANGLES) iangle -= 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 = -(int)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 ();
if(usedoublebuffering) VH_UpdateScreen();
VL_BarScaledCoord (viewscreenx,viewscreeny,viewwidth,viewheight,4);
IN_ClearKeysDown ();
FizzleFade(screenBuffer,viewscreenx,viewscreeny,viewwidth,viewheight,70,false);
IN_UserInput(100);
SD_WaitSoundDone ();
ClearMemory();
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;
if(viewsize != 21)
{
DrawKeys ();
DrawWeapon ();
DrawAmmo ();
DrawHealth ();
DrawFace ();
DrawLives ();
}
}
}
//==========================================================================
/*
===================
=
= GameLoop
=
===================
*/
void GameLoop (void)
{
boolean died;
#ifdef MYPROFILE
clock_t start,end;
#endif
restartgame:
ClearMemory ();
SETFONTCOLOR(0,15);
VW_FadeOut();
DrawPlayScreen ();
died = false;
do
{
if (!loadedgame)
gamestate.score = gamestate.oldscore;
if(!died || viewsize != 21) 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 ();
#ifdef SPEAR
startplayloop:
#endif
PlayLoop ();
#ifdef SPEAR
if (spearflag)
{
SD_StopSound();
SD_PlaySound(GETSPEARSND);
if (DigiMode != sds_Off)
{
Delay(150);
}
else
SD_WaitSoundDone();
ClearMemory ();
gamestate.oldscore = gamestate.score;
gamestate.mapon = 20;
SetupGameLevel ();
StartMusic ();
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:
if(viewsize == 21) DrawPlayScreen();
gamestate.keys = 0;
DrawKeys ();
VW_FadeOut ();
ClearMemory ();
LevelCompleted (); // do the intermission
if(viewsize == 21) DrawPlayScreen();
#ifdef SPEARDEMO
if (gamestate.mapon == 1)
{
died = true; // don't "get psyched!"
VW_FadeOut ();
ClearMemory ();
CheckHighScore (gamestate.score,gamestate.mapon+1);
#ifndef JAPAN
strcpy(MainMenu[viewscores].string,STR_VS);
#endif
MainMenu[viewscores].routine = CP_ViewScores;
return;
}
#endif
#ifdef JAPDEMO
if (gamestate.mapon == 3)
{
died = true; // don't "get psyched!"
VW_FadeOut ();
ClearMemory ();
CheckHighScore (gamestate.score,gamestate.mapon+1);
#ifndef JAPAN
strcpy(MainMenu[viewscores].string,STR_VS);
#endif
MainMenu[viewscores].routine = CP_ViewScores;
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 ();
if(screenHeight % 200 != 0)
VL_ClearScreen(0);
#ifdef _arch_dreamcast
DC_StatusClearLCD();
#endif
ClearMemory ();
CheckHighScore (gamestate.score,gamestate.mapon+1);
#ifndef JAPAN
strcpy(MainMenu[viewscores].string,STR_VS);
#endif
MainMenu[viewscores].routine = CP_ViewScores;
return;
case ex_victorious:
if(viewsize == 21) DrawPlayScreen();
#ifndef SPEAR
VW_FadeOut ();
#else
VL_FadeOut (0,255,0,17,17,300);
#endif
ClearMemory ();
Victory ();
ClearMemory ();
CheckHighScore (gamestate.score,gamestate.mapon+1);
#ifndef JAPAN
strcpy(MainMenu[viewscores].string,STR_VS);
#endif
MainMenu[viewscores].routine = CP_ViewScores;
return;
default:
if(viewsize == 21) DrawPlayScreen();
ClearMemory ();
break;
}
} while (1);
}