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
--- 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) {
--- 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
--- 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;
--- 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 <iostream>
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) {
--- 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
--- 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<Player*>::iterator it = player_list.begin(); it != player_list.end(); it++) {
--- 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*> player_list;
+
+ /**
+ * All active projectiles
+ */
std::list<Projectile*> 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
--- 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(
--- 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
--- 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 <cassert>
-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) {
--- 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);
};
/**
--- 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;
--- 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);
--- 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 {
--- 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 <functional>
-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();
}
--- 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<PhysicsObject*> objects;
-protected:
- std::list<PhysicsObject*> 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.
--- 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(
--- 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<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
--- 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;
--- 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;
--- 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<std::vector<int> > 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