Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

Cleaned up the process passing the collisionshape which was hit to the Pawn. Started implementation of ModularSpaceShip and ShipPart.

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