Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/archive/Presentation_FS16/src/orxonox/weaponsystem/WeaponMode.cc

Last change on this file was 11208, checked in by fvultier, 8 years ago

merged discharger, thats the last one

  • Property svn:eol-style set to native
File size: 14.7 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 *   Author:
23 *      Martin Polak
24 *      Fabian 'x3n' Landau
25 *   Co-authors:
26 *      Johannes Sager
27 *
28 */
29
30#include "WeaponMode.h"
31
32#include "core/CoreIncludes.h"
33#include "core/XMLPort.h"
34#include "controllers/Controller.h"
35#include "worldentities/pawns/Pawn.h"
36
37#include "Munition.h"
38#include "Weapon.h"
39#include "WeaponPack.h"
40#include "WeaponSystem.h"
41#include "WeaponSlot.h"
42
43#include "sound/WorldSound.h"
44
45namespace orxonox
46{
47    RegisterAbstractClass(WeaponMode).inheritsFrom<BaseObject>();
48
49    WeaponMode::WeaponMode(Context* context) : BaseObject(context)
50    {
51        RegisterObject(WeaponMode);
52
53        this->weapon_ = nullptr;
54        this->mode_ = WeaponSystem::WEAPON_MODE_UNASSIGNED;
55
56        this->munition_ = nullptr;
57        this->initialMunition_ = 0;
58        this->initialMagazines_ = 0;
59        this->munitionPerShot_ = 1;
60
61        this->reloadTime_ = 0.25;
62        this->bReloading_ = false;
63        this->bAutoReload_ = true;
64        this->bParallelReload_ = true;
65        this->chargeable_ = false;              // most weapons are not chargeable
66        this->charges_ = 0;                     // always start with no charges
67        this->maxCharges_ = 100;                // default maximum charges one can have are 100
68
69        this->reloadTimer_.setTimer(0.0f, false, createExecutor(createFunctor(&WeaponMode::reloaded, this)));
70        this->reloadTimer_.stopTimer();
71
72        this->damage_ = 0;
73        this->healthdamage_ = 0;
74        this->shielddamage_ = 0;
75
76        this->muzzleOffset_ = Vector3::ZERO;
77        this->muzzlePosition_ = Vector3::ZERO;
78        this->muzzleOrientation_ = Quaternion::IDENTITY;
79
80        hudImageString_ = "Orxonox/WSHUD_WM_Unknown";
81
82        this->fireSoundPath_ = BLANKSTRING;
83        this->fireSoundVolume_ = 1.0;
84        this->fireSounds_.clear();
85
86        this->reloadSoundPath_ = BLANKSTRING;
87        this->reloadSoundVolume_ = 1.0;
88        this->reloadSound_ = nullptr;
89    }
90
91    WeaponMode::~WeaponMode()
92    {
93        if (this->isInitialized())
94        {
95            for (auto sound : fireSounds_)
96            {
97                sound->destroy();
98            }
99        }
100    }
101
102    void WeaponMode::XMLPort(Element& xmlelement, XMLPort::Mode mode)
103    {
104        SUPER(WeaponMode, XMLPort, xmlelement, mode);
105
106        XMLPortParam(WeaponMode, "mode",             setMode,             getMode,             xmlelement, mode);
107
108        XMLPortParam(WeaponMode, "munitiontype",     setMunitionName,     getMunitionName,     xmlelement, mode);
109        XMLPortParam(WeaponMode, "initialmunition",  setInitialMunition,  getInitialMunition,  xmlelement, mode);
110        XMLPortParam(WeaponMode, "initialmagazines", setInitialMagazines, getInitialMagazines, xmlelement, mode);
111        XMLPortParam(WeaponMode, "munitionpershot",  setMunitionPerShot,  getMunitionPerShot,  xmlelement, mode);
112
113        XMLPortParam(WeaponMode, "reloadtime",       setReloadTime,       getReloadTime,       xmlelement, mode);
114        XMLPortParam(WeaponMode, "autoreload",       setAutoReload,       getAutoReload,       xmlelement, mode).description("If true, the weapon reloads the magazine automatically");
115        XMLPortParam(WeaponMode, "parallelreload",   setParallelReload,   getParallelReload,   xmlelement, mode).description("If true, the weapon reloads in parallel to the magazine reloading");
116
117        XMLPortParam(WeaponMode, "damage",           setDamage,           getDamage,           xmlelement, mode);
118        XMLPortParam(WeaponMode, "healthdamage",     setHealthDamage,     getHealthDamage,     xmlelement, mode);
119        XMLPortParam(WeaponMode, "shielddamage",     setShieldDamage,     getShieldDamage,     xmlelement, mode);
120        XMLPortParam(WeaponMode, "muzzleoffset",     setMuzzleOffset,     getMuzzleOffset,     xmlelement, mode);
121    }
122
123    bool WeaponMode::fire(float* reloadTime)
124    {
125        (*reloadTime) = this->reloadTime_;
126        // Fireing is only possible if this weapon mode is not reloading and there is enough munition
127        if (!this->bReloading_ && this->munition_ && this->munition_->takeMunition(this->munitionPerShot_, this))
128        {
129            float tempReloadtime = this->reloadTime_;
130
131            if (this->bAutoReload_ && this->munition_->needReload(this))
132            {
133                if (this->munition_->reload(this))
134                {
135                    // If true, the weapon reloads in parallel to the magazine reloading
136                    if (this->bParallelReload_)
137                    {
138                        // The time needed to reload is the maximum of the reload time of the weapon mode and the magazine.
139                        tempReloadtime = std::max(this->reloadTime_, this->munition_->getReloadTime());
140                    }                       
141                    else
142                    {
143                        // The time needed to reload is the sum of the reload time of the weapon mode and the magazine.
144                        tempReloadtime = this->reloadTime_ + this->munition_->getReloadTime();
145                    }
146                    playReloadSound();
147                }
148            }
149
150            // For stacked munition, a reload sound is played after every fired projectile
151            if (this->munition_->getMunitionDeployment() == MunitionDeployment::Stack)
152            {
153                playReloadSound();
154            }
155
156            // Mark this weapon mode as reloading and start the reload timer
157            this->bReloading_ = true;
158            this->reloadTimer_.setInterval(tempReloadtime);
159            this->reloadTimer_.startTimer();
160
161            // Play the fire sound and fire the weapon mode
162            this->playFireSound();
163            this->fire();
164
165            return true;
166        }
167        else
168        {
169            return false;
170        }
171    }
172
173    bool WeaponMode::push(float* reloadTime)
174    {
175
176        if( this->chargeable_)                                                                                                          // chargeable weapons are supposed to charge on push
177        {
178            this->munition_ = this->weapon_->getWeaponPack()->getWeaponSystem()->getMunition(&this->munitiontype_);                     // updates the pointer to the munition(which we use in the next step)
179            if(this->charges_ < this->maxCharges_ && this->bReloading_ == false && this->munition_->canTakeMunition(1, this))           // charges up unless:
180            {                                                                                                                           // - we are fully charged
181                this->charges_ += 1;                                                                                                    // - we are reloading
182            }                                                                                                                           // - we have no munition
183            return false;
184        }
185        else                                                                                                                            // normal (not chargeable) weapons are supposed to fire on push
186        {
187            return fire(reloadTime);
188        }
189    }
190
191    bool WeaponMode::release(float* reloadTime)                 
192    {
193        if( this->chargeable_)                                                                                                          // chargeable weapons are supposed to fire on release
194        { 
195            return fire(reloadTime);
196        }
197        else                                                                                                                            // normal (not chargeable) weapons should do nothing on release
198        {
199            return false;
200        }
201    }
202
203    bool WeaponMode::reload()
204    {
205        if (this->munition_ && this->munition_->reload(this))
206        {
207            if (!this->bParallelReload_)
208            {
209                this->bReloading_ = true;
210                this->reloadTimer_.setInterval(this->reloadTime_ + this->munition_->getReloadTime());
211                this->reloadTimer_.startTimer();
212            }
213
214            return true;
215        }
216
217        return false;
218    }
219
220    void WeaponMode::setMunitionType(Identifier* identifier)
221    {
222        this->munitionname_ = identifier->getName();
223        this->munitiontype_ = identifier;
224        this->updateMunition();
225    }
226
227    void WeaponMode::setMunitionName(const std::string& munitionname)
228    {
229        this->munitionname_ = munitionname;
230        Identifier* identifier = ClassByString(this->munitionname_);
231        if (identifier)
232            this->munitiontype_ = identifier;
233        else
234            orxout(internal_warning) << "No munition class defined in WeaponMode " << this->getName() << endl;
235        this->updateMunition();
236    }
237
238    void WeaponMode::updateMunition()
239    {
240        if (this->munitiontype_ && this->weapon_ && this->weapon_->getWeaponPack() && this->weapon_->getWeaponPack()->getWeaponSystem())
241        {
242            this->munition_ = this->weapon_->getWeaponPack()->getWeaponSystem()->getMunition(&this->munitiontype_);
243
244            if (this->munition_)
245            {
246                // Add the initial magazines
247                this->munition_->addMagazines(this->initialMagazines_);
248
249                // Maybe we have to reload (if this munition is used the first time or if there weren't any magazines available before)
250                if (this->munition_->needReload(this))
251                    this->munition_->reload(this, false);
252
253                // Add the initial munition
254                if (this->initialMunition_ > 0 && this->munition_->getNumMunitionInCurrentMagazine(this) == this->munition_->getMaxMunitionPerMagazine())
255                {
256                    // The current magazine is still full, so let's just add another magazine to
257                    // the stack and reduce the current magazine to the given amount of munition
258
259                    unsigned int initialmunition = this->initialMunition_;
260                    if (initialmunition > this->munition_->getMaxMunitionPerMagazine())
261                        initialmunition = this->munition_->getMaxMunitionPerMagazine();
262
263                    this->munition_->takeMunition(this->munition_->getMaxMunitionPerMagazine() - initialmunition, this);
264                    this->munition_->addMagazines(1);
265                }
266                else
267                {
268                    // The current magazine isn't full, add the munition directly
269
270                    this->munition_->addMunition(this->initialMunition_);
271                }
272            }
273        }
274        else
275        {
276            this->munition_ = nullptr;
277        }
278    }
279
280    void WeaponMode::reloaded()
281    {
282        this->bReloading_ = false;
283    }
284
285    void WeaponMode::computeMuzzleParameters(const Vector3& target)
286    {
287        if (this->weapon_)
288        {
289            this->muzzlePosition_ = this->weapon_->getWorldPosition() + this->weapon_->getWorldOrientation() * this->muzzleOffset_;
290
291            Vector3 muzzleDirection;
292            muzzleDirection = target - this->muzzlePosition_;
293            this->muzzleOrientation_ = (this->weapon_->getWorldOrientation() * WorldEntity::FRONT).getRotationTo(muzzleDirection) * this->weapon_->getWorldOrientation();
294        }
295        else
296        {
297            this->muzzlePosition_ = this->muzzleOffset_;
298            this->muzzleOrientation_ = Quaternion::IDENTITY;
299        }
300    }
301
302    Vector3 WeaponMode::getMuzzleDirection() const
303    {
304        if (this->weapon_)
305            return (this->getMuzzleOrientation() * WorldEntity::FRONT);
306        else
307            return WorldEntity::FRONT;
308    }
309
310    void WeaponMode::setFireSound(const std::string& soundPath, const float soundVolume)
311    {
312        fireSoundPath_ = soundPath;
313        fireSoundVolume_ = soundVolume;
314    }
315
316    const std::string& WeaponMode::getFireSound()
317    {
318        return fireSoundPath_;
319    }
320
321    void WeaponMode::setReloadSound(const std::string& soundPath, const float soundVolume)
322    {
323        reloadSoundPath_ = soundPath;
324        reloadSoundVolume_ = soundVolume;
325    }
326
327    const std::string& WeaponMode::getReloadSound()
328    {
329        return reloadSoundPath_;
330    }
331
332    void WeaponMode::playFireSound()
333    {
334        WorldSound* unusedSound = nullptr;
335
336        if (!GameMode::isMaster())
337        {
338            return;
339        }
340
341        // If no sound path or no weapon was specified, then no sound is played.
342        if (fireSoundPath_ == BLANKSTRING || !this->getWeapon())
343        {
344            return;
345        }
346
347        // Search in the sound list for a WorldSound that may be used. It must be an idle WorldSound instance (i.e. it is not playing a sound now)
348        for (auto sound : fireSounds_)
349        {
350            if( sound && !(sound->isPlaying()))
351            {
352                // Unused sound found
353                unusedSound = sound;
354                break;
355            }
356        }
357
358        // If no unused sound was found, create a new one and add it to the list
359        if (!unusedSound)
360        {
361            unusedSound = new WorldSound(this->getContext());
362            fireSounds_.push_back(unusedSound);
363            unusedSound->setLooping(false);
364            unusedSound->setSource(fireSoundPath_);
365            unusedSound->setVolume(fireSoundVolume_);
366            this->getWeapon()->attach(unusedSound);
367        }
368
369        // Play the fire sound
370        unusedSound->play();
371    }
372
373    void WeaponMode::playReloadSound()
374    {
375        if (!GameMode::isMaster())
376        {
377            return;
378        }
379
380        // If no sound path or no weapon was specified, then no sound is played.
381        if (reloadSoundPath_ == BLANKSTRING || !this->getWeapon())
382        {
383            return;
384        }
385
386        // Create a reload WorldSound if not done yet
387        if (!reloadSound_)
388        {
389            reloadSound_ = new WorldSound(this->getContext());
390            reloadSound_->setSource(reloadSoundPath_);
391            reloadSound_->setVolume(reloadSoundVolume_);
392            this->getWeapon()->attach(reloadSound_);
393        }
394
395        // Play the reload sound
396        reloadSound_->play();
397    }
398}
Note: See TracBrowser for help on using the repository browser.