Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 7346 was 7346, checked in by bensch, 18 years ago

orxonox/trunk: extreme cleanup in Playable

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