Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/orxonox/weaponsystem/Munition.cc @ 12023

Last change on this file since 12023 was 11083, checked in by muemart, 9 years ago

Fix some clang-tidy warnings.
Also, Serialise.h was doing some C-style casts that ended up being const casts. I moved those const casts as close to the source as possible and changed the loadAndIncrease functions to not do that.

  • Property svn:eol-style set to native
File size: 19.1 KB
RevLine 
[2049]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 *
[2918]22 *   Authors:
[2049]23 *      Martin Polak
[2918]24 *      Fabian 'x3n' Landau
[2049]25 *   Co-authors:
[11052]26 *      Fabien Vultier
[2049]27 *
28 */
29
[2912]30#include "Munition.h"
[8729]31
[2049]32#include "core/CoreIncludes.h"
[8729]33#include "core/command/Executor.h"
[11052]34#include "core/XMLPort.h"
[2049]35
36namespace orxonox
37{
[9667]38    RegisterClass(Munition);
[2662]39
[9667]40    Munition::Munition(Context* context) : BaseObject(context)
[2049]41    {
42        RegisterObject(Munition);
[2912]43
[2918]44        this->maxMunitionPerMagazine_ = 10;
45        this->maxMagazines_ = 10;
[11052]46        this->unassignedMagazines_ = 10;
[2918]47
[11052]48        this->deployment_ = MunitionDeployment::Stack;
[2918]49        this->bAllowMunitionRefilling_ = true;
50        this->bAllowMultiMunitionRemovementUnderflow_ = true;
51
[11052]52        this->reloadTime_ = 0.5f;
53
[11071]54        this->lastFilledWeaponMode_ = nullptr;
[2049]55    }
56
57    Munition::~Munition()
58    {
[11071]59        for (const auto& mapEntry : this->assignedMagazines_)
60            delete mapEntry.second;
[2049]61    }
62
[11052]63    void Munition::XMLPort(Element& xmlelement, XMLPort::Mode mode)
64    {
65        SUPER(Munition, XMLPort, xmlelement, mode);
66
67        XMLPortParam(Munition, "initialmagazines", setNumMagazines, getNumMagazines, xmlelement, mode);
68        XMLPortParam(Munition, "maxmagazines", setMaxMagazines, getMaxMagazines, xmlelement, mode);
69        XMLPortParam(Munition, "munitionpermagazine", setMaxMunitionPerMagazine, getMaxMunitionPerMagazine, xmlelement, mode);
70    }
71
[2918]72    Munition::Magazine* Munition::getMagazine(WeaponMode* user) const
[2096]73    {
[11052]74        if (deployment_ == MunitionDeployment::Separate)
[2918]75        {
76            // For separated magazines we definitively need a given user
77            if (!user)
[11071]78                return nullptr;
[2918]79
80            // Use the map to get the magazine assigned to the given user
[11052]81            std::map<WeaponMode*, Magazine*>::const_iterator it = this->assignedMagazines_.find(user);
82            if (it != this->assignedMagazines_.end())
[2918]83                return it->second;
84        }
[2662]85        else
[2918]86        {
87            // We don't use separate magazines for each user, so just take the first magazine
[11052]88            if (this->assignedMagazines_.size() > 0)
89                return this->assignedMagazines_.begin()->second;
[2918]90        }
91
[11071]92        return nullptr;
[2662]93    }
[2096]94
[2918]95    unsigned int Munition::getNumMunition(WeaponMode* user) const
[2662]96    {
[2918]97        Magazine* magazine = this->getMagazine(user);
98        if (magazine)
99        {
[11052]100            if (deployment_ == MunitionDeployment::Stack)
[2918]101                // With stacked munition every magazine contributes to the total amount
[11052]102                return this->maxMunitionPerMagazine_ * this->unassignedMagazines_ + magazine->munition_;
[2918]103            else
104                // Wihtout stacked munition we just consider the current magazine
105                return magazine->munition_;
106        }
107        return 0;
108    }
109
110    unsigned int Munition::getNumMunitionInCurrentMagazine(WeaponMode* user) const
111    {
112        // In contrast to getNumMunition() we really just consider the current magazine, even if we're stacking munition
113        Magazine* magazine = this->getMagazine(user);
114        if (magazine)
115            return magazine->munition_;
[2662]116        else
117            return 0;
[2096]118    }
119
[2918]120    unsigned int Munition::getNumMagazines() const
121    {
[11052]122        if (deployment_ == MunitionDeployment::Stack)
[2918]123        {
124            // If we stack munition and the current magazine is still full, it counts too
125            Magazine* magazine = this->getMagazine(0);
126            if (magazine && magazine->munition_ == this->maxMunitionPerMagazine_)
[11052]127                return this->unassignedMagazines_ + 1;
[2918]128        }
[2662]129
[11052]130        return this->unassignedMagazines_;
[2918]131    }
[2662]132
[11052]133    void Munition::setNumMagazines(unsigned int numMagazines)
134    {
135        this->unassignedMagazines_ = numMagazines;
136    }
137
[2918]138    unsigned int Munition::getMaxMunition() const
[2662]139    {
[11052]140        if (deployment_ == MunitionDeployment::Stack)
[2918]141            return this->maxMunitionPerMagazine_ * this->maxMagazines_;
142        else
143            return this->maxMunitionPerMagazine_;
[2662]144    }
145
[2918]146    bool Munition::canTakeMunition(unsigned int amount, WeaponMode* user) const
[2662]147    {
[2918]148        Magazine* magazine = this->getMagazine(user);
149        if (magazine && magazine->bLoaded_)
150        {
151            unsigned int munition = magazine->munition_;
152
[11052]153            // If we stack munition, we don't care about the current magazine - we just need enough munition in total
154            if (deployment_ == MunitionDeployment::Stack)
155            {
156                munition += this->maxMunitionPerMagazine_ * this->unassignedMagazines_;
157            }
[2918]158
159            if (munition == 0)
[11052]160            {
[2918]161                // Absolutely no munition - no chance to take munition
162                return false;
[11052]163            }
[2918]164            else if (this->bAllowMultiMunitionRemovementUnderflow_)
[11052]165            {
[2918]166                // We're not empty AND we allow underflow, so this will always work
167                return true;
[11052]168            }
[2918]169            else
[11052]170            {
[2918]171                // We don't allow underflow, so we have to check the amount
172                return (munition >= amount);
[11052]173            }
174
[2918]175        }
176        return false;
[2662]177    }
178
[2918]179    bool Munition::takeMunition(unsigned int amount, WeaponMode* user)
[2662]180    {
[2918]181        if (!this->canTakeMunition(amount, user))
182            return false;
183
184        Magazine* magazine = this->getMagazine(user);
185        if (magazine && magazine->bLoaded_)
[2662]186        {
[2918]187            if (magazine->munition_ >= amount)
188            {
189                // Enough munition
190                magazine->munition_ -= amount;
191                return true;
192            }
193            else
194            {
195                // Not enough munition
[11052]196                if (deployment_ == MunitionDeployment::Stack)
[2918]197                {
198                    // We stack munition, so just take what we can and then load the next magazine
199                    amount -= magazine->munition_;
200                    magazine->munition_ = 0;
201
[11071]202                    if (this->reload(nullptr))
[2918]203                        // Successfully reloaded, continue recursively
204                        return this->takeMunition(amount, 0);
205                    else
206                        // We don't have more magazines, so let's just hope we allow underflow
207                        return this->bAllowMultiMunitionRemovementUnderflow_;
208                }
209                else
210                {
211                    // We don't stack, so we can only take munition if this is allowed
212                    if (magazine->munition_ > 0 && this->bAllowMultiMunitionRemovementUnderflow_)
213                    {
[7847]214                        magazine->munition_ = 0;
[2918]215                        return true;
216                    }
217                }
218            }
[2662]219        }
[2918]220        return false;
221    }
222
223    bool Munition::canReload() const
224    {
225        // As long as we have enough magazines (and don't stack munition) we can reload
[11052]226        return (this->unassignedMagazines_ > 0 && deployment_ != MunitionDeployment::Stack);
[2918]227    }
228
229    bool Munition::needReload(WeaponMode* user) const
230    {
231        Magazine* magazine = this->getMagazine(user);
232        if (magazine)
233        {
[11052]234            if (deployment_ == MunitionDeployment::Stack)
[2918]235                // With stacked munition, we never have to reload
236                return false;
237            else
238                // We need to reload if an already loaded magazine is empty
239                return (magazine->bLoaded_ && magazine->munition_ == 0);
240        }
[2662]241        else
[2918]242            // No magazine - we definitively need to reload
243            return true;
[2662]244    }
245
[2918]246    bool Munition::reload(WeaponMode* user, bool bUseReloadTime)
[2662]247    {
[2918]248        // Don't reload if we're already reloading
249        Magazine* magazine = this->getMagazine(user);
250        if (magazine && !magazine->bLoaded_)
251            return false;
252
253        // Check if we actually can reload
[11052]254        if (this->unassignedMagazines_ == 0)
[2918]255            return false;
256
257        // If we use separate magazines for each user, we definitively need a user given
[11052]258        if (deployment_ == MunitionDeployment::Separate && !user)
[2918]259            return false;
260
261        // If we don't use separate magazines, set user to 0
[11052]262        if (deployment_ != MunitionDeployment::Separate)
263        {
[11071]264            user = nullptr;
[11052]265        }
[2918]266
267        // Remove the current magazine for the given user
[11052]268        std::map<WeaponMode*, Magazine*>::iterator it = this->assignedMagazines_.find(user);
269        if (it != this->assignedMagazines_.end())
[2662]270        {
[11052]271            if (it->first == lastFilledWeaponMode_)
272            {
[11071]273                lastFilledWeaponMode_ = nullptr;
[11052]274            }           
[2918]275            delete it->second;
[11052]276            this->assignedMagazines_.erase(it);
[2662]277        }
[2918]278
279        // Load a new magazine
[11052]280        this->assignedMagazines_[user] = new Magazine(this, bUseReloadTime);
281        this->unassignedMagazines_--;
[2918]282
283        return true;
284    }
285
286    bool Munition::canAddMunition(unsigned int amount) const
287    {
[8706]288        // TODO: 'amount' is not used
289
[2918]290        if (!this->bAllowMunitionRefilling_)
291            return false;
292
[11052]293        if (deployment_ == MunitionDeployment::Stack)
[2918]294        {
295            // If we stack munition, we can always add munition until we reach the limit
296            return (this->getNumMunition(0) < this->getMaxMunition());
297        }
[2662]298        else
[2918]299        {
300            // Return true if any of the current magazines is not full (loading counts as full although it returns 0 munition)
[11071]301            for (const auto& mapEntry : this->assignedMagazines_)
302                if (mapEntry.second->munition_ < this->maxMunitionPerMagazine_ && mapEntry.second->bLoaded_)
[2918]303                    return true;
304        }
305
306        return false;
[2662]307    }
308
[2918]309    bool Munition::addMunition(unsigned int amount)
310    {
311        if (!this->canAddMunition(amount))
312            return false;
[2662]313
[11052]314        if (deployment_ == MunitionDeployment::Stack)
[2918]315        {
316            // Stacking munition means, if a magazine gets full, the munition adds to a new magazine
[11071]317            Magazine* magazine = this->getMagazine(nullptr);
[2918]318            if (magazine)
319            {
320                // Add the whole amount
321                magazine->munition_ += amount;
322
323                // Add new magazines if the current magazine is overfull
324                while (magazine->munition_ > this->maxMunitionPerMagazine_)
325                {
326                    magazine->munition_ -= this->maxMunitionPerMagazine_;
[11052]327                    this->unassignedMagazines_++;
[2918]328                }
329
[11052]330                // If we reached the limit, reduce both magazines and munition to the maximum
331                if (this->unassignedMagazines_ >= this->maxMagazines_)
[2918]332                {
[11052]333                    this->unassignedMagazines_ = this->maxMagazines_;
[2918]334                    magazine->munition_ = this->maxMunitionPerMagazine_;
335                }
336
337                return true;
338            }
339
340            // Something went wrong
341            return false;
342        }
343        else
344        {
[11052]345            std::map<WeaponMode*, Magazine*>::iterator it;
346
[11071]347            // If the pointer to the weapon mode whose magazine got munition added to is nullptr, then set the iterator to the beginning of the map
[11052]348            // Otherwise set it to the next weapon mode
[11071]349            if (lastFilledWeaponMode_ == nullptr)
[11052]350            {
351                it = this->assignedMagazines_.begin();
352            }
353            else
354            {
355                it = this->assignedMagazines_.find(lastFilledWeaponMode_);
356                ++ it;
357            }
358
[2918]359            // Share the munition equally to the current magazines
[11052]360            bool firstLoop = true;
[2918]361            while (amount > 0)
362            {
363                bool change = false;
[11052]364                while (it != this->assignedMagazines_.end())
[2918]365                {
366                    // Add munition if the magazine isn't full (but only to loaded magazines)
367                    if (amount > 0 && it->second->munition_ < this->maxMunitionPerMagazine_ && it->second->bLoaded_)
368                    {
369                        it->second->munition_++;
370                        amount--;
[11052]371                        lastFilledWeaponMode_ = it->first;
[2918]372                        change = true;
373                    }
[11052]374
375                    ++it;
[2918]376                }
377
378                // If there was no change in a loop, all magazines are full (or locked due to loading)
[11052]379                // Because the first loop does not start at the beginning of the map we have to treat is separately
380                if (!change && !firstLoop)
381                {
[2918]382                    break;
[11052]383                }                   
384
385                it = this->assignedMagazines_.begin();
386                firstLoop = false;
[2918]387            }
388
389            return true;
390        }
391    }
392
393    bool Munition::canAddMagazines(unsigned int amount) const
[2662]394    {
[8706]395        // TODO: 'amount' is not used
396
[11052]397        if (deployment_ == MunitionDeployment::Stack)
[2918]398            // If we stack munition, we can always add new magazines because they contribute directly to the munition
399            return (this->getNumMunition(0) < this->getMaxMunition());
400        else
401            // If we don't stack munition, we're more limited
[11052]402            return ((this->assignedMagazines_.size() + this->unassignedMagazines_) < this->maxMagazines_);
[2662]403    }
404
[11052]405    unsigned int Munition::addMagazines(unsigned int amount)
[2662]406    {
[11052]407        unsigned int addedMagazines = 0;
408
[2918]409        if (!this->canAddMagazines(amount))
[11052]410            return 0;
[2918]411
412        // Calculate how many magazines are needed
[11052]413        int needed_magazines = this->maxMagazines_ - this->unassignedMagazines_ - this->assignedMagazines_.size();
[2918]414
415        // If zero or less magazines are needed, we definitively don't need more magazines (unless we stack munition - then a magazine contributes directly to the munition)
[11052]416        if (needed_magazines <= 0 && deployment_ != MunitionDeployment::Stack)
417            return 0;
[2918]418
[3301]419        if (amount <= static_cast<unsigned int>(needed_magazines))
[2918]420        {
421            // We need more magazines than we get, so just add them
[11052]422            this->unassignedMagazines_ += amount;
423            addedMagazines = amount;
[2918]424        }
425        else
426        {
427            // We get more magazines than we need, so just add the needed amount
[11052]428            this->unassignedMagazines_ += needed_magazines;
429            addedMagazines = needed_magazines;
430            if (deployment_ == MunitionDeployment::Stack)
[2918]431            {
432                // We stack munition, so the additional amount contributes directly to the munition of the current magazine
433                Magazine* magazine = this->getMagazine(0);
434                if (magazine)
435                    magazine->munition_ = this->maxMunitionPerMagazine_;
436            }
437        }
438
[11052]439        // Reload as many empty magazines as possible
440        // Double loop and break is needed because the reload function changes the assigned magazines. This may confuse the iterator.
441        for (unsigned int i = 0; i < addedMagazines; ++i)
442        {
[11071]443            for (const auto& mapEntry : this->assignedMagazines_)
[11052]444            {
[11071]445                if (needReload(mapEntry.first))
[11052]446                {
[11071]447                    reload(mapEntry.first);
[11052]448                    break;
449                }
450            }
451        }
452
453        return addedMagazines;
[2662]454    }
[2918]455
456    bool Munition::canRemoveMagazines(unsigned int amount) const
457    {
[11052]458        if (deployment_ == MunitionDeployment::Stack)
[2918]459        {
[11052]460            if (this->unassignedMagazines_ >= amount)
[2918]461            {
462                // We have enough magazines
463                return true;
464            }
[11052]465            else if (this->unassignedMagazines_ == amount - 1)
[2918]466            {
467                // We lack one magazine, check if the current magazine is still full, if yes we're fine
468                Magazine* magazine = this->getMagazine(0);
469                if (magazine && magazine->munition_ == this->maxMunitionPerMagazine_)
470                    return true;
471            }
472            else
473            {
474                // We don't have enough magazines
475                return false;
476            }
477        }
478        else
479        {
480            // In case we're not stacking munition, just check the number of magazines
[11052]481            return (this->unassignedMagazines_ >= amount);
[2918]482        }
[3053]483
484        return false;
[2918]485    }
486
487    bool Munition::removeMagazines(unsigned int amount)
488    {
489        if (!this->canRemoveMagazines(amount))
490            return false;
491
[11052]492        if (this->unassignedMagazines_ >= amount)
[2918]493        {
494            // We have enough magazines, just remove the amount
[11052]495            this->unassignedMagazines_ -= amount;
[2918]496        }
[11052]497        else if (deployment_ == MunitionDeployment::Stack)
[2918]498        {
499            // We don't have enough magazines, but we're stacking munition, so additionally remove the bullets from the current magazine
[11052]500            this->unassignedMagazines_ = 0;
[2918]501            Magazine* magazine = this->getMagazine(0);
502            if (magazine)
503                magazine->munition_ = 0;
504        }
505
506        return true;
507    }
508
509    bool Munition::dropMagazine(WeaponMode* user)
510    {
511        // If we use separate magazines, we need a user
[11052]512        if (deployment_ == MunitionDeployment::Separate && !user)
[2918]513            return false;
514
515        // If we don't use separate magazines, set user to 0
[11052]516        if (deployment_ != MunitionDeployment::Separate)
[11071]517            user = nullptr;
[2918]518
519        // Remove the current magazine for the given user
[11052]520        std::map<WeaponMode*, Magazine*>::iterator it = this->assignedMagazines_.find(user);
521        if (it != this->assignedMagazines_.end())
[2918]522        {
[11052]523            if (it->first == lastFilledWeaponMode_)
524            {
[11071]525                lastFilledWeaponMode_ = nullptr;
[11052]526            }
[2918]527            delete it->second;
[11052]528            this->assignedMagazines_.erase(it);           
[2918]529            return true;
530        }
531
532        return false;
533    }
534
535
536    /////////////////////
537    // Magazine struct //
538    /////////////////////
539    Munition::Magazine::Magazine(Munition* munition, bool bUseReloadTime)
540    {
541        this->munition_ = 0;
542        this->bLoaded_ = false;
543
[11052]544        if (bUseReloadTime && munition->reloadTime_ > 0 && munition->deployment_ != MunitionDeployment::Stack)
[2918]545        {
[11083]546            const ExecutorPtr executor = createExecutor(createFunctor(&Magazine::loaded, this));
[2918]547            executor->setDefaultValues(munition);
548
[5929]549            this->loadTimer_.setTimer(munition->reloadTime_, false, executor);
[2918]550        }
551        else
552            this->loaded(munition);
553    }
554
555    void Munition::Magazine::loaded(Munition* munition)
556    {
557        this->bLoaded_ = true;
558        this->munition_ = munition->maxMunitionPerMagazine_;
559    }
[11071]560}
Note: See TracBrowser for help on using the repository browser.