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