# HG changeset patch # User Tero Marttila # Date 1232586163 -7200 # Node ID b5b9d2aafdcbad537da2120923d64c031fbb117b # Parent 1a03ff151abce90b05bede99902af11dcec7a6b8# Parent 278020dcd9b7cd9bbd30fa63a732ebcfc6c78b31 merge new_graphics back into default, it should work OK, and there's a lot of additional functionality already diff -r 1a03ff151abc -r b5b9d2aafdcb src/Application.cc --- 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 +#endif #include #include @@ -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 &modes = Graphics::getDisplayModes(); + // get the list of display modes from graphics + const std::vector &modes = graphics::Display::getDisplayModes(); + PixelCoordinate last_resolution; std::cout << "Available display modes:" << std::endl; - - for (std::vector::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::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; diff -r 1a03ff151abc -r b5b9d2aafdcb src/Application.hh --- 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); diff -r 1a03ff151abc -r b5b9d2aafdcb src/CMakeLists.txt --- 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. diff -r 1a03ff151abc -r b5b9d2aafdcb src/Config.hh --- 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 -// 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 diff -r 1a03ff151abc -r b5b9d2aafdcb src/Configuration.hh --- /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 diff -r 1a03ff151abc -r b5b9d2aafdcb src/Engine.cc --- 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 +#include -Engine::Engine (const std::string resource_xml_path) : - terrain(NULL), game_state(NULL), graphics_config(NULL), graphics(NULL), net_server(NULL), net_client_connect(NULL), - is_running(true), resources(resource_xml_path) +/* + * 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); -} - diff -r 1a03ff151abc -r b5b9d2aafdcb src/Engine.hh --- 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); + } }; diff -r 1a03ff151abc -r b5b9d2aafdcb src/GameMessageView.cc --- 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::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); -} - diff -r 1a03ff151abc -r b5b9d2aafdcb src/GameMessageView.hh --- 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 -#include -#include - -struct GameMessage { - CL_Color color; - std::string message; - - GameMessage (CL_Color color, std::string message) : color(color), message(message) { } - GameMessage (const GameMessage ©) : color(copy.color), message(copy.message) { } - GameMessage &operator= (const GameMessage ©) { color = copy.color; message = copy.message; return *this; } -}; - -class GameMessageView { - protected: - PixelArea area; - std::vector 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 diff -r 1a03ff151abc -r b5b9d2aafdcb src/GameState.cc --- 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::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::iterator it = projectiles.begin(); it != projectiles.end(); it++) { - (*it)->draw(g, camera); + (*it)->draw(display, camera); } } - +#endif + diff -r 1a03ff151abc -r b5b9d2aafdcb src/GameState.hh --- 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 #include #include @@ -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 diff -r 1a03ff151abc -r b5b9d2aafdcb src/Graphics.cc --- 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 -#include - -/* - * 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 & Graphics::getDisplayModes (void) { - return CL_DisplayMode::get_display_modes(); -} - -const CL_DisplayMode Graphics::getBestMode (void) { - const std::vector &modes = Graphics::getDisplayModes(); - - const CL_DisplayMode *best_mode = NULL; - - for (std::vector::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"); -} diff -r 1a03ff151abc -r b5b9d2aafdcb src/Graphics.hh --- 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 -#include -#include - -/** - * 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 & 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 */ diff -r 1a03ff151abc -r b5b9d2aafdcb src/Graphics/CMakeLists.txt --- /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) + diff -r 1a03ff151abc -r b5b9d2aafdcb src/Graphics/Console.cc --- /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); +} + + +} diff -r 1a03ff151abc -r b5b9d2aafdcb src/Graphics/Console.hh --- /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 + +namespace graphics +{ + +class Console : public CL_Component { +protected: + /** our list of messages */ + std::list 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 diff -r 1a03ff151abc -r b5b9d2aafdcb src/Graphics/Display.cc --- /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 & Display::getDisplayModes (void) { + return CL_DisplayMode::get_display_modes(); +} + +const CL_DisplayMode Display::getBestMode (void) { + const std::vector &modes = getDisplayModes(); + + const CL_DisplayMode *best_mode = NULL; + + for (std::vector::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)); +} + + +} + diff -r 1a03ff151abc -r b5b9d2aafdcb src/Graphics/Display.hh --- /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 + +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 & 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 diff -r 1a03ff151abc -r b5b9d2aafdcb src/Graphics/Drawable.hh --- /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 diff -r 1a03ff151abc -r b5b9d2aafdcb src/Graphics/FontManager.hh --- /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 + +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 diff -r 1a03ff151abc -r b5b9d2aafdcb src/Graphics/GUI.hh --- /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 + + +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 diff -r 1a03ff151abc -r b5b9d2aafdcb src/Graphics/GUIStyle.hh --- /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 + +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 diff -r 1a03ff151abc -r b5b9d2aafdcb src/Graphics/GameView.cc --- /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 + +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())); +} + + +} diff -r 1a03ff151abc -r b5b9d2aafdcb src/Graphics/GameView.hh --- /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 diff -r 1a03ff151abc -r b5b9d2aafdcb src/Graphics/Graphics.cc --- /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 + +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; + +} + +} diff -r 1a03ff151abc -r b5b9d2aafdcb src/Graphics/Graphics.hh --- /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 diff -r 1a03ff151abc -r b5b9d2aafdcb src/Graphics/Input.cc --- /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 + +namespace graphics +{ + +InputKeymapEntry 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 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 +InputKeyRepeatEntry::InputKeyRepeatEntry (BitEnumType value, TimeMS expire) : + value(value), expire(expire) +{ + +} + +template +bool InputKeyRepeatEntry::operator< (const struct InputKeyRepeatEntry &other) { + return other.expire > expire; +} + +template +bool InputKeyRepeatEntry::updateExpired (TimeMS dt) { + if (expire == 0) + return false; + + expire -= dt; + + return (expire <= 0); +} + +/* + * InputKeyRepeatQueue + */ +template +InputKeyRepeatQueue::InputKeyRepeatQueue (TimeMS expire) : + expire(expire) +{ + +} + +template +void InputKeyRepeatQueue::push (BitEnumType bit, bool expire) { + list.push_back(InputKeyRepeatEntry(bit, expire ? this->expire : 0)); +} + +template +void InputKeyRepeatQueue::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 +bool InputKeyRepeatQueue::find (BitEnumType bit) { + for (list_iterator it = list.begin(); it != list.end(); it++) { + if (it->value == bit) + return true; + } + + return false; +} + +template +void InputKeyRepeatQueue::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 +void InputKeyRepeatQueue::clear (void) { + // just clear our list of events + list.clear(); +} + +/* + * InputHandler + */ +template +InputHandler::InputHandler (CL_InputDevice &keyboard, InputKeymapEntry *keymap, TimeMS keyrepeat_expire) : + keyboard(keyboard), + keymap(keymap), + value(0), + prev_value(0), + dt(0), + queue(keyrepeat_expire), + _enabled(false) +{ + +} + +template +bool InputHandler::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 +void InputHandler::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 *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 +void InputHandler::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::update); + slots.connect(update_timer.sig_tick(), &gui, &InputHandler::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; +} + +} diff -r 1a03ff151abc -r b5b9d2aafdcb src/Graphics/Input.hh --- /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 +#include +#include + +#include + +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 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 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 class InputKeyRepeatQueue { + private: + TimeMS expire; + + typedef InputKeyRepeatEntry entry_type; + + std::list list; + + typedef typename std::list::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 class InputHandler { + private: + /** + * The keyboard that we read input from + */ + CL_InputDevice &keyboard; + + /** + * The keymap that we use + */ + InputKeymapEntry *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 queue; + + /** + * Are we enabled or not? + */ + bool _enabled; + + /** + * The keyevent signal + */ + CL_Signal_v2 _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 *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& 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 player; + + /** + * Our GuiInput + */ + InputHandler 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 + +namespace graphics +{ + +template +void InputHandler::enable (void) { + // sanity-check + assert(!_enabled); + + // update state + _enabled = true; +} + +template +void InputHandler::disable (void) { + // sanity-check + assert(_enabled); + + // update state + value = prev_value = 0; + _enabled = false; + + // and clear keyrepeat list + queue.clear(); +} + + +} + +#endif diff -r 1a03ff151abc -r b5b9d2aafdcb src/Graphics/MessageView.cc --- /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::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); +} + +} diff -r 1a03ff151abc -r b5b9d2aafdcb src/Graphics/MessageView.hh --- /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 +#include +#include + +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 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 diff -r 1a03ff151abc -r b5b9d2aafdcb src/Graphics/PlayerInfoView.cc --- /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 + +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 + ); +} + + + +} diff -r 1a03ff151abc -r b5b9d2aafdcb src/Graphics/PlayerInfoView.hh --- /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 diff -r 1a03ff151abc -r b5b9d2aafdcb src/Graphics/View.hh --- /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 diff -r 1a03ff151abc -r b5b9d2aafdcb src/GraphicsPointer.hh --- 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 diff -r 1a03ff151abc -r b5b9d2aafdcb src/Input.cc --- 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 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 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 -InputKeyRepeatEntry::InputKeyRepeatEntry (BitEnumType value, TimeMS expire) : - value(value), expire(expire) -{ - -} - -template -bool InputKeyRepeatEntry::operator< (const struct InputKeyRepeatEntry &other) { - return other.expire > expire; -} - -template -bool InputKeyRepeatEntry::updateExpired (TimeMS dt) { - if (expire == 0) - return false; - - expire -= dt; - - return (expire <= 0); -} - -/* - * InputKeyRepeatQueue - */ -template -InputKeyRepeatQueue::InputKeyRepeatQueue (TimeMS expire) : - expire(expire) -{ - -} - -template -void InputKeyRepeatQueue::push (BitEnumType bit, bool expire) { - list.push_back(InputKeyRepeatEntry(bit, expire ? this->expire : 0)); -} - -template -void InputKeyRepeatQueue::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 -bool InputKeyRepeatQueue::find (BitEnumType bit) { - for (list_iterator it = list.begin(); it != list.end(); it++) { - if (it->value == bit) - return true; - } - - return false; -} - -template -void InputKeyRepeatQueue::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 -InputHandler::InputHandler (CL_InputDevice &keyboard, InputKeymapEntry *keymap, TimeMS keyrepeat_expire) : - keyboard(keyboard), - keymap(keymap), - value(0), - prev_value(0), - dt(0), - queue(keyrepeat_expire) -{ - -} - -template -bool InputHandler::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 -void InputHandler::readValue (BitMaskType &mask, TimeMS &dt) { - // copy to args - mask = this->value; - dt = this->dt; - - this->value = 0; - this->dt = 0; -} - -template -void InputHandler::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 *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::update); - slots.connect(update_timer.sig_tick(), &gui, &InputHandler::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; -} - diff -r 1a03ff151abc -r b5b9d2aafdcb src/Input.hh --- 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 -#include -#include - -// 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 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 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 class InputKeyRepeatQueue { - private: - TimeMS expire; - - typedef InputKeyRepeatEntry entry_type; - - std::list list; - - typedef typename std::list::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 class InputHandler { - private: - /** - * The keyboard that we read input from - */ - CL_InputDevice &keyboard; - - /** - * The keymap that we use - */ - InputKeymapEntry *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 queue; - - public: - /** - * Constructs the InputHandler using the given keyboard, keymap and key-repeat expire time - */ - InputHandler (CL_InputDevice &keyboard, InputKeymapEntry *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 player; - - /** - * Our GuiInput - */ - InputHandler 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 diff -r 1a03ff151abc -r b5b9d2aafdcb src/Logger.cc --- 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; -} - diff -r 1a03ff151abc -r b5b9d2aafdcb src/Logger.hh --- 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 +#include "Types.hh" #include /** - * 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 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 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 +inline Logger& Logger::operator<< (const T val) { + if (show) + stream << val; + + // chaining + return *this; +} #endif /* LOGGER_HH */ diff -r 1a03ff151abc -r b5b9d2aafdcb src/Network/Buffer.cc --- 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 #include diff -r 1a03ff151abc -r b5b9d2aafdcb src/Network/CMakeLists.txt --- /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) + diff -r 1a03ff151abc -r b5b9d2aafdcb src/Network/Client.cc --- 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); diff -r 1a03ff151abc -r b5b9d2aafdcb src/Network/Config.hh --- 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 - -/** 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 */ diff -r 1a03ff151abc -r b5b9d2aafdcb src/Network/Packet.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" diff -r 1a03ff151abc -r b5b9d2aafdcb src/Network/Server.cc --- 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); diff -r 1a03ff151abc -r b5b9d2aafdcb src/Network/Socket.hh --- 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 + /** * 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. diff -r 1a03ff151abc -r b5b9d2aafdcb src/Player.cc --- 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 #include @@ -10,11 +11,15 @@ #include #include - -// 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 diff -r 1a03ff151abc -r b5b9d2aafdcb src/Player.hh --- 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 /** @@ -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 }; /** diff -r 1a03ff151abc -r b5b9d2aafdcb src/Projectile.cc --- 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 + diff -r 1a03ff151abc -r b5b9d2aafdcb src/Projectile.hh --- 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 diff -r 1a03ff151abc -r b5b9d2aafdcb src/Rope.cc --- 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 #include @@ -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) { diff -r 1a03ff151abc -r b5b9d2aafdcb src/Rope.hh --- 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 diff -r 1a03ff151abc -r b5b9d2aafdcb src/Terrain.cc --- 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 @@ -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 diff -r 1a03ff151abc -r b5b9d2aafdcb src/Terrain.hh --- 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 /** * 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 - -/** * 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 > 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 diff -r 1a03ff151abc -r b5b9d2aafdcb src/Types.hh --- 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 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 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 diff -r 1a03ff151abc -r b5b9d2aafdcb src/config.h.in --- 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