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
35 - Quest Items (Sandwich, Grappling Hook, Passcard, Molly)
\r
50 =============================================================================
\r
54 =============================================================================
\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
61 ===========================
\r
65 ===========================
\r
68 void C_ClipSide(objtype *ob, objtype *hit)
\r
70 if (hit->obclass == keenobj)
\r
72 playerkludgeclipcancel = true;
\r
73 ClipToSpriteSide(hit, ob);
\r
74 playerkludgeclipcancel = false;
\r
79 ===========================
\r
83 ===========================
\r
86 void C_ClipTop(objtype *ob, objtype *hit)
\r
88 if (hit->obclass == keenobj)
\r
89 ClipToSpriteTop(hit, ob);
\r
93 ===========================
\r
97 ===========================
\r
100 void R_Land(objtype *ob)
\r
102 if (ob->hiteast || ob->hitwest)
\r
111 if (ob->state->nextstate)
\r
113 ChangeState(ob, ob->state->nextstate);
\r
122 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);
\r
126 ===========================
\r
130 ===========================
\r
133 void R_Bounce(objtype *ob)
\r
135 Uint16 wall,absx,absy,angle,newangle;
\r
139 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);
\r
141 if (ob->hiteast || ob->hitwest)
\r
142 ob->xspeed = -ob->xspeed/2;
\r
146 ob->yspeed = -ob->yspeed/2;
\r
150 wall = ob->hitnorth;
\r
161 if (ob->yspeed < 0)
\r
164 absx = abs(ob->xspeed);
\r
168 if (absx>absy*2) // 22 degrees
\r
171 speed = absx*286; // x*sqrt(5)/2
\r
176 speed = absx*362; // x*sqrt(2)
\r
181 if (absy>absx*2) // 90 degrees
\r
188 angle = 2; // 67 degrees
\r
189 speed = absy*286; // y*sqrt(5)/2
\r
192 if (ob->xspeed > 0)
\r
196 newangle = bounceangle[ob->hitnorth][angle];
\r
200 ob->xspeed = speed / 286;
\r
201 ob->yspeed = -ob->xspeed / 2;
\r
204 ob->xspeed = speed / 362;
\r
205 ob->yspeed = -ob->xspeed;
\r
208 ob->yspeed = -(speed / 286);
\r
209 ob->xspeed = -ob->yspeed / 2;
\r
215 ob->yspeed = -(speed / 256);
\r
218 ob->yspeed = -(speed / 286);
\r
219 ob->xspeed = ob->yspeed / 2;
\r
222 ob->xspeed = ob->yspeed = -(speed / 362);
\r
225 ob->xspeed = -(speed / 286);
\r
226 ob->yspeed = ob->xspeed / 2;
\r
230 ob->xspeed = -(speed / 286);
\r
231 ob->yspeed = -ob->xspeed / 2;
\r
234 ob->xspeed = -(speed / 362);
\r
235 ob->yspeed = -ob->xspeed;
\r
238 ob->yspeed = speed / 286;
\r
239 ob->xspeed = -ob->yspeed / 2;
\r
245 ob->yspeed = -(speed / 256);
\r
248 ob->yspeed = speed / 286;
\r
249 ob->xspeed = ob->yspeed / 2;
\r
252 ob->xspeed = speed / 362;
\r
253 ob->yspeed = speed / 362;
\r
256 ob->xspeed = speed / 286;
\r
257 ob->yspeed = ob->xspeed / 2;
\r
261 if (speed < 256*16)
\r
263 ChangeState(ob, ob->state->nextstate);
\r
269 =============================================================================
\r
274 temp2 = base shape number
\r
275 temp3 = last animated shape number +1
\r
277 =============================================================================
\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
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
291 Uint16 bonusshape[] = {
\r
292 REDGEM1SPR, YELLOWGEM1SPR, BLUEGEM1SPR, GREENGEM1SPR,
\r
293 SUGAR1ASPR, SUGAR2ASPR, SUGAR3ASPR,
\r
294 SUGAR4ASPR, SUGAR5ASPR, SUGAR6ASPR,
\r
295 ONEUPASPR, STUNCLIP1SPR
\r
299 ===========================
\r
303 ===========================
\r
306 void SpawnBonus(Uint16 tileX, Uint16 tileY, Uint16 type)
\r
309 new->needtoclip = cl_noclip;
\r
311 new->obclass = bonusobj;
\r
312 new->x = CONVERT_TILE_TO_GLOBAL(tileX);
\r
313 new->y = CONVERT_TILE_TO_GLOBAL(tileY);
\r
316 new->temp2=new->shapenum = bonusshape[type];
\r
317 new->temp3 = new->temp2+2;
\r
318 NewState(new, &s_bonus1);
\r
322 ===========================
\r
326 ===========================
\r
329 void SpawnSplash(Uint16 tileX, Uint16 tileY)
\r
332 new->needtoclip = cl_noclip;
\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
341 ===========================
\r
345 ===========================
\r
348 void T_Bonus(objtype *ob)
\r
350 if (++ob->shapenum == ob->temp3)
\r
351 ob->shapenum = ob->temp2;
\r
355 ===========================
\r
359 ===========================
\r
362 void T_FlyBonus(objtype *ob)
\r
365 ob->state = &s_bonus1;
\r
367 if (++ob->shapenum == ob->temp3)
\r
368 ob->shapenum = ob->temp2;
\r
374 =============================================================================
\r
378 =============================================================================
\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
387 ===========================
\r
391 ===========================
\r
394 void SpawnGrabbiter(Uint16 tileX, Uint16 tileY)
\r
397 new->active = ac_yes;
\r
398 new->needtoclip = cl_noclip;
\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
405 NewState(new, &s_grabbitersleep1);
\r
409 NewState(new, &s_grabbiter1);
\r
414 ===========================
\r
418 ===========================
\r
421 void C_Grabbiter(objtype *ob, objtype *hit)
\r
423 // BUG: this is executed for every object, not just (Map-)Keen!
\r
424 switch (gamestate.sandwichstate)
\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
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
442 SD_PlaySound(SND_NOWAY);
\r
444 IN_ClearKeysDown();
\r
449 xtry = -hit->xmove;
\r
450 ytry = -hit->ymove;
\r
451 hit->xdir = hit->ydir = 0;
\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
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
473 IN_ClearKeysDown();
\r
475 ChangeState(ob, &s_grabbitersleep1);
\r
481 =============================================================================
\r
486 temp2 = countdown to next dir check
\r
488 =============================================================================
\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
497 ===========================
\r
501 ===========================
\r
504 void SpawnRocket(Uint16 tileX, Uint16 tileY, Uint16 state)
\r
506 if (gamestate.rocketstate == state)
\r
509 new->active = ac_yes;
\r
510 new->needtoclip = cl_noclip;
\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
520 ===========================
\r
524 ===========================
\r
527 void T_Rocket(objtype *ob)
\r
529 ob->needtoreact = true;
\r
533 ===========================
\r
537 ===========================
\r
540 void C_Rocket(objtype *ob, objtype *hit)
\r
542 // BUG: this is executed for every object, not just (Map-)Keen!
\r
543 switch (gamestate.passcardstate)
\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
553 "The door makes a loud\n"
\r
554 "blooping noise.\n"
\r
556 "\"Passcard required\n"
\r
560 SD_PlaySound(SND_NOWAY);
\r
562 IN_ClearKeysDown();
\r
567 xtry = -hit->xmove;
\r
568 ytry = -hit->ymove;
\r
569 hit->xdir = hit->ydir = 0;
\r
574 ob->temp1 = arrow_North;
\r
575 ob->temp2 = TILEGLOBAL;
\r
576 ChangeState(ob, &s_rocketfly1);
\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
588 ===========================
\r
592 ===========================
\r
595 void C_RocketFly(objtype *ob, objtype *hit)
\r
597 if (hit->obclass == keenobj)
\r
600 hit->y = ob->y+TILEGLOBAL;
\r
601 ChangeState(hit, hit->state);
\r
606 ===========================
\r
610 ===========================
\r
613 void T_RocketFly(objtype *ob)
\r
615 Uint16 move, tx, ty;
\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
624 if (xtry == 0 && ytry == 0)
\r
626 if (!SD_SoundPlaying())
\r
627 SD_PlaySound(SND_ROCKETFLY);
\r
630 if (ob->temp2 > move)
\r
632 ob->temp2 = ob->temp2 - move;
\r
633 dir = pdirx[ob->temp1];
\r
638 else if (dir == -1)
\r
642 dir = pdiry[ob->temp1];
\r
648 else if (dir == -1)
\r
655 dir = pdirx[ob->temp1];
\r
660 else if (dir == -1)
\r
664 dir = pdiry[ob->temp1];
\r
669 else if (dir == -1)
\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
681 ChangeState(ob, &s_rocket);
\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
694 ob->temp2 = TILEGLOBAL - move;
\r
695 dir = pdirx[ob->temp1];
\r
698 xtry = xtry + move;
\r
700 else if (dir == -1)
\r
702 xtry = xtry - move;
\r
704 dir = pdiry[ob->temp1];
\r
707 ytry = ytry + move;
\r
709 else if (dir == -1)
\r
711 ytry = ytry - move;
\r
719 =============================================================================
\r
723 temp1 = type (0 = top of cliff, 1 = bottom of cliff)
\r
725 =============================================================================
\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
737 ===========================
\r
741 ===========================
\r
744 void SpawnGrappleSpot(Uint16 tileX, Uint16 tileY, Uint16 type)
\r
747 new->active = ac_yes;
\r
748 new->needtoclip = cl_noclip;
\r
750 new->obclass = grapplespotobj;
\r
751 new->tileleft = new->tileright = tileX;
\r
752 new->tiletop = new->tilebottom = tileY;
\r
754 new->x = new->left = CONVERT_TILE_TO_GLOBAL(tileX);
\r
755 new->right = new->left + TILEGLOBAL;
\r
758 new->y = new->top = CONVERT_TILE_TO_GLOBAL(tileY+1)-1;
\r
762 new->y = new->top = CONVERT_TILE_TO_GLOBAL(tileY);
\r
764 new->bottom = new->top + 1;
\r
765 NewState(new, &s_grapplespot);
\r
767 if (gamestate.hookstate == 2 && type)
\r
770 new->active = ac_yes;
\r
771 new->needtoclip = cl_noclip;
\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
781 ===========================
\r
785 ===========================
\r
788 void T_ThrowRope(objtype *ob)
\r
791 new->active = ac_yes;
\r
792 new->needtoclip = cl_noclip;
\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
799 ob->obclass = keenobj;
\r
800 ob->shapenum = WORLDKEENU3SPR;
\r
804 ===========================
\r
808 ===========================
\r
811 void T_ClimbRope(objtype *ob)
\r
813 if (--ob->temp4 == 0)
\r
817 ob->y += 3*PIXGLOBAL;
\r
818 ob->shapenum = WORLDKEEND3SPR;
\r
822 ob->y -= 3*PIXGLOBAL;
\r
823 ob->shapenum = WORLDKEENU3SPR;
\r
825 ob->obclass = keenobj;
\r
826 NewState(ob, &s_worldkeen);
\r
831 ===========================
\r
835 ===========================
\r
838 void C_GrappleSpot(objtype *ob, objtype *hit)
\r
840 if (hit->obclass == keenobj)
\r
842 switch (gamestate.hookstate)
\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
852 "What a tall cliff!\n"
\r
853 "Wish I had a rope\n"
\r
854 "and grappling hook.\n"
\r
857 SD_PlaySound(SND_NOWAY);
\r
859 IN_ClearKeysDown();
\r
864 xtry = -hit->xmove;
\r
865 ytry = -hit->ymove;
\r
866 hit->xdir = hit->ydir = 0;
\r
871 gamestate.hookstate++;
\r
872 SD_PlaySound(SND_THROWROPE);
\r
873 ChangeState(hit, &s_throwrope1);
\r
874 hit->obclass = inertobj;
\r
880 hit->y += 4*PIXGLOBAL;
\r
886 hit->y -= 4*PIXGLOBAL;
\r
890 NewState(hit, &s_climbrope1);
\r
891 hit->obclass = inertobj;
\r
897 =============================================================================
\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
908 =============================================================================
\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
919 ===========================
\r
921 = SpawnSatelliteStop
\r
923 ===========================
\r
926 void SpawnSatelliteStop(Uint16 tileX, Uint16 tileY, Uint16 type)
\r
929 new->active = ac_allways;
\r
930 new->needtoclip = cl_noclip;
\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
944 ===========================
\r
948 ===========================
\r
951 void SpawnSatellite(Uint16 tileX, Uint16 tileY)
\r
956 new->needtoclip = cl_noclip;
\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
964 NewState(new, &s_satellite1);
\r
967 (mapsegs[2]+mapbwidthtable[tileY]/2)[tileX] = (dir+DIRARROWSTART);
\r
969 new->temp2 = TILEGLOBAL;
\r
974 ===========================
\r
978 ===========================
\r
981 void T_Satellite(objtype *ob)
\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
989 if (xtry == 0 && ytry == 0)
\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
995 if (!(ob->temp3 & 0xFF) && (ob->temp3 & 0xFF00))
\r
997 ob->temp4 = ob->temp3 >> 8;
\r
1000 // move current type into previous type and set current stop type to 0
\r
1005 // follow the arrow path like a GoPlat
\r
1012 ===========================
\r
1016 ===========================
\r
1019 void C_Satellite(objtype *ob, objtype *hit)
\r
1024 if (hit->state == &s_satellitestopspot)
\r
1026 ob->temp3 |= hit->temp1;
\r
1028 else if (hit->obclass == keenobj)
\r
1031 // check if satellite has reaced a new stop spot
\r
1033 temp = ob->temp3 >> 8;
\r
1034 if (temp && temp != ob->temp4)
\r
1036 SD_PlaySound(SND_GRABSATELLITE);
\r
1038 // update last spot value (don't grab or drop Keen until moved to the next spot)
\r
1041 if (player->state == &s_worldkeensatellite)
\r
1044 // drop Keen off at the current stop spot
\r
1046 for (o=player->next; o; o=o->next)
\r
1048 if (o->obclass == satellitestopobj && o->temp1 == temp)
\r
1052 hit->shapenum = WORLDKEENU3SPR;
\r
1053 ChangeState(player, &s_worldkeen);
\r
1054 hit->needtoclip = cl_midclip;
\r
1062 // grab and carry Keen
\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
1070 else if (hit->state == &s_worldkeensatellite)
\r
1073 // move Keen along with the satellite
\r
1075 hit->x = ob->x + 12*PIXGLOBAL;
\r
1076 hit->y = ob->y + 16*PIXGLOBAL;
\r
1077 ChangeState(hit, hit->state);
\r
1083 ===========================
\r
1085 = R_WorldKeenSatellite
\r
1087 ===========================
\r
1090 void R_WorldKeenSatellite(objtype *ob)
\r
1092 RF_PlaceSprite(&ob->sprite, ob->x + 4*PIXGLOBAL, ob->y + 8*PIXGLOBAL, WORLDKEENHANGSPR, spritedraw, 1);
\r
1096 =============================================================================
\r
1100 =============================================================================
\r
1103 statetype s_sandwich = {SANDWICHSPR, SANDWICHSPR, think, false, false, 0, 0, 0, NULL, C_Molly, R_Draw, NULL};
\r
1106 ===========================
\r
1110 ===========================
\r
1113 void SpawnSandwich(Uint16 tileX, Uint16 tileY)
\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
1125 =============================================================================
\r
1129 =============================================================================
\r
1132 statetype s_hook = {HOOKSPR, HOOKSPR, think, false, false, 0, 0, 0, NULL, C_Molly, R_Draw, NULL};
\r
1135 ===========================
\r
1139 ===========================
\r
1142 void SpawnHook(Uint16 tileX, Uint16 tileY)
\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
1154 =============================================================================
\r
1158 =============================================================================
\r
1161 statetype s_passcard = {PASSCARDSPR, PASSCARDSPR, think, false, false, 0, 0, 0, NULL, C_Molly, R_Draw, NULL};
\r
1164 ===========================
\r
1168 ===========================
\r
1171 void SpawnPasscard(Uint16 tileX, Uint16 tileY)
\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
1182 //============================================================================
\r
1185 ===========================
\r
1189 ===========================
\r
1192 void C_Molly(objtype *ob, objtype *hit)
\r
1194 if (hit->obclass == keenobj)
\r
1196 switch (ob->obclass)
\r
1199 playstate = ex_sandwich;
\r
1203 playstate = ex_hook;
\r
1207 playstate = ex_card;
\r
1211 playstate = ex_molly;
\r
1217 =============================================================================
\r
1221 =============================================================================
\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
1230 ===========================
\r
1234 ===========================
\r
1237 void SpawnMolly(Uint16 tileX, Uint16 tileY)
\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
1254 NewState(new, &s_molly1);
\r
1258 =============================================================================
\r
1262 =============================================================================
\r
1265 statetype s_platform = {PLATFORMSPR, PLATFORMSPR, think, false, false, 0, 0, 0, T_Platform, NULL, R_Draw, NULL};
\r
1268 ===========================
\r
1272 ===========================
\r
1275 void SpawnPlatform(Uint16 tileX, Uint16 tileY, Sint16 dir)
\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
1301 NewState(new, &s_platform);
\r
1305 ===========================
\r
1309 ===========================
\r
1312 void T_Platform(objtype *ob)
\r
1314 Uint16 newpos, newtile;
\r
1316 xtry = ob->xdir * 12 * tics;
\r
1317 ytry = ob->ydir * 12 * tics;
\r
1319 if (ob->xdir == 1)
\r
1321 newpos = ob->right + xtry;
\r
1322 newtile = CONVERT_GLOBAL_TO_TILE(newpos);
\r
1323 if (ob->tileright != newtile)
\r
1325 if (*(mapsegs[2]+mapbwidthtable[ob->tiletop]/2 + newtile) == PLATFORMBLOCK)
\r
1328 xtry = xtry - (newpos & 0xFF);
\r
1332 else if (ob->xdir == -1)
\r
1334 newpos = ob->left + xtry;
\r
1335 newtile = CONVERT_GLOBAL_TO_TILE(newpos);
\r
1336 if (ob->tileleft != newtile)
\r
1338 if (*(mapsegs[2]+mapbwidthtable[ob->tiletop]/2 + newtile) == PLATFORMBLOCK)
\r
1341 xtry = xtry + (TILEGLOBAL - (newpos & 0xFF));
\r
1345 else if (ob->ydir == 1)
\r
1347 newpos = ob->bottom + ytry;
\r
1348 newtile = CONVERT_GLOBAL_TO_TILE(newpos);
\r
1349 if (ob->tilebottom != newtile)
\r
1351 if (*(mapsegs[2]+mapbwidthtable[newtile]/2 + ob->tileleft) == PLATFORMBLOCK)
\r
1353 if (*(mapsegs[2]+mapbwidthtable[newtile-2]/2 + ob->tileleft) == PLATFORMBLOCK)
\r
1356 ob->needtoreact = true;
\r
1361 ytry = ytry - (newpos & 0xFF);
\r
1366 else if (ob->ydir == -1)
\r
1368 newpos = ob->top + ytry;
\r
1369 newtile = CONVERT_GLOBAL_TO_TILE(newpos);
\r
1370 if (ob->tiletop != newtile)
\r
1372 if (*(mapsegs[2]+mapbwidthtable[newtile]/2 + ob->tileleft) == PLATFORMBLOCK)
\r
1374 if (*(mapsegs[2]+mapbwidthtable[newtile+2]/2 + ob->tileleft) == PLATFORMBLOCK)
\r
1377 ob->needtoreact = true;
\r
1382 ytry = ytry + (TILEGLOBAL - (newpos & 0xFF));
\r
1390 =============================================================================
\r
1394 temp1 = initial y position
\r
1396 =============================================================================
\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
1404 ===========================
\r
1408 ===========================
\r
1411 void SpawnDropPlat(Uint16 tileX, Uint16 tileY)
\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
1421 new->needtoclip = cl_noclip;
\r
1422 NewState(new, &s_dropplatsit);
\r
1426 ===========================
\r
1430 ===========================
\r
1433 void T_DropPlatSit(objtype *ob)
\r
1435 if (gamestate.riding == ob)
\r
1437 ytry = tics << 4; //tics * 16;
\r
1439 if (ob->y + ytry - ob->temp1 >= 8*PIXGLOBAL)
\r
1440 ob->state = &s_fallplatfall;
\r
1445 ===========================
\r
1449 ===========================
\r
1452 void T_DropPlatFall(objtype *ob)
\r
1454 Uint16 newpos, newtile;
\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
1464 newpos = ob->bottom + ytry;
\r
1465 newtile = CONVERT_GLOBAL_TO_TILE(newpos);
\r
1466 if (ob->tilebottom != newtile)
\r
1468 if (*(mapsegs[2]+mapbwidthtable[newtile]/2 + ob->tileleft) == PLATFORMBLOCK)
\r
1470 ytry = 0xFF - (ob->bottom & 0xFF);
\r
1471 if (gamestate.riding != ob)
\r
1472 ob->state = &s_fallplatrise;
\r
1478 ===========================
\r
1482 ===========================
\r
1485 void T_DropPlatRise(objtype *ob)
\r
1487 if (gamestate.riding == ob)
\r
1490 ob->state = &s_fallplatfall;
\r
1492 else if (ob->y <= ob->temp1)
\r
1494 ytry = ob->temp1 - ob->y;
\r
1495 ob->state = &s_dropplatsit;
\r
1500 =============================================================================
\r
1504 temp1 = initial y position (is set but never used)
\r
1506 =============================================================================
\r
1509 statetype s_staticplatform = {PLATFORMSPR, PLATFORMSPR, step, false, false, 32000, 0, 0, NULL, NULL, R_Draw, &s_staticplatform};
\r
1512 ===========================
\r
1516 ===========================
\r
1519 void SpawnStaticPlat(Uint16 tileX, Uint16 tileY)
\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
1529 new->needtoclip = cl_noclip;
\r
1530 NewState(new, &s_staticplatform);
\r
1534 =============================================================================
\r
1539 temp2 = countdown to next dir check
\r
1540 temp3 = sprite pointer for the Bip sprite
\r
1542 =============================================================================
\r
1545 statetype s_goplat = {PLATFORMSPR, PLATFORMSPR, think, false, false, 0, 0, 0, T_GoPlat, NULL, R_GoPlat, NULL};
\r
1548 ===========================
\r
1552 ===========================
\r
1555 void SpawnGoPlat(Uint16 tileX, Uint16 tileY, Sint16 dir)
\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
1565 new->needtoclip = cl_noclip;
\r
1566 NewState(new, &s_goplat);
\r
1567 *(mapsegs[2]+mapbwidthtable[tileY]/2 + tileX) = dir + DIRARROWSTART;
\r
1569 new->temp2 = TILEGLOBAL;
\r
1573 ===========================
\r
1577 ===========================
\r
1580 void T_GoPlat(objtype *ob)
\r
1587 if (ob->temp2 > move)
\r
1589 ob->temp2 = ob->temp2 - move;
\r
1591 dir = pdirx[ob->temp1];
\r
1594 xtry = xtry + move;
\r
1596 else if (dir == -1)
\r
1598 xtry = xtry + -move;
\r
1601 dir = pdiry[ob->temp1];
\r
1604 ytry = ytry + move;
\r
1606 else if (dir == -1)
\r
1608 ytry = ytry + -move;
\r
1613 dir = pdirx[ob->temp1];
\r
1616 xtry += ob->temp2;
\r
1618 else if (dir == -1)
\r
1620 xtry += -ob->temp2;
\r
1623 dir = pdiry[ob->temp1];
\r
1626 ytry += ob->temp2;
\r
1628 else if (dir == -1)
\r
1630 ytry += -ob->temp2;
\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
1638 Quit("Goplat moved to a bad spot!");
\r
1641 move -= ob->temp2;
\r
1642 ob->temp2 = TILEGLOBAL - move;
\r
1644 dir = pdirx[ob->temp1];
\r
1647 xtry = xtry + move;
\r
1649 else if (dir == -1)
\r
1651 xtry = xtry - move;
\r
1654 dir = pdiry[ob->temp1];
\r
1657 ytry = ytry + move;
\r
1659 else if (dir == -1)
\r
1661 ytry = ytry - move;
\r
1667 ===========================
\r
1671 ===========================
\r
1674 void R_GoPlat(objtype *ob)
\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
1681 =============================================================================
\r
1685 temp1 = initial x position (is set but never used)
\r
1687 =============================================================================
\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
1695 ===========================
\r
1699 ===========================
\r
1702 void SpawnSneakPlat(Uint16 tileX, Uint16 tileY)
\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
1712 new->needtoclip = cl_noclip;
\r
1713 NewState(new, &s_sneakplatsit);
\r
1717 ===========================
\r
1721 ===========================
\r
1724 void T_SneakPlat(objtype *ob)
\r
1728 if (player->state != &s_keenjump1)
\r
1731 if (player->xdir == 1)
\r
1733 dist = ob->left-player->right;
\r
1734 if (dist > 4*TILEGLOBAL || dist < 0)
\r
1739 dist = player->left-ob->right;
\r
1740 if (dist > 4*TILEGLOBAL || dist < 0)
\r
1744 dist = player->y - ob->y;
\r
1745 if (dist < -6*TILEGLOBAL || dist > 6*TILEGLOBAL)
\r
1748 ob->xdir = player->xdir;
\r
1749 ob->state = &s_sneakplatdodge;
\r
1753 =============================================================================
\r
1757 =============================================================================
\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
1767 ===========================
\r
1771 ===========================
\r
1774 void SpawnBloog(Uint16 tileX, Uint16 tileY)
\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
1791 NewState(new, &s_bloogwalk1);
\r
1795 ===========================
\r
1799 ===========================
\r
1802 void T_BloogWalk(objtype *ob)
\r
1804 if (US_RndT() < 0x20)
\r
1806 if (ob->x < player->x)
\r
1818 ===========================
\r
1822 ===========================
\r
1825 void C_Bloog(objtype *ob, objtype *hit)
\r
1827 if (hit->obclass == keenobj)
\r
1831 else if (hit->obclass == stunshotobj)
\r
1833 StunObj(ob, hit, &s_bloogstun);
\r
1838 =============================================================================
\r
1842 temp1 = flash countdown
\r
1845 =============================================================================
\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
1859 ===========================
\r
1863 ===========================
\r
1866 void SpawnBlooguard(Uint16 tileX, Uint16 tileY)
\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
1883 new->temp2 = 3; // health
\r
1884 NewState(new, &s_blooguardwalk1);
\r
1888 ===========================
\r
1892 ===========================
\r
1895 void T_BlooguardWalk(objtype *ob)
\r
1897 if (US_RndT() < 0x20)
\r
1899 if (ob->x < player->x)
\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
1911 ob->state = &s_blooguardattack1;
\r
1916 ===========================
\r
1918 = T_BlooguardAttack
\r
1920 ===========================
\r
1924 void T_BlooguardAttack(objtype *ob)
\r
1926 SD_PlaySound(SND_SMASH);
\r
1928 if (player->hitnorth)
\r
1930 ChangeState(player, &s_keenstun);
\r
1935 ===========================
\r
1939 ===========================
\r
1942 void C_Blooguard(objtype *ob, objtype *hit)
\r
1944 if (hit->obclass == keenobj)
\r
1948 if (hit->obclass == stunshotobj) // not 'else if' in the original code
\r
1950 if (--ob->temp2 == 0) // handle health
\r
1952 StunObj(ob, hit, &s_blooguardstun);
\r
1956 ob->temp1 = 2; // draw white twice
\r
1957 ob->needtoreact = true;
\r
1964 ===========================
\r
1968 ===========================
\r
1971 void R_Blooguard(objtype *ob)
\r
1973 if (ob->xdir == 1 && ob->hitwest)
\r
1975 ob->x -= ob->xmove;
\r
1977 ob->nothink = US_RndT() >> 5;
\r
1978 ChangeState(ob, ob->state);
\r
1980 else if (ob->xdir == -1 && ob->hiteast)
\r
1982 ob->x -= ob->xmove;
\r
1984 ob->nothink = US_RndT() >> 5;
\r
1985 ChangeState(ob, ob->state);
\r
1987 else if (!ob->hitnorth)
\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
1997 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, maskdraw, ob->priority);
\r
2001 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);
\r
2006 =============================================================================
\r
2012 =============================================================================
\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
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
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
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
2044 ===========================
\r
2048 ===========================
\r
2051 void SpawnBlooglet(Uint16 tileX, Uint16 tileY, Sint16 type)
\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
2068 new->temp1 = type;
\r
2073 NewState(new, &s_rbloogletwalk1);
\r
2077 NewState(new, &s_ybloogletwalk1);
\r
2081 NewState(new, &s_bbloogletwalk1);
\r
2085 NewState(new, &s_gbloogletwalk1);
\r
2090 ===========================
\r
2094 ===========================
\r
2097 void C_Blooglet(objtype *ob, objtype *hit)
\r
2099 static statetype *stunnedstate[4] = {
\r
2107 if (hit->obclass == keenobj && hit->state->contact)
\r
2109 playerkludgeclipcancel = true;
\r
2110 ClipToSpriteSide(hit, ob);
\r
2111 playerkludgeclipcancel = false;
\r
2113 else if (hit->obclass == stunshotobj)
\r
2115 color = ob->temp1 & 3;
\r
2116 if (ob->temp1 > 3)
\r
2119 // spawn a key gem
\r
2122 new->needtoclip = cl_midclip;
\r
2123 new->priority = 2;
\r
2124 new->obclass = bonusobj;
\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
2135 StunObj(ob, hit, stunnedstate[color]);
\r