Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 7176 was 7126, checked in by bensch, 19 years ago

orxonox/trunk: the delete Mechanism of PNode seems to work again.
This is done by always deleting the first node, and not walking through a list, that is eleminated during deletion time

File size: 19.4 KB
Line 
1
2/*
3   orxonox - the future of 3D-vertical-scrollers
4
5   Copyright (C) 2004 orx
6
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 2, or (at your option)
10   any later version.
11
12### File Specific
13   main-programmer: Patrick Boenzli
14   co-programmer: Benjamin Grauer
15
16   2005-07-15: Benjamin Grauer: restructurating the entire Class
17*/
18
19#define DEBUG_SPECIAL_MODULE DEBUG_MODULE_WEAPON
20
21#include "weapon.h"
22
23#include "fast_factory.h"
24#include "world_entities/projectiles/projectile.h"
25
26#include "resource_manager.h"
27#include "class_list.h"
28#include "load_param.h"
29#include "state.h"
30#include "animation3d.h"
31
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 char* projectile)
168{
169  if (projectile == NULL)
170    return;
171  FastFactory* tmpFac = FastFactory::searchFastFactory(projectile);
172  if (tmpFac != NULL)
173  {
174    this->setProjectileType(tmpFac->getStoredID());
175  }
176  else
177  {
178    PRINTF(1)("Projectile %s does not exist for weapon %s\n", projectile, this->getName());
179  }
180}
181
182
183/**
184 * prepares Projectiles of the Weapon
185 * @param count how many Projectiles to create (they will be stored in the ProjectileFactory)
186 */
187void Weapon::prepareProjectiles(unsigned int count)
188{
189  if (likely(this->projectileFactory != NULL))
190    projectileFactory->prepare(count);
191  else
192    PRINTF(2)("unable to create %d projectile for Weapon %s (%s)\n", count, this->getName(), this->getClassName());
193}
194
195
196/**
197 * resurects and returns a Projectile
198 * @returns a Projectile on success, NULL on error
199 *
200 * errors: 1. (ProjectileFastFactory not Found)
201 *         2. No more Projectiles availiable.
202 */
203Projectile* Weapon::getProjectile()
204{
205  if (likely (this->projectileFactory != NULL))
206  {
207    Projectile* pj = dynamic_cast<Projectile*>(this->projectileFactory->resurrect());
208    pj->toList((OM_LIST)(this->getOMListNumber()+1));
209    return pj;
210  }
211  else
212  {
213    PRINTF(2)("No projectile defined for Weapon %s(%s) can't return any\n", this->getName(), this->getClassName());
214    return NULL;
215  }
216}
217
218
219/**
220 * sets the emissionPoint's relative position from the Weapon
221 * @param point the Point relative to the mass-point of the Weapon
222 */
223void Weapon::setEmissionPoint(const Vector& point)
224{
225  this->emissionPoint.setRelCoor(point);
226}
227
228
229/**
230 * assigns a Sound-file to an action
231 * @param action the action the sound should be assigned too
232 * @param soundFile the soundFile's relative position to the data-directory (will be looked for by the ResourceManager)
233 */
234void Weapon::setActionSound(WeaponAction action, const char* soundFile)
235{
236  if (action >= WA_ACTION_COUNT)
237    return;
238  if (this->soundBuffers[action] != NULL)
239    ResourceManager::getInstance()->unload(this->soundBuffers[action]);
240
241  else if (soundFile != NULL)
242  {
243    this->soundBuffers[action] = (SoundBuffer*)ResourceManager::getInstance()->load(soundFile, WAV);
244    if (this->soundBuffers[action] != NULL)
245    {
246      PRINTF(4)("Loaded sound %s to action %s.\n", soundFile, actionToChar(action));
247    }
248    else
249    {
250      PRINTF(2)("Failed to load sound %s to %s.\n.", soundFile, actionToChar(action));
251    }
252  }
253  else
254    this->soundBuffers[action] = NULL;
255}
256
257
258/**
259 * creates/returns an Animation3D for a certain State.
260 * @param state what State should the Animation be created/returned for
261 * @param node the node this Animation should apply to. (NULL is fine if the animation was already created)
262 * @returns The created animation.Animation(), NULL on error (or if the animation does not yet exist).
263 *
264 * This function does only generate the Animation Object, and if set it will
265 * automatically be executed, when a certain State is reached.
266 * What this does not do, is set keyframes, you have to operate on the returned animation.
267 */
268Animation3D* Weapon::getAnimation(WeaponState state, PNode* node)
269{
270  if (state >= WS_STATE_COUNT) // if the state is not known
271    return NULL;
272
273  if (unlikely(this->animation[state] == NULL)) // if the animation does not exist yet create it.
274  {
275    if (likely(node != NULL))
276      return this->animation[state] = new Animation3D(node);
277    else
278    {
279      PRINTF(2)("Node not defined for the Creation of the 3D-animation of state %s\n", stateToChar(state));
280      return NULL;
281    }
282  }
283  else
284    return this->animation[state];
285}
286
287GLGuiWidget* Weapon::getEnergyWidget()
288{
289  if (this->energyWidget == NULL)
290  {
291    this->energyWidget = new GLGuiBar;
292    this->energyWidget->setSize2D( 20, 100);
293    this->energyWidget->setMaximum(this->getEnergyMax());
294    this->energyWidget->setValue(this->getEnergy());
295  }
296  return this->energyWidget;
297}
298
299void Weapon::updateWidgets()
300{
301  if (this->energyWidget != NULL)
302  {
303    this->energyWidget->setMaximum(this->energyMax);
304    this->energyWidget->setValue(this->energy);
305  }
306}
307
308/////////////////
309//  EXECUTION  //
310// GAME ACTION //
311/////////////////
312/**
313 * request an action that should be executed,
314 * @param action the next action to take
315 *
316 * This function must be called instead of the actions (like fire/reload...)
317 * to make all the checks needed to have a usefull WeaponSystem.
318 */
319void Weapon::requestAction(WeaponAction action)
320{
321  if (likely(this->isActive()))
322  {
323    if (this->requestedAction != WA_NONE)
324      return;
325    PRINTF(5)("next action will be %s in %f seconds\n", actionToChar(action), this->stateDuration);
326    this->requestedAction = action;
327  }
328  //else
329  else if (unlikely(action == WA_ACTIVATE))
330  {
331    this->currentState = WS_ACTIVATING;
332    this->requestedAction = WA_ACTIVATE;
333  }
334}
335
336
337/**
338 * adds energy to the Weapon
339 * @param energyToAdd The amount of energy
340 * @returns the amount of energy we did not pick up, because the weapon is already full
341 */
342float Weapon::increaseEnergy(float energyToAdd)
343{
344  float maxAddEnergy = this->energyMax - this->energy;
345
346  if (maxAddEnergy >= energyToAdd)
347  {
348    this->energy += energyToAdd;
349    return 0.0;
350  }
351  else
352  {
353    this->energy += maxAddEnergy;
354    return energyToAdd - maxAddEnergy;
355  }
356}
357
358
359////////////////////////////////////////////////////////////
360// WEAPON INTERNALS                                       //
361// These are functions, that no other Weapon should over- //
362// write. No class has direct Access to them, as it is    //
363// quite a complicated process, handling a Weapon from    //
364// the outside                                            //
365////////////////////////////////////////////////////////////
366/**
367 * executes an action, and with it starts a new State.
368 * @return true, if it worked, false otherwise
369 *
370 * This function checks, wheter the possibility of executing an action is valid,
371 * and does all the necessary stuff, to execute them. If an action does not succeed,
372 * it tries to go around it. (ex. shoot->noAmo->reload()->wait until shoot comes again)
373 */
374bool Weapon::execute()
375{
376#if DEBUG > 4
377  PRINTF(4)("trying to execute action %s\n", actionToChar(this->requestedAction));
378  this->debug();
379#endif
380
381  WeaponAction action = this->requestedAction;
382  this->requestedAction = WA_NONE;
383
384  switch (action)
385  {
386  case WA_SHOOT:
387    return this->fireW();
388    break;
389  case WA_CHARGE:
390    return this->chargeW();
391    break;
392  case WA_RELOAD:
393    return this->reloadW();
394    break;
395  case WA_DEACTIVATE:
396    return this->deactivateW();
397    break;
398  case WA_ACTIVATE:
399    return this->activateW();
400    break;
401  }
402}
403
404/**
405 * checks and activates the Weapon.
406 * @return true on success.
407 */
408bool Weapon::activateW()
409{
410  //  if (this->currentState == WS_INACTIVE)
411  {
412    // play Sound
413    if (likely(this->soundBuffers[WA_ACTIVATE] != NULL))
414      this->soundSource->play(this->soundBuffers[WA_ACTIVATE]);
415    this->updateWidgets();
416    // activate
417    PRINTF(4)("Activating the Weapon %s\n", this->getName());
418    this->activate();
419    // setting up for next action
420    this->enterState(WS_ACTIVATING);
421  }
422}
423
424/**
425 * checks and deactivates the Weapon
426 * @return true on success.
427 */
428bool Weapon::deactivateW()
429{
430  //  if (this->currentState != WS_INACTIVE)
431  {
432    PRINTF(4)("Deactivating the Weapon %s\n", this->getName());
433    // play Sound
434    if (this->soundBuffers[WA_DEACTIVATE] != NULL)
435      this->soundSource->play(this->soundBuffers[WA_DEACTIVATE]);
436    // deactivate
437    this->deactivate();
438    this->enterState(WS_DEACTIVATING);
439  }
440}
441
442/**
443 * checks and charges the Weapon
444 * @return true on success.
445 */
446bool Weapon::chargeW()
447{
448  if ( this->currentState != WS_INACTIVE && this->energy >= this->minCharge)
449  {
450    // playing Sound
451    if (this->soundBuffers[WA_CHARGE] != NULL)
452      this->soundSource->play(this->soundBuffers[WA_CHARGE]);
453
454    // charge
455    this->charge();
456    // setting up for the next state
457    this->enterState(WS_CHARGING);
458  }
459  else // deactivate the Weapon if we do not have enough energy
460  {
461    this->requestAction(WA_RELOAD);
462  }
463}
464
465/**
466 * checks and fires the Weapon
467 * @return true on success.
468 */
469bool Weapon::fireW()
470{
471  //if (likely(this->currentState != WS_INACTIVE))
472  if (this->minCharge <= this->energy)
473  {
474    // playing Sound
475    if (this->soundBuffers[WA_SHOOT] != NULL)
476      this->soundSource->play(this->soundBuffers[WA_SHOOT]);
477    this->updateWidgets();
478    // fire
479    this->energy -= this->minCharge;
480    this->fire();
481    // setting up for the next state
482    this->enterState(WS_SHOOTING);
483  }
484  else  // reload if we still have the charge
485  {
486    this->requestAction(WA_RELOAD);
487    this->execute();
488  }
489}
490
491/**
492 * checks and Reloads the Weapon
493 * @return true on success.
494 */
495bool Weapon::reloadW()
496{
497  PRINTF(4)("Reloading Weapon %s\n", this->getName());
498  if (this->ammoContainer.get() != NULL &&
499        unlikely(this->energy + this->ammoContainer->getStoredEnergy() < this->minCharge))
500  {
501    this->requestAction(WA_DEACTIVATE);
502    this->execute();
503    return false;
504  }
505
506
507  if (this->soundBuffers[WA_RELOAD] != NULL)
508    this->soundSource->play(this->soundBuffers[WA_RELOAD]);
509
510  if (this->ammoContainer.get() != NULL)
511    this->ammoContainer->fillWeapon(this);
512  else
513  {
514    this->energy = this->energyMax;
515  }
516  this->updateWidgets();
517  this->reload();
518  this->enterState(WS_RELOADING);
519}
520
521/**
522 * enters the requested State, plays back animations updates the timing.
523 * @param state the state to enter.
524 */
525inline void Weapon::enterState(WeaponState state)
526{
527  PRINTF(4)("ENTERING STATE %s\n", stateToChar(state));
528  // playing animation if availiable
529  if (likely(this->animation[state] != NULL))
530    this->animation[state]->replay();
531
532  this->stateDuration += this->times[state];
533  this->currentState = state;
534}
535
536///////////////////
537//  WORLD-ENTITY //
538// FUNCTIONALITY //
539///////////////////
540/**
541 * tick signal for time dependent/driven stuff
542*/
543bool Weapon::tickW(float dt)
544{
545  //printf("%s ", stateToChar(this->currentState));
546
547  // setting up the timing properties
548  this->stateDuration -= dt;
549
550  if (this->stateDuration <= 0.0)
551  {
552    if (unlikely (this->currentState == WS_DEACTIVATING))
553    {
554      this->currentState = WS_INACTIVE;
555      return false;
556    }
557    else
558      this->currentState = WS_IDLE;
559
560    if (this->requestedAction != WA_NONE)
561    {
562      this->stateDuration = -dt;
563      this->execute();
564    }
565  }
566  return true;
567}
568
569
570
571
572//////////////////////
573// HELPER FUNCTIONS //
574//////////////////////
575/**
576 * checks wether all the Weapons functions are valid, and if it is possible to go to action with it.
577 * @todo IMPLEMENT the Weapons Check
578 */
579bool Weapon::check() const
580{
581  bool retVal = true;
582
583  //  if (this->projectile == NULL)
584  {
585    PRINTF(1)("There was no projectile assigned to the Weapon.\n");
586    retVal = false;
587  }
588
589
590
591
592  return retVal;
593}
594
595/**
596 * some nice debugging information about this Weapon
597 */
598void Weapon::debug() const
599{
600  PRINT(0)("Weapon-Debug %s, state: %s (duration: %fs), nextAction: %s\n", this->getName(), Weapon::stateToChar(this->currentState), this->stateDuration, Weapon::actionToChar(requestedAction));
601  PRINT(0)("Energy: max: %f; current: %f; chargeMin: %f, chargeMax %f\n",
602           this->energyMax, this->energy, this->minCharge, this->maxCharge);
603
604
605}
606
607////////////////////////////////////////////////////////
608// static Definitions (transormators for readability) //
609////////////////////////////////////////////////////////
610/**
611 * Converts a String into an Action.
612 * @param action the String input holding the Action.
613 * @return The Action if known, WA_NONE otherwise.
614 */
615WeaponAction Weapon::charToAction(const char* action)
616{
617  if (!strcmp(action, "none"))
618    return WA_NONE;
619  else if (!strcmp(action, "shoot"))
620    return WA_SHOOT;
621  else if (!strcmp(action, "charge"))
622    return WA_CHARGE;
623  else if (!strcmp(action, "reload"))
624    return WA_RELOAD;
625  else if (!strcmp(action, "acitvate"))
626    return WA_ACTIVATE;
627  else if (!strcmp(action, "deactivate"))
628    return WA_DEACTIVATE;
629  else if (!strcmp(action, "special1"))
630    return WA_SPECIAL1;
631  else
632  {
633    PRINTF(2)("action %s could not be identified.\n", action);
634    return WA_NONE;
635  }
636}
637
638/**
639 * converts an action into a String
640 * @param action the action to convert
641 * @return a String matching the name of the action
642 */
643const char* Weapon::actionToChar(WeaponAction action)
644{
645  switch (action)
646  {
647  case WA_SHOOT:
648    return "shoot";
649    break;
650  case WA_CHARGE:
651    return "charge";
652    break;
653  case WA_RELOAD:
654    return "reload";
655    break;
656  case WA_ACTIVATE:
657    return "activate";
658    break;
659  case WA_DEACTIVATE:
660    return "deactivate";
661    break;
662  case WA_SPECIAL1:
663    return "special1";
664    break;
665  default:
666    return "none";
667    break;
668  }
669}
670
671/**
672 * Converts a String into a State.
673 * @param state the String input holding the State.
674 * @return The State if known, WS_NONE otherwise.
675 */
676WeaponState Weapon::charToState(const char* state)
677{
678  if (!strcmp(state, "none"))
679    return WS_NONE;
680  else if (!strcmp(state, "shooting"))
681    return WS_SHOOTING;
682  else if (!strcmp(state, "charging"))
683    return WS_CHARGING;
684  else if (!strcmp(state, "reloading"))
685    return WS_RELOADING;
686  else if (!strcmp(state, "activating"))
687    return WS_ACTIVATING;
688  else if (!strcmp(state, "deactivating"))
689    return WS_DEACTIVATING;
690  else if (!strcmp(state, "inactive"))
691    return WS_INACTIVE;
692  else if (!strcmp(state, "idle"))
693    return WS_IDLE;
694  else
695  {
696    PRINTF(2)("state %s could not be identified.\n", state);
697    return WS_NONE;
698  }
699}
700
701/**
702 * converts a State into a String
703 * @param state the state to convert
704 * @return a String matching the name of the state
705 */
706const char* Weapon::stateToChar(WeaponState state)
707{
708  switch (state)
709  {
710  case WS_SHOOTING:
711    return "shooting";
712    break;
713  case WS_CHARGING:
714    return "charging";
715    break;
716  case WS_RELOADING:
717    return "reloading";
718    break;
719  case WS_ACTIVATING:
720    return "activating";
721    break;
722  case WS_DEACTIVATING:
723    return "deactivating";
724    break;
725  case WS_IDLE:
726    return "idle";
727    break;
728  case WS_INACTIVE:
729    return "inactive";
730    break;
731  default:
732    return "none";
733    break;
734  }
735}
Note: See TracBrowser for help on using the repository browser.