Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/presentationFS15/src/orxonox/worldentities/pawns/Pawn.cc @ 10495

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

ParticleEffects branch merged to presentation. New level PresentationFS15. New SpaceShip spaceshipFS15 with both RocketFire and RocketFireOld

  • Property svn:eol-style set to native
File size: 20.8 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 "sound/WorldSound.h"
49
50#include "controllers/FormationController.h"
51
52namespace orxonox
53{
54    RegisterClass(Pawn);
55
56    Pawn::Pawn(Context* context)
57        : ControllableEntity(context)
58        , RadarViewable(this, static_cast<WorldEntity*>(this))
59    {
60        RegisterObject(Pawn);
61
62        this->bAlive_ = true;
63        this->bReload_ = false;
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
74        this->reloadRate_ = 0;
75        this->reloadWaitTime_ = 1.0f;
76        this->reloadWaitCountdown_ = 0;
77
78        this->lastHitOriginator_ = 0;
79
80        // set damage multiplier to default value 1, meaning nominal damage
81        this->damageMultiplier_ = 1;
82
83        this->spawnparticleduration_ = 3.0f;
84
85        this->aimPosition_ = Vector3::ZERO;
86
87        if (GameMode::isMaster())
88        {
89            this->weaponSystem_ = new WeaponSystem(this->getContext());
90            this->weaponSystem_->setPawn(this);
91        }
92        else
93            this->weaponSystem_ = 0;
94
95        this->setRadarObjectColour(ColourValue::Red);
96        this->setRadarObjectShape(RadarViewable::Dot);
97
98        this->registerVariables();
99
100        this->isHumanShip_ = this->hasLocalController();
101
102        this->setSyncMode(ObjectDirection::Bidirectional); // needed to synchronise e.g. aimposition
103
104        if (GameMode::isMaster())
105        {
106            this->explosionSound_ = new WorldSound(this->getContext());
107            this->explosionSound_->setVolume(1.0f);
108        }
109        else
110        {
111            this->explosionSound_ = 0;
112        }
113    }
114
115    Pawn::~Pawn()
116    {
117        if (this->isInitialized())
118        {
119            if (this->weaponSystem_)
120                this->weaponSystem_->destroy();
121        }
122    }
123
124    void Pawn::XMLPort(Element& xmlelement, XMLPort::Mode mode)
125    {
126        SUPER(Pawn, XMLPort, xmlelement, mode);
127
128        XMLPortParam(Pawn, "health", setHealth, getHealth, xmlelement, mode).defaultValues(100);
129        XMLPortParam(Pawn, "maxhealth", setMaxHealth, getMaxHealth, xmlelement, mode).defaultValues(200);
130        XMLPortParam(Pawn, "initialhealth", setInitialHealth, getInitialHealth, xmlelement, mode).defaultValues(100);
131
132        XMLPortParam(Pawn, "shieldhealth", setShieldHealth, getShieldHealth, xmlelement, mode).defaultValues(0);
133        XMLPortParam(Pawn, "initialshieldhealth", setInitialShieldHealth, getInitialShieldHealth, xmlelement, mode).defaultValues(0);
134        XMLPortParam(Pawn, "maxshieldhealth", setMaxShieldHealth, getMaxShieldHealth, xmlelement, mode).defaultValues(100);
135        XMLPortParam(Pawn, "shieldabsorption", setShieldAbsorption, getShieldAbsorption, xmlelement, mode).defaultValues(0);
136
137        XMLPortParam(Pawn, "spawnparticlesource", setSpawnParticleSource, getSpawnParticleSource, xmlelement, mode);
138        XMLPortParam(Pawn, "spawnparticleduration", setSpawnParticleDuration, getSpawnParticleDuration, xmlelement, mode).defaultValues(3.0f);
139        XMLPortParam(Pawn, "explosionchunks", setExplosionChunks, getExplosionChunks, xmlelement, mode).defaultValues(7);
140
141        XMLPortObject(Pawn, WeaponSlot, "weaponslots", addWeaponSlot, getWeaponSlot, xmlelement, mode);
142        XMLPortObject(Pawn, WeaponSet, "weaponsets", addWeaponSet, getWeaponSet, xmlelement, mode);
143        XMLPortObject(Pawn, WeaponPack, "weapons", addWeaponPackXML, getWeaponPack, xmlelement, mode);
144
145        XMLPortParam(Pawn, "reloadrate", setReloadRate, getReloadRate, xmlelement, mode).defaultValues(0);
146        XMLPortParam(Pawn, "reloadwaittime", setReloadWaitTime, getReloadWaitTime, 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->bReload_,          VariableDirection::ToServer);
162        registerVariable(this->aimPosition_,      VariableDirection::ToServer);  // For the moment this variable gets only transfered to the server
163    }
164
165    void Pawn::tick(float dt)
166    {
167        SUPER(Pawn, tick, dt);
168
169        this->bReload_ = false;
170
171        // TODO: use the existing timer functions instead
172        if(this->reloadWaitCountdown_ > 0)
173        {
174            this->decreaseReloadCountdownTime(dt);
175        }
176        else
177        {
178            this->addShieldHealth(this->getReloadRate() * dt);
179            this->resetReloadCountdown();
180        }
181
182        if (GameMode::isMaster())
183        {
184            if (this->health_ <= 0 && bAlive_)
185            {
186                this->fireEvent(); // Event to notify anyone who wants to know about the death.
187                this->death();
188            }
189        }
190    }
191
192    void Pawn::preDestroy()
193    {
194        // yay, multiple inheritance!
195        this->ControllableEntity::preDestroy();
196        this->PickupCarrier::preDestroy();
197    }
198
199    void Pawn::setPlayer(PlayerInfo* player)
200    {
201        ControllableEntity::setPlayer(player);
202
203        if (this->getGametype())
204            this->getGametype()->playerStartsControllingPawn(player, this);
205    }
206
207    void Pawn::removePlayer()
208    {
209        if (this->getGametype())
210            this->getGametype()->playerStopsControllingPawn(this->getPlayer(), this);
211
212        ControllableEntity::removePlayer();
213    }
214
215
216    void Pawn::setHealth(float health)
217    {
218        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
219    }
220
221    void Pawn::setShieldHealth(float shieldHealth)
222    {
223        this->shieldHealth_ = std::min(shieldHealth, this->maxShieldHealth_);
224    }
225
226    void Pawn::setMaxShieldHealth(float maxshieldhealth)
227    {
228        this->maxShieldHealth_ = maxshieldhealth;
229    }
230
231    void Pawn::setReloadRate(float reloadrate)
232    {
233        this->reloadRate_ = reloadrate;
234    }
235
236    void Pawn::setReloadWaitTime(float reloadwaittime)
237    {
238        this->reloadWaitTime_ = reloadwaittime;
239    }
240
241    void Pawn::decreaseReloadCountdownTime(float dt)
242    {
243        this->reloadWaitCountdown_ -= dt;
244    }
245
246    void Pawn::damage(float damage, float healthdamage, float shielddamage, Pawn* originator, const btCollisionShape* cs)
247    {
248        // Applies multiplier given by the DamageBoost Pickup.
249        if (originator)
250            damage *= originator->getDamageMultiplier();
251
252        if (this->getGametype() && this->getGametype()->allowPawnDamage(this, originator))
253        {
254            if (shielddamage >= this->getShieldHealth())
255            {
256                this->setShieldHealth(0);
257                this->setHealth(this->health_ - (healthdamage + damage));
258            }
259            else
260            {
261                this->setShieldHealth(this->shieldHealth_ - shielddamage);
262
263                // remove remaining shieldAbsorpton-Part of damage from shield
264                shielddamage = damage * this->shieldAbsorption_;
265                shielddamage = std::min(this->getShieldHealth(),shielddamage);
266                this->setShieldHealth(this->shieldHealth_ - shielddamage);
267
268                // set remaining damage to health
269                this->setHealth(this->health_ - (damage - shielddamage) - healthdamage);
270            }
271
272            this->lastHitOriginator_ = originator;
273        }
274    }
275
276// TODO: Still valid?
277/* HIT-Funktionen
278    Die hit-Funktionen muessen auch in src/orxonox/controllers/Controller.h angepasst werden! (Visuelle Effekte)
279
280*/
281    void Pawn::hit(Pawn* originator, const Vector3& force, const btCollisionShape* cs, float damage, float healthdamage, float shielddamage)
282    {
283        if (this->getGametype() && this->getGametype()->allowPawnHit(this, originator) && (!this->getController() || !this->getController()->getGodMode()) )
284        {
285            this->damage(damage, healthdamage, shielddamage, originator, cs);
286            this->setVelocity(this->getVelocity() + force);
287        }
288    }
289
290    void Pawn::hit(Pawn* originator, btManifoldPoint& contactpoint, const btCollisionShape* cs, float damage, float healthdamage, float shielddamage)
291    {
292        if (this->getGametype() && this->getGametype()->allowPawnHit(this, originator) && (!this->getController() || !this->getController()->getGodMode()) )
293        {
294            this->damage(damage, healthdamage, shielddamage, originator, cs);
295
296            if ( this->getController() )
297                this->getController()->hit(originator, contactpoint, damage); // changed to damage, why shielddamage?
298        }
299    }
300
301
302    void Pawn::kill()
303    {
304        this->damage(this->health_);
305        this->death();
306    }
307
308    void Pawn::spawneffect()
309    {
310        // play spawn effect
311        if (!this->spawnparticlesource_.empty())
312        {
313            ParticleSpawner* effect = new ParticleSpawner(this->getContext());
314            effect->setPosition(this->getPosition());
315            effect->setOrientation(this->getOrientation());
316            effect->setDestroyAfterLife(true);
317            effect->setSource(this->spawnparticlesource_);
318            effect->setLifetime(this->spawnparticleduration_);
319        }
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 PawnManager to do the destruction
329            this->bAlive_ = false;
330
331            this->setDestroyWhenPlayerLeft(false);
332
333            if (this->getGametype())
334                this->getGametype()->pawnKilled(this, this->lastHitOriginator_);
335
336            if (this->getPlayer() && this->getPlayer()->getControllableEntity() == this)
337            {
338                // Start to control a new entity if you're the master of a formation
339                if(this->hasSlaves())
340                {
341                    Controller* slave = this->getSlave();
342                    ControllableEntity* entity = slave->getControllableEntity();
343
344
345                    if(!entity->hasHumanController())
346                    {
347                        // delete the AIController // <-- TODO: delete? nothing is deleted here... should we delete the controller?
348                        slave->setControllableEntity(0);
349
350                        // set a new master within the formation
351                        orxonox_cast<FormationController*>(this->getController())->setNewMasterWithinFormation(orxonox_cast<FormationController*>(slave));
352
353                        // start to control a slave
354                        this->getPlayer()->startControl(entity);
355                    }
356                    else
357                    {
358                        this->getPlayer()->stopControl();
359                    }
360
361                }
362                else
363                {
364                    this->getPlayer()->stopControl();
365                }
366            }
367            if (GameMode::isMaster())
368            {
369                this->deatheffect();
370                this->goWithStyle();
371            }
372        }
373    }
374    void Pawn::goWithStyle()
375    {
376        this->bAlive_ = false;
377        this->setDestroyWhenPlayerLeft(false);
378
379        BigExplosion* chunk = new BigExplosion(this->getContext());
380        chunk->setPosition(this->getPosition());
381        chunk->setVelocity(this->getVelocity());
382
383        this->explosionSound_->setPosition(this->getPosition());
384        this->explosionSound_->play();
385    }
386    void Pawn::deatheffect()
387    {
388        // play death effect
389        /*{
390            ParticleSpawner* effect = new ParticleSpawner(this->getContext());
391            effect->setPosition(this->getPosition());
392            effect->setOrientation(this->getOrientation());
393            effect->setDestroyAfterLife(true);
394            effect->setSource("Orxonox/explosion2b");
395            effect->setLifetime(4.0f);
396        }
397        {
398            ParticleSpawner* effect = new ParticleSpawner(this->getContext());
399            effect->setPosition(this->getPosition());
400            effect->setOrientation(this->getOrientation());
401            effect->setDestroyAfterLife(true);
402            effect->setSource("Orxonox/smoke6");
403            effect->setLifetime(4.0f);
404        }
405        {
406            ParticleSpawner* effect = new ParticleSpawner(this->getContext());
407            effect->setPosition(this->getPosition());
408            effect->setOrientation(this->getOrientation());
409            effect->setDestroyAfterLife(true);
410            effect->setSource("Orxonox/sparks");
411            effect->setLifetime(4.0f);
412        }*/
413       
414       
415        {
416            ParticleSpawner* effect = new ParticleSpawner(this->getContext());
417            effect->setPosition(this->getPosition());
418            effect->setOrientation(this->getOrientation());
419            effect->setDestroyAfterLife(true);
420            effect->setSource("orxonox/explosion_flash2");
421            effect->setLifetime(5.0f);
422        }
423        {
424            ParticleSpawner* effect = new ParticleSpawner(this->getContext());
425            effect->setPosition(this->getPosition());
426            effect->setOrientation(this->getOrientation());
427            effect->setDestroyAfterLife(true);
428            effect->setSource("orxonox/explosion_flame2");
429            effect->setLifetime(5.0f);
430        }
431        {
432            ParticleSpawner* effect = new ParticleSpawner(this->getContext());
433            effect->setPosition(this->getPosition());
434            effect->setOrientation(this->getOrientation());
435            effect->setDestroyAfterLife(true);
436            effect->setSource("orxonox/explosion_shockwave2");
437            effect->scale(20);
438            effect->setLifetime(5.0f);
439        }{
440            ParticleSpawner* effect = new ParticleSpawner(this->getContext());
441            effect->setPosition(this->getPosition());
442            effect->setOrientation(this->getOrientation());
443            effect->setDestroyAfterLife(true);
444            effect->setSource("orxonox/explosion_sparks2");
445            effect->setLifetime(5.0f);
446        }
447        {
448            ParticleSpawner* effect = new ParticleSpawner(this->getContext());
449            effect->setPosition(this->getPosition());
450            effect->setOrientation(this->getOrientation());
451            effect->setDestroyAfterLife(true);
452            effect->setSource("orxonox/explosion_streak2");
453            effect->setLifetime(5.0f);
454        }
455        {
456            ParticleSpawner* effect = new ParticleSpawner(this->getContext());
457            effect->setPosition(this->getPosition());
458            effect->setOrientation(this->getOrientation());
459            effect->setDestroyAfterLife(true);
460            effect->setSource("orxonox/explosion_afterglow");
461            effect->scale(20);
462            effect->setLifetime(5.0f);
463        }
464       
465       
466        for (unsigned int i = 0; i < this->numexplosionchunks_; ++i)
467        {
468            ExplosionChunk* chunk = new ExplosionChunk(this->getContext());
469            chunk->setPosition(this->getPosition());
470        }
471    }
472
473    void Pawn::fired(unsigned int firemode)
474    {
475        if (this->weaponSystem_)
476            this->weaponSystem_->fire(firemode);
477    }
478
479    void Pawn::reload()
480    {
481        this->bReload_ = true;
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    //Tell the Map (RadarViewable), if this is a playership
554    void Pawn::startLocalHumanControl()
555    {
556//        SUPER(ControllableEntity, changedPlayer());
557        ControllableEntity::startLocalHumanControl();
558        this->isHumanShip_ = true;
559    }
560
561    void Pawn::changedVisibility(void)
562    {
563        SUPER(Pawn, changedVisibility);
564
565        // enable proper radarviewability when the visibility is changed
566        this->RadarViewable::settingsChanged();
567    }
568
569
570    // A function to check if this pawn's controller is the master of any formationcontroller
571    bool Pawn::hasSlaves()
572    {
573        for (ObjectList<FormationController>::iterator it =
574             ObjectList<FormationController>::begin();
575             it != ObjectList<FormationController>::end(); ++it )
576        {
577            // checks if the pawn's controller has a slave
578            if (this->hasHumanController() && it->getMaster() == this->getPlayer()->getController())
579                return true;
580        }
581        return false;
582    }
583
584    // A function that returns a slave of the pawn's controller
585    Controller* Pawn::getSlave(){
586        for (ObjectList<FormationController>::iterator it =
587                ObjectList<FormationController>::begin();
588                it != ObjectList<FormationController>::end(); ++it )
589        {
590            if (this->hasHumanController() && it->getMaster() == this->getPlayer()->getController())
591                return it->getController();
592        }
593        return 0;
594    }
595
596
597    void Pawn::setExplosionSound(const std::string &explosionSound)
598    {
599        if(explosionSound_ )
600            explosionSound_->setSource(explosionSound);
601        else
602            assert(0); // This should never happen, because soundpointer is only available on master
603    }
604
605    const std::string& Pawn::getExplosionSound()
606    {
607        if( explosionSound_ )
608            return explosionSound_->getSource();
609        else
610            assert(0);
611        return BLANKSTRING;
612    }
613}
Note: See TracBrowser for help on using the repository browser.