Napalm.cpp

Go to the documentation of this file.
00001 ////////////////////////////////////////////////////////////////////////////////
00002 //    Scorched3D (c) 2000-2009
00003 //
00004 //    This file is part of Scorched3D.
00005 //
00006 //    Scorched3D is free software; you can redistribute it and/or modify
00007 //    it under the terms of the GNU General Public License as published by
00008 //    the Free Software Foundation; either version 2 of the License, or
00009 //    (at your option) any later version.
00010 //
00011 //    Scorched3D is distributed in the hope that it will be useful,
00012 //    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013 //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014 //    GNU General Public License for more details.
00015 //
00016 //    You should have received a copy of the GNU General Public License
00017 //    along with Scorched3D; if not, write to the Free Software
00018 //    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019 ////////////////////////////////////////////////////////////////////////////////
00020 
00021 #include <engine/ScorchedContext.h>
00022 #include <engine/ActionController.h>
00023 #include <target/TargetContainer.h>
00024 #include <target/TargetDamageCalc.h>
00025 #include <target/TargetRenderer.h>
00026 #include <target/TargetState.h>
00027 #include <target/TargetSpace.h>
00028 #include <actions/Napalm.h>
00029 #include <actions/CameraPositionAction.h>
00030 #ifndef S3D_SERVER
00031         #include <sprites/ExplosionTextures.h>
00032         #include <GLEXT/GLStateExtension.h>
00033         #include <landscape/Landscape.h>
00034         #include <landscape/DeformTextures.h>
00035         #include <landscape/Smoke.h>
00036         #include <client/ScorchedClient.h>
00037 #endif
00038 #include <landscapemap/LandscapeMaps.h>
00039 #include <landscapedef/LandscapeDefinition.h>
00040 #include <landscapedef/LandscapeTex.h>
00041 #include <weapons/AccessoryStore.h>
00042 #include <common/Defines.h>
00043 
00044 static const int deformSize = 3;
00045 static DeformLandscape::DeformPoints deformMap;
00046 static bool deformCreated = false;
00047 
00048 #define XY_TO_UINT(x, y) ((((unsigned int) x) << 16) | (((unsigned int) y) & 0xffff))
00049 #define XY2_TO_UINT(x, y) ((((unsigned int) x - x % 2) << 16) | (((unsigned int) y - y % 2) & 0xffff))
00050 #define UINT_TO_X(pt) ((int)(pt >> 16))
00051 #define UINT_TO_Y(pt) ((int)(pt & 0xffff))
00052 
00053 Napalm::Napalm(int x, int y, Weapon *weapon, 
00054         NapalmParams *params,
00055         WeaponFireContext &weaponContext) :
00056         ActionReferenced("Napalm"),
00057         startX_(x), startY_(y), napalmTime_(0), 
00058         weapon_(weapon), params_(params),
00059         weaponContext_(weaponContext), 
00060         totalTime_(0), hurtTime_(0),
00061         counter_(0.1f, 0.1f), set_(0),
00062         particleSet_(0)
00063 {
00064 }
00065 
00066 Napalm::~Napalm()
00067 {
00068         delete params_;
00069 }
00070 
00071 void Napalm::init()
00072 {
00073         if (!deformCreated)
00074         {
00075                 deformCreated = true;
00076 
00077                 Vector center(deformSize + 1, deformSize + 1);
00078                 for (int a=0; a<(deformSize + 1) * 2; a++)
00079                 {
00080                         for (int b=0; b<(deformSize + 1) * 2; b++)
00081                         {
00082                                 Vector pos(a, b);
00083                                 float dist = (center - pos).Magnitude();
00084                                 dist /= deformSize;
00085                                 dist = 1.0f - MIN(1.0f, dist);
00086 
00087                                 DIALOG_ASSERT(a < 100 && b < 100);
00088                                 deformMap.map[a][b] = fixed::fromFloat(dist);
00089                         }
00090                 }
00091         }
00092 
00093         fixed ShowTime = 5;
00094         FixedVector position(fixed(startX_), fixed(startY_), context_->getLandscapeMaps().
00095                 getGroundMaps().getHeight(startX_, startY_));
00096         CameraPositionAction *pos = new CameraPositionAction(
00097                 position, ShowTime, 5);
00098         context_->getActionController().addAction(pos);
00099 
00100         edgePoints_.insert(XY_TO_UINT(startX_, startY_));
00101 
00102 #ifndef S3D_SERVER
00103         if (!context_->getServerMode()) 
00104         {
00105                 set_ = ExplosionTextures::instance()->getTextureSetByName(
00106                         params_->getNapalmTexture());
00107         }
00108 #endif // #ifndef S3D_SERVER
00109 }
00110 
00111 std::string Napalm::getActionDetails()
00112 {
00113         return S3D::formatStringBuffer("%i,%i %s",
00114                 startX_, startY_, weapon_->getParent()->getName());
00115 }
00116 
00117 void Napalm::simulate(fixed frameTime, bool &remove)
00118 {
00119 #ifndef S3D_SERVER
00120         if (!context_->getServerMode())
00121         {
00122                 if (!napalmPoints_.empty() &&
00123                         !params_->getNoSmoke() &&
00124                         counter_.nextDraw(frameTime.asFloat()))
00125                 {
00126                         int count = rand() % napalmPoints_.size();
00127 
00128                         NapalmEntry *entry = napalmRANDPoints_[napalmRANDPoints_.size() - 1 - count];
00129                         fixed posZ = 
00130                                 ScorchedClient::instance()->getLandscapeMaps().getGroundMaps().getHeight(
00131                                 entry->posX, entry->posY);
00132                         Landscape::instance()->getSmoke().
00133                                 addSmoke(float(entry->posX), float(entry->posY), posZ.asFloat());
00134                 }
00135         }
00136 #endif // #ifndef S3D_SERVER
00137 
00138         // Add napalm for the period of the time interval
00139         // once the time interval has expired then start taking it away
00140         // Once all napalm has disapeared the simulation is over
00141         totalTime_ += frameTime;
00142         while (totalTime_ > params_->getStepTime())
00143         {
00144                 totalTime_ -= params_->getStepTime();
00145                 napalmTime_ += params_->getStepTime();
00146                 if (napalmTime_ < params_->getNapalmTime())
00147                 {
00148                         // Still within the time period, add more napalm
00149                         if (int(napalmPoints_.size()) < params_->getNumberParticles()) 
00150                         {
00151                                 simulateAddStep();
00152                         }
00153 
00154                         // Check for the case where we land in water
00155                         if (napalmPoints_.empty())
00156                         {
00157                                 remove = true;
00158                                 break;
00159                         }
00160                 }
00161                 else
00162                 {
00163                         // Not within the time period remove napalm
00164                         if (!napalmPoints_.empty())
00165                         {
00166                                 simulateRmStep();
00167                         }
00168                         else
00169                         {
00170                                 remove = true;
00171                                 break;
00172                         }
00173                 }
00174         }
00175 
00176         // Calculate how much damage to make to the tanks
00177         hurtTime_ += frameTime;
00178         while (hurtTime_ > params_->getHurtStepTime())
00179         {
00180                 hurtTime_ -= params_->getHurtStepTime();
00181 
00182                 simulateDamage();
00183         }
00184 
00185         Action::simulate(frameTime, remove);
00186 }
00187 
00188 fixed Napalm::getHeight(int x, int y)
00189 {
00190         LandscapeMaps *hmap = &context_->getLandscapeMaps();
00191         if (x < 0 || y < 0 ||
00192                 x > hmap->getGroundMaps().getLandscapeWidth() ||
00193                 y > hmap->getGroundMaps().getLandscapeHeight())
00194         {
00195                 // The height at the sides of the landscape is huge
00196                 // so we will never go there with the napalm
00197                 return fixed::MAX_FIXED;
00198         }
00199 
00200         // Return the correct height the square + the
00201         // height of all the napalm on this square
00202         // the napalm builds up and get higher so
00203         // we can go over small bumps
00204         return hmap->getGroundMaps().getHeight(x, y) +
00205                 hmap->getGroundMaps().getNapalmHeight(x, y);
00206 }
00207 
00208 void Napalm::simulateRmStep()
00209 {
00210         int pset = napalmPoints_.front()->pset;
00211         while (!napalmPoints_.empty()) 
00212         {
00213                 // Check if the entry should be removed
00214                 NapalmEntry *entry = napalmPoints_.front();
00215                 if (pset != entry->pset) break;
00216 
00217                 // Remove the first napalm point from the list
00218                 // and remove the height from the landscape
00219                 napalmPoints_.pop_front();
00220                 int x = entry->posX;
00221                 int y = entry->posY;
00222                 delete entry;
00223 
00224                 unsigned int pointsCount = XY2_TO_UINT(x, y);
00225                 std::map<unsigned int, int>::iterator countItor =
00226                         napalmPointsCount_.find(pointsCount);
00227                 if (countItor != napalmPointsCount_.end())
00228                 {
00229                         countItor->second--;
00230                         if (countItor->second == 0) napalmPointsCount_.erase(countItor);
00231                 }
00232 
00233                 context_->getLandscapeMaps().getGroundMaps().getNapalmHeight(x, y) -= params_->getNapalmHeight();
00234         }
00235 }
00236 
00237 void Napalm::simulateAddStep()
00238 {
00239         particleSet_++;
00240 
00241         std::set<unsigned int> currentEdges = edgePoints_;
00242         edgePoints_.clear();
00243 
00244         std::set<unsigned int>::iterator itor;
00245         for (itor = currentEdges.begin();
00246                 itor != currentEdges.end();
00247                 itor++)
00248         {
00249                 unsigned int currentEdge = *itor;
00250                 int x = UINT_TO_X(currentEdge);
00251                 int y = UINT_TO_Y(currentEdge);
00252 
00253                 simulateAddEdge(x, y);
00254         }
00255 }
00256 
00257 void Napalm::simulateAddEdge(int x, int y)
00258 {
00259         // Get the height of this point
00260         fixed height = getHeight(x, y);
00261 
00262         if (!params_->getAllowUnderWater())
00263         {
00264                 // Check napalm is under water 
00265                 fixed waterHeight = -10;
00266                 LandscapeTex &tex = *context_->getLandscapeMaps().getDefinitions().getTex();
00267                 if (tex.border->getType() == LandscapeTexType::eWater)
00268                 {
00269                         LandscapeTexBorderWater *water = 
00270                                 (LandscapeTexBorderWater *) tex.border;
00271                 waterHeight = water->height;
00272                 }
00273 
00274                 if (height < waterHeight) // Water height
00275                 {
00276                         // Perhaps we could add a boiling water sound at some point
00277                         return;
00278                 }
00279         } 
00280 
00281         // Add this current point to the napalm map
00282         RandomGenerator &random = context_->getActionController().getRandom();
00283         int offset = (random.getRandFixed() * 31).asInt();
00284         NapalmEntry *newEntry = new NapalmEntry(x, y, offset, particleSet_);
00285         napalmPoints_.push_back(newEntry);
00286         napalmRANDPoints_.push_back(newEntry);
00287 
00288         unsigned int pointsCount = XY2_TO_UINT(x, y);
00289         std::map<unsigned int, int>::iterator countItor =
00290                 napalmPointsCount_.find(pointsCount);
00291         if (countItor == napalmPointsCount_.end())
00292         {
00293                 napalmPointsCount_.insert(std::pair<unsigned int, int>(pointsCount, 1));
00294         }
00295         else
00296         {
00297                 countItor->second++;
00298         }
00299 
00300 #ifndef S3D_SERVER
00301         if (!context_->getServerMode())
00302         {
00303                 ParticleEmitter emitter;
00304                 emitter.setAttributes(
00305                         params_->getNapalmTime().asFloat(), params_->getNapalmTime().asFloat(),
00306                         0.5f, 1.0f, // Mass
00307                         0.01f, 0.02f, // Friction
00308                         Vector(0.0f, 0.0f, 0.0f), Vector(0.0f, 0.0f, 0.0f), // Velocity
00309                         Vector(1.0f, 1.0f, 1.0f), 0.9f, // StartColor1
00310                         Vector(1.0f, 1.0f, 1.0f), 0.6f, // StartColor2
00311                         Vector(1.0f, 1.0f, 1.0f), 0.0f, // EndColor1
00312                         Vector(1.0f, 1.0f, 1.0f), 0.1f, // EndColor2
00313                         1.5f, 1.5f, 1.5f, 1.5f, // Start Size
00314                         1.5f, 1.5f, 1.5f, 1.5f, // EndSize
00315                         Vector(0.0f, 0.0f, 0.0f), // Gravity
00316                         params_->getLuminance(),
00317                         false);
00318                 Vector position1(float(x) + 0.5f, float(y) - 0.2f, 0.0f);
00319                 Vector position2(float(x) - 0.5f, float(y) - 0.2f, 0.0f);
00320                 Vector position3(float(x) + 0.0f, float(y) + 0.5f, 0.0f);
00321                 emitter.emitNapalm(
00322                         position1, 
00323                         ScorchedClient::instance()->getParticleEngine(),
00324                         set_);
00325                 emitter.emitNapalm(
00326                         position2, 
00327                         ScorchedClient::instance()->getParticleEngine(),
00328                         set_);
00329                 emitter.emitNapalm(
00330                         position3, 
00331                         ScorchedClient::instance()->getParticleEngine(),
00332                         set_);
00333 
00334                 // Add the ground scorch
00335                 if (!GLStateExtension::getNoTexSubImage())
00336                 {
00337                         if (height == context_->getLandscapeMaps().getGroundMaps().getHeight(x, y)) 
00338                         {
00339                                 if (RAND < params_->getGroundScorchPer().asFloat())
00340                                 {
00341                                         Vector pos(x, y);
00342                                         DeformTextures::deformLandscape(pos, 
00343                                                 (int) (deformSize + 1),
00344                                                 ExplosionTextures::instance()->getScorchBitmap(
00345                                                         params_->getDeformTexture()),
00346                                                 deformMap);
00347                                 }
00348                         }
00349                 }
00350         }
00351 #endif // #ifndef S3D_SERVER
00352 
00353         context_->getLandscapeMaps().getGroundMaps().getNapalmHeight(x, y) += params_->getNapalmHeight();
00354 
00355         // Calculate every time as the landscape may change
00356         // due to other actions
00357         fixed heightL = getHeight(x-1, y);
00358         fixed heightR = getHeight(x+1, y);
00359         fixed heightU = getHeight(x, y+1);
00360         fixed heightD = getHeight(x, y-1);
00361 
00362         if (params_->getSingleFlow()) 
00363         {
00364                 fixed *heightLR = 0;
00365                 int LR = 0;
00366                 if (heightL < heightR)
00367                 {
00368                         heightLR = &heightL;
00369                         LR = -1;
00370                 }
00371                 else if (heightL == heightR)
00372                 {
00373                         if (random.getRandUInt() % 2 == 0)
00374                         {
00375                                 heightLR = &heightL;
00376                                 LR = -1;
00377                         }
00378                         else
00379                         {
00380                                 heightLR = &heightR;
00381                                 LR = +1;
00382                         }
00383                 }
00384                 else
00385                 {
00386                         heightLR = &heightR;
00387                         LR = +1;
00388                 }
00389 
00390                 fixed *heightUD = 0;
00391                 int UD = 0;
00392                 if (heightU < heightD)
00393                 {
00394                         heightUD = &heightU;    
00395                         UD = +1;
00396                 }
00397                 else if (heightU == heightD)
00398                 {
00399                         if (random.getRandUInt() % 2 == 0)
00400                         {
00401                                 heightUD = &heightU;    
00402                                 UD = +1;
00403                         }
00404                         else
00405                         {
00406                                 heightUD = &heightD;    
00407                                 UD = -1;
00408                         }
00409                 }
00410                 else 
00411                 {
00412                         heightUD = &heightD;    
00413                         UD = -1;
00414                 }
00415 
00416                 enum Direction
00417                 {
00418                         eUD,
00419                         eLR,
00420                         eNone
00421                 } dir = eNone;
00422                 if (*heightLR < *heightUD)
00423                 {
00424                         if (*heightLR < height) dir = eLR;
00425                 }
00426                 else if (*heightLR == *heightUD)
00427                 {
00428                         if (*heightLR < height) 
00429                         {
00430                                 if (random.getRandUInt() % 2 == 0)
00431                                 {
00432                                         dir = eUD;
00433                                 }
00434                                 else
00435                                 {
00436                                         dir = eLR;
00437                                 }                               
00438                         }
00439                 }
00440                 else
00441                 {
00442                         if (*heightUD < height) 
00443                         {
00444                                 if (*heightLR < height) dir = eUD;
00445                         }
00446                 }
00447 
00448                 switch (dir)
00449                 {
00450                 case eUD:
00451                         edgePoints_.insert(XY_TO_UINT(x, y + UD));
00452                         break;
00453                 case eLR:
00454                         edgePoints_.insert(XY_TO_UINT(x + LR, y));
00455                         break;
00456                 default:
00457                         // None of the landscape is currently lower than the current point
00458                         // Just wait, as this point will be now be covered in napalm
00459                         // and may get higher and higher until it is
00460                         edgePoints_.insert(XY_TO_UINT(x, y));
00461                         break;
00462                 }
00463         }
00464         else
00465         {
00466                 int addedCount = 0;
00467                 if (heightL < height)
00468                 {
00469                         // Move left
00470                         addedCount++;
00471                         edgePoints_.insert(XY_TO_UINT(x - 1, y));
00472                 }
00473                 if (heightR < height)
00474                 {
00475                         // Move right
00476                         addedCount++;
00477                         edgePoints_.insert(XY_TO_UINT(x + 1, y));
00478                 }
00479                 if (heightU < height)
00480                 {
00481                         // Move up
00482                         addedCount++;
00483                         edgePoints_.insert(XY_TO_UINT(x, y + 1));
00484                 }
00485                 if (heightD < height)
00486                 {
00487                         // Move down
00488                         addedCount++;
00489                         edgePoints_.insert(XY_TO_UINT(x, y - 1));
00490                 }
00491                 if (addedCount == 0)
00492                 {
00493                         // None of the landscape is currently lower than the current point
00494                         // Just wait, as this point will be now be covered in napalm
00495                         // and may get higher and higher until it is
00496                         edgePoints_.insert(XY_TO_UINT(x, y));
00497                 }
00498         }
00499 }
00500 
00501 void Napalm::simulateDamage()
00502 {
00503         const int EffectRadius = params_->getEffectRadius();
00504 
00505         // Store how much each tank is damaged
00506         // Keep in a map so we don't need to create multiple
00507         // damage actions.  Now we only create one per tank
00508         static std::map<unsigned int, fixed> TargetDamageCalc;
00509         TargetDamageCalc.clear();
00510 
00511         // Add damage into the damage map for each napalm point that is near to
00512         // the tanks
00513         std::map<unsigned int, int>::iterator itor =
00514                 napalmPointsCount_.begin();
00515         std::map<unsigned int, int>::iterator endItor = 
00516                 napalmPointsCount_.end();
00517         for (;itor != endItor; itor++)
00518         {
00519                 unsigned int pointsCount = itor->first;
00520                 fixed count = fixed(itor->second);
00521                 int x = UINT_TO_X(pointsCount);
00522                 int y = UINT_TO_Y(pointsCount);
00523 
00524                 fixed height = context_->getLandscapeMaps().getGroundMaps().
00525                         getHeight(x, y);
00526                 FixedVector position(
00527                         fixed(x), 
00528                         fixed(y),
00529                         height);
00530 
00531                 std::map<unsigned int, Target *> collisionTargets;
00532                 context_->getTargetSpace().getCollisionSet(position, 
00533                         fixed(EffectRadius), collisionTargets);
00534                 std::map<unsigned int, Target *>::iterator itor;
00535                 for (itor = collisionTargets.begin();
00536                         itor != collisionTargets.end();
00537                         itor++)
00538                 {
00539                         Target *target = (*itor).second;
00540                         if (target->getAlive())
00541                         {
00542                                 std::map<unsigned int, fixed>::iterator damageItor = 
00543                                         TargetDamageCalc.find(target->getPlayerId());
00544                                 if (damageItor == TargetDamageCalc.end())
00545                                 {
00546                                         TargetDamageCalc[target->getPlayerId()] = count * params_->getHurtPerSecond();
00547                                 }
00548                                 else
00549                                 {
00550                                         TargetDamageCalc[target->getPlayerId()] += count * params_->getHurtPerSecond();
00551                                 }
00552                         }
00553                 }
00554         }
00555 
00556         // Add all the damage to the tanks (if any)
00557         if (!TargetDamageCalc.empty())
00558         {
00559                 std::map<unsigned int, fixed>::iterator damageItor;
00560                 for (damageItor = TargetDamageCalc.begin();
00561                         damageItor != TargetDamageCalc.end();
00562                         damageItor++)
00563                 {
00564                         Target *target = 
00565                                 context_->getTargetContainer().getTargetById(damageItor->first);
00566                         fixed damage = (*damageItor).second;
00567 
00568                         // Add damage to the tank
00569                         // If allowed for this target type (mainly for trees)
00570                         if (!target->getTargetState().getNoDamageBurn())
00571                         {
00572                                 TargetDamageCalc::damageTarget(*context_, target, weapon_, 
00573                                         weaponContext_, damage, true, false, false);
00574                         }
00575 
00576                         // Set this target to burnt
00577                         if (target->getRenderer() &&
00578                                 !params_->getNoObjectDamage())
00579                         {
00580                                 target->getRenderer()->targetBurnt();
00581                         }
00582                 }
00583                 TargetDamageCalc.clear();
00584         }
00585 }

Generated on Mon Feb 16 15:14:48 2009 for Scorched3D by  doxygen 1.5.3