Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: orxonox.OLD/trunk/src/world_entities/playable.cc @ 7731

Last change on this file since 7731 was 7713, checked in by patrick, 19 years ago

trunk: removed some valgrind errors, but no serious ones. Thunder sound now works, found a bug in my old class. its absolete anyway with the atmoshperic engine

File size: 13.9 KB
RevLine 
[5838]1/*
2   orxonox - the future of 3D-vertical-scrollers
3
4   Copyright (C) 2004 orx
5
6   This program is free software; you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation; either version 2, or (at your option)
9   any later version.
10
11### File Specific:
[5841]12   main-programmer: Silvan Nellen
13   co-programmer: Benjamin Knecht
[5838]14*/
15
[5881]16
[5838]17#include "playable.h"
[7350]18#include "event_handler.h"
[5895]19
[5875]20#include "player.h"
[6241]21#include "state.h"
[7347]22#include "camera.h"
23
[7193]24#include "util/loading/load_param.h"
[5838]25
[6547]26#include "power_ups/weapon_power_up.h"
27#include "power_ups/param_power_up.h"
[5872]28
[7044]29#include "game_rules.h"
[6547]30
[6959]31#include "dot_emitter.h"
32#include "sprite_particles.h"
33
[7121]34#include "shared_network_data.h"
35
[7118]36#include "effects/explosion.h"
[7482]37#include "kill.cc"
[6959]38
[7350]39#include "shell_command.h"
[7356]40SHELL_COMMAND_STATIC(orxoWeapon, Playable, Playable::addSomeWeapons_CHEAT)
41  ->setAlias("orxoWeapon");
[7118]42
[7350]43
[5838]44Playable::Playable()
[7338]45    : weaponMan(this),
46    supportedPlaymodes(Playable::Full3D),
47    playmode(Playable::Full3D)
[5838]48{
[6442]49  this->setClassID(CL_PLAYABLE, "Playable");
50  PRINTF(4)("PLAYABLE INIT\n");
51
52  this->toList(OM_GROUP_01);
53
54  // the reference to the Current Player is NULL, because we dont have one at the beginning.
55  this->currentPlayer = NULL;
[6695]56
[6804]57  this->bFire = false;
[6868]58  this->oldFlags = 0;
[6804]59
[6695]60  this->setSynchronized(true);
[6959]61
62  this->score = 0;
63  this->oldScore = 0;
[7713]64  this->collider = NULL;
[6959]65
[7118]66  this->bDead = false;
[5838]67}
68
[6695]69
[7346]70/**
71 * @brief destroys the Playable
72 */
[5875]73Playable::~Playable()
[5838]74{
[6986]75  // THE DERIVED CLASS MUST UNSUBSCRIBE THE PLAYER THROUGH
76  // this->setPlayer(NULL);
77  // IN ITS DESTRUCTOR.
78  assert(this->currentPlayer == NULL);
[5875]79}
80
[7346]81/**
82 * @brief loads Playable parameters onto the Playable
83 * @param root the XML-root to load from
84 */
[7092]85void Playable::loadParams(const TiXmlElement* root)
86{
87  WorldEntity::loadParams(root);
88
[7346]89  LoadParam(root, "abs-dir", this, Playable, setPlayDirection);
[7092]90}
91
[7346]92/**
93 * @brief picks up a powerup
94 * @param powerUp the PowerUp that should be picked.
95 * @returns true on success (pickup was picked, false otherwise)
96 *
97 * This function also checks if the Pickup can be picked up by this Playable
98 */
99bool Playable::pickup(PowerUp* powerUp)
100{
101  if(powerUp->isA(CL_WEAPON_POWER_UP))
102  {
103    return dynamic_cast<WeaponPowerUp*>(powerUp)->process(&this->getWeaponManager());
104  }
105  else if(powerUp->isA(CL_PARAM_POWER_UP))
106  {
107    ParamPowerUp* ppu = dynamic_cast<ParamPowerUp*>(powerUp);
108    switch(ppu->getType())
109    {
110      case POWERUP_PARAM_HEALTH:
111        this->increaseHealth(ppu->getValue());
112        return true;
113      case POWERUP_PARAM_MAX_HEALTH:
114        this->increaseHealthMax(ppu->getValue());
115        return true;
116    }
117  }
118  return false;
119}
120
121/**
122 * @brief adds a Weapon to the Playable.
123 * @param weapon the Weapon to add.
124 * @param configID the Configuration ID to add this weapon to.
125 * @param slotID the slotID to add the Weapon to.
126 */
[7350]127bool Playable::addWeapon(Weapon* weapon, int configID, int slotID)
[6443]128{
[7350]129  if(this->weaponMan.addWeapon(weapon, configID, slotID))
130  {
131    this->weaponConfigChanged();
132    return true;
133  }
134  else
135  {
136    if (weapon != NULL)
137      PRINTF(2)("Unable to add Weapon (%s::%s) to %s::%s\n",
[7351]138                weapon->getClassName(), weapon->getName(), this->getClassName(), this->getName());
[7350]139    else
140      PRINTF(2)("No weapon defined\n");
141    return false;
[6443]142
[7350]143  }
[6443]144}
145
[7346]146/**
147 * @brief removes a Weapon.
148 * @param weapon the Weapon to remove.
149 */
[6443]150void Playable::removeWeapon(Weapon* weapon)
151{
[7337]152  this->weaponMan.removeWeapon(weapon);
[6443]153
[7338]154  this->weaponConfigChanged();
[6443]155}
156
[7346]157/**
158 * @brief jumps to the next WeaponConfiguration
159 */
[6444]160void Playable::nextWeaponConfig()
161{
[7337]162  this->weaponMan.nextWeaponConfig();
[7338]163  this->weaponConfigChanged();
[6444]164}
[6443]165
[7346]166/**
167 * @brief moves to the last WeaponConfiguration
168 */
[6444]169void Playable::previousWeaponConfig()
170{
[7337]171  this->weaponMan.previousWeaponConfig();
[7338]172  this->weaponConfigChanged();
[6444]173}
174
[7346]175/**
176 * @brief tells the Player, that the Weapon-Configuration has changed.
177 *
178 * TODO remove this
179 * This function is needed, so that the WeponManager of this Playable can easily update the HUD
180 */
[6568]181void Playable::weaponConfigChanged()
182{
[6578]183  if (this->currentPlayer != NULL)
184    this->currentPlayer->weaponConfigChanged();
[6568]185}
[6444]186
[7350]187/**
188 * @brief a Cheat that gives us some Weapons
189 */
190void Playable::addSomeWeapons_CHEAT()
191{
[7351]192  if (State::getPlayer() != NULL)
193  {
194    Playable* playable = State::getPlayer()->getPlayable();
195    if (playable != NULL)
196    {
197      PRINTF(2)("ADDING WEAPONS - you cheater\n");
198      playable->addWeapon(Weapon::createWeapon(CL_HYPERBLASTER));
199      playable->addWeapon(Weapon::createWeapon(CL_TURRET));
200      playable->addWeapon(Weapon::createWeapon(CL_AIMING_TURRET));
201      playable->addWeapon(Weapon::createWeapon(CL_CANNON));
202      playable->addWeapon(Weapon::createWeapon(CL_TARGETING_TURRET));
203      PRINTF(2)("ADDING WEAPONS FINISHED\n");
204    }
205  }
[7350]206}
[7346]207
[7173]208/**
[7346]209 * @brief subscribe to all events the controllable needs
210 * @param player the player that shall controll this Playable
211 * @returns false on error true otherwise.
212 */
213bool Playable::setPlayer(Player* player)
214{
215  // if we already have a Player inside do nothing
216  if (this->currentPlayer != NULL && player != NULL)
217  {
218    return false;
219  }
220
221  // eject the Player if player == NULL
222  if (this->currentPlayer != NULL && player == NULL)
223  {
224    PRINTF(4)("Player gets ejected\n");
225
226    // unsubscibe all events.
227    EventHandler* evh = EventHandler::getInstance();
228    std::vector<int>::iterator ev;
229    for (ev = this->events.begin(); ev != events.end(); ev++)
230      evh->unsubscribe( ES_GAME, (*ev));
231
232    // leave the entity
233    this->leave();
234
235    // eject the current Player.
236    Player* ejectPlayer = this->currentPlayer;
237    this->currentPlayer = NULL;
238    // eject the Player.
239    ejectPlayer->setPlayable(NULL);
240
241    return true;
242  }
243
244  // get the new Player inside
245  if (this->currentPlayer == NULL && player != NULL)
246  {
247    PRINTF(4)("New Player gets inside\n");
248    this->currentPlayer = player;
249    if (this->currentPlayer->getPlayable() != this)
250      this->currentPlayer->setPlayable(this);
251
252    /*EventHandler*/
253    EventHandler* evh = EventHandler::getInstance();
254    std::vector<int>::iterator ev;
255    for (ev = this->events.begin(); ev != events.end(); ev++)
256      evh->subscribe(player, ES_GAME, (*ev));
257
258    this->enter();
259    return true;
260  }
261
262  return false;
263}
264
265/**
266 * @brief attaches the current Camera to this Playable
267 *
268 * this function can be derived, so that any Playable can make the attachment differently.
269 */
270void Playable::attachCamera()
271{
272  State::getCameraNode()->setParentSoft(this);
273  State::getCameraTargetNode()->setParentSoft(this);
274
275}
276
277/**
278 * @brief detaches the Camera
279 * @see void Playable::attachCamera()
280 */
281void  Playable::detachCamera()
282{}
283
284
285/**
[7173]286 * @brief sets the CameraMode.
287 * @param cameraMode: the Mode to switch to.
288 */
289void Playable::setCameraMode(unsigned int cameraMode)
[7347]290{
291  State::getCamera()->setViewMode((Camera::ViewMode)cameraMode);
292}
[7338]293
[7346]294
[7338]295/**
296 * @brief sets the Playmode
297 * @param playmode the Playmode
298 * @returns true on success, false otherwise
299 */
[7339]300bool Playable::setPlaymode(Playable::Playmode playmode)
[7173]301{
[7338]302  if (!this->playmodeSupported(playmode))
303    return false;
304  else
[7345]305  {
[7347]306    this->enterPlaymode(playmode);
[7338]307    this->playmode = playmode;
[7345]308    return true;
309  }
[7173]310}
311
[7346]312/**
313 * @brief This function look, that the Playable rotates to the given rotation.
314 * @param angle the Angle around
315 * @param dirX directionX
316 * @param dirY directionY
317 * @param dirZ directionZ
318 * @param speed how fast to turn there.
319 */
320void Playable::setPlayDirection(float angle, float dirX, float dirY, float dirZ, float speed)
321{
322  this->setPlayDirection(Quaternion(angle, Vector(dirX, dirY, dirZ)), speed);
323}
[7173]324
[5872]325/**
[7346]326 * @brief all Playable will enter the Playmode Differently, say here how to do it.
327 * @param playmode the Playmode to enter.
328 *
329 * In this function all the actions that are required to enter the Playmode are described.
330 * e.g: camera, rotation, wait cycle and so on...
[7347]331 *
332 * on enter of this function the playmode is still the old playmode.
[7346]333 */
334void Playable::enterPlaymode(Playable::Playmode playmode)
335{
[7347]336  switch(playmode)
337  {
338    default:
339      this->attachCamera();
340      break;
341    case Playable::Horizontal:
342      this->setCameraMode(Camera::ViewTop);
343      break;
344    case Playable::Vertical:
345      this->setCameraMode(Camera::ViewLeft);
346      break;
347    case Playable::FromBehind:
348      this->setCameraMode(Camera::ViewBehind);
349      break;
350  }
[7346]351}
352/**
[6436]353 * @brief helps us colliding Playables
[7346]354 * @param entity the Entity to collide
355 * @param location where the collision occured.
[6436]356 */
357void Playable::collidesWith(WorldEntity* entity, const Vector& location)
358{
[7072]359  if (entity == collider)
360    return;
361  collider = entity;
362
[7121]363  if ( entity->isA(CL_PROJECTILE) && ( !State::isOnline() || SharedNetworkData::getInstance()->isGameServer() ) )
[6966]364  {
[7072]365    this->decreaseHealth(entity->getHealth() *(float)rand()/(float)RAND_MAX);
[6966]366    // EXTREME HACK
[7072]367    if (this->getHealth() <= 0.0f)
[6966]368    {
369      this->die();
[7482]370
371      if( State::getGameRules() != NULL)
372        State::getGameRules()->registerKill(Kill(entity, this));
[6966]373    }
374  }
375}
[6436]376
[6966]377
[7044]378void Playable::respawn()
[6966]379{
[7044]380  PRINTF(0)("Playable respawn\n");
381  // only if this is the spaceship of the player
382  if( this == State::getPlayer()->getPlayable())
383    State::getGameRules()->onPlayerSpawn();
[6966]384
[7085]385
[7082]386  if( this->getOwner() % 2 == 0)
387  {
[7338]388    //     this->toList(OM_GROUP_00);
[7082]389    this->setAbsCoor(213.37, 57.71, -47.98);
[7100]390    this->setAbsDir(0, 0, 1, 0);
[7082]391  }
[6966]392  else
[7099]393  { // red team
[7338]394    //     this->toList(OM_GROUP_01);
[7082]395    this->setAbsCoor(-314.450, 40.701, 83.554);
[7100]396    this->setAbsDir(1.0, -0.015, -0.012, 0.011);
[7082]397  }
[7118]398  this->reset();
399  this->bDead = false;
[6436]400}
401
[6966]402
[7088]403
[7044]404void Playable::die()
405{
[7119]406  Explosion::explode(dynamic_cast<PNode*>(this), Vector(1.0f, 1.0f, 1.0f));
407
408
[7118]409  if( !this->bDead)
410  {
411    PRINTF(0)("Playable dies\n");
[7338]412    // only if this is the spaceship of the player
[7118]413    if (State::isOnline())
414    {
415      if( this == State::getPlayer()->getPlayable())
416        State::getGameRules()->onPlayerDeath();
[7044]417
[7338]418      //     this->toList(OM_GROUP_05);
419      //HACK: moves the entity to an unknown place far far away: in the future, GameRules will look for that
[7118]420      this->setAbsCoor(-2000.0, -2000.0, -2000.0);
[7078]421
[7338]422      //explosion hack
[7118]423
424    }
425    this->bDead = true;
[7072]426  }
[7044]427}
428
429
[6986]430
431
432
[5896]433/**
[7346]434 * @brief add an event to the event list of events this Playable can capture
[5898]435 * @param eventType the Type of event to add
[5889]436 */
[5896]437void Playable::registerEvent(int eventType)
[5889]438{
439  this->events.push_back(eventType);
440
[5896]441  if (this->currentPlayer != NULL)
442    EventHandler::getInstance()->subscribe(this->currentPlayer, ES_GAME, eventType);
[5889]443}
444
[5896]445/**
[7339]446 * @brief remove an event to the event list this Playable can capture.
[5898]447 * @param event the event to unregister.
[5896]448 */
449void Playable::unregisterEvent(int eventType)
450{
[7338]451  std::vector<int>::iterator rmEvent = std::find(this->events.begin(), this->events.end(), eventType);
452  this->events.erase(rmEvent);
[5889]453
[5896]454  if (this->currentPlayer != NULL)
455    EventHandler::getInstance()->unsubscribe(ES_GAME, eventType);
456}
[5889]457
[6804]458/**
459 * @brief ticks a Playable
460 * @param dt: the passed time since the last Tick
461 */
462void Playable::tick(float dt)
463{
[7337]464  this->weaponMan.tick(dt);
[6804]465  if (this->bFire)
[7337]466    weaponMan.fire();
[6804]467}
[5896]468
[6804]469
470/**
471 * @brief processes Playable events.
472 * @param event the Captured Event.
473 */
474void Playable::process(const Event &event)
475{
476  if( event.type == KeyMapper::PEV_FIRE1)
477    this->bFire = event.bPressed;
478  else if( event.type == KeyMapper::PEV_NEXT_WEAPON && event.bPressed)
479  {
480    this->nextWeaponConfig();
481  }
482  else if ( event.type == KeyMapper::PEV_PREVIOUS_WEAPON && event.bPressed)
483    this->previousWeaponConfig();
484}
485
486
[6959]487#define DATA_FLAGS    1
488#define DATA_SCORE    2
489
[6868]490#define FLAGS_bFire   1
491
492int Playable::writeSync( const byte * data, int length, int sender )
493{
494  SYNCHELP_READ_BEGIN();
[7010]495
[6994]496  byte b;
497  SYNCHELP_READ_BYTE( b, NWT_PL_B );
[7010]498
[6868]499  byte flags;
[7010]500
[6994]501  if ( b == DATA_FLAGS )
502  {
503    SYNCHELP_READ_BYTE( flags, NWT_PL_FLAGS );
[6871]504
[6994]505    bFire = (flags & FLAGS_bFire) != 0;
[7010]506
[6994]507    return SYNCHELP_READ_N;
508  }
[7010]509
[6994]510  if ( b == DATA_SCORE )
511  {
512    int newScore;
513    SYNCHELP_READ_BYTE( newScore, NWT_PL_SCORE );
514    setScore( newScore );
[7010]515
[6994]516    return SYNCHELP_READ_N;
517  }
[6871]518
[6868]519  return SYNCHELP_READ_N;
520}
521
522int Playable::readSync( byte * data, int maxLength )
523{
524  SYNCHELP_WRITE_BEGIN();
[7010]525
[6994]526  if ( score != oldScore && isServer() )
527  {
528    SYNCHELP_WRITE_BYTE( DATA_SCORE, NWT_PL_B);
529    SYNCHELP_WRITE_INT( score, NWT_PL_SCORE );
530    oldScore = score;
[7010]531
[6994]532    return SYNCHELP_WRITE_N;
533  }
[7010]534
[6868]535  byte flags = 0;
[6871]536
[6868]537  if ( bFire )
538    flags |= FLAGS_bFire;
539
[6871]540
[6994]541  SYNCHELP_WRITE_BYTE( DATA_FLAGS, NWT_PL_B);
[6868]542  SYNCHELP_WRITE_BYTE( flags, NWT_PL_FLAGS );
543  oldFlags = flags;
544
[6871]545
[6868]546  return SYNCHELP_WRITE_N;
547}
548
549bool Playable::needsReadSync( )
550{
[6994]551  if ( score != oldScore && isServer() )
552    return true;
[6959]553
[6868]554  byte flags = 0;
[6871]555
[6868]556  if ( bFire )
557    flags |= FLAGS_bFire;
[6871]558
[6868]559  return flags!=oldFlags;
560}
[7339]561
562
[7346]563/**
564 * @brief converts a string into a Playable::Playmode.
565 * @param playmode the string naming the Playable::Playmode to convert.
566 * @returns the Playable::Playmode converted from playmode.
567 */
[7339]568Playable::Playmode Playable::stringToPlaymode(const std::string& playmode)
569{
[7412]570  if (playmode == Playable::playmodeNames[0])
[7339]571    return Playable::Vertical;
[7412]572  if (playmode == Playable::playmodeNames[1])
[7339]573    return Playable::Horizontal;
[7412]574  if (playmode == Playable::playmodeNames[2])
[7339]575    return Playable::FromBehind;
[7412]576  if (playmode == Playable::playmodeNames[3])
[7339]577    return Playable::Full3D;
578
579  return Playable::Full3D;
580}
581
[7346]582
583/**
584 * @brief converts a playmode into a string.
585 * @param playmode the Playable::Playmode to convert.
586 * @returns the String.
587 */
[7412]588const std::string& Playable::playmodeToString(Playable::Playmode playmode)
[7339]589{
590  switch(playmode)
591  {
592    case Playable::Vertical:
[7412]593      return Playable::playmodeNames[0];
[7339]594    case Playable::Horizontal:
[7412]595      return Playable::playmodeNames[1];
[7339]596    case Playable::FromBehind:
[7412]597      return Playable::playmodeNames[2];
[7339]598    case Playable::Full3D:
[7412]599      return Playable::playmodeNames[3];
[7339]600
601    default:
[7412]602      return Playable::playmodeNames[3];
[7339]603  }
[7412]604}
[7339]605
[7412]606/**
607 * @brief PlaymodeNames
608 */
609const std::string Playable::playmodeNames[] =
610{
611  "Vertical",
612  "Horizontal",
613  "FromBehind",
614  "Full3D"
615};
Note: See TracBrowser for help on using the repository browser.