1 /* Reconstructed Commander Keen 4-6 Source Code
\r
2 * Copyright (C) 2021 K1n9_Duk3
\r
4 * This file is loosely based on:
\r
5 * Keen Dreams Source Code
\r
6 * Copyright (C) 2014 Javier M. Chavez
\r
8 * This program is free software; you can redistribute it and/or modify
\r
9 * it under the terms of the GNU General Public License as published by
\r
10 * the Free Software Foundation; either version 2 of the License, or
\r
11 * (at your option) any later version.
\r
13 * This program is distributed in the hope that it will be useful,
\r
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
16 * GNU General Public License for more details.
\r
18 * You should have received a copy of the GNU General Public License along
\r
19 * with this program; if not, write to the Free Software Foundation, Inc.,
\r
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\r
27 Contains the following actor types (in this order):
\r
29 - some shared routines
\r
31 - Teleport and Fuse effects
\r
45 =============================================================================
\r
49 =============================================================================
\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
56 ===========================
\r
60 ===========================
\r
63 Sint16 CheckSpawnShot(Uint16 x, Uint16 y, statetype *state)
\r
65 if (GetNewObj(true) == -1)
\r
69 new->obclass = mshotobj;
\r
70 new->active = ac_removable;
\r
71 NewState(new, state);
\r
72 if (!CheckPosition(new))
\r
81 ===========================
\r
85 ===========================
\r
88 void C_ClipSide(objtype *ob, objtype *hit)
\r
90 if (hit->obclass == keenobj)
\r
92 playerkludgeclipcancel = true;
\r
93 ClipToSpriteSide(hit, ob);
\r
94 playerkludgeclipcancel = false;
\r
99 ===========================
\r
103 ===========================
\r
106 void C_ClipTop(objtype *ob, objtype *hit)
\r
108 if (hit->obclass == keenobj)
\r
109 ClipToSpriteTop(hit, ob);
\r
113 ===========================
\r
117 ===========================
\r
120 void R_Land(objtype *ob)
\r
122 if (ob->hiteast || ob->hitwest)
\r
131 if (ob->state->nextstate)
\r
133 ChangeState(ob, ob->state->nextstate);
\r
142 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);
\r
146 ===========================
\r
150 ===========================
\r
153 void R_Bounce(objtype *ob)
\r
155 Uint16 wall,absx,absy,angle,newangle;
\r
159 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);
\r
161 if (ob->hiteast || ob->hitwest)
\r
162 ob->xspeed = -ob->xspeed/2;
\r
166 ob->yspeed = -ob->yspeed/2;
\r
170 wall = ob->hitnorth;
\r
173 if (ob->yspeed < 0)
\r
176 absx = abs(ob->xspeed);
\r
180 if (absx>absy*2) // 22 degrees
\r
183 speed = absx*286; // x*sqrt(5)/2
\r
188 speed = absx*362; // x*sqrt(2)
\r
193 if (absy>absx*2) // 90 degrees
\r
200 angle = 2; // 67 degrees
\r
201 speed = absy*286; // y*sqrt(5)/2
\r
204 if (ob->xspeed > 0)
\r
208 newangle = bounceangle[ob->hitnorth][angle];
\r
212 ob->xspeed = speed / 286;
\r
213 ob->yspeed = -ob->xspeed / 2;
\r
216 ob->xspeed = speed / 362;
\r
217 ob->yspeed = -ob->xspeed;
\r
220 ob->yspeed = -(speed / 286);
\r
221 ob->xspeed = -ob->yspeed / 2;
\r
227 ob->yspeed = -(speed / 256);
\r
230 ob->yspeed = -(speed / 286);
\r
231 ob->xspeed = ob->yspeed / 2;
\r
234 ob->xspeed = ob->yspeed = -(speed / 362);
\r
237 ob->xspeed = -(speed / 286);
\r
238 ob->yspeed = ob->xspeed / 2;
\r
242 ob->xspeed = -(speed / 286);
\r
243 ob->yspeed = -ob->xspeed / 2;
\r
246 ob->xspeed = -(speed / 362);
\r
247 ob->yspeed = -ob->xspeed;
\r
250 ob->yspeed = speed / 286;
\r
251 ob->xspeed = -ob->yspeed / 2;
\r
257 ob->yspeed = -(speed / 256);
\r
260 ob->yspeed = speed / 286;
\r
261 ob->xspeed = ob->yspeed / 2;
\r
264 ob->xspeed = speed / 362;
\r
265 ob->yspeed = speed / 362;
\r
268 ob->xspeed = speed / 286;
\r
269 ob->yspeed = ob->xspeed / 2;
\r
273 if (speed < 256*16)
\r
275 ChangeState(ob, ob->state->nextstate);
\r
281 =============================================================================
\r
285 temp2 = base shape number
\r
286 temp3 = last animated shape number +1
\r
288 =============================================================================
\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
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
309 ===========================
\r
313 ===========================
\r
316 void SpawnBonus(Uint16 tileX, Uint16 tileY, Uint16 type)
\r
319 new->needtoclip = cl_noclip;
\r
321 new->obclass = bonusobj;
\r
322 new->x = CONVERT_TILE_TO_GLOBAL(tileX);
\r
323 new->y = CONVERT_TILE_TO_GLOBAL(tileY);
\r
326 new->temp2=new->shapenum = bonusshape[type];
\r
327 new->temp3 = new->temp2+2;
\r
328 NewState(new, &s_bonus1);
\r
332 ===========================
\r
336 ===========================
\r
339 void SpawnSplash(Uint16 tileX, Uint16 tileY)
\r
342 new->needtoclip = cl_noclip;
\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
351 ===========================
\r
355 ===========================
\r
358 void T_Bonus(objtype *ob)
\r
360 if (++ob->shapenum == ob->temp3)
\r
361 ob->shapenum = ob->temp2;
\r
365 ===========================
\r
369 ===========================
\r
372 void T_FlyBonus(objtype *ob)
\r
375 ob->state = &s_bonus1;
\r
377 if (++ob->shapenum == ob->temp3)
\r
378 ob->shapenum = ob->temp2;
\r
384 =============================================================================
\r
388 =============================================================================
\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
397 ===========================
\r
401 ===========================
\r
404 void SpawnTeleport(void)
\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
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
422 SD_PlaySound(SND_TELEPORT);
\r
426 =============================================================================
\r
430 =============================================================================
\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
438 ===========================
\r
442 ===========================
\r
445 void SpawnFuseFlash(Uint16 tileX, Uint16 tileY)
\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
458 =============================================================================
\r
462 =============================================================================
\r
465 statetype s_deadmachine = {-1, -1, step, false, false, 60, 0, 0, T_DeadMachine, NULL, R_Draw, NULL};
\r
468 ===========================
\r
472 ===========================
\r
475 void SpawnDeadMachine(void)
\r
478 new->active = ac_allways;
\r
479 new->needtoclip = cl_noclip;
\r
480 NewState(new, &s_deadmachine);
\r
484 ===========================
\r
488 ===========================
\r
492 void T_DeadMachine(objtype *ob)
\r
496 playstate = ex_qedbroke;
\r
500 playstate = ex_fusebroke;
\r
505 =============================================================================
\r
509 =============================================================================
\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
518 ===========================
\r
522 ===========================
\r
525 void SpawnPlatform(Uint16 tileX, Uint16 tileY, Sint16 dir, Sint16 type)
\r
528 new->obclass = platformobj;
\r
529 new->active = ac_allways;
\r
531 new->x = CONVERT_TILE_TO_GLOBAL(tileX);
\r
532 new->y = CONVERT_TILE_TO_GLOBAL(tileY);
\r
553 new->x += 4*PIXGLOBAL;
\r
554 new->y += 4*PIXGLOBAL;
\r
555 NewState(new, &s_slotplat1);
\r
559 NewState(new, &s_platform);
\r
564 ===========================
\r
568 ===========================
\r
571 void T_Platform(objtype *ob)
\r
573 Uint16 newpos, newtile;
\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
581 if (!xtry && !ytry)
\r
583 xtry = ob->xdir * 12 * tics;
\r
584 ytry = ob->ydir * 12 * tics;
\r
588 newpos = ob->right + xtry;
\r
589 newtile = CONVERT_GLOBAL_TO_TILE(newpos);
\r
590 if (ob->tileright != newtile)
\r
592 if (*(mapsegs[2]+mapbwidthtable[ob->tiletop]/2 + newtile) == PLATFORMBLOCK)
\r
595 xtry = xtry - (newpos & 0xFF);
\r
599 else if (ob->xdir == -1)
\r
601 newpos = ob->left + xtry;
\r
602 newtile = CONVERT_GLOBAL_TO_TILE(newpos);
\r
603 if (ob->tileleft != newtile)
\r
605 if (*(mapsegs[2]+mapbwidthtable[ob->tiletop]/2 + newtile) == PLATFORMBLOCK)
\r
608 xtry = xtry + (TILEGLOBAL - (newpos & 0xFF));
\r
612 else if (ob->ydir == 1)
\r
614 newpos = ob->bottom + ytry;
\r
615 newtile = CONVERT_GLOBAL_TO_TILE(newpos);
\r
616 if (ob->tilebottom != newtile)
\r
618 if (*(mapsegs[2]+mapbwidthtable[newtile]/2 + ob->tileleft) == PLATFORMBLOCK)
\r
620 if (*(mapsegs[2]+mapbwidthtable[newtile-2]/2 + ob->tileleft) == PLATFORMBLOCK)
\r
623 ob->needtoreact = true;
\r
628 ytry = ytry - (newpos & 0xFF);
\r
633 else if (ob->ydir == -1)
\r
635 newpos = ob->top + ytry;
\r
636 newtile = CONVERT_GLOBAL_TO_TILE(newpos);
\r
637 if (ob->tiletop != newtile)
\r
639 if (*(mapsegs[2]+mapbwidthtable[newtile]/2 + ob->tileleft) == PLATFORMBLOCK)
\r
641 if (*(mapsegs[2]+mapbwidthtable[newtile+2]/2 + ob->tileleft) == PLATFORMBLOCK)
\r
644 ob->needtoreact = true;
\r
649 ytry = ytry + (TILEGLOBAL - (newpos & 0xFF));
\r
658 ===========================
\r
662 ===========================
\r
665 void T_Slotplat(objtype *ob)
\r
667 Uint16 newpos, newtile;
\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
675 if (!xtry && !ytry)
\r
677 xtry = ob->xdir * 12 * tics;
\r
678 ytry = ob->ydir * 12 * tics;
\r
682 newpos = ob->right + xtry;
\r
683 newtile = CONVERT_GLOBAL_TO_TILE(newpos);
\r
684 if (ob->tileright != newtile)
\r
686 if (*(mapsegs[2]+mapbwidthtable[ob->tiletop]/2 + newtile) == PLATFORMBLOCK)
\r
689 xtry = xtry - (newpos & 0xFF);
\r
693 else if (ob->xdir == -1)
\r
695 newpos = ob->left + xtry;
\r
696 newtile = CONVERT_GLOBAL_TO_TILE(newpos);
\r
697 if (ob->tileleft != newtile)
\r
699 if (*(mapsegs[2]+mapbwidthtable[ob->tiletop]/2 + newtile) == PLATFORMBLOCK)
\r
702 xtry = xtry + (TILEGLOBAL - (newpos & 0xFF));
\r
706 else if (ob->ydir == 1)
\r
708 newpos = ob->bottom + ytry;
\r
709 newtile = CONVERT_GLOBAL_TO_TILE(newpos);
\r
710 if (ob->tilebottom != newtile)
\r
712 if (*(mapsegs[2]+mapbwidthtable[newtile]/2 + ob->tileleft + 1) == PLATFORMBLOCK)
\r
714 if (*(mapsegs[2]+mapbwidthtable[newtile-2]/2 + ob->tileleft) == PLATFORMBLOCK) // BUG? '+ 1' is missing after 'tileleft'
\r
717 ob->needtoreact = true;
\r
722 ytry = ytry - (newpos & 0xFF);
\r
727 else if (ob->ydir == -1)
\r
729 newpos = ob->top + ytry;
\r
730 newtile = CONVERT_GLOBAL_TO_TILE(newpos);
\r
731 if (ob->tiletop != newtile)
\r
733 if (*(mapsegs[2]+mapbwidthtable[newtile]/2 + ob->tileleft + 1) == PLATFORMBLOCK)
\r
735 if (*(mapsegs[2]+mapbwidthtable[newtile+2]/2 + ob->tileleft + 1) == PLATFORMBLOCK)
\r
738 ob->needtoreact = true;
\r
743 ytry = ytry + (TILEGLOBAL - (newpos & 0xFF));
\r
752 =============================================================================
\r
756 temp1 = initial y position
\r
758 =============================================================================
\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
766 ===========================
\r
770 ===========================
\r
773 void SpawnDropPlat(Uint16 tileX, Uint16 tileY)
\r
776 new->obclass = platformobj;
\r
777 new->active = ac_allways;
\r
779 new->x = CONVERT_TILE_TO_GLOBAL(tileX);
\r
780 new->y=new->temp1 = CONVERT_TILE_TO_GLOBAL(tileY);
\r
783 new->needtoclip = cl_noclip;
\r
784 NewState(new, &s_dropplatsit);
\r
788 ===========================
\r
792 ===========================
\r
795 void T_DropPlatSit(objtype *ob)
\r
797 if (gamestate.riding == ob)
\r
799 ytry = tics << 4; //tics * 16;
\r
801 if (ob->y + ytry - ob->temp1 >= 8*PIXGLOBAL)
\r
802 ob->state = &s_dropplatfall;
\r
807 ===========================
\r
811 ===========================
\r
814 void T_DropPlatFall(objtype *ob)
\r
816 Uint16 newpos, newtile;
\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
826 newpos = ob->bottom + ytry;
\r
827 newtile = CONVERT_GLOBAL_TO_TILE(newpos);
\r
828 if (ob->tilebottom != newtile)
\r
830 if (*(mapsegs[2]+mapbwidthtable[newtile]/2 + ob->tileleft) == PLATFORMBLOCK)
\r
832 ytry = 0xFF - (ob->bottom & 0xFF);
\r
833 if (gamestate.riding != ob)
\r
834 ob->state = &s_dropplatrise;
\r
840 ===========================
\r
844 ===========================
\r
847 void T_DropPlatRise(objtype *ob)
\r
849 if (gamestate.riding == ob)
\r
852 ob->state = &s_dropplatfall;
\r
854 else if (ob->y <= ob->temp1)
\r
856 ytry = ob->temp1 - ob->y;
\r
857 ob->state = &s_dropplatsit;
\r
862 =============================================================================
\r
866 temp1 = initial y position (is set but never used)
\r
868 =============================================================================
\r
871 statetype s_statplat = {PLATFORMSPR, PLATFORMSPR, step, false, false, 32000, 0, 0, NULL, NULL, R_Draw, &s_statplat};
\r
874 ===========================
\r
878 ===========================
\r
881 void SpawnStaticPlat(Uint16 tileX, Uint16 tileY)
\r
884 new->obclass = platformobj;
\r
885 new->active = ac_yes;
\r
887 new->x = CONVERT_TILE_TO_GLOBAL(tileX);
\r
888 new->y=new->temp1 = CONVERT_TILE_TO_GLOBAL(tileY);
\r
891 new->needtoclip = cl_noclip;
\r
892 NewState(new, &s_statplat);
\r
896 =============================================================================
\r
901 temp2 = countdown to next dir check
\r
903 =============================================================================
\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
912 ===========================
\r
916 ===========================
\r
919 void SpawnGoPlat(Uint16 tileX, Uint16 tileY, Sint16 dir, Sint16 type)
\r
922 new->obclass = platformobj;
\r
923 new->active = ac_allways;
\r
925 new->x = CONVERT_TILE_TO_GLOBAL(tileX);
\r
926 new->y = CONVERT_TILE_TO_GLOBAL(tileY);
\r
929 new->needtoclip = cl_noclip;
\r
932 new->x += 4*PIXGLOBAL;
\r
933 new->y += 4*PIXGLOBAL;
\r
934 NewState(new, &s_slotgoplat1);
\r
938 NewState(new, &s_goplat);
\r
940 *(mapsegs[2]+mapbwidthtable[tileY]/2 + tileX) = DIRARROWSTART + dir;
\r
942 new->temp2 = TILEGLOBAL;
\r
946 ===========================
\r
950 ===========================
\r
953 void T_GoPlat(objtype *ob)
\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
965 if (!xtry && !ytry)
\r
968 if (ob->temp2 > move)
\r
970 ob->temp2 = ob->temp2 - move;
\r
972 dir = pdirx[ob->temp1];
\r
975 xtry = xtry + move;
\r
977 else if (dir == -1)
\r
979 xtry = xtry + -move;
\r
982 dir = pdiry[ob->temp1];
\r
985 ytry = ytry + move;
\r
987 else if (dir == -1)
\r
989 ytry = ytry + -move;
\r
994 dir = pdirx[ob->temp1];
\r
999 else if (dir == -1)
\r
1001 xtry += -ob->temp2;
\r
1004 dir = pdiry[ob->temp1];
\r
1007 ytry += ob->temp2;
\r
1009 else if (dir == -1)
\r
1011 ytry += -ob->temp2;
\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
1019 char error[60] = "Goplat moved to a bad spot: ";
\r
1022 strcat(error, itoa(ob->x, buf, 16));
\r
1023 strcat(error, ",");
\r
1024 strcat(error, itoa(ob->y, buf, 16));
\r
1028 move -= ob->temp2;
\r
1029 ob->temp2 = TILEGLOBAL - move;
\r
1031 dir = pdirx[ob->temp1];
\r
1034 xtry = xtry + move;
\r
1036 else if (dir == -1)
\r
1038 xtry = xtry - move;
\r
1041 dir = pdiry[ob->temp1];
\r
1044 ytry = ytry + move;
\r
1046 else if (dir == -1)
\r
1048 ytry = ytry - move;
\r
1055 ===========================
\r
1059 ===========================
\r
1062 void T_GoSlotPlat(objtype *ob)
\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
1074 if (!xtry && !ytry)
\r
1077 if (ob->temp2 > move)
\r
1079 ob->temp2 = ob->temp2 - move;
\r
1081 dir = pdirx[ob->temp1];
\r
1084 xtry = xtry + move;
\r
1086 else if (dir == -1)
\r
1088 xtry = xtry + -move;
\r
1091 dir = pdiry[ob->temp1];
\r
1094 ytry = ytry + move;
\r
1096 else if (dir == -1)
\r
1098 ytry = ytry + -move;
\r
1103 dir = pdirx[ob->temp1];
\r
1106 xtry += ob->temp2;
\r
1108 else if (dir == -1)
\r
1110 xtry += -ob->temp2;
\r
1113 dir = pdiry[ob->temp1];
\r
1116 ytry += ob->temp2;
\r
1118 else if (dir == -1)
\r
1120 ytry += -ob->temp2;
\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
1128 Quit("Goplat moved to a bad spot!");
\r
1131 move -= ob->temp2;
\r
1132 ob->temp2 = TILEGLOBAL - move;
\r
1134 dir = pdirx[ob->temp1];
\r
1137 xtry = xtry + move;
\r
1139 else if (dir == -1)
\r
1141 xtry = xtry - move;
\r
1144 dir = pdiry[ob->temp1];
\r
1147 ytry = ytry + move;
\r
1149 else if (dir == -1)
\r
1151 ytry = ytry - move;
\r
1158 =============================================================================
\r
1163 temp2 = countdown to next dir check
\r
1165 =============================================================================
\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
1175 ===========================
\r
1179 ===========================
\r
1182 void SpawnVolte(Uint16 tileX, Uint16 tileY)
\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
1200 else if (map[1] == DIRARROWSTART + arrow_West)
\r
1204 else if (*(map-mapwidth) == DIRARROWSTART + arrow_South)
\r
1206 dir = arrow_South;
\r
1208 else if (*(map+mapwidth) == DIRARROWSTART + arrow_North)
\r
1210 dir = arrow_North;
\r
1212 map[0] = dir + DIRARROWSTART;
\r
1214 new->temp2 = TILEGLOBAL;
\r
1218 ===========================
\r
1222 ===========================
\r
1225 void T_Volte(objtype *ob)
\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
1237 if (!xtry && !ytry)
\r
1240 if (ob->temp2 > move)
\r
1242 ob->temp2 = ob->temp2 - move;
\r
1244 dir = pdirx[ob->temp1];
\r
1247 xtry = xtry + move;
\r
1249 else if (dir == -1)
\r
1251 xtry = xtry + -move;
\r
1254 dir = pdiry[ob->temp1];
\r
1257 ytry = ytry + move;
\r
1259 else if (dir == -1)
\r
1261 ytry = ytry + -move;
\r
1266 dir = pdirx[ob->temp1];
\r
1269 xtry += ob->temp2;
\r
1271 else if (dir == -1)
\r
1273 xtry += -ob->temp2;
\r
1276 dir = pdiry[ob->temp1];
\r
1279 ytry += ob->temp2;
\r
1281 else if (dir == -1)
\r
1283 ytry += -ob->temp2;
\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
1291 char error[60] = "Volte moved to a bad spot: ";
\r
1294 strcat(error, itoa(ob->x, buf, 16));
\r
1295 strcat(error, ",");
\r
1296 strcat(error, itoa(ob->y, buf, 16));
\r
1300 move -= ob->temp2;
\r
1301 ob->temp2 = TILEGLOBAL - move;
\r
1303 dir = pdirx[ob->temp1];
\r
1306 xtry = xtry + move;
\r
1308 else if (dir == -1)
\r
1310 xtry = xtry - move;
\r
1313 dir = pdiry[ob->temp1];
\r
1316 ytry = ytry + move;
\r
1318 else if (dir == -1)
\r
1320 ytry = ytry - move;
\r
1327 ===========================
\r
1331 ===========================
\r
1334 void C_Volte(objtype *ob, objtype *hit)
\r
1336 if (hit->obclass == keenobj)
\r
1340 else if (hit->obclass == stunshotobj)
\r
1343 ChangeState(ob, &s_voltestun);
\r
1348 =============================================================================
\r
1352 temp1 = initial x position (is set but never used)
\r
1354 =============================================================================
\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
1362 ===========================
\r
1366 ===========================
\r
1369 void SpawnSneakPlat(Uint16 tileX, Uint16 tileY)
\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
1379 new->needtoclip = cl_noclip;
\r
1380 NewState(new, &s_sneakplatsit);
\r
1384 ===========================
\r
1388 ===========================
\r
1391 void T_SneakPlat(objtype *ob)
\r
1395 if (player->state != &s_keenjump1)
\r
1398 if (player->xdir == 1)
\r
1400 dist = ob->left-player->right;
\r
1401 if (dist > 4*TILEGLOBAL || dist < 0)
\r
1406 dist = player->left-ob->right;
\r
1407 if (dist > 4*TILEGLOBAL || dist < 0)
\r
1410 dist = player->y - ob->y;
\r
1411 if (dist < -6*TILEGLOBAL || dist > 6*TILEGLOBAL)
\r
1414 ob->xdir = player->xdir;
\r
1415 ob->state = &s_sneakplatdodge;
\r
1419 =============================================================================
\r
1425 =============================================================================
\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
1438 ===========================
\r
1442 ===========================
\r
1445 void SpawnCannon(Uint16 tileX, Uint16 tileY, Sint16 dir)
\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
1455 NewState(new, &s_cannon);
\r
1459 ===========================
\r
1463 ===========================
\r
1466 void T_Cannon(objtype *ob)
\r
1469 new->obclass = mshotobj;
\r
1470 new->active = ac_removable;
\r
1473 switch (ob->temp1)
\r
1476 new->yspeed = -64;
\r
1485 new->xspeed = -64;
\r
1487 NewState(new, &s_cshot1);
\r
1488 SD_PlaySound(SND_ENEMYSHOT);
\r
1492 ===========================
\r
1496 ===========================
\r
1499 void C_CShot(objtype *ob, objtype *hit)
\r
1501 if (hit->obclass == keenobj)
\r
1504 ChangeState(ob, &s_cshothit1);
\r
1509 ===========================
\r
1513 ===========================
\r
1516 void R_CShot(objtype *ob)
\r
1518 if (ob->hitnorth || ob->hiteast || ob->hitsouth || ob->hitwest)
\r
1520 SD_PlaySound(SND_ENEMYSHOTEXPLODE);
\r
1521 ChangeState(ob, &s_cshothit1);
\r
1523 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);
\r