Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/presentationHS15/src/orxonox/weaponsystem/Munition.cc @ 10961

Last change on this file since 10961 was 10961, checked in by maxima, 10 years ago

Merged presentation and fabiens branch. Had to modify hoverHUD and invaderHUD, because the text of the healthbar wasn't correctly displayed and the weapon settings of the hovership.

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