]> git.mxchange.org Git - quix0rs-blobwars.git/blob - src/bosses.cpp
Don't define variables in header files.
[quix0rs-blobwars.git] / src / bosses.cpp
1 /*
2 Copyright (C) 2004-2011 Parallel Realities
3 Copyright (C) 2011-2015 Perpendicular Dimensions
4
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.
9
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.
13
14 See the GNU General Public License for more details.
15
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.
19
20 */
21
22 #include "bosses.h"
23
24 // this is used exclusively by the bosses
25 Boss *self;
26
27 void tankBossLevel()
28 {
29         if (player.health < 1)
30                 return;
31         
32         if (map.bossNextThink > 0)
33         {
34                 map.bossNextThink--;
35                 return;
36         }
37         
38         int r = 0;
39         
40         switch (Math::prand() % 4)
41         {
42                 case 0:
43                         r = ITEM_MACHINEGUN;
44                         break;
45                 case 1:
46                         r = ITEM_CHERRY;
47                         break;
48                 case 2:
49                         r = ITEM_DOUBLECHERRY;
50                         break;
51                 case 3:
52                         r = ITEM_TRIPLECHERRY;
53                         break;
54         }
55         
56         int x = 0;
57         int y = 8448;
58         
59         switch (Math::prand() % 4)
60         {
61                 case 0:
62                         x = 905;
63                         break;
64                 case 1:
65                         x = 840;
66                         break;
67                 case 2:
68                         x = 495;
69                         break;
70                 case 3:
71                         x = 425;
72                         break;
73         }
74         
75         addItem(defItem[r].id, defItem[r].name, x, y, defItem[r].sprite[0]->name, 240, defItem[r].value, ENT_DYING, true);
76         
77         Entity *enemy = (Entity*)map.enemyList.getHead();
78         
79         bool blob1 = false;
80         bool blob2 = false;
81         
82         if (game.skill >= 2)
83         {
84                 if ((Math::prand() % 3) == 0)
85                 {
86                         addEnemy("Red Blob", Math::rrand(525, 800), 8100, 0);
87                 }
88                 
89                 if (game.skill == 3)
90                 {
91                         addEnemy("Red Blob", Math::rrand(525, 800), 8100, 0);
92                 } 
93         }
94
95         while (enemy->next != NULL)
96         {
97                 enemy = (Entity*)enemy->next;
98                 
99                 if (strcmp(enemy->name, "Red Blob 1") == 0)
100                         blob1 = true;
101                 
102                 if (strcmp(enemy->name, "Red Blob 2") == 0)
103                         blob2 = true;   
104         }
105         
106         if (!blob1)
107         {
108                 addEnemy("Red Blob 1", 1100, 8352, 0);
109                 debug(("Added Red Blob #1\n"));
110         }
111                 
112         if (!blob2)
113         {
114                 addEnemy("Red Blob 2", 120, 8352, 0);
115                 debug(("Added Red Blob #2\n"));
116         }
117         
118         map.bossNextThink = Math::rrand(10, 15) * 60;
119 }
120
121 void aquaBossLevel()
122 {
123         if (player.health < 1)
124                 return;
125         
126         if (map.bossNextThink > 0)
127         {
128                 map.bossNextThink--;
129                 return;
130         }
131         
132         switch (Math::prand() % 4)
133         {
134                 case 0:
135                         addEnemy("Aqua Blob", 1600, 530, 0);
136                         break;
137                 case 1:
138                         addEnemy("Aqua Blob", 1600, 690, 0);
139                         break;
140                 case 2:
141                         addEnemy("Aqua Blob", 1600, 820, 0);
142                         break;
143                 case 3:
144                         addEnemy("Aqua Blob", 1600, 940, 0);
145                         break;
146         }
147         
148         map.bossNextThink = Math::rrand(5, 10) * 60;
149         if (game.skill >= 2)
150                 map.bossNextThink = Math::rrand(2, 6) * 60;
151 }
152
153 void droidBossLevel()
154 {
155         if (player.health < 1)
156                 return;
157         
158         if (map.bossNextThink > 0)
159         {
160                 map.bossNextThink--;
161                 return;
162         }
163         
164         Entity *item = (Entity*)map.itemList.getHead();
165         
166         bool addPistol, addMachineGun, addLaser, addSpread;
167         addPistol = addMachineGun = addLaser = addSpread = true;
168
169         while (item->next != NULL)
170         {
171                 item = (Entity*)item->next;
172                 
173                 if (item->flags & ENT_DYING)
174                         continue;
175                 
176                 switch (item->id)
177                 {
178                         case ITEM_PISTOL:
179                                 addPistol = false;
180                                 break;
181                         case ITEM_MACHINEGUN:
182                                 addMachineGun = false;
183                                 break;
184                         case ITEM_LASER:
185                                 addLaser = false;
186                                 break;
187                         case ITEM_SPREAD:
188                                 addSpread = false;
189                                 break;
190                 }
191         }
192         
193         if (addPistol)
194                 addItem(defItem[ITEM_PISTOL].id, defItem[ITEM_PISTOL].name, 2432, 4576, defItem[ITEM_PISTOL].sprite[0]->name, 240, defItem[ITEM_PISTOL].value, 0, false);
195                 
196         if (addMachineGun)
197                 addItem(defItem[ITEM_MACHINEGUN].id, defItem[ITEM_MACHINEGUN].name, 2432, 4224, defItem[ITEM_MACHINEGUN].sprite[0]->name, 240, defItem[ITEM_MACHINEGUN].value, 0, false);
198         
199         if (addLaser)
200                 addItem(defItem[ITEM_LASER].id, defItem[ITEM_LASER].name, 3552, 4384, defItem[ITEM_LASER].sprite[0]->name, 240, defItem[ITEM_LASER].value, 0, false);
201         
202         if (addSpread)
203                 addItem(defItem[ITEM_SPREAD].id, defItem[ITEM_SPREAD].name, 3552, 4800, defItem[ITEM_SPREAD].sprite[0]->name, 240, defItem[ITEM_SPREAD].value, 0, false);
204         
205         int r = Math::prand() % 6;
206         if (game.skill == 2)
207                 r = Math::prand() % 4;
208         
209         int x, y;
210         
211         switch (r)
212         {
213                 case 0:
214                         addEnemy("Eye Droid V4.0", 3648, 4704, 0);
215                         break;
216                 case 1:
217                         addEnemy("Eye Droid V4.0", 3648, 4224, 0);
218                         break;
219                 case 2:
220                         addEnemy("Eye Droid V4.0", 2336, 4128, 0);
221                         break;
222                 case 3:
223                         addEnemy("Eye Droid V4.0", 2336, 4736, 0);
224                         break;
225                 default:
226                         break;
227         }
228         
229         for (int i = 0 ; i < 2 ; i++)
230         {
231                 r = Math::rrand(ITEM_CHERRY, ITEM_DOUBLECHERRY);
232                 x = (int)player.x >> BRICKSHIFT;
233                 y = (int)player.y >> BRICKSHIFT;
234                 x += Math::rrand(-10, 10);
235                 y += Math::rrand(-10, 10);
236                 if (map.data[x][y] == MAP_AIR)
237                 {
238                         x = x << BRICKSHIFT;
239                         y = y << BRICKSHIFT;
240                         addItem(defItem[r].id, defItem[r].name, x, y, defItem[r].sprite[0]->name, 240, defItem[r].value, ENT_DYING, true);
241                 }
242         }
243         
244         map.bossNextThink = Math::rrand(10, 15) * 60;
245 }
246
247 void galdovLevel()
248 {
249 }
250
251 void galdovFinalLevel()
252 {
253         if (player.health < 1)
254                 return;
255         
256         if (map.bossNextThink > 0)
257         {
258                 map.bossNextThink--;
259                 return;
260         }
261         
262         map.bossNextThink = Math::rrand(10, 15) * 60;
263         
264         if ((map.boss[0]->health > 30 * game.skill) || (map.boss[0]->health <= 15 * game.skill))
265         {
266                 return;
267         }
268         
269         int bossIndex = -1;
270         
271         for (int i = 6 ; i < 10 ; i++)
272         {
273                 if (map.boss[i] == NULL)
274                         continue;
275                 
276                 if (map.boss[i]->health <= -60)
277                 {
278                         bossIndex = i;
279                         break;
280                 }
281         }
282         
283         if (bossIndex == -1)
284                 return;
285         
286         int x = Math::rrand(23, 62);
287         int y = Math::rrand(42, 61);
288         
289         if (map.data[x][y] == MAP_AIR)
290         {
291                 x = x << BRICKSHIFT;
292                 y = y << BRICKSHIFT;
293                 map.boss[bossIndex]->active = true;
294                 map.boss[bossIndex]->place(x, y);
295                 map.boss[bossIndex]->dx = 0;
296                 map.boss[bossIndex]->dy = 0;
297                 map.boss[bossIndex]->health = 3 * game.skill;
298                 map.boss[bossIndex]->maxHealth = 3 * game.skill;
299                 Math::addBit(&map.boss[bossIndex]->flags, ENT_FLIES);
300                 addTeleportParticles(map.boss[bossIndex]->x + (map.boss[bossIndex]->width / 2), map.boss[bossIndex]->y + (map.boss[bossIndex]->height / 2), 50, SND_TELEPORT2);
301         }
302 }
303
304 void createBoss(const char *stageName)
305 {
306         map.mainBossPart = NULL;
307         
308         if (!map.isBossMission)
309         {
310                 return;
311         }
312         
313         debug(("createBoss()\n"));
314         
315         if (strcmp(stageName, "BioMech Supply Depot") == 0)
316         {
317                 tankBossMGInit();
318                 tankBossGLInit();
319                 map.bossNextThink = 0;
320                 map.doBossLevelAction = &tankBossLevel;
321         }
322         else if (strcmp(stageName, "BioMech Communications") == 0)
323         {
324                 aquaBossInit();
325                 map.bossNextThink = 0;
326                 map.doBossLevelAction = &aquaBossLevel;
327         }
328         else if (strcmp(stageName, "BioMech Assimilator") == 0)
329         {
330                 droidBossInit();
331                 map.bossNextThink = 0;
332                 map.doBossLevelAction = &droidBossLevel;
333         }
334         else if (strcmp(stageName, "BioMech HQ") == 0)
335         {
336                 galdovInit();
337                 map.bossNextThink = 0;
338                 map.doBossLevelAction = &galdovLevel;
339         }
340         else if (strcmp(stageName, "Final Battle") == 0)
341         {
342                 galdovFinalInit();
343                 map.bossNextThink = 0;
344                 map.doBossLevelAction = &galdovFinalLevel;
345         }
346         
347         debug(("createBoss(): Done\n"));
348 }
349
350 void doBosses()
351 {
352         int x, y, absX, absY;
353         
354         self = NULL;
355         
356         for (int i = 0 ; i < 10 ; i++)
357         {
358                 if (map.boss[i] == NULL)
359                 {
360                         continue;
361                 }
362                 
363                 self = map.boss[i];
364                         
365                 moveEntity((Entity*)self);
366                 
367                 if (self->flags & ENT_TELEPORTING)
368                         continue;
369                 
370                 x = (int)(self->x - engine.playerPosX);
371                 y = (int)(self->y - engine.playerPosY);
372                 
373                 absX = abs(x);
374                 absY = abs(y);
375                 
376                 if ((absX < 700) && (absY < 500))
377                 {
378                         if (self->flags & ENT_FIRETRAIL)
379                         {
380                                 addFireTrailParticle(self->x + (self->face * 16) + Math::rrand(-1, 1), self->y + Math::rrand(-1, 1));
381                         }
382                         graphics.blit(self->getFaceImage(), x, y, graphics.screen, false);
383                         self->animate();
384                 }
385                 
386                 if (!self->active)
387                 {
388                         continue;
389                 }
390                 
391                 if (self->health <= 0)
392                 {
393                         if (self->health > -100)
394                         {
395                                 self->die();
396                         }
397                         else
398                         {
399                                 checkObjectives(self->name, false);     
400
401                                 if (!self->referenced)
402                                 {
403                                         debug(("Deleting unreferenced Boss...\n"));
404                                         
405                                         if(map.mainBossPart == map.boss[i])
406                                                 map.mainBossPart = NULL;
407                                         delete map.boss[i];
408                                         map.boss[i] = NULL;
409                                 } else
410                                         self->referenced = false;
411                         }
412                 }
413                 else if (self->actionFinished())
414                 {       
415                         if (self->readyForThink())
416                         {
417                                 self->think();
418                         }
419                 }
420         }
421         
422         map.doBossLevelAction();
423 }
424
425 bool isCorrectShieldFrequency(Entity *bullet)
426 {       
427         if (bullet->id != self->shieldFrequency)
428         {
429                 bullet->owner = self;
430                 bullet->dx = -bullet->dx;
431                 bullet->dx *= 0.25;
432                 bullet->dy = -2;
433                 
434                 bullet->x += bullet->dx;
435                 bullet->y += bullet->dy;
436                 
437                 bullet->health += 60;
438                 
439                 bullet->owner = self;
440                 
441                 Math::removeBit(&bullet->flags, ENT_WEIGHTLESS);
442                 Math::removeBit(&bullet->flags, ENT_BOUNCES);
443                 
444                 audio.playSound(SND_RICO1, CH_ANY, bullet->x);
445                 
446                 return false;
447         }
448         
449         return true;
450 }
451
452 void checkBossBulletCollisions(Entity *bullet)
453 {
454         if ((bullet->health < 1) || (player.health <= -60))
455         {
456                 return;
457         }
458
459         if (game.missionOver > 0)
460         {
461                 return;
462         }
463         
464         self = NULL;
465
466         if (bullet->owner == &player)
467         {
468                 for (int i = 0 ; i < 10 ; i++)
469                 {
470                         if (map.boss[i] == NULL)
471                         {
472                                 continue;
473                         }
474                         
475                         self = map.boss[i];
476                 
477                         if (Collision::collision((Entity*)self, bullet))
478                         {
479                                 
480                                 if (map.boss[i]->shieldFrequency != 999)
481                                 {
482                                         if (!isCorrectShieldFrequency(bullet))
483                                         {
484                                                 return;
485                                         }
486                                 }
487                                 
488                                 if (!(self->flags & ENT_IMMUNE))
489                                 {
490                                         self->health -= bullet->damage;
491                                         audio.playSound(SND_CLANG, CH_ANY, bullet->x);
492                                         addColorParticles(bullet->x, bullet->y, Math::rrand(25, 75), -1);
493                                         Math::removeBit(&bullet->flags, ENT_SPARKS);
494                                         Math::removeBit(&bullet->flags, ENT_PUFFS);
495                                 }
496                                 
497                                 if (self->react != NULL && self->health > 0)
498                                 {
499                                         self->react();
500                                 }
501         
502                                 if (bullet->id != WP_LASER)
503                                 {
504                                         bullet->health = 0;
505                                 }
506                         }
507                 }
508         }
509 }
510
511 void doGaldovAI(Entity *galdov)
512 {
513         map.fightingGaldov = true;
514
515         if (SDL_GetTicks() < map.bossNextThink)
516                 return;
517
518         map.bossNextThink = SDL_GetTicks() + Math::rrand(250, 1500);
519
520         switch (Math::prand() % 4)
521         {
522                 case 0:
523                         if ((Math::prand() % 5) == 0)
524                         {
525                                 if (galdov->flags & ENT_FLIES)
526                                 {
527                                         Math::removeBit(&galdov->flags, ENT_FLIES);
528                                         Math::removeBit(&galdov->flags, ENT_FIRETRAIL);
529                                 }
530                                 else
531                                 {
532                                         Math::addBit(&galdov->flags, ENT_FLIES);
533                                         Math::addBit(&galdov->flags, ENT_FIRETRAIL);
534                                 }
535                         }
536                         break;
537                 case 1:
538                         if (galdov->flags & ENT_JUMPS)
539                         {
540                                 Math::removeBit(&galdov->flags, ENT_JUMPS);
541                         }
542                         else
543                         {
544                                 Math::addBit(&galdov->flags, ENT_JUMPS);
545                         }
546                         break;
547                 case 2:
548                         galdov->currentWeapon = getRandomGaldovWeapon();
549                         break;
550                 case 3:
551                         if (galdov->flags & ENT_RAPIDFIRE)
552                         {
553                                 Math::removeBit(&galdov->flags, ENT_RAPIDFIRE);
554                         }
555                         else
556                         {
557                                 Math::addBit(&galdov->flags, ENT_RAPIDFIRE);
558                         }
559                         break;
560         }
561         
562         if ((Math::prand() % 25) == 0)
563         {
564                 switch (Math::prand() % 5)
565                 {
566                         case 0:
567                                 engine.setInfoMessage("Galdov: Stupid creature!! Give up and join us!", 0, INFO_HINT);
568                                 break;
569                         case 1:
570                                 engine.setInfoMessage("Galdov: Why do you persist in fighting us?!", 0, INFO_HINT);
571                                 break;
572                         case 2:
573                                 engine.setInfoMessage("Galdov: And this is the best the Blob Army can offer?", 0, INFO_HINT);
574                                 break;
575                         case 3:
576                                 engine.setInfoMessage("Galdov: We WILL have the crystals! NOTHING will stop us!!", 0, INFO_HINT);
577                                 break;
578                         case 4:
579                                 engine.setInfoMessage("Galdov: You're mine now!!!", 0, INFO_HINT);
580                                 break;
581                 }
582         }
583 }