Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

orxonox/trunk: Objects now get cleanly ereased.
This is a fix in the Weapon-class, that kills the Resurected Projectiles created for information-gathering

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