2 Copyright (C) 2004 Parallel Realities
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 #include "finalBattle.h"
23 void galdovMiniAttack();
24 void galdovFinalFlee1();
25 void blackDroidAttack();
27 // ************************ Black Droids for Part 2 *************************************
31 (self->x < player.x) ? self->face = 0 : self->face = 1;
33 if (!hasClearShot(self))
36 self->setActionFinished(9);
37 self->setThinkTime(2);
38 self->think = &blackDroidFire;
40 addBullet((Entity*)self, 0, 0);
43 if (self->custom == 0)
45 self->think = &blackDroidAttack;
52 Math::removeBit(&self->flags, ENT_FLIES);
54 if (self->health < -10)
56 if ((self->health % 3) == 0)
58 addExplosion(self->x, self->y, 35, self);
59 addSmokeAndFire(self, Math::rrand(-15, 15), Math::rrand(-15, 15), 2);
63 if (self->health <= -60)
65 if ((Math::prand() % 2) == 0)
67 dropItems((int)self->x, (int)self->y);
68 dropItems((int)self->x, (int)self->y);
72 addItem(100, "Red Keycard", (int)self->x, (int)self->y, "RedKeyCard", 240, 1, ENT_DYING, true);
75 self->place(9999, 9999);
79 void blackDroidChasePlayer()
81 self->think = &blackDroidAttack;
83 (self->x < player.x) ? self->face = 0 : self->face = 1;
87 Math::calculateSlope(player.x, player.y, self->x, self->y, &dx, &dy);
92 self->setThinkTime(10);
93 self->setActionFinished(10);
96 void blackDroidMoveRandom()
98 (self->x < player.x) ? self->face = 0 : self->face = 1;
100 self->think = &blackDroidAttack;
102 self->dx = Math::rrand(-200, 200);
103 self->dy = Math::rrand(-200, 200);
108 self->setThinkTime(2);
109 self->setActionFinished(Math::rrand(60, 90));
112 void blackDroidAttack()
114 self->think = blackDroidAttack;
115 self->setThinkTime(2);
116 self->setActionFinished(2);
118 int r = Math::prand() % 15;
122 self->think = &blackDroidFire;
123 self->setThinkTime(2);
124 self->setActionFinished(2);
125 self->custom = Math::rrand(2, 4) * game.skill;
129 self->think = &blackDroidChasePlayer;
130 self->setThinkTime(2);
131 self->setActionFinished(2);
135 self->think = &blackDroidMoveRandom;
136 self->setThinkTime(2);
137 self->setActionFinished(2);
141 void galdovInitBlackDroids()
143 for (int i = 6 ; i < 10 ; i++)
145 if (map.boss[i] == NULL)
147 map.boss[i] = new Boss();
149 debug(("BlackDroid %d init\n", i));
150 strcpy(map.boss[i]->name, "BlackDrod");
151 map.boss[i]->health = -90;
152 map.boss[i]->maxHealth = -90;
153 map.boss[i]->setSprites(graphics.getSprite("BlackDroidRight", true), graphics.getSprite("BlackDroidLeft", true), graphics.getSprite("BlackDroidDie", true));
154 map.boss[i]->currentWeapon = &weapon[WP_PLASMARIFLE];
155 map.boss[i]->face = 0;
156 map.boss[i]->active = false;
157 map.boss[i]->place(9999, 9999);
158 map.boss[i]->immune = 0;
159 map.boss[i]->setThinkTime(2);
160 map.boss[i]->think = &blackDroidAttack;
161 map.boss[i]->react = NULL;
162 map.boss[i]->die = &blackDroidDie;
163 map.boss[i]->setActionFinished(120);
164 Math::addBit(&map.boss[i]->flags, ENT_FLIES);
165 Math::addBit(&map.boss[i]->flags, ENT_AIMS);
166 Math::removeBit(&map.boss[i]->flags, ENT_FIRETRAIL);
170 // ************************ First part of final battle **********************************
172 void galdovFinalKeepAlive1()
174 map.boss[0]->health = 30 * game.skill;
177 void galdovFinalPainThrow()
182 self->think = &galdovFinalFlee1;
183 self->setThinkTime(2);
184 self->setActionFinished(140);
187 void galdovFinalDropCrystal()
189 self->setThinkTime(2);
190 self->setActionFinished(2);
192 int x = (int)player.x >> BRICKSHIFT;
193 int y = (int)player.y >> BRICKSHIFT;
194 x += Math::rrand(-2, 2);
195 y += Math::rrand(-2, 2);
197 Math::limitInt(&x, 2, 62);
198 Math::limitInt(&y, 2, 26);
200 if (map.data[x][y] == MAP_AIR)
206 addTeleportParticles(self->x + 10, self->y + 10, 50, SND_TELEPORT2);
207 addItem(101, "Reality Crystal", (int)self->x, (int)self->y, "RealityCrystal", 100, 1, 0, true);
208 self->think = &galdovFinalPainThrow;
209 self->react = &galdovFinalKeepAlive1;
210 Math::removeBit(&self->flags, ENT_FLIES);
211 Math::removeBit(&self->flags, ENT_FIRETRAIL);
212 map.setMainBossPart(NULL);
213 map.boss[0]->health = 30 * game.skill;
214 map.boss[0]->setSprites(graphics.getSprite("GaldovPain", true), graphics.getSprite("GaldovPain", true), graphics.getSprite("GaldovPain", true));
215 engine.setInfoMessage("Galdov has dropped the crystal! Quick! Get it!!", 99, INFO_HINT);
216 audio.playSound(SND_BOSSCUSTOM2, CH_AMBIANCE);
220 void galdovFinalDie()
222 if (game.missionOverReason == MIS_INPROGRESS)
224 game.missionOverReason = MIS_GAMECOMPLETE;
226 audio.playSound(SND_BOSSCUSTOM3, CH_AMBIANCE);
231 Math::removeBit(&self->flags, ENT_FLIES);
232 Math::removeBit(&self->flags, ENT_FIRETRAIL);
233 presentPlayerMedal("Final_Battle");
236 self->setActionFinished(1);
237 self->setThinkTime(1);
241 if (self->health > -35)
244 if (self->health == -99)
246 checkObjectives(self->name, false);
249 Math::wrapInt(&self->health, -99, -41);
251 if ((self->health % 3) == 0)
253 self->setActionFinished(5);
254 self->setThinkTime(2);
256 self->dx = Math::rrand(-5, 5);
257 self->dy = Math::rrand(-5, 5);
259 addExplosion(self->x, self->y, 50, &player);
260 addSmokeAndFire(self, Math::rrand(-5, 5), Math::rrand(-5, 5), 2);
264 void galdovMiniFire()
266 (self->x < player.x) ? self->face = 0 : self->face = 1;
268 if (!hasClearShot(self))
271 self->setActionFinished(9);
272 self->setThinkTime(2);
273 self->think = &galdovMiniFire;
275 addBullet((Entity*)self, 0, 0);
278 if (self->custom == 0)
280 self->think = &galdovMiniAttack;
284 void galdovFinalTeleport()
286 self->setThinkTime(2);
287 self->setActionFinished(2);
289 int x = (int)player.x >> BRICKSHIFT;
290 int y = (int)player.y >> BRICKSHIFT;
291 x += Math::rrand(-10, 10);
292 y += Math::rrand(-10, 10);
294 Math::limitInt(&x, 2, 62);
295 Math::limitInt(&y, 2, 26);
297 if (self == map.boss[0])
299 if (self->health < 45 * game.skill)
301 Math::limitInt(&x, 23, 62);
302 Math::limitInt(&y, 42, 61);
303 self->currentWeapon = getRandomGaldovWeapon();
307 if (map.data[x][y] == MAP_AIR)
313 addTeleportParticles(self->x + 10, self->y + 10, 50, SND_TELEPORT2);
314 self->think = &galdovMiniAttack;
318 void galdovMiniChasePlayer()
320 (self->x < player.x) ? self->face = 0 : self->face = 1;
324 Math::calculateSlope(player.x, player.y, self->x, self->y, &dx, &dy);
329 self->setThinkTime(10);
330 self->setActionFinished(10);
331 self->think = &galdovMiniAttack;
334 void galdovMiniMoveRandom()
336 self->think = &galdovMiniAttack;
338 if (self->flags & ENT_FLIES)
340 self->dx = Math::rrand(-200, 200);
341 self->dy = Math::rrand(-200, 200);
346 self->setThinkTime(2);
347 self->setActionFinished(Math::rrand(60, 90));
351 self->dy = Math::rrand(-12, 0);
353 (player.x < self->x) ? self->dx = -3 : self->dx = 3;
355 self->think = &galdovMiniAttack;
356 self->setActionFinished(45);
360 void galdovMiniAttack()
362 if (player.health < 1)
364 self->dx = self->dy = 0;
368 (self->x < player.x) ? self->face = 0 : self->face = 1;
370 self->think = &galdovMiniAttack;
371 self->setThinkTime(2);
372 self->setActionFinished(2);
374 int r = Math::prand() % 100;
378 self->think = &galdovMiniFire;
379 self->custom = Math::rrand(1, 8);
383 addTeleportParticles(self->x + 10, self->y + 10, 50, SND_TELEPORT2);
386 self->setActionFinished(120);
387 self->setThinkTime(2);
388 self->think = &galdovFinalTeleport;
392 self->think = &galdovMiniChasePlayer;
396 self->think = &galdovMiniMoveRandom;
400 if ((self->flags & ENT_FLIES) && (Math::prand() % 5) == 0)
402 Math::removeBit(&self->flags, ENT_FLIES);
403 Math::removeBit(&self->flags, ENT_FIRETRAIL);
407 Math::addBit(&self->flags, ENT_FLIES);
408 Math::addBit(&self->flags, ENT_FIRETRAIL);
415 addExplosion(self->x, self->y, 5, self);
416 dropItems((int)self->x, (int)self->y);
417 self->setThinkTime(2);
418 self->setActionFinished(2);
419 self->active = false;
420 self->place(9999, 9999);
422 // Don't kill it properly(!!)
427 for (int i = 1 ; i < 10 ; i++)
429 if (map.boss[i]->health > 0)
437 map.boss[0]->think = &galdovFinalDropCrystal;
438 map.boss[0]->setThinkTime(2);
439 map.boss[0]->setActionFinished(2);
445 debug(("galdovRejoin\n"));
447 for (int i = 1 ; i < 10 ; i++)
449 if (map.boss[i] != NULL)
451 addTeleportParticles(map.boss[i]->x + 4, map.boss[i]->y + 4, 75, -1);
453 if ((Math::prand() % (2 + (game.skill))) == 0)
455 dropItems((int)map.boss[i]->x, (int)map.boss[i]->y);
458 map.boss[i]->place(9999, 9999);
459 map.boss[i]->active = false;
463 self->setThinkTime(2);
464 self->setActionFinished(120);
465 self->think = &galdovFinalTeleport;
466 self->currentWeapon = getRandomGaldovWeapon();
468 audio.playSound(SND_BOSSCUSTOM1, CH_AMBIANCE);
470 map.setMainBossPart(map.boss[0]);
472 debug(("galdovRejoin: Done\n"));
475 void galdovFinalMiniReact()
477 if (self->health > 0)
479 map.setMainBossPart(self);
483 map.setMainBossPart(NULL);
487 void galdovFinalSplit()
489 debug(("galdovFinalSplit\n"));
491 for (int i = 1 ; i < 10 ; i++)
493 if (map.boss[i] == NULL)
495 map.boss[i] = new Boss();
496 strcpy(map.boss[i]->name, "MiniGaldov");
497 map.boss[i]->health = 10 * game.skill;
498 map.boss[i]->maxHealth = 10 * game.skill;
500 map.boss[i]->setSprites(graphics.getSprite("GaldovMiniRight", true), graphics.getSprite("GaldovMiniLeft", true), graphics.getSprite("GaldovMiniLeft", true));
501 map.boss[i]->currentWeapon = getRandomAimedWeapon();
502 map.boss[i]->face = 0;
503 map.boss[i]->active = true;
505 if (map.boss[i]->health > 0)
507 map.boss[i]->dx = Math::rrand(-2, 2);
508 map.boss[i]->dy = Math::rrand(-2, 2);
509 map.boss[i]->x = map.boss[0]->x;
510 map.boss[i]->y = map.boss[0]->y;
514 map.boss[i]->active = false;
517 map.boss[i]->immune = 0;
518 map.boss[i]->setThinkTime(2);
519 map.boss[i]->setActionFinished(120);
520 map.boss[i]->think = &galdovMiniAttack;
521 map.boss[i]->react = &galdovFinalMiniReact;
522 map.boss[i]->die = &galdovMiniDie;
523 Math::addBit(&map.boss[i]->flags, ENT_AIMS);
524 Math::addBit(&map.boss[i]->flags, ENT_FLIES);
525 Math::addBit(&map.boss[i]->flags, ENT_FIRETRAIL);
527 addTeleportParticles(map.boss[i]->x + 10 + map.boss[i]->dx, map.boss[i]->y + 10 + map.boss[i]->dy, 75, -1);
530 map.boss[0]->place(9999, 9999);
531 map.boss[0]->setThinkTime(2);
532 map.boss[0]->setActionFinished(Math::rrand(10, 15) * 60);
533 map.boss[0]->think = &galdovRejoin;
534 map.boss[0]->active = true;
536 audio.playSound(SND_BOSSCUSTOM1, CH_AMBIANCE);
537 map.setMainBossPart(NULL);
539 debug(("galdovFinalSplit: Done\n"));
542 void galdovFinalReact()
544 map.boss[0]->health = 45 * game.skill;
546 if ((Math::prand() % 5) == 0)
548 map.boss[0]->setThinkTime(2);
549 map.boss[0]->setActionFinished(2);
550 map.boss[0]->think = &galdovFinalSplit;
554 void galdovFinalInit()
556 debug(("galdovFinalInit\n"));
558 map.boss[0] = new Boss();
559 strcpy(map.boss[0]->name, "Galdov");
560 map.boss[0]->health = 45 * game.skill;
561 map.boss[0]->maxHealth = 45 * game.skill;
562 map.boss[0]->setSprites(graphics.getSprite("GaldovRight", true), graphics.getSprite("GaldovLeft", true), graphics.getSprite("GaldovDie", true));
563 map.boss[0]->currentWeapon = &weapon[WP_AIMEDPISTOL];
564 map.boss[0]->face = 0;
565 map.boss[0]->active = true;
566 map.boss[0]->x = 480;
567 map.boss[0]->y = 576;
568 map.boss[0]->immune = 0;
569 map.boss[0]->setThinkTime(2);
570 map.boss[0]->setActionFinished(2);
571 map.boss[0]->think = &galdovMiniAttack;
572 //map.boss[0]->think = &galdovFinalDropCrystal;
573 map.boss[0]->react = &galdovFinalReact;
574 map.boss[0]->die = &galdovFinalDie;
575 Math::addBit(&map.boss[0]->flags, ENT_AIMS);
577 audio.loadSound(SND_BOSSCUSTOM1, "sound/galdovSplit.wav");
578 audio.loadSound(SND_BOSSCUSTOM2, "sound/galdovPain.wav");
579 audio.loadSound(SND_BOSSCUSTOM3, "sound/galdovDie.wav");
581 map.setMainBossPart(map.boss[0]);
583 debug(("galdovFinalInit: Done\n"));
586 // ******************** Second part of Final Battle *********************************
588 void galdovFinalReact2()
590 if (self->health < 1)
593 if ((Math::prand() % 12) == 0)
595 addTeleportParticles(self->x + (self->width / 2), self->y + (self->height / 2), 50, SND_TELEPORT3);
596 map.boss[0]->setThinkTime(2);
597 map.boss[0]->setActionFinished(2);
598 map.boss[0]->think = &galdovFinalTeleport;
602 void galdovFinalStandUp()
604 map.boss[0]->setSprites(graphics.getSprite("GaldovShieldRight", true), graphics.getSprite("GaldovShieldLeft", true), graphics.getSprite("GaldovShieldLeft", true));
605 self->setThinkTime(2);
606 self->setActionFinished(2);
607 addTeleportParticles(self->x + (self->width / 2), self->y + (self->height / 2), 50, SND_TELEPORT3);
608 self->think = &galdovFinalTeleport;
610 if (map.boss[0]->health <= 15 * game.skill)
612 self->setSprites(graphics.getSprite("GaldovRight", true), graphics.getSprite("GaldovLeft", true), graphics.getSprite("GaldovDie", true));
613 Math::removeBit(&map.boss[0]->flags, ENT_IMMUNE);
614 map.boss[0]->react = &galdovFinalReact2;
618 void galdovFinalPainThrow2()
623 self->think = &galdovFinalStandUp;
624 self->setThinkTime(2);
625 self->setActionFinished(90);
630 (self->x < player.x) ? self->face = 0 : self->face = 1;
634 Math::calculateSlope(map.boss[0]->x, map.boss[0]->y, self->x, self->y, &dx, &dy);
636 self->dx = (dx * (1 + game.skill));
637 self->dy = (dy * (1 + game.skill));
639 self->setThinkTime(2);
640 self->setActionFinished(5);
641 self->think = &orbSeekGaldov;
643 int distX = (int)fabs(map.boss[0]->x - self->x);
644 int distY = (int)fabs(map.boss[0]->y - self->y);
649 int distance = (int)sqrt(distX + distY);
653 self->active = false;
654 self->setActionFinished(60);
655 self->setThinkTime(60);
656 addExplosion(self->x, self->y, 75, &player);
657 self->place(9999, 9999);
658 map.boss[0]->setSprites(graphics.getSprite("GaldovPain", true), graphics.getSprite("GaldovPain", true), graphics.getSprite("GaldovPain", true));
659 map.boss[0]->think = &galdovFinalPainThrow2;
660 map.boss[0]->health -= (3 * game.skill);
661 Math::removeBit(&map.boss[0]->flags, ENT_FLIES);
662 Math::removeBit(&map.boss[0]->flags, ENT_FIRETRAIL);
663 audio.playSound(SND_BOSSCUSTOM2, CH_AMBIANCE);
667 void galdovFinalShieldInit()
669 self->setActionFinished(60);
670 self->setThinkTime(60);
671 self->think = &galdovMiniAttack;
674 Math::addBit(&map.boss[0]->flags, ENT_IMMUNE);
676 for (int i = 1 ; i < 6 ; i++)
678 if (map.boss[i] != NULL)
682 map.boss[i] = new Boss();
683 strcpy(map.boss[i]->name, "OrbBomb");
684 map.boss[i]->setSprites(graphics.getSprite("DroidOrb", true), graphics.getSprite("DroidOrb", true), graphics.getSprite("DroidOrb", true));
685 map.boss[i]->health = 999999;
686 map.boss[i]->maxHealth = 99999;
687 map.boss[i]->immune = 0;
688 Math::addBit(&map.boss[0]->flags, ENT_IMMUNE);
689 map.boss[i]->active = false;
690 map.boss[i]->setActionFinished(2);
691 map.boss[i]->setThinkTime(2);
692 map.boss[i]->think = &orbSeekGaldov;
693 map.boss[i]->react = NULL;
694 Math::addBit(&map.boss[i]->flags, ENT_FLIES);
697 map.boss[1]->place(864, 1536);
698 map.boss[2]->place(864, 1792);
699 map.boss[3]->place(1856, 1792);
700 map.boss[4]->place(1888, 1440);
701 map.boss[5]->place(1408, 1472);
703 galdovInitBlackDroids();
706 void galdovFinalWaitForObjective()
708 self->setActionFinished(120);
709 self->setThinkTime(2);
711 Objective *objective = (Objective*)map.objectiveList.getHead();
713 while (objective->next != NULL)
715 objective = (Objective*)objective->next;
717 if (strstr(objective->target, "Reality"))
719 if (objective->completed)
723 Math::addBit(&player.flags, ENT_TELEPORTING);
724 self->think = &galdovFinalShieldInit;
725 map.boss[0]->setSprites(graphics.getSprite("GaldovShieldRight", true), graphics.getSprite("GaldovShieldLeft", true), graphics.getSprite("GaldovShieldLeft", true));
726 self->setActionFinished(2);
727 self->setThinkTime(2);
728 map.setMainBossPart(map.boss[0]);
734 void galdovFinalFlee1()
738 addTeleportParticles(self->x + (self->width / 2), self->y + (self->height / 2), 50, SND_TELEPORT3);
739 Math::addBit(&self->flags, ENT_TELEPORTING);
740 Math::addBit(&self->flags, ENT_FLIES);
741 Math::addBit(&self->flags, ENT_FIRETRAIL);
742 self->think = &galdovFinalWaitForObjective;
743 self->setActionFinished(2);
744 self->setThinkTime(2);