Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: orxonox.OLD/branches/spaceshipcontrol/src/story_entities/world.cc @ 5891

Last change on this file since 5891 was 5891, checked in by snellen, 18 years ago

space_ship.cc and world.cc updated

File size: 21.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
23#include "state.h"
24
25#include "p_node.h"
26#include "null_parent.h"
27#include "pilot_node.h"
28#include "world_entity.h"
29#include "player.h"
30#include "camera.h"
31#include "environment.h"
32#include "skysphere.h"
33#include "skybox.h"
34#include "satellite.h"
35#include "test_entity.h"
36#include "terrain.h"
37#include "light.h"
38#include "load_param.h"
39#include "shell.h"
40
41#include "garbage_collector.h"
42#include "fast_factory.h"
43#include "animation_player.h"
44#include "particle_engine.h"
45#include "graphics_engine.h"
46#include "physics_engine.h"
47#include "fields.h"
48
49#include "md2Model.h"
50
51#include "glmenu_imagescreen.h"
52#include "list.h"
53#include "game_loader.h"
54
55#include "animation3d.h"
56
57#include "substring.h"
58
59#include "factory.h"
60
61#include "weapons/projectile.h"
62#include "event_handler.h"
63#include "sound_engine.h"
64#include "ogg_player.h"
65
66#include "class_list.h"
67
68#include "cd_engine.h"
69#include "npcs/npc_test1.h"
70#include "shader.h"
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  *  create a new World
91
92    This creates a new empty world!
93*/
94World::World (const char* name)
95{
96  this->path = NULL;
97  this->constuctorInit(name, -1);
98}
99
100/**
101 *  creates a new World...
102 * @param worldID with this ID
103*/
104World::World (int worldID)
105{
106  this->path = NULL;
107  this->constuctorInit(NULL, worldID);
108}
109
110/**
111 *  remove the World from memory
112
113    delete everything explicitly, that isn't contained in the parenting tree!
114    things contained in the tree are deleted automaticaly
115 */
116World::~World ()
117{
118  delete this->shell;
119  PRINTF(3)("World::~World() - deleting current world\n");
120
121  delete this->localPlayer;
122
123  // here everything that is alocated by the World is deleted
124  delete this->entities;
125  State::setWorldEntityList(NULL);
126
127
128  // delete all the initialized Engines.
129  FastFactory::flushAll(true);
130  delete LightManager::getInstance();
131  delete ParticleEngine::getInstance();
132  delete AnimationPlayer::getInstance();
133  delete PhysicsEngine::getInstance();
134
135  // external engines initialized by the orxonox-class get deleted
136  SoundEngine::getInstance()->flushAllBuffers();
137  SoundEngine::getInstance()->flushAllSources();
138
139
140  // erease everything that is left.
141  delete NullParent::getInstance();
142  Shader::suspendShader();
143
144  // unload the resources !!
145  ResourceManager::getInstance()->unloadAllByPriority(RP_LEVEL);
146
147  delete[] this->path;
148}
149
150/**
151 * initializes the world.
152 * @param name the name of the world
153 * @param worldID the ID of this world
154 *
155 * set all stuff here that is world generic and does not use to much memory
156 * because the real init() function StoryEntity::init() will be called
157 * shortly before start of the game.
158 * since all worlds are initiated/referenced before they will be started.
159 * NO LEVEL LOADING HERE - NEVER!
160*/
161void World::constuctorInit(const char* name, int worldID)
162{
163  this->setClassID(CL_WORLD, "World");
164
165  this->setName(name);
166  this->debugWorldNr = worldID;
167  this->gameTime = 0.0f;
168  this->setSpeed(1.0);
169  this->music = NULL;
170  this->shell = NULL;
171  this->entities = NULL;
172  this->localPlayer = NULL;
173  this->localCamera = NULL;
174
175  this->showPNodes = false;
176  this->showBV = false;
177}
178
179/**
180 * loads the parameters of a World from an XML-element
181 * @param root the XML-element to load from
182 */
183void World::loadParams(const TiXmlElement* root)
184{
185  PRINTF(4)("Creating a World\n");
186
187  LoadParam(root, "identifier", this, World, setStoryID)
188    .describe("Sets the StoryID of this world");
189
190  LoadParam(root, "nextid", this, World, setNextStoryID)
191    .describe("Sets the ID of the next world");
192
193  LoadParam(root, "path", this, World, setPath)
194    .describe("The Filename of this World (relative from the data-dir)");
195}
196
197/**
198 * this is executed just before load
199 *
200 * since the load function sometimes needs data, that has been initialized
201 * before the load and after the proceeding storyentity has finished
202*/
203ErrorMessage World::preLoad()
204{
205  State::setWorldEntityList(this->entities = new tList<WorldEntity>());
206  this->cycle = 0;
207
208  /* init the world interface */
209  this->shell = new Shell();
210
211  LightManager::getInstance();
212  NullParent::getInstance ();
213
214  AnimationPlayer::getInstance(); // initializes the animationPlayer
215  PhysicsEngine::getInstance();
216
217  this->localCamera = new Camera();
218  this->localCamera->setName ("World-Camera");
219
220  State::setCamera(this->localCamera, this->localCamera->getTarget());
221
222  GraphicsEngine::getInstance()->displayFPS(true);
223
224  CDEngine::getInstance()->setEntityList( this->entities);
225}
226
227
228/**
229 *  loads the World by initializing all resources, and set their default values.
230*/
231ErrorMessage World::load()
232{
233  PRINTF(3)("> Loading world: '%s'\n", getPath());
234  TiXmlElement* element;
235  GameLoader* loader = GameLoader::getInstance();
236
237  if( getPath() == NULL)
238    {
239      PRINTF(1)("World has no path specified for loading");
240      this->loadDebugWorld(this->getStoryID());
241      return (ErrorMessage){213,"Path not specified","World::load()"};
242    }
243
244  TiXmlDocument* XMLDoc = new TiXmlDocument( path);
245  // load the campaign document
246  if( !XMLDoc->LoadFile())
247  {
248    // report an error
249    PRINTF(1)("loading XML File: %s @ %d:%d\n", XMLDoc->ErrorDesc(), XMLDoc->ErrorRow(), XMLDoc->ErrorCol());
250    delete XMLDoc;
251    return (ErrorMessage){213,"XML File parsing error","World::load()"};
252  }
253
254  // check basic validity
255  TiXmlElement* root = XMLDoc->RootElement();
256  assert( root != NULL);
257
258  if( root == NULL || root->Value() == NULL || strcmp( root->Value(), "WorldDataFile"))
259    {
260      // report an error
261      PRINTF(1)("Specified XML File is not an orxonox world data file (WorldDataFile element missing)\n");
262      delete XMLDoc;
263      return (ErrorMessage){213,"Path not a WorldDataFile","World::load()"};
264    }
265
266  // load the parameters
267  // name
268  const char* string = grabParameter( root, "name");
269  if( string == NULL)
270    {
271      PRINTF(2)("World is missing a proper 'name'\n");
272      this->setName("Unknown");
273    }
274  else
275    {
276      this->setName(string);
277    }
278
279  ////////////////
280  // LOADSCREEN //
281  ////////////////
282  element = root->FirstChildElement("LoadScreen");
283  if (element == NULL)
284    {
285      PRINTF(2)("no LoadScreen specified, loading default\n");
286
287      glmis->setBackgroundImage("pictures/load_screen.jpg");
288      this->glmis->setMaximum(8);
289      this->glmis->draw();
290    }
291  else
292    {
293      this->glmis->loadParams(element);
294      this->glmis->draw();
295    }
296  this->glmis->draw();
297
298  ////////////////////////
299  // find WorldEntities //
300  ////////////////////////
301
302  element = root->FirstChildElement("WorldEntities");
303
304  if( element == NULL)
305    {
306      PRINTF(1)("World is missing 'WorldEntities'\n");
307    }
308  else
309    {
310      element = element->FirstChildElement();
311      // load Players/Objects/Whatever
312      PRINTF(4)("Loading WorldEntities\n");
313      while( element != NULL)
314        {
315          WorldEntity* created = dynamic_cast<WorldEntity*>( loader->fabricate( element));
316          if( created != NULL) this->spawn( created);
317          // if we load a 'Player' we use it as localPlayer
318
319          //todo do this more elegant
320          if( element->Value() != NULL && !strcmp( element->Value(), "SkyBox")) sky = (SkyBox*) created;
321          if( element->Value() != NULL && !strcmp( element->Value(), "Terrain"))
322          {
323            terrain = (Terrain*) created;
324            CDEngine::getInstance()->setTerrain(terrain);
325          }
326          element = element->NextSiblingElement();
327          glmis->step(); //! @todo temporary
328        }
329      PRINTF(4)("Done loading WorldEntities\n");
330    }
331
332    //////////////////////////////
333    // LOADING ADDITIONAL STUFF //
334    //////////////////////////////
335
336    LoadParamXML(root, "LightManager", LightManager::getInstance(), LightManager, loadParams);
337
338//    LoadParamXML(root, "ParticleEngine", ParticleEngine::getInstance(), ParticleEngine, loadParams);
339//    LoadParamXML(root, "PhysicsEngine", PhysicsEngine::getInstance(), PhysicsEngine, loadParams);
340
341  // free the XML data
342
343  delete XMLDoc;
344  /* GENERIC LOADING PROCESS FINISHED */
345
346
347  // Create a Player
348  this->localPlayer = new Player();
349
350  Playable* playable;
351  list<BaseObject*>* playableList = ClassList::getList(CL_PLAYABLE);
352  if (playableList != NULL)
353  {
354    playable = dynamic_cast<Playable*>(playableList->front());
355    if (playable != NULL)
356      ;//this->localPlayer->setControllable(playable);
357  }
358  // bind input
359
360
361  // bind camera
362  playable->addChild (this->localCamera);
363
364
365//   //localCamera->setParent(TrackNode::getInstance());
366//  tn->addChild(this->localCamera);
367  localCamera->setClipRegion(1, 1000.0);
368//  this->localPlayer->setParentMode(PNODE_ALL);
369  if (sky != NULL)
370  {
371    this->sky->setParent(this->localCamera);
372    this->sky->setParentMode(PNODE_MOVEMENT);
373  }
374
375  // initialize debug coord system
376  objectList = glGenLists(1);
377  glNewList (objectList, GL_COMPILE);
378
379  glEndList();
380
381  SoundEngine::getInstance()->setListener(this->localCamera);
382
383
384
385  ////////////
386  // STATIC //
387  ////////////
388
389//   TestEntity* testEntity = new TestEntity();
390//   testEntity->setRelCoor(Vector(570, 10, -15));
391//   testEntity->setRelDir(Quaternion(M_PI, Vector(0, 1, 0)));
392//   this->spawn(testEntity);
393
394  for(int i = 0; i < 100; i++)
395  {
396    WorldEntity* tmp = new NPCTest1();
397    char npcChar[10];
398    sprintf (npcChar, "NPC_%d", i);
399        tmp->setName(npcChar);
400    tmp->setAbsCoor(((float)rand()/RAND_MAX) * 5000, 50/*+ (float)rand()/RAND_MAX*20*/, ((float)rand()/RAND_MAX -.5) *30);
401    this->spawn(tmp);
402  }
403
404  this->music = NULL;//(OggPlayer*)ResourceManager::getInstance()->load("sound/00-luke_grey_-_hypermode.ogg", OGG, RP_LEVEL);
405  //music->playback();
406}
407
408
409
410/**
411 * creates a debug world: only for experimental stuff
412*/
413void World::loadDebugWorld(int worldID)
414{
415  /*monitor progress*/
416  this->glmis->step();
417  // stuff beyond this point remains to be loaded properly
418
419  // LIGHT initialisation
420  LightManager::getInstance()->setAmbientColor(.1,.1,.1);
421//  LightManager::getInstance()->addLight();
422  LightManager::getInstance()->debug();
423
424  switch(this->debugWorldNr)
425    {
426      /*
427        this loads the hard-coded debug world. this only for simplicity and will be
428        removed by a reald world-loader, which interprets a world-file.
429        if you want to add an own debug world, just add a case DEBUG_WORLD_[nr] and
430        make whatever you want...
431      */
432    case DEBUG_WORLD_0:
433      {
434        LightManager::getInstance()->getLight()->setAbsCoor(-5.0, 10.0, -40.0);
435        /*monitor progress*/
436        this->glmis->step();
437
438        // bind camera
439        this->localCamera = new Camera();
440        this->localCamera->setName ("camera");
441        /*monitor progress*/
442        this->glmis->step();
443
444
445        // Create SkySphere
446        this->sky = new Skysphere("pictures/sky-replace.jpg");
447        this->sky->setName("SkySphere");
448        this->spawn(this->sky);
449        this->localCamera->addChild(this->sky);
450        this->sky->setParentMode(PNODE_MOVEMENT);
451        /*monitor progress*/
452        this->glmis->step();
453
454
455        terrain = new Terrain("worlds/newGround.obj");
456        terrain->setRelCoor(Vector(0,-10,0));
457        this->spawn(terrain);
458        /*monitor progress*/
459        this->glmis->step();
460
461        this->glmis->step();
462        break;
463      }
464    case DEBUG_WORLD_1:
465      {
466
467        break;
468      }
469    case DEBUG_WORLD_2:
470      {
471
472        break;
473      }
474    default:
475      break;
476    }
477}
478
479/**
480 *  initializes a new World shortly before start
481 *
482 * this is the function, that will be loaded shortly before the world is
483 * started
484*/
485ErrorMessage World::init()
486{
487  this->bPause = false;
488  this->pilotNode = NULL;
489
490  /* update the object position before game start - so there are no wrong coordinates used in the first processing */
491  NullParent::getInstance()->updateNode (0.001f);
492  NullParent::getInstance()->updateNode (0.001f);
493
494}
495
496
497/**
498 *  starts the World
499*/
500ErrorMessage World::start()
501{
502  PRINTF(3)("World::start() - starting current World: nr %i\n", this->debugWorldNr);
503  this->bQuitOrxonox = false;
504  this->bQuitCurrentGame = false;
505  this->mainLoop();
506}
507
508/**
509 *  stops the world.
510
511   This happens, when the player decides to end the Level.
512*/
513ErrorMessage World::stop()
514{
515  PRINTF(3)("World::stop() - got stop signal\n");
516  this->bQuitCurrentGame = true;
517}
518
519/**
520 *  pauses the Game
521*/
522ErrorMessage World::pause()
523{
524  this->isPaused = true;
525}
526
527/**
528 *  ends the pause Phase
529*/
530ErrorMessage World::resume()
531{
532  this->isPaused = false;
533}
534
535/**
536 *  destroys the World
537*/
538ErrorMessage World::destroy()
539{
540
541}
542
543/**
544 *  shows the loading screen
545*/
546void World::displayLoadScreen ()
547{
548  PRINTF(3)("World::displayLoadScreen - start\n");
549
550  //GLMenuImageScreen*
551  this->glmis = new GLMenuImageScreen();
552  this->glmis->setMaximum(8);
553
554  PRINTF(3)("World::displayLoadScreen - end\n");
555}
556
557/**
558 *  removes the loadscreen, and changes over to the game
559
560   @todo take out the delay
561*/
562void World::releaseLoadScreen ()
563{
564  PRINTF(3)("World::releaseLoadScreen - start\n");
565  this->glmis->setValue(this->glmis->getMaximum());
566  PRINTF(3)("World::releaseLoadScreen - end\n");
567  delete this->glmis;
568}
569
570
571/**
572 *  gets the list of entities from the world
573 * @returns entity list
574*/
575tList<WorldEntity>* World::getEntities()
576{
577  return this->entities;
578}
579
580
581/**
582 *  this returns the current game time
583 * @returns elapsed game time
584*/
585double World::getGameTime()
586{
587  return this->gameTime;
588}
589
590
591/**
592 *  function to put your own debug stuff into it. it can display informations about
593   the current class/procedure
594*/
595void World::debug()
596{
597  PRINTF(0)("Printing out the List of alive WorldEntities:\n");
598  tIterator<WorldEntity>* iterator = this->entities->getIterator();
599  WorldEntity* entity = iterator->firstElement();
600  while( entity != NULL)
601  {
602    PRINTF(0)("%s::%s\n", entity->getClassName(), entity->getName());
603    entity = iterator->nextElement();
604  }
605  delete iterator;
606}
607
608
609/**
610  \brief main loop of the world: executing all world relevant function
611
612  in this loop we synchronize (if networked), handle input events, give the heart-beat to
613  all other member-entities of the world (tick to player, enemies etc.), checking for
614  collisions drawing everything to the screen.
615*/
616void World::mainLoop()
617{
618  this->lastFrame = SDL_GetTicks ();
619  PRINTF(3)("World::mainLoop() - Entering main loop\n");
620
621  while( !this->bQuitOrxonox && !this->bQuitCurrentGame) /* @todo implement pause */
622    {
623      ++this->cycle;
624      PRINTF(4)("World::mainloop() - number of entities: %i\n", this->entities->getSize());
625      // Network
626      this->synchronize ();
627      // Process input
628      this->handleInput ();
629      if( this->bQuitCurrentGame || this->bQuitOrxonox)
630          break;
631      // Process time
632      this->tick ();
633      // Process collision
634      this->collide ();
635      // Update the state
636      this->update ();
637      // Draw
638      this->display ();
639    }
640
641  PRINTF(3)("World::mainLoop() - Exiting the main loop\n");
642}
643
644
645/**
646 *  synchronize local data with remote data
647*/
648void World::synchronize ()
649{
650  // Get remote input
651  // Update synchronizables
652}
653
654
655/**
656 *  run all input processing
657
658   the command node is the central input event dispatcher. the node uses the even-queue from
659   sdl and has its own event-passing-queue.
660*/
661void World::handleInput ()
662{
663  EventHandler::getInstance()->process();
664
665  // remoteinput
666}
667
668
669/**
670 *  advance the timeline
671
672   this calculates the time used to process one frame (with all input handling, drawing, etc)
673   the time is mesured in ms and passed to all world-entities and other classes that need
674   a heart-beat.
675*/
676void World::tick ()
677{
678  Uint32 currentFrame = SDL_GetTicks();
679  if(!this->bPause)
680    {
681      this->dt = currentFrame - this->lastFrame;
682
683      if( this->dt > 10)
684        {
685          float fps = 1000/dt;
686
687          // temporary, only for showing how fast the text-engine is
688          char tmpChar[20];
689          sprintf(tmpChar, "fps: %4.0f", fps);
690        }
691      else
692        {
693          /* the frame-rate is limited to 100 frames per second, all other things are for
694             nothing.
695          */
696          PRINTF(3)("fps = 1000 - frame rate is adjusted\n");
697          SDL_Delay(10-dt);
698          this->dt = 10;
699        }
700
701      this->dtS = (float)this->dt / 1000.0 * this->speed;
702      this->gameTime += this->dtS;
703
704      tIterator<WorldEntity>* iterator = this->entities->getIterator();
705      WorldEntity* entity = iterator->firstElement();
706      while( entity != NULL)
707        {
708          entity->tick (this->dtS);
709          entity = iterator->nextElement();
710        }
711      delete iterator;
712
713      /* update tick the rest */
714      this->localCamera->tick(this->dtS);
715      // tick the engines
716      AnimationPlayer::getInstance()->tick(this->dtS);
717//      if (this->cycle > 5)
718        PhysicsEngine::getInstance()->tick(this->dtS);
719
720      ParticleEngine::getInstance()->tick(this->dtS);
721      GarbageCollector::getInstance()->tick(this->dtS);
722
723
724      /** actualy the Graphics Engine should tick the world not the other way around...
725         but since we like the things not too complicated we got it this way around
726         until there is need or time to do it the other way around.
727         @todo: GraphicsEngine ticks world: separation of processes and data...
728
729        bensch: in my opinion the GraphicsEngine could draw the world, but not tick it,
730         beceause graphics have nothing(or at least not much) to do with Motion.
731      */
732      GraphicsEngine::getInstance()->tick(this->dtS);
733    }
734  this->lastFrame = currentFrame;
735}
736
737
738/**
739 *  this function gives the world a consistant state
740
741   after ticking (updating the world state) this will give a constistant
742   state to the whole system.
743*/
744void World::update()
745{
746  GarbageCollector::getInstance()->update();
747  GraphicsEngine::getInstance()->update(this->dtS);
748  NullParent::getInstance()->updateNode (this->dtS);
749
750  SoundEngine::getInstance()->update();
751  //music->update();
752}
753
754
755void World::collide()
756{
757  CDEngine::getInstance()->checkCollisions();
758}
759
760/**
761 *  render the current frame
762
763   clear all buffers and draw the world
764*/
765void World::display ()
766{
767  // clear buffer
768  glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
769  // set camera
770  this->localCamera->apply ();
771  // draw world
772  this->draw();
773  // draw HUD
774  /** @todo draw HUD */
775  // flip buffers
776  GraphicsEngine::swapBuffers();
777  //SDL_Surface* screen = Orxonox::getInstance()->getScreen ();
778  //SDL_Flip (screen);
779}
780
781
782/**
783 *  runs through all entities calling their draw() methods
784 */
785void World::draw ()
786{
787  /* draw entities */
788  WorldEntity* entity;
789  glLoadIdentity();
790  tIterator<WorldEntity>* iterator = this->entities->getIterator();
791  entity = iterator->firstElement();
792  while( entity != NULL )
793  {
794    if( entity->isVisible() ) entity->draw();
795    if( unlikely( this->showBV)) entity->drawBVTree(3, 226);  // to draw the bounding boxes of the objects at level 2 for debug purp
796    entity = iterator->nextElement();
797  }
798  delete iterator;
799
800  glCallList (objectList);
801
802  ParticleEngine::getInstance()->draw();
803
804  if (unlikely(this->showPNodes))
805    NullParent::getInstance()->debugDraw(0);
806
807  GraphicsEngine::getInstance()->draw();
808  //TextEngine::getInstance()->draw();
809}
810
811/**
812 *  add and spawn a new entity to this world
813 * @param entity to be added
814*/
815void World::spawn(WorldEntity* entity)
816{
817  this->entities->add (entity);
818  entity->postSpawn ();
819}
820
821
822/**
823 *  add and spawn a new entity to this world
824 * @param entity to be added
825 * @param absCoor At what coordinates to add this entity.
826 * @param absDir In which direction should it look.
827*/
828void World::spawn(WorldEntity* entity, Vector* absCoor, Quaternion* absDir)
829{
830  this->entities->add (entity);
831
832  entity->setAbsCoor (*absCoor);
833  entity->setAbsDir (*absDir);
834
835  entity->postSpawn ();
836}
837
838
839/**
840 *  add and spawn a new entity to this world
841 * @param entity to be added
842 * @param entity to be added to (PNode)
843 * @param At what relative  coordinates to add this entity.
844 * @param In which relative direction should it look.
845*/
846void World::spawn(WorldEntity* entity, PNode* parentNode,
847                  Vector* relCoor, Quaternion* relDir)
848{
849  if( parentNode != NULL)
850    {
851      parentNode->addChild (entity);
852
853      entity->setRelCoor (*relCoor);
854      entity->setRelDir (*relDir);
855
856      this->entities->add (entity);
857
858      entity->postSpawn ();
859    }
860}
861
862void World::setPath( const char* name)
863{
864  if (this->path)
865    delete this->path;
866  if (ResourceManager::isFile(name))
867  {
868    this->path = new char[strlen(name)+1];
869    strcpy(this->path, name);
870  }
871  else
872    {
873      this->path = new char[strlen(ResourceManager::getInstance()->getDataDir()) + strlen(name) +1];
874      sprintf(this->path, "%s%s", ResourceManager::getInstance()->getDataDir(), name);
875    }
876}
877
878const char* World::getPath( void)
879{
880  return path;
881}
Note: See TracBrowser for help on using the repository browser.