]> 4ch.mooo.com Git - 16.git/blob - 16/keen456/KEEN4-6/CK_PLAY.C
extrcted keen code remake
[16.git] / 16 / keen456 / KEEN4-6 / CK_PLAY.C
1 /* Reconstructed Commander Keen 4-6 Source Code\r
2  * Copyright (C) 2021 K1n9_Duk3\r
3  *\r
4  * This file is loosely based on:\r
5  * Keen Dreams Source Code\r
6  * Copyright (C) 2014 Javier M. Chavez\r
7  *\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
12  *\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
17  *\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
21  */\r
22 \r
23 #include "CK_DEF.H"\r
24 \r
25 /*\r
26 =============================================================================\r
27 \r
28                                                  GLOBAL VARIABLES\r
29 \r
30 =============================================================================\r
31 */\r
32 \r
33 ScanCode firescan = sc_Space;\r
34 \r
35 boolean singlestep, jumpcheat, godmode, keenkilled;\r
36 \r
37 exittype playstate;\r
38 gametype gamestate;\r
39 \r
40 objtype *new, *check, *player, *scoreobj;\r
41 \r
42 Uint16 originxtilemax;\r
43 Uint16 originytilemax;\r
44 \r
45 ControlInfo c;\r
46 boolean button2, button3;       // never used\r
47 \r
48 objtype dummyobj;\r
49 \r
50 Sint16 invincible;\r
51 \r
52 boolean oldshooting, showscorebox, joypad;\r
53 \r
54 Sint16 groundslam;\r
55 \r
56 boolean debugok;\r
57 boolean jumpbutton, jumpheld, pogobutton, pogoheld, firebutton, fireheld, upheld;\r
58 \r
59 \r
60 /*\r
61 =============================================================================\r
62 \r
63                                                  LOCAL VARIABLES\r
64 \r
65 =============================================================================\r
66 */\r
67 \r
68 objtype *obj;\r
69 \r
70 Uint16 centerlevel;\r
71 \r
72 Uint16 objectcount;\r
73 objtype objarray[MAXACTORS];\r
74 objtype *lastobj;\r
75 objtype *objfreelist;\r
76 \r
77 Sint16 inactivateleft;\r
78 Sint16 inactivateright;\r
79 Sint16 inactivatetop;\r
80 Sint16 inactivatebottom;\r
81 \r
82 #ifdef KEEN6Ev15\r
83 Uint16 __dummy__;       // never used, but must be present to recreate the original EXE\r
84 #endif\r
85 \r
86 Uint16 extravbls;\r
87 \r
88 Uint16 windowofs;\r
89 Sint16 vislines;\r
90 boolean scrollup;\r
91 \r
92 Sint16 oldfirecount;\r
93 \r
94 //===========================================================================\r
95 \r
96 /*\r
97 ==================\r
98 =\r
99 = CountObjects\r
100 =\r
101 ==================\r
102 */\r
103 \r
104 void CountObjects(void)\r
105 {\r
106         Uint16 activeobjects, inactiveobjects;\r
107         objtype *ob;\r
108 \r
109         activeobjects = inactiveobjects = 0;\r
110         for (ob=player; ob; ob=ob->next)\r
111         {\r
112                 if (ob->active)\r
113                 {\r
114                         activeobjects++;\r
115                 }\r
116                 else\r
117                 {\r
118                         inactiveobjects++;\r
119                 }\r
120         }\r
121         VW_FixRefreshBuffer();\r
122         US_CenterWindow(18, 4);\r
123         PrintY += 7;\r
124         US_Print("Active Objects :");\r
125         US_PrintUnsigned(activeobjects);\r
126         US_Print("\nInactive Objects:");\r
127         US_PrintUnsigned(inactiveobjects);\r
128         VW_UpdateScreen();\r
129         IN_Ack();\r
130 }\r
131 \r
132 /*\r
133 ==================\r
134 =\r
135 = DebugMemory\r
136 =\r
137 ==================\r
138 */\r
139 \r
140 void DebugMemory(void)\r
141 {\r
142         VW_FixRefreshBuffer();\r
143         US_CenterWindow(16, 7);\r
144         US_CPrint("Memory Usage");\r
145         US_CPrint("------------");\r
146         US_Print("Total     :");\r
147         US_PrintUnsigned((mminfo.mainmem+mminfo.EMSmem+mminfo.XMSmem)/1024);\r
148         US_Print("k\nFree      :");\r
149         US_PrintUnsigned(MM_UnusedMemory()/1024);\r
150         US_Print("k\nWith purge:");\r
151         US_PrintUnsigned(MM_TotalFree()/1024);\r
152         US_Print("k\n");\r
153         VW_UpdateScreen();\r
154         IN_Ack();\r
155 #if GRMODE != CGAGR\r
156         MM_ShowMemory();\r
157 #endif\r
158 }\r
159 \r
160 /*\r
161 ===================\r
162 =\r
163 = TestSprites\r
164 =\r
165 ===================\r
166 */\r
167 \r
168 void TestSprites(void)\r
169 {\r
170         Uint16 infox, infoy;\r
171         Sint16 chunk, oldchunk;\r
172         Sint16 shift;\r
173         Uint16 infobottom, drawx;\r
174         spritetabletype far *info;\r
175         Uint8 _seg *block;\r
176         Uint16 size;\r
177         Uint16 scan;\r
178         Uint32 totalsize;\r
179 \r
180         VW_FixRefreshBuffer();\r
181         US_CenterWindow(30, 17);\r
182         totalsize = 0;\r
183         US_CPrint("Sprite Test");\r
184         US_CPrint("-----------");\r
185         infoy = PrintY;\r
186         infox = (PrintX + 56) & ~7;\r
187         drawx = infox + 40;\r
188         US_Print("Chunk:\nWidth:\nHeight:\nOrgx:\nOrgy:\nXl:\nYl:\nXh:\nYh:\nShifts:\nMem:\n");\r
189         infobottom = PrintY;\r
190         chunk = STARTSPRITES;\r
191         shift = 0;\r
192         while (1)\r
193         {\r
194                 if (chunk >= STARTSPRITES+NUMSPRITES)\r
195                 {\r
196                         chunk = STARTSPRITES+NUMSPRITES-1;\r
197                 }\r
198                 else if (chunk < STARTSPRITES)\r
199                 {\r
200                         chunk = STARTSPRITES;\r
201                 }\r
202                 info = &spritetable[chunk-STARTSPRITES];\r
203                 block = grsegs[chunk];\r
204                 VWB_Bar(infox, infoy, 40, infobottom-infoy, WHITE);\r
205                 PrintX = infox;\r
206                 PrintY = infoy;\r
207                 US_PrintUnsigned(chunk);\r
208                 US_Print("\n");\r
209                 PrintX = infox;\r
210                 US_PrintUnsigned(info->width);\r
211                 US_Print("\n");\r
212                 PrintX = infox;\r
213                 US_PrintUnsigned(info->height);\r
214                 US_Print("\n");\r
215                 PrintX = infox;\r
216                 US_PrintSigned(info->orgx);\r
217                 US_Print("\n");\r
218                 PrintX = infox;\r
219                 US_PrintSigned(info->orgy);\r
220                 US_Print("\n");\r
221                 PrintX = infox;\r
222                 US_PrintSigned(info->xl);\r
223                 US_Print("\n");\r
224                 PrintX = infox;\r
225                 US_PrintSigned(info->yl);\r
226                 US_Print("\n");\r
227                 PrintX = infox;\r
228                 US_PrintSigned(info->xh);\r
229                 US_Print("\n");\r
230                 PrintX = infox;\r
231                 US_PrintSigned(info->yh);\r
232                 US_Print("\n");\r
233                 PrintX = infox;\r
234                 US_PrintSigned(info->shifts);\r
235                 US_Print("\n");\r
236                 PrintX = infox;\r
237                 if (!block)\r
238                 {\r
239                         US_Print("-----");\r
240                 }\r
241                 else\r
242                 {\r
243                         size = ((spritetype far *)block)->sourceoffset[3] + ((spritetype far *)block)->planesize[3]*5;\r
244                         size = (size + 15) & ~15;       //round up to multiples of 16\r
245                         totalsize += size;      //useless: the value stored in 'totalsize' is never used\r
246                         US_PrintUnsigned(size);\r
247                         US_Print("=");\r
248                 }\r
249                 oldchunk = chunk;\r
250                 do\r
251                 {\r
252                         VWB_Bar(drawx, infoy, 110, infobottom-infoy, WHITE);\r
253                         if (block)\r
254                         {\r
255                                 PrintX = drawx;\r
256                                 PrintY = infoy;\r
257                                 US_Print("Shift:");\r
258                                 US_PrintUnsigned(shift);\r
259                                 US_Print("\n");\r
260                                 VWB_DrawSprite(drawx + 2*shift + 16, PrintY, chunk);\r
261                         }\r
262                         VW_UpdateScreen();\r
263                         scan = IN_WaitForKey();\r
264                         switch (scan)\r
265                         {\r
266                         case sc_UpArrow:\r
267                                 chunk++;\r
268                                 break;\r
269                         case sc_DownArrow:\r
270                                 chunk--;\r
271                                 break;\r
272                         case sc_PgUp:\r
273                                 chunk += 10;\r
274                                 if (chunk >= STARTSPRITES+NUMSPRITES)\r
275                                 {\r
276                                         chunk = STARTSPRITES+NUMSPRITES-1;\r
277                                 }\r
278                                 break;\r
279                         case sc_PgDn:\r
280                                 chunk -= 10;\r
281                                 if (chunk < STARTSPRITES)\r
282                                 {\r
283                                         chunk = STARTSPRITES;\r
284                                 }\r
285                                 break;\r
286                         case sc_LeftArrow:\r
287                                 if (--shift == -1)\r
288                                 {\r
289                                         shift = 3;\r
290                                 }\r
291                                 break;\r
292                         case sc_RightArrow:\r
293                                 if (++shift == 4)\r
294                                 {\r
295                                         shift = 0;\r
296                                 }\r
297                                 break;\r
298                         case sc_Escape:\r
299                                 return;\r
300                         }\r
301 \r
302                 } while (chunk == oldchunk);\r
303 \r
304         }\r
305 }\r
306 \r
307 /*\r
308 ===================\r
309 =\r
310 = PicturePause\r
311 =\r
312 ===================\r
313 */\r
314 \r
315 void PicturePause(void)\r
316 {\r
317         Uint16 source;\r
318         Sint16 y;\r
319 \r
320 //\r
321 // wait for a key press, abort if it's not Enter\r
322 //\r
323         IN_ClearKeysDown();\r
324         while (!LastScan);\r
325         if (LastScan != sc_Enter)\r
326         {\r
327                 IN_ClearKeysDown();\r
328                 return;\r
329         }\r
330 \r
331         SD_PlaySound(SND_JUMP);\r
332         SD_WaitSoundDone();\r
333 \r
334 //\r
335 // rearrange onscreen image into base EGA layout, so that it\r
336 // can be grabbed correctly by an external screenshot tool\r
337 //\r
338         source = displayofs + panadjust;\r
339 \r
340         VW_ColorBorder(15);     // white (can't use WHITE as parameter, since that's defined as 3 for CGA and this must use 15)\r
341         VW_SetLineWidth(40);\r
342         VW_SetScreen(0, 0);\r
343 \r
344         if (source < 0x10000l-200*64)\r
345         {\r
346         //\r
347         // copy top line first\r
348         //\r
349                 for (y=0; y<200; y++)\r
350                 {\r
351                         VW_ScreenToScreen(source+y*64, y*40, 40, 1);\r
352                 }\r
353         }\r
354         else\r
355         {\r
356         //\r
357         // copy bottom line first\r
358         //\r
359                 for (y=199; y>=0; y--)\r
360                 {\r
361                         VW_ScreenToScreen(source+y*64, y*40, 40, 1);\r
362                 }\r
363         }\r
364 \r
365 //\r
366 // shut down input manager so that screenshot tool can see input again\r
367 //\r
368         IN_Shutdown();\r
369 \r
370         SD_PlaySound(SND_EXTRAKEEN);\r
371         SD_WaitSoundDone();\r
372 \r
373 //\r
374 // shut down the remaining ID managers, except VW (stay in graphics mode!)\r
375 //\r
376         US_Shutdown();\r
377         SD_Shutdown();\r
378         IN_Shutdown();\r
379         RF_Shutdown();\r
380         CA_Shutdown();\r
381         MM_Shutdown();\r
382 \r
383 //\r
384 // wait until user hits Escape\r
385 //\r
386         while (((bioskey(0) >> 8) & 0xFF) != sc_Escape);\r
387 \r
388 //\r
389 // back to text mode and exit to DOS\r
390 //\r
391         VW_Shutdown();\r
392         exit(0);\r
393 }\r
394 \r
395 /*\r
396 ===================\r
397 =\r
398 = MaskOnTile\r
399 =\r
400 ===================\r
401 */\r
402 \r
403 void MaskOnTile(Uint16 dest, Uint16 source)\r
404 {\r
405         Sint16 i;\r
406         Uint16 _seg *sourceseg;\r
407         Uint16 _seg *destseg;\r
408         Uint16 sourceval, maskindex, sourcemask;\r
409 \r
410         sourceseg = (grsegs+STARTTILE16M)[source];\r
411         destseg = (grsegs+STARTTILE16M)[dest];\r
412         for (i=0; i<64; i++)\r
413         {\r
414                 maskindex = i & 15;\r
415 #ifdef KEEN6Ev15\r
416                 sourceval = sourceseg[16+i];\r
417 #else\r
418                 sourceval = (sourceseg+16)[i];\r
419 #endif\r
420                 sourcemask = sourceseg[maskindex];\r
421                 destseg[maskindex] &= sourcemask;\r
422                 destseg[16+i] &= sourcemask;\r
423                 destseg[16+i] |= sourceval;\r
424         }\r
425 }\r
426 \r
427 /*\r
428 ===================\r
429 =\r
430 = WallDebug\r
431 =\r
432 ===================\r
433 */\r
434 \r
435 void WallDebug(void)\r
436 {\r
437         Sint16 i, val;\r
438 \r
439         VW_FixRefreshBuffer();\r
440         US_CenterWindow(24, 3);\r
441         US_PrintCentered("WORKING");\r
442         VW_UpdateScreen();\r
443         for (i=STARTTILE16M+108; i<STARTTILE16M+124; i++)\r
444         {\r
445                 CA_CacheGrChunk(i);\r
446         }\r
447         for (i=0; i<NUMTILE16M; i++)\r
448         {\r
449                 if (!grsegs[STARTTILE16M+i])\r
450                 {\r
451                         continue;\r
452                 }\r
453                 val = tinf[i+NORTHWALL] & 7;\r
454                 if (val)\r
455                 {\r
456                         MaskOnTile(i, val+107);\r
457                 }\r
458                 val = tinf[i+SOUTHWALL] & 7;\r
459                 if (val)\r
460                 {\r
461                         MaskOnTile(i, val+115);\r
462                 }\r
463                 val = tinf[i+EASTWALL] & 7;\r
464                 if (val > 1)\r
465                 {\r
466                         strcpy(str, "WallDebug: East wall other than 1:");\r
467                         itoa(i, str2, 10);\r
468                         strcat(str, str2);\r
469                         Quit(str);\r
470                 }\r
471                 if (val)\r
472                 {\r
473                         MaskOnTile(i, val+114); //Note: val is always 1 here, so you could use 115 as 2nd arg\r
474                 }\r
475                 val = tinf[i+WESTWALL] & 7;\r
476                 if (val > 1)\r
477                 {\r
478                         strcpy(str, "WallDebug: West wall other than 1:");\r
479                         itoa(i, str2, 10);\r
480                         strcat(str, str2);\r
481                         Quit(str);\r
482                 }\r
483                 if (val)\r
484                 {\r
485                         MaskOnTile(i, val+122); //Note: val is always 1 here, so you could use 123 as 2nd arg\r
486                 }\r
487         }\r
488 }\r
489 \r
490 \r
491 //===========================================================================\r
492 \r
493 /*\r
494 ================\r
495 =\r
496 = DebugKeys\r
497 =\r
498 ================\r
499 */\r
500 \r
501 boolean DebugKeys(void)\r
502 {\r
503         Sint16 level, i, esc;\r
504 \r
505         if (Keyboard[sc_B] && ingame)           // B = border color\r
506         {\r
507                 VW_FixRefreshBuffer();\r
508                 US_CenterWindow(24, 3);\r
509                 PrintY += 6;\r
510                 US_Print(" Border color (0-15):");\r
511                 VW_UpdateScreen();\r
512                 esc = !US_LineInput(px, py, str, NULL, true, 2, 0);\r
513                 if (!esc)\r
514                 {\r
515                         level = atoi(str);\r
516                         if (level >= 0 && level <= 15)\r
517                         {\r
518                                 VW_ColorBorder(level);\r
519                         }\r
520                 }\r
521                 return true;\r
522         }\r
523 \r
524         if (Keyboard[sc_C] && ingame)           // C = count objects\r
525         {\r
526                 CountObjects();\r
527                 return true;\r
528         }\r
529 \r
530         if (Keyboard[sc_D] && ingame)           // D = start / end demo record\r
531         {\r
532                 if (DemoMode == demo_Off)\r
533                 {\r
534                         StartDemoRecord();\r
535                 }\r
536                 else if (DemoMode == demo_Record)\r
537                 {\r
538                         EndDemoRecord();\r
539                         playstate = ex_completed;\r
540                 }\r
541                 return true;\r
542         }\r
543 \r
544         if (Keyboard[sc_E] && ingame)           // E = quit level\r
545         {\r
546                 if (tedlevel)\r
547                 {\r
548                         TEDDeath();\r
549                 }\r
550                 playstate = ex_completed;\r
551                 //BUG? there is no return in this branch (should return false)\r
552         }\r
553 \r
554         if (Keyboard[sc_G] && ingame)           // G = god mode\r
555         {\r
556                 VW_FixRefreshBuffer();\r
557                 US_CenterWindow(12, 2);\r
558                 if (godmode)\r
559                 {\r
560                         US_PrintCentered("God mode OFF");\r
561                 }\r
562                 else\r
563                 {\r
564                         US_PrintCentered("God mode ON");\r
565                 }\r
566                 VW_UpdateScreen();\r
567                 IN_Ack();\r
568                 godmode ^= true;\r
569                 return true;\r
570         }\r
571         else if (Keyboard[sc_I])                        // I = item cheat\r
572         {\r
573                 VW_FixRefreshBuffer();\r
574                 US_CenterWindow(12, 3);\r
575                 US_PrintCentered("Free items!");\r
576                 for (i=0; i<4; i++)\r
577                 {\r
578                         gamestate.keys[i] = 99;\r
579                 }\r
580                 gamestate.ammo = 99;\r
581 #if defined KEEN4\r
582                 gamestate.wetsuit = true;\r
583 #elif defined KEEN5\r
584                 gamestate.keycard = true;\r
585 #elif defined KEEN6\r
586                 gamestate.passcardstate=gamestate.hookstate=gamestate.sandwichstate = 1;\r
587 #endif\r
588                 VW_UpdateScreen();\r
589                 IN_Ack();\r
590                 GivePoints(3000);\r
591                 return true;\r
592         }\r
593         else if (Keyboard[sc_J])                        // J = jump cheat\r
594         {\r
595                 jumpcheat ^= true;\r
596                 VW_FixRefreshBuffer();\r
597                 US_CenterWindow(18, 3);\r
598                 if (jumpcheat)\r
599                 {\r
600                         US_PrintCentered("Jump cheat ON");\r
601                 }\r
602                 else\r
603                 {\r
604                         US_PrintCentered("Jump cheat OFF");\r
605                 }\r
606                 VW_UpdateScreen();\r
607                 IN_Ack();\r
608                 return true;\r
609         }\r
610         else if (Keyboard[sc_M])                        // M = memory info\r
611         {\r
612                 DebugMemory();\r
613                 return true;\r
614         }\r
615         else if (Keyboard[sc_N])                        // N = no clip\r
616         {\r
617                 VW_FixRefreshBuffer();\r
618                 US_CenterWindow(18, 3);\r
619                 if (player->needtoclip)\r
620                 {\r
621                         US_PrintCentered("No clipping ON");\r
622                         player->needtoclip = cl_noclip;\r
623                 }\r
624                 else\r
625                 {\r
626                         US_PrintCentered("No clipping OFF");\r
627                         player->needtoclip = cl_midclip;\r
628                 }\r
629                 VW_UpdateScreen();\r
630                 IN_Ack();\r
631                 return true;\r
632         }\r
633         else if (Keyboard[sc_P])                        // P = pause with no screen disruptioon\r
634         {\r
635                 IN_ClearKeysDown();\r
636                 PicturePause();\r
637                 return true;\r
638         }\r
639         else if (Keyboard[sc_S] && ingame)      // S = slow motion\r
640         {\r
641                 singlestep ^= true;\r
642                 VW_FixRefreshBuffer();\r
643                 US_CenterWindow(18, 3);\r
644                 if (singlestep)\r
645                 {\r
646                         US_PrintCentered("Slow motion ON");\r
647                 }\r
648                 else\r
649                 {\r
650                         US_PrintCentered("Slow motion OFF");\r
651                 }\r
652                 VW_UpdateScreen();\r
653                 IN_Ack();\r
654                 return true;\r
655         }\r
656         else if (Keyboard[sc_T])                        // T = sprite test\r
657         {\r
658                 TestSprites();\r
659                 return true;\r
660         }\r
661         else if (Keyboard[sc_V])                        // V = extra VBLs\r
662         {\r
663                 VW_FixRefreshBuffer();\r
664                 US_CenterWindow(30, 3);\r
665                 PrintY += 6;\r
666                 US_Print("  Add how many extra VBLs(0-8):");\r
667                 VW_UpdateScreen();\r
668                 esc = !US_LineInput(px, py, str, NULL, true, 2, 0);\r
669                 if (!esc)\r
670                 {\r
671                         level = atoi(str);\r
672                         if (level >= 0 && level <= 8)\r
673                         {\r
674                                 extravbls = level;\r
675                         }\r
676                 }\r
677                 return true;\r
678         }\r
679         else if (Keyboard[sc_W] && ingame)      // W = warp to level\r
680         {\r
681                 VW_FixRefreshBuffer();\r
682                 US_CenterWindow(26, 3);\r
683                 PrintY += 6;\r
684                 US_Print("  Warp to which level(1-18):");\r
685                 VW_UpdateScreen();\r
686                 esc = !US_LineInput(px, py, str, NULL, true, 2, 0);\r
687                 if (!esc)\r
688                 {\r
689                         level = atoi(str);\r
690                         if (level > 0 && level <= 18)\r
691                         {\r
692                                 gamestate.mapon = level;\r
693                                 playstate = ex_warped;\r
694                         }\r
695                 }\r
696                 return true;\r
697         }\r
698         else if (Keyboard[sc_Y])                        // Y = wall debug\r
699         {\r
700                 WallDebug();\r
701                 return true;\r
702         }\r
703         else if (Keyboard[sc_Z])                        // Z = game over\r
704         {\r
705                 gamestate.lives = 0;\r
706                 KillKeen();\r
707                 return false;\r
708         }\r
709         return false;\r
710 }\r
711 \r
712 //===========================================================================\r
713 \r
714 /*\r
715 ================\r
716 =\r
717 = UserCheat\r
718 =\r
719 ================\r
720 */\r
721 \r
722 void UserCheat(void)\r
723 {\r
724         Sint16 i;\r
725 \r
726         for (i=sc_A; i<=sc_Z; i++)      //Note: this does NOT check the keys in alphabetical order!\r
727         {\r
728                 if (i != sc_B && i != sc_A && i != sc_T && Keyboard[i])\r
729                 {\r
730                         return;\r
731                 }\r
732         }\r
733         US_CenterWindow(20, 7);\r
734         PrintY += 2;\r
735         US_CPrint(\r
736                 "Cheat Option!\n"\r
737                 "\n"\r
738                 "You just got all\n"\r
739                 "the keys, 99 shots,\n"\r
740                 "and an extra keen!");\r
741         VW_UpdateScreen();\r
742         IN_Ack();\r
743         RF_ForceRefresh();\r
744         gamestate.ammo = 99;\r
745         gamestate.lives++;\r
746 #ifdef KEEN5\r
747         gamestate.keycard = true;\r
748 #endif\r
749         gamestate.keys[0] = gamestate.keys[1] = gamestate.keys[2] = gamestate.keys[3] = 1;\r
750 }\r
751 \r
752 //===========================================================================\r
753 \r
754 /*\r
755 =====================\r
756 =\r
757 = CheckKeys\r
758 =\r
759 =====================\r
760 */\r
761 \r
762 void CheckKeys(void)\r
763 {\r
764         if (screenfaded)                        // don't do anything with a faded screen\r
765         {\r
766                 return;\r
767         }\r
768 \r
769 //\r
770 // Enter for status screen\r
771 //\r
772         if (Keyboard[sc_Enter] || (GravisGamepad && GravisAction[ga_Status]))\r
773         {\r
774                 StatusWindow();\r
775                 IN_ClearKeysDown();\r
776                 RF_ForceRefresh();\r
777                 lasttimecount = TimeCount;      // BUG: should be the other way around\r
778         }\r
779 \r
780 //\r
781 // pause key wierdness can't be checked as a scan code\r
782 //\r
783         if (Paused)\r
784         {\r
785                 SD_MusicOff();\r
786                 VW_FixRefreshBuffer();\r
787                 US_CenterWindow(8, 3);\r
788                 US_PrintCentered("PAUSED");\r
789                 VW_UpdateScreen();\r
790                 IN_Ack();\r
791                 RF_ForceRefresh();\r
792                 Paused = false;\r
793                 SD_MusicOn();\r
794         }\r
795 \r
796 #ifndef KEEN6\r
797 //\r
798 // F1 to enter help screens\r
799 //\r
800         if (LastScan == sc_F1)\r
801         {\r
802                 StopMusic();\r
803                 HelpScreens();\r
804                 StartMusic(gamestate.mapon);\r
805                 if (showscorebox)\r
806                 {\r
807                         scoreobj->temp2 = -1;\r
808                         scoreobj->temp1 = -1;\r
809                         scoreobj->temp3 = -1;\r
810                         scoreobj->temp4 = -1;\r
811                 }\r
812                 RF_ForceRefresh();\r
813         }\r
814 #endif\r
815 \r
816         if (!storedemo)\r
817         {\r
818 //\r
819 // F2-F7/ESC to enter control panel\r
820 //\r
821                 if (LastScan >= sc_F2 && LastScan <= sc_F7 || LastScan == sc_Escape)\r
822                 {\r
823                         VW_FixRefreshBuffer();\r
824                         StopMusic();\r
825                         US_ControlPanel();\r
826                         RF_FixOfs();\r
827                         StartMusic(gamestate.mapon);\r
828                         if (!showscorebox && scoreobj->sprite)\r
829                         {\r
830                                 RF_RemoveSprite(&scoreobj->sprite);\r
831                         }\r
832                         if (showscorebox)\r
833                         {\r
834                                 scoreobj->temp2 = -1;\r
835                                 scoreobj->temp1 = -1;\r
836                                 scoreobj->temp3 = -1;\r
837                                 scoreobj->temp4 = -1;\r
838                         }\r
839                         IN_ClearKeysDown();\r
840                         if (restartgame)\r
841                         {\r
842                                 playstate = ex_resetgame;\r
843                         }\r
844                         else if (!loadedgame)\r
845                         {\r
846                                 RF_ForceRefresh();\r
847                         }\r
848                         if (abortgame)\r
849                         {\r
850                                 abortgame = false;\r
851                                 playstate = ex_abortgame;\r
852                         }\r
853                         if (loadedgame)\r
854                         {\r
855                                 playstate = ex_loadedgame;\r
856                         }\r
857                         lasttimecount = TimeCount;      // BUG: should be the other way around\r
858                 }\r
859 \r
860 //\r
861 // F9 boss key\r
862 //\r
863                 if (LastScan == sc_F9)\r
864                 {\r
865                         VW_Shutdown();\r
866                         SD_MusicOff();\r
867                         cputs("C:>");\r
868                         IN_ClearKeysDown();\r
869                         while (LastScan != sc_Escape);\r
870                         VW_SetScreenMode(GRMODE);\r
871                         VW_ColorBorder(bordercolor);\r
872                         RF_ForceRefresh();\r
873                         IN_ClearKeysDown();\r
874                         lasttimecount = TimeCount;      // BUG: should be the other way around\r
875                         SD_MusicOn();\r
876                 }\r
877         }\r
878 \r
879 //\r
880 // B-A-T cheat code\r
881 //\r
882         if (Keyboard[sc_B] && Keyboard[sc_A] && Keyboard[sc_T])\r
883         {\r
884                 UserCheat();\r
885         }\r
886 \r
887 //\r
888 // F10-? debug keys\r
889 //\r
890         if (debugok && Keyboard[sc_F10])\r
891         {\r
892                 if (DebugKeys())\r
893                 {\r
894                         RF_ForceRefresh();\r
895                         lasttimecount = TimeCount;      // BUG: should be the other way around\r
896                 }\r
897         }\r
898 \r
899 //\r
900 // Ctrl-S toggles sound (only in storedemo mode)\r
901 //\r
902         if (storedemo && Keyboard[sc_Control] && LastScan == sc_S)\r
903         {\r
904                 if (SoundMode != sdm_Off)\r
905                 {\r
906                         SD_SetSoundMode(sdm_Off);\r
907                         SD_SetMusicMode(smm_Off);\r
908                 }\r
909                 else\r
910                 {\r
911                         if (AdLibPresent)\r
912                         {\r
913                                 SD_SetSoundMode(sdm_AdLib);\r
914                                 QuietFX = false;\r
915                                 SD_SetMusicMode(smm_AdLib);\r
916                         }\r
917                         else\r
918                         {\r
919                                 SD_SetSoundMode(sdm_PC);\r
920                                 SD_SetMusicMode(smm_Off);\r
921                         }\r
922                         CA_LoadAllSounds();\r
923                 }\r
924         }\r
925 \r
926 //\r
927 // Ctrl-Q quick quit\r
928 //\r
929         if (Keyboard[sc_Control] && LastScan == sc_Q)\r
930         {\r
931                 IN_ClearKeysDown();\r
932                 Quit(NULL);\r
933         }\r
934 }\r
935 \r
936 //===========================================================================\r
937 \r
938 /*\r
939 ==================\r
940 =\r
941 = PrintNumbers\r
942 =\r
943 ==================\r
944 */\r
945 \r
946 void PrintNumbers(Sint16 x, Sint16 y, Sint16 maxlen, Sint16 basetile, Sint32 number)\r
947 {\r
948         register Sint16 i;\r
949         Sint16 len;\r
950         char buffer[20];\r
951 \r
952         ltoa(number, buffer, 10);\r
953         len = strlen(buffer);\r
954         i = maxlen;\r
955         while (i>len)\r
956         {\r
957                 VWB_DrawTile8(x, y, basetile);\r
958                 i--;\r
959                 x += 8;\r
960         }\r
961         while (i>0)\r
962         {\r
963                 VWB_DrawTile8(x, y, basetile+buffer[len-i]+(1-'0'));\r
964                 i--;\r
965                 x += 8;\r
966         }\r
967 }\r
968 \r
969 /*\r
970 ==================\r
971 =\r
972 = DrawStatusWindow\r
973 =\r
974 ==================\r
975 */\r
976 \r
977 #if GRMODE == CGAGR\r
978 \r
979 #define BACKCOLOR WHITE\r
980 #define TEXTBACK BLACK\r
981 #define NUMBERBACK BLACK\r
982 \r
983 #else\r
984 \r
985 #define BACKCOLOR LIGHTGRAY\r
986 #define TEXTBACK WHITE\r
987 #define NUMBERBACK BLACK\r
988 \r
989 #endif\r
990 \r
991 void DrawStatusWindow(void)\r
992 {\r
993         Sint16 off, x, y, w, h, i;\r
994         Uint16 width, height;\r
995 \r
996         x = 64;\r
997         y = 16;\r
998         w = 184;\r
999         h = 144;\r
1000         VWB_DrawTile8(x, y, 54);\r
1001         VWB_DrawTile8(x, y+h, 60);\r
1002         for (i=x+8; i<=x+w-8; i+=8)\r
1003         {\r
1004                 VWB_DrawTile8(i, y, 55);\r
1005                 VWB_DrawTile8(i, y+h, 61);\r
1006         }\r
1007         VWB_DrawTile8(i, y, 56);\r
1008         VWB_DrawTile8(i, y+h, 62);\r
1009         for (i=y+8; i<=y+h-8; i+=8)\r
1010         {\r
1011                 VWB_DrawTile8(x, i, 57);\r
1012                 VWB_DrawTile8(x+w, i, 59);\r
1013         }\r
1014         VWB_Bar(72, 24, 176, 136, BACKCOLOR);\r
1015 \r
1016         PrintY = 28;\r
1017         WindowX = 80;\r
1018         WindowW = 160;\r
1019         US_CPrint("LOCATION");\r
1020         VWB_Bar(79, 38, 162, 20, TEXTBACK);\r
1021 #ifdef KEEN5\r
1022         if (mapon == 0 && player->y > 100*TILEGLOBAL)\r
1023                 _fstrcpy(str, levelnames[13]);\r
1024         else\r
1025                 _fstrcpy(str, levelnames[gamestate.mapon]);\r
1026 #else\r
1027         _fstrcpy(str, levelnames[gamestate.mapon]);\r
1028 #endif\r
1029         SizeText(str, &width, &height);\r
1030         PrintY = (20-height)/2+40-2;\r
1031         US_CPrint(str);\r
1032 \r
1033         PrintY = 61;\r
1034         WindowX = 80;\r
1035         WindowW = 64;\r
1036         US_CPrint("SCORE");\r
1037         VWB_Bar(79, 71, 66, 10, NUMBERBACK);\r
1038         PrintNumbers(80, 72, 8, 41, gamestate.score);\r
1039 \r
1040         PrintY = 61;\r
1041         WindowX = 176;\r
1042         WindowW = 64;\r
1043         US_CPrint("EXTRA");\r
1044         VWB_Bar(175, 71, 66, 10, NUMBERBACK);\r
1045         PrintNumbers(176, 72, 8, 41, gamestate.nextextra);\r
1046 \r
1047 #if defined KEEN4\r
1048         PrintY = 85;\r
1049         WindowX = 80;\r
1050         WindowW = 64;\r
1051         US_CPrint("RESCUED");\r
1052         VWB_Bar(79, 95, 66, 10, NUMBERBACK);\r
1053         for (i = 0; i < gamestate.rescued; i++, off+=8)\r
1054         {\r
1055                 VWB_DrawTile8(i*8 + 80, 96, 40);\r
1056         }\r
1057 #elif defined KEEN5\r
1058         PrintY = 92;\r
1059         PrintX = 80;\r
1060         US_Print("KEYCARD");\r
1061         VWB_Bar(135, 91, 10, 10, NUMBERBACK);\r
1062         if (gamestate.keycard)\r
1063         {\r
1064                 VWB_DrawTile8(136, 92, 40);\r
1065         }\r
1066 #endif\r
1067 \r
1068         PrintY = 85;\r
1069         WindowX = 176;\r
1070         WindowW = 64;\r
1071         US_CPrint("LEVEL");\r
1072         VWB_Bar(175, 95, 66, 10, TEXTBACK);\r
1073         PrintY = 96;\r
1074         WindowX = 176;\r
1075         WindowW = 64;\r
1076         switch (gamestate.difficulty)\r
1077         {\r
1078         case gd_Easy:\r
1079                 US_CPrint("Easy");\r
1080                 break;\r
1081         case gd_Normal:\r
1082                 US_CPrint("Normal");\r
1083                 break;\r
1084         case gd_Hard:\r
1085                 US_CPrint("Hard");\r
1086                 break;\r
1087         }\r
1088 \r
1089 #ifdef KEEN6\r
1090         PrintX = 80;\r
1091         PrintY = 96;\r
1092         US_Print("ITEMS");\r
1093         VWB_Bar(127, 95, 26, 10, NUMBERBACK);\r
1094         if (gamestate.sandwichstate == 1)\r
1095         {\r
1096                 VWB_DrawTile8(128, 96, 2);\r
1097         }\r
1098         else\r
1099         {\r
1100                 VWB_DrawTile8(128, 96, 1);\r
1101         }\r
1102         if (gamestate.hookstate == 1)\r
1103         {\r
1104                 VWB_DrawTile8(136, 96, 4);\r
1105         }\r
1106         else\r
1107         {\r
1108                 VWB_DrawTile8(136, 96, 3);\r
1109         }\r
1110         if (gamestate.passcardstate == 1)\r
1111         {\r
1112                 VWB_DrawTile8(144, 96, 6);\r
1113         }\r
1114         else\r
1115         {\r
1116                 VWB_DrawTile8(144, 96, 5);\r
1117         }\r
1118 #endif\r
1119 \r
1120         PrintX = 80;\r
1121         PrintY = 112;\r
1122         US_Print("KEYS");\r
1123         VWB_Bar(119, 111, 34, 10, NUMBERBACK);\r
1124         for (i = 0; i < 4; i++)\r
1125         {\r
1126                 if (gamestate.keys[i])\r
1127                 {\r
1128                         VWB_DrawTile8(i*8+120, 112, 36+i);\r
1129                 }\r
1130         }\r
1131 \r
1132         PrintX = 176;\r
1133         PrintY = 112;\r
1134         US_Print("AMMO");\r
1135         VWB_Bar(215, 111, 26, 10, NUMBERBACK);\r
1136         PrintNumbers(216, 112, 3, 41, gamestate.ammo);\r
1137 \r
1138         PrintX = 80;\r
1139         PrintY = 128;\r
1140         US_Print("KEENS");\r
1141         VWB_Bar(127, 127, 18, 10, NUMBERBACK);\r
1142         PrintNumbers(128, 128, 2, 41, gamestate.lives);\r
1143 \r
1144         PrintX = 176;\r
1145         PrintY = 128;\r
1146         US_Print(DROPSNAME);\r
1147         VWB_Bar(224, 127, 16, 10, NUMBERBACK);\r
1148         PrintNumbers(224, 128, 2, 41, gamestate.drops);\r
1149 \r
1150 #ifdef KEEN4\r
1151         VWB_Bar(79, 143, 66, 10, TEXTBACK);\r
1152         PrintY = 144;\r
1153         WindowX = 80;\r
1154         WindowW = 64;\r
1155         if (gamestate.wetsuit)\r
1156         {\r
1157                 US_CPrint("Wetsuit");\r
1158         }\r
1159         else\r
1160         {\r
1161                 US_CPrint("???");\r
1162         }\r
1163 #endif\r
1164 \r
1165         // draw the tiles for "PRESS A KEY":\r
1166         for (i = 0; i < 10; i++)\r
1167         {\r
1168                 VWB_DrawTile8(i*8+STATUS_PRESSKEY_X, 140, i+72);\r
1169                 VWB_DrawTile8(i*8+STATUS_PRESSKEY_X, 148, i+82);\r
1170         }\r
1171 }\r
1172 \r
1173 /*\r
1174 ==================\r
1175 =\r
1176 = ScrollStatusWindow\r
1177 =\r
1178 ==================\r
1179 */\r
1180 \r
1181 void ScrollStatusWindow(void)\r
1182 {\r
1183         Uint16 source, dest;\r
1184         Sint16 height;\r
1185 \r
1186         if (vislines > 152)\r
1187         {\r
1188                 height = vislines - 152;\r
1189                 source = windowofs + panadjust + 8;\r
1190                 dest = bufferofs + panadjust + 8;\r
1191                 VW_ScreenToScreen(source, dest, 192/BYTEPIXELS, height);\r
1192                 VW_ClipDrawMPic((pansx+136)/BYTEPIXELS, -(16-height)+pansy, METALPOLEPICM);\r
1193                 source = windowofs + panadjust + 16*SCREENWIDTH + 8*CHARWIDTH;\r
1194                 dest = bufferofs + panadjust + height*SCREENWIDTH + 8;\r
1195                 height = 152;\r
1196         }\r
1197         else\r
1198         {\r
1199                 source = windowofs + panadjust + (152-vislines)*SCREENWIDTH + 16*SCREENWIDTH + 8*CHARWIDTH;\r
1200                 dest = bufferofs + panadjust + 8;\r
1201                 height = vislines;\r
1202         }\r
1203         if (height > 0)\r
1204         {\r
1205                 VW_ScreenToScreen(source, dest, 192/BYTEPIXELS, height);\r
1206         }\r
1207         if (scrollup)\r
1208         {\r
1209                 height = 168-vislines;\r
1210                 source = masterofs + panadjust + vislines*SCREENWIDTH + 8;\r
1211                 dest = bufferofs + panadjust + vislines*SCREENWIDTH + 8;\r
1212                 VW_ScreenToScreen(source, dest, 192/BYTEPIXELS, height);\r
1213                 height = vislines;\r
1214                 source = windowofs + panadjust + 8 - 24/BYTEPIXELS;\r
1215                 dest = bufferofs + panadjust + 8 - 24/BYTEPIXELS;\r
1216                 if (height > 0)\r
1217                         VW_ScreenToScreen(source, dest, 24/BYTEPIXELS, height);\r
1218         }\r
1219         else\r
1220         {\r
1221                 height = vislines + -72;\r
1222                 if (height > 0)\r
1223                 {\r
1224                         source = windowofs + panadjust + 8 - 24/BYTEPIXELS;\r
1225                         dest = bufferofs + panadjust + 8 - 24/BYTEPIXELS;\r
1226                         if (height > 0)\r
1227                                 VW_ScreenToScreen(source, dest, 24/BYTEPIXELS, height);\r
1228                 }\r
1229         }\r
1230         if (vislines >= 72)\r
1231         {\r
1232                 VW_ClipDrawMPic((pansx+40)/BYTEPIXELS, vislines-168+pansy, CORDPICM);\r
1233         }\r
1234         VW_UpdateScreen();\r
1235 }\r
1236 \r
1237 /*\r
1238 ==================\r
1239 =\r
1240 = StatusWindow\r
1241 =\r
1242 ==================\r
1243 */\r
1244 \r
1245 void StatusWindow(void)\r
1246 {\r
1247 #if GRMODE == CGAGR\r
1248 \r
1249         if (Keyboard[sc_A] && Keyboard[sc_2])\r
1250         {\r
1251                 US_CenterWindow(20, 2);\r
1252                 PrintY += 2;\r
1253                 US_Print("Debug keys active");\r
1254                 VW_UpdateScreen();\r
1255                 IN_Ack();\r
1256                 debugok = true;\r
1257         }\r
1258 \r
1259         WindowX = 0;\r
1260         WindowW = 320;\r
1261         WindowY = 0;\r
1262         WindowH = 200;\r
1263         DrawStatusWindow();\r
1264         VW_UpdateScreen();\r
1265         IN_ClearKeysDown();\r
1266         IN_Ack();\r
1267 \r
1268 #else\r
1269 \r
1270         Uint16 oldbufferofs;\r
1271 \r
1272         WindowX = 0;\r
1273         WindowW = 320;\r
1274         WindowY = 0;\r
1275         WindowH = 200;\r
1276 \r
1277         if (Keyboard[sc_A] && Keyboard[sc_2])\r
1278         {\r
1279                 US_CenterWindow(20, 2);\r
1280                 PrintY += 2;\r
1281                 US_Print("Debug keys active");\r
1282                 VW_UpdateScreen();\r
1283                 IN_Ack();\r
1284                 debugok = true;\r
1285         }\r
1286 \r
1287         RF_Refresh();\r
1288         RFL_InitAnimList();\r
1289         oldbufferofs = bufferofs;\r
1290         bufferofs = windowofs = RF_FindFreeBuffer();\r
1291         VW_ScreenToScreen(displayofs, displayofs, 44, 224);     // useless (source and dest offsets are identical)\r
1292         VW_ScreenToScreen(displayofs, masterofs, 44, 224);\r
1293         VW_ScreenToScreen(displayofs, bufferofs, 44, 168);\r
1294         DrawStatusWindow();\r
1295         bufferofs = oldbufferofs;\r
1296         RF_Refresh();\r
1297 \r
1298         SD_PlaySound(SND_SHOWSTATUS);\r
1299         vislines = 16;\r
1300         scrollup = false;\r
1301         RF_SetRefreshHook(ScrollStatusWindow);\r
1302 \r
1303         while (true)\r
1304         {\r
1305                 RF_Refresh();\r
1306                 if (vislines == 168)\r
1307                         break;\r
1308                 vislines = vislines + tics*8;\r
1309                 if (vislines > 168)\r
1310                         vislines = 168;\r
1311         }\r
1312 \r
1313         RF_Refresh();\r
1314         RF_SetRefreshHook(NULL);\r
1315         IN_ClearKeysDown();\r
1316         IN_Ack();\r
1317 \r
1318         SD_PlaySound(SND_HIDESTATUS);\r
1319         vislines -= 16;\r
1320         scrollup = true;\r
1321         RF_SetRefreshHook(ScrollStatusWindow);\r
1322 \r
1323         while (true)\r
1324         {\r
1325                 RF_Refresh();\r
1326                 if (vislines == 0)\r
1327                         break;\r
1328                 vislines = vislines - tics*8;\r
1329                 if (vislines < 0)\r
1330                         vislines = 0;\r
1331         }\r
1332 \r
1333         RF_SetRefreshHook(NULL);\r
1334 \r
1335         scoreobj->x = 0;        //force scorebox to redraw?\r
1336 \r
1337 #endif\r
1338 }\r
1339 \r
1340 //===========================================================================\r
1341 \r
1342 /*\r
1343 ==================\r
1344 =\r
1345 = CenterActor\r
1346 =\r
1347 ==================\r
1348 */\r
1349 \r
1350 void CenterActor(objtype *ob)\r
1351 {\r
1352         Uint16 orgx, orgy;\r
1353 \r
1354         centerlevel = 140;\r
1355         if (ob->x < 152*PIXGLOBAL)\r
1356         {\r
1357                 orgx = 0;\r
1358         }\r
1359         else\r
1360         {\r
1361                 orgx = ob->x - 152*PIXGLOBAL;\r
1362         }\r
1363         if (mapon == 0)\r
1364         {\r
1365                 if (ob->y < 80*PIXGLOBAL)\r
1366                 {\r
1367                         orgy = 0;\r
1368                 }\r
1369                 else\r
1370                 {\r
1371                         orgy = ob->y - 80*PIXGLOBAL;\r
1372                 }\r
1373         }\r
1374         else\r
1375         {\r
1376                 if (ob->bottom < 140*PIXGLOBAL)\r
1377                 {\r
1378                         orgy = 0;\r
1379                 }\r
1380                 else\r
1381                 {\r
1382                         orgy = ob->bottom - 140*PIXGLOBAL;\r
1383                 }\r
1384         }\r
1385         if (!scorescreenkludge)\r
1386         {\r
1387                 RF_NewPosition(orgx, orgy);\r
1388         }\r
1389 \r
1390 //\r
1391 // update limits for onscreen and inactivate checks\r
1392 //\r
1393         originxtilemax = (originxtile + PORTTILESWIDE) - 1;\r
1394         originytilemax = (originytile + PORTTILESHIGH) - 1;\r
1395         inactivateleft = originxtile - INACTIVATEDIST;\r
1396         if (inactivateleft < 0)\r
1397         {\r
1398                 inactivateleft = 0;\r
1399         }\r
1400         inactivateright = originxtilemax + INACTIVATEDIST;\r
1401         if (inactivateright < 0)\r
1402         {\r
1403                 inactivateright = 0;\r
1404         }\r
1405         inactivatetop = originytile - INACTIVATEDIST;\r
1406         if (inactivatetop < 0)\r
1407         {\r
1408                 inactivatetop = 0;\r
1409         }\r
1410         inactivatebottom = originytilemax + INACTIVATEDIST;\r
1411         if (inactivatebottom < 0)\r
1412         {\r
1413                 inactivatebottom = 0;\r
1414         }\r
1415 }\r
1416 \r
1417 //===========================================================================\r
1418 \r
1419 /*\r
1420 ==================\r
1421 =\r
1422 = WorldScrollScreen\r
1423 =\r
1424 = Scroll if Keen is nearing an edge\r
1425 =\r
1426 ==================\r
1427 */\r
1428 \r
1429 void WorldScrollScreen(objtype *ob)\r
1430 {\r
1431         Sint16 xscroll, yscroll;\r
1432 \r
1433         if (keenkilled)\r
1434                 return;\r
1435 \r
1436         if (ob->left < originxglobal + 9*TILEGLOBAL)\r
1437         {\r
1438                 xscroll = ob->left - (originxglobal + 9*TILEGLOBAL);\r
1439         }\r
1440         else if (ob->right > originxglobal + 12*TILEGLOBAL)\r
1441         {\r
1442                 xscroll = ob->right + 16 - (originxglobal + 12*TILEGLOBAL);\r
1443         }\r
1444         else\r
1445         {\r
1446                 xscroll = 0;\r
1447         }\r
1448 \r
1449         if (ob->top < originyglobal + 5*TILEGLOBAL)\r
1450         {\r
1451                 yscroll = ob->top - (originyglobal + 5*TILEGLOBAL);\r
1452         }\r
1453         else if (ob->bottom > originyglobal + 7*TILEGLOBAL)\r
1454         {\r
1455                 yscroll = ob->bottom - (originyglobal + 7*TILEGLOBAL);\r
1456         }\r
1457         else\r
1458         {\r
1459                 yscroll = 0;\r
1460         }\r
1461 \r
1462         if (!xscroll && !yscroll)\r
1463                 return;\r
1464 \r
1465 //\r
1466 // don't scroll more than one tile per frame\r
1467 //\r
1468         if (xscroll >= 0x100)\r
1469         {\r
1470                 xscroll = 0xFF;\r
1471         }\r
1472         else if (xscroll <= -0x100)\r
1473         {\r
1474                 xscroll = -0xFF;\r
1475         }\r
1476         if (yscroll >= 0x100)\r
1477         {\r
1478                 yscroll = 0xFF;\r
1479         }\r
1480         else if (yscroll <= -0x100)\r
1481         {\r
1482                 yscroll = -0xFF;\r
1483         }\r
1484 \r
1485         RF_Scroll(xscroll, yscroll);\r
1486 \r
1487 //\r
1488 // update limits for onscreen and inactivate checks\r
1489 //\r
1490         originxtilemax = (originxtile + PORTTILESWIDE) - 1;\r
1491         originytilemax = (originytile + PORTTILESHIGH) - 1;\r
1492         inactivateleft = originxtile - INACTIVATEDIST;\r
1493         if (inactivateleft < 0)\r
1494         {\r
1495                 inactivateleft = 0;\r
1496         }\r
1497         inactivateright = originxtilemax + INACTIVATEDIST;\r
1498         if (inactivateright < 0)\r
1499         {\r
1500                 inactivateright = 0;\r
1501         }\r
1502         inactivatetop = originytile - INACTIVATEDIST;\r
1503         if (inactivatetop < 0)\r
1504         {\r
1505                 inactivatetop = 0;\r
1506         }\r
1507         inactivatebottom = originytilemax + INACTIVATEDIST;\r
1508         if (inactivatebottom < 0)\r
1509         {\r
1510                 inactivatebottom = 0;\r
1511         }\r
1512 }\r
1513 \r
1514 //===========================================================================\r
1515 \r
1516 /*\r
1517 ==================\r
1518 =\r
1519 = ScrollScreen\r
1520 =\r
1521 = Scroll if Keen is nearing an edge\r
1522 = Set playstate to ex_completes\r
1523 =\r
1524 ==================\r
1525 */\r
1526 \r
1527 void ScrollScreen(objtype *ob)\r
1528 {\r
1529         Sint16 xscroll, yscroll, pix, speed;\r
1530         Uint16 bottom;\r
1531 \r
1532         if (keenkilled)\r
1533                 return;\r
1534 \r
1535 //\r
1536 // walked off edge of map?\r
1537 //\r
1538         if (ob->left < originxmin || ob->right > originxmax + 320*PIXGLOBAL)\r
1539         {\r
1540                 playstate = ex_completed;\r
1541                 return;\r
1542         }\r
1543 \r
1544 //\r
1545 // fallen off bottom of world?\r
1546 //\r
1547         if (ob->bottom > originymax + 13*TILEGLOBAL)\r
1548         {\r
1549                 ob->y -= ob->bottom - (originymax + 13*TILEGLOBAL);\r
1550                 SD_PlaySound(SND_PLUMMET);\r
1551                 godmode = false;\r
1552                 KillKeen();\r
1553                 return;\r
1554         }\r
1555 \r
1556         xscroll=yscroll=0;\r
1557 \r
1558         if (ob->x < originxglobal + 9*TILEGLOBAL)\r
1559         {\r
1560                 xscroll = ob->x - (originxglobal + 9*TILEGLOBAL);\r
1561         }\r
1562         else if (ob->x > originxglobal + 12*TILEGLOBAL)\r
1563         {\r
1564                 xscroll = ob->x - (originxglobal + 12*TILEGLOBAL);\r
1565         }\r
1566 \r
1567         if (ob->state == &s_keenlookup2)\r
1568         {\r
1569                 if (centerlevel+tics > 167)\r
1570                 {\r
1571                         pix = 167-centerlevel;\r
1572                 }\r
1573                 else\r
1574                 {\r
1575                         pix = tics;\r
1576                 }\r
1577                 centerlevel += pix;\r
1578                 yscroll = CONVERT_PIXEL_TO_GLOBAL(-pix);\r
1579         }\r
1580         else if (ob->state == &s_keenlookdown3)\r
1581         {\r
1582                 if (centerlevel-tics < 33)\r
1583                 {\r
1584                         pix = centerlevel + -33;\r
1585                 }\r
1586                 else\r
1587                 {\r
1588                         pix = tics;\r
1589                 }\r
1590                 centerlevel -= pix;\r
1591                 yscroll = CONVERT_PIXEL_TO_GLOBAL(pix);\r
1592         }\r
1593 \r
1594 #ifdef KEEN6\r
1595         if (groundslam)\r
1596         {\r
1597                 static Sint16 shaketable[] = {0,\r
1598                          -64,  -64,  -64,  64,  64,  64,\r
1599                         -200, -200, -200, 200, 200, 200,\r
1600                         -250, -250, -250, 250, 250, 250,\r
1601                         -250, -250, -250, 250, 250, 250\r
1602                 };\r
1603                 yscroll = yscroll + (bottom - (ob->bottom + shaketable[groundslam]));   // BUG: 'bottom' has not been initialized yet!\r
1604         }\r
1605         else\r
1606 #endif\r
1607         if ( (ob->hitnorth || !ob->needtoclip || ob->state == &s_keenholdon))\r
1608         {\r
1609                 if (  ob->state != &s_keenclimbup\r
1610                         && ob->state != &s_keenclimbup2\r
1611                         && ob->state != &s_keenclimbup3\r
1612                         && ob->state != &s_keenclimbup4)\r
1613                 {\r
1614                         yscroll += ob->ymove;\r
1615                         bottom = originyglobal + yscroll + CONVERT_PIXEL_TO_GLOBAL(centerlevel);\r
1616                         if (ob->bottom == bottom)\r
1617                         {\r
1618                                 // player didn't move, no additional scrolling\r
1619                         }\r
1620                         else\r
1621                         {\r
1622                                 if (ob->bottom < bottom)\r
1623                                 {\r
1624                                         pix = bottom - ob->bottom;\r
1625                                 }\r
1626                                 else\r
1627                                 {\r
1628                                         pix = ob->bottom - bottom;\r
1629                                 }\r
1630                                 speed = CONVERT_PIXEL_TO_GLOBAL(pix) >> 7;\r
1631                                 if (speed > 0x30)\r
1632                                 {\r
1633                                         speed = 0x30;\r
1634                                 }\r
1635                                 speed *= tics;\r
1636                                 if (speed < 0x10)\r
1637                                 {\r
1638                                         if (pix < 0x10)\r
1639                                         {\r
1640                                                 speed = pix;\r
1641                                         }\r
1642                                         else\r
1643                                         {\r
1644                                                 speed = 0x10;\r
1645                                         }\r
1646                                 }\r
1647                                 if (ob->bottom < bottom)\r
1648                                 {\r
1649                                         yscroll -= speed;\r
1650                                 }\r
1651                                 else\r
1652                                 {\r
1653                                         yscroll += speed;\r
1654                                 }\r
1655                         }\r
1656                 }\r
1657         }\r
1658         else\r
1659         {\r
1660                 centerlevel = 140;\r
1661         }\r
1662 \r
1663         pix = (ob->bottom-32*PIXGLOBAL)-(originyglobal+yscroll);\r
1664         if (pix < 0)\r
1665         {\r
1666                 yscroll += pix;\r
1667         }\r
1668         pix = (ob->bottom+32*PIXGLOBAL)-(originyglobal+yscroll+200*PIXGLOBAL);\r
1669         if (pix > 0)\r
1670         {\r
1671                 yscroll += pix;\r
1672         }\r
1673 \r
1674         if (xscroll == 0 && yscroll == 0)\r
1675                 return;\r
1676 \r
1677 //\r
1678 // don't scroll more than one tile per frame\r
1679 //\r
1680         if (xscroll >= 0x100)\r
1681         {\r
1682                 xscroll = 0xFF;\r
1683         }\r
1684         else if (xscroll <= -0x100)\r
1685         {\r
1686                 xscroll = -0xFF;\r
1687         }\r
1688         if (yscroll >= 0x100)\r
1689         {\r
1690                 yscroll = 0xFF;\r
1691         }\r
1692         else if (yscroll <= -0x100)\r
1693         {\r
1694                 yscroll = -0xFF;\r
1695         }\r
1696         RF_Scroll(xscroll, yscroll);\r
1697 \r
1698 //\r
1699 // update limits for onscreen and inactivate checks\r
1700 //\r
1701         originxtilemax = (originxtile + PORTTILESWIDE) - 1;\r
1702         originytilemax = (originytile + PORTTILESHIGH) - 1;\r
1703         inactivateleft = originxtile - INACTIVATEDIST;\r
1704         if (inactivateleft < 0)\r
1705         {\r
1706                 inactivateleft = 0;\r
1707         }\r
1708         inactivateright = originxtilemax + INACTIVATEDIST;\r
1709         if (inactivateright < 0)\r
1710         {\r
1711                 inactivateright = 0;\r
1712         }\r
1713         inactivatetop = originytile - INACTIVATEDIST;\r
1714         if (inactivatetop < 0)\r
1715         {\r
1716                 inactivatetop = 0;\r
1717         }\r
1718         inactivatebottom = originytilemax + INACTIVATEDIST;\r
1719         if (inactivatebottom < 0)\r
1720         {\r
1721                 inactivatebottom = 0;\r
1722         }\r
1723 }\r
1724 \r
1725 //===========================================================================\r
1726 \r
1727 \r
1728 /*\r
1729 #############################################################################\r
1730 \r
1731                                   The objarray data structure\r
1732 \r
1733 #############################################################################\r
1734 \r
1735 Objarray contains structures for every actor currently playing.  The structure\r
1736 is accessed as a linked list starting at *player, ending when ob->next ==\r
1737 NULL.  GetNewObj inserts a new object at the end of the list, meaning that\r
1738 if an actor spawns another actor, the new one WILL get to think and react the\r
1739 same frame.  RemoveObj unlinks the given object and returns it to the free\r
1740 list, but does not damage the objects ->next pointer, so if the current object\r
1741 removes itself, a linked list following loop can still safely get to the\r
1742 next element.\r
1743 \r
1744 <backwardly linked free list>\r
1745 \r
1746 #############################################################################\r
1747 */\r
1748 \r
1749 \r
1750 /*\r
1751 =========================\r
1752 =\r
1753 = InitObjArray\r
1754 =\r
1755 = Call to clear out the entire object list, returning them all to the free\r
1756 = list.  Allocates a special spot for the player.\r
1757 =\r
1758 =========================\r
1759 */\r
1760 \r
1761 void InitObjArray(void)\r
1762 {\r
1763         Sint16 i;\r
1764 \r
1765         for (i=0; i<MAXACTORS; i++)\r
1766         {\r
1767                 objarray[i].prev = &objarray[i+1];\r
1768                 objarray[i].next = NULL;\r
1769         }\r
1770 \r
1771         objarray[MAXACTORS-1].prev = NULL;\r
1772 \r
1773         objfreelist = &objarray[0];\r
1774         lastobj = NULL;\r
1775 \r
1776         objectcount = 0;\r
1777 \r
1778 //\r
1779 // give the player and score the first free spots\r
1780 //\r
1781         GetNewObj(false);\r
1782         player = new;\r
1783         GetNewObj(false);\r
1784         scoreobj = new;\r
1785 }\r
1786 \r
1787 //===========================================================================\r
1788 \r
1789 /*\r
1790 =========================\r
1791 =\r
1792 = GetNewObj\r
1793 =\r
1794 = Sets the global variable new to point to a free spot in objarray.\r
1795 = The free spot is inserted at the end of the liked list\r
1796 =\r
1797 = When the object list is full, the caller can either have it bomb out or\r
1798 = return a dummy object pointer that will never get used\r
1799 =\r
1800 = Returns -1 when list was full, otherwise returns 0.\r
1801 =\r
1802 =========================\r
1803 */\r
1804 \r
1805 Sint16 GetNewObj(boolean usedummy)\r
1806 {\r
1807         if (!objfreelist)\r
1808         {\r
1809                 if (usedummy)\r
1810                 {\r
1811                         new = &dummyobj;\r
1812                         return -1;\r
1813                 }\r
1814                 Quit("GetNewObj: No free spots in objarray!");\r
1815         }\r
1816         new = objfreelist;\r
1817         objfreelist = new->prev;\r
1818         memset(new, 0, sizeof(*new));\r
1819         if (lastobj)\r
1820         {\r
1821                 lastobj->next = new;\r
1822         }\r
1823         new->prev = lastobj;    // new->next is allready NULL from memset\r
1824 \r
1825         new->active = ac_yes;\r
1826         new->needtoclip = cl_midclip;\r
1827         lastobj = new;\r
1828 \r
1829         objectcount++;\r
1830         return 0;\r
1831 }\r
1832 \r
1833 //===========================================================================\r
1834 \r
1835 /*\r
1836 =========================\r
1837 =\r
1838 = RemoveObj\r
1839 =\r
1840 = Add the given object back into the free list, and unlink it from it's\r
1841 = neighbors\r
1842 =\r
1843 =========================\r
1844 */\r
1845 \r
1846 void RemoveObj(objtype *ob)\r
1847 {\r
1848         if (ob == player)\r
1849                 Quit("RemoveObj: Tried to remove the player!");\r
1850 \r
1851 //\r
1852 // erase it from the refresh manager\r
1853 //\r
1854         RF_RemoveSprite(&ob->sprite);\r
1855         if (ob->obclass == stunnedobj)\r
1856         {\r
1857                 RF_RemoveSprite((void **)&ob->temp3);\r
1858         }\r
1859 \r
1860 //\r
1861 // fix the next object's back link\r
1862 //\r
1863         if (ob == lastobj)\r
1864         {\r
1865                 lastobj = ob->prev;\r
1866         }\r
1867         else\r
1868         {\r
1869                 ob->next->prev = ob->prev;\r
1870         }\r
1871 \r
1872 //\r
1873 // fix the previous object's forward link\r
1874 //\r
1875         ob->prev->next = ob->next;\r
1876 \r
1877 //\r
1878 // add it back in to the free list\r
1879 //\r
1880         ob->prev = objfreelist;\r
1881         objfreelist = ob;\r
1882 }\r
1883 \r
1884 //==========================================================================\r
1885 \r
1886 /*\r
1887 ====================\r
1888 =\r
1889 = GivePoints\r
1890 =\r
1891 = Grants extra men at 20k,40k,80k,160k,320k\r
1892 =\r
1893 ====================\r
1894 */\r
1895 \r
1896 void GivePoints(Uint16 points)\r
1897 {\r
1898         gamestate.score += points;\r
1899         if (!DemoMode && gamestate.score >= gamestate.nextextra)\r
1900         {\r
1901                 SD_PlaySound(SND_EXTRAKEEN);\r
1902                 gamestate.lives++;\r
1903                 gamestate.nextextra *= 2;\r
1904         }\r
1905 }\r
1906 \r
1907 //==========================================================================\r
1908 \r
1909 /*\r
1910 ===================\r
1911 =\r
1912 = PollControls\r
1913 =\r
1914 ===================\r
1915 */\r
1916 \r
1917 void PollControls(void)\r
1918 {\r
1919         IN_ReadControl(0, &c);\r
1920         if (c.yaxis != -1)\r
1921                 upheld = false;\r
1922 \r
1923         if (GravisGamepad && !DemoMode)\r
1924         {\r
1925                 jumpbutton = GravisAction[ga_Jump];\r
1926                 pogobutton = GravisAction[ga_Pogo];\r
1927                 firebutton = GravisAction[ga_Fire];\r
1928                 if (!jumpbutton)\r
1929                         jumpheld = false;\r
1930                 if (!pogobutton)\r
1931                         pogoheld = false;\r
1932                 if (!firebutton)\r
1933                         fireheld = false;\r
1934         }\r
1935         else if (oldshooting || DemoMode)\r
1936         {\r
1937                 if (c.button0 && c.button1)\r
1938                 {\r
1939                         firebutton = true;\r
1940                         jumpbutton = pogobutton = jumpheld = pogoheld = false;\r
1941                 }\r
1942                 else\r
1943                 {\r
1944                         firebutton = fireheld = false;\r
1945                         if (c.button0)\r
1946                         {\r
1947                                 jumpbutton = true;\r
1948                         }\r
1949                         else\r
1950                         {\r
1951                                 jumpbutton = jumpheld = false;\r
1952                         }\r
1953                         if (c.button1)\r
1954                         {\r
1955                                 if (oldfirecount <= 8)\r
1956                                 {\r
1957                                         oldfirecount = oldfirecount + tics;\r
1958                                 }\r
1959                                 else\r
1960                                 {\r
1961                                         pogobutton = true;\r
1962                                 }\r
1963                         }\r
1964                         else\r
1965                         {\r
1966                                 if (oldfirecount != 0)\r
1967                                 {\r
1968                                         pogobutton = true;\r
1969                                 }\r
1970                                 else\r
1971                                 {\r
1972                                         pogobutton = pogoheld = false;\r
1973                                 }\r
1974                                 oldfirecount = 0;\r
1975                         }\r
1976                 }\r
1977         }\r
1978         else\r
1979         {\r
1980                 jumpbutton = c.button0;\r
1981                 pogobutton = c.button1;\r
1982                 firebutton = Keyboard[firescan];\r
1983                 if (!jumpbutton)\r
1984                         jumpheld = false;\r
1985                 if (!pogobutton)\r
1986                         pogoheld = false;\r
1987                 if (!firebutton)\r
1988                         fireheld = false;\r
1989         }\r
1990 }\r
1991 \r
1992 //==========================================================================\r
1993 \r
1994 \r
1995 /*\r
1996 =================\r
1997 =\r
1998 = StopMusic\r
1999 =\r
2000 =================\r
2001 */\r
2002 \r
2003 void StopMusic(void)\r
2004 {\r
2005         Sint16 i;\r
2006 \r
2007         SD_MusicOff();\r
2008         for (i=0; i<LASTMUSIC; i++)\r
2009         {\r
2010                 if (audiosegs[STARTMUSIC+i])\r
2011                 {\r
2012 #ifdef FIX_MUSIC_MEMORY_ISSUES\r
2013                         //unlock any music blocks so that they can be purged\r
2014                         MM_SetLock(&(memptr)audiosegs[STARTMUSIC+i], false);\r
2015 #endif\r
2016                         MM_SetPurge(&(memptr)audiosegs[STARTMUSIC+i], PURGE_FIRST);\r
2017                 }\r
2018         }\r
2019 }\r
2020 \r
2021 //==========================================================================\r
2022 \r
2023 /*\r
2024 =================\r
2025 =\r
2026 = StartMusic\r
2027 =\r
2028 =================\r
2029 */\r
2030 \r
2031 void StartMusic(Uint16 num)\r
2032 {\r
2033         static Sint16 songs[] =\r
2034         {\r
2035 #if defined KEEN4\r
2036                 SHADOWS_MUS,\r
2037                 KICKPANT_MUS,\r
2038                 OASIS_MUS,\r
2039                 OASIS_MUS,\r
2040                 TOOHOT_MUS,\r
2041                 TOOHOT_MUS,\r
2042                 KICKPANT_MUS,\r
2043                 OASIS_MUS,\r
2044                 VEGGIES_MUS,\r
2045                 VEGGIES_MUS,\r
2046                 VEGGIES_MUS,\r
2047                 TOOHOT_MUS,\r
2048                 TOOHOT_MUS,\r
2049                 TOOHOT_MUS,\r
2050                 TOOHOT_MUS,\r
2051                 TOOHOT_MUS,\r
2052                 TOOHOT_MUS,\r
2053                 VEGGIES_MUS,\r
2054                 OASIS_MUS,\r
2055                 -1\r
2056 #elif defined KEEN5\r
2057                 ROBOROCK_MUS,\r
2058                 WEDNESDY_MUS,\r
2059                 BREATHE_MUS,\r
2060                 SPHEREFUL_MUS,\r
2061                 TIGHTER_MUS,\r
2062                 SPHEREFUL_MUS,\r
2063                 TIGHTER_MUS,\r
2064                 SPHEREFUL_MUS,\r
2065                 TIGHTER_MUS,\r
2066                 SPHEREFUL_MUS,\r
2067                 TIGHTER_MUS,\r
2068                 SNOOPING_MUS,\r
2069                 FEARSOME_MUS,\r
2070                 BAGPIPES_MUS,\r
2071                 FANFARE_MUS,\r
2072                 SKATING_MUS,\r
2073                 ROCK_ME_MUS,\r
2074                 HAVING_T_MUS,\r
2075                 CAMEIN_MUS,\r
2076                 SHIKAIRE_MUS,\r
2077 #elif defined KEEN6\r
2078                 ALIENATE_MUS,\r
2079                 FASTER_MUS,\r
2080                 BRERTAR_MUS,\r
2081                 MAMSNAKE_MUS,\r
2082                 MAMSNAKE_MUS,\r
2083                 MAMSNAKE_MUS,\r
2084                 METAL_MUS,\r
2085                 TOFUTURE_MUS,\r
2086                 METAL_MUS,\r
2087                 BRERTAR_MUS,\r
2088                 FASTER_MUS,\r
2089                 TOFUTURE_MUS,\r
2090                 BRERTAR_MUS,\r
2091                 SPACFUNK_MUS,\r
2092                 SPACFUNK_MUS,\r
2093                 OMINOUS_MUS,\r
2094                 TOFUTURE_MUS,\r
2095                 WONDER_MUS,\r
2096                 WONDER_MUS,\r
2097                 WONDER_MUS\r
2098 #endif\r
2099         };\r
2100 \r
2101         Sint16 song;\r
2102         boolean wasfaded;\r
2103 \r
2104         if (num >= ARRAYLENGTH(songs) && num != 0xFFFF)\r
2105         {\r
2106                 Quit("StartMusic() - bad level number");\r
2107         }\r
2108 \r
2109 #ifdef FIX_MUSIC_MEMORY_ISSUES\r
2110         StopMusic();\r
2111 #else\r
2112         SD_MusicOff();\r
2113 #endif\r
2114 \r
2115 #ifdef KEEN4\r
2116         if (num == 0xFFFF)\r
2117         {\r
2118                 song = WONDER_MUS;\r
2119         }\r
2120         else\r
2121         {\r
2122                 song = songs[num];\r
2123         }\r
2124 #else\r
2125         song = songs[num];\r
2126 #endif\r
2127 \r
2128         if (song == -1 || MusicMode != smm_AdLib)\r
2129         {\r
2130                 return;\r
2131         }\r
2132 \r
2133         MM_BombOnError(false);\r
2134         CA_CacheAudioChunk(STARTMUSIC+song);\r
2135         MM_BombOnError(true);\r
2136         if (mmerror)\r
2137         {\r
2138                 mmerror = false;\r
2139                 if (!DemoMode)\r
2140                 {\r
2141                         US_CenterWindow(20, 8);\r
2142                         PrintY += 20;\r
2143                         US_CPrint("Insufficient memory\nfor background music!");\r
2144                         VW_UpdateScreen();\r
2145                         wasfaded = screenfaded;\r
2146                         if (wasfaded)\r
2147                         {\r
2148                                 VW_SetDefaultColors();\r
2149                         }\r
2150                         IN_ClearKeysDown();\r
2151                         IN_UserInput(3*TickBase, false);\r
2152                         if (wasfaded)\r
2153                         {\r
2154                                 VW_FadeOut();\r
2155                         }\r
2156                 }\r
2157         }\r
2158         else\r
2159         {\r
2160 #ifdef FIX_MUSIC_MEMORY_ISSUES\r
2161                 //The current music should be locked, so the memory manager will not\r
2162                 //mess with it when compressing memory blocks in MM_SortMem().\r
2163                 MM_SetLock(&(memptr)audiosegs[STARTMUSIC+song], true);\r
2164 #endif\r
2165                 SD_StartMusic((MusicGroup far *)audiosegs[STARTMUSIC+song]);\r
2166         }\r
2167 }\r
2168 \r
2169 //==========================================================================\r
2170 \r
2171 \r
2172 /*\r
2173 ===================\r
2174 =\r
2175 = PlayLoop\r
2176 =\r
2177 ===================\r
2178 */\r
2179 \r
2180 void PlayLoop(void)\r
2181 {\r
2182         objtype *check;\r
2183 \r
2184         StartMusic(gamestate.mapon);\r
2185         fireheld = pogoheld = upheld = jumpheld = false;\r
2186         ingame = true;\r
2187         playstate = ex_stillplaying;\r
2188         invincible = keenkilled = oldfirecount = 0;\r
2189 \r
2190         CenterActor(player);\r
2191 \r
2192         if (DemoMode)\r
2193         {\r
2194                 US_InitRndT(false);\r
2195         }\r
2196         else\r
2197         {\r
2198                 US_InitRndT(true);\r
2199         }\r
2200         TimeCount = lasttimecount = tics = 3;\r
2201 \r
2202         do\r
2203         {\r
2204                 PollControls();\r
2205 \r
2206 //\r
2207 // go through state changes and propose movements\r
2208 //\r
2209                 for (obj=player; obj; obj=obj->next)\r
2210                 {\r
2211                         if (!obj->active && obj->tileright >= originxtile-1\r
2212                                 && obj->tileleft <= originxtilemax+1 && obj->tiletop <= originytilemax+1\r
2213                                 && obj->tilebottom >= originytile-1)\r
2214                         {\r
2215                                 obj->needtoreact = true;\r
2216                                 obj->active = ac_yes;\r
2217                         }\r
2218                         if (obj->active)\r
2219                         {\r
2220                                 if (obj->tileright < inactivateleft\r
2221                                         || obj->tileleft > inactivateright\r
2222                                         || obj->tiletop > inactivatebottom\r
2223                                         || obj->tilebottom < inactivatetop)\r
2224                                 {\r
2225                                         if (obj->active == ac_removable)\r
2226                                         {\r
2227                                                 RemoveObj(obj);\r
2228                                                 continue;\r
2229                                         }\r
2230                                         else if (obj->active != ac_allways)\r
2231                                         {\r
2232                                                 if (US_RndT() < tics*2 || screenfaded || loadedgame)\r
2233                                                 {\r
2234                                                         RF_RemoveSprite(&obj->sprite);\r
2235                                                         if (obj->obclass == stunnedobj)\r
2236                                                                 RF_RemoveSprite((void **)&obj->temp3);\r
2237                                                         obj->active = ac_no;\r
2238                                                         continue;\r
2239                                                 }\r
2240                                         }\r
2241                                 }\r
2242                                 StateMachine(obj);\r
2243                         }\r
2244                 }\r
2245 \r
2246                 if (gamestate.riding)\r
2247                 {\r
2248                         HandleRiding(player);\r
2249                 }\r
2250 \r
2251 //\r
2252 // check for and handle collisions between objects\r
2253 //\r
2254                 for (obj=player; obj; obj=obj->next)\r
2255                 {\r
2256                         if (obj->active)\r
2257                         {\r
2258                                 for (check=obj->next; check; check=check->next)\r
2259                                 {\r
2260                                         if (!check->active)\r
2261                                         {\r
2262                                                 continue;\r
2263                                         }\r
2264                                         if (obj->right > check->left && obj->left < check->right\r
2265                                                 && obj->top < check->bottom && obj->bottom > check->top)\r
2266                                         {\r
2267                                                 if (obj->state->contact)\r
2268                                                 {\r
2269                                                         obj->state->contact(obj, check);\r
2270                                                 }\r
2271                                                 if (check->state->contact)\r
2272                                                 {\r
2273                                                         check->state->contact(check, obj);\r
2274                                                 }\r
2275                                                 if (obj->obclass == nothing)    //useless -- obclass is NOT set to nothing by RemoveObj\r
2276                                                 {\r
2277                                                         break;\r
2278                                                 }\r
2279                                         }\r
2280                                 }\r
2281                         }\r
2282                 }\r
2283 \r
2284 //\r
2285 // check intiles\r
2286 //\r
2287                 if (mapon != 0)\r
2288                 {\r
2289                         CheckInTiles(player);\r
2290                 }\r
2291                 else\r
2292                 {\r
2293                         CheckWorldInTiles(player);\r
2294                 }\r
2295 \r
2296 //\r
2297 // react to whatever happened, and post sprites to the refresh manager\r
2298 //\r
2299                 for (obj=player; obj; obj=obj->next)\r
2300                 {\r
2301                         if (!obj->active)\r
2302                         {\r
2303                                 continue;\r
2304                         }\r
2305                         if (obj->tilebottom >= mapheight-1)\r
2306                         {\r
2307                                 if (obj->obclass == keenobj)\r
2308                                 {\r
2309                                         playstate = ex_died;\r
2310                                 }\r
2311                                 else\r
2312                                 {\r
2313                                         RemoveObj(obj);\r
2314                                 }\r
2315                                 continue;\r
2316                         }\r
2317                         if (obj->needtoreact && obj->state->react)\r
2318                         {\r
2319                                 obj->needtoreact = false;\r
2320                                 obj->state->react(obj);\r
2321                         }\r
2322                 }\r
2323 \r
2324 //\r
2325 // scroll the screen and update the score box\r
2326 //\r
2327 #ifdef KEEN4\r
2328                 if (mapon != 0 && mapon != 17)\r
2329 #else\r
2330                 if (mapon != 0)\r
2331 #endif\r
2332                 {\r
2333                         ScrollScreen(player);\r
2334                 }\r
2335                 else\r
2336                 {\r
2337                         WorldScrollScreen(player);\r
2338                 }\r
2339                 UpdateScore(scoreobj);\r
2340                 if (loadedgame)\r
2341                 {\r
2342                         loadedgame = false;\r
2343                 }\r
2344 \r
2345 //\r
2346 // update the screen and calculate the number of tics it took to execute\r
2347 // this cycle of events (for adaptive timing of next cycle)\r
2348 //\r
2349                 RF_Refresh();\r
2350 \r
2351                 if (invincible)\r
2352                 {\r
2353                         if ((invincible = invincible - tics) < 0)\r
2354                                 invincible = 0;\r
2355                 }\r
2356 \r
2357 #ifdef KEEN6\r
2358                 if (groundslam)\r
2359                 {\r
2360                         if ((groundslam = groundslam - tics) < 0)\r
2361                                 groundslam = 0;\r
2362                 }\r
2363 #endif\r
2364 //\r
2365 // single step debug mode\r
2366 //\r
2367                 if (singlestep)\r
2368                 {\r
2369                         VW_WaitVBL(14); //reduces framerate to 5 fps on VGA or 4.3 fps on EGA cards\r
2370                         lasttimecount = TimeCount;\r
2371                 }\r
2372 //\r
2373 // extra VBLs debug mode\r
2374 //\r
2375                 if (extravbls)\r
2376                 {\r
2377                         VW_WaitVBL(extravbls);\r
2378                 }\r
2379 \r
2380 //\r
2381 // handle user inputs\r
2382 //\r
2383                 if (DemoMode == demo_Playback)\r
2384                 {\r
2385                         if (!screenfaded && IN_IsUserInput())\r
2386                         {\r
2387                                 playstate = ex_completed;\r
2388                                 if (LastScan != sc_F1)\r
2389                                 {\r
2390                                         LastScan = sc_Space;\r
2391                                 }\r
2392                         }\r
2393                 }\r
2394                 else if (DemoMode == demo_PlayDone)\r
2395                 {\r
2396                         playstate = ex_completed;\r
2397                 }\r
2398                 else\r
2399                 {\r
2400                         CheckKeys();\r
2401                 }\r
2402 \r
2403 //\r
2404 // E-N-D cheat\r
2405 //\r
2406                 if (Keyboard[sc_E] && Keyboard[sc_N] && Keyboard[sc_D])\r
2407                 {\r
2408 #if defined KEEN4\r
2409                         gamestate.rescued = 7;\r
2410                         playstate = ex_rescued;\r
2411 #elif defined KEEN5\r
2412                         playstate = ex_qedbroke;\r
2413 #elif defined KEEN6\r
2414                         playstate = ex_molly;\r
2415 #endif\r
2416                 }\r
2417 \r
2418         } while (playstate == ex_stillplaying);\r
2419 \r
2420         ingame = false;\r
2421         StopMusic();\r
2422 }