Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/fabienHS15/src/orxonox/weaponsystem/Munition.cc @ 10714

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