git-svn-id: https://scorched3d.svn.sourceforge.net/svnroot/scorched3d/trunk/scorched...
[scorched3d/parasti.git] / src / common / actions / Napalm.cpp
blob798f9b11c16143081d58dc61ae1515b6ce908f6a
1 ////////////////////////////////////////////////////////////////////////////////
2 // Scorched3D (c) 2000-2009
3 //
4 // This file is part of Scorched3D.
5 //
6 // Scorched3D is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 2 of the License, or
9 // (at your option) any later version.
11 // Scorched3D is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with Scorched3D; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 ////////////////////////////////////////////////////////////////////////////////
21 #include <engine/ScorchedContext.h>
22 #include <engine/ActionController.h>
23 #include <target/TargetContainer.h>
24 #include <target/TargetDamageCalc.h>
25 #include <target/TargetRenderer.h>
26 #include <target/TargetState.h>
27 #include <target/TargetSpace.h>
28 #include <actions/Napalm.h>
29 #include <actions/CameraPositionAction.h>
30 #ifndef S3D_SERVER
31 #include <sprites/ExplosionTextures.h>
32 #include <GLEXT/GLStateExtension.h>
33 #include <landscape/Landscape.h>
34 #include <landscape/DeformTextures.h>
35 #include <landscape/Smoke.h>
36 #include <client/ScorchedClient.h>
37 #endif
38 #include <landscapemap/LandscapeMaps.h>
39 #include <landscapedef/LandscapeDefinition.h>
40 #include <landscapedef/LandscapeTex.h>
41 #include <weapons/AccessoryStore.h>
42 #include <common/Defines.h>
44 static const int deformSize = 3;
45 static DeformLandscape::DeformPoints deformMap;
46 static bool deformCreated = false;
48 #define XY_TO_UINT(x, y) ((((unsigned int) x) << 16) | (((unsigned int) y) & 0xffff))
49 #define XY2_TO_UINT(x, y) ((((unsigned int) x - x % 2) << 16) | (((unsigned int) y - y % 2) & 0xffff))
50 #define UINT_TO_X(pt) ((int)(pt >> 16))
51 #define UINT_TO_Y(pt) ((int)(pt & 0xffff))
53 Napalm::Napalm(int x, int y, Weapon *weapon,
54 NapalmParams *params,
55 WeaponFireContext &weaponContext) :
56 ActionReferenced("Napalm"),
57 startX_(x), startY_(y), napalmTime_(0),
58 weapon_(weapon), params_(params),
59 weaponContext_(weaponContext),
60 totalTime_(0), hurtTime_(0),
61 counter_(0.1f, 0.1f), set_(0),
62 particleSet_(0)
66 Napalm::~Napalm()
68 delete params_;
71 void Napalm::init()
73 if (!deformCreated)
75 deformCreated = true;
77 Vector center(deformSize + 1, deformSize + 1);
78 for (int a=0; a<(deformSize + 1) * 2; a++)
80 for (int b=0; b<(deformSize + 1) * 2; b++)
82 Vector pos(a, b);
83 float dist = (center - pos).Magnitude();
84 dist /= deformSize;
85 dist = 1.0f - MIN(1.0f, dist);
87 DIALOG_ASSERT(a < 100 && b < 100);
88 deformMap.map[a][b] = fixed::fromFloat(dist);
93 fixed ShowTime = 5;
94 FixedVector position(fixed(startX_), fixed(startY_), context_->getLandscapeMaps().
95 getGroundMaps().getHeight(startX_, startY_));
96 CameraPositionAction *pos = new CameraPositionAction(
97 position, ShowTime, 5);
98 context_->getActionController().addAction(pos);
100 edgePoints_.insert(XY_TO_UINT(startX_, startY_));
102 #ifndef S3D_SERVER
103 if (!context_->getServerMode())
105 set_ = ExplosionTextures::instance()->getTextureSetByName(
106 params_->getNapalmTexture());
108 #endif // #ifndef S3D_SERVER
111 std::string Napalm::getActionDetails()
113 return S3D::formatStringBuffer("%i,%i %s",
114 startX_, startY_, weapon_->getParent()->getName());
117 void Napalm::simulate(fixed frameTime, bool &remove)
119 #ifndef S3D_SERVER
120 if (!context_->getServerMode())
122 if (!napalmPoints_.empty() &&
123 !params_->getNoSmoke() &&
124 counter_.nextDraw(frameTime.asFloat()))
126 int count = rand() % napalmPoints_.size();
128 NapalmEntry *entry = napalmRANDPoints_[napalmRANDPoints_.size() - 1 - count];
129 fixed posZ =
130 ScorchedClient::instance()->getLandscapeMaps().getGroundMaps().getHeight(
131 entry->posX, entry->posY);
132 Landscape::instance()->getSmoke().
133 addSmoke(float(entry->posX), float(entry->posY), posZ.asFloat());
136 #endif // #ifndef S3D_SERVER
138 // Add napalm for the period of the time interval
139 // once the time interval has expired then start taking it away
140 // Once all napalm has disapeared the simulation is over
141 totalTime_ += frameTime;
142 while (totalTime_ > params_->getStepTime())
144 totalTime_ -= params_->getStepTime();
145 napalmTime_ += params_->getStepTime();
146 if (napalmTime_ < params_->getNapalmTime())
148 // Still within the time period, add more napalm
149 if (int(napalmPoints_.size()) < params_->getNumberParticles())
151 simulateAddStep();
154 // Check for the case where we land in water
155 if (napalmPoints_.empty())
157 remove = true;
158 break;
161 else
163 // Not within the time period remove napalm
164 if (!napalmPoints_.empty())
166 simulateRmStep();
168 else
170 remove = true;
171 break;
176 // Calculate how much damage to make to the tanks
177 hurtTime_ += frameTime;
178 while (hurtTime_ > params_->getHurtStepTime())
180 hurtTime_ -= params_->getHurtStepTime();
182 simulateDamage();
185 Action::simulate(frameTime, remove);
188 fixed Napalm::getHeight(int x, int y)
190 LandscapeMaps *hmap = &context_->getLandscapeMaps();
191 if (x < 0 || y < 0 ||
192 x > hmap->getGroundMaps().getLandscapeWidth() ||
193 y > hmap->getGroundMaps().getLandscapeHeight())
195 // The height at the sides of the landscape is huge
196 // so we will never go there with the napalm
197 return fixed::MAX_FIXED;
200 // Return the correct height the square + the
201 // height of all the napalm on this square
202 // the napalm builds up and get higher so
203 // we can go over small bumps
204 return hmap->getGroundMaps().getHeight(x, y) +
205 hmap->getGroundMaps().getNapalmHeight(x, y);
208 void Napalm::simulateRmStep()
210 int pset = napalmPoints_.front()->pset;
211 while (!napalmPoints_.empty())
213 // Check if the entry should be removed
214 NapalmEntry *entry = napalmPoints_.front();
215 if (pset != entry->pset) break;
217 // Remove the first napalm point from the list
218 // and remove the height from the landscape
219 napalmPoints_.pop_front();
220 int x = entry->posX;
221 int y = entry->posY;
222 delete entry;
224 unsigned int pointsCount = XY2_TO_UINT(x, y);
225 std::map<unsigned int, int>::iterator countItor =
226 napalmPointsCount_.find(pointsCount);
227 if (countItor != napalmPointsCount_.end())
229 countItor->second--;
230 if (countItor->second == 0) napalmPointsCount_.erase(countItor);
233 context_->getLandscapeMaps().getGroundMaps().getNapalmHeight(x, y) -= params_->getNapalmHeight();
237 void Napalm::simulateAddStep()
239 particleSet_++;
241 std::set<unsigned int> currentEdges = edgePoints_;
242 edgePoints_.clear();
244 std::set<unsigned int>::iterator itor;
245 for (itor = currentEdges.begin();
246 itor != currentEdges.end();
247 itor++)
249 unsigned int currentEdge = *itor;
250 int x = UINT_TO_X(currentEdge);
251 int y = UINT_TO_Y(currentEdge);
253 simulateAddEdge(x, y);
257 void Napalm::simulateAddEdge(int x, int y)
259 // Get the height of this point
260 fixed height = getHeight(x, y);
262 if (!params_->getAllowUnderWater())
264 // Check napalm is under water
265 fixed waterHeight = -10;
266 LandscapeTex &tex = *context_->getLandscapeMaps().getDefinitions().getTex();
267 if (tex.border->getType() == LandscapeTexType::eWater)
269 LandscapeTexBorderWater *water =
270 (LandscapeTexBorderWater *) tex.border;
271 waterHeight = water->height;
274 if (height < waterHeight) // Water height
276 // Perhaps we could add a boiling water sound at some point
277 return;
281 // Add this current point to the napalm map
282 RandomGenerator &random = context_->getActionController().getRandom();
283 int offset = (random.getRandFixed() * 31).asInt();
284 NapalmEntry *newEntry = new NapalmEntry(x, y, offset, particleSet_);
285 napalmPoints_.push_back(newEntry);
286 napalmRANDPoints_.push_back(newEntry);
288 unsigned int pointsCount = XY2_TO_UINT(x, y);
289 std::map<unsigned int, int>::iterator countItor =
290 napalmPointsCount_.find(pointsCount);
291 if (countItor == napalmPointsCount_.end())
293 napalmPointsCount_.insert(std::pair<unsigned int, int>(pointsCount, 1));
295 else
297 countItor->second++;
300 #ifndef S3D_SERVER
301 if (!context_->getServerMode())
303 ParticleEmitter emitter;
304 emitter.setAttributes(
305 params_->getNapalmTime().asFloat(), params_->getNapalmTime().asFloat(),
306 0.5f, 1.0f, // Mass
307 0.01f, 0.02f, // Friction
308 Vector(0.0f, 0.0f, 0.0f), Vector(0.0f, 0.0f, 0.0f), // Velocity
309 Vector(1.0f, 1.0f, 1.0f), 0.9f, // StartColor1
310 Vector(1.0f, 1.0f, 1.0f), 0.6f, // StartColor2
311 Vector(1.0f, 1.0f, 1.0f), 0.0f, // EndColor1
312 Vector(1.0f, 1.0f, 1.0f), 0.1f, // EndColor2
313 1.5f, 1.5f, 1.5f, 1.5f, // Start Size
314 1.5f, 1.5f, 1.5f, 1.5f, // EndSize
315 Vector(0.0f, 0.0f, 0.0f), // Gravity
316 params_->getLuminance(),
317 false);
318 Vector position1(float(x) + 0.5f, float(y) - 0.2f, 0.0f);
319 Vector position2(float(x) - 0.5f, float(y) - 0.2f, 0.0f);
320 Vector position3(float(x) + 0.0f, float(y) + 0.5f, 0.0f);
321 emitter.emitNapalm(
322 position1,
323 ScorchedClient::instance()->getParticleEngine(),
324 set_);
325 emitter.emitNapalm(
326 position2,
327 ScorchedClient::instance()->getParticleEngine(),
328 set_);
329 emitter.emitNapalm(
330 position3,
331 ScorchedClient::instance()->getParticleEngine(),
332 set_);
334 // Add the ground scorch
335 if (!GLStateExtension::getNoTexSubImage())
337 if (height == context_->getLandscapeMaps().getGroundMaps().getHeight(x, y))
339 if (RAND < params_->getGroundScorchPer().asFloat())
341 Vector pos(x, y);
342 DeformTextures::deformLandscape(pos,
343 (int) (deformSize + 1),
344 ExplosionTextures::instance()->getScorchBitmap(
345 params_->getDeformTexture()),
346 deformMap);
351 #endif // #ifndef S3D_SERVER
353 context_->getLandscapeMaps().getGroundMaps().getNapalmHeight(x, y) += params_->getNapalmHeight();
355 // Calculate every time as the landscape may change
356 // due to other actions
357 fixed heightL = getHeight(x-1, y);
358 fixed heightR = getHeight(x+1, y);
359 fixed heightU = getHeight(x, y+1);
360 fixed heightD = getHeight(x, y-1);
362 if (params_->getSingleFlow())
364 // Find the new point to move to (if any)
365 // This point must be lower than the current point
366 if (heightL < height &&
367 heightL < heightR &&
368 heightL < heightU &&
369 heightL < heightD)
371 // Move left
372 edgePoints_.insert(XY_TO_UINT(x - 1, y));
374 else if (heightR < height &&
375 heightR < heightL &&
376 heightR < heightU &&
377 heightR < heightD)
379 // Move right
380 edgePoints_.insert(XY_TO_UINT(x + 1, y));
382 else if (heightU < height &&
383 heightU < heightL &&
384 heightU < heightR &&
385 heightU < heightD)
387 // Move up
388 edgePoints_.insert(XY_TO_UINT(x, y + 1));
390 else if (heightD < height)
392 // Move down
393 edgePoints_.insert(XY_TO_UINT(x, y - 1));
395 else
397 // None of the landscape is currently lower than the current point
398 // Just wait, as this point will be now be covered in napalm
399 // and may get higher and higher until it is
400 edgePoints_.insert(XY_TO_UINT(x, y));
403 else
405 int addedCount = 0;
406 if (heightL < height)
408 // Move left
409 addedCount++;
410 edgePoints_.insert(XY_TO_UINT(x - 1, y));
412 if (heightR < height)
414 // Move right
415 addedCount++;
416 edgePoints_.insert(XY_TO_UINT(x + 1, y));
418 if (heightU < height)
420 // Move up
421 addedCount++;
422 edgePoints_.insert(XY_TO_UINT(x, y + 1));
424 if (heightD < height)
426 // Move down
427 addedCount++;
428 edgePoints_.insert(XY_TO_UINT(x, y - 1));
430 if (addedCount == 0)
432 // None of the landscape is currently lower than the current point
433 // Just wait, as this point will be now be covered in napalm
434 // and may get higher and higher until it is
435 edgePoints_.insert(XY_TO_UINT(x, y));
440 void Napalm::simulateDamage()
442 const int EffectRadius = params_->getEffectRadius();
444 // Store how much each tank is damaged
445 // Keep in a map so we don't need to create multiple
446 // damage actions. Now we only create one per tank
447 static std::map<Target *, fixed> TargetDamageCalc;
448 TargetDamageCalc.clear();
450 // Add damage into the damage map for each napalm point that is near to
451 // the tanks
452 std::map<unsigned int, int>::iterator itor =
453 napalmPointsCount_.begin();
454 std::map<unsigned int, int>::iterator endItor =
455 napalmPointsCount_.end();
456 for (;itor != endItor; itor++)
458 unsigned int pointsCount = itor->first;
459 fixed count = fixed(itor->second);
460 int x = UINT_TO_X(pointsCount);
461 int y = UINT_TO_Y(pointsCount);
463 fixed height = context_->getLandscapeMaps().getGroundMaps().
464 getHeight(x, y);
465 FixedVector position(
466 fixed(x),
467 fixed(y),
468 height);
470 std::map<unsigned int, Target *> collisionTargets;
471 context_->getTargetSpace().getCollisionSet(position,
472 fixed(EffectRadius), collisionTargets);
473 std::map<unsigned int, Target *>::iterator itor;
474 for (itor = collisionTargets.begin();
475 itor != collisionTargets.end();
476 itor++)
478 Target *target = (*itor).second;
479 if (target->getAlive())
481 std::map<Target *, fixed>::iterator damageItor =
482 TargetDamageCalc.find(target);
483 if (damageItor == TargetDamageCalc.end())
485 TargetDamageCalc[target] = count * params_->getHurtPerSecond();
487 else
489 TargetDamageCalc[target] += count * params_->getHurtPerSecond();
495 // Add all the damage to the tanks (if any)
496 if (!TargetDamageCalc.empty())
498 std::map<Target *, fixed>::iterator damageItor;
499 for (damageItor = TargetDamageCalc.begin();
500 damageItor != TargetDamageCalc.end();
501 damageItor++)
503 Target *target = (*damageItor).first;
504 fixed damage = (*damageItor).second;
506 // Add damage to the tank
507 // If allowed for this target type (mainly for trees)
508 if (!target->getTargetState().getNoDamageBurn())
510 TargetDamageCalc::damageTarget(*context_, target, weapon_,
511 weaponContext_, damage, true, false, false);
514 // Set this target to burnt
515 if (target->getRenderer() &&
516 !params_->getNoObjectDamage())
518 target->getRenderer()->targetBurnt();
521 TargetDamageCalc.clear();