00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
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
00103 Target *damagedTarget =
00104 context_->getTargetContainer().getTargetById(damagedPlayerId_);
00105 if (!damagedTarget || !damagedTarget->getAlive()) return;
00106
00107
00108 if (!damagedTarget->isTarget())
00109 {
00110
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
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
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
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
00203 bool killedTank = (damagedTarget->getLife().getLife() == 0);
00204
00205
00206
00207 Tank *firedTank =
00208 context_->getTankContainer().getTankById(firedPlayerId);
00209 if (firedTank && !damagedTarget->isTarget())
00210 {
00211 Tank *damagedTank = (Tank *) damagedTarget;
00212
00213
00214 damagedTank->getScore().getHurtBy().insert(firedTank->getPlayerId());
00215
00216
00217 bool selfKill = (damagedPlayerId_ == firedPlayerId);
00218 bool teamKill = ((context_->getOptionsGame().getTeams() > 1) &&
00219 (firedTank->getTeam() == damagedTank->getTeam()));
00220
00221 if (!killedTank)
00222 {
00223
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
00242
00243
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
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
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
00306 if (hurtByTank == damagedTank) continue;
00307
00308
00309 if (hurtByTank == firedTank) continue;
00310
00311
00312 if ((context_->getOptionsGame().getTeams() > 1) &&
00313 (hurtByTank->getTeam() == damagedTank->getTeam())) continue;
00314
00315
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
00335 calculateDeath();
00336
00337 if (!damagedTarget->isTarget())
00338 {
00339
00340 Tank *damagedTank = (Tank *) damagedTarget;
00341 damagedTank->getState().setState(TankState::sDead);
00342
00343
00344 if (damagedTank->getState().getMaxLives() > 0)
00345 {
00346 damagedTank->getState().setLives(
00347 damagedTank->getState().getLives() - 1);
00348 }
00349 }
00350 }
00351 }
00352
00353
00354 if (checkFall_ && damagedTarget->getAlive())
00355 {
00356
00357 FixedVector &position = damagedTarget->getLife().getTargetPosition();
00358 if (context_->getLandscapeMaps().getGroundMaps().
00359 getInterpHeight(position[0], position[1]) < position[2])
00360 {
00361
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
00373 context_->getActionController().addAction(
00374 new TankFalling(weapon_, damagedPlayerId_, weaponContext_,
00375 parachute));
00376 }
00377 }
00378 }
00379
00380
00381
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
00407 logDeath();
00408
00409
00410
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
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 }