%20WL_PLAY.C
// WL_PLAY.C
#include "WL_DEF.H"
#pragma hdrstop
/*
=============================================================================
LOCAL CONSTANTS
=============================================================================
*/
#define sc_Question 0x35
/*
=============================================================================
GLOBAL VARIABLES
=============================================================================
*/
boolean madenoise; // true when shooting or screaming
exit_t playstate;
int DebugOk;
objtype objlist[MAXACTORS],*new,*obj,*player,*lastobj,
*objfreelist,*killerobj;
unsigned farmapylookup[MAPSIZE];
byte *nearmapylookup[MAPSIZE];
boolean singlestep,godmode,noclip;
int extravbls;
byte tilemap[MAPSIZE][MAPSIZE]; // wall values only
byte spotvis[MAPSIZE][MAPSIZE];
objtype *actorat[MAPSIZE][MAPSIZE];
//
// replacing refresh manager
//
unsigned mapwidth,mapheight,tics;
boolean compatability;
byte *updateptr;
unsigned mapwidthtable[64];
unsigned uwidthtable[UPDATEHIGH];
unsigned blockstarts[UPDATEWIDE*UPDATEHIGH];
byte update[UPDATESIZE];
//
// control info
//
boolean mouseenabled,joystickenabled,joypadenabled,joystickprogressive;
int joystickport;
int dirscan[4] = {sc_UpArrow,sc_RightArrow,sc_DownArrow,sc_LeftArrow};
int buttonscan[NUMBUTTONS] =
{sc_Control,sc_Alt,sc_RShift,sc_Space,sc_1,sc_2,sc_3,sc_4};
int buttonmouse[4]={bt_attack,bt_strafe,bt_use,bt_nobutton};
int buttonjoy[4]={bt_attack,bt_strafe,bt_use,bt_run};
int viewsize;
boolean buttonheld[NUMBUTTONS];
boolean demorecord,demoplayback;
char far *demoptr, far *lastdemoptr;
memptr demobuffer;
//
// curent user input
//
int controlx,controly; // range from -100 to 100 per tic
boolean buttonstate[NUMBUTTONS];
//===========================================================================
void CenterWindow(word w,word h);
void InitObjList (void);
void RemoveObj (objtype *gone);
void PollControls (void);
void StopMusic(void);
void StartMusic(void);
void PlayLoop (void);
/*
=============================================================================
LOCAL VARIABLES
=============================================================================
*/
objtype dummyobj;
//
// LIST OF SONGS FOR EACH VERSION
//
int songs[]=
{
#ifndef SPEAR
//
// Episode One
//
GETTHEM_MUS,
SEARCHN_MUS,
POW_MUS,
SUSPENSE_MUS,
GETTHEM_MUS,
SEARCHN_MUS,
POW_MUS,
SUSPENSE_MUS,
WARMARCH_MUS, // Boss level
CORNER_MUS, // Secret level
//
// Episode Two
//
NAZI_OMI_MUS,
PREGNANT_MUS,
GOINGAFT_MUS,
HEADACHE_MUS,
NAZI_OMI_MUS,
PREGNANT_MUS,
HEADACHE_MUS,
GOINGAFT_MUS,
WARMARCH_MUS, // Boss level
DUNGEON_MUS, // Secret level
//
// Episode Three
//
INTROCW3_MUS,
NAZI_RAP_MUS,
TWELFTH_MUS,
ZEROHOUR_MUS,
INTROCW3_MUS,
NAZI_RAP_MUS,
TWELFTH_MUS,
ZEROHOUR_MUS,
ULTIMATE_MUS, // Boss level
PACMAN_MUS, // Secret level
//
// Episode Four
//
GETTHEM_MUS,
SEARCHN_MUS,
POW_MUS,
SUSPENSE_MUS,
GETTHEM_MUS,
SEARCHN_MUS,
POW_MUS,
SUSPENSE_MUS,
WARMARCH_MUS, // Boss level
CORNER_MUS, // Secret level
//
// Episode Five
//
NAZI_OMI_MUS,
PREGNANT_MUS,
GOINGAFT_MUS,
HEADACHE_MUS,
NAZI_OMI_MUS,
PREGNANT_MUS,
HEADACHE_MUS,
GOINGAFT_MUS,
WARMARCH_MUS, // Boss level
DUNGEON_MUS, // Secret level
//
// Episode Six
//
INTROCW3_MUS,
NAZI_RAP_MUS,
TWELFTH_MUS,
ZEROHOUR_MUS,
INTROCW3_MUS,
NAZI_RAP_MUS,
TWELFTH_MUS,
ZEROHOUR_MUS,
ULTIMATE_MUS, // Boss level
FUNKYOU_MUS // Secret level
#else
//////////////////////////////////////////////////////////////
//
// SPEAR OF DESTINY TRACKS
//
//////////////////////////////////////////////////////////////
XTIPTOE_MUS,
XFUNKIE_MUS,
XDEATH_MUS,
XGETYOU_MUS, // DON'T KNOW
ULTIMATE_MUS, // Trans Gr”sse
DUNGEON_MUS,
GOINGAFT_MUS,
POW_MUS,
TWELFTH_MUS,
ULTIMATE_MUS, // Barnacle Wilhelm BOSS
NAZI_OMI_MUS,
GETTHEM_MUS,
SUSPENSE_MUS,
SEARCHN_MUS,
ZEROHOUR_MUS,
ULTIMATE_MUS, // Super Mutant BOSS
XPUTIT_MUS,
ULTIMATE_MUS, // Death Knight BOSS
XJAZNAZI_MUS, // Secret level
XFUNKIE_MUS, // Secret level (DON'T KNOW)
XEVIL_MUS // Angel of Death BOSS
#endif
};
/*
=============================================================================
USER CONTROL
=============================================================================
*/
#define BASEMOVE 35
#define RUNMOVE 70
#define BASETURN 35
#define RUNTURN 70
#define JOYSCALE 2
/*
===================
=
= PollKeyboardButtons
=
===================
*/
void PollKeyboardButtons (void)
{
int i;
for (i=0;i 64)
controlx += (joyx-64)*JOYSCALE*tics;
else if (joyx < -64)
controlx -= (-joyx-64)*JOYSCALE*tics;
if (joyy > 64)
controlx += (joyy-64)*JOYSCALE*tics;
else if (joyy < -64)
controly -= (-joyy-64)*JOYSCALE*tics;
}
else if (buttonstate[bt_run])
{
if (joyx > 64)
controlx += RUNMOVE*tics;
else if (joyx < -64)
controlx -= RUNMOVE*tics;
if (joyy > 64)
controly += RUNMOVE*tics;
else if (joyy < -64)
controly -= RUNMOVE*tics;
}
else
{
if (joyx > 64)
controlx += BASEMOVE*tics;
else if (joyx < -64)
controlx -= BASEMOVE*tics;
if (joyy > 64)
controly += BASEMOVE*tics;
else if (joyy < -64)
controly -= BASEMOVE*tics;
}
}
/*
===================
=
= PollControls
=
= Gets user or demo input, call once each frame
=
= controlx set between -100 and 100 per tic
= controly
= buttonheld[] the state of the buttons LAST frame
= buttonstate[] the state of the buttons THIS frame
=
===================
*/
void PollControls (void)
{
int max,min,i;
byte buttonbits;
//
// get timing info for last frame
//
if (demoplayback)
{
while (TimeCount>= 1;
}
controlx = *demoptr++;
controly = *demoptr++;
if (demoptr == lastdemoptr)
playstate = ex_completed; // demo is done
controlx *= (int)tics;
controly *= (int)tics;
return;
}
//
// get button states
//
PollKeyboardButtons ();
if (mouseenabled)
PollMouseButtons ();
if (joystickenabled)
PollJoystickButtons ();
//
// get movements
//
PollKeyboardMove ();
if (mouseenabled)
PollMouseMove ();
if (joystickenabled)
PollJoystickMove ();
//
// bound movement to a maximum
//
max = 100*tics;
min = -max;
if (controlx > max)
controlx = max;
else if (controlx < min)
controlx = min;
if (controly > max)
controly = max;
else if (controly < min)
controly = min;
if (demorecord)
{
//
// save info out to demo buffer
//
controlx /= (int)tics;
controly /= (int)tics;
buttonbits = 0;
for (i=NUMBUTTONS-1;i>=0;i--)
{
buttonbits <<= 1;
if (buttonstate[i])
buttonbits |= 1;
}
*demoptr++ = buttonbits;
*demoptr++ = controlx;
*demoptr++ = controly;
if (demoptr >= lastdemoptr)
Quit ("Demo buffer overflowed!");
controlx *= (int)tics;
controly *= (int)tics;
}
}
//==========================================================================
///////////////////////////////////////////////////////////////////////////
//
// CenterWindow() - Generates a window of a given width & height in the
// middle of the screen
//
///////////////////////////////////////////////////////////////////////////
#define MAXX 320
#define MAXY 160
void CenterWindow(word w,word h)
{
FixOfs ();
US_DrawWindow(((MAXX / 8) - w) / 2,((MAXY / 8) - h) / 2,w,h);
}
//===========================================================================
/*
=====================
=
= CheckKeys
=
=====================
*/
void CheckKeys (void)
{
int i;
byte scan;
unsigned temp;
if (screenfaded || demoplayback) // don't do anything with a faded screen
return;
scan = LastScan;
#ifdef SPEAR
//
// SECRET CHEAT CODE: TAB-G-F10
//
if (Keyboard[sc_Tab] &&
Keyboard[sc_G] &&
Keyboard[sc_F10])
{
WindowH = 160;
if (godmode)
{
Message ("God mode OFF");
SD_PlaySound (NOBONUSSND);
}
else
{
Message ("God mode ON");
SD_PlaySound (ENDBONUS2SND);
}
IN_Ack();
godmode ^= 1;
DrawAllPlayBorderSides ();
IN_ClearKeysDown();
return;
}
#endif
//
// SECRET CHEAT CODE: 'MLI'
//
if (Keyboard[sc_M] &&
Keyboard[sc_L] &&
Keyboard[sc_I])
{
gamestate.health = 100;
gamestate.ammo = 99;
gamestate.keys = 3;
gamestate.score = 0;
gamestate.TimeCount += 42000L;
GiveWeapon (wp_chaingun);
DrawWeapon();
DrawHealth();
DrawKeys();
DrawAmmo();
DrawScore();
ClearMemory ();
CA_CacheGrChunk (STARTFONT+1);
ClearSplitVWB ();
VW_ScreenToScreen (displayofs,bufferofs,80,160);
Message(STR_CHEATER1"\n"
STR_CHEATER2"\n\n"
STR_CHEATER3"\n"
STR_CHEATER4"\n"
STR_CHEATER5);
UNCACHEGRCHUNK(STARTFONT+1);
PM_CheckMainMem ();
IN_ClearKeysDown();
IN_Ack();
DrawAllPlayBorder ();
}
//
// OPEN UP DEBUG KEYS
//
#ifndef SPEAR
if (Keyboard[sc_BackSpace] &&
Keyboard[sc_LShift] &&
Keyboard[sc_Alt] &&
MS_CheckParm("goobers"))
#else
if (Keyboard[sc_BackSpace] &&
Keyboard[sc_LShift] &&
Keyboard[sc_Alt] &&
MS_CheckParm("debugmode"))
#endif
{
ClearMemory ();
CA_CacheGrChunk (STARTFONT+1);
ClearSplitVWB ();
VW_ScreenToScreen (displayofs,bufferofs,80,160);
Message("Debugging keys are\nnow available!");
UNCACHEGRCHUNK(STARTFONT+1);
PM_CheckMainMem ();
IN_ClearKeysDown();
IN_Ack();
DrawAllPlayBorderSides ();
DebugOk=1;
}
//
// TRYING THE KEEN CHEAT CODE!
//
if (Keyboard[sc_B] &&
Keyboard[sc_A] &&
Keyboard[sc_T])
{
ClearMemory ();
CA_CacheGrChunk (STARTFONT+1);
ClearSplitVWB ();
VW_ScreenToScreen (displayofs,bufferofs,80,160);
Message("Commander Keen is also\n"
"available from Apogee, but\n"
"then, you already know\n"
"that - right, Cheatmeister?!");
UNCACHEGRCHUNK(STARTFONT+1);
PM_CheckMainMem ();
IN_ClearKeysDown();
IN_Ack();
DrawAllPlayBorder ();
}
//
// pause key weirdness can't be checked as a scan code
//
if (Paused)
{
bufferofs = displayofs;
LatchDrawPic (20-4,80-2*8,PAUSEDPIC);
SD_MusicOff();
IN_Ack();
IN_ClearKeysDown ();
SD_MusicOn();
Paused = false;
if (MousePresent)
Mouse(MDelta); // Clear accumulated mouse movement
return;
}
//
// F1-F7/ESC to enter control panel
//
if (
#ifndef DEBCHECK
scan == sc_F10 ||
#endif
scan == sc_F9 ||
scan == sc_F7 ||
scan == sc_F8) // pop up quit dialog
{
ClearMemory ();
ClearSplitVWB ();
VW_ScreenToScreen (displayofs,bufferofs,80,160);
US_ControlPanel(scan);
DrawAllPlayBorderSides ();
if (scan == sc_F9)
StartMusic ();
PM_CheckMainMem ();
SETFONTCOLOR(0,15);
IN_ClearKeysDown();
return;
}
if ( (scan >= sc_F1 && scan <= sc_F9) || scan == sc_Escape)
{
StopMusic ();
ClearMemory ();
VW_FadeOut ();
US_ControlPanel(scan);
SETFONTCOLOR(0,15);
IN_ClearKeysDown();
DrawPlayScreen ();
if (!startgame && !loadedgame)
{
VW_FadeIn ();
StartMusic ();
}
if (loadedgame)
playstate = ex_abort;
lasttimecount = TimeCount;
if (MousePresent)
Mouse(MDelta); // Clear accumulated mouse movement
PM_CheckMainMem ();
return;
}
//
// TAB-? debug keys
//
if (Keyboard[sc_Tab] && DebugOk)
{
CA_CacheGrChunk (STARTFONT);
fontnumber=0;
SETFONTCOLOR(0,15);
DebugKeys();
if (MousePresent)
Mouse(MDelta); // Clear accumulated mouse movement
lasttimecount = TimeCount;
return;
}
}
//===========================================================================
/*
#############################################################################
The objlist data structure
#############################################################################
objlist containt structures for every actor currently playing. The structure
is accessed as a linked list starting at *player, ending when ob->next ==
NULL. GetNewObj inserts a new object at the end of the list, meaning that
if an actor spawn another actor, the new one WILL get to think and react the
same frame. RemoveObj unlinks the given object and returns it to the free
list, but does not damage the objects ->next pointer, so if the current object
removes itself, a linked list following loop can still safely get to the
next element.
#############################################################################
*/
/*
=========================
=
= InitActorList
=
= Call to clear out the actor object lists returning them all to the free
= list. Allocates a special spot for the player.
=
=========================
*/
int objcount;
void InitActorList (void)
{
int i;
//
// init the actor lists
//
for (i=0;iprev;
memset (new,0,sizeof(*new));
if (lastobj)
lastobj->next = new;
new->prev = lastobj; // new->next is allready NULL from memset
new->active = false;
lastobj = new;
objcount++;
}
//===========================================================================
/*
=========================
=
= RemoveObj
=
= Add the given object back into the free list, and unlink it from it's
= neighbors
=
=========================
*/
void RemoveObj (objtype *gone)
{
objtype **spotat;
if (gone == player)
Quit ("RemoveObj: Tried to remove the player!");
gone->state = NULL;
//
// fix the next object's back link
//
if (gone == lastobj)
lastobj = (objtype *)gone->prev;
else
gone->next->prev = gone->prev;
//
// fix the previous object's forward link
//
gone->prev->next = gone->next;
//
// add it back in to the free list
//
gone->prev = objfreelist;
objfreelist = gone;
objcount--;
}
/*
=============================================================================
MUSIC STUFF
=============================================================================
*/
/*
=================
=
= StopMusic
=
=================
*/
void StopMusic(void)
{
int i;
SD_MusicOff();
for (i = 0;i < LASTMUSIC;i++)
if (audiosegs[STARTMUSIC + i])
{
MM_SetPurge(&((memptr)audiosegs[STARTMUSIC + i]),3);
MM_SetLock(&((memptr)audiosegs[STARTMUSIC + i]),false);
}
}
//==========================================================================
/*
=================
=
= StartMusic
=
=================
*/
void StartMusic(void)
{
musicnames chunk;
SD_MusicOff();
chunk = songs[gamestate.mapon+gamestate.episode*10];
// if ((chunk == -1) || (MusicMode != smm_AdLib))
//DEBUG control panel return;
MM_BombOnError (false);
CA_CacheAudioChunk(STARTMUSIC + chunk);
MM_BombOnError (true);
if (mmerror)
mmerror = false;
else
{
MM_SetLock(&((memptr)audiosegs[STARTMUSIC + chunk]),true);
SD_StartMusic((MusicGroup far *)audiosegs[STARTMUSIC + chunk]);
}
}
/*
=============================================================================
PALETTE SHIFTING STUFF
=============================================================================
*/
#define NUMREDSHIFTS 6
#define REDSTEPS 8
#define NUMWHITESHIFTS 3
#define WHITESTEPS 20
#define WHITETICS 6
byte far redshifts[NUMREDSHIFTS][768];
byte far whiteshifts[NUMREDSHIFTS][768];
int damagecount,bonuscount;
boolean palshifted;
extern byte far gamepal;
/*
=====================
=
= InitRedShifts
=
=====================
*/
void InitRedShifts (void)
{
byte far *workptr, far *baseptr;
int i,j,delta;
//
// fade through intermediate frames
//
for (i=1;i<=NUMREDSHIFTS;i++)
{
workptr = (byte far *)&redshifts[i-1][0];
baseptr = &gamepal;
for (j=0;j<=255;j++)
{
delta = 64-*baseptr;
*workptr++ = *baseptr++ + delta * i / REDSTEPS;
delta = -*baseptr;
*workptr++ = *baseptr++ + delta * i / REDSTEPS;
delta = -*baseptr;
*workptr++ = *baseptr++ + delta * i / REDSTEPS;
}
}
for (i=1;i<=NUMWHITESHIFTS;i++)
{
workptr = (byte far *)&whiteshifts[i-1][0];
baseptr = &gamepal;
for (j=0;j<=255;j++)
{
delta = 64-*baseptr;
*workptr++ = *baseptr++ + delta * i / WHITESTEPS;
delta = 62-*baseptr;
*workptr++ = *baseptr++ + delta * i / WHITESTEPS;
delta = 0-*baseptr;
*workptr++ = *baseptr++ + delta * i / WHITESTEPS;
}
}
}
/*
=====================
=
= ClearPaletteShifts
=
=====================
*/
void ClearPaletteShifts (void)
{
bonuscount = damagecount = 0;
}
/*
=====================
=
= StartBonusFlash
=
=====================
*/
void StartBonusFlash (void)
{
bonuscount = NUMWHITESHIFTS*WHITETICS; // white shift palette
}
/*
=====================
=
= StartDamageFlash
=
=====================
*/
void StartDamageFlash (int damage)
{
damagecount += damage;
}
/*
=====================
=
= UpdatePaletteShifts
=
=====================
*/
void UpdatePaletteShifts (void)
{
int red,white;
if (bonuscount)
{
white = bonuscount/WHITETICS +1;
if (white>NUMWHITESHIFTS)
white = NUMWHITESHIFTS;
bonuscount -= tics;
if (bonuscount < 0)
bonuscount = 0;
}
else
white = 0;
if (damagecount)
{
red = damagecount/10 +1;
if (red>NUMREDSHIFTS)
red = NUMREDSHIFTS;
damagecount -= tics;
if (damagecount < 0)
damagecount = 0;
}
else
red = 0;
if (red)
{
VW_WaitVBL(1);
VL_SetPalette (redshifts[red-1]);
palshifted = true;
}
else if (white)
{
VW_WaitVBL(1);
VL_SetPalette (whiteshifts[white-1]);
palshifted = true;
}
else if (palshifted)
{
VW_WaitVBL(1);
VL_SetPalette (&gamepal); // back to normal
palshifted = false;
}
}
/*
=====================
=
= FinishPaletteShifts
=
= Resets palette to normal if needed
=
=====================
*/
void FinishPaletteShifts (void)
{
if (palshifted)
{
palshifted = 0;
VW_WaitVBL(1);
VL_SetPalette (&gamepal);
}
}
/*
=============================================================================
CORE PLAYLOOP
=============================================================================
*/
/*
=====================
=
= DoActor
=
=====================
*/
void DoActor (objtype *ob)
{
void (*think)(objtype *);
if (!ob->active && !areabyplayer[ob->areanumber])
return;
if (!(ob->flags&(FL_NONMARK|FL_NEVERMARK)) )
actorat[ob->tilex][ob->tiley] = NULL;
//
// non transitional object
//
if (!ob->ticcount)
{
think = ob->state->think;
if (think)
{
think (ob);
if (!ob->state)
{
RemoveObj (ob);
return;
}
}
if (ob->flags&FL_NEVERMARK)
return;
if ( (ob->flags&FL_NONMARK) && actorat[ob->tilex][ob->tiley])
return;
actorat[ob->tilex][ob->tiley] = ob;
return;
}
//
// transitional object
//
ob->ticcount-=tics;
while ( ob->ticcount <= 0)
{
think = ob->state->action; // end of state action
if (think)
{
think (ob);
if (!ob->state)
{
RemoveObj (ob);
return;
}
}
ob->state = ob->state->next;
if (!ob->state)
{
RemoveObj (ob);
return;
}
if (!ob->state->tictime)
{
ob->ticcount = 0;
goto think;
}
ob->ticcount += ob->state->tictime;
}
think:
//
// think
//
think = ob->state->think;
if (think)
{
think (ob);
if (!ob->state)
{
RemoveObj (ob);
return;
}
}
if (ob->flags&FL_NEVERMARK)
return;
if ( (ob->flags&FL_NONMARK) && actorat[ob->tilex][ob->tiley])
return;
actorat[ob->tilex][ob->tiley] = ob;
}
//==========================================================================
/*
===================
=
= PlayLoop
=
===================
*/
long funnyticount;
void PlayLoop (void)
{
int give;
int helmetangle;
playstate = TimeCount = lasttimecount = 0;
frameon = 0;
running = false;
anglefrac = 0;
facecount = 0;
funnyticount = 0;
memset (buttonstate,0,sizeof(buttonstate));
ClearPaletteShifts ();
if (MousePresent)
Mouse(MDelta); // Clear accumulated mouse movement
if (demoplayback)
IN_StartAck ();
do
{
if (virtualreality)
{
helmetangle = peek (0x40,0xf0);
player->angle += helmetangle;
if (player->angle >= ANGLES)
player->angle -= ANGLES;
}
PollControls();
//
// actor thinking
//
madenoise = false;
MoveDoors ();
MovePWalls ();
for (obj = player;obj;obj = obj->next)
DoActor (obj);
UpdatePaletteShifts ();
ThreeDRefresh ();
//
// MAKE FUNNY FACE IF BJ DOESN'T MOVE FOR AWHILE
//
#ifdef SPEAR
funnyticount += tics;
if (funnyticount > 30l*70)
{
funnyticount = 0;
StatusDrawPic (17,4,BJWAITING1PIC+(US_RndT()&1));
facecount = 0;
}
#endif
gamestate.TimeCount+=tics;
SD_Poll ();
UpdateSoundLoc(); // JAB
if (screenfaded)
VW_FadeIn ();
CheckKeys();
//
// debug aids
//
if (singlestep)
{
VW_WaitVBL(14);
lasttimecount = TimeCount;
}
if (extravbls)
VW_WaitVBL(extravbls);
if (demoplayback)
{
if (IN_CheckAck ())
{
IN_ClearKeysDown ();
playstate = ex_abort;
}
}
if (virtualreality)
{
player->angle -= helmetangle;
if (player->angle < 0)
player->angle += ANGLES;
}
}while (!playstate && !startgame);
if (playstate != ex_died)
FinishPaletteShifts ();
}