]> 4ch.mooo.com Git - 16.git/blobdiff - 16/keen456/KEEN4-6/CK_PLAY.C
extrcted keen code remake
[16.git] / 16 / keen456 / KEEN4-6 / CK_PLAY.C
diff --git a/16/keen456/KEEN4-6/CK_PLAY.C b/16/keen456/KEEN4-6/CK_PLAY.C
new file mode 100755 (executable)
index 0000000..0e08140
--- /dev/null
@@ -0,0 +1,2422 @@
+/* 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
+ScanCode firescan = sc_Space;\r
+\r
+boolean singlestep, jumpcheat, godmode, keenkilled;\r
+\r
+exittype playstate;\r
+gametype gamestate;\r
+\r
+objtype *new, *check, *player, *scoreobj;\r
+\r
+Uint16 originxtilemax;\r
+Uint16 originytilemax;\r
+\r
+ControlInfo c;\r
+boolean button2, button3;      // never used\r
+\r
+objtype dummyobj;\r
+\r
+Sint16 invincible;\r
+\r
+boolean oldshooting, showscorebox, joypad;\r
+\r
+Sint16 groundslam;\r
+\r
+boolean debugok;\r
+boolean jumpbutton, jumpheld, pogobutton, pogoheld, firebutton, fireheld, upheld;\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+objtype *obj;\r
+\r
+Uint16 centerlevel;\r
+\r
+Uint16 objectcount;\r
+objtype objarray[MAXACTORS];\r
+objtype *lastobj;\r
+objtype *objfreelist;\r
+\r
+Sint16 inactivateleft;\r
+Sint16 inactivateright;\r
+Sint16 inactivatetop;\r
+Sint16 inactivatebottom;\r
+\r
+#ifdef KEEN6Ev15\r
+Uint16 __dummy__;      // never used, but must be present to recreate the original EXE\r
+#endif\r
+\r
+Uint16 extravbls;\r
+\r
+Uint16 windowofs;\r
+Sint16 vislines;\r
+boolean scrollup;\r
+\r
+Sint16 oldfirecount;\r
+\r
+//===========================================================================\r
+\r
+/*\r
+==================\r
+=\r
+= CountObjects\r
+=\r
+==================\r
+*/\r
+\r
+void CountObjects(void)\r
+{\r
+       Uint16 activeobjects, inactiveobjects;\r
+       objtype *ob;\r
+\r
+       activeobjects = inactiveobjects = 0;\r
+       for (ob=player; ob; ob=ob->next)\r
+       {\r
+               if (ob->active)\r
+               {\r
+                       activeobjects++;\r
+               }\r
+               else\r
+               {\r
+                       inactiveobjects++;\r
+               }\r
+       }\r
+       VW_FixRefreshBuffer();\r
+       US_CenterWindow(18, 4);\r
+       PrintY += 7;\r
+       US_Print("Active Objects :");\r
+       US_PrintUnsigned(activeobjects);\r
+       US_Print("\nInactive Objects:");\r
+       US_PrintUnsigned(inactiveobjects);\r
+       VW_UpdateScreen();\r
+       IN_Ack();\r
+}\r
+\r
+/*\r
+==================\r
+=\r
+= DebugMemory\r
+=\r
+==================\r
+*/\r
+\r
+void DebugMemory(void)\r
+{\r
+       VW_FixRefreshBuffer();\r
+       US_CenterWindow(16, 7);\r
+       US_CPrint("Memory Usage");\r
+       US_CPrint("------------");\r
+       US_Print("Total     :");\r
+       US_PrintUnsigned((mminfo.mainmem+mminfo.EMSmem+mminfo.XMSmem)/1024);\r
+       US_Print("k\nFree      :");\r
+       US_PrintUnsigned(MM_UnusedMemory()/1024);\r
+       US_Print("k\nWith purge:");\r
+       US_PrintUnsigned(MM_TotalFree()/1024);\r
+       US_Print("k\n");\r
+       VW_UpdateScreen();\r
+       IN_Ack();\r
+#if GRMODE != CGAGR\r
+       MM_ShowMemory();\r
+#endif\r
+}\r
+\r
+/*\r
+===================\r
+=\r
+= TestSprites\r
+=\r
+===================\r
+*/\r
+\r
+void TestSprites(void)\r
+{\r
+       Uint16 infox, infoy;\r
+       Sint16 chunk, oldchunk;\r
+       Sint16 shift;\r
+       Uint16 infobottom, drawx;\r
+       spritetabletype far *info;\r
+       Uint8 _seg *block;\r
+       Uint16 size;\r
+       Uint16 scan;\r
+       Uint32 totalsize;\r
+\r
+       VW_FixRefreshBuffer();\r
+       US_CenterWindow(30, 17);\r
+       totalsize = 0;\r
+       US_CPrint("Sprite Test");\r
+       US_CPrint("-----------");\r
+       infoy = PrintY;\r
+       infox = (PrintX + 56) & ~7;\r
+       drawx = infox + 40;\r
+       US_Print("Chunk:\nWidth:\nHeight:\nOrgx:\nOrgy:\nXl:\nYl:\nXh:\nYh:\nShifts:\nMem:\n");\r
+       infobottom = PrintY;\r
+       chunk = STARTSPRITES;\r
+       shift = 0;\r
+       while (1)\r
+       {\r
+               if (chunk >= STARTSPRITES+NUMSPRITES)\r
+               {\r
+                       chunk = STARTSPRITES+NUMSPRITES-1;\r
+               }\r
+               else if (chunk < STARTSPRITES)\r
+               {\r
+                       chunk = STARTSPRITES;\r
+               }\r
+               info = &spritetable[chunk-STARTSPRITES];\r
+               block = grsegs[chunk];\r
+               VWB_Bar(infox, infoy, 40, infobottom-infoy, WHITE);\r
+               PrintX = infox;\r
+               PrintY = infoy;\r
+               US_PrintUnsigned(chunk);\r
+               US_Print("\n");\r
+               PrintX = infox;\r
+               US_PrintUnsigned(info->width);\r
+               US_Print("\n");\r
+               PrintX = infox;\r
+               US_PrintUnsigned(info->height);\r
+               US_Print("\n");\r
+               PrintX = infox;\r
+               US_PrintSigned(info->orgx);\r
+               US_Print("\n");\r
+               PrintX = infox;\r
+               US_PrintSigned(info->orgy);\r
+               US_Print("\n");\r
+               PrintX = infox;\r
+               US_PrintSigned(info->xl);\r
+               US_Print("\n");\r
+               PrintX = infox;\r
+               US_PrintSigned(info->yl);\r
+               US_Print("\n");\r
+               PrintX = infox;\r
+               US_PrintSigned(info->xh);\r
+               US_Print("\n");\r
+               PrintX = infox;\r
+               US_PrintSigned(info->yh);\r
+               US_Print("\n");\r
+               PrintX = infox;\r
+               US_PrintSigned(info->shifts);\r
+               US_Print("\n");\r
+               PrintX = infox;\r
+               if (!block)\r
+               {\r
+                       US_Print("-----");\r
+               }\r
+               else\r
+               {\r
+                       size = ((spritetype far *)block)->sourceoffset[3] + ((spritetype far *)block)->planesize[3]*5;\r
+                       size = (size + 15) & ~15;       //round up to multiples of 16\r
+                       totalsize += size;      //useless: the value stored in 'totalsize' is never used\r
+                       US_PrintUnsigned(size);\r
+                       US_Print("=");\r
+               }\r
+               oldchunk = chunk;\r
+               do\r
+               {\r
+                       VWB_Bar(drawx, infoy, 110, infobottom-infoy, WHITE);\r
+                       if (block)\r
+                       {\r
+                               PrintX = drawx;\r
+                               PrintY = infoy;\r
+                               US_Print("Shift:");\r
+                               US_PrintUnsigned(shift);\r
+                               US_Print("\n");\r
+                               VWB_DrawSprite(drawx + 2*shift + 16, PrintY, chunk);\r
+                       }\r
+                       VW_UpdateScreen();\r
+                       scan = IN_WaitForKey();\r
+                       switch (scan)\r
+                       {\r
+                       case sc_UpArrow:\r
+                               chunk++;\r
+                               break;\r
+                       case sc_DownArrow:\r
+                               chunk--;\r
+                               break;\r
+                       case sc_PgUp:\r
+                               chunk += 10;\r
+                               if (chunk >= STARTSPRITES+NUMSPRITES)\r
+                               {\r
+                                       chunk = STARTSPRITES+NUMSPRITES-1;\r
+                               }\r
+                               break;\r
+                       case sc_PgDn:\r
+                               chunk -= 10;\r
+                               if (chunk < STARTSPRITES)\r
+                               {\r
+                                       chunk = STARTSPRITES;\r
+                               }\r
+                               break;\r
+                       case sc_LeftArrow:\r
+                               if (--shift == -1)\r
+                               {\r
+                                       shift = 3;\r
+                               }\r
+                               break;\r
+                       case sc_RightArrow:\r
+                               if (++shift == 4)\r
+                               {\r
+                                       shift = 0;\r
+                               }\r
+                               break;\r
+                       case sc_Escape:\r
+                               return;\r
+                       }\r
+\r
+               } while (chunk == oldchunk);\r
+\r
+       }\r
+}\r
+\r
+/*\r
+===================\r
+=\r
+= PicturePause\r
+=\r
+===================\r
+*/\r
+\r
+void PicturePause(void)\r
+{\r
+       Uint16 source;\r
+       Sint16 y;\r
+\r
+//\r
+// wait for a key press, abort if it's not Enter\r
+//\r
+       IN_ClearKeysDown();\r
+       while (!LastScan);\r
+       if (LastScan != sc_Enter)\r
+       {\r
+               IN_ClearKeysDown();\r
+               return;\r
+       }\r
+\r
+       SD_PlaySound(SND_JUMP);\r
+       SD_WaitSoundDone();\r
+\r
+//\r
+// rearrange onscreen image into base EGA layout, so that it\r
+// can be grabbed correctly by an external screenshot tool\r
+//\r
+       source = displayofs + panadjust;\r
+\r
+       VW_ColorBorder(15);     // white (can't use WHITE as parameter, since that's defined as 3 for CGA and this must use 15)\r
+       VW_SetLineWidth(40);\r
+       VW_SetScreen(0, 0);\r
+\r
+       if (source < 0x10000l-200*64)\r
+       {\r
+       //\r
+       // copy top line first\r
+       //\r
+               for (y=0; y<200; y++)\r
+               {\r
+                       VW_ScreenToScreen(source+y*64, y*40, 40, 1);\r
+               }\r
+       }\r
+       else\r
+       {\r
+       //\r
+       // copy bottom line first\r
+       //\r
+               for (y=199; y>=0; y--)\r
+               {\r
+                       VW_ScreenToScreen(source+y*64, y*40, 40, 1);\r
+               }\r
+       }\r
+\r
+//\r
+// shut down input manager so that screenshot tool can see input again\r
+//\r
+       IN_Shutdown();\r
+\r
+       SD_PlaySound(SND_EXTRAKEEN);\r
+       SD_WaitSoundDone();\r
+\r
+//\r
+// shut down the remaining ID managers, except VW (stay in graphics mode!)\r
+//\r
+       US_Shutdown();\r
+       SD_Shutdown();\r
+       IN_Shutdown();\r
+       RF_Shutdown();\r
+       CA_Shutdown();\r
+       MM_Shutdown();\r
+\r
+//\r
+// wait until user hits Escape\r
+//\r
+       while (((bioskey(0) >> 8) & 0xFF) != sc_Escape);\r
+\r
+//\r
+// back to text mode and exit to DOS\r
+//\r
+       VW_Shutdown();\r
+       exit(0);\r
+}\r
+\r
+/*\r
+===================\r
+=\r
+= MaskOnTile\r
+=\r
+===================\r
+*/\r
+\r
+void MaskOnTile(Uint16 dest, Uint16 source)\r
+{\r
+       Sint16 i;\r
+       Uint16 _seg *sourceseg;\r
+       Uint16 _seg *destseg;\r
+       Uint16 sourceval, maskindex, sourcemask;\r
+\r
+       sourceseg = (grsegs+STARTTILE16M)[source];\r
+       destseg = (grsegs+STARTTILE16M)[dest];\r
+       for (i=0; i<64; i++)\r
+       {\r
+               maskindex = i & 15;\r
+#ifdef KEEN6Ev15\r
+               sourceval = sourceseg[16+i];\r
+#else\r
+               sourceval = (sourceseg+16)[i];\r
+#endif\r
+               sourcemask = sourceseg[maskindex];\r
+               destseg[maskindex] &= sourcemask;\r
+               destseg[16+i] &= sourcemask;\r
+               destseg[16+i] |= sourceval;\r
+       }\r
+}\r
+\r
+/*\r
+===================\r
+=\r
+= WallDebug\r
+=\r
+===================\r
+*/\r
+\r
+void WallDebug(void)\r
+{\r
+       Sint16 i, val;\r
+\r
+       VW_FixRefreshBuffer();\r
+       US_CenterWindow(24, 3);\r
+       US_PrintCentered("WORKING");\r
+       VW_UpdateScreen();\r
+       for (i=STARTTILE16M+108; i<STARTTILE16M+124; i++)\r
+       {\r
+               CA_CacheGrChunk(i);\r
+       }\r
+       for (i=0; i<NUMTILE16M; i++)\r
+       {\r
+               if (!grsegs[STARTTILE16M+i])\r
+               {\r
+                       continue;\r
+               }\r
+               val = tinf[i+NORTHWALL] & 7;\r
+               if (val)\r
+               {\r
+                       MaskOnTile(i, val+107);\r
+               }\r
+               val = tinf[i+SOUTHWALL] & 7;\r
+               if (val)\r
+               {\r
+                       MaskOnTile(i, val+115);\r
+               }\r
+               val = tinf[i+EASTWALL] & 7;\r
+               if (val > 1)\r
+               {\r
+                       strcpy(str, "WallDebug: East wall other than 1:");\r
+                       itoa(i, str2, 10);\r
+                       strcat(str, str2);\r
+                       Quit(str);\r
+               }\r
+               if (val)\r
+               {\r
+                       MaskOnTile(i, val+114); //Note: val is always 1 here, so you could use 115 as 2nd arg\r
+               }\r
+               val = tinf[i+WESTWALL] & 7;\r
+               if (val > 1)\r
+               {\r
+                       strcpy(str, "WallDebug: West wall other than 1:");\r
+                       itoa(i, str2, 10);\r
+                       strcat(str, str2);\r
+                       Quit(str);\r
+               }\r
+               if (val)\r
+               {\r
+                       MaskOnTile(i, val+122); //Note: val is always 1 here, so you could use 123 as 2nd arg\r
+               }\r
+       }\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+================\r
+=\r
+= DebugKeys\r
+=\r
+================\r
+*/\r
+\r
+boolean DebugKeys(void)\r
+{\r
+       Sint16 level, i, esc;\r
+\r
+       if (Keyboard[sc_B] && ingame)           // B = border color\r
+       {\r
+               VW_FixRefreshBuffer();\r
+               US_CenterWindow(24, 3);\r
+               PrintY += 6;\r
+               US_Print(" Border color (0-15):");\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 <= 15)\r
+                       {\r
+                               VW_ColorBorder(level);\r
+                       }\r
+               }\r
+               return true;\r
+       }\r
+\r
+       if (Keyboard[sc_C] && ingame)           // C = count objects\r
+       {\r
+               CountObjects();\r
+               return true;\r
+       }\r
+\r
+       if (Keyboard[sc_D] && ingame)           // D = start / end demo record\r
+       {\r
+               if (DemoMode == demo_Off)\r
+               {\r
+                       StartDemoRecord();\r
+               }\r
+               else if (DemoMode == demo_Record)\r
+               {\r
+                       EndDemoRecord();\r
+                       playstate = ex_completed;\r
+               }\r
+               return true;\r
+       }\r
+\r
+       if (Keyboard[sc_E] && ingame)           // E = quit level\r
+       {\r
+               if (tedlevel)\r
+               {\r
+                       TEDDeath();\r
+               }\r
+               playstate = ex_completed;\r
+               //BUG? there is no return in this branch (should return false)\r
+       }\r
+\r
+       if (Keyboard[sc_G] && ingame)           // G = god mode\r
+       {\r
+               VW_FixRefreshBuffer();\r
+               US_CenterWindow(12, 2);\r
+               if (godmode)\r
+               {\r
+                       US_PrintCentered("God mode OFF");\r
+               }\r
+               else\r
+               {\r
+                       US_PrintCentered("God mode ON");\r
+               }\r
+               VW_UpdateScreen();\r
+               IN_Ack();\r
+               godmode ^= true;\r
+               return true;\r
+       }\r
+       else if (Keyboard[sc_I])                        // I = item cheat\r
+       {\r
+               VW_FixRefreshBuffer();\r
+               US_CenterWindow(12, 3);\r
+               US_PrintCentered("Free items!");\r
+               for (i=0; i<4; i++)\r
+               {\r
+                       gamestate.keys[i] = 99;\r
+               }\r
+               gamestate.ammo = 99;\r
+#if defined KEEN4\r
+               gamestate.wetsuit = true;\r
+#elif defined KEEN5\r
+               gamestate.keycard = true;\r
+#elif defined KEEN6\r
+               gamestate.passcardstate=gamestate.hookstate=gamestate.sandwichstate = 1;\r
+#endif\r
+               VW_UpdateScreen();\r
+               IN_Ack();\r
+               GivePoints(3000);\r
+               return true;\r
+       }\r
+       else if (Keyboard[sc_J])                        // J = jump cheat\r
+       {\r
+               jumpcheat ^= true;\r
+               VW_FixRefreshBuffer();\r
+               US_CenterWindow(18, 3);\r
+               if (jumpcheat)\r
+               {\r
+                       US_PrintCentered("Jump cheat ON");\r
+               }\r
+               else\r
+               {\r
+                       US_PrintCentered("Jump cheat OFF");\r
+               }\r
+               VW_UpdateScreen();\r
+               IN_Ack();\r
+               return true;\r
+       }\r
+       else if (Keyboard[sc_M])                        // M = memory info\r
+       {\r
+               DebugMemory();\r
+               return true;\r
+       }\r
+       else if (Keyboard[sc_N])                        // N = no clip\r
+       {\r
+               VW_FixRefreshBuffer();\r
+               US_CenterWindow(18, 3);\r
+               if (player->needtoclip)\r
+               {\r
+                       US_PrintCentered("No clipping ON");\r
+                       player->needtoclip = cl_noclip;\r
+               }\r
+               else\r
+               {\r
+                       US_PrintCentered("No clipping OFF");\r
+                       player->needtoclip = cl_midclip;\r
+               }\r
+               VW_UpdateScreen();\r
+               IN_Ack();\r
+               return true;\r
+       }\r
+       else if (Keyboard[sc_P])                        // P = pause with no screen disruptioon\r
+       {\r
+               IN_ClearKeysDown();\r
+               PicturePause();\r
+               return true;\r
+       }\r
+       else if (Keyboard[sc_S] && ingame)      // S = slow motion\r
+       {\r
+               singlestep ^= true;\r
+               VW_FixRefreshBuffer();\r
+               US_CenterWindow(18, 3);\r
+               if (singlestep)\r
+               {\r
+                       US_PrintCentered("Slow motion ON");\r
+               }\r
+               else\r
+               {\r
+                       US_PrintCentered("Slow motion OFF");\r
+               }\r
+               VW_UpdateScreen();\r
+               IN_Ack();\r
+               return true;\r
+       }\r
+       else if (Keyboard[sc_T])                        // T = sprite test\r
+       {\r
+               TestSprites();\r
+               return true;\r
+       }\r
+       else if (Keyboard[sc_V])                        // V = extra VBLs\r
+       {\r
+               VW_FixRefreshBuffer();\r
+               US_CenterWindow(30, 3);\r
+               PrintY += 6;\r
+               US_Print("  Add how many extra VBLs(0-8):");\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 <= 8)\r
+                       {\r
+                               extravbls = level;\r
+                       }\r
+               }\r
+               return true;\r
+       }\r
+       else if (Keyboard[sc_W] && ingame)      // W = warp to level\r
+       {\r
+               VW_FixRefreshBuffer();\r
+               US_CenterWindow(26, 3);\r
+               PrintY += 6;\r
+               US_Print("  Warp to which level(1-18):");\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 <= 18)\r
+                       {\r
+                               gamestate.mapon = level;\r
+                               playstate = ex_warped;\r
+                       }\r
+               }\r
+               return true;\r
+       }\r
+       else if (Keyboard[sc_Y])                        // Y = wall debug\r
+       {\r
+               WallDebug();\r
+               return true;\r
+       }\r
+       else if (Keyboard[sc_Z])                        // Z = game over\r
+       {\r
+               gamestate.lives = 0;\r
+               KillKeen();\r
+               return false;\r
+       }\r
+       return false;\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+================\r
+=\r
+= UserCheat\r
+=\r
+================\r
+*/\r
+\r
+void UserCheat(void)\r
+{\r
+       Sint16 i;\r
+\r
+       for (i=sc_A; i<=sc_Z; i++)      //Note: this does NOT check the keys in alphabetical order!\r
+       {\r
+               if (i != sc_B && i != sc_A && i != sc_T && Keyboard[i])\r
+               {\r
+                       return;\r
+               }\r
+       }\r
+       US_CenterWindow(20, 7);\r
+       PrintY += 2;\r
+       US_CPrint(\r
+               "Cheat Option!\n"\r
+               "\n"\r
+               "You just got all\n"\r
+               "the keys, 99 shots,\n"\r
+               "and an extra keen!");\r
+       VW_UpdateScreen();\r
+       IN_Ack();\r
+       RF_ForceRefresh();\r
+       gamestate.ammo = 99;\r
+       gamestate.lives++;\r
+#ifdef KEEN5\r
+       gamestate.keycard = true;\r
+#endif\r
+       gamestate.keys[0] = gamestate.keys[1] = gamestate.keys[2] = gamestate.keys[3] = 1;\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
+       {\r
+               return;\r
+       }\r
+\r
+//\r
+// Enter for status screen\r
+//\r
+       if (Keyboard[sc_Enter] || (GravisGamepad && GravisAction[ga_Status]))\r
+       {\r
+               StatusWindow();\r
+               IN_ClearKeysDown();\r
+               RF_ForceRefresh();\r
+               lasttimecount = TimeCount;      // BUG: should be the other way around\r
+       }\r
+\r
+//\r
+// pause key wierdness can't be checked as a scan code\r
+//\r
+       if (Paused)\r
+       {\r
+               SD_MusicOff();\r
+               VW_FixRefreshBuffer();\r
+               US_CenterWindow(8, 3);\r
+               US_PrintCentered("PAUSED");\r
+               VW_UpdateScreen();\r
+               IN_Ack();\r
+               RF_ForceRefresh();\r
+               Paused = false;\r
+               SD_MusicOn();\r
+       }\r
+\r
+#ifndef KEEN6\r
+//\r
+// F1 to enter help screens\r
+//\r
+       if (LastScan == sc_F1)\r
+       {\r
+               StopMusic();\r
+               HelpScreens();\r
+               StartMusic(gamestate.mapon);\r
+               if (showscorebox)\r
+               {\r
+                       scoreobj->temp2 = -1;\r
+                       scoreobj->temp1 = -1;\r
+                       scoreobj->temp3 = -1;\r
+                       scoreobj->temp4 = -1;\r
+               }\r
+               RF_ForceRefresh();\r
+       }\r
+#endif\r
+\r
+       if (!storedemo)\r
+       {\r
+//\r
+// F2-F7/ESC to enter control panel\r
+//\r
+               if (LastScan >= sc_F2 && LastScan <= sc_F7 || LastScan == sc_Escape)\r
+               {\r
+                       VW_FixRefreshBuffer();\r
+                       StopMusic();\r
+                       US_ControlPanel();\r
+                       RF_FixOfs();\r
+                       StartMusic(gamestate.mapon);\r
+                       if (!showscorebox && scoreobj->sprite)\r
+                       {\r
+                               RF_RemoveSprite(&scoreobj->sprite);\r
+                       }\r
+                       if (showscorebox)\r
+                       {\r
+                               scoreobj->temp2 = -1;\r
+                               scoreobj->temp1 = -1;\r
+                               scoreobj->temp3 = -1;\r
+                               scoreobj->temp4 = -1;\r
+                       }\r
+                       IN_ClearKeysDown();\r
+                       if (restartgame)\r
+                       {\r
+                               playstate = ex_resetgame;\r
+                       }\r
+                       else if (!loadedgame)\r
+                       {\r
+                               RF_ForceRefresh();\r
+                       }\r
+                       if (abortgame)\r
+                       {\r
+                               abortgame = false;\r
+                               playstate = ex_abortgame;\r
+                       }\r
+                       if (loadedgame)\r
+                       {\r
+                               playstate = ex_loadedgame;\r
+                       }\r
+                       lasttimecount = TimeCount;      // BUG: should be the other way around\r
+               }\r
+\r
+//\r
+// F9 boss key\r
+//\r
+               if (LastScan == sc_F9)\r
+               {\r
+                       VW_Shutdown();\r
+                       SD_MusicOff();\r
+                       cputs("C:>");\r
+                       IN_ClearKeysDown();\r
+                       while (LastScan != sc_Escape);\r
+                       VW_SetScreenMode(GRMODE);\r
+                       VW_ColorBorder(bordercolor);\r
+                       RF_ForceRefresh();\r
+                       IN_ClearKeysDown();\r
+                       lasttimecount = TimeCount;      // BUG: should be the other way around\r
+                       SD_MusicOn();\r
+               }\r
+       }\r
+\r
+//\r
+// B-A-T cheat code\r
+//\r
+       if (Keyboard[sc_B] && Keyboard[sc_A] && Keyboard[sc_T])\r
+       {\r
+               UserCheat();\r
+       }\r
+\r
+//\r
+// F10-? debug keys\r
+//\r
+       if (debugok && Keyboard[sc_F10])\r
+       {\r
+               if (DebugKeys())\r
+               {\r
+                       RF_ForceRefresh();\r
+                       lasttimecount = TimeCount;      // BUG: should be the other way around\r
+               }\r
+       }\r
+\r
+//\r
+// Ctrl-S toggles sound (only in storedemo mode)\r
+//\r
+       if (storedemo && Keyboard[sc_Control] && LastScan == sc_S)\r
+       {\r
+               if (SoundMode != sdm_Off)\r
+               {\r
+                       SD_SetSoundMode(sdm_Off);\r
+                       SD_SetMusicMode(smm_Off);\r
+               }\r
+               else\r
+               {\r
+                       if (AdLibPresent)\r
+                       {\r
+                               SD_SetSoundMode(sdm_AdLib);\r
+                               QuietFX = false;\r
+                               SD_SetMusicMode(smm_AdLib);\r
+                       }\r
+                       else\r
+                       {\r
+                               SD_SetSoundMode(sdm_PC);\r
+                               SD_SetMusicMode(smm_Off);\r
+                       }\r
+                       CA_LoadAllSounds();\r
+               }\r
+       }\r
+\r
+//\r
+// Ctrl-Q quick quit\r
+//\r
+       if (Keyboard[sc_Control] && LastScan == sc_Q)\r
+       {\r
+               IN_ClearKeysDown();\r
+               Quit(NULL);\r
+       }\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+==================\r
+=\r
+= PrintNumbers\r
+=\r
+==================\r
+*/\r
+\r
+void PrintNumbers(Sint16 x, Sint16 y, Sint16 maxlen, Sint16 basetile, Sint32 number)\r
+{\r
+       register Sint16 i;\r
+       Sint16 len;\r
+       char buffer[20];\r
+\r
+       ltoa(number, buffer, 10);\r
+       len = strlen(buffer);\r
+       i = maxlen;\r
+       while (i>len)\r
+       {\r
+               VWB_DrawTile8(x, y, basetile);\r
+               i--;\r
+               x += 8;\r
+       }\r
+       while (i>0)\r
+       {\r
+               VWB_DrawTile8(x, y, basetile+buffer[len-i]+(1-'0'));\r
+               i--;\r
+               x += 8;\r
+       }\r
+}\r
+\r
+/*\r
+==================\r
+=\r
+= DrawStatusWindow\r
+=\r
+==================\r
+*/\r
+\r
+#if GRMODE == CGAGR\r
+\r
+#define BACKCOLOR WHITE\r
+#define TEXTBACK BLACK\r
+#define NUMBERBACK BLACK\r
+\r
+#else\r
+\r
+#define BACKCOLOR LIGHTGRAY\r
+#define TEXTBACK WHITE\r
+#define NUMBERBACK BLACK\r
+\r
+#endif\r
+\r
+void DrawStatusWindow(void)\r
+{\r
+       Sint16 off, x, y, w, h, i;\r
+       Uint16 width, height;\r
+\r
+       x = 64;\r
+       y = 16;\r
+       w = 184;\r
+       h = 144;\r
+       VWB_DrawTile8(x, y, 54);\r
+       VWB_DrawTile8(x, y+h, 60);\r
+       for (i=x+8; i<=x+w-8; i+=8)\r
+       {\r
+               VWB_DrawTile8(i, y, 55);\r
+               VWB_DrawTile8(i, y+h, 61);\r
+       }\r
+       VWB_DrawTile8(i, y, 56);\r
+       VWB_DrawTile8(i, y+h, 62);\r
+       for (i=y+8; i<=y+h-8; i+=8)\r
+       {\r
+               VWB_DrawTile8(x, i, 57);\r
+               VWB_DrawTile8(x+w, i, 59);\r
+       }\r
+       VWB_Bar(72, 24, 176, 136, BACKCOLOR);\r
+\r
+       PrintY = 28;\r
+       WindowX = 80;\r
+       WindowW = 160;\r
+       US_CPrint("LOCATION");\r
+       VWB_Bar(79, 38, 162, 20, TEXTBACK);\r
+#ifdef KEEN5\r
+       if (mapon == 0 && player->y > 100*TILEGLOBAL)\r
+               _fstrcpy(str, levelnames[13]);\r
+       else\r
+               _fstrcpy(str, levelnames[gamestate.mapon]);\r
+#else\r
+       _fstrcpy(str, levelnames[gamestate.mapon]);\r
+#endif\r
+       SizeText(str, &width, &height);\r
+       PrintY = (20-height)/2+40-2;\r
+       US_CPrint(str);\r
+\r
+       PrintY = 61;\r
+       WindowX = 80;\r
+       WindowW = 64;\r
+       US_CPrint("SCORE");\r
+       VWB_Bar(79, 71, 66, 10, NUMBERBACK);\r
+       PrintNumbers(80, 72, 8, 41, gamestate.score);\r
+\r
+       PrintY = 61;\r
+       WindowX = 176;\r
+       WindowW = 64;\r
+       US_CPrint("EXTRA");\r
+       VWB_Bar(175, 71, 66, 10, NUMBERBACK);\r
+       PrintNumbers(176, 72, 8, 41, gamestate.nextextra);\r
+\r
+#if defined KEEN4\r
+       PrintY = 85;\r
+       WindowX = 80;\r
+       WindowW = 64;\r
+       US_CPrint("RESCUED");\r
+       VWB_Bar(79, 95, 66, 10, NUMBERBACK);\r
+       for (i = 0; i < gamestate.rescued; i++, off+=8)\r
+       {\r
+               VWB_DrawTile8(i*8 + 80, 96, 40);\r
+       }\r
+#elif defined KEEN5\r
+       PrintY = 92;\r
+       PrintX = 80;\r
+       US_Print("KEYCARD");\r
+       VWB_Bar(135, 91, 10, 10, NUMBERBACK);\r
+       if (gamestate.keycard)\r
+       {\r
+               VWB_DrawTile8(136, 92, 40);\r
+       }\r
+#endif\r
+\r
+       PrintY = 85;\r
+       WindowX = 176;\r
+       WindowW = 64;\r
+       US_CPrint("LEVEL");\r
+       VWB_Bar(175, 95, 66, 10, TEXTBACK);\r
+       PrintY = 96;\r
+       WindowX = 176;\r
+       WindowW = 64;\r
+       switch (gamestate.difficulty)\r
+       {\r
+       case gd_Easy:\r
+               US_CPrint("Easy");\r
+               break;\r
+       case gd_Normal:\r
+               US_CPrint("Normal");\r
+               break;\r
+       case gd_Hard:\r
+               US_CPrint("Hard");\r
+               break;\r
+       }\r
+\r
+#ifdef KEEN6\r
+       PrintX = 80;\r
+       PrintY = 96;\r
+       US_Print("ITEMS");\r
+       VWB_Bar(127, 95, 26, 10, NUMBERBACK);\r
+       if (gamestate.sandwichstate == 1)\r
+       {\r
+               VWB_DrawTile8(128, 96, 2);\r
+       }\r
+       else\r
+       {\r
+               VWB_DrawTile8(128, 96, 1);\r
+       }\r
+       if (gamestate.hookstate == 1)\r
+       {\r
+               VWB_DrawTile8(136, 96, 4);\r
+       }\r
+       else\r
+       {\r
+               VWB_DrawTile8(136, 96, 3);\r
+       }\r
+       if (gamestate.passcardstate == 1)\r
+       {\r
+               VWB_DrawTile8(144, 96, 6);\r
+       }\r
+       else\r
+       {\r
+               VWB_DrawTile8(144, 96, 5);\r
+       }\r
+#endif\r
+\r
+       PrintX = 80;\r
+       PrintY = 112;\r
+       US_Print("KEYS");\r
+       VWB_Bar(119, 111, 34, 10, NUMBERBACK);\r
+       for (i = 0; i < 4; i++)\r
+       {\r
+               if (gamestate.keys[i])\r
+               {\r
+                       VWB_DrawTile8(i*8+120, 112, 36+i);\r
+               }\r
+       }\r
+\r
+       PrintX = 176;\r
+       PrintY = 112;\r
+       US_Print("AMMO");\r
+       VWB_Bar(215, 111, 26, 10, NUMBERBACK);\r
+       PrintNumbers(216, 112, 3, 41, gamestate.ammo);\r
+\r
+       PrintX = 80;\r
+       PrintY = 128;\r
+       US_Print("KEENS");\r
+       VWB_Bar(127, 127, 18, 10, NUMBERBACK);\r
+       PrintNumbers(128, 128, 2, 41, gamestate.lives);\r
+\r
+       PrintX = 176;\r
+       PrintY = 128;\r
+       US_Print(DROPSNAME);\r
+       VWB_Bar(224, 127, 16, 10, NUMBERBACK);\r
+       PrintNumbers(224, 128, 2, 41, gamestate.drops);\r
+\r
+#ifdef KEEN4\r
+       VWB_Bar(79, 143, 66, 10, TEXTBACK);\r
+       PrintY = 144;\r
+       WindowX = 80;\r
+       WindowW = 64;\r
+       if (gamestate.wetsuit)\r
+       {\r
+               US_CPrint("Wetsuit");\r
+       }\r
+       else\r
+       {\r
+               US_CPrint("???");\r
+       }\r
+#endif\r
+\r
+       // draw the tiles for "PRESS A KEY":\r
+       for (i = 0; i < 10; i++)\r
+       {\r
+               VWB_DrawTile8(i*8+STATUS_PRESSKEY_X, 140, i+72);\r
+               VWB_DrawTile8(i*8+STATUS_PRESSKEY_X, 148, i+82);\r
+       }\r
+}\r
+\r
+/*\r
+==================\r
+=\r
+= ScrollStatusWindow\r
+=\r
+==================\r
+*/\r
+\r
+void ScrollStatusWindow(void)\r
+{\r
+       Uint16 source, dest;\r
+       Sint16 height;\r
+\r
+       if (vislines > 152)\r
+       {\r
+               height = vislines - 152;\r
+               source = windowofs + panadjust + 8;\r
+               dest = bufferofs + panadjust + 8;\r
+               VW_ScreenToScreen(source, dest, 192/BYTEPIXELS, height);\r
+               VW_ClipDrawMPic((pansx+136)/BYTEPIXELS, -(16-height)+pansy, METALPOLEPICM);\r
+               source = windowofs + panadjust + 16*SCREENWIDTH + 8*CHARWIDTH;\r
+               dest = bufferofs + panadjust + height*SCREENWIDTH + 8;\r
+               height = 152;\r
+       }\r
+       else\r
+       {\r
+               source = windowofs + panadjust + (152-vislines)*SCREENWIDTH + 16*SCREENWIDTH + 8*CHARWIDTH;\r
+               dest = bufferofs + panadjust + 8;\r
+               height = vislines;\r
+       }\r
+       if (height > 0)\r
+       {\r
+               VW_ScreenToScreen(source, dest, 192/BYTEPIXELS, height);\r
+       }\r
+       if (scrollup)\r
+       {\r
+               height = 168-vislines;\r
+               source = masterofs + panadjust + vislines*SCREENWIDTH + 8;\r
+               dest = bufferofs + panadjust + vislines*SCREENWIDTH + 8;\r
+               VW_ScreenToScreen(source, dest, 192/BYTEPIXELS, height);\r
+               height = vislines;\r
+               source = windowofs + panadjust + 8 - 24/BYTEPIXELS;\r
+               dest = bufferofs + panadjust + 8 - 24/BYTEPIXELS;\r
+               if (height > 0)\r
+                       VW_ScreenToScreen(source, dest, 24/BYTEPIXELS, height);\r
+       }\r
+       else\r
+       {\r
+               height = vislines + -72;\r
+               if (height > 0)\r
+               {\r
+                       source = windowofs + panadjust + 8 - 24/BYTEPIXELS;\r
+                       dest = bufferofs + panadjust + 8 - 24/BYTEPIXELS;\r
+                       if (height > 0)\r
+                               VW_ScreenToScreen(source, dest, 24/BYTEPIXELS, height);\r
+               }\r
+       }\r
+       if (vislines >= 72)\r
+       {\r
+               VW_ClipDrawMPic((pansx+40)/BYTEPIXELS, vislines-168+pansy, CORDPICM);\r
+       }\r
+       VW_UpdateScreen();\r
+}\r
+\r
+/*\r
+==================\r
+=\r
+= StatusWindow\r
+=\r
+==================\r
+*/\r
+\r
+void StatusWindow(void)\r
+{\r
+#if GRMODE == CGAGR\r
+\r
+       if (Keyboard[sc_A] && Keyboard[sc_2])\r
+       {\r
+               US_CenterWindow(20, 2);\r
+               PrintY += 2;\r
+               US_Print("Debug keys active");\r
+               VW_UpdateScreen();\r
+               IN_Ack();\r
+               debugok = true;\r
+       }\r
+\r
+       WindowX = 0;\r
+       WindowW = 320;\r
+       WindowY = 0;\r
+       WindowH = 200;\r
+       DrawStatusWindow();\r
+       VW_UpdateScreen();\r
+       IN_ClearKeysDown();\r
+       IN_Ack();\r
+\r
+#else\r
+\r
+       Uint16 oldbufferofs;\r
+\r
+       WindowX = 0;\r
+       WindowW = 320;\r
+       WindowY = 0;\r
+       WindowH = 200;\r
+\r
+       if (Keyboard[sc_A] && Keyboard[sc_2])\r
+       {\r
+               US_CenterWindow(20, 2);\r
+               PrintY += 2;\r
+               US_Print("Debug keys active");\r
+               VW_UpdateScreen();\r
+               IN_Ack();\r
+               debugok = true;\r
+       }\r
+\r
+       RF_Refresh();\r
+       RFL_InitAnimList();\r
+       oldbufferofs = bufferofs;\r
+       bufferofs = windowofs = RF_FindFreeBuffer();\r
+       VW_ScreenToScreen(displayofs, displayofs, 44, 224);     // useless (source and dest offsets are identical)\r
+       VW_ScreenToScreen(displayofs, masterofs, 44, 224);\r
+       VW_ScreenToScreen(displayofs, bufferofs, 44, 168);\r
+       DrawStatusWindow();\r
+       bufferofs = oldbufferofs;\r
+       RF_Refresh();\r
+\r
+       SD_PlaySound(SND_SHOWSTATUS);\r
+       vislines = 16;\r
+       scrollup = false;\r
+       RF_SetRefreshHook(ScrollStatusWindow);\r
+\r
+       while (true)\r
+       {\r
+               RF_Refresh();\r
+               if (vislines == 168)\r
+                       break;\r
+               vislines = vislines + tics*8;\r
+               if (vislines > 168)\r
+                       vislines = 168;\r
+       }\r
+\r
+       RF_Refresh();\r
+       RF_SetRefreshHook(NULL);\r
+       IN_ClearKeysDown();\r
+       IN_Ack();\r
+\r
+       SD_PlaySound(SND_HIDESTATUS);\r
+       vislines -= 16;\r
+       scrollup = true;\r
+       RF_SetRefreshHook(ScrollStatusWindow);\r
+\r
+       while (true)\r
+       {\r
+               RF_Refresh();\r
+               if (vislines == 0)\r
+                       break;\r
+               vislines = vislines - tics*8;\r
+               if (vislines < 0)\r
+                       vislines = 0;\r
+       }\r
+\r
+       RF_SetRefreshHook(NULL);\r
+\r
+       scoreobj->x = 0;        //force scorebox to redraw?\r
+\r
+#endif\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+==================\r
+=\r
+= CenterActor\r
+=\r
+==================\r
+*/\r
+\r
+void CenterActor(objtype *ob)\r
+{\r
+       Uint16 orgx, orgy;\r
+\r
+       centerlevel = 140;\r
+       if (ob->x < 152*PIXGLOBAL)\r
+       {\r
+               orgx = 0;\r
+       }\r
+       else\r
+       {\r
+               orgx = ob->x - 152*PIXGLOBAL;\r
+       }\r
+       if (mapon == 0)\r
+       {\r
+               if (ob->y < 80*PIXGLOBAL)\r
+               {\r
+                       orgy = 0;\r
+               }\r
+               else\r
+               {\r
+                       orgy = ob->y - 80*PIXGLOBAL;\r
+               }\r
+       }\r
+       else\r
+       {\r
+               if (ob->bottom < 140*PIXGLOBAL)\r
+               {\r
+                       orgy = 0;\r
+               }\r
+               else\r
+               {\r
+                       orgy = ob->bottom - 140*PIXGLOBAL;\r
+               }\r
+       }\r
+       if (!scorescreenkludge)\r
+       {\r
+               RF_NewPosition(orgx, orgy);\r
+       }\r
+\r
+//\r
+// update limits for onscreen and inactivate checks\r
+//\r
+       originxtilemax = (originxtile + PORTTILESWIDE) - 1;\r
+       originytilemax = (originytile + PORTTILESHIGH) - 1;\r
+       inactivateleft = originxtile - INACTIVATEDIST;\r
+       if (inactivateleft < 0)\r
+       {\r
+               inactivateleft = 0;\r
+       }\r
+       inactivateright = originxtilemax + INACTIVATEDIST;\r
+       if (inactivateright < 0)\r
+       {\r
+               inactivateright = 0;\r
+       }\r
+       inactivatetop = originytile - INACTIVATEDIST;\r
+       if (inactivatetop < 0)\r
+       {\r
+               inactivatetop = 0;\r
+       }\r
+       inactivatebottom = originytilemax + INACTIVATEDIST;\r
+       if (inactivatebottom < 0)\r
+       {\r
+               inactivatebottom = 0;\r
+       }\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+==================\r
+=\r
+= WorldScrollScreen\r
+=\r
+= Scroll if Keen is nearing an edge\r
+=\r
+==================\r
+*/\r
+\r
+void WorldScrollScreen(objtype *ob)\r
+{\r
+       Sint16 xscroll, yscroll;\r
+\r
+       if (keenkilled)\r
+               return;\r
+\r
+       if (ob->left < originxglobal + 9*TILEGLOBAL)\r
+       {\r
+               xscroll = ob->left - (originxglobal + 9*TILEGLOBAL);\r
+       }\r
+       else if (ob->right > originxglobal + 12*TILEGLOBAL)\r
+       {\r
+               xscroll = ob->right + 16 - (originxglobal + 12*TILEGLOBAL);\r
+       }\r
+       else\r
+       {\r
+               xscroll = 0;\r
+       }\r
+\r
+       if (ob->top < originyglobal + 5*TILEGLOBAL)\r
+       {\r
+               yscroll = ob->top - (originyglobal + 5*TILEGLOBAL);\r
+       }\r
+       else if (ob->bottom > originyglobal + 7*TILEGLOBAL)\r
+       {\r
+               yscroll = ob->bottom - (originyglobal + 7*TILEGLOBAL);\r
+       }\r
+       else\r
+       {\r
+               yscroll = 0;\r
+       }\r
+\r
+       if (!xscroll && !yscroll)\r
+               return;\r
+\r
+//\r
+// don't scroll more than one tile per frame\r
+//\r
+       if (xscroll >= 0x100)\r
+       {\r
+               xscroll = 0xFF;\r
+       }\r
+       else if (xscroll <= -0x100)\r
+       {\r
+               xscroll = -0xFF;\r
+       }\r
+       if (yscroll >= 0x100)\r
+       {\r
+               yscroll = 0xFF;\r
+       }\r
+       else if (yscroll <= -0x100)\r
+       {\r
+               yscroll = -0xFF;\r
+       }\r
+\r
+       RF_Scroll(xscroll, yscroll);\r
+\r
+//\r
+// update limits for onscreen and inactivate checks\r
+//\r
+       originxtilemax = (originxtile + PORTTILESWIDE) - 1;\r
+       originytilemax = (originytile + PORTTILESHIGH) - 1;\r
+       inactivateleft = originxtile - INACTIVATEDIST;\r
+       if (inactivateleft < 0)\r
+       {\r
+               inactivateleft = 0;\r
+       }\r
+       inactivateright = originxtilemax + INACTIVATEDIST;\r
+       if (inactivateright < 0)\r
+       {\r
+               inactivateright = 0;\r
+       }\r
+       inactivatetop = originytile - INACTIVATEDIST;\r
+       if (inactivatetop < 0)\r
+       {\r
+               inactivatetop = 0;\r
+       }\r
+       inactivatebottom = originytilemax + INACTIVATEDIST;\r
+       if (inactivatebottom < 0)\r
+       {\r
+               inactivatebottom = 0;\r
+       }\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+==================\r
+=\r
+= ScrollScreen\r
+=\r
+= Scroll if Keen is nearing an edge\r
+= Set playstate to ex_completes\r
+=\r
+==================\r
+*/\r
+\r
+void ScrollScreen(objtype *ob)\r
+{\r
+       Sint16 xscroll, yscroll, pix, speed;\r
+       Uint16 bottom;\r
+\r
+       if (keenkilled)\r
+               return;\r
+\r
+//\r
+// walked off edge of map?\r
+//\r
+       if (ob->left < originxmin || ob->right > originxmax + 320*PIXGLOBAL)\r
+       {\r
+               playstate = ex_completed;\r
+               return;\r
+       }\r
+\r
+//\r
+// fallen off bottom of world?\r
+//\r
+       if (ob->bottom > originymax + 13*TILEGLOBAL)\r
+       {\r
+               ob->y -= ob->bottom - (originymax + 13*TILEGLOBAL);\r
+               SD_PlaySound(SND_PLUMMET);\r
+               godmode = false;\r
+               KillKeen();\r
+               return;\r
+       }\r
+\r
+       xscroll=yscroll=0;\r
+\r
+       if (ob->x < originxglobal + 9*TILEGLOBAL)\r
+       {\r
+               xscroll = ob->x - (originxglobal + 9*TILEGLOBAL);\r
+       }\r
+       else if (ob->x > originxglobal + 12*TILEGLOBAL)\r
+       {\r
+               xscroll = ob->x - (originxglobal + 12*TILEGLOBAL);\r
+       }\r
+\r
+       if (ob->state == &s_keenlookup2)\r
+       {\r
+               if (centerlevel+tics > 167)\r
+               {\r
+                       pix = 167-centerlevel;\r
+               }\r
+               else\r
+               {\r
+                       pix = tics;\r
+               }\r
+               centerlevel += pix;\r
+               yscroll = CONVERT_PIXEL_TO_GLOBAL(-pix);\r
+       }\r
+       else if (ob->state == &s_keenlookdown3)\r
+       {\r
+               if (centerlevel-tics < 33)\r
+               {\r
+                       pix = centerlevel + -33;\r
+               }\r
+               else\r
+               {\r
+                       pix = tics;\r
+               }\r
+               centerlevel -= pix;\r
+               yscroll = CONVERT_PIXEL_TO_GLOBAL(pix);\r
+       }\r
+\r
+#ifdef KEEN6\r
+       if (groundslam)\r
+       {\r
+               static Sint16 shaketable[] = {0,\r
+                        -64,  -64,  -64,  64,  64,  64,\r
+                       -200, -200, -200, 200, 200, 200,\r
+                       -250, -250, -250, 250, 250, 250,\r
+                       -250, -250, -250, 250, 250, 250\r
+               };\r
+               yscroll = yscroll + (bottom - (ob->bottom + shaketable[groundslam]));   // BUG: 'bottom' has not been initialized yet!\r
+       }\r
+       else\r
+#endif\r
+       if ( (ob->hitnorth || !ob->needtoclip || ob->state == &s_keenholdon))\r
+       {\r
+               if (  ob->state != &s_keenclimbup\r
+                       && ob->state != &s_keenclimbup2\r
+                       && ob->state != &s_keenclimbup3\r
+                       && ob->state != &s_keenclimbup4)\r
+               {\r
+                       yscroll += ob->ymove;\r
+                       bottom = originyglobal + yscroll + CONVERT_PIXEL_TO_GLOBAL(centerlevel);\r
+                       if (ob->bottom == bottom)\r
+                       {\r
+                               // player didn't move, no additional scrolling\r
+                       }\r
+                       else\r
+                       {\r
+                               if (ob->bottom < bottom)\r
+                               {\r
+                                       pix = bottom - ob->bottom;\r
+                               }\r
+                               else\r
+                               {\r
+                                       pix = ob->bottom - bottom;\r
+                               }\r
+                               speed = CONVERT_PIXEL_TO_GLOBAL(pix) >> 7;\r
+                               if (speed > 0x30)\r
+                               {\r
+                                       speed = 0x30;\r
+                               }\r
+                               speed *= tics;\r
+                               if (speed < 0x10)\r
+                               {\r
+                                       if (pix < 0x10)\r
+                                       {\r
+                                               speed = pix;\r
+                                       }\r
+                                       else\r
+                                       {\r
+                                               speed = 0x10;\r
+                                       }\r
+                               }\r
+                               if (ob->bottom < bottom)\r
+                               {\r
+                                       yscroll -= speed;\r
+                               }\r
+                               else\r
+                               {\r
+                                       yscroll += speed;\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       else\r
+       {\r
+               centerlevel = 140;\r
+       }\r
+\r
+       pix = (ob->bottom-32*PIXGLOBAL)-(originyglobal+yscroll);\r
+       if (pix < 0)\r
+       {\r
+               yscroll += pix;\r
+       }\r
+       pix = (ob->bottom+32*PIXGLOBAL)-(originyglobal+yscroll+200*PIXGLOBAL);\r
+       if (pix > 0)\r
+       {\r
+               yscroll += pix;\r
+       }\r
+\r
+       if (xscroll == 0 && yscroll == 0)\r
+               return;\r
+\r
+//\r
+// don't scroll more than one tile per frame\r
+//\r
+       if (xscroll >= 0x100)\r
+       {\r
+               xscroll = 0xFF;\r
+       }\r
+       else if (xscroll <= -0x100)\r
+       {\r
+               xscroll = -0xFF;\r
+       }\r
+       if (yscroll >= 0x100)\r
+       {\r
+               yscroll = 0xFF;\r
+       }\r
+       else if (yscroll <= -0x100)\r
+       {\r
+               yscroll = -0xFF;\r
+       }\r
+       RF_Scroll(xscroll, yscroll);\r
+\r
+//\r
+// update limits for onscreen and inactivate checks\r
+//\r
+       originxtilemax = (originxtile + PORTTILESWIDE) - 1;\r
+       originytilemax = (originytile + PORTTILESHIGH) - 1;\r
+       inactivateleft = originxtile - INACTIVATEDIST;\r
+       if (inactivateleft < 0)\r
+       {\r
+               inactivateleft = 0;\r
+       }\r
+       inactivateright = originxtilemax + INACTIVATEDIST;\r
+       if (inactivateright < 0)\r
+       {\r
+               inactivateright = 0;\r
+       }\r
+       inactivatetop = originytile - INACTIVATEDIST;\r
+       if (inactivatetop < 0)\r
+       {\r
+               inactivatetop = 0;\r
+       }\r
+       inactivatebottom = originytilemax + INACTIVATEDIST;\r
+       if (inactivatebottom < 0)\r
+       {\r
+               inactivatebottom = 0;\r
+       }\r
+}\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+#############################################################################\r
+\r
+                                 The objarray data structure\r
+\r
+#############################################################################\r
+\r
+Objarray contains 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 spawns 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
+= InitObjArray\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 InitObjArray(void)\r
+{\r
+       Sint16 i;\r
+\r
+       for (i=0; i<MAXACTORS; i++)\r
+       {\r
+               objarray[i].prev = &objarray[i+1];\r
+               objarray[i].next = NULL;\r
+       }\r
+\r
+       objarray[MAXACTORS-1].prev = NULL;\r
+\r
+       objfreelist = &objarray[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
+       GetNewObj(false);\r
+       scoreobj = new;\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=========================\r
+=\r
+= GetNewObj\r
+=\r
+= Sets the global variable new to point to a free spot in objarray.\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 or\r
+= return a dummy object pointer that will never get used\r
+=\r
+= Returns -1 when list was full, otherwise returns 0.\r
+=\r
+=========================\r
+*/\r
+\r
+Sint16 GetNewObj(boolean usedummy)\r
+{\r
+       if (!objfreelist)\r
+       {\r
+               if (usedummy)\r
+               {\r
+                       new = &dummyobj;\r
+                       return -1;\r
+               }\r
+               Quit("GetNewObj: No free spots in objarray!");\r
+       }\r
+       new = objfreelist;\r
+       objfreelist = new->prev;\r
+       memset(new, 0, sizeof(*new));\r
+       if (lastobj)\r
+       {\r
+               lastobj->next = new;\r
+       }\r
+       new->prev = lastobj;    // new->next is allready NULL from memset\r
+\r
+       new->active = ac_yes;\r
+       new->needtoclip = cl_midclip;\r
+       lastobj = new;\r
+\r
+       objectcount++;\r
+       return 0;\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 *ob)\r
+{\r
+       if (ob == player)\r
+               Quit("RemoveObj: Tried to remove the player!");\r
+\r
+//\r
+// erase it from the refresh manager\r
+//\r
+       RF_RemoveSprite(&ob->sprite);\r
+       if (ob->obclass == stunnedobj)\r
+       {\r
+               RF_RemoveSprite((void **)&ob->temp3);\r
+       }\r
+\r
+//\r
+// fix the next object's back link\r
+//\r
+       if (ob == lastobj)\r
+       {\r
+               lastobj = ob->prev;\r
+       }\r
+       else\r
+       {\r
+               ob->next->prev = ob->prev;\r
+       }\r
+\r
+//\r
+// fix the previous object's forward link\r
+//\r
+       ob->prev->next = ob->next;\r
+\r
+//\r
+// add it back in to the free list\r
+//\r
+       ob->prev = objfreelist;\r
+       objfreelist = ob;\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= GivePoints\r
+=\r
+= Grants extra men at 20k,40k,80k,160k,320k\r
+=\r
+====================\r
+*/\r
+\r
+void GivePoints(Uint16 points)\r
+{\r
+       gamestate.score += points;\r
+       if (!DemoMode && gamestate.score >= gamestate.nextextra)\r
+       {\r
+               SD_PlaySound(SND_EXTRAKEEN);\r
+               gamestate.lives++;\r
+               gamestate.nextextra *= 2;\r
+       }\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+===================\r
+=\r
+= PollControls\r
+=\r
+===================\r
+*/\r
+\r
+void PollControls(void)\r
+{\r
+       IN_ReadControl(0, &c);\r
+       if (c.yaxis != -1)\r
+               upheld = false;\r
+\r
+       if (GravisGamepad && !DemoMode)\r
+       {\r
+               jumpbutton = GravisAction[ga_Jump];\r
+               pogobutton = GravisAction[ga_Pogo];\r
+               firebutton = GravisAction[ga_Fire];\r
+               if (!jumpbutton)\r
+                       jumpheld = false;\r
+               if (!pogobutton)\r
+                       pogoheld = false;\r
+               if (!firebutton)\r
+                       fireheld = false;\r
+       }\r
+       else if (oldshooting || DemoMode)\r
+       {\r
+               if (c.button0 && c.button1)\r
+               {\r
+                       firebutton = true;\r
+                       jumpbutton = pogobutton = jumpheld = pogoheld = false;\r
+               }\r
+               else\r
+               {\r
+                       firebutton = fireheld = false;\r
+                       if (c.button0)\r
+                       {\r
+                               jumpbutton = true;\r
+                       }\r
+                       else\r
+                       {\r
+                               jumpbutton = jumpheld = false;\r
+                       }\r
+                       if (c.button1)\r
+                       {\r
+                               if (oldfirecount <= 8)\r
+                               {\r
+                                       oldfirecount = oldfirecount + tics;\r
+                               }\r
+                               else\r
+                               {\r
+                                       pogobutton = true;\r
+                               }\r
+                       }\r
+                       else\r
+                       {\r
+                               if (oldfirecount != 0)\r
+                               {\r
+                                       pogobutton = true;\r
+                               }\r
+                               else\r
+                               {\r
+                                       pogobutton = pogoheld = false;\r
+                               }\r
+                               oldfirecount = 0;\r
+                       }\r
+               }\r
+       }\r
+       else\r
+       {\r
+               jumpbutton = c.button0;\r
+               pogobutton = c.button1;\r
+               firebutton = Keyboard[firescan];\r
+               if (!jumpbutton)\r
+                       jumpheld = false;\r
+               if (!pogobutton)\r
+                       pogoheld = false;\r
+               if (!firebutton)\r
+                       fireheld = false;\r
+       }\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+=================\r
+=\r
+= StopMusic\r
+=\r
+=================\r
+*/\r
+\r
+void StopMusic(void)\r
+{\r
+       Sint16 i;\r
+\r
+       SD_MusicOff();\r
+       for (i=0; i<LASTMUSIC; i++)\r
+       {\r
+               if (audiosegs[STARTMUSIC+i])\r
+               {\r
+#ifdef FIX_MUSIC_MEMORY_ISSUES\r
+                       //unlock any music blocks so that they can be purged\r
+                       MM_SetLock(&(memptr)audiosegs[STARTMUSIC+i], false);\r
+#endif\r
+                       MM_SetPurge(&(memptr)audiosegs[STARTMUSIC+i], PURGE_FIRST);\r
+               }\r
+       }\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+=================\r
+=\r
+= StartMusic\r
+=\r
+=================\r
+*/\r
+\r
+void StartMusic(Uint16 num)\r
+{\r
+       static Sint16 songs[] =\r
+       {\r
+#if defined KEEN4\r
+               SHADOWS_MUS,\r
+               KICKPANT_MUS,\r
+               OASIS_MUS,\r
+               OASIS_MUS,\r
+               TOOHOT_MUS,\r
+               TOOHOT_MUS,\r
+               KICKPANT_MUS,\r
+               OASIS_MUS,\r
+               VEGGIES_MUS,\r
+               VEGGIES_MUS,\r
+               VEGGIES_MUS,\r
+               TOOHOT_MUS,\r
+               TOOHOT_MUS,\r
+               TOOHOT_MUS,\r
+               TOOHOT_MUS,\r
+               TOOHOT_MUS,\r
+               TOOHOT_MUS,\r
+               VEGGIES_MUS,\r
+               OASIS_MUS,\r
+               -1\r
+#elif defined KEEN5\r
+               ROBOROCK_MUS,\r
+               WEDNESDY_MUS,\r
+               BREATHE_MUS,\r
+               SPHEREFUL_MUS,\r
+               TIGHTER_MUS,\r
+               SPHEREFUL_MUS,\r
+               TIGHTER_MUS,\r
+               SPHEREFUL_MUS,\r
+               TIGHTER_MUS,\r
+               SPHEREFUL_MUS,\r
+               TIGHTER_MUS,\r
+               SNOOPING_MUS,\r
+               FEARSOME_MUS,\r
+               BAGPIPES_MUS,\r
+               FANFARE_MUS,\r
+               SKATING_MUS,\r
+               ROCK_ME_MUS,\r
+               HAVING_T_MUS,\r
+               CAMEIN_MUS,\r
+               SHIKAIRE_MUS,\r
+#elif defined KEEN6\r
+               ALIENATE_MUS,\r
+               FASTER_MUS,\r
+               BRERTAR_MUS,\r
+               MAMSNAKE_MUS,\r
+               MAMSNAKE_MUS,\r
+               MAMSNAKE_MUS,\r
+               METAL_MUS,\r
+               TOFUTURE_MUS,\r
+               METAL_MUS,\r
+               BRERTAR_MUS,\r
+               FASTER_MUS,\r
+               TOFUTURE_MUS,\r
+               BRERTAR_MUS,\r
+               SPACFUNK_MUS,\r
+               SPACFUNK_MUS,\r
+               OMINOUS_MUS,\r
+               TOFUTURE_MUS,\r
+               WONDER_MUS,\r
+               WONDER_MUS,\r
+               WONDER_MUS\r
+#endif\r
+       };\r
+\r
+       Sint16 song;\r
+       boolean wasfaded;\r
+\r
+       if (num >= ARRAYLENGTH(songs) && num != 0xFFFF)\r
+       {\r
+               Quit("StartMusic() - bad level number");\r
+       }\r
+\r
+#ifdef FIX_MUSIC_MEMORY_ISSUES\r
+       StopMusic();\r
+#else\r
+       SD_MusicOff();\r
+#endif\r
+\r
+#ifdef KEEN4\r
+       if (num == 0xFFFF)\r
+       {\r
+               song = WONDER_MUS;\r
+       }\r
+       else\r
+       {\r
+               song = songs[num];\r
+       }\r
+#else\r
+       song = songs[num];\r
+#endif\r
+\r
+       if (song == -1 || MusicMode != smm_AdLib)\r
+       {\r
+               return;\r
+       }\r
+\r
+       MM_BombOnError(false);\r
+       CA_CacheAudioChunk(STARTMUSIC+song);\r
+       MM_BombOnError(true);\r
+       if (mmerror)\r
+       {\r
+               mmerror = false;\r
+               if (!DemoMode)\r
+               {\r
+                       US_CenterWindow(20, 8);\r
+                       PrintY += 20;\r
+                       US_CPrint("Insufficient memory\nfor background music!");\r
+                       VW_UpdateScreen();\r
+                       wasfaded = screenfaded;\r
+                       if (wasfaded)\r
+                       {\r
+                               VW_SetDefaultColors();\r
+                       }\r
+                       IN_ClearKeysDown();\r
+                       IN_UserInput(3*TickBase, false);\r
+                       if (wasfaded)\r
+                       {\r
+                               VW_FadeOut();\r
+                       }\r
+               }\r
+       }\r
+       else\r
+       {\r
+#ifdef FIX_MUSIC_MEMORY_ISSUES\r
+               //The current music should be locked, so the memory manager will not\r
+               //mess with it when compressing memory blocks in MM_SortMem().\r
+               MM_SetLock(&(memptr)audiosegs[STARTMUSIC+song], true);\r
+#endif\r
+               SD_StartMusic((MusicGroup far *)audiosegs[STARTMUSIC+song]);\r
+       }\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+===================\r
+=\r
+= PlayLoop\r
+=\r
+===================\r
+*/\r
+\r
+void PlayLoop(void)\r
+{\r
+       objtype *check;\r
+\r
+       StartMusic(gamestate.mapon);\r
+       fireheld = pogoheld = upheld = jumpheld = false;\r
+       ingame = true;\r
+       playstate = ex_stillplaying;\r
+       invincible = keenkilled = oldfirecount = 0;\r
+\r
+       CenterActor(player);\r
+\r
+       if (DemoMode)\r
+       {\r
+               US_InitRndT(false);\r
+       }\r
+       else\r
+       {\r
+               US_InitRndT(true);\r
+       }\r
+       TimeCount = lasttimecount = tics = 3;\r
+\r
+       do\r
+       {\r
+               PollControls();\r
+\r
+//\r
+// go through state changes and propose movements\r
+//\r
+               for (obj=player; obj; obj=obj->next)\r
+               {\r
+                       if (!obj->active && obj->tileright >= originxtile-1\r
+                               && obj->tileleft <= originxtilemax+1 && obj->tiletop <= originytilemax+1\r
+                               && obj->tilebottom >= originytile-1)\r
+                       {\r
+                               obj->needtoreact = true;\r
+                               obj->active = ac_yes;\r
+                       }\r
+                       if (obj->active)\r
+                       {\r
+                               if (obj->tileright < inactivateleft\r
+                                       || obj->tileleft > inactivateright\r
+                                       || obj->tiletop > inactivatebottom\r
+                                       || obj->tilebottom < inactivatetop)\r
+                               {\r
+                                       if (obj->active == ac_removable)\r
+                                       {\r
+                                               RemoveObj(obj);\r
+                                               continue;\r
+                                       }\r
+                                       else if (obj->active != ac_allways)\r
+                                       {\r
+                                               if (US_RndT() < tics*2 || screenfaded || loadedgame)\r
+                                               {\r
+                                                       RF_RemoveSprite(&obj->sprite);\r
+                                                       if (obj->obclass == stunnedobj)\r
+                                                               RF_RemoveSprite((void **)&obj->temp3);\r
+                                                       obj->active = ac_no;\r
+                                                       continue;\r
+                                               }\r
+                                       }\r
+                               }\r
+                               StateMachine(obj);\r
+                       }\r
+               }\r
+\r
+               if (gamestate.riding)\r
+               {\r
+                       HandleRiding(player);\r
+               }\r
+\r
+//\r
+// check for and handle collisions between objects\r
+//\r
+               for (obj=player; obj; obj=obj->next)\r
+               {\r
+                       if (obj->active)\r
+                       {\r
+                               for (check=obj->next; check; check=check->next)\r
+                               {\r
+                                       if (!check->active)\r
+                                       {\r
+                                               continue;\r
+                                       }\r
+                                       if (obj->right > check->left && obj->left < check->right\r
+                                               && obj->top < check->bottom && obj->bottom > check->top)\r
+                                       {\r
+                                               if (obj->state->contact)\r
+                                               {\r
+                                                       obj->state->contact(obj, check);\r
+                                               }\r
+                                               if (check->state->contact)\r
+                                               {\r
+                                                       check->state->contact(check, obj);\r
+                                               }\r
+                                               if (obj->obclass == nothing)    //useless -- obclass is NOT set to nothing by RemoveObj\r
+                                               {\r
+                                                       break;\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+\r
+//\r
+// check intiles\r
+//\r
+               if (mapon != 0)\r
+               {\r
+                       CheckInTiles(player);\r
+               }\r
+               else\r
+               {\r
+                       CheckWorldInTiles(player);\r
+               }\r
+\r
+//\r
+// react to whatever happened, and post sprites to the refresh manager\r
+//\r
+               for (obj=player; obj; obj=obj->next)\r
+               {\r
+                       if (!obj->active)\r
+                       {\r
+                               continue;\r
+                       }\r
+                       if (obj->tilebottom >= mapheight-1)\r
+                       {\r
+                               if (obj->obclass == keenobj)\r
+                               {\r
+                                       playstate = ex_died;\r
+                               }\r
+                               else\r
+                               {\r
+                                       RemoveObj(obj);\r
+                               }\r
+                               continue;\r
+                       }\r
+                       if (obj->needtoreact && obj->state->react)\r
+                       {\r
+                               obj->needtoreact = false;\r
+                               obj->state->react(obj);\r
+                       }\r
+               }\r
+\r
+//\r
+// scroll the screen and update the score box\r
+//\r
+#ifdef KEEN4\r
+               if (mapon != 0 && mapon != 17)\r
+#else\r
+               if (mapon != 0)\r
+#endif\r
+               {\r
+                       ScrollScreen(player);\r
+               }\r
+               else\r
+               {\r
+                       WorldScrollScreen(player);\r
+               }\r
+               UpdateScore(scoreobj);\r
+               if (loadedgame)\r
+               {\r
+                       loadedgame = false;\r
+               }\r
+\r
+//\r
+// update the screen and calculate the number of tics it took to execute\r
+// this cycle of events (for adaptive timing of next cycle)\r
+//\r
+               RF_Refresh();\r
+\r
+               if (invincible)\r
+               {\r
+                       if ((invincible = invincible - tics) < 0)\r
+                               invincible = 0;\r
+               }\r
+\r
+#ifdef KEEN6\r
+               if (groundslam)\r
+               {\r
+                       if ((groundslam = groundslam - tics) < 0)\r
+                               groundslam = 0;\r
+               }\r
+#endif\r
+//\r
+// single step debug mode\r
+//\r
+               if (singlestep)\r
+               {\r
+                       VW_WaitVBL(14); //reduces framerate to 5 fps on VGA or 4.3 fps on EGA cards\r
+                       lasttimecount = TimeCount;\r
+               }\r
+//\r
+// extra VBLs debug mode\r
+//\r
+               if (extravbls)\r
+               {\r
+                       VW_WaitVBL(extravbls);\r
+               }\r
+\r
+//\r
+// handle user inputs\r
+//\r
+               if (DemoMode == demo_Playback)\r
+               {\r
+                       if (!screenfaded && IN_IsUserInput())\r
+                       {\r
+                               playstate = ex_completed;\r
+                               if (LastScan != sc_F1)\r
+                               {\r
+                                       LastScan = sc_Space;\r
+                               }\r
+                       }\r
+               }\r
+               else if (DemoMode == demo_PlayDone)\r
+               {\r
+                       playstate = ex_completed;\r
+               }\r
+               else\r
+               {\r
+                       CheckKeys();\r
+               }\r
+\r
+//\r
+// E-N-D cheat\r
+//\r
+               if (Keyboard[sc_E] && Keyboard[sc_N] && Keyboard[sc_D])\r
+               {\r
+#if defined KEEN4\r
+                       gamestate.rescued = 7;\r
+                       playstate = ex_rescued;\r
+#elif defined KEEN5\r
+                       playstate = ex_qedbroke;\r
+#elif defined KEEN6\r
+                       playstate = ex_molly;\r
+#endif\r
+               }\r
+\r
+       } while (playstate == ex_stillplaying);\r
+\r
+       ingame = false;\r
+       StopMusic();\r
+}
\ No newline at end of file