/* * 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" #include "Scene.h" #include #include #include #include namespace orxonox { RegisterClass(CommonController); float SPEED = 0.7f; float ROTATEFACTOR = 0.3f; CommonController::CommonController(Context* context) : Controller(context) { this->bSetupWorked = false; this->targetMask_.exclude(ClassByString("BaseObject")); this->targetMask_.include(ClassByString("WorldEntity")); this->targetMask_.exclude(ClassByString("Projectile")); 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(); } } this->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::isLookingAtTarget(float angle) const { if (!this->getControllableEntity()) return false; return (getAngle(this->getControllableEntity()->getPosition(), this->getControllableEntity()->getOrientation() * WorldEntity::FRONT, this->targetPosition_) < angle); } bool CommonController::canFire() { if ( this->bShooting_ && this->isCloseAtTarget(3000) && this->isLookingAtTarget(math::pi / 20.0f) ) { return true; } else { return false; } } void CommonController::doFire() { if (!this->target_ || !this->getControllableEntity()) return; static const float hardcoded_projectile_speed = 750; this->targetPosition_ = getPredictedPosition(this->getControllableEntity()->getWorldPosition(), hardcoded_projectile_speed, this->target_->getWorldPosition(), this->target_->getVelocity()); this->bHasTargetPosition_ = (this->targetPosition_ != Vector3::ZERO); Pawn* pawn = orxonox_cast(this->getControllableEntity()); if (pawn) pawn->setAimPosition(this->getControllableEntity()->getWorldPosition() + 4000*(this->getControllableEntity()->getOrientation() * WorldEntity::FRONT)); this->getControllableEntity()->fire(0); } }