Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 5980 was 5929, checked in by rgrieder, 15 years ago

Merged core5 branch back to the trunk.
Key features include clean level unloading and an extended XML event system.

Two important notes:
Delete your keybindings.ini files! * or you will still get parser errors when loading the key bindings.
Delete build_dir/lib/modules/libgamestates.module! * or orxonox won't start.
Best thing to do is to delete the build folder ;)

  • Property svn:eol-style set to native
File size: 16.0 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        if (!this->bAllowMunitionRefilling_)
256            return false;
257
258        if (this->bStackMunition_)
259        {
260            // If we stack munition, we can always add munition until we reach the limit
261            return (this->getNumMunition(0) < this->getMaxMunition());
262        }
263        else
264        {
265            // Return true if any of the current magazines is not full (loading counts as full although it returns 0 munition)
266            for (std::map<WeaponMode*, Magazine*>::const_iterator it = this->currentMagazines_.begin(); it != this->currentMagazines_.end(); ++it)
267                if (it->second->munition_ < this->maxMunitionPerMagazine_ && it->second->bLoaded_)
268                    return true;
269        }
270
271        return false;
272    }
273
274    bool Munition::addMunition(unsigned int amount)
275    {
276        if (!this->canAddMunition(amount))
277            return false;
278
279        if (this->bStackMunition_)
280        {
281            // Stacking munition means, if a magazine gets full, the munition adds to a new magazine
282            Magazine* magazine = this->getMagazine(0);
283            if (magazine)
284            {
285                // Add the whole amount
286                magazine->munition_ += amount;
287
288                // Add new magazines if the current magazine is overfull
289                while (magazine->munition_ > this->maxMunitionPerMagazine_)
290                {
291                    magazine->munition_ -= this->maxMunitionPerMagazine_;
292                    this->magazines_++;
293                }
294
295                // If we reached the limit, reduze both magazines and munition to the maximum
296                if (this->magazines_ >= this->maxMagazines_)
297                {
298                    this->magazines_ = this->maxMagazines_ - 1;
299                    magazine->munition_ = this->maxMunitionPerMagazine_;
300                }
301
302                return true;
303            }
304
305            // Something went wrong
306            return false;
307        }
308        else
309        {
310            // Share the munition equally to the current magazines
311            while (amount > 0)
312            {
313                bool change = false;
314                for (std::map<WeaponMode*, Magazine*>::iterator it = this->currentMagazines_.begin(); it != this->currentMagazines_.end(); ++it)
315                {
316                    // Add munition if the magazine isn't full (but only to loaded magazines)
317                    if (amount > 0 && it->second->munition_ < this->maxMunitionPerMagazine_ && it->second->bLoaded_)
318                    {
319                        it->second->munition_++;
320                        amount--;
321                        change = true;
322                    }
323                }
324
325                // If there was no change in a loop, all magazines are full (or locked due to loading)
326                if (!change)
327                    break;
328            }
329
330            return true;
331        }
332    }
333
334    bool Munition::canAddMagazines(unsigned int amount) const
335    {
336        if (this->bStackMunition_)
337            // If we stack munition, we can always add new magazines because they contribute directly to the munition
338            return (this->getNumMunition(0) < this->getMaxMunition());
339        else
340            // If we don't stack munition, we're more limited
341            return ((this->currentMagazines_.size() + this->magazines_) < this->maxMagazines_);
342    }
343
344    bool Munition::addMagazines(unsigned int amount)
345    {
346        if (!this->canAddMagazines(amount))
347            return false;
348
349        // Calculate how many magazines are needed
350        int needed_magazines = this->maxMagazines_ - this->magazines_ - this->currentMagazines_.size();
351
352        // 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)
353        if (needed_magazines <= 0 && !this->bStackMunition_)
354            return false;
355
356        if (amount <= static_cast<unsigned int>(needed_magazines))
357        {
358            // We need more magazines than we get, so just add them
359            this->magazines_ += amount;
360        }
361        else
362        {
363            // We get more magazines than we need, so just add the needed amount
364            this->magazines_ += needed_magazines;
365            if (this->bStackMunition_)
366            {
367                // We stack munition, so the additional amount contributes directly to the munition of the current magazine
368                Magazine* magazine = this->getMagazine(0);
369                if (magazine)
370                    magazine->munition_ = this->maxMunitionPerMagazine_;
371            }
372        }
373
374        return true;
375    }
376
377    bool Munition::canRemoveMagazines(unsigned int amount) const
378    {
379        if (this->bStackMunition_)
380        {
381            if (this->magazines_ >= amount)
382            {
383                // We have enough magazines
384                return true;
385            }
386            else if (this->magazines_ == amount - 1)
387            {
388                // We lack one magazine, check if the current magazine is still full, if yes we're fine
389                Magazine* magazine = this->getMagazine(0);
390                if (magazine && magazine->munition_ == this->maxMunitionPerMagazine_)
391                    return true;
392            }
393            else
394            {
395                // We don't have enough magazines
396                return false;
397            }
398        }
399        else
400        {
401            // In case we're not stacking munition, just check the number of magazines
402            return (this->magazines_ >= amount);
403        }
404
405        return false;
406    }
407
408    bool Munition::removeMagazines(unsigned int amount)
409    {
410        if (!this->canRemoveMagazines(amount))
411            return false;
412
413        if (this->magazines_ >= amount)
414        {
415            // We have enough magazines, just remove the amount
416            this->magazines_ -= amount;
417        }
418        else if (this->bStackMunition_)
419        {
420            // We don't have enough magazines, but we're stacking munition, so additionally remove the bullets from the current magazine
421            this->magazines_ = 0;
422            Magazine* magazine = this->getMagazine(0);
423            if (magazine)
424                magazine->munition_ = 0;
425        }
426
427        return true;
428    }
429
430    bool Munition::dropMagazine(WeaponMode* user)
431    {
432        // If we use separate magazines, we need a user
433        if (this->bUseSeparateMagazines_ && !user)
434            return false;
435
436        // If we don't use separate magazines, set user to 0
437        if (!this->bUseSeparateMagazines_)
438            user = 0;
439
440        // Remove the current magazine for the given user
441        std::map<WeaponMode*, Magazine*>::iterator it = this->currentMagazines_.find(user);
442        if (it != this->currentMagazines_.end())
443        {
444            delete it->second;
445            this->currentMagazines_.erase(it);
446            return true;
447        }
448
449        return false;
450    }
451
452
453    /////////////////////
454    // Magazine struct //
455    /////////////////////
456    Munition::Magazine::Magazine(Munition* munition, bool bUseReloadTime)
457    {
458        this->munition_ = 0;
459        this->bLoaded_ = false;
460
461        if (bUseReloadTime && (munition->reloadTime_ > 0 || munition->bStackMunition_))
462        {
463            Executor* executor = createExecutor(createFunctor(&Magazine::loaded, this));
464            executor->setDefaultValues(munition);
465
466            this->loadTimer_.setTimer(munition->reloadTime_, false, executor);
467        }
468        else
469            this->loaded(munition);
470    }
471
472    void Munition::Magazine::loaded(Munition* munition)
473    {
474        this->bLoaded_ = true;
475        this->munition_ = munition->maxMunitionPerMagazine_;
476    }
477}
Note: See TracBrowser for help on using the repository browser.