+/* 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
+/*\r
+K4_SPEC.C\r
+=========\r
+\r
+Contains (in this order):\r
+\r
+- lump definition\r
+- "Star Wars" crawl text\r
+- level names & messages\r
+- ScanInfoPlane() - for spawning the level objects and marking required sprites\r
+- messages for Lindsey, Janitor, Oracle Members and more\r
+\r
+- actor states & implementation for swimming Keen\r
+\r
+*/\r
+\r
+#include "CK_DEF.H"\r
+\r
+enum {\r
+ CONTROLS_LUMP, // 0\r
+ KEEN_LUMP, // 1\r
+ SUGAR1_LUMP, // 2\r
+ SUGAR2_LUMP, // 3\r
+ SUGAR3_LUMP, // 4\r
+ SUGAR4_LUMP, // 5\r
+ SUGAR5_LUMP, // 6\r
+ SUGAR6_LUMP, // 7\r
+ ONEUP_LUMP, // 8\r
+ AMMO_LUMP, // 9\r
+ WOLRDKEEN_LUMP, // 10\r
+ SLUG_LUMP, // 11\r
+ MADMUSHROOM_LUMP, // 12\r
+ UNUSED1_LUMP, // 13\r
+ LINDSEY_LUMP, // 14\r
+ INCHWORM_LUMP, // 15\r
+ EATER_LUMP, // 16\r
+ COUNCIL_LUMP, // 17\r
+ EGGBIRD_LUMP, // 18\r
+ MIMROCK_LUMP, // 19\r
+ DOPEFISH_LUMP, // 20\r
+ SCHOOLFISH_LUMP, // 21\r
+ ARACHNUT_LUMP, // 22\r
+ SKYPEST_LUMP, // 23\r
+ WORMOUTH_LUMP, // 24\r
+ LICK_LUMP, // 25\r
+ PLATFORM_LUMP, // 26\r
+ BOUNDER_LUMP, // 27\r
+ THUNDERCLOUD_LUMP, // 28\r
+ BERKELOID_LUMP, // 29\r
+ KEYGEM_LUMP, // 30\r
+ DARTS_LUMP, // 31\r
+ SCUBAKEEN_LUMP, // 32\r
+ SPRITE_LUMP, // 33\r
+ MINE_LUMP, // 34\r
+ MOON_LUMP, // 35\r
+ EGG_LUMP, // 36\r
+ NUMLUMPS // 37\r
+};\r
+\r
+Uint16 lumpstart[NUMLUMPS] = {\r
+ CONTROLS_LUMP_START,\r
+ KEEN_LUMP_START,\r
+ SUGAR1_LUMP_START,\r
+ SUGAR2_LUMP_START,\r
+ SUGAR3_LUMP_START,\r
+ SUGAR4_LUMP_START,\r
+ SUGAR5_LUMP_START,\r
+ SUGAR6_LUMP_START,\r
+ ONEUP_LUMP_START,\r
+ AMMO_LUMP_START,\r
+ WORLDKEEN_LUMP_START,\r
+ SLUG_LUMP_START,\r
+ MADMUSHROOM_LUMP_START,\r
+ 0,\r
+ LINDSEY_LUMP_START,\r
+ INCHWORM_LUMP_START,\r
+ EATER_LUMP_START,\r
+ COUNCIL_LUMP_START,\r
+ EGGBIRD_LUMP_START,\r
+ MIMROCK_LUMP_START,\r
+ DOPEFISH_LUMP_START,\r
+ SCHOOLFISH_LUMP_START,\r
+ ARACHNUT_LUMP_START,\r
+ SKYPEST_LUMP_START,\r
+ WORMOUTH_LUMP_START,\r
+ LICK_LUMP_START,\r
+ PLATFORM_LUMP_START,\r
+ BOUNDER_LUMP_START,\r
+ THUNDERCLOUD_LUMP_START,\r
+ BERKELOID_LUMP_START,\r
+ KEYGEM_LUMP_START,\r
+ DARTS_LUMP_START,\r
+ SCUBAKEEN_LUMP_START,\r
+ SPRITE_LUMP_START,\r
+ MINE_LUMP_START,\r
+ MOON_LUMP_START,\r
+ EGG_LUMP_START\r
+};\r
+\r
+Uint16 lumpend[NUMLUMPS] = {\r
+ CONTROLS_LUMP_END,\r
+ KEEN_LUMP_END,\r
+ SUGAR1_LUMP_END,\r
+ SUGAR2_LUMP_END,\r
+ SUGAR3_LUMP_END,\r
+ SUGAR4_LUMP_END,\r
+ SUGAR5_LUMP_END,\r
+ SUGAR6_LUMP_END,\r
+ ONEUP_LUMP_END,\r
+ AMMO_LUMP_END,\r
+ WORLDKEEN_LUMP_END,\r
+ SLUG_LUMP_END,\r
+ MADMUSHROOM_LUMP_END,\r
+ 0,\r
+ LINDSEY_LUMP_END,\r
+ INCHWORM_LUMP_END,\r
+ EATER_LUMP_END,\r
+ COUNCIL_LUMP_END,\r
+ EGGBIRD_LUMP_END,\r
+ MIMROCK_LUMP_END,\r
+ DOPEFISH_LUMP_END,\r
+ SCHOOLFISH_LUMP_END,\r
+ ARACHNUT_LUMP_END,\r
+ SKYPEST_LUMP_END,\r
+ WORMOUTH_LUMP_END,\r
+ LICK_LUMP_END,\r
+ PLATFORM_LUMP_END,\r
+ BOUNDER_LUMP_END,\r
+ THUNDERCLOUD_LUMP_END,\r
+ BERKELOID_LUMP_END,\r
+ KEYGEM_LUMP_END,\r
+ DARTS_LUMP_END,\r
+ SCUBAKEEN_LUMP_END,\r
+ SPRITE_LUMP_END,\r
+ MINE_LUMP_END,\r
+ MOON_LUMP_END,\r
+ EGG_LUMP_END\r
+};\r
+\r
+boolean lumpneeded[NUMLUMPS];\r
+\r
+#if GRMODE == EGAGR\r
+\r
+char far swtext[] =\r
+ "Episode Four\n"\r
+ "\n"\r
+ "Secret of the Oracle\n"\r
+ "\n"\r
+ "After delivering a\n"\r
+ "crippling blow to the\n"\r
+ "plans of Mortimer\n"\r
+ "McMire and receiving\n"\r
+ "the praise of the\n"\r
+ "Vorticon race,\n"\r
+ "Commander Keen\n"\r
+ "returned to his home in\n"\r
+ "the suburbs.\n"\r
+ "\n"\r
+ "Here he was forced to\n"\r
+ "go to bed at an early\n"\r
+ "hour, and to eat mashed\n"\r
+ "potatoes.\n"\r
+ "\n"\r
+ "Months later, Billy\n"\r
+ "tinkered around with\n"\r
+ "his latest invention,\n"\r
+ "the Photachyon\n"\r
+ "Transceiver, or faster-\n"\r
+ "than-light radio. After\n"\r
+ "picking up a lot of bad\n"\r
+ "alien sitcoms, he\n"\r
+ "stumbled upon a strange\n"\r
+ "message of terrible\n"\r
+ "importance....\n";\r
+\r
+#endif\r
+\r
+char far l0n[] = "Shadowlands";\r
+char far l1n[] = "Border Village";\r
+char far l2n[] = "Slug Village";\r
+char far l3n[] = "The Perilous Pit";\r
+char far l4n[] = "Cave of the Descendents";\r
+char far l5n[] = "Chasm of Chills";\r
+char far l6n[] = "Crystalus";\r
+char far l7n[] = "Hillville";\r
+char far l8n[] = "Sand Yego";\r
+char far l9n[] = "Miragia";\r
+char far l10n[] = "Lifewater Oasis";\r
+char far l11n[] = "Pyramid of the Moons";\r
+char far l12n[] = "Pyramid of Shadows";\r
+char far l13n[] = "Pyramid of the\nGnosticene Ancients";\r
+char far l14n[] = "Pyramid of the Forbidden";\r
+char far l15n[] = "Isle of Tar";\r
+char far l16n[] = "Isle of Fire";\r
+char far l17n[] = "Well of Wishes";\r
+char far l18n[] = "Bean-with-Bacon\nMegarocket";\r
+\r
+char far l0e[] = "Keen enters the\nShadowlands";\r
+char far l1e[] = "Keen makes a run for\nthe Border Village";\r
+char far l2e[] = "Keen slips into\nSlug Village";\r
+char far l3e[] = "Keen plummets into\nthe The Perilous Pit"; // sic!\r
+char far l4e[] = "Keen plods down into\nthe Cave of the\nDescendents";\r
+char far l5e[] = "Keen shivers along\nthe Chasm of Chills";\r
+char far l6e[] = "Keen reflects upon\nentering Crystalus";\r
+char far l7e[] = "Keen stumbles upon\nHillville";\r
+char far l8e[] = "Keen grits his teeth\nand enters Sand Yego";\r
+char far l9e[] = "Keen disappears into\nMiragia";\r
+char far l10e[] = "Keen crawls into\nLifewater Oasis";\r
+char far l11e[] = "Keen backs into the\nPyramid of the Moons";\r
+char far l12e[] = "Keen move silently in\nthe Pyramid of Shadows"; // sic!\r
+char far l13e[] = "Keen reverently enters\nthe Pyramid of the\nGnosticene Ancients";\r
+char far l14e[] = "Keen hesitantly crosses\ninto the Pyramid of the\nForbidden";\r
+char far l15e[] = "Keen mucks along the\nIsle of Tar";\r
+char far l16e[] = "Keen blazes across the\nIsle of Fire";\r
+char far l17e[] = "Keen hopefully enters\nthe Well of Wishes";\r
+char far l18e[] = "Keen launches into the\nBean-with-Bacon\nMegarocket";\r
+\r
+char far *levelnames[GAMELEVELS] = {\r
+ l0n,\r
+ l1n,\r
+ l2n,\r
+ l3n,\r
+ l4n,\r
+ l5n,\r
+ l6n,\r
+ l7n,\r
+ l8n,\r
+ l9n,\r
+ l10n,\r
+ l11n,\r
+ l12n,\r
+ l13n,\r
+ l14n,\r
+ l15n,\r
+ l16n,\r
+ l17n,\r
+ l18n\r
+};\r
+\r
+char far *levelenter[GAMELEVELS] = {\r
+ l0e,\r
+ l1e,\r
+ l2e,\r
+ l3e,\r
+ l4e,\r
+ l5e,\r
+ l6e,\r
+ l7e,\r
+ l8e,\r
+ l9e,\r
+ l10e,\r
+ l11e,\r
+ l12e,\r
+ l13e,\r
+ l14e,\r
+ l15e,\r
+ l16e,\r
+ l17e,\r
+ l18e\r
+};\r
+\r
+Uint16 bonuslump[] = {\r
+ KEYGEM_LUMP, KEYGEM_LUMP, KEYGEM_LUMP, KEYGEM_LUMP,\r
+ SUGAR1_LUMP, SUGAR2_LUMP, SUGAR3_LUMP,\r
+ SUGAR4_LUMP, SUGAR5_LUMP, SUGAR6_LUMP,\r
+ ONEUP_LUMP, AMMO_LUMP\r
+};\r
+\r
+//==========================================================================\r
+\r
+/*\r
+==========================\r
+=\r
+= ScanInfoPlane\r
+=\r
+= Spawn all actors and mark down special places\r
+=\r
+==========================\r
+*/\r
+\r
+void ScanInfoPlane(void)\r
+{\r
+ objtype *ob;\r
+ Uint16 i, x, y, chunk;\r
+ Sint16 info;\r
+ Uint16 far *map;\r
+\r
+ InitObjArray(); // start spawning things with a clean slate\r
+\r
+ memset(lumpneeded, 0, sizeof(lumpneeded));\r
+ map = mapsegs[2];\r
+\r
+ for (y=0; y<mapheight; y++)\r
+ {\r
+ for (x=0; x<mapwidth; x++)\r
+ {\r
+ info = *map++;\r
+\r
+ if (info == 0)\r
+ continue;\r
+\r
+ switch (info)\r
+ {\r
+ case 1:\r
+ SpawnKeen(x, y, 1);\r
+ SpawnScore();\r
+ lumpneeded[KEEN_LUMP] = true;\r
+ CA_MarkGrChunk(SCOREBOXSPR);\r
+ break;\r
+\r
+ case 2:\r
+ SpawnKeen(x, y, -1);\r
+ SpawnScore();\r
+ lumpneeded[KEEN_LUMP] = true;\r
+ CA_MarkGrChunk(SCOREBOXSPR);\r
+ break;\r
+\r
+ case 3:\r
+ SpawnWorldKeen(x, y);\r
+ SpawnScore();\r
+ lumpneeded[WOLRDKEEN_LUMP] = true;\r
+ CA_MarkGrChunk(SCOREBOXSPR);\r
+ break;\r
+\r
+ case 4:\r
+ SpawnCouncil(x, y);\r
+ lumpneeded[COUNCIL_LUMP] = true;\r
+ break;\r
+\r
+ case 50:\r
+ if (gamestate.difficulty < gd_Hard)\r
+ break;\r
+ case 49:\r
+ if (gamestate.difficulty < gd_Normal)\r
+ break;\r
+ case 5:\r
+ SpawnBerkeloid(x, y);\r
+ lumpneeded[BERKELOID_LUMP] = true;\r
+ break;\r
+\r
+ case 6:\r
+ SpawnLindsey(x, y);\r
+ lumpneeded[LINDSEY_LUMP] = true;\r
+ break;\r
+\r
+ case 52:\r
+ if (gamestate.difficulty < gd_Hard)\r
+ break;\r
+ case 51:\r
+ if (gamestate.difficulty < gd_Normal)\r
+ break;\r
+ case 7:\r
+ SpawnWormMouth(x, y);\r
+ lumpneeded[WORMOUTH_LUMP] = true;\r
+ break;\r
+\r
+ case 46:\r
+ if (gamestate.difficulty < gd_Hard)\r
+ break;\r
+ case 45:\r
+ if (gamestate.difficulty < gd_Normal)\r
+ break;\r
+ case 8:\r
+ SpawnSkypest(x, y);\r
+ lumpneeded[SKYPEST_LUMP] = true;\r
+ break;\r
+\r
+ case 9:\r
+ SpawnCloudster(x, y);\r
+ lumpneeded[THUNDERCLOUD_LUMP] = true;\r
+ break;\r
+\r
+ case 10:\r
+ SpawnFoot(x, y);\r
+ lumpneeded[INCHWORM_LUMP] = true; // lump includes the foot sprite\r
+ // Note: The smoke sprites aren't actually required for the foot!\r
+ for (i=SMOKE1SPR; i<=SMOKE4SPR; i++)\r
+ {\r
+ CA_MarkGrChunk(i);\r
+ }\r
+ break;\r
+\r
+ case 11:\r
+ SpawnInchworm(x, y);\r
+ lumpneeded[INCHWORM_LUMP] = true;\r
+ for (i=SMOKE1SPR; i<=SMOKE4SPR; i++)\r
+ {\r
+ CA_MarkGrChunk(i);\r
+ }\r
+ break;\r
+\r
+ case 12:\r
+ SpawnBounder(x, y);\r
+ lumpneeded[BOUNDER_LUMP] = true;\r
+ break;\r
+\r
+ case 13:\r
+ SpawnEggbird(x, y);\r
+ lumpneeded[EGGBIRD_LUMP] = true;\r
+ lumpneeded[EGG_LUMP] = true;\r
+ break;\r
+\r
+ case 48:\r
+ if (gamestate.difficulty < gd_Hard)\r
+ break;\r
+ case 47:\r
+ if (gamestate.difficulty < gd_Normal)\r
+ break;\r
+ case 14:\r
+ SpawnLick(x, y);\r
+ lumpneeded[LICK_LUMP] = true;\r
+ break;\r
+\r
+ case 88:\r
+ if (gamestate.difficulty < gd_Hard)\r
+ break;\r
+ case 87:\r
+ if (gamestate.difficulty < gd_Normal)\r
+ break;\r
+ case 15:\r
+ SpawnDopefish(x, y);\r
+ lumpneeded[DOPEFISH_LUMP] = true;\r
+ break;\r
+\r
+ case 16:\r
+ SpawnSchoolfish(x, y);\r
+ lumpneeded[SCHOOLFISH_LUMP] = true;\r
+ break;\r
+\r
+ case 24:\r
+ if (gamestate.difficulty < gd_Hard)\r
+ break;\r
+ case 23:\r
+ if (gamestate.difficulty < gd_Normal)\r
+ break;\r
+ case 17:\r
+ SpawnPixie(x, y);\r
+ lumpneeded[SPRITE_LUMP] = true;\r
+ break;\r
+\r
+ case 18:\r
+ SpawnEater(x, y);\r
+ lumpneeded[EATER_LUMP] = true;\r
+ break;\r
+\r
+ case 19:\r
+ SpawnMimrock(x, y);\r
+ lumpneeded[MIMROCK_LUMP] = true;\r
+ break;\r
+\r
+ case 74:\r
+ if (gamestate.difficulty < gd_Hard)\r
+ break;\r
+ case 73:\r
+ if (gamestate.difficulty < gd_Normal)\r
+ break;\r
+ case 20:\r
+ SpawnArachnut(x, y);\r
+ lumpneeded[ARACHNUT_LUMP] = true;\r
+ break;\r
+\r
+ case 21:\r
+ SpawnMadMushroom(x, y);\r
+ lumpneeded[MADMUSHROOM_LUMP] = true;\r
+ break;\r
+\r
+ case 44:\r
+ if (gamestate.difficulty < gd_Hard)\r
+ break;\r
+ case 43:\r
+ if (gamestate.difficulty < gd_Normal)\r
+ break;\r
+ case 22:\r
+ SpawnSlug(x, y);\r
+ lumpneeded[SLUG_LUMP] = true;\r
+ break;\r
+\r
+ case 25:\r
+ RF_SetScrollBlock(x, y, 1);\r
+ break;\r
+\r
+ case 26:\r
+ RF_SetScrollBlock(x, y, 0);\r
+ break;\r
+\r
+ case 27:\r
+ case 28:\r
+ case 29:\r
+ case 30:\r
+ SpawnPlatform(x, y, info-27);\r
+ lumpneeded[PLATFORM_LUMP] = true;;\r
+ break;\r
+\r
+ case 32:\r
+ SpawnDropPlat(x, y);\r
+ lumpneeded[PLATFORM_LUMP] = true;\r
+ break;\r
+\r
+ case 33:\r
+ SpawnMiragia(x, y);\r
+ break;\r
+\r
+ case 34:\r
+ if (gamestate.ammo < 5)\r
+ {\r
+ SpawnBonus(x, y, 11);\r
+ lumpneeded[bonuslump[11]] = true;\r
+ }\r
+ break;\r
+\r
+ case 35:\r
+ SpawnScuba(x, y);\r
+ CA_MarkGrChunk(SCUBASPR);\r
+ break;\r
+\r
+ case 42:\r
+ SpawnSwimKeen(x, y);\r
+ SpawnScore();\r
+ lumpneeded[SCUBAKEEN_LUMP] = true;\r
+ //mark pickup shapes:\r
+ for (i=BONUS100SPR; i<=BONUSCLIPSPR; i++)\r
+ {\r
+ CA_MarkGrChunk(i);\r
+ }\r
+ CA_MarkGrChunk(SCOREBOXSPR);\r
+ break;\r
+\r
+ case 83:\r
+ case 84:\r
+ case 85:\r
+ case 86:\r
+ if (gamestate.difficulty < gd_Hard)\r
+ break;\r
+ SpawnDartShooter(x, y, info-83);\r
+ lumpneeded[DARTS_LUMP] = true;\r
+ break;\r
+\r
+ case 79:\r
+ case 80:\r
+ case 81:\r
+ case 82:\r
+ if (gamestate.difficulty < gd_Normal)\r
+ break;\r
+ SpawnDartShooter(x, y, info-79);\r
+ lumpneeded[DARTS_LUMP] = true;\r
+ break;\r
+\r
+ case 53:\r
+ case 54:\r
+ case 55:\r
+ case 56:\r
+ SpawnDartShooter(x, y, info-53);\r
+ lumpneeded[DARTS_LUMP] = true;\r
+ break;\r
+\r
+ case 57:\r
+ case 58:\r
+ case 59:\r
+ case 60:\r
+ case 61:\r
+ case 62:\r
+ case 63:\r
+ case 64:\r
+ case 65:\r
+ case 66:\r
+ case 67:\r
+ case 68:\r
+ SpawnBonus(x, y, info-57);\r
+ lumpneeded[bonuslump[info-57]] = true;\r
+ break;\r
+\r
+ case 69:\r
+ case 70:\r
+ case 71:\r
+ case 72:\r
+ SpawnMine(x, y, info-69);\r
+ lumpneeded[MINE_LUMP] = true;\r
+ break;\r
+\r
+ case 75:\r
+ lumpneeded[MOON_LUMP] = true;\r
+ break;\r
+\r
+ case 78:\r
+ if (gamestate.difficulty < gd_Hard)\r
+ break;\r
+ case 77:\r
+ if (gamestate.difficulty < gd_Normal)\r
+ break;\r
+ case 76:\r
+ SpawnEggbirdOut(x, y);\r
+ lumpneeded[EGGBIRD_LUMP] = true;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ for (ob = player; ob; ob = ob->next)\r
+ {\r
+ if (ob->active != ac_allways)\r
+ ob->active = ac_no;\r
+ }\r
+\r
+ for (i = 0; i < NUMLUMPS; i++)\r
+ {\r
+ if (lumpneeded[i])\r
+ {\r
+ for (chunk = lumpstart[i]; chunk <= lumpend[i]; chunk++)\r
+ {\r
+ CA_MarkGrChunk(chunk);\r
+ }\r
+ }\r
+ }\r
+}\r
+\r
+//============================================================================\r
+\r
+/*\r
+===========================\r
+=\r
+= PrincessLindsey\r
+=\r
+===========================\r
+*/\r
+\r
+char *lindseytext[2] =\r
+{\r
+ "There's gear to help\n"\r
+ "you swim in Three-Tooth\n"\r
+ "Lake. It is hidden in\n"\r
+ "Miragia.\n"\r
+ ,\r
+ "The way to the Pyramid\n"\r
+ "of the Forbidden lies\n"\r
+ "under the Pyramid of\n"\r
+ "Moons.\n"\r
+};\r
+\r
+char *klindseytext[2] =\r
+{\r
+ "Thanks, your Highness!"\r
+ ,\r
+ "Thanks for the\n"\r
+ "mysterious clue,\n"\r
+ "Princess!\n"\r
+};\r
+\r
+void PrincessLindsey(void)\r
+{\r
+ SD_WaitSoundDone();\r
+ StopMusic();\r
+ CA_UpLevel();\r
+ CA_MarkGrChunk(LINDSEYPIC);\r
+ CA_MarkGrChunk(KEENTALK1PIC);\r
+ CA_MarkGrChunk(KEENTALK2PIC);\r
+ CA_CacheMarks(NULL);\r
+ VW_FixRefreshBuffer();\r
+\r
+ US_CenterWindow(26, 8);\r
+ VWB_DrawPic(WindowX, WindowY, LINDSEYPIC);\r
+ PrintY += 6;\r
+ WindowW -= 48;\r
+ WindowX += 48;\r
+ US_CPrint("Princess Lindsey says:\n");\r
+ if (mapon == 7)\r
+ {\r
+ US_CPrint(lindseytext[0]);\r
+ }\r
+ else\r
+ {\r
+ US_CPrint(lindseytext[1]);\r
+ }\r
+ VW_UpdateScreen();\r
+ SD_PlaySound(SND_MAKEFOOT);\r
+ VW_WaitVBL(60);\r
+ IN_ClearKeysDown();\r
+ IN_Ack();\r
+\r
+ US_CenterWindow(26, 8);\r
+ VWB_DrawPic(WindowX+WindowW-48, WindowY, KEENTALK1PIC);\r
+ WindowW -= 48;\r
+ PrintY += 12;\r
+ if (mapon == 7)\r
+ {\r
+ US_CPrint(klindseytext[0]);\r
+ }\r
+ else\r
+ {\r
+ US_CPrint(klindseytext[1]);\r
+ }\r
+ VW_UpdateScreen();\r
+ VW_WaitVBL(30);\r
+ IN_ClearKeysDown();\r
+ IN_Ack();\r
+\r
+ VWB_DrawPic(WindowX+WindowW, WindowY, KEENTALK2PIC);\r
+ VW_UpdateScreen();\r
+ VW_WaitVBL(30);\r
+ IN_ClearKeysDown();\r
+ IN_Ack();\r
+\r
+ CA_DownLevel();\r
+ StartMusic(gamestate.mapon);\r
+\r
+ //reset scorebox (sprite may have been re-cached by CA_DownLevel)\r
+ scoreobj->temp2 = -1;\r
+ scoreobj->temp1 = -1;\r
+ scoreobj->temp3 = -1;\r
+ scoreobj->temp4 = -1;\r
+}\r
+\r
+//============================================================================\r
+\r
+/*\r
+===========================\r
+=\r
+= RescueJanitor\r
+=\r
+===========================\r
+*/\r
+\r
+char far jantext1[] =\r
+ "Thanks for going to all\n"\r
+ "that trouble, but I'm\n"\r
+ "just the janitor for the\n"\r
+ "High Council.";\r
+\r
+char far jantext2[] =\r
+ "I tried to tell the\n"\r
+ "Shikadi that but they\n"\r
+ "just wouldn't listen...";\r
+\r
+char far keenjantext[] =\r
+ "This had better\n"\r
+ "be a joke.";\r
+\r
+char far jantext3[] =\r
+ "Sorry. You aren't\n"\r
+ "mad, are you?";\r
+\r
+void RescueJanitor(void)\r
+{\r
+ char str[200];\r
+\r
+ SD_WaitSoundDone();\r
+ CA_UpLevel();\r
+ CA_MarkGrChunk(ORACLEPIC);\r
+ CA_MarkGrChunk(KEENTALK1PIC);\r
+ CA_MarkGrChunk(KEENMADPIC);\r
+ CA_CacheMarks(NULL);\r
+ VW_FixRefreshBuffer();\r
+ StartMusic(-1);\r
+ \r
+ US_CenterWindow(26, 8);\r
+ VWB_DrawPic(WindowX, WindowY, ORACLEPIC);\r
+ PrintY += 6;\r
+ WindowW -= 48;\r
+ WindowX += 48;\r
+ _fstrcpy(str, jantext1);\r
+ US_CPrint(str);\r
+ VW_UpdateScreen();\r
+ VW_WaitVBL(60);\r
+ IN_ClearKeysDown();\r
+ IN_Ack();\r
+\r
+ US_CenterWindow(26, 8);\r
+ VWB_DrawPic(WindowX, WindowY, ORACLEPIC);\r
+ PrintY += 6;\r
+ WindowW -= 48;\r
+ WindowX += 48;\r
+ _fstrcpy(str, jantext2);\r
+ US_CPrint(str);\r
+ VW_UpdateScreen();\r
+ VW_WaitVBL(60);\r
+ IN_ClearKeysDown();\r
+ IN_Ack();\r
+\r
+ US_CenterWindow(26, 8);\r
+ VWB_DrawPic(WindowX+WindowW-48, WindowY, KEENTALK1PIC);\r
+ WindowW -= 48;\r
+ PrintY += 12;\r
+ _fstrcpy(str, keenjantext);\r
+ US_CPrint(str);\r
+ VW_UpdateScreen();\r
+ VW_WaitVBL(60);\r
+ IN_ClearKeysDown();\r
+ IN_Ack();\r
+\r
+ US_CenterWindow(26, 8);\r
+ VWB_DrawPic(WindowX, WindowY, ORACLEPIC);\r
+ PrintY += 6;\r
+ WindowW -= 48;\r
+ WindowX += 48;\r
+ _fstrcpy(str, jantext3);\r
+ US_CPrint(str);\r
+ VW_UpdateScreen();\r
+ VW_WaitVBL(60);\r
+ IN_ClearKeysDown();\r
+ IN_Ack();\r
+\r
+ US_CenterWindow(26, 8);\r
+ VWB_DrawPic(WindowX+WindowW-48, WindowY, KEENTALK1PIC);\r
+ VWB_DrawPic(WindowX+WindowW-40, WindowY+24, KEENMADPIC);\r
+ VW_UpdateScreen();\r
+ VW_WaitVBL(30);\r
+ IN_ClearKeysDown();\r
+ IN_Ack();\r
+\r
+ StopMusic();\r
+ CA_DownLevel();\r
+ StartMusic(gamestate.mapon);\r
+\r
+ //BUG: scorebox needs to be reset here (sprite may have been re-cached by CA_DownLevel)\r
+}\r
+\r
+//============================================================================\r
+\r
+/*\r
+===========================\r
+=\r
+= CanitSwim\r
+=\r
+===========================\r
+*/\r
+\r
+void CantSwim(void)\r
+{\r
+ SD_WaitSoundDone();\r
+ CA_UpLevel(); // kinda useless without CA_CacheMarks or CA_SetGrPurge\r
+ // BUG: haven't made anything purgable here, caching the pic may cause an "out of memory" crash\r
+ CA_CacheGrChunk(KEENTALK1PIC);\r
+\r
+ US_CenterWindow(26, 8);\r
+ VWB_DrawPic(WindowX+WindowW-48, WindowY, KEENTALK1PIC);\r
+ WindowW -= 48;\r
+ PrintY += 12;\r
+ US_CPrint("I can't swim!");\r
+ VW_UpdateScreen();\r
+ VW_WaitVBL(30);\r
+ IN_ClearKeysDown();\r
+ IN_Ack();\r
+ CA_DownLevel();\r
+\r
+ //Note: scorebox sprite has not been re-cached here (didn't use CA_CacheMarks or anything else that would have made the sprite purgable)\r
+}\r
+\r
+//============================================================================\r
+\r
+/*\r
+===========================\r
+=\r
+= GotScuba\r
+=\r
+===========================\r
+*/\r
+\r
+void GotScuba(void)\r
+{\r
+ SD_WaitSoundDone();\r
+ CA_UpLevel();\r
+ CA_MarkGrChunk(KEENTALK1PIC);\r
+ CA_MarkGrChunk(KEENTALK2PIC);\r
+ CA_CacheMarks(NULL);\r
+\r
+ US_CenterWindow(26, 8);\r
+ VWB_DrawPic(WindowX+WindowW-48, WindowY, KEENTALK1PIC);\r
+ WindowW -= 48;\r
+ PrintY += 12;\r
+ US_CPrint(\r
+ "Cool! I can breathe\n"\r
+ "under water now!"\r
+ );\r
+ VW_UpdateScreen();\r
+ VW_WaitVBL(30);\r
+ IN_ClearKeysDown();\r
+ IN_Ack();\r
+\r
+ VWB_DrawPic(WindowX+WindowW, WindowY, KEENTALK2PIC);\r
+ VW_UpdateScreen();\r
+ VW_WaitVBL(30);\r
+ IN_ClearKeysDown();\r
+ IN_Ack();\r
+\r
+ CA_DownLevel();\r
+\r
+ //Note: scorebox sprite may have been re-cached by CA_DownLevel, but the level ends after this anyway\r
+}\r
+\r
+//============================================================================\r
+\r
+/*\r
+===========================\r
+=\r
+= RescuedMember\r
+=\r
+===========================\r
+*/\r
+\r
+char *keentext[] = {\r
+ "No sweat, oh guardian\n"\r
+ "of wisdom!"\r
+ ,\r
+ "Sounds like a plan,\n"\r
+ "bearded one!"\r
+ ,\r
+ "No problemo."\r
+ ,\r
+ "Great. You know, you\n"\r
+ "look a lot like the\n"\r
+ "last guy I rescued..."\r
+ ,\r
+ "Good idea, Gramps."\r
+ ,\r
+ "May the road rise\n"\r
+ "to meet your feet,\n"\r
+ "Mr. Member."\r
+ ,\r
+ "Wise plan of action,\n"\r
+ "your ancientness."\r
+ ,\r
+ "You're the last one,\n"\r
+ "fella. Let's both\n"\r
+ "get back to the\n"\r
+ "Oracle chamber!"\r
+};\r
+\r
+void RescuedMember(void)\r
+{\r
+ SD_WaitSoundDone();\r
+ CA_UpLevel();\r
+ CA_MarkGrChunk(ORACLEPIC);\r
+ CA_MarkGrChunk(KEENTALK1PIC);\r
+ CA_MarkGrChunk(KEENTALK2PIC);\r
+ CA_CacheMarks(NULL);\r
+ StartMusic(-1);\r
+ VW_FixRefreshBuffer();\r
+\r
+ US_CenterWindow(26, 8);\r
+ VWB_DrawPic(WindowX, WindowY, ORACLEPIC);\r
+ PrintY += 6;\r
+ WindowW -= 48;\r
+ WindowX += 48;\r
+ if (mapon == 17)\r
+ {\r
+ US_CPrint(\r
+ "Ggoh thig you sogh mg\n"\r
+ "fgor regscuing mgge!\n"\r
+ "I'gll regur tgo the\n"\r
+ "Goracle chagber\n"\r
+ "igmediatggely. Blub."\r
+ );\r
+ }\r
+ else\r
+ {\r
+ US_CPrint(\r
+ "Oh thank you so much\n"\r
+ "for rescuing me!\n"\r
+ "I'll return to the\n"\r
+ "Oracle chamber\n"\r
+ "immediately."\r
+ );\r
+ }\r
+ VW_UpdateScreen();\r
+ VW_WaitVBL(60);\r
+ IN_ClearKeysDown();\r
+ IN_Ack();\r
+\r
+ US_CenterWindow(26, 8);\r
+ VWB_DrawPic(WindowX+WindowW-48, WindowY, KEENTALK1PIC);\r
+ WindowW -= 48;\r
+ PrintY += 12;\r
+ US_CPrint(keentext[gamestate.rescued]);\r
+ VW_UpdateScreen();\r
+ VW_WaitVBL(30);\r
+ IN_ClearKeysDown();\r
+ IN_Ack();\r
+\r
+ VWB_DrawPic(WindowX+WindowW, WindowY, KEENTALK2PIC);\r
+ VW_UpdateScreen();\r
+ VW_WaitVBL(30);\r
+ IN_ClearKeysDown();\r
+ IN_Ack();\r
+\r
+ gamestate.rescued++;\r
+ CA_DownLevel();\r
+ StopMusic();\r
+\r
+ //Note: scorebox sprite may have been re-cached by CA_DownLevel, but the level ends after this anyway\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+ SWIMMING KEEN\r
+\r
+temp4 = counter for spawning bubbles\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_keenswimslow1 = {SCUBAKEENL1SPR, SCUBAKEENR1SPR, stepthink, false, false, 50, 0, 0, T_KeenSwimSlow, C_KeenSwim, R_KeenSwim, &s_keenswimslow2};\r
+statetype s_keenswimslow2 = {SCUBAKEENL2SPR, SCUBAKEENR2SPR, stepthink, false, false, 50, 0, 0, T_KeenSwimSlow, C_KeenSwim, R_KeenSwim, &s_keenswimslow1};\r
+statetype s_keenswim1 = {SCUBAKEENL1SPR, SCUBAKEENR1SPR, stepthink, false, false, 50, 0, 0, T_KeenSwim, C_KeenSwim, R_KeenSwim, &s_keenswimslow2};\r
+statetype s_keenswim2 = {SCUBAKEENL2SPR, SCUBAKEENR2SPR, stepthink, false, false, 50, 0, 0, T_KeenSwim, C_KeenSwim, R_KeenSwim, &s_keenswimslow1};\r
+//Note: the die states for swimming Keen are in CK_KEEN.C and K4_ACT3.C (dopefish section)\r
+\r
+statetype s_kbubble1 = {SMALLBUBBLE1SPR, SMALLBUBBLE1SPR, think, false, false, 20, 0, 24, T_Bubble, NULL, R_Draw, &s_kbubble1};\r
+statetype s_kbubble2 = {SMALLBUBBLE2SPR, SMALLBUBBLE2SPR, think, false, false, 20, 0, 24, T_Bubble, NULL, R_Draw, &s_kbubble2};\r
+statetype s_kbubble3 = {SMALLBUBBLE3SPR, SMALLBUBBLE3SPR, think, false, false, 20, 0, 24, T_Bubble, NULL, R_Draw, &s_kbubble3};\r
+statetype s_kbubble4 = {SMALLBUBBLE4SPR, SMALLBUBBLE4SPR, think, false, false, 20, 0, 24, T_Bubble, NULL, R_Draw, &s_kbubble4};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnSwimKeen\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnSwimKeen(Sint16 x, Sint16 y)\r
+{\r
+ player->obclass = keenobj;\r
+ player->active = ac_allways;\r
+ player->priority = 1;\r
+ player->x = CONVERT_TILE_TO_GLOBAL(x);\r
+ player->y = CONVERT_TILE_TO_GLOBAL(y);\r
+ player->xdir = 1;\r
+ player->ydir = 1;\r
+ player->needtoclip = cl_fullclip;\r
+ NewState(player, &s_keenswimslow1);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnKbubble\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnKbubble(objtype *ob)\r
+{\r
+ ob->temp4 = 0;\r
+ GetNewObj(true);\r
+ if (ob->xdir == -1)\r
+ {\r
+ new->x = ob->x;\r
+ }\r
+ else\r
+ {\r
+ new->x = ob->x + 24*PIXGLOBAL;\r
+ }\r
+ new->y = ob->y;\r
+ new->obclass = inertobj;\r
+ new->priority = 3;\r
+ new->active = ac_removable;\r
+ new->needtoclip = cl_noclip;\r
+ new->yspeed = -24;\r
+ new->xspeed = 4;\r
+ switch (US_RndT() / 64)\r
+ {\r
+ case 0:\r
+ NewState(new, &s_kbubble1);\r
+ break;\r
+ case 1:\r
+ NewState(new, &s_kbubble2);\r
+ break;\r
+ case 2:\r
+ NewState(new, &s_kbubble3);\r
+ break;\r
+ case 3:\r
+ NewState(new, &s_kbubble4);\r
+ break;\r
+ }\r
+ SD_PlaySound(SND_BLUB);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_KeenSwimSlow\r
+=\r
+===========================\r
+*/\r
+\r
+void T_KeenSwimSlow(objtype *ob)\r
+{\r
+ Sint32 i;\r
+ Sint16 vx, vy, xc, yc;\r
+\r
+ xc = ob->xspeed < 0;\r
+ yc = ob->yspeed < 4;\r
+\r
+ ob->temp4 = ob->temp4 + tics;\r
+ if (ob->temp4 > 60)\r
+ SpawnKbubble(ob);\r
+\r
+ if (jumpbutton && !jumpheld)\r
+ {\r
+ jumpheld = true;\r
+ if (c.xaxis)\r
+ ob->xspeed = c.xaxis * 18;\r
+ if (c.yaxis)\r
+ ob->yspeed = c.yaxis * 18;\r
+ ob->state = ob->state->nextstate;\r
+ }\r
+ if (c.xaxis)\r
+ ob->xdir = c.xaxis;\r
+\r
+ for (i = lasttimecount-tics; i < lasttimecount; i++)\r
+ {\r
+ if ((i & 7) == 0)\r
+ {\r
+ if (ob->xspeed > 12)\r
+ {\r
+ vx = -3;\r
+ }\r
+ else if (ob->xspeed > 0)\r
+ {\r
+ vx = -1;\r
+ }\r
+ else if (ob->xspeed > -12)\r
+ {\r
+ vx = 1;\r
+ }\r
+ else\r
+ {\r
+ vx = 3;\r
+ }\r
+ vx += c.xaxis;\r
+ vx += c.xaxis;\r
+ ob->xspeed += vx;\r
+\r
+ if (c.xaxis == 0 && (ob->xspeed < 0) != xc)\r
+ ob->xspeed = 0;\r
+\r
+ if (ob->yspeed > 12)\r
+ {\r
+ vy = -3;\r
+ }\r
+ else if (ob->yspeed > 4)\r
+ {\r
+ vy = -1;\r
+ }\r
+ else if (ob->yspeed > -12)\r
+ {\r
+ vy = 1;\r
+ }\r
+ else\r
+ {\r
+ vy = 3;\r
+ }\r
+ vy += c.yaxis;\r
+ vy += c.yaxis;\r
+ ob->yspeed += vy;\r
+\r
+ if (c.yaxis == 0 && ob->yspeed > 4 && yc)\r
+ ob->yspeed = 0;\r
+ }\r
+ xtry += ob->xspeed;\r
+ ytry += ob->yspeed;\r
+ }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_KeenSwim\r
+=\r
+===========================\r
+*/\r
+\r
+void T_KeenSwim(objtype *ob) //never actually used\r
+{\r
+ ob->temp4 = ob->temp4 + tics;\r
+ if (ob->temp4 > 60)\r
+ SpawnKbubble(ob);\r
+\r
+ if (jumpbutton && !jumpheld)\r
+ {\r
+ jumpheld = true;\r
+ ob->xspeed = c.xaxis * 18;\r
+ if (c.yaxis)\r
+ ob->yspeed = c.yaxis * 18;\r
+\r
+ if (ob->state == &s_keenswim1)\r
+ {\r
+ ob->state = &s_keenswim2;\r
+ }\r
+ else\r
+ {\r
+ ob->state = &s_keenswim1;\r
+ }\r
+ }\r
+\r
+ xtry = xtry + ob->xspeed * tics;\r
+ ytry = ytry + ob->yspeed * tics;\r
+ if (xtry > 0)\r
+ {\r
+ ob->xdir = 1;\r
+ }\r
+ else if (xtry < 0)\r
+ {\r
+ ob->xdir = -1;\r
+ }\r
+\r
+ ytry = ytry + tics*4;\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_KeenSwim\r
+=\r
+===========================\r
+*/\r
+\r
+void C_KeenSwim(objtype *ob, objtype *hit)\r
+{\r
+ switch (hit->obclass)\r
+ {\r
+ case bonusobj:\r
+ switch (hit->temp1)\r
+ {\r
+ case 0:\r
+ case 1:\r
+ case 2:\r
+ case 3:\r
+ case 4:\r
+ case 5:\r
+ case 6:\r
+ case 7:\r
+ case 8:\r
+ case 9:\r
+ case 10:\r
+ case 11:\r
+ SD_PlaySound(bonussound[hit->temp1]);\r
+ hit->obclass = inertobj;\r
+ hit->priority = 3;\r
+ hit->shapenum = bonussprite[hit->temp1];\r
+ GivePoints(bonuspoints[hit->temp1]);\r
+ if (hit->temp1 < 4)\r
+ {\r
+ gamestate.keys[hit->temp1]++;\r
+ }\r
+ else if (hit->temp1 == 10)\r
+ {\r
+ gamestate.lives++;\r
+ }\r
+ else if (hit->temp1 == 11)\r
+ {\r
+ gamestate.ammo += shotsinclip[gamestate.difficulty];\r
+ }\r
+ ChangeState(hit, &s_bonusrise);\r
+ break;\r
+ }\r
+ break;\r
+\r
+ case oracleobj:\r
+ playstate = ex_rescued;\r
+ break;\r
+ }\r
+ ob++; // shut up compiler\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_KeenSwim\r
+=\r
+===========================\r
+*/\r
+\r
+void R_KeenSwim(objtype *ob)\r
+{\r
+ if (ob->hiteast && ob->xspeed < 0 || ob->hitwest && ob->xspeed > 0)\r
+ ob->xspeed = 0;\r
+\r
+ if (ob->hitnorth && ob->yspeed > 0 || ob->hitsouth && ob->yspeed < 0)\r
+ ob->yspeed = 0;\r
+\r
+ RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}\r