--- /dev/null
+/* Catacomb 3-D Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// C3_PLAY.C\r
+\r
+#include "C3_DEF.H"\r
+#pragma hdrstop\r
+\r
+/*\r
+=============================================================================\r
+\r
+ LOCAL CONSTANTS\r
+\r
+=============================================================================\r
+*/\r
+\r
+#define POINTTICS 6\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+ GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+ControlInfo c;\r
+boolean running,slowturn;\r
+\r
+int bordertime;\r
+objtype objlist[MAXACTORS],*new,*obj,*player,*lastobj,*objfreelist;\r
+\r
+unsigned farmapylookup[MAPSIZE];\r
+byte *nearmapylookup[MAPSIZE];\r
+\r
+boolean singlestep,godmode;\r
+int extravbls;\r
+\r
+//\r
+// replacing refresh manager\r
+//\r
+unsigned mapwidth,mapheight,tics;\r
+boolean compatability;\r
+byte *updateptr;\r
+unsigned mapwidthtable[64];\r
+unsigned uwidthtable[UPDATEHIGH];\r
+unsigned blockstarts[UPDATEWIDE*UPDATEHIGH];\r
+#define UPDATESCREENSIZE (UPDATEWIDE*PORTTILESHIGH+2)\r
+#define UPDATESPARESIZE (UPDATEWIDE*2+4)\r
+#define UPDATESIZE (UPDATESCREENSIZE+2*UPDATESPARESIZE)\r
+byte update[UPDATESIZE];\r
+\r
+int mousexmove,mouseymove;\r
+int pointcount,pointsleft;\r
+\r
+/*\r
+=============================================================================\r
+\r
+ LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+void CalcBounds (objtype *ob);\r
+void DrawPlayScreen (void);\r
+\r
+\r
+//\r
+// near data map array (wall values only, get text number from far data)\r
+//\r
+byte tilemap[MAPSIZE][MAPSIZE];\r
+byte spotvis[MAPSIZE][MAPSIZE];\r
+objtype *actorat[MAPSIZE][MAPSIZE];\r
+\r
+objtype dummyobj;\r
+\r
+int bordertime;\r
+int objectcount;\r
+\r
+void StopMusic(void);\r
+void StartMusic(void);\r
+\r
+\r
+//==========================================================================\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// CenterWindow() - Generates a window of a given width & height in the\r
+// middle of the screen\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+\r
+#define MAXX 264\r
+#define MAXY 146\r
+\r
+void CenterWindow(word w,word h)\r
+{\r
+ US_DrawWindow(((MAXX / 8) - w) / 2,((MAXY / 8) - h) / 2,w,h);\r
+}\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= CheckKeys\r
+=\r
+=====================\r
+*/\r
+\r
+void CheckKeys (void)\r
+{\r
+ if (screenfaded) // don't do anything with a faded screen\r
+ return;\r
+\r
+//\r
+// pause key wierdness can't be checked as a scan code\r
+//\r
+ if (Paused)\r
+ {\r
+ CenterWindow (8,3);\r
+ US_PrintCentered ("PAUSED");\r
+ VW_UpdateScreen ();\r
+ SD_MusicOff();\r
+ IN_Ack();\r
+ SD_MusicOn();\r
+ Paused = false;\r
+ if (MousePresent) Mouse(MDelta); // Clear accumulated mouse movement\r
+ }\r
+\r
+//\r
+// F1-F7/ESC to enter control panel\r
+//\r
+ if ( (LastScan >= sc_F1 && LastScan <= sc_F7) || LastScan == sc_Escape)\r
+ {\r
+ StopMusic ();\r
+ NormalScreen ();\r
+ FreeUpMemory ();\r
+ US_CenterWindow (20,8);\r
+ US_CPrint ("Loading");\r
+ VW_UpdateScreen ();\r
+ US_ControlPanel();\r
+ if (abortgame)\r
+ {\r
+ playstate = ex_abort;\r
+ return;\r
+ }\r
+ StartMusic ();\r
+ IN_ClearKeysDown();\r
+ if (restartgame)\r
+ playstate = ex_resetgame;\r
+ if (loadedgame)\r
+ playstate = ex_loadedgame;\r
+ DrawPlayScreen ();\r
+ CacheScaleds ();\r
+ lasttimecount = TimeCount;\r
+ if (MousePresent) Mouse(MDelta); // Clear accumulated mouse movement\r
+ }\r
+\r
+//\r
+// F10-? debug keys\r
+//\r
+ if (Keyboard[sc_F10])\r
+ {\r
+ DebugKeys();\r
+ if (MousePresent) Mouse(MDelta); // Clear accumulated mouse movement\r
+ lasttimecount = TimeCount;\r
+ }\r
+\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+#############################################################################\r
+\r
+ The objlist data structure\r
+\r
+#############################################################################\r
+\r
+objlist containt structures for every actor currently playing. The structure\r
+is accessed as a linked list starting at *player, ending when ob->next ==\r
+NULL. GetNewObj inserts a new object at the end of the list, meaning that\r
+if an actor spawn another actor, the new one WILL get to think and react the\r
+same frame. RemoveObj unlinks the given object and returns it to the free\r
+list, but does not damage the objects ->next pointer, so if the current object\r
+removes itself, a linked list following loop can still safely get to the\r
+next element.\r
+\r
+<backwardly linked free list>\r
+\r
+#############################################################################\r
+*/\r
+\r
+\r
+/*\r
+=========================\r
+=\r
+= InitObjList\r
+=\r
+= Call to clear out the entire object list, returning them all to the free\r
+= list. Allocates a special spot for the player.\r
+=\r
+=========================\r
+*/\r
+\r
+void InitObjList (void)\r
+{\r
+ int i;\r
+\r
+ for (i=0;i<MAXACTORS;i++)\r
+ {\r
+ objlist[i].prev = &objlist[i+1];\r
+ objlist[i].next = NULL;\r
+ }\r
+\r
+ objlist[MAXACTORS-1].prev = NULL;\r
+\r
+ objfreelist = &objlist[0];\r
+ lastobj = NULL;\r
+\r
+ objectcount = 0;\r
+\r
+//\r
+// give the player and score the first free spots\r
+//\r
+ GetNewObj (false);\r
+ player = new;\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=========================\r
+=\r
+= GetNewObj\r
+=\r
+= Sets the global variable new to point to a free spot in objlist.\r
+= The free spot is inserted at the end of the liked list\r
+=\r
+= When the object list is full, the caller can either have it bomb out ot\r
+= return a dummy object pointer that will never get used\r
+=\r
+=========================\r
+*/\r
+\r
+void GetNewObj (boolean usedummy)\r
+{\r
+ if (!objfreelist)\r
+ {\r
+ if (usedummy)\r
+ {\r
+ new = &dummyobj;\r
+ return;\r
+ }\r
+ Quit ("GetNewObj: No free spots in objlist!");\r
+ }\r
+\r
+ new = objfreelist;\r
+ objfreelist = new->prev;\r
+ memset (new,0,sizeof(*new));\r
+\r
+ if (lastobj)\r
+ lastobj->next = new;\r
+ new->prev = lastobj; // new->next is allready NULL from memset\r
+\r
+ new->active = false;\r
+ lastobj = new;\r
+\r
+ objectcount++;\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=========================\r
+=\r
+= RemoveObj\r
+=\r
+= Add the given object back into the free list, and unlink it from it's\r
+= neighbors\r
+=\r
+=========================\r
+*/\r
+\r
+void RemoveObj (objtype *gone)\r
+{\r
+ objtype **spotat;\r
+\r
+ if (gone == player)\r
+ Quit ("RemoveObj: Tried to remove the player!");\r
+\r
+//\r
+// fix the next object's back link\r
+//\r
+ if (gone == lastobj)\r
+ lastobj = (objtype *)gone->prev;\r
+ else\r
+ gone->next->prev = gone->prev;\r
+\r
+//\r
+// fix the previous object's forward link\r
+//\r
+ gone->prev->next = gone->next;\r
+\r
+//\r
+// add it back in to the free list\r
+//\r
+ gone->prev = objfreelist;\r
+ objfreelist = gone;\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+===================\r
+=\r
+= PollControls\r
+=\r
+===================\r
+*/\r
+\r
+void PollControls (void)\r
+{\r
+ unsigned buttons;\r
+\r
+ IN_ReadControl(0,&c);\r
+\r
+ if (MousePresent)\r
+ {\r
+ Mouse(MButtons);\r
+ buttons = _BX;\r
+ Mouse(MDelta);\r
+ mousexmove = _CX;\r
+ mouseymove = _DX;\r
+\r
+ if (buttons&1)\r
+ c.button0 = 1;\r
+ if (buttons&2)\r
+ c.button1 = 1;\r
+\r
+ }\r
+\r
+ if (Controls[0]==ctrl_Joystick)\r
+ {\r
+ if (c.x>120 || c.x <-120 || c.y>120 || c.y<-120)\r
+ running = true;\r
+ else\r
+ running = false;\r
+ if (c.x>-48 && c.x<48)\r
+ slowturn = true;\r
+ else\r
+ slowturn = false;\r
+ }\r
+ else\r
+ {\r
+ if (Keyboard[sc_RShift])\r
+ running = true;\r
+ else\r
+ running = false;\r
+ if (c.button0)\r
+ slowturn = true;\r
+ else\r
+ slowturn = false;\r
+ }\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+=================\r
+=\r
+= StopMusic\r
+=\r
+=================\r
+*/\r
+\r
+void StopMusic(void)\r
+{\r
+ int i;\r
+\r
+ SD_MusicOff();\r
+ for (i = 0;i < LASTMUSIC;i++)\r
+ if (audiosegs[STARTMUSIC + i])\r
+ {\r
+ MM_SetPurge(&((memptr)audiosegs[STARTMUSIC + i]),3);\r
+ MM_SetLock(&((memptr)audiosegs[STARTMUSIC + i]),false);\r
+ }\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+=================\r
+=\r
+= StartMusic\r
+=\r
+=================\r
+*/\r
+\r
+// JAB - Cache & start the appropriate music for this level\r
+void StartMusic(void)\r
+{\r
+ musicnames chunk;\r
+\r
+ SD_MusicOff();\r
+ chunk = TOOHOT_MUS;\r
+// if ((chunk == -1) || (MusicMode != smm_AdLib))\r
+//DEBUG control panel return;\r
+\r
+ MM_BombOnError (false);\r
+ CA_CacheAudioChunk(STARTMUSIC + chunk);\r
+ MM_BombOnError (true);\r
+ if (mmerror)\r
+ mmerror = false;\r
+ else\r
+ {\r
+ MM_SetLock(&((memptr)audiosegs[STARTMUSIC + chunk]),true);\r
+ SD_StartMusic((MusicGroup far *)audiosegs[STARTMUSIC + chunk]);\r
+ }\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+===================\r
+=\r
+= PlayLoop\r
+=\r
+===================\r
+*/\r
+\r
+void PlayLoop (void)\r
+{\r
+ int give;\r
+\r
+ void (*think)();\r
+\r
+ ingame = true;\r
+ playstate = TimeCount = 0;\r
+ gamestate.shotpower = handheight = 0;\r
+ pointcount = pointsleft = 0;\r
+\r
+ DrawLevelNumber (gamestate.mapon);\r
+ DrawBars ();\r
+\r
+#ifndef PROFILE\r
+ fizzlein = true; // fizzle fade in the first refresh\r
+#endif\r
+ TimeCount = lasttimecount = lastnuke = 0;\r
+\r
+ PollControls (); // center mouse\r
+ StartMusic ();\r
+ do\r
+ {\r
+#ifndef PROFILE\r
+ PollControls();\r
+#else\r
+ c.xaxis = 1;\r
+ if (++TimeCount == 300)\r
+ return;\r
+#endif\r
+\r
+ for (obj = player;obj;obj = obj->next)\r
+ if (obj->active)\r
+ {\r
+ if (obj->ticcount)\r
+ {\r
+ obj->ticcount-=tics;\r
+ while ( obj->ticcount <= 0)\r
+ {\r
+ think = obj->state->think;\r
+ if (think)\r
+ {\r
+ think (obj);\r
+ if (!obj->state)\r
+ {\r
+ RemoveObj (obj);\r
+ goto nextactor;\r
+ }\r
+ }\r
+\r
+ obj->state = obj->state->next;\r
+ if (!obj->state)\r
+ {\r
+ RemoveObj (obj);\r
+ goto nextactor;\r
+ }\r
+ if (!obj->state->tictime)\r
+ {\r
+ obj->ticcount = 0;\r
+ goto nextactor;\r
+ }\r
+ if (obj->state->tictime>0)\r
+ obj->ticcount += obj->state->tictime;\r
+ }\r
+ }\r
+ think = obj->state->think;\r
+ if (think)\r
+ {\r
+ think (obj);\r
+ if (!obj->state)\r
+ RemoveObj (obj);\r
+ }\r
+nextactor:;\r
+ }\r
+\r
+\r
+ if (bordertime)\r
+ {\r
+ bordertime -= tics;\r
+ if (bordertime<=0)\r
+ {\r
+ bordertime = 0;\r
+ VW_ColorBorder (3);\r
+ }\r
+ }\r
+\r
+ if (pointcount)\r
+ {\r
+ pointcount -= tics;\r
+ if (pointcount <= 0)\r
+ {\r
+ pointcount += POINTTICS;\r
+ give = (pointsleft > 1000)? 1000 :\r
+ (\r
+ (pointsleft > 100)? 100 :\r
+ ((pointsleft < 20)? pointsleft : 20)\r
+ );\r
+ SD_PlaySound (GETPOINTSSND);\r
+ AddPoints (give);\r
+ pointsleft -= give;\r
+ if (!pointsleft)\r
+ pointcount = 0;\r
+ }\r
+ }\r
+\r
+ ThreeDRefresh ();\r
+\r
+ CheckKeys();\r
+ if (singlestep)\r
+ {\r
+ VW_WaitVBL(14);\r
+ lasttimecount = TimeCount;\r
+ }\r
+ if (extravbls)\r
+ VW_WaitVBL(extravbls);\r
+\r
+ }while (!playstate);\r
+ StopMusic ();\r
+\r
+ ingame = false;\r
+ if (bordertime)\r
+ {\r
+ bordertime = 0;\r
+ VW_ColorBorder (3);\r
+ }\r
+\r
+ if (!abortgame)\r
+ AddPoints (pointsleft);\r
+ else\r
+ abortgame = false;\r
+}\r
+\r