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
Line 
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
11 *   of the License, or ( at your option )any later version.
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 */
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
29#include "controllers/CommonController.h"
30#include "core/XMLPort.h"
31
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"
38
39#include "Scene.h"
40#include <OgreRay.h>
41#include <OgreSceneQuery.h>
42#include <OgreCamera.h>
43#include <OgreSceneManager.h>
44namespace orxonox
45{
46
47    RegisterClass( CommonController );
48    float SPEED = 0.9f/0.02f;
49    float ROTATEFACTOR = 1.0f/0.02f;
50
51    CommonController::CommonController( Context* context ): Controller( context )
52    {
53        this->bSetupWorked = false;
54
55        this->executingManeuver_ = false;
56        this->executingMoveToPoint_ = false;
57        this->action_ = Action::FLY;
58        this->stopLookingAtTarget();
59        this->maneuverType_ = ManeuverType::NONE;
60        RegisterObject( CommonController );
61    }
62
63
64    CommonController::~CommonController() 
65    {
66        orxout(internal_error) << "I died, my Rank is " << rank_ << endl;
67    }
68
69    void CommonController::XMLPort( Element& xmlelement, XMLPort::Mode mode )
70    {
71        SUPER( CommonController, XMLPort, xmlelement, mode );
72        XMLPortParam( CommonController, "formationMode", setFormationModeXML, getFormationModeXML,  xmlelement, mode );
73
74    }
75    void CommonController::setFormationModeXML( std::string val )
76    {
77        const std::string valUpper = getUppercase( val );
78        FormationMode::Value value;
79        if ( valUpper == "VEE" )
80            value = FormationMode::VEE;
81        else if ( valUpper == "WALL" )
82            value = FormationMode::WALL;
83        else if ( valUpper == "FINGER4" )
84            value = FormationMode::FINGER4;
85        else if ( valUpper == "DIAMOND" )
86            value = FormationMode::DIAMOND;
87        else
88            ThrowException( ParseError, std::string( "Attempting to set an unknown FormationMode: '" )+ val + "'." );
89        this->setFormationMode( value );
90       
91    }
92    std::string CommonController::getFormationModeXML() 
93    {
94        switch ( this->formationMode_ )
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;
119
120        }
121    }
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    }
164    void CommonController::maneuver() 
165    {
166        counter++;
167
168        if (counter > 5)
169            counter = 0;
170        if ( this->target_ && this->getControllableEntity())
171        {
172            Vector3 thisPosition = this->getControllableEntity()->getWorldPosition();
173            //Quaternion thisOrientation = this->getControllableEntity()->getOrientation();
174
175            this->setPositionOfTarget( getPredictedPosition( 
176                thisPosition, 
177                hardcoded_projectile_speed, 
178                this->target_->getWorldPosition() , 
179                this->target_->getVelocity() 
180                )  );
181            this->setOrientationOfTarget( this->target_->getOrientation() );
182
183
184            Vector3 diffVector = this->positionOfTarget_ - thisPosition;
185            float diffLength = diffVector.length();
186            Vector3 diffUnit = diffVector/diffLength;
187
188
189
190            //bool bThisIsLookingAtTarget = this->isLooking ( getControllableEntity(), this->target_, math::pi/4 );
191            bool bTargetIsLookingAtThis = this->isLooking ( this->target_, getControllableEntity(), math::pi/5.0f );
192           
193
194
195            //too far? well, come closer then
196            if ( diffLength > 3000 )
197            {
198                if (diffLength < 6000)
199                {
200
201                }
202                else
203                {
204                }
205                this->setTargetPosition( this->positionOfTarget_ );
206            }
207            //too close? How do u expect to dodge anything? Just attack!
208            else if ( diffLength < 500 )
209            {   
210                //at this point, just look and shoot
211                if ( diffLength < 250 )
212                {
213                    this->stopMoving();
214                    this->startLookingAtTarget();
215                }
216                else
217                {
218                    this->setTargetPosition( this->positionOfTarget_ );
219                }
220            }
221            //Good distance? Check if target looks at us. It doesn't? Go hunt!
222            else if ( !bTargetIsLookingAtThis )
223            {
224                this->setTargetPosition( this->positionOfTarget_ );
225              /*  if (counter == 0)
226                {
227                    this->setTargetPosition( this->positionOfTarget_ );   
228                    return;
229                }
230                else
231                {
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)
240                {
241                    this->setTargetPosition( this->positionOfTarget_ );   
242                    return;
243                }
244                dodge( thisPosition, diffUnit );
245               
246            }
247        }
248        if ( this->getControllableEntity()  && !this->target_ )
249        {
250
251            this->maneuverType_ = ManeuverType::NONE;
252        }
253        //orxout ( internal_error ) << "ManeuverType = " << this->maneuverType_ << endl;
254    }
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   
338    bool CommonController::setWingman ( CommonController* wingman )
339    {
340        return false;
341    }
342   
343    bool CommonController::hasWingman() 
344    {
345        return true;
346    }
347    void CommonController::setTarget( ControllableEntity* target )
348    {
349        this->target_ = target;
350        //orxout ( internal_error ) << " TARGET SET " << endl;
351       
352        if ( this->target_ )
353        {
354            this->setPositionOfTarget( target_->getWorldPosition() );
355
356        }
357    }
358    bool CommonController::hasTarget() 
359    {
360        if ( this->target_ )
361            return true;
362        return false;
363    }
364    void CommonController::setPositionOfTarget( const Vector3& target )
365    {
366        this->positionOfTarget_ = target;
367        this->bHasPositionOfTarget_ = true;
368    }
369    void CommonController::setOrientationOfTarget( const Quaternion& orient )
370    {
371        this->orientationOfTarget_=orient;
372        this->bHasOrientationOfTarget_=true;
373    }
374
375    void CommonController::setTargetPosition( const Vector3& target )
376    {
377        this->targetPosition_ = target;
378        this->bHasTargetPosition_ = true;
379    }
380
381    void CommonController::setTargetOrientation( const Quaternion& orient )
382    {
383        this->targetOrientation_=orient;
384        this->bHasTargetOrientation_=true;
385    }
386
387    void CommonController::setTargetOrientation( ControllableEntity* target )
388    {
389        if ( target )
390            setTargetOrientation( target->getOrientation() );
391    }
392
393
394
395    //copy the Roll orientation of given Quaternion.
396    void CommonController::copyOrientation( const Quaternion& orient, float dt )
397    {
398        //roll angle difference in radian
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 );
403    }
404    void CommonController::copyTargetOrientation( float dt )
405    {
406        if ( bHasTargetOrientation_ )
407        {   
408            copyOrientation( targetOrientation_, dt );
409        }
410    }
411
412
413
414
415    void CommonController::moveToTargetPosition( float dt )
416    {
417        this->moveToPosition( this->targetPosition_, dt );
418    }
419    void CommonController::moveToPosition( const Vector3& target, float dt )
420    {
421     
422       
423        //100 is ( so far )the smallest tolerance ( empirically found )that can be reached,
424        //with smaller distance spaceships can't reach position and go circles around it instead
425        int tolerance = 65;
426
427        ControllableEntity* entity = this->getControllableEntity();
428        Vector2 coord = get2DViewCoordinates
429            ( entity->getPosition() , 
430            entity->getOrientation()  * WorldEntity::FRONT, 
431            entity->getOrientation()  * WorldEntity::UP, 
432            target );
433
434        float distance = ( target - this->getControllableEntity() ->getPosition() ).length();
435
436        //rotates should be in range [-1,+1], clamp cuts off all that is not
437        float rotateX = -clamp( coord.x * 10, -1.0f, 1.0f );
438        float rotateY = clamp( coord.y * 10, -1.0f, 1.0f );
439
440       
441        if ( distance > tolerance )
442        {
443            //Yaw and Pitch are enough to start facing the target
444            this->getControllableEntity() ->rotateYaw( ROTATEFACTOR * rotateX * dt );
445            this->getControllableEntity() ->rotatePitch( ROTATEFACTOR * rotateY * dt );
446
447            //300 works, maybe less is better
448            if ( distance < 400 )
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.
454                if ( bHasTargetOrientation_ )
455                {
456                    copyTargetOrientation( dt );
457                }
458            }
459
460            this->getControllableEntity() ->moveFrontBack( SPEED * dt );
461        }
462        else
463        {     
464            bHasTargetPosition_ = false;
465            bHasTargetOrientation_ = false;
466        }
467    }
468    float CommonController::randomInRange( float a, float b )
469    {
470        float random = rnd( 1.0f );
471        float diff = b - a;
472        float r = random * diff;
473        return a + r;
474    }
475   
476
477    //to be called in action
478    //PRE: relativeTargetPosition is desired position relative to the spaceship,
479    //angleRoll is the angle in degrees of Roll that should be applied by the end of the movement
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 )
482    {
483        ControllableEntity* entity = this->getControllableEntity();
484        if ( !entity )
485            return;
486        Quaternion orient = entity->getWorldOrientation();
487        Quaternion rotation = Quaternion( Degree( angleRoll ), Vector3::UNIT_Z );
488
489        Vector3 target = orient * relativeTargetPosition + entity->getWorldPosition();
490        setTargetPosition( target );
491        orient = orient * rotation;
492        this->setTargetOrientation( orient );
493       
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
503    //dt is normally around 0.02f, which makes it 1/0.02 = 50 frames/sec
504    bool CommonController::moveAndRoll( float dt )
505    {
506        float factor = 1;
507        if ( !this->getControllableEntity() )
508            return false;
509        if ( this->rank_ == Rank::DIVISIONLEADER )
510            factor = 0.8;
511        if ( this->rank_ == Rank::SECTIONLEADER )
512            factor = 0.9;
513        int tolerance = 60;
514       
515        ControllableEntity* entity = this->getControllableEntity();
516        if ( !entity )
517            return true;
518        Vector2 coord = get2DViewCoordinates
519            ( entity->getPosition() , 
520            entity->getOrientation()  * WorldEntity::FRONT, 
521            entity->getOrientation()  * WorldEntity::UP, 
522            targetPosition_ );
523
524        float distance = ( targetPosition_ - this->getControllableEntity() ->getPosition() ).length();
525
526        //rotates should be in range [-1,+1], clamp cuts off all that is not
527        float rotateX = clamp( coord.x * 10, -1.0f, 1.0f );
528        float rotateY = clamp( coord.y * 10, -1.0f, 1.0f );
529
530       
531        if ( distance > tolerance )
532        {
533            //Yaw and Pitch are enough to start facing the target
534            this->getControllableEntity() ->rotateYaw( -2.0f * ROTATEFACTOR * rotateX * dt );
535            this->getControllableEntity() ->rotatePitch( 2.0f * ROTATEFACTOR * rotateY * dt );
536           
537            //Roll
538            if ( bHasTargetOrientation_ )
539            {
540                copyTargetOrientation( dt );
541            }
542         
543            //Move
544            this->getControllableEntity() ->moveFrontBack( 1.2f * SPEED * factor * dt );
545            //if still moving, return false
546            return false;
547        }
548        else
549        {     
550           
551            //if finished, return true;
552            return true;
553        }
554    }
555
556    float CommonController::squaredDistanceToTarget()  const
557    {
558        if ( !this->getControllableEntity()  )
559            return 0;
560        if ( !this->target_ || !this->getControllableEntity() )
561            return ( this->getControllableEntity() ->getPosition() .squaredDistance( this->targetPosition_ ) );
562        else
563            return ( this->getControllableEntity() ->getPosition() .squaredDistance( this->positionOfTarget_ ) );
564    }
565   
566    bool CommonController::isLookingAtTarget( float angle )const
567    {
568        if ( !this->getControllableEntity()  || !this->target_ )
569            return false;
570
571        return ( getAngle( this->getControllableEntity() ->getPosition() , 
572            this->getControllableEntity() ->getOrientation()  * WorldEntity::FRONT, this->positionOfTarget_ ) < angle );
573    }
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    }
582
583    bool CommonController::canFire() 
584    {
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
597        float squaredDistance = squaredDistanceToTarget();
598
599        if ( squaredDistance < 9000000.0f && this->isLookingAtTarget( math::pi / 10.0f)) {
600            return true;
601        }
602        else
603        {
604            return false;
605        }
606
607    }
608    void CommonController::doFire() 
609    {
610        if ( !this->target_ || !this->getControllableEntity() )
611        {
612            return;
613        }
614     
615
616       
617        Pawn* pawn = orxonox_cast<Pawn*>( this->getControllableEntity() );
618
619        if ( pawn )
620            //pawn->setAimPosition( this->getControllableEntity() ->getWorldPosition()  + 4000*( this->getControllableEntity() ->getOrientation()  * WorldEntity::FRONT ));
621            pawn->setAimPosition( this->positionOfTarget_ );
622   
623        this->getControllableEntity() ->fire( 0 );
624    }
625   
626
627}
Note: See TracBrowser for help on using the repository browser.