Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

orxonox/trunk: loadParams is now virtual.
ALL THE CLASSES HAVE TO CALL

SuperClass::loadParams(root);

isntead of:
static_cast<SuperClass*>(this)→loadParams(root);

which was quite stupid anyways

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