Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/AI_HS15/src/orxonox/controllers/CommonController.cc @ 10805

Last change on this file since 10805 was 10805, checked in by gania, 9 years ago

Finished groundwork: AI fights enemies like I want it to. TODO implement all the functionality of old AI, like Waypoints, XMLPort

File size: 20.1 KB
RevLine 
[10719]1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
3 *                    > www.orxonox.net <
4 *
5 *
6 *   License notice:
7 *
8 *   This program is free software; you can redistribute it and/or
9 *   modify it under the terms of the GNU General Public License
10 *   as published by the Free Software Foundation; either version 2
[10799]11 *   of the License, or ( at your option )any later version.
[10719]12 *
13 *   This program is distributed in the hope that it will be useful,
14 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *   GNU General Public License for more details.
17 *
18 *   You should have received a copy of the GNU General Public License
19 *   along with this program; if not, write to the Free Software
20 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 *
22 *   Author:
23 *      Fabian 'x3n' Landau
24 *   Co-authors:
25 *      Dominik Solenicki
26 *
27 */
[10800]28//bug or feature? Press 4 control keys from {Q,W,E,A,S,D,C} at the same time or 3 keys from {Q,E,A,D}, spaceship goes in free fly mode
[10719]29#include "controllers/CommonController.h"
[10759]30#include "core/XMLPort.h"
31
[10737]32#include "weaponsystem/WeaponMode.h"
33#include "weaponsystem/WeaponPack.h"
34#include "weaponsystem/Weapon.h"
35#include "weaponsystem/WeaponSlot.h"
36#include "weaponsystem/WeaponSlot.h"
37#include "worldentities/pawns/SpaceShip.h"
[10719]38
[10780]39#include "Scene.h"
40#include <OgreRay.h>
41#include <OgreSceneQuery.h>
42#include <OgreCamera.h>
43#include <OgreSceneManager.h>
[10719]44namespace orxonox
45{
46
[10799]47    RegisterClass( CommonController );
[10800]48    float SPEED = 0.9f/0.02f;
[10803]49    float ROTATEFACTOR = 1.0f/0.02f;
[10719]50
[10799]51    CommonController::CommonController( Context* context ): Controller( context )
[10719]52    {
[10759]53        this->bSetupWorked = false;
[10731]54
[10789]55        this->executingManeuver_ = false;
56        this->executingMoveToPoint_ = false;
[10805]57        this->action_ = Action::FLY;
[10803]58        this->stopLookingAtTarget();
[10793]59        this->maneuverType_ = ManeuverType::NONE;
[10799]60        RegisterObject( CommonController );
[10719]61    }
[10731]62
63
[10799]64    CommonController::~CommonController() 
[10719]65    {
[10805]66        orxout(internal_error) << "I died, my Rank is " << rank_ << endl;
[10731]67    }
68
[10799]69    void CommonController::XMLPort( Element& xmlelement, XMLPort::Mode mode )
[10759]70    {
[10799]71        SUPER( CommonController, XMLPort, xmlelement, mode );
72        XMLPortParam( CommonController, "formationMode", setFormationModeXML, getFormationModeXML,  xmlelement, mode );
[10731]73
[10759]74    }
[10799]75    void CommonController::setFormationModeXML( std::string val )
[10759]76    {
[10799]77        const std::string valUpper = getUppercase( val );
[10759]78        FormationMode::Value value;
[10799]79        if ( valUpper == "VEE" )
[10759]80            value = FormationMode::VEE;
[10799]81        else if ( valUpper == "WALL" )
[10759]82            value = FormationMode::WALL;
[10799]83        else if ( valUpper == "FINGER4" )
[10759]84            value = FormationMode::FINGER4;
[10799]85        else if ( valUpper == "DIAMOND" )
[10759]86            value = FormationMode::DIAMOND;
87        else
[10799]88            ThrowException( ParseError, std::string( "Attempting to set an unknown FormationMode: '" )+ val + "'." );
89        this->setFormationMode( value );
[10759]90       
91    }
[10799]92    std::string CommonController::getFormationModeXML() 
[10759]93    {
[10799]94        switch ( this->formationMode_ )
[10759]95        {
96            case FormationMode::VEE:
97            {
98                return "VEE";
99                break;
100            }
101            case FormationMode::WALL:
102            {
103                return "WALL";
104                break;
105            }
106            case FormationMode::FINGER4:
107            {
108                return "FINGER4";
109                break;
110            }
111            case FormationMode::DIAMOND:
112            {
113                return "DIAMOND";
114                break;
115            }
116            default:
117                return "DIAMOND";
118                break;
[10731]119
[10759]120        }
121    }
[10805]122    Action::Value CommonController::getAction ()
123    {
124        return this->action_;
125    }
126
127    void CommonController::setAction (Action::Value action, ControllableEntity* target)
128    {
129        this->action_ = action;
130        if (action == Action::FIGHT)
131        {   
132            if (target)
133                this->setTarget (target);
134        }
135        else if (action == Action::PROTECT)
136        {
137        }
138    }
139    void CommonController::setAction (Action::Value action, const Vector3& target)
140    {
141        this->action_ = action;
142        if (action == Action::FLY)
143        {
144            this->setTargetPosition (target);
145        }
146        else if (action == Action::PROTECT)
147        {
148
149        }
150    }
151    void CommonController::setAction (Action::Value action, const Vector3& target,  const Quaternion& orient )
152    {
153        this->action_ = action;
154        if (action == Action::FLY)
155        {
156            this->setTargetPosition (target);
157            this->setTargetOrientation (orient);
158        }
159        else if (action == Action::PROTECT)
160        {
161           
162        }
163    }
[10799]164    void CommonController::maneuver() 
[10797]165    {
[10803]166        counter++;
[10797]167
[10803]168        if (counter > 5)
169            counter = 0;
170        if ( this->target_ && this->getControllableEntity())
[10797]171        {
[10803]172            Vector3 thisPosition = this->getControllableEntity()->getWorldPosition();
173            //Quaternion thisOrientation = this->getControllableEntity()->getOrientation();
[10799]174
[10803]175            this->setPositionOfTarget( getPredictedPosition( 
[10799]176                thisPosition, 
177                hardcoded_projectile_speed, 
178                this->target_->getWorldPosition() , 
179                this->target_->getVelocity() 
[10803]180                )  );
[10800]181            this->setOrientationOfTarget( this->target_->getOrientation() );
[10799]182
183
184            Vector3 diffVector = this->positionOfTarget_ - thisPosition;
[10797]185            float diffLength = diffVector.length();
186            Vector3 diffUnit = diffVector/diffLength;
187
188
189
[10803]190            //bool bThisIsLookingAtTarget = this->isLooking ( getControllableEntity(), this->target_, math::pi/4 );
[10805]191            bool bTargetIsLookingAtThis = this->isLooking ( this->target_, getControllableEntity(), math::pi/5.0f );
[10803]192           
[10799]193
194
[10803]195            //too far? well, come closer then
[10805]196            if ( diffLength > 3000 )
[10797]197            {
[10803]198                if (diffLength < 6000)
[10797]199                {
[10805]200
[10797]201                }
202                else
203                {
204                }
[10803]205                this->setTargetPosition( this->positionOfTarget_ );
[10797]206            }
[10803]207            //too close? How do u expect to dodge anything? Just attack!
[10805]208            else if ( diffLength < 500 )
[10803]209            {   
210                //at this point, just look and shoot
[10805]211                if ( diffLength < 250 )
[10800]212                {
[10803]213                    this->stopMoving();
214                    this->startLookingAtTarget();
[10800]215                }
216                else
217                {
[10803]218                    this->setTargetPosition( this->positionOfTarget_ );
[10800]219                }
220            }
[10803]221            //Good distance? Check if target looks at us. It doesn't? Go hunt!
222            else if ( !bTargetIsLookingAtThis )
[10793]223            {
[10803]224                this->setTargetPosition( this->positionOfTarget_ );
225              /*  if (counter == 0)
[10793]226                {
[10803]227                    this->setTargetPosition( this->positionOfTarget_ );   
228                    return;
[10793]229                }
[10803]230                else
[10793]231                {
[10803]232                    dodge( thisPosition, diffUnit );
233                }*/
234            }
235            //That's unfortunate, he is looking and probably shooting... try to dodge what we can... 
236            else 
237            {   
238           
239                if (counter == 0)
[10793]240                {
[10803]241                    this->setTargetPosition( this->positionOfTarget_ );   
242                    return;
[10793]243                }
[10803]244                dodge( thisPosition, diffUnit );
245               
[10793]246            }
247        }
[10799]248        if ( this->getControllableEntity()  && !this->target_ )
[10793]249        {
[10805]250
[10793]251            this->maneuverType_ = ManeuverType::NONE;
252        }
[10805]253        //orxout ( internal_error ) << "ManeuverType = " << this->maneuverType_ << endl;
[10793]254    }
[10803]255    ControllableEntity* CommonController::getTarget()
256    {
257        return this->target_;
258    }
259    void CommonController::dodge(Vector3& thisPosition, Vector3& diffUnit)
260    {
261        float factorX = 0, factorY = 0, factorZ = 0;
262        float rand = randomInRange (0, 1);
263        if (rand <= 0.5)
264        {
265            factorX = 1;
266        }
267        else
268        {
269            factorX = -1;
270        }
271        rand = randomInRange (0, 1);
272        if (rand <= 0.5)
273        {
274            factorY = 1;
275        }
276        else
277        {
278            factorY = -1;
279        }
280        rand = randomInRange (0, 1);
281        if (rand <= 0.5)
282        {
283            factorZ = 1;
284        }
285        else
286        {
287            factorZ = -1;
288        }
289        Vector3 target = ( diffUnit )* 8000.0f;
290        Vector3* randVector = new Vector3( 
291            factorX * randomInRange( 10000, 40000 ), 
292            factorY * randomInRange( 10000, 40000 ), 
293            factorZ * randomInRange( 10000, 40000 ) 
294        );
295        Vector3 projection = randVector->dotProduct( diffUnit )* diffUnit;
296        *randVector -= projection;
297        target += *randVector;
298        this->setTargetPosition( thisPosition + target );
299    }
300    void CommonController::stopMoving()
301    {
302        this->bHasTargetPosition_ = false;
303    }
304    void CommonController::startLookingAtTarget()
305    {
306        this->bLookAtTarget_ = true;
307    }
308    void CommonController::stopLookingAtTarget()
309    {
310        this->bLookAtTarget_ = false;
311    }
312    void CommonController::lookAtTarget(float dt)
313    {
314
315       
316        ControllableEntity* entity = this->getControllableEntity();
317        if ( !entity )
318            return;
319        Vector2 coord = get2DViewCoordinates
320            ( entity->getPosition() , 
321            entity->getOrientation()  * WorldEntity::FRONT, 
322            entity->getOrientation()  * WorldEntity::UP, 
323            positionOfTarget_ );
324
325        //rotates should be in range [-1,+1], clamp cuts off all that is not
326        float rotateX = -clamp( coord.x * 10, -1.0f, 1.0f );
327        float rotateY = clamp( coord.y * 10, -1.0f, 1.0f );
328
329       
330   
331        //Yaw and Pitch are enough to start facing the target
332        this->getControllableEntity() ->rotateYaw( ROTATEFACTOR * rotateX * dt );
333        this->getControllableEntity() ->rotatePitch( ROTATEFACTOR * rotateY * dt );
334       
335           
336    }
337   
[10799]338    bool CommonController::setWingman ( CommonController* wingman )
[10731]339    {
[10719]340        return false;
341    }
[10731]342   
[10799]343    bool CommonController::hasWingman() 
[10722]344    {
345        return true;
346    }
[10799]347    void CommonController::setTarget( ControllableEntity* target )
[10759]348    {
349        this->target_ = target;
[10805]350        //orxout ( internal_error ) << " TARGET SET " << endl;
[10793]351       
[10799]352        if ( this->target_ )
[10793]353        {
[10799]354            this->setPositionOfTarget( target_->getWorldPosition() );
[10793]355
356        }
[10759]357    }
[10799]358    bool CommonController::hasTarget() 
[10793]359    {
[10799]360        if ( this->target_ )
[10793]361            return true;
362        return false;
363    }
[10799]364    void CommonController::setPositionOfTarget( const Vector3& target )
[10793]365    {
366        this->positionOfTarget_ = target;
367        this->bHasPositionOfTarget_ = true;
368    }
[10799]369    void CommonController::setOrientationOfTarget( const Quaternion& orient )
[10793]370    {
371        this->orientationOfTarget_=orient;
372        this->bHasOrientationOfTarget_=true;
373    }
[10722]374
[10799]375    void CommonController::setTargetPosition( const Vector3& target )
[10719]376    {
[10731]377        this->targetPosition_ = target;
378        this->bHasTargetPosition_ = true;
379    }
[10719]380
[10799]381    void CommonController::setTargetOrientation( const Quaternion& orient )
[10731]382    {
383        this->targetOrientation_=orient;
384        this->bHasTargetOrientation_=true;
[10719]385    }
386
[10799]387    void CommonController::setTargetOrientation( ControllableEntity* target )
[10731]388    {
[10799]389        if ( target )
390            setTargetOrientation( target->getOrientation() );
[10731]391    }
[10719]392
[10731]393
394
[10729]395    //copy the Roll orientation of given Quaternion.
[10799]396    void CommonController::copyOrientation( const Quaternion& orient, float dt )
[10729]397    {
398        //roll angle difference in radian
[10799]399        float diff=orient.getRoll( false ).valueRadians() -( this->getControllableEntity() ->getOrientation() .getRoll( false ).valueRadians() );
400        while( diff>math::twoPi )diff-=math::twoPi;
401        while( diff<-math::twoPi )diff+=math::twoPi;
402        this->getControllableEntity() ->rotateRoll( diff*ROTATEFACTOR * dt );
[10729]403    }
[10799]404    void CommonController::copyTargetOrientation( float dt )
[10729]405    {
[10799]406        if ( bHasTargetOrientation_ )
[10729]407        {   
[10799]408            copyOrientation( targetOrientation_, dt );
[10729]409        }
410    }
[10731]411
412
413
414
[10799]415    void CommonController::moveToTargetPosition( float dt )
[10729]416    {
[10799]417        this->moveToPosition( this->targetPosition_, dt );
[10729]418    }
[10799]419    void CommonController::moveToPosition( const Vector3& target, float dt )
[10725]420    {
[10805]421     
[10729]422       
[10799]423        //100 is ( so far )the smallest tolerance ( empirically found )that can be reached,
[10729]424        //with smaller distance spaceships can't reach position and go circles around it instead
[10803]425        int tolerance = 65;
[10725]426
[10729]427        ControllableEntity* entity = this->getControllableEntity();
428        Vector2 coord = get2DViewCoordinates
[10799]429            ( entity->getPosition() , 
430            entity->getOrientation()  * WorldEntity::FRONT, 
431            entity->getOrientation()  * WorldEntity::UP, 
432            target );
[10729]433
[10799]434        float distance = ( target - this->getControllableEntity() ->getPosition() ).length();
[10729]435
436        //rotates should be in range [-1,+1], clamp cuts off all that is not
[10803]437        float rotateX = -clamp( coord.x * 10, -1.0f, 1.0f );
[10799]438        float rotateY = clamp( coord.y * 10, -1.0f, 1.0f );
[10725]439
440       
[10799]441        if ( distance > tolerance )
[10725]442        {
[10729]443            //Yaw and Pitch are enough to start facing the target
[10803]444            this->getControllableEntity() ->rotateYaw( ROTATEFACTOR * rotateX * dt );
445            this->getControllableEntity() ->rotatePitch( ROTATEFACTOR * rotateY * dt );
[10729]446
447            //300 works, maybe less is better
[10799]448            if ( distance < 400 )
[10729]449            {
450                //Change roll when close. When Spaceship faces target, roll doesn't affect it's trajectory
451                //It's important that roll is not changed in the process of changing yaw and pitch
452                //Wingmen won't face same direction as Leaders, but when Leaders start moving
453                //Yaw and Pitch will adapt.
[10799]454                if ( bHasTargetOrientation_ )
[10729]455                {
[10799]456                    copyTargetOrientation( dt );
[10729]457                }
458            }
[10763]459
[10805]460            this->getControllableEntity() ->moveFrontBack( SPEED * dt );
[10725]461        }
[10729]462        else
463        {     
464            bHasTargetPosition_ = false;
465            bHasTargetOrientation_ = false;
466        }
[10731]467    }
[10799]468    float CommonController::randomInRange( float a, float b )
[10796]469    {
[10799]470        float random = rnd( 1.0f );
[10796]471        float diff = b - a;
472        float r = random * diff;
473        return a + r;
474    }
[10803]475   
[10796]476
[10782]477    //to be called in action
478    //PRE: relativeTargetPosition is desired position relative to the spaceship,
[10789]479    //angleRoll is the angle in degrees of Roll that should be applied by the end of the movement
[10799]480    //POST: target orientation and position are set, so that it can be used by MoveAndRoll()
481    void CommonController::moveToPoint( const Vector3& relativeTargetPosition, float angleRoll )
[10782]482    {
483        ControllableEntity* entity = this->getControllableEntity();
[10799]484        if ( !entity )
[10789]485            return;
[10782]486        Quaternion orient = entity->getWorldOrientation();
[10799]487        Quaternion rotation = Quaternion( Degree( angleRoll ), Vector3::UNIT_Z );
[10737]488
[10782]489        Vector3 target = orient * relativeTargetPosition + entity->getWorldPosition();
[10799]490        setTargetPosition( target );
[10789]491        orient = orient * rotation;
[10799]492        this->setTargetOrientation( orient );
[10789]493       
[10782]494    }
495    //to be called in tick
496    //PRE: MoveToPoint was called
497    //POST: spaceship changes its yaw and pitch to point towards targetPosition_,
498    //moves towards targetPosition_ by amount depending on dt and its speed,
499    //rolls by amount depending on the difference between angleRoll_ and angleRolled_, dt, and
500    //angular speed
501    //if position reached with a certain tolerance, and angleRolled_ = angleRoll_, returns false,
502    //otherwise returns true
[10789]503    //dt is normally around 0.02f, which makes it 1/0.02 = 50 frames/sec
[10799]504    bool CommonController::moveAndRoll( float dt )
[10737]505    {
[10789]506        float factor = 1;
[10799]507        if ( !this->getControllableEntity() )
[10789]508            return false;
[10799]509        if ( this->rank_ == Rank::DIVISIONLEADER )
[10789]510            factor = 0.8;
[10799]511        if ( this->rank_ == Rank::SECTIONLEADER )
[10789]512            factor = 0.9;
[10782]513        int tolerance = 60;
[10789]514       
[10782]515        ControllableEntity* entity = this->getControllableEntity();
[10799]516        if ( !entity )
[10789]517            return true;
[10782]518        Vector2 coord = get2DViewCoordinates
[10799]519            ( entity->getPosition() , 
520            entity->getOrientation()  * WorldEntity::FRONT, 
521            entity->getOrientation()  * WorldEntity::UP, 
522            targetPosition_ );
[10737]523
[10799]524        float distance = ( targetPosition_ - this->getControllableEntity() ->getPosition() ).length();
[10782]525
526        //rotates should be in range [-1,+1], clamp cuts off all that is not
[10799]527        float rotateX = clamp( coord.x * 10, -1.0f, 1.0f );
528        float rotateY = clamp( coord.y * 10, -1.0f, 1.0f );
[10782]529
530       
[10799]531        if ( distance > tolerance )
[10782]532        {
533            //Yaw and Pitch are enough to start facing the target
[10799]534            this->getControllableEntity() ->rotateYaw( -2.0f * ROTATEFACTOR * rotateX * dt );
535            this->getControllableEntity() ->rotatePitch( 2.0f * ROTATEFACTOR * rotateY * dt );
[10782]536           
[10789]537            //Roll
[10799]538            if ( bHasTargetOrientation_ )
[10789]539            {
[10799]540                copyTargetOrientation( dt );
[10789]541            }
542         
[10782]543            //Move
[10799]544            this->getControllableEntity() ->moveFrontBack( 1.2f * SPEED * factor * dt );
[10782]545            //if still moving, return false
546            return false;
547        }
[10737]548        else
[10782]549        {     
[10789]550           
[10782]551            //if finished, return true;
[10789]552            return true;
[10782]553        }
[10737]554    }
[10782]555
[10799]556    float CommonController::squaredDistanceToTarget()  const
[10782]557    {
[10799]558        if ( !this->getControllableEntity()  )
[10782]559            return 0;
[10803]560        if ( !this->target_ || !this->getControllableEntity() )
[10799]561            return ( this->getControllableEntity() ->getPosition() .squaredDistance( this->targetPosition_ ) );
[10782]562        else
[10800]563            return ( this->getControllableEntity() ->getPosition() .squaredDistance( this->positionOfTarget_ ) );
[10782]564    }
565   
[10799]566    bool CommonController::isLookingAtTarget( float angle )const
[10737]567    {
[10803]568        if ( !this->getControllableEntity()  || !this->target_ )
[10762]569            return false;
570
[10803]571        return ( getAngle( this->getControllableEntity() ->getPosition() , 
572            this->getControllableEntity() ->getOrientation()  * WorldEntity::FRONT, this->positionOfTarget_ ) < angle );
[10780]573    }
[10803]574    bool CommonController::isLooking( ControllableEntity* entityThatLooks, ControllableEntity* entityBeingLookedAt, float angle )const
575    {
576        if ( !entityThatLooks || !entityBeingLookedAt )
577            return false;
578        return ( getAngle( entityThatLooks ->getPosition() , 
579            entityThatLooks->getOrientation()  * WorldEntity::FRONT, 
580            entityBeingLookedAt->getWorldPosition() ) < angle );
581    }
[10764]582
[10799]583    bool CommonController::canFire() 
[10780]584    {
[10803]585
586        //no target? why fire?
587        if ( !this->target_ )
588            return false;
589
590        Vector3 newPositionOfTarget = getPredictedPosition( this->getControllableEntity() ->getWorldPosition() , 
591            hardcoded_projectile_speed, this->target_->getWorldPosition() , this->target_->getVelocity() );
592        if ( newPositionOfTarget != Vector3::ZERO )
593        {
594            this->setPositionOfTarget( newPositionOfTarget );
595        }
596
[10782]597        float squaredDistance = squaredDistanceToTarget();
[10800]598
[10805]599        if ( squaredDistance < 9000000.0f && this->isLookingAtTarget( math::pi / 10.0f)) {
[10780]600            return true;
601        }
602        else
603        {
[10762]604            return false;
[10780]605        }
[10762]606
[10780]607    }
[10799]608    void CommonController::doFire() 
[10780]609    {
[10799]610        if ( !this->target_ || !this->getControllableEntity() )
[10803]611        {
[10780]612            return;
[10803]613        }
614     
[10764]615
[10803]616       
[10799]617        Pawn* pawn = orxonox_cast<Pawn*>( this->getControllableEntity() );
[10793]618
[10799]619        if ( pawn )
620            //pawn->setAimPosition( this->getControllableEntity() ->getWorldPosition()  + 4000*( this->getControllableEntity() ->getOrientation()  * WorldEntity::FRONT ));
[10800]621            pawn->setAimPosition( this->positionOfTarget_ );
[10763]622   
[10799]623        this->getControllableEntity() ->fire( 0 );
[10731]624    }
[10762]625   
[10725]626
[10719]627}
Note: See TracBrowser for help on using the repository browser.