merge new_graphics back into default, it should work OK, and there's a lot of additional functionality already
authorTero Marttila <terom@fixme.fi>
Thu, 22 Jan 2009 03:02:43 +0200
changeset 421 b5b9d2aafdcb
parent 409 1a03ff151abc (current diff)
parent 420 278020dcd9b7 (diff)
child 422 5e4c8631779e
merge new_graphics back into default, it should work OK, and there's a lot of additional functionality already
src/GameMessageView.cc
src/GameMessageView.hh
src/Graphics.cc
src/Graphics.hh
src/GraphicsPointer.hh
src/Input.cc
src/Input.hh
src/Network/Config.hh
--- a/src/Application.cc	Wed Jan 21 00:21:42 2009 +0200
+++ b/src/Application.cc	Thu Jan 22 03:02:43 2009 +0200
@@ -1,5 +1,14 @@
 
 #include "Application.hh"
+#include "Config.hh"
+
+#if GRAPHICS_ENABLED
+    // for dump_display_modes
+    #include "Graphics/Display.hh"
+    
+    // for CL_SetupGL
+    #include <ClanLib/gl.h>
+#endif
 
 #include <stdexcept>
 #include <sstream>
@@ -19,6 +28,7 @@
     ARG_VERSION         = 0xff02,
     ARG_TERRAIN_SEED    = 0xff03,
     ARG_TERRAIN_SIZE    = 0xff04,
+    ARG_LOG_LEVEL       = 0xff05,
 
 };
 
@@ -36,42 +46,65 @@
  * Set the arg_* members
  */
 bool Main::parse_args (int argc, char **argv) {
-    // set up the options
-    args.add_option(ARG_HELP, "help", "",
-            "display argument help and exit");
-
-    args.add_option(ARG_PORT, "port", "PORT",
-            "set network port used");
-
-    args.add_option(ARG_SERVER, "server", "",
-            "act as a network server");
 
-    args.add_option(ARG_CLIENT, "client", "SERVERHOST",
-            "act as a network client");
-
-    args.add_option(ARG_GRAPHICS, "graphics", "",        
-            "run graphics/local input. Implied with --connect");
+    // set up the options
+    args.add_usage("[OPTIONS]");
+    args.set_help_indent(30);
+    
+    if (NETWORK_ENABLED) {
+        args.add_group("Network");
 
-    args.add_option(ARG_FULLSCREEN, "fullscreen", "",
-            "run graphics in fullscreen mode");
+        args.add_option(ARG_PORT, "port", "PORT",
+                "set network port used");
 
-    args.add_option(ARG_RESOLUTION, "resolution", "WIDTHxHEIGHT",
-            "set graphics resolution");
-    
-    args.add_option(ARG_LIST_MODES, "list-modes", "",
-            "output a list of available display modes and exit");
+        args.add_option(ARG_SERVER, "server", "",
+                "act as a network server");
+        
+        // require graphics for network-client
+        if (GRAPHICS_ENABLED)
+            args.add_option(ARG_CLIENT, "client", "SERVERHOST",
+                    "act as a network client");
+    }
 
+    if (GRAPHICS_ENABLED) {
+        args.add_group("Graphics");
+
+        args.add_option(ARG_GRAPHICS, "graphics", "",        
+                "run graphics/local input. Implied with --client");
+
+        args.add_option(ARG_FULLSCREEN, "fullscreen", "",
+                "run graphics in fullscreen mode");
+
+        args.add_option(ARG_RESOLUTION, "resolution", "WIDTHxHEIGHT",
+                "set graphics resolution");
+        
+        args.add_option(ARG_LIST_MODES, "list-modes", "",
+                "output a list of available display modes and exit");
+    }
+
+    args.add_group("Game");
     args.add_option(ARG_TERRAIN_SEED, "terrain-seed", "SEED",
             "set seed for terrain random generator");
 
     args.add_option(ARG_TERRAIN_SIZE, "terrain-size", "WIDTHxHEIGHT",
             "set terrain size for random generator");
 
+    
+    args.add_group("General");
+    args.add_option(ARG_LOG_LEVEL, "log-level", "LEVEL",
+            "set maximum log level to one of FATAL/ERROR/WARN/INFO/DEBUG");
+
+    args.add_option(ARG_HELP, "help", "",
+            "display argument help and exit");
+
     args.add_option(ARG_VERSION, "version", "",
-            "output application version and exit");
+            "output application version plus configuration and exit");
 
+#if GRAPHICS_ENABLED    
     // extra state
     bool resolution_default = true;
+
+#endif    
     
     try {
         // parse args
@@ -87,6 +120,11 @@
                 args.print_help();
                 return false;
 
+            case ARG_LOG_LEVEL:
+                engine.log_level = parse_arg_loglevel(args.get_argument(), "--log-level");
+                break;
+
+#if NETWORK_ENABLED                
             case ARG_PORT:
                 net_port = args.get_argument();
                 break;
@@ -99,31 +137,35 @@
                 net_connect = args.get_argument();
                 break;
 
+#endif                
+
+#if GRAPHICS_ENABLED                
             case ARG_GRAPHICS:
                 graphics_enabled = true;
                 break;
 
             case ARG_FULLSCREEN:
-                graphics.fullscreen = true;
+                display.fullscreen = true;
                 
                 // choose best resolution unless explicitly set
                 if (resolution_default) {
-                    const CL_DisplayMode best_mode = Graphics::getBestMode();
+                    const CL_DisplayMode best_mode = graphics::Display::getBestMode();
                     const CL_Size best_resolution = best_mode.get_resolution();
 
-                    graphics.resolution = PixelCoordinate(best_resolution.width, best_resolution.height);
+                    display.resolution = PixelDimensions(best_resolution.width, best_resolution.height);
                 }
 
                 break;
 
             case ARG_RESOLUTION:
-                graphics.resolution = parse_arg_dimensions(args.get_argument(), "--resolution");
+                display.resolution = parse_arg_dimensions(args.get_argument(), "--resolution");
                 resolution_default = false;
                 break;
 
             case ARG_LIST_MODES:
                 dump_display_modes();
                 return false;
+#endif
 
             case ARG_VERSION:
                 dump_version();
@@ -146,14 +188,18 @@
         }
     }
 
+#if NETWORK_ENABLED    
     // check for invalid combinations of arugments
     if (net_server and !net_connect.empty())
         throw ArgumentError("cannot be both server and client");
+#endif
 
+#if GRAPHICS_ENABLED    
     // enable graphics by default unless server
     if (!net_server)
         graphics_enabled = true;
-    
+#endif    
+
     // continue
     return true;
 }
@@ -170,27 +216,69 @@
     return int_val;
 }
 
-PixelCoordinate Main::parse_arg_dimensions (const std::string &arg_val, const char *arg_name) {
+PixelDimensions Main::parse_arg_dimensions (const std::string &arg_val, const char *arg_name) {
     unsigned int w, h;
     
     // sccanf as unsigned
     if (sscanf(arg_val.c_str(), "%ux%u", &w, &h) != 2)
         throw ArgumentError(std::string() + "invalid format for " + arg_name + ": " + arg_val);
     
-    return PixelCoordinate(w, h);
+    return PixelDimensions(w, h);
 }
-        
+
+LogLevel Main::parse_arg_loglevel (const std::string &arg_val, const char *arg_name) {
+    // brute-force if-elseif
+    if (arg_val == "FATAL")
+        return FATAL;
+
+    else if (arg_val == "ERROR")
+        return ERROR;
+
+    else if (arg_val == "WARN")
+        return WARN;
+
+    else if (arg_val == "INFO")
+        return INFO;
+
+    else if (arg_val == "DEBUG")
+        return DEBUG;
+
+    else
+        throw ArgumentError(std::string() + "invalid log level for " + arg_name + ": " + arg_val);
+}
+
+#if GRAPHICS_ENABLED
 void Main::dump_display_modes (void) {
-    const std::vector<CL_DisplayMode> &modes = Graphics::getDisplayModes();
+    // get the list of display modes from graphics
+    const std::vector<CL_DisplayMode> &modes = graphics::Display::getDisplayModes();
+    PixelCoordinate last_resolution;
     
     std::cout << "Available display modes:" << std::endl;
-
-    for (std::vector<CL_DisplayMode>::const_iterator it = modes.begin(); it != modes.end(); it++)
-        std::cout << "\t" << it->get_resolution().width << "x" << it->get_resolution().height << std::endl;
+    
+    // iterate over the list of available display modes
+    for (std::vector<CL_DisplayMode>::const_iterator it = modes.begin(); it != modes.end(); it++) {
+        PixelCoordinate resolution(it->get_resolution().width, it->get_resolution().height);
+        
+        // filter out those that haven't changed
+        if (resolution != last_resolution)
+            std::cout << "\t" << it->get_resolution().width << "x" << it->get_resolution().height << std::endl;
+        
+        // update for next item
+        last_resolution = resolution;
+    }
 }
+#endif
         
 void Main::dump_version (void) {
-    std::cout << PROJECT_LONG_NAME << " version " << PROJECT_VERSION << " built " << PROJECT_BUILD_TIMESTAMP << std::endl;
+    std::cout 
+        << PROJECT_LONG_NAME << " version " << PROJECT_VERSION << " built [" << PROJECT_BUILD_TIMESTAMP << "]" << std::endl;
+
+    // then some additional stuff about what's enabled
+    std::cout
+        << std::endl
+        << "Enabled components:" << std::endl
+        << "\tGraphics:\t" << (GRAPHICS_ENABLED ? "Yes" : "No") << std::endl
+        << "\tNetwork:\t" << (NETWORK_ENABLED ? "Yes" : "No") << std::endl;
 }
 
 /**
@@ -199,9 +287,12 @@
 int Main::main (int argc, char **argv) {
     // initialize the ClanLib components that we use
     CL_SetupCore setup_core;
-    CL_SetupNetwork setup_network;
+
+#if GRAPHICS_ENABLED    
+    // XXX: move to Graphics/Graphics.hh?
     CL_SetupDisplay setup_disp;
     CL_SetupGL setup_gl;
+#endif
 
     try {
         // parse arugments, exit if false
@@ -209,7 +300,7 @@
             return 0;
 
         // our engine
-        Engine engine;
+        Engine engine(this->engine);
 
         // setup game unless client
         if (net_connect.empty())
@@ -217,7 +308,7 @@
         
         // setup graphics
         if (graphics_enabled)
-            engine.setupGraphics(graphics);
+            engine.setupGraphics(display);
 
         // setup either network server, client or singleplayer
         if (net_server) {
@@ -226,8 +317,12 @@
         } else if (!net_connect.empty()) {
             engine.setupNetworkClient(net_connect, net_port);
         
+        } else if (graphics_enabled) {
+            engine.setupSinglePlayer();
+
         } else {
-            engine.setupSinglePlayer();
+            throw ArgumentError("Nothing to do");
+
         }
 
         // run the main loop
@@ -237,11 +332,14 @@
         return 0;
     
     } catch (ArgumentError &e) {
-        std::cerr << e.what() << std::endl;
+        std::cerr 
+            << "Error: " << e.what() << std::endl
+            << std::endl;
+
         args.print_help();
 
-        // XXX: handle --help
         return 1;
+
     } catch (CL_Error &e) {
         std::cerr << "main: CL_Error:" << e.message << std::endl;
 
--- a/src/Application.hh	Wed Jan 21 00:21:42 2009 +0200
+++ b/src/Application.hh	Thu Jan 22 03:02:43 2009 +0200
@@ -28,7 +28,7 @@
         /**
          * --fullscreen and --resolution
          */
-        GraphicsConfig graphics;
+        DisplayConfig display;
 
         /**
          * --terrain-seed and --terrain-size
@@ -36,6 +36,11 @@
         TerrainConfig terrain;
 
         /**
+         * --log-level
+         */
+        EngineConfig engine;
+
+        /**
          * --port
          */
         std::string net_port;
@@ -66,7 +71,12 @@
         /**
          * Parse unsigned WIDTHxHEIGHT argument
          */
-        PixelCoordinate parse_arg_dimensions (const std::string &arg_val, const char *arg_name);
+        PixelDimensions parse_arg_dimensions (const std::string &arg_val, const char *arg_name);
+
+        /**
+         * Parse a LogLevel
+         */
+        LogLevel parse_arg_loglevel (const std::string &arg_val, const char *arg_name);
         
         /**
          * Print out a list of display modes
@@ -74,7 +84,7 @@
         void dump_display_modes (void);
         
         /**
-         * Print out our project version
+         * Print out our project version and some configuration info
          */
         void dump_version (void);
 
--- a/src/CMakeLists.txt	Wed Jan 21 00:21:42 2009 +0200
+++ b/src/CMakeLists.txt	Thu Jan 22 03:02:43 2009 +0200
@@ -1,19 +1,47 @@
-FILE(GLOB SOURCE_FILES "*.cc" "Network/*.cc")
-FILE(GLOB HEADER_FILES "*.hh" "Network/*.hh")
+FILE(GLOB SOURCE_FILES "*.cc")
+FILE(GLOB HEADER_FILES "*.hh")
 
 set_source_files_properties("version.c" PROPERTIES GENERATED true)
-set(SOURCES ${SOURCE_FILES} ${HEADER_FILES} "version.c")
+
+# Component selection
+set (GRAPHICS_ENABLED true)
+set (NETWORK_ENABLED true)
+
+mark_as_advanced (CLEAR GRAPHICS_ENABLED NETWORK_ENABLED)
 
 # Generate config.h
 configure_file("${CMAKE_CURRENT_SOURCE_DIR}/config.h.in" "${CMAKE_CURRENT_BINARY_DIR}/config.h" @ONLY)
 include_directories("${CMAKE_CURRENT_BINARY_DIR}")
 
+# main module
+set (CORE_CLANLIB_COMPONENTS Core App Signals)
+set (CORE_SOURCES ${SOURCE_FILES} ${HEADER_FILES} "version.c")
+
+# Graphics module
+if (GRAPHICS_ENABLED)
+    add_subdirectory (Graphics)
+
+endif (GRAPHICS_ENABLED)
+
+# Network module
+if (NETWORK_ENABLED)
+    add_subdirectory (Network)
+
+endif (NETWORK_ENABLED)    
+
 # Libraries
+set (CLANLIB_VERSION 0.8)
+set (CLANLIB_COMPONENTS ${CORE_CLANLIB_COMPONENTS} ${GRAPHICS_CLANLIB_COMPONENTS} ${NETWORK_CLANLIB_COMPONENTS})
 
 # ClanLib 0.8
-find_package(ClanLib 0.8 REQUIRED COMPONENTS Core App Signals Display GL Sound Network)
+message (STATUS "Finding ClanLib version=${CLANLIB_VERSION}, components=${CLANLIB_COMPONENTS}" )
+find_package(ClanLib ${CLANLIB_VERSION} REQUIRED COMPONENTS ${CLANLIB_COMPONENTS})
+
 include_directories(${ClanLib_INCLUDE_DIRS})
+
+# build list of libs/source files
 set(LIBS ${LIBS} ${ClanLib_LIBRARIES})
+set(SOURCES ${CORE_SOURCES} ${GRAPHICS_SOURCES} ${NETWORK_SOURCES})
 
 # Assumes the project generates only one executable. If you need more, you'll need to alter
 # the script and replace ${PROJECT_SHORT_NAME} by executable name.
--- a/src/Config.hh	Wed Jan 21 00:21:42 2009 +0200
+++ b/src/Config.hh	Thu Jan 22 03:02:43 2009 +0200
@@ -1,50 +1,85 @@
 #ifndef CONFIG_HH
 #define CONFIG_HH
 
-// XXX: merge this as Config.hh.in?
+/**
+ * @file
+ *
+ * Various constants used to define application/game behaviour
+ */
+
+// build settings
 #include "config.h"
 
+// custom types
 #include "Types.hh"
 
+// for CL_Color
 #include <ClanLib/display.h>
 
-// This is a temporary way to declare different constants. Maybe we
-// should do somekind of resource manager? Do we have time?
-
 /**
- * The value of pi used in physics/graphics calculations
+ * @name General
+ * @{
  */
-const float KG_PI = 3.14159265;
+
+// @}
 
 /**
- * Random generator seed to use for terrain generator
+ * @name Terrain
+ * @{
  */
+
+/** Default random generator seed to use for terrain generator */
 const int TERRAIN_RANDOM_SEED = 1337;
 
+/** Default terrain size, equal to physics simulation size */
+const PixelDimensions TERRAIN_DIMENSIONS (1000, 800);
+
+/** Base color of empty space (i.e. background color) */
+const CL_Color COLOR_EMPTY(86, 41, 0);
+
+/** Base color of dirt */
+const CL_Color COLOR_DIRT(144, 82, 23);
+
+/** Base color of rock */
+const CL_Color COLOR_ROCK(132, 136, 135);
+
+
+// @}
+
 /**
- * Terrain size, equal to physics simulation size
+ * @name Engine
+ * @{
  */
-const PixelDimension TERRAIN_WIDTH = 1000;
-const PixelDimension TERRAIN_HEIGHT = 800;
 
 /** Engine timeout, this determines our minimum tick rate */
 const TimeMS ENGINE_TIMEOUT_MS = 10;
 
+/** Default log output level */
+const LogLevel DEFAULT_LOG_LEVEL = DEBUG;
+
+// @}
+
+/**
+ * @name Physics
+ * @{
+ */
+
+/** The value of pi used in physics/graphics calculations */
+const float KG_PI = 3.14159265;
+
 /** Physics tick interval */
 const TimeMS PHYSICS_TICK_MS = 10;
 
-/** Input handling keyboard poll interval */
-const TimeMS INPUT_POLL_INTERVAL = 10;
+/** The force of gravity on objects, exerted on the Y axis */
+const float MAP_GRAVITY = 1500.0;
 
-/** How long to block INPUT_FLAG_NOREPEAT for */
-const TimeMS INPUT_REPEAT_DELAY = 250;
+// @}
 
 /**
- * The force of gravity on objects, exerted on the Y axis
+ * @name Player
+ * @{
  */
-const float MAP_GRAVITY = 1500.0;
 
-// Player properties
 const float PLAYER_MASS = 10.0;
 const float PLAYER_MOVE_FORCE = 1100.0;
 const float PLAYER_MIN_SPEED = 10.0;
@@ -56,16 +91,12 @@
 const float PLAYER_CROSSHAIR_DISTANCE = 20.0;
 const float PLAYER_CROSSHAIR_WIDTH = 4;
 const float CROSSHAIR_ANGLE_SPEED = PI/2000;
-
 const float PLAYER_MAX_SPEED = 43;
 const float PLAYER_WALK_SPEED = 33;
-
-const float PLAYER_COLLISION_ELASTICITY = 0.25; // TODO: This could be
-                                        // different for different
-                                        // objects
+const float PLAYER_COLLISION_ELASTICITY = 0.25;
 const float PLAYER_RADIUS = 10;
 
-/** What size of hole to dig */
+/** Radius if ground removed on digging */
 const float PLAYER_DIG_RADIUS = 15;
 
 /** Player's initial health */
@@ -74,17 +105,22 @@
 /** How long to wait after dying to respawn */
 const TimeMS PLAYER_RESPAWN_DELAY = 2500;
 
-/*
- * Projectiles
+// @}
+
+/**
+ * @name Projectile
+ * @{
  */
 
-// object mass...
+/** Projectiles mass is irrelevant */
 const float PROJECTILE_MASS = 10.0;
 
-// how far away from the player the projectile spawns
+/** How far away from the player the projectile spawns */
 const float PROJECTILE_START_DISTANCE = 10.0;
 
-/*
+// @}
+
+/**
  * @name Rope
  * @{
  */
@@ -112,25 +148,61 @@
 
 // @}
 
-// Graphical properties
-const CL_Color COLOR_EMPTY(86, 41, 0);
-const CL_Color COLOR_DIRT(144, 82, 23);
-const CL_Color COLOR_ROCK(132, 136, 135);
+/**
+ * @name Graphics/Input
+ * @{
+ */
 
-// graphics params
+/** Window title, XXX: PROJECT_VERSION is unreliable */
 const std::string GRAPHICS_WINDOW_TITLE = (std::string() + PROJECT_LONG_NAME + " : Version " + PROJECT_VERSION);
-const uint32_t GRAPHICS_RESOLUTION_WIDTH = 800;
-const uint32_t GRAPHICS_RESOLUTION_HEIGHT = 600;
-const uint16_t GRAPHICS_UPDATE_INTERVAL_MS = 20;
+
+/** Default resolution */
+const PixelDimensions GRAPHICS_RESOLUTION (800, 600);
+
+/** Interval between frames */
+const TimeMS GRAPHICS_UPDATE_INTERVAL_MS = 20;
+
+/** Default fullscreen mode? */
 const bool GRAPHICS_FULLSCREEN = false;
 
-/**
- * Number of pixels between lines
- */
+/** Number of pixels between lines */
 const PixelDimension GRAPHICS_INFO_TEXT_LINE_OFFSET = 5;
 
-// Filesystem paths
+/** Input handling keyboard poll interval */
+const TimeMS INPUT_POLL_INTERVAL = 10;
+
+/** How long to block INPUT_FLAG_NOREPEAT for */
+const TimeMS INPUT_REPEAT_DELAY = 250;
+
+// @}
+
+/**
+ * @name Network
+ * @{
+ */
+
+/** Default port used for network games */
+const std::string NETWORK_PORT_STR = "9338";
+
+/** Maximum packet size used for normal NetworkPackets... aligned to a sensible UDP mtu */
+const size_t NETWORK_PACKET_SIZE = 1280;
+
+/** Backlog used for TCP server... doesn't really matter all that much */
+const int NETWORK_LISTEN_BACKLOG = 5;
+
+/** Magic string used to identify game between client/server */
+const char NETWORK_MAGIC_STR[8+1] = "KISNGLIS";
+const uint64_t NETWORK_MAGIC_ID = * ((const uint64_t *) NETWORK_MAGIC_STR);
+
+// @}
+
+/**
+ * @name Filesystem paths
+ * @{
+ */
 const std::string PLAYER_SKIN_PATH = (PROJECT_DATA_DIR "/skin.png");
 const std::string RESOURCE_XML_PATH = (PROJECT_DATA_DIR "/resources.xml");
 
+// @}
+
 #endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Configuration.hh	Thu Jan 22 03:02:43 2009 +0200
@@ -0,0 +1,58 @@
+#ifndef CONFIGURATION_HH
+#define CONFIGURATION_HH
+
+/**
+ * @file
+ *
+ * Various classes/modules have their own configuration structures, which are defined here
+ *
+ * XXX: rename to "Settings" to distinguish from Config/config?
+ */
+
+#include "Types.hh"
+#include "Config.hh"
+
+/**
+ * Engine configuration
+ */
+struct EngineConfig {
+    /** Path to the XML ClanLib resource file */
+    std::string resource_path;
+
+    /** Log output level */
+    enum LogLevel log_level;
+
+    /** Defaults */
+    EngineConfig (void) : resource_path(RESOURCE_XML_PATH), log_level(DEFAULT_LOG_LEVEL) { }
+};
+
+/**
+ * Terrain configuration
+ */
+struct TerrainConfig {
+    /** Size of the terrain field*/
+    PixelDimensions dimensions;
+
+    /** Set to nonzero to generate random map */
+    int random_seed;
+    
+    /** Defaults */
+    TerrainConfig (void) : dimensions(TERRAIN_DIMENSIONS), random_seed(TERRAIN_RANDOM_SEED) { }
+};
+
+/**
+ * Graphics display configuration
+ */
+struct DisplayConfig {
+    /** Display resolution */
+    PixelDimensions resolution;
+
+    /** Fullscreen mode? */
+    bool fullscreen;
+
+    /** Defaults */
+    DisplayConfig (void) : resolution(GRAPHICS_RESOLUTION), fullscreen(GRAPHICS_FULLSCREEN) { }
+};
+
+
+#endif
--- a/src/Engine.cc	Wed Jan 21 00:21:42 2009 +0200
+++ b/src/Engine.cc	Thu Jan 22 03:02:43 2009 +0200
@@ -1,19 +1,39 @@
 
 #include "Engine.hh"
 #include "SinglePlayer.hh"
+#include "Config.hh"
+
+// XXX: how does this work if we don't have NETWORK_ENABLED?
 #include "Network/Reactor.hh"
-#include "Config.hh"
+
+// include the real component definitions
+#if NETWORK_ENABLED
+    #include "Network/Client.hh"
+    #include "Network/Server.hh"
+#endif
+
+#if GRAPHICS_ENABLED
+    #include "Graphics/Graphics.hh"
+#endif
 
 #include <iostream>
+#include <cassert>
 
-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)
+/*
+ * Initialize static Engine global state
+ */
+LogLevel Engine::log_level = DEBUG;
+
+Engine::Engine (const EngineConfig &config) : 
+    terrain(NULL), game_state(NULL), graphics(NULL), net_server(NULL), net_client_connect(NULL),
+    game_view(NULL),
+    is_running(true), resources(config.resource_path)
 {
-    
+    // update global log_level
+    Engine::log_level = config.log_level;
 }
 
-GameState& Engine::setupGame (Terrain *terrain) {
+void Engine::setupGame (Terrain *terrain) {
     // ensure this isn't called in inappropriate ways
     assert(!net_server);
 
@@ -22,12 +42,6 @@
 
     // create the GameState
     game_state = new GameState(*terrain);
-    
-    // start graphics?
-    if (graphics_config)
-        startGraphics();
-    
-    return *game_state;
 }
         
 void Engine::setupGame (const TerrainConfig &config) {
@@ -35,38 +49,78 @@
     setupGame(new Terrain(config));
 }
 
-void Engine::setupGraphics (const GraphicsConfig &config) {
-    // store config
-    graphics_config = &config;
-    
-    // start already?
-    if (game_state)
-        startGraphics();
-}
+void Engine::setupGraphics (const DisplayConfig &config) {
 
-void Engine::startGraphics (void) {
-    // check state
-    assert(game_state && graphics_config);
+#if GRAPHICS_ENABLED    
+    assert(!graphics);
 
     // create the graphics
-    graphics = new Graphics(*this, *game_state, *graphics_config);
+    graphics = new graphics::Graphics(*this, resources, config);
+
+#else
+    (void) config;
+
+    throw Error("No Graphics support available");
+
+#endif
+
+}
+        
+void Engine::startGameView (LocalPlayer *player) {
+
+#if GRAPHICS_ENABLED    
+    assert(graphics && !game_view);
+
+    game_view = graphics->displayGameView(*game_state, player);
+
+#else
+    (void) player;
+
+    throw Error("No Graphics support available");
+
+#endif
+
 }
 
 void Engine::setupNetworkServer (const std::string &listen_port) {
+
+#if NETWORK_ENABLED    
     NetworkEndpoint listen_addr(listen_port);
     
     assert(terrain && game_state);
     
     // create the server
     net_server = new NetworkServer(*game_state, listen_addr);
+
+    // put graphics into GameView mode
+    if (graphics)
+        startGameView(NULL);
+
+#else
+    (void) listen_port;
+
+    throw Error("No Network support available");
+
+#endif    
 }
 
 void Engine::setupNetworkClient (const std::string &connect_host, const std::string &connect_port) {
+
+#if NETWORK_ENABLED 
     // connect_to
     NetworkEndpoint connect_addr(connect_host, connect_port);
 
     // begin connecting, the client will callback us with setupGame once it's connected
     net_client_connect = new NetworkClientConnect(*this, connect_addr);
+
+#else
+    (void) connect_host;
+    (void) connect_port;
+
+    throw Error("No Network support available");
+
+#endif    
+
 }
 
 void Engine::setupSinglePlayer (void) {
@@ -77,6 +131,29 @@
 
     // add to gamestate
 	game_state->setLocalPlayer(lp);
+
+    // put graphics into GameView mode
+    if (graphics)
+        startGameView(lp);
+}
+        
+
+GameState& Engine::onNetworkClientConnected (Terrain *terrain) {
+    // setup the game
+    setupGame(terrain);
+
+    // start the GameView, but no LocalPlayer yet
+    startGameView(NULL);
+
+    // return GameState
+    return *game_state;
+}
+        
+void Engine::onNetworkClientPlayer (LocalPlayer *player) {
+    assert(game_view);
+
+    // set the GameView's player
+    game_view->setPlayer(player);
 }
 
 void Engine::stop (void) {
@@ -84,34 +161,28 @@
 }
 
 void Engine::run (void) {
-    // our NetworkReactor
-    NetworkReactor *reactor = NetworkReactor::current;
-
-    // timeout info
+    // timeout for NetworkReactor
     timeval timeout;
 
     while (is_running) {
-        // this does.... magical things
-        CL_System::keep_alive();
+        /*
+         * Run internal ClanLib stuff (also includes our timers) until our timeout has elapsed
+         */
+        CL_System::keep_alive(ENGINE_TIMEOUT_MS);
         
-        // setup our timeout to ENGINE_TIMEOUT_MS
+#if NETWORK_ENABLED 
+        // setup timeout to zero
         timeout.tv_sec = 0;
-        timeout.tv_usec = ENGINE_TIMEOUT_MS * 1000;
-       
+        timeout.tv_usec = 0;
+
         /*
          * Thursday came and went, I re-wrote clan-event.
          *
-         * We use the NetworkReactor for sleeping, as it handles it effeciently even if we're not using network.
+         * (actually, we only use it for zero-timeout polling now... not sure if this is better than using the above
+         * CL_System::keep_alive)
          */
-        reactor->poll(&timeout);
+        NetworkReactor::current->poll(&timeout);
+#endif        
     }
 }
 
-CL_ResourceManager* Engine::getResourceManager (void) {
-    return &resources;
-}
-
-Logger Engine::log (enum LogLevel level, const char *type) {
-    return Logger(level <= WARN ? std::cerr : std::cout, level, type);
-}
-
--- a/src/Engine.hh	Wed Jan 21 00:21:42 2009 +0200
+++ b/src/Engine.hh	Thu Jan 22 03:02:43 2009 +0200
@@ -5,48 +5,60 @@
 class Engine;
 
 #include "GameState.hh"
-#include "Graphics.hh"
-#include "Network/Server.hh"
-#include "Network/Client.hh"
+#include "Configuration.hh"
+#include "Logger.hh"
 
-#include "Logger.hh"
+// forward-declare component pointer types
+// XXX: move to some kind of Components.hh file?
+namespace graphics { class Graphics; class GameView; }
+class NetworkServer;
+class NetworkClientConnect;
+class NetworkClient;
+class NetworkClientController;
 
 /**
  * This is the core class that glues all the other components together and runs the main loop
  */
 class Engine {
     private:
-        // game state
+        /** The game's terrain */
         Terrain *terrain;
-        GameState *game_state;
-
-        /** Set if setupGraphics has been called */
-        const GraphicsConfig *graphics_config;
-        
-        // Graphics/Input
-        Graphics *graphics;
 
-        // network server/client
+        /** The game's state */
+        GameState *game_state;
+        
+        /** The graphics+input component */
+        graphics::Graphics *graphics;
+        
+        /** Network server */
         NetworkServer *net_server;
-        NetworkClientConnect *net_client_connect;
-        // XXX: currently unused: NetworkClient *net_client;
 
-        // to exit the mainloop
+        /** Network client connector */
+        NetworkClientConnect *net_client_connect;
+
+        /** Network client, currently unused */
+        NetworkClient *net_client;
+
+        /** The GameView, if open */
+        graphics::GameView *game_view;
+
+        /** Is the mainloop still running? */
         bool is_running;
+        
+        /** Used to load ClanLib resources */
+        CL_ResourceManager resources;
 
-        // ClanLib resources
-        CL_ResourceManager resources;
+        /** *Global* log level */
+        static LogLevel log_level;
     
     public:    
         // default constructor
-        Engine (const std::string resource_xml_path = RESOURCE_XML_PATH);
+        Engine (const EngineConfig &config);
 
         /**
          * Setup game world using the given terrain, returning the new GameState
-         *
-         * XXX: fix to return void
          */
-        GameState& setupGame (Terrain *terrain);
+        void setupGame (Terrain *terrain);
 
         /**
          * Setup game world using given terrain configuration
@@ -54,17 +66,23 @@
         void setupGame (const TerrainConfig &config);
 
         /**
-         * Enable graphics
+         * Enable graphics. 
+         *
+         * Requires: GRAPHICS_ENABLED
          */
-        void setupGraphics (const GraphicsConfig &config);
+        void setupGraphics (const DisplayConfig &config);
         
         /**
          * Setup server, must call setupGame first
+         *
+         * Requires: NETWORK_ENABLED
          */
         void setupNetworkServer (const std::string &listen_port);
 
         /**
          * Setup client, do *not* call setupGame, configuration comes from the server
+         *
+         * Requires: NETWORK_ENABLED
          */
         void setupNetworkClient (const std::string &connect_host, const std::string &connect_port);
 
@@ -85,16 +103,40 @@
 
     private:
         /**
-         * Actually start graphics, requires that game_state is now set
+         * Puts graphics into GameView mode, using our GameState and the given player
          */
-        void startGraphics (void);
+        void startGameView (LocalPlayer *player);
+
+    protected:
+        friend class NetworkClientConnect;
+        friend class NetworkClientController;
+
+        /**
+         * Interface for NetworkClient to use once it has connected
+         */
+        GameState& onNetworkClientConnected (Terrain *terrain);
+
+        /**
+         * Interface for NetworkClient to use once it has set up the player
+         */
+        void onNetworkClientPlayer (LocalPlayer *player);
 
     public:
-        // get a pointer to our resource manager
-        CL_ResourceManager* getResourceManager (void);
-
-        // logging utility
-        static Logger log (enum LogLevel level, const char *type);
+        /**
+         * Get a pointer to our resource manager, please don't break it.
+         *
+         * Guaranteed to never be NULL
+         */
+        CL_ResourceManager* getResourceManager (void) { return &resources; }
+        
+        /**
+         * Log output, see Logger for more info
+         *
+         * @see Logger
+         */
+        static inline Logger log (LogLevel level, const char *type) {
+            return Logger(level <= WARN ? std::cerr : std::cout, level, type, log_level);
+        }
 
 };
 
--- a/src/GameMessageView.cc	Wed Jan 21 00:21:42 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,56 +0,0 @@
-
-#include "GameMessageView.hh"
-#include "Graphics.hh"
-#include "Engine.hh"
-
-GameMessageView::GameMessageView (PixelArea area) :
-    area(area), messages()
-{
-    
-}
-
-void GameMessageView::add_message (CL_Color color, std::string message) {
-    GameMessage msg (color, message);
-
-    messages.push_back(msg);
-}
-
-void GameMessageView::draw (Graphics *g) {
-    // get font
-    CL_Font &font = g->getSimpleFont(); 
-
-    // remember color
-    CL_Color font_color = font.get_color();
-    
-    // maximum width
-    CL_Size max_size = CL_Size(area.right - area.left, 0);
-
-    // starting point for drawing
-    PixelDimension offset_prev = area.bottom;
-
-    // render messages, bottom up
-    for (std::vector<GameMessage>::reverse_iterator it = messages.rbegin(); it != messages.rend(); it++) {
-        // set message color
-        font.set_color(it->color);
-
-        // calculate height
-        PixelDimension height = font.get_height(it->message, max_size) + GRAPHICS_INFO_TEXT_LINE_OFFSET;
-       
-        // new draw_point
-        PixelDimension offset_this = offset_prev - height;
-
-        // break if it doesn't fit anymore
-        if (offset_this < area.top)
-            break;
- 
-        // draw text
-        font.draw(CL_Rect(area.left, offset_this, area.right, offset_prev), it->message, g->get_gc());
-        
-        // advance offset
-        offset_prev = offset_this;
-    }
-
-    // restore font color
-    font.set_color(font_color);
-}
-
--- a/src/GameMessageView.hh	Wed Jan 21 00:21:42 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +0,0 @@
-#ifndef GRAPHICS_INFO_TEXT_HH
-#define GRAPHICS_INFO_TEXT_HH
-
-#include "GraphicsPointer.hh"
-#include "Types.hh"
-
-#include <string>
-#include <vector>
-#include <ClanLib/display.h>
-
-struct GameMessage {
-    CL_Color color;
-    std::string message;
-
-    GameMessage (CL_Color color, std::string message) : color(color), message(message) { }
-    GameMessage (const GameMessage &copy) : color(copy.color), message(copy.message) { }
-    GameMessage &operator= (const GameMessage &copy) { color = copy.color; message = copy.message; return *this; }
-};
-
-class GameMessageView {
-    protected:
-        PixelArea area;
-        std::vector<GameMessage> messages;
-
-    public:
-        /**
-         * Define the area where messages are drawn
-         */
-        GameMessageView (PixelArea area);
-
-        /**
-         * Update draw area
-         */
-        void on_resize (PixelArea new_area) { this->area = new_area; }
-
-        /**
-         * Add a message to the list of messages displayed
-         */
-        void add_message (CL_Color color, std::string message);
-        
-        /**
-         * Draw as many messages as fits
-         */
-        void draw (Graphics *g);
-};
-
-#endif
--- a/src/GameState.cc	Wed Jan 21 00:21:42 2009 +0200
+++ b/src/GameState.cc	Thu Jan 22 03:02:43 2009 +0200
@@ -2,6 +2,7 @@
 #include "GameState.hh"
 #include "Engine.hh"
 #include "Config.hh"
+#include "Error.hh"
 
 GameState::GameState (Terrain &terrain) : 
     world(Vector(0, MAP_GRAVITY), Vector(terrain.getWidth(), terrain.getHeight()), terrain), 
@@ -49,10 +50,11 @@
     if (event_handler)
         event_handler->on_player_left(player);
 }
-    
-void GameState::draw(Graphics *g, PixelCoordinate camera, bool displayWeapon) {
+
+#if GRAPHICS_ENABLED
+void GameState::draw (graphics::Display &display, PixelCoordinate camera, bool displayWeapon) {
     // Draw terrain
-    terrain.draw(g, camera);
+    terrain.draw(display, camera);
 
     // Draw players
     for (std::list<Player*>::iterator it = player_list.begin(); it != player_list.end(); it++) {
@@ -60,14 +62,15 @@
         
         // our LocalPlayer has it's own draw method
         if (p == local_player)
-            local_player->draw(g, displayWeapon, camera);
+            local_player->draw(display, displayWeapon, camera);
         else
-            p->draw(g, camera);
+            p->draw(display, camera);
     }
 
     // Draw projectiles
     for (std::list<Projectile*>::iterator it = projectiles.begin(); it != projectiles.end(); it++) {
-        (*it)->draw(g, camera);
+        (*it)->draw(display, camera);
     }
 }
- 
+#endif
+
--- a/src/GameState.hh	Wed Jan 21 00:21:42 2009 +0200
+++ b/src/GameState.hh	Thu Jan 22 03:02:43 2009 +0200
@@ -4,13 +4,13 @@
 class GameState;
 
 #include "PhysicsWorld.hh"
+#include "Terrain.hh"
 #include "Player.hh"
 #include "Projectile.hh"
-#include "Rope.hh"
-#include "Input.hh"
-#include "GraphicsPointer.hh"
 #include "Config.hh"
 
+#include "Graphics/Drawable.hh"
+
 #include <list>
 #include <stdexcept>
 #include <cmath>
@@ -93,11 +93,13 @@
      * Removes the given player from player_list. If the given player was the local_player, set that to NULL
      */
     void removePlayer (Player *player);
-    
+
+#if GRAPHICS_ENABLED    
     /**
      * Draws the terrain, players and projectiles
      */
-    virtual void draw (Graphics *g, PixelCoordinate camera, bool displayWeapon);
+    virtual void draw (graphics::Display &display, PixelCoordinate camera, bool displayWeapon);
+#endif
 };
 
 #endif
--- a/src/Graphics.cc	Wed Jan 21 00:21:42 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,352 +0,0 @@
-
-#include "Graphics.hh"
-#include "GameState.hh"
-#include <cmath>
-#include <sstream>
-
-/*
- * XXX: until we figure out a better way to layout stuff
- */
-static PixelArea getMessageViewArea (PixelCoordinate resolution) {
-    return PixelArea(
-            400,
-            resolution.y - 100,
-            resolution.x,
-            resolution.y
-    );
-}
-
-Graphics::Graphics (Engine &engine, GameState &state, const GraphicsConfig &config) :
-    CL_DisplayWindow(GRAPHICS_WINDOW_TITLE, config.resolution.x, config.resolution.y, config.fullscreen, true),
-    engine(engine), 
-    state(state), 
-    resolution(config.resolution),
-    fullscreen_resolution(0, 0), window_resolution(0, 0),
-    update_timer(GRAPHICS_UPDATE_INTERVAL_MS),
-    input(get_ic()->get_keyboard()),
-    simple_font("Font2", engine.getResourceManager()),
-    message_view(getMessageViewArea(resolution))
-{
-
-    // connect timer signal
-    slots.connect(update_timer.sig_tick(), this, &Graphics::on_update);
-    slots.connect(this->sig_resize(), this, &Graphics::on_window_resize);
-
-    // enable
-    update_timer.start();
-
-    // push something to message_view
-    message_view.add_message(CL_Color::white, "Hello World"); 
-
-    // GameState events....
-    state.setEventHandler(this);
-
-    // set correct resolution
-    if (config.fullscreen) {
-        fullscreen_resolution = resolution;
-
-    } else {
-        CL_DisplayMode best_mode = getBestMode();
-
-        fullscreen_resolution = PixelCoordinate(best_mode.get_resolution().width, best_mode.get_resolution().height);
-        window_resolution = resolution;
-    }
-
-}
-
-const std::vector<CL_DisplayMode> & Graphics::getDisplayModes (void) {
-    return CL_DisplayMode::get_display_modes();
-}
-
-const CL_DisplayMode Graphics::getBestMode (void) {
-    const std::vector<CL_DisplayMode> &modes = Graphics::getDisplayModes();
-
-    const CL_DisplayMode *best_mode = NULL;
-    
-    for (std::vector<CL_DisplayMode>::const_iterator it = modes.begin(); it != modes.end(); it++)
-        if (best_mode == NULL || (
-                it->get_resolution().width * it->get_resolution().height > 
-                best_mode->get_resolution().width * best_mode->get_resolution().height    
-        ))
-            best_mode = &*it;
-    
-    if (best_mode == NULL)
-        throw Error("No available video modes!");
-    
-    return *best_mode;
-}
-
-void Graphics::check_input (void) {
-    LocalPlayer *player;
-    PlayerInput input_mask;
-    TimeMS input_dt;
-    
-    // update gui flags
-    handle_input(input.readGuiInput());
-
-    // stop here if we don't have a local player
-    if ((player = state.getLocalPlayer()) == NULL)
-        return;
-    
-    // build input_mask
-    input.readPlayerInput(input_mask, input_dt);
-    
-    // apply input if there was any
-    if (input_mask)
-        player->handleInput(input_mask, input_dt);
-}
-    
-void Graphics::toggle_fullscreen (void) {
-    if (is_fullscreen()) {
-        // remember current fullscreen resolution
-        fullscreen_resolution = resolution;
-        
-        // enter windowed mode
-        set_windowed();
-
-/*        
-        // do we have a window-mode resolution stored?
-        if (window_resolution.x && window_resolution.y)
-            set_size(window_resolution.x, window_resolution.y);
-*/
-    } else {
-        // remember current window resolution
-        window_resolution = resolution;
-
-        // enter fullscreen mode
-        set_fullscreen(fullscreen_resolution.x, fullscreen_resolution.y);
-        
-        // update resolution
-        resolution = fullscreen_resolution;
-        
-        // log
-        message_view.add_message(CL_Color::yellow, CL_String::format("[ Enter fullscreen mode with %1 x %2 resolution ]", 
-                (int) resolution.x, (int) resolution.y));
-    }
-}
-
-void Graphics::handle_input (GuiInput flags) {
-    // update flags
-    this->flags = flags;
-
-    // quit?
-    if (flags & GUI_INPUT_QUIT) {
-        engine.stop();
-
-        return;
-    }
-
-    // dump player debug info on stderr
-    if ((flags & GUI_INPUT_DEBUG_PLAYER) && state.getLocalPlayer()) {
-        state.getLocalPlayer()->printDebugInfo();
-        
-        message_view.add_message(CL_Color::green, "...");
-    }
-    
-    // toggle fullscreen?
-    if (flags & GUI_INPUT_TOGGLE_FULLSCREEN)
-        toggle_fullscreen();
-}
-
-static PixelDimension value_between (PixelDimension low, PixelDimension value, PixelDimension high) {
-    if (high < low)
-        return (high + low) / 2;
-
-    else if (value < low)
-        return low;
-
-    else if (value > high)
-        return high;
-
-    else
-        return value;
-}
-
-void Graphics::do_redraw (void) {
-    CL_GraphicContext *gc = get_gc();
-    LocalPlayer *player;
-
-    // calculate camera
-    PixelCoordinate camera(0, 0);
-    
-    // ...to track our local player
-    if ((player = state.getLocalPlayer()) != NULL) {
-        // try and center the screen on the player
-        PixelCoordinate target = player->getCoordinate() - PixelCoordinate(resolution.x / 2, (resolution.y - 100) / 2);
-
-        // ...but keep the world in view
-        PixelCoordinate max = state.terrain.getDimensions() - resolution + PixelCoordinate(0, 100);
-        
-        // ...by limiting the value to 0...max
-        camera = PixelCoordinate(
-            value_between(0, target.x, max.x),
-            value_between(0, target.y, max.y)
-        );
-    }
-    
-    // Black background
-    gc->clear(CL_Color::black);
-
-    // Draw the game
-    state.draw(this, camera, flags & GUI_INPUT_DISPLAY_WEAPON);
-    
-    // draw player info box
-    if (player != NULL) {
-        draw_player_info(gc, player);
-    }
-    
-    // draw messages
-    message_view.draw(this);
-
-    // Flip window buffer, sync
-    flip(1);
-}
-
-void Graphics::on_update (TimeMS tick_length) {
-    (void) tick_length;
-
-    // check keyboard input
-    check_input();
-
-    // redraw display
-    do_redraw();
-}
-
-void Graphics::draw_player_info(CL_GraphicContext *gc, Player *p) {
-    int box_top = resolution.y - 100;
-    int box_left = 0;
-    int box_right = resolution.x;
-    int box_bottom = resolution.y;
-    int bar_length = 3; // *100
-
-    // draw status info at bottom of display
-    gc->fill_rect(
-        CL_Rect(
-            box_left,
-            box_top,
-            box_right,
-            box_bottom
-        ),
-        CL_Gradient(
-            CL_Color(0, 0, 0),
-            CL_Color(50, 50, 50),
-            CL_Color(50, 50, 50, 150),
-            CL_Color(100, 100, 100, 200)
-        )
-    );
-    
-    // Health
-    gc->draw_rect(
-        CL_Rect(
-            box_left + 9,
-            box_top + 9,
-            box_left + 11 + 100 * bar_length,
-            box_top + 31
-        ),
-        CL_Color(150, 150, 150)
-    );
-
-    gc->fill_rect(
-        CL_Rect(
-            box_left + 10,
-            box_top + 10,
-            box_left + 10 + (int) (p->getHealthPercent() * bar_length),
-            box_top + 30
-        ),
-        CL_Gradient(
-            CL_Color(200, 0, 0),
-            CL_Color(200 - (int)(p->getHealthPercent() * 2), (int)(p->getHealthPercent() * 2), 0),
-            CL_Color(200, 0, 0),
-            CL_Color(200 - (int)(p->getHealthPercent() * 2), (int)(p->getHealthPercent() * 2), 0)
-        )
-    );
-
-    // stats - kills
-    std::stringstream sskills;
-    sskills << "Kills:  " << p->getKills();
-    getSimpleFont().draw(
-        box_left + 20 + 100 * bar_length,
-        box_top + 10,
-        sskills.str(),
-        get_gc()
-    );
-    
-    // stats - deaths
-    std::stringstream ssdeaths;
-    ssdeaths << "Deaths:  " << p->getDeaths();
-    getSimpleFont().draw(
-        box_left + 20 + 100 * bar_length,
-        box_top + 30,
-        ssdeaths.str(),
-        get_gc()
-    );
-    
-    // stats - ratio
-    std::stringstream ssratio;
-    ssratio << "Ratio:  " << (p->getKills()+1) / (p->getDeaths()+1);
-    getSimpleFont().draw(
-        box_left + 20 + 100 * bar_length,
-        box_top + 50,
-        ssratio.str(),
-        get_gc()
-    );
-    
-
-    // Weapon clip / reloading
-    gc->draw_rect(
-        CL_Rect(
-            box_left + 9,
-            box_top + 69,
-            box_left + 11 + 100 * bar_length,
-            box_top + 91
-        ),
-        CL_Color(150, 150, 150)
-    );
-
-    gc->fill_rect(
-        CL_Rect(
-            box_left + 10,
-            box_top + 70,
-            box_left + 10 + (100 - (int) (p->getCurrentWeapon()->getReloadTimer() * 100 / p->getCurrentWeapon()->getReloadTime())) * bar_length,
-            box_top + 90
-        ),
-        CL_Gradient(
-            CL_Color(100, 100, 0),
-            CL_Color(100, 100, 0),
-            CL_Color(100, 100, 0),
-            CL_Color(100, 100, 100)
-        )
-    );
-   
-    // current weapon name
-    getSimpleFont().draw(
-        box_left + 20 + 100 * bar_length,
-        box_top + 70,
-        p->getCurrentWeapon()->getName(),
-        get_gc()
-    );
-
-}
-
-void Graphics::on_window_resize (int new_x, int new_y) {
-    // ignore resize in fullscreen mode
-    if (is_fullscreen())
-        return;
-    
-    // update resolution
-    resolution = PixelCoordinate(new_x, new_y);
-    
-    // resize components
-    message_view.on_resize(getMessageViewArea(resolution));
-    
-    // log message
-    message_view.add_message(CL_Color::yellow, CL_String::format("[ Resized window to %1 x %2 ]", new_x, new_y));
-}
-    
-void Graphics::on_player_joined (Player *p) {
-    message_view.add_message(CL_Color::white, " *** Player joined");
-}
-    
-void Graphics::on_player_left (Player *p) {
-    message_view.add_message(CL_Color::white, " *** Player left");
-}
--- a/src/Graphics.hh	Wed Jan 21 00:21:42 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,155 +0,0 @@
-#ifndef GRAPHICS_HH
-#define GRAPHICS_HH
-
-#include "GraphicsPointer.hh"
-#include "Types.hh"
-#include "Config.hh"
-
-/** 
- * Parameters used by Graphics
- */
-struct GraphicsConfig {
-    /** Initial resolution to use */
-    PixelCoordinate resolution;
-    
-    /* Use fullscreen mode at startup */
-    bool fullscreen;
-
-    /** Defaults */
-    GraphicsConfig (void) : resolution(GRAPHICS_RESOLUTION_WIDTH, GRAPHICS_RESOLUTION_HEIGHT), fullscreen(GRAPHICS_FULLSCREEN) { }
-}; 
-
-#include "GameState.hh"
-#include "Input.hh"
-#include "Timer.hh"
-#include "Engine.hh"
-
-#include "GameMessageView.hh"
-
-#include <ClanLib/core.h>
-#include <ClanLib/gl.h>
-#include <ClanLib/display.h>
-
-/**
- * This handles drawing the GameState with an appropriate camera view each frame, loading fonts, and currently,
- * handling the input from Input to GameState.
- */
-class Graphics : public GameStateEventHandler, public CL_DisplayWindow {
-private:
-    /**
-     * Our engine reference
-     */
-    Engine &engine;
-
-    /**
-     * GameState we are associated with
-     */
-    GameState &state;
-    
-    /**
-     * Current window resolution
-     */
-    PixelCoordinate resolution;
-
-    /**
-     * Target resolution in fullscreen/windowed mode
-     */
-    PixelCoordinate fullscreen_resolution, window_resolution;
-    
-    /**
-     * ClanLib signal slots
-     */ 
-    CL_SlotContainer slots;
-    
-    /**
-     * Our timer that drives redraws
-     */
-    Timer update_timer;
-    
-    /**
-     * Our input handler for GUI and Player input
-     */
-    Input input;
-
-    /**
-     * Current GUI input state
-     */
-    GuiInput flags;
-    
-    /**
-     * A basic font
-     */
-    CL_Font simple_font;
-
-    /**
-     * View components
-     */
-    GameMessageView message_view;
-
-public:
-    /**
-     *
-     */
-    Graphics (Engine &engine, GameState &state, const GraphicsConfig &config);
-    
-    /**
-     * Returns a CL_Font that can be used for drawing text
-     */
-    CL_Font& getSimpleFont (void) { return simple_font; }
-    
-    /**
-     * Returns a vector of CL_DisplayModes that lists possible display modes to use for fullscreen mode.
-     */
-    static const std::vector<CL_DisplayMode> & getDisplayModes (void);
-
-    /**
-     * Returns the "best" CL_DisplayMode to use for fullscreen mode.
-     *
-     * Currently, this just means the largest resolution.
-     */
-    static const CL_DisplayMode getBestMode (void);
-    
-private:
-    /**
-     * Shift back and forth between fullscreen and windowed mode, retaining resolutions
-     */
-    void toggle_fullscreen (void);
-
-    /**
-     * Reads current input events from Input and applies them, using LocalPlayer::handleInput and
-     * Graphics::handle_input.
-     */
-    void check_input (void);
-    
-    /**
-     * Handles GuiInput flags read from Input.
-     */
-    void handle_input (GuiInput flags);
-    
-    /**
-     * Redraws entire screen
-     */
-    void do_redraw (void);
-    
-    /**
-     * Handles input and redraws screen
-     */
-    void on_update (TimeMS tick_length);
-    
-    /**
-     * Draws status view
-     */
-    void draw_player_info (CL_GraphicContext *gc, Player *p);
-
-    /**
-     * Handles window resize. This just updates resolution
-     */
-    void on_window_resize (int new_x, int new_y);
-        
-protected:
-    /* GameStateEventHandler */    
-    virtual void on_player_joined (Player *p);
-    virtual void on_player_left (Player *p);
-};
-
-#endif /* GRAPHICS_HH */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Graphics/CMakeLists.txt	Thu Jan 22 03:02:43 2009 +0200
@@ -0,0 +1,9 @@
+FILE(GLOB GRAPHICS_SOURCE_FILES "*.cc")
+FILE(GLOB GRAPHICS_HEADER_FILES "*.hh")
+
+# what ClanLib components we need
+set (GRAPHICS_CLANLIB_COMPONENTS Display GL GUI PARENT_SCOPE)
+
+# list of source files
+set (GRAPHICS_SOURCES ${GRAPHICS_SOURCE_FILES} ${GRAPHICS_HEADER_FILES} PARENT_SCOPE)
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Graphics/Console.cc	Thu Jan 22 03:02:43 2009 +0200
@@ -0,0 +1,23 @@
+
+#include "Console.hh"
+
+namespace graphics 
+{
+
+Console::Console (const CL_Rect& pos, CL_Component* parent) :
+    CL_Component(pos, parent),
+    messages(), input(getInputPosition(), this)
+{    
+    // hide by default
+    show(false);
+
+}
+
+CL_Rect Console::getInputPosition (void) {
+    CL_Rect pos = get_position();
+
+    return CL_Rect(pos.left, pos.bottom - 30, pos.right, pos.bottom);
+}
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Graphics/Console.hh	Thu Jan 22 03:02:43 2009 +0200
@@ -0,0 +1,34 @@
+#ifndef GRAPHICS_CONSOLE_HH
+#define GRAPHICS_CONSOLE_HH
+
+#include "GUI.hh"
+
+#include <list>
+
+namespace graphics 
+{
+
+class Console : public CL_Component {
+protected:
+    /** our list of messages */
+    std::list<std::string> messages;
+
+    /** our input dialog */
+    CL_InputBox input;
+
+public:
+    /**
+     * Construct a new console, positioned in the given area, with the given parent
+     */
+    Console (const CL_Rect& pos, CL_Component* parent);
+
+private:
+    /**
+     * Calculate the position for the input box
+     */
+    CL_Rect getInputPosition (void);
+};
+
+}
+
+#endif 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Graphics/Display.cc	Thu Jan 22 03:02:43 2009 +0200
@@ -0,0 +1,102 @@
+
+#include "Display.hh"
+#include "../Error.hh"
+
+namespace graphics
+{
+
+Display::Display (const DisplayConfig &config) :
+    CL_DisplayWindow(GRAPHICS_WINDOW_TITLE, config.resolution.width, config.resolution.height, config.fullscreen, true),
+    config(config), 
+    fullscreen_resolution(0, 0),
+    update_timer(GRAPHICS_UPDATE_INTERVAL_MS), 
+    current_view(NULL)
+{
+    // connect timer signal
+    slots.connect(update_timer.sig_tick(), this, &Display::on_update);
+    slots.connect(this->sig_resize(), this, &Display::on_window_resize);
+
+    // enable timer
+    update_timer.start();
+
+    // set correct fullscreen resolution
+    if (config.fullscreen) {
+        fullscreen_resolution = config.resolution;
+
+    } else {
+        CL_DisplayMode best_mode = getBestMode();
+
+        fullscreen_resolution = PixelDimensions(best_mode.get_resolution().width, best_mode.get_resolution().height);
+    }
+}
+
+const std::vector<CL_DisplayMode> & Display::getDisplayModes (void) {
+    return CL_DisplayMode::get_display_modes();
+}
+
+const CL_DisplayMode Display::getBestMode (void) {
+    const std::vector<CL_DisplayMode> &modes = getDisplayModes();
+
+    const CL_DisplayMode *best_mode = NULL;
+    
+    for (std::vector<CL_DisplayMode>::const_iterator it = modes.begin(); it != modes.end(); it++)
+        if (best_mode == NULL || (
+                it->get_resolution().width * it->get_resolution().height > 
+                best_mode->get_resolution().width * best_mode->get_resolution().height    
+        ))
+            best_mode = &*it;
+    
+    if (best_mode == NULL)
+        throw Error("No available video modes!");
+    
+    return *best_mode;
+}
+
+void Display::setView (View *view) {
+    this->current_view = view;
+    
+    // resize to fill display
+    if (view)
+        view->resize(PixelArea(0, 0, get_width(), get_height()));
+}
+
+void Display::toggleFullscreen (void) {
+    if (is_fullscreen()) {
+        // enter windowed mode
+        set_windowed();
+
+    } else {
+        // enter fullscreen mode
+        set_fullscreen(fullscreen_resolution.width, fullscreen_resolution.height);
+    }
+}
+
+void Display::on_update (TimeMS dt) {
+    (void) dt;
+
+    // do we have something to draw?
+    if (current_view) {
+        // draw it
+        current_view->draw(*this);
+
+        // flip buffers, sync
+        flip(1);
+    }
+}
+
+void Display::on_window_resize (int new_x, int new_y) {
+    (void) new_x;
+    (void) new_y;
+
+    // ignore resize in fullscreen mode
+    if (is_fullscreen())
+        return;
+
+    // resize view
+    if (current_view)
+        current_view->resize(PixelArea(0, 0, new_x, new_y));
+}
+
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Graphics/Display.hh	Thu Jan 22 03:02:43 2009 +0200
@@ -0,0 +1,102 @@
+#ifndef GRAPHICS_DISPLAY_HH
+#define GRAPHICS_DISPLAY_HH
+
+#include "../Types.hh"
+#include "../Config.hh"
+
+namespace graphics
+{
+
+class Display;
+
+}
+
+#include "View.hh"
+#include "../Timer.hh"
+#include "../Configuration.hh"
+
+#include <ClanLib/display.h>
+
+namespace graphics
+{
+
+/**
+ * We wrap ClanLib's DisplayWindow for our own functionality. This is the core of the graphics code
+ */
+class Display : public CL_DisplayWindow {
+private:
+    /**
+     * Our configuration
+     */
+    DisplayConfig config;
+
+    /**
+     * Target fullscreen resolution
+     */
+    PixelDimensions fullscreen_resolution;
+    
+    /**
+     * Our timer that drives redraws
+     */
+    Timer update_timer;
+
+    /**
+     * What we draw
+     */
+    View *current_view;
+
+    CL_SlotContainer slots;
+
+public:
+    /**
+     * Construct default display, empty window unless otherwise build
+     */
+    Display (const DisplayConfig &config);
+
+    /**
+     * Returns a vector of CL_DisplayModes that lists possible display modes to use for fullscreen mode.
+     */
+    static const std::vector<CL_DisplayMode> & getDisplayModes (void);
+
+    /**
+     * Returns the "best" CL_DisplayMode to use for fullscreen mode.
+     *
+     * Currently, this just means the largest resolution.
+     */
+    static const CL_DisplayMode getBestMode (void);
+
+    /**
+     * Get current resolution
+     *
+     * XXX: should be PixelDimensions...
+     */
+    PixelCoordinate getResolution (void) { 
+        return PixelCoordinate(get_width(), get_height());
+    }
+
+    /**
+     * Display the given view, this will initialize resize it to the right size
+     */
+    void setView (View *view);
+
+    /**
+     * Shift back and forth between fullscreen and windowed mode, retaining resolutions
+     */
+    void toggleFullscreen (void);
+
+private:
+    /**
+     * Draw next frame
+     */
+    void on_update (TimeMS dt);
+
+    /**
+     * Handles window resize. This just updates resolution
+     */
+    void on_window_resize (int new_x, int new_y);
+
+};
+
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Graphics/Drawable.hh	Thu Jan 22 03:02:43 2009 +0200
@@ -0,0 +1,30 @@
+#ifndef GRAPHICS_DRAWABLE_HH
+#define GRAPHICS_DRAWABLE_HH
+
+namespace graphics
+{
+
+class Drawable;
+
+}
+
+#include "Display.hh"
+
+namespace graphics
+{
+
+/**
+ * Abstract interface class to define something that's drawable
+ */
+class Drawable {
+public:
+    /**
+     * Draw graphics onto the given display
+     */
+    virtual void draw (Display &display) = 0;
+};
+
+}
+
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Graphics/FontManager.hh	Thu Jan 22 03:02:43 2009 +0200
@@ -0,0 +1,43 @@
+#ifndef GRAPHICS_FONT_MANAGER_HH
+#define GRAPHICS_FONT_MANAGER_HH
+
+#include <ClanLib/display.h>
+
+namespace graphics
+{
+
+/**
+ * Loads fonts and lets other components use them
+ */
+class FontManager {
+private:
+    /**
+     * Our resource manager for loading these
+     */
+    CL_ResourceManager &resources;
+
+    /**
+     * A basic monospace font
+     */
+    CL_Font simple_font;
+
+public:
+    /**
+     * Load fonts
+     */
+    FontManager (CL_ResourceManager &resources) :
+        resources(resources), simple_font("Font2", &resources)
+    {
+
+    }
+
+    /**
+     * Returns a CL_Font that can be used for drawing text
+     */
+    CL_Font& getSimpleFont (void) { return simple_font; }
+};
+
+
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Graphics/GUI.hh	Thu Jan 22 03:02:43 2009 +0200
@@ -0,0 +1,31 @@
+#ifndef GRAPHICS_GUI_HH
+#define GRAPHICS_GUI_HH
+
+#include "GUIStyle.hh"
+
+#include <ClanLib/gui.h>
+
+
+namespace graphics
+{
+
+/**
+ * Our CL_GUIManager, for when we use ClanLib's GUI stuff
+ */    
+class GUI : public CL_GUIManager {
+public:
+    /**
+     * Construct default manager
+     */
+    GUI (CL_ResourceManager *resources) :
+        CL_GUIManager(new GUIStyle(resources))
+    {
+
+    }
+
+        
+};
+
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Graphics/GUIStyle.hh	Thu Jan 22 03:02:43 2009 +0200
@@ -0,0 +1,37 @@
+#ifndef GRAPHICS_GUI_STYLE_HH
+#define GRAPHICS_GUI_STYLE_HH
+
+#include "GUI.hh"
+
+#include <ClanLib/guistylesilver.h>
+
+namespace graphics
+{
+
+/**
+ * Our CL_StyleManager used for drawing ClanLib's GUI components
+ */    
+class GUIStyle : public CL_StyleManager_Silver {
+public:
+    /**
+     * Construct GUI style
+     */
+    GUIStyle (CL_ResourceManager *resources) :
+        CL_StyleManager_Silver(resources)
+    {
+
+    }
+    
+    /**
+     * Handle attaching style objects to components
+     */
+    virtual void connect_styles (const std::string &type, CL_Component *component) {
+
+        // default to parent impl
+        CL_StyleManager_Silver::connect_styles(type, component);
+    }
+};
+
+}
+
+#endif 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Graphics/GameView.cc	Thu Jan 22 03:02:43 2009 +0200
@@ -0,0 +1,136 @@
+
+#include "GameView.hh"
+#include "Graphics.hh"
+
+#include <cassert>
+
+namespace graphics
+{
+
+GameView::GameView (GameState &state, LocalPlayer *player) :
+    View(PixelArea(0, 0, graphics->display.get_width(), graphics->display.get_height())),
+    state(state), player(NULL), info_view(NULL), message_view(getMessageViewArea()),
+    flags(0)
+{
+    // have player?
+    if (player)
+        setPlayer(player);
+
+    // insert message
+    message_view.add_message(CL_Color::white, "Hello World!");
+
+    // enable GUI input
+    slots.connect(graphics->input.gui.sig_input(), this, &GameView::handleInput);
+    graphics->input.gui.enable();
+}
+
+void GameView::setPlayer (LocalPlayer *player) {
+    assert(!this->player && player);
+    
+    // remember it
+    this->player = player;
+
+    // build the info_view as well
+    info_view = new PlayerInfoView(getInfoViewArea(), player);
+
+    // enable player input
+    slots.connect(graphics->input.player.sig_input(), player, &LocalPlayer::handleInput);
+    graphics->input.player.enable();
+}
+
+void GameView::handleInput (GuiInput flags, TimeMS dt) {
+    // ignore timing info
+    (void) dt;
+
+    // update our flags
+    this->flags = flags;
+
+    // quit?
+    if (flags & GUI_INPUT_QUIT) {
+        graphics->engine.stop();
+        return;
+    }
+
+    // dump player debug info on stderr
+    if ((flags & GUI_INPUT_DEBUG_PLAYER) && player) {
+        player->printDebugInfo();
+
+        message_view.add_message(CL_Color::green, "...");
+    }
+
+    // toggle fullscreen?
+    if (flags & GUI_INPUT_TOGGLE_FULLSCREEN)
+        graphics->display.toggleFullscreen();
+}
+
+
+/*
+ * Helper function for Camera
+ */
+static PixelDimension value_between (PixelDimension low, PixelDimension value, PixelDimension high) {
+        if (high < low)
+            return (high + low) / 2;
+
+        else if (value < low)
+            return low;
+
+        else if (value > high)
+            return high;
+
+        else
+            return value;
+}
+
+void GameView::draw (Display &display) {
+    CL_GraphicContext *gc = display.get_gc();
+    
+    // calculate camera
+    PixelCoordinate camera(0, 0);
+    
+    // ...to track our local player
+    if (player != NULL) {
+        // display resolution
+        PixelCoordinate resolution = display.getResolution();
+
+        // try and center the screen on the player
+        PixelCoordinate target = player->getCoordinate() - PixelCoordinate(resolution.x / 2, (resolution.y - 100) / 2);
+
+        // ...but keep the world in view
+        PixelCoordinate max = state.terrain.getDimensions() - resolution + PixelCoordinate(0, 100);
+        
+        // ...by limiting the value to 0...max
+        camera = PixelCoordinate(
+            value_between(0, target.x, max.x),
+            value_between(0, target.y, max.y)
+        );
+    }
+    
+    // Black background
+    gc->clear(CL_Color::black);
+
+    // Draw the game
+    state.draw(display, camera, flags & GUI_INPUT_DISPLAY_WEAPON);
+    
+    // draw info view?
+    if (info_view)
+        info_view->draw(display);
+
+    // draw messages
+    message_view.draw(display);
+}
+
+void GameView::resize (const PixelArea &new_area) {
+    View::resize(new_area);
+    
+    // resize subcomponents
+    if (info_view)
+        info_view->resize(getInfoViewArea());
+    
+    message_view.resize(getMessageViewArea());
+
+    // log message
+    message_view.add_message(CL_Color::yellow, CL_String::format("[ Resized window to %1 x %2 ]", (int) getWidth(), (int) getHeight()));
+}
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Graphics/GameView.hh	Thu Jan 22 03:02:43 2009 +0200
@@ -0,0 +1,89 @@
+#ifndef GRAPHICS_GAME_VIEW_HH
+#define GRAPHICS_GAME_VIEW_HH
+
+#include "Drawable.hh"
+#include "PlayerInfoView.hh"
+#include "MessageView.hh"
+#include "Input.hh"
+#include "../GameState.hh"
+
+
+namespace graphics
+{
+
+/**
+ * This is the main in-game view, which is what the player sees when they are playing.
+ *
+ * This enables graphics->input.gui/player
+ */    
+class GameView : public View {
+protected:
+    /** The GameState that we're drawing */
+    GameState &state;
+    
+    /** The player that we are controlling, if any */
+    LocalPlayer *player;
+
+    /**
+     * The PlayerInfo view is built once we have a player
+     */
+    PlayerInfoView *info_view;
+
+    /**
+     * The message list view
+     */
+    MessageView message_view;
+    
+    /**
+     * Input flags
+     */
+    GuiInput flags;
+
+    CL_SlotContainer slots;
+
+public:
+    /**
+     * Constructed once the game is running
+     */ 
+    GameView (GameState &state, LocalPlayer *player);
+
+    /**
+     * Set a player where none was set before
+     */
+    void setPlayer (LocalPlayer *player);
+
+private:
+    /**
+     * Calculate new area for the info_view based on our own area
+     */
+    PixelArea getInfoViewArea (void) {
+        return PixelArea(0, area.bottom - 100, area.right, area.bottom);
+    }
+
+    /**
+     * Calculate new area for the message view
+     */
+    PixelArea getMessageViewArea (void) {
+        return PixelArea(400, area.bottom - 100, area.right, area.bottom);
+    }
+
+    /**
+     * Handle GUI input
+     */
+    void handleInput (GuiInput flags, TimeMS dt);
+
+public:    
+    
+    /**
+     * Draw this view onto the given display
+     */
+    virtual void draw (Display &display);
+    
+    /**
+     * Resize sub-views
+     */
+    virtual void resize (const PixelArea &new_area);
+};
+
+}
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Graphics/Graphics.cc	Thu Jan 22 03:02:43 2009 +0200
@@ -0,0 +1,26 @@
+
+#include "Graphics.hh"
+
+#include <cassert>
+
+namespace graphics
+{
+
+// initialize the global graphics object
+Graphics *graphics = NULL;
+
+Graphics::Graphics (Engine &engine, CL_ResourceManager &resources, const DisplayConfig &display_config) :
+    engine(engine), display(display_config), 
+    // display must be set up before fonts, due to implicit CL_DisplayWindow
+    fonts(resources), 
+    // get the InputContext from display
+    input(display.get_ic()->get_keyboard())
+{
+    assert(!graphics);
+
+    // set the global graphics object
+    graphics = this;
+    
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Graphics/Graphics.hh	Thu Jan 22 03:02:43 2009 +0200
@@ -0,0 +1,75 @@
+#ifndef GRAPHICS_GRAPHICS_HH
+#define GRAPHICS_GRAPHICS_HH
+
+namespace graphics
+{
+
+class Graphics;
+
+}
+
+#include "../Engine.hh"
+#include "Display.hh"
+#include "FontManager.hh"
+#include "Input.hh"
+#include "GameView.hh"
+
+namespace graphics
+{
+
+/**
+ * Core class that ties everything else together
+ */
+class Graphics {
+public:
+    /**
+     * Our reference to the engine
+     */
+    Engine &engine;
+
+    /**
+     * Our primary display
+     */
+    Display display;
+
+    /**
+     * For loading fonts
+     */
+    FontManager fonts;
+
+    /**
+     * Input handling
+     *
+     * XXX: move Input class into this?
+     */
+    Input input;
+   
+    /**
+     * Initialize the graphics subsystem
+     */
+    Graphics (Engine &engine, CL_ResourceManager &resources, const DisplayConfig &display_config);
+
+    /**
+     * Display a new GameView
+     */
+    GameView* displayGameView (GameState &state, LocalPlayer *player) {
+        // allocate a new GameView
+        GameView *view = new GameView(state, player);
+
+        // assign it to the display
+        display.setView(view);
+        
+        // return it
+        return view;
+    }
+
+};
+
+/**
+ * The global Graphics instance
+ */
+extern Graphics *graphics;
+
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Graphics/Input.cc	Thu Jan 22 03:02:43 2009 +0200
@@ -0,0 +1,254 @@
+
+#include "Input.hh"
+#include "../Error.hh"
+#include "../Config.hh"
+
+#include <cassert>
+
+namespace graphics
+{
+
+InputKeymapEntry<PlayerInputBit> INPUT_PLAYER_KEYMAP[] = {
+    {   INPUT_AIM_UP,       INPUT_FLAG_UNLIMITED,   { -CL_KEY_ENTER,    CL_KEY_UP       } },
+    {   INPUT_AIM_DOWN,     INPUT_FLAG_UNLIMITED,   { -CL_KEY_ENTER,    CL_KEY_DOWN     } },
+    {   INPUT_MOVE_LEFT,    INPUT_FLAG_UNLIMITED,   { -CL_KEY_ENTER,    CL_KEY_LEFT     } },
+    {   INPUT_MOVE_RIGHT,   INPUT_FLAG_UNLIMITED,   { -CL_KEY_ENTER,    CL_KEY_RIGHT    } },
+    {   INPUT_JUMP,         INPUT_FLAG_SLOWREPEAT,  { -CL_KEY_ENTER,    CL_KEY_RSHIFT   } },
+    {   INPUT_DIG,          INPUT_FLAG_NOREPEAT,    { CL_KEY_LEFT,      CL_KEY_RIGHT    } },
+    {   INPUT_SHOOT,        INPUT_FLAG_UNLIMITED,   { CL_KEY_RCONTROL,  0               } },
+    {   INPUT_CHANGE_PREV,  INPUT_FLAG_SLOWREPEAT,  { CL_KEY_ENTER,     CL_KEY_LEFT     } },
+    {   INPUT_CHANGE_NEXT,  INPUT_FLAG_SLOWREPEAT,  { CL_KEY_ENTER,     CL_KEY_RIGHT    } },
+    {   INPUT_ROPE,         INPUT_FLAG_NOREPEAT,    { CL_KEY_ENTER,     CL_KEY_RSHIFT   } },
+    {   INPUT_UNROPE,       INPUT_FLAG_SLOWREPEAT,  { -CL_KEY_ENTER,    CL_KEY_RSHIFT   } },
+    {   INPUT_ROPE_UP,      INPUT_FLAG_UNLIMITED,   { CL_KEY_ENTER,     CL_KEY_UP       } },
+    {   INPUT_ROPE_DOWN,    INPUT_FLAG_UNLIMITED,   { CL_KEY_ENTER,     CL_KEY_DOWN     } },
+    {   INPUT_SUICIDE,      INPUT_FLAG_NOREPEAT,    { CL_KEY_LCONTROL,  CL_KEY_K        } },
+    {   INPUT_NONE,         0,                      { 0,                0               } }
+};
+
+InputKeymapEntry<GuiInputBit> INPUT_GUI_KEYMAP[] = {
+    {   GUI_INPUT_QUIT,                 0,                  { CL_KEY_ESCAPE,    0               } },
+    {   GUI_INPUT_DISPLAY_WEAPON,       0,                  { CL_KEY_ENTER,     0               } },
+    {   GUI_INPUT_DEBUG_PLAYER,         0,                  { CL_KEY_I,         0               } },
+    {   GUI_INPUT_TOGGLE_FULLSCREEN,    INPUT_FLAG_NOREPEAT,{ CL_KEY_LCONTROL,  CL_KEY_F        } },
+    {   GUI_INPUT_NONE,                 0,                  { 0,                0,              } }
+};
+
+/*
+ * InputKeyRepeatEntry
+ */
+template <typename BitEnumType> 
+InputKeyRepeatEntry<BitEnumType>::InputKeyRepeatEntry (BitEnumType value, TimeMS expire) : 
+    value(value), expire(expire) 
+{
+
+}
+
+template <typename BitEnumType> 
+bool InputKeyRepeatEntry<BitEnumType>::operator< (const struct InputKeyRepeatEntry &other) {
+    return other.expire > expire;
+}
+
+template <typename BitEnumType> 
+bool InputKeyRepeatEntry<BitEnumType>::updateExpired (TimeMS dt) {
+    if (expire == 0)
+        return false;
+
+    expire -= dt;
+
+    return (expire <= 0);
+}
+
+/*
+ * InputKeyRepeatQueue
+ */
+template <typename BitEnumType> 
+InputKeyRepeatQueue<BitEnumType>::InputKeyRepeatQueue (TimeMS expire) :
+    expire(expire)
+{
+
+}
+
+template <typename BitEnumType> 
+void InputKeyRepeatQueue<BitEnumType>::push (BitEnumType bit, bool expire) {
+    list.push_back(InputKeyRepeatEntry<BitEnumType>(bit, expire ? this->expire : 0));
+}
+
+template <typename BitEnumType> 
+void InputKeyRepeatQueue<BitEnumType>::forget (BitEnumType bit) {
+    // go through the list, looking for it
+    for (list_iterator it = list.begin(); it != list.end(); it++) {
+        if (it->value == bit) {
+            // found, erase it and return
+            list.erase(it);
+            
+            return;
+        }
+    }
+}
+
+template <typename BitEnumType> 
+bool InputKeyRepeatQueue<BitEnumType>::find (BitEnumType bit) {
+    for (list_iterator it = list.begin(); it != list.end(); it++) {
+        if (it->value == bit)
+            return true;
+    }
+
+    return false;
+}
+
+template <typename BitEnumType> 
+void InputKeyRepeatQueue<BitEnumType>::update (TimeMS dt) {
+    list_iterator it = list.begin();
+    
+    // go through each entry, updateExpired and remove if expired
+    while (it != list.end()) {
+        if (it->updateExpired(dt))
+            it = list.erase(it);
+        else
+            it++;
+    }
+}
+
+template <typename BitEnumType> 
+void InputKeyRepeatQueue<BitEnumType>::clear (void) {
+    // just clear our list of events
+    list.clear();
+}
+
+/*
+ * InputHandler
+ */
+template <typename BitEnumType, typename BitMaskType> 
+InputHandler<BitEnumType, BitMaskType>::InputHandler (CL_InputDevice &keyboard, InputKeymapEntry<BitEnumType> *keymap, TimeMS keyrepeat_expire) :
+    keyboard(keyboard), 
+    keymap(keymap), 
+    value(0), 
+    prev_value(0),
+    dt(0),
+    queue(keyrepeat_expire),
+    _enabled(false)
+{
+
+}
+
+template <typename BitEnumType, typename BitMaskType> 
+bool InputHandler<BitEnumType, BitMaskType>::checkKeycode (int keycode) {
+    if (keycode > 0)
+        return keyboard.get_keycode(keycode);
+
+    else if (keycode < 0)
+        return !keyboard.get_keycode(-keycode);
+
+    else // == 0
+        return true;
+}
+ 
+template <typename BitEnumType, typename BitMaskType> 
+void InputHandler<BitEnumType, BitMaskType>::update (TimeMS dt) {
+    // ignore if not enabled
+    if (!_enabled)
+        return;
+
+    // all bits that are held down, even those ignored
+    BitMaskType raw_value = 0, this_value = 0;
+
+    // update the key-repeat queue
+    queue.update(dt);
+
+    // then go through the keymap
+    for (InputKeymapEntry<BitEnumType> *e = keymap; e->input != 0; e++) {
+        // check if we've got the correct keycodes
+        if (checkKeycode(e->keycodes[0]) && checkKeycode(e->keycodes[1])) {
+            // set raw_value
+            raw_value |= e->input;
+
+            if (e->flags & INPUT_FLAG_SLOWREPEAT) {
+                // repeat, but slowly
+                if (!(prev_value & e->input)) {
+                    // we've released the key earlier, move it to the back of the queue
+                    queue.forget(e->input);
+                    queue.push(e->input);
+
+                } else if (queue.find(e->input)) {
+                    // it's still in the queue, so ignore, but set it in ignore_value
+                    continue;
+
+                } else {
+                    // ok, but add it to the queue
+                    queue.push(e->input);
+                }
+
+            } else if (e->flags & INPUT_FLAG_NOREPEAT) {
+                // do not repeat at all
+                if (prev_value & e->input) {
+                    // ignore repeats
+                    continue;
+                }
+            }
+
+            // set bit in value mask
+            this_value |= e->input;
+        }
+    }
+
+    // signal unless value was and remains zero
+    if (this_value || prev_value) {
+        // trigger signal
+        _sig_input(this_value, dt);
+    } 
+
+    // update prev_value, also adding ignored values
+    prev_value = raw_value;
+
+    // update our collective value + dt for use with readValue
+    // XXX: remove this functionality?
+    value |= this_value;
+    this->dt += dt;
+}
+ 
+template <typename BitEnumType, typename BitMaskType> 
+void InputHandler<BitEnumType, BitMaskType>::readValue (BitMaskType &mask, TimeMS &dt) {
+    // throw exception if disabled
+    if (!_enabled)
+        throw Error("InputHandler is disabled");
+
+    // copy to args
+    mask = this->value;
+    dt = this->dt;
+
+    this->value = 0;
+    this->dt = 0;
+}
+       
+/**
+ * Input
+ */
+Input::Input (CL_InputDevice &keyboard) :
+    keyboard(keyboard),
+    update_timer(INPUT_POLL_INTERVAL),
+    player(keyboard, INPUT_PLAYER_KEYMAP, INPUT_REPEAT_DELAY),
+    gui(keyboard, INPUT_GUI_KEYMAP, INPUT_REPEAT_DELAY)
+{
+    // connect timer 
+    slots.connect(update_timer.sig_tick(), &player, &InputHandler<PlayerInputBit, PlayerInput>::update);
+    slots.connect(update_timer.sig_tick(), &gui, &InputHandler<GuiInputBit, GuiInput>::update);
+
+    // enable timer
+    update_timer.start();
+}
+
+void Input::readPlayerInput (PlayerInput &mask, TimeMS &dt) {
+    player.readValue(mask, dt);
+}
+
+GuiInput Input::readGuiInput (void) {
+    GuiInput mask;
+    TimeMS dt;
+
+    gui.readValue(mask, dt);
+
+    return mask;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Graphics/Input.hh	Thu Jan 22 03:02:43 2009 +0200
@@ -0,0 +1,348 @@
+#ifndef GRAPHICS_INPUT_HH
+#define GRAPHICS_INPUT_HH
+
+#include "../Input.hh"
+#include "../Timer.hh"
+#include "../Types.hh"
+
+#include <ClanLib/signals.h>
+#include <ClanLib/Display/input_device.h>
+#include <ClanLib/Display/keys.h>
+
+#include <list>
+
+namespace graphics
+{
+
+/**
+ * Flags to control input behaviour
+ */
+enum InputFlagBits {
+    /** Default */
+
+    /**
+     * The bit is not limited, i.e. it is set every time if it's present
+     */
+    INPUT_FLAG_UNLIMITED    = 0x0000,
+
+    /**
+     * The bit is repeat-limited using INPUT_REPEAT_DELAY
+     */
+    INPUT_FLAG_SLOWREPEAT   = 0x0001,
+
+    /**
+     * The bit is repeat-limited using an infinite delay, i.e. you must release the key to trigger it again
+     */
+    INPUT_FLAG_NOREPEAT     = 0x0002,
+};
+
+
+/**
+ * The bits used in the GuiInput bitmask, each represents something handled locally by Graphics.
+ */
+enum GuiInputBit {
+    GUI_INPUT_NONE              = 0x0000,
+
+    GUI_INPUT_QUIT              = 0x0001,
+    GUI_INPUT_DISPLAY_WEAPON    = 0x0002,
+    GUI_INPUT_DEBUG_PLAYER      = 0x0004,
+
+    GUI_INPUT_TOGGLE_FULLSCREEN = 0x0008,
+};
+
+/**
+ * Bitmask of InputFlagBits
+ */
+typedef uint8_t InputFlags;
+
+/**
+ * Bitmask for GuiInputBits
+ *
+ * @see GuiInputBit
+ */
+typedef uint16_t GuiInput;
+
+/**
+ * Keymap definition struct
+ */
+template <typename BitEnumType> struct InputKeymapEntry {
+    /**
+     * The input bit to set if present
+     */
+    BitEnumType input;
+
+    /**
+     * Flags to use
+     *
+     * @see InputFlagBits
+     */
+    InputFlags flags;
+
+    /**
+     * Up to two keycodes to check
+     */
+    int keycodes[2];
+};
+
+
+/**
+ * A InputKeyRepeatQueue entry, this contains the input bit value itself, and then the remaining expire time
+ */
+template <typename BitEnumType> struct InputKeyRepeatEntry {
+        BitEnumType value;
+
+        /**
+         * The remaining expire time. If this is zero, it never expires
+         */
+        TimeMS expire;
+
+    public:
+        InputKeyRepeatEntry (BitEnumType value, TimeMS expire);
+        
+        /**
+         * Since priority_queue always gives the greatest item, the one with the longest expire is the least item
+         */
+        bool operator< (const struct InputKeyRepeatEntry &other);
+        
+        /**
+         * Decrements expire, returning true if it has now expired, false otherwise. Always returns false if expire is
+         * zero.
+         */
+        bool updateExpired (TimeMS dt);
+};
+
+/**
+ * A InputKeyRepeatQueue maintains a list of InputKeyRepeatEntry's, lets you add new input values, find old ones,
+ * and update the list
+ */
+template <typename BitEnumType> class InputKeyRepeatQueue {
+    private:
+        TimeMS expire;
+
+        typedef InputKeyRepeatEntry<BitEnumType> entry_type;
+
+        std::list<entry_type> list;
+
+        typedef typename std::list<entry_type>::iterator list_iterator;
+
+    public:
+        /**
+         * Constructs this queue to contain entries with the given expiry time
+         */
+        InputKeyRepeatQueue (TimeMS expire);
+        
+        /**
+         * Push a new input bit onto the queue.
+         *
+         * If expire is true, the bit will automatically expire after our expire time, otherwise, it iwll never expire
+         * until forget()'d
+         */
+        void push (BitEnumType bit, bool expire = true);
+
+        /**
+         * Remove any entry for the given bit
+         */
+        void forget (BitEnumType bit);
+
+        /**
+         * Checks if the given input is in the queue
+         */
+        bool find (BitEnumType bit);
+        
+        /**
+         * Updates the list, removing expired items
+         */
+        void update (TimeMS dt);
+
+        /**
+         * Clear the queue completely
+         */
+        void clear (void);
+};
+
+/**
+ * An InputHandler uses an InputKeymapEntry to maintain a BitMaskType of current inputs
+ */
+template <typename BitEnumType, typename BitMaskType> class InputHandler {
+    private:
+        /**
+         * The keyboard that we read input from
+         */
+        CL_InputDevice &keyboard;
+        
+        /**
+         * The keymap that we use
+         */
+        InputKeymapEntry<BitEnumType> *keymap;
+        
+        /**
+         * The current bitmask value
+         */
+        BitMaskType value;
+
+        /**
+         * The previous value, used to detect key-up. This also includes keys that were filtered out
+         */
+        BitMaskType prev_value;
+
+        /**
+         * How long the bitmask was held...
+         */
+        TimeMS dt;
+        
+        /**
+         * The KeyRepeatQueue
+         */
+        InputKeyRepeatQueue<BitEnumType> queue;
+
+        /**
+         * Are we enabled or not?
+         */
+        bool _enabled;
+
+        /**
+         * The keyevent signal
+         */
+        CL_Signal_v2<BitMaskType, TimeMS> _sig_input;
+    
+    public:
+        /**
+         * Constructs the InputHandler using the given keyboard, keymap and key-repeat expire time.
+         *
+         * The InputHandler is initially disabled, and must be enabled using enable() for use. 
+         */
+        InputHandler (CL_InputDevice &keyboard, InputKeymapEntry<BitEnumType> *keymap, TimeMS keyrepeat_expire);
+    
+    private: 
+        /**
+         * Returns true if the keycode is valid, false if not.
+         *
+         * A positive keycode requires that the keycode be active, a negative keycode that the keycode be inactive,
+         * and a zero keycode always returns true.
+         *
+         * @param keycode A positive keycode to check that it's set, negative keycode to check that it's not set, or zero
+         * @returns bool true if positive+set/negavtive+notset/zero, false otherwise
+         */
+        bool checkKeycode (int keycode);
+    
+    public:
+        /**
+         * Updates the keyrepeat queue
+         */
+        void update (TimeMS dt);
+
+        /**
+         * Reads the current input mask value and length into mask and dt, and reset ours to zero.
+         *
+         * It is an error to attempt to read input while disabled.
+         *
+         * @param mask our BitMaskType value is returned using this
+         * @param dt how long the input was held for
+         */
+        void readValue (BitMaskType &mask, TimeMS &dt);
+        
+        /**
+         * Enables this input handler, collecting bits in value
+         */
+        void enable (void);
+
+        /**
+         * Disables this input handler, zeroing any state, so that enable() works cleanly
+         */
+        void disable (void);
+
+        /**
+         * Current enable/disable state
+         */
+        bool enabled (void) const { return _enabled; }
+
+        /**
+         * This signal is triggered whenever there is nonzero input, or when the input goes to zero
+         */
+        CL_Signal_v2<BitMaskType, TimeMS>& sig_input (void) { return _sig_input; }
+};
+
+/**
+ * Handles reading input from a keyboard and mapping it to PlayerInput/GuiInput bitmasks
+ */
+class Input {
+    protected:
+        /**
+         * The keyboard device that we use
+         */
+        CL_InputDevice &keyboard;
+
+        /**
+         * Our update timer
+         */
+        Timer update_timer;
+
+        CL_SlotContainer slots;
+    
+    public:        
+        /**
+         * Our PlayerInput
+         */
+        InputHandler<PlayerInputBit, PlayerInput> player;
+        
+        /**
+         * Our GuiInput
+         */
+        InputHandler<GuiInputBit, GuiInput> gui;
+
+    public:
+        /**
+         * Build the input handler using the given keyboard and the default keymaps
+         */
+        Input (CL_InputDevice &keyboard);
+
+    public:
+        /**
+         * Reads the current PlayerInput value via mask, and the length of the input via dt
+         */
+        void readPlayerInput (PlayerInput &mask, TimeMS &dt);
+
+        /**
+         * Reads the current GuiInput mask and returns it
+         */
+        GuiInput readGuiInput (void);
+};
+
+}
+
+
+
+/*
+ * Public template class method definitions
+ */
+#include <cassert>
+
+namespace graphics
+{
+
+template <typename BitEnumType, typename BitMaskType> 
+void InputHandler<BitEnumType, BitMaskType>::enable (void) {
+    // sanity-check
+    assert(!_enabled);
+
+    // update state
+    _enabled = true;
+}
+
+template <typename BitEnumType, typename BitMaskType> 
+void InputHandler<BitEnumType, BitMaskType>::disable (void) {
+    // sanity-check
+    assert(_enabled);
+
+    // update state
+    value = prev_value = 0;
+    _enabled = false;
+
+    // and clear keyrepeat list
+    queue.clear();
+}
+       
+
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Graphics/MessageView.cc	Thu Jan 22 03:02:43 2009 +0200
@@ -0,0 +1,59 @@
+
+#include "MessageView.hh"
+#include "Graphics.hh"
+
+namespace graphics
+{
+
+MessageView::MessageView (const PixelArea &area) :
+    View(area), messages()
+{
+    
+}
+
+void MessageView::add_message (CL_Color color, std::string message) {
+    Message msg (color, message);
+
+    messages.push_back(msg);
+}
+
+void MessageView::draw (Display &display) {
+    // get font
+    CL_Font &font = graphics->fonts.getSimpleFont(); 
+
+    // remember color
+    CL_Color font_color = font.get_color();
+    
+    // maximum width
+    CL_Size max_size = CL_Size(area.right - area.left, 0);
+
+    // starting point for drawing
+    PixelDimension offset_prev = area.bottom;
+
+    // render messages, bottom up
+    for (std::vector<Message>::reverse_iterator it = messages.rbegin(); it != messages.rend(); it++) {
+        // set message color
+        font.set_color(it->color);
+
+        // calculate height
+        PixelDimension height = font.get_height(it->message, max_size) + MESSAGE_VIEW_LINE_OFFSET;
+       
+        // new draw_point
+        PixelDimension offset_this = offset_prev - height;
+
+        // break if it doesn't fit anymore
+        if (offset_this < area.top)
+            break;
+ 
+        // draw text
+        font.draw(CL_Rect(area.left, offset_this, area.right, offset_prev), it->message, display.get_gc());
+        
+        // advance offset
+        offset_prev = offset_this;
+    }
+
+    // restore font color
+    font.set_color(font_color);
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Graphics/MessageView.hh	Thu Jan 22 03:02:43 2009 +0200
@@ -0,0 +1,48 @@
+#ifndef GRAPHICS_MESSAGE_VIEW_HH
+#define GRAPHICS_MESSAGE_VIEW_HH
+
+#include "View.hh"
+
+#include <ClanLib/display.h>
+#include <string>
+#include <vector>
+
+namespace graphics
+{
+
+/**
+ * Offset between consecutive lines
+ */
+const PixelDimension MESSAGE_VIEW_LINE_OFFSET = 5;
+
+struct Message {
+    CL_Color color;
+    std::string message;
+
+    Message (CL_Color color, std::string message) : color(color), message(message) { }
+};
+
+class MessageView : public View {
+protected:
+    std::vector<Message> messages;
+
+public:
+    /**
+     * Define the area where messages are drawn
+     */
+    MessageView (const PixelArea &area);
+
+    /**
+     * Add a message to the list of messages displayed
+     */
+    void add_message (CL_Color color, std::string message);
+    
+    /**
+     * Draw as many messages as fits
+     */
+    virtual void draw (Display &display);
+};
+
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Graphics/PlayerInfoView.cc	Thu Jan 22 03:02:43 2009 +0200
@@ -0,0 +1,125 @@
+
+#include "PlayerInfoView.hh"
+#include "Graphics.hh"
+
+#include <sstream>
+
+namespace graphics
+{
+
+void PlayerInfoView::draw (Display &display) {
+    CL_GraphicContext *gc = display.get_gc();
+    CL_Font font = graphics->fonts.getSimpleFont();
+
+    int bar_length = 3;
+
+    // draw status info at bottom of display
+    gc->fill_rect(
+        CL_Rect(area.left, area.top, area.right, area.bottom),
+        CL_Gradient(
+            CL_Color(0, 0, 0),
+            CL_Color(50, 50, 50),
+            CL_Color(50, 50, 50, 150),
+            CL_Color(100, 100, 100, 200)
+        )
+    );
+    
+    // Health
+    double health_percent = player->getHealthPercent();
+
+    gc->draw_rect(
+        CL_Rect(
+            area.left + 9,
+            area.top + 9,
+            area.left + 11 + 100 * bar_length,
+            area.top + 31
+        ),
+        CL_Color(150, 150, 150)
+    );
+
+    gc->fill_rect(
+        CL_Rect(
+            area.left + 10,
+            area.top + 10,
+            area.left + 10 + (int) (health_percent * bar_length),
+            area.top + 30
+        ),
+        CL_Gradient(
+            CL_Color(200, 0, 0),
+            CL_Color(200 - (int)(health_percent * 2), (int)(health_percent * 2), 0),
+            CL_Color(200, 0, 0),
+            CL_Color(200 - (int)(health_percent * 2), (int)(health_percent * 2), 0)
+        )
+    );
+
+    // stats - kills
+    std::stringstream sskills;
+    sskills << "Kills:  " << player->getKills();
+    font.draw(
+        area.left + 20 + 100 * bar_length,
+        area.top + 10,
+        sskills.str(),
+        gc
+    );
+    
+    // stats - deaths
+    std::stringstream ssdeaths;
+    ssdeaths << "Deaths:  " << player->getDeaths();
+    font.draw(
+        area.left + 20 + 100 * bar_length,
+        area.top + 30,
+        ssdeaths.str(),
+        gc
+    );
+    
+    // stats - ratio
+    std::stringstream ssratio;
+    ssratio << "Ratio:  " << (player->getKills() + 1) / (player->getDeaths() + 1);
+    font.draw(
+        area.left + 20 + 100 * bar_length,
+        area.top + 50,
+        ssratio.str(),
+        gc
+    );
+    
+
+    // Weapon clip / reloading
+    gc->draw_rect(
+        CL_Rect(
+            area.left + 9,
+            area.top + 69,
+            area.left + 11 + 100 * bar_length,
+            area.top + 91
+        ),
+        CL_Color(150, 150, 150)
+    );
+
+    gc->fill_rect(
+        CL_Rect(
+            area.left + 10,
+            area.top + 70,
+            area.left + 10 + (100 - (int) (
+                    player->getCurrentWeapon()->getReloadTimer() * 100 / player->getCurrentWeapon()->getReloadTime()
+            )) * bar_length,
+            area.top + 90
+        ),
+        CL_Gradient(
+            CL_Color(100, 100, 0),
+            CL_Color(100, 100, 0),
+            CL_Color(100, 100, 0),
+            CL_Color(100, 100, 100)
+        )
+    );
+   
+    // current weapon name
+    font.draw(
+        area.left + 20 + 100 * bar_length,
+        area.top + 70,
+        player->getCurrentWeapon()->getName(),
+        gc
+    );
+}
+
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Graphics/PlayerInfoView.hh	Thu Jan 22 03:02:43 2009 +0200
@@ -0,0 +1,37 @@
+#ifndef GRAPHICS_PLAYER_INFO_VIEW_HH
+#define GRAPHICS_PLAYER_INFO_VIEW_HH
+
+#include "View.hh"
+#include "../Player.hh"
+
+namespace graphics
+{
+
+class PlayerInfoView : public View {
+private:
+    /** 
+     * The player whose info we are drawing 
+     *
+     * XXX: should this be LocalPlayer or is Player good?
+     */
+    Player *player;
+
+public:
+    /**
+     * Set initial view area and player
+     */
+    PlayerInfoView (const PixelArea &area, Player *player) :
+        View(area), player(player)
+    {
+
+    }
+    
+    /**
+     * Draw the player info onto the given display
+     */
+    virtual void draw (Display &display);
+};
+
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Graphics/View.hh	Thu Jan 22 03:02:43 2009 +0200
@@ -0,0 +1,67 @@
+#ifndef GRAPHICS_VIEW_HH
+#define GRAPHICS_VIEW_HH
+
+namespace graphics
+{
+
+class View;
+
+}
+
+#include "Drawable.hh"
+#include "Display.hh"
+#include "../Types.hh"
+
+namespace graphics
+{
+
+/**
+ * A view is some area of the display that displays something
+ */
+class View /* : public Drawable */ {
+protected:
+    /**
+     * The area of the screen that is ours to draw on
+     */
+    PixelArea area;
+
+public:
+    /**
+     * Set the initial area that is drawn into
+     */
+    View (const PixelArea &area) :
+        area(area)
+    {
+
+    }
+
+    /**
+     * Draw into the view area
+     */
+    virtual void draw (Display &display) = 0;
+
+    /**
+     * Update the view area
+     */
+    virtual void resize (const PixelArea &new_area) {
+        this->area = new_area;
+    }
+
+    /**
+     * Get current width
+     */
+    PixelDimension getWidth (void) const {
+        return area.right - area.left;
+    }
+
+    /**
+     * Get current height
+     */
+    PixelDimension getHeight (void) const {
+        return area.bottom - area.top;
+    }
+};
+
+}
+
+#endif
--- a/src/GraphicsPointer.hh	Wed Jan 21 00:21:42 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,9 +0,0 @@
-#ifndef GRAPHICS_POINTER_HH
-#define GRAPHICS_POINTER_HH
-
-/*
- * Break our #include-loop dependancy
- */
-class Graphics;
-
-#endif 
--- a/src/Input.cc	Wed Jan 21 00:21:42 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,225 +0,0 @@
-
-#include "Input.hh"
-#include "Engine.hh"
-#include "Config.hh"
-
-InputKeymapEntry<PlayerInputBit> INPUT_PLAYER_KEYMAP[] = {
-    {   INPUT_AIM_UP,       INPUT_FLAG_UNLIMITED,   { -CL_KEY_ENTER,    CL_KEY_UP       } },
-    {   INPUT_AIM_DOWN,     INPUT_FLAG_UNLIMITED,   { -CL_KEY_ENTER,    CL_KEY_DOWN     } },
-    {   INPUT_MOVE_LEFT,    INPUT_FLAG_UNLIMITED,   { -CL_KEY_ENTER,    CL_KEY_LEFT     } },
-    {   INPUT_MOVE_RIGHT,   INPUT_FLAG_UNLIMITED,   { -CL_KEY_ENTER,    CL_KEY_RIGHT    } },
-    {   INPUT_JUMP,         INPUT_FLAG_SLOWREPEAT,  { -CL_KEY_ENTER,    CL_KEY_RSHIFT   } },
-    {   INPUT_DIG,          INPUT_FLAG_NOREPEAT,    { CL_KEY_LEFT,      CL_KEY_RIGHT    } },
-    {   INPUT_SHOOT,        INPUT_FLAG_UNLIMITED,   { CL_KEY_RCONTROL,  0               } },
-    {   INPUT_CHANGE_PREV,  INPUT_FLAG_SLOWREPEAT,  { CL_KEY_ENTER,     CL_KEY_LEFT     } },
-    {   INPUT_CHANGE_NEXT,  INPUT_FLAG_SLOWREPEAT,  { CL_KEY_ENTER,     CL_KEY_RIGHT    } },
-    {   INPUT_ROPE,         INPUT_FLAG_NOREPEAT,    { CL_KEY_ENTER,     CL_KEY_RSHIFT   } },
-    {   INPUT_UNROPE,       INPUT_FLAG_SLOWREPEAT,  { -CL_KEY_ENTER,    CL_KEY_RSHIFT   } },
-    {   INPUT_ROPE_UP,      INPUT_FLAG_UNLIMITED,   { CL_KEY_ENTER,     CL_KEY_UP       } },
-    {   INPUT_ROPE_DOWN,    INPUT_FLAG_UNLIMITED,   { CL_KEY_ENTER,     CL_KEY_DOWN     } },
-    {   INPUT_SUICIDE,      INPUT_FLAG_NOREPEAT,    { CL_KEY_LCONTROL,  CL_KEY_K        } },
-    {   INPUT_NONE,         0,                      { 0,                0               } }
-};
-
-InputKeymapEntry<GuiInputBit> INPUT_GUI_KEYMAP[] = {
-    {   GUI_INPUT_QUIT,                 0,                  { CL_KEY_ESCAPE,    0               } },
-    {   GUI_INPUT_DISPLAY_WEAPON,       0,                  { CL_KEY_ENTER,     0               } },
-    {   GUI_INPUT_DEBUG_PLAYER,         0,                  { CL_KEY_I,         0               } },
-    {   GUI_INPUT_TOGGLE_FULLSCREEN,    INPUT_FLAG_NOREPEAT,{ CL_KEY_LCONTROL,  CL_KEY_F        } },
-    {   GUI_INPUT_NONE,                 0,                  { 0,                0,              } }
-};
-
-/*
- * InputKeyRepeatEntry
- */
-template <typename BitEnumType> 
-InputKeyRepeatEntry<BitEnumType>::InputKeyRepeatEntry (BitEnumType value, TimeMS expire) : 
-    value(value), expire(expire) 
-{
-
-}
-
-template <typename BitEnumType> 
-bool InputKeyRepeatEntry<BitEnumType>::operator< (const struct InputKeyRepeatEntry &other) {
-    return other.expire > expire;
-}
-
-template <typename BitEnumType> 
-bool InputKeyRepeatEntry<BitEnumType>::updateExpired (TimeMS dt) {
-    if (expire == 0)
-        return false;
-
-    expire -= dt;
-
-    return (expire <= 0);
-}
-
-/*
- * InputKeyRepeatQueue
- */
-template <typename BitEnumType> 
-InputKeyRepeatQueue<BitEnumType>::InputKeyRepeatQueue (TimeMS expire) :
-    expire(expire)
-{
-
-}
-
-template <typename BitEnumType> 
-void InputKeyRepeatQueue<BitEnumType>::push (BitEnumType bit, bool expire) {
-    list.push_back(InputKeyRepeatEntry<BitEnumType>(bit, expire ? this->expire : 0));
-}
-
-template <typename BitEnumType> 
-void InputKeyRepeatQueue<BitEnumType>::forget (BitEnumType bit) {
-    // go through the list, looking for it
-    for (list_iterator it = list.begin(); it != list.end(); it++) {
-        if (it->value == bit) {
-            // found, erase it and return
-            list.erase(it);
-            
-            return;
-        }
-    }
-}
-
-template <typename BitEnumType> 
-bool InputKeyRepeatQueue<BitEnumType>::find (BitEnumType bit) {
-    for (list_iterator it = list.begin(); it != list.end(); it++) {
-        if (it->value == bit)
-            return true;
-    }
-
-    return false;
-}
-
-template <typename BitEnumType> 
-void InputKeyRepeatQueue<BitEnumType>::update (TimeMS dt) {
-    list_iterator it = list.begin();
-    
-    // go through each entry, updateExpired and remove if expired
-    while (it != list.end()) {
-        if (it->updateExpired(dt))
-            it = list.erase(it);
-        else
-            it++;
-    }
-}
-
-/*
- * InputHandler
- */
-template <typename BitEnumType, typename BitMaskType> 
-InputHandler<BitEnumType, BitMaskType>::InputHandler (CL_InputDevice &keyboard, InputKeymapEntry<BitEnumType> *keymap, TimeMS keyrepeat_expire) :
-    keyboard(keyboard), 
-    keymap(keymap), 
-    value(0), 
-    prev_value(0),
-    dt(0),
-    queue(keyrepeat_expire)
-{
-
-}
-
-template <typename BitEnumType, typename BitMaskType> 
-bool InputHandler<BitEnumType, BitMaskType>::checkKeycode (int keycode) {
-    if (keycode > 0)
-        return keyboard.get_keycode(keycode);
-
-    else if (keycode < 0)
-        return !keyboard.get_keycode(-keycode);
-
-    else // == 0
-        return true;
-}
-        
-template <typename BitEnumType, typename BitMaskType> 
-void InputHandler<BitEnumType, BitMaskType>::readValue (BitMaskType &mask, TimeMS &dt) {
-    // copy to args
-    mask = this->value;
-    dt = this->dt;
-
-    this->value = 0;
-    this->dt = 0;
-}
-
-template <typename BitEnumType, typename BitMaskType> 
-void InputHandler<BitEnumType, BitMaskType>::update (TimeMS dt) {
-    // all bits that are held down, even those ignored
-    BitMaskType raw_value = 0;
-
-    // update the key-repeat queue
-    queue.update(dt);
-
-    // then go through the keymap
-    for (InputKeymapEntry<BitEnumType> *e = keymap; e->input != 0; e++) {
-        // check if we've got the correct keycodes
-        if (checkKeycode(e->keycodes[0]) && checkKeycode(e->keycodes[1])) {
-            // set raw_value
-            raw_value |= e->input;
-
-            if (e->flags & INPUT_FLAG_SLOWREPEAT) {
-                // repeat, but slowly
-                if (!(prev_value & e->input)) {
-                    // we've released the key earlier, move it to the back of the queue
-                    queue.forget(e->input);
-                    queue.push(e->input);
-
-                } else if (queue.find(e->input)) {
-                    // it's still in the queue, so ignore, but set it in ignore_value
-                    continue;
-
-                } else {
-                    // ok, but add it to the queue
-                    queue.push(e->input);
-                }
-
-            } else if (e->flags & INPUT_FLAG_NOREPEAT) {
-                // do not repeat at all
-                if (prev_value & e->input) {
-                    // ignore repeats
-                    continue;
-                }
-            }
-
-            // set bit in masks
-            this->value |= e->input;
-        }
-    }
-
-    // update prev_value, also adding ignored values
-    prev_value = raw_value;
-
-    // then increment our dt
-    this->dt += dt;
-}
-
-/**
- * Input
- */
-Input::Input (CL_InputDevice &keyboard) :
-    keyboard(keyboard),
-    update_timer(INPUT_POLL_INTERVAL),
-    player(keyboard, INPUT_PLAYER_KEYMAP, INPUT_REPEAT_DELAY),
-    gui(keyboard, INPUT_GUI_KEYMAP, INPUT_REPEAT_DELAY)
-{
-    // connect timer 
-    slots.connect(update_timer.sig_tick(), &player, &InputHandler<PlayerInputBit, PlayerInput>::update);
-    slots.connect(update_timer.sig_tick(), &gui, &InputHandler<GuiInputBit, GuiInput>::update);
-
-    // enable timer
-    update_timer.start();
-}
-
-void Input::readPlayerInput (PlayerInput &mask, TimeMS &dt) {
-    player.readValue(mask, dt);
-}
-
-GuiInput Input::readGuiInput (void) {
-    GuiInput mask;
-    TimeMS dt;
-
-    gui.readValue(mask, dt);
-
-    return mask;
-}
-
--- a/src/Input.hh	Wed Jan 21 00:21:42 2009 +0200
+++ b/src/Input.hh	Thu Jan 22 03:02:43 2009 +0200
@@ -1,36 +1,11 @@
 #ifndef INPUT_HH
 #define INPUT_HH
 
-#include "Timer.hh"
-#include "Types.hh"
-
-#include <ClanLib/Display/input_device.h>
-#include <ClanLib/Display/keys.h>
-#include <queue>
-
-// const TimeMS INPUT_INTERVAL_MS = 20;
-
 /**
- * Flags to control input behaviour
+ * Core game input events, used to control LocalPlayer
  */
-enum InputFlagBits {
-    /** Default */
 
-    /**
-     * The bit is not limited, i.e. it is set every time if it's present
-     */
-    INPUT_FLAG_UNLIMITED    = 0x0000,
-
-    /**
-     * The bit is repeat-limited using INPUT_REPEAT_DELAY
-     */
-    INPUT_FLAG_SLOWREPEAT   = 0x0001,
-
-    /**
-     * The bit is repeat-limited using an infinite delay, i.e. you must release the key to trigger it again
-     */
-    INPUT_FLAG_NOREPEAT     = 0x0002,
-};
+#include "Types.hh"
 
 /**
  * The bits used in the PlayerInput bitmask, each represents a separate action handled by LocalPlayer::handleInput.
@@ -60,242 +35,10 @@
 };
 
 /**
- * The bits used in the GuiInput bitmask, each represents something handled locally by Graphics.
- */
-enum GuiInputBit {
-    GUI_INPUT_NONE              = 0x0000,
-
-    GUI_INPUT_QUIT              = 0x0001,
-    GUI_INPUT_DISPLAY_WEAPON    = 0x0002,
-    GUI_INPUT_DEBUG_PLAYER      = 0x0004,
-
-    GUI_INPUT_TOGGLE_FULLSCREEN = 0x0008,
-};
-
-/**
- * Bitmask of InputFlagBits
- */
-typedef uint8_t InputFlags;
-
-/**
  * Bitmask of PlayerInputBits
  *
  * @see PlayerInputBit
  */
 typedef uint16_t PlayerInput;
 
-/**
- * Bitmask for GuiInputBits
- *
- * @see GuiInputBit
- */
-typedef uint16_t GuiInput;
-
-/**
- * Keymap definition struct
- */
-template <typename BitEnumType> struct InputKeymapEntry {
-    /**
-     * The input bit to set if present
-     */
-    BitEnumType input;
-
-    /**
-     * Flags to use
-     *
-     * @see InputFlagBits
-     */
-    InputFlags flags;
-
-    /**
-     * Up to two keycodes to check
-     */
-    int keycodes[2];
-};
-
-
-/**
- * A InputKeyRepeatQueue entry, this contains the input bit value itself, and then the remaining expire time
- */
-template <typename BitEnumType> struct InputKeyRepeatEntry {
-        BitEnumType value;
-
-        /**
-         * The remaining expire time. If this is zero, it never expires
-         */
-        TimeMS expire;
-
-    public:
-        InputKeyRepeatEntry (BitEnumType value, TimeMS expire);
-        
-        /**
-         * Since priority_queue always gives the greatest item, the one with the longest expire is the least item
-         */
-        bool operator< (const struct InputKeyRepeatEntry &other);
-        
-        /**
-         * Decrements expire, returning true if it has now expired, false otherwise. Always returns false if expire is
-         * zero.
-         */
-        bool updateExpired (TimeMS dt);
-};
-
-/**
- * A InputKeyRepeatQueue maintains a list of InputKeyRepeatEntry's, lets you add new input values, find old ones,
- * and update the list
- */
-template <typename BitEnumType> class InputKeyRepeatQueue {
-    private:
-        TimeMS expire;
-
-        typedef InputKeyRepeatEntry<BitEnumType> entry_type;
-
-        std::list<entry_type> list;
-
-        typedef typename std::list<entry_type>::iterator list_iterator;
-
-    public:
-        /**
-         * Constructs this queue to contain entries with the given expiry time
-         */
-        InputKeyRepeatQueue (TimeMS expire);
-        
-        /**
-         * Push a new input bit onto the queue.
-         *
-         * If expire is true, the bit will automatically expire after our expire time, otherwise, it iwll never expire
-         * until forget()'d
-         */
-        void push (BitEnumType bit, bool expire = true);
-
-        /**
-         * Remove any entry for the given bit
-         */
-        void forget (BitEnumType bit);
-
-        /**
-         * Checks if the given input is in the queue
-         */
-        bool find (BitEnumType bit);
-        
-        /**
-         * Updates the list, removing expired items
-         */
-        void update (TimeMS dt);
-};
-
-/**
- * An InputHandler uses an InputKeymapEntry to maintain a BitMaskType of current inputs
- */
-template <typename BitEnumType, typename BitMaskType> class InputHandler {
-    private:
-        /**
-         * The keyboard that we read input from
-         */
-        CL_InputDevice &keyboard;
-        
-        /**
-         * The keymap that we use
-         */
-        InputKeymapEntry<BitEnumType> *keymap;
-        
-        /**
-         * The current bitmask value
-         */
-        BitMaskType value;
-
-        /**
-         * The previous value, used to detect key-up. This also includes keys that were filtered out
-         */
-        BitMaskType prev_value;
-
-        /**
-         * How long the bitmask was held...
-         */
-        TimeMS dt;
-        
-        /**
-         * The KeyRepeatQueue
-         */
-        InputKeyRepeatQueue<BitEnumType> queue;
-    
-    public:
-        /**
-         * Constructs the InputHandler using the given keyboard, keymap and key-repeat expire time
-         */
-        InputHandler (CL_InputDevice &keyboard, InputKeymapEntry<BitEnumType> *keymap, TimeMS keyrepeat_expire);
-    
-    private: 
-        /**
-         * Returns true if the keycode is valid, false if not.
-         *
-         * A positive keycode requires that the keycode be active, a negative keycode that the keycode be inactive,
-         * and a zero keycode always returns true.
-         *
-         * @param keycode A positive keycode to check that it's set, negative keycode to check that it's not set, or zero
-         * @returns bool true if positive+set/negavtive+notset/zero, false otherwise
-         */
-        bool checkKeycode (int keycode);
-    
-    public:
-        /**
-         * Updates the keyrepeat queue
-         */
-        void update (TimeMS dt);
-
-        /**
-         * Reads the current input mask value and length into mask and dt, and reset ours to zero
-         *
-         * @param mask our BitMaskType value is returned using this
-         * @param dt how long the input was held for
-         */
-        void readValue (BitMaskType &mask, TimeMS &dt);
-
-};
-
-/**
- * Handles reading input from a keyboard and mapping it to PlayerInput/GuiInput bitmasks
- */
-class Input {
-    protected:
-        /**
-         * The keyboard device that we use
-         */
-        CL_InputDevice &keyboard;
-
-        /**
-         * Our update timer
-         */
-        Timer update_timer;
-
-        CL_SlotContainer slots;
-        
-        /**
-         * Our PlayerInput
-         */
-        InputHandler<PlayerInputBit, PlayerInput> player;
-        
-        /**
-         * Our GuiInput
-         */
-        InputHandler<GuiInputBit, GuiInput> gui;
-
-    public:
-        /**
-         * Build the input handler using the given keyboard and the default keymaps
-         */
-        Input (CL_InputDevice &keyboard);
-
-    public:
-        /**
-         * Reads the current PlayerInput value via mask, and the length of the input via dt
-         */
-        void readPlayerInput (PlayerInput &mask, TimeMS &dt);
-
-        /**
-         * Reads the current GuiInput mask and returns it
-         */
-        GuiInput readGuiInput (void);
-};
-
 #endif
--- a/src/Logger.cc	Wed Jan 21 00:21:42 2009 +0200
+++ b/src/Logger.cc	Thu Jan 22 03:02:43 2009 +0200
@@ -1,8 +1,8 @@
 
 #include "Logger.hh"
 
-Logger::Logger (std::ostream &stream, enum LogLevel level, const char *module) : 
-    stream(stream), level(level), module(module) 
+Logger::Logger (std::ostream &stream, LogLevel level, const char *module, LogLevel max_level) : 
+    stream(stream), show(level <= max_level)
 {
     const char *l;
 
@@ -14,33 +14,13 @@
         case DEBUG: l = "DEBUG"; break;
         default: l = "???"; break;
     };
-
-#ifndef NDEBUG    
-    stream << l << " [" << module << "] ";
-#endif
+    
+    if (show)
+        stream << l << " [" << module << "] ";
 }
 
 Logger::~Logger (void) {
-#ifndef NDEBUG    
-    stream << std::endl;
-#endif
-}
-
-std::ostream& operator<< (std::ostream &s, CL_NetComputer &c) {
-    s << "[" << c.get_address().get_address() << ":" << c.get_address().get_port() << "]";
-
-    return s;
+    if (show)
+        stream << std::endl;
 }
 
-std::ostream& operator<< (std::ostream &s, CL_NetObject_Server &obj) {
-    s << "%" << obj.get_obj_id();
-
-    return s;
-}
-
-std::ostream& operator<< (std::ostream &s, CL_NetObject_Client &obj) {
-    s << "%" << obj.get_obj_id();
-
-    return s;
-}
-
--- a/src/Logger.hh	Wed Jan 21 00:21:42 2009 +0200
+++ b/src/Logger.hh	Thu Jan 22 03:02:43 2009 +0200
@@ -1,52 +1,69 @@
 #ifndef LOGGER_HH
 #define LOGGER_HH
 
-#include <ClanLib/network.h>
+#include "Types.hh"
 
 #include <iostream>
 
 /**
- * Message severity, WARN and above should go to stderr, INFO and DEBUG to stdout
+ * Utility class used for logging, use Engine::log() to construct these. Implements the same streaming behaviour as 
+ * std::ostream, but the construtor prints out a header, and the destructor a newline. 
  *
- * @see Engine::log
- */
-enum LogLevel {
-    FATAL,
-    ERROR,
-    WARN,
-    INFO,
-    DEBUG,
-};
-
-/**
- * Utility class used for logging, use Engine::log to construct these
+ * Useage example:
+ * 
+ * \code
+ *      Engine::log(INFO, "foo.bar") << "value of quux=" << quux;
+ *
+ * \endcode
+ *
+ * This will result in output like the following to stdout:
+ *
+ * \verbatim
+ * INFO [foo.bar] value of quux=5
+ * \endverbatim
  *
  * @see Engine::log
  */
 class Logger {
     private:
+        /**
+         * The stream we output to
+         */
         std::ostream &stream;
-        enum LogLevel level;
-        const char *module;
+
+        /**
+         * Do we even care?
+         */
+        bool show;
 
     public:
-        Logger (std::ostream &stream, enum LogLevel level, const char *module);
-
-        template <typename T> Logger& operator<< (const T val) {
-#ifndef NDEBUG
-            stream << val;
-#else
-            (void) val;            
-#endif
-
-            return *this;
-        }
-
+        /**
+         * If the given level is smaller than the given maximum level, output the header. Otherwise, act as a
+         * no-op.
+         */
+        Logger (std::ostream &stream, LogLevel level, const char *module, LogLevel max_level);
+        
+        /**
+         * Stream the given value to our output stream, just like with std::ostream, unless we're acting as a no-op.
+         */
+        template <typename T> Logger& operator<< (const T val);
+        
+        /**
+         * Output the final newline
+         */
         ~Logger (void);
 };
 
-std::ostream& operator<< (std::ostream &s, CL_NetComputer &c);
-std::ostream& operator<< (std::ostream &s, CL_NetObject_Server &obj);
-std::ostream& operator<< (std::ostream &s, CL_NetObject_Client &obj);
+/*
+ * Logger template method definition
+ */
+template <typename T> 
+inline Logger& Logger::operator<< (const T val) {
+    if (show)
+        stream << val;
+    
+    // chaining
+    return *this;
+}
 
 #endif /* LOGGER_HH */
--- a/src/Network/Buffer.cc	Wed Jan 21 00:21:42 2009 +0200
+++ b/src/Network/Buffer.cc	Thu Jan 22 03:02:43 2009 +0200
@@ -1,6 +1,6 @@
 
 #include "Buffer.hh"
-#include "Config.hh"
+#include "../Config.hh"
 
 #include <ClanLib/core.h>
 #include <cstdlib>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Network/CMakeLists.txt	Thu Jan 22 03:02:43 2009 +0200
@@ -0,0 +1,9 @@
+FILE(GLOB NETWORK_SOURCE_FILES "*.cc")
+FILE(GLOB NETWORK_HEADER_FILES "*.hh")
+
+# what ClanLib components we need
+set (NETWORK_CLANLIB_COMPONENTS "" PARENT_SCOPE)
+
+# list of source files
+set (NETWORK_SOURCES ${NETWORK_SOURCE_FILES} ${NETWORK_HEADER_FILES} PARENT_SCOPE)
+
--- a/src/Network/Client.cc	Wed Jan 21 00:21:42 2009 +0200
+++ b/src/Network/Client.cc	Thu Jan 22 03:02:43 2009 +0200
@@ -1,7 +1,7 @@
 
 #include "Client.hh"
 #include "Protocol.hh"
-#include "Config.hh"
+#include "../Config.hh"
 #include "../Engine.hh"
 #include "../Logger.hh"
 
@@ -13,8 +13,12 @@
 NetworkClientConnect::NetworkClientConnect (Engine &engine, const NetworkEndpoint &connect_to) :
     engine(engine), netsession(NETWORK_MAGIC_ID)
 {
+    Engine::log(INFO, "net.client") << "Connecting to server: " << connect_to;
+
     // connect NetworkSession to get server node (this is still blocking)
     server = netsession.connect(connect_to);
+
+    Engine::log(INFO, "net.client") << "Connected, receiving game data";
     
     // connect slots
     slots.connect(netsession.sig_chan_message(NETCHAN_TERRAIN_ARRAY), this, &NetworkClientConnect::on_terrain_array);
@@ -57,7 +61,9 @@
 
 void NetworkClientConnect::connectDone (Terrain *terrain) {
     // pass Terrain to engine to create game
-    GameState &gs = engine.setupGame(terrain);
+    GameState &gs = engine.onNetworkClientConnected(terrain);
+
+    Engine::log(INFO, "net.client") << "Got game data, creating player";
 
     // create our new NetworkClient object
     client = new NetworkClient(engine, gs, netsession, server);
@@ -116,17 +122,22 @@
     // read the packet
     Vector position = pkt.read_vector();
     
-    Engine::log(INFO, "client.on_server_hello") << this << ": pos=" << position;
+    Engine::log(DEBUG, "client.on_server_hello") << this << ": pos=" << position;
 
     // create the LocalPlayer object
-    new NetworkClientLocalPlayer(client, obj_id, position);
+    NetworkClientLocalPlayer *player = new NetworkClientLocalPlayer(client, obj_id, position);
+
+    // pass it on to engine
+    client.engine.onNetworkClientPlayer(player);
+
+    Engine::log(INFO, "net.client") << "Joined server: " << player;
 }
         
 void NetworkClientController::on_player_info (NetworkObjectID obj_id, NetworkPacketInput &pkt) {
     // read the packet
     Vector position = pkt.read_vector();
     
-    Engine::log(INFO, "client.on_player_info") << this << ": pos=" << position;
+    Engine::log(DEBUG, "client.on_player_info") << this << ": pos=" << position;
 
     // create the LocalPlayer object
     new NetworkClientRemotePlayer(client, obj_id, position);
@@ -136,10 +147,12 @@
     // read the packet
     Vector position = pkt.read_vector();
     
-    Engine::log(INFO, "client.on_player_join") << this << ": pos=" << position;
+    Engine::log(DEBUG, "client.on_player_join") << this << ": pos=" << position;
     
     // create the RemotePlayer object
-    new NetworkClientRemotePlayer(client, obj_id, position);
+    NetworkClientRemotePlayer *player = new NetworkClientRemotePlayer(client, obj_id, position);
+
+    Engine::log(INFO, "net.client") << "Player joined: " << player;
 }
         
 void NetworkClientController::on_projectile_player_fired (NetworkObjectID obj_id, NetworkPacketInput &pkt) {
@@ -164,7 +177,7 @@
         Engine::log(ERROR, "client.on_projectile_player_fired") << this << ": Unknown weapon id: player=" << player << ", weapon_id=" << weapon_id;
     }
 
-    Engine::log(INFO, "client.on_projectile_create") << this << ": player=" << player << ", pos=" << position << ", velocity=" << velocity << ", weapon=" << weapon;
+    Engine::log(DEBUG, "client.on_projectile_create") << this << ": player=" << player << ", pos=" << position << ", velocity=" << velocity << ", weapon=" << weapon;
 
     // create the NetworkClientPorjectile object
     new NetworkClientProjectile(client, obj_id, player, position, velocity, weapon);
@@ -221,7 +234,7 @@
     int flags = pkt.read_uint8();
     float aim = pkt.read_float32();
 
-//    Engine::log(INFO, "client_player.on_position") << "obj=" << obj << ", position=" << position << ", velocity=" << velocity << ", aim=" << aim << ", [" << flags << "]";
+//    Engine::log(DEBUG, "client_player.on_position") << "obj=" << obj << ", position=" << position << ", velocity=" << velocity << ", aim=" << aim << ", [" << flags << "]";
     
     // just update... 
     updatePhysics(position, velocity, 
@@ -235,7 +248,7 @@
     Vector position = pkt.read_vector();
     float radius = pkt.read_float32();
 
-    Engine::log(INFO, "client_player.on_dig") << this << ": position=" << position << ", radius=" << radius;
+    Engine::log(DEBUG, "client_player.on_dig") << this << ": position=" << position << ", radius=" << radius;
     
     // just update... 
     handleDig(position, radius);
@@ -244,7 +257,7 @@
 void NetworkClientPlayerBase::on_weapon_change (NetworkPacketInput &pkt) {
     uint8_t weapon_index = pkt.read_uint8();
 
-    Engine::log(INFO, "client_player.on_weapon_change") << this << ": weapon_index=" << weapon_index;
+    Engine::log(DEBUG, "client_player.on_weapon_change") << this << ": weapon_index=" << weapon_index;
 
     handleChangeWeapon(weapon_index);
 }
@@ -254,7 +267,7 @@
     Vector velocity = pkt.read_vector();
     float length = pkt.read_float32();
 
-    Engine::log(INFO, "client_player.on_rope_throw") << this << ": position=" << position << ", velocity=" << velocity << ", length=" << length;
+    Engine::log(DEBUG, "client_player.on_rope_throw") << this << ": position=" << position << ", velocity=" << velocity << ", length=" << length;
 
     rope.updateState(ROPE_FLYING, position, velocity, length, NULL);
 }
@@ -271,7 +284,7 @@
         return;
     }
     
-    Engine::log(INFO, "client_player.on_rope_fixed") << this << ": position=" << position << ", length=" << length 
+    Engine::log(DEBUG, "client_player.on_rope_fixed") << this << ": position=" << position << ", length=" << length 
         << ", player=" << player;
 
     rope.updateState(ROPE_FIXED, position, Vector(0, 0), length, player);
@@ -280,7 +293,7 @@
 void NetworkClientPlayerBase::on_rope_released (NetworkPacketInput &pkt) {
     (void) pkt;
 
-    Engine::log(INFO, "client_player.on_rope_released") << this;
+    Engine::log(DEBUG, "client_player.on_rope_released") << this;
     
     // use rope.getPosition() instead of e.g. Vector(0, 0) because it will collide there...
     rope.updateState(ROPE_FOLDED, rope.getPosition(), Vector(0, 0), 0, NULL);
@@ -289,7 +302,7 @@
 void NetworkClientPlayerBase::on_rope_length (NetworkPacketInput &pkt) {
     float length = pkt.read_float32();
     
-    Engine::log(INFO, "client_player.on_rope_length") << this << ": length=" << length;
+    Engine::log(DEBUG, "client_player.on_rope_length") << this << ": length=" << length;
 
     rope.updateLength(length);
 }
@@ -298,7 +311,7 @@
     // read packet
     Vector position = pkt.read_vector();
 
-    Engine::log(INFO, "client_player.on_spawn") << this << ": position=" << position;
+    Engine::log(DEBUG, "client_player.on_spawn") << this << ": position=" << position;
     
     // super
     Player::spawn(position);
@@ -307,7 +320,7 @@
 void NetworkClientPlayerBase::on_die (NetworkPacketInput &pkt) {
     (void) pkt;
 
-    Engine::log(INFO, "client_player.on_die") << this;
+    Engine::log(DEBUG, "client_player.on_die") << this;
 
     // super, but don't start the respawn_timer
     Player::die(false);
@@ -350,7 +363,7 @@
     // pkt is empty
     (void) pkt;
 
-    Engine::log(INFO, "client_player.on_quit") << this;
+    Engine::log(INFO, "net.client") << "Player quit: " << this;
 
     client.player_quit(this);
 
@@ -391,7 +404,7 @@
     Vector position = pkt.read_vector();
     uint8_t flags = pkt.read_uint8();
 
-    Engine::log(INFO, "client_projectile.on_destroy") << this << ": position=" << position << ", flags=" << flags;
+    Engine::log(DEBUG, "client_projectile.on_destroy") << this << ": position=" << position << ", flags=" << flags;
     
     // pass on to super
     Projectile::onDestroy(position, flags & NETWORK_PROJECTILE_REMOVE_GROUND);
@@ -409,7 +422,7 @@
         return;
     }
     
-    Engine::log(INFO, "client_projectile.hit_player") << this << ": player=" << player;
+    Engine::log(DEBUG, "client_projectile.hit_player") << this << ": player=" << player;
     
     // pass on to super
     Projectile::onHitPlayer(player);
--- a/src/Network/Config.hh	Wed Jan 21 00:21:42 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,25 +0,0 @@
-#ifndef NETWORK_CONFIG_HH
-#define NETWORK_CONFIG_HH
-
-/**
- * @file
- *
- * Network-related configuration
- */
-
-#include <string>
-
-/** Default port used for network games */
-const std::string NETWORK_PORT_STR = "9338";
-
-/** Maximum packet size used for normal NetworkPackets... aligned to a sensible UDP mtu */
-const size_t NETWORK_PACKET_SIZE = 1280;
-
-/** Backlog used for TCP server... doesn't really matter all that much */
-const int NETWORK_LISTEN_BACKLOG = 5;
-
-/** Magic string used to identify game between client/server */
-const char NETWORK_MAGIC_STR[8+1] = "KISNGLIS";
-const uint64_t NETWORK_MAGIC_ID = * ((const uint64_t *) NETWORK_MAGIC_STR);
-
-#endif /* NETWORK_CONFIG_HH */
--- a/src/Network/Packet.hh	Wed Jan 21 00:21:42 2009 +0200
+++ b/src/Network/Packet.hh	Thu Jan 22 03:02:43 2009 +0200
@@ -7,7 +7,7 @@
  * Provides the NetworkPackets that are used to communicate over a NetworkTCP/NetworkUDP sockets.
  */
 
-#include "Config.hh"
+#include "../Config.hh"
 #include "../Types.hh"
 #include "../Error.hh"
 
--- a/src/Network/Server.cc	Wed Jan 21 00:21:42 2009 +0200
+++ b/src/Network/Server.cc	Thu Jan 22 03:02:43 2009 +0200
@@ -1,7 +1,7 @@
 
 #include "Server.hh"
 #include "Protocol.hh"
-#include "Config.hh"
+#include "../Config.hh"
 #include "../Engine.hh"
 #include "../Logger.hh"
 
@@ -16,10 +16,12 @@
     // and then we listen
     netsession.listen(listen_addr);
 
-    Engine::log(INFO, "server") << "running, listen_addr=" << listen_addr;
+    Engine::log(INFO, "net.server") << "Listening on interface: " << listen_addr;
 }
 
 void NetworkServer::on_node_connected (NetworkNode *node) {
+    Engine::log(INFO, "net.server") << "Client connected, sending terrain data: " << node->getRemoteAddress();
+
     // send the terrain data
     send_terrain_data(node);
 
@@ -28,7 +30,6 @@
 
     // add to players
     players.push_back(player);
-
 }
  
 void NetworkServer::send_terrain_data (NetworkNode *node) {
@@ -90,9 +91,6 @@
 NetworkServerPlayer::NetworkServerPlayer (NetworkServer &server, NetworkNode *node) : 
     Player(server.state, Vector(PLAYER_INITIAL_X, PLAYER_INITIAL_Y), true), NetworkServerObject(server), node(node) 
 {
-    // log
-    Engine::log(INFO, "server_player.connected") << this << ": node=" << node;
-
     // messages
     slots.connect(node->sig_disconnected(), this, &NetworkServerPlayer::on_disconnected);
     slots.connect(this->sig_message(NETMSG_CLIENT_INPUT), this, &NetworkServerPlayer::on_input);
@@ -123,6 +121,8 @@
 
     // broadcast NETMSG_PLAYER_JOIN to all clients except current
     this->send_all_except(NETMSG_PLAYER_JOIN, hello_pkt, node, true);
+
+    Engine::log(INFO, "net.server") << "Player joined: " << this << " from " << node->getRemoteAddress();
 }
 
 void NetworkServerPlayer::handleDig (Vector position, float radius) {
@@ -131,7 +131,7 @@
     pkt.write_vector(position);
     pkt.write_float32(radius);
 
-    Engine::log(INFO, "server_player.handle_dig") << "position=" << position << ", radius=" << radius;
+    Engine::log(DEBUG, "server_player.handle_dig") << "position=" << position << ", radius=" << radius;
     
     // tell everyone... make this reliable... 
     this->send_all(NETMSG_PLAYER_DIG, pkt, true);
@@ -141,7 +141,7 @@
 }
 
 void NetworkServerPlayer::handleFireWeapon (Weapon *weapon, Vector position, Vector velocity) {
-    Engine::log(INFO, "server_player.fire_weapon") << "weapon='" << weapon->getName() << "', position=" << position << ", velocity=" << velocity;
+    Engine::log(DEBUG, "server_player.fire_weapon") << "weapon='" << weapon->getName() << "', position=" << position << ", velocity=" << velocity;
 
     // create new NetworkServerProjectile object
     new NetworkServerProjectile(server, this, position, velocity, weapon);
@@ -153,7 +153,7 @@
 void NetworkServerPlayer::handleChangeWeapon (unsigned int weaponIndex) {
     NetworkPacket pkt;
 
-    Engine::log(INFO, "server_player.change_weapon") << "weaponIndex=" << weaponIndex;
+    Engine::log(DEBUG, "server_player.change_weapon") << "weaponIndex=" << weaponIndex;
     
     // write packet
     pkt.write_uint8(weaponIndex);
@@ -168,7 +168,7 @@
 void NetworkServerPlayer::handleRopeState (RopeState state) {
     NetworkPacket pkt; 
 
-    Engine::log(INFO, "server_player.rope_state") << "state=" << rope.getState() << ", position=" << rope.getPosition() << ", velocity=" << rope.getVelocity() << ", length=" << rope.getLength() << ", pivotPlayer=" << rope.getPivotPlayer();
+    Engine::log(DEBUG, "server_player.rope_state") << "state=" << rope.getState() << ", position=" << rope.getPosition() << ", velocity=" << rope.getVelocity() << ", length=" << rope.getLength() << ", pivotPlayer=" << rope.getPivotPlayer();
 
     switch (state) {
     case ROPE_FLYING:
@@ -216,7 +216,7 @@
     // write packet
     pkt.write_vector(position);
 
-    Engine::log(INFO, "server_player.spawn") << this << ": position=" << position;
+    Engine::log(DEBUG, "server_player.spawn") << this << ": position=" << position;
     
     // send
     send_all(NETMSG_PLAYER_SPAWN, pkt, true);
@@ -228,7 +228,7 @@
 void NetworkServerPlayer::die (bool start_timer) {
     NetworkPacket pkt;
 
-    Engine::log(INFO, "server_player.die") << this;
+    Engine::log(DEBUG, "server_player.die") << this;
     
     // send
     send_all(NETMSG_PLAYER_DIE, pkt, true);
@@ -240,7 +240,7 @@
 void NetworkServerPlayer::on_disconnected (void) {
     NetworkPacket pkt;
     
-    Engine::log(INFO, "server_player.disconnected") << this << ": node=" << node;
+    Engine::log(INFO, "net.server") << "Player disconnected: " << this;
     
     // remove from server
     server.handle_disconnect(this);
@@ -309,7 +309,7 @@
 void NetworkServerProjectile::onDestroy (Vector position, bool removeGround) {
     NetworkPacket pkt;
 
-    Engine::log(INFO, "server_projectile.destroy") << this << "position=" << position << ", removeGround=" << removeGround;
+    Engine::log(DEBUG, "server_projectile.destroy") << this << "position=" << position << ", removeGround=" << removeGround;
     
     pkt.write_vector(position);
     pkt.write_uint8(removeGround ? NETWORK_PROJECTILE_REMOVE_GROUND : 0);
@@ -329,7 +329,7 @@
     if (player == NULL) 
         throw Error("NetworkServerProjectile::onHitPlayer called with non-NetworkServerPlayer player");
     
-    Engine::log(INFO, "server_projectile.hit_player") << this << ": player=" << player;
+    Engine::log(DEBUG, "server_projectile.hit_player") << this << ": player=" << player;
     
     // write packet
     controller.write_object(pkt, player);
--- a/src/Network/Socket.hh	Wed Jan 21 00:21:42 2009 +0200
+++ b/src/Network/Socket.hh	Thu Jan 22 03:02:43 2009 +0200
@@ -15,6 +15,8 @@
 #include "Address.hh"
 #include "Reactor.hh"
 
+#include <ClanLib/signals.h>
+
 /**
  * This is a socket class that wraps an OS socket filedescriptor and provides the more important socket operations
  * as methods. The implementation aims to be address-family agnostic, and should thence work with both IPv6 and IPv4.
--- a/src/Player.cc	Wed Jan 21 00:21:42 2009 +0200
+++ b/src/Player.cc	Thu Jan 22 03:02:43 2009 +0200
@@ -2,7 +2,8 @@
 #include "Player.hh"
 #include "Weapons.hh"
 #include "Engine.hh"
-#include "Graphics.hh"
+
+#include "Graphics/Graphics.hh"
 
 #include <cstdlib>
 #include <ClanLib/display.h>
@@ -10,11 +11,15 @@
 #include <string>
 #include <cassert>
 
-
-// player static state
+#if GRAPHICS_ENABLED
+/*
+ * Static draw-related state
+ */
 bool Player::skin_loaded = false;
 CL_Surface Player::skin_surface;
 
+#endif
+
 // XXX: give these better names and move elsewhere
 const int img_num_aim = 5;
 const int img_num_step = 4;
@@ -324,14 +329,15 @@
     kills++;
 }
 
-void Player::draw (Graphics *g, PixelCoordinate camera) {
-    CL_GraphicContext *gc = g->get_gc();
+#if GRAPHICS_ENABLED
+void Player::draw (graphics::Display &display, PixelCoordinate camera) {
+    CL_GraphicContext *gc = display.get_gc();
 
     if (!isAlive())
         return;
 
     // draw rope behind player
-    rope.draw(g, camera);
+    rope.draw(display, camera);
     
     // animation indexes
     int aim_img_idx = (int)((1 - (getAim() + KG_PI / 2) / KG_PI) * img_num_aim);
@@ -375,23 +381,28 @@
     );    
 }
 
-void LocalPlayer::draw (Graphics *g, bool displayWeapon, PixelCoordinate camera) {
+void LocalPlayer::draw (graphics::Display &display, bool displayWeapon, PixelCoordinate camera) {
     // superclass draw
-    Player::draw(g, camera);
+    Player::draw(display, camera);
 
     // display weapon name?
     if (isAlive() && displayWeapon && getCurrentWeapon()) {
         const std::string weaponName = getCurrentWeapon()->getName();
+        
+        // position
+        PixelCoordinate pc = getCoordinate() - camera;
 
-        PixelCoordinate pc = getCoordinate() - camera;
+        // get font
+        CL_Font &font = graphics::graphics->fonts.getSimpleFont();
         
         // XXX: fix magic constants once we know how big the worm is
-        g->getSimpleFont().draw(
-                pc.x - g->getSimpleFont().get_width(weaponName) / 2,
-                pc.y - 20,
-                weaponName,
-                g->get_gc()
+        font.draw(
+            pc.x - font.get_width(weaponName) / 2,
+            pc.y - 20,
+            weaponName,
+            display.get_gc()
         );
     }
 }
+#endif
 
--- a/src/Player.hh	Wed Jan 21 00:21:42 2009 +0200
+++ b/src/Player.hh	Thu Jan 22 03:02:43 2009 +0200
@@ -9,10 +9,12 @@
 #include "Projectile.hh"
 #include "GameState.hh"
 #include "PhysicsObject.hh"
-#include "Input.hh"
 #include "Rope.hh"
-#include "GraphicsPointer.hh"
 #include "Types.hh"
+#include "Input.hh"
+
+#include "Graphics/Drawable.hh"
+
 #include <vector>
 
 /**
@@ -150,14 +152,27 @@
      */
     void addKill ();
 
+#if GRAPHICS_ENABLED    
     /*
      * Drawing requires the skin texture, which is loaded on-demand when draw is called
      */
     static bool skin_loaded;
     static CL_Surface skin_surface;
-    virtual void draw (Graphics *g, PixelCoordinate camera);
 
+    /**
+     * Draw this player
+     */
+    virtual void draw (graphics::Display &display, PixelCoordinate camera);
+#endif
+
+    /**
+     * Returns statistics on the number of kills for this player
+     */
     uint16_t getKills() { return kills; }
+
+    /**
+     * Returns statistics on the number of deaths for this player
+     */
     uint16_t getDeaths() { return deaths; }
 };
 
@@ -187,10 +202,12 @@
      */
     virtual void handleInput (PlayerInput input, TimeMS dt);
         
+#if GRAPHICS_ENABLED    
     /**
      * As Player, but also draws the current weapon name if displayWeapon
      */
-    virtual void draw (Graphics *g, bool displayWeapon, PixelCoordinate camera);
+    virtual void draw (graphics::Display &display, bool displayWeapon, PixelCoordinate camera);
+#endif    
 };
 
 /**
--- a/src/Projectile.cc	Wed Jan 21 00:21:42 2009 +0200
+++ b/src/Projectile.cc	Thu Jan 22 03:02:43 2009 +0200
@@ -1,5 +1,4 @@
 #include "Projectile.hh"
-#include "Graphics.hh"
 #include "Timer.hh"
 
    
@@ -75,8 +74,9 @@
     PhysicsObject::tick(dt);
 }
 
-void Projectile::draw(Graphics *g, PixelCoordinate camera) const {
-    CL_GraphicContext *gc = g->get_gc();
+#if GRAPHICS_ENABLED
+void Projectile::draw(graphics::Display &display, PixelCoordinate camera) const {
+    CL_GraphicContext *gc = display.get_gc();
 
     if (visible) {
         PixelCoordinate pos = getCoordinate() - camera;
@@ -93,4 +93,5 @@
         gc->fill_quad(projectile, CL_Color::green);
     }
 }
- 
+#endif
+
--- a/src/Projectile.hh	Wed Jan 21 00:21:42 2009 +0200
+++ b/src/Projectile.hh	Thu Jan 22 03:02:43 2009 +0200
@@ -8,7 +8,8 @@
 #include "PhysicsObject.hh"
 #include "Timer.hh"
 #include "Types.hh"
-#include "GraphicsPointer.hh"
+
+#include "Graphics/Drawable.hh"
 
 /**
  * A projectile is a flying PhysicsObject, created by firing a player's weapon. It has an initial velocity, is
@@ -47,11 +48,6 @@
     virtual ~Projectile (void);
     
     /**
-     * Draw
-     */
-    virtual void draw (Graphics *g, PixelCoordinate camera) const;
-
-    /**
      * Get damage inflicted by this projectile.
      *
      * @return Damage inflicted by projectile.
@@ -93,6 +89,18 @@
      * If we have expired, call onDestory and removeGround
      */
     virtual void tick (TimeMS dt);
+
+public:
+
+#if GRAPHICS_ENABLED    
+    /**
+     * Draw
+     */
+    virtual void draw (graphics::Display &display, PixelCoordinate camera) const;
+
+#endif
+
+
 };
  
 #endif
--- a/src/Rope.cc	Wed Jan 21 00:21:42 2009 +0200
+++ b/src/Rope.cc	Thu Jan 22 03:02:43 2009 +0200
@@ -1,7 +1,10 @@
+
+// XXX: must include Player first, as it contains an instance of Rope
 #include "Player.hh"
 #include "Rope.hh"      
 #include "Engine.hh"
-#include "Graphics.hh"
+#include "Error.hh"
+
 #include <math.h>
 #include <stdexcept>
 
@@ -150,7 +153,8 @@
     this->length = length;
 }
 
-void Rope::draw (Graphics *g, PixelCoordinate camera) {
+#if GRAPHICS_ENABLED
+void Rope::draw (graphics::Display &display, PixelCoordinate camera) {
     PixelCoordinate player_pos = player.getCoordinate() - camera;
     PixelCoordinate target_pos;
     
@@ -175,12 +179,13 @@
     target_pos -= camera;
     
     // draw a line from the player to the target chosen above
-    g->get_gc()->draw_line(
+    display.get_gc()->draw_line(
         player_pos.x, player_pos.y,
         target_pos.x, target_pos.y,
         ROPE_COLOR_DARK
     );
 }
+#endif
 
 void Rope::tick (TimeMS dt) {
     if (state == ROPE_FLYING) {
--- a/src/Rope.hh	Wed Jan 21 00:21:42 2009 +0200
+++ b/src/Rope.hh	Thu Jan 22 03:02:43 2009 +0200
@@ -5,10 +5,6 @@
 // and the Player wants to know the rope.
 class Rope;
 
-#include "Player.hh"
-#include "PhysicsObject.hh"
-#include "GraphicsPointer.hh"
-
 /**
  * The rope can be in one of three states...
  *
@@ -20,6 +16,11 @@
     ROPE_FIXED      //<<< The rope is attached to something
 };
 
+#include "Player.hh"
+#include "PhysicsObject.hh"
+
+#include "Graphics/Drawable.hh"
+
 /**
  * A rope is a PhysicsObject that can be thrown, whereupon it then flies until it hits something, whereupon
  * it attaches to that, and sets itself as the player's pivot.
@@ -83,10 +84,12 @@
         
     virtual void tick (TimeMS dt);
 
+#if GRAPHICS_ENABLED    
     /*
-     * Just draws it
+     * Draw the rope, in the FLYING/FIXED state
      */ 
-    virtual void draw (Graphics *c, PixelCoordinate camera);
+    virtual void draw (graphics::Display &display, PixelCoordinate camera);
+#endif    
 };
 
 #endif
--- a/src/Terrain.cc	Wed Jan 21 00:21:42 2009 +0200
+++ b/src/Terrain.cc	Thu Jan 22 03:02:43 2009 +0200
@@ -1,5 +1,5 @@
+
 #include "Terrain.hh"
-#include "Graphics.hh"
 #include "Engine.hh"
 
 #include <cmath>
@@ -20,7 +20,7 @@
 
 Terrain::Terrain (const TerrainConfig &config) :
     terrain_buf(NULL),
-    width(config.dimensions.x), height(config.dimensions.y)
+    width(config.dimensions.width), height(config.dimensions.height)
 {
     // allocate terrain_buf
     terrain_buf = new TerrainPixel[width * height];
@@ -101,6 +101,8 @@
 }
 
 void Terrain::generatePixelBuffer (void) {
+
+#if GRAPHICS_ENABLED    
     // initialize textures
     generateTexture();
 
@@ -114,6 +116,12 @@
             pixbuf.draw_pixel(x, y, getTexturePixel(x, y));
         }
     }
+
+#else
+    // no-op
+
+#endif    
+
 }
 
 /*
@@ -410,13 +418,15 @@
     return normal;
 }
 
-void Terrain::draw (Graphics *g, PixelCoordinate camera) {
+#if GRAPHICS_ENABLED
+void Terrain::draw (graphics::Display &display, PixelCoordinate camera) {
     // XXX: can we optimize this somehow?
     
     // load the terrain pixbuf as a surface
     CL_Surface surf (pixbuf);
     
     // draw it onto the graphics, offset by camera position
-    surf.draw(-camera.x, -camera.y, g->get_gc());
+    surf.draw(-camera.x, -camera.y, display.get_gc());
 }
+#endif
 
--- a/src/Terrain.hh	Wed Jan 21 00:21:42 2009 +0200
+++ b/src/Terrain.hh	Thu Jan 22 03:02:43 2009 +0200
@@ -2,9 +2,13 @@
 #define TERRAIN_HH
 
 #include "Vector.hh"
-#include "GraphicsPointer.hh"
 #include "Types.hh"
 #include "Config.hh"
+#include "Configuration.hh"
+
+#include "Graphics/Drawable.hh"
+
+#include <vector>
 
 /**
  * Different types of terrain available
@@ -21,27 +25,6 @@
 };
 
 /**
- * Terrain "pixel" type
- */
-typedef uint8_t TerrainPixel;
-
-/**
- * Terrain configuration
- */
-struct TerrainConfig {
-    /** Size of the terrain field*/
-    PixelCoordinate dimensions;
-
-    /** Set to nonzero to generate random map */
-    int random_seed;
-    
-    /** Defaults */
-    TerrainConfig (void) : dimensions(TERRAIN_WIDTH, TERRAIN_HEIGHT), random_seed(TERRAIN_RANDOM_SEED) { }
-};
-
-#include <vector>
-
-/**
  * Terrain class. Represents game terrain and contains member
  * functions to manipulate terrain and get info about it.
  * 
@@ -58,10 +41,13 @@
 
     /** Terrain dimensions */
     PixelDimension width, height;
-    
+
+#if GRAPHICS_ENABLED    
     /** We pre-render the textured terrain data for display */
     CL_PixelBuffer pixbuf;
 
+#endif    
+
     // XXX: terrain texture
     std::vector<std::vector<int> > texture;
 
@@ -102,8 +88,10 @@
     inline void setType (PixelDimension x, PixelDimension y, TerrainType t) {
         terrain_buf[y * width + x] = (TerrainPixel) t;
 
+#if GRAPHICS_ENABLED        
         // XXX: locking?
         pixbuf.draw_pixel(x, y, getTexturePixel(x, y));
+#endif        
     }
 
     /**
@@ -240,13 +228,15 @@
      */
     Vector getNormal (Vector point, Vector prevPoint) const;
 
+#if GRAPHICS_ENABLED        
     /**
      * Draw the terrain onto the given graphics context
      *
      * @param gc Graphics to draw on
      * @param camera view position
      */
-    virtual void draw (Graphics *g, PixelCoordinate camera = PixelCoordinate(0, 0));
+    virtual void draw (graphics::Display &display, PixelCoordinate camera = PixelCoordinate(0, 0));
+#endif    
 };
 
 #endif
--- a/src/Types.hh	Wed Jan 21 00:21:42 2009 +0200
+++ b/src/Types.hh	Thu Jan 22 03:02:43 2009 +0200
@@ -15,6 +15,21 @@
 typedef VectorType<float> Vector;
 
 /**
+ * Message severity, WARN and above should go to stderr, INFO and DEBUG to stdout
+ *
+ * @see Engine::log
+ * @see EngineConfig::log_level
+ * @see Logger
+ */
+enum LogLevel {
+    FATAL,
+    ERROR,
+    WARN,
+    INFO,
+    DEBUG,
+};
+
+/**
  * A player's health is measured in...
  */
 typedef int16_t Health;
@@ -30,6 +45,19 @@
 typedef VectorType<PixelDimension> PixelCoordinate;
 
 /**
+ * Dimensions of something in pixels
+ */
+struct PixelDimensions {
+    /** Item width/height */
+    PixelDimension width, height;
+    
+    /** Simple constructor */
+    PixelDimensions (PixelDimension width, PixelDimension height) :
+        width(width), height(height)
+    { }
+};
+
+/**
  * A rectangular area of pixels
  */
 struct PixelArea {
@@ -50,4 +78,11 @@
  */
 typedef int32_t TickCount;
 
+/**
+ * Terrain "pixel" type
+ *
+ * @see TerrainType
+ */
+typedef uint8_t TerrainPixel;
+
 #endif
--- a/src/config.h.in	Wed Jan 21 00:21:42 2009 +0200
+++ b/src/config.h.in	Thu Jan 22 03:02:43 2009 +0200
@@ -13,5 +13,17 @@
  */
 extern const char *PROJECT_VERSION, *PROJECT_BUILD_TIMESTAMP;
 
+/**
+ * Provide possibility to compile without graphics code.
+ *
+ * Defined as a boolean value (so use #if GRAPHICS_ENABLED / if (GRAPHICS_ENABLED) )
+ */
+#define GRAPHICS_ENABLED @GRAPHICS_ENABLED@
+
+/**
+ * Provide possibility to compile without network code.
+ */
+#define NETWORK_ENABLED @NETWORK_ENABLED@
+
 #endif