forked from KolibriOS/kolibrios
9ba1adb152
- Now used standart SDL timer functions; - Removed old files and useless functions. git-svn-id: svn://kolibrios.org@9790 a494cfbc-eb01-0410-851d-a64ba20cac60
1354 lines
27 KiB
C++
Executable File
1354 lines
27 KiB
C++
Executable File
// WL_PLAY.C
|
||
|
||
#include "wl_def.h"
|
||
#pragma hdrstop
|
||
|
||
#include "wl_cloudsky.h"
|
||
#include "wl_shade.h"
|
||
|
||
/*
|
||
=============================================================================
|
||
|
||
LOCAL CONSTANTS
|
||
|
||
=============================================================================
|
||
*/
|
||
|
||
#define sc_Question 0x35
|
||
|
||
/*
|
||
=============================================================================
|
||
|
||
GLOBAL VARIABLES
|
||
|
||
=============================================================================
|
||
*/
|
||
|
||
boolean madenoise; // true when shooting or screaming
|
||
|
||
exit_t playstate;
|
||
|
||
static musicnames lastmusicchunk = (musicnames) 0;
|
||
|
||
static int DebugOk;
|
||
|
||
objtype objlist[MAXACTORS];
|
||
objtype *newobj, *obj, *player, *lastobj, *objfreelist, *killerobj;
|
||
|
||
boolean noclip, ammocheat;
|
||
int godmode, singlestep, extravbls = 0;
|
||
|
||
byte tilemap[MAPSIZE][MAPSIZE]; // wall values only
|
||
byte spotvis[MAPSIZE][MAPSIZE];
|
||
objtype *actorat[MAPSIZE][MAPSIZE];
|
||
|
||
//
|
||
// replacing refresh manager
|
||
//
|
||
unsigned tics;
|
||
|
||
//
|
||
// control info
|
||
//
|
||
boolean mouseenabled, joystickenabled;
|
||
int dirscan[4] = { sc_UpArrow, sc_RightArrow, sc_DownArrow, sc_LeftArrow };
|
||
int buttonscan[NUMBUTTONS] = { sc_Control, sc_Alt, sc_LShift, sc_Space, sc_1, sc_2, sc_3, sc_4 };
|
||
int buttonmouse[4] = { bt_attack, bt_strafe, bt_use, bt_nobutton };
|
||
int buttonjoy[32] = {
|
||
#ifdef _arch_dreamcast
|
||
bt_attack, bt_strafe, bt_use, bt_run, bt_esc, bt_prevweapon, bt_nobutton, bt_nextweapon,
|
||
bt_pause, bt_strafeleft, bt_straferight, bt_nobutton, bt_nobutton, bt_nobutton, bt_nobutton, bt_nobutton,
|
||
#else
|
||
bt_attack, bt_strafe, bt_use, bt_run, bt_strafeleft, bt_straferight, bt_esc, bt_pause,
|
||
bt_prevweapon, bt_nextweapon, bt_nobutton, bt_nobutton, bt_nobutton, bt_nobutton, bt_nobutton, bt_nobutton,
|
||
#endif
|
||
bt_nobutton, bt_nobutton, bt_nobutton, bt_nobutton, bt_nobutton, bt_nobutton, bt_nobutton, bt_nobutton,
|
||
bt_nobutton, bt_nobutton, bt_nobutton, bt_nobutton, bt_nobutton, bt_nobutton, bt_nobutton, bt_nobutton
|
||
};
|
||
|
||
int viewsize;
|
||
|
||
boolean buttonheld[NUMBUTTONS];
|
||
|
||
boolean demorecord, demoplayback;
|
||
int8_t *demoptr, *lastdemoptr;
|
||
memptr demobuffer;
|
||
|
||
//
|
||
// current user input
|
||
//
|
||
int controlx, controly; // range from -100 to 100 per tic
|
||
boolean buttonstate[NUMBUTTONS];
|
||
|
||
int lastgamemusicoffset = 0;
|
||
|
||
|
||
//===========================================================================
|
||
|
||
|
||
void CenterWindow (word w, word h);
|
||
void InitObjList (void);
|
||
void RemoveObj (objtype * gone);
|
||
void PollControls (void);
|
||
int StopMusic (void);
|
||
void StartMusic (void);
|
||
void ContinueMusic (int offs);
|
||
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<47>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
|
||
|
||
=============================================================================
|
||
*/
|
||
|
||
/*
|
||
===================
|
||
=
|
||
= PollKeyboardButtons
|
||
=
|
||
===================
|
||
*/
|
||
|
||
void PollKeyboardButtons (void)
|
||
{
|
||
int i;
|
||
|
||
for (i = 0; i < NUMBUTTONS; i++)
|
||
if (Keyboard[buttonscan[i]])
|
||
buttonstate[i] = true;
|
||
}
|
||
|
||
|
||
/*
|
||
===================
|
||
=
|
||
= PollMouseButtons
|
||
=
|
||
===================
|
||
*/
|
||
|
||
void PollMouseButtons (void)
|
||
{
|
||
int buttons = IN_MouseButtons ();
|
||
|
||
if (buttons & 1)
|
||
buttonstate[buttonmouse[0]] = true;
|
||
if (buttons & 2)
|
||
buttonstate[buttonmouse[1]] = true;
|
||
if (buttons & 4)
|
||
buttonstate[buttonmouse[2]] = true;
|
||
}
|
||
|
||
|
||
|
||
/*
|
||
===================
|
||
=
|
||
= PollJoystickButtons
|
||
=
|
||
===================
|
||
*/
|
||
|
||
void PollJoystickButtons (void)
|
||
{
|
||
int buttons = IN_JoyButtons();
|
||
|
||
for(int i = 0, val = 1; i < JoyNumButtons; i++, val <<= 1)
|
||
{
|
||
if(buttons & val)
|
||
buttonstate[buttonjoy[i]] = true;
|
||
}
|
||
}
|
||
|
||
|
||
/*
|
||
===================
|
||
=
|
||
= PollKeyboardMove
|
||
=
|
||
===================
|
||
*/
|
||
|
||
void PollKeyboardMove (void)
|
||
{
|
||
int delta = buttonstate[bt_run] ? RUNMOVE * tics : BASEMOVE * tics;
|
||
|
||
if (Keyboard[dirscan[di_north]])
|
||
controly -= delta;
|
||
if (Keyboard[dirscan[di_south]])
|
||
controly += delta;
|
||
if (Keyboard[dirscan[di_west]])
|
||
controlx -= delta;
|
||
if (Keyboard[dirscan[di_east]])
|
||
controlx += delta;
|
||
}
|
||
|
||
|
||
/*
|
||
===================
|
||
=
|
||
= PollMouseMove
|
||
=
|
||
===================
|
||
*/
|
||
|
||
void PollMouseMove (void)
|
||
{
|
||
int mousexmove, mouseymove;
|
||
|
||
SDL_GetMouseState(&mousexmove, &mouseymove);
|
||
if(IN_IsInputGrabbed())
|
||
IN_CenterMouse();
|
||
|
||
mousexmove -= screenWidth / 2;
|
||
mouseymove -= screenHeight / 2;
|
||
|
||
controlx += mousexmove * 10 / (13 - mouseadjustment);
|
||
controly += mouseymove * 20 / (13 - mouseadjustment);
|
||
}
|
||
|
||
|
||
/*
|
||
===================
|
||
=
|
||
= PollJoystickMove
|
||
=
|
||
===================
|
||
*/
|
||
|
||
void PollJoystickMove (void)
|
||
{
|
||
int joyx, joyy;
|
||
|
||
IN_GetJoyDelta (&joyx, &joyy);
|
||
|
||
int delta = buttonstate[bt_run] ? RUNMOVE * tics : BASEMOVE * tics;
|
||
|
||
if (joyx > 64 || buttonstate[bt_turnright])
|
||
controlx += delta;
|
||
else if (joyx < -64 || buttonstate[bt_turnleft])
|
||
controlx -= delta;
|
||
if (joyy > 64 || buttonstate[bt_movebackward])
|
||
controly += delta;
|
||
else if (joyy < -64 || buttonstate[bt_moveforward])
|
||
controly -= delta;
|
||
}
|
||
|
||
/*
|
||
===================
|
||
=
|
||
= 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;
|
||
|
||
IN_ProcessEvents();
|
||
|
||
//
|
||
// get timing info for last frame
|
||
//
|
||
if (demoplayback || demorecord) // demo recording and playback needs to be constant
|
||
{
|
||
// wait up to DEMOTICS Wolf tics
|
||
uint32_t curtime = SDL_GetTicks();
|
||
lasttimecount += DEMOTICS;
|
||
int32_t timediff = (lasttimecount * 100) / 7 - curtime;
|
||
if(timediff > 0)
|
||
SDL_Delay(timediff);
|
||
|
||
if(timediff < -2 * DEMOTICS) // more than 2-times DEMOTICS behind?
|
||
lasttimecount = (curtime * 7) / 100; // yes, set to current timecount
|
||
|
||
tics = DEMOTICS;
|
||
}
|
||
else
|
||
CalcTics ();
|
||
|
||
controlx = 0;
|
||
controly = 0;
|
||
memcpy (buttonheld, buttonstate, sizeof (buttonstate));
|
||
memset (buttonstate, 0, sizeof (buttonstate));
|
||
|
||
if (demoplayback)
|
||
{
|
||
//
|
||
// read commands from demo buffer
|
||
//
|
||
buttonbits = *demoptr++;
|
||
for (i = 0; i < NUMBUTTONS; i++)
|
||
{
|
||
buttonstate[i] = buttonbits & 1;
|
||
buttonbits >>= 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 && IN_IsInputGrabbed())
|
||
PollMouseButtons ();
|
||
|
||
if (joystickenabled)
|
||
PollJoystickButtons ();
|
||
|
||
//
|
||
// get movements
|
||
//
|
||
PollKeyboardMove ();
|
||
|
||
if (mouseenabled && IN_IsInputGrabbed())
|
||
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;
|
||
|
||
// TODO: Support 32-bit buttonbits
|
||
for (i = NUMBUTTONS - 1; i >= 0; i--)
|
||
{
|
||
buttonbits <<= 1;
|
||
if (buttonstate[i])
|
||
buttonbits |= 1;
|
||
}
|
||
|
||
*demoptr++ = buttonbits;
|
||
*demoptr++ = controlx;
|
||
*demoptr++ = controly;
|
||
|
||
if (demoptr >= lastdemoptr - 8)
|
||
playstate = ex_completed;
|
||
else
|
||
{
|
||
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)
|
||
{
|
||
US_DrawWindow (((MAXX / 8) - w) / 2, ((MAXY / 8) - h) / 2, w, h);
|
||
}
|
||
|
||
//===========================================================================
|
||
|
||
|
||
/*
|
||
=====================
|
||
=
|
||
= CheckKeys
|
||
=
|
||
=====================
|
||
*/
|
||
|
||
void CheckKeys (void)
|
||
{
|
||
ScanCode scan;
|
||
|
||
|
||
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;
|
||
DrawPlayBorderSides ();
|
||
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 ();
|
||
|
||
Message (STR_CHEATER1 "\n"
|
||
STR_CHEATER2 "\n\n" STR_CHEATER3 "\n" STR_CHEATER4 "\n" STR_CHEATER5);
|
||
|
||
UNCACHEGRCHUNK (STARTFONT + 1);
|
||
IN_ClearKeysDown ();
|
||
IN_Ack ();
|
||
|
||
if (viewsize < 17)
|
||
DrawPlayBorder ();
|
||
}
|
||
|
||
//
|
||
// OPEN UP DEBUG KEYS
|
||
//
|
||
#ifdef DEBUGKEYS
|
||
if (Keyboard[sc_BackSpace] && Keyboard[sc_LShift] && Keyboard[sc_Alt] && param_debugmode)
|
||
{
|
||
ClearMemory ();
|
||
CA_CacheGrChunk (STARTFONT + 1);
|
||
ClearSplitVWB ();
|
||
|
||
Message ("Debugging keys are\nnow available!");
|
||
UNCACHEGRCHUNK (STARTFONT + 1);
|
||
IN_ClearKeysDown ();
|
||
IN_Ack ();
|
||
|
||
DrawPlayBorderSides ();
|
||
DebugOk = 1;
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// TRYING THE KEEN CHEAT CODE!
|
||
//
|
||
if (Keyboard[sc_B] && Keyboard[sc_A] && Keyboard[sc_T])
|
||
{
|
||
ClearMemory ();
|
||
CA_CacheGrChunk (STARTFONT + 1);
|
||
ClearSplitVWB ();
|
||
|
||
Message ("Commander Keen is also\n"
|
||
"available from Apogee, but\n"
|
||
"then, you already know\n" "that - right, Cheatmeister?!");
|
||
|
||
UNCACHEGRCHUNK (STARTFONT + 1);
|
||
IN_ClearKeysDown ();
|
||
IN_Ack ();
|
||
|
||
if (viewsize < 18)
|
||
DrawPlayBorder ();
|
||
}
|
||
|
||
//
|
||
// pause key weirdness can't be checked as a scan code
|
||
//
|
||
if(buttonstate[bt_pause]) Paused = true;
|
||
if(Paused)
|
||
{
|
||
int lastoffs = StopMusic();
|
||
LatchDrawPic (20 - 4, 80 - 2 * 8, PAUSEDPIC);
|
||
VH_UpdateScreen();
|
||
IN_Ack ();
|
||
Paused = false;
|
||
ContinueMusic(lastoffs);
|
||
if (MousePresent && IN_IsInputGrabbed())
|
||
IN_CenterMouse(); // Clear accumulated mouse movement
|
||
lasttimecount = GetTimeCount();
|
||
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
|
||
{
|
||
short oldmapon = gamestate.mapon;
|
||
short oldepisode = gamestate.episode;
|
||
ClearMemory ();
|
||
ClearSplitVWB ();
|
||
US_ControlPanel (scan);
|
||
|
||
DrawPlayBorderSides ();
|
||
|
||
SETFONTCOLOR (0, 15);
|
||
IN_ClearKeysDown ();
|
||
return;
|
||
}
|
||
|
||
if ((scan >= sc_F1 && scan <= sc_F9) || scan == sc_Escape || buttonstate[bt_esc])
|
||
{
|
||
int lastoffs = StopMusic ();
|
||
ClearMemory ();
|
||
VW_FadeOut ();
|
||
|
||
US_ControlPanel (buttonstate[bt_esc] ? sc_Escape : scan);
|
||
|
||
SETFONTCOLOR (0, 15);
|
||
IN_ClearKeysDown ();
|
||
VW_FadeOut();
|
||
if(viewsize != 21)
|
||
DrawPlayScreen ();
|
||
if (!startgame && !loadedgame)
|
||
ContinueMusic (lastoffs);
|
||
if (loadedgame)
|
||
playstate = ex_abort;
|
||
lasttimecount = GetTimeCount();
|
||
if (MousePresent && IN_IsInputGrabbed())
|
||
IN_CenterMouse(); // Clear accumulated mouse movement
|
||
return;
|
||
}
|
||
|
||
//
|
||
// TAB-? debug keys
|
||
//
|
||
#ifdef DEBUGKEYS
|
||
if (Keyboard[sc_Tab] && DebugOk)
|
||
{
|
||
CA_CacheGrChunk (STARTFONT);
|
||
fontnumber = 0;
|
||
SETFONTCOLOR (0, 15);
|
||
if (DebugKeys () && viewsize < 20)
|
||
DrawPlayBorder (); // dont let the blue borders flash
|
||
|
||
if (MousePresent && IN_IsInputGrabbed())
|
||
IN_CenterMouse(); // Clear accumulated mouse movement
|
||
|
||
lasttimecount = GetTimeCount();
|
||
return;
|
||
}
|
||
#endif
|
||
}
|
||
|
||
|
||
//===========================================================================
|
||
|
||
/*
|
||
#############################################################################
|
||
|
||
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 = newobj;
|
||
|
||
}
|
||
|
||
//===========================================================================
|
||
|
||
/*
|
||
=========================
|
||
=
|
||
= 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!");
|
||
|
||
newobj = objfreelist;
|
||
objfreelist = newobj->prev;
|
||
memset (newobj, 0, sizeof (*newobj));
|
||
|
||
if (lastobj)
|
||
lastobj->next = newobj;
|
||
newobj->prev = lastobj; // new->next is allready NULL from memset
|
||
|
||
newobj->active = ac_no;
|
||
lastobj = newobj;
|
||
|
||
objcount++;
|
||
}
|
||
|
||
//===========================================================================
|
||
|
||
/*
|
||
=========================
|
||
=
|
||
= RemoveObj
|
||
=
|
||
= Add the given object back into the free list, and unlink it from it's
|
||
= neighbors
|
||
=
|
||
=========================
|
||
*/
|
||
|
||
void RemoveObj (objtype * gone)
|
||
{
|
||
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
|
||
=
|
||
=================
|
||
*/
|
||
int StopMusic (void)
|
||
{
|
||
int lastoffs = SD_MusicOff ();
|
||
|
||
UNCACHEAUDIOCHUNK (STARTMUSIC + lastmusicchunk);
|
||
|
||
return lastoffs;
|
||
}
|
||
|
||
//==========================================================================
|
||
|
||
|
||
/*
|
||
=================
|
||
=
|
||
= StartMusic
|
||
=
|
||
=================
|
||
*/
|
||
|
||
void StartMusic ()
|
||
{
|
||
SD_MusicOff ();
|
||
lastmusicchunk = (musicnames) songs[gamestate.mapon + gamestate.episode * 10];
|
||
SD_StartMusic(STARTMUSIC + lastmusicchunk);
|
||
}
|
||
|
||
void ContinueMusic (int offs)
|
||
{
|
||
SD_MusicOff ();
|
||
lastmusicchunk = (musicnames) songs[gamestate.mapon + gamestate.episode * 10];
|
||
SD_ContinueMusic(STARTMUSIC + lastmusicchunk, offs);
|
||
}
|
||
|
||
/*
|
||
=============================================================================
|
||
|
||
PALETTE SHIFTING STUFF
|
||
|
||
=============================================================================
|
||
*/
|
||
|
||
#define NUMREDSHIFTS 6
|
||
#define REDSTEPS 8
|
||
|
||
#define NUMWHITESHIFTS 3
|
||
#define WHITESTEPS 20
|
||
#define WHITETICS 6
|
||
|
||
|
||
SDL_Color redshifts[NUMREDSHIFTS][256];
|
||
SDL_Color whiteshifts[NUMWHITESHIFTS][256];
|
||
|
||
int damagecount, bonuscount;
|
||
boolean palshifted;
|
||
|
||
/*
|
||
=====================
|
||
=
|
||
= InitRedShifts
|
||
=
|
||
=====================
|
||
*/
|
||
|
||
void InitRedShifts (void)
|
||
{
|
||
SDL_Color *workptr, *baseptr;
|
||
int i, j, delta;
|
||
|
||
|
||
//
|
||
// fade through intermediate frames
|
||
//
|
||
for (i = 1; i <= NUMREDSHIFTS; i++)
|
||
{
|
||
workptr = redshifts[i - 1];
|
||
baseptr = gamepal;
|
||
|
||
for (j = 0; j <= 255; j++)
|
||
{
|
||
delta = 256 - baseptr->r;
|
||
workptr->r = baseptr->r + delta * i / REDSTEPS;
|
||
delta = -baseptr->g;
|
||
workptr->g = baseptr->g + delta * i / REDSTEPS;
|
||
delta = -baseptr->b;
|
||
workptr->b = baseptr->b + delta * i / REDSTEPS;
|
||
baseptr++;
|
||
workptr++;
|
||
}
|
||
}
|
||
|
||
for (i = 1; i <= NUMWHITESHIFTS; i++)
|
||
{
|
||
workptr = whiteshifts[i - 1];
|
||
baseptr = gamepal;
|
||
|
||
for (j = 0; j <= 255; j++)
|
||
{
|
||
delta = 256 - baseptr->r;
|
||
workptr->r = baseptr->r + delta * i / WHITESTEPS;
|
||
delta = 248 - baseptr->g;
|
||
workptr->g = baseptr->g + delta * i / WHITESTEPS;
|
||
delta = 0-baseptr->b;
|
||
workptr->b = baseptr->b + delta * i / WHITESTEPS;
|
||
baseptr++;
|
||
workptr++;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/*
|
||
=====================
|
||
=
|
||
= ClearPaletteShifts
|
||
=
|
||
=====================
|
||
*/
|
||
|
||
void ClearPaletteShifts (void)
|
||
{
|
||
bonuscount = damagecount = 0;
|
||
palshifted = false;
|
||
}
|
||
|
||
|
||
/*
|
||
=====================
|
||
=
|
||
= 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)
|
||
{
|
||
VL_SetPalette (redshifts[red - 1], false);
|
||
palshifted = true;
|
||
}
|
||
else if (white)
|
||
{
|
||
VL_SetPalette (whiteshifts[white - 1], false);
|
||
palshifted = true;
|
||
}
|
||
else if (palshifted)
|
||
{
|
||
VL_SetPalette (gamepal, false); // back to normal
|
||
palshifted = false;
|
||
}
|
||
}
|
||
|
||
|
||
/*
|
||
=====================
|
||
=
|
||
= FinishPaletteShifts
|
||
=
|
||
= Resets palette to normal if needed
|
||
=
|
||
=====================
|
||
*/
|
||
|
||
void FinishPaletteShifts (void)
|
||
{
|
||
if (palshifted)
|
||
{
|
||
palshifted = 0;
|
||
VL_SetPalette (gamepal, true);
|
||
}
|
||
}
|
||
|
||
|
||
/*
|
||
=============================================================================
|
||
|
||
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 = (void (*)(objtype *)) 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 -= (short) tics;
|
||
while (ob->ticcount <= 0)
|
||
{
|
||
think = (void (*)(objtype *)) 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 = (void (*)(objtype *)) 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
|
||
=
|
||
===================
|
||
*/
|
||
int32_t funnyticount;
|
||
|
||
|
||
void PlayLoop (void)
|
||
{
|
||
#if defined(USE_FEATUREFLAGS) && defined(USE_CLOUDSKY)
|
||
if(GetFeatureFlags() & FF_CLOUDSKY)
|
||
InitSky();
|
||
#endif
|
||
|
||
#ifdef USE_SHADING
|
||
InitLevelShadeTable();
|
||
#endif
|
||
|
||
playstate = ex_stillplaying;
|
||
lasttimecount = GetTimeCount();
|
||
frameon = 0;
|
||
anglefrac = 0;
|
||
facecount = 0;
|
||
funnyticount = 0;
|
||
memset (buttonstate, 0, sizeof (buttonstate));
|
||
ClearPaletteShifts ();
|
||
|
||
if (MousePresent && IN_IsInputGrabbed())
|
||
IN_CenterMouse(); // Clear accumulated mouse movement
|
||
|
||
if (demoplayback)
|
||
IN_StartAck ();
|
||
|
||
do
|
||
{
|
||
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;
|
||
if(viewsize != 21)
|
||
StatusDrawFace(BJWAITING1PIC + (US_RndT () & 1));
|
||
facecount = 0;
|
||
}
|
||
#endif
|
||
|
||
gamestate.TimeCount += tics;
|
||
|
||
UpdateSoundLoc (); // JAB
|
||
if (screenfaded)
|
||
VW_FadeIn ();
|
||
|
||
CheckKeys ();
|
||
|
||
//
|
||
// debug aids
|
||
//
|
||
if (singlestep)
|
||
{
|
||
VW_WaitVBL (singlestep);
|
||
lasttimecount = GetTimeCount();
|
||
}
|
||
if (extravbls)
|
||
VW_WaitVBL (extravbls);
|
||
|
||
if (demoplayback)
|
||
{
|
||
if (IN_CheckAck ())
|
||
{
|
||
IN_ClearKeysDown ();
|
||
playstate = ex_abort;
|
||
}
|
||
}
|
||
}
|
||
while (!playstate && !startgame);
|
||
|
||
if (playstate != ex_died)
|
||
FinishPaletteShifts ();
|
||
}
|