//===========================================================================
/*
#############################################################################
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.
backwardly linked free list
#############################################################################
*/
/*
=========================
=
= 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;i(MAXACTORS;i++)
{
objlist[i].prev = &objlist[i+1];
objlist[i].next = NULL;
}
objlist[MAXACTORS-1].prev = NULL;
objfreelist = &objlist[0];
lastobj = NULL;
objcount = 0;
//
// give the player the first free spots
//
GetNewActor ();
player = new;
}
//===========================================================================
/*
=========================
=
= GetNewActor
=
= Sets the global variable new to point to a free spot in objlist.
= The free spot is inserted at the end of the liked list
=
= When the object list is full, the caller can either have it bomb out ot
= return a dummy object pointer that will never get used
=
=========================
*/
void GetNewActor (void)
{
if (!objfreelist)
Quit ("GetNewActor: No free spots in objlist!");
new = objfreelist;
objfreelist = new->prev;
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 (&gamepal);
palshifted = true;
}
else if (white)
{
VW_WaitVBL(1);
VL_SetPalette (&gamepal);
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 ();
}