1 
2 #include "../stdai.h"
3 #include "omega.h"
4 #include "omega.fdh"
5 
6 #define OMEGA_RISE_HEIGHT			48
7 #define OMEGA_SINK_DEPTH			60
8 #define OMEGA_WAIT_TIME				7
9 #define OMEGA_SPEED					(1<<CSF)
10 
11 #define OMG_APPEAR					20  // this MUST be 20 because misery sets this to begin the battle
12 #define OMG_WAIT					30
13 #define OMG_MOVE					40
14 #define OMG_JAWS_OPEN				50
15 #define OMG_FIRE					60
16 #define OMG_JAWS_CLOSE				70
17 #define OMG_UNDERGROUND				80
18 #define OMG_JUMP					90
19 #define OMG_EXPLODING				100	// start fancy victory animation
20 #define OMG_EXPLODED				110 // full-screen flash in progress
21 
22 #define LEGD_MIN				(25<<CSF)
23 #define LEGD_MAX				(46<<CSF)
24 
25 #define OMEGA_DAMAGE			20
26 #define HP_TRIGGER_POINT		280
27 
28 enum Pieces
29 {
30 	LEFTLEG, RIGHTLEG,
31 	LEFTSTRUT, RIGHTSTRUT,
32 
33 	NUM_PIECES
34 };
35 
INITFUNC(AIRoutines)36 INITFUNC(AIRoutines)
37 {
38 	ONDEATH(OBJ_OMEGA_BODY, ondeath_omega_body);
39 	ONTICK(OBJ_OMEGA_SHOT, ai_omega_shot);
40 }
41 
42 /*
43 void c------------------------------() {}
44 */
45 
OnMapEntry(void)46 void OmegaBoss::OnMapEntry(void)
47 {
48 	memset(&omg, 0, sizeof(omg));
49 	memset(pieces, 0, sizeof(pieces));
50 
51 	omg.form = 1;
52 	omg.firefreq = 3;
53 	omg.shotxspd = 0x100;
54 	omg.startfiring = 20;
55 	omg.stopfiring = 80;
56 	omg.endfirestate = 200;
57 	omg.movetime = OMEGA_RISE_HEIGHT;
58 
59 	// *MUST* create in this order so that the z-order is correct
60 	pieces[LEFTLEG] = CreateObject(0, 0, OBJ_OMEGA_LEG);
61 	pieces[RIGHTLEG] = CreateObject(0, 0, OBJ_OMEGA_LEG);
62 	pieces[LEFTSTRUT] = CreateObject(0, 0, OBJ_OMEGA_STRUT);
63 	pieces[RIGHTSTRUT] = CreateObject(0, 0, OBJ_OMEGA_STRUT);
64 
65 	game.stageboss.object = CreateObject(0, 0, OBJ_OMEGA_BODY);
66 	game.stageboss.object->hp = omg.lasthp = 400;
67 
68 	game.stageboss.object->flags |= FLAG_SHOW_FLOATTEXT;
69 	game.stageboss.object->flags &= ~FLAG_SOLID_MUSHY;
70 	game.stageboss.object->sprite = SPR_OMG_CLOSED;
71 
72 	objprop[OBJ_OMEGA_BODY].shaketime = 0;		// we do our own shaketime
73 
74 	pieces[LEFTLEG]->dir = LEFT;
75 	pieces[RIGHTLEG]->dir = RIGHT;
76 	pieces[LEFTSTRUT]->dir = LEFT;
77 	pieces[RIGHTSTRUT]->dir = RIGHT;
78 
79 	omg.leg_descend = LEGD_MIN;
80 	pieces[LEFTLEG]->sprite = pieces[RIGHTLEG]->sprite = SPR_OMG_LEG_INAIR;
81 
82 	game.stageboss.object->x = ((217 * TILE_W) + 5) << CSF;
83 	game.stageboss.object->y = ((14 * TILE_H) - 5) << CSF;
84 	omg.orgx = game.stageboss.object->x;
85 	omg.orgy = game.stageboss.object->y;
86 
87 	omg.shaketimer = 0;
88 }
89 
OnMapExit(void)90 void OmegaBoss::OnMapExit(void)
91 {
92 	if (game.stageboss.object)
93 		game.stageboss.object->Delete();
94 }
95 
96 /*
97 void c------------------------------() {}
98 */
99 
Run(void)100 void OmegaBoss::Run(void)
101 {
102 	Object *&o = game.stageboss.object;
103 
104 	if (omg.defeated)
105 		return;
106 
107 	switch(o->state)
108 	{
109 		case 0:	break;	// waiting for trigger by script
110 
111 		case OMG_WAIT:	// waits for a moment then go to omg.nextstate
112 		{
113 			o->state++;
114 			omg.timer = 0;
115 		}
116 		case OMG_WAIT+1:
117 		{
118 			if (++omg.timer >= OMEGA_WAIT_TIME)
119 			{
120 				omg.timer = 0;
121 				o->state = omg.nextstate;
122 			}
123 		}
124 		break;
125 
126 		case OMG_APPEAR:
127 		{
128 			omg.timer = 0;
129 			o->frame = 0;
130 			o->state = OMG_MOVE;
131 			omg.movedir = -OMEGA_SPEED;
132 			o->flags |= FLAG_SOLID_MUSHY;
133 		}
134 		case OMG_MOVE:	// rising up/going back into ground
135 		{
136 			o->frame = 0;
137 			o->y += omg.movedir;
138 
139 			game.quaketime = 2;
140 
141 			omg.timer++;
142 			if ((omg.timer & 3) == 0) sound(SND_QUAKE);
143 
144 			if (omg.timer >= omg.movetime)
145 			{
146 				if (omg.movedir < 0)
147 				{	// was rising out of ground
148 					omg.nextstate = OMG_JAWS_OPEN;
149 					o->state = OMG_WAIT;
150 				}
151 				else
152 				{	// was going back into ground
153 					omg.timer = 0;
154 					o->state = OMG_UNDERGROUND;
155 					o->flags &= ~(FLAG_SOLID_MUSHY | FLAG_SOLID_BRICK);
156 				}
157 			}
158 		}
159 		break;
160 
161 		case OMG_JAWS_OPEN:			// jaws opening
162 		{
163 			o->state++;
164 			omg.animtimer = 0;
165 			sound(SND_JAWS);
166 			o->sprite = SPR_OMG_OPENED;			// select "open" bounding box
167 		}
168 		case OMG_JAWS_OPEN+1:
169 		{
170 			omg.animtimer++;
171 			if (omg.animtimer > 2)
172 			{
173 				omg.animtimer = 0;
174 				o->frame++;
175 				if (o->frame==3)
176 				{
177 					o->state = OMG_FIRE;
178 					omg.firecounter = 0;
179 					o->flags |= FLAG_SHOOTABLE;
180 				}
181 			}
182 		}
183 		break;
184 
185 		case OMG_FIRE:	// throwing out red stuff
186 		{
187 			omg.firecounter++;
188 
189 			if (omg.firecounter > omg.startfiring && omg.firecounter < omg.stopfiring)
190 			{
191 				if ((omg.firecounter % omg.firefreq)==0)
192 				{
193 					Object *shot;
194 
195 					sound(SND_EM_FIRE);
196 
197 					shot = SpawnObjectAtActionPoint(o, OBJ_OMEGA_SHOT);
198 					shot->xinertia = random(-omg.shotxspd, omg.shotxspd);
199 					shot->yinertia = -0x333;
200 					if (omg.form==2 || random(0, 9) < 8)
201 					{
202 						shot->sprite = SPR_OMG_BULLET_NORMAL;
203 						shot->flags = FLAG_SHOOTABLE;
204 					}
205 					else
206 					{
207 						shot->sprite = SPR_OMG_BULLET_HARD;
208 						shot->flags = (FLAG_SHOOTABLE | FLAG_INVULNERABLE);
209 					}
210 
211 					shot->timer = (random(0, 7) >= 4) ? random(300, 400):0;
212 					shot->damage = 4;
213 				}
214 			}
215 			else if (omg.firecounter >= omg.endfirestate || sound_is_playing(SND_MISSILE_HIT))
216 			{	// snap jaws shut
217 				omg.animtimer = 0;
218 				o->state = OMG_JAWS_CLOSE;
219 				sound(SND_JAWS);
220 			}
221 		}
222 		break;
223 
224 		case OMG_JAWS_CLOSE:	// jaws closing
225 		{
226 			omg.animtimer++;
227 			if (omg.animtimer > 2)
228 			{
229 				omg.animtimer = 0;
230 
231 				o->frame--;
232 				if (o->frame == 0)
233 				{
234 					sound_stop(SND_JAWS);
235 					sound(SND_BLOCK_DESTROY);
236 
237 					o->sprite = SPR_OMG_CLOSED;		// select "closed" bounding box
238 
239 					o->flags &= ~FLAG_SHOOTABLE;
240 					o->damage = 0;
241 
242 					if (omg.form == 1)
243 					{	// form 1: return to sand
244 						o->state = OMG_WAIT;
245 						omg.nextstate = OMG_MOVE;
246 						omg.movedir = OMEGA_SPEED;
247 						omg.movetime = OMEGA_SINK_DEPTH;
248 					}
249 					else
250 					{	// form 2: jump
251 						sound(SND_FUNNY_EXPLODE);
252 						if (o->x < player->x) o->xinertia = 0xC0;
253 										 else o->xinertia = -0xC0;
254 						o->state = OMG_JUMP;
255 						o->yinertia = -0x5FF;
256 						omg.orgy = o->y;
257 					}
258 				}
259 			}
260 
261 			// hurt player if he was standing in the middle when the jaws shut
262 			if (player->riding == o)
263 			{
264 				hurtplayer(OMEGA_DAMAGE);
265 			}
266 		}
267 		break;
268 
269 		case OMG_UNDERGROUND:		// underground waiting to reappear
270 		{
271 			if (++omg.timer >= 120)
272 			{
273 				omg.timer = 0;
274 				o->state = OMG_APPEAR;
275 
276 				o->x = omg.orgx + (random(-64, 64) << CSF);
277 				o->y = omg.orgy;
278 				omg.movetime = OMEGA_RISE_HEIGHT;
279 
280 				// switch to jumping out of ground when we get low on life
281 				if (omg.form==1 && o->hp <= HP_TRIGGER_POINT)
282 				{
283 					o->flags |= FLAG_SOLID_MUSHY;
284 
285 					omg.form = 2;
286 					omg.firefreq = 5;
287 					omg.shotxspd = 0x155;
288 					omg.startfiring = 0;
289 					omg.stopfiring = 30;
290 					omg.endfirestate = 50;
291 					omg.movetime = OMEGA_RISE_HEIGHT+3;
292 				}
293 			}
294 		}
295 		break;
296 
297 		case OMG_JUMP:	// init for jump
298 		{
299 			omg.orgy = o->y;
300 			o->state++;
301 			omg.timer = 0;
302 		}
303 		case OMG_JUMP+1:	// jumping
304 		{
305 			o->yinertia += 0x24;
306 			if (o->yinertia > 0x5ff) o->yinertia = 0x5ff;
307 
308 			if (o->yinertia > 0)
309 			{	// coming down
310 
311 				pieces[LEFTLEG]->sprite = pieces[RIGHTLEG]->sprite = SPR_OMG_LEG_ONGROUND;
312 
313 				// retract legs a little when we hit the ground
314 				if (pieces[LEFTLEG]->blockd || pieces[RIGHTLEG]->blockd)
315 				{
316 					o->xinertia = 0;
317 					omg.leg_descend -= o->yinertia;
318 					if (++omg.timer >= 3)
319 					{
320 						o->yinertia = 0;
321 						o->state = OMG_JAWS_OPEN;
322 					}
323 				}
324 
325 				// --- squash player if we land on him -------------
326 				// if top of player is higher than bottom of our bounding box
327 				// but bottom of player's bounding box is not...
328 				if (player->blockd)
329 				{
330 					int omg_bottom = o->y + (sprites[o->sprite].solidbox.y2 << CSF);
331 					if (player->y <= omg_bottom)
332 					{
333 						if (player->y + (sprites[player->sprite].solidbox.y2 << CSF) >= omg_bottom)
334 						{
335 							if (hitdetect(o, player))	// easy way to verify the X's are lined up
336 							{	// SQUISH!
337 								hurtplayer(OMEGA_DAMAGE);
338 							}
339 						}
340 					}
341 				}
342 			}
343 			else
344 			{	// jumping up; extend legs
345 				omg.leg_descend = (omg.orgy - o->y) + LEGD_MIN;
346 				if (omg.leg_descend > LEGD_MAX) omg.leg_descend = LEGD_MAX;
347 				pieces[LEFTLEG]->sprite = pieces[RIGHTLEG]->sprite = SPR_OMG_LEG_INAIR;
348 			}
349 		}
350 		break;
351 
352 		/// victory
353 		case OMG_EXPLODING:
354 		{
355 			omg.timer = 0;
356 			o->state++;
357 		}
358 		case OMG_EXPLODING+1:
359 		{
360 			int x, y;
361 
362 			o->xinertia = o->yinertia = 0;
363 
364 			x = o->CenterX() + (random(-48, 48)<<CSF);
365 			y = o->CenterY() + (random(-48, 24)<<CSF);
366 			SmokePuff(x, y);
367 			effect(x, y, EFFECT_BOOMFLASH);
368 
369 			game.quaketime = 2;
370 
371 			if ((omg.timer % 12)==0) sound(SND_ENEMY_HURT_BIG);
372 
373 			if (++omg.timer > 100)
374 			{
375 				omg.timer = 0;
376 				starflash.Start(o->CenterX(), o->CenterY());
377 				o->state = OMG_EXPLODED;
378 			}
379 			else if (omg.timer==24)
380 			{
381 				StartScript(210);
382 			}
383 		}
384 		break;
385 
386 		case OMG_EXPLODED:
387 		{
388 			game.quaketime = 40;
389 
390 			if (++omg.timer > 50)
391 			{
392 				o->Delete();
393 				for(int i=0;i<NUM_PIECES;i++)
394 					pieces[i]->Delete();
395 
396 				omg.defeated = true;
397 				return;
398 			}
399 		}
400 		break;
401 	}
402 
403 	// implement shaking when shot
404 	// we do it manually instead of used the usual shared code
405 	// because we want all the pieces to shake at once
406 	if (o->hp != omg.lasthp && !omg.shaketimer)
407 	{
408 		omg.shaketimer = 3;
409 		// why did I write this? anyway, I'm sure it's important
410 		if (o->x > player->x) o->display_xoff = -1;
411 						 else o->display_xoff = 1;
412 
413 		omg.lasthp = o->hp;
414 	}
415 	if (omg.shaketimer)
416 	{
417 		int xoff = -o->display_xoff;
418 
419 		if (!--omg.shaketimer) xoff = 0;
420 
421 		o->display_xoff = xoff;
422 		pieces[LEFTLEG]->display_xoff = xoff;
423 		pieces[RIGHTLEG]->display_xoff = xoff;
424 		pieces[LEFTSTRUT]->display_xoff = xoff;
425 		pieces[RIGHTSTRUT]->display_xoff = xoff;
426 	}
427 
428 	if (o->state)
429 	{
430 		o->blockl |= pieces[LEFTLEG]->blockl;
431 		o->blockr |= pieces[RIGHTLEG]->blockr;
432 
433 		pieces[LEFTLEG]->x = o->x - (4 << CSF); pieces[LEFTLEG]->y = o->y + omg.leg_descend;
434 		pieces[RIGHTLEG]->x = o->x + (38 << CSF); pieces[RIGHTLEG]->y = o->y + omg.leg_descend;
435 		pieces[LEFTSTRUT]->x = o->x + (9 << CSF); pieces[LEFTSTRUT]->y = o->y + (27 << CSF);
436 		pieces[RIGHTSTRUT]->x = o->x + (43 << CSF); pieces[RIGHTSTRUT]->y = o->y + (27 << CSF);
437 	}
438 }
439 
ondeath_omega_body(Object * o)440 void ondeath_omega_body(Object *o)
441 {
442 	o->flags &= ~FLAG_SHOOTABLE;
443 	KillObjectsOfType(OBJ_OMEGA_SHOT);
444 
445 	game.stageboss.SetState(OMG_EXPLODING);
446 }
447 
448 /*
449 void c------------------------------() {}
450 */
451 
ai_omega_shot(Object * o)452 void ai_omega_shot(Object *o)
453 {
454 	o->nxflags |= NXFLAG_FOLLOW_SLOPE;
455 
456 	o->yinertia += 5;
457 	if (o->blockd) o->yinertia = -0x100;
458 
459 	if (o->blockl || o->blockr) o->xinertia = -o->xinertia;
460 	if (o->blocku) o->yinertia = -o->yinertia;
461 
462 	if (++o->animtimer > 2) { o->frame ^= 1; o->animtimer = 0; }
463 
464 	if (++o->timer > 750)
465 	{
466 		effect(o->CenterX(), o->CenterY(), EFFECT_FISHY);
467 		o->Delete();
468 	}
469 }
470 
471