/* * 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/FightingController.h" #include "core/XMLPort.h" #include "util/Math.h" namespace orxonox { RegisterClass (FightingController); FightingController::FightingController( Context* context ): FlyingController( context ) { this->attackRange_ = 2500; this->stopLookingAtTarget(); RegisterObject( FightingController ); } FightingController::~FightingController() { } void FightingController::XMLPort( Element& xmlelement, XMLPort::Mode mode ) { SUPER( FightingController, XMLPort, xmlelement, mode ); } void FightingController::lookAtTarget(float dt) { ControllableEntity* entity = this->getControllableEntity(); if ( !entity ) return; Vector2 coord = get2DViewCoordinates ( entity->getPosition() , entity->getOrientation() * WorldEntity::FRONT, entity->getOrientation() * WorldEntity::UP, positionOfTarget_ ); //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 ); //Yaw and Pitch are enough to start facing the target this->getControllableEntity() ->rotateYaw( ROTATEFACTOR * rotateX * dt ); this->getControllableEntity() ->rotatePitch( ROTATEFACTOR * rotateY * dt ); } void FightingController::stopLookingAtTarget() { this->bLookAtTarget_ = false; } void FightingController::startLookingAtTarget() { this->bLookAtTarget_ = true; } bool FightingController::hasTarget() { if ( this->target_ ) return true; return false; } void FightingController::setTarget( ControllableEntity* target ) { this->target_ = target; if ( this->target_ ) { this->setPositionOfTarget( target_->getWorldPosition() ); } } void FightingController::setPositionOfTarget( const Vector3& target ) { this->positionOfTarget_ = target; this->bHasPositionOfTarget_ = true; } void FightingController::setOrientationOfTarget( const Quaternion& orient ) { this->orientationOfTarget_=orient; this->bHasOrientationOfTarget_=true; } void FightingController::maneuver() { maneuverCounter_++; if (maneuverCounter_ > 5) maneuverCounter_ = 0; if ( !this->target_ || !this->getControllableEntity()) return; Vector3 thisPosition = this->getControllableEntity()->getWorldPosition(); this->setPositionOfTarget( getPredictedPosition( thisPosition, hardcoded_projectile_speed, this->target_->getWorldPosition() , this->target_->getVelocity() ) ); this->setOrientationOfTarget( this->target_->getOrientation() ); Vector3 diffVector = this->positionOfTarget_ - thisPosition; float diffLength = diffVector.length(); Vector3 diffUnit = diffVector/diffLength; bool bTargetIsLookingAtThis = this->isLooking ( this->target_, getControllableEntity(), math::pi/10.0f ); //too far? well, come closer then if ( diffLength > this->attackRange_ ) { this->setTargetPosition( this->positionOfTarget_ ); } //too close? How do u expect to dodge anything? Just attack! else if ( diffLength < 500 ) { //at this point, just look and shoot if ( diffLength < 250 ) { this->stopMoving(); this->startLookingAtTarget(); } else { this->setTargetPosition( this->positionOfTarget_ ); } } //Good distance? Check if target looks at us. It doesn't? Go hunt! else if ( !bTargetIsLookingAtThis ) { this->setTargetPosition( this->positionOfTarget_ ); } //That's unfortunate, he is looking and probably shooting... try to dodge what we can... else { if (maneuverCounter_ == 0) { this->setTargetPosition( this->positionOfTarget_ ); return; } dodge( thisPosition, diffUnit ); } } void FightingController::dodge(Vector3& thisPosition, Vector3& diffUnit) { float factorX = 0, factorY = 0, factorZ = 0; float rand = randomInRange (0, 1); if (rand <= 0.5) { factorX = 1; } else { factorX = -1; } rand = randomInRange (0, 1); if (rand <= 0.5) { factorY = 1; } else { factorY = -1; } rand = randomInRange (0, 1); if (rand <= 0.5) { factorZ = 1; } else { factorZ = -1; } Vector3 target = ( diffUnit )* 8000.0f; Vector3* randVector = new Vector3( factorX * randomInRange( 10000, 40000 ), factorY * randomInRange( 10000, 40000 ), factorZ * randomInRange( 10000, 40000 ) ); Vector3 projection = randVector->dotProduct( diffUnit )* diffUnit; *randVector -= projection; target += *randVector; this->setTargetPosition( thisPosition + target ); } bool FightingController::canFire() { //no target? why fire? if ( !this->target_ ) return false; Vector3 newPositionOfTarget = getPredictedPosition( this->getControllableEntity() ->getWorldPosition() , hardcoded_projectile_speed, this->target_->getWorldPosition() , this->target_->getVelocity() ); if ( newPositionOfTarget != Vector3::ZERO ) { this->setPositionOfTarget( newPositionOfTarget ); } float squaredDistance = squaredDistanceToTarget(); if ( squaredDistance < this->attackRange_*this->attackRange_ && this->isLookingAtTarget( math::pi / 20.0f)) { return true; } else { return false; } } void FightingController::doFire() { if ( !this->target_ || !this->getControllableEntity() ) { return; } Pawn* pawn = orxonox_cast( this->getControllableEntity() ); if ( pawn ) pawn->setAimPosition( this->positionOfTarget_ ); this->getControllableEntity() ->fire( 0 ); } float FightingController::squaredDistanceToTarget() const { if ( !this->getControllableEntity() ) return 0; if ( !this->target_ || !this->getControllableEntity() ) return ( this->getControllableEntity() ->getPosition() .squaredDistance( this->targetPosition_ ) ); else return ( this->getControllableEntity() ->getPosition() .squaredDistance( this->positionOfTarget_ ) ); } bool FightingController::isLookingAtTarget( float angle ) { if ( !this->getControllableEntity() || !this->target_ ) return false; return this->isLooking(this->getControllableEntity(), this->getTarget(), angle); } void FightingController::setClosestTarget() { this->setTarget (static_cast( closestTarget() ) ); } Pawn* FightingController::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; } }