//////////////////////////////////////////////////////////////////////////////// // 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 TankAIComputer::TankAIComputer() : primaryShot_(true), availableForRandom_(true), availableForPlayers_(true), useShields_(true), useParachutes_(true), useBatteries_(true), useResign_(true), tankBuyer_(0) { } TankAIComputer::~TankAIComputer() { } TankAIComputerBuyer *TankAIComputer::getTankBuyer() { if (!tankBuyer_) { if (!tankBuyers_.empty()) { tankBuyer_ = &tankBuyers_[rand() % tankBuyers_.size()]; } else DIALOG_ASSERT(0); } return tankBuyer_; } void TankAIComputer::setTank(Tank *tank) { std::vector::iterator itor; for (itor = tankBuyers_.begin(); itor != tankBuyers_.end(); itor++) { (*itor).setTank(tank); } tankTarget_.setTank(tank); tankAim_.setTank(tank); TankAI::setTank(tank); } bool TankAIComputer::parseConfig(AccessoryStore *store, XMLNode *node) { // Name std::string name; if (!node->getNamedChild("name", name)) return false; // Description std::string description; if (!node->getNamedChild("description", description)) return false; description_.setText( name.c_str(), description.c_str()); node->getNamedChild("availableforplayers", availableForPlayers_, false); if (!store) return true; ////// Shallow parse // Defenses bool noDefenses; if (!node->getNamedChild("nodefenses", noDefenses)) return false; if (noDefenses) { useShields_ = false; useParachutes_ = false; useBatteries_ = false; } // No resign bool noResign; if (!node->getNamedChild("noresign", noResign)) return false; useResign_ = !noResign; // Available when random is chosen if (!node->getNamedChild("availableforrandom", availableForRandom_)) return false; if (!availableForPlayers_) availableForRandom_ = false; // Aiming info if (!tankAim_.parseConfig(node)) return false; // Weapons XMLNode *weaponsNode = 0; while (node->getNamedChild("weaponset", weaponsNode, false)) { TankAIComputerBuyer buyer; tankBuyers_.push_back(buyer); if (!tankBuyers_.back().parseConfig(*store, weaponsNode)) return false; } if (tankBuyers_.empty()) { dialogExit("TankAIComputer", "Must have at least one weapons set defined for an ai.\n" "(Even if it is empty)"); } // Targeting if (!tankTarget_.parseConfig(node)) return false; return node->failChildren(); } void TankAIComputer::newMatch() { //getTankBuyer()->dumpAccessories(); tankTarget_.newMatch(); } void TankAIComputer::newGame() { tankAim_.newGame(); } void TankAIComputer::tankHurt(Weapon *weapon, unsigned int firer) { tankTarget_.tankHurt(weapon, firer); if (currentTank_->getState().getState() == TankState::sDead) { const char *line = TankAIStrings::instance()->getDeathLine(); if (line) say(line); } } void TankAIComputer::shotLanded(ScorchedCollisionType action, ScorchedCollisionInfo *collision, Weapon *weapon, unsigned int firer, Vector &position, unsigned int landedCounter) { tankTarget_.shotLanded(action, collision, weapon, firer, position, landedCounter); if (primaryShot_ && firer == currentTank_->getPlayerId()) { Vector newPosition = position; if (action == CollisionWall) { // Pretend the shot went through the wall so // the ai gets the length it would have travelled and thus // knows to back of the power newPosition = (position - currentTank_->getPosition().getTankPosition()) / 4.0f + position; } tankAim_.ourShotLanded(weapon, newPosition); primaryShot_ = false; } } void TankAIComputer::autoDefense() { raiseDefenses(); } void TankAIComputer::buyAccessories() { getTankBuyer()->buyAccessories(10); ComsPlayedMoveMessage *message = new ComsPlayedMoveMessage(currentTank_->getPlayerId(), ComsPlayedMoveMessage::eFinishedBuy); ServerShotHolder::instance()->addShot(currentTank_->getPlayerId(), message); } void TankAIComputer::selectFirstShield() { if (currentTank_->getShield().getCurrentShield()) return; std::list &shields = currentTank_->getAccessories().getAllAccessoriesByType( AccessoryPart::AccessoryShield); if (!shields.empty()) { Accessory *shield = shields.front(); shieldsUpDown(shield->getAccessoryId()); } } void TankAIComputer::selectFirstParachute() { if (currentTank_->getParachute().getCurrentParachute()) return; std::list ¶chutes = currentTank_->getAccessories().getAllAccessoriesByType( AccessoryPart::AccessoryParachute); if (!parachutes.empty()) { Accessory *parachute = parachutes.front(); parachutesUpDown(parachute->getAccessoryId()); } } void TankAIComputer::raiseDefenses() { // Try to enable parachutes (fails if we don't have any) if (useParachutes_) selectFirstParachute(); // Try to raise shields (fails if we don't have any) if (useShields_) selectFirstShield(); } void TankAIComputer::say(const char *text) { std::string newText(currentTank_->getName()); newText += ": "; unsigned int infoLen = newText.length(); newText += text; ScorchedServer::instance()->getActionController().addAction( new TankSay(currentTank_->getPlayerId(), newText.c_str(), infoLen)); } void TankAIComputer::playMove(const unsigned state, float frameTime, char *buffer, unsigned int keyState) { // Play move is called when the computer opponent must make there move // Make sure defenses are raised (if we don't have an autodefense) raiseDefenses(); // Use batteries if we need to and have them while (useBatteries_ && currentTank_->getLife().getLife() < currentTank_->getLife().getMaxLife() && currentTank_->getAccessories().getBatteries().getNoBatteries() != 0) { std::list &entries = currentTank_->getAccessories().getAllAccessoriesByType( AccessoryPart::AccessoryBattery); if (!entries.empty()) { useBattery(entries.front()->getAccessoryId()); } } // Is there any point in making a move // Done after select weapons to allow batteries to be used if (useResign_ && currentTank_->getLife().getLife() < 10) { resign(); return; } Tank *target = tankTarget_.findTankToShootAt(); if (target) { int noShots; float distance; bool sniper = false; TankAIComputerAim::AimResult aimResult = tankAim_.aimAtTank(target, getTankBuyer(), distance, noShots, sniper); if (aimResult == TankAIComputerAim::AimOk) { std::vector weapons; if (sniper) { weapons = getTankBuyer()->getWeaponType("sniper"); } if (weapons.empty()) { if (distance < 15.0f && noShots > 1) { weapons = getTankBuyer()->getWeaponType("explosionlarge"); } } if (weapons.empty()) { weapons = getTankBuyer()->getWeaponType("explosionsmall"); } if (!weapons.empty()) { int pos = rand() % weapons.size(); Accessory *accessory = weapons[pos]; currentTank_->getAccessories().getWeapons().setWeapon(accessory); } if (currentTank_->getAccessories().getWeapons().getCurrent()) { fireShot(); return; } } else if (aimResult == TankAIComputerAim::AimBurried) { std::vector digs = getTankBuyer()->getWeaponType("dig"); if (!digs.empty()) { int pos = rand() % digs.size(); Accessory *accessory = digs[pos]; currentTank_->getAccessories().getWeapons().setWeapon(accessory); fireShot(); return; } } } skipShot(); } void TankAIComputer::fireShot() { primaryShot_ = true; Accessory *currentWeapon = currentTank_->getAccessories().getWeapons().getCurrent(); if (currentWeapon) { ComsPlayedMoveMessage *message = new ComsPlayedMoveMessage(currentTank_->getPlayerId(), ComsPlayedMoveMessage::eShot); message->setShot( currentWeapon->getAccessoryId(), currentTank_->getPosition().getRotationGunXY(), currentTank_->getPosition().getRotationGunYZ(), currentTank_->getPosition().getPower(), currentTank_->getPosition().getSelectPositionX(), currentTank_->getPosition().getSelectPositionY()); ServerShotHolder::instance()->addShot(currentTank_->getPlayerId(), message); } } void TankAIComputer::skipShot() { ComsPlayedMoveMessage *message = new ComsPlayedMoveMessage(currentTank_->getPlayerId(), ComsPlayedMoveMessage::eSkip); ServerShotHolder::instance()->addShot(currentTank_->getPlayerId(), message); } void TankAIComputer::resign() { ComsPlayedMoveMessage *message = new ComsPlayedMoveMessage(currentTank_->getPlayerId(), ComsPlayedMoveMessage::eResign); ServerShotHolder::instance()->addShot(currentTank_->getPlayerId(), message); } void TankAIComputer::parachutesUpDown(unsigned int paraId) { ComsDefenseMessage defenseMessage( currentTank_->getPlayerId(), (paraId!=0)?ComsDefenseMessage::eParachutesUp:ComsDefenseMessage::eParachutesDown, paraId); ServerDefenseHandler::instance()->processDefenseMessage(defenseMessage, currentTank_); } void TankAIComputer::shieldsUpDown(unsigned int shieldId) { ComsDefenseMessage defenseMessage( currentTank_->getPlayerId(), (shieldId!=0)?ComsDefenseMessage::eShieldUp:ComsDefenseMessage::eShieldDown, shieldId); ServerDefenseHandler::instance()->processDefenseMessage(defenseMessage, currentTank_); } void TankAIComputer::useBattery(unsigned int batteryId) { ComsDefenseMessage defenseMessage( currentTank_->getPlayerId(), ComsDefenseMessage::eBatteryUse, batteryId); ServerDefenseHandler::instance()->processDefenseMessage(defenseMessage, currentTank_); }