Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/formationupdate/src/orxonox/controllers/ArtificialController.cc @ 10191

Last change on this file since 10191 was 9252, checked in by landauf, 13 years ago

added missing call to RegisterObject (it crashed on windows)
moved XMLPort from WaypointController to ArtificialController

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