]> 4ch.mooo.com Git - 16.git/blob - 16/keen456/KEEN4-6/KEEN6/K6_ACT1.C
extrcted keen code remake
[16.git] / 16 / keen456 / KEEN4-6 / KEEN6 / K6_ACT1.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 /*\r
24 K6_ACT1.C\r
25 =========\r
26 \r
27 Contains the following actor types (in this order):\r
28 \r
29 - some shared routines\r
30 - Bonus Items\r
31 - Grabbiter\r
32 - Rocket\r
33 - Grapple spots\r
34 - Satellite\r
35 - Quest Items (Sandwich, Grappling Hook, Passcard, Molly)\r
36 - Platforms\r
37 - falling platforms\r
38 - static platforms\r
39 - Goplat platforms\r
40 - Trick platforms\r
41 - Bloog\r
42 - Blooguard\r
43 - Blooglet\r
44 \r
45 */\r
46 \r
47 #include "CK_DEF.H"\r
48 \r
49 /*\r
50 =============================================================================\r
51 \r
52                                                   SHARED STUFF\r
53 \r
54 =============================================================================\r
55 */\r
56 \r
57 Sint16 pdirx[] = {0, 1, 0, -1, 1, 1, -1, -1};\r
58 Sint16 pdiry[] = {-1, 0, 1, 0, -1, 1, 1, -1};\r
59 \r
60 /*\r
61 ===========================\r
62 =\r
63 = C_ClipSide\r
64 =\r
65 ===========================\r
66 */\r
67 \r
68 void C_ClipSide(objtype *ob, objtype *hit)\r
69 {\r
70         if (hit->obclass == keenobj)\r
71         {\r
72                 playerkludgeclipcancel = true;\r
73                 ClipToSpriteSide(hit, ob);\r
74                 playerkludgeclipcancel = false;\r
75         }\r
76 }\r
77 \r
78 /*\r
79 ===========================\r
80 =\r
81 = C_ClipTop\r
82 =\r
83 ===========================\r
84 */\r
85 \r
86 void C_ClipTop(objtype *ob, objtype *hit)\r
87 {\r
88         if (hit->obclass == keenobj)\r
89                 ClipToSpriteTop(hit, ob);\r
90 }\r
91 \r
92 /*\r
93 ===========================\r
94 =\r
95 = R_Land\r
96 =\r
97 ===========================\r
98 */\r
99 \r
100 void R_Land(objtype *ob)\r
101 {\r
102         if (ob->hiteast || ob->hitwest)\r
103                 ob->xspeed = 0;\r
104 \r
105         if (ob->hitsouth)\r
106                 ob->yspeed = 0;\r
107 \r
108         if (ob->hitnorth)\r
109         {\r
110                 ob->yspeed = 0;\r
111                 if (ob->state->nextstate)\r
112                 {\r
113                         ChangeState(ob, ob->state->nextstate);\r
114                 }\r
115                 else\r
116                 {\r
117                         RemoveObj(ob);\r
118                         return;\r
119                 }\r
120         }\r
121 \r
122         RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
123 }\r
124 \r
125 /*\r
126 ===========================\r
127 =\r
128 = R_Bounce\r
129 =\r
130 ===========================\r
131 */\r
132 \r
133 void R_Bounce(objtype *ob)\r
134 {\r
135         Uint16 wall,absx,absy,angle,newangle;\r
136         Uint32 speed;\r
137 \r
138 \r
139         RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
140 \r
141         if (ob->hiteast || ob->hitwest)\r
142                 ob->xspeed = -ob->xspeed/2;\r
143 \r
144         if (ob->hitsouth)\r
145         {\r
146                 ob->yspeed = -ob->yspeed/2;\r
147                 return;\r
148         }\r
149 \r
150         wall = ob->hitnorth;\r
151 #ifdef KEEN6Ev15\r
152         if (!wall)\r
153         {\r
154                 return;\r
155         }\r
156         else\r
157 #else\r
158         if (wall)\r
159 #endif\r
160         {\r
161                 if (ob->yspeed < 0)\r
162                         ob->yspeed = 0;\r
163 \r
164                 absx = abs(ob->xspeed);\r
165                 absy = ob->yspeed;\r
166                 if (absx>absy)\r
167                 {\r
168                         if (absx>absy*2)        // 22 degrees\r
169                         {\r
170                                 angle = 0;\r
171                                 speed = absx*286;       // x*sqrt(5)/2\r
172                         }\r
173                         else                            // 45 degrees\r
174                         {\r
175                                 angle = 1;\r
176                                 speed = absx*362;       // x*sqrt(2)\r
177                         }\r
178                 }\r
179                 else\r
180                 {\r
181                         if (absy>absx*2)        // 90 degrees\r
182                         {\r
183                                 angle = 3;\r
184                                 speed = absy*256;\r
185                         }\r
186                         else\r
187                         {\r
188                                 angle = 2;              // 67 degrees\r
189                                 speed = absy*286;       // y*sqrt(5)/2\r
190                         }\r
191                 }\r
192                 if (ob->xspeed > 0)\r
193                         angle = 7-angle;\r
194 \r
195                 speed >>= 1;\r
196                 newangle = bounceangle[ob->hitnorth][angle];\r
197                 switch (newangle)\r
198                 {\r
199                 case 0:\r
200                         ob->xspeed = speed / 286;\r
201                         ob->yspeed = -ob->xspeed / 2;\r
202                         break;\r
203                 case 1:\r
204                         ob->xspeed = speed / 362;\r
205                         ob->yspeed = -ob->xspeed;\r
206                         break;\r
207                 case 2:\r
208                         ob->yspeed = -(speed / 286);\r
209                         ob->xspeed = -ob->yspeed / 2;\r
210                         break;\r
211                 case 3:\r
212 \r
213                 case 4:\r
214                         ob->xspeed = 0;\r
215                         ob->yspeed = -(speed / 256);\r
216                         break;\r
217                 case 5:\r
218                         ob->yspeed = -(speed / 286);\r
219                         ob->xspeed = ob->yspeed / 2;\r
220                         break;\r
221                 case 6:\r
222                         ob->xspeed = ob->yspeed = -(speed / 362);\r
223                         break;\r
224                 case 7:\r
225                         ob->xspeed = -(speed / 286);\r
226                         ob->yspeed = ob->xspeed / 2;\r
227                         break;\r
228 \r
229                 case 8:\r
230                         ob->xspeed = -(speed / 286);\r
231                         ob->yspeed = -ob->xspeed / 2;\r
232                         break;\r
233                 case 9:\r
234                         ob->xspeed = -(speed / 362);\r
235                         ob->yspeed = -ob->xspeed;\r
236                         break;\r
237                 case 10:\r
238                         ob->yspeed = speed / 286;\r
239                         ob->xspeed = -ob->yspeed / 2;\r
240                         break;\r
241                 case 11:\r
242 \r
243                 case 12:\r
244                         ob->xspeed = 0;\r
245                         ob->yspeed = -(speed / 256);\r
246                         break;\r
247                 case 13:\r
248                         ob->yspeed = speed / 286;\r
249                         ob->xspeed = ob->yspeed / 2;\r
250                         break;\r
251                 case 14:\r
252                         ob->xspeed = speed / 362;\r
253                         ob->yspeed = speed / 362;\r
254                         break;\r
255                 case 15:\r
256                         ob->xspeed = speed / 286;\r
257                         ob->yspeed = ob->xspeed / 2;\r
258                         break;\r
259                 }\r
260 \r
261                 if (speed < 256*16)\r
262                 {\r
263                         ChangeState(ob, ob->state->nextstate);\r
264                 }\r
265         }\r
266 }\r
267 \r
268 /*\r
269 =============================================================================\r
270 \r
271                                                   BONUS ITEMS\r
272 \r
273 temp1 = bonus type\r
274 temp2 = base shape number\r
275 temp3 = last animated shape number +1\r
276 \r
277 =============================================================================\r
278 */\r
279 \r
280 statetype s_bonus1    = {0, 0, step,      false, false, 20, 0, 0, T_Bonus, NULL, R_Draw, &s_bonus2};\r
281 statetype s_bonus2    = {0, 0, step,      false, false, 20, 0, 0, T_Bonus, NULL, R_Draw, &s_bonus1};\r
282 statetype s_bonusfly1 = {0, 0, stepthink, false, false, 20, 0, 0, T_FlyBonus, NULL, R_Draw, &s_bonusfly2};\r
283 statetype s_bonusfly2 = {0, 0, stepthink, false, false, 20, 0, 0, T_FlyBonus, NULL, R_Draw, &s_bonusfly1};\r
284 statetype s_bonusrise = {0, 0, slide,     false, false, 40, 0, 8, NULL, NULL, R_Draw, NULL};\r
285 \r
286 statetype s_splash1   = {VIVASPLASH1SPR, VIVASPLASH1SPR, step, false, false, 8, 0, 0, NULL, NULL, R_Draw, &s_splash2};\r
287 statetype s_splash2   = {VIVASPLASH2SPR, VIVASPLASH2SPR, step, false, false, 8, 0, 0, NULL, NULL, R_Draw, &s_splash3};\r
288 statetype s_splash3   = {VIVASPLASH3SPR, VIVASPLASH3SPR, step, false, false, 8, 0, 0, NULL, NULL, R_Draw, &s_splash4};\r
289 statetype s_splash4   = {VIVASPLASH4SPR, VIVASPLASH4SPR, step, false, false, 8, 0, 0, NULL, NULL, R_Draw, NULL};\r
290 \r
291 Uint16 bonusshape[] = {\r
292         REDGEM1SPR, YELLOWGEM1SPR, BLUEGEM1SPR, GREENGEM1SPR,\r
293         SUGAR1ASPR, SUGAR2ASPR, SUGAR3ASPR,\r
294         SUGAR4ASPR, SUGAR5ASPR, SUGAR6ASPR,\r
295         ONEUPASPR, STUNCLIP1SPR\r
296 };\r
297 \r
298 /*\r
299 ===========================\r
300 =\r
301 = SpawnBonus\r
302 =\r
303 ===========================\r
304 */\r
305 \r
306 void SpawnBonus(Uint16 tileX, Uint16 tileY, Uint16 type)\r
307 {\r
308         GetNewObj(false);\r
309         new->needtoclip = cl_noclip;\r
310         new->priority = 2;\r
311         new->obclass = bonusobj;\r
312         new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
313         new->y = CONVERT_TILE_TO_GLOBAL(tileY);\r
314         new->ydir = -1;\r
315         new->temp1 = type;\r
316         new->temp2=new->shapenum = bonusshape[type];\r
317         new->temp3 = new->temp2+2;\r
318         NewState(new, &s_bonus1);\r
319 }\r
320 \r
321 /*\r
322 ===========================\r
323 =\r
324 = SpawnSplash\r
325 =\r
326 ===========================\r
327 */\r
328 \r
329 void SpawnSplash(Uint16 tileX, Uint16 tileY)\r
330 {\r
331         GetNewObj(true);\r
332         new->needtoclip = cl_noclip;\r
333         new->priority = 3;\r
334         new->obclass = inertobj;\r
335         new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
336         new->y = CONVERT_TILE_TO_GLOBAL(tileY);\r
337         NewState(new, &s_splash1);\r
338 }\r
339 \r
340 /*\r
341 ===========================\r
342 =\r
343 = T_Bonus\r
344 =\r
345 ===========================\r
346 */\r
347 \r
348 void T_Bonus(objtype *ob)\r
349 {\r
350         if (++ob->shapenum == ob->temp3)\r
351                 ob->shapenum = ob->temp2;\r
352 }\r
353 \r
354 /*\r
355 ===========================\r
356 =\r
357 = T_FlyBonus\r
358 =\r
359 ===========================\r
360 */\r
361 \r
362 void T_FlyBonus(objtype *ob)\r
363 {\r
364         if (ob->hitnorth)\r
365                 ob->state = &s_bonus1;\r
366 \r
367         if (++ob->shapenum == ob->temp3)\r
368                 ob->shapenum = ob->temp2;\r
369 \r
370         DoGravity(ob);\r
371 }\r
372 \r
373 /*\r
374 =============================================================================\r
375 \r
376                                                   GRABBITER\r
377 \r
378 =============================================================================\r
379 */\r
380 \r
381 statetype s_grabbiter1      = {GRABBITER1SPR,      GRABBITER1SPR,      step, false, false, 12, 0, 0, NULL, C_Grabbiter, R_Draw, &s_grabbiter2};\r
382 statetype s_grabbiter2      = {GRABBITER2SPR,      GRABBITER2SPR,      step, false, false, 12, 0, 0, NULL, C_Grabbiter, R_Draw, &s_grabbiter1};\r
383 statetype s_grabbitersleep1 = {GRABBITERSLEEP1SPR, GRABBITERSLEEP1SPR, step, false, false, 12, 0, 0, NULL, NULL, R_Draw, &s_grabbitersleep2};\r
384 statetype s_grabbitersleep2 = {GRABBITERSLEEP2SPR, GRABBITERSLEEP2SPR, step, false, false, 12, 0, 0, NULL, NULL, R_Draw, &s_grabbitersleep1};\r
385 \r
386 /*\r
387 ===========================\r
388 =\r
389 = SpawnGrabbiter\r
390 =\r
391 ===========================\r
392 */\r
393 \r
394 void SpawnGrabbiter(Uint16 tileX, Uint16 tileY)\r
395 {\r
396         GetNewObj(false);\r
397         new->active = ac_yes;\r
398         new->needtoclip = cl_noclip;\r
399         new->priority = 2;\r
400         new->obclass = grabbiterobj;\r
401         new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
402         new->y = CONVERT_TILE_TO_GLOBAL(tileY);\r
403         if (gamestate.sandwichstate == 2)\r
404         {\r
405                 NewState(new, &s_grabbitersleep1);\r
406         }\r
407         else\r
408         {\r
409                 NewState(new, &s_grabbiter1);\r
410         }\r
411 }\r
412 \r
413 /*\r
414 ===========================\r
415 =\r
416 = C_Grabbiter\r
417 =\r
418 ===========================\r
419 */\r
420 \r
421 void C_Grabbiter(objtype *ob, objtype *hit)\r
422 {\r
423         // BUG: this is executed for every object, not just (Map-)Keen!\r
424         switch (gamestate.sandwichstate)\r
425         {\r
426         case 0:\r
427                 CA_CacheGrChunk(KEENTALK1PIC);\r
428                 SD_PlaySound(SND_GRABBITER);\r
429                 VW_FixRefreshBuffer();\r
430                 US_CenterWindow(26, 8);\r
431                 VWB_DrawPic(WindowX+WindowW-48, WindowY, KEENTALK1PIC);\r
432                 WindowW -= 48;\r
433                 PrintY += 5;\r
434                 US_CPrint(\r
435                         "Oh, no!\n"\r
436                         "It's a slavering\n"\r
437                         "Grabbiter! He says,\n"\r
438                         "\"Get me lunch and\n"\r
439                         "I'll tell ya a secret!\""\r
440                         );\r
441                 VW_UpdateScreen();\r
442                 SD_PlaySound(SND_NOWAY);\r
443                 VW_WaitVBL(30);\r
444                 IN_ClearKeysDown();\r
445                 IN_Ack();\r
446                 RF_ForceRefresh();\r
447 \r
448                 //push Keen back\r
449                 xtry = -hit->xmove;\r
450                 ytry = -hit->ymove;\r
451                 hit->xdir = hit->ydir = 0;\r
452                 ClipToWalls(hit);\r
453                 break;\r
454 \r
455         case 1:\r
456                 gamestate.sandwichstate++;\r
457                 CA_CacheGrChunk(KEENTALK1PIC);\r
458                 VW_FixRefreshBuffer();\r
459                 US_CenterWindow(26, 8);\r
460                 VWB_DrawPic(WindowX+WindowW-48, WindowY, KEENTALK1PIC);\r
461                 WindowW -= 48;\r
462                 PrintY += 2;\r
463                 US_CPrint(\r
464                         "The Grabbiter grabs\n"\r
465                         "the gigantic sandwich,\n"\r
466                         "downs it in one bite,\n"\r
467                         "and says,\"Here's your\n"\r
468                         "secret. Big meals\n"\r
469                         "make me sleepy!\n"     // BUG: quote is missing at the end\r
470                         );\r
471                 VW_UpdateScreen();\r
472                 VW_WaitVBL(30);\r
473                 IN_ClearKeysDown();\r
474                 IN_Ack();\r
475                 ChangeState(ob, &s_grabbitersleep1);\r
476                 RF_ForceRefresh();\r
477         }\r
478 }\r
479 \r
480 /*\r
481 =============================================================================\r
482 \r
483                                                   ROCKET\r
484 \r
485 temp1 = direction\r
486 temp2 = countdown to next dir check\r
487 \r
488 =============================================================================\r
489 */\r
490 \r
491 statetype s_rocket        = {ROCKETSPR,     ROCKETSPR,     think,     false, false, 0, 0, 0, NULL, C_Rocket, R_Draw, NULL};\r
492 statetype s_rocketfly1    = {ROCKETFLY1SPR, ROCKETFLY1SPR, stepthink, false, false, 8, 0, 0, T_RocketFly, C_RocketFly, R_Draw, &s_rocketfly2};\r
493 statetype s_rocketfly2    = {ROCKETFLY2SPR, ROCKETFLY2SPR, stepthink, false, false, 8, 0, 0, T_RocketFly, C_RocketFly, R_Draw, &s_rocketfly1};\r
494 statetype s_keenrocket    = {0,             0,             think,     false, false, 0, 0, 0, T_Rocket, NULL, R_Draw, NULL};\r
495 \r
496 /*\r
497 ===========================\r
498 =\r
499 = SpawnRocket\r
500 =\r
501 ===========================\r
502 */\r
503 \r
504 void SpawnRocket(Uint16 tileX, Uint16 tileY, Uint16 state)\r
505 {\r
506         if (gamestate.rocketstate == state)\r
507         {\r
508                 GetNewObj(false);\r
509                 new->active = ac_yes;\r
510                 new->needtoclip = cl_noclip;\r
511                 new->priority = 3;\r
512                 new->obclass = rocketobj;\r
513                 new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
514                 new->y = CONVERT_TILE_TO_GLOBAL(tileY);\r
515                 NewState(new, &s_rocket);\r
516         }\r
517 }\r
518 \r
519 /*\r
520 ===========================\r
521 =\r
522 = T_Rocket\r
523 =\r
524 ===========================\r
525 */\r
526 \r
527 void T_Rocket(objtype *ob)\r
528 {\r
529         ob->needtoreact = true;\r
530 }\r
531 \r
532 /*\r
533 ===========================\r
534 =\r
535 = C_Rocket\r
536 =\r
537 ===========================\r
538 */\r
539 \r
540 void C_Rocket(objtype *ob, objtype *hit)\r
541 {\r
542         // BUG: this is executed for every object, not just (Map-)Keen!\r
543         switch (gamestate.passcardstate)\r
544         {\r
545         case 0:\r
546                 CA_CacheGrChunk(KEENTALK1PIC);\r
547                 VW_FixRefreshBuffer();\r
548                 US_CenterWindow(26, 8);\r
549                 VWB_DrawPic(WindowX+WindowW-48, WindowY, KEENTALK1PIC);\r
550                 WindowW -= 48;\r
551                 PrintY += 5;\r
552                 US_CPrint(\r
553                         "The door makes a loud\n"\r
554                         "blooping noise.\n"\r
555                         "It says,\n"\r
556                         "\"Passcard required\n"\r
557                         "for entry.\""\r
558                         );\r
559                 VW_UpdateScreen();\r
560                 SD_PlaySound(SND_NOWAY);\r
561                 VW_WaitVBL(30);\r
562                 IN_ClearKeysDown();\r
563                 IN_Ack();\r
564                 RF_ForceRefresh();\r
565 \r
566                 //push Keen back\r
567                 xtry = -hit->xmove;\r
568                 ytry = -hit->ymove;\r
569                 hit->xdir = hit->ydir = 0;\r
570                 ClipToWalls(hit);\r
571                 break;\r
572 \r
573         case 1:\r
574                 ob->temp1 = arrow_North;\r
575                 ob->temp2 = TILEGLOBAL;\r
576                 ChangeState(ob, &s_rocketfly1);\r
577 \r
578                 hit->x = ob->x;\r
579                 hit->y = ob->y + TILEGLOBAL;\r
580                 hit->needtoclip = cl_noclip;\r
581                 ChangeState(hit, &s_keenrocket);\r
582                 SD_PlaySound(SND_ROCKETSTART);\r
583                 SD_WaitSoundDone();\r
584         }\r
585 }\r
586 \r
587 /*\r
588 ===========================\r
589 =\r
590 = C_RocketFly\r
591 =\r
592 ===========================\r
593 */\r
594 \r
595 void C_RocketFly(objtype *ob, objtype *hit)\r
596 {\r
597         if (hit->obclass == keenobj)\r
598         {\r
599                 hit->x = ob->x;\r
600                 hit->y = ob->y+TILEGLOBAL;\r
601                 ChangeState(hit, hit->state);\r
602         }\r
603 }\r
604 \r
605 /*\r
606 ===========================\r
607 =\r
608 = T_RocketFly\r
609 =\r
610 ===========================\r
611 */\r
612 \r
613 void T_RocketFly(objtype *ob)\r
614 {\r
615         Uint16 move, tx, ty;\r
616         Sint16 dir;\r
617 \r
618         //\r
619         // this code could be executed twice during the same frame because the\r
620         // object's animation/state changed during that frame, so don't update\r
621         // anything if we already have movement for the current frame i.e. the\r
622         // update code has already been executed this frame\r
623         //\r
624         if (xtry == 0 && ytry == 0)\r
625         {\r
626                 if (!SD_SoundPlaying())\r
627                         SD_PlaySound(SND_ROCKETFLY);\r
628 \r
629                 move = tics << 5;\r
630                 if (ob->temp2 > move)\r
631                 {\r
632                         ob->temp2 = ob->temp2 - move;\r
633                         dir = pdirx[ob->temp1];\r
634                         if (dir == 1)\r
635                         {\r
636                                 xtry = move;\r
637                         }\r
638                         else if (dir == -1)\r
639                         {\r
640                                 xtry = -move;\r
641                         }\r
642                         dir = pdiry[ob->temp1];\r
643                         if (dir == 1)\r
644                         {\r
645                                 ytry = move;\r
646 \r
647                         }\r
648                         else if (dir == -1)\r
649                         {\r
650                                 ytry = -move;\r
651                         }\r
652                 }\r
653                 else\r
654                 {\r
655                         dir = pdirx[ob->temp1];\r
656                         if (dir == 1)\r
657                         {\r
658                                 xtry = ob->temp2;\r
659                         }\r
660                         else if (dir == -1)\r
661                         {\r
662                                 xtry = -ob->temp2;\r
663                         }\r
664                         dir = pdiry[ob->temp1];\r
665                         if (dir == 1)\r
666                         {\r
667                                 ytry = ob->temp2;\r
668                         }\r
669                         else if (dir == -1)\r
670                         {\r
671                                 ytry = -ob->temp2;\r
672                         }\r
673 \r
674                         tx = CONVERT_GLOBAL_TO_TILE(ob->x + xtry);\r
675                         ty = CONVERT_GLOBAL_TO_TILE(ob->y + ytry);\r
676                         ob->temp1 = *(mapsegs[2]+mapbwidthtable[ty]/2 + tx)-DIRARROWSTART;\r
677                         if (ob->temp1 < arrow_North || ob->temp1 > arrow_None)\r
678                         {\r
679                                 ob->x += xtry;\r
680                                 ob->y += ytry;\r
681                                 ChangeState(ob, &s_rocket);\r
682 \r
683                                 player->x = CONVERT_TILE_TO_GLOBAL(tx+1) + 1*PIXGLOBAL;\r
684                                 player->y = CONVERT_TILE_TO_GLOBAL(ty+1);\r
685                                 player->obclass = keenobj;\r
686                                 player->shapenum = WORLDKEENR3SPR;\r
687                                 player->needtoclip = cl_midclip;\r
688                                 NewState(player, &s_worldkeen);\r
689                                 gamestate.rocketstate ^= 1;\r
690                         }\r
691                         else\r
692                         {\r
693                                 move -= ob->temp2;\r
694                                 ob->temp2 = TILEGLOBAL - move;\r
695                                 dir = pdirx[ob->temp1];\r
696                                 if (dir == 1)\r
697                                 {\r
698                                         xtry = xtry + move;\r
699                                 }\r
700                                 else if (dir == -1)\r
701                                 {\r
702                                         xtry = xtry - move;\r
703                                 }\r
704                                 dir = pdiry[ob->temp1];\r
705                                 if (dir == 1)\r
706                                 {\r
707                                         ytry = ytry + move;\r
708                                 }\r
709                                 else if (dir == -1)\r
710                                 {\r
711                                         ytry = ytry - move;\r
712                                 }\r
713                         }\r
714                 }\r
715         }\r
716 }\r
717 \r
718 /*\r
719 =============================================================================\r
720 \r
721                                                   GRAPPLE SPOT\r
722 \r
723 temp1 = type (0 = top of cliff, 1 = bottom of cliff)\r
724 \r
725 =============================================================================\r
726 */\r
727 \r
728 statetype s_grapplespot   = {-1,                 -1,                 think, false, false,  0, 0,  0, NULL, C_GrappleSpot, R_Draw, NULL};\r
729 statetype s_throwrope1    = {WORLDKEENTRHOW1SPR, WORLDKEENTRHOW1SPR, step,  false, false, 10, 0,  0, NULL, NULL, R_Draw, &s_throwrope2};\r
730 statetype s_throwrope2    = {WORLDKEENTRHOW2SPR, WORLDKEENTRHOW2SPR, step,  false, false,  8, 0,  0, T_ThrowRope, NULL, R_Draw, &s_worldkeen};\r
731 statetype s_climbrope1    = {WORLDKEENCLIMB1SPR, WORLDKEENCLIMB1SPR, slide, true,  false,  4, 0, 16, NULL, NULL, R_Draw, &s_climbrope2};\r
732 statetype s_climbrope2    = {WORLDKEENCLIMB2SPR, WORLDKEENCLIMB2SPR, slide, true,  false,  4, 0, 16, T_ClimbRope, NULL, R_Draw, &s_climbrope1};\r
733 statetype s_maprope       = {ROPETHROW2SPR,      ROPETHROW2SPR,      think, false, false,  0, 0,  0, NULL, NULL, R_Draw, NULL};\r
734 statetype s_mapropeshort  = {ROPETHROW1SPR,      ROPETHROW1SPR,      step,  false, false, 10, 0,  0, NULL, NULL, R_Draw, &s_mapropeshort};\r
735 \r
736 /*\r
737 ===========================\r
738 =\r
739 = SpawnGrappleSpot\r
740 =\r
741 ===========================\r
742 */\r
743 \r
744 void SpawnGrappleSpot(Uint16 tileX, Uint16 tileY, Uint16 type)\r
745 {\r
746         GetNewObj(false);\r
747         new->active = ac_yes;\r
748         new->needtoclip = cl_noclip;\r
749         new->priority = 2;\r
750         new->obclass = grapplespotobj;\r
751         new->tileleft = new->tileright = tileX;\r
752         new->tiletop = new->tilebottom = tileY;\r
753         new->temp1 = type;\r
754         new->x = new->left = CONVERT_TILE_TO_GLOBAL(tileX);\r
755         new->right = new->left + TILEGLOBAL;\r
756         if (type)\r
757         {\r
758                 new->y = new->top = CONVERT_TILE_TO_GLOBAL(tileY+1)-1;\r
759         }\r
760         else\r
761         {\r
762                 new->y = new->top = CONVERT_TILE_TO_GLOBAL(tileY);\r
763         }\r
764         new->bottom = new->top + 1;\r
765         NewState(new, &s_grapplespot);\r
766 \r
767         if (gamestate.hookstate == 2 && type)\r
768         {\r
769                 GetNewObj(false);\r
770                 new->active = ac_yes;\r
771                 new->needtoclip = cl_noclip;\r
772                 new->priority = 0;\r
773                 new->obclass = inertobj;\r
774                 new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
775                 new->y = CONVERT_TILE_TO_GLOBAL(tileY+1);\r
776                 NewState(new, &s_maprope);\r
777         }\r
778 }\r
779 \r
780 /*\r
781 ===========================\r
782 =\r
783 = T_ThrowRope\r
784 =\r
785 ===========================\r
786 */\r
787 \r
788 void T_ThrowRope(objtype *ob)\r
789 {\r
790         GetNewObj(false);\r
791         new->active = ac_yes;\r
792         new->needtoclip = cl_noclip;\r
793         new->priority = 0;\r
794         new->obclass = inertobj;\r
795         new->x = CONVERT_TILE_TO_GLOBAL(ob->tileright);\r
796         new->y = ob->y - 2*TILEGLOBAL;\r
797         NewState(new, &s_maprope);\r
798 \r
799         ob->obclass = keenobj;\r
800         ob->shapenum = WORLDKEENU3SPR;\r
801 }\r
802 \r
803 /*\r
804 ===========================\r
805 =\r
806 = T_ClimbRope\r
807 =\r
808 ===========================\r
809 */\r
810 \r
811 void T_ClimbRope(objtype *ob)\r
812 {\r
813         if (--ob->temp4 == 0)\r
814         {\r
815                 if (ob->ydir == 1)\r
816                 {\r
817                         ob->y += 3*PIXGLOBAL;\r
818                         ob->shapenum = WORLDKEEND3SPR;\r
819                 }\r
820                 else\r
821                 {\r
822                         ob->y -= 3*PIXGLOBAL;\r
823                         ob->shapenum = WORLDKEENU3SPR;\r
824                 }\r
825                 ob->obclass = keenobj;\r
826                 NewState(ob, &s_worldkeen);\r
827         }\r
828 }\r
829 \r
830 /*\r
831 ===========================\r
832 =\r
833 = C_GrappleSpot\r
834 =\r
835 ===========================\r
836 */\r
837 \r
838 void C_GrappleSpot(objtype *ob, objtype *hit)\r
839 {\r
840         if (hit->obclass == keenobj)\r
841         {\r
842                 switch (gamestate.hookstate)\r
843                 {\r
844                 case 0:\r
845                         CA_CacheGrChunk(KEENTALK1PIC);\r
846                         VW_FixRefreshBuffer();\r
847                         US_CenterWindow(26, 8);\r
848                         VWB_DrawPic(WindowX+WindowW-48, WindowY, KEENTALK1PIC);\r
849                         WindowW -= 48;\r
850                         PrintY += 15;\r
851                         US_CPrint(\r
852                                 "What a tall cliff!\n"\r
853                                 "Wish I had a rope\n"\r
854                                 "and grappling hook.\n"\r
855                                 );\r
856                         VW_UpdateScreen();\r
857                         SD_PlaySound(SND_NOWAY);\r
858                         VW_WaitVBL(30);\r
859                         IN_ClearKeysDown();\r
860                         IN_Ack();\r
861                         RF_ForceRefresh();\r
862 \r
863                         //push Keen back\r
864                         xtry = -hit->xmove;\r
865                         ytry = -hit->ymove;\r
866                         hit->xdir = hit->ydir = 0;\r
867                         ClipToWalls(hit);\r
868                         break;\r
869 \r
870                 case 1:\r
871                         gamestate.hookstate++;\r
872                         SD_PlaySound(SND_THROWROPE);\r
873                         ChangeState(hit, &s_throwrope1);\r
874                         hit->obclass = inertobj;\r
875                         break;\r
876 \r
877                 case 2:\r
878                         if (ob->temp1)\r
879                         {\r
880                                 hit->y += 4*PIXGLOBAL;\r
881                                 hit->temp4 = 6;\r
882                                 hit->ydir = 1;\r
883                         }\r
884                         else\r
885                         {\r
886                                 hit->y -= 4*PIXGLOBAL;\r
887                                 hit->temp4 = 6;\r
888                                 hit->ydir = -1;\r
889                         }\r
890                         NewState(hit, &s_climbrope1);\r
891                         hit->obclass = inertobj;\r
892                 }\r
893         }\r
894 }\r
895 \r
896 /*\r
897 =============================================================================\r
898 \r
899                                                   SATELLITE\r
900 \r
901 temp1 = direction (satellite) / type (stop points)\r
902 temp2 = countdown to next dir check\r
903 temp3 = type of stop point touched (low byte: current; high byte: previous)\r
904         is updated every frame and resets to 0 when no longer touching a spot\r
905 temp4 = type of last stop point passed over (1 or 2, never 0)\r
906         is updated when no longer touching the spot\r
907 \r
908 =============================================================================\r
909 */\r
910 \r
911 statetype s_satellitestopspot  = {-1,               -1,               think,     false, false,  0, 0, 0, NULL, NULL, NULL, NULL};\r
912 statetype s_worldkeensatellite = {WORLDKEENHANGSPR, WORLDKEENHANGSPR, think,     false, false,  0, 0, 0, NULL, NULL, R_WorldKeenSatellite, NULL};\r
913 statetype s_satellite1         = {SATELLITE1SPR,    SATELLITE1SPR,    stepthink, false, false, 10, 0, 0, T_Satellite, C_Satellite, R_Draw, &s_satellite2};\r
914 statetype s_satellite2         = {SATELLITE2SPR,    SATELLITE2SPR,    stepthink, false, false, 10, 0, 0, T_Satellite, C_Satellite, R_Draw, &s_satellite3};\r
915 statetype s_satellite3         = {SATELLITE3SPR,    SATELLITE3SPR,    stepthink, false, false, 10, 0, 0, T_Satellite, C_Satellite, R_Draw, &s_satellite4};\r
916 statetype s_satellite4         = {SATELLITE4SPR,    SATELLITE4SPR,    stepthink, false, false, 10, 0, 0, T_Satellite, C_Satellite, R_Draw, &s_satellite1};\r
917 \r
918 /*\r
919 ===========================\r
920 =\r
921 = SpawnSatelliteStop\r
922 =\r
923 ===========================\r
924 */\r
925 \r
926 void SpawnSatelliteStop(Uint16 tileX, Uint16 tileY, Uint16 type)\r
927 {\r
928         GetNewObj(false);\r
929         new->active = ac_allways;\r
930         new->needtoclip = cl_noclip;\r
931         new->priority = 2;\r
932         new->obclass = satellitestopobj;\r
933         new->tileleft=new->tileright=tileX;\r
934         new->tiletop=new->tilebottom=tileY;\r
935         new->temp1 = (type ^ 1) + 1;    // type is either 0 or 1, so this just maps 0 to 2 and 1 to 1\r
936         new->x=new->left = CONVERT_TILE_TO_GLOBAL(tileX);\r
937         new->right = new->left + TILEGLOBAL;\r
938         new->y=new->top = CONVERT_TILE_TO_GLOBAL(tileY);\r
939         new->bottom = new->top + TILEGLOBAL;\r
940         NewState(new, &s_satellitestopspot);\r
941 }\r
942 \r
943 /*\r
944 ===========================\r
945 =\r
946 = SpawnSatellite\r
947 =\r
948 ===========================\r
949 */\r
950 \r
951 void SpawnSatellite(Uint16 tileX, Uint16 tileY)\r
952 {\r
953         Sint16 dir;\r
954 \r
955         GetNewObj(false);\r
956         new->needtoclip = cl_noclip;\r
957         new->priority = 2;\r
958         new->active = ac_allways;\r
959         new->obclass = satelliteobj;\r
960         new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
961         new->y = CONVERT_TILE_TO_GLOBAL(tileY);\r
962         new->xdir = 0;\r
963         new->ydir = 1;\r
964         NewState(new, &s_satellite1);\r
965 \r
966         dir = arrow_West;\r
967         (mapsegs[2]+mapbwidthtable[tileY]/2)[tileX] = (dir+DIRARROWSTART);\r
968         new->temp1 = dir;\r
969         new->temp2 = TILEGLOBAL;\r
970         new->temp4 = 2;\r
971 }\r
972 \r
973 /*\r
974 ===========================\r
975 =\r
976 = T_Satellite\r
977 =\r
978 ===========================\r
979 */\r
980 \r
981 void T_Satellite(objtype *ob)\r
982 {\r
983         //\r
984         // this code could be executed twice during the same frame because the\r
985         // object's animation/state changed during that frame, so don't update\r
986         // anything if we already have movement for the current frame i.e. the\r
987         // update code has already been executed this frame\r
988         //\r
989         if (xtry == 0 && ytry == 0)\r
990         {\r
991                 //\r
992                 // if current stop spot type is 0 (not touching a spot), but previous\r
993                 // type is not 0, then set temp4 to the previous type\r
994                 //\r
995                 if (!(ob->temp3 & 0xFF) && (ob->temp3 & 0xFF00))\r
996                 {\r
997                         ob->temp4 = ob->temp3 >> 8;\r
998                 }\r
999                 //\r
1000                 // move current type into previous type and set current stop type to 0\r
1001                 //\r
1002                 ob->temp3 <<= 8;\r
1003 \r
1004                 //\r
1005                 // follow the arrow path like a GoPlat\r
1006                 //\r
1007                 T_GoPlat(ob);\r
1008         }\r
1009 }\r
1010 \r
1011 /*\r
1012 ===========================\r
1013 =\r
1014 = C_Satellite\r
1015 =\r
1016 ===========================\r
1017 */\r
1018 \r
1019 void C_Satellite(objtype *ob, objtype *hit)\r
1020 {\r
1021         Sint16 temp;\r
1022         objtype *o;\r
1023 \r
1024         if (hit->state == &s_satellitestopspot)\r
1025         {\r
1026                 ob->temp3 |= hit->temp1;\r
1027         }\r
1028         else if (hit->obclass == keenobj)\r
1029         {\r
1030                 //\r
1031                 // check if satellite has reaced a new stop spot\r
1032                 //\r
1033                 temp = ob->temp3 >> 8;\r
1034                 if (temp && temp != ob->temp4)\r
1035                 {\r
1036                         SD_PlaySound(SND_GRABSATELLITE);\r
1037                         //\r
1038                         // update last spot value (don't grab or drop Keen until moved to the next spot)\r
1039                         //\r
1040                         ob->temp4 = temp;\r
1041                         if (player->state == &s_worldkeensatellite)\r
1042                         {\r
1043                                 //\r
1044                                 // drop Keen off at the current stop spot\r
1045                                 //\r
1046                                 for (o=player->next; o; o=o->next)\r
1047                                 {\r
1048                                         if (o->obclass == satellitestopobj && o->temp1 == temp)\r
1049                                         {\r
1050                                                 hit->x = o->x;\r
1051                                                 hit->y = o->y;\r
1052                                                 hit->shapenum = WORLDKEENU3SPR;\r
1053                                                 ChangeState(player, &s_worldkeen);\r
1054                                                 hit->needtoclip = cl_midclip;\r
1055                                                 return;\r
1056                                         }\r
1057                                 }\r
1058                         }\r
1059                         else\r
1060                         {\r
1061                                 //\r
1062                                 // grab and carry Keen\r
1063                                 //\r
1064                                 hit->x = ob->x + 12*PIXGLOBAL;\r
1065                                 hit->y = ob->y + 16*PIXGLOBAL;\r
1066                                 hit->needtoclip = cl_noclip;\r
1067                                 ChangeState(player, &s_worldkeensatellite);\r
1068                         }\r
1069                 }\r
1070                 else if (hit->state == &s_worldkeensatellite)\r
1071                 {\r
1072                         //\r
1073                         // move Keen along with the satellite\r
1074                         //\r
1075                         hit->x = ob->x + 12*PIXGLOBAL;\r
1076                         hit->y = ob->y + 16*PIXGLOBAL;\r
1077                         ChangeState(hit, hit->state);\r
1078                 }\r
1079         }\r
1080 }\r
1081 \r
1082 /*\r
1083 ===========================\r
1084 =\r
1085 = R_WorldKeenSatellite\r
1086 =\r
1087 ===========================\r
1088 */\r
1089 \r
1090 void R_WorldKeenSatellite(objtype *ob)\r
1091 {\r
1092         RF_PlaceSprite(&ob->sprite, ob->x + 4*PIXGLOBAL, ob->y + 8*PIXGLOBAL, WORLDKEENHANGSPR, spritedraw, 1);\r
1093 }\r
1094 \r
1095 /*\r
1096 =============================================================================\r
1097 \r
1098                                                   SANDWICH\r
1099 \r
1100 =============================================================================\r
1101 */\r
1102 \r
1103 statetype s_sandwich      = {SANDWICHSPR, SANDWICHSPR, think, false, false, 0, 0, 0, NULL, C_Molly, R_Draw, NULL};\r
1104 \r
1105 /*\r
1106 ===========================\r
1107 =\r
1108 = SpawnSandwich\r
1109 =\r
1110 ===========================\r
1111 */\r
1112 \r
1113 void SpawnSandwich(Uint16 tileX, Uint16 tileY)\r
1114 {\r
1115         GetNewObj(false);\r
1116         new->needtoclip = cl_noclip;\r
1117         new->priority = 2;\r
1118         new->obclass = sandwichobj;\r
1119         new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
1120         new->y = CONVERT_TILE_TO_GLOBAL(tileY);\r
1121         NewState(new, &s_sandwich);\r
1122 }\r
1123 \r
1124 /*\r
1125 =============================================================================\r
1126 \r
1127                                                   GRAPPLING HOOK\r
1128 \r
1129 =============================================================================\r
1130 */\r
1131 \r
1132 statetype s_hook          = {HOOKSPR, HOOKSPR, think, false, false, 0, 0, 0, NULL, C_Molly, R_Draw, NULL};\r
1133 \r
1134 /*\r
1135 ===========================\r
1136 =\r
1137 = SpawnHook\r
1138 =\r
1139 ===========================\r
1140 */\r
1141 \r
1142 void SpawnHook(Uint16 tileX, Uint16 tileY)\r
1143 {\r
1144         GetNewObj(false);\r
1145         new->needtoclip = cl_noclip;\r
1146         new->priority = 2;\r
1147         new->obclass = hookobj;\r
1148         new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
1149         new->y = CONVERT_TILE_TO_GLOBAL(tileY);\r
1150         NewState(new, &s_hook);\r
1151 }\r
1152 \r
1153 /*\r
1154 =============================================================================\r
1155 \r
1156                                                   PASSCARD\r
1157 \r
1158 =============================================================================\r
1159 */\r
1160 \r
1161 statetype s_passcard      = {PASSCARDSPR, PASSCARDSPR, think, false, false, 0, 0, 0, NULL, C_Molly, R_Draw, NULL};\r
1162 \r
1163 /*\r
1164 ===========================\r
1165 =\r
1166 = SpawnPasscard\r
1167 =\r
1168 ===========================\r
1169 */\r
1170 \r
1171 void SpawnPasscard(Uint16 tileX, Uint16 tileY)\r
1172 {\r
1173         GetNewObj(false);\r
1174         new->needtoclip = cl_noclip;\r
1175         new->priority = 2;\r
1176         new->obclass = passcardobj;\r
1177         new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
1178         new->y = CONVERT_TILE_TO_GLOBAL(tileY);\r
1179         NewState(new, &s_passcard);\r
1180 }\r
1181 \r
1182 //============================================================================\r
1183 \r
1184 /*\r
1185 ===========================\r
1186 =\r
1187 = C_Molly\r
1188 =\r
1189 ===========================\r
1190 */\r
1191 \r
1192 void C_Molly(objtype *ob, objtype *hit)\r
1193 {\r
1194         if (hit->obclass == keenobj)\r
1195         {\r
1196                 switch (ob->obclass)\r
1197                 {\r
1198                 case sandwichobj:\r
1199                         playstate = ex_sandwich;\r
1200                         break;\r
1201 \r
1202                 case hookobj:\r
1203                         playstate = ex_hook;\r
1204                         break;\r
1205 \r
1206                 case passcardobj:\r
1207                         playstate = ex_card;\r
1208                         break;\r
1209 \r
1210                 case mollyobj:\r
1211                         playstate = ex_molly;\r
1212                 }\r
1213         }\r
1214 }\r
1215 \r
1216 /*\r
1217 =============================================================================\r
1218 \r
1219                                                   MOLLY\r
1220 \r
1221 =============================================================================\r
1222 */\r
1223 \r
1224 statetype s_molly1        = {MOLLY1SPR, MOLLY1SPR, step, false, false, 20, 0, 0, NULL, C_Molly, R_Draw, &s_molly2};\r
1225 statetype s_molly2        = {MOLLY2SPR, MOLLY2SPR, step, false, false, 40, 0, 0, NULL, C_Molly, R_Draw, &s_molly3};\r
1226 statetype s_molly3        = {MOLLY1SPR, MOLLY1SPR, step, false, false, 40, 0, 0, NULL, C_Molly, R_Draw, &s_molly4};\r
1227 statetype s_molly4        = {MOLLY2SPR, MOLLY2SPR, step, false, false, 20, 0, 0, NULL, C_Molly, R_Draw, &s_molly1};\r
1228 \r
1229 /*\r
1230 ===========================\r
1231 =\r
1232 = SpawnMolly\r
1233 =\r
1234 ===========================\r
1235 */\r
1236 \r
1237 void SpawnMolly(Uint16 tileX, Uint16 tileY)\r
1238 {\r
1239         GetNewObj(false);\r
1240         new->obclass = mollyobj;\r
1241         new->active = ac_yes;\r
1242         new->priority = 0;\r
1243         new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
1244         new->y = CONVERT_TILE_TO_GLOBAL(tileY) + -8*PIXGLOBAL;\r
1245         if (US_RndT() < 0x80)\r
1246         {\r
1247                 new->xdir = 1;\r
1248         }\r
1249         else\r
1250         {\r
1251                 new->xdir = -1;\r
1252         }\r
1253         new->ydir = 1;\r
1254         NewState(new, &s_molly1);\r
1255 }\r
1256 \r
1257 /*\r
1258 =============================================================================\r
1259 \r
1260                                                   PLATFORM\r
1261 \r
1262 =============================================================================\r
1263 */\r
1264 \r
1265 statetype s_platform      = {PLATFORMSPR, PLATFORMSPR, think, false, false, 0, 0, 0, T_Platform, NULL, R_Draw, NULL};\r
1266 \r
1267 /*\r
1268 ===========================\r
1269 =\r
1270 = SpawnPlatform\r
1271 =\r
1272 ===========================\r
1273 */\r
1274 \r
1275 void SpawnPlatform(Uint16 tileX, Uint16 tileY, Sint16 dir)\r
1276 {\r
1277         GetNewObj(false);\r
1278         new->obclass = platformobj;\r
1279         new->active = ac_allways;\r
1280         new->priority = 0;\r
1281         new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
1282         new->y = CONVERT_TILE_TO_GLOBAL(tileY);\r
1283         switch (dir)\r
1284         {\r
1285         case 0:\r
1286                 new->xdir = 0;\r
1287                 new->ydir = -1;\r
1288                 break;\r
1289         case 1:\r
1290                 new->xdir = 1;\r
1291                 new->ydir = 0;\r
1292                 break;\r
1293         case 2:\r
1294                 new->xdir = 0;\r
1295                 new->ydir = 1;\r
1296                 break;\r
1297         case 3:\r
1298                 new->xdir = -1;\r
1299                 new->ydir = 0;\r
1300         }\r
1301         NewState(new, &s_platform);\r
1302 }\r
1303 \r
1304 /*\r
1305 ===========================\r
1306 =\r
1307 = T_Platform\r
1308 =\r
1309 ===========================\r
1310 */\r
1311 \r
1312 void T_Platform(objtype *ob)\r
1313 {\r
1314         Uint16 newpos, newtile;\r
1315 \r
1316         xtry = ob->xdir * 12 * tics;\r
1317         ytry = ob->ydir * 12 * tics;\r
1318 \r
1319         if (ob->xdir == 1)\r
1320         {\r
1321                 newpos = ob->right + xtry;\r
1322                 newtile = CONVERT_GLOBAL_TO_TILE(newpos);\r
1323                 if (ob->tileright != newtile)\r
1324                 {\r
1325                         if (*(mapsegs[2]+mapbwidthtable[ob->tiletop]/2 + newtile) == PLATFORMBLOCK)\r
1326                         {\r
1327                                 ob->xdir = -1;\r
1328                                 xtry = xtry - (newpos & 0xFF);\r
1329                         }\r
1330                 }\r
1331         }\r
1332         else if (ob->xdir == -1)\r
1333         {\r
1334                 newpos = ob->left + xtry;\r
1335                 newtile = CONVERT_GLOBAL_TO_TILE(newpos);\r
1336                 if (ob->tileleft != newtile)\r
1337                 {\r
1338                         if (*(mapsegs[2]+mapbwidthtable[ob->tiletop]/2 + newtile) == PLATFORMBLOCK)\r
1339                         {\r
1340                                 ob->xdir = 1;\r
1341                                 xtry = xtry + (TILEGLOBAL - (newpos & 0xFF));\r
1342                         }\r
1343                 }\r
1344         }\r
1345         else if (ob->ydir == 1)\r
1346         {\r
1347                 newpos = ob->bottom + ytry;\r
1348                 newtile = CONVERT_GLOBAL_TO_TILE(newpos);\r
1349                 if (ob->tilebottom != newtile)\r
1350                 {\r
1351                         if (*(mapsegs[2]+mapbwidthtable[newtile]/2 + ob->tileleft) == PLATFORMBLOCK)\r
1352                         {\r
1353                                 if (*(mapsegs[2]+mapbwidthtable[newtile-2]/2 + ob->tileleft) == PLATFORMBLOCK)\r
1354                                 {\r
1355                                         ytry = 0;\r
1356                                         ob->needtoreact = true;\r
1357                                 }\r
1358                                 else\r
1359                                 {\r
1360                                         ob->ydir = -1;\r
1361                                         ytry = ytry - (newpos & 0xFF);\r
1362                                 }\r
1363                         }\r
1364                 }\r
1365         }\r
1366         else if (ob->ydir == -1)\r
1367         {\r
1368                 newpos = ob->top + ytry;\r
1369                 newtile = CONVERT_GLOBAL_TO_TILE(newpos);\r
1370                 if (ob->tiletop != newtile)\r
1371                 {\r
1372                         if (*(mapsegs[2]+mapbwidthtable[newtile]/2 + ob->tileleft) == PLATFORMBLOCK)\r
1373                         {\r
1374                                 if (*(mapsegs[2]+mapbwidthtable[newtile+2]/2 + ob->tileleft) == PLATFORMBLOCK)\r
1375                                 {\r
1376                                         ytry = 0;\r
1377                                         ob->needtoreact = true;\r
1378                                 }\r
1379                                 else\r
1380                                 {\r
1381                                         ob->ydir = 1;\r
1382                                         ytry = ytry + (TILEGLOBAL - (newpos & 0xFF));\r
1383                                 }\r
1384                         }\r
1385                 }\r
1386         }\r
1387 }\r
1388 \r
1389 /*\r
1390 =============================================================================\r
1391 \r
1392                                                   DROPPING PLATFORM\r
1393 \r
1394 temp1 = initial y position\r
1395 \r
1396 =============================================================================\r
1397 */\r
1398 \r
1399 statetype s_dropplatsit  = {PLATFORMSPR, PLATFORMSPR, think,      false, false, 0, 0,   0, T_DropPlatSit, NULL, R_Draw, NULL};\r
1400 statetype s_fallplatfall = {PLATFORMSPR, PLATFORMSPR, think,      false, false, 0, 0,   0, T_DropPlatFall, NULL, R_Draw, NULL};\r
1401 statetype s_fallplatrise = {PLATFORMSPR, PLATFORMSPR, slidethink, false, false, 0, 0, -32, T_DropPlatRise, NULL, R_Draw, NULL};\r
1402 \r
1403 /*\r
1404 ===========================\r
1405 =\r
1406 = SpawnDropPlat\r
1407 =\r
1408 ===========================\r
1409 */\r
1410 \r
1411 void SpawnDropPlat(Uint16 tileX, Uint16 tileY)\r
1412 {\r
1413         GetNewObj(false);\r
1414         new->obclass = platformobj;\r
1415         new->active = ac_allways;\r
1416         new->priority = 0;\r
1417         new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
1418         new->y=new->temp1 = CONVERT_TILE_TO_GLOBAL(tileY);\r
1419         new->xdir = 0;\r
1420         new->ydir = 1;\r
1421         new->needtoclip = cl_noclip;\r
1422         NewState(new, &s_dropplatsit);\r
1423 }\r
1424 \r
1425 /*\r
1426 ===========================\r
1427 =\r
1428 = T_DropPlatSit\r
1429 =\r
1430 ===========================\r
1431 */\r
1432 \r
1433 void T_DropPlatSit(objtype *ob)\r
1434 {\r
1435         if (gamestate.riding == ob)\r
1436         {\r
1437                 ytry = tics << 4;       //tics * 16;\r
1438                 ob->yspeed = 0;\r
1439                 if (ob->y + ytry - ob->temp1 >= 8*PIXGLOBAL)\r
1440                         ob->state = &s_fallplatfall;\r
1441         }\r
1442 }\r
1443 \r
1444 /*\r
1445 ===========================\r
1446 =\r
1447 = T_DropPlatFall\r
1448 =\r
1449 ===========================\r
1450 */\r
1451 \r
1452 void T_DropPlatFall(objtype *ob)\r
1453 {\r
1454         Uint16 newpos, newtile;\r
1455 \r
1456         DoGravity(ob);\r
1457 \r
1458 #if 0\r
1459         // bugfix: don't skip a tile (this is present in Keen 4, but missing in 5 & 6)\r
1460         if (ytry >= 15*PIXGLOBAL)\r
1461                 ytry = 15*PIXGLOBAL;\r
1462 #endif\r
1463 \r
1464         newpos = ob->bottom + ytry;\r
1465         newtile = CONVERT_GLOBAL_TO_TILE(newpos);\r
1466         if (ob->tilebottom != newtile)\r
1467         {\r
1468                 if (*(mapsegs[2]+mapbwidthtable[newtile]/2 + ob->tileleft) == PLATFORMBLOCK)\r
1469                 {\r
1470                         ytry = 0xFF - (ob->bottom & 0xFF);\r
1471                         if (gamestate.riding != ob)\r
1472                                 ob->state = &s_fallplatrise;\r
1473                 }\r
1474         }\r
1475 }\r
1476 \r
1477 /*\r
1478 ===========================\r
1479 =\r
1480 = T_DropPlatRise\r
1481 =\r
1482 ===========================\r
1483 */\r
1484 \r
1485 void T_DropPlatRise(objtype *ob)\r
1486 {\r
1487         if (gamestate.riding == ob)\r
1488         {\r
1489                 ob->yspeed = 0;\r
1490                 ob->state = &s_fallplatfall;\r
1491         }\r
1492         else if (ob->y <= ob->temp1)\r
1493         {\r
1494                 ytry = ob->temp1 - ob->y;\r
1495                 ob->state = &s_dropplatsit;\r
1496         }\r
1497 }\r
1498 \r
1499 /*\r
1500 =============================================================================\r
1501 \r
1502                                                   STATIC PLATFORM\r
1503 \r
1504 temp1 = initial y position (is set but never used)\r
1505 \r
1506 =============================================================================\r
1507 */\r
1508 \r
1509 statetype s_staticplatform = {PLATFORMSPR, PLATFORMSPR, step, false, false, 32000, 0, 0, NULL, NULL, R_Draw, &s_staticplatform};\r
1510 \r
1511 /*\r
1512 ===========================\r
1513 =\r
1514 = SpawnStaticPlat\r
1515 =\r
1516 ===========================\r
1517 */\r
1518 \r
1519 void SpawnStaticPlat(Uint16 tileX, Uint16 tileY)\r
1520 {\r
1521         GetNewObj(false);\r
1522         new->obclass = platformobj;\r
1523         new->active = ac_yes;\r
1524         new->priority = 0;\r
1525         new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
1526         new->y=new->temp1 = CONVERT_TILE_TO_GLOBAL(tileY);\r
1527         new->xdir = 0;\r
1528         new->ydir = 1;\r
1529         new->needtoclip = cl_noclip;\r
1530         NewState(new, &s_staticplatform);\r
1531 }\r
1532 \r
1533 /*\r
1534 =============================================================================\r
1535 \r
1536                                                   GO PLATFORM\r
1537 \r
1538 temp1 = direction\r
1539 temp2 = countdown to next dir check\r
1540 temp3 = sprite pointer for the Bip sprite\r
1541 \r
1542 =============================================================================\r
1543 */\r
1544 \r
1545 statetype s_goplat        = {PLATFORMSPR, PLATFORMSPR, think, false, false, 0, 0, 0, T_GoPlat, NULL, R_GoPlat, NULL};\r
1546 \r
1547 /*\r
1548 ===========================\r
1549 =\r
1550 = SpawnGoPlat\r
1551 =\r
1552 ===========================\r
1553 */\r
1554 \r
1555 void SpawnGoPlat(Uint16 tileX, Uint16 tileY, Sint16 dir)\r
1556 {\r
1557         GetNewObj(false);\r
1558         new->obclass = platformobj;\r
1559         new->active = ac_allways;\r
1560         new->priority = 0;\r
1561         new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
1562         new->y = CONVERT_TILE_TO_GLOBAL(tileY);\r
1563         new->xdir = 0;\r
1564         new->ydir = 1;\r
1565         new->needtoclip = cl_noclip;\r
1566         NewState(new, &s_goplat);\r
1567         *(mapsegs[2]+mapbwidthtable[tileY]/2 + tileX) = dir + DIRARROWSTART;\r
1568         new->temp1 = dir;\r
1569         new->temp2 = TILEGLOBAL;\r
1570 }\r
1571 \r
1572 /*\r
1573 ===========================\r
1574 =\r
1575 = T_GoPlat\r
1576 =\r
1577 ===========================\r
1578 */\r
1579 \r
1580 void T_GoPlat(objtype *ob)\r
1581 {\r
1582         Uint16 move;\r
1583         Sint16 dir;\r
1584         Uint16 tx, ty;\r
1585 \r
1586         move = tics * 12;\r
1587         if (ob->temp2 > move)\r
1588         {\r
1589                 ob->temp2 = ob->temp2 - move;\r
1590 \r
1591                 dir = pdirx[ob->temp1];\r
1592                 if (dir == 1)\r
1593                 {\r
1594                         xtry = xtry + move;\r
1595                 }\r
1596                 else if (dir == -1)\r
1597                 {\r
1598                         xtry = xtry + -move;\r
1599                 }\r
1600 \r
1601                 dir = pdiry[ob->temp1];\r
1602                 if (dir == 1)\r
1603                 {\r
1604                         ytry = ytry + move;\r
1605                 }\r
1606                 else if (dir == -1)\r
1607                 {\r
1608                         ytry = ytry + -move;\r
1609                 }\r
1610         }\r
1611         else\r
1612         {\r
1613                 dir = pdirx[ob->temp1];\r
1614                 if (dir == 1)\r
1615                 {\r
1616                         xtry += ob->temp2;\r
1617                 }\r
1618                 else if (dir == -1)\r
1619                 {\r
1620                         xtry += -ob->temp2;\r
1621                 }\r
1622 \r
1623                 dir = pdiry[ob->temp1];\r
1624                 if (dir == 1)\r
1625                 {\r
1626                         ytry += ob->temp2;\r
1627                 }\r
1628                 else if (dir == -1)\r
1629                 {\r
1630                         ytry += -ob->temp2;\r
1631                 }\r
1632 \r
1633                 tx = CONVERT_GLOBAL_TO_TILE(ob->x + xtry);\r
1634                 ty = CONVERT_GLOBAL_TO_TILE(ob->y + ytry);\r
1635                 ob->temp1 = *(mapsegs[2]+mapbwidthtable[ty]/2 + tx) - DIRARROWSTART;\r
1636                 if (ob->temp1 < arrow_North || ob->temp1 > arrow_None)\r
1637                 {\r
1638                         Quit("Goplat moved to a bad spot!");\r
1639                 }\r
1640 \r
1641                 move -= ob->temp2;\r
1642                 ob->temp2 = TILEGLOBAL - move;\r
1643 \r
1644                 dir = pdirx[ob->temp1];\r
1645                 if (dir == 1)\r
1646                 {\r
1647                         xtry = xtry + move;\r
1648                 }\r
1649                 else if (dir == -1)\r
1650                 {\r
1651                         xtry = xtry - move;\r
1652                 }\r
1653 \r
1654                 dir = pdiry[ob->temp1];\r
1655                 if (dir == 1)\r
1656                 {\r
1657                         ytry = ytry + move;\r
1658                 }\r
1659                 else if (dir == -1)\r
1660                 {\r
1661                         ytry = ytry - move;\r
1662                 }\r
1663         }\r
1664 }\r
1665 \r
1666 /*\r
1667 ===========================\r
1668 =\r
1669 = R_GoPlat\r
1670 =\r
1671 ===========================\r
1672 */\r
1673 \r
1674 void R_GoPlat(objtype *ob)\r
1675 {\r
1676         RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
1677         RF_PlaceSprite((void**)&ob->temp3, ob->x+TILEGLOBAL, ob->y+TILEGLOBAL, ob->temp1+PLATBIP1SPR, spritedraw, ob->priority);\r
1678 }\r
1679 \r
1680 /*\r
1681 =============================================================================\r
1682 \r
1683                                                   SNEAKY PLATFORM\r
1684 \r
1685 temp1 = initial x position (is set but never used)\r
1686 \r
1687 =============================================================================\r
1688 */\r
1689 \r
1690 statetype s_sneakplatsit    = {PLATFORMSPR, PLATFORMSPR, think, false, false,  0,   0, 0, T_SneakPlat, NULL, R_Draw, NULL};\r
1691 statetype s_sneakplatdodge  = {PLATFORMSPR, PLATFORMSPR, slide, false, false, 48,  32, 0, NULL, NULL, R_Draw, &s_sneakplatreturn};\r
1692 statetype s_sneakplatreturn = {PLATFORMSPR, PLATFORMSPR, slide, false, false, 96, -16, 0, NULL, NULL, R_Draw, &s_sneakplatsit};\r
1693 \r
1694 /*\r
1695 ===========================\r
1696 =\r
1697 = SpawnSneakPlat\r
1698 =\r
1699 ===========================\r
1700 */\r
1701 \r
1702 void SpawnSneakPlat(Uint16 tileX, Uint16 tileY)\r
1703 {\r
1704         GetNewObj(false);\r
1705         new->obclass = platformobj;\r
1706         new->active = ac_allways;\r
1707         new->priority = 0;\r
1708         new->x=new->temp1 = CONVERT_TILE_TO_GLOBAL(tileX);\r
1709         new->y = CONVERT_TILE_TO_GLOBAL(tileY);\r
1710         new->xdir = 0;\r
1711         new->ydir = 1;\r
1712         new->needtoclip = cl_noclip;\r
1713         NewState(new, &s_sneakplatsit);\r
1714 }\r
1715 \r
1716 /*\r
1717 ===========================\r
1718 =\r
1719 = T_SneakPlat\r
1720 =\r
1721 ===========================\r
1722 */\r
1723 \r
1724 void T_SneakPlat(objtype *ob)\r
1725 {\r
1726         Sint16 dist;\r
1727 \r
1728         if (player->state != &s_keenjump1)\r
1729                 return;\r
1730 \r
1731         if (player->xdir == 1)\r
1732         {\r
1733                 dist = ob->left-player->right;\r
1734                 if (dist > 4*TILEGLOBAL || dist < 0)\r
1735                         return;\r
1736         }\r
1737         else\r
1738         {\r
1739                 dist = player->left-ob->right;\r
1740                 if (dist > 4*TILEGLOBAL || dist < 0)\r
1741                         return;\r
1742         }\r
1743 \r
1744         dist = player->y - ob->y;\r
1745         if (dist < -6*TILEGLOBAL || dist > 6*TILEGLOBAL)\r
1746                 return;\r
1747 \r
1748         ob->xdir = player->xdir;\r
1749         ob->state = &s_sneakplatdodge;\r
1750 }\r
1751 \r
1752 /*\r
1753 =============================================================================\r
1754 \r
1755                                                   BLOOG\r
1756 \r
1757 =============================================================================\r
1758 */\r
1759 \r
1760 statetype s_bloogwalk1 = {BLOOGWALKL1SPR, BLOOGWALKR1SPR, step,  false, true, 10, 128, 0, T_BloogWalk, C_Bloog, R_Walk, &s_bloogwalk2};\r
1761 statetype s_bloogwalk2 = {BLOOGWALKL2SPR, BLOOGWALKR2SPR, step,  false, true, 10, 128, 0, T_BloogWalk, C_Bloog, R_Walk, &s_bloogwalk3};\r
1762 statetype s_bloogwalk3 = {BLOOGWALKL3SPR, BLOOGWALKR3SPR, step,  false, true, 10, 128, 0, T_BloogWalk, C_Bloog, R_Walk, &s_bloogwalk4};\r
1763 statetype s_bloogwalk4 = {BLOOGWALKL4SPR, BLOOGWALKR4SPR, step,  false, true, 10, 128, 0, T_BloogWalk, C_Bloog, R_Walk, &s_bloogwalk1};\r
1764 statetype s_bloogstun  = {BLOOGSTUNSPR,   BLOOGSTUNSPR,   think, false, false, 0,   0, 0, T_Projectile, NULL, R_Stunned, &s_bloogstun};\r
1765 \r
1766 /*\r
1767 ===========================\r
1768 =\r
1769 = SpawnBloog\r
1770 =\r
1771 ===========================\r
1772 */\r
1773 \r
1774 void SpawnBloog(Uint16 tileX, Uint16 tileY)\r
1775 {\r
1776         GetNewObj(false);\r
1777         new->obclass = bloogobj;\r
1778         new->active = ac_yes;\r
1779         new->priority = 0;\r
1780         new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
1781         new->y = CONVERT_TILE_TO_GLOBAL(tileY) + -2*TILEGLOBAL;\r
1782         if (US_RndT() < 0x80)\r
1783         {\r
1784                 new->xdir = 1;\r
1785         }\r
1786         else\r
1787         {\r
1788                 new->xdir = -1;\r
1789         }\r
1790         new->ydir = 1;\r
1791         NewState(new, &s_bloogwalk1);\r
1792 }\r
1793 \r
1794 /*\r
1795 ===========================\r
1796 =\r
1797 = T_BloogWalk\r
1798 =\r
1799 ===========================\r
1800 */\r
1801 \r
1802 void T_BloogWalk(objtype *ob)\r
1803 {\r
1804         if (US_RndT() < 0x20)\r
1805         {\r
1806                 if (ob->x < player->x)\r
1807                 {\r
1808                         ob->xdir = 1;\r
1809                 }\r
1810                 else\r
1811                 {\r
1812                         ob->xdir = -1;\r
1813                 }\r
1814         }\r
1815 }\r
1816 \r
1817 /*\r
1818 ===========================\r
1819 =\r
1820 = C_Bloog\r
1821 =\r
1822 ===========================\r
1823 */\r
1824 \r
1825 void C_Bloog(objtype *ob, objtype *hit)\r
1826 {\r
1827         if (hit->obclass == keenobj)\r
1828         {\r
1829                 KillKeen();\r
1830         }\r
1831         else if (hit->obclass == stunshotobj)\r
1832         {\r
1833                 StunObj(ob, hit, &s_bloogstun);\r
1834         }\r
1835 }\r
1836 \r
1837 /*\r
1838 =============================================================================\r
1839 \r
1840                                                   BLOOGUARD\r
1841 \r
1842 temp1 = flash countdown\r
1843 temp2 = health\r
1844 \r
1845 =============================================================================\r
1846 */\r
1847 \r
1848 statetype s_blooguardwalk1   = {BLOOGUARDWALKL1SPR,  BLOOGUARDWALKR1SPR,  step,  false, true,  9, 128, 0, T_BlooguardWalk, C_Blooguard, R_Blooguard, &s_blooguardwalk2};\r
1849 statetype s_blooguardwalk2   = {BLOOGUARDWALKL2SPR,  BLOOGUARDWALKR2SPR,  step,  false, true,  9, 128, 0, T_BlooguardWalk, C_Blooguard, R_Blooguard, &s_blooguardwalk3};\r
1850 statetype s_blooguardwalk3   = {BLOOGUARDWALKL3SPR,  BLOOGUARDWALKR3SPR,  step,  false, true,  9, 128, 0, T_BlooguardWalk, C_Blooguard, R_Blooguard, &s_blooguardwalk4};\r
1851 statetype s_blooguardwalk4   = {BLOOGUARDWALKL4SPR,  BLOOGUARDWALKR4SPR,  step,  false, true,  9, 128, 0, T_BlooguardWalk, C_Blooguard, R_Blooguard, &s_blooguardwalk1};\r
1852 statetype s_blooguardattack1 = {BLOOGUARDSWINGL1SPR, BLOOGUARDSWINGR1SPR, step,  false, true, 30,   0, 0, NULL, C_Blooguard, R_Blooguard, &s_blooguardattack2};\r
1853 statetype s_blooguardattack2 = {BLOOGUARDSWINGL2SPR, BLOOGUARDSWINGR2SPR, step,  false, true,  9,   0, 0, NULL, C_Blooguard, R_Blooguard, &s_blooguardattack3};\r
1854 statetype s_blooguardattack3 = {BLOOGUARDSWINGL3SPR, BLOOGUARDSWINGR3SPR, step,  true,  true,  1,   0, 0, T_BlooguardAttack, C_Blooguard, R_Blooguard, &s_blooguardattack4};\r
1855 statetype s_blooguardattack4 = {BLOOGUARDSWINGL3SPR, BLOOGUARDSWINGR3SPR, step,  false, true,  9,   0, 0, NULL, C_Blooguard, R_Blooguard, &s_blooguardwalk1};\r
1856 statetype s_blooguardstun    = {BLOOGUARDSTUNSPR,    BLOOGUARDSTUNSPR,    think, false, false, 0,   0, 0, T_Projectile, NULL, R_Stunned, &s_blooguardstun};\r
1857 \r
1858 /*\r
1859 ===========================\r
1860 =\r
1861 = SpawnBlooguard\r
1862 =\r
1863 ===========================\r
1864 */\r
1865 \r
1866 void SpawnBlooguard(Uint16 tileX, Uint16 tileY)\r
1867 {\r
1868         GetNewObj(false);\r
1869         new->obclass = blooguardobj;\r
1870         new->active = ac_yes;\r
1871         new->priority = 0;\r
1872         new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
1873         new->y = CONVERT_TILE_TO_GLOBAL(tileY) + -40*PIXGLOBAL;\r
1874         if (US_RndT() < 0x80)\r
1875         {\r
1876                 new->xdir = 1;\r
1877         }\r
1878         else\r
1879         {\r
1880                 new->xdir = -1;\r
1881         }\r
1882         new->ydir = 1;\r
1883         new->temp2 = 3; // health\r
1884         NewState(new, &s_blooguardwalk1);\r
1885 }\r
1886 \r
1887 /*\r
1888 ===========================\r
1889 =\r
1890 = T_BlooguardWalk\r
1891 =\r
1892 ===========================\r
1893 */\r
1894 \r
1895 void T_BlooguardWalk(objtype *ob)\r
1896 {\r
1897         if (US_RndT() < 0x20)\r
1898         {\r
1899                 if (ob->x < player->x)\r
1900                 {\r
1901                         ob->xdir = 1;\r
1902                 }\r
1903                 else\r
1904                 {\r
1905                         ob->xdir = -1;\r
1906                 }\r
1907         }\r
1908         if ( ((ob->xdir == 1 && ob->x < player->x) || (ob->xdir == -1 && ob->x > player->x))\r
1909                 && ob->bottom == player->bottom && US_RndT() < 0x20)\r
1910         {\r
1911                 ob->state = &s_blooguardattack1;\r
1912         }\r
1913 }\r
1914 \r
1915 /*\r
1916 ===========================\r
1917 =\r
1918 = T_BlooguardAttack\r
1919 =\r
1920 ===========================\r
1921 */\r
1922 \r
1923 #pragma argsused\r
1924 void T_BlooguardAttack(objtype *ob)\r
1925 {\r
1926         SD_PlaySound(SND_SMASH);\r
1927         groundslam = 23;\r
1928         if (player->hitnorth)\r
1929         {\r
1930                 ChangeState(player, &s_keenstun);\r
1931         }\r
1932 }\r
1933 \r
1934 /*\r
1935 ===========================\r
1936 =\r
1937 = C_Blooguard\r
1938 =\r
1939 ===========================\r
1940 */\r
1941 \r
1942 void C_Blooguard(objtype *ob, objtype *hit)\r
1943 {\r
1944         if (hit->obclass == keenobj)\r
1945         {\r
1946                 KillKeen();\r
1947         }\r
1948         if (hit->obclass == stunshotobj)        // not 'else if' in the original code\r
1949         {\r
1950                 if (--ob->temp2 == 0)   // handle health\r
1951                 {\r
1952                         StunObj(ob, hit, &s_blooguardstun);\r
1953                 }\r
1954                 else\r
1955                 {\r
1956                         ob->temp1 = 2;  // draw white twice\r
1957                         ob->needtoreact = true;\r
1958                         ExplodeShot(hit);\r
1959                 }\r
1960         }\r
1961 }\r
1962 \r
1963 /*\r
1964 ===========================\r
1965 =\r
1966 = R_Blooguard\r
1967 =\r
1968 ===========================\r
1969 */\r
1970 \r
1971 void R_Blooguard(objtype *ob)\r
1972 {\r
1973         if (ob->xdir == 1 && ob->hitwest)\r
1974         {\r
1975                 ob->x -= ob->xmove;\r
1976                 ob->xdir = -1;\r
1977                 ob->nothink = US_RndT() >> 5;\r
1978                 ChangeState(ob, ob->state);\r
1979         }\r
1980         else if (ob->xdir == -1 && ob->hiteast)\r
1981         {\r
1982                 ob->x -= ob->xmove;\r
1983                 ob->xdir = 1;\r
1984                 ob->nothink = US_RndT() >> 5;\r
1985                 ChangeState(ob, ob->state);\r
1986         }\r
1987         else if (!ob->hitnorth)\r
1988         {\r
1989                 ob->x -= ob->xmove*2;\r
1990                 ob->xdir = -ob->xdir;\r
1991                 ob->nothink = US_RndT() >> 5;\r
1992                 ChangeState(ob, ob->state);\r
1993         }\r
1994         if (ob->temp1)\r
1995         {\r
1996                 ob->temp1--;\r
1997                 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, maskdraw, ob->priority);\r
1998         }\r
1999         else\r
2000         {\r
2001                 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
2002         }\r
2003 }\r
2004 \r
2005 /*\r
2006 =============================================================================\r
2007 \r
2008                                                   BLOOGLET\r
2009 \r
2010 temp1 = type\r
2011 \r
2012 =============================================================================\r
2013 */\r
2014 \r
2015 // red Blooglet:\r
2016 statetype s_rbloogletwalk1 = {RBLOOGLETWALKL1SPR, RBLOOGLETWALKR1SPR, step,  false, true,  5, 128, 0, T_BloogWalk, C_Blooglet, R_Walk, &s_rbloogletwalk2};\r
2017 statetype s_rbloogletwalk2 = {RBLOOGLETWALKL2SPR, RBLOOGLETWALKR2SPR, step,  false, true,  5, 128, 0, T_BloogWalk, C_Blooglet, R_Walk, &s_rbloogletwalk3};\r
2018 statetype s_rbloogletwalk3 = {RBLOOGLETWALKL3SPR, RBLOOGLETWALKR3SPR, step,  false, true,  5, 128, 0, T_BloogWalk, C_Blooglet, R_Walk, &s_rbloogletwalk4};\r
2019 statetype s_rbloogletwalk4 = {RBLOOGLETWALKL4SPR, RBLOOGLETWALKR4SPR, step,  false, true,  5, 128, 0, T_BloogWalk, C_Blooglet, R_Walk, &s_rbloogletwalk1};\r
2020 statetype s_rbloogletstun  = {RBLOOGLETSTUNSPR,   RBLOOGLETSTUNSPR,   think, false, false, 0,   0, 0, T_Projectile, NULL, R_Stunned, NULL};\r
2021 \r
2022 // yellow Blooglet:\r
2023 statetype s_ybloogletwalk1 = {YBLOOGLETWALKL1SPR, YBLOOGLETWALKR1SPR, step,  false, true,  5, 128, 0, T_BloogWalk, C_Blooglet, R_Walk, &s_ybloogletwalk2};\r
2024 statetype s_ybloogletwalk2 = {YBLOOGLETWALKL2SPR, YBLOOGLETWALKR2SPR, step,  false, true,  5, 128, 0, T_BloogWalk, C_Blooglet, R_Walk, &s_ybloogletwalk3};\r
2025 statetype s_ybloogletwalk3 = {YBLOOGLETWALKL3SPR, YBLOOGLETWALKR3SPR, step,  false, true,  5, 128, 0, T_BloogWalk, C_Blooglet, R_Walk, &s_ybloogletwalk4};\r
2026 statetype s_ybloogletwalk4 = {YBLOOGLETWALKL4SPR, YBLOOGLETWALKR4SPR, step,  false, true,  5, 128, 0, T_BloogWalk, C_Blooglet, R_Walk, &s_ybloogletwalk1};\r
2027 statetype s_ybloogletstun  = {YBLOOGLETSTUNSPR,   YBLOOGLETSTUNSPR,   think, false, false, 0,   0, 0, T_Projectile, NULL, R_Stunned, NULL};\r
2028 \r
2029 // blue Blooglet:\r
2030 statetype s_bbloogletwalk1 = {BBLOOGLETWALKL1SPR, BBLOOGLETWALKR1SPR, step,  false, true,  5, 128, 0, T_BloogWalk, C_Blooglet, R_Walk, &s_bbloogletwalk2};\r
2031 statetype s_bbloogletwalk2 = {BBLOOGLETWALKL2SPR, BBLOOGLETWALKR2SPR, step,  false, true,  5, 128, 0, T_BloogWalk, C_Blooglet, R_Walk, &s_bbloogletwalk3};\r
2032 statetype s_bbloogletwalk3 = {BBLOOGLETWALKL3SPR, BBLOOGLETWALKR3SPR, step,  false, true,  5, 128, 0, T_BloogWalk, C_Blooglet, R_Walk, &s_bbloogletwalk4};\r
2033 statetype s_bbloogletwalk4 = {BBLOOGLETWALKL4SPR, BBLOOGLETWALKR4SPR, step,  false, true,  5, 128, 0, T_BloogWalk, C_Blooglet, R_Walk, &s_bbloogletwalk1};\r
2034 statetype s_bbloogletstun  = {BBLOOGLETSTUNSPR,   BBLOOGLETSTUNSPR,   think, false, false, 0,   0, 0, T_Projectile, NULL, R_Stunned, NULL};\r
2035 \r
2036 // green Blooglet:\r
2037 statetype s_gbloogletwalk1 = {GBLOOGLETWALKL1SPR, GBLOOGLETWALKR1SPR, step,  false, true,  5, 128, 0, T_BloogWalk, C_Blooglet, R_Walk, &s_gbloogletwalk2};\r
2038 statetype s_gbloogletwalk2 = {GBLOOGLETWALKL2SPR, GBLOOGLETWALKR2SPR, step,  false, true,  5, 128, 0, T_BloogWalk, C_Blooglet, R_Walk, &s_gbloogletwalk3};\r
2039 statetype s_gbloogletwalk3 = {GBLOOGLETWALKL3SPR, GBLOOGLETWALKR3SPR, step,  false, true,  5, 128, 0, T_BloogWalk, C_Blooglet, R_Walk, &s_gbloogletwalk4};\r
2040 statetype s_gbloogletwalk4 = {GBLOOGLETWALKL4SPR, GBLOOGLETWALKR4SPR, step,  false, true,  5, 128, 0, T_BloogWalk, C_Blooglet, R_Walk, &s_gbloogletwalk1};\r
2041 statetype s_gbloogletstun  = {GBLOOGLETSTUNSPR,   GBLOOGLETSTUNSPR,   think, false, false, 0,   0, 0, T_Projectile, NULL, R_Stunned, NULL};\r
2042 \r
2043 /*\r
2044 ===========================\r
2045 =\r
2046 = SpawnBlooglet\r
2047 =\r
2048 ===========================\r
2049 */\r
2050 \r
2051 void SpawnBlooglet(Uint16 tileX, Uint16 tileY, Sint16 type)\r
2052 {\r
2053         GetNewObj(false);\r
2054         new->obclass = bloogletobj;\r
2055         new->active = ac_yes;\r
2056         new->priority = 0;\r
2057         new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
2058         new->y = CONVERT_TILE_TO_GLOBAL(tileY) + -8*PIXGLOBAL;\r
2059         if (US_RndT() < 0x80)\r
2060         {\r
2061                 new->xdir = 1;\r
2062         }\r
2063         else\r
2064         {\r
2065                 new->xdir = -1;\r
2066         }\r
2067         new->ydir = 1;\r
2068         new->temp1 = type;\r
2069 \r
2070         switch (type % 4)\r
2071         {\r
2072         case 0:\r
2073                 NewState(new, &s_rbloogletwalk1);\r
2074                 break;\r
2075 \r
2076         case 1:\r
2077                 NewState(new, &s_ybloogletwalk1);\r
2078                 break;\r
2079 \r
2080         case 2:\r
2081                 NewState(new, &s_bbloogletwalk1);\r
2082                 break;\r
2083 \r
2084         case 3:\r
2085                 NewState(new, &s_gbloogletwalk1);\r
2086         }\r
2087 }\r
2088 \r
2089 /*\r
2090 ===========================\r
2091 =\r
2092 = C_Blooglet\r
2093 =\r
2094 ===========================\r
2095 */\r
2096 \r
2097 void C_Blooglet(objtype *ob, objtype *hit)\r
2098 {\r
2099         static statetype *stunnedstate[4] = {\r
2100                 &s_rbloogletstun,\r
2101                 &s_ybloogletstun,\r
2102                 &s_bbloogletstun,\r
2103                 &s_gbloogletstun\r
2104         };\r
2105         Sint16 color;\r
2106 \r
2107         if (hit->obclass == keenobj && hit->state->contact)\r
2108         {\r
2109                 playerkludgeclipcancel = true;\r
2110                 ClipToSpriteSide(hit, ob);\r
2111                 playerkludgeclipcancel = false;\r
2112         }\r
2113         else if (hit->obclass == stunshotobj)\r
2114         {\r
2115                 color = ob->temp1 & 3;\r
2116                 if (ob->temp1 > 3)\r
2117                 {\r
2118                         //\r
2119                         // spawn a key gem\r
2120                         //\r
2121                         GetNewObj(false);\r
2122                         new->needtoclip = cl_midclip;\r
2123                         new->priority = 2;\r
2124                         new->obclass = bonusobj;\r
2125                         new->x = ob->x;\r
2126                         new->y = ob->y;\r
2127                         new->ydir = -1;\r
2128                         new->yspeed = -40;\r
2129                         new->temp1 = color;\r
2130                         new->temp2=new->shapenum = bonusshape[color];\r
2131                         new->temp3 = new->temp2 + 2;\r
2132                         NewState(new, &s_bonusfly1);\r
2133                         SD_PlaySound(SND_DROPKEY);\r
2134                 }\r
2135                 StunObj(ob, hit, stunnedstate[color]);\r
2136         }\r
2137 }