/* * 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 "controllers/CommonController.h" #include "core/XMLPort.h" #include "weaponsystem/WeaponMode.h" #include "weaponsystem/WeaponPack.h" #include "weaponsystem/Weapon.h" #include "weaponsystem/WeaponSlot.h" #include "weaponsystem/WeaponSlot.h" #include "worldentities/pawns/SpaceShip.h" namespace orxonox { RegisterClass(CommonController); float SPEED = 0.7f; float ROTATEFACTOR = 0.3f; CommonController::CommonController(Context* context) : Controller(context) { this->bSetupWorked = false; RegisterObject(CommonController); } CommonController::~CommonController() { } void CommonController::XMLPort(Element& xmlelement, XMLPort::Mode mode) { SUPER(CommonController, XMLPort, xmlelement, mode); XMLPortParam(CommonController, "formationMode", setFormationModeXML, getFormationModeXML, xmlelement, mode); } void CommonController::setFormationModeXML(std::string val) { const std::string valUpper = getUppercase(val); FormationMode::Value value; if (valUpper == "VEE") value = FormationMode::VEE; else if (valUpper == "WALL") value = FormationMode::WALL; else if (valUpper == "FINGER4") value = FormationMode::FINGER4; else if (valUpper == "DIAMOND") value = FormationMode::DIAMOND; else ThrowException(ParseError, std::string("Attempting to set an unknown FormationMode: '") + val + "'."); this->setFormationMode(value); } std::string CommonController::getFormationModeXML() { switch (this->formationMode_) { case FormationMode::VEE: { return "VEE"; break; } case FormationMode::WALL: { return "WALL"; break; } case FormationMode::FINGER4: { return "FINGER4"; break; } case FormationMode::DIAMOND: { return "DIAMOND"; break; } default: return "DIAMOND"; break; } } bool CommonController::setWingman (CommonController* wingman) { return false; } bool CommonController::hasWingman() { return true; } void CommonController::setTarget(ControllableEntity* target) { this->target_ = target; orxout (internal_error) << " TARGET SET " << endl; if (target) this->targetPosition_ = target->getPosition(); } void CommonController::setTargetPosition(const Vector3& target) { this->targetPosition_ = target; this->bHasTargetPosition_ = true; } void CommonController::setTargetOrientation(const Quaternion& orient) { this->targetOrientation_=orient; this->bHasTargetOrientation_=true; } void CommonController::setTargetOrientation(ControllableEntity* target) { if (target) setTargetOrientation(target->getOrientation()); } /*void CommonController::spin() { this->moveToTargetPosition(); this->getControllableEntity()->rotateRoll(8.0f); } void CommonController::turn180() { Vector2 coord = get2DViewdirection(this->getControllableEntity()->getPosition(), this->getControllableEntity()->getOrientation() * WorldEntity::FRONT, this->getControllableEntity()->getOrientation() * WorldEntity::UP, this->targetPosition_); this->getControllableEntity()->rotateYaw(-2.0f * sgn(coord.x) * coord.x*coord.x); this->getControllableEntity()->rotatePitch(2.0f * sgn(coord.y) * coord.y*coord.y); this->getControllableEntity()->moveFrontBack(SPEED); }*/ //copy the Roll orientation of given Quaternion. void CommonController::copyOrientation(const Quaternion& orient) { //roll angle difference in radian float diff=orient.getRoll(false).valueRadians()-(this->getControllableEntity()->getOrientation().getRoll(false).valueRadians()); while(diff>math::twoPi) diff-=math::twoPi; while(diff<-math::twoPi) diff+=math::twoPi; this->getControllableEntity()->rotateRoll(diff*ROTATEFACTOR); } void CommonController::copyTargetOrientation() { if (bHasTargetOrientation_) { copyOrientation(targetOrientation_); } } void CommonController::moveToTargetPosition() { this->moveToPosition(this->targetPosition_); } void CommonController::moveToPosition(const Vector3& target) { float factor = 1; if (!this->getControllableEntity()) return; if (this->rank_ == Rank::DIVISIONLEADER) factor = 0.8; if (this->rank_ == Rank::SECTIONLEADER) factor = 0.9; //100 is (so far) the smallest tolerance (empirically found) that can be reached, //with smaller distance spaceships can't reach position and go circles around it instead int tolerance = 60; ControllableEntity* entity = this->getControllableEntity(); Vector2 coord = get2DViewCoordinates (entity->getPosition(), entity->getOrientation() * WorldEntity::FRONT, entity->getOrientation() * WorldEntity::UP, target); float distance = (target - this->getControllableEntity()->getPosition()).length(); //rotates should be in range [-1,+1], clamp cuts off all that is not float rotateX = clamp(coord.x * 10, -1.0f, 1.0f); float rotateY = clamp(coord.y * 10, -1.0f, 1.0f); if (distance > tolerance) { //Yaw and Pitch are enough to start facing the target this->getControllableEntity()->rotateYaw(-2.0f * ROTATEFACTOR * rotateX); this->getControllableEntity()->rotatePitch(2.0f * ROTATEFACTOR * rotateY); //300 works, maybe less is better if (distance < 400) { //Change roll when close. When Spaceship faces target, roll doesn't affect it's trajectory //It's important that roll is not changed in the process of changing yaw and pitch //Wingmen won't face same direction as Leaders, but when Leaders start moving //Yaw and Pitch will adapt. if (bHasTargetOrientation_) { copyTargetOrientation(); } } orxout (internal_error) << "MOVING" <getControllableEntity()->moveFrontBack(1.2f*SPEED*factor); } else { bHasTargetPosition_ = false; bHasTargetOrientation_ = false; } } bool CommonController::isCloseAtTarget(float distance) const { if (!this->getControllableEntity()) return false; if (!this->target_) return (this->getControllableEntity()->getPosition().squaredDistance(this->targetPosition_) < distance*distance); else return (this->getControllableEntity()->getPosition().squaredDistance(this->target_->getPosition()) < distance*distance); } bool CommonController::canFire() { float tolerance = 50.0f; //check pointers if (!this->getControllableEntity() || !this->target_ || !this->target_->getControllableEntity()) return false; //check if this points in the direction of target_ Vector3 myPosition = this->getControllableEntity()->getWorldPosition(); Vector3 targetPosition = this->target_->getControllableEntity()->getWorldPosition(); Vector3 differenceVector = targetPosition - myPosition; float differenceLength = differenceVector.length(); Vector3 myDirection = this->getControllableEntity()->getOrientation() * WorldEntity::FRONT; float angle = getAngle (myPosition, myDirection, targetPosition); float heightLength = sin(angle) * differenceLength; if (heightLength > tolerance) return false; //check if there are allies on the way Vector3 allyPosition, allyDifference; float allyDifferenceLength, allyAngle, allyHeightLength; for (ObjectList::iterator it = ObjectList::begin(); it; ++it) { if (!it->getControllableEntity()) continue; if ((this->getControllableEntity()->getTeam() == (it)->getControllableEntity()->getTeam())) { allyPosition = it->getControllableEntity()->getWorldPosition(); allyDifference = allyPosition - myPosition; allyDifferenceLength = allyDifference.length(); allyAngle = getAngle (myPosition, myDirection, allyPosition); allyHeightLength = sin(allyAngle) * allyDifferenceLength; if (allyAngle > math::pi /2) continue; if (allyHeightLength <= tolerance) return false; } } Pawn* pawn = orxonox_cast(this->getControllableEntity()); if (pawn) pawn->setAimPosition(WorldEntity::FRONT); return true; } void CommonController::doFire() { this->getControllableEntity()->fire(0); } }