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
41 =============================================================================
\r
45 temp1 = step counter for running on thin air
\r
46 temp2 = low byte: running flag; high byte: flash countdown
\r
47 temp3 = sprite pointer for the question mark
\r
50 =============================================================================
\r
53 statetype s_nospikestand = {NOSPIKESTANDSPR, NOSPIKESTANDSPR, step, false, true, 90, 0, 0, NULL, C_Nospike, R_Walk, &s_nospikewalk1};
\r
54 statetype s_nospikewalk1 = {NOSPIKEWALKL1SPR, NOSPIKEWALKR1SPR, step, false, true, 10, 128, 0, T_NospikeWalk, C_Nospike, R_Walk, &s_nospikewalk2};
\r
55 statetype s_nospikewalk2 = {NOSPIKEWALKL2SPR, NOSPIKEWALKR2SPR, step, false, true, 10, 128, 0, T_NospikeWalk, C_Nospike, R_Walk, &s_nospikewalk3};
\r
56 statetype s_nospikewalk3 = {NOSPIKEWALKL3SPR, NOSPIKEWALKR3SPR, step, false, true, 10, 128, 0, T_NospikeWalk, C_Nospike, R_Walk, &s_nospikewalk4};
\r
57 statetype s_nospikewalk4 = {NOSPIKEWALKL4SPR, NOSPIKEWALKR4SPR, step, false, true, 10, 128, 0, T_NospikeWalk, C_Nospike, R_Walk, &s_nospikewalk1};
\r
58 statetype s_nospikerun1 = {NOSPIKERUNL1SPR, NOSPIKERUNR1SPR, step, false, true, 4, 128, 0, T_NospikeRun, C_Nospike, R_NospikeRun, &s_nospikerun2};
\r
59 statetype s_nospikerun2 = {NOSPIKERUNL2SPR, NOSPIKERUNR2SPR, step, false, true, 4, 128, 0, T_NospikeRun, C_Nospike, R_NospikeRun, &s_nospikerun3};
\r
60 statetype s_nospikerun3 = {NOSPIKERUNL3SPR, NOSPIKERUNR3SPR, step, false, true, 4, 128, 0, T_NospikeRun, C_Nospike, R_NospikeRun, &s_nospikerun4};
\r
61 statetype s_nospikerun4 = {NOSPIKERUNL4SPR, NOSPIKERUNR4SPR, step, false, true, 4, 128, 0, T_NospikeRun, C_Nospike, R_NospikeRun, &s_nospikerun1};
\r
62 statetype s_nospikeconfused1 = {NOSPIKESTANDSPR, NOSPIKESTANDSPR, step, false, false, 20, 0, 1, NULL, NULL, R_Draw, &s_nospikeconfused2};
\r
63 statetype s_nospikeconfused2 = {NOSPIKESTANDSPR, NOSPIKESTANDSPR, step, false, false, 90, 0, 0, T_NospikeConfused, NULL, R_NospikeConfused, &s_nospikeconfused3};
\r
64 statetype s_nospikeconfused3 = {NOSPIKESTANDSPR, NOSPIKESTANDSPR, step, false, false, 20, 0, 0, NULL, NULL, R_Draw, &s_nospikefall};
\r
65 statetype s_nospikefall = {NOSPIKESTANDSPR, NOSPIKESTANDSPR, think, false, false, 0, 0, 0, T_Projectile, NULL, R_NospikeFall, NULL};
\r
66 statetype s_nospikestun = {NOSPIKESTUNSPR, NOSPIKESTUNSPR, think, false, false, 0, 0, 0, T_Projectile, NULL, R_Stunned, &s_nospikestun};
\r
69 ===========================
\r
73 ===========================
\r
76 void SpawnNospike(Uint16 tileX, Uint16 tileY)
\r
79 new->obclass = nospikeobj;
\r
80 new->active = ac_yes;
\r
82 new->x = CONVERT_TILE_TO_GLOBAL(tileX);
\r
83 new->y = CONVERT_TILE_TO_GLOBAL(tileY) + -24*PIXGLOBAL;
\r
84 if (US_RndT() < 0x80)
\r
93 NewState(new, &s_nospikestand);
\r
94 new->temp4 = 4; // health
\r
98 ===========================
\r
102 ===========================
\r
105 void T_NospikeWalk(objtype *ob)
\r
107 if (US_RndT() < 0x10)
\r
109 ob->state = &s_nospikestand;
\r
111 else if (ob->bottom == player->bottom && US_RndT() <= 0x20)
\r
114 // start running towards player
\r
116 if (player->x > ob->x)
\r
124 ob->temp1 = 0; // nospike is still on solid ground (should already be 0 anyway)
\r
125 ob->temp2 = 1; // nospike is running
\r
126 if (ob->state == &s_nospikewalk1)
\r
128 ob->state = &s_nospikerun2;
\r
130 else if (ob->state == &s_nospikewalk2)
\r
132 ob->state = &s_nospikerun3;
\r
134 else if (ob->state == &s_nospikewalk3)
\r
136 ob->state = &s_nospikerun4;
\r
138 else if (ob->state == &s_nospikewalk4)
\r
140 ob->state = &s_nospikerun1;
\r
146 ===========================
\r
150 ===========================
\r
153 void T_NospikeRun(objtype *ob)
\r
156 return; // nospike is running on thin air, so we'd better not stop
\r
158 if ( ( ( player->bottom != ob->bottom // not on same ground level as Keen?
\r
159 || (ob->xdir == -1 && ob->x < player->x)
\r
160 || (ob->xdir == 1 && ob->x > player->x) ) // already ran past Keen?
\r
162 || !OnScreen(ob) ) // always stop running when off-screen
\r
168 if (ob->state == &s_nospikerun1)
\r
170 ob->state = &s_nospikewalk2;
\r
172 else if (ob->state == &s_nospikerun2)
\r
174 ob->state = &s_nospikewalk3;
\r
176 else if (ob->state == &s_nospikerun3)
\r
178 ob->state = &s_nospikewalk4;
\r
180 else if (ob->state == &s_nospikerun4)
\r
182 ob->state = &s_nospikewalk1;
\r
188 ===========================
\r
192 ===========================
\r
195 void C_Nospike(objtype *ob, objtype *hit)
\r
197 if (hit->obclass == keenobj)
\r
201 else if (hit->obclass == stunshotobj)
\r
203 if (--ob->temp4 == 0) // handle health
\r
205 StunObj(ob, hit, &s_nospikestun);
\r
210 if (player->x > ob->x)
\r
218 ob->temp2 |= 0x400; // draw white 4 times
\r
219 ob->needtoreact = true;
\r
220 if (ob->state == &s_nospikestand || ob->state == &s_nospikewalk1)
\r
222 ChangeState(ob, &s_nospikerun2);
\r
224 else if (ob->state == &s_nospikewalk2)
\r
226 ChangeState(ob, &s_nospikerun3);
\r
228 else if (ob->state == &s_nospikewalk3)
\r
230 ChangeState(ob, &s_nospikerun4);
\r
232 else if (ob->state == &s_nospikewalk4)
\r
234 ChangeState(ob, &s_nospikerun1);
\r
239 else if (hit->obclass == nospikeobj
\r
240 && (hit->temp2 & 0xFF) && (ob->temp2 & 0xFF) // both nospikes are running?
\r
241 && hit->xdir != ob->xdir) // running in opposite directions?
\r
244 // stun both nospikes
\r
246 ob->temp1=ob->temp2=ob->temp3=hit->temp1=hit->temp2=hit->temp3 = 0;
\r
247 ob->temp4 = hit->temp4 = ob->obclass;
\r
248 ChangeState(ob, &s_nospikestun);
\r
249 ChangeState(hit, &s_nospikestun);
\r
250 SD_PlaySound(SND_SMASH);
\r
251 ob->obclass = hit->obclass = stunnedobj;
\r
252 ob->yspeed = hit->yspeed = -24;
\r
257 ===========================
\r
259 = T_NospikeConfused
\r
261 ===========================
\r
264 void T_NospikeConfused(objtype* ob)
\r
266 RF_RemoveSprite((void**)&ob->temp3);
\r
270 ===========================
\r
272 = R_NospikeConfused
\r
274 ===========================
\r
277 void R_NospikeConfused(objtype *ob)
\r
279 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);
\r
280 RF_PlaceSprite((void**)&ob->temp3, ob->x+TILEGLOBAL, ob->y-8*PIXGLOBAL, QUESTIONMARKSPR, spritedraw, 3);
\r
284 ===========================
\r
288 ===========================
\r
291 void R_NospikeFall(objtype *ob)
\r
293 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);
\r
296 ob->temp1=ob->temp2=ob->temp3 = 0;
\r
297 ob->temp4 = ob->obclass;
\r
298 ChangeState(ob, &s_nospikestun);
\r
299 SD_PlaySound(SND_SMASH);
\r
300 ob->obclass = stunnedobj;
\r
306 ===========================
\r
310 ===========================
\r
313 void R_NospikeRun(objtype *ob)
\r
317 ob->temp1 = 0; // on solid ground
\r
318 if (ob->hiteast || ob->hitwest)
\r
320 ob->x -= ob->xdir << 7;
\r
321 NewState(ob, &s_nospikestand);
\r
322 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);
\r
323 ob->temp2 = 0; // no longer running or flashing white
\r
329 if (++ob->temp1 == 6) // not on solid ground for 6 steps?
\r
331 ChangeState(ob, &s_nospikeconfused1);
\r
334 ob->nothink = 0; // to make sure T_NospikeConfused can remove the question mark sprite
\r
338 if (ob->temp2 & 0xFF00)
\r
340 ob->temp2 -= 0x100;
\r
341 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, maskdraw, ob->priority);
\r
345 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);
\r
350 =============================================================================
\r
354 =============================================================================
\r
357 statetype s_gikwalk1 = {GIKWALKL1SPR, GIKWALKR1SPR, step, false, true, 10, 128, 0, T_GikWalk, C_ClipTop, R_Walk, &s_gikwalk2};
\r
358 statetype s_gikwalk2 = {GIKWALKL2SPR, GIKWALKR2SPR, step, false, true, 10, 128, 0, T_GikWalk, C_ClipTop, R_Walk, &s_gikwalk3};
\r
359 statetype s_gikwalk3 = {GIKWALKL3SPR, GIKWALKR3SPR, step, false, true, 10, 128, 0, T_GikWalk, C_ClipTop, R_Walk, &s_gikwalk1};
\r
360 statetype s_gikjump = {GIKJUMPLSPR, GIKJUMPRSPR, think, false, false, 0, 0, 0, T_Projectile, C_ClipSide, R_GikJump, &s_gikslide1};
\r
361 statetype s_gikslide1 = {GIKSLIDEL1SPR, GIKSLIDER1SPR, stepthink, false, false, 6, 0, 0, T_GikSlide, C_Lethal, R_GikSlide, &s_gikslide2};
\r
362 statetype s_gikslide2 = {GIKSLIDEL2SPR, GIKSLIDER2SPR, stepthink, false, false, 6, 0, 0, T_GikSlide, C_Lethal, R_GikSlide, &s_gikslide1};
\r
363 statetype s_gikstand = {GIKSLIDEL1SPR, GIKSLIDER1SPR, step, false, true, 20, 0, 0, NULL, C_Lethal, R_Walk, &s_gikwalk1};
\r
366 ===========================
\r
370 ===========================
\r
373 void SpawnGik(Uint16 tileX, Uint16 tileY)
\r
376 new->obclass = gikobj;
\r
377 new->active = ac_yes;
\r
379 new->x = CONVERT_TILE_TO_GLOBAL(tileX);
\r
380 new->y = CONVERT_TILE_TO_GLOBAL(tileY);
\r
381 if (US_RndT() < 0x80)
\r
390 NewState(new, &s_gikwalk1);
\r
394 ===========================
\r
398 ===========================
\r
401 void T_GikWalk(objtype *ob)
\r
405 if (ob->hitnorth != 9) // if NOT on flat ground that kills Keen
\r
407 xdist = player->x - ob->x;
\r
408 if (player->bottom <= ob->bottom && ob->bottom - player->bottom <= 4*TILEGLOBAL)
\r
418 if (xdist >= -7*TILEGLOBAL && xdist <= 7*TILEGLOBAL
\r
419 && (xdist <= -TILEGLOBAL || xdist >= TILEGLOBAL) )
\r
430 ob->state = &s_gikjump;
\r
431 SD_PlaySound(SND_GIKJUMP);
\r
438 ===========================
\r
442 ===========================
\r
445 void T_GikSlide(objtype *ob)
\r
447 // tic masks for friction, based on slope type and direction
\r
449 // 7 - lowest friction (speed decreases every 8 tics)
\r
450 // 3 - medium friction (speed decreases every 4 tics)
\r
451 // 1 - highest friction (speed decreases every 2 tics)
\r
452 static Sint16 rticmask[8] = {0, 7, 0, 0, 0, 3, 3, 1};
\r
453 static Sint16 lticmask[8] = {0, 7, 3, 3, 1, 0, 0, 0};
\r
461 slope = ob->hitnorth & 7;
\r
464 ticmask = rticmask[slope];
\r
468 ticmask = lticmask[slope];
\r
471 if (ob->xspeed == 0 && ob->hitnorth)
\r
473 ob->state = &s_gikstand;
\r
477 for (i = lasttimecount-tics; i < lasttimecount; i++)
\r
479 if (ticmask && !(i & ticmask))
\r
481 if ((ob->xspeed < 0 && ++ob->xspeed == 0)
\r
482 || (ob-> xspeed > 0 && --ob->xspeed == 0))
\r
484 ob->state = &s_gikstand;
\r
488 xtry += ob->xspeed;
\r
494 ===========================
\r
498 ===========================
\r
501 void R_GikJump(objtype *ob)
\r
503 if (ob->hiteast || ob->hitwest)
\r
512 SD_PlaySound(SND_GIKLAND);
\r
513 ChangeState(ob, ob->state->nextstate);
\r
515 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);
\r
519 ===========================
\r
523 ===========================
\r
526 void R_GikSlide(objtype *ob)
\r
528 if ((ob->hiteast && ob->xspeed < 0) || (ob->hitwest && ob->xspeed > 0))
\r
531 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);
\r
535 =============================================================================
\r
541 =============================================================================
\r
544 statetype s_cannon = {0, 0, step, false, false, 120, 0, 0, NULL, NULL, R_Draw, &s_cannonfire};
\r
545 statetype s_cannonfire = {0, 0, step, true, false, 1, 0, 0, T_Cannon, NULL, R_Draw, &s_cannon};
\r
546 statetype s_cshot1 = {LASER1SPR, LASER1SPR, stepthink, false, false, 8, 0, 0, T_Velocity, C_CShot, R_CShot, &s_cshot2};
\r
547 statetype s_cshot2 = {LASER2SPR, LASER2SPR, stepthink, false, false, 8, 0, 0, T_Velocity, C_CShot, R_CShot, &s_cshot3};
\r
548 statetype s_cshot3 = {LASER3SPR, LASER3SPR, stepthink, false, false, 8, 0, 0, T_Velocity, C_CShot, R_CShot, &s_cshot4};
\r
549 statetype s_cshot4 = {LASER4SPR, LASER4SPR, stepthink, false, false, 8, 0, 0, T_Velocity, C_CShot, R_CShot, &s_cshot1};
\r
550 statetype s_cshothit1 = {LASERHIT1SPR, LASERHIT1SPR, step, false, false, 10, 0, 0, NULL, NULL, R_Draw, &s_cshothit2};
\r
551 statetype s_cshothit2 = {LASERHIT2SPR, LASERHIT2SPR, step, false, false, 10, 0, 0, NULL, NULL, R_Draw, NULL};
\r
554 ===========================
\r
558 ===========================
\r
561 void SpawnCannon(Uint16 tileX, Uint16 tileY, Sint16 dir)
\r
564 new->obclass = cannonobj;
\r
565 new->active = ac_yes;
\r
566 new->tileright = new->tileleft = tileX;
\r
567 new->tiletop = new->tilebottom = tileY;
\r
568 new->x = new->left = new->right = CONVERT_TILE_TO_GLOBAL(tileX);
\r
569 new->y = new->top = new->bottom = CONVERT_TILE_TO_GLOBAL(tileY);
\r
571 NewState(new, &s_cannon);
\r
575 ===========================
\r
579 ===========================
\r
582 void T_Cannon(objtype *ob)
\r
585 new->obclass = mshotobj;
\r
586 new->active = ac_yes; // BUG? NOT removable in Keen 6 (checked v1.0, v1.4 and v1.5)
\r
603 NewState(new, &s_cshot1);
\r
604 SD_PlaySound(SND_ENEMYSHOT);
\r
608 ===========================
\r
612 ===========================
\r
615 void C_CShot(objtype *ob, objtype *hit)
\r
617 if (hit->obclass == keenobj)
\r
620 ChangeState(ob, &s_cshothit1);
\r
625 ===========================
\r
629 ===========================
\r
632 void R_CShot(objtype *ob)
\r
634 if (ob->hitnorth || ob->hiteast || ob->hitsouth || ob->hitwest)
\r
636 SD_PlaySound(SND_ENEMYSHOTEXPLODE);
\r
637 ChangeState(ob, &s_cshothit1);
\r
639 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);
\r
643 =============================================================================
\r
647 temp1 = bounce counter
\r
648 temp2 = amount to move up during uncurl animation
\r
649 temp3 = float offset
\r
650 temp4 = float direction (up or down)
\r
652 =============================================================================
\r
655 statetype s_orbatrix1 = {ORBATRIXL1SPR, ORBATRIXR1SPR, slide, false, true, 12, 16, 0, T_OrbatrixFly, C_Orbatrix, R_Orbatrix, &s_orbatrix2};
\r
656 statetype s_orbatrix2 = {ORBATRIXL2SPR, ORBATRIXR2SPR, slide, false, true, 12, 16, 0, T_OrbatrixFly, C_Orbatrix, R_Orbatrix, &s_orbatrix1};
\r
657 statetype s_orbatrixcurl1 = {ORBATRIX1SPR, ORBATRIX1SPR, stepthink, false, true, 12, 0, 0, NULL, C_Orbatrix, R_Orbatrix, &s_orbatrixcurl2};
\r
658 statetype s_orbatrixcurl2 = {ORBATRIXCURLSPR, ORBATRIXCURLSPR, stepthink, false, true, 12, 0, 0, NULL, C_Orbatrix, R_Orbatrix, &s_orbatrixcurl3};
\r
659 statetype s_orbatrixcurl3 = {ORBATRIXCURLSPR, ORBATRIXCURLSPR, think, false, true, 12, 0, 0, T_OrbatrixCurl, C_Orbatrix, R_Orbatrix, &s_orbatrixbounce1};
\r
660 statetype s_orbatrixuncurl1 = {ORBATRIXSPIN1SPR, ORBATRIXSPIN1SPR, think, false, false, 12, 0, 0, T_OrbatrixUncurl, C_OrbatrixBounce, R_Draw, &s_orbatrixuncurl2};
\r
661 statetype s_orbatrixuncurl2 = {ORBATRIXCURLSPR, ORBATRIXCURLSPR, step, false, false, 12, 0, 0, NULL, C_OrbatrixBounce, R_Draw, &s_orbatrixidle1};
\r
662 statetype s_orbatrixidle1 = {ORBATRIX1SPR, ORBATRIX1SPR, stepthink, false, true, 12, 0, 0, NULL, C_Orbatrix, R_Orbatrix, &s_orbatrixidle2};
\r
663 statetype s_orbatrixidle2 = {ORBATRIX2SPR, ORBATRIX2SPR, stepthink, false, true, 12, 0, 0, NULL, C_Orbatrix, R_Orbatrix, &s_orbatrixidle3};
\r
664 statetype s_orbatrixidle3 = {ORBATRIX3SPR, ORBATRIX3SPR, stepthink, false, true, 12, 0, 0, NULL, C_Orbatrix, R_Orbatrix, &s_orbatrixidle4};
\r
665 statetype s_orbatrixidle4 = {ORBATRIX4SPR, ORBATRIX4SPR, stepthink, false, true, 12, 0, 0, NULL, C_Orbatrix, R_Orbatrix, &s_orbatrix1};
\r
666 statetype s_orbatrixbounce1 = {ORBATRIXSPIN4SPR, ORBATRIXSPIN1SPR, stepthink, false, false, 6, 0, 0, T_Projectile, C_OrbatrixBounce, R_OrbatrixBounce, &s_orbatrixbounce2};
\r
667 statetype s_orbatrixbounce2 = {ORBATRIXSPIN3SPR, ORBATRIXSPIN2SPR, stepthink, false, false, 6, 0, 0, T_Projectile, C_OrbatrixBounce, R_OrbatrixBounce, &s_orbatrixbounce3};
\r
668 statetype s_orbatrixbounce3 = {ORBATRIXSPIN2SPR, ORBATRIXSPIN3SPR, stepthink, false, false, 6, 0, 0, T_Projectile, C_OrbatrixBounce, R_OrbatrixBounce, &s_orbatrixbounce4};
\r
669 statetype s_orbatrixbounce4 = {ORBATRIXSPIN1SPR, ORBATRIXSPIN4SPR, stepthink, false, false, 6, 0, 0, T_Projectile, C_OrbatrixBounce, R_OrbatrixBounce, &s_orbatrixbounce1};
\r
672 ===========================
\r
676 ===========================
\r
679 void SpawnOrbatrix(Uint16 tileX, Uint16 tileY)
\r
682 new->obclass = orbatrixobj;
\r
683 new->active = ac_yes;
\r
685 new->x = CONVERT_TILE_TO_GLOBAL(tileX);
\r
686 new->y = CONVERT_TILE_TO_GLOBAL(tileY) + -24*PIXGLOBAL;
\r
687 if (US_RndT() < 0x80)
\r
697 NewState(new, &s_orbatrix1);
\r
701 ===========================
\r
705 ===========================
\r
708 void T_OrbatrixFly(objtype *ob)
\r
712 if (US_RndT() < 0x20)
\r
714 ob->state = &s_orbatrixidle1;
\r
718 if (ob->bottom != player->bottom)
\r
723 dist = player->x - ob->x;
\r
724 ob->xdir = (dist < 0)? -1 : 1;
\r
725 if (dist > -5*TILEGLOBAL && dist < 5*TILEGLOBAL)
\r
727 ob->state = &s_orbatrixcurl1;
\r
732 ===========================
\r
736 ===========================
\r
739 void C_Orbatrix(objtype *ob, objtype *hit)
\r
741 if (hit->obclass == stunshotobj)
\r
744 ChangeState(ob, &s_orbatrixidle1);
\r
749 ===========================
\r
753 ===========================
\r
756 void R_Orbatrix(objtype *ob)
\r
759 // ugly hack: apply float offset before drawing the sprite
\r
760 // (it's ugly because the sprite moves up/down, but the hitbox doesn't)
\r
762 ob->y -= ob->temp3;
\r
764 ob->y += ob->temp3;
\r
767 // update the float offset
\r
769 ob->temp3 = ob->temp3 + ob->temp4 * tics * 4;
\r
770 if (ob->temp3 > 8*PIXGLOBAL)
\r
772 ob->temp3 = 8*PIXGLOBAL;
\r
775 else if (ob->temp3 < -8*PIXGLOBAL)
\r
777 ob->temp3 = -8*PIXGLOBAL;
\r
783 ===========================
\r
787 ===========================
\r
790 void R_OrbatrixBounce(objtype *ob)
\r
792 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);
\r
796 ob->yspeed = -ob->yspeed;
\r
798 if (ob->hitnorth || ob->hitwest || ob->hiteast)
\r
800 ob->xspeed = -ob->xspeed;
\r
801 SD_PlaySound(SND_ORBATRIXBOUNCE);
\r
802 if (ob->hitnorth && --ob->temp1 == 0)
\r
804 ChangeState(ob, &s_orbatrixuncurl1);
\r
805 ob->temp2 = 24*PIXGLOBAL;
\r
811 ===========================
\r
815 ===========================
\r
818 void T_OrbatrixCurl(objtype *ob)
\r
820 if (ob->temp3 >= 16)
\r
822 ob->xspeed = ob->xdir * 60;
\r
824 ob->y -= ob->temp3;
\r
825 ob->temp1 = 5; // bounce 5 times
\r
826 ob->state = ob->state->nextstate;
\r
828 ob->needtoreact = true;
\r
832 ===========================
\r
836 ===========================
\r
839 void T_OrbatrixUncurl(objtype *ob)
\r
841 ob->temp2 += (ytry = tics * -8);
\r
842 if (ob->temp2 <= 0)
\r
845 ob->state = ob->state->nextstate;
\r
850 ===========================
\r
854 ===========================
\r
857 void C_OrbatrixBounce(objtype *ob, objtype *hit)
\r
859 if (hit->obclass == keenobj)
\r
863 else if (hit->obclass == stunshotobj)
\r
871 =============================================================================
\r
875 =============================================================================
\r
878 statetype s_bipstand = {BIPSTANDSPR, BIPSTANDSPR, step, false, true, 30, 0, 0, NULL, C_Bip, R_Walk, &s_bipwalk1};
\r
879 statetype s_bipwalk1 = {BIPWALKL1SPR, BIPWALKR1SPR, step, true, true, 4, 32, 0, T_BipWalk, C_Bip, R_Walk, &s_bipwalk2};
\r
880 statetype s_bipwalk2 = {BIPWALKL2SPR, BIPWALKR2SPR, step, true, true, 4, 32, 0, T_BipWalk, C_Bip, R_Walk, &s_bipwalk3};
\r
881 statetype s_bipwalk3 = {BIPWALKL3SPR, BIPWALKR3SPR, step, true, true, 4, 32, 0, T_BipWalk, C_Bip, R_Walk, &s_bipwalk4};
\r
882 statetype s_bipwalk4 = {BIPWALKL4SPR, BIPWALKR4SPR, step, true, true, 4, 32, 0, T_BipWalk, C_Bip, R_Walk, &s_bipwalk1};
\r
883 statetype s_bipsquished = {BIPSQUISHEDSPR, BIPSQUISHEDSPR, think, false, true, 0, 0, 0, T_Projectile, NULL, R_Draw, NULL};
\r
886 ===========================
\r
890 ===========================
\r
893 void T_BipWalk(objtype *ob)
\r
895 if (ob->bottom == player->bottom)
\r
897 if (ob->right < player->left - 4*PIXGLOBAL)
\r
900 if (ob->left > player->right + 4*PIXGLOBAL)
\r
903 else if (US_RndT() < 0x10)
\r
905 ob->xdir = -ob->xdir;
\r
906 ob->state = &s_bipstand;
\r
911 ===========================
\r
915 ===========================
\r
918 void C_Bip(objtype *ob, objtype *hit)
\r
920 if (hit->obclass == keenobj && hit->ymove > 0)
\r
922 SD_PlaySound(SND_BIPSQUISH);
\r
923 ob->obclass = inertobj;
\r
924 ChangeState(ob, &s_bipsquished);
\r
929 =============================================================================
\r
933 =============================================================================
\r
936 statetype s_bipship = {BIPSHIPLSPR, BIPSHIPRSPR, think, false, true, 0, 0, 0, T_BipshipFly, C_Bipship, R_Draw, &s_bipship};
\r
937 statetype s_bipshipshot = {BIPSHIPSHOTSPR, BIPSHIPSHOTSPR, think, false, false, 0, 0, 0, T_Velocity, C_Lethal, R_BipShot, NULL};
\r
938 statetype s_bipshipturn1 = {BIPSHIPRTURN1SPR, BIPSHIPLTURN1SPR, stepthink, false, true, 10, 0, 0, T_BipshipTurn, C_Bipship, R_Draw, &s_bipshipturn2};
\r
939 statetype s_bipshipturn2 = {BIPSHIPRTURN2SPR, BIPSHIPLTURN2SPR, stepthink, false, true, 10, 0, 0, T_BipshipTurn, C_Bipship, R_Draw, &s_bipshipturn3};
\r
940 statetype s_bipshipturn3 = {BIPSHIPRTURN3SPR, BIPSHIPLTURN3SPR, stepthink, false, true, 10, 0, 0, T_BipshipTurn, C_Bipship, R_Draw, &s_bipshipturn4};
\r
941 statetype s_bipshipturn4 = {BIPSHIPRTURN4SPR, BIPSHIPLTURN4SPR, stepthink, false, true, 10, 0, 0, T_BipshipTurn, C_Bipship, R_Draw, &s_bipship};
\r
942 statetype s_bipshipexplode1 = {BIPSHIPEXPLODE2SPR, BIPSHIPEXPLODE1SPR, think, false, false, 0, 0, 0, T_Projectile, NULL, R_Land, &s_bipshipexplode2};
\r
943 statetype s_bipshipexplode2 = {BIPSHIPEXPLODE2SPR, BIPSHIPEXPLODE1SPR, step, true, false, 1, 0, 0, T_BipshipExplode, NULL, R_Land, &s_bipshipexplode3};
\r
944 statetype s_bipshipexplode3 = {BIPSHIPEXPLODE5SPR, BIPSHIPEXPLODE5SPR, step, true, false, 30000, 0, 0, NULL, NULL, R_Land, &s_bipshipexplode3};
\r
945 statetype s_bipshipsmoke1 = {BIPSHIPEXPLODE3SPR, BIPSHIPEXPLODE3SPR, step, true, false, 10, 0, 0, NULL, NULL, R_Draw, &s_bipshipsmoke2};
\r
946 statetype s_bipshipsmoke2 = {BIPSHIPEXPLODE4SPR, BIPSHIPEXPLODE4SPR, step, true, false, 10, 0, 0, NULL, NULL, R_Draw, NULL};
\r
949 ===========================
\r
953 ===========================
\r
956 void SpawnBipship(Uint16 tileX, Uint16 tileY)
\r
959 new->obclass = bipshipobj;
\r
960 new->active = ac_yes;
\r
961 new->x = CONVERT_TILE_TO_GLOBAL(tileX);
\r
962 new->y = CONVERT_TILE_TO_GLOBAL(tileY)+ -24*PIXGLOBAL;
\r
963 if (US_RndT() < 0x80)
\r
971 new->xspeed = new->xdir * 20;
\r
972 NewState(new, &s_bipship);
\r
976 ===========================
\r
980 ===========================
\r
983 void R_BipShot(objtype *ob)
\r
985 if (ob->hitnorth || ob->hitsouth || ob->hiteast || ob->hitwest)
\r
991 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);
\r
996 ===========================
\r
1000 ===========================
\r
1003 void T_BipshipTurn(objtype *ob)
\r
1005 AccelerateX(ob, ob->xdir, 20);
\r
1009 ===========================
\r
1013 ===========================
\r
1016 void T_BipshipFly(objtype *ob)
\r
1020 Uint16 tile, tx, ty;
\r
1022 AccelerateX(ob, ob->xdir, 20);
\r
1024 if (player->bottom + TILEGLOBAL - ob->bottom <= 2*TILEGLOBAL)
\r
1026 if (player->x < ob->x)
\r
1034 if (ob->xdir == dir && US_RndT() < tics*4)
\r
1036 SD_PlaySound(SND_KEENFIRE);
\r
1038 new->obclass = mshotobj;
\r
1039 new->active = ac_removable;
\r
1040 new->priority = 1;
\r
1041 if (ob->xdir == 1)
\r
1043 new->x = ob->x + TILEGLOBAL;
\r
1049 new->xspeed = -64;
\r
1051 new->y = ob->y + 10*PIXGLOBAL;
\r
1053 NewState(new, &s_bipshipshot);
\r
1057 tx = ob->tilemidx + dir*4;
\r
1058 map = mapsegs[1] + mapbwidthtable[ob->tiletop]/2 + tx;
\r
1060 for (ty = ob->tiletop; ty <= ob->tilebottom; ty++, map += mapwidth)
\r
1063 if (tinf[tile+EASTWALL] || tinf[tile+WESTWALL])
\r
1070 if (!tinf[tile+NORTHWALL])
\r
1075 if (dir != ob->xdir)
\r
1078 ChangeState(ob, &s_bipshipturn1);
\r
1083 ===========================
\r
1085 = T_BipshipExplode
\r
1087 ===========================
\r
1090 void T_BipshipExplode(objtype *ob)
\r
1092 SD_PlaySound(SND_BIPSHIPEXPLODE);
\r
1095 new->obclass = inertobj;
\r
1096 new->active = ac_yes;
\r
1097 new->priority = 2;
\r
1099 new->y = ob->y - 24*PIXGLOBAL;
\r
1100 NewState(new, &s_bipshipsmoke1);
\r
1103 new->obclass = bipobj;
\r
1104 new->active = ac_yes;
\r
1105 new->priority = 0;
\r
1107 new->y = ob->y - 8*PIXGLOBAL;
\r
1108 if (US_RndT() < 0x80)
\r
1116 NewState(new, &s_bipstand);
\r
1120 ===========================
\r
1124 ===========================
\r
1127 void C_Bipship(objtype *ob, objtype *hit)
\r
1129 if (hit->obclass == stunshotobj)
\r
1132 ChangeState(ob, &s_bipshipexplode1);
\r
1137 =============================================================================
\r
1141 =============================================================================
\r
1144 statetype s_flectstand = {FLECTSTANDLSPR, FLECTSTANDRSPR, think, false, true, 60, 0, 0, T_FlectStand, C_Flect, R_Flect, &s_flectwalk1};
\r
1145 statetype s_flectturn = {FLECTSTANDSPR, FLECTSTANDSPR, step, false, true, 8, 0, 0, NULL, C_Flect, R_Flect, &s_flectwalk1};
\r
1146 statetype s_flectwalk1 = {FLECTWALKL1SPR, FLECTWALKR1SPR, step, false, true, 10, 128, 0, T_FlectWalk, C_Flect, R_Flect, &s_flectwalk2};
\r
1147 statetype s_flectwalk2 = {FLECTWALKL2SPR, FLECTWALKR2SPR, step, false, true, 10, 128, 0, T_FlectWalk, C_Flect, R_Flect, &s_flectwalk3};
\r
1148 statetype s_flectwalk3 = {FLECTWALKL3SPR, FLECTWALKR3SPR, step, false, true, 10, 128, 0, T_FlectWalk, C_Flect, R_Flect, &s_flectwalk4};
\r
1149 statetype s_flectwalk4 = {FLECTWALKL4SPR, FLECTWALKR4SPR, step, false, true, 10, 128, 0, T_FlectWalk, C_Flect, R_Flect, &s_flectwalk1};
\r
1150 statetype s_flectstun = {FLECTSTUNSPR, FLECTSTUNSPR, think, false, false, 0, 0, 0, T_Projectile, NULL, R_Stunned, &s_flectstun};
\r
1153 ===========================
\r
1157 ===========================
\r
1160 void SpawnFlect(Uint16 tileX, Uint16 tileY)
\r
1163 new->obclass = flectobj;
\r
1164 new->active = ac_yes;
\r
1165 new->priority = 0;
\r
1166 new->x = CONVERT_TILE_TO_GLOBAL(tileX);
\r
1167 new->y = CONVERT_TILE_TO_GLOBAL(tileY) + -1*TILEGLOBAL;
\r
1168 if (US_RndT() < 0x80)
\r
1177 NewState(new, &s_flectwalk1);
\r
1181 ===========================
\r
1185 ===========================
\r
1188 void T_FlectStand(objtype *ob)
\r
1190 if (player->x < ob->x)
\r
1192 if (ob->xdir != -1)
\r
1194 ob->state = &s_flectturn;
\r
1199 ob->state = &s_flectwalk1;
\r
1204 if (ob->xdir != 1)
\r
1206 ob->state = &s_flectturn;
\r
1211 ob->state = &s_flectwalk1;
\r
1217 ===========================
\r
1221 ===========================
\r
1224 void T_FlectWalk(objtype *ob)
\r
1226 if (player->x < ob->x && ob->xdir == 1)
\r
1228 if (ob->xdir != -1) // always true here!
\r
1230 ob->state = &s_flectturn;
\r
1235 if (player->x > ob->x && ob->xdir == -1)
\r
1237 if (ob->xdir != 1) // always true here!
\r
1239 ob->state = &s_flectturn;
\r
1244 if (US_RndT() < 0x20)
\r
1246 ob->state = &s_flectstand;
\r
1251 ===========================
\r
1255 ===========================
\r
1258 void C_Flect(objtype *ob, objtype *hit)
\r
1260 if (hit->obclass == keenobj)
\r
1262 ClipToSpriteSide(hit, ob);
\r
1264 else if (hit->obclass == stunshotobj)
\r
1266 if (hit->xdir == 0)
\r
1268 StunObj(ob, hit, &s_flectstun);
\r
1270 else if (hit->xdir != ob->xdir)
\r
1273 hit->xdir = ob->xdir;
\r
1274 hit->temp4 = true; // shot can now stun Keen
\r
1275 SD_PlaySound(SND_SHOTBOUNCE);
\r
1281 ===========================
\r
1285 ===========================
\r
1288 void R_Flect(objtype *ob)
\r
1290 if (ob->xdir == 1 && ob->hitwest)
\r
1292 ob->x -= ob->xmove;
\r
1294 ob->nothink = US_RndT() >> 5;
\r
1295 ChangeState(ob, ob->state);
\r
1297 else if (ob->xdir == -1 && ob->hiteast)
\r
1299 ob->x -= ob->xmove;
\r
1301 ob->nothink = US_RndT() >> 5;
\r
1302 ChangeState(ob, ob->state);
\r
1304 else if (!ob->hitnorth)
\r
1306 ob->x -= ob->xmove;
\r
1307 ob->xdir = -ob->xdir;
\r
1308 ChangeState(ob, ob->state);
\r
1310 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);
\r