--- /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
+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