/* * 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: * Gani Aliguzhinov * Co-authors: * ... * */ #include "ActionpointController.h" #include "core/XMLPort.h" #include #include "worldentities/Actionpoint.h" namespace orxonox { RegisterClass(ActionpointController); ActionpointController::ActionpointController(Context* context) : FightingController(context) { this->actionpointControllerId_ = 0; ActionpointController::sTicks_ = 0; ActionpointController::nextActionpointControllerId_ = 0; this->ticks_ = 0; this->bPatrolling_ = false; this->bInLoop_ = false; this->bLoop_ = false; this->bEndLoop_ = false; loopActionpoints_.clear(); parsedActionpoints_.clear(); actionpoints_.clear(); this->bTakenOver_ = false; this->action_ = Action::NONE; this->squaredaccuracy_ = 2500; this->bFirstTick_ = true; this->bStartedDodging_ = false; this->bDefaultPatrol_ = true; this->bDefaultFightAll_ = true; this->stop_ = false; RegisterObject(ActionpointController); } void ActionpointController::XMLPort( Element& xmlelement, XMLPort::Mode mode ) { SUPER( ActionpointController, XMLPort, xmlelement, mode ); // XMLPortEventSink(ActionpointController, BaseObject, "stop", stop, xmlelement, mode); // XMLPortEventSink(ActionpointController, BaseObject, "go", stop, xmlelement, mode); XMLPortObject(ActionpointController, WorldEntity, "actionpoints", addActionpoint, getActionpoint, xmlelement, mode); XMLPortParam(ActionpointController, "defaultFightAll", setDefaultFightAll, getDefaultFightAll, xmlelement, mode).defaultValues(true); XMLPortParam(ActionpointController, "defaultPatrol", setDefaultPatrol, getDefaultPatrol, xmlelement, mode).defaultValues(true); } // void ActionpointController::XMLEventPort(Element& xmlelement, XMLPort::Mode mode) // { // SUPER(ActionpointController, XMLEventPort, xmlelement, mode); // XMLPortEventSink(ActionpointController, BaseObject, "stop", stop, xmlelement, mode); // XMLPortEventSink(ActionpointController, BaseObject, "go", stop, xmlelement, mode); // } // bool ActionpointController::stop(bool bTriggered, BaseObject* trigger) // { // this->stop_ = true; // return true; // } // bool ActionpointController::go(bool bTriggered, BaseObject* trigger) // { // this->stop_ = false; // return true; // } ActionpointController::~ActionpointController() { loopActionpoints_.clear(); parsedActionpoints_.clear(); actionpoints_.clear(); this->actionpointControllerId_ = 0; /*ActionpointController::nextActionpointControllerId_--; if (ActionpointController::nextActionpointControllerId_ < 0) ActionpointController::nextActionpointControllerId_ = 0;*/ } void ActionpointController::tick(float dt) { if (!this || !this->getControllableEntity() || !this->isActive()) return; ++this->ticks_; // orxout (internal_error) << "this id = " << this->actionpointControllerId_ << endl; if (ActionpointController::sTicks_ < this->ticks_) { // orxout (internal_error) << "total id's = " << ActionpointController::nextActionpointControllerId_ << endl; ++ActionpointController::sTicks_; } if (!this || !this->getControllableEntity()) return; if (ActionpointController::sTicks_ == 1) { this->actionpointControllerId_ = ActionpointController::nextActionpointControllerId_++; std::reverse(parsedActionpoints_.begin(), parsedActionpoints_.end()); std::reverse(actionpoints_.begin(), actionpoints_.end()); if (this->parsedActionpoints_.empty()) { this->action_ = Action::FIGHTALL; } //orxout (internal_error) << "first tick was called by id = " << this->actionpointControllerId_ << ", total = " << ActionpointController::nextActionpointControllerId_ << endl; } if (ActionpointController::sTicks_ == 1) { this->bActionCalled_ = false; this->bFirstTick_ = false; } /* if (ActionpointController::sTicks_ % (ActionpointController::nextActionpointControllerId_+1) != this->actionpointControllerId_) { return; }*/ if (!this || !this->getControllableEntity()) return; if (this->bHasTargetPosition_) { this->moveToTargetPosition(dt); } else if (this->bLookAtTarget_) { this->lookAtTarget(dt); } if (!this || !this->getControllableEntity()) return; if (stop_) return; if (timeout_ <= 0) this->bFiredRocket_ = false; if (!this || !this->getControllableEntity()) return; if (this->timeout_ > 0 && this->bFiredRocket_) { --this->timeout_; } if (!this || !this->getControllableEntity()) return; //maneuver every 0.25 sec -> int step = 4; if (ActionpointController::sTicks_ % 100 <= 10) { this->bDodge_ = false; } else { this->bDodge_ = true; } if (ActionpointController::sTicks_ % 100 == step * this->actionpointControllerId_ + 1) { //orxout (internal_error) << "Team " << this->getControllableEntity()->getTeam() << (this->hasTarget() ? ", got " : ", don't have") << " target" << endl; this->action(); } if (!this || !this->getControllableEntity()) return; /* orxout (internal_error) << "id = " << this->actionpointControllerId_ << ", in total # ids = " << ActionpointController::nextActionpointControllerId_ << ", I " << (this->hasTarget() ? "have" : "don't have") << " a target, my team is " << this->getControllableEntity()->getTeam() << endl; */ if (this->hasTarget() && ActionpointController::sTicks_ % (ActionpointController::nextActionpointControllerId_ + 1) == (this->actionpointControllerId_)) { if (!this || !this->getControllableEntity()) return; this->maneuver(); if (!this || !this->getControllableEntity()) return; this->bShooting_ = this->canFire(); if (!this || !this->getControllableEntity()) return; if (this->bShooting_) { this->doFire(); } this->deltaHp = orxonox_cast (this->getControllableEntity())->getHealth() - this->previousHp; this->previousHp = orxonox_cast (this->getControllableEntity())->getHealth(); } SUPER(ActionpointController, tick, dt); } void ActionpointController::action() { if (!this || !this->getControllableEntity()) return; if (!this->getControllableEntity() || !orxonox_cast (this->getControllableEntity())) return; if (this->bDefaultPatrol_ || (this->action_ != Action::FLY && this->action_ != Action::NONE)) { this->startAttackingEnemiesThatAreClose(); } if (!this || !this->getControllableEntity()) return; if (!this || !this->getControllableEntity()) return; // if (this->actionCounter_ % 2 == 0) //No action -> pop one from stack if (this->action_ == Action::NONE || this->bTakenOver_) { if (this->parsedActionpoints_.empty() && this->loopActionpoints_.empty() && this->bDefaultFightAll_) { Point p = { Action::FIGHTALL, "", Vector3::ZERO, false }; this->parsedActionpoints_.push_back (p); } this->executeActionpoint(); this->bTakenOver_ = false; // this->action(); } if (!this || !this->getControllableEntity()) return; //Action fightall -> fight till nobody alive if (this->action_ == Action::FIGHTALL) { if (!this->hasTarget()) { ControllableEntity* newTarget = this->closestTarget(); if (newTarget) { this->setAction (Action::FIGHTALL, newTarget); //this->action(); } else { this->nextActionpoint(); this->executeActionpoint(); } } } //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); //this->action(); } else { this->nextActionpoint(); this->executeActionpoint(); } } 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(); this->executeActionpoint(); } } } } else if (this->action_ == Action::FLY) { if (this->squaredDistanceToTarget() <= this->squaredaccuracy_) { this->nextActionpoint(); this->executeActionpoint(); } } else if (this->action_ == Action::PROTECT) { if (!this->getProtect()) { this->nextActionpoint(); this->executeActionpoint(); } this->stayNearProtect(); } else if (this->action_ == Action::ATTACK) { if (!this->hasTarget()) { this->nextActionpoint(); this->executeActionpoint(); } } } 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 (actionpoint->getIdentifier()->getName() == "Actionpoint") { Actionpoint* ap = orxonox_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 { inLoop = true; 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"; } case Action::FLY: { return "FLY"; } case Action::PROTECT: { return "PROTECT"; } case Action::NONE: { return "NONE"; } case Action::FIGHTALL: { return "FIGHTALL"; } case Action::ATTACK: { return "ATTACK"; } 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 || !this->getControllableEntity()) return; Point p; if (this->bLoop_ && !loopActionpoints_.empty()) { p = loopActionpoints_.back(); } else if (this->bLoop_) { this->bLoop_ = false; return; } else if (!this->bLoop_ && !parsedActionpoints_.empty()) { p = parsedActionpoints_.back(); } else { if (!this || !this->getControllableEntity()) return; this->setTarget(0); this->setTargetPosition(this->getControllableEntity()->getWorldPosition()); this->action_ = Action::NONE; return; } if (!this->bLoop_ && this->parsedActionpoints_.back().inLoop) { //MOVES all points that are in loop to a loop vector this->fillLoop(); this->bLoop_ = true; executeActionpoint(); return; } this->setAction (p.action); if (!this || !this->getControllableEntity()) return; switch (this->action_) { case Action::FIGHT: { std::string targetName = p.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( p.position ); if (this->squaredDistanceToTarget() <= this->squaredaccuracy_) { this->nextActionpoint(); this->executeActionpoint(); } break; } case Action::PROTECT: { if (!this || !this->getControllableEntity()) return; std::string protectName = p.name; if (protectName == "reservedKeyword:human") { for (ObjectList::iterator itP = ObjectList::begin(); itP; ++itP) { if (orxonox_cast(*itP) && ((*itP)->getController()) && ((*itP)->getController()->getIdentifier()->getName() == "NewHumanController")) { 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 = p.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; } } void ActionpointController::stayNearProtect() { if (!this || !this->getControllableEntity()) return; Vector3 targetRelativePosition(0, 300, 300); if (!this->getProtect()) { this->nextActionpoint(); return; } Vector3 targetAbsolutePosition = ((this->getProtect()->getWorldPosition()) + (this->getProtect()->getWorldOrientation()* (targetRelativePosition))); this->setTargetPosition(targetAbsolutePosition); if (!this->getProtect()) { this->nextActionpoint(); return; } this->setTargetOrientation(this->getProtect()->getWorldOrientation()); } void ActionpointController::nextActionpoint() { if (!this || !this->getControllableEntity()) return; if (this->bLoop_) { if (this->bPatrolling_) { this->loopActionpoints_.pop_back(); this->bPatrolling_ = false; } else if (!this->loopActionpoints_.empty()) { this->moveBackToTop(); } } else { if (!this->parsedActionpoints_.empty()) { this->parsedActionpoints_.pop_back(); } } this->setAction(Action::NONE); this->bHasTargetPosition_ = false; } void ActionpointController::moveBackToTop() { if (!this || !this->getControllableEntity()) return; 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::takeActionpoints (const std::vector& vector, const std::vector& loop, bool b) { this->parsedActionpoints_ = vector; this->loopActionpoints_ = loop; this->bLoop_ = b; this->bTakenOver_ = true; } void ActionpointController::setClosestTarget() { this->setTarget (static_cast( closestTarget() ) ); } Pawn* ActionpointController::closestTarget() { if (!this || !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 || !this->getControllableEntity()) return; //if (this->action_ != Action::FIGHT && this->action_ != Action::FIGHTALL) { if (!this->target_ || (this->target_ && CommonController::distance (this->getControllableEntity(), this->target_) > this->attackRange_)) { Pawn* newTarget = this->closestTarget(); if ( newTarget && CommonController::distance (this->getControllableEntity(), static_cast(newTarget)) <= this->attackRange_ ) { if (!this || !this->getControllableEntity()) return; this->setTarget(newTarget); if (this->bLoop_ && !this->bPatrolling_) { Point p = { Action::FIGHT, "", Vector3::ZERO, true }; this->loopActionpoints_.push_back(p); } else if (!this->bPatrolling_) { //orxout (internal_error) << "found new target " << CommonController::getName(newTarget) <parsedActionpoints_.push_back(p); } this->bPatrolling_ = true; this->executeActionpoint(); } } } } int ActionpointController::nextActionpointControllerId_ = 0; int ActionpointController::sTicks_ = 0; }