Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

WeaponManger now handles Ammo.
If a Weapon has NO ammo then the Ammo will be infinite

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