1 ////////////////////////////////////////////////////////////////////////////////
2 // Scorched3D (c) 2000-2009
4 // This file is part of Scorched3D.
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>
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>
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
,
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),
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
++)
83 float dist
= (center
- pos
).Magnitude();
85 dist
= 1.0f
- MIN(1.0f
, dist
);
87 DIALOG_ASSERT(a
< 100 && b
< 100);
88 deformMap
.map
[a
][b
] = fixed::fromFloat(dist
);
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_
));
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
)
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
];
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())
154 // Check for the case where we land in water
155 if (napalmPoints_
.empty())
163 // Not within the time period remove napalm
164 if (!napalmPoints_
.empty())
176 // Calculate how much damage to make to the tanks
177 hurtTime_
+= frameTime
;
178 while (hurtTime_
> params_
->getHurtStepTime())
180 hurtTime_
-= params_
->getHurtStepTime();
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();
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())
230 if (countItor
->second
== 0) napalmPointsCount_
.erase(countItor
);
233 context_
->getLandscapeMaps().getGroundMaps().getNapalmHeight(x
, y
) -= params_
->getNapalmHeight();
237 void Napalm::simulateAddStep()
241 std::set
<unsigned int> currentEdges
= edgePoints_
;
244 std::set
<unsigned int>::iterator itor
;
245 for (itor
= currentEdges
.begin();
246 itor
!= currentEdges
.end();
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
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));
301 if (!context_
->getServerMode())
303 ParticleEmitter emitter
;
304 emitter
.setAttributes(
305 params_
->getNapalmTime().asFloat(), params_
->getNapalmTime().asFloat(),
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(),
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
);
323 ScorchedClient::instance()->getParticleEngine(),
327 ScorchedClient::instance()->getParticleEngine(),
331 ScorchedClient::instance()->getParticleEngine(),
334 // Add the ground scorch
335 if (!GLStateExtension::getNoTexSubImage())
337 if (height
== context_
->getLandscapeMaps().getGroundMaps().getHeight(x
, y
))
339 if (RAND
< params_
->getGroundScorchPer().asFloat())
342 DeformTextures::deformLandscape(pos
,
343 (int) (deformSize
+ 1),
344 ExplosionTextures::instance()->getScorchBitmap(
345 params_
->getDeformTexture()),
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
&&
372 edgePoints_
.insert(XY_TO_UINT(x
- 1, y
));
374 else if (heightR
< height
&&
380 edgePoints_
.insert(XY_TO_UINT(x
+ 1, y
));
382 else if (heightU
< height
&&
388 edgePoints_
.insert(XY_TO_UINT(x
, y
+ 1));
390 else if (heightD
< height
)
393 edgePoints_
.insert(XY_TO_UINT(x
, y
- 1));
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
));
406 if (heightL
< height
)
410 edgePoints_
.insert(XY_TO_UINT(x
- 1, y
));
412 if (heightR
< height
)
416 edgePoints_
.insert(XY_TO_UINT(x
+ 1, y
));
418 if (heightU
< height
)
422 edgePoints_
.insert(XY_TO_UINT(x
, y
+ 1));
424 if (heightD
< height
)
428 edgePoints_
.insert(XY_TO_UINT(x
, y
- 1));
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
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().
465 FixedVector
position(
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();
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();
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();
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();