]> git.mxchange.org Git - quix0rs-blobwars.git/blob - src/bosses.cpp
Don't link pak tool with SDL.
[quix0rs-blobwars.git] / src / bosses.cpp
1 /*
2 Copyright (C) 2004-2011 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                                         if(map.mainBossPart == map.boss[i])
402                                                 map.mainBossPart = NULL;
403                                         delete map.boss[i];
404                                         map.boss[i] = NULL;
405                                 } else
406                                         self->referenced = false;
407                         }
408                 }
409                 else if (self->actionFinished())
410                 {       
411                         if (self->readyForThink())
412                         {
413                                 self->think();
414                         }
415                 }
416         }
417         
418         map.doBossLevelAction();
419 }
420
421 bool isCorrectShieldFrequency(Entity *bullet)
422 {       
423         if (bullet->id != self->shieldFrequency)
424         {
425                 bullet->owner = self;
426                 bullet->dx = -bullet->dx;
427                 bullet->dx *= 0.25;
428                 bullet->dy = -2;
429                 
430                 bullet->x += bullet->dx;
431                 bullet->y += bullet->dy;
432                 
433                 bullet->health += 60;
434                 
435                 bullet->owner = self;
436                 
437                 Math::removeBit(&bullet->flags, ENT_WEIGHTLESS);
438                 Math::removeBit(&bullet->flags, ENT_BOUNCES);
439                 
440                 audio.playSound(SND_RICO1, CH_ANY);
441                 
442                 return false;
443         }
444         
445         return true;
446 }
447
448 void checkBossBulletCollisions(Entity *bullet)
449 {
450         if ((bullet->health < 1) || (player.health <= -60))
451         {
452                 return;
453         }
454
455         if (game.missionOver > 0)
456         {
457                 return;
458         }
459         
460         self = NULL;
461
462         if (bullet->owner == &player)
463         {
464                 for (int i = 0 ; i < 10 ; i++)
465                 {
466                         if (map.boss[i] == NULL)
467                         {
468                                 continue;
469                         }
470                         
471                         self = map.boss[i];
472                 
473                         if (Collision::collision((Entity*)self, bullet))
474                         {
475                                 
476                                 if (map.boss[i]->shieldFrequency != 999)
477                                 {
478                                         if (!isCorrectShieldFrequency(bullet))
479                                         {
480                                                 return;
481                                         }
482                                 }
483                                 
484                                 if (!(self->flags & ENT_IMMUNE))
485                                 {
486                                         self->health -= bullet->damage;
487                                         audio.playSound(SND_CLANG, CH_ANY);
488                                         addColorParticles(bullet->x, bullet->y, Math::rrand(25, 75), -1);
489                                         Math::removeBit(&bullet->flags, ENT_SPARKS);
490                                         Math::removeBit(&bullet->flags, ENT_PUFFS);
491                                 }
492                                 
493                                 if (self->react != NULL && self->health > 0)
494                                 {
495                                         self->react();
496                                 }
497         
498                                 if (bullet->id != WP_LASER)
499                                 {
500                                         bullet->health = 0;
501                                 }
502                         }
503                 }
504         }
505 }
506
507 void doGaldovAI(Entity *galdov)
508 {
509         map.fightingGaldov = true;
510
511         if (SDL_GetTicks() < map.bossNextThink)
512                 return;
513
514         map.bossNextThink = SDL_GetTicks() + Math::rrand(250, 1500);
515
516         switch (Math::prand() % 4)
517         {
518                 case 0:
519                         if ((Math::prand() % 5) == 0)
520                         {
521                                 if (galdov->flags & ENT_FLIES)
522                                 {
523                                         Math::removeBit(&galdov->flags, ENT_FLIES);
524                                         Math::removeBit(&galdov->flags, ENT_FIRETRAIL);
525                                 }
526                                 else
527                                 {
528                                         Math::addBit(&galdov->flags, ENT_FLIES);
529                                         Math::addBit(&galdov->flags, ENT_FIRETRAIL);
530                                 }
531                         }
532                         break;
533                 case 1:
534                         if (galdov->flags & ENT_JUMPS)
535                         {
536                                 Math::removeBit(&galdov->flags, ENT_JUMPS);
537                         }
538                         else
539                         {
540                                 Math::addBit(&galdov->flags, ENT_JUMPS);
541                         }
542                         break;
543                 case 2:
544                         galdov->currentWeapon = getRandomGaldovWeapon();
545                         break;
546                 case 3:
547                         if (galdov->flags & ENT_RAPIDFIRE)
548                         {
549                                 Math::removeBit(&galdov->flags, ENT_RAPIDFIRE);
550                         }
551                         else
552                         {
553                                 Math::addBit(&galdov->flags, ENT_RAPIDFIRE);
554                         }
555                         break;
556         }
557         
558         if ((Math::prand() % 25) == 0)
559         {
560                 switch (Math::prand() % 5)
561                 {
562                         case 0:
563                                 engine.setInfoMessage("Galdov: Stupid creature!! Give up and join us!", 0, INFO_HINT);
564                                 break;
565                         case 1:
566                                 engine.setInfoMessage("Galdov: Why do you persist in fighting us?!", 0, INFO_HINT);
567                                 break;
568                         case 2:
569                                 engine.setInfoMessage("Galdov: And this is the best the Blob Army can offer?", 0, INFO_HINT);
570                                 break;
571                         case 3:
572                                 engine.setInfoMessage("Galdov: We WILL have the crystals! NOTHING will stop us!!", 0, INFO_HINT);
573                                 break;
574                         case 4:
575                                 engine.setInfoMessage("Galdov: You're mine now!!!", 0, INFO_HINT);
576                                 break;
577                 }
578         }
579 }