]> 4ch.mooo.com Git - 16.git/blob - 16/keen456/KEEN4-6/KEEN5/K5_ACT1.C
extrcted keen code remake
[16.git] / 16 / keen456 / KEEN4-6 / KEEN5 / K5_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 K5_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 - Teleport and Fuse effects\r
32 - Platforms\r
33 - falling platforms\r
34 - static platforms\r
35 - Goplat platforms\r
36 - Volte Face\r
37 - sneaky platforms\r
38 - Turrets\r
39 \r
40 */\r
41 \r
42 #include "CK_DEF.H"\r
43 \r
44 /*\r
45 =============================================================================\r
46 \r
47                                                   SHARED STUFF\r
48 \r
49 =============================================================================\r
50 */\r
51 \r
52 Sint16 pdirx[] = {0, 1, 0, -1, 1, 1, -1, -1};\r
53 Sint16 pdiry[] = {-1, 0, 1, 0, -1, 1, 1, -1};\r
54 \r
55 /*\r
56 ===========================\r
57 =\r
58 = CheckSpawnShot\r
59 =\r
60 ===========================\r
61 */\r
62 \r
63 Sint16 CheckSpawnShot(Uint16 x, Uint16 y, statetype *state)\r
64 {\r
65         if (GetNewObj(true) == -1)\r
66                 return -1;\r
67         new->x = x;\r
68         new->y = y;\r
69         new->obclass = mshotobj;\r
70         new->active = ac_removable;\r
71         NewState(new, state);\r
72         if (!CheckPosition(new))\r
73         {\r
74                 RemoveObj(new);\r
75                 return -1;\r
76         }\r
77         return 0;\r
78 }\r
79 \r
80 /*\r
81 ===========================\r
82 =\r
83 = C_ClipSide\r
84 =\r
85 ===========================\r
86 */\r
87 \r
88 void C_ClipSide(objtype *ob, objtype *hit)\r
89 {\r
90         if (hit->obclass == keenobj)\r
91         {\r
92                 playerkludgeclipcancel = true;\r
93                 ClipToSpriteSide(hit, ob);\r
94                 playerkludgeclipcancel = false;\r
95         }\r
96 }\r
97 \r
98 /*\r
99 ===========================\r
100 =\r
101 = C_ClipTop\r
102 =\r
103 ===========================\r
104 */\r
105 \r
106 void C_ClipTop(objtype *ob, objtype *hit)\r
107 {\r
108         if (hit->obclass == keenobj)\r
109                 ClipToSpriteTop(hit, ob);\r
110 }\r
111 \r
112 /*\r
113 ===========================\r
114 =\r
115 = R_Land\r
116 =\r
117 ===========================\r
118 */\r
119 \r
120 void R_Land(objtype *ob)\r
121 {\r
122         if (ob->hiteast || ob->hitwest)\r
123                 ob->xspeed = 0;\r
124 \r
125         if (ob->hitsouth)\r
126                 ob->yspeed = 0;\r
127 \r
128         if (ob->hitnorth)\r
129         {\r
130                 ob->yspeed = 0;\r
131                 if (ob->state->nextstate)\r
132                 {\r
133                         ChangeState(ob, ob->state->nextstate);\r
134                 }\r
135                 else\r
136                 {\r
137                         RemoveObj(ob);\r
138                         return;\r
139                 }\r
140         }\r
141 \r
142         RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
143 }\r
144 \r
145 /*\r
146 ===========================\r
147 =\r
148 = R_Bounce\r
149 =\r
150 ===========================\r
151 */\r
152 \r
153 void R_Bounce(objtype *ob)\r
154 {\r
155         Uint16 wall,absx,absy,angle,newangle;\r
156         Uint32 speed;\r
157 \r
158 \r
159         RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
160 \r
161         if (ob->hiteast || ob->hitwest)\r
162                 ob->xspeed = -ob->xspeed/2;\r
163 \r
164         if (ob->hitsouth)\r
165         {\r
166                 ob->yspeed = -ob->yspeed/2;\r
167                 return;\r
168         }\r
169 \r
170         wall = ob->hitnorth;\r
171         if (wall)\r
172         {\r
173                 if (ob->yspeed < 0)\r
174                         ob->yspeed = 0;\r
175 \r
176                 absx = abs(ob->xspeed);\r
177                 absy = ob->yspeed;\r
178                 if (absx>absy)\r
179                 {\r
180                         if (absx>absy*2)        // 22 degrees\r
181                         {\r
182                                 angle = 0;\r
183                                 speed = absx*286;       // x*sqrt(5)/2\r
184                         }\r
185                         else                            // 45 degrees\r
186                         {\r
187                                 angle = 1;\r
188                                 speed = absx*362;       // x*sqrt(2)\r
189                         }\r
190                 }\r
191                 else\r
192                 {\r
193                         if (absy>absx*2)        // 90 degrees\r
194                         {\r
195                                 angle = 3;\r
196                                 speed = absy*256;\r
197                         }\r
198                         else\r
199                         {\r
200                                 angle = 2;              // 67 degrees\r
201                                 speed = absy*286;       // y*sqrt(5)/2\r
202                         }\r
203                 }\r
204                 if (ob->xspeed > 0)\r
205                         angle = 7-angle;\r
206 \r
207                 speed >>= 1;\r
208                 newangle = bounceangle[ob->hitnorth][angle];\r
209                 switch (newangle)\r
210                 {\r
211                 case 0:\r
212                         ob->xspeed = speed / 286;\r
213                         ob->yspeed = -ob->xspeed / 2;\r
214                         break;\r
215                 case 1:\r
216                         ob->xspeed = speed / 362;\r
217                         ob->yspeed = -ob->xspeed;\r
218                         break;\r
219                 case 2:\r
220                         ob->yspeed = -(speed / 286);\r
221                         ob->xspeed = -ob->yspeed / 2;\r
222                         break;\r
223                 case 3:\r
224 \r
225                 case 4:\r
226                         ob->xspeed = 0;\r
227                         ob->yspeed = -(speed / 256);\r
228                         break;\r
229                 case 5:\r
230                         ob->yspeed = -(speed / 286);\r
231                         ob->xspeed = ob->yspeed / 2;\r
232                         break;\r
233                 case 6:\r
234                         ob->xspeed = ob->yspeed = -(speed / 362);\r
235                         break;\r
236                 case 7:\r
237                         ob->xspeed = -(speed / 286);\r
238                         ob->yspeed = ob->xspeed / 2;\r
239                         break;\r
240 \r
241                 case 8:\r
242                         ob->xspeed = -(speed / 286);\r
243                         ob->yspeed = -ob->xspeed / 2;\r
244                         break;\r
245                 case 9:\r
246                         ob->xspeed = -(speed / 362);\r
247                         ob->yspeed = -ob->xspeed;\r
248                         break;\r
249                 case 10:\r
250                         ob->yspeed = speed / 286;\r
251                         ob->xspeed = -ob->yspeed / 2;\r
252                         break;\r
253                 case 11:\r
254 \r
255                 case 12:\r
256                         ob->xspeed = 0;\r
257                         ob->yspeed = -(speed / 256);\r
258                         break;\r
259                 case 13:\r
260                         ob->yspeed = speed / 286;\r
261                         ob->xspeed = ob->yspeed / 2;\r
262                         break;\r
263                 case 14:\r
264                         ob->xspeed = speed / 362;\r
265                         ob->yspeed = speed / 362;\r
266                         break;\r
267                 case 15:\r
268                         ob->xspeed = speed / 286;\r
269                         ob->yspeed = ob->xspeed / 2;\r
270                         break;\r
271                 }\r
272 \r
273                 if (speed < 256*16)\r
274                 {\r
275                         ChangeState(ob, ob->state->nextstate);\r
276                 }\r
277         }\r
278 }\r
279 \r
280 /*\r
281 =============================================================================\r
282 \r
283                                                   BONUS ITEMS\r
284 temp1 = bonus type\r
285 temp2 = base shape number\r
286 temp3 = last animated shape number +1\r
287 \r
288 =============================================================================\r
289 */\r
290 \r
291 statetype s_bonus1    = {0,            0,            step,      false, false, 20, 0, 0, T_Bonus, NULL, R_Draw, &s_bonus2};\r
292 statetype s_bonus2    = {0,            0,            step,      false, false, 20, 0, 0, T_Bonus, NULL, R_Draw, &s_bonus1};\r
293 statetype s_bonusfly1 = {0,            0,            stepthink, false, false, 20, 0, 0, T_FlyBonus, NULL, R_Draw, &s_bonusfly2};\r
294 statetype s_bonusfly2 = {0,            0,            stepthink, false, false, 20, 0, 0, T_FlyBonus, NULL, R_Draw, &s_bonusfly1};\r
295 statetype s_bonusrise = {0,            0,            slide,     false, false, 40, 0, 8, NULL, NULL, R_Draw, NULL};\r
296 statetype s_splash1   = {VIVAPOOF1SPR, VIVAPOOF1SPR, step,      false, false,  8, 0, 0, NULL, NULL, R_Draw, &s_splash2};\r
297 statetype s_splash2   = {VIVAPOOF2SPR, VIVAPOOF2SPR, step,      false, false,  8, 0, 0, NULL, NULL, R_Draw, &s_splash3};\r
298 statetype s_splash3   = {VIVAPOOF3SPR, VIVAPOOF3SPR, step,      false, false,  8, 0, 0, NULL, NULL, R_Draw, &s_splash4};\r
299 statetype s_splash4   = {VIVAPOOF4SPR, VIVAPOOF4SPR, step,      false, false,  8, 0, 0, NULL, NULL, R_Draw, NULL};\r
300 \r
301 Uint16 bonusshape[] = {\r
302         REDGEM1SPR, YELLOWGEM1SPR, BLUEGEM1SPR, GREENGEM1SPR,\r
303         SUGAR1ASPR, SUGAR2ASPR, SUGAR3ASPR,\r
304         SUGAR4ASPR, SUGAR5ASPR, SUGAR6ASPR,\r
305         ONEUPASPR, STUNCLIP1SPR, DOORCARD1SPR\r
306 };\r
307 \r
308 /*\r
309 ===========================\r
310 =\r
311 = SpawnBonus\r
312 =\r
313 ===========================\r
314 */\r
315 \r
316 void SpawnBonus(Uint16 tileX, Uint16 tileY, Uint16 type)\r
317 {\r
318         GetNewObj(false);\r
319         new->needtoclip = cl_noclip;\r
320         new->priority = 2;\r
321         new->obclass = bonusobj;\r
322         new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
323         new->y = CONVERT_TILE_TO_GLOBAL(tileY);\r
324         new->ydir = -1;\r
325         new->temp1 = type;\r
326         new->temp2=new->shapenum = bonusshape[type];\r
327         new->temp3 = new->temp2+2;\r
328         NewState(new, &s_bonus1);\r
329 }\r
330 \r
331 /*\r
332 ===========================\r
333 =\r
334 = SpawnSplash\r
335 =\r
336 ===========================\r
337 */\r
338 \r
339 void SpawnSplash(Uint16 tileX, Uint16 tileY)\r
340 {\r
341         GetNewObj(true);\r
342         new->needtoclip = cl_noclip;\r
343         new->priority = 3;\r
344         new->obclass = inertobj;\r
345         new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
346         new->y = CONVERT_TILE_TO_GLOBAL(tileY);\r
347         NewState(new, &s_splash1);\r
348 }\r
349 \r
350 /*\r
351 ===========================\r
352 =\r
353 = T_Bonus\r
354 =\r
355 ===========================\r
356 */\r
357 \r
358 void T_Bonus(objtype *ob)\r
359 {\r
360         if (++ob->shapenum == ob->temp3)\r
361                 ob->shapenum = ob->temp2;\r
362 }\r
363 \r
364 /*\r
365 ===========================\r
366 =\r
367 = T_FlyBonus\r
368 =\r
369 ===========================\r
370 */\r
371 \r
372 void T_FlyBonus(objtype *ob)\r
373 {\r
374         if (ob->hitnorth)\r
375                 ob->state = &s_bonus1;\r
376 \r
377         if (++ob->shapenum == ob->temp3)\r
378                 ob->shapenum = ob->temp2;\r
379 \r
380         DoGravity(ob);\r
381 }\r
382 \r
383 /*\r
384 =============================================================================\r
385 \r
386                                                   TELEPORT EFFECTS\r
387 \r
388 =============================================================================\r
389 */\r
390 \r
391 statetype s_teleport1     = {TELEPORTSPARK1SPR, TELEPORTSPARK1SPR, step, false, false, 6, 0, 0, NULL, NULL, R_Draw, &s_teleport2};\r
392 statetype s_teleport2     = {TELEPORTSPARK2SPR, TELEPORTSPARK2SPR, step, false, false, 6, 0, 0, NULL, NULL, R_Draw, &s_teleport1};\r
393 statetype s_teleportzap1  = {TELEPORTZAP1SPR,   TELEPORTZAP1SPR,   step, false, false, 6, 0, 0, NULL, NULL, R_Draw, &s_teleportzap2};\r
394 statetype s_teleportzap2  = {TELEPORTZAP2SPR,   TELEPORTZAP2SPR,   step, false, false, 6, 0, 0, NULL, NULL, R_Draw, &s_teleportzap1};\r
395 \r
396 /*\r
397 ===========================\r
398 =\r
399 = SpawnTeleport\r
400 =\r
401 ===========================\r
402 */\r
403 \r
404 void SpawnTeleport(void)\r
405 {\r
406         GetNewObj(true);\r
407         new->priority = 3;\r
408         new->needtoclip = cl_noclip;\r
409         new->obclass = teleporterobj;\r
410         new->x = CONVERT_TILE_TO_GLOBAL(player->tileleft) - 8*PIXGLOBAL;\r
411         new->y = CONVERT_TILE_TO_GLOBAL(player->tilebottom) - 5*TILEGLOBAL;\r
412         NewState(new, &s_teleport1);\r
413 \r
414         GetNewObj(true);\r
415         new->priority = 3;\r
416         new->needtoclip = cl_noclip;\r
417         new->obclass = teleporterobj;\r
418         new->x = CONVERT_TILE_TO_GLOBAL(player->tileleft);\r
419         new->y = CONVERT_TILE_TO_GLOBAL(player->tiletop) - 8*PIXGLOBAL;\r
420         NewState(new, &s_teleportzap1);\r
421 \r
422         SD_PlaySound(SND_TELEPORT);\r
423 }\r
424 \r
425 /*\r
426 =============================================================================\r
427 \r
428                                                   FUSE FLASH\r
429 \r
430 =============================================================================\r
431 */\r
432 \r
433 statetype s_fuseflash1    = {FUSEFLASH1SPR, FUSEFLASH1SPR, step, false, false, 10, 0, 0, NULL, NULL, R_Draw, &s_fuseflash2};\r
434 statetype s_fuseflash2    = {FUSEFLASH2SPR, FUSEFLASH2SPR, step, false, false, 20, 0, 0, NULL, NULL, R_Draw, &s_fuseflash3};\r
435 statetype s_fuseflash3    = {FUSEFLASH3SPR, FUSEFLASH3SPR, step, false, false, 10, 0, 0, NULL, NULL, R_Draw, NULL};\r
436 \r
437 /*\r
438 ===========================\r
439 =\r
440 = SpawnFuseFlash\r
441 =\r
442 ===========================\r
443 */\r
444 \r
445 void SpawnFuseFlash(Uint16 tileX, Uint16 tileY)\r
446 {\r
447         GetNewObj(true);\r
448         new->priority = 3;\r
449         new->needtoclip = cl_noclip;\r
450         new->obclass = teleporterobj;\r
451         new->x = CONVERT_TILE_TO_GLOBAL(tileX-1);\r
452         new->y = CONVERT_TILE_TO_GLOBAL(tileY);\r
453         NewState(new, &s_fuseflash1);\r
454         SD_PlaySound(SND_BIGSPARK);\r
455 }\r
456 \r
457 /*\r
458 =============================================================================\r
459 \r
460                                                   DEAD MACHINE\r
461 \r
462 =============================================================================\r
463 */\r
464 \r
465 statetype s_deadmachine   = {-1, -1, step, false, false, 60, 0, 0, T_DeadMachine, NULL, R_Draw, NULL};\r
466 \r
467 /*\r
468 ===========================\r
469 =\r
470 = SpawnDeadMachine\r
471 =\r
472 ===========================\r
473 */\r
474 \r
475 void SpawnDeadMachine(void)\r
476 {\r
477         GetNewObj(false);\r
478         new->active = ac_allways;\r
479         new->needtoclip = cl_noclip;\r
480         NewState(new, &s_deadmachine);\r
481 }\r
482 \r
483 /*\r
484 ===========================\r
485 =\r
486 = T_DeadMachine\r
487 =\r
488 ===========================\r
489 */\r
490 \r
491 #pragma argsused\r
492 void T_DeadMachine(objtype *ob)\r
493 {\r
494         if (mapon == 12)\r
495         {\r
496                 playstate = ex_qedbroke;\r
497         }\r
498         else\r
499         {\r
500                 playstate = ex_fusebroke;\r
501         }\r
502 }\r
503 \r
504 /*\r
505 =============================================================================\r
506 \r
507                                                   PLATFORMS\r
508 \r
509 =============================================================================\r
510 */\r
511 \r
512 statetype s_platform      = {PLATFORMSPR,  PLATFORMSPR,  think,     false, false, 0, 0, 0, T_Platform, NULL, R_Draw, NULL};\r
513 statetype s_slotplat1     = {SLOTPLAT1SPR, SLOTPLAT1SPR, stepthink, false, false, 0, 0, 0, T_Slotplat, NULL, R_Draw, &s_slotplat2};\r
514 statetype s_slotplat2     = {SLOTPLAT2SPR, SLOTPLAT2SPR, stepthink, false, false, 0, 0, 0, T_Slotplat, NULL, R_Draw, &s_slotplat1};\r
515 // BUG? the slotplat states have a tictime of 0, so they never transition to the next state\r
516 \r
517 /*\r
518 ===========================\r
519 =\r
520 = SpawnPlatform\r
521 =\r
522 ===========================\r
523 */\r
524 \r
525 void SpawnPlatform(Uint16 tileX, Uint16 tileY, Sint16 dir, Sint16 type)\r
526 {\r
527         GetNewObj(false);\r
528         new->obclass = platformobj;\r
529         new->active = ac_allways;\r
530         new->priority = 0;\r
531         new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
532         new->y = CONVERT_TILE_TO_GLOBAL(tileY);\r
533         switch (dir)\r
534         {\r
535         case 0:\r
536                 new->xdir = 0;\r
537                 new->ydir = -1;\r
538                 break;\r
539         case 1:\r
540                 new->xdir = 1;\r
541                 new->ydir = 0;\r
542                 break;\r
543         case 2:\r
544                 new->xdir = 0;\r
545                 new->ydir = 1;\r
546                 break;\r
547         case 3:\r
548                 new->xdir = -1;\r
549                 new->ydir = 0;\r
550         }\r
551         if (type)\r
552         {\r
553                 new->x += 4*PIXGLOBAL;\r
554                 new->y += 4*PIXGLOBAL;\r
555                 NewState(new, &s_slotplat1);\r
556         }\r
557         else\r
558         {\r
559                 NewState(new, &s_platform);\r
560         }\r
561 }\r
562 \r
563 /*\r
564 ===========================\r
565 =\r
566 = T_Platform\r
567 =\r
568 ===========================\r
569 */\r
570 \r
571 void T_Platform(objtype *ob)\r
572 {\r
573         Uint16 newpos, newtile;\r
574 \r
575         //\r
576         // this code could be executed twice during the same frame because the\r
577         // object's animation/state changed during that frame, so don't update\r
578         // anything if we already have movement for the current frame i.e. the\r
579         // update code has already been executed this frame\r
580         //\r
581         if (!xtry && !ytry)\r
582         {\r
583                 xtry = ob->xdir * 12 * tics;\r
584                 ytry = ob->ydir * 12 * tics;\r
585 \r
586                 if (ob->xdir == 1)\r
587                 {\r
588                         newpos = ob->right + xtry;\r
589                         newtile = CONVERT_GLOBAL_TO_TILE(newpos);\r
590                         if (ob->tileright != newtile)\r
591                         {\r
592                                 if (*(mapsegs[2]+mapbwidthtable[ob->tiletop]/2 + newtile) == PLATFORMBLOCK)\r
593                                 {\r
594                                         ob->xdir = -1;\r
595                                         xtry = xtry - (newpos & 0xFF);\r
596                                 }\r
597                         }\r
598                 }\r
599                 else if (ob->xdir == -1)\r
600                 {\r
601                         newpos = ob->left + xtry;\r
602                         newtile = CONVERT_GLOBAL_TO_TILE(newpos);\r
603                         if (ob->tileleft != newtile)\r
604                         {\r
605                                 if (*(mapsegs[2]+mapbwidthtable[ob->tiletop]/2 + newtile) == PLATFORMBLOCK)\r
606                                 {\r
607                                         ob->xdir = 1;\r
608                                         xtry = xtry + (TILEGLOBAL - (newpos & 0xFF));\r
609                                 }\r
610                         }\r
611                 }\r
612                 else if (ob->ydir == 1)\r
613                 {\r
614                         newpos = ob->bottom + ytry;\r
615                         newtile = CONVERT_GLOBAL_TO_TILE(newpos);\r
616                         if (ob->tilebottom != newtile)\r
617                         {\r
618                                 if (*(mapsegs[2]+mapbwidthtable[newtile]/2 + ob->tileleft) == PLATFORMBLOCK)\r
619                                 {\r
620                                         if (*(mapsegs[2]+mapbwidthtable[newtile-2]/2 + ob->tileleft) == PLATFORMBLOCK)\r
621                                         {\r
622                                                 ytry = 0;\r
623                                                 ob->needtoreact = true;\r
624                                         }\r
625                                         else\r
626                                         {\r
627                                                 ob->ydir = -1;\r
628                                                 ytry = ytry - (newpos & 0xFF);\r
629                                         }\r
630                                 }\r
631                         }\r
632                 }\r
633                 else if (ob->ydir == -1)\r
634                 {\r
635                         newpos = ob->top + ytry;\r
636                         newtile = CONVERT_GLOBAL_TO_TILE(newpos);\r
637                         if (ob->tiletop != newtile)\r
638                         {\r
639                                 if (*(mapsegs[2]+mapbwidthtable[newtile]/2 + ob->tileleft) == PLATFORMBLOCK)\r
640                                 {\r
641                                         if (*(mapsegs[2]+mapbwidthtable[newtile+2]/2 + ob->tileleft) == PLATFORMBLOCK)\r
642                                         {\r
643                                                 ytry = 0;\r
644                                                 ob->needtoreact = true;\r
645                                         }\r
646                                         else\r
647                                         {\r
648                                                 ob->ydir = 1;\r
649                                                 ytry = ytry + (TILEGLOBAL - (newpos & 0xFF));\r
650                                         }\r
651                                 }\r
652                         }\r
653                 }\r
654         }\r
655 }\r
656 \r
657 /*\r
658 ===========================\r
659 =\r
660 = T_Slotplat\r
661 =\r
662 ===========================\r
663 */\r
664 \r
665 void T_Slotplat(objtype *ob)\r
666 {\r
667         Uint16 newpos, newtile;\r
668 \r
669         //\r
670         // this code could be executed twice during the same frame because the\r
671         // object's animation/state changed during that frame, so don't update\r
672         // anything if we already have movement for the current frame i.e. the\r
673         // update code has already been executed this frame\r
674         //\r
675         if (!xtry && !ytry)\r
676         {\r
677                 xtry = ob->xdir * 12 * tics;\r
678                 ytry = ob->ydir * 12 * tics;\r
679 \r
680                 if (ob->xdir == 1)\r
681                 {\r
682                         newpos = ob->right + xtry;\r
683                         newtile = CONVERT_GLOBAL_TO_TILE(newpos);\r
684                         if (ob->tileright != newtile)\r
685                         {\r
686                                 if (*(mapsegs[2]+mapbwidthtable[ob->tiletop]/2 + newtile) == PLATFORMBLOCK)\r
687                                 {\r
688                                         ob->xdir = -1;\r
689                                         xtry = xtry - (newpos & 0xFF);\r
690                                 }\r
691                         }\r
692                 }\r
693                 else if (ob->xdir == -1)\r
694                 {\r
695                         newpos = ob->left + xtry;\r
696                         newtile = CONVERT_GLOBAL_TO_TILE(newpos);\r
697                         if (ob->tileleft != newtile)\r
698                         {\r
699                                 if (*(mapsegs[2]+mapbwidthtable[ob->tiletop]/2 + newtile) == PLATFORMBLOCK)\r
700                                 {\r
701                                         ob->xdir = 1;\r
702                                         xtry = xtry + (TILEGLOBAL - (newpos & 0xFF));\r
703                                 }\r
704                         }\r
705                 }\r
706                 else if (ob->ydir == 1)\r
707                 {\r
708                         newpos = ob->bottom + ytry;\r
709                         newtile = CONVERT_GLOBAL_TO_TILE(newpos);\r
710                         if (ob->tilebottom != newtile)\r
711                         {\r
712                                 if (*(mapsegs[2]+mapbwidthtable[newtile]/2 + ob->tileleft + 1) == PLATFORMBLOCK)\r
713                                 {\r
714                                         if (*(mapsegs[2]+mapbwidthtable[newtile-2]/2 + ob->tileleft) == PLATFORMBLOCK)  // BUG? '+ 1' is missing after 'tileleft'\r
715                                         {\r
716                                                 ytry = 0;\r
717                                                 ob->needtoreact = true;\r
718                                         }\r
719                                         else\r
720                                         {\r
721                                                 ob->ydir = -1;\r
722                                                 ytry = ytry - (newpos & 0xFF);\r
723                                         }\r
724                                 }\r
725                         }\r
726                 }\r
727                 else if (ob->ydir == -1)\r
728                 {\r
729                         newpos = ob->top + ytry;\r
730                         newtile = CONVERT_GLOBAL_TO_TILE(newpos);\r
731                         if (ob->tiletop != newtile)\r
732                         {\r
733                                 if (*(mapsegs[2]+mapbwidthtable[newtile]/2 + ob->tileleft + 1) == PLATFORMBLOCK)\r
734                                 {\r
735                                         if (*(mapsegs[2]+mapbwidthtable[newtile+2]/2 + ob->tileleft + 1) == PLATFORMBLOCK)\r
736                                         {\r
737                                                 ytry = 0;\r
738                                                 ob->needtoreact = true;\r
739                                         }\r
740                                         else\r
741                                         {\r
742                                                 ob->ydir = 1;\r
743                                                 ytry = ytry + (TILEGLOBAL - (newpos & 0xFF));\r
744                                         }\r
745                                 }\r
746                         }\r
747                 }\r
748         }\r
749 }\r
750 \r
751 /*\r
752 =============================================================================\r
753 \r
754                                                   DROPPING PLATFORM\r
755 \r
756 temp1 = initial y position\r
757 \r
758 =============================================================================\r
759 */\r
760 \r
761 statetype s_dropplatsit  = {PLATFORMSPR, PLATFORMSPR, think,      false, false, 0, 0,   0, T_DropPlatSit, NULL, R_Draw, NULL};\r
762 statetype s_dropplatfall = {PLATFORMSPR, PLATFORMSPR, think,      false, false, 0, 0,   0, T_DropPlatFall, NULL, R_Draw, NULL};\r
763 statetype s_dropplatrise = {PLATFORMSPR, PLATFORMSPR, slidethink, false, false, 0, 0, -32, T_DropPlatRise, NULL, R_Draw, NULL};\r
764 \r
765 /*\r
766 ===========================\r
767 =\r
768 = SpawnDropPlat\r
769 =\r
770 ===========================\r
771 */\r
772 \r
773 void SpawnDropPlat(Uint16 tileX, Uint16 tileY)\r
774 {\r
775         GetNewObj(false);\r
776         new->obclass = platformobj;\r
777         new->active = ac_allways;\r
778         new->priority = 0;\r
779         new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
780         new->y=new->temp1 = CONVERT_TILE_TO_GLOBAL(tileY);\r
781         new->xdir = 0;\r
782         new->ydir = 1;\r
783         new->needtoclip = cl_noclip;\r
784         NewState(new, &s_dropplatsit);\r
785 }\r
786 \r
787 /*\r
788 ===========================\r
789 =\r
790 = T_DropPlatSit\r
791 =\r
792 ===========================\r
793 */\r
794 \r
795 void T_DropPlatSit(objtype *ob)\r
796 {\r
797         if (gamestate.riding == ob)\r
798         {\r
799                 ytry = tics << 4;       //tics * 16;\r
800                 ob->yspeed = 0;\r
801                 if (ob->y + ytry - ob->temp1 >= 8*PIXGLOBAL)\r
802                         ob->state = &s_dropplatfall;\r
803         }\r
804 }\r
805 \r
806 /*\r
807 ===========================\r
808 =\r
809 = T_DropPlatFall\r
810 =\r
811 ===========================\r
812 */\r
813 \r
814 void T_DropPlatFall(objtype *ob)\r
815 {\r
816         Uint16 newpos, newtile;\r
817 \r
818         DoGravity(ob);\r
819 \r
820 #if 0\r
821         // bugfix: don't skip a tile (this is present in Keen 4, but missing in 5 & 6)\r
822         if (ytry >= 15*PIXGLOBAL)\r
823                 ytry = 15*PIXGLOBAL;\r
824 #endif\r
825 \r
826         newpos = ob->bottom + ytry;\r
827         newtile = CONVERT_GLOBAL_TO_TILE(newpos);\r
828         if (ob->tilebottom != newtile)\r
829         {\r
830                 if (*(mapsegs[2]+mapbwidthtable[newtile]/2 + ob->tileleft) == PLATFORMBLOCK)\r
831                 {\r
832                         ytry = 0xFF - (ob->bottom & 0xFF);\r
833                         if (gamestate.riding != ob)\r
834                                 ob->state = &s_dropplatrise;\r
835                 }\r
836         }\r
837 }\r
838 \r
839 /*\r
840 ===========================\r
841 =\r
842 = T_DropPlatRise\r
843 =\r
844 ===========================\r
845 */\r
846 \r
847 void T_DropPlatRise(objtype *ob)\r
848 {\r
849         if (gamestate.riding == ob)\r
850         {\r
851                 ob->yspeed = 0;\r
852                 ob->state = &s_dropplatfall;\r
853         }\r
854         else if (ob->y <= ob->temp1)\r
855         {\r
856                 ytry = ob->temp1 - ob->y;\r
857                 ob->state = &s_dropplatsit;\r
858         }\r
859 }\r
860 \r
861 /*\r
862 =============================================================================\r
863 \r
864                                                   STATIC PLATFORM\r
865 \r
866 temp1 = initial y position (is set but never used)\r
867 \r
868 =============================================================================\r
869 */\r
870 \r
871 statetype s_statplat    = {PLATFORMSPR, PLATFORMSPR, step, false, false, 32000, 0, 0, NULL, NULL, R_Draw, &s_statplat};\r
872 \r
873 /*\r
874 ===========================\r
875 =\r
876 = SpawnStaticPlat\r
877 =\r
878 ===========================\r
879 */\r
880 \r
881 void SpawnStaticPlat(Uint16 tileX, Uint16 tileY)\r
882 {\r
883         GetNewObj(false);\r
884         new->obclass = platformobj;\r
885         new->active = ac_yes;\r
886         new->priority = 0;\r
887         new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
888         new->y=new->temp1 = CONVERT_TILE_TO_GLOBAL(tileY);\r
889         new->xdir = 0;\r
890         new->ydir = 1;\r
891         new->needtoclip = cl_noclip;\r
892         NewState(new, &s_statplat);\r
893 }\r
894 \r
895 /*\r
896 =============================================================================\r
897 \r
898                                                   GO PLATFORMS\r
899 \r
900 temp1 = direction\r
901 temp2 = countdown to next dir check\r
902 \r
903 =============================================================================\r
904 */\r
905 \r
906 statetype s_goplat        = {PLATFORMSPR,  PLATFORMSPR,  think,     false, false, 0, 0, 0, T_GoPlat, NULL, R_Draw, NULL};\r
907 statetype s_slotgoplat1   = {SLOTPLAT1SPR, SLOTPLAT1SPR, stepthink, false, false, 0, 0, 0, T_GoSlotPlat, NULL, R_Draw, &s_slotgoplat2};\r
908 statetype s_slotgoplat2   = {SLOTPLAT2SPR, SLOTPLAT2SPR, stepthink, false, false, 0, 0, 0, T_GoSlotPlat, NULL, R_Draw, &s_slotgoplat1};\r
909 // BUG? the slotgoplat states have a tictime of 0, so they never transition to the next state\r
910 \r
911 /*\r
912 ===========================\r
913 =\r
914 = SpawnGoPlat\r
915 =\r
916 ===========================\r
917 */\r
918 \r
919 void SpawnGoPlat(Uint16 tileX, Uint16 tileY, Sint16 dir, Sint16 type)\r
920 {\r
921         GetNewObj(false);\r
922         new->obclass = platformobj;\r
923         new->active = ac_allways;\r
924         new->priority = 0;\r
925         new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
926         new->y = CONVERT_TILE_TO_GLOBAL(tileY);\r
927         new->xdir = 0;\r
928         new->ydir = 1;\r
929         new->needtoclip = cl_noclip;\r
930         if (type)\r
931         {\r
932                 new->x += 4*PIXGLOBAL;\r
933                 new->y += 4*PIXGLOBAL;\r
934                 NewState(new, &s_slotgoplat1);\r
935         }\r
936         else\r
937         {\r
938                 NewState(new, &s_goplat);\r
939         }\r
940         *(mapsegs[2]+mapbwidthtable[tileY]/2 + tileX) = DIRARROWSTART + dir;\r
941         new->temp1 = dir;\r
942         new->temp2 = TILEGLOBAL;\r
943 }\r
944 \r
945 /*\r
946 ===========================\r
947 =\r
948 = T_GoPlat\r
949 =\r
950 ===========================\r
951 */\r
952 \r
953 void T_GoPlat(objtype *ob)\r
954 {\r
955         Uint16 move;\r
956         Uint16 tx, ty;\r
957         Sint16 dir;\r
958 \r
959         //\r
960         // this code could be executed twice during the same frame because the\r
961         // object's animation/state changed during that frame, so don't update\r
962         // anything if we already have movement for the current frame i.e. the\r
963         // update code has already been executed this frame\r
964         //\r
965         if (!xtry && !ytry)\r
966         {\r
967                 move = tics * 12;\r
968                 if (ob->temp2 > move)\r
969                 {\r
970                         ob->temp2 = ob->temp2 - move;\r
971 \r
972                         dir = pdirx[ob->temp1];\r
973                         if (dir == 1)\r
974                         {\r
975                                 xtry = xtry + move;\r
976                         }\r
977                         else if (dir == -1)\r
978                         {\r
979                                 xtry = xtry + -move;\r
980                         }\r
981 \r
982                         dir = pdiry[ob->temp1];\r
983                         if (dir == 1)\r
984                         {\r
985                                 ytry = ytry + move;\r
986                         }\r
987                         else if (dir == -1)\r
988                         {\r
989                                 ytry = ytry + -move;\r
990                         }\r
991                 }\r
992                 else\r
993                 {\r
994                         dir = pdirx[ob->temp1];\r
995                         if (dir == 1)\r
996                         {\r
997                                 xtry += ob->temp2;\r
998                         }\r
999                         else if (dir == -1)\r
1000                         {\r
1001                                 xtry += -ob->temp2;\r
1002                         }\r
1003 \r
1004                         dir = pdiry[ob->temp1];\r
1005                         if (dir == 1)\r
1006                         {\r
1007                                 ytry += ob->temp2;\r
1008                         }\r
1009                         else if (dir == -1)\r
1010                         {\r
1011                                 ytry += -ob->temp2;\r
1012                         }\r
1013 \r
1014                         tx = CONVERT_GLOBAL_TO_TILE(ob->x + xtry);\r
1015                         ty = CONVERT_GLOBAL_TO_TILE(ob->y + ytry);\r
1016                         ob->temp1 = *(mapsegs[2]+mapbwidthtable[ty]/2 + tx) - DIRARROWSTART;\r
1017                         if (ob->temp1 < arrow_North || ob->temp1 > arrow_None)\r
1018                         {\r
1019                                 char error[60] = "Goplat moved to a bad spot: ";\r
1020                                 char buf[5] = "";\r
1021 \r
1022                                 strcat(error, itoa(ob->x, buf, 16));\r
1023                                 strcat(error, ",");\r
1024                                 strcat(error, itoa(ob->y, buf, 16));\r
1025                                 Quit(error);\r
1026                         }\r
1027 \r
1028                         move -= ob->temp2;\r
1029                         ob->temp2 = TILEGLOBAL - move;\r
1030 \r
1031                         dir = pdirx[ob->temp1];\r
1032                         if (dir == 1)\r
1033                         {\r
1034                                 xtry = xtry + move;\r
1035                         }\r
1036                         else if (dir == -1)\r
1037                         {\r
1038                                 xtry = xtry - move;\r
1039                         }\r
1040 \r
1041                         dir = pdiry[ob->temp1];\r
1042                         if (dir == 1)\r
1043                         {\r
1044                                 ytry = ytry + move;\r
1045                         }\r
1046                         else if (dir == -1)\r
1047                         {\r
1048                                 ytry = ytry - move;\r
1049                         }\r
1050                 }\r
1051         }\r
1052 }\r
1053 \r
1054 /*\r
1055 ===========================\r
1056 =\r
1057 = T_GoSlotPlat\r
1058 =\r
1059 ===========================\r
1060 */\r
1061 \r
1062 void T_GoSlotPlat(objtype *ob)\r
1063 {\r
1064         Uint16 move;\r
1065         Uint16 tx, ty;\r
1066         Sint16 dir;\r
1067 \r
1068         //\r
1069         // this code could be executed twice during the same frame because the\r
1070         // object's animation/state changed during that frame, so don't update\r
1071         // anything if we already have movement for the current frame i.e. the\r
1072         // update code has already been executed this frame\r
1073         //\r
1074         if (!xtry && !ytry)\r
1075         {\r
1076                 move = tics * 12;\r
1077                 if (ob->temp2 > move)\r
1078                 {\r
1079                         ob->temp2 = ob->temp2 - move;\r
1080 \r
1081                         dir = pdirx[ob->temp1];\r
1082                         if (dir == 1)\r
1083                         {\r
1084                                 xtry = xtry + move;\r
1085                         }\r
1086                         else if (dir == -1)\r
1087                         {\r
1088                                 xtry = xtry + -move;\r
1089                         }\r
1090 \r
1091                         dir = pdiry[ob->temp1];\r
1092                         if (dir == 1)\r
1093                         {\r
1094                                 ytry = ytry + move;\r
1095                         }\r
1096                         else if (dir == -1)\r
1097                         {\r
1098                                 ytry = ytry + -move;\r
1099                         }\r
1100                 }\r
1101                 else\r
1102                 {\r
1103                         dir = pdirx[ob->temp1];\r
1104                         if (dir == 1)\r
1105                         {\r
1106                                 xtry += ob->temp2;\r
1107                         }\r
1108                         else if (dir == -1)\r
1109                         {\r
1110                                 xtry += -ob->temp2;\r
1111                         }\r
1112 \r
1113                         dir = pdiry[ob->temp1];\r
1114                         if (dir == 1)\r
1115                         {\r
1116                                 ytry += ob->temp2;\r
1117                         }\r
1118                         else if (dir == -1)\r
1119                         {\r
1120                                 ytry += -ob->temp2;\r
1121                         }\r
1122 \r
1123                         tx = CONVERT_GLOBAL_TO_TILE(ob->x + xtry + 4*PIXGLOBAL);\r
1124                         ty = CONVERT_GLOBAL_TO_TILE(ob->y + ytry + 4*PIXGLOBAL);\r
1125                         ob->temp1 = *(mapsegs[2]+mapbwidthtable[ty]/2 + tx) - DIRARROWSTART;\r
1126                         if (ob->temp1 < arrow_North || ob->temp1 > arrow_None)\r
1127                         {\r
1128                                 Quit("Goplat moved to a bad spot!");\r
1129                         }\r
1130 \r
1131                         move -= ob->temp2;\r
1132                         ob->temp2 = TILEGLOBAL - move;\r
1133 \r
1134                         dir = pdirx[ob->temp1];\r
1135                         if (dir == 1)\r
1136                         {\r
1137                                 xtry = xtry + move;\r
1138                         }\r
1139                         else if (dir == -1)\r
1140                         {\r
1141                                 xtry = xtry - move;\r
1142                         }\r
1143 \r
1144                         dir = pdiry[ob->temp1];\r
1145                         if (dir == 1)\r
1146                         {\r
1147                                 ytry = ytry + move;\r
1148                         }\r
1149                         else if (dir == -1)\r
1150                         {\r
1151                                 ytry = ytry - move;\r
1152                         }\r
1153                 }\r
1154         }\r
1155 }\r
1156 \r
1157 /*\r
1158 =============================================================================\r
1159 \r
1160                                                   VOLTEFACE\r
1161 \r
1162 temp1 = direction\r
1163 temp2 = countdown to next dir check\r
1164 \r
1165 =============================================================================\r
1166 */\r
1167 \r
1168 statetype s_volte1     = {VOLTEFACE1SPR,    VOLTEFACE1SPR,    stepthink, false, false,   6, 0, 0, T_Volte, C_Volte, R_Draw, &s_volte2};\r
1169 statetype s_volte2     = {VOLTEFACE2SPR,    VOLTEFACE2SPR,    stepthink, false, false,   6, 0, 0, T_Volte, C_Volte, R_Draw, &s_volte3};\r
1170 statetype s_volte3     = {VOLTEFACE3SPR,    VOLTEFACE3SPR,    stepthink, false, false,   6, 0, 0, T_Volte, C_Volte, R_Draw, &s_volte4};\r
1171 statetype s_volte4     = {VOLTEFACE4SPR,    VOLTEFACE4SPR,    stepthink, false, false,   6, 0, 0, T_Volte, C_Volte, R_Draw, &s_volte1};\r
1172 statetype s_voltestun  = {VOLTEFACESTUNSPR, VOLTEFACESTUNSPR, step,      false, false, 300, 0, 0, NULL, NULL, R_Draw, &s_volte1};\r
1173 \r
1174 /*\r
1175 ===========================\r
1176 =\r
1177 = SpawnVolte\r
1178 =\r
1179 ===========================\r
1180 */\r
1181 \r
1182 void SpawnVolte(Uint16 tileX, Uint16 tileY)\r
1183 {\r
1184         Uint16 dir;\r
1185         Uint16 far *map;\r
1186 \r
1187         GetNewObj(false);\r
1188         new->obclass = volteobj;\r
1189         new->active = ac_allways;\r
1190         new->priority = 2;\r
1191         new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
1192         new->y = CONVERT_TILE_TO_GLOBAL(tileY);\r
1193         new->needtoclip = cl_noclip;\r
1194         NewState(new, &s_volte1);\r
1195         map = mapsegs[2] + mapbwidthtable[tileY]/2 + tileX;\r
1196         if (map[-1] == DIRARROWSTART + arrow_East)\r
1197         {\r
1198                 dir = arrow_East;\r
1199         }\r
1200         else if (map[1] == DIRARROWSTART + arrow_West)\r
1201         {\r
1202                 dir = arrow_West;\r
1203         }\r
1204         else if (*(map-mapwidth) == DIRARROWSTART + arrow_South)\r
1205         {\r
1206                 dir = arrow_South;\r
1207         }\r
1208         else if (*(map+mapwidth) == DIRARROWSTART + arrow_North)\r
1209         {\r
1210                 dir = arrow_North;\r
1211         }\r
1212         map[0] = dir + DIRARROWSTART;\r
1213         new->temp1 = dir;\r
1214         new->temp2 = TILEGLOBAL;\r
1215 }\r
1216 \r
1217 /*\r
1218 ===========================\r
1219 =\r
1220 = T_Volte\r
1221 =\r
1222 ===========================\r
1223 */\r
1224 \r
1225 void T_Volte(objtype *ob)\r
1226 {\r
1227         Uint16 move;\r
1228         Uint16 tx, ty;\r
1229         Sint16 dir;\r
1230 \r
1231         //\r
1232         // this code could be executed twice during the same frame because the\r
1233         // object's animation/state changed during that frame, so don't update\r
1234         // anything if we already have movement for the current frame i.e. the\r
1235         // update code has already been executed this frame\r
1236         //\r
1237         if (!xtry && !ytry)\r
1238         {\r
1239                 move = tics << 5;\r
1240                 if (ob->temp2 > move)\r
1241                 {\r
1242                         ob->temp2 = ob->temp2 - move;\r
1243 \r
1244                         dir = pdirx[ob->temp1];\r
1245                         if (dir == 1)\r
1246                         {\r
1247                                 xtry = xtry + move;\r
1248                         }\r
1249                         else if (dir == -1)\r
1250                         {\r
1251                                 xtry = xtry + -move;\r
1252                         }\r
1253 \r
1254                         dir = pdiry[ob->temp1];\r
1255                         if (dir == 1)\r
1256                         {\r
1257                                 ytry = ytry + move;\r
1258                         }\r
1259                         else if (dir == -1)\r
1260                         {\r
1261                                 ytry = ytry + -move;\r
1262                         }\r
1263                 }\r
1264                 else\r
1265                 {\r
1266                         dir = pdirx[ob->temp1];\r
1267                         if (dir == 1)\r
1268                         {\r
1269                                 xtry += ob->temp2;\r
1270                         }\r
1271                         else if (dir == -1)\r
1272                         {\r
1273                                 xtry += -ob->temp2;\r
1274                         }\r
1275 \r
1276                         dir = pdiry[ob->temp1];\r
1277                         if (dir == 1)\r
1278                         {\r
1279                                 ytry += ob->temp2;\r
1280                         }\r
1281                         else if (dir == -1)\r
1282                         {\r
1283                                 ytry += -ob->temp2;\r
1284                         }\r
1285 \r
1286                         tx = CONVERT_GLOBAL_TO_TILE(ob->x + xtry);\r
1287                         ty = CONVERT_GLOBAL_TO_TILE(ob->y + ytry);\r
1288                         ob->temp1 = *(mapsegs[2]+mapbwidthtable[ty]/2 + tx) - DIRARROWSTART;\r
1289                         if (ob->temp1 < arrow_North || ob->temp1 > arrow_None)\r
1290                         {\r
1291                                 char error[60] = "Volte moved to a bad spot: ";\r
1292                                 char buf[5] = "";\r
1293 \r
1294                                 strcat(error, itoa(ob->x, buf, 16));\r
1295                                 strcat(error, ",");\r
1296                                 strcat(error, itoa(ob->y, buf, 16));\r
1297                                 Quit(error);\r
1298                         }\r
1299 \r
1300                         move -= ob->temp2;\r
1301                         ob->temp2 = TILEGLOBAL - move;\r
1302 \r
1303                         dir = pdirx[ob->temp1];\r
1304                         if (dir == 1)\r
1305                         {\r
1306                                 xtry = xtry + move;\r
1307                         }\r
1308                         else if (dir == -1)\r
1309                         {\r
1310                                 xtry = xtry - move;\r
1311                         }\r
1312 \r
1313                         dir = pdiry[ob->temp1];\r
1314                         if (dir == 1)\r
1315                         {\r
1316                                 ytry = ytry + move;\r
1317                         }\r
1318                         else if (dir == -1)\r
1319                         {\r
1320                                 ytry = ytry - move;\r
1321                         }\r
1322                 }\r
1323         }\r
1324 }\r
1325 \r
1326 /*\r
1327 ===========================\r
1328 =\r
1329 = C_Volte\r
1330 =\r
1331 ===========================\r
1332 */\r
1333 \r
1334 void C_Volte(objtype *ob, objtype *hit)\r
1335 {\r
1336         if (hit->obclass == keenobj)\r
1337         {\r
1338                 KillKeen();\r
1339         }\r
1340         else if (hit->obclass == stunshotobj)\r
1341         {\r
1342                 ExplodeShot(hit);\r
1343                 ChangeState(ob, &s_voltestun);\r
1344         }\r
1345 }\r
1346 \r
1347 /*\r
1348 =============================================================================\r
1349 \r
1350                                                   SNEAKY PLATFORM\r
1351 \r
1352 temp1 = initial x position (is set but never used)\r
1353 \r
1354 =============================================================================\r
1355 */\r
1356 \r
1357 statetype s_sneakplatsit    = {PLATFORMSPR, PLATFORMSPR, think, false, false,  0,   0, 0, T_SneakPlat, NULL, R_Draw, NULL};\r
1358 statetype s_sneakplatdodge  = {PLATFORMSPR, PLATFORMSPR, slide, false, false, 48,  32, 0, NULL, NULL, R_Draw, &s_sneakplatreturn};\r
1359 statetype s_sneakplatreturn = {PLATFORMSPR, PLATFORMSPR, slide, false, false, 96, -16, 0, NULL, NULL, R_Draw, &s_sneakplatsit};\r
1360 \r
1361 /*\r
1362 ===========================\r
1363 =\r
1364 = SpawnSneakPlat\r
1365 =\r
1366 ===========================\r
1367 */\r
1368 \r
1369 void SpawnSneakPlat(Uint16 tileX, Uint16 tileY)\r
1370 {\r
1371         GetNewObj(false);\r
1372         new->obclass = platformobj;\r
1373         new->active = ac_allways;\r
1374         new->priority = 0;\r
1375         new->x=new->temp1 = CONVERT_TILE_TO_GLOBAL(tileX);\r
1376         new->y = CONVERT_TILE_TO_GLOBAL(tileY);\r
1377         new->xdir = 0;\r
1378         new->ydir = 1;\r
1379         new->needtoclip = cl_noclip;\r
1380         NewState(new, &s_sneakplatsit);\r
1381 }\r
1382 \r
1383 /*\r
1384 ===========================\r
1385 =\r
1386 = T_SneakPlat\r
1387 =\r
1388 ===========================\r
1389 */\r
1390 \r
1391 void T_SneakPlat(objtype *ob)\r
1392 {\r
1393         Sint16 dist;\r
1394 \r
1395         if (player->state != &s_keenjump1)\r
1396                 return;\r
1397 \r
1398         if (player->xdir == 1)\r
1399         {\r
1400                 dist = ob->left-player->right;\r
1401                 if (dist > 4*TILEGLOBAL || dist < 0)\r
1402                         return;\r
1403         }\r
1404         else\r
1405         {\r
1406                 dist = player->left-ob->right;\r
1407                 if (dist > 4*TILEGLOBAL || dist < 0)\r
1408                         return;\r
1409         }\r
1410         dist = player->y - ob->y;\r
1411         if (dist < -6*TILEGLOBAL || dist > 6*TILEGLOBAL)\r
1412                 return;\r
1413 \r
1414         ob->xdir = player->xdir;\r
1415         ob->state = &s_sneakplatdodge;\r
1416 }\r
1417 \r
1418 /*\r
1419 =============================================================================\r
1420 \r
1421                                                   CANNON\r
1422 \r
1423 temp1 = direction\r
1424 \r
1425 =============================================================================\r
1426 */\r
1427 \r
1428 statetype s_cannon     = {0,            0,            step,      false, false, 120, 0, 0, NULL, NULL, R_Draw, &s_cannonfire};\r
1429 statetype s_cannonfire = {0,            0,            step,      true,  false,   1, 0, 0, T_Cannon, NULL, R_Draw, &s_cannon};\r
1430 statetype s_cshot1     = {LASER1SPR,    LASER1SPR,    stepthink, false, false,   8, 0, 0, T_Velocity, C_CShot, R_CShot, &s_cshot2};\r
1431 statetype s_cshot2     = {LASER2SPR,    LASER2SPR,    stepthink, false, false,   8, 0, 0, T_Velocity, C_CShot, R_CShot, &s_cshot3};\r
1432 statetype s_cshot3     = {LASER3SPR,    LASER3SPR,    stepthink, false, false,   8, 0, 0, T_Velocity, C_CShot, R_CShot, &s_cshot4};\r
1433 statetype s_cshot4     = {LASER4SPR,    LASER4SPR,    stepthink, false, false,   8, 0, 0, T_Velocity, C_CShot, R_CShot, &s_cshot1};\r
1434 statetype s_cshothit1  = {LASERHIT1SPR, LASERHIT1SPR, step,      false, false,  10, 0, 0, NULL, NULL, R_Draw, &s_cshothit2};\r
1435 statetype s_cshothit2  = {LASERHIT2SPR, LASERHIT2SPR, step,      false, false,  10, 0, 0, NULL, NULL, R_Draw, NULL};\r
1436 \r
1437 /*\r
1438 ===========================\r
1439 =\r
1440 = SpawnCannon\r
1441 =\r
1442 ===========================\r
1443 */\r
1444 \r
1445 void SpawnCannon(Uint16 tileX, Uint16 tileY, Sint16 dir)\r
1446 {\r
1447         GetNewObj(false);\r
1448         new->obclass = cannonobj;\r
1449         new->active = ac_yes;\r
1450         new->tileright = new->tileleft = tileX;\r
1451         new->tiletop = new->tilebottom = tileY;\r
1452         new->x = new->left = new->right = CONVERT_TILE_TO_GLOBAL(tileX);\r
1453         new->y = new->top = new->bottom = CONVERT_TILE_TO_GLOBAL(tileY);\r
1454         new->temp1 = dir;\r
1455         NewState(new, &s_cannon);\r
1456 }\r
1457 \r
1458 /*\r
1459 ===========================\r
1460 =\r
1461 = T_Cannon\r
1462 =\r
1463 ===========================\r
1464 */\r
1465 \r
1466 void T_Cannon(objtype *ob)\r
1467 {\r
1468         GetNewObj(true);\r
1469         new->obclass = mshotobj;\r
1470         new->active = ac_removable;\r
1471         new->x = ob->x;\r
1472         new->y = ob->y;\r
1473         switch (ob->temp1)\r
1474         {\r
1475         case 0:\r
1476                 new->yspeed = -64;\r
1477                 break;\r
1478         case 1:\r
1479                 new->xspeed = 64;\r
1480                 break;\r
1481         case 2:\r
1482                 new->yspeed = 64;\r
1483                 break;\r
1484         case 3:\r
1485                 new->xspeed = -64;\r
1486         }\r
1487         NewState(new, &s_cshot1);\r
1488         SD_PlaySound(SND_ENEMYSHOT);\r
1489 }\r
1490 \r
1491 /*\r
1492 ===========================\r
1493 =\r
1494 = C_CShot\r
1495 =\r
1496 ===========================\r
1497 */\r
1498 \r
1499 void C_CShot(objtype *ob, objtype *hit)\r
1500 {\r
1501         if (hit->obclass == keenobj)\r
1502         {\r
1503                 KillKeen();\r
1504                 ChangeState(ob, &s_cshothit1);\r
1505         }\r
1506 }\r
1507 \r
1508 /*\r
1509 ===========================\r
1510 =\r
1511 = R_CShot\r
1512 =\r
1513 ===========================\r
1514 */\r
1515 \r
1516 void R_CShot(objtype *ob)\r
1517 {\r
1518         if (ob->hitnorth || ob->hiteast || ob->hitsouth || ob->hitwest)\r
1519         {\r
1520                 SD_PlaySound(SND_ENEMYSHOTEXPLODE);\r
1521                 ChangeState(ob, &s_cshothit1);\r
1522         }\r
1523         RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
1524 }