1 /************************************************************************
2  *                                                                      *
3  *  FreeSynd - a remake of the classic Bullfrog game "Syndicate".       *
4  *                                                                      *
5  *   Copyright (C) 2005  Stuart Binge  <skbinge@gmail.com>              *
6  *   Copyright (C) 2005  Joost Peters  <joostp@users.sourceforge.net>   *
7  *   Copyright (C) 2006  Trent Waddington <qg@biodome.org>              *
8  *   Copyright (C) 2006  Tarjei Knapstad <tarjei.knapstad@gmail.com>    *
9  *   Copyright (C) 2010  Bohdan Stelmakh <chamel@users.sourceforge.net> *
10  *   Copyright (C) 2013  Benoit Blancard <benblan@users.sourceforge.net>*
11  *                                                                      *
12  *    This program is free software;  you can redistribute it and / or  *
13  *  modify it  under the  terms of the  GNU General  Public License as  *
14  *  published by the Free Software Foundation; either version 2 of the  *
15  *  License, or (at your option) any later version.                     *
16  *                                                                      *
17  *    This program is  distributed in the hope that it will be useful,  *
18  *  but WITHOUT  ANY WARRANTY;  without even  the implied  warranty of  *
19  *  MERCHANTABILITY  or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU  *
20  *  General Public License for more details.                            *
21  *                                                                      *
22  *    You can view the GNU  General Public License, online, at the GNU  *
23  *  project's  web  site;  see <http://www.gnu.org/licenses/gpl.html>.  *
24  *  The full text of the license is also included in the file COPYING.  *
25  *                                                                      *
26  ************************************************************************/
27 
28 #include <map>
29 
30 #include "app.h"
31 #include "model/shot.h"
32 #include "mission.h"
33 #include "ped.h"
34 #include "vehicle.h"
35 
inflictDamage(Mission * pMission)36 void InstantImpactShot::inflictDamage(Mission *pMission) {
37     WorldPoint originLocW(dmg_.d_owner->position()); // origin of shooting
38     // get how much impacts does the weapon generate
39     int nbImpacts = dmg_.pWeapon->getWeaponClass()->shotsPerAmmo();
40 
41     // If there are many impacts, a target can be hit by several impacts
42     // so this map stores number of impacts for a target
43     std::map<ShootableMapObject *, int> hitsByObject;
44 
45     for (int i = 0; i < nbImpacts; ++i) {
46         WorldPoint impactPosW = dmg_.aimedLocW;
47 
48         if (nbImpacts > 1) {
49             // When multiple impacts, they're spread
50             diffuseImpact(pMission, originLocW, &impactPosW);
51         }
52 
53         // Verify if shot hit something or was blocked by a tile
54         ShootableMapObject *pTargetHit = NULL;
55         pMission->checkIfBlockersInShootingLine(
56             originLocW, &pTargetHit, &impactPosW, true, false, dmg_.pWeapon->range(), NULL, dmg_.d_owner);
57 
58         if (pTargetHit != NULL) {
59             hitsByObject[pTargetHit] = hitsByObject[pTargetHit] + 1;
60         }
61         // creates impact sprite
62         createImpactAnimation(pMission, pTargetHit, impactPosW);
63     }
64 
65     // finally distribute damage
66     std::map<ShootableMapObject *, int>::iterator it;
67     for (it = hitsByObject.begin(); it != hitsByObject.end(); it++) {
68         dmg_.dvalue = (*it).second * dmg_.pWeapon->getWeaponClass()->damagePerShot();
69         (*it).first->handleHit(dmg_);
70     }
71 }
72 
diffuseImpact(Mission * pMission,const WorldPoint & originLocW,WorldPoint * pImpactPosW)73 void InstantImpactShot::diffuseImpact(Mission *pMission, const WorldPoint &originLocW, WorldPoint *pImpactPosW) {
74     double angle = dmg_.pWeapon->getWeaponClass()->shotAngle();
75     if (angle == 0)
76         return;
77 
78     angle *= (double)(69 + (rand() & 0x1F)) / 100.0;
79     int cx = originLocW.x;
80     int cy = originLocW.y;
81     int cz = originLocW.z;
82 
83     int tx = pImpactPosW->x;
84     int ty = pImpactPosW->y;
85     int tz = pImpactPosW->z;
86     double dtx = (double)(tx - cx);
87     double dty = (double)(ty - cy);
88     double dtz = (double)(tz - cz);
89     double dist_cur = 0.0;
90     dist_cur = sqrt(dtx * dtx + dty * dty + dtz * dtz);
91     if (dist_cur == 0.0)
92         return;
93 
94     angle /= (180.0 / PI);
95     double angx = acos(dtx/dist_cur);
96     double angy = acos(dty/dist_cur);
97     double angz = acos(dtz/dist_cur);
98 
99     double set_sign = 1.0;
100     if (rand() % 100 < 50)
101         set_sign = -1.0;
102     double diff_ang = (angle * (double)(rand() % 100) / 200.0) * set_sign;
103     angx += diff_ang;
104     angle -= fabs(diff_ang);
105     int gtx = cx + (int)(cos(angx) * dist_cur);
106 
107     set_sign = 1.0;
108     if (rand() % 100 < 50)
109         set_sign = -1.0;
110     diff_ang = (angle * (double)(rand() % 100) / 200.0) * set_sign;
111     angy += diff_ang;
112     angle -= fabs(diff_ang);
113     int gty = cy + (int)(cos(angy) * dist_cur);
114 
115     set_sign = 1.0;
116     if (rand() % 100 < 50)
117         set_sign = -1.0;
118     angz += (angle * (double)(rand() % 100) / 200.0) * set_sign;
119 
120     int gtz = cz + (int)(cos(angz) * dist_cur);
121 
122     if (gtx < 0) {
123         if (cos(angx) == 0.0) {
124             gtx = 0;
125         } else {
126             dist_cur -= fabs((double)gtx / cos(angx));
127             gtx = 0;
128             gty = cy + (int)(cos(angy) * dist_cur);
129             gtz = cz + (int)(cos(angz) * dist_cur);
130         }
131     }
132     if (gty < 0) {
133         if (cos(angy) == 0.0) {
134             gty = 0;
135         } else {
136             dist_cur -= fabs((double)gty / cos(angy));
137             gty = 0;
138             gtx = cx + (int)(cos(angx) * dist_cur);
139             gtz = cz + (int)(cos(angz) * dist_cur);
140         }
141     }
142     if (gtz < 0) {
143         if (cos(angz) == 0.0) {
144             gtz = 0;
145         } else {
146             dist_cur -= fabs((double)gtz / cos(angz));
147             gtz = 0;
148             gtx = cx + (int)(cos(angx) * dist_cur);
149             gty = cy + (int)(cos(angy) * dist_cur);
150         }
151     }
152 
153     int max_x = (pMission->mmax_x_ - 1) * 256;
154     int max_y = (pMission->mmax_y_ - 1) * 256;
155     int max_z = (pMission->mmax_z_ - 1) * 128;
156     if (gtx > max_x) {
157         if (cos(angx) == 0.0) {
158             gtx = max_x;
159         } else {
160             dist_cur -= fabs((double)(gtx - max_x) / cos(angx));
161             gtx = max_x;
162             gty = cy + (int)(cos(angy) * dist_cur);
163             gtz = cz + (int)(cos(angz) * dist_cur);
164         }
165     }
166     if (gty > max_y) {
167         if (cos(angy) == 0.0) {
168             gty = max_y;
169         } else {
170             dist_cur -= fabs((double)(gty - max_y) / cos(angy));
171             gty = max_y;
172             gtx = cx + (int)(cos(angx) * dist_cur);
173             gtz = cz + (int)(cos(angz) * dist_cur);
174         }
175     }
176     if (gtz > max_z) {
177         if (cos(angx) == 0.0) {
178             gtz = max_z;
179         } else {
180             dist_cur -= fabs((double)(gtz - max_z) / cos(angz));
181             gtz = max_z;
182             gtx = cx + (int)(cos(angx) * dist_cur);
183             gty = cy + (int)(cos(angy) * dist_cur);
184         }
185     }
186     assert(gtx >= 0 && gty >= 0 && gtz >= 0);
187     assert(gtx <= max_x && gty <= max_y && gtz <= max_z);
188 
189     pImpactPosW->x = gtx;
190     pImpactPosW->y = gty;
191     pImpactPosW->z = gtz;
192 }
193 
194 /*!
195  * Creates the impact animation based on the type of target hit.
196  * Some weapons can have no animations for an impact.
197  */
createImpactAnimation(Mission * pMission,ShootableMapObject * pTargetHit,const WorldPoint impactPosW)198 void InstantImpactShot::createImpactAnimation(Mission *pMission, ShootableMapObject * pTargetHit, const WorldPoint impactPosW) {
199     SFXObject::SfxTypeEnum impactAnimId =
200         (pTargetHit != NULL ?
201             dmg_.pWeapon->getWeaponClass()->impactAnims()->objectHit :
202             dmg_.pWeapon->getWeaponClass()->impactAnims()->groundHit);
203 
204     if (impactAnimId != SFXObject::sfxt_Unknown) {
205         SFXObject *so = new SFXObject(pMission->map(), impactAnimId);
206         so->setPosition(impactPosW);
207         so->correctZ();
208         pMission->addSfxObject(so);
209     }
210 }
211 
212 /*!
213  * Convenient method to create explosions.
214  * \param pMission Mission data
215  * \param pOwner What's at the origin of the explosion
216  * \param The range for damage
217  * \param The value of the damage
218  */
createExplosion(Mission * pMission,ShootableMapObject * pOwner,double range,int dmgValue)219 void Explosion::createExplosion(Mission *pMission, ShootableMapObject *pOwner, double range, int dmgValue) {
220     WorldPoint location(pOwner->position());
221 
222     createExplosion(pMission, pOwner, location, range, dmgValue);
223 }
224 
createExplosion(Mission * pMission,ShootableMapObject * pOwner,const WorldPoint & location,double range,int dmgValue)225 void Explosion::createExplosion(Mission *pMission, ShootableMapObject *pOwner, const WorldPoint &location, double range, int dmgValue) {
226     ShootableMapObject::DamageInflictType dmg;
227     if (pOwner && pOwner->nature() == MapObject::kNatureWeapon) {
228         // It's a bomb that exploded (other waepons do not explode)
229         dmg.pWeapon = dynamic_cast<WeaponInstance *>(pOwner);
230     } else {
231         dmg.pWeapon = NULL;
232     }
233 
234     dmg.d_owner = pOwner;
235     dmg.dtype = MapObject::dmg_Explosion;
236     dmg.range = range;
237     dmg.dvalue =  dmgValue;
238     dmg.originLocW = location;
239     dmg.originLocW.z += 8;
240 
241     Explosion explosion(dmg);
242     explosion.inflictDamage(pMission);
243 }
244 
Explosion(const ShootableMapObject::DamageInflictType & dmg)245 Explosion::Explosion(const ShootableMapObject::DamageInflictType &dmg) : Shot(dmg) {
246     // GaussGun has a different animation for explosion
247     if (dmg_.pWeapon && dmg_.pWeapon->getWeaponType() == Weapon::GaussGun) {
248         rngDmgAnim_ = SFXObject::sfxt_LargeFire;
249     } else {
250         rngDmgAnim_ = SFXObject::sfxt_ExplosionFire;
251     }
252 };
253 
254 /*!
255  *
256  */
inflictDamage(Mission * pMission)257 void Explosion::inflictDamage(Mission *pMission) {
258     std::vector<ShootableMapObject *> objInRangeLst;
259     // Get all destructible objects in range
260     getAllShootablesWithinRange(pMission, dmg_.originLocW, objInRangeLst);
261 
262     for (std::vector<ShootableMapObject *>::iterator it = objInRangeLst.begin();
263         it != objInRangeLst.end(); it++)
264     {
265         ShootableMapObject *smo = *it;
266         // distribute damage
267         smo->handleHit(dmg_);
268         // draw a explosion ball above each object that was hit
269         SFXObject *so = new SFXObject(pMission->map(), SFXObject::sfxt_ExplosionBall);
270         so->setPosition(smo->tileX(), smo->tileY(), smo->tileZ(), smo->offX(),
271             smo->offY(), smo->offZ());
272         so->correctZ();
273         pMission->addSfxObject(so);
274     }
275     // create the ring of fire around the origin of explosion
276     generateFlameWaves(pMission, &(dmg_.originLocW), dmg_.range);
277 
278     g_App.gameSounds().play(snd::EXPLOSION_BIG);
279 }
280 
281 /*! Draws animation of impact/explosion
282  * \param pMission Mission data
283  * \param pOrigin center of explosion
284  * \param dmg_rng effective range for drawing
285  */
generateFlameWaves(Mission * pMission,WorldPoint * pOrigin,double dmg_rng)286 void Explosion::generateFlameWaves(Mission *pMission, WorldPoint *pOrigin, double dmg_rng)
287 {
288     WorldPoint base_pos = *pOrigin;
289     pOrigin->z += 4;
290     if (pOrigin->z > (pMission->mmax_z_ - 1) * 128)
291         pOrigin->z = (pMission->mmax_z_ - 1) * 128;
292     // TODO: exclude flames on water, put these flames to the ground,
293     // don't draw in air(, stairs problem?)
294     double angle_inc = PI;
295     const uint8 waves = (int)dmg_rng / 144 + 1;
296 
297     for (uint8 i = 0; i < waves; i++) {
298         double base_angle = 0.0;
299         if (rand() % 100 > 74)
300             base_angle += angle_inc;
301 
302         for (int j = 0; j < (4 << i); j++) {
303             double x = (double)(144 * i) * cos(base_angle);
304             double y = (double)(144 * i) * sin(base_angle);
305             base_angle += angle_inc;
306             WorldPoint flamePosW;
307             flamePosW.x = base_pos.x + (int)x;
308             flamePosW.y = base_pos.y + (int)y;
309             flamePosW.z = base_pos.z;
310 
311             uint8 block_mask = pMission->checkBlockedByTile(*pOrigin, &flamePosW, true, dmg_rng);
312             if (block_mask != 32) {
313                 SFXObject *so = new SFXObject(pMission->map(), rngDmgAnim_,
314                                 100 * (rand() % 16));
315                 so->setPosition(flamePosW);
316                 pMission->addSfxObject(so);
317             }
318         }
319         angle_inc /= 2.0;
320     }
321 }
322 
323 /*!
324  * Returns all ShootableMapObject that are alive and in range of
325  * weapon who generated this explosion.
326  * Every object found is added to the objInRangeVec vector.
327  * \param pMission Mission data
328  * \param originLocW Origin of explosion
329  * \param objInRangeVec Result list
330  */
getAllShootablesWithinRange(Mission * pMission,const WorldPoint & originLocW,std::vector<ShootableMapObject * > & objInRangeVec)331 void Explosion::getAllShootablesWithinRange(Mission *pMission,
332                                        const WorldPoint &originLocW,
333                                        std::vector<ShootableMapObject *> &objInRangeVec) {
334     // Look at all peds alive, in range of explosion and not in a vehicle
335     for (size_t i = 0; i < pMission->numPeds(); ++i) {
336         PedInstance *p = pMission->ped(i);
337         if (p->isAlive() && p->isCloseTo(originLocW, dmg_.range) && p->inVehicle() == NULL) {
338             WorldPoint pedPosW(p->position());
339             if (pMission->checkBlockedByTile(originLocW, &pedPosW, false, dmg_.range) == 1) {
340                 objInRangeVec.push_back(p);
341             }
342         }
343     }
344 
345     for (size_t i = 0; i < pMission->numStatics(); ++i) {
346         Static *st = pMission->statics(i);
347         if (!st->isExcludedFromBlockers() && st->isAlive() && st->isCloseTo(originLocW, dmg_.range)) {
348             WorldPoint staticPosW(st->position());
349             if (pMission->checkBlockedByTile(originLocW, &staticPosW, false, dmg_.range) == 1) {
350                 objInRangeVec.push_back(st);
351             }
352         }
353     }
354 
355     // look at all vehicles
356     for (size_t i = 0; i < pMission->numVehicles(); ++i) {
357         ShootableMapObject *v = pMission->vehicle(i);
358         if (v->isAlive() && v->isCloseTo(originLocW, dmg_.range)) {
359             WorldPoint vehiclePosW(v->position());
360             if (pMission->checkBlockedByTile(originLocW, &vehiclePosW, false, dmg_.range) == 1) {
361                 objInRangeVec.push_back(v);
362             }
363         }
364     }
365 
366     // look at all bombs on the ground except the weapon that generated the shot
367     for (size_t i = 0; i < pMission->numWeapons(); ++i) {
368         WeaponInstance *w = pMission->weapon(i);
369         if (w->getWeaponType() == Weapon::TimeBomb && w != dmg_.pWeapon && !w->hasOwner() && w->isAlive()) {
370             WorldPoint weaponPosW(w->position());
371             if (pMission->checkBlockedByTile(originLocW, &weaponPosW, false, dmg_.range) == 1) {
372                 objInRangeVec.push_back(w);
373             }
374         }
375     }
376 }
377 
ProjectileShot(const ShootableMapObject::DamageInflictType & dmg)378 ProjectileShot::ProjectileShot(const ShootableMapObject::DamageInflictType &dmg) : Shot(dmg) {
379     elapsed_ = -1;
380     curPosW_ = dmg.originLocW;
381     currentDistance_ = 0;
382     lifeOver_ = false;
383     drawImpact_ = false;
384     pShootableHit_ = NULL;
385 
386     speed_ = dmg.pWeapon->getWeaponClass()->shotSpeed();
387     // distance from origin of shoot to target on each axis
388     targetLocW_.x = dmg.aimedLocW.x;
389     targetLocW_.y = dmg.aimedLocW.y;
390     targetLocW_.z = dmg.aimedLocW.z;
391 
392     double diffx = (double)(targetLocW_.x - curPosW_.x);
393     double diffy = (double)(targetLocW_.y - curPosW_.y);
394     double diffz = (double)(targetLocW_.z - curPosW_.z);
395 
396     double distanceToTarget  = sqrt(diffx * diffx + diffy * diffy + diffz * diffz);
397     if (distanceToTarget != 0) {
398         incX_ = diffx / distanceToTarget;
399         incY_ = diffy / distanceToTarget;
400         incZ_ = diffz / distanceToTarget;
401     }
402 
403     distanceMax_ = dmg.pWeapon->getWeaponClass()->range();
404     if (distanceToTarget < distanceMax_) {
405         distanceMax_ = distanceToTarget;
406     }
407 }
408 
animate(int elapsed,Mission * pMission)409 bool ProjectileShot::animate(int elapsed, Mission *pMission) {
410     if (elapsed_ == -1) {
411         // It's the first time the animate method is called since shot
412         // was created : start counting
413         elapsed_ = 0;
414         return false;
415     }
416 
417     if (moveProjectile(elapsed, pMission)) {
418         inflictDamage(pMission);
419     }
420 
421     return true;
422 }
423 
424 /*!
425  * Updates the projectile position and animation of trace.
426  * \param elapsed Time elapsed since last frame.
427  * \param pMission Mission data
428  * \return True if projectile has reached its target or max distance.
429  */
moveProjectile(int elapsed,Mission * pMission)430 bool ProjectileShot::moveProjectile(int elapsed, Mission *pMission) {
431     bool endMove = false;
432     elapsed_ += elapsed;
433 
434     // Distance crossed in the elapsed time
435     double inc_dist = speed_ * (double)elapsed / 1000;
436     if ((currentDistance_ + inc_dist) > distanceMax_) {
437         // Projectile reached the maximum distance
438         if (currentDistance_ > distanceMax_) {
439             currentDistance_ = distanceMax_;
440         }
441         inc_dist = distanceMax_ - currentDistance_;
442         endMove = true;
443     }
444 
445     // This is the distance after the move
446     double nextDist = currentDistance_ + inc_dist;
447     WorldPoint nextPosW; // This is the position of projectile after this move
448     bool do_recalc = false;
449 
450     nextPosW.x = dmg_.originLocW.x + (int)(incX_ * nextDist);
451     if (nextPosW.x < 0) {
452         nextPosW.x = 0;
453         endMove = true;
454         do_recalc = true;
455     } else if (nextPosW.x > (pMission->mmax_x_ - 1) * 256) {
456         nextPosW.x = (pMission->mmax_x_ - 1) * 256;
457         endMove = true;
458         do_recalc = true;
459     }
460     if (do_recalc) {
461         do_recalc = false;
462         if (incX_ != 0) {
463             nextDist = (double)(nextPosW.x - dmg_.originLocW.x) / incX_;
464         }
465     }
466 
467     nextPosW.y = dmg_.originLocW.y + (int)(incY_ * nextDist);
468     if (nextPosW.y < 0) {
469         nextPosW.y = 0;
470         endMove = true;
471         do_recalc = true;
472     } else if (nextPosW.y > (pMission->mmax_y_ - 1) * 256) {
473         nextPosW.y = (pMission->mmax_y_ - 1) * 256;
474         endMove = true;
475         do_recalc = true;
476     }
477     if (do_recalc) {
478         do_recalc = false;
479         if (incY_ != 0) {
480             nextDist = (double)(nextPosW.y - dmg_.originLocW.y) / incY_;
481             nextPosW.x = dmg_.originLocW.x + (int)(incX_ * nextDist);
482         }
483     }
484 
485     nextPosW.z = dmg_.originLocW.z + (int)(incZ_ * nextDist);
486     if (nextPosW.z < 0) {
487         nextPosW.z = 0;
488         endMove = true;
489         do_recalc = true;
490     } else if (nextPosW.z > (pMission->mmax_z_ - 1) * 128) {
491         nextPosW.z = (pMission->mmax_z_ - 1) * 128;
492         endMove = true;
493         do_recalc = true;
494     }
495     if (do_recalc) {
496         if (incZ_ != 0) {
497             nextDist = (double)(nextPosW.z - dmg_.originLocW.z) / incZ_;
498             nextPosW.x = dmg_.originLocW.x + (int)(incX_ * nextDist);
499             nextPosW.y = dmg_.originLocW.y + (int)(incY_ * nextDist);
500         }
501     }
502 
503     // maxr here is set to maximum that projectile can fly from its
504     // current position
505     uint8 block_mask = pMission->checkIfBlockersInShootingLine(
506         curPosW_, &pShootableHit_, &nextPosW, true, false, distanceMax_ - currentDistance_, NULL, dmg_.d_owner);
507 
508     if (block_mask == 1) {
509         // Projectile has reached initial target
510         if (nextPosW.equals(targetLocW_)) {
511             // we can stop the move and draw the explosion
512             drawImpact_ = true;
513             endMove = true;
514         }
515     } else if (block_mask == 32) {
516         // projectile is out of map : do not draw explosion
517         // not sure if necessary
518         endMove = true;
519     } else {
520         // projectile has hit something
521         drawImpact_ = true;
522         endMove = true;
523     }
524 
525     curPosW_ = nextPosW;
526     currentDistance_ = nextDist;
527     drawTrace(pMission);
528 
529     return endMove;
530 }
531 
GaussGunShot(const ShootableMapObject::DamageInflictType & dmg)532 GaussGunShot::GaussGunShot(const ShootableMapObject::DamageInflictType &dmg) : ProjectileShot(dmg) {
533     lastAnimDist_ = 0;
534 }
535 
inflictDamage(Mission * pMission)536 void GaussGunShot::inflictDamage(Mission *pMission) {
537     lifeOver_ = true;
538 
539     if (drawImpact_) {
540         int dmgRange = dmg_.pWeapon->getWeaponClass()->rangeDmg();
541         Explosion::createExplosion(pMission, dmg_.pWeapon, curPosW_, dmgRange, dmg_.dvalue);
542     }
543 }
544 
545 /*!
546  * Draws the animation of smoke behind the projectile.
547  * \param pMission Mission data
548  */
drawTrace(Mission * pMission)549 void GaussGunShot::drawTrace(Mission *pMission) {
550     // distance between 2 animations
551     double anim_d = 64;
552 
553     double diffx = (double) (dmg_.originLocW.x - curPosW_.x);
554     double diffy = (double) (dmg_.originLocW.y - curPosW_.y);
555     double diffz = (double) (dmg_.originLocW.z - curPosW_.z);
556     double d = sqrt(diffx * diffx + diffy * diffy
557         + diffz * diffz);
558 
559     if (d > lastAnimDist_) {
560         int diff_dist = (int) ((d - lastAnimDist_) / anim_d);
561         if (diff_dist != 0) {
562             for (int i = 1; i <= diff_dist; i++) {
563                 WorldPoint t;
564                 lastAnimDist_ += anim_d;
565                 t.x = dmg_.originLocW.x + (int)(lastAnimDist_ * incX_);
566                 t.y = dmg_.originLocW.y + (int)(lastAnimDist_ * incY_);
567                 t.z = dmg_.originLocW.z + (int)(lastAnimDist_ * incZ_);
568 
569                 t.z += 128;
570                 if (t.z > (pMission->mmax_z_ - 1) * 128)
571                     t.z = (pMission->mmax_z_ - 1) * 128;
572 
573                 SFXObject *so = new SFXObject(pMission->map(),
574                     dmg_.pWeapon->getWeaponClass()->impactAnims()->trace_anim);
575                 so->setPosition(t);
576                 pMission->addSfxObject(so);
577             }
578         }
579     }
580 }
581 
FlamerShot(Mission * pMission,const ShootableMapObject::DamageInflictType & dmg)582 FlamerShot::FlamerShot(Mission *pMission, const ShootableMapObject::DamageInflictType &dmg) :
583         ProjectileShot(dmg) {
584     // We create a SFXObjet that we keep in memory to updateits position
585     pFlame_ = new SFXObject(pMission->map(),
586                             dmg_.pWeapon->getWeaponClass()->impactAnims()->trace_anim);
587     // The sfxObject will loop to keep it alive
588     pFlame_->setLoopAnimation(true);
589     pFlame_->setPosition(dmg.originLocW);
590     pMission->addSfxObject(pFlame_);
591 }
592 
~FlamerShot()593 FlamerShot::~FlamerShot() {
594     if(pFlame_ != NULL) {
595         delete pFlame_;
596     }
597 }
598 
599 /*!
600  * Draws the animation of smoke behind the projectile.
601  * \param pMission Mission data
602  */
drawTrace(Mission * pMission)603 void FlamerShot::drawTrace(Mission *pMission) {
604 
605     pFlame_->setPosition(curPosW_);
606 }
607 
inflictDamage(Mission * pMission)608 void FlamerShot::inflictDamage(Mission *pMission) {
609     lifeOver_ = true;
610     // target was hit (or shot reached an end)  so we
611     // can get rid of sfxobject
612     // it will be destroyed by the GameplayMenu loop
613     pFlame_->setLoopAnimation(false);
614     pFlame_ = NULL;
615     if (pShootableHit_ != NULL) {
616         pShootableHit_->handleHit(dmg_);
617     }
618 }
619 
620