Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/presentation/src/orxonox/weaponsystem/Munition.cc @ 8740

Last change on this file since 8740 was 8612, checked in by landauf, 14 years ago

fixed possible bug in LastTeamStanding

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