Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/cpp11_v2/src/orxonox/weaponsystem/Munition.cc @ 10990

Last change on this file since 10990 was 10917, checked in by landauf, 9 years ago

made mapEntry const& wherever possible

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