TankDamage.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 <actions/TankDamage.h>
00022 #include <actions/TankFalling.h>
00023 #include <actions/TankSay.h>
00024 #include <actions/CameraPositionAction.h>
00025 #ifndef S3D_SERVER
00026         #include <sprites/TextActionRenderer.h>
00027 #endif
00028 #include <common/OptionsScorched.h>
00029 #include <common/Defines.h>
00030 #include <common/ChannelManager.h>
00031 #include <common/StatsLogger.h>
00032 #include <weapons/AccessoryStore.h>
00033 #include <weapons/Shield.h>
00034 #include <landscapemap/LandscapeMaps.h>
00035 #include <engine/ScorchedContext.h>
00036 #include <engine/ActionController.h>
00037 #include <tank/TankContainer.h>
00038 #include <tank/TankTeamScore.h>
00039 #include <tank/TankScore.h>
00040 #include <tank/TankState.h>
00041 #include <tank/TankPosition.h>
00042 #include <tankai/TankAI.h>
00043 #include <target/TargetShield.h>
00044 #include <target/TargetLife.h>
00045 #include <target/TargetParachute.h>
00046 #include <target/TargetState.h>
00047 #include <tankai/TankAIStrings.h>
00048 #include <lang/LangResource.h>
00049 
00050 TankDamage::TankDamage(Weapon *weapon, 
00051                 unsigned int damagedPlayerId, WeaponFireContext &weaponContext,
00052                 fixed damage, bool useShieldDamage, bool checkFall,
00053                 bool shieldOnlyDamage) :
00054         ActionReferenced("TankDamage"),
00055         weapon_(weapon), firstTime_(true),
00056         damagedPlayerId_(damagedPlayerId), weaponContext_(weaponContext),
00057         damage_(damage), useShieldDamage_(useShieldDamage), checkFall_(checkFall),
00058         shieldOnlyDamage_(shieldOnlyDamage)
00059 {
00060 }
00061 
00062 TankDamage::~TankDamage()
00063 {
00064 }
00065 
00066 void TankDamage::init()
00067 {
00068         Target *damagedTarget = 
00069                 context_->getTargetContainer().getTargetById(damagedPlayerId_);
00070         if (damagedTarget && !damagedTarget->isTarget())
00071         {
00072                 CameraPositionAction *pos = new CameraPositionAction(
00073                         damagedTarget->getLife().getTargetPosition(), 
00074                         4,
00075                         15);
00076                 context_->getActionController().addAction(pos);
00077         }
00078 }
00079 
00080 std::string TankDamage::getActionDetails()
00081 {
00082         return S3D::formatStringBuffer("%u %i %s",
00083                 damagedPlayerId_, damage_.getInternal(), weapon_->getParent()->getName());
00084 }
00085 
00086 void TankDamage::simulate(fixed frameTime, bool &remove)
00087 {
00088         if (firstTime_)
00089         {
00090                 firstTime_ = false;
00091                 calculateDamage();
00092         }
00093 
00094         remove = true;
00095         Action::simulate(frameTime, remove);
00096 }
00097 
00098 void TankDamage::calculateDamage()
00099 {
00100         unsigned int firedPlayerId = weaponContext_.getPlayerId();
00101 
00102         // Find the tank that has been damaged
00103         Target *damagedTarget = 
00104                 context_->getTargetContainer().getTargetById(damagedPlayerId_);
00105         if (!damagedTarget || !damagedTarget->getAlive()) return;
00106 
00107         // Tell this tanks ai that is has been hurt by another tank
00108         if (!damagedTarget->isTarget())
00109         {
00110                 // Tell all AIs about this collision
00111                 std::map<unsigned int, Tank *> tanks = 
00112                         context_->getTankContainer().getAllTanks();
00113                 std::map<unsigned int, Tank *>::iterator itor;
00114                 for (itor = tanks.begin();
00115                         itor != tanks.end();
00116                         itor++)
00117                 {
00118                         Tank *tank = (*itor).second;
00119                         TankAI *ai = tank->getTankAI();
00120                         if (ai)
00121                         {               
00122                                 if (tank->getState().getState() == TankState::sNormal &&
00123                                         !tank->getState().getSpectator())
00124                                 {
00125                                         ai->tankHurt(weapon_, damage_.asFloat(),
00126                                                 damagedTarget->getPlayerId(), 
00127                                                 firedPlayerId);
00128                                 }
00129                         }
00130                 }
00131         }
00132 
00133         // Remove any damage from shield first
00134         if (damage_ > 0)
00135         {
00136                 fixed shieldDamage = 0;
00137                 Accessory *sh = damagedTarget->getShield().getCurrentShield();
00138                 if (sh && useShieldDamage_)
00139                 {
00140                         Shield *shield = (Shield *) sh->getAction();
00141                         fixed shieldPowerRequired = 
00142                                 damage_ * shield->getHitPenetration();
00143                         fixed shieldPower = 
00144                                 damagedTarget->getShield().getShieldPower();
00145                         if (shieldPower > shieldPowerRequired)
00146                         {
00147                                 shieldPower -= shieldPowerRequired;
00148                                 damage_ = 0;
00149                         }
00150                         else
00151                         {
00152                                 fixed p = shieldPower / shield->getHitPenetration();
00153                                 shieldPower = 0;
00154                                 damage_ -= p;
00155                         }
00156 
00157                         damagedTarget->getShield().setShieldPower(shieldPower);
00158                 }
00159         }
00160 
00161         // Remove the remaining damage from the tank
00162         if (damage_ > 0 && !shieldOnlyDamage_)
00163         {
00164 #ifndef S3D_SERVER
00165                 if (!context_->getServerMode() &&
00166                         damagedTarget->getTargetState().getDisplayDamage())
00167                 {
00168                         Vector position = damagedTarget->getLife().getFloatPosition();
00169                         position[0] += RAND * 5.0f - 2.5f;
00170                         position[1] += RAND * 5.0f - 2.5f;
00171                         position[2] += RAND * 5.0f - 2.5f;
00172 
00173                         Vector redColor(0.75f, 0.0f, 0.0f);
00174                         context_->getActionController().addAction(
00175                                 new SpriteAction(
00176                                         new TextActionRenderer(
00177                                                 position,
00178                                                 redColor,
00179                                                 S3D::formatStringBuffer("%.0f", damage_.asFloat()))));
00180                 }
00181 #endif // #ifndef S3D_SERVER
00182 
00183                 // Remove the life
00184                 if (damage_ > damagedTarget->getLife().getLife()) damage_ = 
00185                         damagedTarget->getLife().getLife();
00186                 damagedTarget->getLife().setLife(damagedTarget->getLife().getLife() - damage_);
00187                 if (context_->getOptionsGame().getLimitPowerByHealth() &&
00188                         !damagedTarget->isTarget())
00189                 {
00190                         Tank *damagedTank = (Tank *) damagedTarget;
00191                         damagedTank->getPosition().changePower(0, true);
00192                 }
00193 
00194                 if (context_->getOptionsGame().getActionSyncCheck())
00195                 {
00196                         context_->getActionController().addSyncCheck(
00197                                 S3D::formatStringBuffer("TankDamage: %u %i", 
00198                                         damagedTarget->getPlayerId(),
00199                                         damagedTarget->getLife().getLife().getInternal()));
00200                 }
00201 
00202                 // Check if the tank is dead
00203                 bool killedTank = (damagedTarget->getLife().getLife() == 0);
00204 
00205                 // Add any score got from this endevour
00206                 // Should always be a tank that has fired
00207                 Tank *firedTank = 
00208                         context_->getTankContainer().getTankById(firedPlayerId);
00209                 if (firedTank && !damagedTarget->isTarget())
00210                 {       
00211                         Tank *damagedTank = (Tank *) damagedTarget;
00212 
00213                         // Add this tank as a tank that assisted in the kill
00214                         damagedTank->getScore().getHurtBy().insert(firedTank->getPlayerId());
00215 
00216                         // Calculate team kills
00217                         bool selfKill = (damagedPlayerId_ ==  firedPlayerId);
00218                         bool teamKill = ((context_->getOptionsGame().getTeams() > 1) &&
00219                                 (firedTank->getTeam() == damagedTank->getTeam()));
00220 
00221                         if (!killedTank)
00222                         {
00223                                 // Calculate money won for not killing this tank
00224                                 int moneyPerHit = 
00225                                         context_->getOptionsGame().getMoneyWonPerHitPoint() *
00226                                                 weapon_->getArmsLevel();
00227                                 if (context_->getOptionsGame().getMoneyPerHealthPoint()) 
00228                                         moneyPerHit = (moneyPerHit * damage_.asInt()) / 100;
00229                                 if (selfKill || teamKill) moneyPerHit *= -1;
00230 
00231                                 firedTank->getScore().setMoney(
00232                                         firedTank->getScore().getMoney() + moneyPerHit);
00233                         }
00234                         else 
00235                         {
00236                                 int moneyPerKill = 
00237                                         context_->getOptionsGame().getMoneyWonPerKillPoint() *
00238                                                 weapon_->getArmsLevel();
00239                                 if (!selfKill && !teamKill)
00240                                 {
00241                                         // Note this is done before turn kills is updated
00242                                         // so for the first kill turn kills will be 0
00243                                         // i.e. no multikill bonus for 1st kill
00244                                         moneyPerKill +=
00245                                                 context_->getOptionsGame().getMoneyWonPerMultiKillPoint() *
00246                                                         weapon_->getArmsLevel() *
00247                                                         firedTank->getScore().getTurnKills();
00248                                 }
00249                                 if (context_->getOptionsGame().getMoneyPerHealthPoint()) 
00250                                         moneyPerKill = (moneyPerKill * damage_.asInt()) / 100;
00251                                 int scorePerKill = context_->getOptionsGame().getScorePerKill();
00252 
00253                                 int moneyPerAssist = 
00254                                         context_->getOptionsGame().getMoneyWonPerAssistPoint() *
00255                                                 weapon_->getArmsLevel();
00256                                 int scorePerAssist = context_->getOptionsGame().getScorePerAssist();
00257 
00258                                 // Update kills and score
00259                                 if (selfKill || teamKill)
00260                                 {
00261                                         firedTank->getScore().setKills(
00262                                                 firedTank->getScore().getKills() - 1);
00263                                         firedTank->getScore().setMoney(
00264                                                 firedTank->getScore().getMoney() - moneyPerKill);
00265                                         firedTank->getScore().setScore(
00266                                                 firedTank->getScore().getScore() - scorePerKill);
00267 
00268                                         if (firedTank->getTeam() > 0)
00269                                         {
00270                                                 context_->getTankTeamScore().addScore(
00271                                                         -scorePerKill, firedTank->getTeam());
00272                                         }
00273                                 }
00274                                 else
00275                                 {
00276                                         firedTank->getScore().setKills(
00277                                                 firedTank->getScore().getKills() + 1);
00278                                         firedTank->getScore().setTurnKills(
00279                                                 firedTank->getScore().getTurnKills() + 1);
00280                                         firedTank->getScore().setMoney(
00281                                                 firedTank->getScore().getMoney() + moneyPerKill);
00282                                         firedTank->getScore().setScore(
00283                                                 firedTank->getScore().getScore() + scorePerKill);
00284 
00285                                         if (firedTank->getTeam() > 0)
00286                                         {
00287                                                 context_->getTankTeamScore().addScore(
00288                                                         scorePerKill, firedTank->getTeam());
00289                                         }
00290                                 }
00291 
00292                                 // Update assists
00293                                 std::set<unsigned int> &hurtBy = 
00294                                         damagedTank->getScore().getHurtBy();
00295                                 std::set<unsigned int>::iterator itor;
00296                                 for (itor = hurtBy.begin();
00297                                         itor != hurtBy.end();
00298                                         itor++)
00299                                 {
00300                                         unsigned int hurtByPlayer = (*itor);
00301                                         Tank *hurtByTank = 
00302                                                 context_->getTankContainer().getTankById(hurtByPlayer);
00303                                         if (!hurtByTank) continue;
00304 
00305                                         // Only score when the tank does not hurt itself
00306                                         if (hurtByTank == damagedTank) continue;
00307 
00308                                         // You don't get an assist for your kill
00309                                         if (hurtByTank == firedTank) continue;
00310 
00311                                         // or a team member
00312                                         if ((context_->getOptionsGame().getTeams() > 1) &&
00313                                                 (hurtByTank->getTeam() == damagedTank->getTeam())) continue;
00314 
00315                                         // Update assist score
00316                                         hurtByTank->getScore().setAssists(
00317                                                 hurtByTank->getScore().getAssists() + 1);
00318                                         hurtByTank->getScore().setMoney(
00319                                                 hurtByTank->getScore().getMoney() + moneyPerAssist);
00320                                         hurtByTank->getScore().setScore(
00321                                                 hurtByTank->getScore().getScore() + scorePerAssist);
00322 
00323                                         if (hurtByTank->getTeam() > 0)
00324                                         {
00325                                                 context_->getTankTeamScore().addScore(
00326                                                         scorePerAssist, hurtByTank->getTeam());
00327                                         }
00328                                 }
00329                         }
00330                 }
00331 
00332                 if (killedTank)
00333                 {
00334                         // The tank has died, make it blow up etc.
00335                         calculateDeath();
00336 
00337                         if (!damagedTarget->isTarget())
00338                         {
00339                                 // The tank is now dead
00340                                 Tank *damagedTank = (Tank *) damagedTarget;
00341                                 damagedTank->getState().setState(TankState::sDead);
00342 
00343                                 // This tank has lost a life
00344                                 if (damagedTank->getState().getMaxLives() > 0)
00345                                 {
00346                                         damagedTank->getState().setLives(
00347                                                 damagedTank->getState().getLives() - 1);
00348                                 }
00349                         }
00350                 }
00351         }
00352 
00353         // Check if the tank needs to fall
00354         if (checkFall_ && damagedTarget->getAlive())
00355         {
00356                 // The tank is not dead check if it needs to fall
00357                 FixedVector &position = damagedTarget->getLife().getTargetPosition();
00358                 if (context_->getLandscapeMaps().getGroundMaps().
00359                         getInterpHeight(position[0], position[1]) < position[2])
00360                 {
00361                         // Check this tank is not already falling
00362                         if (!damagedTarget->getTargetState().getFalling())
00363                         {
00364                                 Parachute *parachute = 0;
00365                                 Accessory *paraAccessory = 
00366                                         damagedTarget->getParachute().getCurrentParachute();
00367                                 if (paraAccessory)
00368                                 {
00369                                         parachute = (Parachute *) paraAccessory->getAction();
00370                                 }
00371 
00372                                 // Tank falling
00373                                 context_->getActionController().addAction(
00374                                         new TankFalling(weapon_, damagedPlayerId_, weaponContext_,
00375                                                 parachute));
00376                         }
00377                 }
00378         }
00379 
00380         // DO LAST
00381         // If the tank is a target, remove the target
00382         if (!damagedTarget->getAlive() &&
00383                 damagedTarget->isTarget())
00384         {
00385                 Target *removedTarget = 
00386                         context_->getTargetContainer().
00387                                 removeTarget(damagedTarget->getPlayerId());
00388                 if (context_->getOptionsGame().getActionSyncCheck())
00389                 {
00390                         context_->getActionController().addSyncCheck(
00391                                 S3D::formatStringBuffer("RemoveTarget : %u %s", 
00392                                         removedTarget->getPlayerId(),
00393                                         removedTarget->getCStrName().c_str()));
00394                 }
00395 
00396                 delete removedTarget;
00397         }
00398 }
00399 
00400 void TankDamage::calculateDeath()
00401 {
00402         Target *killedTarget = 
00403                 context_->getTargetContainer().getTargetById(damagedPlayerId_);
00404         if (!killedTarget) return;
00405 
00406         // Log the death
00407         logDeath();
00408 
00409         // Add the tank death explosion
00410         // Make the tank explode in one of many ways
00411         Weapon *weapon = killedTarget->getDeathAction();
00412         if (weapon)
00413         {
00414                 if (context_->getOptionsGame().getActionSyncCheck())
00415                 {
00416                         context_->getActionController().addSyncCheck(
00417                                 S3D::formatStringBuffer("DeathAction: %s", 
00418                                         weapon->getParent()->getName()));
00419                 }
00420 
00421                 FixedVector position = killedTarget->getLife().getTargetPosition();
00422                 FixedVector velocity;
00423                 WeaponFireContext weaponContext(weaponContext_.getPlayerId(), 
00424                         Weapon::eDataDeathAnimation);
00425                 weapon->fireWeapon(*context_, weaponContext, 
00426                         position, velocity);
00427                 StatsLogger::instance()->weaponFired(weapon, true);
00428         }
00429 }
00430 
00431 void TankDamage::logDeath()
00432 {
00433         unsigned int firedPlayerId = weaponContext_.getPlayerId();
00434 
00435         Target *killedTarget = 
00436                 context_->getTargetContainer().getTargetById(damagedPlayerId_);
00437         if (killedTarget->isTarget()) return;
00438 
00439         // Print the banner on who killed who
00440         GLTexture *weaponTexture = 0;
00441 #ifndef S3D_SERVER
00442         if (!context_->getServerMode())
00443         {
00444                 Accessory *accessory = 
00445                         context_->getAccessoryStore().
00446                         findByAccessoryId(weapon_->getParent()->getAccessoryId());
00447                 if (accessory)
00448                 {
00449                         weaponTexture = accessory->getTexture();
00450                 }
00451         }
00452 #endif // #ifndef S3D_SERVER
00453 
00454         Tank *killedTank = (Tank *) killedTarget;
00455 
00456         if (killedTank->getDestinationId() == 0)
00457         {
00458                 const char *line = TankAIStrings::instance()->getDeathLine(*context_);
00459                 if (line)
00460                 {
00461                         context_->getActionController().addAction(
00462                                 new TankSay(killedTank->getPlayerId(), 
00463                                 LANG_STRING(line)));
00464                 }
00465         }
00466 
00467         Tank *firedTank = 0;
00468         if (firedPlayerId != 0) firedTank = context_->getTankContainer().getTankById(firedPlayerId);
00469         else
00470         {
00471                 Vector white(1.0f, 1.0f, 1.0f);
00472                 static Tank envTank(*context_, 0, 0, 
00473                         LANG_STRING("Environment"), 
00474                         white, 
00475                         "", "");
00476                 envTank.setUniqueId("Environment");
00477                 firedTank = &envTank;
00478         }
00479 
00480         if (firedTank)
00481         {
00482                 if (damagedPlayerId_ == firedPlayerId)
00483                 {
00484                         int skillChange = -50;
00485                         firedTank->getScore().setSkill(firedTank->getScore().getSkill() + skillChange);
00486 
00487                         StatsLogger::instance()->
00488                                 tankSelfKilled(firedTank, weapon_);
00489                         StatsLogger::instance()->
00490                                 weaponKilled(weapon_, (weaponContext_.getData() & Weapon::eDataDeathAnimation));
00491                         {
00492                                 ChannelText text("combat",
00493                                         LANG_RESOURCE_3(
00494                                                 "TANK_KILLED_SELF", 
00495                                                 "[p:{0}] killed self with a [w:{1}] ({2} skill)",
00496                                                 firedTank->getTargetName(),
00497                                                 weapon_->getParent()->getName(),
00498                                                 S3D::formatStringBuffer("%i", skillChange)));
00499                                 ChannelManager::showText(*context_, text);
00500                         }
00501                 }
00502                 else if ((context_->getOptionsGame().getTeams() > 1) &&
00503                                 (firedTank->getTeam() == killedTank->getTeam())) 
00504                 {
00505                         int skillChange = -50;
00506                         firedTank->getScore().setSkill(firedTank->getScore().getSkill() + skillChange);
00507 
00508                         StatsLogger::instance()->
00509                                 tankTeamKilled(firedTank, killedTank, weapon_);
00510                         StatsLogger::instance()->
00511                                 weaponKilled(weapon_, (weaponContext_.getData() & Weapon::eDataDeathAnimation));
00512                         {
00513                                 ChannelText text("combat", 
00514                                         LANG_RESOURCE_4(
00515                                                 "TANK_KILLED_TEAM",
00516                                                 "[p:{0}] team killed [p:{1}] with a [w:{2}] ({3} skill)",
00517                                                 firedTank->getTargetName(),
00518                                                 killedTank->getTargetName(),
00519                                                 weapon_->getParent()->getName(),
00520                                                 S3D::formatStringBuffer("%i", skillChange)));
00521                                 ChannelManager::showText(*context_, text);
00522                         }
00523                 }
00524                 else
00525                 {
00526                         int skillChange = 0;
00527                         if (firedTank->getPlayerId() != 0 && killedTank->getPlayerId() != 0) 
00528                         {
00529                                 float weaponMult = (float(weapon_->getArmsLevel()) / 10.0f) + 1.0f;
00530                                 skillChange = int(
00531                                         (20.0f * weaponMult) / 
00532                                         (1.0f + powf(10.0f, (
00533                                         float(firedTank->getScore().getSkill() - killedTank->getScore().getSkill()) / 1000.0f)))
00534                                         );
00535                         }
00536                         firedTank->getScore().setSkill(firedTank->getScore().getSkill() + skillChange);
00537                         killedTank->getScore().setSkill(killedTank->getScore().getSkill() - skillChange);
00538 
00539                         StatsLogger::instance()->
00540                                 tankKilled(firedTank, killedTank, weapon_);
00541                         StatsLogger::instance()->
00542                                 weaponKilled(weapon_, (weaponContext_.getData() & Weapon::eDataDeathAnimation));
00543                         {
00544                                 if (firedTank->getScore().getTurnKills() > 1)
00545                                 {
00546                                         ChannelText text("combat", 
00547                                                 LANG_RESOURCE_4(
00548                                                 "TANK_KILLED_MULTIOTHER",
00549                                                 "[p:{0}] multi-killed [p:{1}] with a [w:{2}] ({3} skill)",
00550                                                 firedTank->getTargetName(),
00551                                                 killedTank->getTargetName(),
00552                                                 weapon_->getParent()->getName(),
00553                                                 S3D::formatStringBuffer("%i", skillChange)));
00554                                         ChannelManager::showText(*context_, text);
00555                                 }
00556                                 else
00557                                 {
00558                                         ChannelText text("combat", 
00559                                                 LANG_RESOURCE_4(
00560                                                 "TANK_KILLED_OTHER",
00561                                                 "[p:{0}] killed [p:{1}] with a [w:{2}] ({3} skill)",
00562                                                 firedTank->getTargetName(),
00563                                                 killedTank->getTargetName(),
00564                                                 weapon_->getParent()->getName(),
00565                                                 S3D::formatStringBuffer("%i", skillChange)));
00566                                         ChannelManager::showText(*context_, text);
00567                                 }
00568                         }
00569                 }
00570         }
00571 }

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