Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 8721 was 8706, checked in by dafrick, 13 years ago

Merging presentation branch back into trunk.
There are many new features and also a lot of other changes and bugfixes, if you want to know, digg through the svn log.
Not everything is yet working as it should, but it should be fairly stable. If you habe any bug reports, just send me an email.

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