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