Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/modularships/src/orxonox/worldentities/pawns/Pawn.cc @ 9996

Last change on this file since 9996 was 9996, checked in by noep, 10 years ago

Fixed the method outputting the projectile's CollisionBox instead of the one of the Pawn it collides into.

  • 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)
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    void Pawn::customDamage(float damage, float healthdamage, float shielddamage, Pawn* originator, const btCollisionShape* cs)
277    {
278        // Applies multiplier given by the DamageBoost Pickup.
279        if (originator)
280            damage *= originator->getDamageMultiplier();
281
282        orxout() << "damage(): Custom collision detected on " << this->getRadarName() << ", CS: " << cs << endl;
283
284        if (this->getGametype() && this->getGametype()->allowPawnDamage(this, originator))
285        {
286            if (shielddamage >= this->getShieldHealth())
287            {
288                this->setShieldHealth(0);
289                this->setHealth(this->health_ - (healthdamage + damage));
290            }
291            else
292            {
293                this->setShieldHealth(this->shieldHealth_ - shielddamage);
294
295                // remove remaining shieldAbsorpton-Part of damage from shield
296                shielddamage = damage * this->shieldAbsorption_;
297                shielddamage = std::min(this->getShieldHealth(),shielddamage);
298                this->setShieldHealth(this->shieldHealth_ - shielddamage);
299
300                // set remaining damage to health
301                this->setHealth(this->health_ - (damage - shielddamage) - healthdamage);
302            }
303
304            this->lastHitOriginator_ = originator;
305        }
306    }
307
308// TODO: Still valid?
309/* HIT-Funktionen
310    Die hit-Funktionen muessen auch in src/orxonox/controllers/Controller.h angepasst werden! (Visuelle Effekte)
311
312*/
313    void Pawn::hit(Pawn* originator, const Vector3& force, float damage, float healthdamage, float shielddamage)
314    {
315        if (this->getGametype() && this->getGametype()->allowPawnHit(this, originator) && (!this->getController() || !this->getController()->getGodMode()) )
316        {
317            this->damage(damage, healthdamage, shielddamage, originator);
318            this->setVelocity(this->getVelocity() + force);
319        }
320    }
321
322    void Pawn::customHit(Pawn* originator, const Vector3& force, const btCollisionShape* cs, float damage, float healthdamage, float shielddamage)
323    {
324        if (this->getGametype() && this->getGametype()->allowPawnHit(this, originator) && (!this->getController() || !this->getController()->getGodMode()) )
325        {
326            this->customDamage(damage, healthdamage, shielddamage, originator, cs);
327            this->setVelocity(this->getVelocity() + force);
328        }
329    }
330
331    void Pawn::hit(Pawn* originator, btManifoldPoint& contactpoint, float damage, float healthdamage, float shielddamage)
332    {
333        if (this->getGametype() && this->getGametype()->allowPawnHit(this, originator) && (!this->getController() || !this->getController()->getGodMode()) )
334        {
335            this->damage(damage, healthdamage, shielddamage, originator);
336
337            if ( this->getController() )
338                this->getController()->hit(originator, contactpoint, damage); // changed to damage, why shielddamage?
339        }
340    }
341
342    void Pawn::customHit(Pawn* originator, btManifoldPoint& contactpoint, const btCollisionShape* cs, float damage, float healthdamage, float shielddamage)
343    {
344        if (this->getGametype() && this->getGametype()->allowPawnHit(this, originator) && (!this->getController() || !this->getController()->getGodMode()) )
345        {
346            this->customDamage(damage, healthdamage, shielddamage, originator, cs);
347
348            if ( this->getController() )
349                this->getController()->hit(originator, contactpoint, damage); // changed to damage, why shielddamage?
350        }
351    }
352
353
354    void Pawn::kill()
355    {
356        this->damage(this->health_);
357        this->death();
358    }
359
360    void Pawn::spawneffect()
361    {
362        // play spawn effect
363        if (!this->spawnparticlesource_.empty())
364        {
365            ParticleSpawner* effect = new ParticleSpawner(this->getContext());
366            effect->setPosition(this->getPosition());
367            effect->setOrientation(this->getOrientation());
368            effect->setDestroyAfterLife(true);
369            effect->setSource(this->spawnparticlesource_);
370            effect->setLifetime(this->spawnparticleduration_);
371        }
372    }
373
374
375    void Pawn::death()
376    {
377        this->setHealth(1);
378        if (this->getGametype() && this->getGametype()->allowPawnDeath(this, this->lastHitOriginator_))
379        {
380            // Set bAlive_ to false and wait for PawnManager to do the destruction
381            this->bAlive_ = false;
382
383            this->setDestroyWhenPlayerLeft(false);
384
385            if (this->getGametype())
386                this->getGametype()->pawnKilled(this, this->lastHitOriginator_);
387
388            if (this->getPlayer() && this->getPlayer()->getControllableEntity() == this)
389            {
390                // Start to control a new entity if you're the master of a formation
391                if(this->hasSlaves())
392                {
393                    Controller* slave = this->getSlave();
394                    ControllableEntity* entity = slave->getControllableEntity();
395
396
397                    if(!entity->hasHumanController())
398                    {
399                        // delete the AIController // <-- TODO: delete? nothing is deleted here... should we delete the controller?
400                        slave->setControllableEntity(0);
401
402                        // set a new master within the formation
403                        orxonox_cast<FormationController*>(this->getController())->setNewMasterWithinFormation(orxonox_cast<FormationController*>(slave));
404
405                        // start to control a slave
406                        this->getPlayer()->startControl(entity);
407                    }
408                    else
409                    {
410                        this->getPlayer()->stopControl();
411                    }
412
413                }
414                else
415                {
416                    this->getPlayer()->stopControl();
417                }
418            }
419            if (GameMode::isMaster())
420            {
421//                this->deathEffect();
422                this->goWithStyle();
423            }
424        }
425    }
426    void Pawn::goWithStyle()
427    {
428        this->bAlive_ = false;
429        this->setDestroyWhenPlayerLeft(false);
430
431        BigExplosion* chunk = new BigExplosion(this->getContext());
432        chunk->setPosition(this->getPosition());
433        chunk->setVelocity(this->getVelocity());
434
435        this->explosionSound_->setPosition(this->getPosition());
436        this->explosionSound_->play();
437    }
438    void Pawn::deatheffect()
439    {
440        // play death effect
441        {
442            ParticleSpawner* effect = new ParticleSpawner(this->getContext());
443            effect->setPosition(this->getPosition());
444            effect->setOrientation(this->getOrientation());
445            effect->setDestroyAfterLife(true);
446            effect->setSource("Orxonox/explosion2b");
447            effect->setLifetime(4.0f);
448        }
449        {
450            ParticleSpawner* effect = new ParticleSpawner(this->getContext());
451            effect->setPosition(this->getPosition());
452            effect->setOrientation(this->getOrientation());
453            effect->setDestroyAfterLife(true);
454            effect->setSource("Orxonox/smoke6");
455            effect->setLifetime(4.0f);
456        }
457        {
458            ParticleSpawner* effect = new ParticleSpawner(this->getContext());
459            effect->setPosition(this->getPosition());
460            effect->setOrientation(this->getOrientation());
461            effect->setDestroyAfterLife(true);
462            effect->setSource("Orxonox/sparks");
463            effect->setLifetime(4.0f);
464        }
465        for (unsigned int i = 0; i < this->numexplosionchunks_; ++i)
466        {
467            ExplosionChunk* chunk = new ExplosionChunk(this->getContext());
468            chunk->setPosition(this->getPosition());
469        }
470    }
471
472    void Pawn::fired(unsigned int firemode)
473    {
474        if (this->weaponSystem_)
475            this->weaponSystem_->fire(firemode);
476    }
477
478    void Pawn::reload()
479    {
480        this->bReload_ = true;
481    }
482
483    void Pawn::postSpawn()
484    {
485        this->setHealth(this->initialHealth_);
486        if (GameMode::isMaster())
487            this->spawneffect();
488    }
489
490    /* WeaponSystem:
491    *   functions load Slot, Set, Pack from XML and make sure all parent-pointers are set.
492    *   with setWeaponPack you can not just load a Pack from XML but if a Pack already exists anywhere, you can attach it.
493    *       --> e.g. Pickup-Items
494    */
495    void Pawn::addWeaponSlot(WeaponSlot * wSlot)
496    {
497        this->attach(wSlot);
498        if (this->weaponSystem_)
499            this->weaponSystem_->addWeaponSlot(wSlot);
500    }
501
502    WeaponSlot * Pawn::getWeaponSlot(unsigned int index) const
503    {
504        if (this->weaponSystem_)
505            return this->weaponSystem_->getWeaponSlot(index);
506        else
507            return 0;
508    }
509
510    void Pawn::addWeaponSet(WeaponSet * wSet)
511    {
512        if (this->weaponSystem_)
513            this->weaponSystem_->addWeaponSet(wSet);
514    }
515
516    WeaponSet * Pawn::getWeaponSet(unsigned int index) const
517    {
518        if (this->weaponSystem_)
519            return this->weaponSystem_->getWeaponSet(index);
520        else
521            return 0;
522    }
523
524    void Pawn::addWeaponPack(WeaponPack * wPack)
525    {
526        if (this->weaponSystem_)
527        {
528            this->weaponSystem_->addWeaponPack(wPack);
529            this->addedWeaponPack(wPack);
530        }
531    }
532
533    void Pawn::addWeaponPackXML(WeaponPack * wPack)
534    {
535        if (this->weaponSystem_)
536        {
537            if (!this->weaponSystem_->addWeaponPack(wPack))
538                wPack->destroy();
539            else
540                this->addedWeaponPack(wPack);
541        }
542    }
543
544    WeaponPack * Pawn::getWeaponPack(unsigned int index) const
545    {
546        if (this->weaponSystem_)
547            return this->weaponSystem_->getWeaponPack(index);
548        else
549            return 0;
550    }
551
552    //Tell the Map (RadarViewable), if this is a playership
553    void Pawn::startLocalHumanControl()
554    {
555//        SUPER(ControllableEntity, changedPlayer());
556        ControllableEntity::startLocalHumanControl();
557        this->isHumanShip_ = true;
558    }
559
560    void Pawn::changedVisibility(void)
561    {
562        SUPER(Pawn, changedVisibility);
563
564        // enable proper radarviewability when the visibility is changed
565        this->RadarViewable::settingsChanged();
566    }
567
568
569    // A function to check if this pawn's controller is the master of any formationcontroller
570    bool Pawn::hasSlaves()
571    {
572        for (ObjectList<FormationController>::iterator it =
573             ObjectList<FormationController>::begin();
574             it != ObjectList<FormationController>::end(); ++it )
575        {
576            // checks if the pawn's controller has a slave
577            if (this->hasHumanController() && it->getMaster() == this->getPlayer()->getController())
578                return true;
579        }
580        return false;
581    }
582
583    // A function that returns a slave of the pawn's controller
584    Controller* Pawn::getSlave(){
585        for (ObjectList<FormationController>::iterator it =
586                ObjectList<FormationController>::begin();
587                it != ObjectList<FormationController>::end(); ++it )
588        {
589            if (this->hasHumanController() && it->getMaster() == this->getPlayer()->getController())
590                return it->getController();
591        }
592        return 0;
593    }
594
595
596    void Pawn::setExplosionSound(const std::string &explosionSound)
597    {
598        if(explosionSound_ )
599            explosionSound_->setSource(explosionSound);
600        else
601            assert(0); // This should never happen, because soundpointer is only available on master
602    }
603
604    const std::string& Pawn::getExplosionSound()
605    {
606        if( explosionSound_ )
607            return explosionSound_->getSource();
608        else
609            assert(0);
610        return BLANKSTRING;
611    }
612
613
614}
Note: See TracBrowser for help on using the repository browser.