--- /dev/null
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is loosely based on:\r
+ * Keen Dreams Source Code\r
+ * Copyright (C) 2014 Javier M. Chavez\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
+#include "CK_DEF.H"\r
+\r
+/*\r
+=============================================================================\r
+\r
+ GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+Uint16 fadecount;\r
+Sint16 levelcompleted;\r
+Sint32 chunkcount, chunkmax, handpic;\r
+\r
+/*\r
+=============================================================================\r
+\r
+ LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+void FadeAndUnhook(void);\r
+\r
+//===========================================================================\r
+\r
+/*\r
+============================\r
+=\r
+= FreeGraphics\r
+=\r
+============================\r
+*/\r
+\r
+void FreeGraphics(void)\r
+{\r
+ Sint16 i;\r
+ for (i=STARTSPRITES; i<STARTSPRITES+NUMSPRITES; i++)\r
+ {\r
+ if (grsegs[i])\r
+ {\r
+ MM_SetPurge(&grsegs[i], PURGE_LAST);\r
+ }\r
+ }\r
+ for (i=STARTTILE16; i<STARTEXTERNS; i++)\r
+ {\r
+ if (grsegs[i])\r
+ {\r
+ MM_SetPurge(&grsegs[i], PURGE_LAST);\r
+ }\r
+ }\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= NewGame\r
+=\r
+= Set up new game to start from the beginning\r
+=\r
+=====================\r
+*/\r
+\r
+void NewGame(void)\r
+{\r
+ memset(&gamestate, 0, sizeof(gamestate));\r
+ gamestate.nextextra = 20000;\r
+ gamestate.lives = 3;\r
+ gamestate.ammo = 5;\r
+}\r
+\r
+//===========================================================================\r
+\r
+#ifndef KEEN5\r
+/*\r
+============================\r
+=\r
+= GameOver\r
+=\r
+============================\r
+*/\r
+\r
+void GameOver(void)\r
+{\r
+ VW_FixRefreshBuffer();\r
+ US_CenterWindow(16, 3);\r
+ US_PrintCentered("Game Over!");\r
+ VW_UpdateScreen();\r
+ IN_ClearKeysDown();\r
+ IN_UserInput(4*TickBase, false);\r
+}\r
+#endif\r
+\r
+//===========================================================================\r
+\r
+/*\r
+============================\r
+=\r
+= SaveTheGame\r
+=\r
+============================\r
+*/\r
+\r
+#define RLETAG 0xABCD\r
+\r
+boolean SaveTheGame(Sint16 handle)\r
+{\r
+ Uint16 i,compressed,expanded;\r
+ objtype *ob;\r
+ memptr bigbuffer;\r
+\r
+ gamestate.riding = NULL;\r
+\r
+ if (!CA_FarWrite(handle, (byte far *)&gamestate, sizeof(gamestate)))\r
+ return false;\r
+\r
+ expanded = mapwidth * mapheight * 2;\r
+ MM_GetPtr(&bigbuffer, expanded);\r
+\r
+ for (i = 0; i < 3; i++)\r
+ {\r
+ compressed = CA_RLEWCompress(mapsegs[i], expanded, (Uint16 huge *)bigbuffer+1, RLETAG);\r
+ *(Uint16 huge *)bigbuffer = compressed;\r
+ if (!CA_FarWrite(handle, bigbuffer, compressed+2))\r
+ {\r
+ MM_FreePtr(&bigbuffer);\r
+ return false;\r
+ }\r
+ }\r
+ for (ob = player; ob; ob=ob->next)\r
+ {\r
+ if (!CA_FarWrite(handle, (byte far *)ob, sizeof(objtype)))\r
+ {\r
+ MM_FreePtr(&bigbuffer);\r
+ return false;\r
+ }\r
+ }\r
+ MM_FreePtr(&bigbuffer);\r
+ return true;\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+============================\r
+=\r
+= LoadTheGame\r
+=\r
+============================\r
+*/\r
+\r
+boolean LoadTheGame(Sint16 handle)\r
+{\r
+ Uint16 i;\r
+ objtype *prev,*next,*followed;\r
+ Uint16 compressed,expanded;\r
+ memptr bigbuffer;\r
+#ifdef KEEN5\r
+ Sint16 numfuses;\r
+#endif\r
+\r
+ if (!CA_FarRead(handle, (byte far *)&gamestate, sizeof(gamestate)))\r
+ return false;\r
+\r
+#ifdef KEEN5\r
+ //\r
+ // remember the fuses value for later - SetupGameLevel calls\r
+ // ScanInfoPlane, which resets this part of the gamestate\r
+ //\r
+ numfuses = gamestate.numfuses;\r
+#endif\r
+\r
+ ca_levelbit >>= 1;\r
+ ca_levelnum--;\r
+ SetupGameLevel(false);\r
+ if (mmerror)\r
+ {\r
+ mmerror = false;\r
+ US_CenterWindow(20, 8);\r
+ PrintY += 20;\r
+ US_CPrint("Not enough memory\nto load game!");\r
+ VW_UpdateScreen();\r
+ IN_Ack();\r
+ return false;\r
+ }\r
+ ca_levelbit <<= 1;\r
+ ca_levelnum++;\r
+\r
+ expanded = mapwidth * mapheight * 2;\r
+ MM_BombOnError(true); //BUG: this should use false to avoid an instant crash\r
+ MM_GetPtr(&bigbuffer, expanded);\r
+ MM_BombOnError(false); //BUG: this should use true to force an instant crash\r
+ if (mmerror)\r
+ {\r
+ mmerror = false;\r
+ US_CenterWindow(20, 8);\r
+ PrintY += 20;\r
+ US_CPrint("Not enough memory\nto load game!");\r
+ VW_UpdateScreen();\r
+ IN_Ack();\r
+ return false;\r
+ }\r
+ for (i = 0; i < 3; i++)\r
+ {\r
+ if (!CA_FarRead(handle, (byte far *)&compressed, sizeof(compressed)))\r
+ {\r
+ MM_FreePtr(&bigbuffer);\r
+ return false;\r
+ }\r
+ if (!CA_FarRead(handle, (byte far *)bigbuffer, compressed))\r
+ {\r
+ MM_FreePtr(&bigbuffer);\r
+ return false;\r
+ }\r
+ CA_RLEWexpand(bigbuffer, mapsegs[i], expanded, RLETAG);\r
+ }\r
+ MM_FreePtr(&bigbuffer);\r
+\r
+ InitObjArray();\r
+ new = player;\r
+ prev = new->prev;\r
+ next = new->next;\r
+ if (!CA_FarRead(handle, (byte far *)new, sizeof(objtype)))\r
+ {\r
+ return false;\r
+ }\r
+ new->prev = prev;\r
+ new->next = next;\r
+ new->needtoreact = true;\r
+ new->sprite = NULL;\r
+ new = scoreobj;\r
+ while (true)\r
+ {\r
+ prev = new->prev;\r
+ next = new->next;\r
+ if (!CA_FarRead(handle, (byte far *)new, sizeof(objtype)))\r
+ {\r
+ return false;\r
+ }\r
+ followed = new->next;\r
+ new->prev = prev;\r
+ new->next = next;\r
+ new->needtoreact = true;\r
+ new->sprite = NULL;\r
+ if (new->obclass == stunnedobj)\r
+ {\r
+ new->temp3 = 0; //clear sprite ptr for the stars\r
+ }\r
+#if defined KEEN4\r
+ else if (new->obclass == platformobj)\r
+ {\r
+ new->temp2 = new->temp3 = 0; //clear sprite ptrs\r
+ }\r
+#elif defined KEEN5\r
+ else if (new->obclass == mineobj)\r
+ {\r
+ new->temp4 = 0; //clear sprite ptr\r
+ }\r
+ else if (new->obclass == spherefulobj)\r
+ {\r
+ new->temp1 = new->temp2 = new->temp3 = new->temp4 = 0; //clear sprite ptrs\r
+ }\r
+#elif defined KEEN6\r
+ else if (new->obclass == platformobj)\r
+ {\r
+ new->temp3 = 0; //clear sprite ptr\r
+ }\r
+#endif\r
+ if (followed)\r
+ {\r
+ GetNewObj(false);\r
+ }\r
+ else\r
+ {\r
+ break;\r
+ }\r
+ }\r
+ scoreobj->temp2 = -1;\r
+ scoreobj->temp1 = -1;\r
+ scoreobj->temp3 = -1;\r
+ scoreobj->temp4 = -1;\r
+#ifdef KEEN5\r
+ gamestate.numfuses = numfuses; // put value from saved game back in place \r
+#endif\r
+ return true;\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+============================\r
+=\r
+= ResetGame\r
+=\r
+============================\r
+*/\r
+\r
+void ResetGame(void)\r
+{\r
+ NewGame();\r
+ ca_levelnum--;\r
+ ca_levelbit >>= 1;\r
+ CA_ClearMarks();\r
+ ca_levelbit <<= 1;\r
+ ca_levelnum++;\r
+}\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+==========================\r
+=\r
+= PatchWorldMap\r
+=\r
+= Takes out blocking squares and spawns flags\r
+=\r
+==========================\r
+*/\r
+\r
+void PatchWorldMap(void)\r
+{\r
+ Uint16 x, y, planeoff, info, level, tag;\r
+ Uint16 far *infoptr;\r
+\r
+ planeoff = 0;\r
+ infoptr = mapsegs[2];\r
+ for (y = 0; y < mapheight; y++)\r
+ {\r
+ for (x = 0; x < mapwidth; x++, infoptr++, planeoff++)\r
+ {\r
+ info = *infoptr;\r
+ level = info & 0xFF;\r
+ if (level >= MINDONELEVEL && level <= MAXDONELEVEL && gamestate.leveldone[level])\r
+ {\r
+ tag = info >> 8;\r
+ *infoptr = 0; // BUG: infoplane value should only be set to 0 if tag == 0xC0\r
+ if (tag == 0xD0)\r
+ {\r
+ mapsegs[1][planeoff] = 0;\r
+ }\r
+ else if (tag == 0xF0)\r
+ {\r
+#ifdef KEEN5\r
+ SpawnFlag(x, y);\r
+#else\r
+ if (levelcompleted == level)\r
+ {\r
+ SpawnThrowFlag(x, y);\r
+ }\r
+ else\r
+ {\r
+ SpawnFlag(x, y);\r
+ }\r
+#endif\r
+ }\r
+ }\r
+ }\r
+ }\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+==========================\r
+=\r
+= DelayedFade\r
+=\r
+= Fades out and latches FadeAndUnhook onto the refresh\r
+=\r
+==========================\r
+*/\r
+\r
+void DelayedFade(void)\r
+{\r
+ VW_FadeOut();\r
+ fadecount = 0;\r
+ RF_SetRefreshHook(&FadeAndUnhook);\r
+}\r
+\r
+/*\r
+==========================\r
+=\r
+= FadeAndUnhook\r
+=\r
+= Latch this onto the refresh so the screen only gets faded in after two\r
+= refreshes. This lets all actors draw themselves to both pages before\r
+= fading the screen in.\r
+=\r
+==========================\r
+*/\r
+\r
+void FadeAndUnhook(void)\r
+{\r
+ if (++fadecount == 2)\r
+ {\r
+ VW_FadeIn();\r
+ RF_SetRefreshHook(NULL);\r
+ TimeCount = lasttimecount; // don't adaptively time the fade\r
+ }\r
+}\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+==========================\r
+=\r
+= SetupGameLevel\r
+=\r
+= Load in map mapon and cache everything needed for it\r
+=\r
+==========================\r
+*/\r
+\r
+void SetupGameLevel(boolean loadnow)\r
+{\r
+//\r
+// randomize if not a demo\r
+//\r
+ if (DemoMode)\r
+ {\r
+ US_InitRndT(false);\r
+ gamestate.difficulty = gd_Normal;\r
+ }\r
+ else\r
+ {\r
+ US_InitRndT(true);\r
+ }\r
+\r
+//\r
+// load the level header and three map planes\r
+//\r
+ CA_CacheMap(gamestate.mapon);\r
+\r
+//\r
+// let the refresh manager set up some variables\r
+//\r
+ RF_NewMap();\r
+\r
+//\r
+// decide which graphics are needed and spawn actors\r
+//\r
+ CA_ClearMarks();\r
+ ScanInfoPlane();\r
+ if (mapon == 0)\r
+ {\r
+ PatchWorldMap();\r
+ }\r
+ RF_MarkTileGraphics();\r
+\r
+//\r
+// have the caching manager load and purge stuff to make sure all marks\r
+// are in memory\r
+//\r
+ MM_BombOnError(false);\r
+ CA_LoadAllSounds();\r
+ if (loadnow)\r
+ {\r
+ if (scorescreenkludge)\r
+ {\r
+ CA_CacheMarks(NULL);\r
+ }\r
+ else if (DemoMode)\r
+ {\r
+ CA_CacheMarks("DEMO");\r
+ }\r
+#ifdef KEEN5\r
+ else if (mapon == 0 && player->tiletop > 100)\r
+ {\r
+ CA_CacheMarks("Keen steps out\nonto Korath III");\r
+ }\r
+#endif\r
+ else\r
+ {\r
+ _fstrcpy(str, levelenter[mapon]);\r
+ CA_CacheMarks(str);\r
+ }\r
+ }\r
+ MM_BombOnError(true);\r
+\r
+ if (!mmerror && loadnow)\r
+ {\r
+ DelayedFade();\r
+ }\r
+}\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+==========================\r
+=\r
+= DialogDraw\r
+=\r
+==========================\r
+*/\r
+\r
+void DialogDraw(char *title, Uint16 numcache)\r
+{\r
+ Sint16 i;\r
+ Uint16 width, height;\r
+ Sint32 totalfree;\r
+\r
+ totalfree = MM_TotalFree();\r
+ if (totalfree < 2048)\r
+ {\r
+ handpic = 5;\r
+ }\r
+ else\r
+ {\r
+ handpic = 0;\r
+ for (i = 0; i < 6; i++)\r
+ {\r
+ CA_CacheGrChunk(i+KEENCOUNT1PIC);\r
+ CA_UnmarkGrChunk(i+KEENCOUNT1PIC);\r
+ if (grsegs[i+KEENCOUNT1PIC])\r
+ {\r
+ MM_SetPurge(&grsegs[i+KEENCOUNT1PIC], PURGE_FIRST);\r
+ }\r
+ else\r
+ {\r
+ mmerror = false;\r
+ handpic = 5;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ US_CenterWindow(26, 8);\r
+ if (grsegs[KEENCOUNT1PIC])\r
+ {\r
+ VWB_DrawPic(WindowX, WindowY, KEENCOUNT1PIC);\r
+ }\r
+ else\r
+ {\r
+ handpic = 5;\r
+ }\r
+ CA_UnmarkGrChunk(KEENCOUNT1PIC); //redundant\r
+ WindowW -= 48;\r
+ WindowX += 48;\r
+ SizeText(title, &width, &height);\r
+ PrintY += (WindowH-height)/2 - 4;\r
+ US_CPrint(title);\r
+ VW_UpdateScreen();\r
+ chunkmax = chunkcount = numcache / 6;\r
+ if (!chunkmax && !handpic)\r
+ {\r
+ handpic = 5;\r
+ if (grsegs[KEENCOUNT6PIC])\r
+ VWB_DrawPic(WindowX-24, WindowY+40, KEENCOUNT6PIC);\r
+ VW_UpdateScreen();\r
+ }\r
+}\r
+\r
+/*\r
+==========================\r
+=\r
+= DialogUpdate\r
+=\r
+==========================\r
+*/\r
+\r
+void DialogUpdate(void)\r
+{\r
+ if (--chunkcount || handpic > 4)\r
+ return;\r
+\r
+ chunkcount = chunkmax;\r
+ if (grsegs[handpic+KEENCOUNT2PIC])\r
+ {\r
+ VWB_DrawPic(WindowX-24, WindowY+40, handpic+KEENCOUNT2PIC);\r
+ }\r
+ VW_UpdateScreen();\r
+ handpic++;\r
+}\r
+\r
+/*\r
+==========================\r
+=\r
+= DialogFinish\r
+=\r
+==========================\r
+*/\r
+\r
+void DialogFinish(void)\r
+{\r
+ //this is empty\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+==================\r
+=\r
+= StartDemoRecord\r
+=\r
+==================\r
+*/\r
+\r
+void StartDemoRecord(void)\r
+{\r
+ Sint16 level;\r
+ boolean esc;\r
+\r
+ VW_FixRefreshBuffer();\r
+ US_CenterWindow(30, 3);\r
+ PrintY += 6;\r
+ US_Print(" Record a demo from level(0-21):");\r
+ VW_UpdateScreen();\r
+ esc = !US_LineInput(px, py, str, NULL, true, 2, 0);\r
+ if (!esc)\r
+ {\r
+ level = atoi(str);\r
+ if (level >= 0 && level <= 21)\r
+ {\r
+ gamestate.mapon = level;\r
+ playstate = ex_warped;\r
+ IN_StartDemoRecord(0x1000);\r
+ }\r
+ }\r
+}\r
+\r
+/*\r
+==================\r
+=\r
+= EndDemoRecord\r
+=\r
+==================\r
+*/\r
+\r
+void EndDemoRecord(void)\r
+{\r
+ Sint16 handle;\r
+ boolean esc;\r
+ char filename[] = "DEMO?."EXTENSION;\r
+\r
+ IN_StopDemo();\r
+ VW_FixRefreshBuffer();\r
+ US_CenterWindow(22, 3);\r
+ PrintY += 6;\r
+ US_Print(" Save as demo #(0-9):");\r
+ VW_UpdateScreen();\r
+ esc = !US_LineInput(px, py, str, NULL, true, 2, 0);\r
+ if (!esc && str[0] >= '0' && str[0] <= '9')\r
+ {\r
+ filename[4] = str[0];\r
+ handle = open(filename, O_BINARY|O_WRONLY|O_CREAT, S_IFREG|S_IREAD|S_IWRITE);\r
+ if (handle == -1)\r
+ {\r
+ Quit("EndDemoRecord: Cannot write demo file!");\r
+ }\r
+ write(handle, &mapon, sizeof(mapon));\r
+ write(handle, &DemoOffset, sizeof(DemoOffset));\r
+ CA_FarWrite(handle, DemoBuffer, DemoOffset);\r
+ close(handle);\r
+ }\r
+ IN_FreeDemoBuffer();\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+==========================\r
+=\r
+= HandleDeath\r
+=\r
+==========================\r
+*/\r
+\r
+void HandleDeath(void)\r
+{\r
+ Uint16 y, color, top, bottom, selection, w, h;\r
+\r
+ _fstrcpy(str, levelnames[mapon]);\r
+ SizeText(str, &w, &h);\r
+\r
+ memset(gamestate.keys, 0, sizeof(gamestate.keys));\r
+ gamestate.lives--;\r
+ if (gamestate.lives >= 0)\r
+ {\r
+ VW_FixRefreshBuffer();\r
+ US_CenterWindow(20, 8);\r
+ PrintY += 3;\r
+ US_CPrint("You didn't make it past");\r
+ top = PrintY+22;\r
+ if (h < 15)\r
+ PrintY += 4;\r
+ US_CPrint(str);\r
+ PrintY = top+2;\r
+ US_CPrint("Try Again");\r
+ PrintY += 4;\r
+ bottom = PrintY-2;\r
+ US_CPrint("Exit to "WORLDMAPNAME);\r
+\r
+ IN_ClearKeysDown();\r
+ selection = 0;\r
+ while (true)\r
+ {\r
+ if (selection)\r
+ {\r
+ y = bottom;\r
+ }\r
+ else\r
+ {\r
+ y = top;\r
+ }\r
+\r
+// draw select bar\r
+ if ((TimeCount / 16) & 1)\r
+ {\r
+ color = SECONDCOLOR;\r
+ }\r
+ else\r
+ {\r
+ color = FIRSTCOLOR;\r
+ }\r
+ VWB_Hlin(WindowX+4, WindowX+WindowW-4, y, color);\r
+ VWB_Hlin(WindowX+4, WindowX+WindowW-4, y+1, color);\r
+ VWB_Hlin(WindowX+4, WindowX+WindowW-4, y+12, color);\r
+ VWB_Hlin(WindowX+4, WindowX+WindowW-4, y+13, color);\r
+ VWB_Vlin(y+1, y+11, WindowX+4, color);\r
+ VWB_Vlin(y+1, y+11, WindowX+5, color);\r
+ VWB_Vlin(y+1, y+11, WindowX+WindowW-4, color);\r
+ VWB_Vlin(y+1, y+11, WindowX+WindowW-5, color);\r
+\r
+ VW_UpdateScreen();\r
+\r
+// erase select bar\r
+ VWB_Hlin(WindowX+4, WindowX+WindowW-4, y, WHITE);\r
+ VWB_Hlin(WindowX+4, WindowX+WindowW-4, y+1, WHITE);\r
+ VWB_Hlin(WindowX+4, WindowX+WindowW-4, y+12, WHITE);\r
+ VWB_Hlin(WindowX+4, WindowX+WindowW-4, y+13, WHITE);\r
+ VWB_Vlin(y+1, y+11, WindowX+4, WHITE);\r
+ VWB_Vlin(y+1, y+11, WindowX+5, WHITE);\r
+ VWB_Vlin(y+1, y+11, WindowX+WindowW-4, WHITE);\r
+ VWB_Vlin(y+1, y+11, WindowX+WindowW-5, WHITE);\r
+\r
+ if (LastScan == sc_Escape)\r
+ {\r
+ gamestate.mapon = 0; // exit to world map\r
+ IN_ClearKeysDown();\r
+ return;\r
+ }\r
+\r
+ IN_ReadControl(0, &c);\r
+ if (c.button0 || c.button1 || LastScan == sc_Return || LastScan == sc_Space)\r
+ {\r
+ if (selection)\r
+ gamestate.mapon = 0; // exit to world map\r
+ return;\r
+ }\r
+ if (c.yaxis == -1 || LastScan == sc_UpArrow)\r
+ {\r
+ selection = 0;\r
+ }\r
+ else if (c.yaxis == 1 || LastScan == sc_DownArrow)\r
+ {\r
+ selection = 1;\r
+ }\r
+ }\r
+ }\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+============================\r
+=\r
+= GameLoop\r
+=\r
+= A game has just started (after the cinematic or load game)\r
+=\r
+============================\r
+*/\r
+\r
+void GameLoop(void)\r
+{\r
+ Uint16 temp;\r
+#ifdef KEEN6\r
+ Uint16 i;\r
+#endif\r
+\r
+#ifdef KEEN6\r
+ if (!storedemo)\r
+ {\r
+ if (!US_ManualCheck())\r
+ {\r
+ loadedgame = false;\r
+ restartgame = gd_Continue;\r
+ return;\r
+ }\r
+ }\r
+#endif\r
+\r
+ if (playstate == ex_loadedgame)\r
+ {\r
+ goto loaded;\r
+ }\r
+reset:\r
+ gamestate.difficulty = restartgame;\r
+ restartgame = gd_Continue;\r
+ do\r
+ {\r
+startlevel:\r
+ SetupGameLevel(true);\r
+ if (mmerror)\r
+ {\r
+ if (gamestate.mapon != 0)\r
+ {\r
+ mmerror = false;\r
+ US_CenterWindow(20, 8);\r
+ PrintY += 20;\r
+ US_CPrint("Insufficient memory\nto load level!");\r
+ VW_UpdateScreen();\r
+ IN_Ack();\r
+ gamestate.mapon = 0; // exit to world map\r
+ SetupGameLevel(true);\r
+ }\r
+ if (mmerror)\r
+ {\r
+ Quit("GameLoop: Insufficient memory to load world map!");\r
+ }\r
+ }\r
+loaded:\r
+ keenkilled = false;\r
+ SD_WaitSoundDone();\r
+\r
+ PlayLoop();\r
+\r
+ if (playstate != ex_loadedgame)\r
+ {\r
+ memset(gamestate.keys, 0, sizeof(gamestate.keys));\r
+#ifdef KEEN5\r
+ gamestate.keycard = false;\r
+#endif\r
+ }\r
+ VW_FixRefreshBuffer();\r
+\r
+ if (tedlevel)\r
+ {\r
+ if (playstate == ex_loadedgame)\r
+ {\r
+ goto loaded;\r
+ }\r
+ else if (playstate == ex_died)\r
+ {\r
+ goto startlevel;\r
+ }\r
+ else\r
+ {\r
+ TEDDeath();\r
+ }\r
+ }\r
+\r
+ levelcompleted = -1;\r
+ switch (playstate)\r
+ {\r
+ case ex_resetgame:\r
+ goto reset;\r
+\r
+ case ex_loadedgame:\r
+ goto loaded;\r
+\r
+ case ex_died:\r
+ HandleDeath();\r
+ break;\r
+\r
+#if defined KEEN4\r
+ case ex_rescued:\r
+ if (mapon != 0)\r
+ {\r
+ SD_PlaySound(SND_LEVELDONE);\r
+ }\r
+ levelcompleted = mapon;\r
+ gamestate.leveldone[mapon] = true;\r
+ RescuedMember();\r
+ if (gamestate.rescued != 8)\r
+ {\r
+ gamestate.mapon = 0;\r
+ }\r
+ else\r
+ {\r
+ FreeGraphics();\r
+ RF_FixOfs();\r
+ VW_FixRefreshBuffer();\r
+ FinaleLayout();\r
+ CheckHighScore(gamestate.score, gamestate.rescued);\r
+ return;\r
+ }\r
+ break;\r
+\r
+#elif defined KEEN5\r
+ case ex_fusebroke:\r
+ SD_PlaySound(SND_LEVELDONE);\r
+ levelcompleted = mapon;\r
+ gamestate.leveldone[mapon] = ex_fusebroke;\r
+ FinishedFuse();\r
+ gamestate.mapon = 0;\r
+ break;\r
+\r
+ case ex_qedbroke:\r
+ FreeGraphics();\r
+ RF_FixOfs();\r
+ VW_FixRefreshBuffer();\r
+ FinaleLayout();\r
+ CheckHighScore(gamestate.score, 0);\r
+ return;\r
+\r
+#elif defined KEEN6\r
+ case ex_hook:\r
+ GotHook();\r
+ goto completed;\r
+\r
+ case ex_sandwich:\r
+ GotSandwich();\r
+ goto completed;\r
+\r
+ case ex_card:\r
+ GotPasscard();\r
+ goto completed;\r
+\r
+ case ex_molly:\r
+ FreeGraphics();\r
+ RF_FixOfs();\r
+ VW_FixRefreshBuffer();\r
+ FinaleLayout();\r
+ goto check_score;\r
+\r
+#endif\r
+ case ex_completed:\r
+ case ex_foot:\r
+ case ex_portout:\r
+completed:\r
+ if (mapon != 0)\r
+ {\r
+ SD_PlaySound(SND_LEVELDONE);\r
+ gamestate.mapon = 0;\r
+ levelcompleted = mapon;\r
+ gamestate.leveldone[mapon] = true;\r
+ if (storedemo && mapon == 2)\r
+ {\r
+ IN_ClearKeysDown();\r
+ return;\r
+ }\r
+ }\r
+ else\r
+ {\r
+#if GRMODE != CGAGR\r
+ temp = bufferofs;\r
+ bufferofs = displayofs;\r
+#endif\r
+ US_CenterWindow(26, 8);\r
+ PrintY += 25;\r
+ US_CPrint("One moment");\r
+#if GRMODE == CGAGR\r
+ VW_UpdateScreen();\r
+#else\r
+ bufferofs = temp;\r
+#endif\r
+ }\r
+ break;\r
+\r
+ case ex_abortgame:\r
+ IN_ClearKeysDown();\r
+ return;\r
+ }\r
+ } while (gamestate.lives >= 0);\r
+\r
+ GameOver();\r
+\r
+check_score:\r
+#if defined KEEN4\r
+ CheckHighScore(gamestate.score, gamestate.rescued);\r
+#else\r
+ temp = 0;\r
+#if defined KEEN6\r
+ for (i = 0; i < GAMELEVELS; i++)\r
+ {\r
+ if (gamestate.leveldone[i])\r
+ temp++;\r
+ }\r
+#endif\r
+ CheckHighScore(gamestate.score, temp);\r
+#endif\r
+}
\ No newline at end of file