Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

trunk: fixed most -Wall warnings… but there are still many missing :/

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