/* * 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->actionCounter_ = 0; this->bInLoop_ = false; this->bLoop_ = false; this->bEndLoop_ = false; loopActionpoints_.clear(); parsedActionpoints_.clear(); actionpoints_.clear(); healthSpawners_.clear(); damageSpawners_.clear(); speedSpawners_.clear(); this->bTakenOver_ = false; this->action_ = Action::NONE; this->squaredaccuracy_ = 2500; this->bFirstTick_ = true; this->bStartedDodging_ = false; this->timeDodged_ = 0; 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(); healthSpawners_.clear(); damageSpawners_.clear(); speedSpawners_.clear(); } void ActionpointController::tick(float dt) { if (stop_) return; if (this->timeout_ > 0 && this->bFiredRocket_) { this->timeout_ -= dt; } if (this->bStartedDodging_) { this->timeDodged_ += dt; if (this->timeDodged_ > 2.0f) { this->bStartedDodging_ = false; this->timeDodged_ = 0; } } maneuverCounter_ += dt; if (maneuverCounter_ > 6.0f) maneuverCounter_ = 0; this->timeOffset_ += dt; if (this->timeOffset_ >= 2.0f) this->timeOffset_ = 0.0f; if (timeout_ <= 0) this->bFiredRocket_ = false; if (this->bHasTargetPosition_) { this->moveToTargetPosition(dt); } else if (this->bLookAtTarget_) { this->lookAtTarget(dt); } if (this->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->timeOffset_ = 0.0f; this->bActionCalled_ = false; setSpawners(); // orxout(internal_error) << "health spawners size = " << this->healthSpawners_.size() << // ", damage spawners size = " << this->damageSpawners_.size() << // ", speed spawners size = " << this->speedSpawners_.size() << endl; this->bFirstTick_ = false; } //maneuver every 0.25 sec -> float currentPeriodTime = this->timeOffset_ - static_cast(this->timeOffset_); if (this->hasTarget() && ((currentPeriodTime >= 0.25f && currentPeriodTime <= 0.5f) || (currentPeriodTime >= 0.75 && currentPeriodTime <= 0.999f)) && !this->bManeuverCalled_) { this->bManeuverCalled_ = true; this->maneuver(); this->bShooting_ = this->canFire(); } if ((currentPeriodTime >= 0.0f && currentPeriodTime <= 0.25f) || (currentPeriodTime >= 0.5f && currentPeriodTime <= 0.75f)) this->bManeuverCalled_ = false; 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(); } this->deltaHp = orxonox_cast (this->getControllableEntity())->getHealth() - this->previousHp; this->previousHp = orxonox_cast (this->getControllableEntity())->getHealth(); // if (this->actionCounter_ % 2 == 0) //No action -> pop one from stack if (this->action_ == Action::NONE || this->bTakenOver_) { // Point point = closestPickup(PickupType::HEALTH); // if (point.action != Action::NONE) // { // orxout(internal_error) << "found health" << endl; // this->parsedActionpoints_.push_back(point); // } 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(); } //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(); if (!(this->parsedActionpoints_.empty() && this->loopActionpoints_.empty())) { this->action(); } else { } } } } //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->action(); } } 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->action(); } } } } else if (this->action_ == Action::FLY) { if (this->squaredDistanceToTarget() <= this->squaredaccuracy_) { this->nextActionpoint(); this->action(); } } else if (this->action_ == Action::PROTECT) { if (!this->getProtect()) { this->nextActionpoint(); this->action(); } this->stayNearProtect(); } else if (this->action_ == Action::ATTACK) { if (!this->hasTarget()) { this->nextActionpoint(); this->action(); } } // Vector3 healthPosition = bestHealthPickup((this->target_->getWorldPosition() - this->getControllableEntity()->getWorldPosition()).length()); // if ((this->getControllableEntity()->getWorldPosition() - healthPosition).length() < this->tolerance_) // { // //----choose where to go---- // this->maneuver(); // } // else // { // this->dodgeTowards(healthPosition); // } // //----fire if you can---- // this->bShooting_ = this->canFire(); this->actionCounter_ += this->actionCounter_ < 100000 ? 1 : -this->actionCounter_ ; } 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"; 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() { 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 { 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); 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: { 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() { 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->loopActionpoints_.empty()) { this->moveBackToTop(); } } else { if (!this->parsedActionpoints_.empty()) { this->parsedActionpoints_.pop_back(); } } this->setAction(Action::NONE); this->bHasTargetPosition_ = false; } 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::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->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->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->bLoop_) { Point p = { Action::FIGHT, CommonController::getName(newTarget), Vector3::ZERO, true }; this->loopActionpoints_.push_back(p); } else { //orxout (internal_error) << "found new target " << CommonController::getName(newTarget) <parsedActionpoints_.push_back(p); } this->executeActionpoint(); } } } } Vector3 ActionpointController::bestHealthPickup(float searchRadius) { //1000000/distance^2 * 1/index+1 float maxUse = 0; float tempUse = -1; int index = 0; float distance = 0; Vector3 bestPosition = this->getControllableEntity()->getWorldPosition(); for (std::multimap >::iterator it=healthSpawners_.begin(); it!=healthSpawners_.end(); ++it) { distance = (this->getControllableEntity()->getWorldPosition() - (*it).second.first->getWorldPosition()).length(); if (distance < this->tolerance_ || !(*it).second.second) { (*it).second.second = false; continue; } if (distance > searchRadius) continue; index = (*it).first; tempUse = 1000000*1/(1+distance*distance) * 1/((index/2.0f)+1); if (tempUse > maxUse) { bestPosition = (*it).second.first->getWorldPosition(); maxUse = tempUse; } } //std::multimap >::iterator it = this->healthSpawners_.find(index); //P.S. check if it == ... .end() //orxout (internal_error) << "best position = " << bestPosition << endl; return bestPosition; } void ActionpointController::setSpawners() { std::vector damagePickups; std::vector healthPickups; std::vector speedPickups; int index = 0; damagePickups.push_back("largedamageboostpickup"); damagePickups.push_back("dronepickup"); damagePickups.push_back("mediumdamageboostpickup"); damagePickups.push_back("smalldamageboostpickup"); healthPickups.push_back("triplehealthspeedinvisibilitypickup"); healthPickups.push_back("crazyhealthpickup"); healthPickups.push_back("hugehealthpickup"); healthPickups.push_back("hugeshieldpickup"); healthPickups.push_back("hugeinvisiblepickup"); healthPickups.push_back("hugeshrinkpickup"); healthPickups.push_back("mediumhealthpickup"); healthPickups.push_back("mediumshieldpickup"); healthPickups.push_back("mediuminvisiblepickup"); healthPickups.push_back("mediumshrinkpickup"); healthPickups.push_back("smallhealthpickup"); healthPickups.push_back("smallshieldpickup"); healthPickups.push_back("smallinvisiblepickup"); healthPickups.push_back("smallshrinkpickup"); speedPickups.push_back("triplehealthspeedinvisibilitypickup"); speedPickups.push_back("hugespeedpickup"); speedPickups.push_back("smalljumppickup"); speedPickups.push_back("mediumspeedpickup"); speedPickups.push_back("smallspeedpickup"); for (ObjectList::iterator it = ObjectList::begin(); it; ++it) { if ((*it)->getIdentifier()->getName() != "PickupSpawner") continue; PickupSpawner* spawner = static_cast(*it); std::string name = spawner->getPickupTemplateName(); //float distance = ((*it)->getWorldPosition() - this->getControllableEntity()->getWorldPosition()).length(); // if (distance < 50.0f) // continue; for (unsigned int i = 0; i < healthPickups.size(); ++i) { if (name == healthPickups.at(i)) { index = i; this->healthSpawners_.insert(std::make_pair(index, std::make_pair(spawner, true))); break; } } for (unsigned int i = 0; i < damagePickups.size(); ++i) { if (name == damagePickups.at(i)) { //tempUse = 1000000*1/(1+distance*distance) * 1/((i/2.0f)+1); index = i; this->damageSpawners_.insert(std::make_pair(index, std::make_pair(spawner, true))); break; } } for (unsigned int i = 0; i < speedPickups.size(); ++i) { if (name == speedPickups.at(i)) { //tempUse = 1000000*1/(1+distance*distance) * 1/((i/2.0f)+1); index = i; this->speedSpawners_.insert(std::make_pair(index, std::make_pair(spawner, true))); break; } } // if (tempUse > maxUse) // { // Point p = { Action::FLY, "", (*it)->getWorldPosition(), false }; // maxUse = tempUse; // maxPoint = p; // } // else // { // tempUse = -1; // continue; // } // orxout(internal_error) << "resp time = " << static_cast(*it)->getRespawnTime() << endl; } } }