Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: orxonox.OLD/trunk/src/world_entities/weapons/weapon.cc @ 5356

Last change on this file since 5356 was 5356, checked in by bensch, 19 years ago

orxonox/trunk: saver Weapon-Projectile-generation and Stuff

File size: 17.0 KB
RevLine 
[3573]1
[4597]2/*
[3573]3   orxonox - the future of 3D-vertical-scrollers
4
5   Copyright (C) 2004 orx
6
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 2, or (at your option)
10   any later version.
11
[4826]12### File Specific
[3573]13   main-programmer: Patrick Boenzli
[4832]14   co-programmer: Benjamin Grauer
[4885]15
16   2005-07-15: Benjamin Grauer: restructurating the entire Class
[3573]17*/
18
[4885]19#define DEBUG_SPECIAL_MODULE DEBUG_MODULE_WEAPON
20
[4828]21#include "weapon.h"
22
[5355]23#include "fast_factory.h"
[4834]24#include "projectile.h"
25
[5143]26#include "resource_manager.h"
[4894]27#include "class_list.h"
[4834]28#include "load_param.h"
[4828]29#include "state.h"
[4948]30#include "sound_engine.h"
[4885]31#include "animation3d.h"
[4948]32#include "vector.h"
[3573]33
[4892]34////////////////////
35// INITAILISATION //
36// SETTING VALUES //
37////////////////////
[3870]38/**
[4885]39 * standard constructor
40 *
41 * creates a new weapon
[3575]42*/
[4955]43Weapon::Weapon (WeaponManager* weaponManager)
[3620]44{
[4885]45  this->init();
[4955]46  this->setWeaponManager(weaponManager);
[3620]47}
[3573]48
[3575]49/**
[4885]50 * standard deconstructor
[3575]51*/
[4597]52Weapon::~Weapon ()
[3573]53{
[4885]54  for (int i = 0; i < WS_STATE_COUNT; i++)
[4894]55    if (this->animation[i] && ClassList::exists(animation[i], CL_ANIMATION))  //!< @todo this should check animation3D
[4885]56      delete this->animation[i];
57  for (int i = 0; i < WA_ACTION_COUNT; i++)
[5302]58    if (this->soundBuffers[i] != NULL && ClassList::exists(this->soundBuffers[i], CL_SOUND_BUFFER))
[4885]59      ResourceManager::getInstance()->unload(this->soundBuffers[i]);
[4959]60
61  if (ClassList::exists(this->soundSource, CL_SOUND_SOURCE))
62    delete this->soundSource;
[4885]63}
[4597]64
[4885]65/**
66 * initializes the Weapon with ALL default values
67 */
68void Weapon::init()
69{
70  this->currentState     = WS_INACTIVE;
71  this->requestedAction  = WA_NONE;
72  this->stateDuration    = 0.0;
73  for (int i = 0; i < WS_STATE_COUNT; i++)
74    {
75      this->times[i] = 0.0;
76      this->animation[i] = NULL;
77    }
78  for (int i = 0; i < WA_ACTION_COUNT; i++)
79    this->soundBuffers[i] = NULL;
[3888]80
[4885]81  this->soundSource = new SoundSource(this);
[4892]82  this->emissionPoint.setParent(this);
[4885]83
[4947]84  this->projectile = CL_NULL;
85  this->projectileFactory = NULL;
[4885]86
[4906]87  this->hideInactive = true;
88
[4885]89  this->minCharge = 1.0;
90  this->maxCharge = 1.0;
[4927]91
[4885]92  this->energyLoaded = .0;
[4930]93  this->energyLoadedMax = 5.0;
[4885]94  this->energy = .0;
[4930]95  this->energyMax = 10.0;
[4955]96
97  this->setWeaponManager(NULL);
[3573]98}
99
[4972]100void Weapon::loadParams(const TiXmlElement* root)
101{
102  static_cast<WorldEntity*>(this)->loadParams(root);
103
[5356]104  LoadParam<Weapon>(root, "projectile", this, &Weapon::setProjectileType)
[4972]105      .describe("Sets the name of the Projectile to load onto the Entity");
106
107  LoadParam<Weapon>(root, "emission-point", this, &Weapon::setEmissionPoint)
108      .describe("Sets the Point of emission of this weapon");
109
110  LoadParam<Weapon>(root, "state-duration", this, &Weapon::setStateDuration)
111      .describe("Sets the duration of a given state (1: state-Name; 2: duration in seconds)");
112
113  LoadParam<Weapon>(root, "action-sound", this, &Weapon::setActionSound)
114      .describe("Sets a given sound to an action (1: action-Name; 2: name of the sound (relative to the Data-Path))");
115}
116
[4947]117/**
118 * sets the Projectile to use for this weapon.
119 * @param projectile The ID of the Projectile to use
[4950]120 * @returns true, if it was sucessfull, false on error
[4947]121 *
122 * be aware, that this function does not create Factories, as this is job of Bullet-classes.
123 */
[5356]124void Weapon::setProjectileType(ClassID projectile)
[4947]125{
126  if (projectile == CL_NULL)
[4972]127    return;
[4947]128  this->projectile = projectile;
129  this->projectileFactory = FastFactory::searchFastFactory(projectile);
130  if (this->projectileFactory == NULL)
[4979]131  {
132    PRINTF(1)("unable to find FastFactory for the Projectile.\n");
[4972]133    return;
[4979]134  }
[4948]135  else
136  {
137    // grabbing Parameters from the Projectile to have them at hand here.
138    Projectile* pj = dynamic_cast<Projectile*>(this->projectileFactory->resurrect());
139    this->minCharge = pj->getEnergyMin();
140    this->maxCharge = pj->getEnergyMax();
141    this->chargeable = pj->isChageable();
[4979]142    this->projectileFactory->kill(pj);
[4948]143  }
[4979]144}
[3573]145
[4891]146/**
[4950]147 * @see bool Weapon::setProjectile(ClassID projectile)
148 * @param projectile the Name of the Projectile.
149 */
[5356]150void Weapon::setProjectileType(const char* projectile)
[4950]151{
152  if (projectile == NULL)
[4972]153    return;
[4950]154  FastFactory* tmpFac = FastFactory::searchFastFactory(projectile);
155  if (tmpFac != NULL)
156  {
[5356]157    this->setProjectileType(tmpFac->getStoredID());
[4950]158  }
[4972]159  else
160  {
[5356]161    PRINTF(1)("Projectile %s does not exist for weapon %s\n", projectile, this->getName());
[4972]162  }
[4950]163}
164
165/**
[5356]166 * prepares Projectiles of the Weapon
167 * @param count how many Projectiles to create
168 */
169void Weapon::prepareProjectiles(unsigned int count)
170{
171  if (this->projectileFactory != NULL)
172    projectileFactory->prepare(count);
173  else
174    PRINTF(2)("unable to create %d projectile for Weapon %s\n", count, this->getName());
175}
176
177/**
178 * resurects and returns a Projectile
179 * @returns a Projectile on success, NULL on error (ProjectileFastFactory not Found)
180 */
181Projectile* Weapon::getProjectile()
182{
183  if (this->projectileFactory)
184    return dynamic_cast<Projectile*>(this->projectileFactory->resurrect());
185  else
186  {
187    PRINTF(2)("No projectile defined for Weapon %s cant return any\n", this->getName());
188    return NULL;
189  }
190}
191
192
193/**
[4892]194 * sets the emissionPoint's relative position from the Weapon
195 * @param point the Point relative to the mass-point of the Weapon
196 */
197void Weapon::setEmissionPoint(const Vector& point)
198{
199  this->emissionPoint.setRelCoor(point);
200}
201
202/**
[4891]203 * assigns a Sound-file to an action
204 * @param action the action the sound should be assigned too
205 * @param soundFile the soundFile's relative position to the data-directory (will be looked for by the ResourceManager)
206 */
[4885]207void Weapon::setActionSound(WeaponAction action, const char* soundFile)
208{
209  if (action >= WA_ACTION_COUNT)
210    return;
[4930]211  if (this->soundBuffers[action] != NULL)
212    ResourceManager::getInstance()->unload(this->soundBuffers[action]);
213
[4885]214  else if (soundFile != NULL)
215  {
216    this->soundBuffers[action] = (SoundBuffer*)ResourceManager::getInstance()->load(soundFile, WAV);
217    if (this->soundBuffers[action] != NULL)
218    {
[4967]219      PRINTF(4)("Loaded sound %s to action %s.\n", soundFile, actionToChar(action));
[4885]220    }
221    else
222    {
[5041]223      PRINTF(2)("Failed to load sound %s to %s.\n.", soundFile, actionToChar(action));
[4885]224    }
225  }
226  else
227    this->soundBuffers[action] = NULL;
228}
229
[4893]230/**
[4895]231 * creates/returns an Animation3D for a certain State.
232 * @param state what State should the Animation be created/returned for
233 * @param node the node this Animation should apply to. (NULL is fine if the animation was already created)
234 * @returns The created animation.Animation(), NULL on error (or if the animation does not yet exist).
[4893]235 *
236 * This function does only generate the Animation Object, and if set it will
237 * automatically be executed, when a certain State is reached.
238 * What this does not do, is set keyframes, you have to operate on the returned animation.
239 */
[4895]240Animation3D* Weapon::getAnimation(WeaponState state, PNode* node)
[4893]241{
[4895]242  if (state >= WS_STATE_COUNT) // if the state is not known
[4893]243    return NULL;
244
[4895]245  if (unlikely(this->animation[state] == NULL)) // if the animation does not exist yet create it.
[4893]246  {
[4895]247    if (likely(node != NULL))
248      return this->animation[state] = new Animation3D(node);
249    else
250    {
251      PRINTF(2)("Node not defined for the Creation of the 3D-animation of state %s\n", stateToChar(state));
252      return NULL;
253    }
[4893]254  }
[4895]255  else
256    return this->animation[state];
[4893]257}
258
[4892]259/////////////////
260//  EXECUTION  //
261// GAME ACTION //
262/////////////////
[4597]263/**
[4885]264 * request an action that should be executed,
265 * @param action the next action to take
266 *
267 * This function must be called instead of the actions (like fire/reload...)
268 * to make all the checks needed to have a usefull WeaponSystem.
269 */
270void Weapon::requestAction(WeaponAction action)
271{
[4906]272  if (likely(this->isActive()))
[4885]273  {
[4906]274    if (this->requestedAction != WA_NONE)
275      return;
[5041]276    PRINTF(5)("next action will be %s in %f seconds\n", actionToChar(action), this->stateDuration);
[4885]277    this->requestedAction = action;
278  }
[4906]279  //else
280  else if (unlikely(action == WA_ACTIVATE))
281  {
282    this->currentState = WS_ACTIVATING;
[4926]283    this->requestedAction = WA_ACTIVATE;
[4906]284  }
[4885]285}
[3577]286
[4890]287/**
288 * adds energy to the Weapon
289 * @param energyToAdd The amount of energy
290 * @returns the amount of energy we did not pick up, because the weapon is already full
291 */
292float Weapon::increaseEnergy(float energyToAdd)
293{
294  float maxAddEnergy = this->energyMax - this->energy;
295
296  if (maxAddEnergy >= energyToAdd)
297  {
298    this->energy += energyToAdd;
299    return 0.0;
300  }
301  else
302  {
303    this->energy += maxAddEnergy;
304    return energyToAdd - maxAddEnergy;
305  }
306}
307
[4892]308//////////////////////
309// WEAPON INTERNALS //
310//////////////////////
[4891]311/**
312 * executes an action, and with it starts a new State.
313 * @return true, if it worked, false otherwise
314 *
315 * This function checks, wheter the possibility of executing an action is valid,
316 * and does all the necessary stuff, to execute them. If an action does not succeed,
317 * it tries to go around it. (ex. shoot->noAmo->reload()->wait until shoot comes again)
318 */
[4885]319bool Weapon::execute()
[3583]320{
[4906]321#if DEBUG > 4
[4885]322  PRINTF(4)("trying to execute action %s\n", actionToChar(this->requestedAction));
323  this->debug();
[4906]324#endif
[4885]325
[4926]326  WeaponAction action = this->requestedAction;
327  this->requestedAction = WA_NONE;
328
329  switch (action)
[4885]330  {
331    case WA_SHOOT:
[4894]332      return this->fireW();
[4885]333      break;
334    case WA_CHARGE:
[4894]335      return this->chargeW();
[4885]336      break;
337    case WA_RELOAD:
[4894]338      return this->reloadW();
[4885]339      break;
340    case WA_DEACTIVATE:
[4894]341      return this->deactivateW();
[4885]342      break;
343    case WA_ACTIVATE:
[4894]344      return this->activateW();
[4885]345      break;
346  }
[3583]347}
[3577]348
[4597]349/**
[4894]350 * checks and activates the Weapon.
351 * @return true on success.
[4892]352 */
353bool Weapon::activateW()
[3583]354{
[4926]355//  if (this->currentState == WS_INACTIVE)
[4892]356  {
357        // play Sound
[4893]358    if (likely(this->soundBuffers[WA_ACTIVATE] != NULL))
[4892]359      this->soundSource->play(this->soundBuffers[WA_ACTIVATE]);
360        // activate
[4895]361    PRINTF(4)("Activating the Weapon %s\n", this->getName());
[4892]362    this->activate();
[4895]363    // setting up for next action
[4926]364    this->enterState(WS_ACTIVATING);
[4892]365  }
[3583]366}
[3577]367
[4597]368/**
[4894]369 * checks and deactivates the Weapon
370 * @return true on success.
[4892]371 */
372bool Weapon::deactivateW()
[3583]373{
[4949]374//  if (this->currentState != WS_INACTIVE)
[4892]375  {
376    PRINTF(4)("Deactivating the Weapon %s\n", this->getName());
377        // play Sound
378    if (this->soundBuffers[WA_DEACTIVATE] != NULL)
379      this->soundSource->play(this->soundBuffers[WA_DEACTIVATE]);
[4926]380    // deactivate
[4892]381    this->deactivate();
[4926]382    this->enterState(WS_DEACTIVATING);
[4892]383  }
[3583]384}
[3577]385
[4892]386/**
[4894]387 * checks and charges the Weapon
388 * @return true on success.
[4892]389 */
390bool Weapon::chargeW()
[4885]391{
[4892]392  if ( this->currentState != WS_INACTIVE && this->energyLoaded >= this->minCharge)
393  {
394        // playing Sound
395    if (this->soundBuffers[WA_CHARGE] != NULL)
396      this->soundSource->play(this->soundBuffers[WA_CHARGE]);
[4893]397
[4892]398        // charge
399    this->charge();
400        // setting up for the next state
[4926]401    this->enterState(WS_CHARGING);
[4892]402  }
403  else // deactivate the Weapon if we do not have enough energy
404  {
405    this->requestAction(WA_RELOAD);
406  }
[4885]407}
[3573]408
[4892]409/**
[4894]410 * checks and fires the Weapon
411 * @return true on success.
[4892]412 */
413bool Weapon::fireW()
[3575]414{
[4892]415     //if (likely(this->currentState != WS_INACTIVE))
416  if (this->minCharge <= this->energyLoaded)
417  {
418          // playing Sound
419    if (this->soundBuffers[WA_SHOOT] != NULL)
420      this->soundSource->play(this->soundBuffers[WA_SHOOT]);
421          // fire
422    this->fire();
423    this->energyLoaded -= this->minCharge;
424          // setting up for the next state
[4926]425    this->enterState(WS_SHOOTING);
[4892]426  }
427  else  // reload if we still have the charge
428  {
429    this->requestAction(WA_RELOAD);
[4930]430    this->execute();
[4892]431  }
432}
433
434/**
[4894]435 * checks and Reloads the Weapon
436 * @return true on success.
[4892]437 */
438bool Weapon::reloadW()
439{
[4885]440  PRINTF(4)("Reloading Weapon %s\n", this->getName());
[4892]441  if (unlikely(this->energy + this->energyLoaded < this->minCharge))
[4885]442  {
443    this->requestAction(WA_DEACTIVATE);
[4930]444    this->execute();
[4892]445    return false;
[4885]446  }
[3573]447
[4885]448  float chargeSize = this->energyLoadedMax - this->energyLoaded;       //!< The energy to be charged
[3573]449
[4892]450  if (this->soundBuffers[WA_RELOAD] != NULL)
451    this->soundSource->play(this->soundBuffers[WA_RELOAD]);
452
[4885]453  if (chargeSize > this->energy)
454  {
455    this->energyLoaded += this->energy;
456    this->energy = 0.0;
[5041]457    PRINT(5)("Energy depleted\n");
[4885]458  }
459  else
460  {
[5041]461    PRINTF(5)("Loaded %f energy into the Guns Buffer\n", chargeSize);
[4885]462    this->energyLoaded += chargeSize;
463    this->energy -= chargeSize;
464  }
[4892]465  this->reload();
[4926]466  this->enterState(WS_RELOADING);
467}
[3575]468
[4926]469/**
470 * enters the requested State, plays back animations updates the timing.
471 * @param state the state to enter.
472 */
473inline void Weapon::enterState(WeaponState state)
474{
[5041]475  PRINTF(4)("ENTERING STATE %s\n", stateToChar(state));
[4926]476  // playing animation if availiable
477  if (likely(this->animation[state] != NULL))
478    this->animation[state]->replay();
479
480  this->stateDuration = this->times[state] + this->stateDuration;
481  this->currentState = state;
[3575]482}
483
[4927]484///////////////////
485//  WORLD-ENTITY //
486// FUNCTIONALITY //
487///////////////////
[3575]488/**
[4885]489 * tick signal for time dependent/driven stuff
[3575]490*/
[4906]491void Weapon::tickW(float dt)
[4885]492{
[4934]493  //printf("%s ", stateToChar(this->currentState));
[4910]494
[4885]495  // setting up the timing properties
496  this->stateDuration -= dt;
[3575]497
[4949]498  if (this->stateDuration <= 0.0)
[4885]499  {
[4949]500    if (unlikely (this->currentState == WS_DEACTIVATING))
[4885]501    {
[4949]502      this->currentState = WS_INACTIVE;
503      return;
504    }
505    else
506      this->currentState = WS_IDLE;
[4906]507
[4949]508    if (this->requestedAction != WA_NONE)
509    {
510      this->stateDuration = -dt;
511      this->execute();
[4885]512    }
513  }
[4906]514  tick(dt);
[4885]515}
516
[3575]517/**
[4885]518 *  this will draw the weapon
[3575]519*/
[4885]520void Weapon::draw ()
[3575]521{}
522
523
524
525
[3576]526
[4885]527//////////////////////
528// HELPER FUNCTIONS //
529//////////////////////
[3576]530/**
[4891]531 * checks wether all the Weapons functions are valid, and if it is possible to go to action with it.
532 *
533 */
534bool Weapon::check() const
535{
536  bool retVal = true;
537
[4947]538//  if (this->projectile == NULL)
[4891]539  {
[5041]540    PRINTF(1)("There was no projectile assigned to the Weapon.\n");
[4891]541    retVal = false;
542  }
543
544
545
546
547  return retVal;
548}
549
550/**
[4885]551 * some nice debugging information about this Weapon
552 */
553void Weapon::debug() const
554{
[5041]555  PRINT(3)("Weapon-Debug %s, state: %s, nextAction: %s\n", this->getName(), Weapon::stateToChar(this->currentState), Weapon::actionToChar(requestedAction));
[4885]556  PRINT(3)("Energy: max: %f; current: %f;  loadedMax: %f; loadedCurrent: %f; chargeMin: %f, chargeMax %f\n",
557            this->energyMax, this->energy, this->energyLoadedMax, this->energyLoaded, this->minCharge, this->maxCharge);
[4967]558
559
[4885]560}
[3575]561
562
[4885]563// static
564/**
565 * Converts a String into an Action.
566 * @param action the String input holding the Action.
567 * @return The Action if known, WA_NONE otherwise.
568 */
569WeaponAction Weapon::charToAction(const char* action)
570{
571  if (!strcmp(action, "none"))
572    return WA_NONE;
573  else if (!strcmp(action, "shoot"))
574    return WA_SHOOT;
575  else if (!strcmp(action, "charge"))
576    return WA_CHARGE;
577  else if (!strcmp(action, "reload"))
578    return WA_RELOAD;
579  else if (!strcmp(action, "acitvate"))
580    return WA_ACTIVATE;
581  else if (!strcmp(action, "deactivate"))
582    return WA_DEACTIVATE;
583  else if (!strcmp(action, "special1"))
584    return WA_SPECIAL1;
585  else
586    {
587      PRINTF(2)("action %s could not be identified.\n", action);
588      return WA_NONE;
589    }
590}
[3575]591
592/**
[4885]593 * converts an action into a String
594 * @param action the action to convert
595 * @return a String matching the name of the action
596 */
597const char* Weapon::actionToChar(WeaponAction action)
598{
599  switch (action)
600  {
601    case WA_SHOOT:
602      return "shoot";
603      break;
604    case WA_CHARGE:
605      return "charge";
606      break;
607    case WA_RELOAD:
608      return "reload";
609      break;
610    case WA_ACTIVATE:
611      return "activate";
612      break;
613    case WA_DEACTIVATE:
614      return "deactivate";
615      break;
616    case WA_SPECIAL1:
617      return "special1";
618      break;
619    default:
620      return "none";
621      break;
622  }
623}
[3577]624
625/**
[4885]626 * Converts a String into a State.
627 * @param state the String input holding the State.
628 * @return The State if known, WS_NONE otherwise.
629 */
630WeaponState Weapon::charToState(const char* state)
631{
632  if (!strcmp(state, "none"))
633    return WS_NONE;
634  else if (!strcmp(state, "shooting"))
635    return WS_SHOOTING;
636  else if (!strcmp(state, "charging"))
637    return WS_CHARGING;
638  else if (!strcmp(state, "reloading"))
639    return WS_RELOADING;
640  else if (!strcmp(state, "activating"))
641    return WS_ACTIVATING;
642  else if (!strcmp(state, "deactivating"))
643    return WS_DEACTIVATING;
644  else if (!strcmp(state, "inactive"))
645    return WS_INACTIVE;
646  else if (!strcmp(state, "idle"))
647    return WS_IDLE;
648  else
649    {
650      PRINTF(2)("state %s could not be identified.\n", state);
651      return WS_NONE;
652    }
653}
[3583]654
655/**
[4885]656 * converts a State into a String
657 * @param state the state to convert
658 * @return a String matching the name of the state
659 */
660const char* Weapon::stateToChar(WeaponState state)
661{
662  switch (state)
663  {
664    case WS_SHOOTING:
665      return "shooting";
666      break;
667    case WS_CHARGING:
668      return "charging";
669      break;
670    case WS_RELOADING:
671      return "reloading";
672      break;
673    case WS_ACTIVATING:
674      return "activating";
675      break;
676    case WS_DEACTIVATING:
677      return "deactivating";
678      break;
679    case WS_IDLE:
680      return "idle";
681      break;
[4906]682    case WS_INACTIVE:
683      return "inactive";
684      break;
[4885]685    default:
686      return "none";
687      break;
688  }
689}
Note: See TracBrowser for help on using the repository browser.