Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 6121 was 6054, checked in by bensch, 19 years ago

orxonox/trunk: multiple new Reparenting modes in PNode.
Testing the stuff in GuidedMissile
Projectile has a PNode as reference not as pointer
some minor weapon changes

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