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
30 - Thundercloud & Lightning
\r
31 - Berkeloid & Fireball
\r
32 - Inchworm & Foot (in-level)
\r
43 =============================================================================
\r
47 =============================================================================
\r
50 statetype s_worm = {WORMOUTHSPR, WORMOUTHSPR, slide, true, true, 4, 16, 0, T_Worm, NULL, R_Walk, &s_worm};
\r
51 statetype s_wormpeek1 = {WORMOUTHPEEKL1SPR, WORMOUTHPEEKL1SPR, step, false, false, 20, 0, 0, NULL, C_Worm, R_Draw, &s_wormpeek2};
\r
52 statetype s_wormpeek2 = {WORMOUTHPEEKL2SPR, WORMOUTHPEEKL2SPR, step, false, false, 8, 0, 0, NULL, C_Worm, R_Draw, &s_wormpeek3};
\r
53 statetype s_wormpeek3 = {WORMOUTHPEEKL1SPR, WORMOUTHPEEKL1SPR, step, false, false, 20, 0, 0, T_WormLookLeft, C_Worm, R_Draw, &s_wormpeek4};
\r
54 statetype s_wormpeek4 = {WORMOUTHSPR, WORMOUTHSPR, step, false, false, 8, 0, 0, NULL, C_Worm, R_Draw, &s_wormpeek5};
\r
55 statetype s_wormpeek5 = {WORMOUTHPEEKR1SPR, WORMOUTHPEEKR1SPR, step, false, false, 20, 0, 0, NULL, C_Worm, R_Draw, &s_wormpeek6};
\r
56 statetype s_wormpeek6 = {WORMOUTHPEEKR2SPR, WORMOUTHPEEKR2SPR, step, false, false, 8, 0, 0, NULL, C_Worm, R_Draw, &s_wormpeek7};
\r
57 statetype s_wormpeek7 = {WORMOUTHPEEKR1SPR, WORMOUTHPEEKR1SPR, step, false, false, 20, 0, 0, T_WormLookRight, C_Worm, R_Draw, &s_wormpeek8};
\r
58 statetype s_wormpeek8 = {WORMOUTHSPR, WORMOUTHSPR, step, false, false, 8, 0, 0, T_WormLook, NULL, R_Draw, &s_worm};
\r
59 statetype s_wormbite1 = {WORMOUTHBITEL1SPR, WORMOUTHBITER1SPR, step, false, false, 8, 0, 0, NULL, C_WormKill, R_Draw, &s_wormbite2};
\r
60 statetype s_wormbite2 = {WORMOUTHBITEL2SPR, WORMOUTHBITER2SPR, step, false, false, 8, 0, 0, NULL, C_WormKill, R_Draw, &s_wormbite3};
\r
61 statetype s_wormbite3 = {WORMOUTHBITEL3SPR, WORMOUTHBITER3SPR, step, false, false, 16, 0, 0, NULL, C_WormKill, R_Draw, &s_wormbite4};
\r
62 statetype s_wormbite4 = {WORMOUTHBITEL2SPR, WORMOUTHBITER2SPR, step, false, false, 8, 0, 0, NULL, C_WormKill, R_Draw, &s_wormbite5};
\r
63 statetype s_wormbite5 = {WORMOUTHBITEL1SPR, WORMOUTHBITER1SPR, step, false, false, 8, 0, 0, NULL, C_WormKill, R_Draw, &s_worm};
\r
64 statetype s_wormstun = {WORMOUTHSTUNSPR, WORMOUTHSTUNSPR, think, false, false, 10, 0, 0, T_Projectile, NULL, R_Stunned, NULL};
\r
67 ===========================
\r
71 ===========================
\r
74 void SpawnWormMouth(Sint16 x, Sint16 y)
\r
77 new->obclass = wormouthobj;
\r
78 new->active = ac_yes;
\r
80 new->x = CONVERT_TILE_TO_GLOBAL(x);
\r
81 new->y = CONVERT_TILE_TO_GLOBAL(y) + 0x8F;
\r
82 if (US_RndT() < 0x80)
\r
91 NewState(new, &s_worm);
\r
95 ===========================
\r
99 ===========================
\r
102 void T_WormLookRight(objtype *ob)
\r
104 if (player->x > ob->x)
\r
107 ob->state = &s_worm;
\r
112 ===========================
\r
116 ===========================
\r
119 void T_WormLook(objtype *ob)
\r
121 if (player->x > ob->x)
\r
132 ===========================
\r
136 ===========================
\r
139 void T_WormLookLeft(objtype *ob)
\r
141 if (player->x < ob->x)
\r
144 ob->state = &s_worm;
\r
149 ===========================
\r
153 ===========================
\r
156 void T_Worm(objtype *ob)
\r
158 Sint16 xdist, ydist;
\r
160 xdist = player->x - ob->x;
\r
161 ydist = player->bottom - ob->bottom;
\r
162 if ((xdist < -3*TILEGLOBAL || xdist > 3*TILEGLOBAL) && US_RndT() < 6)
\r
164 ob->state = &s_wormpeek1;
\r
166 else if (ydist >= -TILEGLOBAL && ydist <= TILEGLOBAL)
\r
168 if (ob->xdir == 1 && xdist > 8*PIXGLOBAL && xdist < 24*PIXGLOBAL
\r
169 || ob->xdir == -1 && xdist < -8*PIXGLOBAL && xdist > -32*PIXGLOBAL)
\r
171 SD_PlaySound(SND_WORMOUTHATTACK);
\r
172 ob->state = &s_wormbite1;
\r
178 ===========================
\r
182 ===========================
\r
185 void C_Worm(objtype *ob, objtype *hit)
\r
187 if (hit->obclass == stunshotobj)
\r
189 StunObj(ob, hit, &s_wormstun);
\r
194 ===========================
\r
198 ===========================
\r
201 void C_WormKill(objtype *ob, objtype *hit)
\r
203 if (hit->obclass == keenobj)
\r
205 if (!(ob->xdir == 1 && ob->x > hit->x || ob->xdir == -1 && ob->x < hit->x))
\r
218 =============================================================================
\r
222 =============================================================================
\r
225 statetype s_cloudsleep = {CLOUDSPR, CLOUDSPR, think, false, false, 20, 0, 0, NULL, C_CloudSleep, R_Draw, NULL};
\r
226 statetype s_cloudwake = {CLOUDACTIVESPR, CLOUDACTIVESPR, step, false, false, 100, 0, 0, NULL, NULL, R_Cloud, &s_cloud};
\r
227 statetype s_cloud = {CLOUDACTIVESPR, CLOUDACTIVESPR, think, false, false, 20, 0, 0, T_Cloud, NULL, R_Cloud, NULL};
\r
228 statetype s_cloudalign = {CLOUDACTIVESPR, CLOUDACTIVESPR, think, false, false, 20, 0, 0, T_CloudAlign, NULL, R_Cloud, NULL};
\r
229 statetype s_cloudcharge = {CLOUDACTIVESPR, CLOUDACTIVESPR, stepthink, false, false, 60, 0, 0, T_Velocity, NULL, R_Cloud, &s_cloud};
\r
230 statetype s_cloudattack1 = {CLOUDCHARGESPR, CLOUDCHARGESPR, step, false, false, 10, 0, 0, NULL, NULL, R_Draw, &s_cloudattack2};
\r
231 statetype s_cloudattack2 = {CLOUDACTIVESPR, CLOUDACTIVESPR, step, false, false, 10, 0, 0, NULL, NULL, R_Draw, &s_cloudattack3};
\r
232 statetype s_cloudattack3 = {CLOUDCHARGESPR, CLOUDCHARGESPR, step, false, false, 10, 0, 0, NULL, NULL, R_Draw, &s_cloudattack4};
\r
233 statetype s_cloudattack4 = {CLOUDACTIVESPR, CLOUDACTIVESPR, step, false, false, 10, 0, 0, T_CloudShoot, NULL, R_Draw, &s_cloudattack5};
\r
234 statetype s_cloudattack5 = {CLOUDCHARGESPR, CLOUDCHARGESPR, step, false, false, 10, 0, 0, NULL, NULL, R_Draw, &s_cloudattack6};
\r
235 statetype s_cloudattack6 = {CLOUDACTIVESPR, CLOUDACTIVESPR, step, false, false, 10, 0, 0, NULL, NULL, R_Draw, &s_cloudattack7};
\r
236 statetype s_cloudattack7 = {CLOUDCHARGESPR, CLOUDCHARGESPR, step, false, false, 10, 0, 0, NULL, NULL, R_Draw, &s_cloudattack8};
\r
237 statetype s_cloudattack8 = {CLOUDACTIVESPR, CLOUDACTIVESPR, step, false, false, 10, 0, 0, NULL, NULL, R_Draw, &s_cloudattack9};
\r
238 statetype s_cloudattack9 = {CLOUDCHARGESPR, CLOUDCHARGESPR, step, false, false, 48, 0, 0, NULL, NULL, R_Draw, &s_cloudcharge};
\r
240 statetype s_bolt1 = {BOLT1SPR, BOLT1SPR, step, false, false, 8, 0, 0, NULL, C_Lethal, R_Draw, &s_bolt2};
\r
241 statetype s_bolt2 = {BOLT2SPR, BOLT2SPR, step, false, false, 8, 0, 0, NULL, C_Lethal, R_Draw, &s_bolt3};
\r
242 statetype s_bolt3 = {BOLT1SPR, BOLT1SPR, step, false, false, 8, 0, 0, NULL, C_Lethal, R_Draw, &s_bolt4};
\r
243 statetype s_bolt4 = {BOLT2SPR, BOLT2SPR, step, false, false, 8, 0, 0, NULL, C_Lethal, R_Draw, &s_bolt5};
\r
244 statetype s_bolt5 = {BOLT1SPR, BOLT1SPR, step, false, false, 8, 0, 0, NULL, C_Lethal, R_Draw, &s_bolt6};
\r
245 statetype s_bolt6 = {BOLT2SPR, BOLT2SPR, step, false, false, 8, 0, 0, NULL, C_Lethal, R_Draw, NULL};
\r
248 ===========================
\r
252 ===========================
\r
255 void SpawnCloudster(Sint16 x, Sint16 y)
\r
258 new->obclass = thundercloudobj;
\r
259 new->active = ac_yes;
\r
261 new->x = CONVERT_TILE_TO_GLOBAL(x);
\r
262 new->y = CONVERT_TILE_TO_GLOBAL(y);
\r
263 new->ydir = new->xdir = 1;
\r
264 NewState(new, &s_cloudsleep);
\r
268 ===========================
\r
272 ===========================
\r
275 void T_Cloud(objtype *ob)
\r
277 if (US_RndT() < tics)
\r
278 ob->xdir = ob->x > player->x? -1 : 1;
\r
280 AccelerateX(ob, ob->xdir, 10);
\r
281 if (player->bottom < ob->bottom || (Sint16)(player->top - ob->bottom) > 64*PIXGLOBAL)
\r
283 if (ob->left < player->right && ob->right > player->left)
\r
285 ob->state = &s_cloudalign;
\r
290 ===========================
\r
294 ===========================
\r
297 void T_CloudAlign(objtype *ob)
\r
299 AccelerateX(ob, ob->xdir, 10);
\r
300 if (xtry < 0 && (Sint16)((ob->x & 0x7F) + xtry) <= 0)
\r
302 xtry = -(ob->x & 0x7F);
\r
303 ob->state = &s_cloudattack1;
\r
305 if (xtry > 0 && (ob->x & 0x7F) + xtry >= 8*PIXGLOBAL)
\r
307 xtry = 8*PIXGLOBAL - (ob->x & 0x7F);
\r
308 ob->state = &s_cloudattack1;
\r
313 ===========================
\r
317 ===========================
\r
320 void R_Cloud(objtype *ob)
\r
327 else if (ob->hiteast)
\r
332 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);
\r
336 ===========================
\r
340 ===========================
\r
343 void T_CloudShoot(objtype *ob)
\r
346 new->obclass = lightningobj;
\r
347 new->active = ac_removable;
\r
348 new->needtoclip = cl_noclip;
\r
349 new->x = ob->x + TILEGLOBAL;
\r
350 new->y = ob->y + TILEGLOBAL;
\r
351 NewState(new, &s_bolt1);
\r
352 SD_PlaySound(SND_THUNDER);
\r
356 ===========================
\r
360 ===========================
\r
363 void C_CloudSleep(objtype *ob, objtype *hit)
\r
365 if (hit->obclass == keenobj)
\r
367 ChangeState(ob, &s_cloudwake);
\r
372 =============================================================================
\r
376 temp1 = float offset, in global units (added to ob->y when drawing the sprite)
\r
377 temp2 = float speed, in global units per tic (added to temp1 for every tic)
\r
379 =============================================================================
\r
382 statetype s_berkefloat1 = {BERKEWALKL1SPR, BERKEWALKR1SPR, slide, false, true, 6, 8, 0, BerkeThink, C_Berke, BerkeWalkReact, &s_berkefloat2};
\r
383 statetype s_berkefloat2 = {BERKEWALKL2SPR, BERKEWALKR2SPR, slide, false, true, 6, 8, 0, BerkeThink, C_Berke, BerkeWalkReact, &s_berkefloat3};
\r
384 statetype s_berkefloat3 = {BERKEWALKL3SPR, BERKEWALKR3SPR, slide, false, true, 6, 8, 0, BerkeThink, C_Berke, BerkeWalkReact, &s_berkefloat4};
\r
385 statetype s_berkefloat4 = {BERKEWALKL4SPR, BERKEWALKR4SPR, slide, false, true, 6, 8, 0, BerkeThink, C_Berke, BerkeWalkReact, &s_berkefloat1};
\r
386 statetype s_berkethrow1 = {BERKETHROWL1SPR, BERKETHROWR1SPR, step, false, false, 6, 0, 0, SetReactThink, C_Berke, BerkeDrawReact, &s_berkethrow2};
\r
387 statetype s_berkethrow2 = {BERKETHROWL1SPR, BERKETHROWR1SPR, step, false, false, 6, 0, 0, SetReactThink, C_Berke, BerkeDrawReact, &s_berkethrow3};
\r
388 statetype s_berkethrow3 = {BERKETHROWL1SPR, BERKETHROWR1SPR, step, false, false, 6, 0, 0, SetReactThink, C_Berke, BerkeDrawReact, &s_berkethrow4};
\r
389 statetype s_berkethrow4 = {BERKETHROWL1SPR, BERKETHROWR1SPR, step, false, false, 6, 0, 0, SetReactThink, C_Berke, BerkeDrawReact, &s_berkethrow5};
\r
390 statetype s_berkethrow5 = {BERKETHROWL1SPR, BERKETHROWR1SPR, step, false, false, 6, 0, 0, SetReactThink, C_Berke, BerkeDrawReact, &s_berkethrow6};
\r
391 statetype s_berkethrow6 = {BERKETHROWL1SPR, BERKETHROWR1SPR, step, false, false, 6, 0, 0, BerkeThrowThink, C_Berke, BerkeDrawReact, &s_berkethrow7};
\r
392 statetype s_berkethrow7 = {BERKETHROWL2SPR, BERKETHROWR2SPR, step, false, false, 6, 0, 0, SetReactThink, C_Berke, BerkeDrawReact, &s_berkethrow8};
\r
393 statetype s_berkethrow8 = {BERKETHROWL2SPR, BERKETHROWR2SPR, step, false, false, 6, 0, 0, SetReactThink, C_Berke, BerkeDrawReact, &s_berkethrow9};
\r
394 statetype s_berkethrow9 = {BERKETHROWL2SPR, BERKETHROWR2SPR, step, false, false, 6, 0, 0, SetReactThink, C_Berke, BerkeDrawReact, &s_berkethrow10};
\r
395 statetype s_berkethrow10 = {BERKETHROWL2SPR, BERKETHROWR2SPR, step, false, false, 6, 0, 0, SetReactThink, C_Berke, BerkeDrawReact, &s_berkethrow11};
\r
396 statetype s_berkethrow11 = {BERKETHROWL2SPR, BERKETHROWR2SPR, step, false, false, 6, 0, 0, SetReactThink, C_Berke, BerkeDrawReact, &s_berkethrow12};
\r
397 statetype s_berkethrow12 = {BERKETHROWL2SPR, BERKETHROWR2SPR, step, false, false, 6, 0, 0, BerkeThrowDone, C_Berke, BerkeDrawReact, &s_berkefloat1};
\r
399 statetype s_fire1 = {FIREBALL1SPR, FIREBALL1SPR, stepthink, false, false, 6, 0, 0, T_WeakProjectile, C_Lethal, FireReact, &s_fire2};
\r
400 statetype s_fire2 = {FIREBALL2SPR, FIREBALL2SPR, stepthink, false, false, 6, 0, 0, T_WeakProjectile, C_Lethal, FireReact, &s_fire1};
\r
401 statetype s_fireland1 = {FIREBALL1SPR, FIREBALL1SPR, step, false, false, 6, 0, 0, NULL, C_Berke, R_Draw, &s_fireland2};
\r
402 statetype s_fireland2 = {FIREBALL3SPR, FIREBALL3SPR, step, false, false, 12, 0, 0, NULL, C_Berke, R_Draw, &s_fireland3};
\r
403 statetype s_fireland3 = {FIREBALL4SPR, FIREBALL4SPR, step, false, false, 12, 0, 0, NULL, C_Berke, R_Draw, &s_fireland4};
\r
404 statetype s_fireland4 = {FIREBALL3SPR, FIREBALL3SPR, step, false, false, 12, 0, 0, NULL, C_Berke, R_Draw, &s_fireland5};
\r
405 statetype s_fireland5 = {FIREBALL4SPR, FIREBALL4SPR, step, false, false, 12, 0, 0, NULL, C_Berke, R_Draw, &s_fireland6};
\r
406 statetype s_fireland6 = {FIREBALL1SPR, FIREBALL1SPR, step, false, false, 12, 0, 0, NULL, C_Berke, R_Draw, &s_fireland7};
\r
407 statetype s_fireland7 = {FIREBALL2SPR, FIREBALL2SPR, step, false, false, 12, 0, 0, NULL, C_Berke, R_Draw, &s_fireland8};
\r
408 statetype s_fireland8 = {FIREBALL1SPR, FIREBALL1SPR, step, false, false, 12, 0, 0, NULL, C_Berke, R_Draw, &s_fireland9};
\r
409 statetype s_fireland9 = {FIREBALL2SPR, FIREBALL2SPR, step, false, false, 12, 0, 0, NULL, C_Berke, R_Draw, NULL};
\r
412 ===========================
\r
416 ===========================
\r
419 void SpawnBerkeloid(Sint16 x, Sint16 y)
\r
422 new->obclass = berkeloidobj;
\r
423 new->active = ac_yes;
\r
425 new->x = CONVERT_TILE_TO_GLOBAL(x);
\r
426 new->y = CONVERT_TILE_TO_GLOBAL(y) + -2*TILEGLOBAL;
\r
427 if (US_RndT() < 0x80)
\r
437 NewState(new, &s_berkefloat1);
\r
442 ===========================
\r
446 ===========================
\r
449 void BerkeThink(objtype *ob)
\r
451 Sint16 xdist, ydist;
\r
453 if (US_RndT() < 0x20)
\r
454 ob->xdir = player->x < ob->x? -1 : 1;
\r
458 // BUG: might be about to move off a ledge, causing it to get stuck
\r
459 // after throwing (the throw states don't use BerkeWalkReact)!
\r
460 // To avoid that, set xtry to 0 here.
\r
462 ob->state = &s_berkethrow1;
\r
463 // BUG? this doesn't play the attack sound
\r
465 else if (US_RndT() <= 0x40)
\r
467 xdist = player->x - ob->x;
\r
468 ydist = player->y - ob->y;
\r
469 if (ydist >= -TILEGLOBAL && ydist <= TILEGLOBAL
\r
470 && (ob->xdir == 1 && xdist > 0 || ob->xdir == -1 && xdist < 0))
\r
472 // BUG: might be about to move off a ledge, causing it to get stuck
\r
473 // after throwing (the throw states don't use BerkeWalkReact)!
\r
474 // To avoid that, set xtry to 0 here.
\r
476 ob->state = &s_berkethrow1;
\r
477 SD_PlaySound(SND_BERKELOIDATTACK);
\r
483 ===========================
\r
487 ===========================
\r
490 void BerkeThrowThink(objtype *ob)
\r
493 new->active = ac_removable;
\r
494 new->obclass = berkeloidobj;
\r
495 new->y = ob->y + 8*PIXGLOBAL;
\r
500 new->x = ob->x + 32*PIXGLOBAL;
\r
506 new->x = ob->x - 16*PIXGLOBAL;
\r
509 NewState(new, &s_fire1);
\r
510 ob->needtoreact = true;
\r
514 ===========================
\r
518 ===========================
\r
521 void BerkeThrowDone(objtype *ob)
\r
524 ob->needtoreact = true;
\r
528 ===========================
\r
532 ===========================
\r
535 void C_Berke(objtype *ob, objtype *hit)
\r
537 ob++; // shut up compiler
\r
538 if (hit->obclass == stunshotobj)
\r
542 else if (hit->obclass == keenobj)
\r
549 ===========================
\r
553 ===========================
\r
556 void FireReact(objtype *ob)
\r
558 if (ob->hiteast || ob->hitwest)
\r
563 SD_PlaySound(SND_FIREBALLLAND);
\r
564 ChangeState(ob, &s_fireland1);
\r
566 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);
\r
570 ===========================
\r
574 ===========================
\r
577 void BerkeDrawReact(objtype *ob)
\r
580 ob->temp1 = ob->temp1 + ob->temp2 * tics;
\r
586 else if (ob->temp1 < -TILEGLOBAL)
\r
588 ob->temp1 = -TILEGLOBAL;
\r
592 RF_PlaceSprite(&ob->sprite, ob->x, ob->y+ob->temp1, ob->shapenum, spritedraw, 0);
\r
596 ===========================
\r
600 ===========================
\r
603 void BerkeWalkReact(objtype *ob)
\r
605 if (ob->xdir == 1 && ob->hitwest)
\r
607 ob->x -= ob->xmove;
\r
609 ob->nothink = US_RndT() >> 5;
\r
610 ChangeState(ob, ob->state);
\r
612 else if (ob->xdir == -1 && ob->hiteast)
\r
614 ob->x -= ob->xmove;
\r
616 ob->nothink = US_RndT() >> 5;
\r
617 ChangeState(ob, ob->state);
\r
619 else if (!ob->hitnorth)
\r
621 ob->x -= ob->xmove*2;
\r
622 ob->xdir = -ob->xdir;
\r
623 ob->nothink = US_RndT() >> 5;
\r
624 ChangeState(ob, ob->state);
\r
626 BerkeDrawReact(ob);
\r
630 =============================================================================
\r
634 temp1 = last lasttimecount (for resetting the touch counter after each frame)
\r
635 temp2 = touch counter
\r
637 =============================================================================
\r
640 statetype s_footsmoke1 = {SMOKE1SPR, SMOKE1SPR, step, false, false, 12, 0, 0, NULL, NULL, R_Draw, &s_footsmoke2};
\r
641 statetype s_footsmoke2 = {SMOKE2SPR, SMOKE2SPR, step, false, false, 12, 0, 0, NULL, NULL, R_Draw, &s_footsmoke3};
\r
642 statetype s_footsmoke3 = {SMOKE3SPR, SMOKE3SPR, step, false, false, 12, 0, 0, NULL, NULL, R_Draw, &s_footsmoke4};
\r
643 statetype s_footsmoke4 = {SMOKE4SPR, SMOKE4SPR, step, false, false, 12, 0, 0, NULL, NULL, R_Draw, NULL};
\r
644 statetype s_inch1 = {INCHWORML1SPR, INCHWORMR1SPR, step, false, true, 30, 128, 0, InchThink, InchContact, R_Walk, &s_inch2};
\r
645 statetype s_inch2 = {INCHWORML2SPR, INCHWORMR2SPR, step, false, true, 30, 128, 0, InchThink, InchContact, R_Walk, &s_inch1};
\r
646 statetype s_footchange = { -1, -1, step, false, false, 48, 0, 0, NULL, NULL, R_Draw, &s_footwait}; //never used!
\r
647 statetype s_footwait = {FOOTSPR, FOOTSPR, think, false, false, 30000, 0, 0, NULL, FootContact, R_Draw, NULL};
\r
650 ===========================
\r
654 ===========================
\r
657 void SpawnInchworm(Sint16 x, Sint16 y)
\r
660 new->obclass = inchwormobj;
\r
661 new->active = ac_yes;
\r
663 new->x = CONVERT_TILE_TO_GLOBAL(x);
\r
664 new->y = CONVERT_TILE_TO_GLOBAL(y);
\r
665 if (US_RndT() < 0x80)
\r
674 NewState(new, &s_inch1);
\r
675 new->ticcount = US_RndT() / 32;
\r
679 ===========================
\r
683 ===========================
\r
686 void SpawnFoot(Sint16 x, Sint16 y)
\r
689 new->obclass = footobj;
\r
690 new->active = ac_yes;
\r
692 new->x = CONVERT_TILE_TO_GLOBAL(x);
\r
693 new->y = CONVERT_TILE_TO_GLOBAL(y-3);
\r
694 NewState(new, &s_footwait);
\r
698 ===========================
\r
702 ===========================
\r
705 void InchThink(objtype *ob)
\r
707 if (ob->x > player->x)
\r
718 ===========================
\r
722 ===========================
\r
725 void InchContact(objtype *ob, objtype *hit)
\r
729 if (hit->obclass != inchwormobj)
\r
732 if (ob->temp1 != (Sint16)lasttimecount)
\r
734 ob->temp1 = (Sint16)lasttimecount;
\r
738 if (++ob->temp2 != 11) //11 instead of 12 since the object can't contact itself
\r
741 //change current inchworm into a foot:
\r
742 SD_PlaySound(SND_MAKEFOOT);
\r
743 ob->y -= 5*TILEGLOBAL;
\r
744 ob->obclass = footobj;
\r
745 ChangeState(ob, &s_footwait);
\r
747 //Note: It would make more sense to remove the remaining inchworm BEFORE
\r
748 //spawning the smoke, just in case there are not enough free spots in the
\r
749 //objarray to spawn the smoke. The game won't crash either way, though.
\r
753 new->x = ob->x - 8*PIXGLOBAL;
\r
754 new->y = ob->y + 16*PIXGLOBAL;
\r
756 NewState(new, &s_footsmoke1);
\r
759 new->x = ob->x + 16*PIXGLOBAL;
\r
760 new->y = ob->y + 24*PIXGLOBAL;
\r
762 NewState(new, &s_footsmoke1);
\r
765 new->x = ob->x + 40*PIXGLOBAL;
\r
766 new->y = ob->y + 16*PIXGLOBAL;
\r
768 NewState(new, &s_footsmoke1);
\r
772 new->y = ob->y - 8*PIXGLOBAL;
\r
774 NewState(new, &s_footsmoke1);
\r
776 //remove ALL inchworm from the level:
\r
777 for (ob2 = player->next; ob2; ob2=ob2->next)
\r
779 if (ob2->obclass == inchwormobj)
\r
785 ===========================
\r
789 ===========================
\r
792 void FootContact(objtype *ob, objtype *hit) //completely useless
\r
794 ob++; // shut up compiler
\r
795 hit++; // shut up compiler
\r
799 =============================================================================
\r
803 temp1 = if non-zero, pick a new (random) direction when hitting the ground
\r
804 Makes the Bounder jump straight up at least once after having jumped
\r
805 left/right (unless Keen is riding the Bounder)
\r
806 temp2 = jump counter to make the Bounder jump straight up at least twice
\r
807 after Keen has fallen/jumped off
\r
809 =============================================================================
\r
812 statetype s_bounderup1 = {BOUNDERC1SPR, BOUNDERC1SPR, stepthink, false, false, 20, 0, 0, T_Projectile, C_Bounder, R_Bounder, &s_bounderup2};
\r
813 statetype s_bounderup2 = {BOUNDERC2SPR, BOUNDERC2SPR, stepthink, false, false, 20, 0, 0, T_Projectile, C_Bounder, R_Bounder, &s_bounderup1};
\r
814 statetype s_bounderside1 = {BOUNDERL1SPR, BOUNDERR1SPR, stepthink, false, false, 20, 0, 0, T_Projectile, C_Bounder, R_Bounder, &s_bounderside2};
\r
815 statetype s_bounderside2 = {BOUNDERL2SPR, BOUNDERR2SPR, stepthink, false, false, 20, 0, 0, T_Projectile, C_Bounder, R_Bounder, &s_bounderside1};
\r
816 statetype s_bounderstun = {BOUNDERC1SPR, BOUNDERC1SPR, think, false, false, 0, 0, 0, T_Projectile, NULL, R_Stunned, &s_bounderstun2};
\r
817 statetype s_bounderstun2 = {BOUNDERSTUNSPR, BOUNDERSTUNSPR, think, false, false, 0, 0, 0, T_Projectile, NULL, R_Stunned, NULL};
\r
820 ===========================
\r
824 ===========================
\r
827 void SpawnBounder(Sint16 x, Sint16 y)
\r
830 new->obclass = bounderobj;
\r
831 new->active = ac_yes;
\r
833 new->x = CONVERT_TILE_TO_GLOBAL(x);
\r
834 new->y = CONVERT_TILE_TO_GLOBAL(y) + -8*PIXGLOBAL;
\r
837 NewState(new, &s_bounderup1);
\r
841 ===========================
\r
845 ===========================
\r
848 void C_Bounder(objtype *ob, objtype *hit)
\r
850 if (hit->obclass == stunshotobj)
\r
852 //basically StunObj(), but in different order:
\r
856 ob->temp4 = ob->obclass;
\r
858 ChangeState(ob, &s_bounderstun);
\r
859 ob->obclass = stunnedobj;
\r
866 ===========================
\r
870 ===========================
\r
873 void R_Bounder(objtype *ob)
\r
884 SD_PlaySound(SND_BOUNCE2);
\r
887 if (gamestate.riding == ob)
\r
890 if (player->left < ob->left-4*PIXGLOBAL)
\r
894 else if (player->right > ob->right+4*PIXGLOBAL)
\r
902 ob->xspeed = ob->xdir * 24;
\r
904 else if (ob->temp2 <= 2 || ob->temp1 == 0)
\r
907 ob->xdir = ob->xspeed = 0;
\r
908 ChangeState(ob, &s_bounderup1);
\r
913 randnum = US_RndT();
\r
918 else if (randnum < 200)
\r
926 ob->xspeed = ob->xdir * 24;
\r
931 ChangeState(ob, &s_bounderside1);
\r
935 ChangeState(ob, &s_bounderup1);
\r
939 if (ob->hiteast || ob->hitwest)
\r
941 ob->xdir = -ob->xdir;
\r
942 ob->xspeed = -ob->xspeed;
\r
945 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);
\r
949 =============================================================================
\r
953 =============================================================================
\r
956 statetype s_lick1 = {LICKMOVEL1SPR, LICKMOVER1SPR, step, false, false, 10, 0, 0, LickJumpThink, LickContact, R_Draw, &s_lick2};
\r
957 statetype s_lick2 = {LICKMOVEL2SPR, LICKMOVER2SPR, think, false, false, 0, 0, 0, T_Projectile, LickContact, LickAirReact, &s_lick3};
\r
958 statetype s_lick3 = {LICKMOVEL3SPR, LICKMOVER3SPR, think, false, false, 0, 0, 0, T_Projectile, LickContact, LickAirReact, NULL};
\r
959 statetype s_lick4 = {LICKMOVEL4SPR, LICKMOVER4SPR, step, false, false, 10, 0, 0, NULL, LickContact, R_Draw, &s_lick1};
\r
960 statetype s_licklick1 = {LICKATTACKL1SPR, LICKATTACKR1SPR, step, true, false, 4, 0, 0, NULL, LickKillContact, R_Draw, &s_licklick2};
\r
961 statetype s_licklick2 = {LICKATTACKL2SPR, LICKATTACKR2SPR, step, true, false, 4, 0, 0, NULL, LickKillContact, R_Draw, &s_licklick3};
\r
962 statetype s_licklick3 = {LICKATTACKL3SPR, LICKATTACKR3SPR, step, true, false, 4, 0, 0, NULL, LickKillContact, R_Draw, &s_licklick4};
\r
963 statetype s_licklick4 = {LICKATTACKL2SPR, LICKATTACKR2SPR, step, true, false, 4, 0, 0, NULL, LickKillContact, R_Draw, &s_licklick5};
\r
964 statetype s_licklick5 = {LICKATTACKL1SPR, LICKATTACKR1SPR, step, true, false, 4, 0, 0, NULL, LickKillContact, R_Draw, &s_licklick6};
\r
965 statetype s_licklick6 = {LICKATTACKL2SPR, LICKATTACKR2SPR, step, true, false, 4, 0, 0, NULL, LickKillContact, R_Draw, &s_licklick7};
\r
966 statetype s_licklick7 = {LICKATTACKL3SPR, LICKATTACKR3SPR, step, true, false, 4, 0, 0, NULL, LickKillContact, R_Draw, &s_licklick8};
\r
967 statetype s_licklick8 = {LICKATTACKL2SPR, LICKATTACKR2SPR, step, true, false, 4, 0, 0, NULL, LickKillContact, R_Draw, &s_lick3};
\r
968 statetype s_lickstun = {LICKSTUNSPR, LICKSTUNSPR, think, false, false, 0, 0, 0, T_Projectile, NULL, R_Stunned, &s_lickstun2};
\r
969 statetype s_lickstun2 = {LICKSTUNSPR, LICKSTUNSPR, think, false, false, 0, 0, 0, T_Projectile, NULL, R_Stunned, NULL};
\r
972 ===========================
\r
976 ===========================
\r
979 void SpawnLick(Sint16 x, Sint16 y)
\r
982 new->obclass = lickobj;
\r
983 new->active = ac_yes;
\r
985 new->x = CONVERT_TILE_TO_GLOBAL(x);
\r
986 new->y = CONVERT_TILE_TO_GLOBAL(y);
\r
987 if (US_RndT() < 0x80)
\r
996 new->nothink = US_RndT() / 64;
\r
997 NewState(new, &s_lick3);
\r
1001 ===========================
\r
1005 ===========================
\r
1008 void LickJumpThink(objtype *ob)
\r
1010 Sint16 xdist, ydist;
\r
1012 if (ob->x > player->x)
\r
1021 xdist = player->x - ob->x;
\r
1022 ydist = player->y - ob->y;
\r
1024 if (ydist >= -TILEGLOBAL && ydist <= TILEGLOBAL &&
\r
1025 ( ob->xdir == 1 && xdist > -2*PIXGLOBAL && xdist < 24*PIXGLOBAL
\r
1026 || ob->xdir == -1 && xdist < 2*PIXGLOBAL && xdist > -32*PIXGLOBAL ) )
\r
1028 SD_PlaySound(SND_LICKATTACK);
\r
1029 ob->state = &s_licklick1;
\r
1031 else if (abs(xdist) > 3*TILEGLOBAL)
\r
1033 ob->xspeed = ob->xdir * 32;
\r
1038 ob->xspeed = (ob->xdir * 32)/2;
\r
1044 ===========================
\r
1048 ===========================
\r
1051 void LickContact(objtype *ob, objtype *hit)
\r
1053 if (hit->obclass == stunshotobj)
\r
1055 StunObj(ob, hit, &s_lickstun);
\r
1061 ===========================
\r
1065 ===========================
\r
1068 void LickKillContact(objtype *ob, objtype *hit)
\r
1070 if (hit->obclass == keenobj)
\r
1072 if (ob->xdir == 1 && player->x > ob->x
\r
1073 || ob->xdir == -1 && player->x < ob->x)
\r
1080 LickContact(ob, hit);
\r
1085 ===========================
\r
1089 ===========================
\r
1092 void LickAirReact(objtype *ob)
\r
1095 ChangeState(ob, &s_lick4);
\r
1097 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);
\r
1101 =============================================================================
\r
1105 temp2 = additional sprite pointer for thruster sprites
\r
1106 temp3 = additional sprite pointer for thruster sprites
\r
1108 =============================================================================
\r
1111 statetype s_platform = {PLATFORMSPR, PLATFORMSPR, think, false, false, 0, 0, 0, T_Platform, NULL, R_Platform, NULL};
\r
1114 ===========================
\r
1118 ===========================
\r
1121 void SpawnPlatform(Sint16 x, Sint16 y, Sint16 dir)
\r
1124 new->obclass = platformobj;
\r
1125 new->active = ac_allways;
\r
1126 new->priority = 0;
\r
1127 new->x = CONVERT_TILE_TO_GLOBAL(x);
\r
1128 new->y = CONVERT_TILE_TO_GLOBAL(y);
\r
1148 NewState(new, &s_platform);
\r
1152 ===========================
\r
1156 ===========================
\r
1159 void T_Platform(objtype *ob)
\r
1161 Uint16 newpos, newtile;
\r
1163 xtry = ob->xdir * 12 * tics;
\r
1164 ytry = ob->ydir * 12 * tics;
\r
1166 if (ob->xdir == 1)
\r
1168 newpos = ob->right + xtry;
\r
1169 newtile = CONVERT_GLOBAL_TO_TILE(newpos);
\r
1170 if (ob->tileright != newtile)
\r
1172 if (*(mapsegs[2]+mapbwidthtable[ob->tiletop]/2+newtile) == 31)
\r
1175 xtry = xtry - (newpos & 0xFF);
\r
1179 else if (ob->xdir == -1)
\r
1181 newpos = ob->left + xtry;
\r
1182 newtile = CONVERT_GLOBAL_TO_TILE(newpos);
\r
1183 if (ob->tileleft != newtile)
\r
1185 if (*(mapsegs[2]+mapbwidthtable[ob->tiletop]/2+newtile) == 31)
\r
1188 xtry = xtry + (0x100 - (newpos & 0xFF));
\r
1192 else if (ob->ydir == 1)
\r
1194 newpos = ob->bottom + ytry;
\r
1195 newtile = CONVERT_GLOBAL_TO_TILE(newpos);
\r
1196 if (ob->tilebottom != newtile)
\r
1198 if (*(mapsegs[2]+mapbwidthtable[newtile]/2+ob->tileleft) == 31)
\r
1200 if (*(mapsegs[2]+mapbwidthtable[newtile-2]/2+ob->tileleft) == 31)
\r
1203 ob->needtoreact = true;
\r
1208 ytry = ytry - (newpos & 0xFF);
\r
1213 else if (ob->ydir == -1)
\r
1215 newpos = ob->top + ytry;
\r
1216 newtile = CONVERT_GLOBAL_TO_TILE(newpos);
\r
1217 if (ob->tiletop != newtile)
\r
1219 if (*(mapsegs[2]+mapbwidthtable[newtile]/2+ob->tileleft) == 31)
\r
1221 if (*(mapsegs[2]+mapbwidthtable[newtile+2]/2+ob->tileleft) == 31)
\r
1224 ob->needtoreact = true;
\r
1229 ytry = ytry + (0x100 - (newpos & 0xFF));
\r
1237 ===========================
\r
1241 ===========================
\r
1244 void R_Platform(objtype *ob)
\r
1248 //place platform sprite:
\r
1249 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);
\r
1251 //place (or remove) thruster sprites:
\r
1252 frame = (lasttimecount >> 2) & 1;
\r
1253 if (ob->xdir == 1)
\r
1255 RF_PlaceSprite((void**)&ob->temp2, ob->x-1*PIXGLOBAL, ob->y+3*PIXGLOBAL, frame+PLATSIDETHRUST1SPR, spritedraw, 0);
\r
1257 RF_RemoveSprite((void**)&ob->temp3);
\r
1259 else if (ob->xdir == -1)
\r
1262 RF_RemoveSprite((void**)&ob->temp2);
\r
1263 RF_PlaceSprite((void**)&ob->temp3, ob->x+48*PIXGLOBAL, ob->y+5*PIXGLOBAL, frame+PLATSIDETHRUST1SPR, spritedraw, 1);
\r
1265 else if (ob->ydir == -1)
\r
1267 RF_PlaceSprite((void**)&ob->temp2, ob->x+2*PIXGLOBAL, ob->y+9*PIXGLOBAL, frame+PLATLTHRUST1SPR, spritedraw, 0);
\r
1268 RF_PlaceSprite((void**)&ob->temp3, ob->x+46*PIXGLOBAL, ob->y+8*PIXGLOBAL, frame+PLATRTHRUST1SPR, spritedraw, 0);
\r
1270 else if (ob->ydir == 1)
\r
1274 RF_PlaceSprite((void**)&ob->temp2, ob->x+2*PIXGLOBAL, ob->y+9*PIXGLOBAL, frame+PLATLTHRUST1SPR, spritedraw, 0);
\r
1275 RF_PlaceSprite((void**)&ob->temp3, ob->x+46*PIXGLOBAL, ob->y+8*PIXGLOBAL, frame+PLATRTHRUST1SPR, spritedraw, 0);
\r
1280 RF_RemoveSprite((void**)&ob->temp2);
\r
1282 RF_RemoveSprite((void**)&ob->temp3);
\r
1288 =============================================================================
\r
1292 temp1 = initial y position
\r
1294 =============================================================================
\r
1297 statetype s_dropplatsit = {PLATFORMSPR, PLATFORMSPR, think, false, false, 0, 0, 0, T_DropPlatSit, NULL, R_Draw, NULL};
\r
1298 statetype s_dropplatfall = {PLATFORMSPR, PLATFORMSPR, think, false, false, 0, 0, 0, T_DropPlatFall, NULL, R_Draw, NULL};
\r
1299 statetype s_dropplatrise = {PLATFORMSPR, PLATFORMSPR, slidethink, false, false, 0, 0, -32, T_DropPlatRise, NULL, R_Draw, NULL};
\r
1302 ===========================
\r
1306 ===========================
\r
1309 void SpawnDropPlat(Sint16 x, Sint16 y)
\r
1312 new->obclass = platformobj;
\r
1313 new->active = ac_allways;
\r
1314 new->priority = 0;
\r
1315 new->x = CONVERT_TILE_TO_GLOBAL(x);
\r
1316 new->y = new->temp1 = CONVERT_TILE_TO_GLOBAL(y);
\r
1319 new->needtoclip = cl_noclip;
\r
1320 NewState(new, &s_dropplatsit);
\r
1324 ===========================
\r
1328 ===========================
\r
1331 void T_DropPlatSit(objtype *ob)
\r
1333 if (gamestate.riding == ob)
\r
1337 if (ob->y + ytry - ob->temp1 >= 8*PIXGLOBAL)
\r
1338 ob->state = &s_dropplatfall;
\r
1343 ===========================
\r
1347 ===========================
\r
1350 void T_DropPlatFall(objtype *ob)
\r
1356 if (ytry >= 15*PIXGLOBAL)
\r
1357 ytry = 15*PIXGLOBAL;
\r
1359 newy = ob->bottom + ytry;
\r
1360 ty = CONVERT_GLOBAL_TO_TILE(newy);
\r
1361 if (ob->tilebottom != ty)
\r
1363 if (*(mapsegs[2]+mapbwidthtable[ty]/2+ob->tileleft) == 31)
\r
1365 ytry = 0xFF - (ob->bottom & 0xFF);
\r
1366 if (gamestate.riding != ob)
\r
1367 ob->state = &s_dropplatrise;
\r
1373 ===========================
\r
1377 ===========================
\r
1380 void T_DropPlatRise(objtype *ob)
\r
1382 if (gamestate.riding == ob)
\r
1385 ob->state = &s_dropplatfall;
\r
1387 else if (ob->y <= ob->temp1)
\r
1389 ytry = ob->temp1 - ob->y;
\r
1390 ob->state = &s_dropplatsit;
\r