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