Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

orxonox/trunk: Weapons update, Now one can really assign SlotCapabilities and they are used and checked

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