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