Debug system v2 -- now, it supports debug levels, there's a global level, but overrid...
[dbw.git] / src / finalBattle.cpp
blob3d6fff376b27ae82ef98281959ea8cd787df9001
1 /*
2 Copyright © 2004 Parallel Realities
3 Copyright © 2007-2008 Kővágó Zoltán <DirtY.iCE.hu@gmail.com>
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 <cmath>
24 #include "CAudio.h"
25 #include "CEngine.h"
26 #include "CGame.h"
27 #include "CGraphics.h"
28 #include "CMap.h"
29 #include "CMath.h"
30 #include "CObjective.h"
31 #include "console/console.hpp"
32 #include "finalBattle.h"
34 void galdovMiniAttack();
35 void galdovFinalFlee1();
36 void blackDroidAttack();
38 // ************************ Black Droids for Part 2 *************************************
40 void blackDroidFire()
42 (self->x < player.x) ? self->face = 0 : self->face = 1;
44 if (!hasClearShot(self))
45 return;
47 self->setActionFinished(9);
48 self->setThinkTime(2);
49 self->think = &blackDroidFire;
51 addBullet((Entity*)self, 0, 0);
53 self->custom--;
54 if (self->custom == 0)
56 self->think = &blackDroidAttack;
60 void blackDroidDie()
62 self->health--;
63 Math::removeBit(&self->flags, ENT_FLIES);
65 if (self->health < -10)
67 if ((self->health % 3) == 0)
69 addExplosion(self->x, self->y, 35, self);
70 addSmokeAndFire(self, Math::rrand(-15, 15), Math::rrand(-15, 15), 2);
74 if (self->health <= -60)
76 if ((rand() % 2) == 0)
78 dropItems((int)self->x, (int)self->y);
79 dropItems((int)self->x, (int)self->y);
81 else
83 addItem(100, "Red Keycard", (int)self->x, (int)self->y, "RedKeyCard", 240, 1, ENT_DYING, true);
85 self->active = false;
86 self->place(9999, 9999);
90 void blackDroidChasePlayer()
92 self->think = &blackDroidAttack;
94 (self->x < player.x) ? self->face = 0 : self->face = 1;
96 float dx, dy;
98 Math::calculateSlope(player.x, player.y, self->x, self->y, &dx, &dy);
100 self->dx = (dx * 2);
101 self->dy = (dy * 2);
103 self->setThinkTime(10);
104 self->setActionFinished(10);
107 void blackDroidMoveRandom()
109 (self->x < player.x) ? self->face = 0 : self->face = 1;
111 self->think = &blackDroidAttack;
113 self->dx = Math::rrand(-200, 200);
114 self->dy = Math::rrand(-200, 200);
116 self->dx *= 0.01;
117 self->dy *= 0.01;
119 self->setThinkTime(2);
120 self->setActionFinished(Math::rrand(60, 90));
123 void blackDroidAttack()
125 self->think = blackDroidAttack;
126 self->setThinkTime(2);
127 self->setActionFinished(2);
129 int r = rand() % 15;
131 if (r <= 2)
133 self->think = &blackDroidFire;
134 self->setThinkTime(2);
135 self->setActionFinished(2);
136 self->custom = Math::rrand(2, 4) * game.skill;
138 else if (r < 5)
140 self->think = &blackDroidChasePlayer;
141 self->setThinkTime(2);
142 self->setActionFinished(2);
144 else
146 self->think = &blackDroidMoveRandom;
147 self->setThinkTime(2);
148 self->setActionFinished(2);
152 void galdovInitBlackDroids()
154 for (int i = 6 ; i < 10 ; i++)
156 if (map.boss[i] == NULL)
158 map.boss[i] = new Boss();
160 debug("boss", 2) << "BlackDroid " << i << " init" << std::endl;
161 strcpy(map.boss[i]->name, "BlackDrod");
162 map.boss[i]->health = -90;
163 map.boss[i]->maxHealth = -90;
164 map.boss[i]->setSprites(graphics.getSprite("BlackDroidRight", true), graphics.getSprite("BlackDroidLeft", true), graphics.getSprite("BlackDroidDie", true));
165 map.boss[i]->currentWeapon = &weapon[WP_PLASMARIFLE];
166 map.boss[i]->face = 0;
167 map.boss[i]->active = false;
168 map.boss[i]->place(9999, 9999);
169 map.boss[i]->immune = 0;
170 map.boss[i]->setThinkTime(2);
171 map.boss[i]->think = &blackDroidAttack;
172 map.boss[i]->react = NULL;
173 map.boss[i]->die = &blackDroidDie;
174 map.boss[i]->setActionFinished(120);
175 Math::addBit(&map.boss[i]->flags, ENT_FLIES);
176 Math::addBit(&map.boss[i]->flags, ENT_AIMS);
177 Math::removeBit(&map.boss[i]->flags, ENT_FIRETRAIL);
181 // ************************ First part of final battle **********************************
183 void galdovFinalKeepAlive1()
185 map.boss[0]->health = 30 * game.skill;
188 void galdovFinalPainThrow()
190 self->dx = -3;
191 self->dy = -12;
193 self->think = &galdovFinalFlee1;
194 self->setThinkTime(2);
195 self->setActionFinished(140);
198 void galdovFinalDropCrystal()
200 self->setThinkTime(2);
201 self->setActionFinished(2);
203 int x = (int)player.x >> BRICKSHIFT;
204 int y = (int)player.y >> BRICKSHIFT;
205 x += Math::rrand(-2, 2);
206 y += Math::rrand(-2, 2);
208 Math::limitInt(&x, 2, 62);
209 Math::limitInt(&y, 2, 26);
211 if (map.data[x][y] == MAP_AIR)
213 x = x << BRICKSHIFT;
214 y = y << BRICKSHIFT;
215 self->x = x;
216 self->y = y;
217 addTeleportParticles(self->x + 10, self->y + 10, 50, SND_TELEPORT2);
218 addItem(101, "Reality Crystal", (int)self->x, (int)self->y, "RealityCrystal", 100, 1, 0, true);
219 self->think = &galdovFinalPainThrow;
220 self->react = &galdovFinalKeepAlive1;
221 Math::removeBit(&self->flags, ENT_FLIES);
222 Math::removeBit(&self->flags, ENT_FIRETRAIL);
223 map.setMainBossPart(NULL);
224 map.boss[0]->health = 30 * game.skill;
225 map.boss[0]->setSprites(graphics.getSprite("GaldovPain", true), graphics.getSprite("GaldovPain", true), graphics.getSprite("GaldovPain", true));
226 engine.setInfoMessage("Galdov has dropped the crystal! Quick! Get it!!", 99, INFO_HINT);
227 audio.playSound(SND_BOSSCUSTOM2, CH_AMBIANCE);
231 void galdovFinalDie()
233 if (game.missionOverReason == MIS_INPROGRESS)
235 game.missionOverReason = MIS_GAMECOMPLETE;
236 audio.stopMusic();
237 audio.playSound(SND_BOSSCUSTOM3, CH_AMBIANCE);
238 player.health = 10;
239 self->dx = 5;
240 self->dy = -6;
241 self->react = NULL;
242 Math::removeBit(&self->flags, ENT_FLIES);
243 Math::removeBit(&self->flags, ENT_FIRETRAIL);
246 self->setActionFinished(1);
247 self->setThinkTime(1);
249 self->health--;
251 if (self->health > -35)
252 return;
254 if (self->health == -99)
256 checkObjectives(self->name, false);
259 Math::wrapInt(&self->health, -99, -41);
261 if ((self->health % 3) == 0)
263 self->setActionFinished(5);
264 self->setThinkTime(2);
266 self->dx = Math::rrand(-5, 5);
267 self->dy = Math::rrand(-5, 5);
269 addExplosion(self->x, self->y, 50, &player);
270 addSmokeAndFire(self, Math::rrand(-5, 5), Math::rrand(-5, 5), 2);
274 void galdovMiniFire()
276 (self->x < player.x) ? self->face = 0 : self->face = 1;
278 if (!hasClearShot(self))
279 return;
281 self->setActionFinished(9);
282 self->setThinkTime(2);
283 self->think = &galdovMiniFire;
285 addBullet((Entity*)self, 0, 0);
287 self->custom--;
288 if (self->custom == 0)
290 self->think = &galdovMiniAttack;
294 void galdovFinalTeleport()
296 self->setThinkTime(2);
297 self->setActionFinished(2);
299 int x = (int)player.x >> BRICKSHIFT;
300 int y = (int)player.y >> BRICKSHIFT;
301 x += Math::rrand(-10, 10);
302 y += Math::rrand(-10, 10);
304 Math::limitInt(&x, 2, 62);
305 Math::limitInt(&y, 2, 26);
307 if (self == map.boss[0])
309 if (self->health < 45 * game.skill)
311 Math::limitInt(&x, 23, 62);
312 Math::limitInt(&y, 42, 61);
313 self->currentWeapon = getRandomGaldovWeapon();
317 if (map.data[x][y] == MAP_AIR)
319 x = x << BRICKSHIFT;
320 y = y << BRICKSHIFT;
321 self->x = x;
322 self->y = y;
323 addTeleportParticles(self->x + 10, self->y + 10, 50, SND_TELEPORT2);
324 self->think = &galdovMiniAttack;
328 void galdovMiniChasePlayer()
330 (self->x < player.x) ? self->face = 0 : self->face = 1;
332 float dx, dy;
334 Math::calculateSlope(player.x, player.y, self->x, self->y, &dx, &dy);
336 self->dx = (dx * 2);
337 self->dy = (dy * 2);
339 self->setThinkTime(10);
340 self->setActionFinished(10);
341 self->think = &galdovMiniAttack;
344 void galdovMiniMoveRandom()
346 self->think = &galdovMiniAttack;
348 if (self->flags & ENT_FLIES)
350 self->dx = Math::rrand(-200, 200);
351 self->dy = Math::rrand(-200, 200);
353 self->dx *= 0.01;
354 self->dy *= 0.01;
356 self->setThinkTime(2);
357 self->setActionFinished(Math::rrand(60, 90));
359 else
361 self->dy = Math::rrand(-12, 0);
363 (player.x < self->x) ? self->dx = -3 : self->dx = 3;
365 self->think = &galdovMiniAttack;
366 self->setActionFinished(45);
370 void galdovMiniAttack()
372 if (player.health < 1)
374 self->dx = self->dy = 0;
375 return;
378 (self->x < player.x) ? self->face = 0 : self->face = 1;
380 self->think = &galdovMiniAttack;
381 self->setThinkTime(2);
382 self->setActionFinished(2);
384 int r = rand() % 100;
386 if (r < 10)
388 self->think = &galdovMiniFire;
389 self->custom = Math::rrand(1, 8);
391 else if (r < 15)
393 addTeleportParticles(self->x + 10, self->y + 10, 50, SND_TELEPORT2);
394 self->x = -9999;
395 self->y = -9999;
396 self->setActionFinished(120);
397 self->setThinkTime(2);
398 self->think = &galdovFinalTeleport;
400 else if (r < 80)
402 self->think = &galdovMiniChasePlayer;
404 else if (r < 90)
406 self->think = &galdovMiniMoveRandom;
408 else
410 if ((self->flags & ENT_FLIES) && (rand() % 5) == 0)
412 Math::removeBit(&self->flags, ENT_FLIES);
413 Math::removeBit(&self->flags, ENT_FIRETRAIL);
415 else
417 Math::addBit(&self->flags, ENT_FLIES);
418 Math::addBit(&self->flags, ENT_FIRETRAIL);
423 void galdovMiniDie()
425 addExplosion(self->x, self->y, 5, self);
426 dropItems((int)self->x, (int)self->y);
427 self->setThinkTime(2);
428 self->setActionFinished(2);
429 self->active = false;
430 self->place(9999, 9999);
432 // Don't kill it properly(!!)
433 self->health = -1;
435 bool allDead = true;
437 for (int i = 1 ; i < 10 ; i++)
439 if (map.boss[i]->health > 0)
441 allDead = false;
445 if (allDead)
447 map.boss[0]->think = &galdovFinalDropCrystal;
448 map.boss[0]->setThinkTime(2);
449 map.boss[0]->setActionFinished(2);
453 void galdovRejoin()
455 debug("boss", 2) << "galdovRejoin" << std::endl;
457 for (int i = 1 ; i < 10 ; i++)
459 if (map.boss[i] != NULL)
461 addTeleportParticles(map.boss[i]->x + 4, map.boss[i]->y + 4, 75, -1);
463 if ((rand() % (2 + (game.skill))) == 0)
465 dropItems((int)map.boss[i]->x, (int)map.boss[i]->y);
468 map.boss[i]->place(9999, 9999);
469 map.boss[i]->active = false;
473 self->setThinkTime(2);
474 self->setActionFinished(120);
475 self->think = &galdovFinalTeleport;
476 self->currentWeapon = getRandomGaldovWeapon();
478 audio.playSound(SND_BOSSCUSTOM1, CH_AMBIANCE);
480 map.setMainBossPart(map.boss[0]);
482 debug("boss", 2) << "galdovRejoin: Done" << std::endl;
485 void galdovFinalMiniReact()
487 if (self->health > 0)
489 map.setMainBossPart(self);
491 else
493 map.setMainBossPart(NULL);
497 void galdovFinalSplit()
499 debug("boss", 2) << "galdovFinalSplit" << std::endl;
501 for (int i = 1 ; i < 10 ; i++)
503 if (map.boss[i] == NULL)
505 map.boss[i] = new Boss();
506 strcpy(map.boss[i]->name, "MiniGaldov");
507 map.boss[i]->health = 10 * game.skill;
508 map.boss[i]->maxHealth = 10 * game.skill;
510 map.boss[i]->setSprites(graphics.getSprite("GaldovMiniRight", true), graphics.getSprite("GaldovMiniLeft", true), graphics.getSprite("GaldovMiniLeft", true));
511 map.boss[i]->currentWeapon = getRandomAimedWeapon();
512 map.boss[i]->face = 0;
513 map.boss[i]->active = true;
515 if (map.boss[i]->health > 0)
517 map.boss[i]->dx = Math::rrand(-2, 2);
518 map.boss[i]->dy = Math::rrand(-2, 2);
519 map.boss[i]->x = map.boss[0]->x;
520 map.boss[i]->y = map.boss[0]->y;
522 else
524 map.boss[i]->active = false;
527 map.boss[i]->immune = 0;
528 map.boss[i]->setThinkTime(2);
529 map.boss[i]->setActionFinished(120);
530 map.boss[i]->think = &galdovMiniAttack;
531 map.boss[i]->react = &galdovFinalMiniReact;
532 map.boss[i]->die = &galdovMiniDie;
533 Math::addBit(&map.boss[i]->flags, ENT_AIMS);
534 Math::addBit(&map.boss[i]->flags, ENT_FLIES);
535 Math::addBit(&map.boss[i]->flags, ENT_FIRETRAIL);
537 addTeleportParticles(map.boss[i]->x + 10 + map.boss[i]->dx, map.boss[i]->y + 10 + map.boss[i]->dy, 75, -1);
540 map.boss[0]->place(9999, 9999);
541 map.boss[0]->setThinkTime(2);
542 map.boss[0]->setActionFinished(Math::rrand(10, 15) * 60);
543 map.boss[0]->think = &galdovRejoin;
544 map.boss[0]->active = true;
546 audio.playSound(SND_BOSSCUSTOM1, CH_AMBIANCE);
547 map.setMainBossPart(NULL);
549 debug("boss", 2) << "galdovFinalSplit: Done" << std::endl;
552 void galdovFinalReact()
554 map.boss[0]->health = 45 * game.skill;
556 if ((rand() % 5) == 0)
558 map.boss[0]->setThinkTime(2);
559 map.boss[0]->setActionFinished(2);
560 map.boss[0]->think = &galdovFinalSplit;
564 void galdovFinalInit()
566 debug("boss", 2) << "galdovFinalInit" << std::endl;
568 map.boss[0] = new Boss();
569 strcpy(map.boss[0]->name, "Galdov");
570 map.boss[0]->health = 45 * game.skill;
571 map.boss[0]->maxHealth = 45 * game.skill;
572 map.boss[0]->setSprites(graphics.getSprite("GaldovRight", true), graphics.getSprite("GaldovLeft", true), graphics.getSprite("GaldovDie", true));
573 map.boss[0]->currentWeapon = &weapon[WP_AIMEDPISTOL];
574 map.boss[0]->face = 0;
575 map.boss[0]->active = true;
576 map.boss[0]->x = 480;
577 map.boss[0]->y = 576;
578 map.boss[0]->immune = 0;
579 map.boss[0]->setThinkTime(2);
580 map.boss[0]->setActionFinished(2);
581 map.boss[0]->think = &galdovMiniAttack;
582 //map.boss[0]->think = &galdovFinalDropCrystal;
583 map.boss[0]->react = &galdovFinalReact;
584 map.boss[0]->die = &galdovFinalDie;
585 Math::addBit(&map.boss[0]->flags, ENT_AIMS);
587 audio.loadSound(SND_BOSSCUSTOM1, "sound/galdovSplit.wav");
588 audio.loadSound(SND_BOSSCUSTOM2, "sound/galdovPain.wav");
589 audio.loadSound(SND_BOSSCUSTOM3, "sound/galdovDie.wav");
591 map.setMainBossPart(map.boss[0]);
593 debug("boss", 2) << "galdovFinalInit: Done" << std::endl;
596 // ******************** Second part of Final Battle *********************************
598 void galdovFinalReact2()
600 if (self->health < 1)
601 return;
603 if ((rand() % 12) == 0)
605 addTeleportParticles(self->x + (self->width / 2), self->y + (self->height / 2), 50, SND_TELEPORT3);
606 map.boss[0]->setThinkTime(2);
607 map.boss[0]->setActionFinished(2);
608 map.boss[0]->think = &galdovFinalTeleport;
612 void galdovFinalStandUp()
614 map.boss[0]->setSprites(graphics.getSprite("GaldovShieldRight", true), graphics.getSprite("GaldovShieldLeft", true), graphics.getSprite("GaldovShieldLeft", true));
615 self->setThinkTime(2);
616 self->setActionFinished(2);
617 addTeleportParticles(self->x + (self->width / 2), self->y + (self->height / 2), 50, SND_TELEPORT3);
618 self->think = &galdovFinalTeleport;
620 if (map.boss[0]->health <= 15 * game.skill)
622 self->setSprites(graphics.getSprite("GaldovRight", true), graphics.getSprite("GaldovLeft", true), graphics.getSprite("GaldovDie", true));
623 Math::removeBit(&map.boss[0]->flags, ENT_IMMUNE);
624 map.boss[0]->react = &galdovFinalReact2;
628 void galdovFinalPainThrow2()
630 self->dx = -3;
631 self->dy = -12;
633 self->think = &galdovFinalStandUp;
634 self->setThinkTime(2);
635 self->setActionFinished(90);
638 void orbSeekGaldov()
640 (self->x < player.x) ? self->face = 0 : self->face = 1;
642 float dx, dy;
644 Math::calculateSlope(map.boss[0]->x, map.boss[0]->y, self->x, self->y, &dx, &dy);
646 self->dx = (dx * (1 + game.skill));
647 self->dy = (dy * (1 + game.skill));
649 self->setThinkTime(2);
650 self->setActionFinished(5);
651 self->think = &orbSeekGaldov;
653 int distX = (int)fabs(map.boss[0]->x - self->x);
654 int distY = (int)fabs(map.boss[0]->y - self->y);
656 distX *= distX;
657 distY *= distY;
659 int distance = (int)sqrt(distX + distY);
661 if (distance <= 10)
663 self->active = false;
664 self->setActionFinished(60);
665 self->setThinkTime(60);
666 addExplosion(self->x, self->y, 75, &player);
667 self->place(9999, 9999);
668 map.boss[0]->setSprites(graphics.getSprite("GaldovPain", true), graphics.getSprite("GaldovPain", true), graphics.getSprite("GaldovPain", true));
669 map.boss[0]->think = &galdovFinalPainThrow2;
670 map.boss[0]->health -= (3 * game.skill);
671 Math::removeBit(&map.boss[0]->flags, ENT_FLIES);
672 Math::removeBit(&map.boss[0]->flags, ENT_FIRETRAIL);
673 audio.playSound(SND_BOSSCUSTOM2, CH_AMBIANCE);
677 void galdovFinalShieldInit()
679 self->setActionFinished(60);
680 self->setThinkTime(60);
681 self->think = &galdovMiniAttack;
682 self->react = NULL;
684 Math::addBit(&map.boss[0]->flags, ENT_IMMUNE);
686 for (int i = 1 ; i < 6 ; i++)
688 if (map.boss[i] != NULL)
690 delete map.boss[i];
692 map.boss[i] = new Boss();
693 strcpy(map.boss[i]->name, "OrbBomb");
694 map.boss[i]->setSprites(graphics.getSprite("DroidOrb", true), graphics.getSprite("DroidOrb", true), graphics.getSprite("DroidOrb", true));
695 map.boss[i]->health = 999999;
696 map.boss[i]->maxHealth = 99999;
697 map.boss[i]->immune = 0;
698 Math::addBit(&map.boss[0]->flags, ENT_IMMUNE);
699 map.boss[i]->active = false;
700 map.boss[i]->setActionFinished(2);
701 map.boss[i]->setThinkTime(2);
702 map.boss[i]->think = &orbSeekGaldov;
703 map.boss[i]->react = NULL;
704 Math::addBit(&map.boss[i]->flags, ENT_FLIES);
707 map.boss[1]->place(864, 1536);
708 map.boss[2]->place(864, 1792);
709 map.boss[3]->place(1856, 1792);
710 map.boss[4]->place(1888, 1440);
711 map.boss[5]->place(1408, 1472);
713 galdovInitBlackDroids();
716 void galdovFinalWaitForObjective()
718 self->setActionFinished(120);
719 self->setThinkTime(2);
721 Objective *objective = (Objective*)map.objectiveList.getHead();
723 while (objective->next != NULL)
725 objective = (Objective*)objective->next;
727 if (strstr(objective->target, "Reality"))
729 if (objective->completed)
731 player.dx = 1000;
732 player.dy = 1408;
733 Math::addBit(&player.flags, ENT_TELEPORTING);
734 self->think = &galdovFinalShieldInit;
735 map.boss[0]->setSprites(graphics.getSprite("GaldovShieldRight", true), graphics.getSprite("GaldovShieldLeft", true), graphics.getSprite("GaldovShieldLeft", true));
736 self->setActionFinished(2);
737 self->setThinkTime(2);
738 map.setMainBossPart(map.boss[0]);
744 void galdovFinalFlee1()
746 self->dx = 800;
747 self->dy = 1408;
748 addTeleportParticles(self->x + (self->width / 2), self->y + (self->height / 2), 50, SND_TELEPORT3);
749 Math::addBit(&self->flags, ENT_TELEPORTING);
750 Math::addBit(&self->flags, ENT_FLIES);
751 Math::addBit(&self->flags, ENT_FIRETRAIL);
752 self->think = &galdovFinalWaitForObjective;
753 self->setActionFinished(2);
754 self->setThinkTime(2);