#include "wl_def.h" /* ============================================================================= 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]; boolean singlestep,godmode,noclip; byte tilemap[MAPSIZE][MAPSIZE]; // wall values only byte spotvis[MAPSIZE][MAPSIZE]; int actorat[MAPSIZE][MAPSIZE]; int tics; // // control info // boolean mouseenabled,joystickenabled,joypadenabled; 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}; boolean buttonheld[NUMBUTTONS]; boolean demorecord,demoplayback; byte *demoptr, *lastdemoptr; memptr demobuffer; // // curent user input // int controlx,controly; /* range from -100 to 100 per tic */ boolean buttonstate[NUMBUTTONS]; static void RemoveObj(objtype *gone); /* ============================================================================= LOCAL VARIABLES ============================================================================= */ /* LIST OF SONGS FOR EACH VERSION */ static const 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, 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 */ XEVIL_MUS /* Angel of Death BOSS */ #endif }; /* ============================================================================= USER CONTROL ============================================================================= */ #define BASEMOVE 35 #define RUNMOVE 70 /* =================== = = PollKeyboardButtons = =================== */ void PollKeyboardButtons() { int i; for (i=0;i 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; } } void UpdateInput() { // // get button states // PollKeyboardButtons(); if (mouseenabled) PollMouseButtons(); if (joystickenabled) PollJoystickButtons(); // // get movements // PollKeyboardMove(); if (mouseenabled) PollMouseMove(); if (joystickenabled) PollJoystickMove(); } /* =================== = = 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() { int max, min, i; byte buttonbits; 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 = (signed char)*demoptr++; controly = (signed char)*demoptr++; if (demoptr == lastdemoptr) playstate = ex_completed; // demo is done controlx *= (int)tics; controly *= (int)tics; return; } /* Update keys */ IN_CheckAck(); UpdateInput(); // // 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) { US_DrawWindow(((MAXX / 8) - w) / 2,((MAXY / 8) - h) / 2,w,h); } //=========================================================================== /* ===================== = = CheckKeys = ===================== */ void CheckKeys() { byte scan; if (screenfaded || demoplayback) // don't do anything with a faded screen return; scan = LastScan; // // SECRET CHEAT CODE: TAB-G-F10 // if (IN_KeyDown(sc_Tab) && IN_KeyDown(sc_G) && IN_KeyDown(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; } // // SECRET CHEAT CODE: 'MLI' // if (IN_KeyDown(sc_M) && IN_KeyDown(sc_L) && IN_KeyDown(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_CHEATER); CA_UnCacheGrChunk(STARTFONT+1); IN_ClearKeysDown(); IN_Ack(); DrawPlayBorder(); } // // OPEN UP DEBUG KEYS // if (IN_KeyDown(sc_BackSpace) && IN_KeyDown(sc_LShift) && IN_KeyDown(sc_Alt) && MS_CheckParm("debugmode")) { ClearMemory(); CA_CacheGrChunk(STARTFONT+1); ClearSplitVWB(); Message(STR_DEBUG); CA_UnCacheGrChunk(STARTFONT+1); IN_ClearKeysDown(); IN_Ack(); DrawPlayBorderSides(); DebugOk=1; } // // TRYING THE KEEN CHEAT CODE! // if (IN_KeyDown(sc_B) && IN_KeyDown(sc_A) && IN_KeyDown(sc_T)) { ClearMemory(); CA_CacheGrChunk(STARTFONT+1); ClearSplitVWB(); Message(STR_KEEN); CA_UnCacheGrChunk(STARTFONT+1); IN_ClearKeysDown(); IN_Ack(); DrawPlayBorder(); } if (Paused) { VWB_DrawPic(128, 64, PAUSEDPIC); VW_UpdateScreen(); SD_MusicOff(); IN_Ack(); IN_ClearKeysDown(); SD_MusicOn(); Paused = false; return; } // // F1-F7/ESC to enter control panel // if ( scan == sc_F10 || scan == sc_F9 || scan == sc_F7 || scan == sc_F8) // pop up quit dialog { ClearMemory(); ClearSplitVWB(); US_ControlPanel(scan); DrawPlayBorderSides(); VW_UpdateScreen(); if (scan == sc_F9) StartMusic(); SETFONTCOLOR(0,15); IN_ClearKeysDown(); lasttimecount = get_TimeCount(); return; } if ( (scan >= sc_F1 && scan <= sc_F9) || scan == sc_Escape) { StopMusic(); ClearMemory(); VW_FadeOut(); US_ControlPanel(scan); SETFONTCOLOR(0,15); IN_ClearKeysDown(); DrawPlayScreen(); VW_UpdateScreen(); if (!startgame && !loadedgame) { VW_FadeIn(); StartMusic(); } if (loadedgame) playstate = ex_abort; lasttimecount = get_TimeCount(); return; } // // TAB-? debug keys // if (IN_KeyDown(sc_Tab) && DebugOk) { CA_CacheGrChunk (STARTFONT); fontnumber=0; SETFONTCOLOR(0,15); DebugKeys(); lasttimecount = get_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. = ========================= */ void InitActorList() { int i; // // init the actor lists // for (i = 0; i < MAXACTORS; i++) { objlist[i].id = i; objlist[i].prev = &objlist[i+1]; objlist[i].next = NULL; } objlist[MAXACTORS-1].prev = NULL; objfreelist = &objlist[0]; lastobj = NULL; /* 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 = ========================= */ void GetNewActor() { int id; if (!objfreelist) Quit("GetNewActor: No free spots in objlist!"); new = objfreelist; id = new->id; objfreelist = new->prev; memset(new, 0, sizeof(*new)); new->id = id; if (lastobj) lastobj->next = new; new->prev = lastobj; // new->next is already NULL from memset new->active = false; lastobj = new; } //=========================================================================== /* ========================= = = RemoveObj = = Add the given object back into the free list, and unlink it from its = neighbors = ========================= */ static void RemoveObj(objtype *gone) { if (gone == player) Quit("RemoveObj: Tried to remove the player!"); gone->state = s_none; // // fix the next object's back link // if (gone == lastobj) lastobj = 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; } /* ============================================================================= MUSIC STUFF ============================================================================= */ /* ================= = = StopMusic = ================= */ void StopMusic() { FreeMusic(); } /* ================= = = StartMusic = ================= */ void StartMusic() { StartCPMusic(songs[gamestate.mapon+gamestate.episode*10]); } /* ============================================================================= PALETTE SHIFTING STUFF ============================================================================= */ #define NUMREDSHIFTS 6 #define REDSTEPS 8 #define NUMWHITESHIFTS 3 #define WHITESTEPS 20 #define WHITETICS 6 byte redshifts[NUMREDSHIFTS][768]; byte whiteshifts[NUMREDSHIFTS][768]; int damagecount,bonuscount; boolean palshifted; /* ===================== = = InitRedShifts = ===================== */ void InitRedShifts() { byte *workptr; const byte *baseptr; int i,j,delta; // // fade through intermediate frames // for (i=1;i<=NUMREDSHIFTS;i++) { workptr = (byte *)&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 *)&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() { bonuscount = damagecount = 0; } /* ===================== = = StartBonusFlash = ===================== */ void StartBonusFlash() { bonuscount = NUMWHITESHIFTS*WHITETICS; // white shift palette } /* ===================== = = StartDamageFlash = ===================== */ void StartDamageFlash(int damage) { damagecount += damage; } /* ===================== = = UpdatePaletteShifts = ===================== */ void UpdatePaletteShifts() { 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]); palshifted = true; } else if (white) { VL_SetPalette(whiteshifts[white-1]); palshifted = true; } else if (palshifted) { VL_SetPalette(gamepal); // back to normal palshifted = false; } } /* ===================== = = FinishPaletteShifts = = Resets palette to normal if needed = ===================== */ void FinishPaletteShifts() { if (palshifted) { palshifted = 0; 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] = 0; // // non transitional object // if (!ob->ticcount) { think = gamestates[ob->state].think; if (think) { think(ob); if (ob->state == s_none) { 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->id | 0x8000; return; } // // transitional object // ob->ticcount-=tics; while (ob->ticcount <= 0) { think = gamestates[ob->state].action; // end of state action if (think) { think(ob); if (ob->state == s_none) { RemoveObj(ob); return; } } ob->state = gamestates[ob->state].next; if (ob->state == s_none) { RemoveObj(ob); return; } if (!gamestates[ob->state].tictime) { ob->ticcount = 0; goto think; } ob->ticcount += gamestates[ob->state].tictime; } think: // // think // think = gamestates[ob->state].think; if (think) { think(ob); if (ob->state == s_none) { 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->id | 0x8000; } //========================================================================== /* =================== = = PlayLoop = =================== */ long funnyticount; void PlayLoop() { playstate = lasttimecount = 0; frameon = 0; anglefrac = 0; facecount = 0; funnyticount = 0; memset (buttonstate,0,sizeof(buttonstate)); ClearPaletteShifts(); if (demoplayback) IN_StartAck(); set_TimeCount(0); do { /* get timing info for last frame */ CalcTics(); /* handle input */ 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; UpdateSoundLoc(player->x, player->y, player->angle); if (screenfaded) VW_FadeIn(); CheckKeys(); // // debug aids // if (singlestep) { VW_WaitVBL(14); lasttimecount = get_TimeCount(); } if (demoplayback) { if (IN_CheckAck()) { IN_ClearKeysDown(); playstate = ex_abort; } } } while (!playstate && !startgame); if (playstate != ex_died) FinishPaletteShifts(); }