Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 10803 was 10803, checked in by gania, 8 years ago

Works for DivisionController for now. Almost balanced AI! Dodges bullets (still killable, which is good), but goes crazy when close. So to be destroyed when far away. TODO implement formation flight when not in fight

File size: 19.3 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->stopLookingAtTarget();
58        this->maneuverType_ = ManeuverType::NONE;
59        RegisterObject( CommonController );
60    }
61
62
63    CommonController::~CommonController() 
64    {
65    }
66
67    void CommonController::XMLPort( Element& xmlelement, XMLPort::Mode mode )
68    {
69        SUPER( CommonController, XMLPort, xmlelement, mode );
70        XMLPortParam( CommonController, "formationMode", setFormationModeXML, getFormationModeXML,  xmlelement, mode );
71
72    }
73    void CommonController::setFormationModeXML( std::string val )
74    {
75        const std::string valUpper = getUppercase( val );
76        FormationMode::Value value;
77        if ( valUpper == "VEE" )
78            value = FormationMode::VEE;
79        else if ( valUpper == "WALL" )
80            value = FormationMode::WALL;
81        else if ( valUpper == "FINGER4" )
82            value = FormationMode::FINGER4;
83        else if ( valUpper == "DIAMOND" )
84            value = FormationMode::DIAMOND;
85        else
86            ThrowException( ParseError, std::string( "Attempting to set an unknown FormationMode: '" )+ val + "'." );
87        this->setFormationMode( value );
88       
89    }
90    std::string CommonController::getFormationModeXML() 
91    {
92        switch ( this->formationMode_ )
93        {
94            case FormationMode::VEE:
95            {
96                return "VEE";
97                break;
98            }
99            case FormationMode::WALL:
100            {
101                return "WALL";
102                break;
103            }
104            case FormationMode::FINGER4:
105            {
106                return "FINGER4";
107                break;
108            }
109            case FormationMode::DIAMOND:
110            {
111                return "DIAMOND";
112                break;
113            }
114            default:
115                return "DIAMOND";
116                break;
117
118        }
119    }
120    void CommonController::maneuver() 
121    {
122        counter++;
123
124        if (counter > 5)
125            counter = 0;
126        if ( this->target_ && this->getControllableEntity())
127        {
128            Vector3 thisPosition = this->getControllableEntity()->getWorldPosition();
129            //Quaternion thisOrientation = this->getControllableEntity()->getOrientation();
130
131            this->setPositionOfTarget( getPredictedPosition( 
132                thisPosition, 
133                hardcoded_projectile_speed, 
134                this->target_->getWorldPosition() , 
135                this->target_->getVelocity() 
136                )  );
137            this->setOrientationOfTarget( this->target_->getOrientation() );
138
139
140            Vector3 diffVector = this->positionOfTarget_ - thisPosition;
141            float diffLength = diffVector.length();
142            Vector3 diffUnit = diffVector/diffLength;
143
144
145
146            //bool bThisIsLookingAtTarget = this->isLooking ( getControllableEntity(), this->target_, math::pi/4 );
147            bool bTargetIsLookingAtThis = this->isLooking ( this->target_, getControllableEntity(), math::pi/8.0f );
148           
149
150
151            //too far? well, come closer then
152            if ( diffLength > 5000 )
153            {
154                if (diffLength < 6000)
155                {
156                    this->bEngaging_ = true;
157                }
158                else
159                {
160                    this->bEngaging_ = false;
161                }
162                this->setTargetPosition( this->positionOfTarget_ );
163            }
164            //too close? How do u expect to dodge anything? Just attack!
165            else if ( diffLength < 600 )
166            {   
167                //at this point, just look and shoot
168                if ( diffLength < 300 )
169                {
170                    this->stopMoving();
171                    this->startLookingAtTarget();
172                }
173                else
174                {
175                    this->setTargetPosition( this->positionOfTarget_ );
176                }
177            }
178            //Good distance? Check if target looks at us. It doesn't? Go hunt!
179            else if ( !bTargetIsLookingAtThis )
180            {
181                this->setTargetPosition( this->positionOfTarget_ );
182              /*  if (counter == 0)
183                {
184                    this->setTargetPosition( this->positionOfTarget_ );   
185                    return;
186                }
187                else
188                {
189                    dodge( thisPosition, diffUnit );
190                }*/
191            }
192            //That's unfortunate, he is looking and probably shooting... try to dodge what we can... 
193            else 
194            {   
195           
196                if (counter == 0)
197                {
198                    this->setTargetPosition( this->positionOfTarget_ );   
199                    return;
200                }
201                dodge( thisPosition, diffUnit );
202               
203            }
204        }
205        if ( this->getControllableEntity()  && !this->target_ )
206        {
207            this->bEngaging_ = false;
208            this->maneuverType_ = ManeuverType::NONE;
209        }
210        orxout ( internal_error ) << "ManeuverType = " << this->maneuverType_ << endl;
211    }
212    ControllableEntity* CommonController::getTarget()
213    {
214        return this->target_;
215    }
216    void CommonController::dodge(Vector3& thisPosition, Vector3& diffUnit)
217    {
218        float factorX = 0, factorY = 0, factorZ = 0;
219        float rand = randomInRange (0, 1);
220        if (rand <= 0.5)
221        {
222            factorX = 1;
223        }
224        else
225        {
226            factorX = -1;
227        }
228        rand = randomInRange (0, 1);
229        if (rand <= 0.5)
230        {
231            factorY = 1;
232        }
233        else
234        {
235            factorY = -1;
236        }
237        rand = randomInRange (0, 1);
238        if (rand <= 0.5)
239        {
240            factorZ = 1;
241        }
242        else
243        {
244            factorZ = -1;
245        }
246        Vector3 target = ( diffUnit )* 8000.0f;
247        Vector3* randVector = new Vector3( 
248            factorX * randomInRange( 10000, 40000 ), 
249            factorY * randomInRange( 10000, 40000 ), 
250            factorZ * randomInRange( 10000, 40000 ) 
251        );
252        Vector3 projection = randVector->dotProduct( diffUnit )* diffUnit;
253        *randVector -= projection;
254        target += *randVector;
255        this->setTargetPosition( thisPosition + target );
256    }
257    void CommonController::stopMoving()
258    {
259        this->bHasTargetPosition_ = false;
260    }
261    void CommonController::startLookingAtTarget()
262    {
263        this->bLookAtTarget_ = true;
264    }
265    void CommonController::stopLookingAtTarget()
266    {
267        this->bLookAtTarget_ = false;
268    }
269    void CommonController::lookAtTarget(float dt)
270    {
271
272       
273        ControllableEntity* entity = this->getControllableEntity();
274        if ( !entity )
275            return;
276        Vector2 coord = get2DViewCoordinates
277            ( entity->getPosition() , 
278            entity->getOrientation()  * WorldEntity::FRONT, 
279            entity->getOrientation()  * WorldEntity::UP, 
280            positionOfTarget_ );
281
282        //rotates should be in range [-1,+1], clamp cuts off all that is not
283        float rotateX = -clamp( coord.x * 10, -1.0f, 1.0f );
284        float rotateY = clamp( coord.y * 10, -1.0f, 1.0f );
285
286       
287   
288        //Yaw and Pitch are enough to start facing the target
289        this->getControllableEntity() ->rotateYaw( ROTATEFACTOR * rotateX * dt );
290        this->getControllableEntity() ->rotatePitch( ROTATEFACTOR * rotateY * dt );
291       
292           
293    }
294   
295    bool CommonController::setWingman ( CommonController* wingman )
296    {
297        return false;
298    }
299   
300    bool CommonController::hasWingman() 
301    {
302        return true;
303    }
304    void CommonController::setTarget( ControllableEntity* target )
305    {
306        this->target_ = target;
307        orxout ( internal_error ) << " TARGET SET " << endl;
308       
309        if ( this->target_ )
310        {
311            this->setPositionOfTarget( target_->getWorldPosition() );
312
313        }
314    }
315    bool CommonController::hasTarget() 
316    {
317        if ( this->target_ )
318            return true;
319        return false;
320    }
321    void CommonController::setPositionOfTarget( const Vector3& target )
322    {
323        this->positionOfTarget_ = target;
324        this->bHasPositionOfTarget_ = true;
325    }
326    void CommonController::setOrientationOfTarget( const Quaternion& orient )
327    {
328        this->orientationOfTarget_=orient;
329        this->bHasOrientationOfTarget_=true;
330    }
331
332    void CommonController::setTargetPosition( const Vector3& target )
333    {
334        this->targetPosition_ = target;
335        this->bHasTargetPosition_ = true;
336    }
337
338    void CommonController::setTargetOrientation( const Quaternion& orient )
339    {
340        this->targetOrientation_=orient;
341        this->bHasTargetOrientation_=true;
342    }
343
344    void CommonController::setTargetOrientation( ControllableEntity* target )
345    {
346        if ( target )
347            setTargetOrientation( target->getOrientation() );
348    }
349
350
351
352    //copy the Roll orientation of given Quaternion.
353    void CommonController::copyOrientation( const Quaternion& orient, float dt )
354    {
355        //roll angle difference in radian
356        float diff=orient.getRoll( false ).valueRadians() -( this->getControllableEntity() ->getOrientation() .getRoll( false ).valueRadians() );
357        while( diff>math::twoPi )diff-=math::twoPi;
358        while( diff<-math::twoPi )diff+=math::twoPi;
359        this->getControllableEntity() ->rotateRoll( diff*ROTATEFACTOR * dt );
360    }
361    void CommonController::copyTargetOrientation( float dt )
362    {
363        if ( bHasTargetOrientation_ )
364        {   
365            copyOrientation( targetOrientation_, dt );
366        }
367    }
368
369
370
371
372    void CommonController::moveToTargetPosition( float dt )
373    {
374        this->moveToPosition( this->targetPosition_, dt );
375    }
376    void CommonController::moveToPosition( const Vector3& target, float dt )
377    {
378        float factor = 1;
379        if ( !this->getControllableEntity() )
380            return;
381        if ( this->rank_ == Rank::DIVISIONLEADER )
382            factor = 0.9;
383        if ( this->rank_ == Rank::SECTIONLEADER )
384            factor = 0.95;
385       
386        //100 is ( so far )the smallest tolerance ( empirically found )that can be reached,
387        //with smaller distance spaceships can't reach position and go circles around it instead
388        int tolerance = 65;
389
390        ControllableEntity* entity = this->getControllableEntity();
391        Vector2 coord = get2DViewCoordinates
392            ( entity->getPosition() , 
393            entity->getOrientation()  * WorldEntity::FRONT, 
394            entity->getOrientation()  * WorldEntity::UP, 
395            target );
396
397        float distance = ( target - this->getControllableEntity() ->getPosition() ).length();
398
399        //rotates should be in range [-1,+1], clamp cuts off all that is not
400        float rotateX = -clamp( coord.x * 10, -1.0f, 1.0f );
401        float rotateY = clamp( coord.y * 10, -1.0f, 1.0f );
402
403       
404        if ( distance > tolerance )
405        {
406            //Yaw and Pitch are enough to start facing the target
407            this->getControllableEntity() ->rotateYaw( ROTATEFACTOR * rotateX * dt );
408            this->getControllableEntity() ->rotatePitch( ROTATEFACTOR * rotateY * dt );
409
410            //300 works, maybe less is better
411            if ( distance < 400 )
412            {
413                //Change roll when close. When Spaceship faces target, roll doesn't affect it's trajectory
414                //It's important that roll is not changed in the process of changing yaw and pitch
415                //Wingmen won't face same direction as Leaders, but when Leaders start moving
416                //Yaw and Pitch will adapt.
417                if ( bHasTargetOrientation_ )
418                {
419                    copyTargetOrientation( dt );
420                }
421            }
422
423            this->getControllableEntity() ->moveFrontBack( SPEED*factor * dt );
424        }
425        else
426        {     
427            bHasTargetPosition_ = false;
428            bHasTargetOrientation_ = false;
429        }
430    }
431    float CommonController::randomInRange( float a, float b )
432    {
433        float random = rnd( 1.0f );
434        float diff = b - a;
435        float r = random * diff;
436        return a + r;
437    }
438   
439
440    //to be called in action
441    //PRE: relativeTargetPosition is desired position relative to the spaceship,
442    //angleRoll is the angle in degrees of Roll that should be applied by the end of the movement
443    //POST: target orientation and position are set, so that it can be used by MoveAndRoll()
444    void CommonController::moveToPoint( const Vector3& relativeTargetPosition, float angleRoll )
445    {
446        ControllableEntity* entity = this->getControllableEntity();
447        if ( !entity )
448            return;
449        Quaternion orient = entity->getWorldOrientation();
450        Quaternion rotation = Quaternion( Degree( angleRoll ), Vector3::UNIT_Z );
451
452        Vector3 target = orient * relativeTargetPosition + entity->getWorldPosition();
453        setTargetPosition( target );
454        orient = orient * rotation;
455        this->setTargetOrientation( orient );
456       
457    }
458    //to be called in tick
459    //PRE: MoveToPoint was called
460    //POST: spaceship changes its yaw and pitch to point towards targetPosition_,
461    //moves towards targetPosition_ by amount depending on dt and its speed,
462    //rolls by amount depending on the difference between angleRoll_ and angleRolled_, dt, and
463    //angular speed
464    //if position reached with a certain tolerance, and angleRolled_ = angleRoll_, returns false,
465    //otherwise returns true
466    //dt is normally around 0.02f, which makes it 1/0.02 = 50 frames/sec
467    bool CommonController::moveAndRoll( float dt )
468    {
469        float factor = 1;
470        if ( !this->getControllableEntity() )
471            return false;
472        if ( this->rank_ == Rank::DIVISIONLEADER )
473            factor = 0.8;
474        if ( this->rank_ == Rank::SECTIONLEADER )
475            factor = 0.9;
476        int tolerance = 60;
477       
478        ControllableEntity* entity = this->getControllableEntity();
479        if ( !entity )
480            return true;
481        Vector2 coord = get2DViewCoordinates
482            ( entity->getPosition() , 
483            entity->getOrientation()  * WorldEntity::FRONT, 
484            entity->getOrientation()  * WorldEntity::UP, 
485            targetPosition_ );
486
487        float distance = ( targetPosition_ - this->getControllableEntity() ->getPosition() ).length();
488
489        //rotates should be in range [-1,+1], clamp cuts off all that is not
490        float rotateX = clamp( coord.x * 10, -1.0f, 1.0f );
491        float rotateY = clamp( coord.y * 10, -1.0f, 1.0f );
492
493       
494        if ( distance > tolerance )
495        {
496            //Yaw and Pitch are enough to start facing the target
497            this->getControllableEntity() ->rotateYaw( -2.0f * ROTATEFACTOR * rotateX * dt );
498            this->getControllableEntity() ->rotatePitch( 2.0f * ROTATEFACTOR * rotateY * dt );
499           
500            //Roll
501            if ( bHasTargetOrientation_ )
502            {
503                copyTargetOrientation( dt );
504            }
505         
506            //Move
507            this->getControllableEntity() ->moveFrontBack( 1.2f * SPEED * factor * dt );
508            //if still moving, return false
509            return false;
510        }
511        else
512        {     
513           
514            //if finished, return true;
515            return true;
516        }
517    }
518
519    float CommonController::squaredDistanceToTarget()  const
520    {
521        if ( !this->getControllableEntity()  )
522            return 0;
523        if ( !this->target_ || !this->getControllableEntity() )
524            return ( this->getControllableEntity() ->getPosition() .squaredDistance( this->targetPosition_ ) );
525        else
526            return ( this->getControllableEntity() ->getPosition() .squaredDistance( this->positionOfTarget_ ) );
527    }
528   
529    bool CommonController::isLookingAtTarget( float angle )const
530    {
531        if ( !this->getControllableEntity()  || !this->target_ )
532            return false;
533
534        return ( getAngle( this->getControllableEntity() ->getPosition() , 
535            this->getControllableEntity() ->getOrientation()  * WorldEntity::FRONT, this->positionOfTarget_ ) < angle );
536    }
537    bool CommonController::isLooking( ControllableEntity* entityThatLooks, ControllableEntity* entityBeingLookedAt, float angle )const
538    {
539        if ( !entityThatLooks || !entityBeingLookedAt )
540            return false;
541        return ( getAngle( entityThatLooks ->getPosition() , 
542            entityThatLooks->getOrientation()  * WorldEntity::FRONT, 
543            entityBeingLookedAt->getWorldPosition() ) < angle );
544    }
545
546    bool CommonController::canFire() 
547    {
548
549        //no target? why fire?
550        if ( !this->target_ )
551            return false;
552
553        Vector3 newPositionOfTarget = getPredictedPosition( this->getControllableEntity() ->getWorldPosition() , 
554            hardcoded_projectile_speed, this->target_->getWorldPosition() , this->target_->getVelocity() );
555        if ( newPositionOfTarget != Vector3::ZERO )
556        {
557            this->setPositionOfTarget( newPositionOfTarget );
558        }
559
560        float squaredDistance = squaredDistanceToTarget();
561
562        if ( squaredDistance < 25000000.0f && this->isLookingAtTarget( math::pi / 10.0f)) {
563            return true;
564        }
565        else
566        {
567            return false;
568        }
569
570    }
571    void CommonController::doFire() 
572    {
573        if ( !this->target_ || !this->getControllableEntity() )
574        {
575            return;
576        }
577     
578
579       
580        Pawn* pawn = orxonox_cast<Pawn*>( this->getControllableEntity() );
581
582        if ( pawn )
583            //pawn->setAimPosition( this->getControllableEntity() ->getWorldPosition()  + 4000*( this->getControllableEntity() ->getOrientation()  * WorldEntity::FRONT ));
584            pawn->setAimPosition( this->positionOfTarget_ );
585   
586        this->getControllableEntity() ->fire( 0 );
587    }
588   
589
590}
Note: See TracBrowser for help on using the repository browser.