1
2 #include "../stdai.h"
3 #include "balrog.fdh"
4
INITFUNC(AIRoutines)5 INITFUNC(AIRoutines)
6 {
7 ONSPAWN(OBJ_BALROG, onspawn_balrog);
8 ONTICK(OBJ_BALROG, ai_balrog);
9
10 ONTICK(OBJ_BALROG_DROP_IN, ai_balrog_drop_in);
11 ONTICK(OBJ_BALROG_BUST_IN, ai_balrog_bust_in);
12 }
13
14 /*
15 void c------------------------------() {}
16 */
17
onspawn_balrog(Object * o)18 void onspawn_balrog(Object *o)
19 {
20 // in the Boulder Chamber boss fight, Balrog is supposed to go BEHIND Curly.
21 if (game.curmap == STAGE_BOULDER_CHAMBER)
22 {
23 Object *curly = Objects::FindByType(OBJ_CURLY);
24 if (curly)
25 {
26 o->PushBehind(curly);
27
28 // nasty hack to adjust his starting position in the post-fight cutscene.
29 // I'm not sure why this is otherwise wrong.
30 if (GetCurrentScript() == 600)
31 {
32 o->x -= (6 << CSF);
33 }
34 }
35 }
36 }
37
38
ai_balrog(Object * o)39 void ai_balrog(Object *o)
40 {
41 bool fall = true;
42
43 // he is greenish when he first appears in Gum Room
44 if (DoesCurrentStageUseSpriteset(NPCSET_FROG))
45 o->sprite = SPR_BALROG_GREEN;
46
47 switch(o->state)
48 {
49 case 0:
50 {
51 o->flags &= ~FLAG_IGNORE_SOLID;
52 o->xinertia = 0;
53 o->balrog.smoking = false;
54
55 o->frame = 0;
56 randblink(o, 4, 8);
57 }
58 break;
59
60 case 10: // he jumps and flys away
61 o->xinertia = 0;
62 o->frame = 2;
63 o->timer = 0;
64 o->state++;
65 case 11:
66 {
67 if (++o->timer <= 20) break;
68
69 o->state++;
70 o->yinertia = -0x800;
71 o->flags |= FLAG_IGNORE_SOLID;
72 }
73 case 12:
74 {
75 fall = false;
76 o->frame = 3;
77 o->yinertia -= 0x10;
78 if (o->y < 0)
79 {
80 o->Delete();
81 sound(SND_QUAKE);
82 game.quaketime = 30;
83 }
84 }
85 break;
86
87 // he looks shocked and shakes, then flys away
88 // used when he is "hit by something"
89 case 20:
90 {
91 o->state = 21;
92 o->frame = 5;
93 o->xinertia = 0;
94 o->timer = o->timer2 = 0;
95 SmokeClouds(o, 4, 8, 8);
96 sound(SND_BIG_CRASH);
97 o->balrog.smoking = 1;
98 }
99 case 21:
100 {
101 o->timer2++;
102 o->x += ((o->timer2 >> 1) & 1) ? (1<<CSF) : -(1<<CSF);
103
104 if (++o->timer > 100)
105 o->state = 10;
106
107 o->yinertia += 0x20;
108 LIMITY(0x5ff);
109 }
110 break;
111
112 case 30: // he smiles for a moment
113 o->frame = 6;
114 o->timer = 0;
115 o->state = 31;
116 case 31: if (++o->timer > 100) o->state = o->frame = 0;
117 break;
118
119 // flashing white (spell casted on him)
120 // this only works in Gum Room before balfrog fight, as the normal
121 // non-greenish spritesheet doesn't include the required frame.
122 case 40:
123 o->state = 41;
124 o->animtimer = 0;
125 o->animframe = 0;
126 case 41:
127 {
128 static const int flash_seq[] = { 5, 7 };
129 o->animate_seq(1, flash_seq, 2);
130 }
131 break;
132 case 42:
133 o->timer = 0;
134 o->state = 43;
135 case 43:
136 // flashing visibility
137 // (transforming into Balfrog stage boss;
138 // our flashing is interlaced with his)
139 o->timer++;
140 o->invisible = (o->timer & 2) ? false : true;
141 break;
142
143 case 50: // he faces away
144 o->frame = 8;
145 o->xinertia = 0;
146 break;
147
148 case 60: // he walks
149 o->state = 61;
150 balrog_walk_init(o);
151 case 61:
152 {
153 balrog_walk_animation(o);
154 XMOVE(0x200);
155 }
156 break;
157
158 // he is teleported away (looking distressed)
159 // this is when he is sent to Labyrinth at end of Sand Zone
160 case 70:
161 o->xinertia = 0;
162 o->timer = 0;
163 o->frame = 7;
164 o->state++;
165 case 71:
166 if (DoTeleportOut(o, 2))
167 o->Delete();
168 break;
169
170 case 80: // hands up and shakes
171 o->frame = 5;
172 o->state = 81;
173 case 81:
174 {
175 if (++o->timer & 2)
176 o->x += (1 << CSF);
177 else
178 o->x -= (1 << CSF);
179 }
180 break;
181
182 // fly up and lift Curly & PNPC
183 // (post-Ballos ending scene)
184 case 100:
185 {
186 o->state = 101;
187 o->timer = 0;
188 o->frame = 2; // prepare for jump
189 }
190 case 101:
191 {
192 if (++o->timer > 20)
193 {
194 o->state = 102;
195 o->timer = 0;
196 o->frame = 3; // fly up
197
198 DeleteObjectsOfType(OBJ_NPC_PLAYER);
199 DeleteObjectsOfType(OBJ_CURLY);
200
201 CreateObject(0, 0, OBJ_BALROG_PASSENGER, 0, 0, LEFT)->linkedobject = o;
202 CreateObject(0, 0, OBJ_BALROG_PASSENGER, 0, 0, RIGHT)->linkedobject = o;
203
204 o->yinertia = -0x800;
205 o->flags |= FLAG_IGNORE_SOLID; // so can fly through ceiling
206 fall = false;
207 }
208 }
209 break;
210 case 102: // flying up during escape seq
211 {
212 fall = false;
213
214 // bust through ceiling
215 int y = ((o->y + (4<<CSF)) >> CSF) / TILE_H;
216 if (y < 35 && y >= 0)
217 {
218 int x = (o->CenterX() >> CSF) / TILE_W;
219
220 if (map.tiles[x][y] != 0)
221 {
222 // smoke needs to go at the bottom of z-order or you can't
223 // see any of the characters through all the smoke.
224 map_ChangeTileWithSmoke(x, y, 0, 4, false, lowestobject);
225 map_ChangeTileWithSmoke(x-1, y, 0, 4, false, lowestobject);
226 map_ChangeTileWithSmoke(x+1, y, 0, 4, false, lowestobject);
227
228 megaquake(10, 0);
229 sound(SND_MISSILE_HIT);
230 }
231 }
232
233 if (o->Bottom() < -(20<<CSF))
234 {
235 quake(30, 0);
236 o->Delete();
237 }
238 }
239 break;
240
241 case 500: // used during Balfrog death scene
242 {
243 fall = false;
244 }
245 break;
246 }
247
248 if (o->balrog.smoking)
249 {
250 if (++o->balrog.smoketimer > 20 || !random(0, 16))
251 {
252 SmokeClouds(o, 1, 4, 4);
253 o->balrog.smoketimer = 0;
254 }
255 }
256
257 if (fall)
258 {
259 o->yinertia += 0x20;
260 if (o->yinertia >= 0x5ff)
261 o->yinertia = 0x5ff;
262 }
263 }
264
ai_balrog_drop_in(Object * o)265 void ai_balrog_drop_in(Object *o)
266 {
267 switch(o->state)
268 {
269 case 0:
270 {
271 // he is greenish when he first appears in Gum Room
272 if (DoesCurrentStageUseSpriteset(NPCSET_FROG))
273 o->sprite = SPR_BALROG_GREEN;
274
275 // z-order hacking
276 if (game.curmap == STAGE_SEAL_CHAMBER_2)
277 o->PushBehind(lowestobject);
278
279 o->state = 1;
280 o->frame = 3; // falling
281 o->flags |= FLAG_IGNORE_SOLID;
282 }
283 case 1:
284 {
285 // since balrog often falls through the ceiling we must wait until he is free-falling
286 // before we start checking to see if he hit the floor
287 if (!o->blockd && !o->blocku)
288 {
289 o->state = 2;
290 o->flags &= ~FLAG_IGNORE_SOLID;
291 }
292 }
293 break;
294
295 case 2: // free-falling
296 if (o->blockd)
297 {
298 o->yinertia = 0;
299 o->frame = 2;
300 o->state = 3;
301 o->timer = 0;
302
303 SmokeSide(o, 4, DOWN);
304 quake(30);
305 }
306 break;
307
308 case 3: // landed
309 if (++o->timer > 20) { o->state = 4; o->frame = 0; }
310 break;
311 }
312
313 if (o->state == 1 || o->state == 2)
314 o->yinertia += 0x20;
315 }
316
317
318
319 // Balrog busting in the door of the Shack.
320 // he exists like this for only a moment, then the script
321 // changes him to a standard OBJ_BALROG.
ai_balrog_bust_in(Object * o)322 void ai_balrog_bust_in(Object *o)
323 {
324 switch(o->state)
325 {
326 case 0:
327 SmokeClouds(o, 10, 8, 8);
328 o->y += (10 << CSF);
329 o->yinertia = -0x100;
330
331 sound(SND_BLOCK_DESTROY);
332 quake(30);
333
334 o->state = 1;
335 o->frame = 3;
336 case 1: // falling the short distance to ground
337 {
338 o->yinertia += 0x10;
339 if (o->yinertia > 0 && o->blockd)
340 {
341 o->state = 2;
342 o->frame = 2;
343 o->timer = 0;
344
345 quake(30);
346 }
347 }
348 break;
349
350 // landing animation
351 case 2:
352 {
353 if (++o->timer > 16)
354 {
355 o->state = 3;
356 o->frame = 0;
357 o->animtimer = 0;
358 }
359 }
360 break;
361
362 // standing and blinking
363 case 3:
364 case 4:
365 {
366 o->frame = 0;
367 randblink(o, 4, 16, 100);
368 }
369 break;
370 }
371
372 LIMITY(0x5FF);
373 }
374
375 /*
376 void c------------------------------() {}
377 */
378
balrog_walk_init(Object * o)379 void balrog_walk_init(Object *o)
380 {
381 o->frame = 9;
382 o->animtimer = 0;
383 }
384
balrog_walk_animation(Object * o)385 void balrog_walk_animation(Object *o)
386 {
387 if (++o->animtimer > 3)
388 {
389 o->animtimer = 0;
390 o->frame++;
391
392 if (o->frame == 10 || o->frame == 11)
393 sound(SND_THUD);
394
395 if (o->frame > 12)
396 o->frame = 9;
397 }
398 }
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418