1 
2 #include "../stdai.h"
3 #include "undead_core.h"
4 #include "undead_core.fdh"
5 
6 static struct
7 {
8 	SIFPoint offset;	// offset from main object
9 	SIFRect rect;		// actual bbox rect
10 }
11 core_bboxes[] =
12 {
13 	{  { 0, -32 },  { -40, -16, 40, 16 }  },	// upper
14 	{  { 28, 0 },   { -36, -24, 36, 24 }  },	// back/main body
15 	{  { 4, 32 },   { -44, -8, 44, 8 }    },	// lower
16 	{  { -28, 4 },  { -20, -20, 20, 20 }  }		// shoot target
17 };
18 
19 enum CORE_STATES
20 {
21 	CR_FightBegin		= 20,		// scripted
22 	CR_FaceClosed		= 200,
23 	CR_FaceSkull		= 210,
24 	CR_FaceTeeth		= 220,
25 	CR_FaceDoom			= 230,
26 
27 	CR_Defeated			= 500,		// scripted
28 	CR_Exploding		= 1000
29 };
30 
31 enum FACE_STATES
32 {
33 	FC_Closed		= 0,
34 	FC_Skull		= 1,
35 	FC_Teeth		= 2,
36 	FC_Mouth		= 3
37 };
38 
39 enum ROTR_STATES
40 {
41 	RT_Spin_Closed			= 10,
42 	RT_Spin_Open			= 20,
43 	RT_Spin_Slow_Closed		= 30,
44 	RT_Spin_Fast_Closed		= 40
45 };
46 
47 /*
48 void c------------------------------() {}
49 */
50 
INITFUNC(AIRoutines)51 INITFUNC(AIRoutines)
52 {
53 	ONSPAWN(OBJ_UD_MINICORE_IDLE, onspawn_ud_minicore_idle);
54 	ONTICK(OBJ_UDMINI_PLATFORM, ai_udmini_platform);
55 
56 	ONTICK(OBJ_UD_PELLET, ai_ud_pellet);
57 	ONTICK(OBJ_UD_SMOKE, ai_ud_smoke);
58 
59 	ONTICK(OBJ_UD_SPINNER, ai_ud_spinner);
60 	ONTICK(OBJ_UD_SPINNER_TRAIL, ai_ud_spinner_trail);
61 
62 	ONTICK(OBJ_UD_BLAST, ai_ud_blast);
63 }
64 
65 /*
66 void c------------------------------() {}
67 */
68 
69 /*
70 	Main core body:
71 		non-shootable when closed
72 		invulnerable when open
73 		starsolid's appear just behind the little dots
74 
75 		when you shoot the face the starsolid hits at just past his nose.
76 
77 		when face is open it does block the face from shooting below,
78 		but there is still a spot that you can hurt it from below.
79 
80 		four always-dark minicores that spin around it, they also seem
81 		to be switching their z-order.
82 */
83 
OnMapEntry()84 void UDCoreBoss::OnMapEntry()
85 {
86 Object *o;
87 
88 	// main object
89 	o = CreateObject(0, 0, OBJ_UDCORE_MAIN);
90 	main = o;
91 	game.stageboss.object = o;
92 
93 	o->sprite = SPR_NULL;//SPR_MARKER;
94 	objprop[o->type].hurt_sound = SND_CORE_HURT;
95 
96 	o->hp = 700;
97 	o->x = (592 << CSF);
98 	o->y = (120 << CSF);
99 	o->id2 = 1000;	// defeated script
100 	o->flags = (FLAG_SHOW_FLOATTEXT | FLAG_IGNORE_SOLID | FLAG_SCRIPTONDEATH);
101 
102 	// create rear rotators
103 	rotator[2] = create_rotator(0, 1);
104 	rotator[3] = create_rotator(0x80, 1);
105 
106 	// create front & back
107 	front = CreateObject(0, 0, OBJ_UDCORE_FRONT);
108 	back = CreateObject(0, 0, OBJ_UDCORE_BACK);
109 
110 	// create face
111 	face = CreateObject(0, 0, OBJ_UDCORE_FACE);
112 	face->state = FC_Closed;
113 
114 	// create front rotators
115 	rotator[0] = create_rotator(0, 0);
116 	rotator[1] = create_rotator(0x80, 0);
117 
118 	// initilize bboxes
119 	for(int i=0;i<NUM_BBOXES;i++)
120 	{
121 		bbox[i] = CreateObject(0, 0, OBJ_UDMINI_BBOX);
122 		bbox[i]->sprite = SPR_BBOX_PUPPET_1 + i;
123 		bbox[i]->hp = 1000;
124 
125 		sprites[bbox[i]->sprite].bbox = core_bboxes[i].rect;
126 	}
127 
128 	//o->BringToFront();
129 }
130 
OnMapExit()131 void UDCoreBoss::OnMapExit()
132 {
133 	main = NULL;
134 	game.stageboss.object = NULL;
135 }
136 
137 /*
138 void c------------------------------() {}
139 */
140 
Run(void)141 void UDCoreBoss::Run(void)
142 {
143 	Object *o = main;
144 	if (!o) return;
145 
146 	if (RunDefeated())
147 		return;
148 
149 	switch(o->state)
150 	{
151 		// fight begin (scripted)
152 		case CR_FightBegin:
153 		{
154 			o->state = CR_FaceSkull;
155 			o->dir = LEFT;
156 
157 			SetRotatorStates(RT_Spin_Slow_Closed);
158 			SpawnFaceSmoke();
159 		}
160 		break;
161 
162 		// face closed
163 		case CR_FaceClosed:
164 		{
165 			o->state++;
166 			o->timer = 0;
167 
168 			face->state = FC_Closed;
169 			front->frame = 2;		// closed
170 			back->frame = 0;		// not orange
171 
172 			set_bbox_shootable(false);
173 			SetRotatorStates(RT_Spin_Closed);
174 			SpawnFaceSmoke();
175 		}
176 		case CR_FaceClosed+1:
177 		{
178 			o->timer++;
179 
180 			if (o->dir == RIGHT || o->frame > 0 || o->hp < 200)
181 			{
182 				if (o->timer > 200)
183 				{
184 					o->timer2++;
185 					sound(SND_CORE_THRUST);
186 
187 					// select attack mode
188 					if (o->hp < 200)
189 					{
190 						o->state = CR_FaceDoom;
191 					}
192 					else if (o->timer2 > 2)
193 					{
194 						o->state = CR_FaceTeeth;
195 					}
196 					else
197 					{
198 						o->state = CR_FaceSkull;
199 					}
200 				}
201 			}
202 		}
203 		break;
204 
205 		// face open/skull
206 		case CR_FaceSkull:
207 		{
208 			o->state++;
209 			o->timer = 0;
210 
211 			face->state = FC_Skull;
212 			SpawnFaceSmoke();
213 
214 			o->savedhp = o->hp;
215 			set_bbox_shootable(true);
216 		}
217 		case CR_FaceSkull+1:
218 		{
219 			o->timer++;
220 			RunHurtFlash(o->timer);
221 
222 			if (o->timer < 300)
223 			{
224 				if ((o->timer % 120) == 1)
225 				{
226 					SpawnPellet(UP);
227 				}
228 
229 				if ((o->timer % 120) == 61)
230 				{
231 					SpawnPellet(DOWN);
232 				}
233 			}
234 
235 			if (o->timer > 400 || (o->savedhp - o->hp) > 50)
236 			{
237 				o->state = CR_FaceClosed;
238 			}
239 		}
240 		break;
241 
242 		// face open/teeth
243 		case CR_FaceTeeth:
244 		{
245 			o->state++;
246 			o->timer = 0;
247 
248 			face->state = FC_Teeth;
249 			SpawnFaceSmoke();
250 
251 			SetRotatorStates(RT_Spin_Open);
252 			game.quaketime = 100;
253 
254 			o->savedhp = o->hp;
255 			set_bbox_shootable(true);
256 		}
257 		case CR_FaceTeeth+1:
258 		{
259 			o->timer++;
260 			RunHurtFlash(o->timer);
261 
262 			// fire rotators
263 			if ((o->timer % 40) == 1)
264 			{
265 				int i = random(0, 3);
266 				int x = rotator[i]->x - (16<<CSF);
267 				int y = rotator[i]->y;
268 
269 				sound(SND_FUNNY_EXPLODE);
270 				CreateSpinner(x, y);
271 			}
272 
273 			if (o->timer > 400 || (o->savedhp - o->hp) > 150 || o->hp < 200)
274 			{
275 				o->state = CR_FaceClosed;
276 			}
277 		}
278 		break;
279 
280 		// face open/mouth: blasts of doom
281 		case CR_FaceDoom:
282 		{
283 			o->state++;
284 			o->timer = 0;
285 
286 			face->state = FC_Mouth;
287 			SpawnFaceSmoke();
288 			SetRotatorStates(RT_Spin_Fast_Closed);
289 
290 			sound(SND_FUNNY_EXPLODE);
291 
292 			// spawn a whole bunch of crazy spinners from the face
293 			CreateSpinner(face->x - (16<<CSF), face->y);
294 			CreateSpinner(face->x, face->y - (16<<CSF));
295 			CreateSpinner(face->x, face->y + (16<<CSF));
296 
297 			o->savedhp = o->hp;
298 			set_bbox_shootable(true);
299 		}
300 		case CR_FaceDoom+1:
301 		{
302 			o->timer++;
303 			RunHurtFlash(o->timer);
304 
305 			if ((o->timer % 120) == 1)
306 				SpawnPellet(UP);
307 
308 			if ((o->timer % 120) == 61)
309 				SpawnPellet(DOWN);
310 		}
311 		break;
312 	}
313 
314 	// move back and forth
315 	if (o->state >= CR_FightBegin && o->state < CR_Defeated)
316 	{
317 		if (o->x < MAPX(12))
318 			o->dir = RIGHT;
319 
320 		if (o->x > MAPX(map.xsize - 4))
321 			o->dir = LEFT;
322 
323 		XACCEL(4);
324 	}
325 
326 	// spawn minicore platforms
327 	switch(o->state)
328 	{
329 		case CR_FaceClosed+1:
330 		case CR_FaceTeeth+1:
331 		case CR_FaceSkull+1:
332 		case CR_FaceDoom+1:
333 		{
334 			// while I don't think there's any way to get her there without
335 			// a map editor, if you put Curly in the Black Space core room,
336 			// she WILL fight the core, just as she did the first time.
337 			if (o->state != 221 && (o->timer % 100) == 1)
338 				bbox[BB_TARGET]->CurlyTargetHere();
339 
340 			o->timer3++;
341 
342 			// upper platforms
343 			if (o->timer3 == 75)
344 			{
345 				CreateObject(MAPX(map.xsize) + 40, \
346 							 MAPY(3 + random(-3, 0)), OBJ_UDMINI_PLATFORM);
347 			}
348 
349 			// lower platforms
350 			if (o->timer3 == 150)
351 			{
352 				o->timer3 = 0;
353 				CreateObject(MAPX(map.xsize) + 40, \
354 							 MAPY(10 + random(-1, 3)), OBJ_UDMINI_PLATFORM);
355 
356 				break;
357 			}
358 		}
359 		break;
360 	}
361 
362 	LIMITX(0x80);
363 	LIMITY(0x80);
364 }
365 
366 
RunAftermove()367 void UDCoreBoss::RunAftermove()
368 {
369 int i;
370 
371 	Object *o = main;
372 	if (!o) return;
373 
374 	run_face(face);
375 	run_front(front);
376 	run_back(back);
377 
378 	for(i=0;i<4;i++)
379 		run_rotator(rotator[i]);
380 
381 	move_bboxes();
382 }
383 
384 
385 // spawn smoke puffs from face that come when face opens/closes
SpawnFaceSmoke()386 void UDCoreBoss::SpawnFaceSmoke()
387 {
388 	quake(20);
389 
390 	for(int i=0;i<8;i++)
391 	{
392 		int x = face->x + random(-16<<CSF, 32<<CSF);
393 		int y = main->CenterY();
394 		Object *s = SmokePuff(x, y);
395 		s->xinertia = random(-0x200, 0x200);
396 		s->yinertia = random(-0x100, 0x100);
397 	}
398 }
399 
400 // spit a "pellet" shot out of the face. That's what I'm calling the flaming lava-rock
401 // type things that are thrown out and trail along the ceiling or floor.
SpawnPellet(int dir)402 void UDCoreBoss::SpawnPellet(int dir)
403 {
404 	int y = main->y;
405 
406 	if (dir == UP)
407 		y -= (16 << CSF);
408 	else
409 		y += (16 << CSF);
410 
411 	CreateObject(main->x - (32<<CSF), y, OBJ_UD_PELLET)->dir = dir;
412 }
413 
414 
RunHurtFlash(int timer)415 void UDCoreBoss::RunHurtFlash(int timer)
416 {
417 	if (main->shaketime && (timer & 2))
418 	{
419 		front->frame = 1;
420 		back->frame = 1;
421 	}
422 	else
423 	{
424 		front->frame = 0;
425 		back->frame = 0;
426 	}
427 }
428 
429 /*
430 void c------------------------------() {}
431 */
432 
RunDefeated()433 bool UDCoreBoss::RunDefeated()
434 {
435 	Object *o = main;
436 
437 	switch(o->state)
438 	{
439 		// defeated (descending)
440 		case CR_Defeated:
441 		{
442 			o->state++;
443 			o->timer = 0;
444 			o->xinertia = 0;
445 			o->yinertia = 0;
446 
447 			face->state = FC_Closed;
448 			front->frame = 0;		// front closed
449 			back->frame = 0;		// not flashing
450 			SetRotatorStates(RT_Spin_Slow_Closed);
451 
452 			game.quaketime = 20;
453 			SmokeXY(o->x, o->y, 100, 128, 64);
454 
455 			KillObjectsOfType(OBJ_UDMINI_PLATFORM);
456 			set_bbox_shootable(false);
457 		}
458 		case CR_Defeated+1:
459 		{
460 			SmokeXY(o->x, o->y, 1, 64, 32);
461 
462 			o->xinertia = 0x40;
463 			o->yinertia = 0x80;
464 
465 			if (++o->timer > 200)
466 			{
467 				o->state = CR_Exploding;
468 				o->xinertia = 0;
469 				o->yinertia = 0;
470 				o->timer = 0;
471 			}
472 		}
473 		break;
474 
475 		// defeated (exploding)
476 		case CR_Exploding:
477 		{
478 			quake(100, 0);
479 			o->timer++;
480 
481 			if ((o->timer % 8) == 0)
482 				sound(SND_MISSILE_HIT);
483 
484 			int x = o->x + random(-72<<CSF, 72<<CSF);
485 			int y = o->y + random(-64<<CSF, 64<<CSF);
486 			SmokePuff(x, y);
487 			effect(x, y, EFFECT_BOOMFLASH);
488 
489 			if (o->timer > 100)
490 			{
491 				sound(SND_EXPLOSION1);
492 				starflash.Start(o->x, o->y);
493 
494 				o->state++;
495 				o->timer = 0;
496 			}
497 		}
498 		break;
499 		case CR_Exploding+1:
500 		{
501 			game.quaketime = 40;
502 			if (++o->timer > 50)
503 			{
504 				KillObjectsOfType(OBJ_MISERY_MISSILE);
505 
506 				front->Delete();
507 				back->Delete();
508 				face->Delete();
509 				for(int i=0;i<NUM_ROTATORS;i++) rotator[i]->Delete();
510 				for(int i=0;i<NUM_BBOXES;i++) bbox[i]->Delete();
511 				main->Delete();
512 				main = NULL;
513 
514 				return 1;
515 			}
516 		}
517 		break;
518 	}
519 
520 	return 0;
521 }
522 
523 
524 /*
525 void c------------------------------() {}
526 */
527 
run_face(Object * o)528 void UDCoreBoss::run_face(Object *o)
529 {
530 	o->sprite = SPR_UD_FACES;
531 	o->invisible = false;
532 
533 	switch(o->state)
534 	{
535 		// to "show" the closed face, we go invisible and the
536 		// face area of the main core shows through.
537 		case FC_Closed: o->invisible = true; break;
538 		case FC_Skull: o->frame = 0; break;
539 		case FC_Teeth: o->frame = 1; break;
540 
541 		// mouth blasts of doom. Once started, it's perpetual blasting
542 		// until told otherwise.
543 		case FC_Mouth:
544 		{
545 			o->state++;
546 			o->timer = 100;
547 		}
548 		case FC_Mouth+1:
549 		{
550 			if (++o->timer > 300)
551 				o->timer = 0;
552 
553 			if (o->timer > 250)
554 			{
555 				if ((o->timer % 16) == 1)
556 					sound(SND_QUAKE);
557 
558 				if ((o->timer % 16) == 7)
559 				{
560 					CreateObject(o->x, o->y, OBJ_UD_BLAST);
561 					sound(SND_LIGHTNING_STRIKE);
562 				}
563 			}
564 
565 			if (o->timer == 200)
566 				sound(SND_CORE_CHARGE);
567 
568 			if (o->timer >= 200 && (o->timer & 1))
569 				o->frame = 3;	// mouth lit
570 			else
571 				o->frame = 2;	// mouth norm
572 		}
573 		break;
574 	}
575 
576 	o->x = main->x - (36 << CSF);
577 	o->y = main->y - (4 << CSF);
578 }
579 
580 
run_front(Object * o)581 void UDCoreBoss::run_front(Object *o)
582 {
583 	// 0 open (should make a face visible at the same time to go into the gap)
584 	// 1 open/hurt
585 	// 2 closed
586 
587 	switch(o->state)
588 	{
589 		case 0:
590 		{
591 			o->sprite = SPR_UD_FRONT;
592 			o->state = 1;
593 			o->frame = 2;
594 		}
595 		case 1:
596 		{
597 			o->x = main->x - (36<<CSF);
598 			o->y = main->y;
599 		}
600 		break;
601 	}
602 }
603 
604 
run_back(Object * o)605 void UDCoreBoss::run_back(Object *o)
606 {
607 	// 0 normal
608 	// 1 hurt
609 
610 	switch(o->state)
611 	{
612 		case 0:
613 		{
614 			o->sprite = SPR_UD_BACK;
615 			o->state = 1;
616 			o->frame = 0;
617 		}
618 		case 1:
619 		{
620 			o->x = main->x + (44<<CSF);
621 			o->y = main->y;
622 		}
623 		break;
624 	}
625 }
626 
627 
628 /*
629 void c------------------------------() {}
630 */
631 
632 // "front" refers to whether they are doing the front (left) or rear (right)
633 // half of the arc; the ones marked "front" are actually BEHIND the core.
create_rotator(int angle,int front)634 Object *UDCoreBoss::create_rotator(int angle, int front)
635 {
636 	Object *o = CreateObject(0, 0, OBJ_UDMINI_ROTATOR);
637 	o->angle = angle;
638 	o->substate = front;
639 
640 	return o;
641 }
642 
643 // the rotators are 4 minicores that spin around the main core during the battle
644 // and have pseudo-3D effects. They also shoot the spinners during the teeth-face phase.
645 //
646 // instead of having the cores constantly rearranging their Z-Order as they pass
647 // in front and behind the core, an optical illusion is used. 2 cores are always
648 // in front and 2 are always behind. Each set of two cores covers only half the full
649 // circle. When a core in the front set reaches the top, it warps back to the bottom
650 // just as a core in the back set reaches the bottom and warps back to the top.
651 // Thus, they swap places and the core appears to continue around the circle using
652 // the different z-order of the one that was just swapped-in.
run_rotator(Object * o)653 void UDCoreBoss::run_rotator(Object *o)
654 {
655 	//debug("rotr s%d", o->state);
656 
657 	switch(o->state)
658 	{
659 		case 0:
660 		{
661 			o->sprite = SPR_UD_ROTATOR;
662 			o->flags &= ~FLAG_SHOOTABLE;
663 			o->hp = 1000;
664 		}
665 		break;
666 
667 		case RT_Spin_Closed:
668 		{
669 			o->frame = 0;
670 			o->angle += 2;
671 		}
672 		break;
673 
674 		// used when firing spinners in Teeth face
675 		// (it's easier to coordinate if spinners are actually spawned by core
676 		// and just positioned next to us)
677 		case RT_Spin_Open:
678 		{
679 			o->frame = 1;
680 			o->angle += 2;
681 		}
682 		break;
683 
684 		case RT_Spin_Slow_Closed:
685 		{
686 			o->frame = 0;
687 			o->angle++;
688 		}
689 		break;
690 
691 		case RT_Spin_Fast_Closed:
692 		{
693 			o->frame = 0;
694 			o->angle += 4;
695 		}
696 		break;
697 	}
698 
699 	// each "side" covers half the rotation angle
700 	int angle = (o->angle / 2);
701 
702 	if (o->substate)
703 	{	// front (left) half of arc
704 		angle += 0x40;
705 	}
706 	else
707 	{	// back (right) half of arc
708 		angle += 0xC0;
709 	}
710 
711 	o->x = (main->x - (8<<CSF)) + xinertia_from_angle(angle, (48<<CSF));
712 	o->y = main->y + yinertia_from_angle(angle, (80<<CSF));
713 }
714 
SetRotatorStates(int newstate)715 void UDCoreBoss::SetRotatorStates(int newstate)
716 {
717 	for(int i=0;i<NUM_ROTATORS;i++)
718 		rotator[i]->state = newstate;
719 }
720 
721 /*
722 void c------------------------------() {}
723 */
724 
725 // extra bbox puppets/shoot targets
726 // only one, located at the face, is shootable, the other 3 are invulnerable shields.
move_bboxes()727 void UDCoreBoss::move_bboxes()
728 {
729 	for(int i=0;i<NUM_BBOXES;i++)
730 	{
731 		bbox[i]->x = main->x + (core_bboxes[i].offset.x << CSF);
732 		bbox[i]->y = main->y + (core_bboxes[i].offset.y << CSF);
733 	}
734 
735 	transfer_damage(bbox[BB_TARGET], main);
736 }
737 
738 // sets up bboxes for the Core entering shootable or non-shootable mode.
set_bbox_shootable(bool enable)739 void UDCoreBoss::set_bbox_shootable(bool enable)
740 {
741 uint32_t body_flags, target_flags;
742 int i;
743 
744 	// in shootable mode target can be hit and shields are up.
745 	// in non-shootable mode (when face is closed) nothing can be hit.
746 	if (enable)
747 	{
748 		body_flags = FLAG_INVULNERABLE;
749 		target_flags = FLAG_SHOOTABLE;
750 	}
751 	else
752 	{
753 		body_flags = 0;
754 		target_flags = 0;
755 	}
756 
757 	for(i=0;i<NUM_BBOXES;i++)
758 	{
759 		bbox[i]->flags &= ~(FLAG_SHOOTABLE | FLAG_INVULNERABLE);
760 
761 		if (i == BB_TARGET)
762 			bbox[i]->flags |= target_flags;
763 		else
764 			bbox[i]->flags |= body_flags;
765 	}
766 }
767 
768 /*
769 void c------------------------------() {}
770 */
771 
772 // minicores by entrance seen before fight
onspawn_ud_minicore_idle(Object * o)773 void onspawn_ud_minicore_idle(Object *o)
774 {
775 	if (o->dir == RIGHT)
776 		o->flags &= ~FLAG_SOLID_BRICK;
777 }
778 
779 // these are the ones you can ride
ai_udmini_platform(Object * o)780 void ai_udmini_platform(Object *o)
781 {
782 	switch(o->state)
783 	{
784 		case 0:
785 		{
786 			o->state = 1;
787 			o->ymark = o->y;
788 
789 			o->xinertia = -0x200;
790 			o->yinertia = 0x100;
791 			if (random(0, 1)) o->yinertia = -o->yinertia;
792 		}
793 		case 1:
794 		{
795 			if (o->x < -(64 << CSF))
796 				o->Delete();
797 
798 			if (o->y > o->ymark) o->yinertia -= 0x10;
799 			if (o->y < o->ymark) o->yinertia += 0x10;
800 			LIMITY(0x100);
801 
802 			// when player jumps on them, they open up and start
803 			// moving their Y to align with the core.
804 			if (player->riding == o)
805 			{
806 				o->ymark = MAPY(9);
807 				o->frame = 2;
808 			}
809 			else if (o->flags & FLAG_SOLID_BRICK)	// don't reset frame if dimmed
810 			{
811 				o->frame = 0;
812 			}
813 
814 			// don't try to squish the player into anything, rather, dim and go non-solid.
815 			// our bbox is set slightly larger than our solidbox so that we can detect if
816 			// the player is near.
817 			if (hitdetect(o, player))
818 			{
819 				if ((player->blockl && player->Right() < o->CenterX()) || \
820 					(o->yinertia > 0 && player->blockd && player->Top() >= o->CenterY() - (1<<CSF)) || \
821 					(o->yinertia < 0 && player->blocku && player->Bottom() < o->CenterY()))
822 				{
823 					o->flags &= ~FLAG_SOLID_BRICK;
824 					o->frame = 1;
825 				}
826 			}
827 		}
828 		break;
829 	}
830 }
831 
832 /*
833 void c------------------------------() {}
834 */
835 
836 // falling lava-rock thing from Skull face
ai_ud_pellet(Object * o)837 void ai_ud_pellet(Object *o)
838 {
839 	switch(o->state)
840 	{
841 		case 0:
842 		{
843 			o->sprite = SPR_UD_PELLET;
844 			o->xinertia = -0x200;
845 			o->state = 1;
846 		}
847 		case 1:		// falling
848 		{
849 			if (o->dir == UP)
850 			{
851 				o->yinertia -= 0x20;
852 				LIMITY(0x5ff);
853 
854 				if (o->blocku)
855 					o->state = 2;
856 			}
857 			else if (o->dir == DOWN)
858 			{
859 				o->yinertia += 0x20;
860 				LIMITY(0x5ff);
861 
862 				if (o->blockd)
863 					o->state = 2;
864 			}
865 
866 			ANIMATE(3, 0, 1);
867 		}
868 		break;
869 
870 		case 2:		// hit ground/ceiling
871 		{
872 			sound(SND_MISSILE_HIT);
873 			o->xinertia = (o->x > player->x) ? -0x400 : 0x400;
874 			o->yinertia = 0;
875 
876 			o->state = 3;
877 			o->timer = 0;
878 			o->flags |= FLAG_IGNORE_SOLID;
879 
880 			o->sprite = SPR_UD_BANG;
881 			o->x -= (4 << CSF);
882 			o->y -= (4 << CSF);
883 		}
884 		case 3:
885 		{
886 			ANIMATE(0, 0, 2);
887 
888 			if ((++o->timer % 3) == 1)
889 			{
890 				Object *smoke = CreateObject(o->CenterX(), o->CenterY(), OBJ_UD_SMOKE);
891 
892 				if (o->dir == UP)
893 					smoke->yinertia = 0x400;
894 				else
895 					smoke->yinertia = -0x400;
896 
897 				smoke->x += o->xinertia;
898 			}
899 
900 			if (o->CenterX() < (16<<CSF) || \
901 				o->CenterX() > (MAPX(map.xsize) - (16<<CSF)))
902 			{
903 				o->Delete();
904 			}
905 		}
906 		break;
907 	}
908 }
909 
910 
ai_ud_smoke(Object * o)911 void ai_ud_smoke(Object *o)
912 {
913 	switch(o->state)
914 	{
915 		case 0:
916 		{
917 			o->xinertia = random(-4, 4) << CSF;
918 			o->state = 1;
919 		}
920 		case 1:
921 		{
922 			o->xinertia *= 20; o->xinertia /= 21;
923 			o->yinertia *= 20; o->yinertia /= 21;
924 
925 			ANIMATE_FWD(1);
926 			if (o->frame > sprites[o->sprite].nframes)
927 				o->Delete();
928 		}
929 		break;
930 	}
931 }
932 
933 /*
934 void c------------------------------() {}
935 */
936 
CreateSpinner(int x,int y)937 static void CreateSpinner(int x, int y)
938 {
939 	CreateObject(x, y, OBJ_UD_SPINNER);
940 	CreateObject(x, y, OBJ_UD_SPINNER)->angle = 0x80;
941 }
942 
943 // spinny thing shot by rotators during Teeth phase.
944 // they come in pairs.
ai_ud_spinner(Object * o)945 void ai_ud_spinner(Object *o)
946 {
947 	if (o->x < 0 || o->x > MAPX(map.xsize))
948 	{
949 		effect(o->CenterX(), o->CenterY(), EFFECT_BOOMFLASH);
950 		o->Delete();
951 		return;
952 	}
953 
954 	switch(o->state)
955 	{
956 		case 0:
957 		{
958 			o->xmark = o->x;
959 			o->ymark = o->y;
960 			o->state = 1;
961 		}
962 		case 1:
963 		{
964 			o->angle += 24;
965 
966 			o->speed -= 0x15;
967 			o->xmark += o->speed;
968 
969 			o->x = o->xmark + xinertia_from_angle(o->angle, (4<<CSF));
970 			o->y = o->ymark + yinertia_from_angle(o->angle, (6<<CSF));
971 
972 			CreateObject(o->x, o->y, OBJ_UD_SPINNER_TRAIL);
973 			o->BringToFront();
974 		}
975 		break;
976 	}
977 }
978 
ai_ud_spinner_trail(Object * o)979 void ai_ud_spinner_trail(Object *o)
980 {
981 	o->frame++;
982 	if (o->frame > 2)
983 		o->Delete();
984 }
985 
986 /*
987 void c------------------------------() {}
988 */
989 
ai_ud_blast(Object * o)990 void ai_ud_blast(Object *o)
991 {
992 	o->xinertia = -0x1000;
993 	o->frame ^= 1;
994 
995 	SmokePuff(o->CenterX() + (random(0, 16) << CSF), \
996 			  o->CenterY() + (random(-16, 16) << CSF));
997 
998 	if (o->x < -0x4000)
999 		o->Delete();
1000 }
1001 
1002 
1003 
1004 
1005 
1006 
1007