Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/orxonox/controllers/ArtificialController.cc @ 10343

Last change on this file since 10343 was 10294, checked in by landauf, 10 years ago

made projectiles slower (and all have the same speed now). makes it possible to dodge a projectile

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