//////////////////////////////////////////////////////////////////////////////// // Scorched3D (c) 2000-2003 // // This file is part of Scorched3D. // // Scorched3D is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // Scorched3D is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Scorched3D; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA //////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include REGISTER_ACTION_SOURCE(TankDamage); TankDamage::TankDamage() : firstTime_(true) { } TankDamage::TankDamage(Weapon *weapon, unsigned int damagedPlayerId, unsigned int firedPlayerId, float damage, bool useShieldDamage, bool checkFall, bool shieldOnlyDamage, unsigned int data) : weapon_(weapon), firstTime_(true), damagedPlayerId_(damagedPlayerId), firedPlayerId_(firedPlayerId), damage_(damage), useShieldDamage_(useShieldDamage), checkFall_(checkFall), data_(data), shieldOnlyDamage_(shieldOnlyDamage) { } TankDamage::~TankDamage() { } void TankDamage::init() { } void TankDamage::simulate(float frameTime, bool &remove) { if (firstTime_) { firstTime_ = false; calculateDamage(); } remove = true; Action::simulate(frameTime, remove); } void TankDamage::calculateDamage() { // Find the tank that has been damaged Target *damagedTarget = context_->targetContainer->getTargetById(damagedPlayerId_); if (!damagedTarget || !damagedTarget->getAlive()) return; // Remove any damage from shield first if (damage_ > 0.0f) { float shieldDamage = 0.0f; Accessory *sh = damagedTarget->getShield().getCurrentShield(); if (sh && useShieldDamage_) { Shield *shield = (Shield *) sh->getAction(); float shieldPowerRequired = damage_ * shield->getHitPenetration(); float shieldPower = damagedTarget->getShield().getShieldPower(); if (shieldPower > shieldPowerRequired) { shieldPower -= shieldPowerRequired; damage_ = 0.0f; } else { float p = shieldPower / shield->getHitPenetration(); shieldPower = 0.0f; damage_ -= p; } damagedTarget->getShield().setShieldPower(shieldPower); } } // Remove the remaining damage from the tank if (damage_ > 0.0f && !shieldOnlyDamage_) { if (!context_->serverMode) { Vector position = damagedTarget->getTargetPosition(); position[0] += RAND * 5.0f - 2.5f; position[1] += RAND * 5.0f - 2.5f; position[2] += RAND * 5.0f - 2.5f; Vector redColor(0.75f, 0.0f, 0.0f); context_->actionController->addAction( new SpriteAction( new TextActionRenderer( position, redColor, formatString("%.0f", damage_)))); } // Remove the life if (damage_ > damagedTarget->getLife().getLife()) damage_ = damagedTarget->getLife().getLife(); damagedTarget->getLife().setLife(damagedTarget->getLife().getLife() - damage_); if (context_->optionsGame->getLimitPowerByHealth() && !damagedTarget->isTarget()) { Tank *damagedTank = (Tank *) damagedTarget; damagedTank->getPosition().changePower(0.0f, true); } // Check if the tank is dead bool killedTank = (damagedTarget->getLife().getLife() == 0.0f); // Add any score got from this endevour // Should always be a tank that has fired Tank *firedTank = context_->tankContainer->getTankById(firedPlayerId_); if (firedTank && !damagedTarget->isTarget()) { Tank *damagedTank = (Tank *) damagedTarget; // Add this tank as a tank that assisted in the kill damagedTank->getScore().getHurtBy().insert(firedTank->getPlayerId()); // Calculate team kills bool selfKill = (damagedPlayerId_ == firedPlayerId_); bool teamKill = ((context_->optionsGame->getTeams() > 1) && (firedTank->getTeam() == damagedTank->getTeam())); if (!killedTank) { // Calculate money won for not killing this tank int moneyPerHit = context_->optionsGame->getMoneyWonPerHitPoint() * weapon_->getArmsLevel(); if (context_->optionsGame->getMoneyPerHealthPoint()) moneyPerHit = (moneyPerHit * int(damage_)) / 100; if (selfKill || teamKill) moneyPerHit *= -1; firedTank->getScore().setMoney( firedTank->getScore().getMoney() + moneyPerHit); } else { int moneyPerKill = context_->optionsGame->getMoneyWonPerKillPoint() * weapon_->getArmsLevel(); if (!selfKill && !teamKill) { // Note this is done before turn kills is updated // so for the first kill turn kills will be 0 // i.e. no multikill bonus for 1st kill moneyPerKill += context_->optionsGame->getMoneyWonPerMultiKillPoint() * weapon_->getArmsLevel() * firedTank->getScore().getTurnKills(); } if (context_->optionsGame->getMoneyPerHealthPoint()) moneyPerKill = (moneyPerKill * int(damage_)) / 100; int scorePerKill = context_->optionsGame->getScorePerKill(); int moneyPerAssist = context_->optionsGame->getMoneyWonPerAssistPoint() * weapon_->getArmsLevel(); int scorePerAssist = context_->optionsGame->getScorePerAssist(); // Update kills and score if (selfKill || teamKill) { firedTank->getScore().setKills( firedTank->getScore().getKills() - 1); firedTank->getScore().setMoney( firedTank->getScore().getMoney() - moneyPerKill); firedTank->getScore().setScore( firedTank->getScore().getScore() - scorePerKill); if (firedTank->getTeam() > 0) { context_->tankTeamScore->addScore( -scorePerKill, firedTank->getTeam()); } } else { firedTank->getScore().setKills( firedTank->getScore().getKills() + 1); firedTank->getScore().setTurnKills( firedTank->getScore().getTurnKills() + 1); firedTank->getScore().setMoney( firedTank->getScore().getMoney() + moneyPerKill); firedTank->getScore().setScore( firedTank->getScore().getScore() + scorePerKill); if (firedTank->getTeam() > 0) { context_->tankTeamScore->addScore( scorePerKill, firedTank->getTeam()); } } // Update assists std::set &hurtBy = damagedTank->getScore().getHurtBy(); std::set::iterator itor; for (itor = hurtBy.begin(); itor != hurtBy.end(); itor++) { unsigned int hurtByPlayer = (*itor); Tank *hurtByTank = context_->tankContainer->getTankById(hurtByPlayer); if (!hurtByTank) continue; // Only score when the tank does not hurt itself if (hurtByTank == damagedTank) continue; // You don't get an assist for your kill if (hurtByTank == firedTank) continue; // or a team member if ((context_->optionsGame->getTeams() > 1) && (hurtByTank->getTeam() == damagedTank->getTeam())) continue; // Update assist score hurtByTank->getScore().setAssists( hurtByTank->getScore().getAssists() + 1); hurtByTank->getScore().setMoney( hurtByTank->getScore().getMoney() + moneyPerAssist); hurtByTank->getScore().setScore( hurtByTank->getScore().getScore() + scorePerAssist); if (hurtByTank->getTeam() > 0) { context_->tankTeamScore->addScore( scorePerAssist, hurtByTank->getTeam()); } } } } if (killedTank) { // The tank has died, make it blow up etc. calculateDeath(); if (!damagedTarget->isTarget()) { // The tank is now dead Tank *damagedTank = (Tank *) damagedTarget; damagedTank->getState().setState(TankState::sDead); // This tank has lost a life if (damagedTank->getState().getMaxLives() > 0) { damagedTank->getState().setLives( damagedTank->getState().getLives() - 1); } } } } // Tell this tanks ai that is has been hurt by another tank if (!damagedTarget->isTarget()) { Tank *damagedTank = (Tank *) damagedTarget; TankAI *ai = damagedTank->getTankAI(); if (ai) ai->tankHurt(weapon_, firedPlayerId_); } // Check if the tank needs to fall if (checkFall_ && damagedTarget->getAlive()) { // The tank is not dead check if it needs to fall Vector &position = damagedTarget->getTargetPosition(); if (context_->landscapeMaps->getGroundMaps(). getInterpHeight(position[0], position[1]) < position[2]) { // Check this tank is not already falling std::map::iterator findItor = TankFalling::fallingTanks.find(damagedPlayerId_); if (findItor == TankFalling::fallingTanks.end()) { Parachute *parachute = 0; Accessory *paraAccessory = damagedTarget->getParachute().getCurrentParachute(); if (paraAccessory) { parachute = (Parachute *) paraAccessory->getAction(); } // Tank falling context_->actionController->addAction( new TankFalling(weapon_, damagedPlayerId_, firedPlayerId_, parachute, data_)); } } } // Remove from groups context_->landscapeMaps->getGroundMaps().getObjects(). removeFromGroups(&damagedTarget->getGroup()); // DO LAST // If the tank is a target, remove the target if (!damagedTarget->getAlive() && damagedTarget->isTarget()) { Target *removedTarget = context_->targetContainer-> removeTarget(damagedTarget->getPlayerId()); delete removedTarget; } } void TankDamage::calculateDeath() { Target *killedTarget = context_->targetContainer->getTargetById(damagedPlayerId_); if (!killedTarget) return; // Log the death logDeath(); // Add the tank death explosion // Make the tank explode in one of many ways Weapon *weapon = killedTarget->getDeathAction(); if (weapon) { Vector position = killedTarget->getTargetPosition(); Vector velocity; weapon->fireWeapon(*context_, firedPlayerId_, position, velocity, Weapon::eDataDeathAnimation); StatsLogger::instance()->weaponFired(weapon, true); } } void TankDamage::logDeath() { Target *killedTarget = context_->targetContainer->getTargetById(damagedPlayerId_); if (killedTarget->isTarget()) return; // Print the banner on who killed who GLTexture *weaponTexture = 0; if (!context_->serverMode) { Accessory *accessory = context_->accessoryStore-> findByAccessoryId(weapon_->getParent()->getAccessoryId()); if (accessory) { weaponTexture = accessory->getTexture(); } } Tank *killedTank = (Tank *) killedTarget; Tank *firedTank = context_->tankContainer->getTankById(firedPlayerId_); if (firedTank) { if (!context_->serverMode || OptionsParam::instance()->getDedicatedServer()) { if (damagedPlayerId_ == firedPlayerId_) { StatsLogger::instance()-> tankSelfKilled(firedTank, weapon_); StatsLogger::instance()-> weaponKilled(weapon_, (data_ & Weapon::eDataDeathAnimation)); LoggerInfo info(LoggerInfo::TypeDeath, formatString("\"%s\" killed self with a %s", killedTank->getName(), weapon_->getParent()->getName())); info.setPlayerId(firedPlayerId_); info.setOtherPlayerId(damagedPlayerId_); info.setIcon(weaponTexture); Logger::log(info); } else if ((context_->optionsGame->getTeams() > 1) && (firedTank->getTeam() == killedTank->getTeam())) { StatsLogger::instance()-> tankTeamKilled(firedTank, killedTank, weapon_); StatsLogger::instance()-> weaponKilled(weapon_, (data_ & Weapon::eDataDeathAnimation)); LoggerInfo info(LoggerInfo::TypeDeath, formatString("\"%s\" team killed \"%s\" with a %s", firedTank->getName(), killedTank->getName(), weapon_->getParent()->getName())); info.setPlayerId(firedPlayerId_); info.setOtherPlayerId(damagedPlayerId_); info.setIcon(weaponTexture); Logger::log(info); } else { StatsLogger::instance()-> tankKilled(firedTank, killedTank, weapon_); StatsLogger::instance()-> weaponKilled(weapon_, (data_ & Weapon::eDataDeathAnimation)); LoggerInfo info(LoggerInfo::TypeDeath, formatString("\"%s\" %skilled \"%s\" with a %s", firedTank->getName(), ((firedTank->getScore().getTurnKills() > 1)?"multi-":""), killedTank->getName(), weapon_->getParent()->getName())); info.setPlayerId(firedPlayerId_); info.setOtherPlayerId(damagedPlayerId_); info.setIcon(weaponTexture); Logger::log(info); } } } else if (firedPlayerId_ == 0) { if (!context_->serverMode || OptionsParam::instance()->getDedicatedServer()) { static Tank firedTank(*context_, 0, 0, "Environment", Vector::nullVector, "", ""); firedTank.setUniqueId("Environment"); StatsLogger::instance()-> tankKilled(&firedTank, killedTank, weapon_); StatsLogger::instance()-> weaponKilled(weapon_, (data_ & Weapon::eDataDeathAnimation)); LoggerInfo info(LoggerInfo::TypeDeath, formatString("\"%s\" %skilled \"%s\" with a \"%s\"", firedTank.getName(), ((firedTank.getScore().getTurnKills() > 1)?"multi-":""), killedTank->getName(), weapon_->getParent()->getName())); info.setPlayerId(firedPlayerId_); info.setOtherPlayerId(damagedPlayerId_); info.setIcon(weaponTexture); Logger::log(info); } } } bool TankDamage::writeAction(NetBuffer &buffer) { buffer.addToBuffer(damagedPlayerId_); buffer.addToBuffer(firedPlayerId_); buffer.addToBuffer(damage_); buffer.addToBuffer(useShieldDamage_); buffer.addToBuffer(shieldOnlyDamage_); buffer.addToBuffer(checkFall_); buffer.addToBuffer(data_); context_->accessoryStore->writeWeapon(buffer, weapon_); return true; } bool TankDamage::readAction(NetBufferReader &reader) { if (!reader.getFromBuffer(damagedPlayerId_)) return false; if (!reader.getFromBuffer(firedPlayerId_)) return false; if (!reader.getFromBuffer(damage_)) return false; if (!reader.getFromBuffer(useShieldDamage_)) return false; if (!reader.getFromBuffer(shieldOnlyDamage_)) return false; if (!reader.getFromBuffer(checkFall_)) return false; if (!reader.getFromBuffer(data_)) return false; weapon_ = context_->accessoryStore->readWeapon(reader); if (!weapon_) return false; Target *damagedTarget = context_->targetContainer->getTargetById(damagedPlayerId_); if (damagedTarget) { if (damagedTarget->getAlive()) { const float ShowTime = 4.0f; ActionMeta *pos = new CameraPositionAction( damagedTarget->getTargetPosition(), ShowTime, 15); context_->actionController->getBuffer().clientAdd(-3.0f, pos); } } return true; }