Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/core6/src/orxonox/controllers/ArtificialController.cc @ 9629

Last change on this file since 9629 was 9629, checked in by landauf, 11 years ago

BaseObject now requires a Context instead of a creator (BaseObject*) in its constructor.
Namespace, Level, and Scene inherit from Context

  • 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(Context* context) : FormationController(context)
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.