Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: orxonox.OLD/branches/playability/src/world_entities/weapons/weapon.cc @ 10136

Last change on this file since 10136 was 10136, checked in by nicolasc, 17 years ago

updated animation, needs rebuild as weapon.h has been changed

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