]> 4ch.mooo.com Git - 16.git/blob - 16/keen456/KEEN4-6/KEEN6/K6_ACT2.C
extrcted keen code remake
[16.git] / 16 / keen456 / KEEN4-6 / KEEN6 / K6_ACT2.C
1 /* Reconstructed Commander Keen 4-6 Source Code\r
2  * Copyright (C) 2021 K1n9_Duk3\r
3  *\r
4  * This file is loosely based on:\r
5  * Keen Dreams Source Code\r
6  * Copyright (C) 2014 Javier M. Chavez\r
7  *\r
8  * This program is free software; you can redistribute it and/or modify\r
9  * it under the terms of the GNU General Public License as published by\r
10  * the Free Software Foundation; either version 2 of the License, or\r
11  * (at your option) any later version.\r
12  *\r
13  * This program is distributed in the hope that it will be useful,\r
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
16  * GNU General Public License for more details.\r
17  *\r
18  * You should have received a copy of the GNU General Public License along\r
19  * with this program; if not, write to the Free Software Foundation, Inc.,\r
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
21  */\r
22 \r
23 /*\r
24 K6_ACT2.C\r
25 =========\r
26 \r
27 Contains the following actor types (in this order):\r
28 \r
29 - Nospike\r
30 - Gik\r
31 - Turrets\r
32 - Orbatrix\r
33 - Bip & Bipship\r
34 - Flect\r
35 \r
36 */\r
37 \r
38 #include "CK_DEF.H"\r
39 \r
40 /*\r
41 =============================================================================\r
42 \r
43                                                   NOSPIKE\r
44 \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
48 temp4 = health\r
49 \r
50 =============================================================================\r
51 */\r
52 \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
67 \r
68 /*\r
69 ===========================\r
70 =\r
71 = SpawnNospike\r
72 =\r
73 ===========================\r
74 */\r
75 \r
76 void SpawnNospike(Uint16 tileX, Uint16 tileY)\r
77 {\r
78         GetNewObj(false);\r
79         new->obclass = nospikeobj;\r
80         new->active = ac_yes;\r
81         new->priority = 0;\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
85         {\r
86                 new->xdir = 1;\r
87         }\r
88         else\r
89         {\r
90                 new->xdir = -1;\r
91         }\r
92         new->ydir = 1;\r
93         NewState(new, &s_nospikestand);\r
94         new->temp4 = 4; // health\r
95 }\r
96 \r
97 /*\r
98 ===========================\r
99 =\r
100 = T_NospikeWalk\r
101 =\r
102 ===========================\r
103 */\r
104 \r
105 void T_NospikeWalk(objtype *ob)\r
106 {\r
107         if (US_RndT() < 0x10)\r
108         {\r
109                 ob->state = &s_nospikestand;\r
110         }\r
111         else if (ob->bottom == player->bottom && US_RndT() <= 0x20)\r
112         {\r
113                 //\r
114                 // start running towards player\r
115                 //\r
116                 if (player->x > ob->x)\r
117                 {\r
118                         ob->xdir = 1;\r
119                 }\r
120                 else\r
121                 {\r
122                         ob->xdir = -1;\r
123                 }\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
127                 {\r
128                         ob->state = &s_nospikerun2;\r
129                 }\r
130                 else if (ob->state == &s_nospikewalk2)\r
131                 {\r
132                         ob->state = &s_nospikerun3;\r
133                 }\r
134                 else if (ob->state == &s_nospikewalk3)\r
135                 {\r
136                         ob->state = &s_nospikerun4;\r
137                 }\r
138                 else if (ob->state == &s_nospikewalk4)\r
139                 {\r
140                         ob->state = &s_nospikerun1;\r
141                 }\r
142         }\r
143 }\r
144 \r
145 /*\r
146 ===========================\r
147 =\r
148 = T_NospikeRun\r
149 =\r
150 ===========================\r
151 */\r
152 \r
153 void T_NospikeRun(objtype *ob)\r
154 {\r
155         if (ob->temp1)\r
156                 return; // nospike is running on thin air, so we'd better not stop\r
157 \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
161                   && US_RndT() < 8 )\r
162                 || !OnScreen(ob) )      // always stop running when off-screen\r
163         {\r
164                 //\r
165                 // stop running\r
166                 //\r
167                 ob->temp2 = 0;\r
168                 if (ob->state == &s_nospikerun1)\r
169                 {\r
170                         ob->state = &s_nospikewalk2;\r
171                 }\r
172                 else if (ob->state == &s_nospikerun2)\r
173                 {\r
174                         ob->state = &s_nospikewalk3;\r
175                 }\r
176                 else if (ob->state == &s_nospikerun3)\r
177                 {\r
178                         ob->state = &s_nospikewalk4;\r
179                 }\r
180                 else if (ob->state == &s_nospikerun4)\r
181                 {\r
182                         ob->state = &s_nospikewalk1;\r
183                 }\r
184         }\r
185 }\r
186 \r
187 /*\r
188 ===========================\r
189 =\r
190 = C_Nospike\r
191 =\r
192 ===========================\r
193 */\r
194 \r
195 void C_Nospike(objtype *ob, objtype *hit)\r
196 {\r
197         if (hit->obclass == keenobj)\r
198         {\r
199                 KillKeen();\r
200         }\r
201         else if (hit->obclass == stunshotobj)\r
202         {\r
203                 if (--ob->temp4 == 0)   // handle health\r
204                 {\r
205                         StunObj(ob, hit, &s_nospikestun);\r
206                         ob->yspeed = -24;\r
207                 }\r
208                 else\r
209                 {\r
210                         if (player->x > ob->x)\r
211                         {\r
212                                 ob->xdir = 1;\r
213                         }\r
214                         else\r
215                         {\r
216                                 ob->xdir = -1;\r
217                         }\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
221                         {\r
222                                 ChangeState(ob, &s_nospikerun2);\r
223                         }\r
224                         else if (ob->state == &s_nospikewalk2)\r
225                         {\r
226                                 ChangeState(ob, &s_nospikerun3);\r
227                         }\r
228                         else if (ob->state == &s_nospikewalk3)\r
229                         {\r
230                                 ChangeState(ob, &s_nospikerun4);\r
231                         }\r
232                         else if (ob->state == &s_nospikewalk4)\r
233                         {\r
234                                 ChangeState(ob, &s_nospikerun1);\r
235                         }\r
236                         ExplodeShot(hit);\r
237                 }\r
238         }\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
242         {\r
243                 //\r
244                 // stun both nospikes\r
245                 //\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
253         }\r
254 }\r
255 \r
256 /*\r
257 ===========================\r
258 =\r
259 = T_NospikeConfused\r
260 =\r
261 ===========================\r
262 */\r
263 \r
264 void T_NospikeConfused(objtype* ob)\r
265 {\r
266         RF_RemoveSprite((void**)&ob->temp3);\r
267 }\r
268 \r
269 /*\r
270 ===========================\r
271 =\r
272 = R_NospikeConfused\r
273 =\r
274 ===========================\r
275 */\r
276 \r
277 void R_NospikeConfused(objtype *ob)\r
278 {\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
281 }\r
282 \r
283 /*\r
284 ===========================\r
285 =\r
286 = R_NospikeFall\r
287 =\r
288 ===========================\r
289 */\r
290 \r
291 void R_NospikeFall(objtype *ob)\r
292 {\r
293         RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
294         if (ob->hitnorth)\r
295         {\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
301                 ob->yspeed = -24;\r
302         }\r
303 }\r
304 \r
305 /*\r
306 ===========================\r
307 =\r
308 = R_NospikeRun\r
309 =\r
310 ===========================\r
311 */\r
312 \r
313 void R_NospikeRun(objtype *ob)\r
314 {\r
315         if (ob->hitnorth)\r
316         {\r
317                 ob->temp1 = 0;  // on solid ground\r
318                 if (ob->hiteast || ob->hitwest)\r
319                 {\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
324                         return;\r
325                 }\r
326         }\r
327         else\r
328         {\r
329                 if (++ob->temp1 == 6)   // not on solid ground for 6 steps?\r
330                 {\r
331                         ChangeState(ob, &s_nospikeconfused1);\r
332 #if 0\r
333                         // bugfix:\r
334                         ob->nothink = 0;        // to make sure T_NospikeConfused can remove the question mark sprite\r
335 #endif\r
336                 }\r
337         }\r
338         if (ob->temp2 & 0xFF00)\r
339         {\r
340                 ob->temp2 -= 0x100;\r
341                 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, maskdraw, ob->priority);\r
342         }\r
343         else\r
344         {\r
345                 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
346         }\r
347 }\r
348 \r
349 /*\r
350 =============================================================================\r
351 \r
352                                                   GIK\r
353 \r
354 =============================================================================\r
355 */\r
356 \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
364 \r
365 /*\r
366 ===========================\r
367 =\r
368 = SpawnGik\r
369 =\r
370 ===========================\r
371 */\r
372 \r
373 void SpawnGik(Uint16 tileX, Uint16 tileY)\r
374 {\r
375         GetNewObj(false);\r
376         new->obclass = gikobj;\r
377         new->active = ac_yes;\r
378         new->priority = 0;\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
382         {\r
383                 new->xdir = 1;\r
384         }\r
385         else\r
386         {\r
387                 new->xdir = -1;\r
388         }\r
389         new->ydir = 1;\r
390         NewState(new, &s_gikwalk1);\r
391 }\r
392 \r
393 /*\r
394 ===========================\r
395 =\r
396 = T_GikWalk\r
397 =\r
398 ===========================\r
399 */\r
400 \r
401 void T_GikWalk(objtype *ob)\r
402 {\r
403         Sint16 xdist;\r
404 \r
405         if (ob->hitnorth != 9)  // if NOT on flat ground that kills Keen\r
406         {\r
407                 xdist = player->x - ob->x;\r
408                 if (player->bottom <= ob->bottom && ob->bottom - player->bottom <= 4*TILEGLOBAL)\r
409                 {\r
410                         if (xdist < 0)\r
411                         {\r
412                                 ob->xdir = -1;\r
413                         }\r
414                         else\r
415                         {\r
416                                 ob->xdir = 1;\r
417                         }\r
418                         if (xdist >= -7*TILEGLOBAL && xdist <= 7*TILEGLOBAL\r
419                                 && (xdist <= -TILEGLOBAL || xdist >= TILEGLOBAL) )\r
420                         {\r
421                                 if (xdist < 0)\r
422                                 {\r
423                                         ob->xspeed = -40;\r
424                                 }\r
425                                 else\r
426                                 {\r
427                                         ob->xspeed = 40;\r
428                                 }\r
429                                 ob->yspeed = -28;\r
430                                 ob->state = &s_gikjump;\r
431                                 SD_PlaySound(SND_GIKJUMP);\r
432                         }\r
433                 }\r
434         }\r
435 }\r
436 \r
437 /*\r
438 ===========================\r
439 =\r
440 = T_GikSlide\r
441 =\r
442 ===========================\r
443 */\r
444 \r
445 void T_GikSlide(objtype *ob)\r
446 {\r
447         // tic masks for friction, based on slope type and direction\r
448         // 0 - no friction\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
454 \r
455         Sint16 ticmask;\r
456         Sint16 slope;\r
457         Sint32 i;\r
458 \r
459         DoGravity(ob);\r
460 \r
461         slope = ob->hitnorth & 7;\r
462         if (ob->xdir == 1)\r
463         {\r
464                 ticmask = rticmask[slope];\r
465         }\r
466         else\r
467         {\r
468                 ticmask = lticmask[slope];\r
469         }\r
470 \r
471         if (ob->xspeed == 0 && ob->hitnorth)\r
472         {\r
473                 ob->state = &s_gikstand;\r
474         }\r
475         else\r
476         {\r
477                 for (i = lasttimecount-tics; i < lasttimecount; i++)\r
478                 {\r
479                         if (ticmask && !(i & ticmask))\r
480                         {\r
481                                 if ((ob->xspeed < 0 && ++ob->xspeed == 0)\r
482                                         || (ob-> xspeed > 0 && --ob->xspeed == 0))\r
483                                 {\r
484                                         ob->state = &s_gikstand;\r
485                                         return;\r
486                                 }\r
487                         }\r
488                         xtry += ob->xspeed;\r
489                 }\r
490         }\r
491 }\r
492 \r
493 /*\r
494 ===========================\r
495 =\r
496 = R_GikJump\r
497 =\r
498 ===========================\r
499 */\r
500 \r
501 void R_GikJump(objtype *ob)\r
502 {\r
503         if (ob->hiteast || ob->hitwest)\r
504                 ob->xspeed = 0;\r
505 \r
506         if (ob->hitsouth)\r
507                 ob->yspeed = 0;\r
508 \r
509         if (ob->hitnorth)\r
510         {\r
511                 ob->yspeed = 0;\r
512                 SD_PlaySound(SND_GIKLAND);\r
513                 ChangeState(ob, ob->state->nextstate);\r
514         }\r
515         RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
516 }\r
517 \r
518 /*\r
519 ===========================\r
520 =\r
521 = R_GikSlide\r
522 =\r
523 ===========================\r
524 */\r
525 \r
526 void R_GikSlide(objtype *ob)\r
527 {\r
528         if ((ob->hiteast && ob->xspeed < 0) || (ob->hitwest && ob->xspeed > 0))\r
529                 ob->xspeed = 0;\r
530 \r
531         RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
532 }\r
533 \r
534 /*\r
535 =============================================================================\r
536 \r
537                                                   CANNON\r
538 \r
539 temp1 = direction\r
540 \r
541 =============================================================================\r
542 */\r
543 \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
552 \r
553 /*\r
554 ===========================\r
555 =\r
556 = SpawnCannon\r
557 =\r
558 ===========================\r
559 */\r
560 \r
561 void SpawnCannon(Uint16 tileX, Uint16 tileY, Sint16 dir)\r
562 {\r
563         GetNewObj(false);\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
570         new->temp1 = dir;\r
571         NewState(new, &s_cannon);\r
572 }\r
573 \r
574 /*\r
575 ===========================\r
576 =\r
577 = T_Cannon\r
578 =\r
579 ===========================\r
580 */\r
581 \r
582 void T_Cannon(objtype *ob)\r
583 {\r
584         GetNewObj(true);\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
587         new->x = ob->x;\r
588         new->y = ob->y;\r
589         switch (ob->temp1)\r
590         {\r
591         case 0:\r
592                 new->yspeed = -64;\r
593                 break;\r
594         case 1:\r
595                 new->xspeed = 64;\r
596                 break;\r
597         case 2:\r
598                 new->yspeed = 64;\r
599                 break;\r
600         case 3:\r
601                 new->xspeed = -64;\r
602         }\r
603         NewState(new, &s_cshot1);\r
604         SD_PlaySound(SND_ENEMYSHOT);\r
605 }\r
606 \r
607 /*\r
608 ===========================\r
609 =\r
610 = C_CShot\r
611 =\r
612 ===========================\r
613 */\r
614 \r
615 void C_CShot(objtype *ob, objtype *hit)\r
616 {\r
617         if (hit->obclass == keenobj)\r
618         {\r
619                 KillKeen();\r
620                 ChangeState(ob, &s_cshothit1);\r
621         }\r
622 }\r
623 \r
624 /*\r
625 ===========================\r
626 =\r
627 = R_CShot\r
628 =\r
629 ===========================\r
630 */\r
631 \r
632 void R_CShot(objtype *ob)\r
633 {\r
634         if (ob->hitnorth || ob->hiteast || ob->hitsouth || ob->hitwest)\r
635         {\r
636                 SD_PlaySound(SND_ENEMYSHOTEXPLODE);\r
637                 ChangeState(ob, &s_cshothit1);\r
638         }\r
639         RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
640 }\r
641 \r
642 /*\r
643 =============================================================================\r
644 \r
645                                                   ORBATRIX\r
646 \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
651 \r
652 =============================================================================\r
653 */\r
654 \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
670 \r
671 /*\r
672 ===========================\r
673 =\r
674 = SpawnOrbatrix\r
675 =\r
676 ===========================\r
677 */\r
678 \r
679 void SpawnOrbatrix(Uint16 tileX, Uint16 tileY)\r
680 {\r
681         GetNewObj(false);\r
682         new->obclass = orbatrixobj;\r
683         new->active = ac_yes;\r
684         new->priority = 0;\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
688         {\r
689                 new->xdir = 1;\r
690         }\r
691         else\r
692         {\r
693                 new->xdir = -1;\r
694         }\r
695         new->ydir = 1;\r
696         new->temp4 = 1;\r
697         NewState(new, &s_orbatrix1);\r
698 }\r
699 \r
700 /*\r
701 ===========================\r
702 =\r
703 = T_OrbatrixFly\r
704 =\r
705 ===========================\r
706 */\r
707 \r
708 void T_OrbatrixFly(objtype *ob)\r
709 {\r
710         Sint16 dist;\r
711 \r
712         if (US_RndT() < 0x20)\r
713         {\r
714                 ob->state = &s_orbatrixidle1;\r
715                 return;\r
716         }\r
717 \r
718         if (ob->bottom != player->bottom)\r
719         {\r
720                 return;\r
721         }\r
722 \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
726         {\r
727                 ob->state = &s_orbatrixcurl1;\r
728         }\r
729 }\r
730 \r
731 /*\r
732 ===========================\r
733 =\r
734 = C_Orbatrix\r
735 =\r
736 ===========================\r
737 */\r
738 \r
739 void C_Orbatrix(objtype *ob, objtype *hit)\r
740 {\r
741         if (hit->obclass == stunshotobj)\r
742         {\r
743                 ExplodeShot(hit);\r
744                 ChangeState(ob, &s_orbatrixidle1);\r
745         }\r
746 }\r
747 \r
748 /*\r
749 ===========================\r
750 =\r
751 = R_Orbatrix\r
752 =\r
753 ===========================\r
754 */\r
755 \r
756 void R_Orbatrix(objtype *ob)\r
757 {\r
758         //\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
761         //\r
762         ob->y -= ob->temp3;\r
763         R_Walk(ob);\r
764         ob->y += ob->temp3;\r
765 \r
766         //\r
767         // update the float offset\r
768         //\r
769         ob->temp3 = ob->temp3 + ob->temp4 * tics * 4;\r
770         if (ob->temp3 > 8*PIXGLOBAL)\r
771         {\r
772                 ob->temp3 = 8*PIXGLOBAL;\r
773                 ob->temp4 = -1;\r
774         }\r
775         else if (ob->temp3 < -8*PIXGLOBAL)\r
776         {\r
777                 ob->temp3 = -8*PIXGLOBAL;\r
778                 ob->temp4 = 1;\r
779         }\r
780 }\r
781 \r
782 /*\r
783 ===========================\r
784 =\r
785 = R_OrbatrixBounce\r
786 =\r
787 ===========================\r
788 */\r
789 \r
790 void R_OrbatrixBounce(objtype *ob)\r
791 {\r
792         RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
793 \r
794         if (ob->hitnorth)\r
795         {\r
796                 ob->yspeed = -ob->yspeed;\r
797         }\r
798         if (ob->hitnorth || ob->hitwest || ob->hiteast)\r
799         {\r
800                 ob->xspeed = -ob->xspeed;\r
801                 SD_PlaySound(SND_ORBATRIXBOUNCE);\r
802                 if (ob->hitnorth && --ob->temp1 == 0)\r
803                 {\r
804                         ChangeState(ob, &s_orbatrixuncurl1);\r
805                         ob->temp2 = 24*PIXGLOBAL;\r
806                 }\r
807         }\r
808 }\r
809 \r
810 /*\r
811 ===========================\r
812 =\r
813 = T_OrbatrixCurl\r
814 =\r
815 ===========================\r
816 */\r
817 \r
818 void T_OrbatrixCurl(objtype *ob)\r
819 {\r
820         if (ob->temp3 >= 16)\r
821         {\r
822                 ob->xspeed = ob->xdir * 60;\r
823                 ob->yspeed = -32;\r
824                 ob->y -= ob->temp3;\r
825                 ob->temp1 = 5;  // bounce 5 times\r
826                 ob->state = ob->state->nextstate;\r
827         }\r
828         ob->needtoreact = true;\r
829 }\r
830 \r
831 /*\r
832 ===========================\r
833 =\r
834 = T_OrbatrixUncurl\r
835 =\r
836 ===========================\r
837 */\r
838 \r
839 void T_OrbatrixUncurl(objtype *ob)\r
840 {\r
841         ob->temp2 += (ytry = tics * -8);\r
842         if (ob->temp2 <= 0)\r
843         {\r
844                 ytry -= ob->temp2;\r
845                 ob->state = ob->state->nextstate;\r
846         }\r
847 }\r
848 \r
849 /*\r
850 ===========================\r
851 =\r
852 = C_OrbatrixBounce\r
853 =\r
854 ===========================\r
855 */\r
856 \r
857 void C_OrbatrixBounce(objtype *ob, objtype *hit)\r
858 {\r
859         if (hit->obclass == keenobj)\r
860         {\r
861                 KillKeen();\r
862         }\r
863         else if (hit->obclass == stunshotobj)\r
864         {\r
865                 ExplodeShot(hit);\r
866                 ob->xspeed = 0;\r
867         }\r
868 }\r
869 \r
870 /*\r
871 =============================================================================\r
872 \r
873                                                   BIP\r
874 \r
875 =============================================================================\r
876 */\r
877 \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
884 \r
885 /*\r
886 ===========================\r
887 =\r
888 = T_BipWalk\r
889 =\r
890 ===========================\r
891 */\r
892 \r
893 void T_BipWalk(objtype *ob)\r
894 {\r
895         if (ob->bottom == player->bottom)\r
896         {\r
897                 if (ob->right < player->left - 4*PIXGLOBAL)\r
898                         ob->xdir = 1;\r
899 \r
900                 if (ob->left > player->right + 4*PIXGLOBAL)\r
901                         ob->xdir = -1;\r
902         }\r
903         else if (US_RndT() < 0x10)\r
904         {\r
905                 ob->xdir = -ob->xdir;\r
906                 ob->state = &s_bipstand;\r
907         }\r
908 }\r
909 \r
910 /*\r
911 ===========================\r
912 =\r
913 = C_Bip\r
914 =\r
915 ===========================\r
916 */\r
917 \r
918 void C_Bip(objtype *ob, objtype *hit)\r
919 {\r
920         if (hit->obclass == keenobj && hit->ymove > 0)\r
921         {\r
922                 SD_PlaySound(SND_BIPSQUISH);\r
923                 ob->obclass = inertobj;\r
924                 ChangeState(ob, &s_bipsquished);\r
925         }\r
926 }\r
927 \r
928 /*\r
929 =============================================================================\r
930 \r
931                                                   BIPSHIP\r
932 \r
933 =============================================================================\r
934 */\r
935 \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
947 \r
948 /*\r
949 ===========================\r
950 =\r
951 = SpawnBipship\r
952 =\r
953 ===========================\r
954 */\r
955 \r
956 void SpawnBipship(Uint16 tileX, Uint16 tileY)\r
957 {\r
958         GetNewObj(false);\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
964         {\r
965                 new->xdir = 1;\r
966         }\r
967         else\r
968         {\r
969                 new->xdir = -1;\r
970         }\r
971         new->xspeed = new->xdir * 20;\r
972         NewState(new, &s_bipship);\r
973 }\r
974 \r
975 /*\r
976 ===========================\r
977 =\r
978 = R_BipShot\r
979 =\r
980 ===========================\r
981 */\r
982 \r
983 void R_BipShot(objtype *ob)\r
984 {\r
985         if (ob->hitnorth || ob->hitsouth || ob->hiteast || ob->hitwest)\r
986         {\r
987                 RemoveObj(ob);\r
988         }\r
989         else\r
990         {\r
991                 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
992         }\r
993 }\r
994 \r
995 /*\r
996 ===========================\r
997 =\r
998 = T_BipshipTurn\r
999 =\r
1000 ===========================\r
1001 */\r
1002 \r
1003 void T_BipshipTurn(objtype *ob)\r
1004 {\r
1005         AccelerateX(ob, ob->xdir, 20);\r
1006 }\r
1007 \r
1008 /*\r
1009 ===========================\r
1010 =\r
1011 = T_BipshipFly\r
1012 =\r
1013 ===========================\r
1014 */\r
1015 \r
1016 void T_BipshipFly(objtype *ob)\r
1017 {\r
1018         Uint16 far *map;\r
1019         Sint16 dir;\r
1020         Uint16 tile, tx, ty;\r
1021 \r
1022         AccelerateX(ob, ob->xdir, 20);\r
1023         dir = ob->xdir;\r
1024         if (player->bottom + TILEGLOBAL - ob->bottom <= 2*TILEGLOBAL)\r
1025         {\r
1026                 if (player->x < ob->x)\r
1027                 {\r
1028                         dir = -1;\r
1029                 }\r
1030                 else\r
1031                 {\r
1032                         dir = 1;\r
1033                 }\r
1034                 if (ob->xdir == dir && US_RndT() < tics*4)\r
1035                 {\r
1036                         SD_PlaySound(SND_KEENFIRE);\r
1037                         GetNewObj(true);\r
1038                         new->obclass = mshotobj;\r
1039                         new->active = ac_removable;\r
1040                         new->priority = 1;\r
1041                         if (ob->xdir == 1)\r
1042                         {\r
1043                                 new->x = ob->x + TILEGLOBAL;\r
1044                                 new->xspeed = 64;\r
1045                         }\r
1046                         else\r
1047                         {\r
1048                                 new->x = ob->x;\r
1049                                 new->xspeed = -64;\r
1050                         }\r
1051                         new->y = ob->y + 10*PIXGLOBAL;\r
1052                         new->yspeed = 16;\r
1053                         NewState(new, &s_bipshipshot);\r
1054                 }\r
1055         }\r
1056 \r
1057         tx = ob->tilemidx + dir*4;\r
1058         map = mapsegs[1] + mapbwidthtable[ob->tiletop]/2 + tx;\r
1059 \r
1060         for (ty = ob->tiletop; ty <= ob->tilebottom; ty++, map += mapwidth)\r
1061         {\r
1062                 tile = *map;\r
1063                 if (tinf[tile+EASTWALL] || tinf[tile+WESTWALL])\r
1064                 {\r
1065                         dir = -dir;\r
1066                         goto check_turn;\r
1067                 }\r
1068         }\r
1069         tile = *map;\r
1070         if (!tinf[tile+NORTHWALL])\r
1071         {\r
1072                 dir = -dir;\r
1073         }\r
1074 check_turn:\r
1075         if (dir != ob->xdir)\r
1076         {\r
1077                 ob->xdir = dir;\r
1078                 ChangeState(ob, &s_bipshipturn1);\r
1079         }\r
1080 }\r
1081 \r
1082 /*\r
1083 ===========================\r
1084 =\r
1085 = T_BipshipExplode\r
1086 =\r
1087 ===========================\r
1088 */\r
1089 \r
1090 void T_BipshipExplode(objtype *ob)\r
1091 {\r
1092         SD_PlaySound(SND_BIPSHIPEXPLODE);\r
1093 \r
1094         GetNewObj(true);\r
1095         new->obclass = inertobj;\r
1096         new->active = ac_yes;\r
1097         new->priority = 2;\r
1098         new->x = ob->x;\r
1099         new->y = ob->y - 24*PIXGLOBAL;\r
1100         NewState(new, &s_bipshipsmoke1);\r
1101 \r
1102         GetNewObj(true);\r
1103         new->obclass = bipobj;\r
1104         new->active = ac_yes;\r
1105         new->priority = 0;\r
1106         new->x = ob->x;\r
1107         new->y = ob->y - 8*PIXGLOBAL;\r
1108         if (US_RndT() < 0x80)\r
1109         {\r
1110                 new->xdir = 1;\r
1111         }\r
1112         else\r
1113         {\r
1114                 new->xdir = -1;\r
1115         }\r
1116         NewState(new, &s_bipstand);\r
1117 }\r
1118 \r
1119 /*\r
1120 ===========================\r
1121 =\r
1122 = C_Bipship\r
1123 =\r
1124 ===========================\r
1125 */\r
1126 \r
1127 void C_Bipship(objtype *ob, objtype *hit)\r
1128 {\r
1129         if (hit->obclass == stunshotobj)\r
1130         {\r
1131                 ExplodeShot(hit);\r
1132                 ChangeState(ob, &s_bipshipexplode1);\r
1133         }\r
1134 }\r
1135 \r
1136 /*\r
1137 =============================================================================\r
1138 \r
1139                                                   FLECT\r
1140 \r
1141 =============================================================================\r
1142 */\r
1143 \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
1151 \r
1152 /*\r
1153 ===========================\r
1154 =\r
1155 = SpawnFlect\r
1156 =\r
1157 ===========================\r
1158 */\r
1159 \r
1160 void SpawnFlect(Uint16 tileX, Uint16 tileY)\r
1161 {\r
1162         GetNewObj(false);\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
1169         {\r
1170                 new->xdir = 1;\r
1171         }\r
1172         else\r
1173         {\r
1174                 new->xdir = -1;\r
1175         }\r
1176         new->ydir = 1;\r
1177         NewState(new, &s_flectwalk1);\r
1178 }\r
1179 \r
1180 /*\r
1181 ===========================\r
1182 =\r
1183 = T_FlectStand\r
1184 =\r
1185 ===========================\r
1186 */\r
1187 \r
1188 void T_FlectStand(objtype *ob)\r
1189 {\r
1190         if (player->x < ob->x)\r
1191         {\r
1192                 if (ob->xdir != -1)\r
1193                 {\r
1194                         ob->state = &s_flectturn;\r
1195                         ob->xdir = -1;\r
1196                 }\r
1197                 else\r
1198                 {\r
1199                         ob->state = &s_flectwalk1;\r
1200                 }\r
1201         }\r
1202         else\r
1203         {\r
1204                 if (ob->xdir != 1)\r
1205                 {\r
1206                         ob->state = &s_flectturn;\r
1207                         ob->xdir = 1;\r
1208                 }\r
1209                 else\r
1210                 {\r
1211                         ob->state = &s_flectwalk1;\r
1212                 }\r
1213         }\r
1214 }\r
1215 \r
1216 /*\r
1217 ===========================\r
1218 =\r
1219 = T_FlectWalk\r
1220 =\r
1221 ===========================\r
1222 */\r
1223 \r
1224 void T_FlectWalk(objtype *ob)\r
1225 {\r
1226         if (player->x < ob->x && ob->xdir == 1)\r
1227         {\r
1228                 if (ob->xdir != -1)     // always true here!\r
1229                 {\r
1230                         ob->state = &s_flectturn;\r
1231                 }\r
1232                 ob->xdir = -1;\r
1233         }\r
1234 \r
1235         if (player->x > ob->x && ob->xdir == -1)\r
1236         {\r
1237                 if (ob->xdir != 1)      // always true here!\r
1238                 {\r
1239                         ob->state = &s_flectturn;\r
1240                 }\r
1241                 ob->xdir = 1;\r
1242         }\r
1243 \r
1244         if (US_RndT() < 0x20)\r
1245         {\r
1246                 ob->state = &s_flectstand;\r
1247         }\r
1248 }\r
1249 \r
1250 /*\r
1251 ===========================\r
1252 =\r
1253 = C_Flect\r
1254 =\r
1255 ===========================\r
1256 */\r
1257 \r
1258 void C_Flect(objtype *ob, objtype *hit)\r
1259 {\r
1260         if (hit->obclass == keenobj)\r
1261         {\r
1262                 ClipToSpriteSide(hit, ob);\r
1263         }\r
1264         else if (hit->obclass == stunshotobj)\r
1265         {\r
1266                 if (hit->xdir == 0)\r
1267                 {\r
1268                         StunObj(ob, hit, &s_flectstun);\r
1269                 }\r
1270                 else if (hit->xdir != ob->xdir)\r
1271                 {\r
1272                         // reflect shot:\r
1273                         hit->xdir = ob->xdir;\r
1274                         hit->temp4 = true;      // shot can now stun Keen\r
1275                         SD_PlaySound(SND_SHOTBOUNCE);\r
1276                 }\r
1277         }\r
1278 }\r
1279 \r
1280 /*\r
1281 ===========================\r
1282 =\r
1283 = R_Flect\r
1284 =\r
1285 ===========================\r
1286 */\r
1287 \r
1288 void R_Flect(objtype *ob)\r
1289 {\r
1290         if (ob->xdir == 1 && ob->hitwest)\r
1291         {\r
1292                 ob->x -= ob->xmove;\r
1293                 ob->xdir = -1;\r
1294                 ob->nothink = US_RndT() >> 5;\r
1295                 ChangeState(ob, ob->state);\r
1296         }\r
1297         else if (ob->xdir == -1 && ob->hiteast)\r
1298         {\r
1299                 ob->x -= ob->xmove;\r
1300                 ob->xdir = 1;\r
1301                 ob->nothink = US_RndT() >> 5;\r
1302                 ChangeState(ob, ob->state);\r
1303         }\r
1304         else if (!ob->hitnorth)\r
1305         {\r
1306                 ob->x -= ob->xmove;\r
1307                 ob->xdir = -ob->xdir;\r
1308                 ChangeState(ob, ob->state);\r
1309         }\r
1310         RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
1311 }\r