Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/campaignHS15/src/orxonox/controllers/ArtificialController.cc @ 10848

Last change on this file since 10848 was 10631, checked in by landauf, 9 years ago

the AI sometimes selected its own ship as target while it was steering a rocket. after destroying the rocket, the AI would then try to hit itself which led to random crashes (because of a division by zero error which was previously fixed in r10630).
this commit does not fix the root cause, but detects this case and changes the target.

  • Property svn:eol-style set to native
File size: 11.5 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
29#include "ArtificialController.h"
30#include "core/CoreIncludes.h"
31#include "core/XMLPort.h"
32#include "core/command/ConsoleCommandIncludes.h"
33#include "worldentities/pawns/Pawn.h"
34#include "worldentities/pawns/SpaceShip.h"
35
36#include "weaponsystem/WeaponMode.h"
37#include "weaponsystem/WeaponPack.h"
38#include "weaponsystem/Weapon.h"
39#include "weaponsystem/WeaponSlot.h"
40#include "weaponsystem/WeaponSlot.h"
41
42
43namespace orxonox
44{
45    SetConsoleCommand("ArtificialController", "setbotlevel",      &ArtificialController::setAllBotLevel);
46
47    RegisterClass(ArtificialController);
48
49    ArtificialController::ArtificialController(Context* context) : FormationController(context)
50    {
51        RegisterObject(ArtificialController);
52
53        this->bSetupWorked = false;
54        this->botlevel_ = 0.5f;
55        this->timeout_ = 0;
56        this->currentWaypoint_ = 0;
57        this->setAccuracy(5);
58        this->defaultWaypoint_ = NULL;
59        this->mode_ = DEFAULT;//Vector-implementation: mode_.push_back(DEFAULT);
60    }
61
62    ArtificialController::~ArtificialController()
63    {
64        if (this->isInitialized())
65        {//Vector-implementation: mode_.erase(mode_.begin(),mode_.end());
66            this->waypoints_.clear();
67            this->weaponModes_.clear();
68        }
69    }
70
71    void ArtificialController::XMLPort(Element& xmlelement, XMLPort::Mode mode)
72    {
73        SUPER(ArtificialController, XMLPort, xmlelement, mode);
74
75        XMLPortParam(ArtificialController, "accuracy", setAccuracy, getAccuracy, xmlelement, mode).defaultValues(100.0f);
76        XMLPortObject(ArtificialController, WorldEntity, "waypoints", addWaypoint, getWaypoint,  xmlelement, mode);
77    }
78
79    /**
80        @brief Gets called when ControllableEntity is being changed. Resets the bot when it dies.
81    */
82    void ArtificialController::changedControllableEntity()
83    {
84        FormationController::changedControllableEntity(); // super
85
86        if (!this->getControllableEntity())
87            this->removeFromFormation();
88    }
89
90
91    void ArtificialController::aimAtTarget()
92    {
93        if (!this->target_ || !this->getControllableEntity())
94            return;
95
96        static const float hardcoded_projectile_speed = 750;
97
98        this->targetPosition_ = getPredictedPosition(this->getControllableEntity()->getWorldPosition(), hardcoded_projectile_speed, this->target_->getWorldPosition(), this->target_->getVelocity());
99        this->bHasTargetPosition_ = (this->targetPosition_ != Vector3::ZERO);
100
101        Pawn* pawn = orxonox_cast<Pawn*>(this->getControllableEntity());
102        if (pawn)
103            pawn->setAimPosition(this->targetPosition_);
104    }
105
106    bool ArtificialController::isCloseAtTarget(float distance) const
107    {
108        if (!this->getControllableEntity())
109            return false;
110
111        if (!this->target_)
112            return (this->getControllableEntity()->getPosition().squaredDistance(this->targetPosition_) < distance*distance);
113        else
114            return (this->getControllableEntity()->getPosition().squaredDistance(this->target_->getPosition()) < distance*distance);
115    }
116
117    bool ArtificialController::isLookingAtTarget(float angle) const
118    {
119        if (!this->getControllableEntity())
120            return false;
121
122        return (getAngle(this->getControllableEntity()->getPosition(), this->getControllableEntity()->getOrientation() * WorldEntity::FRONT, this->targetPosition_) < angle);
123    }
124
125    void ArtificialController::abandonTarget(Pawn* target)
126    {
127        if (target == this->target_)
128            this->targetDied();
129    }
130
131    /**
132        @brief DoFire is called when a bot should shoot and decides which weapon is used and whether the bot shoots at all.
133    */
134    void ArtificialController::doFire()
135    {
136        if(!this->bSetupWorked)//setup: find out which weapons are active ! hard coded: laser is "0", lens flare is "1", ...
137        {
138            this->setupWeapons();
139        }
140        else if(this->getControllableEntity() && weaponModes_.size()&&this->bShooting_ && this->isCloseAtTarget((1 + 2*botlevel_)*1000) && this->isLookingAtTarget(math::pi / 20.0f))
141        {
142            int firemode;
143            float random = rnd(1);//
144            if (this->isCloseAtTarget(130) && (firemode = getFiremode("LightningGun")) > -1 )
145            {//LENSFLARE: short range weapon
146                this->getControllableEntity()->fire(firemode); //ai uses lens flare if they're close enough to the target
147            }
148            else if( this->isCloseAtTarget(400) && (random < this->botlevel_) && (firemode = getFiremode("RocketFire")) > -1 )
149            {//ROCKET: mid range weapon
150                this->mode_ = ROCKET; //Vector-implementation: mode_.push_back(ROCKET);
151                this->getControllableEntity()->fire(firemode); //launch rocket
152                if(this->getControllableEntity() && this->target_) //after fire(3) is called, getControllableEntity() refers to the rocket!
153                {
154                    float speed = this->getControllableEntity()->getVelocity().length() - target_->getVelocity().length();
155                    if(!speed) speed = 0.1f;
156                    float distance = target_->getPosition().length() - this->getControllableEntity()->getPosition().length();
157                    this->timeout_= distance/speed*sgn(speed*distance) + 1.8f; //predicted time of target hit (+ tolerance)
158                }
159                else
160                    this->timeout_ = 4.0f; //TODO: find better default value
161            }
162            else if ((firemode = getFiremode("HsW01")) > -1 ) //LASER: default weapon
163                this->getControllableEntity()->fire(firemode);
164        }
165    }
166
167    /**
168        @brief Information gathering: Which weapons are ready to use?
169    */
170    void ArtificialController::setupWeapons() //TODO: Make this function generic!! (at the moment is is based on conventions)
171    {
172        this->bSetupWorked = false;
173        if(this->getControllableEntity())
174        {
175            Pawn* pawn = orxonox_cast<Pawn*>(this->getControllableEntity());
176            if(pawn && pawn->isA(Class(SpaceShip))) //fix for First Person Mode: check for SpaceShip
177            {
178                this->weaponModes_.clear(); // reset previous weapon information
179                WeaponSlot* wSlot = 0;
180                for(int l=0; (wSlot = pawn->getWeaponSlot(l)) ; l++)
181                {
182                    WeaponMode* wMode = 0;
183                    for(int i=0; (wMode = wSlot->getWeapon()->getWeaponmode(i)) ; i++)
184                    {
185                        std::string wName = wMode->getIdentifier()->getName();
186                        if(this->getFiremode(wName) == -1) //only add a weapon, if it is "new"
187                            weaponModes_[wName] = wMode->getMode();
188                    }
189                }
190                if(weaponModes_.size())//at least one weapon detected
191                    this->bSetupWorked = true;
192            }//pawn->weaponSystem_->getMunition(SubclassIdentifier< Munition > *identifier)->getNumMunition (WeaponMode *user);
193        }
194    }
195
196
197    void ArtificialController::setBotLevel(float level)
198    {
199        if (level < 0.0f)
200            this->botlevel_ = 0.0f;
201        else if (level > 1.0f)
202            this->botlevel_ = 1.0f;
203        else
204            this->botlevel_ = level;
205    }
206
207    void ArtificialController::setAllBotLevel(float level)
208    {
209        for (ObjectList<ArtificialController>::iterator it = ObjectList<ArtificialController>::begin(); it != ObjectList<ArtificialController>::end(); ++it)
210            it->setBotLevel(level);
211    }
212
213    void ArtificialController::setPreviousMode()
214    {
215        this->mode_ = DEFAULT; //Vector-implementation: mode_.pop_back();
216    }
217
218    /**
219        @brief Manages boost. Switches between boost usage and boost safe mode.
220    */
221    void ArtificialController::boostControl()
222    {
223        SpaceShip* ship = orxonox_cast<SpaceShip*>(this->getControllableEntity());
224        if(ship == NULL) return;
225        if(ship->getBoostPower()*1.5f > ship->getInitialBoostPower() ) //upper limit ->boost
226            this->getControllableEntity()->boost(true);
227        else if(ship->getBoostPower()*4.0f < ship->getInitialBoostPower()) //lower limit ->do not boost
228            this->getControllableEntity()->boost(false);
229    }
230
231    int ArtificialController::getFiremode(std::string name)
232    {
233        for (std::map< std::string, int >::iterator it = this->weaponModes_.begin(); it != this->weaponModes_.end(); ++it)
234        {
235            if (it->first == name)
236                return it->second;
237        }
238        return -1;
239    }
240
241    void ArtificialController::addWaypoint(WorldEntity* waypoint)
242    {
243        this->waypoints_.push_back(waypoint);
244    }
245
246    WorldEntity* ArtificialController::getWaypoint(unsigned int index) const
247    {
248        if (index < this->waypoints_.size())
249            return this->waypoints_[index];
250        else
251            return 0;
252    }
253
254    /**
255        @brief Adds first waypoint of type name to the waypoint stack, which is within the searchDistance
256        @param name object-name of a point of interest (e.g. "PickupSpawner", "ForceField")
257    */
258    void ArtificialController::updatePointsOfInterest(std::string name, float searchDistance)
259    {
260        WorldEntity* waypoint = NULL;
261        for (ObjectList<WorldEntity>::iterator it = ObjectList<WorldEntity>::begin(); it != ObjectList<WorldEntity>::end(); ++it)
262        {
263            if((*it)->getIdentifier() == ClassByString(name))
264            {
265                ControllableEntity* controllable = this->getControllableEntity();
266                if(!controllable) continue;
267                float actualDistance = ( (*it)->getPosition() - controllable->getPosition() ).length();
268                if(actualDistance > searchDistance || actualDistance < 5.0f) continue;
269                    // TODO: PickupSpawner: adjust waypoint accuracy to PickupSpawner's triggerdistance
270                    // TODO: ForceField: analyze is angle between forcefield boost and own flying direction is acceptable
271                else
272                {
273                    waypoint = *it;
274                    break;
275                }
276            }
277        }
278        if(waypoint)
279            this->waypoints_.push_back(waypoint);
280    }
281
282    /**
283        @brief Adds point of interest depending on context.  TODO: Further Possibilites: "ForceField", "PortalEndPoint", "MovableEntity", "Dock"
284    */
285    void ArtificialController::manageWaypoints()
286    {
287        if(!defaultWaypoint_)
288            this->updatePointsOfInterest("PickupSpawner", 200.0f); // long search radius if there is no default goal
289        else
290            this->updatePointsOfInterest("PickupSpawner", 20.0f); // take pickup en passant if there is a default waypoint
291    }
292
293}
Note: See TracBrowser for help on using the repository browser.