Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/presentationHS15/src/orxonox/worldentities/pawns/Pawn.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: 22.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 *   Author:
23 *      Fabian 'x3n' Landau
24 *   Co-authors:
25 *      Simon Miescher
26 *
27 */
28
29#include "Pawn.h"
30
31#include <algorithm>
32
33#include "core/CoreIncludes.h"
34#include "core/GameMode.h"
35#include "core/XMLPort.h"
36#include "network/NetworkFunction.h"
37
38#include "infos/PlayerInfo.h"
39#include "controllers/Controller.h"
40#include "gametypes/Gametype.h"
41#include "graphics/ParticleSpawner.h"
42#include "worldentities/ExplosionChunk.h"
43#include "worldentities/BigExplosion.h"
44#include "weaponsystem/WeaponSystem.h"
45#include "weaponsystem/WeaponSlot.h"
46#include "weaponsystem/WeaponPack.h"
47#include "weaponsystem/WeaponSet.h"
48#include "weaponsystem/Munition.h"
49#include "sound/WorldSound.h"
50
51#include "controllers/FormationController.h"
52
53namespace orxonox
54{
55    RegisterClass(Pawn);
56
57    Pawn::Pawn(Context* context)
58        : ControllableEntity(context)
59        , RadarViewable(this, static_cast<WorldEntity*>(this))
60    {
61        RegisterObject(Pawn);
62
63        this->bAlive_ = true;
64
65        this->health_ = 0;
66        this->maxHealth_ = 0;
67        this->initialHealth_ = 0;
68
69        this->shieldHealth_ = 0;
70        this->initialShieldHealth_ = 0;
71        this->maxShieldHealth_ = 100; //otherwise shield might increase to float_max
72        this->shieldAbsorption_ = 0.5;
73        this->shieldRechargeRate_ = 0;
74        this->shieldRechargeWaitTime_ = 1.0f;
75        this->shieldRechargeWaitCountdown_ = 0;
76
77        this->lastHitOriginator_ = 0;
78
79        // set damage multiplier to default value 1, meaning nominal damage
80        this->damageMultiplier_ = 1;
81
82        this->spawnparticleduration_ = 3.0f;
83
84        this->aimPosition_ = Vector3::ZERO;
85
86        if (GameMode::isMaster())
87        {
88            this->weaponSystem_ = new WeaponSystem(this->getContext());
89            this->weaponSystem_->setPawn(this);
90        }
91        else
92            this->weaponSystem_ = 0;
93
94        this->setRadarObjectColour(ColourValue::Red);
95        this->setRadarObjectShape(RadarViewable::Dot);
96
97        this->registerVariables();
98
99        this->isHumanShip_ = this->hasLocalController();
100
101        this->setSyncMode(ObjectDirection::Bidirectional); // needed to synchronise e.g. aimposition
102
103        if (GameMode::isMaster())
104        {
105            this->explosionSound_ = new WorldSound(this->getContext());
106            this->explosionSound_->setVolume(1.0f);
107        }
108        else
109        {
110            this->explosionSound_ = 0;
111        }
112    }
113
114    Pawn::~Pawn()
115    {
116        if (this->isInitialized())
117        {
118            if (this->weaponSystem_)
119                this->weaponSystem_->destroy();
120        }
121    }
122
123    void Pawn::XMLPort(Element& xmlelement, XMLPort::Mode mode)
124    {
125        SUPER(Pawn, XMLPort, xmlelement, mode);
126
127        XMLPortParam(Pawn, "health", setHealth, getHealth, xmlelement, mode).defaultValues(100);
128        XMLPortParam(Pawn, "maxhealth", setMaxHealth, getMaxHealth, xmlelement, mode).defaultValues(200);
129        XMLPortParam(Pawn, "initialhealth", setInitialHealth, getInitialHealth, xmlelement, mode).defaultValues(100);
130
131        XMLPortParam(Pawn, "shieldhealth", setShieldHealth, getShieldHealth, xmlelement, mode).defaultValues(0);
132        XMLPortParam(Pawn, "initialshieldhealth", setInitialShieldHealth, getInitialShieldHealth, xmlelement, mode).defaultValues(0);
133        XMLPortParam(Pawn, "maxshieldhealth", setMaxShieldHealth, getMaxShieldHealth, xmlelement, mode).defaultValues(100);
134        XMLPortParam(Pawn, "shieldabsorption", setShieldAbsorption, getShieldAbsorption, xmlelement, mode).defaultValues(0);
135
136        XMLPortParam(Pawn, "spawnparticlesource", setSpawnParticleSource, getSpawnParticleSource, xmlelement, mode);
137        XMLPortParam(Pawn, "spawnparticleduration", setSpawnParticleDuration, getSpawnParticleDuration, xmlelement, mode).defaultValues(3.0f);
138        XMLPortParam(Pawn, "explosionchunks", setExplosionChunks, getExplosionChunks, xmlelement, mode).defaultValues(7);
139
140        XMLPortObject(Pawn, WeaponSlot, "weaponslots", addWeaponSlot, getWeaponSlot, xmlelement, mode);
141        XMLPortObject(Pawn, WeaponSet, "weaponsets", addWeaponSet, getWeaponSet, xmlelement, mode);
142        XMLPortObject(Pawn, WeaponPack, "weaponpacks", addWeaponPackXML, getWeaponPack, xmlelement, mode);
143        XMLPortObject(Pawn, Munition, "munition", addMunitionXML, getMunitionXML, xmlelement, mode);
144
145        XMLPortParam(Pawn, "shieldrechargerate", setShieldRechargeRate, getShieldRechargeRate, xmlelement, mode).defaultValues(0);
146        XMLPortParam(Pawn, "shieldrechargewaittime", setShieldRechargeWaitTime, getShieldRechargeWaitTime, xmlelement, mode).defaultValues(1.0f);
147
148        XMLPortParam(Pawn, "explosionSound",  setExplosionSound,  getExplosionSound,  xmlelement, mode);
149
150        XMLPortParam ( RadarViewable, "radarname", setRadarName, getRadarName, xmlelement, mode );
151    }
152
153    void Pawn::registerVariables()
154    {
155        registerVariable(this->bAlive_,            VariableDirection::ToClient);
156        registerVariable(this->health_,            VariableDirection::ToClient);
157        registerVariable(this->maxHealth_,         VariableDirection::ToClient);
158        registerVariable(this->shieldHealth_,      VariableDirection::ToClient);
159        registerVariable(this->maxShieldHealth_,   VariableDirection::ToClient);
160        registerVariable(this->shieldAbsorption_,  VariableDirection::ToClient);
161        registerVariable(this->aimPosition_,       VariableDirection::ToServer);  // For the moment this variable gets only transfered to the server
162    }
163
164    void Pawn::tick(float dt)
165    {
166        SUPER(Pawn, tick, dt);
167
168        // Recharge the shield
169        // TODO: use the existing timer functions instead
170        if(this->shieldRechargeWaitCountdown_ > 0)
171        {
172            this->decreaseShieldRechargeCountdownTime(dt);
173        }
174        else
175        {
176            this->addShieldHealth(this->getShieldRechargeRate() * dt);
177            this->resetShieldRechargeCountdown();
178        }
179
180        if (GameMode::isMaster())
181        {
182            if (this->health_ <= 0 && bAlive_)
183            {
184                this->fireEvent(); // Event to notify anyone who wants to know about the death.
185                this->death();
186            }
187        }
188    }
189
190    void Pawn::preDestroy()
191    {
192        // yay, multiple inheritance!
193        this->ControllableEntity::preDestroy();
194        this->PickupCarrier::preDestroy();
195    }
196
197    void Pawn::setPlayer(PlayerInfo* player)
198    {
199        ControllableEntity::setPlayer(player);
200
201        if (this->getGametype())
202            this->getGametype()->playerStartsControllingPawn(player, this);
203    }
204
205    void Pawn::removePlayer()
206    {
207        if (this->getGametype())
208            this->getGametype()->playerStopsControllingPawn(this->getPlayer(), this);
209
210        ControllableEntity::removePlayer();
211    }
212
213
214    void Pawn::setHealth(float health)
215    {
216        this->health_ = std::min(health, this->maxHealth_); //Health can't be set to a value bigger than maxHealth, otherwise it will be reduced at first hit
217    }
218
219    void Pawn::setShieldHealth(float shieldHealth)
220    {
221        this->shieldHealth_ = std::min(shieldHealth, this->maxShieldHealth_);
222    }
223
224    void Pawn::setMaxShieldHealth(float maxshieldhealth)
225    {
226        this->maxShieldHealth_ = maxshieldhealth;
227    }
228
229    void Pawn::setShieldRechargeRate(float shieldRechargeRate)
230    {
231        this->shieldRechargeRate_ = shieldRechargeRate;
232    }
233
234    void Pawn::setShieldRechargeWaitTime(float shieldRechargeWaitTime)
235    {
236        this->shieldRechargeWaitTime_ = shieldRechargeWaitTime;
237    }
238
239    void Pawn::decreaseShieldRechargeCountdownTime(float dt)
240    {
241        this->shieldRechargeWaitCountdown_ -= dt;
242    }
243
244    void Pawn::damage(float damage, float healthdamage, float shielddamage, Pawn* originator, const btCollisionShape* cs)
245    {
246        // Applies multiplier given by the DamageBoost Pickup.
247        if (originator)
248            damage *= originator->getDamageMultiplier();
249
250        if (this->getGametype() && this->getGametype()->allowPawnDamage(this, originator))
251        {
252            // Health-damage cannot be absorbed by shields.
253            // Shield-damage only reduces shield health.
254            // Normal damage can be (partially) absorbed by shields.
255
256            if (shielddamage >= this->getShieldHealth())
257            {
258                this->setShieldHealth(0);
259                this->setHealth(this->health_ - (healthdamage + damage));
260            }
261            else
262            {
263                this->setShieldHealth(this->shieldHealth_ - shielddamage);
264
265                // remove remaining shieldAbsorpton-Part of damage from shield
266                shielddamage = damage * this->shieldAbsorption_;
267                shielddamage = std::min(this->getShieldHealth(),shielddamage);
268                this->setShieldHealth(this->shieldHealth_ - shielddamage);
269
270                // set remaining damage to health
271                this->setHealth(this->health_ - (damage - shielddamage) - healthdamage);
272            }
273
274            this->lastHitOriginator_ = originator;
275        }
276    }
277
278// TODO: Still valid?
279/* HIT-Funktionen
280    Die hit-Funktionen muessen auch in src/orxonox/controllers/Controller.h angepasst werden! (Visuelle Effekte)
281
282*/
283    void Pawn::hit(Pawn* originator, const Vector3& force, const btCollisionShape* cs, float damage, float healthdamage, float shielddamage)
284    {
285        if (this->getGametype() && this->getGametype()->allowPawnHit(this, originator) && (!this->getController() || !this->getController()->getGodMode()) )
286        {
287            this->damage(damage, healthdamage, shielddamage, originator, cs);
288            this->setVelocity(this->getVelocity() + force);
289        }
290    }
291
292    void Pawn::hit(Pawn* originator, btManifoldPoint& contactpoint, const btCollisionShape* cs, float damage, float healthdamage, float shielddamage)
293    {
294        if (this->getGametype() && this->getGametype()->allowPawnHit(this, originator) && (!this->getController() || !this->getController()->getGodMode()) )
295        {
296            this->damage(damage, healthdamage, shielddamage, originator, cs);
297
298            if ( this->getController() )
299                this->getController()->hit(originator, contactpoint, damage); // changed to damage, why shielddamage?
300        }
301    }
302
303    void Pawn::kill()
304    {
305        this->damage(this->health_);
306        this->death();
307    }
308
309    void Pawn::spawneffect()
310    {
311        // play spawn effect
312        if (!this->spawnparticlesource_.empty())
313        {
314            ParticleSpawner* effect = new ParticleSpawner(this->getContext());
315            effect->setPosition(this->getPosition());
316            effect->setOrientation(this->getOrientation());
317            effect->setDestroyAfterLife(true);
318            effect->setSource(this->spawnparticlesource_);
319            effect->setLifetime(this->spawnparticleduration_);
320        }
321    }
322
323    void Pawn::death()
324    {
325        this->setHealth(1);
326        if (this->getGametype() && this->getGametype()->allowPawnDeath(this, this->lastHitOriginator_))
327        {
328            // Set bAlive_ to false and wait for destroyLater() to do the destruction
329            this->bAlive_ = false;
330            this->destroyLater();
331
332            this->setDestroyWhenPlayerLeft(false);
333
334            if (this->getGametype())
335                this->getGametype()->pawnKilled(this, this->lastHitOriginator_);
336
337            if (this->getPlayer() && this->getPlayer()->getControllableEntity() == this)
338            {
339                // Start to control a new entity if you're the master of a formation
340                if(this->hasSlaves())
341                {
342                    Controller* slave = this->getSlave();
343                    ControllableEntity* entity = slave->getControllableEntity();
344
345
346                    if(!entity->hasHumanController())
347                    {
348                        // delete the AIController // <-- TODO: delete? nothing is deleted here... should we delete the controller?
349                        slave->setControllableEntity(0);
350
351                        // set a new master within the formation
352                        orxonox_cast<FormationController*>(this->getController())->setNewMasterWithinFormation(orxonox_cast<FormationController*>(slave));
353
354                        // start to control a slave
355                        this->getPlayer()->startControl(entity);
356                    }
357                    else
358                    {
359                        this->getPlayer()->stopControl();
360                    }
361
362                }
363                else
364                {
365                    this->getPlayer()->stopControl();
366                }
367            }
368            if (GameMode::isMaster())
369            {
370                this->deatheffect();
371                this->goWithStyle();
372            }
373        }
374    }
375    void Pawn::goWithStyle()
376    {
377        this->bAlive_ = false;
378        this->setDestroyWhenPlayerLeft(false);
379
380        BigExplosion* chunk = new BigExplosion(this->getContext());
381        chunk->setPosition(this->getPosition());
382        chunk->setVelocity(this->getVelocity());
383
384        this->explosionSound_->setPosition(this->getPosition());
385        this->explosionSound_->play();
386    }
387    void Pawn::deatheffect()
388    {
389        // play death effect
390        /*{
391            ParticleSpawner* effect = new ParticleSpawner(this->getContext());
392            effect->setPosition(this->getPosition());
393            effect->setOrientation(this->getOrientation());
394            effect->setDestroyAfterLife(true);
395            effect->setSource("Orxonox/explosion2b");
396            effect->setLifetime(4.0f);
397        }
398        {
399            ParticleSpawner* effect = new ParticleSpawner(this->getContext());
400            effect->setPosition(this->getPosition());
401            effect->setOrientation(this->getOrientation());
402            effect->setDestroyAfterLife(true);
403            effect->setSource("Orxonox/smoke6");
404            effect->setLifetime(4.0f);
405        }
406        {
407            ParticleSpawner* effect = new ParticleSpawner(this->getContext());
408            effect->setPosition(this->getPosition());
409            effect->setOrientation(this->getOrientation());
410            effect->setDestroyAfterLife(true);
411            effect->setSource("Orxonox/sparks");
412            effect->setLifetime(4.0f);
413        }*/
414       
415       
416        {
417            ParticleSpawner* effect = new ParticleSpawner(this->getContext());
418            effect->setPosition(this->getPosition());
419            effect->setOrientation(this->getOrientation());
420            effect->setDestroyAfterLife(true);
421            effect->setSource("orxonox/explosion_flash2");
422            effect->setLifetime(5.0f);
423        }
424        {
425            ParticleSpawner* effect = new ParticleSpawner(this->getContext());
426            effect->setPosition(this->getPosition());
427            effect->setOrientation(this->getOrientation());
428            effect->setDestroyAfterLife(true);
429            effect->setSource("orxonox/explosion_flame2");
430            effect->setLifetime(5.0f);
431        }
432        {
433            ParticleSpawner* effect = new ParticleSpawner(this->getContext());
434            effect->setPosition(this->getPosition());
435            effect->setOrientation(this->getOrientation());
436            effect->setDestroyAfterLife(true);
437            effect->setSource("orxonox/explosion_shockwave2");
438            effect->scale(20);
439            effect->setLifetime(5.0f);
440        }{
441            ParticleSpawner* effect = new ParticleSpawner(this->getContext());
442            effect->setPosition(this->getPosition());
443            effect->setOrientation(this->getOrientation());
444            effect->setDestroyAfterLife(true);
445            effect->setSource("orxonox/explosion_sparks2");
446            effect->setLifetime(5.0f);
447        }
448        {
449            ParticleSpawner* effect = new ParticleSpawner(this->getContext());
450            effect->setPosition(this->getPosition());
451            effect->setOrientation(this->getOrientation());
452            effect->setDestroyAfterLife(true);
453            effect->setSource("orxonox/explosion_streak2");
454            effect->setLifetime(5.0f);
455        }
456        {
457            ParticleSpawner* effect = new ParticleSpawner(this->getContext());
458            effect->setPosition(this->getPosition());
459            effect->setOrientation(this->getOrientation());
460            effect->setDestroyAfterLife(true);
461            effect->setSource("orxonox/explosion_afterglow");
462            effect->scale(20);
463            effect->setLifetime(5.0f);
464        }
465       
466       
467        for (unsigned int i = 0; i < this->numexplosionchunks_; ++i)
468        {
469            ExplosionChunk* chunk = new ExplosionChunk(this->getContext());
470            chunk->setPosition(this->getPosition());
471        }
472    }
473
474    /**
475    @brief
476        Check whether the Pawn has a @ref Orxonox::WeaponSystem and fire it with the specified firemode if it has one.
477    */
478    void Pawn::fired(unsigned int firemode)
479    {
480        if (this->weaponSystem_)
481            this->weaponSystem_->fire(firemode);
482    }
483
484    void Pawn::postSpawn()
485    {
486        this->setHealth(this->initialHealth_);
487        if (GameMode::isMaster())
488            this->spawneffect();
489    }
490
491    /* WeaponSystem:
492    *   functions load Slot, Set, Pack from XML and make sure all parent-pointers are set.
493    *   with setWeaponPack you can not just load a Pack from XML but if a Pack already exists anywhere, you can attach it.
494    *       --> e.g. Pickup-Items
495    */
496    void Pawn::addWeaponSlot(WeaponSlot * wSlot)
497    {
498        this->attach(wSlot);
499        if (this->weaponSystem_)
500            this->weaponSystem_->addWeaponSlot(wSlot);
501    }
502
503    WeaponSlot * Pawn::getWeaponSlot(unsigned int index) const
504    {
505        if (this->weaponSystem_)
506            return this->weaponSystem_->getWeaponSlot(index);
507        else
508            return 0;
509    }
510
511    void Pawn::addWeaponSet(WeaponSet * wSet)
512    {
513        if (this->weaponSystem_)
514            this->weaponSystem_->addWeaponSet(wSet);
515    }
516
517    WeaponSet * Pawn::getWeaponSet(unsigned int index) const
518    {
519        if (this->weaponSystem_)
520            return this->weaponSystem_->getWeaponSet(index);
521        else
522            return 0;
523    }
524
525    void Pawn::addWeaponPack(WeaponPack * wPack)
526    {
527        if (this->weaponSystem_)
528        {
529            this->weaponSystem_->addWeaponPack(wPack);
530            this->addedWeaponPack(wPack);
531        }
532    }
533
534    void Pawn::addWeaponPackXML(WeaponPack * wPack)
535    {
536        if (this->weaponSystem_)
537        {
538            if (!this->weaponSystem_->addWeaponPack(wPack))
539                wPack->destroy();
540            else
541                this->addedWeaponPack(wPack);
542        }
543    }
544
545    WeaponPack * Pawn::getWeaponPack(unsigned int index) const
546    {
547        if (this->weaponSystem_)
548            return this->weaponSystem_->getWeaponPack(index);
549        else
550            return 0;
551    }
552
553    std::vector<WeaponPack *> * Pawn::getAllWeaponPacks()
554    {
555        if (this->weaponSystem_)
556            return this->weaponSystem_->getAllWeaponPacks();
557        else
558            return 0;       
559    }
560
561    void Pawn::addMunitionXML(Munition* munition)
562    {
563        if (this->weaponSystem_ && munition)
564        {
565            this->weaponSystem_->addMunition(munition);
566        }
567    }
568
569    Munition* Pawn::getMunitionXML() const
570    {
571        return NULL;
572    }
573
574    Munition* Pawn::getMunition(SubclassIdentifier<Munition> * identifier)
575    {
576        if (weaponSystem_)
577        {
578            return weaponSystem_->getMunition(identifier);
579        }
580
581        return NULL;
582    }
583
584    //Tell the Map (RadarViewable), if this is a playership
585    void Pawn::startLocalHumanControl()
586    {
587//        SUPER(ControllableEntity, startLocalHumanControl());
588        ControllableEntity::startLocalHumanControl();
589        this->isHumanShip_ = true;
590    }
591
592    void Pawn::changedVisibility(void)
593    {
594        SUPER(Pawn, changedVisibility);
595
596        // enable proper radarviewability when the visibility is changed
597        this->RadarViewable::settingsChanged();
598    }
599
600
601    // A function to check if this pawn's controller is the master of any formationcontroller
602    bool Pawn::hasSlaves()
603    {
604        for (ObjectList<FormationController>::iterator it =
605             ObjectList<FormationController>::begin();
606             it != ObjectList<FormationController>::end(); ++it )
607        {
608            // checks if the pawn's controller has a slave
609            if (this->hasHumanController() && it->getMaster() == this->getPlayer()->getController())
610                return true;
611        }
612        return false;
613    }
614
615    // A function that returns a slave of the pawn's controller
616    Controller* Pawn::getSlave(){
617        for (ObjectList<FormationController>::iterator it =
618                ObjectList<FormationController>::begin();
619                it != ObjectList<FormationController>::end(); ++it )
620        {
621            if (this->hasHumanController() && it->getMaster() == this->getPlayer()->getController())
622                return it->getController();
623        }
624        return 0;
625    }
626
627
628    void Pawn::setExplosionSound(const std::string &explosionSound)
629    {
630        if(explosionSound_ )
631            explosionSound_->setSource(explosionSound);
632        else
633            assert(0); // This should never happen, because soundpointer is only available on master
634    }
635
636    const std::string& Pawn::getExplosionSound()
637    {
638        if( explosionSound_ )
639            return explosionSound_->getSource();
640        else
641            assert(0);
642        return BLANKSTRING;
643    }
644}
Note: See TracBrowser for help on using the repository browser.