Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 3053 was 3053, checked in by landauf, 15 years ago

merged weapons branch back to trunk

  • 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 "OrxonoxStableHeaders.h"
31#include "Munition.h"
32
33#include "core/CoreIncludes.h"
34
35namespace orxonox
36{
37    CreateFactory(Munition);
38
39    Munition::Munition(BaseObject* creator) : BaseObject(creator)
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 (std::map<WeaponMode*, Magazine*>::iterator it = this->currentMagazines_.begin(); it != this->currentMagazines_.end(); ++it)
58            delete it->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 0;
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 0;
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 = 0;
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        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        if (this->bStackMunition_)
339            // If we stack munition, we can always add new magazines because they contribute directly to the munition
340            return (this->getNumMunition(0) < this->getMaxMunition());
341        else
342            // If we don't stack munition, we're more limited
343            return ((this->currentMagazines_.size() + this->magazines_) < this->maxMagazines_);
344    }
345
346    bool Munition::addMagazines(unsigned int amount)
347    {
348        if (!this->canAddMagazines(amount))
349            return false;
350
351        // Calculate how many magazines are needed
352        int needed_magazines = this->maxMagazines_ - this->magazines_ - this->currentMagazines_.size();
353
354        // 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)
355        if (needed_magazines <= 0 && !this->bStackMunition_)
356            return false;
357
358        if (amount <= needed_magazines)
359        {
360            // We need more magazines than we get, so just add them
361            this->magazines_ += amount;
362        }
363        else
364        {
365            // We get more magazines than we need, so just add the needed amount
366            this->magazines_ += needed_magazines;
367            if (this->bStackMunition_)
368            {
369                // We stack munition, so the additional amount contributes directly to the munition of the current magazine
370                Magazine* magazine = this->getMagazine(0);
371                if (magazine)
372                    magazine->munition_ = this->maxMunitionPerMagazine_;
373            }
374        }
375
376        return true;
377    }
378
379    bool Munition::canRemoveMagazines(unsigned int amount) const
380    {
381        if (this->bStackMunition_)
382        {
383            if (this->magazines_ >= amount)
384            {
385                // We have enough magazines
386                return true;
387            }
388            else if (this->magazines_ == amount - 1)
389            {
390                // We lack one magazine, check if the current magazine is still full, if yes we're fine
391                Magazine* magazine = this->getMagazine(0);
392                if (magazine && magazine->munition_ == this->maxMunitionPerMagazine_)
393                    return true;
394            }
395            else
396            {
397                // We don't have enough magazines
398                return false;
399            }
400        }
401        else
402        {
403            // In case we're not stacking munition, just check the number of magazines
404            return (this->magazines_ >= amount);
405        }
406
407        return false;
408    }
409
410    bool Munition::removeMagazines(unsigned int amount)
411    {
412        if (!this->canRemoveMagazines(amount))
413            return false;
414
415        if (this->magazines_ >= amount)
416        {
417            // We have enough magazines, just remove the amount
418            this->magazines_ -= amount;
419        }
420        else if (this->bStackMunition_)
421        {
422            // We don't have enough magazines, but we're stacking munition, so additionally remove the bullets from the current magazine
423            this->magazines_ = 0;
424            Magazine* magazine = this->getMagazine(0);
425            if (magazine)
426                magazine->munition_ = 0;
427        }
428
429        return true;
430    }
431
432    bool Munition::dropMagazine(WeaponMode* user)
433    {
434        // If we use separate magazines, we need a user
435        if (this->bUseSeparateMagazines_ && !user)
436            return false;
437
438        // If we don't use separate magazines, set user to 0
439        if (!this->bUseSeparateMagazines_)
440            user = 0;
441
442        // Remove the current magazine for the given user
443        std::map<WeaponMode*, Magazine*>::iterator it = this->currentMagazines_.find(user);
444        if (it != this->currentMagazines_.end())
445        {
446            delete it->second;
447            this->currentMagazines_.erase(it);
448            return true;
449        }
450
451        return false;
452    }
453
454
455    /////////////////////
456    // Magazine struct //
457    /////////////////////
458    Munition::Magazine::Magazine(Munition* munition, bool bUseReloadTime)
459    {
460        this->munition_ = 0;
461        this->bLoaded_ = false;
462
463        if (bUseReloadTime && (munition->reloadTime_ > 0 || munition->bStackMunition_))
464        {
465            ExecutorMember<Magazine>* executor = createExecutor(createFunctor(&Magazine::loaded));
466            executor->setDefaultValues(munition);
467
468            this->loadTimer_.setTimer(munition->reloadTime_, false, this, executor);
469        }
470        else
471            this->loaded(munition);
472    }
473
474    void Munition::Magazine::loaded(Munition* munition)
475    {
476        this->bLoaded_ = true;
477        this->munition_ = munition->maxMunitionPerMagazine_;
478    }
479}
Note: See TracBrowser for help on using the repository browser.