OSDN Git Service

02bdccdbea52ecbd4e86677bfd6cb6a876b4b307
[proj16/16.git] / src / lib / hb / kd_play.c
1 /* Keen Dreams Source Code\r
2  * Copyright (C) 2014 Javier M. Chavez\r
3  *\r
4  * This program is free software; you can redistribute it and/or modify\r
5  * it under the terms of the GNU General Public License as published by\r
6  * the Free Software Foundation; either version 2 of the License, or\r
7  * (at your option) any later version.\r
8  *\r
9  * This program is distributed in the hope that it will be useful,\r
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
12  * GNU General Public License for more details.\r
13  *\r
14  * You should have received a copy of the GNU General Public License along\r
15  * with this program; if not, write to the Free Software Foundation, Inc.,\r
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
17  */\r
18 \r
19 // KD_PLAY.C\r
20 \r
21 #include "KD_DEF.H"\r
22 #pragma hdrstop\r
23 \r
24 /*\r
25 =============================================================================\r
26 \r
27                                                  LOCAL CONSTANTS\r
28 \r
29 =============================================================================\r
30 */\r
31 \r
32 #define INACTIVATEDIST  10\r
33 \r
34 #define MAXMOVE (TILEGLOBAL-17)\r
35 \r
36 #define NUMLUMPS        22\r
37 \r
38 \r
39 #define CONTROLSLUMP    0\r
40 #define KEENLUMP                1\r
41 #define WORLDKEENLUMP   2\r
42 #define BROCCOLUMP              3\r
43 #define TOMATLUMP       4\r
44 #define CARROTLUMP              5\r
45 #define ASPARLUMP               6\r
46 #define GRAPELUMP               7\r
47 #define TATERLUMP               8\r
48 #define CARTLUMP                9\r
49 #define FRENCHYLUMP             10\r
50 #define MELONLUMP               11\r
51 #define SQUASHLUMP              12\r
52 #define APELLUMP                13\r
53 #define PEALUMP                 14\r
54 #define BOOBUSLUMP              15\r
55 \r
56 /*\r
57 =============================================================================\r
58 \r
59                                                  GLOBAL VARIABLES\r
60 \r
61 =============================================================================\r
62 */\r
63 \r
64 exittype        playstate;\r
65 gametype        gamestate;\r
66 \r
67 boolean         button0held,button1held;\r
68 objtype         *new,*check,*player,*scoreobj;\r
69 \r
70 unsigned        originxtilemax,originytilemax;\r
71 \r
72 ControlInfo     c;\r
73 \r
74 objtype dummyobj;\r
75 \r
76 char            *levelnames[21] =\r
77 {\r
78 "The Land of Tuberia",\r
79 "Horseradish Hill",\r
80 "The Melon Mines",\r
81 "Bridge Bottoms",\r
82 "Rhubarb Rapids",\r
83 "Parsnip Pass",\r
84 "Level 6",\r
85 "Spud City",\r
86 "Level 8",\r
87 "Apple Acres",\r
88 "Grape Grove",\r
89 "Level 11",\r
90 "Brussels Sprout Bay",\r
91 "Level 13",\r
92 "Squash Swamp",\r
93 "Boobus' Chamber",\r
94 "Castle Tuberia",\r
95 "",\r
96 "Title Page"\r
97 };\r
98 \r
99 \r
100 /*\r
101 =============================================================================\r
102 \r
103                                                  LOCAL VARIABLES\r
104 \r
105 =============================================================================\r
106 */\r
107 \r
108 // for asm scaning of map planes\r
109 unsigned        mapx,mapy,mapxcount,mapycount,maptile,mapspot;\r
110 \r
111 int                     plummet;\r
112 \r
113 int                     objectcount;\r
114 \r
115 objtype         objarray[MAXACTORS],*lastobj,*objfreelist;\r
116 \r
117 int                     oldtileleft,oldtiletop,oldtileright,oldtilebottom,oldtilemidx;\r
118 int                     oldleft,oldtop,oldright,oldbottom,oldmidx;\r
119 int                     leftmoved,topmoved,rightmoved,bottommoved,midxmoved;\r
120 \r
121 int                     topmove,bottommove,midxmove;\r
122 \r
123 int                     inactivateleft,inactivateright,inactivatetop,inactivatebottom;\r
124 \r
125 int                     fadecount;\r
126 \r
127 boolean         bombspresent;\r
128 \r
129 boolean         lumpneeded[NUMLUMPS];\r
130 int                     lumpstart[NUMLUMPS] =\r
131 {\r
132 CONTROLS_LUMP_START,\r
133 KEEN_LUMP_START,\r
134 WORLDKEEN_LUMP_START,\r
135 BROCCOLASH_LUMP_START,\r
136 TOMATO_LUMP_START,\r
137 CARROT_LUMP_START,\r
138 ASPAR_LUMP_START,\r
139 GRAPE_LUMP_START,\r
140 TATER_LUMP_START,\r
141 CANTA_LUMP_START,\r
142 FRENCHY_LUMP_START,\r
143 MELONLIPS_LUMP_START,\r
144 SQUASHER_LUMP_START,\r
145 APEL_LUMP_START,\r
146 PEAS_LUMP_START,\r
147 BOOBUS_LUMP_START,\r
148 };\r
149 \r
150 int                     lumpend[NUMLUMPS] =\r
151 {\r
152 CONTROLS_LUMP_END,\r
153 KEEN_LUMP_END,\r
154 WORLDKEEN_LUMP_END,\r
155 BROCCOLASH_LUMP_END,\r
156 TOMATO_LUMP_END,\r
157 CARROT_LUMP_END,\r
158 ASPAR_LUMP_END,\r
159 GRAPE_LUMP_END,\r
160 TATER_LUMP_END,\r
161 CANTA_LUMP_END,\r
162 FRENCHY_LUMP_END,\r
163 MELONLIPS_LUMP_END,\r
164 SQUASHER_LUMP_END,\r
165 APEL_LUMP_END,\r
166 PEAS_LUMP_END,\r
167 BOOBUS_LUMP_END,\r
168 };\r
169 \r
170 \r
171 void    CheckKeys (void);\r
172 void    CalcInactivate (void);\r
173 void    InitObjArray (void);\r
174 void    GetNewObj (boolean usedummy);\r
175 void    RemoveObj (objtype *gone);\r
176 void    ScanInfoPlane (void);\r
177 void    PatchWorldMap (void);\r
178 void    MarkTileGraphics (void);\r
179 void    FadeAndUnhook (void);\r
180 void    SetupGameLevel (boolean loadnow);\r
181 void    ScrollScreen (void);\r
182 void    MoveObjVert (objtype *ob, int ymove);\r
183 void    MoveObjHoriz (objtype *ob, int xmove);\r
184 void    GivePoints (unsigned points);\r
185 void    ClipToEnds (objtype *ob);\r
186 void    ClipToEastWalls (objtype *ob);\r
187 void    ClipToWestWalls (objtype *ob);\r
188 void    ClipToWalls (objtype *ob);\r
189 void    ClipToSpriteSide (objtype *push, objtype *solid);\r
190 void    ClipToSprite (objtype *push, objtype *solid, boolean squish);\r
191 int     DoActor (objtype *ob,int tics);\r
192 void    StateMachine (objtype *ob);\r
193 void    NewState (objtype *ob,statetype *state);\r
194 void    PlayLoop (void);\r
195 void    GameLoop (void);\r
196 \r
197 //===========================================================================\r
198 \r
199 /*\r
200 =====================\r
201 =\r
202 = CheckKeys\r
203 =\r
204 =====================\r
205 */\r
206 \r
207 void CheckKeys (void)\r
208 {\r
209         if (screenfaded)                        // don't do anything with a faded screen\r
210                 return;\r
211 \r
212 //\r
213 // space for status screen\r
214 //\r
215         if (Keyboard[sc_Space])\r
216         {\r
217                 StatusWindow ();\r
218                 IN_ClearKeysDown();\r
219                 RF_ForceRefresh();\r
220                 lasttimecount = TimeCount;\r
221         }\r
222 \r
223 //\r
224 // pause key wierdness can't be checked as a scan code\r
225 //\r
226         if (Paused)\r
227         {\r
228                 VW_FixRefreshBuffer ();\r
229                 US_CenterWindow (8,3);\r
230                 US_PrintCentered ("PAUSED");\r
231                 VW_UpdateScreen ();\r
232                 IN_Ack();\r
233                 RF_ForceRefresh ();\r
234                 Paused = false;\r
235         }\r
236 \r
237 //\r
238 // F1-F7/ESC to enter control panel\r
239 //\r
240         if ( (LastScan >= sc_F1 && LastScan <= sc_F7) || LastScan == sc_Escape)\r
241         {\r
242                 VW_FixRefreshBuffer ();\r
243                 US_CenterWindow (20,8);\r
244                 US_CPrint ("Loading");\r
245                 VW_UpdateScreen ();\r
246                 US_ControlPanel();\r
247                 IN_ClearKeysDown();\r
248                 if (restartgame)\r
249                         playstate = resetgame;\r
250                 else if (!loadedgame)\r
251                         RF_ForceRefresh();              // don't refresh if loading a new game\r
252 \r
253                 lasttimecount = TimeCount;\r
254         }\r
255 \r
256 //\r
257 // F10-? debug keys\r
258 //\r
259         if (Keyboard[sc_F10] && DebugKeys() )\r
260         {\r
261                 RF_ForceRefresh();\r
262                 lasttimecount = TimeCount;\r
263         }\r
264 \r
265 }\r
266 \r
267 //===========================================================================\r
268 \r
269 \r
270 /*\r
271 =======================\r
272 =\r
273 = CalcInactivate\r
274 =\r
275 =======================\r
276 */\r
277 \r
278 void CalcInactivate (void)\r
279 {\r
280         originxtilemax = originxtile+PORTTILESWIDE-1;\r
281         originytilemax = originytile+PORTTILESHIGH-1;\r
282 \r
283         inactivateleft = originxtile-INACTIVATEDIST;\r
284         if (inactivateleft < 0)\r
285                 inactivateleft = 0;\r
286         inactivateright = originxtilemax+INACTIVATEDIST;\r
287         inactivatetop = originytile-INACTIVATEDIST;\r
288         if (inactivatetop < 0)\r
289                 inactivatetop = 0;\r
290         inactivatebottom = originytilemax+INACTIVATEDIST;\r
291 }\r
292 \r
293 \r
294 //===========================================================================\r
295 \r
296 \r
297 /*\r
298 #############################################################################\r
299 \r
300                                   The objarray data structure\r
301 \r
302 #############################################################################\r
303 \r
304 Objarray containt structures for every actor currently playing.  The structure\r
305 is accessed as a linked list starting at *player, ending when ob->next ==\r
306 NULL.  GetNewObj inserts a new object at the end of the list, meaning that\r
307 if an actor spawn another actor, the new one WILL get to think and react the\r
308 same frame.  RemoveObj unlinks the given object and returns it to the free\r
309 list, but does not damage the objects ->next pointer, so if the current object\r
310 removes itself, a linked list following loop can still safely get to the\r
311 next element.\r
312 \r
313 <backwardly linked free list>\r
314 \r
315 #############################################################################\r
316 */\r
317 \r
318 \r
319 /*\r
320 =========================\r
321 =\r
322 = InitObjArray\r
323 =\r
324 = Call to clear out the entire object list, returning them all to the free\r
325 = list.  Allocates a special spot for the player.\r
326 =\r
327 =========================\r
328 */\r
329 \r
330 void InitObjArray (void)\r
331 {\r
332         int     i;\r
333 \r
334         for (i=0;i<MAXACTORS;i++)\r
335         {\r
336                 objarray[i].prev = &objarray[i+1];\r
337                 objarray[i].next = NULL;\r
338         }\r
339 \r
340         objarray[MAXACTORS-1].prev = NULL;\r
341 \r
342         objfreelist = &objarray[0];\r
343         lastobj = NULL;\r
344 \r
345         objectcount = 0;\r
346 \r
347 //\r
348 // give the player and score the first free spots\r
349 //\r
350         GetNewObj (false);\r
351         player = new;\r
352         GetNewObj (false);\r
353         scoreobj = new;\r
354 }\r
355 \r
356 //===========================================================================\r
357 \r
358 /*\r
359 =========================\r
360 =\r
361 = GetNewObj\r
362 =\r
363 = Sets the global variable new to point to a free spot in objarray.\r
364 = The free spot is inserted at the end of the liked list\r
365 =\r
366 = When the object list is full, the caller can either have it bomb out ot\r
367 = return a dummy object pointer that will never get used\r
368 =\r
369 =========================\r
370 */\r
371 \r
372 void GetNewObj (boolean usedummy)\r
373 {\r
374         if (!objfreelist)\r
375         {\r
376                 if (usedummy)\r
377                 {\r
378                         new = &dummyobj;\r
379                         return;\r
380                 }\r
381                 Quit ("GetNewObj: No free spots in objarray!");\r
382         }\r
383 \r
384         new = objfreelist;\r
385         objfreelist = new->prev;\r
386         memset (new,0,sizeof(*new));\r
387 \r
388         if (lastobj)\r
389                 lastobj->next = new;\r
390         new->prev = lastobj;    // new->next is allready NULL from memset\r
391 \r
392         new->active = yes;\r
393         new->needtoclip = true;\r
394         lastobj = new;\r
395 \r
396         objectcount++;\r
397 }\r
398 \r
399 //===========================================================================\r
400 \r
401 /*\r
402 =========================\r
403 =\r
404 = RemoveObj\r
405 =\r
406 = Add the given object back into the free list, and unlink it from it's\r
407 = neighbors\r
408 =\r
409 =========================\r
410 */\r
411 \r
412 void RemoveObj (objtype *gone)\r
413 {\r
414         if (gone == player)\r
415                 Quit ("RemoveObj: Tried to remove the player!");\r
416 \r
417 //\r
418 // erase it from the refresh manager\r
419 //\r
420         RF_RemoveSprite (&gone->sprite);\r
421 \r
422 //\r
423 // fix the next object's back link\r
424 //\r
425         if (gone == lastobj)\r
426                 lastobj = (objtype *)gone->prev;\r
427         else\r
428                 gone->next->prev = gone->prev;\r
429 \r
430 //\r
431 // fix the previous object's forward link\r
432 //\r
433         gone->prev->next = gone->next;\r
434 \r
435 //\r
436 // add it back in to the free list\r
437 //\r
438         gone->prev = objfreelist;\r
439         objfreelist = gone;\r
440 }\r
441 \r
442 //===========================================================================\r
443 \r
444 \r
445 void near HandleInfo (void)\r
446 {\r
447         switch (maptile)\r
448         {\r
449         case 1:\r
450                 SpawnKeen(mapx,mapy,1);\r
451                 break;\r
452         case 2:\r
453                 SpawnKeen(mapx,mapy,-1);\r
454                 break;\r
455         case 19:\r
456                 SpawnWorldKeen(mapx,mapy);\r
457                 lumpneeded[WORLDKEENLUMP] = true;\r
458                 break;\r
459 \r
460         case 31:\r
461                 bombspresent = true;\r
462         case 21:\r
463         case 22:\r
464         case 23:\r
465         case 24:\r
466         case 25:\r
467         case 26:\r
468         case 27:\r
469         case 28:\r
470         case 29:\r
471         case 30:\r
472         case 32:\r
473                 SpawnBonus(mapx,mapy,maptile-21);\r
474                 new->active = false;\r
475                 break;\r
476         case 33:\r
477                 SpawnDoor(mapx,mapy);\r
478                 new->active = false;\r
479                 break;\r
480         case 41:\r
481                 SpawnBrocco(mapx,mapy);\r
482                 new->active = false;\r
483                 lumpneeded[BROCCOLUMP] = true;\r
484                 break;\r
485         case 42:\r
486                 SpawnTomat(mapx,mapy);\r
487                 new->active = false;\r
488                 lumpneeded[TOMATLUMP] = true;\r
489                 break;\r
490         case 43:\r
491                 SpawnCarrot(mapx,mapy);\r
492                 new->active = false;\r
493                 lumpneeded[CARROTLUMP] = true;\r
494                 break;\r
495         case 45:\r
496                 SpawnAspar(mapx,mapy);\r
497                 new->active = false;\r
498                 lumpneeded[ASPARLUMP] = true;\r
499                 break;\r
500         case 46:\r
501                 SpawnGrape(mapx,mapy);\r
502                 new->active = false;\r
503                 lumpneeded[GRAPELUMP] = true;\r
504                 break;\r
505         case 47:\r
506                 SpawnTater(mapx,mapy);\r
507                 new->active = false;\r
508                 lumpneeded[TATERLUMP] = true;\r
509                 break;\r
510         case 48:\r
511                 SpawnCart(mapx,mapy);\r
512                 lumpneeded[CARTLUMP] = true;\r
513                 break;\r
514         case 49:\r
515                 SpawnFrenchy(mapx,mapy);\r
516                 new->active = false;\r
517                 lumpneeded[FRENCHYLUMP] = true;\r
518                 break;\r
519         case 50:\r
520         case 51:\r
521         case 52:\r
522                 SpawnMelon(mapx,mapy,maptile-50);\r
523                 new->active = false;\r
524                 lumpneeded[MELONLUMP] = true;\r
525                 break;\r
526         case 57:\r
527                 SpawnSquasher(mapx,mapy);\r
528                 new->active = false;\r
529                 lumpneeded[SQUASHLUMP] = true;\r
530                 break;\r
531         case 58:\r
532                 SpawnApel(mapx,mapy);\r
533                 new->active = false;\r
534                 lumpneeded[APELLUMP] = true;\r
535                 break;\r
536         case 59:\r
537                 SpawnPeaPod(mapx,mapy);\r
538                 new->active = false;\r
539                 lumpneeded[PEALUMP] = true;\r
540                 break;\r
541         case 60:\r
542                 SpawnPeaBrain(mapx,mapy);\r
543                 new->active = false;\r
544                 lumpneeded[PEALUMP] = true;\r
545                 break;\r
546         case 61:\r
547                 SpawnBoobus(mapx,mapy);\r
548                 lumpneeded[BOOBUSLUMP] = true;\r
549                 break;\r
550         }\r
551 \r
552         if (new->active != allways)\r
553                 new->active = false;\r
554 }\r
555 \r
556 /*\r
557 ==========================\r
558 =\r
559 = ScanInfoPlane\r
560 =\r
561 = Spawn all actors and mark down special places\r
562 =\r
563 ==========================\r
564 */\r
565 \r
566 void ScanInfoPlane (void)\r
567 {\r
568         unsigned        x,y,i,j;\r
569         int                     tile;\r
570         unsigned        far     *start;\r
571 \r
572         InitObjArray();                 // start spawning things with a clean slate\r
573 \r
574         memset (lumpneeded,0,sizeof(lumpneeded));\r
575 \r
576 #if 0\r
577         start = mapsegs[2];\r
578         for (y=0;y<mapheight;y++)\r
579                 for (x=0;x<mapwidth;x++)\r
580                 {\r
581                         tile = *start++;\r
582                         if (!tile)\r
583                                 continue;\r
584                 }\r
585 #endif\r
586 \r
587 //\r
588 // This doesn't really need to be in asm.  I thought it was a bottleneck,\r
589 // but I was wrong...\r
590 //\r
591 \r
592         asm     mov     es,[WORD PTR mapsegs+4]\r
593         asm     xor     si,si\r
594         asm     mov     [mapy],0\r
595         asm     mov     ax,[mapheight]\r
596         asm     mov     [mapycount],ax\r
597 yloop:\r
598         asm     mov     [mapx],0\r
599         asm     mov     ax,[mapwidth]\r
600         asm     mov     [mapxcount],ax\r
601 xloop:\r
602         asm     mov     ax,[es:si]\r
603         asm     or      ax,ax\r
604         asm     jz      nothing\r
605         asm     mov     [maptile],ax\r
606         HandleInfo ();                                          // si is saved\r
607         asm     mov     es,[WORD PTR mapsegs+4]\r
608 nothing:\r
609         asm     inc     [mapx]\r
610         asm     add     si,2\r
611         asm     dec     [mapxcount]\r
612         asm     jnz     xloop\r
613         asm     inc     [mapy]\r
614         asm     dec     [mapycount]\r
615         asm     jnz     yloop\r
616 \r
617         for (i=0;i<NUMLUMPS;i++)\r
618                 if (lumpneeded[i])\r
619                         for (j=lumpstart[i];j<=lumpend[i];j++)\r
620                                 CA_MarkGrChunk(j);\r
621 }\r
622 \r
623 //===========================================================================\r
624 \r
625 \r
626 /*\r
627 ==========================\r
628 =\r
629 = PatchWorldMap\r
630 =\r
631 = Takes out blocking squares and puts in dones\r
632 =\r
633 ==========================\r
634 */\r
635 \r
636 void PatchWorldMap (void)\r
637 {\r
638         unsigned        size,spot,info,foreground;\r
639 \r
640         size = mapwidth*mapheight;\r
641         spot = 0;\r
642         do\r
643         {\r
644                 info = *(mapsegs[2] + spot);\r
645                 // finished a city here?\r
646                 if (info>=3 && info<=18 && gamestate.leveldone[info-2])\r
647                 {\r
648                         *(mapsegs[2] + spot) = 0;\r
649                         foreground = *(mapsegs[1] + spot);\r
650                         if (foreground == 130)\r
651                                 *(mapsegs[1]+spot) = 0; // not blocking now\r
652                         else if (foreground == 90)\r
653                         {\r
654                         // plant done flag\r
655                                 *(mapsegs[1]+spot) = 133;\r
656                                 *(mapsegs[1]+(spot-mapwidth-1)) = 131;\r
657                                 *(mapsegs[1]+(spot-mapwidth)) = 132;\r
658                         }\r
659                 }\r
660                 spot++;\r
661         } while (spot<size);\r
662 }\r
663 \r
664 //===========================================================================\r
665 \r
666 /*\r
667 ==========================\r
668 =\r
669 = FadeAndUnhook\r
670 =\r
671 = Latch this onto the refresh so the screen only gets faded in after two\r
672 = refreshes.  This lets all actors draw themselves to both pages before\r
673 = fading the screen in.\r
674 =\r
675 ==========================\r
676 */\r
677 \r
678 void FadeAndUnhook (void)\r
679 {\r
680         if (++fadecount==2)\r
681         {\r
682                 RF_ForceRefresh();\r
683                 VW_FadeIn ();\r
684                 RF_SetRefreshHook (NULL);\r
685                 lasttimecount = TimeCount;      // don't adaptively time the fade\r
686         }\r
687 }\r
688 \r
689 //===========================================================================\r
690 \r
691 \r
692 /*\r
693 ==========================\r
694 =\r
695 = SetupGameLevel\r
696 =\r
697 = Load in map mapon and cache everything needed for it\r
698 =\r
699 ==========================\r
700 */\r
701 \r
702 void    SetupGameLevel (boolean loadnow)\r
703 {\r
704         long    orgx,orgy;\r
705 \r
706         bombspresent = false;\r
707 //\r
708 // load the level header and three map planes\r
709 //\r
710         CA_CacheMap (gamestate.mapon);\r
711 \r
712 //\r
713 // let the refresh manager set up some variables\r
714 //\r
715         RF_NewMap ();\r
716 \r
717 //\r
718 // decide which graphics are needed and spawn actors\r
719 //\r
720         CA_ClearMarks ();\r
721 \r
722         if (!mapon)\r
723                 PatchWorldMap ();\r
724 \r
725         if (mapon!=20)                  // map 20 is the title screen\r
726                 ScanInfoPlane ();\r
727         RF_MarkTileGraphics ();\r
728 \r
729 //\r
730 // have the caching manager load and purge stuff to make sure all marks\r
731 // are in memory\r
732 //\r
733         if (loadnow)\r
734         {\r
735                 if (bombspresent)\r
736                 {\r
737                         VW_FixRefreshBuffer ();\r
738                         US_DrawWindow (10,1,20,2);\r
739                         US_PrintCentered ("Boobus Bombs Near!");\r
740                         VW_UpdateScreen ();\r
741                 }\r
742                 CA_CacheMarks (levelnames[mapon], 0);\r
743         }\r
744 \r
745 #if 0\r
746         VW_FixRefreshBuffer ();\r
747         US_CenterWindow (20,8);\r
748         US_Print ("\n\n\nObject count:");\r
749         itoa (objectcount,str,10);\r
750         US_Print (str);\r
751         VW_UpdateScreen ();\r
752         IN_Ack ();\r
753 #endif\r
754 \r
755         if (mapon!=20 && loadnow)                       // map 20 is the title screen\r
756         {\r
757                 VW_FadeOut ();\r
758                 fadecount = 0;\r
759                 RF_SetRefreshHook (&FadeAndUnhook);\r
760                 SpawnScore ();\r
761 \r
762 //\r
763 // start the initial view position to center the player\r
764 //\r
765                 orgx = (long)player->x - (150<<G_P_SHIFT);\r
766                 orgy = (long)player->y-(84<<G_P_SHIFT);\r
767                 if (orgx<0)\r
768                         orgx=0;\r
769                 if (orgy<0)\r
770                         orgy=0;\r
771 \r
772                 RF_NewPosition (orgx,orgy);\r
773                 CalcInactivate ();\r
774         }\r
775 \r
776 \r
777 }\r
778 \r
779 //==========================================================================\r
780 \r
781 /*\r
782 ===============\r
783 =\r
784 = ScrollScreen\r
785 =\r
786 = Scroll if Keen is nearing an edge\r
787 = Set playstate to levelcomplete\r
788 =\r
789 ===============\r
790 */\r
791 \r
792 void ScrollScreen (void)\r
793 {\r
794         int     xscroll,yscroll;\r
795 \r
796 //\r
797 // walked off edge of map?\r
798 //\r
799         if (player->left < originxmin\r
800         || player->right > originxmax+20*TILEGLOBAL)\r
801         {\r
802                 playstate = levelcomplete;\r
803                 return;\r
804         }\r
805 \r
806 //\r
807 // fallen off bottom of world?\r
808 //\r
809         if (!plummet && player->bottom > originymax+13*TILEGLOBAL)\r
810         {\r
811                 godmode = 0;\r
812                 plummet = 1;\r
813                 KillKeen ();\r
814                 return;\r
815         }\r
816 \r
817         if (player->x < originxglobal+SCROLLWEST)\r
818                 xscroll = player->x - (originxglobal+SCROLLWEST);\r
819         else if (player->x > originxglobal+SCROLLEAST)\r
820                 xscroll = player->x - (originxglobal+SCROLLEAST);\r
821         else\r
822                 xscroll = 0;\r
823 \r
824         if (player->y < originyglobal+SCROLLNORTH)\r
825                 yscroll = player->y - (originyglobal+SCROLLNORTH);\r
826         else if (player->y > originyglobal+SCROLLSOUTH)\r
827                 yscroll = player->y - (originyglobal+SCROLLSOUTH);\r
828         else yscroll = 0;\r
829 \r
830         if (xscroll || yscroll)\r
831         {\r
832                 RF_Scroll (xscroll,yscroll);\r
833                 CalcInactivate ();\r
834                 scoreobj->needtoreact = true;\r
835         }\r
836 }\r
837 \r
838 //==========================================================================\r
839 \r
840 /*\r
841 ====================\r
842 =\r
843 = GivePoints\r
844 =\r
845 = Grants extra men at 20k,40k,80k,160k,320k\r
846 =\r
847 ====================\r
848 */\r
849 \r
850 void GivePoints (unsigned points)\r
851 {\r
852         gamestate.score += points;\r
853         if (gamestate.score >= gamestate.nextextra)\r
854         {\r
855                 SD_PlaySound (EXTRAKEENSND);\r
856                 gamestate.lives++;\r
857                 gamestate.nextextra*=2;\r
858         }\r
859 }\r
860 \r
861 \r
862 //==========================================================================\r
863 \r
864 /*\r
865 ====================\r
866 =\r
867 = MoveObjVert\r
868 =\r
869 ====================\r
870 */\r
871 \r
872 void MoveObjVert (objtype *ob, int ymove)\r
873 {\r
874         ob->y += ymove;\r
875         ob->top += ymove;\r
876         ob->bottom += ymove;\r
877         ob->tiletop = ob->top >> G_T_SHIFT;\r
878         ob->tilebottom = ob->bottom >> G_T_SHIFT;\r
879 }\r
880 \r
881 \r
882 /*\r
883 ====================\r
884 =\r
885 = MoveObjHoriz\r
886 =\r
887 ====================\r
888 */\r
889 \r
890 void MoveObjHoriz (objtype *ob, int xmove)\r
891 {\r
892         ob->x += xmove;\r
893         ob->left += xmove;\r
894         ob->right += xmove;\r
895         ob->tileleft = ob->left >> G_T_SHIFT;\r
896         ob->tileright = ob->right >> G_T_SHIFT;\r
897 }\r
898 \r
899 \r
900 /*\r
901 =============================================================================\r
902 \r
903                                         Actor to tile clipping rouitnes\r
904 \r
905 =============================================================================\r
906 */\r
907 \r
908 // walltype / x coordinate (0-15)\r
909 \r
910 int     wallclip[8][16] = {                     // the height of a given point in a tile\r
911 { 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256},\r
912 {   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0},\r
913 {   0,0x08,0x10,0x18,0x20,0x28,0x30,0x38,0x40,0x48,0x50,0x58,0x60,0x68,0x70,0x78},\r
914 {0x80,0x88,0x90,0x98,0xa0,0xa8,0xb0,0xb8,0xc0,0xc8,0xd0,0xd8,0xe0,0xe8,0xf0,0xf8},\r
915 {   0,0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80,0x90,0xa0,0xb0,0xc0,0xd0,0xe0,0xf0},\r
916 {0x78,0x70,0x68,0x60,0x58,0x50,0x48,0x40,0x38,0x30,0x28,0x20,0x18,0x10,0x08,   0},\r
917 {0xf8,0xf0,0xe8,0xe0,0xd8,0xd0,0xc8,0xc0,0xb8,0xb0,0xa8,0xa0,0x98,0x90,0x88,0x80},\r
918 {0xf0,0xe0,0xd0,0xc0,0xb0,0xa0,0x90,0x80,0x70,0x60,0x50,0x40,0x30,0x20,0x10,   0}\r
919 };\r
920 \r
921 // assignment within ifs are used heavily here, so turn off the warning\r
922 #pragma warn -pia\r
923 \r
924 /*\r
925 ===========================\r
926 =\r
927 = ClipToEnds\r
928 =\r
929 ===========================\r
930 */\r
931 \r
932 void ClipToEnds (objtype *ob)\r
933 {\r
934         unsigned        far *map,tile,facetile,info,wall;\r
935         int     leftpix,rightpix,midtiles,toppix,bottompix;\r
936         int     x,y,clip,move,totalmove,maxmove,midxpix;\r
937 \r
938         midxpix = (ob->midx&0xf0) >> 4;\r
939 \r
940         maxmove = -abs(midxmoved) - bottommoved - 16;\r
941         map = (unsigned far *)mapsegs[1] +\r
942                 mapbwidthtable[oldtilebottom-1]/2 + ob->tilemidx;\r
943         for (y=oldtilebottom-1 ; y<=ob->tilebottom ; y++,map+=mapwidth)\r
944         {\r
945                 if (wall = tinf[NORTHWALL+*map])\r
946                 {\r
947                         clip = wallclip[wall&7][midxpix];\r
948                         move = ( (y<<G_T_SHIFT)+clip - 1) - ob->bottom;\r
949                         if (move<0 && move>=maxmove)\r
950                         {\r
951                                 ob->hitnorth = wall;\r
952                                 MoveObjVert (ob,move);\r
953                                 return;\r
954                         }\r
955                 }\r
956         }\r
957 \r
958         maxmove = abs(midxmoved) - topmoved + 16;\r
959         map = (unsigned far *)mapsegs[1] +\r
960                 mapbwidthtable[oldtiletop+1]/2 + ob->tilemidx;\r
961         for (y=oldtiletop+1 ; y>=ob->tiletop ; y--,map-=mapwidth)\r
962         {\r
963                 if (wall = tinf[SOUTHWALL+*map])\r
964                 {\r
965                         clip = wallclip[wall&7][midxpix];\r
966                         move = ( ((y+1)<<G_T_SHIFT)-clip ) - ob->top;\r
967                         if (move > 0 && move<=maxmove)\r
968                         {\r
969                                 totalmove = ob->ymove + move;\r
970                                 if (totalmove < TILEGLOBAL && totalmove > -TILEGLOBAL)\r
971                                 {\r
972                                         ob->hitsouth = wall;\r
973                                         MoveObjVert (ob,move);\r
974                                 }\r
975                         }\r
976                 }\r
977         }\r
978 }\r
979 \r
980 \r
981 /*\r
982 ===========================\r
983 =\r
984 = ClipToEastWalls / ClipToWestWalls\r
985 =\r
986 ===========================\r
987 */\r
988 \r
989 void ClipToEastWalls (objtype *ob)\r
990 {\r
991         int                     y,move,top,bottom;\r
992         unsigned        far *map,tile,info,wall;\r
993 \r
994         // clip to east walls if moving west\r
995 \r
996         top = ob->tiletop;\r
997         if (ob->hitsouth>1)\r
998                 top++;                  // on a slope inside a tile\r
999         bottom = ob->tilebottom;\r
1000         if (ob->hitnorth>1)\r
1001                 bottom--;                       // on a slope inside a tile\r
1002 \r
1003         for (y=top;y<=bottom;y++)\r
1004         {\r
1005                 map = (unsigned far *)mapsegs[1] +\r
1006                         mapbwidthtable[y]/2 + ob->tileleft;\r
1007 \r
1008                 if (ob->hiteast = tinf[EASTWALL+*map])\r
1009                 {\r
1010                         move = ( (ob->tileleft+1)<<G_T_SHIFT ) - ob->left;\r
1011                         MoveObjHoriz (ob,move);\r
1012                         return;\r
1013                 }\r
1014         }\r
1015 }\r
1016 \r
1017 \r
1018 void ClipToWestWalls (objtype *ob)\r
1019 {\r
1020         int                     y,move,top,bottom;\r
1021         unsigned        far *map,tile,info,wall;\r
1022 \r
1023         // check west walls if moving east\r
1024 \r
1025         top = ob->tiletop;\r
1026         if (ob->hitsouth>1)\r
1027                 top++;                  // on a slope inside a tile\r
1028         bottom = ob->tilebottom;\r
1029         if (ob->hitnorth>1)\r
1030                 bottom--;                       // on a slope inside a tile\r
1031 \r
1032         for (y=top;y<=bottom;y++)\r
1033         {\r
1034                 map = (unsigned far *)mapsegs[1] +\r
1035                         mapbwidthtable[y]/2 + ob->tileright;\r
1036 \r
1037                 if (ob->hitwest = tinf[WESTWALL+*map])\r
1038                 {\r
1039                         move = ( (ob->tileright<<G_T_SHIFT ) -1) - ob->right;\r
1040                         MoveObjHoriz (ob,move);\r
1041                         return;\r
1042                 }\r
1043         }\r
1044 }\r
1045 \r
1046 // turn 'possibly incorrect assignment' warnings back on\r
1047 #pragma warn +pia\r
1048 \r
1049 \r
1050 //==========================================================================\r
1051 \r
1052 /*\r
1053 ================\r
1054 =\r
1055 = ClipToWalls\r
1056 =\r
1057 = Moves the current object xmove/ymove units, clipping to walls\r
1058 =\r
1059 ================\r
1060 */\r
1061 \r
1062 void ClipToWalls (objtype *ob)\r
1063 {\r
1064         unsigned        x,y,tile;\r
1065         spritetabletype far *shape;\r
1066         boolean endfirst;\r
1067 \r
1068 //\r
1069 // make sure it stays in contact with a 45 degree slope\r
1070 //\r
1071         if (ob->state->pushtofloor)\r
1072         {\r
1073                 if (ob->xmove > 0)\r
1074                         ob->ymove = ob->xmove + 16;\r
1075                 else\r
1076                         ob->ymove = -ob->xmove + 16;\r
1077         }\r
1078 \r
1079 //\r
1080 // move the shape\r
1081 //\r
1082         if (ob->xmove > MAXMOVE)\r
1083                 ob->xmove = MAXMOVE;\r
1084         else if (ob->xmove < -MAXMOVE)\r
1085                 ob->xmove = -MAXMOVE;\r
1086 \r
1087         if (ob->ymove > MAXMOVE+16)                     // +16 for push to floor\r
1088                 ob->ymove = MAXMOVE+16;\r
1089         else if (ob->ymove < -MAXMOVE)\r
1090                 ob->ymove = -MAXMOVE;\r
1091 \r
1092         ob->x += ob->xmove;\r
1093         ob->y += ob->ymove;\r
1094 \r
1095         ob->needtoreact = true;\r
1096 \r
1097         if (!ob->shapenum)                              // can't get a hit rect with no shape!\r
1098                 return;\r
1099 \r
1100         shape = &spritetable[ob->shapenum-STARTSPRITES];\r
1101 \r
1102         oldtileright = ob->tileright;\r
1103         oldtiletop = ob->tiletop;\r
1104         oldtileleft = ob->tileleft;\r
1105         oldtilebottom = ob->tilebottom;\r
1106         oldtilemidx = ob->tilemidx;\r
1107 \r
1108         oldright = ob->right;\r
1109         oldtop = ob->top;\r
1110         oldleft = ob->left;\r
1111         oldbottom = ob->bottom;\r
1112         oldmidx = ob->midx;\r
1113 \r
1114         ob->left = ob->x + shape->xl;\r
1115         ob->right = ob->x + shape->xh;\r
1116         ob->top = ob->y + shape->yl;\r
1117         ob->bottom = ob->y + shape->yh;\r
1118         ob->midx = ob->left + (ob->right - ob->left)/2;\r
1119 \r
1120         ob->tileleft = ob->left >> G_T_SHIFT;\r
1121         ob->tileright = ob->right >> G_T_SHIFT;\r
1122         ob->tiletop = ob->top >> G_T_SHIFT;\r
1123         ob->tilebottom = ob->bottom >> G_T_SHIFT;\r
1124         ob->tilemidx = ob->midx >> G_T_SHIFT;\r
1125 \r
1126         ob->hitnorth = ob->hiteast = ob->hitsouth = ob->hitwest = 0;\r
1127 \r
1128         if (!ob->needtoclip)\r
1129                 return;\r
1130 \r
1131         leftmoved = ob->left - oldleft;\r
1132         rightmoved = ob->right - oldright;\r
1133         topmoved = ob->top - oldtop;\r
1134         bottommoved = ob->bottom - oldbottom;\r
1135         midxmoved = ob->midx - oldmidx;\r
1136 \r
1137 //\r
1138 // clip it\r
1139 //\r
1140 \r
1141         ClipToEnds(ob);\r
1142 \r
1143         if (leftmoved < 0 || ob == player)      // make sure player gets cliped\r
1144                 ClipToEastWalls (ob);\r
1145         if (rightmoved > 0 || ob == player)\r
1146                 ClipToWestWalls (ob);\r
1147 }\r
1148 \r
1149 //==========================================================================\r
1150 \r
1151 \r
1152 /*\r
1153 ==================\r
1154 =\r
1155 = ClipToSpriteSide\r
1156 =\r
1157 = Clips push to solid\r
1158 =\r
1159 ==================\r
1160 */\r
1161 \r
1162 void ClipToSpriteSide (objtype *push, objtype *solid)\r
1163 {\r
1164         int xmove,leftinto,rightinto;\r
1165 \r
1166         //\r
1167         // amount the push shape can be pushed\r
1168         //\r
1169         xmove = solid->xmove - push->xmove;\r
1170 \r
1171         //\r
1172         // amount it is inside\r
1173         //\r
1174         leftinto = solid->right - push->left;\r
1175         rightinto = push->right - solid->left;\r
1176 \r
1177         if (leftinto>0 && leftinto<= xmove)\r
1178         {\r
1179                 push->xmove = leftinto;\r
1180                 if (push->state->pushtofloor)\r
1181                         push->ymove = leftinto+16;\r
1182                 ClipToWalls (push);\r
1183                 push->hiteast = 1;\r
1184                 return;\r
1185         }\r
1186 \r
1187         if (rightinto>0 && rightinto<= -xmove)\r
1188         {\r
1189                 push->xmove = -rightinto;\r
1190                 if (push->state->pushtofloor)\r
1191                         push->ymove = rightinto+16;\r
1192                 ClipToWalls (push);\r
1193                 push->hitwest = 1;\r
1194                 return;\r
1195         }\r
1196 \r
1197 }\r
1198 \r
1199 //==========================================================================\r
1200 \r
1201 \r
1202 /*\r
1203 ==================\r
1204 =\r
1205 = ClipToSprite\r
1206 =\r
1207 = Clips push to solid\r
1208 =\r
1209 ==================\r
1210 */\r
1211 \r
1212 void ClipToSprite (objtype *push, objtype *solid, boolean squish)\r
1213 {\r
1214         boolean temp;\r
1215         int walltemp,xmove,leftinto,rightinto,topinto,bottominto;\r
1216 \r
1217         xmove = solid->xmove - push->xmove;\r
1218 \r
1219         push->xmove = push->ymove = 0;\r
1220 \r
1221         //\r
1222         // left / right\r
1223         //\r
1224         leftinto = solid->right - push->left;\r
1225         rightinto = push->right - solid->left;\r
1226 \r
1227         if (leftinto>0 && leftinto<=xmove)\r
1228         {\r
1229                 push->xmove = leftinto;\r
1230                 walltemp = push->hitnorth;\r
1231                 ClipToWalls (push);\r
1232                 if (!push->hitnorth)\r
1233                         push->hitnorth = walltemp;\r
1234                 if (squish && push->hitwest)\r
1235                         KillKeen ();\r
1236                 push->hiteast = 1;\r
1237                 return;\r
1238         }\r
1239         else if (rightinto>0 && rightinto<=-xmove)\r
1240         {\r
1241                 push->xmove = -rightinto;\r
1242                 walltemp = push->hitnorth;\r
1243                 ClipToWalls (push);\r
1244                 if (!push->hitnorth)\r
1245                         push->hitnorth = walltemp;\r
1246                 if (squish && push->hiteast)\r
1247                         KillKeen ();\r
1248                 push->hitwest = 1;\r
1249                 return;\r
1250         }\r
1251 \r
1252         //\r
1253         // top / bottom\r
1254         //\r
1255         topinto = solid->bottom - push->top;\r
1256         bottominto = push->bottom - solid->top;\r
1257 \r
1258         if (bottominto>0)\r
1259         {\r
1260                 push->ymove = -bottominto+16;\r
1261                 push->xmove = solid->xmove;\r
1262                 temp = push->state->pushtofloor;\r
1263                 push->state->pushtofloor = false;\r
1264                 walltemp = push->hitnorth;\r
1265                 ClipToWalls (push);\r
1266                 if (!push->hitnorth)\r
1267                         push->hitnorth = walltemp;\r
1268                 push->state->pushtofloor = temp;\r
1269                 push->hitnorth = 25;\r
1270         }\r
1271         else if (topinto>0)\r
1272         {\r
1273                 push->ymove = topinto;\r
1274                 ClipToWalls (push);\r
1275                 push->hitsouth = 25;\r
1276         }\r
1277 }\r
1278 \r
1279 //==========================================================================\r
1280 \r
1281 \r
1282 /*\r
1283 ==================\r
1284 =\r
1285 = DoActor\r
1286 =\r
1287 = Moves an actor in its current state by a given number of tics.\r
1288 = If that time takes it into the next state, it changes the state\r
1289 = and returns the number of excess tics after the state change\r
1290 =\r
1291 ==================\r
1292 */\r
1293 \r
1294 int DoActor (objtype *ob,int tics)\r
1295 {\r
1296         int     newtics,movetics,excesstics;\r
1297         statetype *state;\r
1298 \r
1299         state = ob->state;\r
1300 \r
1301         if (state->progress == think)\r
1302         {\r
1303                 if (state->think)\r
1304                 {\r
1305                         if (ob->nothink)\r
1306                                 ob->nothink--;\r
1307                         else\r
1308 #pragma warn -pro\r
1309                                 state->think(ob);\r
1310 #pragma warn +pro\r
1311                 }\r
1312                 return 0;\r
1313         }\r
1314 \r
1315         newtics = ob->ticcount+tics;\r
1316 \r
1317         if (newtics < state->tictime || state->tictime == 0)\r
1318         {\r
1319                 ob->ticcount = newtics;\r
1320                 if (state->progress == slide || state->progress == slidethink)\r
1321                 {\r
1322                         if (ob->xdir)\r
1323                                 ob->xmove += ob->xdir == 1 ? tics*state->xmove\r
1324                                 : -tics*state->xmove;\r
1325                         if (ob->ydir)\r
1326                                 ob->ymove += ob->ydir == 1 ? tics*state->ymove\r
1327                                 : -tics*state->ymove;\r
1328                 }\r
1329                 if (state->progress == slidethink || state->progress == stepthink)\r
1330                 {\r
1331                         if (state->think)\r
1332                         {\r
1333                                 if (ob->nothink)\r
1334                                         ob->nothink--;\r
1335                                 else\r
1336 #pragma warn -pro\r
1337                                         state->think(ob);\r
1338 #pragma warn +pro\r
1339                         }\r
1340                 }\r
1341                 return 0;\r
1342         }\r
1343         else\r
1344         {\r
1345                 movetics = state->tictime - ob->ticcount;\r
1346                 excesstics = newtics - state->tictime;\r
1347                 ob->ticcount = 0;\r
1348                 if (state->progress == slide || state->progress == slidethink)\r
1349                 {\r
1350                         if (ob->xdir)\r
1351                                 ob->xmove += ob->xdir == 1 ? movetics*state->xmove\r
1352                                 : -movetics*state->xmove;\r
1353                         if (ob->ydir)\r
1354                                 ob->ymove += ob->ydir == 1 ? movetics*state->ymove\r
1355                                 : -movetics*state->ymove;\r
1356                 }\r
1357                 else\r
1358                 {\r
1359                         if (ob->xdir)\r
1360                                 ob->xmove += ob->xdir == 1 ? state->xmove : -state->xmove;\r
1361                         if (ob->ydir)\r
1362                                 ob->ymove += ob->ydir == 1 ? state->ymove : -state->ymove;\r
1363                 }\r
1364 \r
1365                 if (state->think)\r
1366                 {\r
1367                         if (ob->nothink)\r
1368                                 ob->nothink--;\r
1369                         else\r
1370 #pragma warn -pro\r
1371                                 state->think(ob);\r
1372 #pragma warn +pro\r
1373                 }\r
1374 \r
1375                 if (ob->state == state)\r
1376                         ob->state = state->nextstate;   // go to next state\r
1377                 else if (!ob->state)\r
1378                         return 0;                       // object removed itself\r
1379                 return excesstics;\r
1380         }\r
1381 }\r
1382 \r
1383 //==========================================================================\r
1384 \r
1385 \r
1386 /*\r
1387 ====================\r
1388 =\r
1389 = StateMachine\r
1390 =\r
1391 = Change state and give directions\r
1392 =\r
1393 ====================\r
1394 */\r
1395 \r
1396 void StateMachine (objtype *ob)\r
1397 {\r
1398         int excesstics,oldshapenum;\r
1399         statetype *state;\r
1400 \r
1401         ob->xmove = ob->ymove = 0;\r
1402         oldshapenum = ob->shapenum;\r
1403 \r
1404         state = ob->state;\r
1405 \r
1406         excesstics = DoActor(ob,tics);\r
1407         if (ob->state != state)\r
1408         {\r
1409                 ob->ticcount = 0;               // start the new state at 0, then use excess\r
1410                 state = ob->state;\r
1411         }\r
1412 \r
1413         while (excesstics)\r
1414         {\r
1415         //\r
1416         // passed through to next state\r
1417         //\r
1418                 if (!state->skippable && excesstics >= state->tictime)\r
1419                         excesstics = DoActor(ob,state->tictime-1);\r
1420                 else\r
1421                         excesstics = DoActor(ob,excesstics);\r
1422                 if (ob->state != state)\r
1423                 {\r
1424                         ob->ticcount = 0;               // start the new state at 0, then use excess\r
1425                         state = ob->state;\r
1426                 }\r
1427         }\r
1428 \r
1429         if (!state)                     // object removed itself\r
1430         {\r
1431                 RemoveObj (ob);\r
1432                 return;\r
1433         }\r
1434 \r
1435 \r
1436         //\r
1437         // if state->rightshapenum == NULL, the state does not have a standard\r
1438         // shape (the think routine should have set it)\r
1439         //\r
1440         if (state->rightshapenum)\r
1441         {\r
1442                 if (ob->xdir>0)\r
1443                         ob->shapenum = state->rightshapenum;\r
1444                 else\r
1445                         ob->shapenum = state->leftshapenum;\r
1446         }\r
1447         if (ob->shapenum == (unsigned)-1)\r
1448                 ob->shapenum = 0;               // make it invisable this time\r
1449 \r
1450         if (ob->xmove || ob->ymove || ob->shapenum != oldshapenum)\r
1451         {\r
1452         //\r
1453         // actor moved or changed shape\r
1454         // make sure the movement is within limits (one tile)\r
1455         //\r
1456                 ClipToWalls (ob);\r
1457         }\r
1458 }\r
1459 \r
1460 //==========================================================================\r
1461 \r
1462 \r
1463 /*\r
1464 ====================\r
1465 =\r
1466 = NewState\r
1467 =\r
1468 ====================\r
1469 */\r
1470 \r
1471 void NewState (objtype *ob,statetype *state)\r
1472 {\r
1473         boolean temp;\r
1474 \r
1475         ob->state = state;\r
1476 \r
1477         if (state->rightshapenum)\r
1478         {\r
1479                 if (ob->xdir>0)\r
1480                         ob->shapenum = state->rightshapenum;\r
1481                 else\r
1482                         ob->shapenum = state->leftshapenum;\r
1483         }\r
1484 \r
1485         temp = ob->needtoclip;\r
1486 \r
1487         ob->needtoclip = false;\r
1488 \r
1489         ClipToWalls (ob);                                       // just calculate values\r
1490 \r
1491         ob->needtoclip = temp;\r
1492 \r
1493         if (ob->needtoclip)\r
1494                 ClipToWalls (ob);\r
1495 \r
1496 }\r
1497 \r
1498 //==========================================================================\r
1499 \r
1500 /*\r
1501 ============================\r
1502 =\r
1503 = PlayLoop\r
1504 =\r
1505 ============================\r
1506 */\r
1507 \r
1508 void PlayLoop (void)\r
1509 {\r
1510         objtype *obj, *check;\r
1511         long    newtime;\r
1512 \r
1513         button0held = button1held = false;\r
1514 \r
1515         ingame = true;\r
1516         playstate = 0;\r
1517         plummet = 0;\r
1518 \r
1519         FixScoreBox ();                                 // draw bomb/flower\r
1520 \r
1521         do\r
1522         {\r
1523                 CalcSingleGravity ();\r
1524                 IN_ReadControl(0,&c);           // get player input\r
1525                 if (!c.button0)\r
1526                         button0held = 0;\r
1527                 if (!c.button1)\r
1528                         button1held = 0;\r
1529 \r
1530 //\r
1531 // go through state changes and propose movements\r
1532 //\r
1533                 obj = player;\r
1534                 do\r
1535                 {\r
1536                         if (!obj->active\r
1537                         && obj->tileright >= originxtile\r
1538                         && obj->tileleft <= originxtilemax\r
1539                         && obj->tiletop <= originytilemax\r
1540                         && obj->tilebottom >= originytile)\r
1541                         {\r
1542                                 obj->needtoreact = true;\r
1543                                 obj->active = yes;\r
1544                         }\r
1545 \r
1546                         if (obj->active)\r
1547                                 StateMachine(obj);\r
1548 \r
1549                         if ( (obj->active == true || obj->active == removable) &&\r
1550                         (  obj->tileright < inactivateleft\r
1551                         || obj->tileleft > inactivateright\r
1552                         || obj->tiletop > inactivatebottom\r
1553                         || obj->tilebottom < inactivatetop) )\r
1554                         {\r
1555                                 if (obj->active == removable)\r
1556                                         RemoveObj (obj);                                // temp thing (shots, etc)\r
1557                                 else\r
1558                                 {\r
1559                                         if (US_RndT()<tics)                             // let them get a random dist\r
1560                                         {\r
1561                                                 RF_RemoveSprite (&obj->sprite);\r
1562                                                 obj->active = no;\r
1563                                         }\r
1564                                 }\r
1565                         }\r
1566 \r
1567                         obj = (objtype *)obj->next;\r
1568                 } while (obj);\r
1569 \r
1570 //\r
1571 // check for and handle collisions between objects\r
1572 //\r
1573                 obj = player;\r
1574                 do\r
1575                 {\r
1576                         if (obj->active)\r
1577                         {\r
1578                                 check = (objtype *)obj->next;\r
1579                                 while (check)\r
1580                                 {\r
1581                                         if ( check->active\r
1582                                         && obj->right > check->left\r
1583                                         && obj->left < check->right\r
1584                                         && obj->top < check->bottom\r
1585                                         && obj->bottom > check->top)\r
1586                                         {\r
1587 #pragma warn -pro\r
1588                                                 if (obj->state->contact)\r
1589                                                         obj->state->contact(obj,check);\r
1590                                                 if (check->state->contact)\r
1591                                                         check->state->contact(check,obj);\r
1592 #pragma warn +pro\r
1593                                                 if (!obj->obclass)\r
1594                                                         break;                          // contact removed object\r
1595                                         }\r
1596                                         check = (objtype *)check->next;\r
1597                                 }\r
1598                         }\r
1599                         obj = (objtype *)obj->next;\r
1600                 } while (obj);\r
1601 \r
1602 \r
1603                 ScrollScreen();\r
1604 \r
1605 //\r
1606 // react to whatever happened, and post sprites to the refresh manager\r
1607 //\r
1608                 obj = player;\r
1609                 do\r
1610                 {\r
1611                         if (obj->needtoreact && obj->state->react)\r
1612                         {\r
1613                                 obj->needtoreact = false;\r
1614 #pragma warn -pro\r
1615                                 obj->state->react(obj);\r
1616 #pragma warn +pro\r
1617                         }\r
1618                         obj = (objtype *)obj->next;\r
1619                 } while (obj);\r
1620 \r
1621 \r
1622 //\r
1623 // update the screen and calculate the number of tics it took to execute\r
1624 // this cycle of events (for adaptive timing of next cycle)\r
1625 //\r
1626                 RF_Refresh();\r
1627 \r
1628 //\r
1629 // single step debug mode\r
1630 //\r
1631                 if (singlestep)\r
1632                 {\r
1633                         VW_WaitVBL(14);\r
1634                         lasttimecount = TimeCount;\r
1635                 }\r
1636 \r
1637                 CheckKeys();\r
1638         } while (!loadedgame && !playstate);\r
1639 \r
1640         ingame = false;\r
1641 }\r
1642 \r
1643 \r
1644 //==========================================================================\r
1645 \r
1646 /*\r
1647 ==========================\r
1648 =\r
1649 = GameFinale\r
1650 =\r
1651 ==========================\r
1652 */\r
1653 \r
1654 void GameFinale (void)\r
1655 {\r
1656 struct date d;\r
1657 \r
1658         VW_FixRefreshBuffer ();\r
1659 \r
1660 /* screen 1 of finale text (16 lines) */\r
1661         US_CenterWindow (30,21);\r
1662         PrintY += 4;\r
1663         US_CPrint (\r
1664 "Yes!  Boobus Tuber's hash-brown-\n"\r
1665 "like remains rained down from\n"\r
1666 "the skies as Commander Keen\n"\r
1667 "walked up to the Dream Machine.\n"\r
1668 "He analyzed all the complex\n"\r
1669 "controls and readouts on it, then\n"\r
1670 "pulled down a huge red lever\n"\r
1671 "marked \"On/Off Switch.\"  The\n"\r
1672 "machine clanked and rattled,\n"\r
1673 "then went silent. He had freed\n"\r
1674 "all the children from their\n"\r
1675 "vegetable-enforced slavery!\n"\r
1676 "Everything around Keen wobbled\n"\r
1677 "in a disconcerting manner, his\n"\r
1678 "eyelids grew heavy, and he\n"\r
1679 "fell asleep....\n"\r
1680         );\r
1681         VW_UpdateScreen();\r
1682         VW_WaitVBL(60);\r
1683         SD_WaitSoundDone ();\r
1684         IN_ClearKeysDown ();\r
1685         IN_Ack();\r
1686 \r
1687 /* screen 2 of finale (15 lines) */\r
1688         US_CenterWindow (30,21);\r
1689         PrintY += 9;\r
1690         US_CPrint (\r
1691 "Billy woke up, looking around the\n"\r
1692 "room, the early morning sun\n"\r
1693 "shining in his face.  Nothing.\n"\r
1694 "No vegetables to be seen.  Was it\n"\r
1695 "all just a dream?\n\n"\r
1696 "Billy's mom entered the room.\n\n"\r
1697 "\"Good morning, dear. I heard some\n"\r
1698 "news on TV that you'd be\n"\r
1699 "interested in,\" she said, sitting\n"\r
1700 "by him on the bed.\n\n"\r
1701 "\"What news?\" Billy asked,\n"\r
1702 "still groggy.\n\n"\r
1703         );\r
1704         VW_UpdateScreen();\r
1705         VW_WaitVBL(60);\r
1706         IN_ClearKeysDown ();\r
1707         IN_Ack();\r
1708 \r
1709 /* screen 3 of finale (12 lines)*/\r
1710         US_CenterWindow (30,21);\r
1711         PrintY += 23;\r
1712         US_CPrint (\r
1713 "\"The President declared today\n"\r
1714 "National 'I Hate Broccoli' Day.\n"\r
1715 "He said kids are allowed to pick\n"\r
1716 "one vegetable today, and they\n"\r
1717 "don't have to eat it.\"\n\n"\r
1718 "\"Aw, mom, I'm not afraid of any\n"\r
1719 "stupid vegetables,\" Billy said.\n"\r
1720 "\"But if it's okay with you, I'd\n"\r
1721 "rather not have any french fries\n"\r
1722 "for awhile.\"\n\n"\r
1723 "THE END"\r
1724         );\r
1725         VW_UpdateScreen();\r
1726         VW_WaitVBL(60);\r
1727         IN_ClearKeysDown ();\r
1728         IN_Ack();\r
1729 \r
1730 }\r
1731 \r
1732 //==========================================================================\r
1733 \r
1734 /*\r
1735 ==========================\r
1736 =\r
1737 = HandleDeath\r
1738 =\r
1739 ==========================\r
1740 */\r
1741 \r
1742 void HandleDeath (void)\r
1743 {\r
1744         unsigned        top,bottom,selection,y,color;\r
1745 \r
1746         gamestate.keys = 0;\r
1747         gamestate.boobusbombs -= gamestate.bombsthislevel;\r
1748         gamestate.lives--;\r
1749         if (gamestate.lives < 0)\r
1750                 return;\r
1751 \r
1752         VW_FixRefreshBuffer ();\r
1753         US_CenterWindow (20,8);\r
1754         PrintY += 4;\r
1755         US_CPrint ("You didn't make it past");\r
1756         US_CPrint (levelnames[mapon]);\r
1757         PrintY += 8;\r
1758         top = PrintY-2;\r
1759         US_CPrint ("Try Again");\r
1760         PrintY += 4;\r
1761         bottom = PrintY-2;\r
1762         US_CPrint ("Exit to Tuberia");\r
1763 \r
1764         selection = 0;\r
1765         do\r
1766         {\r
1767                 if (selection)\r
1768                         y = bottom;\r
1769                 else\r
1770                         y = top;\r
1771 \r
1772 // draw select bar\r
1773                 if ( (TimeCount / 16)&1 )\r
1774                         color = SECONDCOLOR;\r
1775                 else\r
1776                         color = FIRSTCOLOR;\r
1777 \r
1778                 VWB_Hlin (WindowX+4,WindowX+WindowW-4,y,color);\r
1779                 VWB_Hlin (WindowX+4,WindowX+WindowW-4,y+1,color);\r
1780                 VWB_Hlin (WindowX+4,WindowX+WindowW-4,y+12,color);\r
1781                 VWB_Hlin (WindowX+4,WindowX+WindowW-4,y+13,color);\r
1782                 VWB_Vlin (y+1,y+11, WindowX+4,color);\r
1783                 VWB_Vlin (y+1,y+11, WindowX+5,color);\r
1784                 VWB_Vlin (y+1,y+11, WindowX+WindowW-4,color);\r
1785                 VWB_Vlin (y+1,y+11, WindowX+WindowW-5,color);\r
1786 \r
1787                 VW_UpdateScreen ();\r
1788 \r
1789 // erase select bar\r
1790                 VWB_Hlin (WindowX+4,WindowX+WindowW-4,y,WHITE);\r
1791                 VWB_Hlin (WindowX+4,WindowX+WindowW-4,y+1,WHITE);\r
1792                 VWB_Hlin (WindowX+4,WindowX+WindowW-4,y+12,WHITE);\r
1793                 VWB_Hlin (WindowX+4,WindowX+WindowW-4,y+13,WHITE);\r
1794                 VWB_Vlin (y+1,y+11, WindowX+4,WHITE);\r
1795                 VWB_Vlin (y+1,y+11, WindowX+5,WHITE);\r
1796                 VWB_Vlin (y+1,y+11, WindowX+WindowW-4,WHITE);\r
1797                 VWB_Vlin (y+1,y+11, WindowX+WindowW-5,WHITE);\r
1798 \r
1799                 if (LastScan == sc_Escape)\r
1800                 {\r
1801                         gamestate.mapon = 0;            // exit to tuberia\r
1802                         IN_ClearKeysDown ();\r
1803                         return;\r
1804                 }\r
1805 \r
1806                 IN_ReadControl(0,&c);           // get player input\r
1807                 if (c.button0 || c.button1 || LastScan == sc_Return\r
1808                 || LastScan == sc_Space)\r
1809                 {\r
1810                         if (selection)\r
1811                                 gamestate.mapon = 0;            // exit to tuberia\r
1812                         return;\r
1813                 }\r
1814                 if (c.yaxis == -1 || LastScan == sc_UpArrow)\r
1815                         selection = 0;\r
1816                 else if (c.yaxis == 1 || LastScan == sc_DownArrow)\r
1817                         selection = 1;\r
1818         } while (1);\r
1819 \r
1820 }\r
1821 \r
1822 //==========================================================================\r
1823 \r
1824 /*\r
1825 ============================\r
1826 =\r
1827 = GameLoop\r
1828 =\r
1829 = A game has just started (after the cinematic or load game)\r
1830 =\r
1831 ============================\r
1832 */\r
1833 \r
1834 void GameLoop (void)\r
1835 {\r
1836         unsigned        cities,i;\r
1837         long    orgx,orgy;\r
1838 \r
1839         gamestate.difficulty = restartgame;\r
1840         restartgame = gd_Continue;\r
1841 \r
1842         do\r
1843         {\r
1844 startlevel:\r
1845                 if (loadedgame)\r
1846                 {\r
1847                         loadedgame = false;\r
1848                         //\r
1849                         // start the initial view position to center the player\r
1850                         //\r
1851                         orgx = (long)player->x - (150<<G_P_SHIFT);\r
1852                         orgy = (long)player->y-(84<<G_P_SHIFT);\r
1853                         if (orgx<0)\r
1854                                 orgx=0;\r
1855                         if (orgy<0)\r
1856                                 orgy=0;\r
1857 \r
1858                         VW_FadeOut ();\r
1859                         fadecount = 0;\r
1860                         RF_SetRefreshHook (&FadeAndUnhook);\r
1861                         RF_NewPosition (orgx,orgy);\r
1862                         CalcInactivate ();\r
1863                 }\r
1864                 else\r
1865                 {\r
1866                         VW_FixRefreshBuffer ();\r
1867                         US_CenterWindow (20,8);\r
1868                         US_CPrint ("Loading");\r
1869                         VW_UpdateScreen ();\r
1870                         gamestate.bombsthislevel = 0;\r
1871                         SetupGameLevel (true);\r
1872                 }\r
1873 \r
1874 \r
1875                 PlayLoop ();\r
1876 \r
1877 #if FRILLS\r
1878                 if (tedlevel)\r
1879                 {\r
1880                         if (playstate == died)\r
1881                                 goto startlevel;\r
1882                         else\r
1883                                 TEDDeath ();\r
1884                 }\r
1885 #endif\r
1886 \r
1887                 if (loadedgame)\r
1888                         goto startlevel;\r
1889 \r
1890                 switch (playstate)\r
1891                 {\r
1892                 case warptolevel:\r
1893                         goto startlevel;\r
1894 \r
1895                 case died:\r
1896                         HandleDeath ();\r
1897                         break;\r
1898 \r
1899                 case levelcomplete:\r
1900                         if (mapon)\r
1901                                 SD_PlaySound (LEVELDONESND);\r
1902                         gamestate.leveldone[mapon] = true;      // finished the level\r
1903                         if (mapon != 0)\r
1904                                 gamestate.mapon = 0;\r
1905                         break;\r
1906 \r
1907                 case resetgame:\r
1908                         return;\r
1909 \r
1910                 case victorious:\r
1911                         GameFinale ();\r
1912                         goto done;\r
1913                 }\r
1914 \r
1915 \r
1916         } while (gamestate.lives>-1 && playstate!=victorious);\r
1917 \r
1918         GameOver ();\r
1919 \r
1920 done:\r
1921         cities = 0;\r
1922         for (i= 1; i<=16; i++)\r
1923                 if (gamestate.leveldone[i])\r
1924                         cities++;\r
1925         US_CheckHighScore (gamestate.score,cities);\r
1926         VW_ClearVideo (FIRSTCOLOR);\r
1927 }\r
1928 \r