Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: orxonox.OLD/branches/network/src/world_entities/weapons/weapon.cc @ 6686

Last change on this file since 6686 was 6674, checked in by patrick, 19 years ago

network: NullParent now is also synced over network, some more debug output and other minor changes

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