Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

orxonox/trunk: Weapons are ticked only if they are inside of a ticked OM_LIST (not by the WeaponManager anymore)

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