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