/* * ORXONOX - the hottest 3D action shooter ever to exist * > www.orxonox.net < * * * License notice: * * This program 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. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * Author: * Fabian 'x3n' Landau * Co-authors: * Dominik Solenicki * */ #include "ActionpointController.h" #include "core/XMLPort.h" #include "controllers/NewHumanController.h" namespace orxonox { RegisterClass(ActionpointController); //CommonController contains all common functionality of AI Controllers ActionpointController::ActionpointController(Context* context) : FightingController(context) { this->bInLoop_ = false; this->bLoop_ = false; this->bEndLoop_ = false; this->parsedActionpoints_.clear(); this->bTakenOver_ = false; this->action_ = Action::NONE; this->squaredaccuracy_ = 2500; RegisterObject(ActionpointController); } void ActionpointController::XMLPort( Element& xmlelement, XMLPort::Mode mode ) { SUPER( ActionpointController, XMLPort, xmlelement, mode ); XMLPortObject(ActionpointController, WorldEntity, "actionpoints", addActionpoint, getActionpoint, xmlelement, mode); } void ActionpointController::tick(float dt) { if (this->bHasTargetPosition_) { this->moveToTargetPosition(dt); } else if (this->bLookAtTarget_) { this->lookAtTarget(dt); } if (bShooting_) { this->doFire(); } if (this->bFirstTick_) { std::reverse(parsedActionpoints_.begin(), parsedActionpoints_.end()); std::reverse(actionpoints_.begin(), actionpoints_.end()); if (this->parsedActionpoints_.empty()) { this->action_ = Action::FIGHTALL; } } if (this->bFirstTick_) this->bFirstTick_ = false; SUPER(ActionpointController, tick, dt); } ActionpointController::~ActionpointController() { loopActionpoints_.clear(); parsedActionpoints_.clear(); actionpoints_.clear(); } void ActionpointController::setProtect (ControllableEntity* protect) { this->protect_ = protect; } ControllableEntity* ActionpointController::getProtect () { return this->protect_; } void ActionpointController::addActionpoint(WorldEntity* actionpoint) { std::string actionName; Vector3 position; std::string targetName; bool inLoop = false; Point p; if (static_cast (actionpoint)) { Actionpoint* ap = static_cast (actionpoint); actionName = ap->getActionXML(); targetName = ap->getName(); position = ap->getWorldPosition(); if (this->bEndLoop_) { this->bInLoop_ = false; } if (!this->bInLoop_ && ap->getLoopStart()) { this->bInLoop_ = true; } if (this->bInLoop_ && ap->getLoopEnd()) { this->bEndLoop_ = true; } inLoop = this->bInLoop_; Action::Value value; if ( actionName == "FIGHT" ) { value = Action::FIGHT; } else if ( actionName == "FLY" ) { value = Action::FLY; } else if ( actionName == "PROTECT" ) { value = Action::PROTECT; } else if ( actionName == "NONE" ) { value = Action::NONE; } else if ( actionName == "FIGHTALL" ) { value = Action::FIGHTALL; } else if ( actionName == "ATTACK" ) { value = Action::ATTACK; } else ThrowException( ParseError, std::string( "Attempting to set an unknown Action: '" )+ actionName + "'." ); p.action = value; p.name = targetName; p.position = position; p.inLoop = inLoop; } else { p.action = Action::FLY; p.name = ""; p.position = actionpoint->getWorldPosition(); p.inLoop = inLoop; } parsedActionpoints_.push_back(p); this->actionpoints_.push_back(actionpoint); } WorldEntity* ActionpointController::getActionpoint(unsigned int index) const { if (index < this->actionpoints_.size()) return this->actionpoints_[index]; else return 0; } Action::Value ActionpointController::getAction () { return this->action_; } std::string ActionpointController::getActionName() { switch ( this->action_ ) { case Action::FIGHT: { return "FIGHT"; break; } case Action::FLY: { return "FLY"; break; } case Action::PROTECT: { return "PROTECT"; break; } case Action::NONE: { return "NONE"; break; } case Action::FIGHTALL: { return "FIGHTALL"; break; } case Action::ATTACK: { return "ATTACK"; break; } default: return "NONE"; break; } } void ActionpointController::setAction (Action::Value action) { this->action_ = action; } void ActionpointController::setAction (Action::Value action, ControllableEntity* target) { this->action_ = action; if (action == Action::FIGHT || action == Action::FIGHTALL || action == Action::ATTACK) { if (target) this->setTarget (target); } else if (action == Action::PROTECT) { if (target) this->setProtect (target); } } void ActionpointController::setAction (Action::Value action, const Vector3& target) { this->action_ = action; if (action == Action::FLY) { this->setTargetPosition (target); } } void ActionpointController::setAction (Action::Value action, const Vector3& target, const Quaternion& orient ) { this->action_ = action; if (action == Action::FLY) { this->setTargetPosition (target); this->setTargetOrientation (orient); } } //------------------------------------------------------------------------------ //------------------------------Actionpoint methods----------------------------- //------------------------------------------------------------------------------ //POST: this starts doing what was asked by the last element of parsedActionpoints_, //if last element was failed to be parsed, next element will be executed. void ActionpointController::executeActionpoint() { if (this->bLoop_) { if (!this->loopActionpoints_.empty()) { this->action_ = this->loopActionpoints_.back().action; switch ( this->action_ ) { case Action::FIGHT: { std::string targetName = this->loopActionpoints_.back().name; if (targetName == "") { break; } for (ObjectList::iterator itP = ObjectList::begin(); itP; ++itP) { if (CommonController::getName(*itP) == targetName) { this->setTarget (static_cast(*itP)); } } break; } case Action::FLY: { this->setTargetPosition( this->loopActionpoints_.back().position ); if (this->squaredDistanceToTarget() <= this->squaredaccuracy_) { this->nextActionpoint(); this->executeActionpoint(); } break; } case Action::PROTECT: { std::string protectName = this->loopActionpoints_.back().name; if (protectName == "reservedKeyword:human") { for (ObjectList::iterator itP = ObjectList::begin(); itP; ++itP) { if (orxonox_cast(*itP) && ((*itP)->getController()) && orxonox_cast ((*itP)->getController())) { this->setProtect (static_cast(*itP)); } } } else { for (ObjectList::iterator itP = ObjectList::begin(); itP; ++itP) { if (CommonController::getName(*itP) == protectName) { this->setProtect (static_cast(*itP)); } } } if (!this->getProtect()) { this->nextActionpoint(); this->executeActionpoint(); } break; } case Action::NONE: { break; } case Action::FIGHTALL: { break; } case Action::ATTACK: { std::string targetName = this->loopActionpoints_.back().name; for (ObjectList::iterator itP = ObjectList::begin(); itP; ++itP) { if (CommonController::getName(*itP) == targetName) { this->setTarget (static_cast(*itP)); } } if (!this->hasTarget()) { this->nextActionpoint(); this->executeActionpoint(); } break; } default: { break; } } } else { this->bLoop_ = false; } } else { if (!this->parsedActionpoints_.empty()) { if (this->parsedActionpoints_.back().inLoop) { //MOVES all points that are in loop to a loop vector this->fillLoop(); this->bLoop_ = true; executeActionpoint(); return; } this->action_ = this->parsedActionpoints_.back().action; switch ( this->action_ ) { case Action::FIGHT: { std::string targetName = this->parsedActionpoints_.back().name; if (targetName == "") { break; } for (ObjectList::iterator itP = ObjectList::begin(); itP; ++itP) { if (CommonController::getName(*itP) == targetName) { this->setTarget (static_cast(*itP)); } } break; } case Action::FLY: { this->setTargetPosition( this->parsedActionpoints_.back().position ); if (this->squaredDistanceToTarget() <= this->squaredaccuracy_) { this->nextActionpoint(); this->executeActionpoint(); } break; } case Action::PROTECT: { std::string protectName = this->parsedActionpoints_.back().name; if (protectName == "reservedKeyword:human") { for (ObjectList::iterator itP = ObjectList::begin(); itP; ++itP) { if (orxonox_cast(*itP) && ((*itP)->getController()) && orxonox_cast ((*itP)->getController())) { this->setProtect (static_cast(*itP)); } } } else { for (ObjectList::iterator itP = ObjectList::begin(); itP; ++itP) { if (CommonController::getName(*itP) == protectName) { this->setProtect (static_cast(*itP)); } } } if (!this->getProtect()) { this->nextActionpoint(); this->executeActionpoint(); } break; } case Action::NONE: { break; } case Action::FIGHTALL: { break; } case Action::ATTACK: { std::string targetName = this->parsedActionpoints_.back().name; for (ObjectList::iterator itP = ObjectList::begin(); itP; ++itP) { if (CommonController::getName(*itP) == targetName) { this->setTarget (static_cast(*itP)); } } if (!this->hasTarget()) { this->nextActionpoint(); this->executeActionpoint(); } break; } default: break; } } else { this->setTarget(0); this->setTargetPosition(this->getControllableEntity()->getWorldPosition()); this->action_ = Action::NONE; } } } void ActionpointController::stayNearProtect() { Vector3* targetRelativePosition; targetRelativePosition = new Vector3 (0, 300, 300); Vector3 targetAbsolutePosition = ((this->getProtect()->getWorldPosition()) + (this->getProtect()->getWorldOrientation()* (*targetRelativePosition))); this->setTargetPosition(targetAbsolutePosition); } void ActionpointController::nextActionpoint() { if (!this || !this->getControllableEntity()) return; if (this->bLoop_) { if (!this->loopActionpoints_.empty()) { this->moveBackToTop(); } } else { if (!this->parsedActionpoints_.empty()) { this->parsedActionpoints_.pop_back(); } } this->setAction(Action::NONE); } void ActionpointController::moveBackToTop() { Point temp = loopActionpoints_.back(); loopActionpoints_.pop_back(); std::reverse (loopActionpoints_.begin(), loopActionpoints_.end()); loopActionpoints_.push_back(temp); std::reverse (loopActionpoints_.begin(), loopActionpoints_.end()); } void ActionpointController::fillLoop() { loopActionpoints_.clear(); fillLoopReversed(); std::reverse (loopActionpoints_.begin(), loopActionpoints_.end()); } void ActionpointController::fillLoopReversed() { if (parsedActionpoints_.back().inLoop) { loopActionpoints_.push_back(parsedActionpoints_.back()); parsedActionpoints_.pop_back(); } if (parsedActionpoints_.back().inLoop) { fillLoopReversed(); } } void ActionpointController::action() { if (!this || !this->getControllableEntity()) return; // orxout (internal_error) << "Size of actions is " << this->parsedActionpoints_.size() << endl; this->startAttackingEnemiesThatAreClose(); //No action -> pop one from stack if (this->action_ == Action::NONE || this->bTakenOver_) { if (this->parsedActionpoints_.empty() && this->loopActionpoints_.empty()) { Point p = { Action::FIGHTALL, "", Vector3::ZERO, false }; this->parsedActionpoints_.push_back (p); } this->executeActionpoint(); this->bTakenOver_ = false; } //Action fightall -> fight till nobody alive if (this->action_ == Action::FIGHTALL) { if (!this->hasTarget()) { //----find a target---- ControllableEntity* newTarget = this->closestTarget(); if (newTarget) { this->setAction (Action::FIGHTALL, newTarget); } else { this->nextActionpoint(); return; } } else if (this->hasTarget()) { } } //Action fight -> fight as long as enemies in range else if (this->action_ == Action::FIGHT) { if (!this->hasTarget()) { //----find a target---- ControllableEntity* newTarget = this->closestTarget(); if (newTarget && CommonController::distance (this->getControllableEntity(), newTarget) < this->attackRange_) { this->setAction (Action::FIGHT, newTarget); } else { this->nextActionpoint(); return; } } else if (this->hasTarget()) { //----fly in formation if far enough---- Vector3 diffVector = this->positionOfTarget_ - this->getControllableEntity()->getWorldPosition(); if (diffVector.length() > this->attackRange_) { ControllableEntity* newTarget = this->closestTarget(); if (newTarget && CommonController::distance (this->getControllableEntity(), newTarget) < this->attackRange_) { this->setAction (Action::FIGHT, newTarget); } else { this->nextActionpoint(); return; } } } } else if (this->action_ == Action::FLY) { if (this->squaredDistanceToTarget() <= this->squaredaccuracy_) { this->nextActionpoint(); return; } } else if (this->action_ == Action::PROTECT) { if (!this->getProtect()) { this->nextActionpoint(); return; } this->stayNearProtect(); } else if (this->action_ == Action::ATTACK) { if (!this->hasTarget()) { this->nextActionpoint(); return; } } if (this->hasTarget()) { //----choose where to go---- this->maneuver(); //----fire if you can---- this->bShooting_ = this->canFire(); } } void ActionpointController::takeActionpoints (std::vector vector, std::vector loop, bool b) { this->parsedActionpoints_ = vector; this->loopActionpoints_ = loop; this->bLoop_ = this->bLoop_; this->bTakenOver_ = true; // orxout(internal_error) << "Top action is " << this->parsedActionpoints_.back().action << endl; } void ActionpointController::setClosestTarget() { this->setTarget (static_cast( closestTarget() ) ); } Pawn* ActionpointController::closestTarget() { if (!this->getControllableEntity()) return 0; Pawn* closestTarget = 0; float minDistance = std::numeric_limits::infinity(); Gametype* gt = this->getGametype(); for (ObjectList::iterator itP = ObjectList::begin(); itP; ++itP) { if ( CommonController::sameTeam (this->getControllableEntity(), static_cast(*itP), gt) ) continue; float distance = CommonController::distance (*itP, this->getControllableEntity()); if (distance < minDistance) { closestTarget = *itP; minDistance = distance; } } if (closestTarget) { return closestTarget; } return 0; } void ActionpointController::startAttackingEnemiesThatAreClose() { if (this->action_ != Action::FIGHT && this->action_ != Action::FIGHTALL) { if ( (this->target_ && this->distance (this->getControllableEntity(), this->target_) > this->attackRange_) || !this->target_ ) { Pawn* newTarget = this->closestTarget(); if ( newTarget && this->distance (this->getControllableEntity(), static_cast(newTarget)) <= this->attackRange_ ) { Point p = { Action::FIGHT, this->getName(newTarget), Vector3::ZERO, false }; this->parsedActionpoints_.push_back(p); this->executeActionpoint(); } } } } }