Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: orxonox.OLD/trunk/src/story_entities/world.cc @ 6150

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

orxonox/trunk: cleanup of the world begin

File size: 18.2 KB
Line 
1/*
2   orxonox - the future of 3D-vertical-scrollers
3
4   Copyright (C) 2004 orx
5
6   This program is free software; you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation; either version 2, or (at your option)
9   any later version.
10
11   ### File Specific:
12   main-programmer: Patrick Boenzli
13   co-programmer: Christian Meyer
14   co-programmer: Benjamin Grauer
15*/
16
17#define DEBUG_SPECIAL_MODULE DEBUG_MODULE_WORLD
18
19#include "world.h"
20
21#include "shell_command.h"
22#include "resource_manager.h"
23#include "state.h"
24
25#include "p_node.h"
26#include "world_entity.h"
27#include "player.h"
28#include "camera.h"
29#include "environment.h"
30
31#include "test_entity.h"
32#include "terrain.h"
33#include "light.h"
34#include "load_param.h"
35#include "shell.h"
36
37#include "fast_factory.h"
38#include "animation_player.h"
39#include "particle_engine.h"
40#include "graphics_engine.h"
41#include "physics_engine.h"
42#include "fields.h"
43
44#include "md2Model.h"
45
46#include "glmenu_imagescreen.h"
47#include "list.h"
48#include "game_loader.h"
49
50#include "animation3d.h"
51
52#include "substring.h"
53
54#include "factory.h"
55
56#include "weapons/projectile.h"
57#include "event_handler.h"
58#include "sound_engine.h"
59#include "ogg_player.h"
60
61#include "class_list.h"
62
63#include "cd_engine.h"
64#include "npcs/npc_test1.h"
65#include "shader.h"
66
67#include "playable.h"
68#include "network_manager.h"
69#include "playable.h"
70
71
72SHELL_COMMAND(speed, World, setSpeed);
73SHELL_COMMAND(togglePNodeVisibility, World, togglePNodeVisibility);
74SHELL_COMMAND(toggleBVVisibility, World, toggleBVVisibility);
75
76using namespace std;
77
78//! This creates a Factory to fabricate a World
79CREATE_FACTORY(World, CL_WORLD);
80
81World::World(const TiXmlElement* root)
82{
83  this->constuctorInit("", -1);
84  this->path = NULL;
85
86  this->loadParams(root);
87}
88
89/**
90 *  remove the World from memory
91 *
92 *  delete everything explicitly, that isn't contained in the parenting tree!
93 *  things contained in the tree are deleted automaticaly
94 */
95World::~World ()
96{
97  delete this->shell;
98  PRINTF(3)("World::~World() - deleting current world\n");
99
100  delete this->localPlayer;
101
102  // delete all the initialized Engines.
103  FastFactory::flushAll(true);
104  delete LightManager::getInstance();
105  delete ParticleEngine::getInstance();
106  delete AnimationPlayer::getInstance();
107  delete PhysicsEngine::getInstance();
108
109  // external engines initialized by the orxonox-class get deleted
110  SoundEngine::getInstance()->flushAllBuffers();
111  SoundEngine::getInstance()->flushAllSources();
112
113  if (State::getObjectManager() == &this->objectManager)
114    State::setObjectManager(NULL);
115  // erease everything that is left.
116  delete PNode::getNullParent();
117
118  //secondary cleanup of PNodes;
119  const std::list<BaseObject*>* nodeList = ClassList::getList(CL_PARENT_NODE);
120  if (nodeList != NULL)
121    while (!nodeList->empty())
122      delete nodeList->front();
123
124  Shader::suspendShader();
125
126  // unload the resources !!
127  ResourceManager::getInstance()->unloadAllByPriority(RP_LEVEL);
128}
129
130/**
131 * initializes the world.
132 * @param name the name of the world
133 * @param worldID the ID of this world
134 *
135 * set all stuff here that is world generic and does not use to much memory
136 * because the real init() function StoryEntity::init() will be called
137 * shortly before start of the game.
138 * since all worlds are initiated/referenced before they will be started.
139 * NO LEVEL LOADING HERE - NEVER!
140*/
141void World::constuctorInit(const char* name, int worldID)
142{
143  this->setClassID(CL_WORLD, "World");
144
145  this->setName(name);
146  this->gameTime = 0.0f;
147  this->setSpeed(1.0);
148  this->music = NULL;
149  this->shell = NULL;
150  this->localPlayer = NULL;
151  this->localCamera = NULL;
152
153  this->showPNodes = false;
154  this->showBV = false;
155}
156
157/**
158 * loads the parameters of a World from an XML-element
159 * @param root the XML-element to load from
160 */
161void World::loadParams(const TiXmlElement* root)
162{
163  PRINTF(4)("Creating a World\n");
164
165  LoadParam(root, "identifier", this, World, setStoryID)
166    .describe("Sets the StoryID of this world");
167
168  LoadParam(root, "nextid", this, World, setNextStoryID)
169    .describe("Sets the ID of the next world");
170
171  LoadParam(root, "path", this, World, setPath)
172    .describe("The Filename of this World (relative from the data-dir)");
173}
174
175/**
176 * this is executed just before load
177 *
178 * since the load function sometimes needs data, that has been initialized
179 * before the load and after the proceeding storyentity has finished
180*/
181ErrorMessage World::preLoad()
182{
183  State::setObjectManager(&this->objectManager);
184  this->cycle = 0;
185
186  /* init the world interface */
187  this->shell = new Shell();
188
189  LightManager::getInstance();
190  PNode::getNullParent();
191
192  AnimationPlayer::getInstance(); // initializes the animationPlayer
193  ParticleEngine::getInstance();
194  PhysicsEngine::getInstance();
195
196  this->localCamera = new Camera();
197  this->localCamera->setName ("World-Camera");
198
199  State::setCamera(this->localCamera, this->localCamera->getTarget());
200
201  GraphicsEngine::getInstance()->displayFPS(true);
202}
203
204
205/**
206 *  loads the World by initializing all resources, and set their default values.
207 */
208ErrorMessage World::load()
209{
210  PRINTF(3)("> Loading world: '%s'\n", getPath());
211  TiXmlElement* element;
212  GameLoader* loader = GameLoader::getInstance();
213
214  if( getPath() == NULL)
215    {
216      PRINTF(1)("World has no path specified for loading");
217      return (ErrorMessage){213,"Path not specified","World::load()"};
218    }
219
220  TiXmlDocument* XMLDoc = new TiXmlDocument( getPath());
221  // load the campaign document
222  if( !XMLDoc->LoadFile())
223  {
224    // report an error
225    PRINTF(1)("loading XML File: %s @ %s:l%d:c%d\n", XMLDoc->ErrorDesc(), this->getPath(), XMLDoc->ErrorRow(), XMLDoc->ErrorCol());
226    delete XMLDoc;
227    return (ErrorMessage){213,"XML File parsing error","World::load()"};
228  }
229
230  // check basic validity
231  TiXmlElement* root = XMLDoc->RootElement();
232  assert( root != NULL);
233
234  if( root == NULL || root->Value() == NULL || strcmp( root->Value(), "WorldDataFile"))
235    {
236      // report an error
237      PRINTF(1)("Specified XML File is not an orxonox world data file (WorldDataFile element missing)\n");
238      delete XMLDoc;
239      return (ErrorMessage){213,"Path not a WorldDataFile","World::load()"};
240    }
241
242  // load the parameters
243  // name
244  const char* string = grabParameter( root, "name");
245  if( string == NULL)
246    {
247      PRINTF(2)("World is missing a proper 'name'\n");
248      this->setName("Unknown");
249    }
250  else
251    {
252      this->setName(string);
253    }
254
255  ////////////////
256  // LOADSCREEN //
257  ////////////////
258  element = root->FirstChildElement("LoadScreen");
259  if (element == NULL)
260    {
261      PRINTF(2)("no LoadScreen specified, loading default\n");
262
263      glmis->setBackgroundImage("pictures/load_screen.jpg");
264      this->glmis->setMaximum(8);
265      this->glmis->draw();
266    }
267  else
268    {
269      this->glmis->loadParams(element);
270      this->glmis->draw();
271    }
272  this->glmis->draw();
273
274  ////////////////////////
275  // find WorldEntities //
276  ////////////////////////
277
278  element = root->FirstChildElement("WorldEntities");
279
280  if( element == NULL)
281    {
282      PRINTF(1)("World is missing 'WorldEntities'\n");
283    }
284  else
285    {
286      element = element->FirstChildElement();
287      // load Players/Objects/Whatever
288      PRINTF(4)("Loading WorldEntities\n");
289      while( element != NULL)
290        {
291          BaseObject* created = Factory::fabricate(element);
292          if( created != NULL )
293          {
294            if(created->isA(CL_WORLD_ENTITY))
295              this->spawn(dynamic_cast<WorldEntity*>(created));
296            printf("Created a %s: %s\n", created->getClassName(), created->getName());
297          }
298
299          // if we load a 'Player' we use it as localPlayer
300
301
302          //todo do this more elegant
303          if( element->Value() != NULL && !strcmp( element->Value(), "SkyBox"))
304            this->sky = dynamic_cast<WorldEntity*>(created);
305          if( element->Value() != NULL && !strcmp( element->Value(), "Terrain"))
306          {
307            terrain = dynamic_cast<Terrain*>(created);
308            CDEngine::getInstance()->setTerrain(terrain);
309          }
310          element = element->NextSiblingElement();
311          glmis->step(); //! @todo temporary
312        }
313      PRINTF(4)("Done loading WorldEntities\n");
314    }
315
316    //////////////////////////////
317    // LOADING ADDITIONAL STUFF //
318    //////////////////////////////
319
320    LoadParamXML(root, "LightManager", LightManager::getInstance(), LightManager, loadParams);
321
322   LoadParamXML(root, "ParticleEngine", ParticleEngine::getInstance(), ParticleEngine, loadParams);
323//   LoadParamXML(root, "PhysicsEngine", PhysicsEngine::getInstance(), PhysicsEngine, loadParams);
324
325  // free the XML data
326
327  delete XMLDoc;
328  /* GENERIC LOADING PROCESS FINISHED */
329
330
331  // Create a Player
332  this->localPlayer = new Player();
333
334  Playable* playable;
335  const list<BaseObject*>* playableList = ClassList::getList(CL_PLAYABLE);
336  if (playableList != NULL)
337  {
338    playable = dynamic_cast<Playable*>(playableList->front());
339    this->localPlayer->setControllable(playable);
340  }
341
342  // bind camera
343  playable->addChild (this->localCamera);
344
345//   //localCamera->setParent(TrackNode::getInstance());
346//  tn->addChild(this->localCamera);
347  localCamera->setClipRegion(1, 10000.0);
348  localCamera->lookAt(playable);
349//  this->localPlayer->setParentMode(PNODE_ALL);
350  if (sky != NULL)
351  {
352    this->sky->setParent(this->localCamera);
353    this->sky->setParentMode(PNODE_MOVEMENT);
354  }
355
356  SoundEngine::getInstance()->setListener(this->localCamera);
357
358
359
360  ////////////
361  // STATIC //
362  ////////////
363
364
365//   TestEntity* testEntity = new TestEntity();
366//   testEntity->setRelCoor(Vector(570, 10, -15));
367//   testEntity->setRelDir(Quaternion(M_PI, Vector(0, 1, 0)));
368//   this->spawn(testEntity);
369
370  for(int i = 0; i < 100; i++)
371  {
372    WorldEntity* tmp = new NPCTest1();
373    char npcChar[10];
374    sprintf (npcChar, "NPC_%d", i);
375        tmp->setName(npcChar);
376    tmp->setAbsCoor(((float)rand()/RAND_MAX) * 5000, 50/*+ (float)rand()/RAND_MAX*20*/, ((float)rand()/RAND_MAX -.5) *30);
377    this->spawn(tmp);
378  }
379
380  this->music = NULL;
381  //(OggPlayer*)ResourceManager::getInstance()->load("sound/00-luke_grey_-_hypermode.ogg", OGG, RP_LEVEL);
382  //music->playback();
383}
384
385
386/**
387 *  initializes a new World shortly before start
388 *
389 * this is the function, that will be loaded shortly before the world is
390 * started
391*/
392ErrorMessage World::init()
393{
394  this->bPause = false;
395
396  /* update the object position before game start - so there are no wrong coordinates used in the first processing */
397  PNode::getNullParent()->updateNode (0.001f);
398  PNode::getNullParent()->updateNode (0.001f);
399}
400
401
402/**
403 *  starts the World
404 */
405ErrorMessage World::start()
406{
407  this->bQuitOrxonox = false;
408  this->bQuitCurrentGame = false;
409  this->mainLoop();
410}
411
412/**
413 *  stops the world.
414
415   This happens, when the player decides to end the Level.
416*/
417ErrorMessage World::stop()
418{
419  PRINTF(3)("World::stop() - got stop signal\n");
420  this->bQuitCurrentGame = true;
421}
422
423/**
424 *  pauses the Game
425*/
426ErrorMessage World::pause()
427{
428  this->isPaused = true;
429}
430
431/**
432 *  ends the pause Phase
433*/
434ErrorMessage World::resume()
435{
436  this->isPaused = false;
437}
438
439/**
440 *  destroys the World
441*/
442ErrorMessage World::destroy()
443{
444
445}
446
447/**
448 *  shows the loading screen
449*/
450void World::displayLoadScreen ()
451{
452  PRINTF(3)("World::displayLoadScreen - start\n");
453
454  //GLMenuImageScreen*
455  this->glmis = new GLMenuImageScreen();
456  this->glmis->setMaximum(8);
457
458  PRINTF(3)("World::displayLoadScreen - end\n");
459}
460
461/**
462 * @brief removes the loadscreen, and changes over to the game
463 *
464 * @todo take out the delay
465*/
466void World::releaseLoadScreen ()
467{
468  PRINTF(3)("World::releaseLoadScreen - start\n");
469  this->glmis->setValue(this->glmis->getMaximum());
470  PRINTF(3)("World::releaseLoadScreen - end\n");
471  delete this->glmis;
472}
473
474
475/**
476 *  this returns the current game time
477 * @returns elapsed game time
478*/
479double World::getGameTime()
480{
481  return this->gameTime;
482}
483
484
485
486/**
487 *  synchronize local data with remote data
488*/
489void World::synchronize ()
490{
491  // Get remote input
492  // Update synchronizables
493/*  NetworkManager::getInstance()->synchronize();*/
494}
495
496
497/**
498 *  run all input processing
499
500   the command node is the central input event dispatcher. the node uses the even-queue from
501   sdl and has its own event-passing-queue.
502*/
503void World::handleInput ()
504{
505  EventHandler::getInstance()->process();
506
507  // remoteinput
508}
509
510void World::tick(std::list<WorldEntity*> entityList, float dt)
511{
512  std::list<WorldEntity*>::iterator entity;
513  for (entity = entityList.begin(); entity != entityList.end(); entity++)
514    (*entity)->tick(dt);
515
516}
517
518/**
519 *  advance the timeline
520
521   this calculates the time used to process one frame (with all input handling, drawing, etc)
522   the time is mesured in ms and passed to all world-entities and other classes that need
523   a heart-beat.
524*/
525void World::tick ()
526{
527  Uint32 currentFrame = SDL_GetTicks();
528  if(!this->bPause)
529    {
530      this->dt = currentFrame - this->lastFrame;
531
532      if( this->dt > 10)
533        {
534          float fps = 1000/dt;
535
536          // temporary, only for showing how fast the text-engine is
537          char tmpChar[20];
538          sprintf(tmpChar, "fps: %4.0f", fps);
539        }
540      else
541        {
542          /* the frame-rate is limited to 100 frames per second, all other things are for
543             nothing.
544          */
545          PRINTF(3)("fps = 1000 - frame rate is adjusted\n");
546          SDL_Delay(10-dt);
547          this->dt = 10;
548        }
549
550      this->dtS = (float)this->dt / 1000.0 * this->speed;
551      this->gameTime += this->dtS;
552
553/*      this->tick(this->objectManager.getObjectList(OM_DEAD_TICK), this->dtS);
554      this->tick(this->objectManager.getObjectList(OM_COMMON), this->dtS);
555      this->tick(this->objectManager.getObjectList(OM_GROUP_00), this->dtS);*/
556      this->tick(this->objectManager.getObjectList(OM_GROUP_01), this->dtS);
557      this->tick(this->objectManager.getObjectList(OM_GROUP_01_PROJ), this->dtS);
558
559      /* update tick the rest */
560      this->localCamera->tick(this->dtS);
561      // tick the engines
562      AnimationPlayer::getInstance()->tick(this->dtS);
563//      if (this->cycle > 5)
564        PhysicsEngine::getInstance()->tick(this->dtS);
565
566      ParticleEngine::getInstance()->tick(this->dtS);
567
568
569      /** actualy the Graphics Engine should tick the world not the other way around...
570         but since we like the things not too complicated we got it this way around
571         until there is need or time to do it the other way around.
572         @todo: GraphicsEngine ticks world: separation of processes and data...
573
574        bensch: in my opinion the GraphicsEngine could draw the world, but not tick it,
575         beceause graphics have nothing(or at least not much) to do with Motion.
576      */
577      GraphicsEngine::getInstance()->tick(this->dtS);
578    }
579  this->lastFrame = currentFrame;
580}
581
582
583/**
584 *  this function gives the world a consistant state
585
586   after ticking (updating the world state) this will give a constistant
587   state to the whole system.
588*/
589void World::update()
590{
591  GraphicsEngine::getInstance()->update(this->dtS);
592  PNode::getNullParent()->updateNode (this->dtS);
593
594  SoundEngine::getInstance()->update();
595  //music->update();
596}
597
598
599void World::collide()
600{
601  CDEngine::getInstance()->checkCollisions(this->objectManager.getObjectList(OM_GROUP_00),
602                                            this->objectManager.getObjectList(OM_GROUP_01_PROJ));
603  CDEngine::getInstance()->checkCollisions(this->objectManager.getObjectList(OM_GROUP_01),
604                                            this->objectManager.getObjectList(OM_COMMON));
605}
606
607/**
608 *  render the current frame
609
610   clear all buffers and draw the world
611*/
612void World::display ()
613{
614  // clear buffer
615  glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
616  // set camera
617  this->localCamera->apply ();
618  // draw world
619  this->draw();
620  // draw HUD
621  /** @todo draw HUD */
622  // flip buffers
623  GraphicsEngine::swapBuffers();
624  //SDL_Surface* screen = Orxonox::getInstance()->getScreen ();
625  //SDL_Flip (screen);
626}
627
628
629/**
630 *  runs through all entities calling their draw() methods
631 */
632void World::draw ()
633{
634  GraphicsEngine* engine = GraphicsEngine::getInstance();
635  engine->draw(State::getObjectManager()->getObjectList(OM_ENVIRON_NOTICK));
636  engine->draw(State::getObjectManager()->getObjectList(OM_ENVIRON));
637  engine->draw(State::getObjectManager()->getObjectList(OM_COMMON));
638  engine->draw(State::getObjectManager()->getObjectList(OM_GROUP_00));
639  engine->draw(State::getObjectManager()->getObjectList(OM_GROUP_01));
640  engine->draw(State::getObjectManager()->getObjectList(OM_GROUP_01_PROJ));
641
642//   {
643//     if( entity->isVisible() ) entity->draw();
644  //FIXME
645//     if( unlikely( this->showBV)) entity->drawBVTree(3, 226);  // to draw the bounding boxes of the objects at level 2 for debug purp
646//     entity = iterator->nextElement();
647//   }
648
649  ParticleEngine::getInstance()->draw();
650
651  if (unlikely(this->showPNodes))
652    PNode::getNullParent()->debugDraw(0);
653
654  engine->draw();
655  //TextEngine::getInstance()->draw();
656}
657
658
659/**
660 * \brief main loop of the world: executing all world relevant function
661 *
662 * in this loop we synchronize (if networked), handle input events, give the heart-beat to
663 * all other member-entities of the world (tick to player, enemies etc.), checking for
664 * collisions drawing everything to the screen.
665 */
666void World::mainLoop()
667{
668  this->lastFrame = SDL_GetTicks ();
669  PRINTF(3)("World::mainLoop() - Entering main loop\n");
670
671  while( !this->bQuitOrxonox && !this->bQuitCurrentGame) /* @todo implement pause */
672  {
673    ++this->cycle;
674      // Network
675    this->synchronize ();
676      // Process input
677    this->handleInput ();
678    if( this->bQuitCurrentGame || this->bQuitOrxonox)
679      break;
680      // Process time
681    this->tick ();
682      // Process collision
683    this->collide ();
684      // Update the state
685    this->update ();
686      // Draw
687    this->display ();
688  }
689
690  PRINTF(3)("World::mainLoop() - Exiting the main loop\n");
691}
692
693
694
695/**
696 *  add and spawn a new entity to this world
697 * @param entity to be added
698*/
699void World::spawn(WorldEntity* entity)
700{
701//   this->entities->add (entity);
702  entity->postSpawn ();
703}
704
705void World::setPath( const char* name)
706{
707  if (this->path)
708    delete this->path;
709  if (ResourceManager::isFile(name))
710  {
711    this->path = new char[strlen(name)+1];
712    strcpy(this->path, name);
713  }
714  else
715    {
716      this->path = new char[strlen(ResourceManager::getInstance()->getDataDir()) + strlen(name) +1];
717      sprintf(this->path, "%s%s", ResourceManager::getInstance()->getDataDir(), name);
718    }
719}
720
721const char* World::getPath( void)
722{
723  return path;
724}
Note: See TracBrowser for help on using the repository browser.