2 Copyright (C) 2004-2011 Parallel Realities
3 Copyright (C) 2011-2015 Perpendicular Dimensions
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 See the GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 #include "finalBattle.h"
24 void galdovMiniAttack();
25 void galdovFinalFlee1();
26 void blackDroidAttack();
28 // ************************ Black Droids for Part 2 *************************************
32 (self->x < player.x) ? self->face = 0 : self->face = 1;
34 if (!hasClearShot(self))
37 self->setActionFinished(9);
38 self->setThinkTime(2);
39 self->think = &blackDroidFire;
41 addBullet((Entity*)self, 0, 0);
44 if (self->custom == 0)
46 self->think = &blackDroidAttack;
53 Math::removeBit(&self->flags, ENT_FLIES);
55 if (self->health < -10)
57 if ((self->health % 3) == 0)
59 addExplosion(self->x, self->y, 35, self);
60 addSmokeAndFire(self, Math::rrand(-15, 15), Math::rrand(-15, 15), 2);
64 if (self->health <= -60)
66 if ((Math::prand() % 2) == 0)
68 dropItems((int)self->x, (int)self->y);
69 dropItems((int)self->x, (int)self->y);
73 addItem(100, "Red Keycard", (int)self->x, (int)self->y, "RedKeyCard", 240, 1, ENT_DYING, true);
76 self->place(9999, 9999);
80 void blackDroidChasePlayer()
82 self->think = &blackDroidAttack;
84 (self->x < player.x) ? self->face = 0 : self->face = 1;
88 Math::calculateSlope(player.x, player.y, self->x, self->y, &dx, &dy);
93 self->setThinkTime(10);
94 self->setActionFinished(10);
97 void blackDroidMoveRandom()
99 (self->x < player.x) ? self->face = 0 : self->face = 1;
101 self->think = &blackDroidAttack;
103 self->dx = Math::rrand(-200, 200);
104 self->dy = Math::rrand(-200, 200);
109 self->setThinkTime(2);
110 self->setActionFinished(Math::rrand(60, 90));
113 void blackDroidAttack()
115 self->think = blackDroidAttack;
116 self->setThinkTime(2);
117 self->setActionFinished(2);
119 int r = Math::prand() % 15;
123 self->think = &blackDroidFire;
124 self->setThinkTime(2);
125 self->setActionFinished(2);
126 self->custom = Math::rrand(2, 4) * game.skill;
130 self->think = &blackDroidChasePlayer;
131 self->setThinkTime(2);
132 self->setActionFinished(2);
136 self->think = &blackDroidMoveRandom;
137 self->setThinkTime(2);
138 self->setActionFinished(2);
142 void galdovInitBlackDroids()
144 for (int i = 6 ; i < 10 ; i++)
146 if (map.boss[i] == NULL)
148 map.boss[i] = new Boss();
150 debug(("BlackDroid %d init\n", i));
151 strlcpy(map.boss[i]->name, "BlackDrod", sizeof map.boss[i]->name);
152 map.boss[i]->health = -90;
153 map.boss[i]->maxHealth = -90;
154 map.boss[i]->setSprites(graphics.getSprite("BlackDroidRight", true), graphics.getSprite("BlackDroidLeft", true), graphics.getSprite("BlackDroidDie", true));
155 map.boss[i]->currentWeapon = &weapon[WP_PLASMARIFLE];
156 map.boss[i]->face = 0;
157 map.boss[i]->active = false;
158 map.boss[i]->place(9999, 9999);
159 map.boss[i]->immune = 0;
160 map.boss[i]->setThinkTime(2);
161 map.boss[i]->think = &blackDroidAttack;
162 map.boss[i]->react = NULL;
163 map.boss[i]->die = &blackDroidDie;
164 map.boss[i]->setActionFinished(120);
165 Math::addBit(&map.boss[i]->flags, ENT_FLIES);
166 Math::addBit(&map.boss[i]->flags, ENT_AIMS);
167 Math::removeBit(&map.boss[i]->flags, ENT_FIRETRAIL);
171 // ************************ First part of final battle **********************************
173 void galdovFinalKeepAlive1()
175 map.boss[0]->health = 30 * game.skill;
178 void galdovFinalPainThrow()
183 self->think = &galdovFinalFlee1;
184 self->setThinkTime(2);
185 self->setActionFinished(140);
188 void galdovFinalDropCrystal()
190 self->setThinkTime(2);
191 self->setActionFinished(2);
193 int x = (int)player.x >> BRICKSHIFT;
194 int y = (int)player.y >> BRICKSHIFT;
195 x += Math::rrand(-2, 2);
196 y += Math::rrand(-2, 2);
198 Math::limitInt(&x, 2, 62);
199 Math::limitInt(&y, 2, 26);
201 if (map.data[x][y] == MAP_AIR)
207 addTeleportParticles(self->x + 10, self->y + 10, 50, SND_TELEPORT2);
208 addItem(101, "Reality Crystal", (int)self->x, (int)self->y, "RealityCrystal", 100, 1, 0, true);
209 self->think = &galdovFinalPainThrow;
210 self->react = &galdovFinalKeepAlive1;
211 Math::removeBit(&self->flags, ENT_FLIES);
212 Math::removeBit(&self->flags, ENT_FIRETRAIL);
213 map.setMainBossPart(NULL);
214 map.boss[0]->health = 30 * game.skill;
215 map.boss[0]->setSprites(graphics.getSprite("GaldovPain", true), graphics.getSprite("GaldovPain", true), graphics.getSprite("GaldovPain", true));
216 engine.setInfoMessage("Galdov has dropped the crystal! Quick! Get it!!", 99, INFO_HINT);
217 audio.playSound(SND_BOSSCUSTOM2, CH_AMBIANCE, x);
221 void galdovFinalDie()
223 if (game.missionOverReason == MIS_INPROGRESS)
225 game.missionOverReason = MIS_GAMECOMPLETE;
227 audio.playSound(SND_BOSSCUSTOM3, CH_AMBIANCE, self->x);
232 Math::removeBit(&self->flags, ENT_FLIES);
233 Math::removeBit(&self->flags, ENT_FIRETRAIL);
234 presentPlayerMedal("Final_Battle");
237 self->setActionFinished(1);
238 self->setThinkTime(1);
242 if (self->health > -35)
245 if (self->health == -99)
247 checkObjectives(self->name, false);
250 Math::wrapInt(&self->health, -99, -41);
252 if ((self->health % 3) == 0)
254 self->setActionFinished(5);
255 self->setThinkTime(2);
257 self->dx = Math::rrand(-5, 5);
258 self->dy = Math::rrand(-5, 5);
260 addExplosion(self->x, self->y, 50, &player);
261 addSmokeAndFire(self, Math::rrand(-5, 5), Math::rrand(-5, 5), 2);
265 void galdovMiniFire()
267 (self->x < player.x) ? self->face = 0 : self->face = 1;
269 if (!hasClearShot(self))
272 self->setActionFinished(9);
273 self->setThinkTime(2);
274 self->think = &galdovMiniFire;
276 addBullet((Entity*)self, 0, 0);
279 if (self->custom == 0)
281 self->think = &galdovMiniAttack;
285 void galdovFinalTeleport()
287 self->setThinkTime(2);
288 self->setActionFinished(2);
290 int x = (int)player.x >> BRICKSHIFT;
291 int y = (int)player.y >> BRICKSHIFT;
292 x += Math::rrand(-10, 10);
293 y += Math::rrand(-10, 10);
295 Math::limitInt(&x, 2, 62);
296 Math::limitInt(&y, 2, 26);
298 if (self == map.boss[0])
300 if (self->health < 45 * game.skill)
302 Math::limitInt(&x, 23, 62);
303 Math::limitInt(&y, 42, 61);
304 self->currentWeapon = getRandomGaldovWeapon();
308 if (map.data[x][y] == MAP_AIR)
314 addTeleportParticles(self->x + 10, self->y + 10, 50, SND_TELEPORT2);
315 self->think = &galdovMiniAttack;
319 void galdovMiniChasePlayer()
321 (self->x < player.x) ? self->face = 0 : self->face = 1;
325 Math::calculateSlope(player.x, player.y, self->x, self->y, &dx, &dy);
330 self->setThinkTime(10);
331 self->setActionFinished(10);
332 self->think = &galdovMiniAttack;
335 void galdovMiniMoveRandom()
337 self->think = &galdovMiniAttack;
339 if (self->flags & ENT_FLIES)
341 self->dx = Math::rrand(-200, 200);
342 self->dy = Math::rrand(-200, 200);
347 self->setThinkTime(2);
348 self->setActionFinished(Math::rrand(60, 90));
352 self->dy = Math::rrand(-12, 0);
354 (player.x < self->x) ? self->dx = -3 : self->dx = 3;
356 self->think = &galdovMiniAttack;
357 self->setActionFinished(45);
361 void galdovMiniAttack()
363 if (player.health < 1)
365 self->dx = self->dy = 0;
369 (self->x < player.x) ? self->face = 0 : self->face = 1;
371 self->think = &galdovMiniAttack;
372 self->setThinkTime(2);
373 self->setActionFinished(2);
375 int r = Math::prand() % 100;
379 self->think = &galdovMiniFire;
380 self->custom = Math::rrand(1, 8);
384 addTeleportParticles(self->x + 10, self->y + 10, 50, SND_TELEPORT2);
387 self->setActionFinished(120);
388 self->setThinkTime(2);
389 self->think = &galdovFinalTeleport;
393 self->think = &galdovMiniChasePlayer;
397 self->think = &galdovMiniMoveRandom;
401 if ((self->flags & ENT_FLIES) && (Math::prand() % 5) == 0)
403 Math::removeBit(&self->flags, ENT_FLIES);
404 Math::removeBit(&self->flags, ENT_FIRETRAIL);
408 Math::addBit(&self->flags, ENT_FLIES);
409 Math::addBit(&self->flags, ENT_FIRETRAIL);
416 addExplosion(self->x, self->y, 5, self);
417 dropItems((int)self->x, (int)self->y);
418 self->setThinkTime(2);
419 self->setActionFinished(2);
420 self->active = false;
421 self->place(9999, 9999);
423 // Don't kill it properly(!!)
428 for (int i = 1 ; i < 10 ; i++)
430 if (map.boss[i]->health > 0)
438 map.boss[0]->think = &galdovFinalDropCrystal;
439 map.boss[0]->setThinkTime(2);
440 map.boss[0]->setActionFinished(2);
446 debug(("galdovRejoin\n"));
448 for (int i = 1 ; i < 10 ; i++)
450 if (map.boss[i] != NULL)
452 addTeleportParticles(map.boss[i]->x + 4, map.boss[i]->y + 4, 75, -1);
454 if ((Math::prand() % (2 + (game.skill))) == 0)
456 dropItems((int)map.boss[i]->x, (int)map.boss[i]->y);
459 map.boss[i]->place(9999, 9999);
460 map.boss[i]->active = false;
464 self->setThinkTime(2);
465 self->setActionFinished(120);
466 self->think = &galdovFinalTeleport;
467 self->currentWeapon = getRandomGaldovWeapon();
469 audio.playSound(SND_BOSSCUSTOM1, CH_AMBIANCE, self->x);
471 map.setMainBossPart(map.boss[0]);
473 debug(("galdovRejoin: Done\n"));
476 void galdovFinalMiniReact()
478 if (self->health > 0)
480 map.setMainBossPart(self);
484 map.setMainBossPart(NULL);
488 void galdovFinalSplit()
490 debug(("galdovFinalSplit\n"));
492 for (int i = 1 ; i < 10 ; i++)
494 if (map.boss[i] == NULL)
496 map.boss[i] = new Boss();
497 strlcpy(map.boss[i]->name, "MiniGaldov", sizeof map.boss[i]->name);
498 map.boss[i]->health = 10 * game.skill;
499 map.boss[i]->maxHealth = 10 * game.skill;
501 map.boss[i]->setSprites(graphics.getSprite("GaldovMiniRight", true), graphics.getSprite("GaldovMiniLeft", true), graphics.getSprite("GaldovMiniLeft", true));
502 map.boss[i]->currentWeapon = getRandomAimedWeapon();
503 map.boss[i]->face = 0;
504 map.boss[i]->active = true;
506 if (map.boss[i]->health > 0)
508 map.boss[i]->dx = Math::rrand(-2, 2);
509 map.boss[i]->dy = Math::rrand(-2, 2);
510 map.boss[i]->x = map.boss[0]->x;
511 map.boss[i]->y = map.boss[0]->y;
515 map.boss[i]->active = false;
518 map.boss[i]->immune = 0;
519 map.boss[i]->setThinkTime(2);
520 map.boss[i]->setActionFinished(120);
521 map.boss[i]->think = &galdovMiniAttack;
522 map.boss[i]->react = &galdovFinalMiniReact;
523 map.boss[i]->die = &galdovMiniDie;
524 Math::addBit(&map.boss[i]->flags, ENT_AIMS);
525 Math::addBit(&map.boss[i]->flags, ENT_FLIES);
526 Math::addBit(&map.boss[i]->flags, ENT_FIRETRAIL);
528 addTeleportParticles(map.boss[i]->x + 10 + map.boss[i]->dx, map.boss[i]->y + 10 + map.boss[i]->dy, 75, -1);
531 audio.playSound(SND_BOSSCUSTOM1, CH_AMBIANCE, map.boss[0]->x);
533 map.boss[0]->place(9999, 9999);
534 map.boss[0]->setThinkTime(2);
535 map.boss[0]->setActionFinished(Math::rrand(10, 15) * 60);
536 map.boss[0]->think = &galdovRejoin;
537 map.boss[0]->active = true;
539 map.setMainBossPart(NULL);
541 debug(("galdovFinalSplit: Done\n"));
544 void galdovFinalReact()
546 map.boss[0]->health = 45 * game.skill;
548 if ((Math::prand() % 5) == 0)
550 map.boss[0]->setThinkTime(2);
551 map.boss[0]->setActionFinished(2);
552 map.boss[0]->think = &galdovFinalSplit;
556 void galdovFinalInit()
558 debug(("galdovFinalInit\n"));
560 map.boss[0] = new Boss();
561 strlcpy(map.boss[0]->name, "Galdov", sizeof map.boss[0]->name);
562 map.boss[0]->health = 45 * game.skill;
563 map.boss[0]->maxHealth = 45 * game.skill;
564 map.boss[0]->setSprites(graphics.getSprite("GaldovRight", true), graphics.getSprite("GaldovLeft", true), graphics.getSprite("GaldovDie", true));
565 map.boss[0]->currentWeapon = &weapon[WP_AIMEDPISTOL];
566 map.boss[0]->face = 0;
567 map.boss[0]->active = true;
568 map.boss[0]->x = 480;
569 map.boss[0]->y = 576;
570 map.boss[0]->immune = 0;
571 map.boss[0]->setThinkTime(2);
572 map.boss[0]->setActionFinished(2);
573 map.boss[0]->think = &galdovMiniAttack;
574 //map.boss[0]->think = &galdovFinalDropCrystal;
575 map.boss[0]->react = &galdovFinalReact;
576 map.boss[0]->die = &galdovFinalDie;
577 Math::addBit(&map.boss[0]->flags, ENT_AIMS);
579 audio.loadSound(SND_BOSSCUSTOM1, "sound/galdovSplit");
580 audio.loadSound(SND_BOSSCUSTOM2, "sound/galdovPain");
581 audio.loadSound(SND_BOSSCUSTOM3, "sound/galdovDie");
583 map.setMainBossPart(map.boss[0]);
585 debug(("galdovFinalInit: Done\n"));
588 // ******************** Second part of Final Battle *********************************
590 void galdovFinalReact2()
592 if (self->health < 1)
595 if ((Math::prand() % 12) == 0)
597 addTeleportParticles(self->x + (self->width / 2), self->y + (self->height / 2), 50, SND_TELEPORT3);
598 map.boss[0]->setThinkTime(2);
599 map.boss[0]->setActionFinished(2);
600 map.boss[0]->think = &galdovFinalTeleport;
604 void galdovFinalStandUp()
606 map.boss[0]->setSprites(graphics.getSprite("GaldovShieldRight", true), graphics.getSprite("GaldovShieldLeft", true), graphics.getSprite("GaldovShieldLeft", true));
607 self->setThinkTime(2);
608 self->setActionFinished(2);
609 addTeleportParticles(self->x + (self->width / 2), self->y + (self->height / 2), 50, SND_TELEPORT3);
610 self->think = &galdovFinalTeleport;
612 if (map.boss[0]->health <= 15 * game.skill)
614 self->setSprites(graphics.getSprite("GaldovRight", true), graphics.getSprite("GaldovLeft", true), graphics.getSprite("GaldovDie", true));
615 Math::removeBit(&map.boss[0]->flags, ENT_IMMUNE);
616 map.boss[0]->react = &galdovFinalReact2;
620 void galdovFinalPainThrow2()
625 self->think = &galdovFinalStandUp;
626 self->setThinkTime(2);
627 self->setActionFinished(90);
632 (self->x < player.x) ? self->face = 0 : self->face = 1;
636 Math::calculateSlope(map.boss[0]->x, map.boss[0]->y, self->x, self->y, &dx, &dy);
638 self->dx = (dx * (1 + game.skill));
639 self->dy = (dy * (1 + game.skill));
641 self->setThinkTime(2);
642 self->setActionFinished(5);
643 self->think = &orbSeekGaldov;
645 int distX = (int)fabs(map.boss[0]->x - self->x);
646 int distY = (int)fabs(map.boss[0]->y - self->y);
651 int distance = (int)sqrt(distX + distY);
655 self->active = false;
656 self->setActionFinished(60);
657 self->setThinkTime(60);
658 addExplosion(self->x, self->y, 75, &player);
659 audio.playSound(SND_BOSSCUSTOM2, CH_AMBIANCE, self->x);
660 self->place(9999, 9999);
661 map.boss[0]->setSprites(graphics.getSprite("GaldovPain", true), graphics.getSprite("GaldovPain", true), graphics.getSprite("GaldovPain", true));
662 map.boss[0]->think = &galdovFinalPainThrow2;
663 map.boss[0]->health -= (3 * game.skill);
664 Math::removeBit(&map.boss[0]->flags, ENT_FLIES);
665 Math::removeBit(&map.boss[0]->flags, ENT_FIRETRAIL);
669 void galdovFinalShieldInit()
671 self->setActionFinished(60);
672 self->setThinkTime(60);
673 self->think = &galdovMiniAttack;
676 Math::addBit(&map.boss[0]->flags, ENT_IMMUNE);
678 for (int i = 1 ; i < 6 ; i++)
680 if (map.boss[i] != NULL)
684 map.boss[i] = new Boss();
685 strlcpy(map.boss[i]->name, "OrbBomb", sizeof map.boss[i]->name);
686 map.boss[i]->setSprites(graphics.getSprite("DroidOrb", true), graphics.getSprite("DroidOrb", true), graphics.getSprite("DroidOrb", true));
687 map.boss[i]->health = 999999;
688 map.boss[i]->maxHealth = 99999;
689 map.boss[i]->immune = 0;
690 Math::addBit(&map.boss[0]->flags, ENT_IMMUNE);
691 map.boss[i]->active = false;
692 map.boss[i]->setActionFinished(2);
693 map.boss[i]->setThinkTime(2);
694 map.boss[i]->think = &orbSeekGaldov;
695 map.boss[i]->react = NULL;
696 Math::addBit(&map.boss[i]->flags, ENT_FLIES);
699 map.boss[1]->place(864, 1536);
700 map.boss[2]->place(864, 1792);
701 map.boss[3]->place(1856, 1792);
702 map.boss[4]->place(1888, 1440);
703 map.boss[5]->place(1408, 1472);
705 galdovInitBlackDroids();
708 void galdovFinalWaitForObjective()
710 self->setActionFinished(120);
711 self->setThinkTime(2);
713 Objective *objective = (Objective*)map.objectiveList.getHead();
715 while (objective->next != NULL)
717 objective = (Objective*)objective->next;
719 if (strstr(objective->target, "Reality"))
721 if (objective->completed)
725 Math::addBit(&player.flags, ENT_TELEPORTING);
726 self->think = &galdovFinalShieldInit;
727 map.boss[0]->setSprites(graphics.getSprite("GaldovShieldRight", true), graphics.getSprite("GaldovShieldLeft", true), graphics.getSprite("GaldovShieldLeft", true));
728 self->setActionFinished(2);
729 self->setThinkTime(2);
730 map.setMainBossPart(map.boss[0]);
736 void galdovFinalFlee1()
740 addTeleportParticles(self->x + (self->width / 2), self->y + (self->height / 2), 50, SND_TELEPORT3);
741 Math::addBit(&self->flags, ENT_TELEPORTING);
742 Math::addBit(&self->flags, ENT_FLIES);
743 Math::addBit(&self->flags, ENT_FIRETRAIL);
744 self->think = &galdovFinalWaitForObjective;
745 self->setActionFinished(2);
746 self->setThinkTime(2);