1 /* Reconstructed Commander Keen 4-6 Source Code
\r
2 * Copyright (C) 2021 K1n9_Duk3
\r
4 * This file is loosely based on:
\r
5 * Keen Dreams Source Code
\r
6 * Copyright (C) 2014 Javier M. Chavez
\r
8 * This program is free software; you can redistribute it and/or modify
\r
9 * it under the terms of the GNU General Public License as published by
\r
10 * the Free Software Foundation; either version 2 of the License, or
\r
11 * (at your option) any later version.
\r
13 * This program is distributed in the hope that it will be useful,
\r
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
16 * GNU General Public License for more details.
\r
18 * You should have received a copy of the GNU General Public License along
\r
19 * with this program; if not, write to the Free Software Foundation, Inc.,
\r
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\r
27 Contains (in this order):
\r
30 - "Star Wars" crawl text
\r
31 - level names & messages
\r
32 - ScanInfoPlane() - for spawning the level objects and marking required sprites
\r
33 - stunned state for Keen
\r
34 - code to flip the big yellow switches
\r
35 - messages for sandwich, rope and passcard
\r
52 WORLDKEEN_LUMP, // 11
\r
55 RBLOOGLET_LUMP, // 14
\r
56 YBLOOGLET_LUMP, // 15
\r
57 BBLOOGLET_LUMP, // 16
\r
58 GBLOOGLET_LUMP, // 17
\r
59 PLATFORM_LUMP, // 18
\r
64 BLOOGUARD_LUMP, // 23
\r
67 BIPSQUISHED_LUMP, // 26
\r
70 ORBATRIX_LUMP, // 29
\r
74 SANDWICH_LUMP, // 33
\r
76 PASSCARD_LUMP, // 35
\r
79 NUMLUMPS=40 // Keen 6 has 3 unused lumps at the end
\r
82 Uint16 lumpstart[NUMLUMPS] = {
\r
83 CONTROLS_LUMP_START,
\r
94 WORLDKEEN_LUMP_START,
\r
97 RBLOOGLET_LUMP_START,
\r
98 YBLOOGLET_LUMP_START,
\r
99 BBLOOGLET_LUMP_START,
\r
100 GBLOOGLET_LUMP_START,
\r
101 PLATFORM_LUMP_START,
\r
105 BABOBBA_LUMP_START,
\r
106 BLOOGUARD_LUMP_START,
\r
109 BIPSQUISHED_LUMP_START,
\r
110 BIPSHIP_LUMP_START,
\r
111 NOSPIKE_LUMP_START,
\r
112 ORBATRIX_LUMP_START,
\r
113 CEILICK_LUMP_START,
\r
116 SANDWICH_LUMP_START,
\r
118 PASSCARD_LUMP_START,
\r
122 Uint16 lumpend[NUMLUMPS] = {
\r
134 WORLDKEEN_LUMP_END,
\r
137 RBLOOGLET_LUMP_END,
\r
138 YBLOOGLET_LUMP_END,
\r
139 BBLOOGLET_LUMP_END,
\r
140 GBLOOGLET_LUMP_END,
\r
146 BLOOGUARD_LUMP_END,
\r
149 BIPSQUISHED_LUMP_END,
\r
162 boolean lumpneeded[NUMLUMPS];
\r
164 #if GRMODE == EGAGR
\r
166 char far swtext[] =
\r
172 "While out in his\n"
\r
173 "backyard clubhouse,\n"
\r
174 "Billy's baby sitter\n"
\r
175 "Molly calls him for\n"
\r
176 "dinner. He continues\n"
\r
177 "working on his new\n"
\r
178 "wrist computer.\n"
\r
180 "Suddenly, there is a\n"
\r
181 "loud noise outside.\n"
\r
183 "Rushing out, Keen finds\n"
\r
184 "his baby sitter gone\n"
\r
185 "and a note on a patch\n"
\r
186 "of scorched grass. The\n"
\r
187 "Bloogs of Fribbulus Xax\n"
\r
188 "are going to make a\n"
\r
189 "meal out of Molly!\n"
\r
191 "You've got to rescue\n"
\r
192 "her, because your\n"
\r
193 "parents will never\n"
\r
194 "believe you when you\n"
\r
197 "\"Aliens Ate My\n"
\r
198 "Baby Sitter!\"\n";
\r
202 char far l0n[] = "Fribbulus Xax";
\r
203 char far l1n[] = "Bloogwaters\nCrossing";
\r
204 char far l2n[] = "Guard Post One";
\r
205 char far l3n[] = "First Dome\nof Darkness";
\r
206 char far l4n[] = "Second Dome\nof Darkness";
\r
207 char far l5n[] = "The Bloogdome";
\r
208 char far l6n[] = "Bloogton Mfg.,\nIncorporated";
\r
209 char far l7n[] = "Bloogton Tower";
\r
210 char far l8n[] = "Bloogfoods, Inc.";
\r
211 char far l9n[] = "Guard Post Two";
\r
212 char far l10n[] = "Bloogville";
\r
213 char far l11n[] = "BASA";
\r
214 char far l12n[] = "Guard Post Three";
\r
215 char far l13n[] = "Bloogbase Rec\nDistrict";
\r
216 char far l14n[] = "Bloogbase Mgmt.\nDistrict";
\r
217 char far l15n[] = "Bloog Control Center";
\r
218 char far l16n[] = "Blooglab";
\r
219 char far l17n[] = "Bean-with-Bacon\nMegarocket";
\r
220 char far l18n[] = "High Scores";
\r
222 char far l0e[] = "Keen attacks\nFribbulus Xax";
\r
223 char far l1e[] = "Keen hops across\nBloogwaters\nCrossing";
\r
224 char far l2e[] = "Keen fights his way\nthrough Guard Post One";
\r
225 char far l3e[] = "Keen crosses into the\nFirst Dome of Darkness";
\r
226 char far l4e[] = "Keen dares to enter the\nSecond Dome of Darkness";
\r
227 char far l5e[] = "Keen foolishly enters\nthe Bloogdome";
\r
228 char far l6e[] = "Keen makes his way\ninto Bloogton\nManufacturing";
\r
229 char far l7e[] = "Keen ascends\nBloogton Tower";
\r
230 char far l8e[] = "Keen hungrily enters\nBloogfoods, Inc.";
\r
231 char far l9e[] = "Keen smashes through\nGuard Post Two";
\r
232 char far l10e[] = "Keen seeks thrills\nin Bloogville";
\r
233 char far l11e[] = "Keen rockets into the\nBloog Aeronautics and\nSpace Administration";
\r
234 char far l12e[] = "Keen boldly assaults\nGuard Post Three";
\r
235 char far l13e[] = "Keen whoops it up in\nthe Bloogbae\nRecreational District"; // sic!
\r
236 char far l14e[] = "Keen purposefully struts\ninto the Bloogbase\nManagement District";
\r
237 char far l15e[] = "Keen bravely enters the\nBloog Control Center,\nlooking for Molly";
\r
238 char far l16e[] = "Keen warily enters\nBlooglab Space\nStation";
\r
239 char far l17e[] = "Keen returns to the\nBean-with-Bacon\nMegarocket";
\r
240 char far l18e[] = "Keen is in the High\nScore screen. Call Id!";
\r
242 char far *levelnames[GAMELEVELS] = {
\r
264 char far *levelenter[GAMELEVELS] = {
\r
286 Uint16 bonuslump[] = {
\r
287 KEYGEM_LUMP, KEYGEM_LUMP, KEYGEM_LUMP, KEYGEM_LUMP,
\r
288 SUGAR1_LUMP, SUGAR2_LUMP, SUGAR3_LUMP,
\r
289 SUGAR4_LUMP, SUGAR5_LUMP, SUGAR6_LUMP,
\r
290 ONEUP_LUMP, AMMO_LUMP, AMMO_LUMP, 0, 0
\r
293 //============================================================================
\r
296 ==========================
\r
300 = Spawn all actors and mark down special places
\r
302 ==========================
\r
305 void ScanInfoPlane(void)
\r
307 Uint16 i, x, y, chunk;
\r
312 InitObjArray(); // start spawning things with a clean slate
\r
314 memset(lumpneeded, 0, sizeof(lumpneeded));
\r
317 for (y=0; y<mapheight; y++)
\r
319 for (x=0; x<mapwidth; x++)
\r
329 SpawnKeen(x, y, 1);
\r
331 lumpneeded[KEEN_LUMP] = true;
\r
332 CA_MarkGrChunk(SCOREBOXSPR);
\r
336 SpawnKeen(x, y, -1);
\r
338 lumpneeded[KEEN_LUMP] = true;
\r
339 CA_MarkGrChunk(SCOREBOXSPR);
\r
343 SpawnWorldKeen(x, y);
\r
345 lumpneeded[WORLDKEEN_LUMP] = true;
\r
346 CA_MarkGrChunk(SCOREBOXSPR);
\r
350 if (gamestate.difficulty < gd_Hard)
\r
353 if (gamestate.difficulty < gd_Normal)
\r
357 lumpneeded[BLOOG_LUMP] = true;
\r
368 SpawnBlooglet(x, y, info-7);
\r
369 lumpneeded[(info-7) % 4 + RBLOOGLET_LUMP] = true;
\r
371 lumpneeded[KEYGEM_LUMP] = true;
\r
376 SpawnGrappleSpot(x, y, info-15);
\r
379 // case 17 is not used
\r
382 if (gamestate.difficulty < gd_Hard)
\r
385 if (gamestate.difficulty < gd_Normal)
\r
389 lumpneeded[FLEEX_LUMP] = true;
\r
397 lumpneeded[MOLLY_LUMP] = true;
\r
401 RF_SetScrollBlock(x, y, true);
\r
405 RF_SetScrollBlock(x, y, false);
\r
412 SpawnPlatform(x, y, info-27);
\r
413 lumpneeded[PLATFORM_LUMP] = true;
\r
416 // case 31 is the block icon
\r
419 SpawnDropPlat(x, y);
\r
420 lumpneeded[PLATFORM_LUMP] = true;
\r
424 SpawnStaticPlat(x, y);
\r
425 lumpneeded[PLATFORM_LUMP] = true;
\r
428 if (gamestate.difficulty > gd_Normal)
\r
430 SpawnStaticPlat(x, y);
\r
431 lumpneeded[PLATFORM_LUMP] = true;
\r
434 if (gamestate.difficulty > gd_Easy)
\r
436 SpawnStaticPlat(x, y);
\r
437 lumpneeded[PLATFORM_LUMP] = true;
\r
444 SpawnGoPlat(x, y, info-36);
\r
445 lumpneeded[PLATFORM_LUMP] = true;
\r
446 lumpneeded[BIPSQUISHED_LUMP] = true; // why?
\r
450 SpawnSneakPlat(x, y);
\r
451 lumpneeded[PLATFORM_LUMP] = true;
\r
455 if (gamestate.difficulty < gd_Hard)
\r
458 if (gamestate.difficulty < gd_Normal)
\r
462 lumpneeded[BOBBA_LUMP] = true;
\r
467 SpawnSatelliteStop(x, y, info-44);
\r
470 // case 46 is not used
\r
473 if (gamestate.difficulty < gd_Hard)
\r
476 if (gamestate.difficulty < gd_Normal)
\r
479 SpawnNospike(x, y);
\r
480 lumpneeded[NOSPIKE_LUMP] = true;
\r
484 if (gamestate.difficulty < gd_Hard)
\r
487 if (gamestate.difficulty < gd_Normal)
\r
491 lumpneeded[GIK_LUMP] = true;
\r
498 SpawnCannon(x, y, info-53);
\r
499 lumpneeded[LASER_LUMP] = true;
\r
503 if (gamestate.ammo >= 5)
\r
519 SpawnBonus(x, y, info-57);
\r
520 lumpneeded[bonuslump[info-57]] = true;
\r
524 if (gamestate.difficulty < gd_Hard)
\r
527 if (gamestate.difficulty < gd_Normal)
\r
530 SpawnOrbatrix(x, y);
\r
531 lumpneeded[ORBATRIX_LUMP] = true;
\r
535 if (gamestate.difficulty < gd_Hard)
\r
538 if (gamestate.difficulty < gd_Normal)
\r
541 SpawnBipship(x, y);
\r
542 lumpneeded[BIP_LUMP]=lumpneeded[BIPSHIP_LUMP]=lumpneeded[BIPSQUISHED_LUMP] = true;
\r
546 if (gamestate.difficulty < gd_Hard)
\r
549 if (gamestate.difficulty < gd_Normal)
\r
553 lumpneeded[FLECT_LUMP] = true;
\r
557 if (gamestate.difficulty < gd_Hard)
\r
560 if (gamestate.difficulty < gd_Normal)
\r
564 lumpneeded[BLORB_LUMP] = true;
\r
568 if (gamestate.difficulty < gd_Hard)
\r
571 if (gamestate.difficulty < gd_Normal)
\r
574 SpawnCeilick(x, y);
\r
575 lumpneeded[CEILICK_LUMP] = true;
\r
579 if (gamestate.difficulty < gd_Hard)
\r
582 if (gamestate.difficulty < gd_Normal)
\r
585 SpawnBlooguard(x, y);
\r
586 lumpneeded[BLOOGUARD_LUMP] = true;
\r
590 SpawnGrabbiter(x, y);
\r
591 // no additional lump needed - sprites are in WORLDKEEN_LUMP
\r
595 SpawnSatellite(x, y);
\r
596 // no additional lump needed - sprites are in WORLDKEEN_LUMP
\r
599 // case 90 is not used
\r
600 // cases 91 to 98 are direction arrows
\r
604 lumpneeded[HOOK_LUMP] = true;
\r
608 SpawnSandwich(x, y);
\r
609 lumpneeded[SANDWICH_LUMP] = true;
\r
613 SpawnPasscard(x, y);
\r
614 lumpneeded[PASSCARD_LUMP] = true;
\r
618 if (gamestate.difficulty < gd_Hard)
\r
621 if (gamestate.difficulty < gd_Normal)
\r
624 SpawnBabobba(x, y);
\r
625 lumpneeded[BABOBBA_LUMP] = true;
\r
630 SpawnRocket(x, y, info-105);
\r
631 // no additional lump needed - sprites are in WORLDKEEN_LUMP
\r
637 for (ob = player; ob; ob = ob->next)
\r
639 if (ob->active != ac_allways)
\r
640 ob->active = ac_no;
\r
643 for (i = 0; i < NUMLUMPS; i++)
\r
647 for (chunk = lumpstart[i]; chunk <= lumpend[i]; chunk++)
\r
649 CA_MarkGrChunk(chunk);
\r
655 //============================================================================
\r
657 statetype s_keenstun = {KEENSTUNSPR, KEENSTUNSPR, step, false, true, 60, 0, 0, T_Projectile, KeenContact, KeenStandReact, &s_keenstand};
\r
659 //============================================================================
\r
662 ===========================
\r
666 ===========================
\r
669 void FlipBigSwitch(objtype *ob, boolean isup)
\r
673 Uint16 top, mid, bot;
\r
675 Uint16 tile, tx, ty, xi, yi, offset, anim;
\r
679 // handle flipping the switch itself:
\r
683 ty = ob->tilebottom;
\r
687 ty = ob->tiletop - 2;
\r
689 tx = ob->tileleft - 1;
\r
690 map = mapsegs[2] + mapbwidthtable[ty+1]/2 + tx + 1;
\r
696 map = mapsegs[1] + mapbwidthtable[ty]/2 + tx;
\r
698 for (y = 0; y < 3; y++, map += mapwidth)
\r
700 for (x = 0; x < 2; tileptr++, x++)
\r
703 *tileptr = tile + (Sint8)tinf[tile+MANIM];
\r
706 RF_MemToMap(tiles, 1, tx, ty, 2, 3);
\r
708 tile = *(mapsegs[2]+mapbwidthtable[ty+1]/2 + tx + 1);
\r
711 SD_PlaySound(SND_USESWITCH);
\r
714 // toggle whatever was linked to the switch (at tile x, y):
\r
716 offset = mapbwidthtable[y]/2 + x;
\r
717 map = mapsegs[2] + offset;
\r
720 if (tile >= DIRARROWSTART && tile < DIRARROWEND)
\r
722 // turn direction arrow:
\r
723 *map = arrowflip[tile-DIRARROWSTART] + DIRARROWSTART;
\r
727 map = mapsegs[1] + offset;
\r
729 switch (tinf[tile+INTILE] & INTILE_TYPEMASK)
\r
731 case INTILE_NOTHING: // no special tiles
\r
732 mapsegs[2][offset] ^= PLATFORMBLOCK;
\r
735 case INTILE_BRIDGE: // bridge
\r
736 for (yi=y; y+2 > yi; yi++)
\r
738 map = mapsegs[1] + mapbwidthtable[yi]/2 + x - (yi != y);
\r
739 for (xi = x - (yi != y); xi < mapwidth; xi++)
\r
743 anim = tinf[tile + MANIM];
\r
746 tile += (Sint8)anim;
\r
747 RF_MemToMap(&tile, 1, xi, yi, 1, 1);
\r
752 case INTILE_FORCEFIELD: // active force field
\r
757 map = mapsegs[1] + mapbwidthtable[y+1]/2 + x;
\r
759 RF_MemToMap(&top, 1, x, y++, 1, 1);
\r
760 while (tinf[*map+INTILE] == INTILE_DEADLY)
\r
762 RF_MemToMap(&mid, 1, x, y++, 1, 1);
\r
765 RF_MemToMap(&bot, 1, x, y, 1, 1);
\r
768 case INTILE_FORCEFIELDEND: // inactive force field
\r
769 map = mapsegs[1] + 3;
\r
773 map = mapsegs[1] + mapbwidthtable[y+1]/2 + x;
\r
775 RF_MemToMap(&top, 1, x, y++, 1, 1);
\r
776 while (tinf[*map+INTILE] != INTILE_FORCEFIELDEND)
\r
778 RF_MemToMap(&mid, 1, x, y++, 1, 1);
\r
781 RF_MemToMap(&bot, 1, x, y, 1, 1);
\r
786 //============================================================================
\r
789 ===========================
\r
793 ===========================
\r
796 void GotSandwich(void)
\r
798 SD_WaitSoundDone();
\r
799 SD_PlaySound(SND_QUESTITEM);
\r
800 CA_UpLevel(); // kinda useless without CA_CacheMarks or CA_SetGrPurge
\r
801 // BUG: haven't made anything purgable here, caching the pic may cause an "out of memory" crash
\r
802 CA_CacheGrChunk(KEENTALK1PIC);
\r
804 US_CenterWindow(26, 8);
\r
805 VWB_DrawPic(WindowX+WindowW-48, WindowY, KEENTALK1PIC);
\r
809 "This is the second\n"
\r
810 "biggest sandwich\n"
\r
815 IN_ClearKeysDown();
\r
818 gamestate.sandwichstate = 1;
\r
822 ===========================
\r
826 ===========================
\r
831 SD_WaitSoundDone();
\r
832 SD_PlaySound(SND_QUESTITEM);
\r
833 CA_UpLevel(); // kinda useless without CA_CacheMarks or CA_SetGrPurge
\r
834 // BUG: haven't made anything purgable here, caching the pic may cause an "out of memory" crash
\r
835 CA_CacheGrChunk(KEENTALK1PIC);
\r
837 US_CenterWindow(26, 8);
\r
838 VWB_DrawPic(WindowX+WindowW-48, WindowY, KEENTALK1PIC);
\r
842 "Wow! A rope and\n"
\r
843 "grappling hook!\n"
\r
844 "They look useful!\n"
\r
848 IN_ClearKeysDown();
\r
851 gamestate.hookstate = 1;
\r
855 ===========================
\r
859 ===========================
\r
862 void GotPasscard(void)
\r
864 SD_WaitSoundDone();
\r
865 SD_PlaySound(SND_QUESTITEM);
\r
866 CA_UpLevel(); // kinda useless without CA_CacheMarks or CA_SetGrPurge
\r
867 // BUG: haven't made anything purgable here, caching the pic may cause an "out of memory" crash
\r
868 CA_CacheGrChunk(KEENTALK1PIC);
\r
870 US_CenterWindow(26, 8);
\r
871 VWB_DrawPic(WindowX+WindowW-48, WindowY, KEENTALK1PIC);
\r
875 "What's this? Cool!\n"
\r
877 "the Bloogstar Rocket!\n"
\r
878 "(It can fly through\n"
\r
879 "their force field.)"
\r
883 IN_ClearKeysDown();
\r
886 gamestate.passcardstate = 1;
\r