Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

use actual types instead of 'auto'. only exception is for complicated template types, e.g. when iterating over a map

  • 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 (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 (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.