1 #include "ironhead.h"
2 
3 #include "../../ObjManager.h"
4 #include "../../autogen/sprites.h"
5 #include "../../caret.h"
6 #include "../../common/misc.h"
7 #include "../../game.h"
8 #include "../../graphics/Renderer.h"
9 #include "../../map.h"
10 #include "../../player.h"
11 #include "../../sound/SoundManager.h"
12 #include "../../tsc.h"
13 #include "../ai.h"
14 #include "../stdai.h"
15 
16 using namespace NXE::Graphics;
17 
18 #define ARENA_TOP 2
19 #define ARENA_BOTTOM 13
20 
21 #define IRONH_SPAWN_FISHIES 100
22 #define IRONH_SWIM 250
23 #define IRONH_DEFEATED 1000
24 
INITFUNC(AIRoutines)25 INITFUNC(AIRoutines)
26 {
27   ONDEATH(OBJ_IRONH, ondeath_ironhead);
28 
29   ONTICK(OBJ_IRONH_FISHY, ai_ironh_fishy);
30   ONTICK(OBJ_IRONH_SHOT, ai_ironh_shot);
31 
32   ONTICK(OBJ_BRICK_SPAWNER, ai_brick_spawner);
33   ONTICK(OBJ_IRONH_BRICK, ai_ironh_brick);
34 
35   ONTICK(OBJ_IKACHAN_SPAWNER, ai_ikachan_spawner);
36   ONTICK(OBJ_IKACHAN, ai_ikachan);
37 
38   ONTICK(OBJ_MOTION_WALL, ai_motion_wall);
39 
40   objprop[OBJ_IRONH].hurt_sound = NXE::Sound::SFX::SND_ENEMY_HURT_COOL;
41 }
42 
43 /*
44 void c------------------------------() {}
45 */
46 
OnMapEntry()47 void IronheadBoss::OnMapEntry()
48 {
49   o         = CreateObject(0, 0, OBJ_IRONH);
50   o->damage = 10;
51   o->hp     = 400;
52   o->state  = IRONH_SPAWN_FISHIES;
53 
54   game.stageboss.object = o;
55   this->hittimer        = 0;
56 }
57 
OnMapExit()58 void IronheadBoss::OnMapExit()
59 {
60   o                     = NULL;
61   game.stageboss.object = NULL;
62 }
63 
64 /*
65 void c------------------------------() {}
66 */
67 
ironh_smokecloud(Object * o)68 static void ironh_smokecloud(Object *o)
69 {
70   Object *smoke;
71 
72   smoke = CreateObject(o->CenterX() + (random(-128, 128) * CSFI), o->CenterY() + (random(-64, 64) * CSFI),
73                        OBJ_SMOKE_CLOUD);
74 
75   smoke->xinertia = random(-128, 128);
76   smoke->yinertia = random(-128, 128);
77 }
78 
Run(void)79 void IronheadBoss::Run(void)
80 {
81   if (!o)
82     return;
83 
84   switch (o->state)
85   {
86     case IRONH_SPAWN_FISHIES:
87     {
88       o->timer = 0;
89       o->state++;
90     }
91     case IRONH_SPAWN_FISHIES + 1: // wave of fishies comes in
92     {
93       if (++o->timer > 50)
94       {
95         o->timer = 0;
96         o->state = IRONH_SWIM;
97       }
98 
99       if ((o->timer & 3) == 0)
100       {
101         CreateObject((random(15, 18) * TILE_W) * CSFI, (random(ARENA_TOP, ARENA_BOTTOM) * TILE_H) * CSFI,
102                      OBJ_IRONH_FISHY);
103       }
104     }
105     break;
106 
107     case IRONH_SWIM: // swimming attack
108     {
109       o->state++;
110 
111       if (o->dir == RIGHT)
112       { // coming up on player from left
113         o->x = 0x0e000;
114         o->y = player->y;
115       }
116       else
117       { // returning from right side of screen
118         o->x = 0x5a000;
119         o->y = (random(ARENA_TOP, ARENA_BOTTOM) * TILE_H) * CSFI;
120       }
121 
122       o->xmark = o->x;
123       o->ymark = o->y;
124 
125       o->yinertia = random(-0x200, 0x200);
126       o->xinertia = random(-0x200, 0x200);
127 
128       o->flags |= FLAG_SHOOTABLE;
129     }
130     case IRONH_SWIM + 1:
131     {
132       ANIMATE(2, 0, 7);
133 
134       if (o->dir == RIGHT)
135       {
136         o->xmark += 0x400;
137       }
138       else
139       {
140         o->xmark -= 0x200;
141         o->ymark += (o->ymark < player->y) ? 0x200 : -0x200;
142       }
143 
144       // debugXline(o->xmark, 255,0,0);
145       // debugYline(o->ymark, 0,255,0);
146       o->xinertia += (o->x > o->xmark) ? -8 : 8;
147       o->yinertia += (o->y > o->ymark) ? -8 : 8;
148 
149       LIMITY(0x200);
150 
151       if (o->dir == RIGHT)
152       {
153         if (o->x > 0x5a000)
154         {
155           o->dir   = LEFT;
156           o->state = IRONH_SPAWN_FISHIES;
157         }
158       }
159       else
160       {
161         if (o->x < 0x12000)
162         {
163           o->dir   = RIGHT;
164           o->state = IRONH_SPAWN_FISHIES;
165         }
166       }
167 
168       if (o->dir == LEFT)
169       {
170         // fire bullets at player when retreating
171         switch (++o->timer)
172         {
173           case 300:
174           case 310:
175           case 320:
176           {
177             Object *shot   = SpawnObjectAtActionPoint(o, OBJ_IRONH_SHOT);
178             shot->xinertia = (random(-3, 0) * CSFI);
179             shot->yinertia = (random(-3, 3) * CSFI);
180             NXE::Sound::SoundManager::getInstance()->playSfx(NXE::Sound::SFX::SND_EM_FIRE);
181           }
182           break;
183         }
184       }
185     }
186     break;
187 
188     case IRONH_DEFEATED:
189     {
190       NXE::Sound::SoundManager::getInstance()->playSfx(NXE::Sound::SFX::SND_EXPL_SMALL);
191       o->state = IRONH_DEFEATED + 1;
192       o->flags &= ~FLAG_SHOOTABLE;
193       o->frame    = 8;
194       o->damage   = 0;
195       o->xmark    = o->x;
196       o->ymark    = o->y;
197       o->xinertia = o->yinertia = 0;
198       o->timer                  = 0;
199       KillObjectsOfType(OBJ_IRONH_FISHY);
200       KillObjectsOfType(OBJ_IRONH_BRICK);
201       KillObjectsOfType(OBJ_BRICK_SPAWNER);
202       game.quaketime = 20;
203 
204       for (int i = 0; i < 32; i++)
205         ironh_smokecloud(o);
206     }
207     case IRONH_DEFEATED + 1: // retreat back to left...
208     {
209       o->xmark -= (1 * CSFI);
210 
211       o->x = o->xmark + (random(-1, 1) * CSFI);
212       o->y = o->ymark + (random(-1, 1) * CSFI);
213 
214       o->timer++;
215       if ((o->timer & 3) == 0)
216         ironh_smokecloud(o);
217     }
218     break;
219   }
220 
221   // show pink "hit" frame when he's taking damage
222   o->sprite = SPR_IRONH;
223   if (o->shaketime)
224   {
225     this->hittimer++;
226     if (this->hittimer & 2)
227     {
228       o->sprite = SPR_IRONH_HURT;
229     }
230   }
231   else
232   {
233     this->hittimer = 0;
234   }
235 }
236 
ondeath_ironhead(Object * o)237 void ondeath_ironhead(Object *o)
238 {
239   game.tsc->StartScript(1000);
240 }
241 
242 /*
243 void c------------------------------() {}
244 */
245 
ai_ironh_fishy(Object * o)246 void ai_ironh_fishy(Object *o)
247 {
248   switch (o->state)
249   {
250     case 0:
251     {
252       o->state     = 10;
253       o->animtimer = 0;
254       o->yinertia  = random(-0x200, 0x200);
255       o->xinertia  = 0x800;
256     }
257     case 10: // harmless fishy
258     {
259       ANIMATE(2, 0, 1);
260       if (o->xinertia < 0)
261       {
262         o->damage = 3;
263         o->state  = 20;
264       }
265     }
266     break;
267 
268     case 20: // puffer fish
269     {
270       ANIMATE(2, 2, 3);
271 
272       if (o->x < (48 * CSFI))
273         o->Delete();
274     }
275     break;
276   }
277 
278   if (o->blocku)
279     o->yinertia = 0x200;
280   if (o->blockd)
281     o->yinertia = -0x200;
282   o->xinertia -= 0x0c;
283 }
284 
ai_ironh_shot(Object * o)285 void ai_ironh_shot(Object *o)
286 {
287   if (!o->state)
288   {
289     if (++o->timer > 20)
290     {
291       o->state    = 1;
292       o->xinertia = o->yinertia = 0;
293       o->timer2                 = 0;
294     }
295   }
296   else
297   {
298     o->xinertia += 0x20;
299   }
300 
301   ANIMATE(0, 0, 2);
302 
303   if (++o->timer2 > 100 && !o->onscreen)
304   {
305     o->Delete();
306   }
307 
308   if ((o->timer2 & 3) == 1)
309     NXE::Sound::SoundManager::getInstance()->playSfx(NXE::Sound::SFX::SND_IRONH_SHOT_FLY);
310 }
311 
ai_brick_spawner(Object * o)312 void ai_brick_spawner(Object *o)
313 {
314   Object *brick;
315 
316   if (!o->state)
317   {
318     o->state = 1;
319     o->timer = random(0, 200);
320   }
321 
322   if (!o->timer)
323   { // time to spawn a block
324     o->state   = 0;
325     brick      = CreateObject(o->x, o->y + (random(-20, 20) * CSFI), OBJ_IRONH_BRICK);
326     brick->dir = o->dir;
327   }
328   else
329     o->timer--;
330 }
331 
ai_ironh_brick(Object * o)332 void ai_ironh_brick(Object *o)
333 {
334   if (!o->state)
335   {
336     int r = random(0, 9);
337     if (r == 9)
338     {
339       o->sprite = SPR_IRONH_BIGBRICK;
340     }
341     else
342     {
343       o->sprite = SPR_IRONH_BRICK;
344       o->frame  = r;
345     }
346 
347     o->xinertia = random(0x100, 0x200);
348     o->xinertia *= (o->dir == LEFT) ? -2 : 2;
349 
350     o->yinertia = random(-0x200, 0x200);
351     o->state    = 1;
352   }
353 
354   // bounce off the walls
355   if (o->yinertia < 0 && o->y <= (16 * CSFI))
356   {
357     effect(o->CenterX(), o->y, EFFECT_BONKPLUS);
358     o->yinertia = -o->yinertia;
359   }
360 
361   if (o->yinertia > 0 && (o->Bottom() >= (239 * CSFI)))
362   {
363     effect(o->CenterX(), o->Bottom(), EFFECT_BONKPLUS);
364     o->yinertia = -o->yinertia;
365   }
366 
367   if ((o->xinertia < 0 && (o->x < -0x2000)) || (o->x > (map.xsize * TILE_W) * CSFI))
368   {
369     o->Delete();
370   }
371 }
372 
373 /*
374 void c------------------------------() {}
375 */
376 
ai_ikachan_spawner(Object * o)377 void ai_ikachan_spawner(Object *o)
378 {
379   switch (o->state)
380   {
381     case 0:
382     {
383       // oops player got hurt--no ikachans for you!
384       // the deletion of the object causes the flag matching it's id2 to be set,
385       // which is how the scripts know not to give the alien medal.
386       if (player->hurt_time != 0)
387         o->Delete();
388     }
389     break;
390 
391     case 10: // yay spawn ikachans!
392     {
393       o->timer++;
394       if ((o->timer & 3) == 1)
395       {
396         CreateObject(o->x, o->y + ((random(0, 13) * TILE_H) * CSFI), OBJ_IKACHAN);
397       }
398     }
399     break;
400   }
401 }
402 
ai_ikachan(Object * o)403 void ai_ikachan(Object *o)
404 {
405   switch (o->state)
406   {
407     case 0:
408     {
409       o->state = 1;
410       o->timer = random(3, 20);
411     }
412     case 1: // he pushes ahead
413     {
414       if (--o->timer <= 0)
415       {
416         o->state    = 2;
417         o->timer    = random(10, 50);
418         o->frame    = 1;
419         o->xinertia = 0x600;
420       }
421     }
422     break;
423 
424     case 2: // after a short time his tentacles look less whooshed-back
425     {
426       if (--o->timer <= 0)
427       {
428         o->state    = 3;
429         o->timer    = random(40, 50);
430         o->frame    = 2;
431         o->yinertia = random(-0x100, 0x100);
432       }
433     }
434     break;
435 
436     case 3: // gliding
437     {
438       if (--o->timer <= 0)
439       {
440         o->state = 1;
441         o->timer = 0;
442         o->frame = 0;
443       }
444 
445       o->xinertia -= 0x10;
446     }
447     break;
448   }
449 
450   if (o->x > 720 * CSFI)
451     o->Delete();
452 }
453 
ai_motion_wall(Object * o)454 void ai_motion_wall(Object *o) // the walls at the top and bottom of the arena
455 {
456   o->x -= (6 * CSFI);
457 
458   if (o->x < (((9 * TILE_W) - 8) * CSFI))
459     o->x += (Renderer::getInstance()->screenWidth + 160 + 32) * CSFI;
460 }
461