Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 7984 was 7970, checked in by patrick, 18 years ago

cr: registering first reactions

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