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