Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/fabienHS15/src/orxonox/weaponsystem/Munition.cc @ 10688

Last change on this file since 10688 was 10688, checked in by fvultier, 9 years ago

There is now a HUD that shows the status of the weapon system: all weapons, weapon modes and their munition. Progress bars show the progress of replenishing munition.

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