Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: orxonox.OLD/branches/spaceshipcontrol/src/world_entities/weapons/weapon.cc @ 5964

Last change on this file since 5964 was 5750, checked in by bensch, 19 years ago

orxonox/trunk: merged the WorldEntities into the Trunk.
Merged with command:
svn merge branches/world_entities/ trunk/ -r5516:HEAD

conflics from world_entities changed in favor of branches/world_entity
all other conflict in favor of the trunk

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