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