Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: orxonox.OLD/branches/new_class_id/src/world_entities/weapons/weapon.cc @ 9757

Last change on this file since 9757 was 9757, checked in by bensch, 18 years ago

new_class_id: hups… this was bad naming… confusing too.

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