Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

trunk: widget war

File size: 20.3 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  static_cast<WorldEntity*>(this)->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::getMaxEnergyWidget()
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    // activate
424    PRINTF(4)("Activating the Weapon %s\n", this->getName());
425    this->activate();
426    // setting up for next action
427    this->enterState(WS_ACTIVATING);
428  }
429}
430
431/**
432 * checks and deactivates the Weapon
433 * @return true on success.
434 */
435bool Weapon::deactivateW()
436{
437  //  if (this->currentState != WS_INACTIVE)
438  {
439    PRINTF(4)("Deactivating the Weapon %s\n", this->getName());
440    // play Sound
441    if (this->soundBuffers[WA_DEACTIVATE] != NULL)
442      this->soundSource->play(this->soundBuffers[WA_DEACTIVATE]);
443    // deactivate
444    this->deactivate();
445    this->enterState(WS_DEACTIVATING);
446  }
447}
448
449/**
450 * checks and charges the Weapon
451 * @return true on success.
452 */
453bool Weapon::chargeW()
454{
455  if ( this->currentState != WS_INACTIVE && this->energyLoaded >= this->minCharge)
456  {
457    // playing Sound
458    if (this->soundBuffers[WA_CHARGE] != NULL)
459      this->soundSource->play(this->soundBuffers[WA_CHARGE]);
460
461    // charge
462    this->charge();
463    // setting up for the next state
464    this->enterState(WS_CHARGING);
465  }
466  else // deactivate the Weapon if we do not have enough energy
467  {
468    this->requestAction(WA_RELOAD);
469  }
470}
471
472/**
473 * checks and fires the Weapon
474 * @return true on success.
475 */
476bool Weapon::fireW()
477{
478  //if (likely(this->currentState != WS_INACTIVE))
479  if (this->minCharge <= this->energyLoaded)
480  {
481    // playing Sound
482    if (this->soundBuffers[WA_SHOOT] != NULL)
483      this->soundSource->play(this->soundBuffers[WA_SHOOT]);
484    // fire
485    this->energyLoaded -= this->minCharge;
486    this->fire();
487    // setting up for the next state
488    this->enterState(WS_SHOOTING);
489  }
490  else  // reload if we still have the charge
491  {
492    this->requestAction(WA_RELOAD);
493    this->execute();
494  }
495}
496
497/**
498 * checks and Reloads the Weapon
499 * @return true on success.
500 */
501bool Weapon::reloadW()
502{
503  PRINTF(4)("Reloading Weapon %s\n", this->getName());
504  if (unlikely(this->energy + this->energyLoaded < this->minCharge))
505  {
506    this->requestAction(WA_DEACTIVATE);
507    this->execute();
508    return false;
509  }
510
511  float chargeSize = this->energyLoadedMax - this->energyLoaded;       //!< The energy to be charged
512
513  if (this->soundBuffers[WA_RELOAD] != NULL)
514    this->soundSource->play(this->soundBuffers[WA_RELOAD]);
515
516  if (chargeSize > this->energy)
517  {
518    this->energyLoaded += this->energy;
519    this->energy = 0.0;
520    PRINT(5)("Energy depleted\n");
521  }
522  else
523  {
524    PRINTF(5)("Loaded %f energy into the Guns Buffer\n", chargeSize);
525    this->energyLoaded += chargeSize;
526    this->energy -= chargeSize;
527  }
528  this->reload();
529  this->enterState(WS_RELOADING);
530}
531
532/**
533 * enters the requested State, plays back animations updates the timing.
534 * @param state the state to enter.
535 */
536inline void Weapon::enterState(WeaponState state)
537{
538  PRINTF(4)("ENTERING STATE %s\n", stateToChar(state));
539  // playing animation if availiable
540  if (likely(this->animation[state] != NULL))
541    this->animation[state]->replay();
542
543  this->stateDuration = this->times[state] + this->stateDuration;
544  this->currentState = state;
545}
546
547///////////////////
548//  WORLD-ENTITY //
549// FUNCTIONALITY //
550///////////////////
551/**
552 * tick signal for time dependent/driven stuff
553*/
554void Weapon::tick(float dt)
555{
556  //printf("%s ", stateToChar(this->currentState));
557
558  // setting up the timing properties
559  this->stateDuration -= dt;
560
561  if (this->stateDuration <= 0.0)
562  {
563    if (unlikely (this->currentState == WS_DEACTIVATING))
564    {
565      this->currentState = WS_INACTIVE;
566      return;
567    }
568    else
569      this->currentState = WS_IDLE;
570
571    if (this->requestedAction != WA_NONE)
572    {
573      this->stateDuration = -dt;
574      this->execute();
575    }
576  }
577}
578
579
580
581
582//////////////////////
583// HELPER FUNCTIONS //
584//////////////////////
585/**
586 * checks wether all the Weapons functions are valid, and if it is possible to go to action with it.
587 * @todo IMPLEMENT the Weapons Check
588 */
589bool Weapon::check() const
590{
591  bool retVal = true;
592
593  //  if (this->projectile == NULL)
594  {
595    PRINTF(1)("There was no projectile assigned to the Weapon.\n");
596    retVal = false;
597  }
598
599
600
601
602  return retVal;
603}
604
605/**
606 * some nice debugging information about this Weapon
607 */
608void Weapon::debug() const
609{
610  PRINT(0)("Weapon-Debug %s, state: %s (duration: %fs), nextAction: %s\n", this->getName(), Weapon::stateToChar(this->currentState), this->stateDuration, Weapon::actionToChar(requestedAction));
611  PRINT(0)("Energy: max: %f; current: %f;  loadedMax: %f; loadedCurrent: %f; chargeMin: %f, chargeMax %f\n",
612           this->energyMax, this->energy, this->energyLoadedMax, this->energyLoaded, this->minCharge, this->maxCharge);
613
614
615}
616
617////////////////////////////////////////////////////////
618// static Definitions (transormators for readability) //
619////////////////////////////////////////////////////////
620/**
621 * Converts a String into an Action.
622 * @param action the String input holding the Action.
623 * @return The Action if known, WA_NONE otherwise.
624 */
625WeaponAction Weapon::charToAction(const char* action)
626{
627  if (!strcmp(action, "none"))
628    return WA_NONE;
629  else if (!strcmp(action, "shoot"))
630    return WA_SHOOT;
631  else if (!strcmp(action, "charge"))
632    return WA_CHARGE;
633  else if (!strcmp(action, "reload"))
634    return WA_RELOAD;
635  else if (!strcmp(action, "acitvate"))
636    return WA_ACTIVATE;
637  else if (!strcmp(action, "deactivate"))
638    return WA_DEACTIVATE;
639  else if (!strcmp(action, "special1"))
640    return WA_SPECIAL1;
641  else
642  {
643    PRINTF(2)("action %s could not be identified.\n", action);
644    return WA_NONE;
645  }
646}
647
648/**
649 * converts an action into a String
650 * @param action the action to convert
651 * @return a String matching the name of the action
652 */
653const char* Weapon::actionToChar(WeaponAction action)
654{
655  switch (action)
656  {
657    case WA_SHOOT:
658      return "shoot";
659      break;
660    case WA_CHARGE:
661      return "charge";
662      break;
663    case WA_RELOAD:
664      return "reload";
665      break;
666    case WA_ACTIVATE:
667      return "activate";
668      break;
669    case WA_DEACTIVATE:
670      return "deactivate";
671      break;
672    case WA_SPECIAL1:
673      return "special1";
674      break;
675    default:
676      return "none";
677      break;
678  }
679}
680
681/**
682 * Converts a String into a State.
683 * @param state the String input holding the State.
684 * @return The State if known, WS_NONE otherwise.
685 */
686WeaponState Weapon::charToState(const char* state)
687{
688  if (!strcmp(state, "none"))
689    return WS_NONE;
690  else if (!strcmp(state, "shooting"))
691    return WS_SHOOTING;
692  else if (!strcmp(state, "charging"))
693    return WS_CHARGING;
694  else if (!strcmp(state, "reloading"))
695    return WS_RELOADING;
696  else if (!strcmp(state, "activating"))
697    return WS_ACTIVATING;
698  else if (!strcmp(state, "deactivating"))
699    return WS_DEACTIVATING;
700  else if (!strcmp(state, "inactive"))
701    return WS_INACTIVE;
702  else if (!strcmp(state, "idle"))
703    return WS_IDLE;
704  else
705  {
706    PRINTF(2)("state %s could not be identified.\n", state);
707    return WS_NONE;
708  }
709}
710
711/**
712 * converts a State into a String
713 * @param state the state to convert
714 * @return a String matching the name of the state
715 */
716const char* Weapon::stateToChar(WeaponState state)
717{
718  switch (state)
719  {
720    case WS_SHOOTING:
721      return "shooting";
722      break;
723    case WS_CHARGING:
724      return "charging";
725      break;
726    case WS_RELOADING:
727      return "reloading";
728      break;
729    case WS_ACTIVATING:
730      return "activating";
731      break;
732    case WS_DEACTIVATING:
733      return "deactivating";
734      break;
735    case WS_IDLE:
736      return "idle";
737      break;
738    case WS_INACTIVE:
739      return "inactive";
740      break;
741    default:
742      return "none";
743      break;
744  }
745}
Note: See TracBrowser for help on using the repository browser.