Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: orxonox.OLD/branches/shared_lib/src/world_entities/weapons/weapon.cc @ 8453

Last change on this file since 8453 was 7221, checked in by bensch, 19 years ago

orxonox/trunk: merged the std-branche back, it runs on windows and Linux

svn merge https://svn.orxonox.net/orxonox/branches/std . -r7202:HEAD

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