/* 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 () { entities = new List(); } /** \brief remove the World from memory */ World::~World () { unload (); delete entities; } /** \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; 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 loads a simple level for testing purposes */ void World::load_debug_level() { // create some path nodes pathnodes = new Vector[6]; pathnodes[0] = Vector(0, 0, 0); pathnodes[1] = Vector(-100, 40, 0); pathnodes[2] = Vector(-100, 140, 0); pathnodes[3] = Vector(0, 180, 0); pathnodes[4] = Vector(100, 140, 0); pathnodes[5] = Vector(100, 40, 0); // create the tracks tracklen = 6; track = new Track*[6]; for( int i = 0; i < tracklen; i++) { track[i] = new Track( i, (i+1)%tracklen, &pathnodes[i], &pathnodes[(i+1)%tracklen]); } // create a player WorldEntity* myPlayer = (WorldEntity*) spawn(); // bind input Orxonox *orx = Orxonox::getInstance(); orx->get_localinput()->bind (myPlayer); // bind camera orx->get_camera()->bind (myPlayer); } /** \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); } /** \brief sends a damage message to all WorldEntities in the specified vicinity \param loc: the Location of the epicenter of the explosion \param dmg: the damage dealt \param r: the radius of the explosion \param instigator: a pointer to the entity that caused the explosion This is a rather expensive function to call, so I suggest you do not call this every tick when you want to create something that deals damage over time (use a countdown instead that calls this function every second or so). */ void World::explosion (Location* loc, Damage* dmg, float r, WorldEntity* instigator) { Placement mapped; track[loc->part]->map_coords (loc, &mapped); CollisionCluster boom( r, Vector(0,0,0)); unsigned long boomflags, hflags; List *a; WorldEntity *aobj; a = entities->get_next(); while( a != NULL) { aobj = a->get_object(); if( aobj->bCollide && aobj->collisioncluster != NULL) { hflags = 0; if( check_collision ( &aobj->place, aobj->collisioncluster, &hflags, &mapped, &boom, &boomflags)) { aobj->hit (dmg, instigator, hflags); } } a = a->get_next(); } } /** \brief sends a damage message to all WorldEntities in the specified vicinity \param plc: the Placement of the epicenter of the explosion \param dmg: the damage dealt \param r: the radius of the explosion \param instigator: a pointer to the entity that caused the explosion This is a rather expensive function to call, so I suggest you do not call this every tick when you want to create something that deals damage over time (use a countdown instead that calls this function every second or so). */ void World::explosion (Placement* plc, Damage* dmg, float r, WorldEntity* instigator) { CollisionCluster boom( r, Vector(0,0,0)); unsigned long boomflags, hflags; List *a; WorldEntity *aobj; a = entities->get_next(); while( a != NULL) { aobj = a->get_object(); if( aobj->bCollide && aobj->collisioncluster != NULL) { hflags = 0; if( check_collision ( &aobj->place, aobj->collisioncluster, &hflags, plc, &boom, &boomflags)) { aobj->hit (dmg, instigator, hflags); } } a = a->get_next(); } }