]> git.mxchange.org Git - quix0rs-blobwars.git/blob - src/enemies.cpp
Added .gitignore to ignore certain files + fixed access rights on Makefile* as
[quix0rs-blobwars.git] / src / enemies.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 "enemies.h"
23
24 Entity *getDefinedEnemy(const char *name)
25 {
26         for (int i = 0 ; i < MAX_ENEMIES ; i++)
27         {
28                 if (strcmp(name, defEnemy[i].name) == 0)
29                 {
30                         return &defEnemy[i];
31                 }
32         }
33
34         debug(("No Such Enemy '%s'\n", name));
35
36         return NULL;
37 }
38
39 Entity *getEnemy(const char *name)
40 {
41         Entity *enemy = (Entity*)map.enemyList.getHead();
42
43         while (enemy->next != NULL)
44         {
45                 enemy = (Entity*)enemy->next;
46
47                 if (strcmp(name, enemy->name) == 0)
48                 {
49                         return enemy;
50                 }
51         }
52         
53         debug(("No Such Enemy '%s'\n", name));
54
55         return NULL;
56 }
57
58 void addEnemy(const char *name, int x, int y, int flags)
59 {
60         Entity *defEnemy = getDefinedEnemy(name);
61
62         if (defEnemy == NULL)
63         {
64                 debug(("ERROR : COULDN'T FIND ENEMY '%s'!\n", name));
65                 return;
66         }
67
68         Entity *enemy = new Entity();
69         enemy->setName(defEnemy->name);
70         enemy->setSprites(defEnemy->sprite[0], defEnemy->sprite[1], defEnemy->sprite[2]);
71         enemy->currentWeapon = defEnemy->currentWeapon;
72         enemy->value = defEnemy->value;
73         enemy->health = defEnemy->health;
74         enemy->flags = defEnemy->flags;
75
76         enemy->place(x, y);
77         enemy->setVelocity(0, 0);
78         enemy->baseThink = 60;
79
80         enemy->flags += flags;
81         
82         enemy->reload = 120; // Wait about seconds seconds before attacking
83
84         if (map.data[(int)(enemy->x) >> BRICKSHIFT][(int)(enemy->y) >> BRICKSHIFT] == MAP_WATER)
85         {
86                 enemy->environment = ENV_WATER;
87         }
88
89         map.addEnemy(enemy);
90 }
91
92 bool hasClearShot(Entity *enemy)
93 {
94         int mx, my;
95         float x = enemy->x;
96         float y = enemy->y;
97         float dx, dy;
98
99         Math::calculateSlope(player.x, player.y, enemy->x, enemy->y, &dx, &dy);
100
101         if ((dx == 0) && (dy == 0))
102                 return true;
103
104         while (true)
105         {
106                 x += dx;
107                 y += dy;
108
109                 //graphics.blit(graphics.getSprite("AimedShot", true)->getCurrentFrame(), (int)(x - engine.playerPosX), (int)(y - engine.playerPosY), graphics.screen, true);
110
111                 mx = (int)(x) >> BRICKSHIFT;
112                 my = (int)(y) >> BRICKSHIFT;
113
114                 if ((mx < 0) || (my < 0))
115                         return false;
116
117                 if (map.isSolid(mx, my))
118                         return false;
119
120                 if (Collision::collision(x, y, 3, 3, (int)player.x, (int)player.y, player.height, player.width))
121                         break;
122         }
123
124         return true;
125 }
126
127 void lookForPlayer(Entity *enemy)
128 {
129         // player is dead
130         if (player.health <= -60)
131                 return;
132         
133         if (game.missionOverReason == MIS_COMPLETE)
134                 return;
135
136         // can't fire anyway!
137         if (enemy->reload > 0)
138                 return;
139
140         int x = (int)fabs(enemy->x - player.x);
141         int y = (int)fabs(enemy->y - player.y);
142
143         // out of range
144         if (x > 480)
145                 return;
146
147         // can't even jump that high!
148         if (y > 100)
149                 return;
150
151         // Player is in range... go for them!
152         if (enemy->flags & ENT_ALWAYSCHASE)
153         {
154                 enemy->owner->tx = (int)(player.x);
155                 enemy->owner->ty = (int)(player.y);
156         }
157         else if ((Math::prand() % (35 - game.skill)) == 0)
158         {
159                 enemy->owner->tx = (int)(player.x);
160                 enemy->owner->ty = (int)(player.y);
161         }
162
163         // facing the wrong way
164         if ((enemy->face == 0) && (player.x < enemy->x))
165         {
166                 return;
167         }
168
169         // still facing the wrong way
170         if ((enemy->face == 1) && (player.x > enemy->x))
171         {
172                 return;
173         }
174
175         if (hasClearShot(enemy))
176         {
177                 if (enemy->flags & ENT_ALWAYSFIRES)
178                 {
179                         addBullet(enemy, enemy->currentWeapon->getSpeed(enemy->face), 0);
180                         if (enemy->currentWeapon == &weapon[WP_ALIENSPREAD])
181                         {
182                                 addBullet(enemy, enemy->currentWeapon->getSpeed(enemy->face), 2);
183                                 addBullet(enemy, enemy->currentWeapon->getSpeed(enemy->face), -2);
184                         }
185                 }
186                 else if ((Math::prand() % 850) <= (game.skill * 5))
187                 {
188                         addBullet(enemy, enemy->currentWeapon->getSpeed(enemy->face), 0);
189                         if (enemy->currentWeapon == &weapon[WP_ALIENSPREAD])
190                         {
191                                 addBullet(enemy, enemy->currentWeapon->getSpeed(enemy->face), 2);
192                                 addBullet(enemy, enemy->currentWeapon->getSpeed(enemy->face), -2);
193                         }
194                 }
195
196                 if (enemy->flags & ENT_RAPIDFIRE)
197                 {
198                         if (enemy->flags & ENT_ALWAYSFIRES)
199                         {
200                                 if ((Math::prand() % 25) > game.skill * 3)
201                                         Math::removeBit(&enemy->flags, ENT_ALWAYSFIRES);
202                         }
203                         else
204                         {
205                                 if ((Math::prand() % 50) < game.skill * 2)
206                                         Math::addBit(&enemy->flags, ENT_ALWAYSFIRES);
207                         }
208                 }
209         }
210         else
211         {
212                 if (enemy->flags & ENT_RAPIDFIRE)
213                         Math::removeBit(&enemy->flags, ENT_ALWAYSFIRES);
214         }
215
216         if ((enemy->flags & ENT_FLIES) || (enemy->flags & ENT_SWIMS) || (enemy->flags & ENT_NOJUMP))
217                 return;
218                 
219         if (enemy->flags & ENT_JUMPS)
220         {
221                 if (!enemy->falling)
222                 {
223                         if ((Math::prand() % 25) == 0)
224                         {
225                                 int distance = Math::rrand(1, 4);
226                                 enemy->setVelocity(distance - ((distance * 2) * enemy->face), Math::rrand(-12, -10));
227                         }
228                 }
229                 
230                 return;
231         }
232
233         // Jump to try and reach player (even if they are approximately level with you!)
234         if (player.y - 5 < enemy->y)
235         {
236                 if (!enemy->falling)
237                 {
238                         if ((Math::prand() % 100) == 0)
239                         {
240                                 enemy->dy = -12;
241                         }
242                 }
243         }
244 }
245
246 void doAI(Entity *enemy)
247 {
248         if (enemy->flags & ENT_GALDOV)
249         {
250                 doGaldovAI(enemy);
251         }
252         else if (enemy->flags & ENT_BOSS)
253         {
254                 return;
255         }
256
257         int x = (int)enemy->x;
258         int y = (int)enemy->y + enemy->height;
259         
260         if (enemy->dx > 0)
261                 x += enemy->width;
262
263         x = x >> BRICKSHIFT;
264         y = y >> BRICKSHIFT;
265
266         if (enemy->dx == 0)
267                 enemy->tx = (int)enemy->x;
268
269         // Don't enter areas you're not supposed to
270         if (enemy->tx != (int)enemy->x)
271         {
272                 if (!(enemy->flags & (ENT_FLIES|ENT_SWIMS)))
273                 {
274                         if (!map.isSolid(x, y))
275                         {
276                                 enemy->tx = (int)enemy->x;
277                         }
278                 }
279         }
280
281         if ((int)enemy->x == enemy->tx)
282         {
283                 if ((Math::prand() % 100) == 0)
284                 {
285                         enemy->tx = (int)(enemy->x + Math::rrand(-640, 640));
286                         enemy->ty = (int)(enemy->y);
287                         if ((enemy->flags & ENT_FLIES) || (enemy->flags & ENT_SWIMS))
288                         {
289                                 enemy->ty = (int)(enemy->y + Math::rrand(-320, 320));
290                         }
291                 }
292
293                 Math::limitInt(&enemy->tx, 15, (MAPWIDTH * BRICKSIZE)- 20);
294                 Math::limitInt(&enemy->ty, 15, (MAPHEIGHT * BRICKSIZE)- 20);
295
296                 if (map.isSolid((enemy->tx >> BRICKSHIFT), (enemy->ty >> BRICKSHIFT)))
297                 {
298                         enemy->tx = (int)enemy->x;
299                         enemy->ty = (int)enemy->y;
300                 }
301         }
302
303         // Don't enter areas you're not supposed to
304         if (enemy->ty != (int)enemy->y)
305         {
306                 if (enemy->flags & ENT_FLIES)
307                 {
308                         if (map.isLiquid(x, y + 1))
309                         {
310                                 enemy->ty = (int)enemy->y;
311                         }
312                 }
313         }
314
315         if ((int)enemy->y == enemy->ty)
316         {
317                 enemy->y = enemy->ty;
318                 enemy->dx = 0;
319         }
320
321         if (!enemy->falling)
322                 enemy->dx = 0;
323
324         if ((enemy->flags & ENT_FLIES) || (enemy->flags & ENT_SWIMS))
325         {
326                 enemy->dx = enemy->dy = 0;
327
328                 if ((int)enemy->y < enemy->ty) enemy->dy = 1;
329                 if ((int)enemy->y > enemy->ty) enemy->dy = -1;
330         }
331
332         if ((int)enemy->x == enemy->tx) {enemy->dx = 0;}
333         if ((int)enemy->x < enemy->tx) {enemy->dx = 1; enemy->face = 0;}
334         if ((int)enemy->x > enemy->tx) {enemy->dx = -1; enemy->face = 1;}
335
336         if ((enemy->flags & ENT_SWIMS) && (enemy->environment == ENV_WATER))
337         {
338                 enemy->dy = 0;
339
340                 if ((int)enemy->y < enemy->ty) enemy->dy = 1;
341                 if ((int)enemy->y > enemy->ty) enemy->dy = -1;
342         }
343
344         lookForPlayer(enemy);
345 }
346
347 void checkCombo()
348 {
349         int old = game.currentComboHits;
350         
351         game.doCombo();
352         
353         if (old == 24 && game.currentComboHits == 25)
354         {
355                 presentPlayerMedal("25_Hit_Combo");
356         }
357 }
358
359 void enemyBulletCollisions(Entity *bullet)
360 {
361         if (bullet->health < 1)
362         {
363                 return;
364         }
365
366         Entity *enemy = (Entity*)map.enemyList.getHead();
367
368         while (enemy->next != NULL)
369         {
370                 enemy = (Entity*)enemy->next;
371
372                 if ((enemy->flags & ENT_TELEPORTING) || (enemy->dead == DEAD_DYING))
373                 {
374                         continue;
375                 }
376
377                 char comboString[100];
378
379                 if ((bullet->owner == &player) || (bullet->owner == &engine.world) || (bullet->flags & ENT_BOSS))
380                 {
381                         if (Collision::collision(enemy, bullet))
382                         {
383                                 if (bullet->id != WP_LASER)
384                                 {
385                                         bullet->health = 0;
386                                 }
387                                 
388                                 Math::removeBit(&bullet->flags, ENT_SPARKS);
389                                 Math::removeBit(&bullet->flags, ENT_PUFFS);
390
391                                 if ((enemy->flags & ENT_IMMUNE) && (!(enemy->flags & ENT_STATIC)))
392                                 {
393                                         bullet->health = 0; // include the Laser for this one!
394                                         enemy->owner->tx = (int)bullet->owner->x;
395                                         enemy->owner->ty = (int)bullet->owner->y;
396                                         if (enemy->x < enemy->tx) {enemy->owner->dx = 1; enemy->owner->face = 0;}
397                                         if (enemy->x > enemy->tx) {enemy->owner->dx = -1; enemy->owner->face = 1;}
398                                         return;
399                                 }
400
401                                 /*
402                                         Increment the bullet hits counter. The laser can only do this
403                                         if the target has more than 0 health. Overwise the stats screen
404                                         can show an accurracy of 800%. Which is just plain silly.
405                                 */
406                                 if (bullet->owner == &player)
407                                 {
408                                         enemy->tx = (int)player.x;
409                                         enemy->ty = (int)player.y;
410                                         enemy->face = 0;
411                                         
412                                         if (player.x < enemy->x)
413                                         {
414                                                 enemy->face = 1;
415                                         }
416
417                                         if ((bullet->id != WP_LASER) || (enemy->health > 0))
418                                         {
419                                                 game.incBulletsHit();
420                                         }
421                                 }
422
423                                 if (!(enemy->flags & ENT_EXPLODES))
424                                 {
425                                         audio.playSound(SND_HIT, CH_ANY, enemy->x);
426                                         if (game.gore)
427                                         {
428                                                 addBlood(enemy, bullet->dx / 4, Math::rrand(-6, -3), 1);
429                                         }
430                                         else
431                                         {
432                                                 addColorParticles(bullet->x, bullet->y, Math::rrand(25, 75), -1);
433                                         }
434                                 }
435                                 else
436                                 {
437                                         audio.playSound(SND_CLANG, CH_ANY, enemy->x);
438                                         addColorParticles(bullet->x, bullet->y, Math::rrand(25, 75), -1);
439                                 }
440
441                                 if (enemy->health > 0)
442                                 {
443                                         if (!(enemy->flags & ENT_IMMUNE))
444                                         {
445                                                 enemy->health -= bullet->damage;
446                                         }
447                                         
448                                         if (enemy->health <= 0)
449                                         {       
450                                                 if (bullet->owner == &player)
451                                                 {
452                                                         addPlayerScore(enemy->value);
453                                                         game.currentMissionEnemiesDefeated++;
454         
455                                                         if (player.currentWeapon != &weapon[WP_LASER])
456                                                         {
457                                                                 checkCombo();
458                                                         }
459                                                         
460                                                         snprintf(comboString, sizeof comboString, "Combo-%s", bullet->name);
461                                                         checkObjectives(comboString, false);
462                                                         checkObjectives("Enemy", false);
463                                                         checkObjectives(enemy->name, false);
464                                                 }
465         
466                                                 if (!(enemy->flags & ENT_STATIC))
467                                                 {
468                                                         enemy->dx = (bullet->dx / 4);
469                                                         enemy->dy = -10;
470                                                         
471                                                         if (enemy->flags & ENT_EXPLODES)
472                                                         {
473                                                                 audio.playSound(SND_ELECDEATH1 + Math::prand() % 3, CH_DEATH, enemy->x);
474                                                         }
475                                                         else if (game.gore)
476                                                         {
477                                                                 audio.playSound(SND_DEATH1 + Math::prand() % 3, CH_DEATH, enemy->x);
478                                                         }
479                                                 }
480                                         }
481                                         
482                                 }
483                                 
484                                 if (enemy->flags & ENT_STATIC)
485                                 {
486                                         return;
487                                 }
488
489                                 if (enemy->health < 0)
490                                 {
491                                         enemy->dx = Math::rrand(-3, 3);
492                                         enemy->dy = 5 - Math::prand() % 15;
493                                         enemy->health = -1;
494                                         
495                                         if (enemy->flags & ENT_EXPLODES)
496                                         {
497                                                 audio.playSound(SND_ELECDEATH1 + Math::prand() % 3, CH_DEATH, enemy->x);
498                                         }
499                                         else if (game.gore)
500                                         {
501                                                 audio.playSound(SND_DEATH1 + Math::prand() % 3, CH_DEATH, enemy->x);
502                                         }
503
504                                         if (bullet->owner == &player)
505                                         {
506                                                 if (player.currentWeapon != &weapon[WP_LASER])
507                                                 {
508                                                         snprintf(comboString, sizeof comboString, "Combo-%s", bullet->name);
509                                                         checkCombo();
510                                                         checkObjectives(comboString, false);
511                                                 }
512                                         }
513                                 }
514
515                                 if (game.currentComboHits >= 3)
516                                 {
517                                         char message[50];
518                                         snprintf(message, sizeof message, _("%d Hit Combo!"), game.currentComboHits);
519                                         engine.setInfoMessage(message, 0, INFO_NORMAL);
520                                 }
521
522                                 return;
523                         }
524                 }
525         }
526 }
527
528 int getNonGoreParticleColor(const char *name)
529 {
530         int rtn = graphics.yellow;
531         
532         if (strcmp(name, "Pistol Blob") == 0)
533         {
534                 rtn = graphics.green;
535         }
536         else if (strcmp(name, "Grenade Blob") == 0)
537         {
538                 rtn = graphics.skyBlue;
539         }
540         else if (strcmp(name, "Aqua Blob") == 0)
541         {
542                 rtn = graphics.cyan;
543         }
544         else if (strcmp(name, "Laser Blob") == 0)
545         {
546                 rtn = SDL_MapRGB(graphics.screen->format, 255, 0, 255);
547         }
548         else if (strcmp(name, "Machine Gun Blob") == 0)
549         {
550                 rtn = SDL_MapRGB(graphics.screen->format, 200, 64, 24);
551         }
552         
553         return rtn;
554 }
555
556 void gibEnemy(Entity *enemy)
557 {
558         if (enemy->flags & ENT_GALDOV)
559         {
560                 addTeleportParticles(enemy->x, enemy->y, 75, SND_TELEPORT3);
561                 checkObjectives("Galdov", true);
562         }
563
564         if (enemy->flags & ENT_EXPLODES)
565         {
566                 addExplosion(enemy->x + (enemy->width / 2), enemy->y + (enemy->height / 2), 10 + (20 * game.skill), enemy);
567                 addSmokeAndFire(enemy, Math::rrand(-5, 5), Math::rrand(-5, 5), 2);
568                 return;
569         }
570
571         float x, y, dx, dy;
572         int amount = (game.gore) ? 25 : 150;
573         int color = getNonGoreParticleColor(enemy->name);
574
575         for (int i = 0 ; i < amount ; i++)
576         {
577                 x = enemy->x + Math::rrand(-3, 3);
578                 y = enemy->y + Math::rrand(-3, 3);
579                 
580                 if (game.gore)
581                 {
582                         dx = Math::rrand(-5, 5);
583                         dy = Math::rrand(-15, -5);
584                         addEffect(x, y, dx, dy, EFF_BLEEDS);
585                 }
586                 else
587                 {
588                         dx = Math::rrand(-5, 5);
589                         dy = Math::rrand(-5, 5);
590                         addColoredEffect(x, y, dx, dy, color, EFF_COLORED + EFF_WEIGHTLESS);
591                 }
592         }
593         
594         (game.gore) ? audio.playSound(SND_SPLAT, CH_ANY) : audio.playSound(SND_POP, CH_ANY, enemy->x);
595 }
596
597 void doEnemies()
598 {
599         Entity *enemy = (Entity*)map.enemyList.getHead();
600         Entity *previous = enemy;
601         
602         map.fightingGaldov = false;
603
604         int x, y, absX, absY;
605
606         while (enemy->next != NULL)
607         {
608                 enemy = (Entity*)enemy->next;
609                 
610                 if (!engine.cheatBlood)
611                 {
612                         if (enemy->dead == DEAD_DYING)
613                         {
614                                 if (!enemy->referenced)
615                                 {
616                                         debug(("Removing unreferenced enemy '%s'\n", enemy->name));
617                                         map.enemyList.remove(previous, enemy);
618                                         enemy = previous;
619                                 }
620                                 else
621                                 {
622                                         previous = enemy;
623                                 }
624                                 
625                                 enemy->referenced = false;
626                                 continue;
627                         }
628                 }
629
630                 x = (int)(enemy->x - engine.playerPosX);
631                 y = (int)(enemy->y - engine.playerPosY);
632
633                 absX = abs(x);
634                 absY = abs(y);
635
636                 if ((absX < 800) && (absY < 600))
637                 {
638                         // Fly forever
639                         if (enemy->flags & ENT_FLIES)
640                         {
641                                 enemy->fuel = 7;
642                         }
643
644                         if (enemy->owner->flags & ENT_TELEPORTING)
645                         {
646                                 moveEntity(enemy);
647                         }
648                         else
649                         {
650                                 if ((enemy->health > 0) && (!(enemy->flags & ENT_STATIC)))
651                                 {
652                                         enemy->think();
653
654                                         if (enemy->owner == enemy)
655                                         {
656                                                 doAI(enemy);
657                                         }
658                                         else
659                                         {
660                                                 lookForPlayer(enemy);
661                                         }
662                                 }
663                                 
664                                 if (map.isBlizzardLevel)
665                                 {
666                                         enemy->dx += map.windPower * 0.1;
667                                 }
668
669                                 if (enemy->flags & ENT_NOMOVE)
670                                 {
671                                         enemy->dx = 0;
672                                 }
673
674                                 moveEntity(enemy);
675
676                                 if ((absX < 700) && (absY < 500))
677                                 {
678                                         if (enemy->flags & ENT_FIRETRAIL)
679                                         {
680                                                 addFireTrailParticle(enemy->x + (enemy->face * 16) + Math::rrand(-1, 1), enemy->y + Math::rrand(-1, 1));
681                                         }
682                                         
683                                         graphics.blit(enemy->getFaceImage(), x, y, graphics.screen, false);
684                                         
685                                         if ((enemy->dx != 0) || (enemy->flags & ENT_FLIES) || (enemy->flags & ENT_STATIC))
686                                         {
687                                                 enemy->animate();
688                                         }
689                                 }
690                         }
691                 }
692                 else
693                 {
694                         if (enemy->flags & ENT_SPAWNED)
695                         {
696                                 if ((absX > 1920) || (absY > 1440))
697                                 {
698                                         enemy->health = -100;
699                                 }
700                         }
701                 }
702
703                 if (enemy->health > 0)
704                 {
705                         previous = enemy;
706                         
707                         if ((enemy->environment == ENV_SLIME) || (enemy->environment == ENV_LAVA))
708                         {
709                                 checkObjectives(enemy->name, false);
710                                 enemy->health = -1;
711                         }
712                 }
713                 else
714                 {
715                         if (enemy->flags & ENT_GALDOV)
716                         {
717                                 enemy->health = -99;
718                         }
719
720                         if (enemy->health == 0)
721                         {
722                                 Math::removeBit(&enemy->flags, ENT_WEIGHTLESS);
723                                 Math::removeBit(&enemy->flags, ENT_SWIMS);
724                                 Math::removeBit(&enemy->flags, ENT_FLIES);
725                                 Math::addBit(&enemy->flags, ENT_INANIMATE);
726                                 Math::addBit(&enemy->flags, ENT_BOUNCES);
727                                 enemy->health = -1 - Math::prand() % 25;
728                         }
729                         
730                         if (engine.cheatBlood)
731                         {
732                                 if (!(enemy->flags & ENT_EXPLODES))
733                                 {
734                                         if ((enemy->health % 4) == 0)
735                                         {
736                                                 addBlood(enemy, Math::rrand(-2, 2), Math::rrand(-6, -3), 1);
737                                         }
738                                         else if ((enemy->health % 10) == 0)
739                                         {
740                                                 if (game.gore)
741                                                 {
742                                                         audio.playSound(SND_DEATH1 + Math::prand() % 3, CH_DEATH, enemy->x);
743                                                 }
744                                         }
745                                 }
746                         }
747
748                         enemy->health--;
749
750                         if (enemy->flags & ENT_MULTIEXPLODE)
751                         {
752                                 if (enemy->health < -30)
753                                 {
754                                         if ((enemy->health % 3) == 0)
755                                         {
756                                                 addExplosion(enemy->x + Math::prand() % 25, enemy->y + Math::prand() % 25,  10 + (20 * game.skill), enemy);
757                                                 addSmokeAndFire(enemy, Math::rrand(-5, 5), Math::rrand(-5, 5), 2);
758                                         }
759                                 }
760                         }
761
762                         if (enemy->health > -50)
763                         {
764                                 previous = enemy;
765                         }
766                         else
767                         {
768                                 if (enemy->flags & ENT_GALDOVFINAL)
769                                 {
770                                         enemy->health = -30;
771                                         enemy->dx = Math::rrand(-10, 10);
772                                         enemy->dy = Math::rrand(-10, 10);
773                                 }
774                                 else
775                                 {
776                                         if (enemy->dead == DEAD_ALIVE)
777                                         {
778                                                 if ((absX < 800) && (absY < 600))
779                                                 {
780                                                         gibEnemy(enemy);
781                                                         
782                                                         if (enemy->value)
783                                                         {
784                                                                 dropRandomItems((int)enemy->x, (int)enemy->y);
785                                                         }
786                                                 }
787                                                 
788                                                 enemy->dead = DEAD_DYING;
789                                         }
790                                         
791                                         if (enemy->dead == DEAD_DYING)
792                                         {
793                                                 if (!enemy->referenced)
794                                                 {
795                                                         if ((absX < 800) && (absY < 600))
796                                                         {
797                                                                 gibEnemy(enemy);
798                                                                 
799                                                                 if (enemy->value)
800                                                                 {
801                                                                         dropRandomItems((int)enemy->x, (int)enemy->y);
802                                                                 }
803                                                         }
804                                                         
805                                                         debug(("Removing unreferenced enemy '%s'\n", enemy->name));
806                                                         map.enemyList.remove(previous, enemy);
807                                                         enemy = previous;
808                                                 }
809                                         }
810                                 }
811                         }
812                 }
813                 
814                 // default the enemy to not referenced.
815                 // doBullets() will change this if required.
816                 enemy->referenced = false;
817         }
818 }
819
820 void loadEnemy(const char *token)
821 {
822         int enemy = -1;
823
824         for (int i = MAX_ENEMIES - 1; i >= 0; i--)
825                 if (strcmp(defEnemy[i].name, "") == 0)
826                         enemy = i;
827
828         if (enemy == -1)
829         {
830                 printf("Out of enemy define space!\n");
831                 exit(1);
832         }
833
834         char name[50], sprite[3][100], weapon[100], flags[1024];
835         int health, value;
836
837         sscanf(token, "%*c %[^\"] %*c %s %s %s %*c %[^\"] %*c %d %d %s", name, sprite[0], sprite[1], sprite[2], weapon, &health, &value, flags);
838
839         defEnemy[enemy].setName(name);
840         defEnemy[enemy].setSprites(graphics.getSprite(sprite[0], true), graphics.getSprite(sprite[1], true), graphics.getSprite(sprite[2], true));
841         defEnemy[enemy].currentWeapon = getWeaponByName(weapon);
842         defEnemy[enemy].health = health;
843         defEnemy[enemy].value = value;
844
845         defEnemy[enemy].flags = engine.getValueOfFlagTokens(flags);
846 }
847
848 void loadDefEnemies()
849 {
850         for (int i = 0 ; i < MAX_ENEMIES ; i++)
851         {
852                 defEnemy[i].name[0] = 0;
853         }
854
855         int enemy = 0;
856
857         if (!engine.loadData("data/defEnemies"))
858         {
859                 return graphics.showErrorAndExit("Couldn't load enemy definitions file (%s)", "data/defEnemies");
860         }
861
862         char *token = strtok((char*)engine.dataBuffer, "\n");
863
864         char name[50], sprite[3][100], weapon[100], flags[1024];
865         int health, value;
866
867         while (true)
868         {
869                 if (strcmp(token, "@EOF@") == 0)
870                 {
871                         break;
872                 }
873
874                 sscanf(token, "%*c %[^\"] %*c %s %s %s %*c %[^\"] %*c %d %d %s", name, sprite[0], sprite[1], sprite[2], weapon, &health, &value, flags);
875
876                 defEnemy[enemy].setName(name);
877                 defEnemy[enemy].setSprites(graphics.getSprite(sprite[0], true), graphics.getSprite(sprite[1], true), graphics.getSprite(sprite[2], true));
878                 defEnemy[enemy].currentWeapon = getWeaponByName(weapon);
879                 defEnemy[enemy].health = health;
880                 defEnemy[enemy].value = value;
881                 defEnemy[enemy].flags = engine.getValueOfFlagTokens(flags);
882
883                 enemy++;
884
885                 token = strtok(NULL, "\n");
886         }
887 }