/* orxonox - the future of 3D-vertical-scrollers Copyright (C) 2004 orx This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. ### File Specific: main-programmer: Patrick Boenzli co-programmer: Christian Meyer */ #include "world.h" #include "world_entity.h" #include "collision.h" #include "track.h" #include "player.h" #include "command_node.h" #include "camera.h" using namespace std; /** \brief create a new World This creates a new empty world! */ World::World (char* name) { this->worldName = name; this->debugWorldNr = -1; this->entities = new List(); } World::World (int worldID) { this->debugWorldNr = worldID; this->worldName = NULL; this->entities = new List(); } /** \brief remove the World from memory */ World::~World () { Orxonox *orx = Orxonox::getInstance(); orx->get_localinput()->unbind (this->localPlayer); delete this->entities; delete this->localCamera; } /** \brief initialize the world before use. */ Error World::init() { this->bPause = false; } Error World::start() { this->mainLoop(); } Error World::stop() { this->bQuitCurrentGame = true; this->localCamera->setWorld(NULL); this->~World(); } Error World::pause() { this->isPaused = true; } Error World::resume() { this->isPaused = false; } void World::load() { if(this->debugWorldNr != -1) { switch(this->debugWorldNr) { case DEBUG_WORLD_0: { // create some path nodes this->pathnodes = new Vector[6]; this->pathnodes[0] = Vector(0, 0, 0); this->pathnodes[1] = Vector(-100, 40, 0); this->pathnodes[2] = Vector(-100, 140, 0); this->pathnodes[3] = Vector(0, 180, 0); this->pathnodes[4] = Vector(100, 140, 0); this->pathnodes[5] = Vector(100, 40, 0); // create the tracks this->tracklen = 6; this->track = new Track[6]; for( int i = 0; i < this->tracklen; i++) { this->track[i] = Track( i, (i+1)%this->tracklen, &this->pathnodes[i], &this->pathnodes[(i+1)%this->tracklen]); } // create a player //WorldEntity* myPlayer = (WorldEntity*) this->spawn(); WorldEntity* myPlayer = new Player(); this->spawn(myPlayer); this->localPlayer = myPlayer; // bind input Orxonox *orx = Orxonox::getInstance(); orx->get_localinput()->bind (myPlayer); // bind camera this->localCamera = new Camera(this); this->getCamera()->bind (myPlayer); break; } case DEBUG_WORLD_1: { // create some path nodes this->pathnodes = new Vector[6]; this->pathnodes[0] = Vector(0, 0, 0); this->pathnodes[1] = Vector(20, 10, 10); this->pathnodes[2] = Vector(40, 0, 10); this->pathnodes[3] = Vector(60, 10, 0); this->pathnodes[4] = Vector(80, 20, 10); this->pathnodes[5] = Vector(30, 50, 0); // create the tracks this->tracklen = 6; this->track = new Track[6]; for( int i = 0; i < this->tracklen; i++) { this->track[i] = Track( i, (i+1)%this->tracklen, &this->pathnodes[i], &this->pathnodes[(i+1)%this->tracklen]); } // create a player //WorldEntity* myPlayer = (WorldEntity*) this->spawn(); WorldEntity* myPlayer = new Player(); this->spawn(myPlayer); this->localPlayer = myPlayer; // bind input Orxonox *orx = Orxonox::getInstance(); orx->get_localinput()->bind (myPlayer); // bind camera this->localCamera = new Camera(this); this->getCamera()->bind (myPlayer); break; } default: printf("World::load() - no world with ID %i found", this->debugWorldNr ); } } else if(this->worldName != NULL) { } } /** \brief checks for collisions This method runs through all WorldEntities known to the world and checks for collisions between them. In case of collisions the collide() method of the corresponding entities is called. */ void World::collide () { List *a, *b; WorldEntity *aobj, *bobj; a = entities->get_next(); while( a != NULL) { aobj = a->get_object(); if( aobj->bCollide && aobj->collisioncluster != NULL) { b = a->get_next(); while( b != NULL ) { bobj = b->get_object(); if( bobj->bCollide && bobj->collisioncluster != NULL ) { unsigned long ahitflg, bhitflg; if( check_collision ( &aobj->place, aobj->collisioncluster, &ahitflg, &bobj->place, bobj->collisioncluster, &bhitflg) ); { aobj->collide (bobj, ahitflg, bhitflg); bobj->collide (aobj, bhitflg, ahitflg); } } b = b->get_next(); } } a = a->get_next(); } } /** \brief runs through all entities calling their draw() methods */ void World::draw () { // draw geometry // draw entities List *l; WorldEntity* entity; l = entities->get_next(); while( l != NULL ) { entity = l->get_object(); if( entity->bDraw ) entity->draw(); l = l->get_next(); } // draw debug coord system glLoadIdentity(); glBegin(GL_LINES); for( float x = -128.0; x < 128.0; x += 25.0) { for( float y = -128.0; y < 128.0; y += 25.0) { glColor3f(1,0,0); glVertex3f(x,y,-128.0); glVertex3f(x,y,0.0); glColor3f(0.5,0,0); glVertex3f(x,y,0.0); glVertex3f(x,y,128.0); } } for( float y = -128.0; y < 128.0; y += 25.0) { for( float z = -128.0; z < 128.0; z += 25.0) { glColor3f(0,1,0); glVertex3f(-128.0,y,z); glVertex3f(0.0,y,z); glColor3f(0,0.5,0); glVertex3f(0.0,y,z); glVertex3f(128.0,y,z); } } for( float x = -128.0; x < 128.0; x += 25.0) { for( float z = -128.0; z < 128.0; z += 25.0) { glColor3f(0,0,1); glVertex3f(x,-128.0,z); glVertex3f(x,0.0,z); glColor3f(0,0,0.5); glVertex3f(x,0.0,z); glVertex3f(x,128.0,z); } } //draw track glColor3f(0,1,1); for( int i = 0; i < tracklen; i++) { glVertex3f(pathnodes[i].x,pathnodes[i].y,pathnodes[i].z); glVertex3f(pathnodes[(i+1)%tracklen].x,pathnodes[(i+1)%tracklen].y,pathnodes[(i+1)%tracklen].z); } glEnd(); } /** \brief updates Placements and notifies entities when they left the world This runs trough all WorldEntities and maps Locations to Placements if they are bound, checks whether they left the level boundaries and calls appropriate functions. */ void World::update () { List *l; WorldEntity* entity; Location* loc; Placement* plc; Uint32 t; l = entities->get_next(); while( l != NULL ) { entity = l->get_object(); if( !entity->isFree() ) { loc = entity->get_location(); plc = entity->get_placement(); t = loc->part; /* check if entity has still a legal track-id */ if( t >= tracklen ) { printf("An entity is out of the game area\n"); entity->left_world (); } else { while( track[t].map_coords( loc, plc) ) { track[t].post_leave (entity); if( loc->part >= tracklen ) { printf("An entity has left the game area\n"); entity->left_world (); break; } track[loc->part].post_enter (entity); } } } else { /* TO DO: implement check whether this particular free entity is out of the game area TO DO: call function to notify the entity that it left the game area */ } l = l->get_next(); } } /** \brief relays the passed time since the last frame to entities and Track parts \param deltaT: the time passed since the last frame in milliseconds */ void World::time_slice (Uint32 deltaT) { List *l; WorldEntity* entity; float seconds = deltaT; seconds /= 1000; l = entities->get_next(); while( l != NULL) { entity = l->get_object(); entity->tick (seconds); l = l->get_next(); } for( int i = 0; i < tracklen; i++) track[i].tick (seconds); } /** \brief removes level data from memory */ void World::unload() { if( pathnodes) delete []pathnodes; if( track) delete []pathnodes; } /** \brief calls the correct mapping function to convert a given "look at"-Location to a Camera Placement */ void World::calc_camera_pos (Location* loc, Placement* plc) { track[loc->part].map_camera (loc, plc); } void World::setTrackLen(Uint32 len) { this->tracklen = len; } int World::getTrackLen() { return this->tracklen; } void World::debug() { List *l; WorldEntity* entity; printf("counting all entities\n"); l = entities->get_next(); while( l != NULL ) { entity = l->get_object(); if( entity->bDraw ) printf("got an entity\n"); l = l->get_next(); } } void World::mainLoop() { this->lastFrame = SDL_GetTicks(); this->bQuitOrxonox = false; this->bQuitCurrentGame = false; printf("World|Entering main loop\n"); while(!this->bQuitOrxonox && !this->bQuitCurrentGame) /* pause pause pause ?!?!?*/ { //debug routine //debug(); // Network synchronize(); // Process input handle_input(); // Process time time_slice(); // Process collision collision(); // Draw display(); } printf("World|Exiting the main loop\n"); } /** \brief synchronize local data with remote data */ void World::synchronize () { // Get remote input // Update synchronizables } /** \brief run all input processing */ void World::handle_input () { // localinput Orxonox::getInstance()->get_localinput()->process(); // remoteinput } /** \brief advance the timeline */ void World::time_slice () { Uint32 currentFrame = SDL_GetTicks(); if(!this->bPause) { Uint32 dt = currentFrame - this->lastFrame; /* if(dt > 0) { float fps = 1000/dt; printf("fps = %f\n", fps); } else { printf("fps = 1000\n"); } */ this->time_slice (dt); this->update (); this->localCamera->time_slice (dt); } this->lastFrame = currentFrame; } /** \brief compute collision detection */ void World::collision () { this->collide (); } /** \brief handle keyboard commands that are not meant for WorldEntities \param cmd: the command to handle \return true if the command was handled by the system or false if it may be passed to the WorldEntities */ bool World::system_command (Command* cmd) { if( !strcmp( cmd->cmd, "quit")) { if( !cmd->bUp) this->bQuitOrxonox = true; return true; } return false; } /** \brief render the current frame */ void World::display () { // clear buffer glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); // set camera this->localCamera->apply (); // draw world this->draw(); // draw HUD // flip buffers SDL_GL_SwapBuffers(); } Camera* World::getCamera() { return this->localCamera; } void World::spawn(WorldEntity* entity) { Location zeroloc; Location* loc = NULL; WorldEntity* owner; //T* entity = new T(); entities->add (entity, LIST_ADD_NEXT); //if( loc == NULL) //{ zeroloc.dist = 0; zeroloc.part = 0; zeroloc.pos = Vector(); zeroloc.rot = Quaternion(); loc = &zeroloc; //} entity->init (loc, owner); if (entity->bFree) { this->track[loc->part].map_coords( loc, entity->get_placement()); } entity->post_spawn (); //return entity; }