# HG changeset patch # User Tero Marttila # Date 1232487018 -7200 # Node ID e6cfc44266af2d49f9482bd9269450f6c3b83e7e # Parent 443f6f7abcfb9acf575d9c9d6d5680567092fdaf reorganize Terrain/PhysicsWorld/GameState/Engine to use NetworkClientConnect, and hence handle the connection process asynchronously, and finally properly implement receiving the terrain data from the server diff -r 443f6f7abcfb -r e6cfc44266af src/Application.cc --- a/src/Application.cc Tue Jan 20 23:24:04 2009 +0200 +++ b/src/Application.cc Tue Jan 20 23:30:18 2009 +0200 @@ -56,8 +56,8 @@ arg_port = NETWORK_PORT_STR; arg_server = false; arg_connect = ""; - arg_fullscreen = GRAPHICS_FULLSCREEN; - arg_resolution = PixelCoordinate(GRAPHICS_RESOLUTION_WIDTH, GRAPHICS_RESOLUTION_HEIGHT); + graphics.fullscreen = GRAPHICS_FULLSCREEN; + graphics.resolution = PixelCoordinate(GRAPHICS_RESOLUTION_WIDTH, GRAPHICS_RESOLUTION_HEIGHT); // extra state bool resolution_default = true; @@ -93,14 +93,14 @@ break; case ARG_FULLSCREEN: - arg_fullscreen = true; + graphics.fullscreen = true; // choose best resolution unless explicitly set if (resolution_default) { const CL_DisplayMode best_mode = Graphics::getBestMode(); const CL_Size best_resolution = best_mode.get_resolution(); - arg_resolution = PixelCoordinate(best_resolution.width, best_resolution.height); + graphics.resolution = PixelCoordinate(best_resolution.width, best_resolution.height); } break; @@ -147,7 +147,7 @@ throw ArgumentError("invalid format for --resolution"); // store as PixelCoordinate - arg_resolution = PixelCoordinate(w, h); + graphics.resolution = PixelCoordinate(w, h); } void Main::dump_display_modes (void) { @@ -183,7 +183,7 @@ // setup graphics if (arg_graphics) - engine.setupGraphics(arg_resolution, arg_fullscreen); + engine.setupGraphics(graphics); // setup either network server, client or singleplayer if (arg_server) { diff -r 443f6f7abcfb -r e6cfc44266af src/Application.hh --- a/src/Application.hh Tue Jan 20 23:24:04 2009 +0200 +++ b/src/Application.hh Tue Jan 20 23:30:18 2009 +0200 @@ -41,14 +41,9 @@ std::string arg_connect; /** - * --fullscreen + * --fullscreen and --resolution */ - bool arg_fullscreen; - - /** - * --resolution - */ - PixelCoordinate arg_resolution; + GraphicsConfiguration graphics; /** * Set the arg_* members diff -r 443f6f7abcfb -r e6cfc44266af src/Config.hh --- a/src/Config.hh Tue Jan 20 23:24:04 2009 +0200 +++ b/src/Config.hh Tue Jan 20 23:30:18 2009 +0200 @@ -16,11 +16,16 @@ */ const float KG_PI = 3.14159265; -// Physics simulation -// Physics resolution -const uint16_t MAP_WIDTH = 1000; -const uint16_t MAP_HEIGHT = 800; -const float MAP_SCALE = 1; // One "pixel" in "real" units +/** + * Random generator seed to use for terrain generator + */ +const int TERRAIN_RANDOM_SEED = 1337; + +/** + * Terrain size, equal to physics simulation size + */ +const PixelDimension TERRAIN_WIDTH = 1000; +const PixelDimension TERRAIN_HEIGHT = 800; /** Engine timeout, this determines our minimum tick rate */ const TimeMS ENGINE_TIMEOUT_MS = 10; diff -r 443f6f7abcfb -r e6cfc44266af src/Engine.cc --- a/src/Engine.cc Tue Jan 20 23:24:04 2009 +0200 +++ b/src/Engine.cc Tue Jan 20 23:30:18 2009 +0200 @@ -7,37 +7,78 @@ #include Engine::Engine (const std::string resource_xml_path) : + terrain(NULL), game_state(NULL), graphics_config(NULL), graphics(NULL), net_server(NULL), net_client_connect(NULL), is_running(true), resources(resource_xml_path) { } -void Engine::setupGraphics (PixelCoordinate resolution, bool fullscreen) { +GameState& Engine::setupGame (Terrain *terrain) { + // ensure this isn't called in inappropriate ways + assert(!net_server); + + // remember the terrain + this->terrain = terrain; + + // create the GameState + game_state = new GameState(*terrain); + + // start graphics? + if (graphics_config) + startGraphics(); + + return *game_state; +} + +GameState& Engine::setupGame (void) { + // proxy off to setupGame(Terrain *) + return setupGame(new Terrain(TERRAIN_WIDTH, TERRAIN_HEIGHT, TERRAIN_RANDOM_SEED)); +} + +void Engine::setupGraphics (const GraphicsConfiguration &config) { + // store config + graphics_config = &config; + + // start already? + if (game_state) + startGraphics(); +} + +void Engine::startGraphics (void) { + // check state + assert(game_state && graphics_config); + // create the graphics - graphics = new Graphics(*this, game_state, resolution, fullscreen); + graphics = new Graphics(*this, *game_state, *graphics_config); } void Engine::setupNetworkServer (const std::string &listen_port) { NetworkEndpoint listen_addr(listen_port); + + // setup default game + setupGame(); // create the server - net_server = new NetworkServer(game_state, listen_addr); + net_server = new NetworkServer(*game_state, listen_addr); } void Engine::setupNetworkClient (const std::string &connect_host, const std::string &connect_port) { // connect_to NetworkEndpoint connect_addr(connect_host, connect_port); - // create the client - net_client = new NetworkClient(*this, game_state, connect_addr); + // begin connecting, the client will callback us with setupGame once it's connected + net_client_connect = new NetworkClientConnect(*this, connect_addr); } void Engine::setupSinglePlayer (void) { + // setup default game + setupGame(); + // create player directly - LocalPlayer* lp = new SinglePlayer(game_state); + LocalPlayer* lp = new SinglePlayer(*game_state); // add to gamestate - game_state.setLocalPlayer(lp); + game_state->setLocalPlayer(lp); } void Engine::stop (void) { diff -r 443f6f7abcfb -r e6cfc44266af src/Engine.hh --- a/src/Engine.hh Tue Jan 20 23:24:04 2009 +0200 +++ b/src/Engine.hh Tue Jan 20 23:30:18 2009 +0200 @@ -17,14 +17,19 @@ class Engine { private: // game state - GameState game_state; + Terrain *terrain; + GameState *game_state; + + /** Set if setupGraphics has been called */ + const GraphicsConfiguration *graphics_config; // Graphics/Input Graphics *graphics; // network server/client NetworkServer *net_server; - NetworkClient *net_client; + NetworkClientConnect *net_client_connect; + // XXX: currently unused: NetworkClient *net_client; // to exit the mainloop bool is_running; @@ -36,8 +41,18 @@ // default constructor Engine (const std::string resource_xml_path = RESOURCE_XML_PATH); + /** + * Setup game world using the given terrain, returning the new GameState + */ + GameState& setupGame (Terrain *terrain); + + /** + * Setup default game world using constants from Config.hh + */ + GameState& setupGame (void); + // setup graphics - void setupGraphics (PixelCoordinate resolution, bool fullscreen); + void setupGraphics (const GraphicsConfiguration &config); // set up network server/client // setting up both of these will lead to odd behaviour :) @@ -45,11 +60,21 @@ void setupNetworkClient (const std::string &connect_host, const std::string &connect_port); void setupSinglePlayer (void); - // run the main loop + /** + * Run the game main loop. This will not return until the game aborts due to an error, or someone calls stop(). + */ void run (void); + + /** + * Terminate the main loop, causing run() to return once this loop iteration is finished + */ + void stop (void); - // terminate the main loop - void stop (void); + private: + /** + * Actually start graphics, requires that game_state is now set + */ + void startGraphics (void); public: // get a pointer to our resource manager diff -r 443f6f7abcfb -r e6cfc44266af src/GameState.cc --- a/src/GameState.cc Tue Jan 20 23:24:04 2009 +0200 +++ b/src/GameState.cc Tue Jan 20 23:30:18 2009 +0200 @@ -3,8 +3,10 @@ #include "Engine.hh" #include "Config.hh" -GameState::GameState (void) : - world(Vector(0, MAP_GRAVITY), Vector(MAP_WIDTH, MAP_HEIGHT)), local_player(NULL), event_handler(NULL) +GameState::GameState (Terrain &terrain) : + world(Vector(0, MAP_GRAVITY), Vector(terrain.getWidth(), terrain.getHeight()), terrain), + terrain(terrain), + local_player(NULL), event_handler(NULL) { } @@ -49,8 +51,8 @@ } void GameState::draw(Graphics *g, PixelCoordinate camera, bool displayWeapon) { - // Draw world/terrain - world.draw(g, camera); + // Draw terrain + terrain.draw(g, camera); // Draw players for (std::list::iterator it = player_list.begin(); it != player_list.end(); it++) { diff -r 443f6f7abcfb -r e6cfc44266af src/GameState.hh --- a/src/GameState.hh Tue Jan 20 23:24:04 2009 +0200 +++ b/src/GameState.hh Tue Jan 20 23:30:18 2009 +0200 @@ -25,9 +25,25 @@ class GameState { public: + /** + * All active players + */ std::list player_list; + + /** + * All active projectiles + */ std::list projectiles; + + /** + * Our world simulation + */ PhysicsWorld world; + + /** + * Our terrain + */ + Terrain &terrain; /** * The one LocalPlayer that *we* control @@ -42,11 +58,11 @@ public: /** - * ... - * - * This should take some arguments + * Create the game world, using the given terrain + * + * @param terrain the world's terrain */ - GameState (void); + GameState (Terrain &terrain); /** * Set event handler, only one can be set diff -r 443f6f7abcfb -r e6cfc44266af src/Graphics.cc --- a/src/Graphics.cc Tue Jan 20 23:24:04 2009 +0200 +++ b/src/Graphics.cc Tue Jan 20 23:30:18 2009 +0200 @@ -16,11 +16,11 @@ ); } -Graphics::Graphics (Engine &engine, GameState &state, PixelCoordinate resolution, bool fullscreen) : - CL_DisplayWindow(GRAPHICS_WINDOW_TITLE, resolution.x, resolution.y, fullscreen, true), +Graphics::Graphics (Engine &engine, GameState &state, const GraphicsConfiguration &config) : + CL_DisplayWindow(GRAPHICS_WINDOW_TITLE, config.resolution.x, config.resolution.y, config.fullscreen, true), engine(engine), state(state), - resolution(resolution), + resolution(config.resolution), fullscreen_resolution(0, 0), window_resolution(0, 0), update_timer(GRAPHICS_UPDATE_INTERVAL_MS), input(get_ic()->get_keyboard()), @@ -42,7 +42,7 @@ state.setEventHandler(this); // set correct resolution - if (fullscreen) { + if (config.fullscreen) { fullscreen_resolution = resolution; } else { @@ -175,7 +175,7 @@ PixelCoordinate target = player->getCoordinate() - PixelCoordinate(resolution.x / 2, (resolution.y - 100) / 2); // ...but keep the world in view - PixelCoordinate max = state.world.getDimensions() - resolution + PixelCoordinate(0, 100); + PixelCoordinate max = state.terrain.getDimensions() - resolution + PixelCoordinate(0, 100); // ...by limiting the value to 0...max camera = PixelCoordinate( diff -r 443f6f7abcfb -r e6cfc44266af src/Graphics.hh --- a/src/Graphics.hh Tue Jan 20 23:24:04 2009 +0200 +++ b/src/Graphics.hh Tue Jan 20 23:30:18 2009 +0200 @@ -2,6 +2,18 @@ #define GRAPHICS_HH #include "GraphicsPointer.hh" +#include "Types.hh" + +/** + * Parameters used by Graphics + */ +struct GraphicsConfiguration { + /** Initial resolution to use */ + PixelCoordinate resolution; + + /* Use fullscreen mode at startup */ + bool fullscreen; +}; #include "GameState.hh" #include "Input.hh" @@ -75,7 +87,7 @@ /** * */ - Graphics (Engine &engine, GameState &state, PixelCoordinate resolution, bool fullscreen); + Graphics (Engine &engine, GameState &state, const GraphicsConfiguration &config); /** * Returns a CL_Font that can be used for drawing text diff -r 443f6f7abcfb -r e6cfc44266af src/Network/Client.cc --- a/src/Network/Client.cc Tue Jan 20 23:24:04 2009 +0200 +++ b/src/Network/Client.cc Tue Jan 20 23:30:18 2009 +0200 @@ -7,34 +7,39 @@ #include -NetworkClient::NetworkClient (Engine &engine, GameState &state, const NetworkEndpoint &connect_to) : - engine(engine), state(state), netsession(NETWORK_MAGIC_ID), server(netsession.connect(connect_to)), - controller(*this) +/* + * NetworkClientConnect + */ +NetworkClientConnect::NetworkClientConnect (Engine &engine, const NetworkEndpoint &connect_to) : + engine(engine), netsession(NETWORK_MAGIC_ID) { + // connect NetworkSession to get server node (this is still blocking) + server = netsession.connect(connect_to); + // connect slots - slots.connect(netsession.sig_chan_message(NETCHAN_TERRAIN_ARRAY), this, &NetworkClient::on_terrain_array); - slots.connect(server->sig_disconnected(), this, &NetworkClient::on_disconnected); + slots.connect(netsession.sig_chan_message(NETCHAN_TERRAIN_ARRAY), this, &NetworkClientConnect::on_terrain_array); + slots.connect(server->sig_disconnected(), this, &NetworkClientConnect::on_disconnected); + + // then we must wait for the terrain data } - -void NetworkClient::on_disconnected (void) { + +void NetworkClientConnect::on_disconnected (void) { Engine::log(ERROR, "client.on_disconnect") << "Disconnected from server"; engine.stop(); } -void NetworkClient::on_terrain_array (NetworkPacketInput &pkt, NetworkNode *node) { +void NetworkClientConnect::on_terrain_array (NetworkPacketInput &pkt, NetworkNode *node) { // ignore if not from server if (node != server) return; - - Terrain &terrain = state.world; - + // read map width/height PixelDimension map_w = pkt.read_uint32(); PixelDimension map_h = pkt.read_uint32(); // the terrain byte array size_t terrain_size = map_w * map_h; - uint8_t terrain_buf[map_w * map_h]; + TerrainPixel *terrain_buf = new TerrainPixel[map_w * map_h]; // read uncompressed terrain data size_t inflate_size = pkt.read_uncompressed(terrain_buf, terrain_size); @@ -43,12 +48,29 @@ if (inflate_size != terrain_size) throw Error("Corrupt terrain data"); - // XXX: rework access - if (terrain.getDimensions() != PixelCoordinate(map_w, map_h)) - throw Error("terrain is of the wrong size"); + // create the terrain object that we then use, and hand over terrain_buf to it + Terrain *terrain = new Terrain(map_w, map_h, terrain_buf); - // load into terrain - terrain.loadFromBuffer(terrain_buf); + // execute connectDone + connectDone(terrain); +} + +void NetworkClientConnect::connectDone (Terrain *terrain) { + // pass Terrain to engine to create game + GameState &gs = engine.setupGame(terrain); + + // create our new NetworkClient object + client = new NetworkClient(engine, gs, netsession, server); +} + +/* + * NetworkClient + */ +NetworkClient::NetworkClient (Engine &engine, GameState &state, NetworkSession &netsession, NetworkNode *server) : + engine(engine), state(state), netsession(netsession), server(server), + controller(*this) +{ + } void NetworkClient::player_quit (NetworkClientRemotePlayer *player) { diff -r 443f6f7abcfb -r e6cfc44266af src/Network/Client.hh --- a/src/Network/Client.hh Tue Jan 20 23:24:04 2009 +0200 +++ b/src/Network/Client.hh Tue Jan 20 23:30:18 2009 +0200 @@ -8,6 +8,7 @@ */ // forward-declare +class NetworkClientConnect; class NetworkClient; class NetworkClientLocalPlayer; class NetworkClientRemotePlayer; @@ -27,7 +28,7 @@ * The NetworkClient */ NetworkClient &client; - + public: /** * Control objects on the given client using the client.netsession's NETCHAN_CORE channel @@ -86,12 +87,10 @@ */ GameState &state; - CL_SlotContainer slots; - /** * The connect()-mode NetworkSession */ - NetworkSession netsession; + NetworkSession &netsession; /** * The server NetworkNode from Netsession::connect @@ -103,6 +102,8 @@ */ NetworkClientController controller; + CL_SlotContainer slots; + public: /** * Create a NetworkClient with the given GameState, connecting a server on the given NetworkEndpoint @@ -111,7 +112,48 @@ * @param state the GameState to use * @param connect_to the address to connect to */ - NetworkClient (Engine &engine, GameState &state, const NetworkEndpoint &connect_to); + NetworkClient (Engine &engine, GameState &state, NetworkSession &netsession, NetworkNode *server); + + public: + /** + * Called by NetworkClientRemotePlayer when they get disconnected. Doesn't do anything currently + */ + void player_quit (NetworkClientRemotePlayer *player); +}; + +/** + * This handles the actual connection process to the server, and handles the initial data from the server. Once the + * Engine has a game running, we can create the actual NetworkClient object. + */ +class NetworkClientConnect { + protected: + /** + * The Engine we are running under + */ + Engine &engine; + + /** + * The NetworkSession we are using + */ + NetworkSession netsession; + + /** + * The server we've connected to + */ + NetworkNode *server; + + /** + * The NetworkClient that we eventually create + */ + NetworkClient *client; + + CL_SlotContainer slots; + + public: + /** + * Begin the connection process. Once it is complete, we will callback to Engine::networkClientConnected + */ + NetworkClientConnect (Engine &engine, const NetworkEndpoint &connect_to); protected: /** @@ -123,12 +165,11 @@ * Receive the NETCHAN_TERRAIN_ARRAY message from the server and apply it to our GameState::world terrain */ void on_terrain_array (NetworkPacketInput &pkt, NetworkNode *node); - - public: + /** - * Called by NetworkClientRemotePlayer when they get disconnected. Doesn't do anything currently + * Finished connecting */ - void player_quit (NetworkClientRemotePlayer *player); + void connectDone (Terrain *terrain); }; /** diff -r 443f6f7abcfb -r e6cfc44266af src/Network/Server.cc --- a/src/Network/Server.cc Tue Jan 20 23:24:04 2009 +0200 +++ b/src/Network/Server.cc Tue Jan 20 23:30:18 2009 +0200 @@ -20,13 +20,56 @@ } void NetworkServer::on_node_connected (NetworkNode *node) { + // send the terrain data + send_terrain_data(node); + // create the player object (it logs it) NetworkServerPlayer *player = new NetworkServerPlayer(*this, node); // add to players players.push_back(player); + } - + +void NetworkServer::send_terrain_data (NetworkNode *node) { + Terrain &terrain = state.world.terrain; + + // dimensions? + PixelCoordinate map = terrain.getDimensions(); + + // translate to a byte array + size_t terrain_size = map.x * map.y; + + // get terrain buffer + const uint8_t *terrain_buf = terrain.getTerrainBuffer(); + + // allocate our packet... + BigNetworkPacket pkt ( + // NetworkChannel header + NETWORK_SESSION_HEADER_SIZE + + // our own header + + 2 * sizeof(uint32_t) + + // compressed terrain buffer + + NetworkPacketOutput::write_compressed_size(terrain_size) + ); + + // write netsession header + node->write_packet_header(pkt, NETCHAN_TERRAIN_ARRAY); + + // write terrain dimensions + pkt.write_uint32(map.x); + pkt.write_uint32(map.y); + + // write compressed terrain data + pkt.write_compressed(terrain_buf, terrain_size); + + // send + node->send_raw(pkt, true); +} + + void NetworkServer::handle_disconnect (NetworkServerPlayer *player) { // remove from list players.remove(player); @@ -80,9 +123,6 @@ // broadcast NETMSG_PLAYER_JOIN to all clients except current this->send_all_except(NETMSG_PLAYER_JOIN, hello_pkt, node, true); - - // send terrain data... - send_terrain_data(); } void NetworkServerPlayer::handleDig (Vector position, float radius) { @@ -232,44 +272,6 @@ send_position_update(); } -void NetworkServerPlayer::send_terrain_data (void) { - Terrain &terrain = server.state.world; - - // dimensions? - PixelCoordinate map = terrain.getDimensions(); - - // translate to a byte array - size_t terrain_size = map.x * map.y; - - // get terrain buffer - const uint8_t *terrain_buf = terrain.getTerrainBuffer(); - - // allocate our packet... - BigNetworkPacket pkt ( - // NetworkChannel header - NETWORK_SESSION_HEADER_SIZE - - // our own header - + 2 * sizeof(uint32_t) - - // compressed terrain buffer - + NetworkPacketOutput::write_compressed_size(terrain_size) - ); - - // write netsession header - node->write_packet_header(pkt, NETCHAN_TERRAIN_ARRAY); - - // write terrain dimensions - pkt.write_uint32(map.x); - pkt.write_uint32(map.y); - - // write compressed terrain data - pkt.write_compressed(terrain_buf, terrain_size); - - // send - node->send_raw(pkt, true); -} - void NetworkServerPlayer::send_position_update (void) { NetworkPacket pkt; diff -r 443f6f7abcfb -r e6cfc44266af src/Network/Server.hh --- a/src/Network/Server.hh Tue Jan 20 23:24:04 2009 +0200 +++ b/src/Network/Server.hh Tue Jan 20 23:30:18 2009 +0200 @@ -67,6 +67,12 @@ * our list of players */ void on_node_connected (NetworkNode *node); + + /** + * Called from on_node_connected to send the initial Terrain data using the NETCHAN_TERRAIN_ARRAY channel to + * the given node. + */ + void send_terrain_data (NetworkNode *node); }; /** @@ -139,11 +145,6 @@ void on_input (NetworkNode *node, NetworkPacketInput &pkt); /** - * Called from the constructor to send the initial Terrain data using the NETCHAN_TERRAIN_ARRAY channel. - */ - void send_terrain_data (void); - - /** * Called from on_input to broadcast an unreliable position update with this player's physics state */ void send_position_update (void); diff -r 443f6f7abcfb -r e6cfc44266af src/PhysicsObject.cc --- a/src/PhysicsObject.cc Tue Jan 20 23:24:04 2009 +0200 +++ b/src/PhysicsObject.cc Tue Jan 20 23:30:18 2009 +0200 @@ -115,8 +115,8 @@ } bool PhysicsObject::possibleLocation (Vector loc) { - for(unsigned int i = 0; i < this->shape.size(); i++) { - if(world.collides(loc+shape[i])) + for (unsigned int i = 0; i < this->shape.size(); i++) { + if (world.terrain.collides(loc+shape[i])) return false; } return true; @@ -156,9 +156,9 @@ // If the player has stopped and there's some ground under some of the 3 some of the 3t // set inAir false if (this->velocity == Vector(0,0)) { - this->inAir = !world.collides(this->position+shape[1]+Vector(0, 1)) - && !world.collides(this->position+shape[2]+Vector(0, 1)) - && !world.collides(this->position+shape[3]+Vector(0, 1)); + this->inAir = !world.terrain.collides(this->position+shape[1]+Vector(0, 1)) + && !world.terrain.collides(this->position+shape[2]+Vector(0, 1)) + && !world.terrain.collides(this->position+shape[3]+Vector(0, 1)); // If, however, there's a force caused by a bomb, e.g., set it in air. // Still, we have to be able to separate forces caused by walking attempts // and bombs etc (+0.1 because float comparison can be dangerous) @@ -209,12 +209,12 @@ reached += unitVector; // Check if any of the shapes points collide for (uint64_t i = 0; i < shape.size(); i++) { - if (world.collides(reached+shape[i])) { // Collision + if (world.terrain.collides(reached+shape[i])) { // Collision collisionPoint = reached+shape[i]; if (inAir) - this->bounce(world.getNormal(reached + shape[i], reached - unitVector + shape[i])); + this->bounce(world.terrain.getNormal(reached + shape[i], reached - unitVector + shape[i])); reached = reached - unitVector; // Return to last point collided = true; @@ -355,7 +355,7 @@ } PixelCoordinate PhysicsObject::getCoordinate (void) const { - return world.getPixelCoordinate(position); + return world.terrain.getPixelCoordinate(position); } Vector PhysicsObject::getVelocity (void) const { diff -r 443f6f7abcfb -r e6cfc44266af src/PhysicsWorld.cc --- a/src/PhysicsWorld.cc Tue Jan 20 23:24:04 2009 +0200 +++ b/src/PhysicsWorld.cc Tue Jan 20 23:30:18 2009 +0200 @@ -4,13 +4,13 @@ #include -PhysicsWorld::PhysicsWorld (Vector gravity, Vector dimensions) : - // XXX: assume Vector == PixelCoordinate - Terrain((unsigned int) dimensions.x, (unsigned int) dimensions.y, 1337), +PhysicsWorld::PhysicsWorld (Vector gravity, Vector dimensions, Terrain &terrain) : + terrain(terrain), dimensions(dimensions), gravity(gravity), tick_timer(PHYSICS_TICK_MS) { + // wire up our timer slots.connect(tick_timer.sig_tick(), this, &PhysicsWorld::tick); tick_timer.start(); } diff -r 443f6f7abcfb -r e6cfc44266af src/PhysicsWorld.hh --- a/src/PhysicsWorld.hh Tue Jan 20 23:24:04 2009 +0200 +++ b/src/PhysicsWorld.hh Tue Jan 20 23:30:18 2009 +0200 @@ -18,36 +18,44 @@ #include "Config.hh" /** -* PhysicsWorld class. PhysicsWorld contains PhysicsObjects that are -* simulated in the PhysicsWorld. +* PhysicsWorld class. PhysicsWorld contains a number of PhysicsObjects that are simulated forward in time using +* the physics tick rate */ -class PhysicsWorld : public Terrain { +class PhysicsWorld { friend class PhysicsObject; -private: +// XXX: needs some fixing up, move Terrain methods +public: + /** The world's terrain */ + Terrain &terrain; +protected: + /** List of simulated objects*/ + std::list objects; -protected: - std::list objects; + /** Size of simulation area */ + Vector dimensions; - // Contains connections between signals and slots + /** Gravity vector */ + Vector gravity; + + /** Physics simulation ticks */ + Timer tick_timer; + CL_SlotContainer slots; - Vector dimensions; - Vector gravity; - public: - // Someone is going to kill me for this - Timer tick_timer; - - PhysicsWorld(Vector gravity, Vector dimensions); + /** + * Construct a world with the given configuration. + */ + PhysicsWorld (Vector gravity, Vector dimensions, Terrain &terrain); /** * Add object to the PhysicsWorld. * * @param object Pointer to the PhysicsObject to add. */ - void addPhysicsObject(PhysicsObject *object); + void addPhysicsObject (PhysicsObject *object); /** * Remove the object from the PhysicsWorld. diff -r 443f6f7abcfb -r e6cfc44266af src/Player.cc --- a/src/Player.cc Tue Jan 20 23:24:04 2009 +0200 +++ b/src/Player.cc Tue Jan 20 23:30:18 2009 +0200 @@ -63,7 +63,7 @@ void Player::spawn (Vector position) { // dig hole - world.removeGround(position, PLAYER_DIG_RADIUS); + world.terrain.removeGround(position, PLAYER_DIG_RADIUS); // update position setPosition(position); @@ -108,7 +108,7 @@ Vector digPosition = pos + getDirection() * PROJECTILE_START_DISTANCE; // remove directly - world.removeGround(digPosition, radius); + world.terrain.removeGround(digPosition, radius); } void Player::handleFireWeapon (Weapon *weapon, Vector position, Vector velocity) { @@ -363,7 +363,7 @@ ); // draw a proper crosshair-box - PixelCoordinate crosshair = getCoordinate() + world.getPixelCoordinate(getDirection() * PLAYER_CROSSHAIR_DISTANCE) - camera; + PixelCoordinate crosshair = getCoordinate() + world.terrain.getPixelCoordinate(getDirection() * PLAYER_CROSSHAIR_DISTANCE) - camera; gc->draw_rect( CL_Rectf( diff -r 443f6f7abcfb -r e6cfc44266af src/Projectile.cc --- a/src/Projectile.cc Tue Jan 20 23:24:04 2009 +0200 +++ b/src/Projectile.cc Tue Jan 20 23:30:18 2009 +0200 @@ -10,7 +10,7 @@ weapon(weapon) { // set birth tick - birth_tick = world.tick_timer.get_ticks(); + birth_tick = world.getTicks(); // XXX: projectiles should be particles? std::vector shape(4); @@ -32,7 +32,7 @@ void Projectile::onDestroy (Vector position, bool removeGround) { if (removeGround) - world.removeGround(position, weapon->getExplosionRadius()); + world.terrain.removeGround(position, weapon->getExplosionRadius()); destroy(); } @@ -68,7 +68,7 @@ void Projectile::tick (TimeMS dt) { // expire projectiles - if (world.tick_timer.get_ticks() > birth_tick + weapon->getExpire()) + if (world.getTicks() > birth_tick + weapon->getExpire()) onDestroy(position, true); // super diff -r 443f6f7abcfb -r e6cfc44266af src/Rope.cc --- a/src/Rope.cc Tue Jan 20 23:24:04 2009 +0200 +++ b/src/Rope.cc Tue Jan 20 23:30:18 2009 +0200 @@ -193,7 +193,7 @@ return; // If there's no ground on the pivot point anymore, release the rope - if (!world.collides(position)) { + if (!world.terrain.collides(position)) { // XXX: move to some new method state = ROPE_FLYING; length = ROPE_LENGTH; diff -r 443f6f7abcfb -r e6cfc44266af src/Terrain.cc --- a/src/Terrain.cc Tue Jan 20 23:24:04 2009 +0200 +++ b/src/Terrain.cc Tue Jan 20 23:30:18 2009 +0200 @@ -25,13 +25,20 @@ Terrain::Terrain (PixelDimension width, PixelDimension height, int seed) : terrain_buf(NULL), - width(width), - height(height) + width(width), height(height) { // allocate+generate random terrain generateTerrain(seed); } +Terrain::Terrain (PixelDimension width, PixelDimension height, TerrainPixel *terrain_buf) : + terrain_buf(terrain_buf), + width(width), height(height) +{ + // just generate the pixel buffer + generatePixelBuffer(); +} + Terrain::~Terrain (void) { // free terrain data delete[] terrain_buf; @@ -346,8 +353,9 @@ } /** - * Gets the index of the given coordinate direction - * referring to the DIRECTIONS table in Physics.hh + * Gets the index of the given coordinate direction referring to the DIRECTIONS table in Physics.hh + * + * XXX: ugly little "lookup table" */ static int getDirectionIndex (Vector direction) { Vector dir = direction.roundToInt(); @@ -375,32 +383,35 @@ } Vector Terrain::getNormal(Vector point, Vector prevPoint) const { - // XXX: cleanup + // convert location to coordinate PixelCoordinate p = getPixelCoordinate(point); - + + // sanity check assert(point != prevPoint); - - Vector normal(0, 0); - - // These two must be rounded separately + + // round and subtract to get an integer direction vector, and turn this into a direction index int dirIdx = getDirectionIndex(prevPoint.roundToInt() - point.roundToInt()); - - normal += DIRECTIONS[dirIdx]; - + + // always add our own direction to normal + Vector normal = DIRECTIONS[dirIdx]; + + // check the two pixels clockwise from the impact direction for (int i = 1; i <= 2; i++) { if (getType(point + DIRECTIONS[(dirIdx + i + 8) % 8]) == TERRAIN_EMPTY) { normal += DIRECTIONS[(dirIdx + i + 8) % 8]; } } - + + // check the two pixels counterclockwise from the impact direction for (int i = 1; i <= 2; i++) { if (getType(point + DIRECTIONS[(dirIdx - i + 8) % 8]) == TERRAIN_EMPTY) { normal += DIRECTIONS[(dirIdx - i + 8) % 8]; } } - + + // sanity check if (getType(point) == TERRAIN_EMPTY || getType(prevPoint) != TERRAIN_EMPTY) { - Engine::log(DEBUG, "Physics.getNormal ") << "logic ground error"; + Engine::log(DEBUG, "Physics.getNormal ") << "silly collision"; } return normal; diff -r 443f6f7abcfb -r e6cfc44266af src/Terrain.hh --- a/src/Terrain.hh Tue Jan 20 23:24:04 2009 +0200 +++ b/src/Terrain.hh Tue Jan 20 23:30:18 2009 +0200 @@ -39,7 +39,7 @@ */ class Terrain { protected: - /** The terrain data is stored as a linear array in row-major order, with width*height elements */ + /** The terrain data is stored as a linear array in row-major order, with width * height elements */ TerrainPixel *terrain_buf; /** Terrain dimensions */ @@ -51,22 +51,35 @@ // XXX: terrain texture std::vector > texture; +public: /** * Default constructor. The width/height are set to zero and the terrain is invalid until it gets updated */ Terrain (void); /** - * Constructor. + * Construct a randomly generated terrain * * @param width terrain width * @param height terrain height - * @param seed andom number generator seed used to generate the random terrain. + * @param seed random number generator seed used to generate the random terrain. */ Terrain (PixelDimension width, PixelDimension height, int seed); /** - * Destructor + * Construct the terrain using the provided terrain data. The given \a terrain_buf must be a linear array in the + * same format as Terrain::terrain_buf, meaning a row-major order array with width * height elements. The buffer + * must be allocated on the heap using 'new []', and ownership will be transferred (i.e. the buffer is not copied, + * and will eventually be delete []'d). + * + * @param width terrain width + * @param height terrain height + * @param terrain_buf dynamically allocated width * height array of terrain data + */ + Terrain (PixelDimension width, PixelDimension height, TerrainPixel *terrain_buf); + + /** + * Destructor, frees our terrain buffer */ ~Terrain (void); @@ -135,11 +148,22 @@ /** * Return the terrain dimensions à la a PixelCoordinate */ - inline PixelCoordinate getDimensions (void) const { + PixelCoordinate getDimensions (void) const { return PixelCoordinate(width, height); } /** + * Return dimensions in component form + */ + PixelDimension getWidth (void) const { + return width; + } + + PixelDimension getHeight (void) const { + return height; + } + + /** * Return the type of terrain at given position. Returns TERRAIN_ROCK if given point is not inside terrain area. * * @param x terrain x coordinate