Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: orxonox.OLD/branches/cr/src/world_entities/world_entity.cc @ 8181

Last change on this file since 8181 was 8169, checked in by patrick, 19 years ago

cr: using a new damage structure for dealing damage. working

File size: 19.7 KB
Line 
1
2
3/*
4   orxonox - the future of 3D-vertical-scrollers
5
6   Copyright (C) 2004 orx
7
8   This program is free software; you can redistribute it and/or modify
9   it under the terms of the GNU General Public License as published by
10   the Free Software Foundation; either version 2, or (at your option)
11   any later version.
12
13   ### File Specific:
14   main-programmer: Patrick Boenzli
15   co-programmer: Christian Meyer
16*/
17#define DEBUG_SPECIAL_MODULE DEBUG_MODULE_WORLD_ENTITY
18
19#include "world_entity.h"
20#include "shell_command.h"
21
22#include "model.h"
23#include "md2Model.h"
24#include "util/loading/resource_manager.h"
25#include "util/loading/load_param.h"
26#include "vector.h"
27#include "obb_tree.h"
28
29#include "glgui_bar.h"
30
31#include "state.h"
32#include "camera.h"
33
34#include "collision_handle.h"
35#include "collision_event.h"
36
37#include <stdarg.h>
38
39
40using namespace std;
41
42SHELL_COMMAND(model, WorldEntity, loadModel)
43->describe("sets the Model of the WorldEntity")
44->defaultValues("models/ships/fighter.obj", 1.0f);
45
46SHELL_COMMAND(debugEntity, WorldEntity, debugWE);
47
48/**
49 *  Loads the WordEntity-specific Part of any derived Class
50 *
51 * @param root: Normally NULL, as the Derived Entities define a loadParams Function themeselves,
52 *              that can calls WorldEntities loadParams for itself.
53 */
54WorldEntity::WorldEntity()
55    : Synchronizeable()
56{
57  this->setClassID(CL_WORLD_ENTITY, "WorldEntity");
58
59  this->obbTree = NULL;
60  this->healthWidget = NULL;
61  this->healthMax = 1.0f;
62  this->health = 1.0f;
63  this->damage = 0.0f; // no damage dealt by a default entity
64  this->scaling = 1.0f;
65
66  /* OSOLETE */
67  this->bVisible = true;
68  this->bCollide = true;
69
70  this->objectListNumber = OM_INIT;
71  this->objectListIterator = NULL;
72
73  // reset all collision handles to NULL == unsubscribed state
74  for(int i = 0; i < CREngine::CR_NUMBER; ++i)
75    this->collisionHandles[i] = NULL;
76  this->bReactive = false;
77
78  // registering default reactions:
79  this->subscribeReaction(CREngine::CR_OBJECT_DAMAGE, CL_WORLD_ENTITY);
80  this->subscribeReaction(CREngine::CR_PHYSICS_GROUND, CL_TERRAIN);
81
82  this->toList(OM_NULL);
83}
84
85/**
86 *  standard destructor
87*/
88WorldEntity::~WorldEntity ()
89{
90  State::getObjectManager()->toList(this, OM_INIT);
91
92  // Delete the model (unregister it with the ResourceManager)
93  for (unsigned int i = 0; i < this->models.size(); i++)
94    this->setModel(NULL, i);
95
96  // Delete the obbTree
97  if( this->obbTree != NULL)
98    delete this->obbTree;
99
100  if (this->healthWidget != NULL)
101    delete this->healthWidget;
102
103  this->unsubscribeReaction();
104}
105
106/**
107 * loads the WorldEntity Specific Parameters.
108 * @param root: the XML-Element to load the Data From
109 */
110void WorldEntity::loadParams(const TiXmlElement* root)
111{
112  // Do the PNode loading stuff
113  PNode::loadParams(root);
114
115  LoadParam(root, "md2texture", this, WorldEntity, loadMD2Texture)
116  .describe("the fileName of the texture, that should be loaded onto this world-entity. (must be relative to the data-dir)")
117  .defaultValues("");
118
119  // Model Loading
120  LoadParam(root, "model", this, WorldEntity, loadModel)
121  .describe("the fileName of the model, that should be loaded onto this world-entity. (must be relative to the data-dir)")
122  .defaultValues("", 1.0f, 0);
123
124  LoadParam(root, "maxHealth", this, WorldEntity, setHealthMax)
125  .describe("The Maximum health that can be loaded onto this entity")
126  .defaultValues(1.0f);
127
128  LoadParam(root, "health", this, WorldEntity, setHealth)
129  .describe("The Health the WorldEntity has at this moment")
130  .defaultValues(1.0f);
131}
132
133
134/**
135 * loads a Model onto a WorldEntity
136 * @param fileName the name of the model to load
137 * @param scaling the Scaling of the model
138 *
139 * FIXME
140 * @todo: separate the obb tree generation from the model
141 */
142void WorldEntity::loadModel(const std::string& fileName, float scaling, unsigned int modelNumber, unsigned int obbTreeDepth)
143{
144  this->modelLODName = fileName;
145  this->scaling = scaling;
146  if (!fileName.empty())
147  {
148    // search for the special character # in the LoadParam
149    if (fileName.find('#') != std::string::npos)
150    {
151      PRINTF(4)("Found # in %s... searching for LOD's\n", fileName.c_str());
152      std::string lodFile = fileName;
153      unsigned int offset = lodFile.find('#');
154      for (unsigned int i = 0; i < 3; i++)
155      {
156        lodFile[offset] = 48+(int)i;
157        if (ResourceManager::isInDataDir(lodFile))
158          this->loadModel(lodFile, scaling, i);
159      }
160      return;
161    }
162    if (this->scaling <= 0.0)
163    {
164      PRINTF(1)("YOU GAVE ME A CRAPY SCALE resetting to 1.0\n");
165      this->scaling = 1.0;
166    }
167    if(fileName.find(".obj") != std::string::npos)
168    {
169      PRINTF(4)("fetching OBJ file: %s\n", fileName.c_str());
170      BaseObject* loadedModel = ResourceManager::getInstance()->load(fileName, OBJ, RP_CAMPAIGN, this->scaling);
171      if (loadedModel != NULL)
172        this->setModel(dynamic_cast<Model*>(loadedModel), modelNumber);
173      else
174        PRINTF(1)("OBJ-File %s not found.\n", fileName.c_str());
175
176      if( modelNumber == 0)
177        this->buildObbTree(obbTreeDepth);
178    }
179    else if(fileName.find(".md2") != std::string::npos)
180    {
181      PRINTF(4)("fetching MD2 file: %s\n", fileName.c_str());
182      Model* m = new MD2Model(fileName, this->md2TextureFileName, this->scaling);
183      //this->setModel((Model*)ResourceManager::getInstance()->load(fileName, MD2, RP_CAMPAIGN), 0);
184      this->setModel(m, 0);
185
186      if( m != NULL)
187        this->buildObbTree(obbTreeDepth);
188    }
189  }
190  else
191  {
192    this->setModel(NULL);
193  }
194}
195
196/**
197 * sets a specific Model for the Object.
198 * @param model The Model to set
199 * @param modelNumber the n'th model in the List to get.
200 */
201void WorldEntity::setModel(Model* model, unsigned int modelNumber)
202{
203  if (this->models.size() <= modelNumber)
204    this->models.resize(modelNumber+1, NULL);
205
206  if (this->models[modelNumber] != NULL)
207  {
208    Resource* resource = ResourceManager::getInstance()->locateResourceByPointer(dynamic_cast<BaseObject*>(this->models[modelNumber]));
209    if (resource != NULL)
210      ResourceManager::getInstance()->unload(resource, RP_LEVEL);
211    else
212    {
213      PRINTF(4)("Forcing model deletion\n");
214      delete this->models[modelNumber];
215    }
216  }
217
218  this->models[modelNumber] = model;
219
220
221  //   if (this->model != NULL)
222  //     this->buildObbTree(4);
223}
224
225
226/**
227 * builds the obb-tree
228 * @param depth the depth to calculate
229 */
230bool WorldEntity::buildObbTree(int depth)
231{
232  if (this->obbTree)
233    delete this->obbTree;
234
235  if (this->models[0] != NULL)
236  {
237    this->obbTree = new OBBTree(depth, models[0]->getModelInfo(), this);
238    return true;
239  }
240  else
241  {
242    PRINTF(1)("could not create obb-tree, because no model was loaded yet\n");
243    this->obbTree = NULL;
244    return false;
245  }
246}
247
248
249/**
250 * subscribes this world entity to a collision reaction
251 *  @param type the type of reaction to subscribe to
252 *  @param target1 a filter target (classID)
253 */
254void WorldEntity::subscribeReaction(CREngine::CRType type, long target1)
255{
256  this->subscribeReaction(type);
257
258  // add the target filter
259  this->collisionHandles[type]->addTarget(target1);
260}
261
262
263/**
264 * subscribes this world entity to a collision reaction
265 *  @param type the type of reaction to subscribe to
266 *  @param target1 a filter target (classID)
267 */
268void WorldEntity::subscribeReaction(CREngine::CRType type, long target1, long target2)
269{
270  this->subscribeReaction(type);
271
272  // add the target filter
273  this->collisionHandles[type]->addTarget(target1);
274  this->collisionHandles[type]->addTarget(target2);
275}
276
277
278/**
279 * subscribes this world entity to a collision reaction
280 *  @param type the type of reaction to subscribe to
281 *  @param target1 a filter target (classID)
282 */
283void WorldEntity::subscribeReaction(CREngine::CRType type, long target1, long target2, long target3)
284{
285  this->subscribeReaction(type);
286
287  // add the target filter
288  this->collisionHandles[type]->addTarget(target1);
289  this->collisionHandles[type]->addTarget(target2);
290  this->collisionHandles[type]->addTarget(target3);
291}
292
293
294/**
295 * subscribes this world entity to a collision reaction
296 *  @param type the type of reaction to subscribe to
297 *  @param target1 a filter target (classID)
298 */
299void WorldEntity::subscribeReaction(CREngine::CRType type, long target1, long target2, long target3, long target4)
300{
301  this->subscribeReaction(type);
302
303  // add the target filter
304  this->collisionHandles[type]->addTarget(target1);
305  this->collisionHandles[type]->addTarget(target2);
306  this->collisionHandles[type]->addTarget(target3);
307  this->collisionHandles[type]->addTarget(target4);
308}
309
310
311/**
312 * subscribes this world entity to a collision reaction
313 *  @param type the type of reaction to subscribe to
314 *  @param nrOfTargets number of target filters
315 *  @param ... the targets as classIDs
316 */
317void WorldEntity::subscribeReaction(CREngine::CRType type)
318{
319  if( this->collisionHandles[type] != NULL)  {
320    PRINTF(2)("Registering for a CollisionReaction already subscribed to! Skipping\n");
321    return;
322  }
323
324  this->collisionHandles[type] = CREngine::getInstance()->subscribeReaction(this, type);
325
326  // now there is at least one collision reaction subscribed
327  this->bReactive = true;
328}
329
330
331/**
332 * unsubscribes a specific reaction from the worldentity
333 *  @param type the reaction to unsubscribe
334 */
335void WorldEntity::unsubscribeReaction(CREngine::CRType type)
336{
337  if( this->collisionHandles[type] == NULL)
338    return;
339
340  CREngine::getInstance()->unsubscribeReaction(this->collisionHandles[type]);
341  this->collisionHandles[type] = NULL;
342
343  // check if there is still any handler registered
344  for(int i = 0; i < CREngine::CR_NUMBER; ++i)
345  {
346    if( this->collisionHandles[i] != NULL)
347    {
348      this->bReactive = true;
349      return;
350    }
351  }
352  this->bReactive = false;
353}
354
355
356/**
357 * unsubscribes all collision reactions
358 */
359void WorldEntity::unsubscribeReaction()
360{
361  for( int i = 0; i < CREngine::CR_NUMBER; i++)
362    this->unsubscribeReaction((CREngine::CRType)i);
363
364  // there are no reactions subscribed from now on
365  this->bReactive = false;
366}
367
368
369/**
370 * registers a new collision to this world entity
371 *  @param collisionEvent the event
372 */
373bool WorldEntity::registerCollision(WorldEntity* entityA, WorldEntity* entityB, BoundingVolume* bvA, BoundingVolume* bvB)
374{
375  // is there any handler listening?
376  if( !this->bReactive)
377    return false;
378
379  // create a collision event
380  CollisionEvent* c = CREngine::getInstance()->popCollisionEventObject();
381  assert(c != NULL); // if this should fail: we got not enough precached CollisionEvents: alter value in cr_defs.h
382  c->collide(entityA, entityB, bvA, bvB);
383
384  for( int i = 0; i < CREngine::CR_NUMBER; ++i)
385    if( this->collisionHandles[i] != NULL)
386      this->collisionHandles[i]->registerCollisionEvent(c);
387}
388
389
390/**
391 * @brief moves this entity to the List OM_List
392 * @param list the list to set this Entity to.
393 *
394 * this is the same as a call to State::getObjectManager()->toList(entity , list);
395 * directly, but with an easier interface.
396 *
397 * @todo inline this (peut etre)
398 */
399void WorldEntity::toList(OM_LIST list)
400{
401  State::getObjectManager()->toList(this, list);
402}
403
404
405
406/**
407 * sets the character attributes of a worldentity
408 * @param character attributes
409 *
410 * these attributes don't have to be set, only use them, if you need them
411*/
412//void WorldEntity::setCharacterAttributes(CharacterAttributes* charAttr)
413//{}
414
415
416/**
417 *  this function is called, when two entities collide
418 * @param entity: the world entity with whom it collides
419 *
420 * Implement behaviour like damage application or other miscellaneous collision stuff in this function
421 */
422void WorldEntity::collidesWith(WorldEntity* entity, const Vector& location)
423{
424  /**
425   * THIS IS A DEFAULT COLLISION-Effect.
426   * IF YOU WANT TO CREATE A SPECIFIC COLLISION ON EACH OBJECT
427   * USE::
428   * if (entity->isA(CL_WHAT_YOU_ARE_LOOKING_FOR)) { printf "dothings"; };
429   *
430   * You can always define a default Action.... don't be affraid just test it :)
431   */
432  //  PRINTF(3)("collision %s vs %s @ (%f,%f,%f)\n", this->getClassName(), entity->getClassName(), location.x, location.y, location.z);
433}
434
435
436/**
437 *  this is called immediately after the Entity has been constructed, initialized and then Spawned into the World
438 *
439 */
440void WorldEntity::postSpawn ()
441{}
442
443
444/**
445 *  this method is called by the world if the WorldEntity leaves the game
446 */
447void WorldEntity::leaveWorld ()
448{}
449
450
451/**
452 * resets the WorldEntity to its initial values. eg. used for multiplayer games: respawning
453 */
454void WorldEntity::reset()
455{}
456
457/**
458 *  this method is called every frame
459 * @param time: the time in seconds that has passed since the last tick
460 *
461 * Handle all stuff that should update with time inside this method (movement, animation, etc.)
462*/
463void WorldEntity::tick(float time)
464{}
465
466
467/**
468 *  the entity is drawn onto the screen with this function
469 *
470 * This is a central function of an entity: call it to let the entity painted to the screen.
471 * Just override this function with whatever you want to be drawn.
472*/
473void WorldEntity::draw() const
474{
475  //PRINTF(0)("(%s::%s)\n", this->getClassName(), this->getName());
476  //  assert(!unlikely(this->models.empty()));
477  {
478    glMatrixMode(GL_MODELVIEW);
479    glPushMatrix();
480
481    /* translate */
482    glTranslatef (this->getAbsCoor ().x,
483                  this->getAbsCoor ().y,
484                  this->getAbsCoor ().z);
485    Vector tmpRot = this->getAbsDir().getSpacialAxis();
486    glRotatef (this->getAbsDir().getSpacialAxisAngle(), tmpRot.x, tmpRot.y, tmpRot.z );
487
488
489    // This Draws the LOD's
490    float cameraDistance = State::getCamera()->distance(this);
491    if (cameraDistance > 30 && this->models.size() >= 3 && this->models[2] != NULL)
492    {
493      this->models[2]->draw();
494    }
495    else if (cameraDistance > 10 && this->models.size() >= 2 && this->models[1] != NULL)
496    {
497      this->models[1]->draw();
498    }
499    else if (this->models.size() >= 1 && this->models[0] != NULL)
500    {
501      this->models[0]->draw();
502    }
503    glPopMatrix();
504  }
505}
506
507/**
508 * @param health the Health to add.
509 * @returns the health left (this->healthMax - health+this->health)
510 */
511float WorldEntity::increaseHealth(float health)
512{
513  this->health += health;
514  if (this->health > this->healthMax)
515  {
516    float retHealth = this->healthMax - this->health;
517    this->health = this->healthMax;
518    this->updateHealthWidget();
519    return retHealth;
520  }
521  this->updateHealthWidget();
522  return 0.0;
523}
524
525/**
526 * @param health the Health to be removed
527 * @returns 0.0 or the rest, that was not substracted (bellow 0.0)
528 */
529float WorldEntity::decreaseHealth(float health)
530{
531  this->health -= health;
532
533  if (this->health < 0)
534  {
535    float retHealth = -this->health;
536    this->health = 0.0f;
537    this->updateHealthWidget();
538    return retHealth;
539  }
540  this->updateHealthWidget();
541  return 0.0;
542
543}
544
545/**
546 * @param maxHealth the maximal health that can be loaded onto the entity.
547 */
548void WorldEntity::setHealthMax(float healthMax)
549{
550  this->healthMax = healthMax;
551  if (this->health > this->healthMax)
552  {
553    PRINTF(3)("new maxHealth is bigger as the old health. Did you really intend to do this for (%s::%s)\n", this->getClassName(), this->getName());
554    this->health = this->healthMax;
555  }
556  this->updateHealthWidget();
557}
558
559/**
560 * @brief creates the HealthWidget
561 *
562 * since not all entities need an HealthWidget, it is only created on request.
563 */
564void WorldEntity::createHealthWidget()
565{
566  if (this->healthWidget == NULL)
567  {
568    this->healthWidget = new OrxGui::GLGuiBar();
569    this->healthWidget->setSize2D(30,400);
570    this->healthWidget->setAbsCoor2D(10,100);
571
572    this->updateHealthWidget();
573  }
574  else
575    PRINTF(3)("Allready created the HealthWidget for %s::%s\n", this->getClassName(), this->getName());
576}
577
578void WorldEntity::increaseHealthMax(float increaseHealth)
579{
580  this->healthMax += increaseHealth;
581  this->updateHealthWidget();
582}
583
584
585OrxGui::GLGuiWidget* WorldEntity::getHealthWidget()
586{
587  this->createHealthWidget();
588  return this->healthWidget;
589}
590
591/**
592 * @param visibility shows or hides the health-bar
593 * (creates the widget if needed)
594 */
595void WorldEntity::setHealthWidgetVisibilit(bool visibility)
596{
597  if (visibility)
598  {
599    if (this->healthWidget != NULL)
600      this->healthWidget->show();
601    else
602    {
603      this->createHealthWidget();
604      this->updateHealthWidget();
605      this->healthWidget->show();
606    }
607  }
608  else if (this->healthWidget != NULL)
609    this->healthWidget->hide();
610}
611
612/**
613 * @brief updates the HealthWidget
614 */
615void WorldEntity::updateHealthWidget()
616{
617  if (this->healthWidget != NULL)
618  {
619    this->healthWidget->setMaximum(this->healthMax);
620    this->healthWidget->setValue(this->health);
621  }
622}
623
624
625/**
626 * DEBUG-DRAW OF THE BV-Tree.
627 * @param depth What depth to draw
628 * @param drawMode the mode to draw this entity under
629 */
630void WorldEntity::drawBVTree(int depth, int drawMode) const
631{
632  glMatrixMode(GL_MODELVIEW);
633  glPushMatrix();
634  /* translate */
635  glTranslatef (this->getAbsCoor ().x,
636                this->getAbsCoor ().y,
637                this->getAbsCoor ().z);
638  /* rotate */
639  Vector tmpRot = this->getAbsDir().getSpacialAxis();
640  glRotatef (this->getAbsDir().getSpacialAxisAngle(), tmpRot.x, tmpRot.y, tmpRot.z );
641
642
643  if (this->obbTree)
644    this->obbTree->drawBV(depth, drawMode);
645
646
647  glPopMatrix();
648}
649
650
651/**
652 * Debug the WorldEntity
653 */
654void WorldEntity::debugEntity() const
655{
656  PRINT(0)("WorldEntity %s::%s  (DEBUG)\n", this->getClassName(), this->getName());
657  this->debugNode();
658  PRINT(0)("List: %s ; ModelCount %d - ", ObjectManager::OMListToString(this->objectListNumber) , this->models.size());
659  for (unsigned int i = 0; i < this->models.size(); i++)
660  {
661    if (models[i] != NULL)
662      PRINT(0)(" : %d:%s", i, this->models[i]->getName());
663  }
664  PRINT(0)("\n");
665
666}
667
668
669
670
671/********************************************************************************************
672 NETWORK STUFF
673 ********************************************************************************************/
674
675
676/**
677 * Writes data from network containing information about the state
678 * @param data pointer to data
679 * @param length length of data
680 * @param sender hostID of sender
681 */
682int WorldEntity::writeState( const byte * data, int length, int sender )
683{
684  std::string modelFileName;
685  SYNCHELP_READ_BEGIN();
686
687  SYNCHELP_READ_FKT( PNode::writeState, NWT_WE_PN_WRITESTATE );
688
689  SYNCHELP_READ_STRING( modelFileName, NWT_WE_PN_MODELFILENAME );
690  SYNCHELP_READ_FLOAT( scaling, NWT_WE_PN_SCALING );
691  //check if modelFileName is relative to datadir or absolute
692
693
694  PRINTF(0)("================ LOADING MODEL %s, %f\n", modelFileName.c_str(), scaling);
695
696  if ( modelFileName != "" )
697  {
698    loadModel( modelFileName, scaling);
699    PRINTF(0)("modelfilename: %s\n", getModel( 0 )->getName());
700  }
701
702  /*SYNCHELP_READ_STRINGM( modelFileName );
703
704  if ( strcmp(modelFileName, "") )
705    if ( strstr(modelFileName, ResourceManager::getInstance()->getDataDir()) )
706    {
707      this->md2TextureFileName = new char[strlen(modelFileName)-strlen(ResourceManager::getInstance()->getDataDir())+1];
708      strcpy((char*)this->md2TextureFileName, modelFileName+strlen(ResourceManager::getInstance()->getDataDir()));
709    }
710    else
711    {
712      this->md2TextureFileName = modelFileName;
713    }
714  */
715
716  return SYNCHELP_READ_N;
717}
718
719
720/**
721 * data copied in data will bee sent to another host
722 * @param data pointer to data
723 * @param maxLength max length of data
724 * @return the number of bytes writen
725 */
726int WorldEntity::readState( byte * data, int maxLength )
727{
728  SYNCHELP_WRITE_BEGIN();
729
730  SYNCHELP_WRITE_FKT( PNode::readState, NWT_WE_PN_WRITESTATE );
731
732  if ( getModel(0) && getModel(0)->getName() != "" )
733  {
734    std::string name = getModel( 0 )->getName();
735
736    if (  name.find( ResourceManager::getInstance()->getDataDir() ) == 0 )
737    {
738      name.erase(ResourceManager::getInstance()->getDataDir().size());
739    }
740
741    SYNCHELP_WRITE_STRING( name, NWT_WE_PN_MODELFILENAME );
742  }
743  else
744  {
745    SYNCHELP_WRITE_STRING("", NWT_WE_PN_MODELFILENAME);
746  }
747
748  SYNCHELP_WRITE_FLOAT( scaling, NWT_WE_PN_SCALING );
749  /*if ( this->md2TextureFileName!=NULL && strcmp(this->md2TextureFileName, "") )
750  {
751    SYNCHELP_WRITE_STRING(this->md2TextureFileName);
752  }
753  else
754  {
755    SYNCHELP_WRITE_STRING("");
756  }*/
757
758  return SYNCHELP_WRITE_N;
759}
Note: See TracBrowser for help on using the repository browser.