ref: 7ab940ff1683c49a3dfb54aa0b3f8683052c8ea4
dir: /play.c/
// WL_PLAY.C #include "WL_DEF.H" #pragma hdrstop /* ============================================================================= LOCAL CONSTANTS ============================================================================= */ #define sc_Question 0x35 /* ============================================================================= GLOBAL VARIABLES ============================================================================= */ int madenoise; // true when shooting or screaming exit_t playstate; s16int DebugOk; objtype objlist[MAXACTORS],*new,*obj,*player,*lastobj, *objfreelist,*killerobj; u16int farmapylookup[MAPSIZE]; u8int *nearmapylookup[MAPSIZE]; int singlestep,godmode,noclip; s16int extravbls; u8int tilemap[MAPSIZE][MAPSIZE]; // wall values only u8int spotvis[MAPSIZE][MAPSIZE]; objtype *actorat[MAPSIZE][MAPSIZE]; // // replacing refresh manager // u16int mapwidth,mapheight,tics; int compatability; u8int *updateptr; u16int mapwidthtable[64]; u16int uwidthtable[UPDATEHIGH]; u16int blockstarts[UPDATEWIDE*UPDATEHIGH]; u8int update[UPDATESIZE]; // // control info // int mouseenabled,joystickenabled,joypadenabled,joystickprogressive; s16int joystickport; s16int dirscan[4] = {sc_UpArrow,sc_RightArrow,sc_DownArrow,sc_LeftArrow}; s16int buttonscan[NUMBUTTONS] = {sc_Control,sc_Alt,sc_RShift,sc_Space,sc_1,sc_2,sc_3,sc_4}; s16int buttonmouse[4]={bt_attack,bt_strafe,bt_use,bt_nobutton}; s16int buttonjoy[4]={bt_attack,bt_strafe,bt_use,bt_run}; s16int viewsize; int buttonheld[NUMBUTTONS]; int demorecord,demoplayback; char far *demoptr, far *lastdemoptr; uchar *demobuffer; // // curent user input // s16int controlx,controly; // range from -100 to 100 per tic int buttonstate[NUMBUTTONS]; //=========================================================================== void CenterWindow(u16int w,u16int 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 // s16int songs[]= { #ifndef SPEAR // // Episode One // 3, 11, 9, 12, 3, 11, 9, 12, 2, // Boss level 0, // Secret level // // Episode Two // 8, 18, 17, 4, 8, 18, 4, 17, 2, // Boss level 1, // Secret level // // Episode Three // 6, 20, 22, 21, 6, 20, 22, 21, 19, // Boss level 26, // Secret level // // Episode Four // 3, 11, 9, 12, 3, 11, 9, 12, 2, // Boss level 0, // Secret level // // Episode Five // 8, 18, 17, 4, 8, 18, 4, 17, 2, // Boss level 1, // Secret level // // Episode Six // 6, 20, 22, 21, 6, 20, 22, 21, 19, // Boss level 15 // Secret level #else ////////////////////////////////////////////////////////////// // // SPEAR OF DESTINY TRACKS // ////////////////////////////////////////////////////////////// 4, 0, 2, 22, // DON'T KNOW 15, // Trans Gr�sse 1, HITLWLTZ_MUS, 9, 10, 15, // Barnacle Wilhelm BOSS 8, 3, 12, 11, 13, 15, // Super Mutant BOSS 21, 15, // Death Knight BOSS 18, // Secret level 0, // Secret level (DON'T KNOW) 17 // 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) { s16int i; for (i=0;i<NUMBUTTONS;i++) if (Keyboard[buttonscan[i]]) buttonstate[i] = true; } /* =================== = = PollMouseButtons = =================== */ void PollMouseButtons (void) { s16int buttons; buttons = IN_MouseButtons (); if (buttons&1) buttonstate[buttonmouse[0]] = true; if (buttons&2) buttonstate[buttonmouse[1]] = true; if (buttons&4) buttonstate[buttonmouse[2]] = true; } /* =================== = = PollKeyboardMove = =================== */ void PollKeyboardMove (void) { if (buttonstate[bt_run]) { if (Keyboard[dirscan[di_north]]) controly -= RUNMOVE*tics; if (Keyboard[dirscan[di_south]]) controly += RUNMOVE*tics; if (Keyboard[dirscan[di_west]]) controlx -= RUNMOVE*tics; if (Keyboard[dirscan[di_east]]) controlx += RUNMOVE*tics; } else { if (Keyboard[dirscan[di_north]]) controly -= BASEMOVE*tics; if (Keyboard[dirscan[di_south]]) controly += BASEMOVE*tics; if (Keyboard[dirscan[di_west]]) controlx -= BASEMOVE*tics; if (Keyboard[dirscan[di_east]]) controlx += BASEMOVE*tics; } } /* =================== = = PollMouseMove = =================== */ void PollMouseMove (void) { s16int mousexmove,mouseymove; Mouse(MDelta); mousexmove = _CX; mouseymove = _DX; controlx += mousexmove*10/(13-mouseadjustment); controly += mouseymove*20/(13-mouseadjustment); } /* =================== = = 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) { s16int max,min,i; u8int buttonbits; // // get timing info for last frame // if (demoplayback) { while (TimeCount<lasttimecount+DEMOTICS) ; TimeCount = lasttimecount + DEMOTICS; lasttimecount += DEMOTICS; tics = DEMOTICS; } else if (demorecord) // demo recording and playback needs { // to be constant // // take DEMOTICS or more tics, and modify Timecount to reflect time taken // while (TimeCount<lasttimecount+DEMOTICS) ; TimeCount = lasttimecount + DEMOTICS; lasttimecount += DEMOTICS; 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 *= (s16int)tics; controly *= (s16int)tics; return; } // // get button states // PollKeyboardButtons (); if (mouseenabled) PollMouseButtons (); // // get movements // PollKeyboardMove (); if (mouseenabled) PollMouseMove (); // // 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 /= (s16int)tics; controly /= (s16int)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 *= (s16int)tics; controly *= (s16int)tics; } } //========================================================================== /////////////////////////////////////////////////////////////////////////// // // CenterWindow() - Generates a window of a given width & height in the // middle of the screen // /////////////////////////////////////////////////////////////////////////// #define MAXX 320 #define MAXY 160 void CenterWindow(u16int w,u16int h) { FixOfs (); US_DrawWindow(((MAXX / 8) - w) / 2,((MAXY / 8) - h) / 2,w,h); } //=========================================================================== /* ===================== = = CheckKeys = ===================== */ void CheckKeys (void) { s16int i; u8int scan; u16int 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 (Snobonus); } else { Message ("God mode ON"); SD_PlaySound (Sendb2); } 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 (); ClearSplitVWB (); VW_ScreenToScreen (displayofs,bufferofs,80,160); Message("You now have 100% Health,\n" "99 Ammo and both Keys!\n\n" "Note that you have basically\n" "eliminated your chances of\n" "getting a high score!"); PM_CheckMainMem (); IN_ClearKeysDown(); IN_Ack(); DrawAllPlayBorder (); } // // OPEN UP DEBUG KEYS // if (Keyboard[sc_BackSpace] && Keyboard[sc_LShift] && Keyboard[sc_Alt] && debug) { ClearMemory (); ClearSplitVWB (); VW_ScreenToScreen (displayofs,bufferofs,80,160); Message("Debugging keys are\nnow available!"); 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 (); 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?!"); 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,Ppause); 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 ( scan == sc_F10 || 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. <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. = ========================= */ s16int objcount; void InitActorList (void) { s16int 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) { s16int i; SD_MusicOff(); for (i = 0;i < LASTMUSIC;i++) if (audiosegs[STARTMUSIC + i]) { MM_SetPurge(&((uchar *)audiosegs[STARTMUSIC + i]),3); MM_SetLock(&((uchar *)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(&((uchar *)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 u8int far redshifts[NUMREDSHIFTS][768]; u8int far whiteshifts[NUMREDSHIFTS][768]; s16int damagecount,bonuscount; int palshifted; extern u8int far gamepal; /* ===================== = = InitRedShifts = ===================== */ void InitRedShifts (void) { u8int far *workptr, far *baseptr; s16int i,j,delta; // // fade through intermediate frames // for (i=1;i<=NUMREDSHIFTS;i++) { workptr = (u8int 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 = (u8int 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 (s16int damage) { damagecount += damage; } /* ===================== = = UpdatePaletteShifts = ===================== */ void UpdatePaletteShifts (void) { s16int 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 = =================== */ s32int funnyticount; void PlayLoop (void) { s16int give; s16int 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 { 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,Pwait+(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; } } }while (!playstate && !startgame); if (playstate != ex_died) FinishPaletteShifts (); }