Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

trunk: Energy→Health

File size: 19.2 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  // set this object to be synchronized over network
104  //this->setSynchronized(true);
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->getHealthMax();
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
290void Weapon::updateWidgets()
291{
292  if (this->energyWidget != NULL)
293  {
294    this->energyWidget->setMaximum(this->energyMax);
295    this->energyWidget->setValue(this->energy);
296  }
297}
298
299/////////////////
300//  EXECUTION  //
301// GAME ACTION //
302/////////////////
303/**
304 * request an action that should be executed,
305 * @param action the next action to take
306 *
307 * This function must be called instead of the actions (like fire/reload...)
308 * to make all the checks needed to have a usefull WeaponSystem.
309 */
310void Weapon::requestAction(WeaponAction action)
311{
312  if (likely(this->isActive()))
313  {
314    if (this->requestedAction != WA_NONE)
315      return;
316    PRINTF(5)("next action will be %s in %f seconds\n", actionToChar(action), this->stateDuration);
317    this->requestedAction = action;
318  }
319  //else
320  else if (unlikely(action == WA_ACTIVATE))
321  {
322    this->currentState = WS_ACTIVATING;
323    this->requestedAction = WA_ACTIVATE;
324  }
325}
326
327/**
328 * adds energy to the Weapon
329 * @param energyToAdd The amount of energy
330 * @returns the amount of energy we did not pick up, because the weapon is already full
331 */
332float Weapon::increaseEnergy(float energyToAdd)
333{
334  float maxAddEnergy = this->energyMax - this->energy;
335
336  if (maxAddEnergy >= energyToAdd)
337  {
338    this->energy += energyToAdd;
339    return 0.0;
340  }
341  else
342  {
343    this->energy += maxAddEnergy;
344    return energyToAdd - maxAddEnergy;
345  }
346}
347
348////////////////////////////////////////////////////////////
349// WEAPON INTERNALS                                       //
350// These are functions, that no other Weapon should over- //
351// write. No class has direct Access to them, as it is    //
352// quite a complicated process, handling a Weapon from    //
353// the outside                                            //
354////////////////////////////////////////////////////////////
355/**
356 * executes an action, and with it starts a new State.
357 * @return true, if it worked, false otherwise
358 *
359 * This function checks, wheter the possibility of executing an action is valid,
360 * and does all the necessary stuff, to execute them. If an action does not succeed,
361 * it tries to go around it. (ex. shoot->noAmo->reload()->wait until shoot comes again)
362 */
363bool Weapon::execute()
364{
365#if DEBUG > 4
366  PRINTF(4)("trying to execute action %s\n", actionToChar(this->requestedAction));
367  this->debug();
368#endif
369
370  WeaponAction action = this->requestedAction;
371  this->requestedAction = WA_NONE;
372
373  switch (action)
374  {
375  case WA_SHOOT:
376    return this->fireW();
377    break;
378  case WA_CHARGE:
379    return this->chargeW();
380    break;
381  case WA_RELOAD:
382    return this->reloadW();
383    break;
384  case WA_DEACTIVATE:
385    return this->deactivateW();
386    break;
387  case WA_ACTIVATE:
388    return this->activateW();
389    break;
390  }
391}
392
393/**
394 * checks and activates the Weapon.
395 * @return true on success.
396 */
397bool Weapon::activateW()
398{
399  //  if (this->currentState == WS_INACTIVE)
400  {
401    // play Sound
402    if (likely(this->soundBuffers[WA_ACTIVATE] != NULL))
403      this->soundSource->play(this->soundBuffers[WA_ACTIVATE]);
404    this->updateWidgets();
405    // activate
406    PRINTF(4)("Activating the Weapon %s\n", this->getName());
407    this->activate();
408    // setting up for next action
409    this->enterState(WS_ACTIVATING);
410  }
411}
412
413/**
414 * checks and deactivates the Weapon
415 * @return true on success.
416 */
417bool Weapon::deactivateW()
418{
419  //  if (this->currentState != WS_INACTIVE)
420  {
421    PRINTF(4)("Deactivating the Weapon %s\n", this->getName());
422    // play Sound
423    if (this->soundBuffers[WA_DEACTIVATE] != NULL)
424      this->soundSource->play(this->soundBuffers[WA_DEACTIVATE]);
425    // deactivate
426    this->deactivate();
427    this->enterState(WS_DEACTIVATING);
428  }
429}
430
431/**
432 * checks and charges the Weapon
433 * @return true on success.
434 */
435bool Weapon::chargeW()
436{
437  if ( this->currentState != WS_INACTIVE && this->energy >= this->minCharge)
438  {
439    // playing Sound
440    if (this->soundBuffers[WA_CHARGE] != NULL)
441      this->soundSource->play(this->soundBuffers[WA_CHARGE]);
442
443    // charge
444    this->charge();
445    // setting up for the next state
446    this->enterState(WS_CHARGING);
447  }
448  else // deactivate the Weapon if we do not have enough energy
449  {
450    this->requestAction(WA_RELOAD);
451  }
452}
453
454/**
455 * checks and fires the Weapon
456 * @return true on success.
457 */
458bool Weapon::fireW()
459{
460  //if (likely(this->currentState != WS_INACTIVE))
461  if (this->minCharge <= this->energy)
462  {
463    // playing Sound
464    if (this->soundBuffers[WA_SHOOT] != NULL)
465      this->soundSource->play(this->soundBuffers[WA_SHOOT]);
466    this->updateWidgets();
467    // fire
468    this->energy -= this->minCharge;
469    this->fire();
470    // setting up for the next state
471    this->enterState(WS_SHOOTING);
472  }
473  else  // reload if we still have the charge
474  {
475    this->requestAction(WA_RELOAD);
476    this->execute();
477  }
478}
479
480/**
481 * checks and Reloads the Weapon
482 * @return true on success.
483 */
484bool Weapon::reloadW()
485{
486  PRINTF(4)("Reloading Weapon %s\n", this->getName());
487  if (this->ammoContainer.get() != NULL &&
488        unlikely(this->energy + this->ammoContainer->getStoredEnergy() < this->minCharge))
489  {
490    this->requestAction(WA_DEACTIVATE);
491    this->execute();
492    return false;
493  }
494
495
496  if (this->soundBuffers[WA_RELOAD] != NULL)
497    this->soundSource->play(this->soundBuffers[WA_RELOAD]);
498
499  if (this->ammoContainer.get() != NULL)
500    this->ammoContainer->fillWeapon(this);
501  else
502  {
503    this->energy = this->energyMax;
504  }
505  this->updateWidgets();
506  this->reload();
507  this->enterState(WS_RELOADING);
508}
509
510/**
511 * enters the requested State, plays back animations updates the timing.
512 * @param state the state to enter.
513 */
514inline void Weapon::enterState(WeaponState state)
515{
516  PRINTF(4)("ENTERING STATE %s\n", stateToChar(state));
517  // playing animation if availiable
518  if (likely(this->animation[state] != NULL))
519    this->animation[state]->replay();
520
521  this->stateDuration = this->times[state] + this->stateDuration;
522  this->currentState = state;
523}
524
525///////////////////
526//  WORLD-ENTITY //
527// FUNCTIONALITY //
528///////////////////
529/**
530 * tick signal for time dependent/driven stuff
531*/
532void Weapon::tickW(float dt)
533{
534  //printf("%s ", stateToChar(this->currentState));
535
536  // setting up the timing properties
537  this->stateDuration -= dt;
538
539  if (this->stateDuration <= 0.0)
540  {
541    if (unlikely (this->currentState == WS_DEACTIVATING))
542    {
543      this->currentState = WS_INACTIVE;
544      return;
545    }
546    else
547      this->currentState = WS_IDLE;
548
549    if (this->requestedAction != WA_NONE)
550    {
551      this->stateDuration = -dt;
552      this->execute();
553    }
554  }
555  tick(dt);
556}
557
558
559
560
561//////////////////////
562// HELPER FUNCTIONS //
563//////////////////////
564/**
565 * checks wether all the Weapons functions are valid, and if it is possible to go to action with it.
566 * @todo IMPLEMENT the Weapons Check
567 */
568bool Weapon::check() const
569{
570  bool retVal = true;
571
572  //  if (this->projectile == NULL)
573  {
574    PRINTF(1)("There was no projectile assigned to the Weapon.\n");
575    retVal = false;
576  }
577
578
579
580
581  return retVal;
582}
583
584/**
585 * some nice debugging information about this Weapon
586 */
587void Weapon::debug() const
588{
589  PRINT(0)("Weapon-Debug %s, state: %s (duration: %fs), nextAction: %s\n", this->getName(), Weapon::stateToChar(this->currentState), this->stateDuration, Weapon::actionToChar(requestedAction));
590  PRINT(0)("Energy: max: %f; current: %f; chargeMin: %f, chargeMax %f\n",
591           this->energyMax, this->energy, this->minCharge, this->maxCharge);
592
593
594}
595
596////////////////////////////////////////////////////////
597// static Definitions (transormators for readability) //
598////////////////////////////////////////////////////////
599/**
600 * Converts a String into an Action.
601 * @param action the String input holding the Action.
602 * @return The Action if known, WA_NONE otherwise.
603 */
604WeaponAction Weapon::charToAction(const char* action)
605{
606  if (!strcmp(action, "none"))
607    return WA_NONE;
608  else if (!strcmp(action, "shoot"))
609    return WA_SHOOT;
610  else if (!strcmp(action, "charge"))
611    return WA_CHARGE;
612  else if (!strcmp(action, "reload"))
613    return WA_RELOAD;
614  else if (!strcmp(action, "acitvate"))
615    return WA_ACTIVATE;
616  else if (!strcmp(action, "deactivate"))
617    return WA_DEACTIVATE;
618  else if (!strcmp(action, "special1"))
619    return WA_SPECIAL1;
620  else
621  {
622    PRINTF(2)("action %s could not be identified.\n", action);
623    return WA_NONE;
624  }
625}
626
627/**
628 * converts an action into a String
629 * @param action the action to convert
630 * @return a String matching the name of the action
631 */
632const char* Weapon::actionToChar(WeaponAction action)
633{
634  switch (action)
635  {
636  case WA_SHOOT:
637    return "shoot";
638    break;
639  case WA_CHARGE:
640    return "charge";
641    break;
642  case WA_RELOAD:
643    return "reload";
644    break;
645  case WA_ACTIVATE:
646    return "activate";
647    break;
648  case WA_DEACTIVATE:
649    return "deactivate";
650    break;
651  case WA_SPECIAL1:
652    return "special1";
653    break;
654  default:
655    return "none";
656    break;
657  }
658}
659
660/**
661 * Converts a String into a State.
662 * @param state the String input holding the State.
663 * @return The State if known, WS_NONE otherwise.
664 */
665WeaponState Weapon::charToState(const char* state)
666{
667  if (!strcmp(state, "none"))
668    return WS_NONE;
669  else if (!strcmp(state, "shooting"))
670    return WS_SHOOTING;
671  else if (!strcmp(state, "charging"))
672    return WS_CHARGING;
673  else if (!strcmp(state, "reloading"))
674    return WS_RELOADING;
675  else if (!strcmp(state, "activating"))
676    return WS_ACTIVATING;
677  else if (!strcmp(state, "deactivating"))
678    return WS_DEACTIVATING;
679  else if (!strcmp(state, "inactive"))
680    return WS_INACTIVE;
681  else if (!strcmp(state, "idle"))
682    return WS_IDLE;
683  else
684  {
685    PRINTF(2)("state %s could not be identified.\n", state);
686    return WS_NONE;
687  }
688}
689
690/**
691 * converts a State into a String
692 * @param state the state to convert
693 * @return a String matching the name of the state
694 */
695const char* Weapon::stateToChar(WeaponState state)
696{
697  switch (state)
698  {
699  case WS_SHOOTING:
700    return "shooting";
701    break;
702  case WS_CHARGING:
703    return "charging";
704    break;
705  case WS_RELOADING:
706    return "reloading";
707    break;
708  case WS_ACTIVATING:
709    return "activating";
710    break;
711  case WS_DEACTIVATING:
712    return "deactivating";
713    break;
714  case WS_IDLE:
715    return "idle";
716    break;
717  case WS_INACTIVE:
718    return "inactive";
719    break;
720  default:
721    return "none";
722    break;
723  }
724}
Note: See TracBrowser for help on using the repository browser.