From a2566b3ebd93e0bbc55a920a7be08054a9377f11 Mon Sep 17 00:00:00 2001 From: Magnus Auvinen Date: Sat, 15 Dec 2007 10:24:49 +0000 Subject: cleaned up code structure a bit --- src/editor/editor.cpp | 18 +- src/engine/client/client.c | 1146 ------------ src/engine/client/ec_client.c | 1146 ++++++++++++ src/engine/client/ec_gfx.c | 1018 +++++++++++ src/engine/client/ec_inp.c | 210 +++ src/engine/client/ec_snd.c | 441 +++++ src/engine/client/ec_srvbrowse.c | 404 +++++ src/engine/client/ec_ui.c | 277 +++ src/engine/client/ec_ui.h | 36 + src/engine/client/gfx.c | 1018 ----------- src/engine/client/inp.c | 210 --- src/engine/client/snd.c | 441 ----- src/engine/client/srvbrowse.c | 404 ----- src/engine/client/ui.c | 277 --- src/engine/client/ui.h | 36 - src/engine/compression.c | 147 -- src/engine/compression.h | 11 - src/engine/config.c | 196 --- src/engine/config.h | 41 - src/engine/config_variables.h | 56 - src/engine/datafile.c | 630 ------- src/engine/datafile.h | 27 - src/engine/detect.h | 115 -- src/engine/e_compression.c | 147 ++ src/engine/e_compression.h | 11 + src/engine/e_config.c | 196 +++ src/engine/e_config.h | 41 + src/engine/e_config_variables.h | 56 + src/engine/e_datafile.c | 630 +++++++ src/engine/e_datafile.h | 27 + src/engine/e_detect.h | 115 ++ src/engine/e_engine.c | 66 + src/engine/e_engine.h | 5 + src/engine/e_interface.h | 858 +++++++++ src/engine/e_keynames.c | 524 ++++++ src/engine/e_keys.h | 88 + src/engine/e_map.c | 61 + src/engine/e_memheap.c | 102 ++ src/engine/e_memheap.h | 6 + src/engine/e_msg.c | 55 + src/engine/e_network.c | 937 ++++++++++ src/engine/e_network.h | 113 ++ src/engine/e_packer.c | 91 + src/engine/e_packer.h | 35 + src/engine/e_protocol.h | 60 + src/engine/e_snapshot.c | 448 +++++ src/engine/e_snapshot.h | 91 + src/engine/e_system.c | 723 ++++++++ src/engine/e_system.h | 531 ++++++ src/engine/engine.c | 66 - src/engine/engine.h | 5 - src/engine/external/pa.c | 2 +- src/engine/interface.h | 858 --------- src/engine/keynames.c | 524 ------ src/engine/keys.h | 88 - src/engine/map.c | 61 - src/engine/memheap.c | 102 -- src/engine/memheap.h | 6 - src/engine/msg.c | 55 - src/engine/network.c | 937 ---------- src/engine/network.h | 113 -- src/engine/packer.c | 91 - src/engine/packer.h | 35 - src/engine/protocol.h | 60 - src/engine/server/es_server.c | 901 ++++++++++ src/engine/server/server.c | 901 ---------- src/engine/snapshot.c | 448 ----- src/engine/snapshot.h | 91 - src/engine/system.c | 723 -------- src/engine/system.h | 531 ------ src/game/client/cl_render.h | 24 - src/game/client/cl_skin.cpp | 167 -- src/game/client/cl_skin.h | 20 - src/game/client/data/createdir.txt | 1 - src/game/client/game_client.cpp | 3103 --------------------------------- src/game/client/gc_client.cpp | 3103 +++++++++++++++++++++++++++++++++ src/game/client/gc_mapres_image.cpp | 125 ++ src/game/client/gc_mapres_image.h | 19 + src/game/client/gc_mapres_tilemap.cpp | 102 ++ src/game/client/gc_mapres_tilemap.h | 26 + src/game/client/gc_menu.cpp | 1893 ++++++++++++++++++++ src/game/client/gc_menu.h | 16 + src/game/client/gc_render.h | 24 + src/game/client/gc_skin.cpp | 167 ++ src/game/client/gc_skin.h | 20 + src/game/client/mapres_image.cpp | 125 -- src/game/client/mapres_image.h | 19 - src/game/client/mapres_tilemap.cpp | 102 -- src/game/client/mapres_tilemap.h | 26 - src/game/client/menu.cpp | 1865 -------------------- src/game/client/menu.h | 16 - src/game/client/menu2.cpp | 1893 -------------------- src/game/g_game.cpp | 411 +++++ src/game/g_game.h | 171 ++ src/game/g_mapres.h | 14 + src/game/g_mapres_col.cpp | 69 + src/game/g_mapres_col.h | 13 + src/game/g_math.h | 30 + src/game/g_protocol.h | 215 +++ src/game/g_variables.h | 64 + src/game/g_version.h | 4 + src/game/g_vmath.h | 182 ++ src/game/game.cpp | 411 ----- src/game/game.h | 171 -- src/game/game_protocol.h | 215 --- src/game/game_variables.h | 64 - src/game/generated/createdir.txt | 0 src/game/mapres.h | 14 - src/game/mapres_col.cpp | 69 - src/game/mapres_col.h | 13 - src/game/math.h | 30 - src/game/server/data/createdir.txt | 1 - src/game/server/game_server.cpp | 1860 -------------------- src/game/server/game_server.h | 2 - src/game/server/gs_common.cpp | 264 +++ src/game/server/gs_common.h | 327 ++++ src/game/server/gs_game_ctf.cpp | 178 ++ src/game/server/gs_game_ctf.h | 33 + src/game/server/gs_game_dm.cpp | 42 + src/game/server/gs_game_dm.h | 7 + src/game/server/gs_game_tdm.cpp | 32 + src/game/server/gs_game_tdm.h | 10 + src/game/server/gs_server.cpp | 1859 ++++++++++++++++++++ src/game/server/srv_common.cpp | 264 --- src/game/server/srv_common.h | 327 ---- src/game/server/srv_ctf.cpp | 178 -- src/game/server/srv_ctf.h | 33 - src/game/server/srv_dm.cpp | 42 - src/game/server/srv_dm.h | 7 - src/game/server/srv_tdm.cpp | 32 - src/game/server/srv_tdm.h | 10 - src/game/version.h | 4 - src/game/vmath.h | 182 -- src/mastersrv/mastersrv.cpp | 4 +- src/tools/crapnet.cpp | 2 +- src/tools/fake_server.c | 6 +- src/tools/map_resave.c | 2 +- 137 files changed, 19887 insertions(+), 21757 deletions(-) delete mode 100644 src/engine/client/client.c create mode 100644 src/engine/client/ec_client.c create mode 100644 src/engine/client/ec_gfx.c create mode 100644 src/engine/client/ec_inp.c create mode 100644 src/engine/client/ec_snd.c create mode 100644 src/engine/client/ec_srvbrowse.c create mode 100644 src/engine/client/ec_ui.c create mode 100644 src/engine/client/ec_ui.h delete mode 100644 src/engine/client/gfx.c delete mode 100644 src/engine/client/inp.c delete mode 100644 src/engine/client/snd.c delete mode 100644 src/engine/client/srvbrowse.c delete mode 100644 src/engine/client/ui.c delete mode 100644 src/engine/client/ui.h delete mode 100644 src/engine/compression.c delete mode 100644 src/engine/compression.h delete mode 100644 src/engine/config.c delete mode 100644 src/engine/config.h delete mode 100644 src/engine/config_variables.h delete mode 100644 src/engine/datafile.c delete mode 100644 src/engine/datafile.h delete mode 100644 src/engine/detect.h create mode 100644 src/engine/e_compression.c create mode 100644 src/engine/e_compression.h create mode 100644 src/engine/e_config.c create mode 100644 src/engine/e_config.h create mode 100644 src/engine/e_config_variables.h create mode 100644 src/engine/e_datafile.c create mode 100644 src/engine/e_datafile.h create mode 100644 src/engine/e_detect.h create mode 100644 src/engine/e_engine.c create mode 100644 src/engine/e_engine.h create mode 100644 src/engine/e_interface.h create mode 100644 src/engine/e_keynames.c create mode 100644 src/engine/e_keys.h create mode 100644 src/engine/e_map.c create mode 100644 src/engine/e_memheap.c create mode 100644 src/engine/e_memheap.h create mode 100644 src/engine/e_msg.c create mode 100644 src/engine/e_network.c create mode 100644 src/engine/e_network.h create mode 100644 src/engine/e_packer.c create mode 100644 src/engine/e_packer.h create mode 100644 src/engine/e_protocol.h create mode 100644 src/engine/e_snapshot.c create mode 100644 src/engine/e_snapshot.h create mode 100644 src/engine/e_system.c create mode 100644 src/engine/e_system.h delete mode 100644 src/engine/engine.c delete mode 100644 src/engine/engine.h delete mode 100644 src/engine/interface.h delete mode 100644 src/engine/keynames.c delete mode 100644 src/engine/keys.h delete mode 100644 src/engine/map.c delete mode 100644 src/engine/memheap.c delete mode 100644 src/engine/memheap.h delete mode 100644 src/engine/msg.c delete mode 100644 src/engine/network.c delete mode 100644 src/engine/network.h delete mode 100644 src/engine/packer.c delete mode 100644 src/engine/packer.h delete mode 100644 src/engine/protocol.h create mode 100644 src/engine/server/es_server.c delete mode 100644 src/engine/server/server.c delete mode 100644 src/engine/snapshot.c delete mode 100644 src/engine/snapshot.h delete mode 100644 src/engine/system.c delete mode 100644 src/engine/system.h delete mode 100644 src/game/client/cl_render.h delete mode 100644 src/game/client/cl_skin.cpp delete mode 100644 src/game/client/cl_skin.h delete mode 100644 src/game/client/data/createdir.txt delete mode 100644 src/game/client/game_client.cpp create mode 100644 src/game/client/gc_client.cpp create mode 100644 src/game/client/gc_mapres_image.cpp create mode 100644 src/game/client/gc_mapres_image.h create mode 100644 src/game/client/gc_mapres_tilemap.cpp create mode 100644 src/game/client/gc_mapres_tilemap.h create mode 100644 src/game/client/gc_menu.cpp create mode 100644 src/game/client/gc_menu.h create mode 100644 src/game/client/gc_render.h create mode 100644 src/game/client/gc_skin.cpp create mode 100644 src/game/client/gc_skin.h delete mode 100644 src/game/client/mapres_image.cpp delete mode 100644 src/game/client/mapres_image.h delete mode 100644 src/game/client/mapres_tilemap.cpp delete mode 100644 src/game/client/mapres_tilemap.h delete mode 100644 src/game/client/menu.cpp delete mode 100644 src/game/client/menu.h delete mode 100644 src/game/client/menu2.cpp create mode 100644 src/game/g_game.cpp create mode 100644 src/game/g_game.h create mode 100644 src/game/g_mapres.h create mode 100644 src/game/g_mapres_col.cpp create mode 100644 src/game/g_mapres_col.h create mode 100644 src/game/g_math.h create mode 100644 src/game/g_protocol.h create mode 100644 src/game/g_variables.h create mode 100644 src/game/g_version.h create mode 100644 src/game/g_vmath.h delete mode 100644 src/game/game.cpp delete mode 100644 src/game/game.h delete mode 100644 src/game/game_protocol.h delete mode 100644 src/game/game_variables.h create mode 100644 src/game/generated/createdir.txt delete mode 100644 src/game/mapres.h delete mode 100644 src/game/mapres_col.cpp delete mode 100644 src/game/mapres_col.h delete mode 100644 src/game/math.h delete mode 100644 src/game/server/data/createdir.txt delete mode 100644 src/game/server/game_server.cpp delete mode 100644 src/game/server/game_server.h create mode 100644 src/game/server/gs_common.cpp create mode 100644 src/game/server/gs_common.h create mode 100644 src/game/server/gs_game_ctf.cpp create mode 100644 src/game/server/gs_game_ctf.h create mode 100644 src/game/server/gs_game_dm.cpp create mode 100644 src/game/server/gs_game_dm.h create mode 100644 src/game/server/gs_game_tdm.cpp create mode 100644 src/game/server/gs_game_tdm.h create mode 100644 src/game/server/gs_server.cpp delete mode 100644 src/game/server/srv_common.cpp delete mode 100644 src/game/server/srv_common.h delete mode 100644 src/game/server/srv_ctf.cpp delete mode 100644 src/game/server/srv_ctf.h delete mode 100644 src/game/server/srv_dm.cpp delete mode 100644 src/game/server/srv_dm.h delete mode 100644 src/game/server/srv_tdm.cpp delete mode 100644 src/game/server/srv_tdm.h delete mode 100644 src/game/version.h delete mode 100644 src/game/vmath.h (limited to 'src') diff --git a/src/editor/editor.cpp b/src/editor/editor.cpp index 4430cde1..43ee1651 100644 --- a/src/editor/editor.cpp +++ b/src/editor/editor.cpp @@ -3,18 +3,18 @@ #include extern "C" { - #include - #include - #include - #include - #include + #include + #include + #include + #include + #include } -#include -#include +#include +#include //#include "game/mapres_col.h" -#include -#include +#include +#include static int font_texture = 0; static int checker_texture = 0; diff --git a/src/engine/client/client.c b/src/engine/client/client.c deleted file mode 100644 index 646ca4b4..00000000 --- a/src/engine/client/client.c +++ /dev/null @@ -1,1146 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include "ui.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -const int prediction_margin = 10; /* magic network prediction value */ - -/* - Server Time - Client Mirror Time - Client Predicted Time - - Snapshot Latency - Downstream latency - - Prediction Latency - Upstream latency -*/ - -/* network client, must be accessible from other parts like the server browser */ -NETCLIENT *net; - -/* TODO: ugly, fix me */ -extern void client_serverbrowse_set(NETADDR4 *addr, int request, SERVER_INFO *info); - - -static int snapshot_part; -static int64 local_start_time; - -static int debug_font; -static float frametime = 0.0001f; -static float frametime_low = 1.0f; -static float frametime_high = 0.0f; -static int frames = 0; -static NETADDR4 server_address; -static int window_must_refocus = 0; -static int snaploss = 0; -static int snapcrcerrors = 0; - -static int ack_game_tick = -1; -static int current_recv_tick = 0; - -/* current time */ -static int current_tick = 0; -static float intratick = 0; - -/* predicted time */ -static int current_predtick = 0; -static float intrapredtick = 0; - -static struct /* TODO: handle input better */ -{ - int data[MAX_INPUT_SIZE]; /* the input data */ - int tick; /* the tick that the input is for */ - int64 game_time; /* prediction latency when we sent this input */ - int64 time; -} inputs[200]; -static int current_input = 0; - -enum -{ - GRAPH_MAX=128 -}; - -typedef struct -{ - float min, max; - float values[GRAPH_MAX]; - int index; -} GRAPH; - -static void graph_init(GRAPH *g, float min, float max) -{ - g->min = min; - g->max = max; - g->index = 0; -} - -static void graph_add(GRAPH *g, float v) -{ - g->index = (g->index+1)&(GRAPH_MAX-1); - g->values[g->index] = v; -} - -static void graph_render(GRAPH *g, float x, float y, float w, float h) -{ - int i; - gfx_texture_set(-1); - - gfx_quads_begin(); - gfx_setcolor(0, 0, 0, 1); - gfx_quads_drawTL(x, y, w, h); - gfx_quads_end(); - - gfx_lines_begin(); - gfx_setcolor(0.95f, 0.95f, 0.95f, 1); - gfx_lines_draw(x, y+h/2, x+w, y+h/2); - gfx_setcolor(0.5f, 0.5f, 0.5f, 1); - gfx_lines_draw(x, y+(h*3)/4, x+w, y+(h*3)/4); - gfx_lines_draw(x, y+h/4, x+w, y+h/4); - for(i = 1; i < GRAPH_MAX; i++) - { - float a0 = (i-1)/(float)GRAPH_MAX; - float a1 = i/(float)GRAPH_MAX; - int i0 = (g->index+i-1)&(GRAPH_MAX-1); - int i1 = (g->index+i)&(GRAPH_MAX-1); - - float v0 = g->values[i0]; - float v1 = g->values[i1]; - - gfx_setcolor(0, 1, 0, 1); - gfx_lines_draw(x+a0*w, y+h-v0*h, x+a1*w, y+h-v1*h); - } - gfx_lines_end(); -} - -typedef struct -{ - int64 snap; - int64 current; - int64 target; - - int64 rlast; - int64 tlast; - GRAPH graph; -} SMOOTHTIME; - -static void st_init(SMOOTHTIME *st, int64 target) -{ - st->snap = time_get(); - st->current = target; - st->target = target; - graph_init(&st->graph, 0.0f, 1.0f); -} - -static int64 st_get(SMOOTHTIME *st, int64 now) -{ - float adjust_speed, a; - int64 c = st->current + (now - st->snap); - int64 t = st->target + (now - st->snap); - int64 r; - - /* it's faster to adjust upward instead of downward */ - /* we might need to adjust these abit */ - adjust_speed = 0.25f; /*0.99f;*/ - if(t > c) - adjust_speed = 500.0f; - - a = ((now-st->snap)/(float)time_freq()) * adjust_speed; - if(a > 1.0f) - a = 1.0f; - - r = c + (int64)((t-c)*a); - - graph_add(&st->graph, a+0.5f); - - return r; -} - -static void st_update(SMOOTHTIME *st, int64 target) -{ - int64 now = time_get(); - st->current = st_get(st, now); - st->snap = now; - st->target = target; -} - -SMOOTHTIME game_time; -SMOOTHTIME predicted_time; - -GRAPH intra_graph; -GRAPH predict_graph; - -/* --- input snapping --- */ -static int input_data[MAX_INPUT_SIZE] = {0}; -static int input_data_size; -static int input_is_changed = 1; -static GRAPH input_late_graph; -void snap_input(void *data, int size) -{ - if(input_data_size != size || memcmp(input_data, data, size)) - input_is_changed = 1; - mem_copy(input_data, data, size); - input_data_size = size; -} - -/* -- snapshot handling --- */ -enum -{ - NUM_SNAPSHOT_TYPES=2 -}; - -SNAPSTORAGE snapshot_storage; -static SNAPSTORAGE_HOLDER *snapshots[NUM_SNAPSHOT_TYPES]; -static int recived_snapshots; -static char snapshot_incomming_data[MAX_SNAPSHOT_SIZE]; - -/* --- */ - -const void *snap_get_item(int snapid, int index, SNAP_ITEM *item) -{ - SNAPSHOT_ITEM *i; - dbg_assert(snapid >= 0 && snapid < NUM_SNAPSHOT_TYPES, "invalid snapid"); - i = snapshot_get_item(snapshots[snapid]->snap, index); - item->type = snapitem_type(i); - item->id = snapitem_id(i); - return (void *)snapitem_data(i); -} - -const void *snap_find_item(int snapid, int type, int id) -{ - /* TODO: linear search. should be fixed. */ - int i; - for(i = 0; i < snapshots[snapid]->snap->num_items; i++) - { - SNAPSHOT_ITEM *itm = snapshot_get_item(snapshots[snapid]->snap, i); - if(snapitem_type(itm) == type && snapitem_id(itm) == id) - return (void *)snapitem_data(itm); - } - return 0x0; -} - -int snap_num_items(int snapid) -{ - dbg_assert(snapid >= 0 && snapid < NUM_SNAPSHOT_TYPES, "invalid snapid"); - return snapshots[snapid]->snap->num_items; -} - -/* ------ time functions ------ */ -float client_intratick() { return intratick; } -float client_intrapredtick() { return intrapredtick; } -int client_tick() { return current_tick; } -int client_predtick() { return current_predtick; } -int client_tickspeed() { return SERVER_TICK_SPEED; } -float client_frametime() { return frametime; } -float client_localtime() { return (time_get()-local_start_time)/(float)(time_freq()); } - -/* ----- send functions ----- */ -int client_send_msg() -{ - const MSG_INFO *info = msg_get_info(); - NETPACKET packet; - mem_zero(&packet, sizeof(NETPACKET)); - - packet.client_id = 0; - packet.data = info->data; - packet.data_size = info->size; - - if(info->flags&MSGFLAG_VITAL) - packet.flags = PACKETFLAG_VITAL; - - netclient_send(net, &packet); - return 0; -} - -static void client_send_info() -{ - msg_pack_start_system(NETMSG_INFO, MSGFLAG_VITAL); - msg_pack_string(modc_net_version(), 128); - msg_pack_string(config.player_name, 128); - msg_pack_string(config.clan_name, 128); - msg_pack_string(config.password, 128); - msg_pack_end(); - client_send_msg(); -} - - -static void client_send_entergame() -{ - msg_pack_start_system(NETMSG_ENTERGAME, MSGFLAG_VITAL); - msg_pack_end(); - client_send_msg(); -} - -static void client_send_ready() -{ - msg_pack_start_system(NETMSG_READY, MSGFLAG_VITAL); - msg_pack_end(); - client_send_msg(); -} - -void client_rcon(const char *cmd) -{ - msg_pack_start_system(NETMSG_CMD, MSGFLAG_VITAL); - msg_pack_string(config.rcon_password, 32); - msg_pack_string(cmd, 256); - msg_pack_end(); - client_send_msg(); -} - -int client_connection_problems() -{ - return netclient_gotproblems(net); -} - -static void client_send_input() -{ - int64 now = time_get(); - int i; - - if(current_predtick <= 0) - return; - - msg_pack_start_system(NETMSG_INPUT, 0); - msg_pack_int(ack_game_tick); - msg_pack_int(current_predtick); - msg_pack_int(input_data_size); - - inputs[current_input].tick = current_predtick; - inputs[current_input].game_time = st_get(&predicted_time, now); - inputs[current_input].time = now; - - for(i = 0; i < input_data_size/4; i++) - { - inputs[current_input].data[i] = input_data[i]; - msg_pack_int(input_data[i]); - } - - current_input++; - current_input%=200; - - msg_pack_end(); - client_send_msg(); -} - -/* TODO: OPT: do this alot smarter! */ -int *client_get_input(int tick) -{ - int i; - int best = -1; - for(i = 0; i < 200; i++) - { - if(inputs[i].tick <= tick && (best == -1 || inputs[best].tick < inputs[i].tick)) - best = i; - } - - if(best != -1) - return (int *)inputs[best].data; - return 0; -} - -/* ------ state handling ----- */ -static int state = CLIENTSTATE_OFFLINE; -int client_state() { return state; } -static void client_set_state(int s) -{ - int old = state; - if(config.debug) - dbg_msg("client", "state change. last=%d current=%d", state, s); - state = s; - if(old != s) - modc_statechange(state, old); -} - -/* called when the map is loaded and we should init for a new round */ -static void client_on_enter_game() -{ - /* reset input */ - int i; - for(i = 0; i < 200; i++) - inputs[i].tick = -1; - current_input = 0; - - /* reset snapshots */ - snapshots[SNAP_CURRENT] = 0; - snapshots[SNAP_PREV] = 0; - snapstorage_purge_all(&snapshot_storage); - recived_snapshots = 0; - current_predtick = 0; - current_recv_tick = 0; -} - -void client_entergame() -{ - /* now we will wait for two snapshots */ - /* to finish the connection */ - client_send_entergame(); - client_on_enter_game(); -} - -void client_connect(const char *server_address_str) -{ - char buf[512]; - const char *port_str = 0; - int k; - int port = 8303; - - dbg_msg("client", "connecting to '%s'", server_address_str); - - strncpy(buf, server_address_str, 512); - - for(k = 0; buf[k]; k++) - { - if(buf[k] == ':') - { - port_str = &(buf[k+1]); - buf[k] = 0; - break; - } - } - - if(port_str) - port = atoi(port_str); - - if(net_host_lookup(buf, port, &server_address) != 0) - dbg_msg("client", "could not find the address of %s, connecting to localhost", buf); - - netclient_connect(net, &server_address); - client_set_state(CLIENTSTATE_CONNECTING); - - graph_init(&intra_graph, 0.0f, 1.0f); - graph_init(&input_late_graph, 0.0f, 1.0f); - graph_init(&predict_graph, 0.0f, 200.0f); - -} - -void client_disconnect_with_reason(const char *reason) -{ - netclient_disconnect(net, reason); - client_set_state(CLIENTSTATE_OFFLINE); - map_unload(); -} - -void client_disconnect() -{ - netclient_disconnect(net, 0); - client_set_state(CLIENTSTATE_OFFLINE); - map_unload(); -} - -static int client_load_data() -{ - debug_font = gfx_load_texture("data/debug_font.png"); - return 1; -} - -extern int snapshot_data_rate[0xffff]; -extern int snapshot_data_updates[0xffff]; - -static void client_debug_render() -{ - static NETSTATS prev, current; - static int64 last_snap = 0; - static float frametime_avg = 0; - char buffer[512]; - - if(!config.debug) - return; - - gfx_blend_normal(); - gfx_texture_set(debug_font); - gfx_mapscreen(0,0,gfx_screenwidth(),gfx_screenheight()); - - if(time_get()-last_snap > time_freq()/10) - { - last_snap = time_get(); - prev = current; - netclient_stats(net, ¤t); - } - - frametime_avg = frametime_avg*0.9f + frametime*0.1f; - sprintf(buffer, "ticks: %8d %8d send: %6d recv: %6d snaploss: %d mem %dk gfxmem: %dk fps: %3d", - current_tick, current_predtick, - (current.send_bytes-prev.send_bytes)*10, - (current.recv_bytes-prev.recv_bytes)*10, - snaploss, - mem_allocated()/1024, - gfx_memory_usage()/1024, - (int)(1.0f/frametime_avg)); - gfx_quads_text(2, 2, 16, buffer); - - sprintf(buffer, "ui: %p %p", ui_hot_item(), ui_active_item()); - gfx_quads_text(2, 16, 16, buffer); - - - /* render rates */ - { - int i; - for(i = 0; i < 256; i++) - { - if(snapshot_data_rate[i]) - { - sprintf(buffer, "%4d : %8d %8d %8d", i, snapshot_data_rate[i]/8, snapshot_data_updates[i], - (snapshot_data_rate[i]/snapshot_data_updates[i])/8); - gfx_quads_text(2, 100+i*8, 16, buffer); - } - } - } - - /* render graphs */ - gfx_mapscreen(0,0,400.0f,300.0f); - graph_render(&predict_graph, 300, 10, 90, 50); - graph_render(&predicted_time.graph, 300, 10+50+10, 90, 50); - - graph_render(&intra_graph, 300, 10+50+10+50+10, 90, 50); - graph_render(&input_late_graph, 300, 10+50+10+50+10+50+10, 90, 50); - -} - -void client_quit() -{ - client_set_state(CLIENTSTATE_QUITING); -} - -const char *client_error_string() -{ - return netclient_error_string(net); -} - -static void client_render() -{ - gfx_clear(0.0f,0.0f,0.0f); - modc_render(); - client_debug_render(); -} - -static const char *client_load_map(const char *mapname, int wanted_crc) -{ - static char errormsg[128]; - DATAFILE *df; - char buf[512]; - int crc; - - dbg_msg("client", "loading map, map=%s wanted crc=%08x", mapname, wanted_crc); - client_set_state(CLIENTSTATE_LOADING); - - sprintf(buf, "data/maps/%s.map", mapname); - df = datafile_load(buf); - if(!df) - { - sprintf(errormsg, "map '%s' not found", mapname); - return errormsg; - } - - /* get the crc of the map */ - crc = datafile_crc(buf); - if(crc != wanted_crc) - { - datafile_unload(df); - sprintf(errormsg, "map differs from the server. %08x != %08x", crc, wanted_crc); - return errormsg; - } - - map_set(df); - return NULL; -} - -static void client_process_packet(NETPACKET *packet) -{ - if(packet->client_id == -1) - { - /* connectionlesss */ - if(packet->data_size >= (int)sizeof(SERVERBROWSE_LIST) && - memcmp(packet->data, SERVERBROWSE_LIST, sizeof(SERVERBROWSE_LIST)) == 0) - { - int size = packet->data_size-sizeof(SERVERBROWSE_LIST); - int num = size/sizeof(NETADDR4); - NETADDR4 *addrs = (NETADDR4 *)((char*)packet->data+sizeof(SERVERBROWSE_LIST)); - int i; - - for(i = 0; i < num; i++) - { - NETADDR4 addr = addrs[i]; - SERVER_INFO info = {0}; - -#if defined(CONF_ARCH_ENDIAN_BIG) - const char *tmp = (const char *)&addr.port; - addr.port = (tmp[1]<<8) | tmp[0]; -#endif - - info.latency = 999; - sprintf(info.address, "%d.%d.%d.%d:%d", - addr.ip[0], addr.ip[1], addr.ip[2], - addr.ip[3], addr.port); - sprintf(info.name, "\255%d.%d.%d.%d:%d", /* the \255 is to make sure that it's sorted last */ - addr.ip[0], addr.ip[1], addr.ip[2], - addr.ip[3], addr.port); - - client_serverbrowse_set(addrs+i, 1, &info); - } - } - - if(packet->data_size >= (int)sizeof(SERVERBROWSE_INFO) && - memcmp(packet->data, SERVERBROWSE_INFO, sizeof(SERVERBROWSE_INFO)) == 0) - { - /* we got ze info */ - UNPACKER up; - SERVER_INFO info = {0}; - int i; - - unpacker_reset(&up, (unsigned char*)packet->data+sizeof(SERVERBROWSE_INFO), packet->data_size-sizeof(SERVERBROWSE_INFO)); - - strncpy(info.version, unpacker_get_string(&up), 32); - strncpy(info.name, unpacker_get_string(&up), 64); - strncpy(info.map, unpacker_get_string(&up), 32); - info.game_type = atol(unpacker_get_string(&up)); - info.flags = atol(unpacker_get_string(&up)); - info.progression = atol(unpacker_get_string(&up)); - info.num_players = atol(unpacker_get_string(&up)); - info.max_players = atol(unpacker_get_string(&up)); - sprintf(info.address, "%d.%d.%d.%d:%d", - packet->address.ip[0], packet->address.ip[1], packet->address.ip[2], - packet->address.ip[3], packet->address.port); - - for(i = 0; i < info.num_players; i++) - { - strncpy(info.player_names[i], unpacker_get_string(&up), 48); - info.player_scores[i] = atol(unpacker_get_string(&up)); - } - - /* TODO: unpack players aswell */ - client_serverbrowse_set(&packet->address, 0, &info); - } - } - else - { - int sys; - int msg = msg_unpack_start(packet->data, packet->data_size, &sys); - - if(sys) - { - /* system message */ - if(msg == NETMSG_MAP) - { - const char *map = msg_unpack_string(); - int map_crc = msg_unpack_int(); - const char *error = client_load_map(map, map_crc); - - if(!error) - { - dbg_msg("client/network", "loading done"); - client_send_ready(); - modc_connected(); - } - else - { - client_disconnect_with_reason(error); - } - } - else if(msg == NETMSG_SNAP || msg == NETMSG_SNAPSINGLE || msg == NETMSG_SNAPEMPTY) - { - /*dbg_msg("client/network", "got snapshot"); */ - int num_parts = 1; - int part = 0; - int game_tick = msg_unpack_int(); - int delta_tick = game_tick-msg_unpack_int(); - int input_predtick = msg_unpack_int(); - int time_left = msg_unpack_int(); - int part_size = 0; - int crc = 0; - int complete_size = 0; - - if(msg == NETMSG_SNAP) - { - num_parts = msg_unpack_int(); - part = msg_unpack_int(); - } - - if(msg != NETMSG_SNAPEMPTY) - { - crc = msg_unpack_int(); - part_size = msg_unpack_int(); - } - - /* TODO: adjust our prediction time */ - if(time_left) - { - int k; - - graph_add(&input_late_graph, time_left/100.0f+0.5f); - - if(time_left < 0) - dbg_msg("client", "input was late with %d ms", time_left); - - for(k = 0; k < 200; k++) /* TODO: do this better */ - { - if(inputs[k].tick == input_predtick) - { - /*-1000/50 prediction_margin */ - int64 target = inputs[k].game_time + (time_get() - inputs[k].time); - st_update(&predicted_time, target - (int64)(((time_left-prediction_margin)/1000.0f)*time_freq())); - break; - } - } - } - - if(snapshot_part == part && game_tick > current_recv_tick) - { - /* TODO: clean this up abit */ - const char *d = (const char *)msg_unpack_raw(part_size); - mem_copy((char*)snapshot_incomming_data + part*MAX_SNAPSHOT_PACKSIZE, d, part_size); - snapshot_part++; - - if(snapshot_part == num_parts) - { - static SNAPSHOT emptysnap; - SNAPSHOT *deltashot = &emptysnap; - int purgetick; - void *deltadata; - int deltasize; - unsigned char tmpbuffer[MAX_SNAPSHOT_SIZE]; - unsigned char tmpbuffer2[MAX_SNAPSHOT_SIZE]; - unsigned char tmpbuffer3[MAX_SNAPSHOT_SIZE]; - int snapsize; - - complete_size = (num_parts-1) * MAX_SNAPSHOT_PACKSIZE + part_size; - - snapshot_part = 0; - - /* find snapshot that we should use as delta */ - emptysnap.data_size = 0; - emptysnap.num_items = 0; - - /* find delta */ - if(delta_tick >= 0) - { - int deltashot_size = snapstorage_get(&snapshot_storage, delta_tick, 0, &deltashot); - - if(deltashot_size < 0) - { - /* couldn't find the delta snapshots that the server used */ - /* to compress this snapshot. force the server to resync */ - if(config.debug) - dbg_msg("client", "error, couldn't find the delta snapshot"); - - /* ack snapshot */ - /* TODO: combine this with the input message */ - ack_game_tick = -1; - return; - } - } - - /* decompress snapshot */ - deltadata = snapshot_empty_delta(); - deltasize = sizeof(int)*3; - - if(complete_size) - { - int compsize = zerobit_decompress(snapshot_incomming_data, complete_size, tmpbuffer); - int intsize = intpack_decompress(tmpbuffer, compsize, tmpbuffer2); - deltadata = tmpbuffer2; - deltasize = intsize; - } - - /*dbg_msg("UNPACK", "%d unpacked with %d", game_tick, delta_tick); */ - - purgetick = delta_tick; - snapsize = snapshot_unpack_delta(deltashot, (SNAPSHOT*)tmpbuffer3, deltadata, deltasize); - if(msg != NETMSG_SNAPEMPTY && snapshot_crc((SNAPSHOT*)tmpbuffer3) != crc) - { - if(config.debug) - dbg_msg("client", "snapshot crc error %d", snapcrcerrors); - snapcrcerrors++; - if(snapcrcerrors > 10) - { - /* to many errors, send reset */ - ack_game_tick = -1; - client_send_input(); - snapcrcerrors = 0; - } - return; - } - else - { - if(snapcrcerrors) - snapcrcerrors--; - } - - /* purge old snapshots */ - purgetick = delta_tick; - if(snapshots[SNAP_PREV] && snapshots[SNAP_PREV]->tick < purgetick) - purgetick = snapshots[SNAP_PREV]->tick; - if(snapshots[SNAP_CURRENT] && snapshots[SNAP_CURRENT]->tick < purgetick) - purgetick = snapshots[SNAP_PREV]->tick; - snapstorage_purge_until(&snapshot_storage, purgetick); - /*client_snapshot_purge_until(game_tick-50); */ - - /* add new */ - snapstorage_add(&snapshot_storage, game_tick, time_get(), snapsize, (SNAPSHOT*)tmpbuffer3); - - /* apply snapshot, cycle pointers */ - recived_snapshots++; - - - if(current_recv_tick > 0) - snaploss += game_tick-current_recv_tick-1; - current_recv_tick = game_tick; - - /* we got two snapshots until we see us self as connected */ - if(recived_snapshots == 2) - { - /* start at 200ms and work from there */ - st_init(&predicted_time, game_tick*time_freq()/50); - st_init(&game_time, (game_tick-1)*time_freq()/50); - snapshots[SNAP_PREV] = snapshot_storage.first; - snapshots[SNAP_CURRENT] = snapshot_storage.last; - local_start_time = time_get(); - client_set_state(CLIENTSTATE_ONLINE); - } - - { - int64 now = time_get(); - graph_add(&predict_graph, (st_get(&predicted_time, now)-st_get(&game_time, now))/(float)time_freq()); - } - - st_update(&game_time, (game_tick-1)*time_freq()/50); - - /* ack snapshot */ - ack_game_tick = game_tick; - } - } - else - { - dbg_msg("client", "snapsht reset!"); - snapshot_part = 0; - } - } - } - else - { - /* game message */ - modc_message(msg); - } - } -} - -static void client_pump_network() -{ - NETPACKET packet; - - netclient_update(net); - - /* check for errors */ - if(client_state() != CLIENTSTATE_OFFLINE && netclient_state(net) == NETSTATE_OFFLINE) - { - client_set_state(CLIENTSTATE_OFFLINE); - dbg_msg("client", "offline error='%s'", netclient_error_string(net)); - } - - /* */ - if(client_state() == CLIENTSTATE_CONNECTING && netclient_state(net) == NETSTATE_ONLINE) - { - /* we switched to online */ - dbg_msg("client", "connected, sending info"); - client_set_state(CLIENTSTATE_LOADING); - client_send_info(); - } - - /* process packets */ - while(netclient_recv(net, &packet)) - client_process_packet(&packet); -} - -static void client_update() -{ - /* switch snapshot */ - if(client_state() != CLIENTSTATE_OFFLINE && recived_snapshots >= 3) - { - int repredict = 0; - int64 now = st_get(&game_time, time_get()); - int64 pred_now = st_get(&predicted_time, time_get()); - - while(1) - { - SNAPSTORAGE_HOLDER *cur = snapshots[SNAP_CURRENT]; - int64 tickstart = (cur->tick)*time_freq()/50; - - if(tickstart < now) - { - SNAPSTORAGE_HOLDER *next = snapshots[SNAP_CURRENT]->next; - if(next) - { - snapshots[SNAP_PREV] = snapshots[SNAP_CURRENT]; - snapshots[SNAP_CURRENT] = next; - - /* set tick */ - current_tick = snapshots[SNAP_CURRENT]->tick; - - if(snapshots[SNAP_CURRENT] && snapshots[SNAP_PREV]) - { - modc_newsnapshot(); - repredict = 1; - } - } - else - break; - } - else - break; - } - - if(snapshots[SNAP_CURRENT] && snapshots[SNAP_PREV]) - { - int64 curtick_start = (snapshots[SNAP_CURRENT]->tick)*time_freq()/50; - int64 prevtick_start = (snapshots[SNAP_PREV]->tick)*time_freq()/50; - /*tg_add(&predicted_time_graph, pred_now, 0); */ - int prev_pred_tick = (int)(pred_now*50/time_freq()); - int new_pred_tick = prev_pred_tick+1; - static float last_intrapred = 0; - - intratick = (now - prevtick_start) / (float)(curtick_start-prevtick_start); - - graph_add(&intra_graph, intratick*0.25f); - - curtick_start = new_pred_tick*time_freq()/50; - prevtick_start = prev_pred_tick*time_freq()/50; - intrapredtick = (pred_now - prevtick_start) / (float)(curtick_start-prevtick_start); - - - if(new_pred_tick > current_predtick) - { - last_intrapred = intrapredtick; - current_predtick = new_pred_tick; - repredict = 1; - - /* send input */ - client_send_input(); - } - - if(intrapredtick < last_intrapred) - dbg_msg("client", "prediction time goes backwards, that can't be good"); - last_intrapred = intrapredtick; - } - - /* only do sane predictions */ - if(repredict) - { - if(current_predtick > current_tick && current_predtick < current_tick+50) - modc_predict(); - } - } - - /* STRESS TEST: join the server again */ - if(client_state() == CLIENTSTATE_OFFLINE && config.dbg_stress && (frames%100) == 0) - client_connect(config.dbg_stress_server); - - /* pump the network */ - client_pump_network(); - - /* update the server browser */ - client_serverbrowse_update(); -} - -extern int editor_update_and_render(); -extern void editor_init(); - -static void client_run() -{ - NETADDR4 bindaddr; - int64 reporttime = time_get(); - int64 reportinterval = time_freq()*1; - int editor_active = 0; - - local_start_time = time_get(); - snapshot_part = 0; - - /* init graphics and sound */ - if(!gfx_init()) - return; - - /* init the editor */ - editor_init(); - - /* sound is allowed to fail */ - snd_init(); - - /* load data */ - if(!client_load_data()) - return; - - /* init the mod */ - modc_init(); - dbg_msg("client", "version %s", modc_net_version()); - - /* open socket */ - mem_zero(&bindaddr, sizeof(bindaddr)); - net = netclient_open(bindaddr, 0); - - /* connect to the server if wanted */ - /* - if(config.cl_connect[0] != 0) - client_connect(config.cl_connect); - config.cl_connect[0] = 0; - */ - - inp_mouse_mode_relative(); - - while (1) - { - int64 frame_start_time = time_get(); - frames++; - - /* update input */ - inp_update(); - - /* refocus */ - if(!gfx_window_active()) - { - if(window_must_refocus == 0) - inp_mouse_mode_absolute(); - window_must_refocus = 1; - } - - if(window_must_refocus && gfx_window_active()) - { - if(window_must_refocus < 3) - { - inp_mouse_mode_absolute(); - window_must_refocus++; - } - - if(inp_key_pressed(KEY_MOUSE_1)) - { - inp_mouse_mode_relative(); - window_must_refocus = 0; - } - } - - /* screenshot button */ - if(inp_key_down(config.key_screenshot)) - gfx_screenshot(); - - /* some debug keys */ - /* - if(config.debug) - { - if(inp_key_pressed(KEY_F1)) - inp_mouse_mode_absolute(); - if(inp_key_pressed(KEY_F2)) - inp_mouse_mode_relative(); - - if(inp_key_pressed(KEY_F5)) - { - ack_game_tick = -1; - client_send_input(); - } - }*/ - - /* panic quit button */ - if(inp_key_pressed(KEY_LCTRL) && inp_key_pressed(KEY_LSHIFT) && inp_key_pressed('Q')) - break; - - if(inp_key_pressed(KEY_LCTRL) && inp_key_pressed(KEY_LSHIFT) && inp_key_down('E')) - editor_active = editor_active^1; - - if(!gfx_window_open()) - break; - - /* render */ - if(editor_active) - { - client_update(); - editor_update_and_render(); - gfx_swap(); - } - else - { - client_update(); - - if(config.dbg_stress) - { - if((frames%10) == 0) - { - client_render(); - gfx_swap(); - } - } - else - { - client_render(); - gfx_swap(); - } - } - - /* check conditions */ - if(client_state() == CLIENTSTATE_QUITING) - break; - - /* be nice */ - if(config.cl_cpu_throttle || !gfx_window_active()) - thread_sleep(1); - - if(reporttime < time_get()) - { - if(config.debug) - { - dbg_msg("client/report", "fps=%.02f (%.02f %.02f) netstate=%d", - frames/(float)(reportinterval/time_freq()), - 1.0f/frametime_high, - 1.0f/frametime_low, - netclient_state(net)); - } - frametime_low = 1; - frametime_high = 0; - frames = 0; - reporttime += reportinterval; - } - - /* update frametime */ - frametime = (time_get()-frame_start_time)/(float)time_freq(); - if(frametime < frametime_low) - frametime_low = frametime; - if(frametime > frametime_high) - frametime_high = frametime; - } - - modc_shutdown(); - client_disconnect(); - - gfx_shutdown(); - snd_shutdown(); -} - - -int editor_main(int argc, char **argv); - -int main(int argc, char **argv) -{ - /* init the engine */ - dbg_msg("client", "starting..."); - engine_init("Teewars", argc, argv); - - client_run(); - - engine_writeconfig(); - return 0; -} diff --git a/src/engine/client/ec_client.c b/src/engine/client/ec_client.c new file mode 100644 index 00000000..78e7877c --- /dev/null +++ b/src/engine/client/ec_client.c @@ -0,0 +1,1146 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include "ec_ui.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +const int prediction_margin = 10; /* magic network prediction value */ + +/* + Server Time + Client Mirror Time + Client Predicted Time + + Snapshot Latency + Downstream latency + + Prediction Latency + Upstream latency +*/ + +/* network client, must be accessible from other parts like the server browser */ +NETCLIENT *net; + +/* TODO: ugly, fix me */ +extern void client_serverbrowse_set(NETADDR4 *addr, int request, SERVER_INFO *info); + + +static int snapshot_part; +static int64 local_start_time; + +static int debug_font; +static float frametime = 0.0001f; +static float frametime_low = 1.0f; +static float frametime_high = 0.0f; +static int frames = 0; +static NETADDR4 server_address; +static int window_must_refocus = 0; +static int snaploss = 0; +static int snapcrcerrors = 0; + +static int ack_game_tick = -1; +static int current_recv_tick = 0; + +/* current time */ +static int current_tick = 0; +static float intratick = 0; + +/* predicted time */ +static int current_predtick = 0; +static float intrapredtick = 0; + +static struct /* TODO: handle input better */ +{ + int data[MAX_INPUT_SIZE]; /* the input data */ + int tick; /* the tick that the input is for */ + int64 game_time; /* prediction latency when we sent this input */ + int64 time; +} inputs[200]; +static int current_input = 0; + +enum +{ + GRAPH_MAX=128 +}; + +typedef struct +{ + float min, max; + float values[GRAPH_MAX]; + int index; +} GRAPH; + +static void graph_init(GRAPH *g, float min, float max) +{ + g->min = min; + g->max = max; + g->index = 0; +} + +static void graph_add(GRAPH *g, float v) +{ + g->index = (g->index+1)&(GRAPH_MAX-1); + g->values[g->index] = v; +} + +static void graph_render(GRAPH *g, float x, float y, float w, float h) +{ + int i; + gfx_texture_set(-1); + + gfx_quads_begin(); + gfx_setcolor(0, 0, 0, 1); + gfx_quads_drawTL(x, y, w, h); + gfx_quads_end(); + + gfx_lines_begin(); + gfx_setcolor(0.95f, 0.95f, 0.95f, 1); + gfx_lines_draw(x, y+h/2, x+w, y+h/2); + gfx_setcolor(0.5f, 0.5f, 0.5f, 1); + gfx_lines_draw(x, y+(h*3)/4, x+w, y+(h*3)/4); + gfx_lines_draw(x, y+h/4, x+w, y+h/4); + for(i = 1; i < GRAPH_MAX; i++) + { + float a0 = (i-1)/(float)GRAPH_MAX; + float a1 = i/(float)GRAPH_MAX; + int i0 = (g->index+i-1)&(GRAPH_MAX-1); + int i1 = (g->index+i)&(GRAPH_MAX-1); + + float v0 = g->values[i0]; + float v1 = g->values[i1]; + + gfx_setcolor(0, 1, 0, 1); + gfx_lines_draw(x+a0*w, y+h-v0*h, x+a1*w, y+h-v1*h); + } + gfx_lines_end(); +} + +typedef struct +{ + int64 snap; + int64 current; + int64 target; + + int64 rlast; + int64 tlast; + GRAPH graph; +} SMOOTHTIME; + +static void st_init(SMOOTHTIME *st, int64 target) +{ + st->snap = time_get(); + st->current = target; + st->target = target; + graph_init(&st->graph, 0.0f, 1.0f); +} + +static int64 st_get(SMOOTHTIME *st, int64 now) +{ + float adjust_speed, a; + int64 c = st->current + (now - st->snap); + int64 t = st->target + (now - st->snap); + int64 r; + + /* it's faster to adjust upward instead of downward */ + /* we might need to adjust these abit */ + adjust_speed = 0.25f; /*0.99f;*/ + if(t > c) + adjust_speed = 500.0f; + + a = ((now-st->snap)/(float)time_freq()) * adjust_speed; + if(a > 1.0f) + a = 1.0f; + + r = c + (int64)((t-c)*a); + + graph_add(&st->graph, a+0.5f); + + return r; +} + +static void st_update(SMOOTHTIME *st, int64 target) +{ + int64 now = time_get(); + st->current = st_get(st, now); + st->snap = now; + st->target = target; +} + +SMOOTHTIME game_time; +SMOOTHTIME predicted_time; + +GRAPH intra_graph; +GRAPH predict_graph; + +/* --- input snapping --- */ +static int input_data[MAX_INPUT_SIZE] = {0}; +static int input_data_size; +static int input_is_changed = 1; +static GRAPH input_late_graph; +void snap_input(void *data, int size) +{ + if(input_data_size != size || memcmp(input_data, data, size)) + input_is_changed = 1; + mem_copy(input_data, data, size); + input_data_size = size; +} + +/* -- snapshot handling --- */ +enum +{ + NUM_SNAPSHOT_TYPES=2 +}; + +SNAPSTORAGE snapshot_storage; +static SNAPSTORAGE_HOLDER *snapshots[NUM_SNAPSHOT_TYPES]; +static int recived_snapshots; +static char snapshot_incomming_data[MAX_SNAPSHOT_SIZE]; + +/* --- */ + +const void *snap_get_item(int snapid, int index, SNAP_ITEM *item) +{ + SNAPSHOT_ITEM *i; + dbg_assert(snapid >= 0 && snapid < NUM_SNAPSHOT_TYPES, "invalid snapid"); + i = snapshot_get_item(snapshots[snapid]->snap, index); + item->type = snapitem_type(i); + item->id = snapitem_id(i); + return (void *)snapitem_data(i); +} + +const void *snap_find_item(int snapid, int type, int id) +{ + /* TODO: linear search. should be fixed. */ + int i; + for(i = 0; i < snapshots[snapid]->snap->num_items; i++) + { + SNAPSHOT_ITEM *itm = snapshot_get_item(snapshots[snapid]->snap, i); + if(snapitem_type(itm) == type && snapitem_id(itm) == id) + return (void *)snapitem_data(itm); + } + return 0x0; +} + +int snap_num_items(int snapid) +{ + dbg_assert(snapid >= 0 && snapid < NUM_SNAPSHOT_TYPES, "invalid snapid"); + return snapshots[snapid]->snap->num_items; +} + +/* ------ time functions ------ */ +float client_intratick() { return intratick; } +float client_intrapredtick() { return intrapredtick; } +int client_tick() { return current_tick; } +int client_predtick() { return current_predtick; } +int client_tickspeed() { return SERVER_TICK_SPEED; } +float client_frametime() { return frametime; } +float client_localtime() { return (time_get()-local_start_time)/(float)(time_freq()); } + +/* ----- send functions ----- */ +int client_send_msg() +{ + const MSG_INFO *info = msg_get_info(); + NETPACKET packet; + mem_zero(&packet, sizeof(NETPACKET)); + + packet.client_id = 0; + packet.data = info->data; + packet.data_size = info->size; + + if(info->flags&MSGFLAG_VITAL) + packet.flags = PACKETFLAG_VITAL; + + netclient_send(net, &packet); + return 0; +} + +static void client_send_info() +{ + msg_pack_start_system(NETMSG_INFO, MSGFLAG_VITAL); + msg_pack_string(modc_net_version(), 128); + msg_pack_string(config.player_name, 128); + msg_pack_string(config.clan_name, 128); + msg_pack_string(config.password, 128); + msg_pack_end(); + client_send_msg(); +} + + +static void client_send_entergame() +{ + msg_pack_start_system(NETMSG_ENTERGAME, MSGFLAG_VITAL); + msg_pack_end(); + client_send_msg(); +} + +static void client_send_ready() +{ + msg_pack_start_system(NETMSG_READY, MSGFLAG_VITAL); + msg_pack_end(); + client_send_msg(); +} + +void client_rcon(const char *cmd) +{ + msg_pack_start_system(NETMSG_CMD, MSGFLAG_VITAL); + msg_pack_string(config.rcon_password, 32); + msg_pack_string(cmd, 256); + msg_pack_end(); + client_send_msg(); +} + +int client_connection_problems() +{ + return netclient_gotproblems(net); +} + +static void client_send_input() +{ + int64 now = time_get(); + int i; + + if(current_predtick <= 0) + return; + + msg_pack_start_system(NETMSG_INPUT, 0); + msg_pack_int(ack_game_tick); + msg_pack_int(current_predtick); + msg_pack_int(input_data_size); + + inputs[current_input].tick = current_predtick; + inputs[current_input].game_time = st_get(&predicted_time, now); + inputs[current_input].time = now; + + for(i = 0; i < input_data_size/4; i++) + { + inputs[current_input].data[i] = input_data[i]; + msg_pack_int(input_data[i]); + } + + current_input++; + current_input%=200; + + msg_pack_end(); + client_send_msg(); +} + +/* TODO: OPT: do this alot smarter! */ +int *client_get_input(int tick) +{ + int i; + int best = -1; + for(i = 0; i < 200; i++) + { + if(inputs[i].tick <= tick && (best == -1 || inputs[best].tick < inputs[i].tick)) + best = i; + } + + if(best != -1) + return (int *)inputs[best].data; + return 0; +} + +/* ------ state handling ----- */ +static int state = CLIENTSTATE_OFFLINE; +int client_state() { return state; } +static void client_set_state(int s) +{ + int old = state; + if(config.debug) + dbg_msg("client", "state change. last=%d current=%d", state, s); + state = s; + if(old != s) + modc_statechange(state, old); +} + +/* called when the map is loaded and we should init for a new round */ +static void client_on_enter_game() +{ + /* reset input */ + int i; + for(i = 0; i < 200; i++) + inputs[i].tick = -1; + current_input = 0; + + /* reset snapshots */ + snapshots[SNAP_CURRENT] = 0; + snapshots[SNAP_PREV] = 0; + snapstorage_purge_all(&snapshot_storage); + recived_snapshots = 0; + current_predtick = 0; + current_recv_tick = 0; +} + +void client_entergame() +{ + /* now we will wait for two snapshots */ + /* to finish the connection */ + client_send_entergame(); + client_on_enter_game(); +} + +void client_connect(const char *server_address_str) +{ + char buf[512]; + const char *port_str = 0; + int k; + int port = 8303; + + dbg_msg("client", "connecting to '%s'", server_address_str); + + strncpy(buf, server_address_str, 512); + + for(k = 0; buf[k]; k++) + { + if(buf[k] == ':') + { + port_str = &(buf[k+1]); + buf[k] = 0; + break; + } + } + + if(port_str) + port = atoi(port_str); + + if(net_host_lookup(buf, port, &server_address) != 0) + dbg_msg("client", "could not find the address of %s, connecting to localhost", buf); + + netclient_connect(net, &server_address); + client_set_state(CLIENTSTATE_CONNECTING); + + graph_init(&intra_graph, 0.0f, 1.0f); + graph_init(&input_late_graph, 0.0f, 1.0f); + graph_init(&predict_graph, 0.0f, 200.0f); + +} + +void client_disconnect_with_reason(const char *reason) +{ + netclient_disconnect(net, reason); + client_set_state(CLIENTSTATE_OFFLINE); + map_unload(); +} + +void client_disconnect() +{ + netclient_disconnect(net, 0); + client_set_state(CLIENTSTATE_OFFLINE); + map_unload(); +} + +static int client_load_data() +{ + debug_font = gfx_load_texture("data/debug_font.png"); + return 1; +} + +extern int snapshot_data_rate[0xffff]; +extern int snapshot_data_updates[0xffff]; + +static void client_debug_render() +{ + static NETSTATS prev, current; + static int64 last_snap = 0; + static float frametime_avg = 0; + char buffer[512]; + + if(!config.debug) + return; + + gfx_blend_normal(); + gfx_texture_set(debug_font); + gfx_mapscreen(0,0,gfx_screenwidth(),gfx_screenheight()); + + if(time_get()-last_snap > time_freq()/10) + { + last_snap = time_get(); + prev = current; + netclient_stats(net, ¤t); + } + + frametime_avg = frametime_avg*0.9f + frametime*0.1f; + sprintf(buffer, "ticks: %8d %8d send: %6d recv: %6d snaploss: %d mem %dk gfxmem: %dk fps: %3d", + current_tick, current_predtick, + (current.send_bytes-prev.send_bytes)*10, + (current.recv_bytes-prev.recv_bytes)*10, + snaploss, + mem_allocated()/1024, + gfx_memory_usage()/1024, + (int)(1.0f/frametime_avg)); + gfx_quads_text(2, 2, 16, buffer); + + sprintf(buffer, "ui: %p %p", ui_hot_item(), ui_active_item()); + gfx_quads_text(2, 16, 16, buffer); + + + /* render rates */ + { + int i; + for(i = 0; i < 256; i++) + { + if(snapshot_data_rate[i]) + { + sprintf(buffer, "%4d : %8d %8d %8d", i, snapshot_data_rate[i]/8, snapshot_data_updates[i], + (snapshot_data_rate[i]/snapshot_data_updates[i])/8); + gfx_quads_text(2, 100+i*8, 16, buffer); + } + } + } + + /* render graphs */ + gfx_mapscreen(0,0,400.0f,300.0f); + graph_render(&predict_graph, 300, 10, 90, 50); + graph_render(&predicted_time.graph, 300, 10+50+10, 90, 50); + + graph_render(&intra_graph, 300, 10+50+10+50+10, 90, 50); + graph_render(&input_late_graph, 300, 10+50+10+50+10+50+10, 90, 50); + +} + +void client_quit() +{ + client_set_state(CLIENTSTATE_QUITING); +} + +const char *client_error_string() +{ + return netclient_error_string(net); +} + +static void client_render() +{ + gfx_clear(0.0f,0.0f,0.0f); + modc_render(); + client_debug_render(); +} + +static const char *client_load_map(const char *mapname, int wanted_crc) +{ + static char errormsg[128]; + DATAFILE *df; + char buf[512]; + int crc; + + dbg_msg("client", "loading map, map=%s wanted crc=%08x", mapname, wanted_crc); + client_set_state(CLIENTSTATE_LOADING); + + sprintf(buf, "data/maps/%s.map", mapname); + df = datafile_load(buf); + if(!df) + { + sprintf(errormsg, "map '%s' not found", mapname); + return errormsg; + } + + /* get the crc of the map */ + crc = datafile_crc(buf); + if(crc != wanted_crc) + { + datafile_unload(df); + sprintf(errormsg, "map differs from the server. %08x != %08x", crc, wanted_crc); + return errormsg; + } + + map_set(df); + return NULL; +} + +static void client_process_packet(NETPACKET *packet) +{ + if(packet->client_id == -1) + { + /* connectionlesss */ + if(packet->data_size >= (int)sizeof(SERVERBROWSE_LIST) && + memcmp(packet->data, SERVERBROWSE_LIST, sizeof(SERVERBROWSE_LIST)) == 0) + { + int size = packet->data_size-sizeof(SERVERBROWSE_LIST); + int num = size/sizeof(NETADDR4); + NETADDR4 *addrs = (NETADDR4 *)((char*)packet->data+sizeof(SERVERBROWSE_LIST)); + int i; + + for(i = 0; i < num; i++) + { + NETADDR4 addr = addrs[i]; + SERVER_INFO info = {0}; + +#if defined(CONF_ARCH_ENDIAN_BIG) + const char *tmp = (const char *)&addr.port; + addr.port = (tmp[1]<<8) | tmp[0]; +#endif + + info.latency = 999; + sprintf(info.address, "%d.%d.%d.%d:%d", + addr.ip[0], addr.ip[1], addr.ip[2], + addr.ip[3], addr.port); + sprintf(info.name, "\255%d.%d.%d.%d:%d", /* the \255 is to make sure that it's sorted last */ + addr.ip[0], addr.ip[1], addr.ip[2], + addr.ip[3], addr.port); + + client_serverbrowse_set(addrs+i, 1, &info); + } + } + + if(packet->data_size >= (int)sizeof(SERVERBROWSE_INFO) && + memcmp(packet->data, SERVERBROWSE_INFO, sizeof(SERVERBROWSE_INFO)) == 0) + { + /* we got ze info */ + UNPACKER up; + SERVER_INFO info = {0}; + int i; + + unpacker_reset(&up, (unsigned char*)packet->data+sizeof(SERVERBROWSE_INFO), packet->data_size-sizeof(SERVERBROWSE_INFO)); + + strncpy(info.version, unpacker_get_string(&up), 32); + strncpy(info.name, unpacker_get_string(&up), 64); + strncpy(info.map, unpacker_get_string(&up), 32); + info.game_type = atol(unpacker_get_string(&up)); + info.flags = atol(unpacker_get_string(&up)); + info.progression = atol(unpacker_get_string(&up)); + info.num_players = atol(unpacker_get_string(&up)); + info.max_players = atol(unpacker_get_string(&up)); + sprintf(info.address, "%d.%d.%d.%d:%d", + packet->address.ip[0], packet->address.ip[1], packet->address.ip[2], + packet->address.ip[3], packet->address.port); + + for(i = 0; i < info.num_players; i++) + { + strncpy(info.player_names[i], unpacker_get_string(&up), 48); + info.player_scores[i] = atol(unpacker_get_string(&up)); + } + + /* TODO: unpack players aswell */ + client_serverbrowse_set(&packet->address, 0, &info); + } + } + else + { + int sys; + int msg = msg_unpack_start(packet->data, packet->data_size, &sys); + + if(sys) + { + /* system message */ + if(msg == NETMSG_MAP) + { + const char *map = msg_unpack_string(); + int map_crc = msg_unpack_int(); + const char *error = client_load_map(map, map_crc); + + if(!error) + { + dbg_msg("client/network", "loading done"); + client_send_ready(); + modc_connected(); + } + else + { + client_disconnect_with_reason(error); + } + } + else if(msg == NETMSG_SNAP || msg == NETMSG_SNAPSINGLE || msg == NETMSG_SNAPEMPTY) + { + /*dbg_msg("client/network", "got snapshot"); */ + int num_parts = 1; + int part = 0; + int game_tick = msg_unpack_int(); + int delta_tick = game_tick-msg_unpack_int(); + int input_predtick = msg_unpack_int(); + int time_left = msg_unpack_int(); + int part_size = 0; + int crc = 0; + int complete_size = 0; + + if(msg == NETMSG_SNAP) + { + num_parts = msg_unpack_int(); + part = msg_unpack_int(); + } + + if(msg != NETMSG_SNAPEMPTY) + { + crc = msg_unpack_int(); + part_size = msg_unpack_int(); + } + + /* TODO: adjust our prediction time */ + if(time_left) + { + int k; + + graph_add(&input_late_graph, time_left/100.0f+0.5f); + + if(time_left < 0) + dbg_msg("client", "input was late with %d ms", time_left); + + for(k = 0; k < 200; k++) /* TODO: do this better */ + { + if(inputs[k].tick == input_predtick) + { + /*-1000/50 prediction_margin */ + int64 target = inputs[k].game_time + (time_get() - inputs[k].time); + st_update(&predicted_time, target - (int64)(((time_left-prediction_margin)/1000.0f)*time_freq())); + break; + } + } + } + + if(snapshot_part == part && game_tick > current_recv_tick) + { + /* TODO: clean this up abit */ + const char *d = (const char *)msg_unpack_raw(part_size); + mem_copy((char*)snapshot_incomming_data + part*MAX_SNAPSHOT_PACKSIZE, d, part_size); + snapshot_part++; + + if(snapshot_part == num_parts) + { + static SNAPSHOT emptysnap; + SNAPSHOT *deltashot = &emptysnap; + int purgetick; + void *deltadata; + int deltasize; + unsigned char tmpbuffer[MAX_SNAPSHOT_SIZE]; + unsigned char tmpbuffer2[MAX_SNAPSHOT_SIZE]; + unsigned char tmpbuffer3[MAX_SNAPSHOT_SIZE]; + int snapsize; + + complete_size = (num_parts-1) * MAX_SNAPSHOT_PACKSIZE + part_size; + + snapshot_part = 0; + + /* find snapshot that we should use as delta */ + emptysnap.data_size = 0; + emptysnap.num_items = 0; + + /* find delta */ + if(delta_tick >= 0) + { + int deltashot_size = snapstorage_get(&snapshot_storage, delta_tick, 0, &deltashot); + + if(deltashot_size < 0) + { + /* couldn't find the delta snapshots that the server used */ + /* to compress this snapshot. force the server to resync */ + if(config.debug) + dbg_msg("client", "error, couldn't find the delta snapshot"); + + /* ack snapshot */ + /* TODO: combine this with the input message */ + ack_game_tick = -1; + return; + } + } + + /* decompress snapshot */ + deltadata = snapshot_empty_delta(); + deltasize = sizeof(int)*3; + + if(complete_size) + { + int compsize = zerobit_decompress(snapshot_incomming_data, complete_size, tmpbuffer); + int intsize = intpack_decompress(tmpbuffer, compsize, tmpbuffer2); + deltadata = tmpbuffer2; + deltasize = intsize; + } + + /*dbg_msg("UNPACK", "%d unpacked with %d", game_tick, delta_tick); */ + + purgetick = delta_tick; + snapsize = snapshot_unpack_delta(deltashot, (SNAPSHOT*)tmpbuffer3, deltadata, deltasize); + if(msg != NETMSG_SNAPEMPTY && snapshot_crc((SNAPSHOT*)tmpbuffer3) != crc) + { + if(config.debug) + dbg_msg("client", "snapshot crc error %d", snapcrcerrors); + snapcrcerrors++; + if(snapcrcerrors > 10) + { + /* to many errors, send reset */ + ack_game_tick = -1; + client_send_input(); + snapcrcerrors = 0; + } + return; + } + else + { + if(snapcrcerrors) + snapcrcerrors--; + } + + /* purge old snapshots */ + purgetick = delta_tick; + if(snapshots[SNAP_PREV] && snapshots[SNAP_PREV]->tick < purgetick) + purgetick = snapshots[SNAP_PREV]->tick; + if(snapshots[SNAP_CURRENT] && snapshots[SNAP_CURRENT]->tick < purgetick) + purgetick = snapshots[SNAP_PREV]->tick; + snapstorage_purge_until(&snapshot_storage, purgetick); + /*client_snapshot_purge_until(game_tick-50); */ + + /* add new */ + snapstorage_add(&snapshot_storage, game_tick, time_get(), snapsize, (SNAPSHOT*)tmpbuffer3); + + /* apply snapshot, cycle pointers */ + recived_snapshots++; + + + if(current_recv_tick > 0) + snaploss += game_tick-current_recv_tick-1; + current_recv_tick = game_tick; + + /* we got two snapshots until we see us self as connected */ + if(recived_snapshots == 2) + { + /* start at 200ms and work from there */ + st_init(&predicted_time, game_tick*time_freq()/50); + st_init(&game_time, (game_tick-1)*time_freq()/50); + snapshots[SNAP_PREV] = snapshot_storage.first; + snapshots[SNAP_CURRENT] = snapshot_storage.last; + local_start_time = time_get(); + client_set_state(CLIENTSTATE_ONLINE); + } + + { + int64 now = time_get(); + graph_add(&predict_graph, (st_get(&predicted_time, now)-st_get(&game_time, now))/(float)time_freq()); + } + + st_update(&game_time, (game_tick-1)*time_freq()/50); + + /* ack snapshot */ + ack_game_tick = game_tick; + } + } + else + { + dbg_msg("client", "snapsht reset!"); + snapshot_part = 0; + } + } + } + else + { + /* game message */ + modc_message(msg); + } + } +} + +static void client_pump_network() +{ + NETPACKET packet; + + netclient_update(net); + + /* check for errors */ + if(client_state() != CLIENTSTATE_OFFLINE && netclient_state(net) == NETSTATE_OFFLINE) + { + client_set_state(CLIENTSTATE_OFFLINE); + dbg_msg("client", "offline error='%s'", netclient_error_string(net)); + } + + /* */ + if(client_state() == CLIENTSTATE_CONNECTING && netclient_state(net) == NETSTATE_ONLINE) + { + /* we switched to online */ + dbg_msg("client", "connected, sending info"); + client_set_state(CLIENTSTATE_LOADING); + client_send_info(); + } + + /* process packets */ + while(netclient_recv(net, &packet)) + client_process_packet(&packet); +} + +static void client_update() +{ + /* switch snapshot */ + if(client_state() != CLIENTSTATE_OFFLINE && recived_snapshots >= 3) + { + int repredict = 0; + int64 now = st_get(&game_time, time_get()); + int64 pred_now = st_get(&predicted_time, time_get()); + + while(1) + { + SNAPSTORAGE_HOLDER *cur = snapshots[SNAP_CURRENT]; + int64 tickstart = (cur->tick)*time_freq()/50; + + if(tickstart < now) + { + SNAPSTORAGE_HOLDER *next = snapshots[SNAP_CURRENT]->next; + if(next) + { + snapshots[SNAP_PREV] = snapshots[SNAP_CURRENT]; + snapshots[SNAP_CURRENT] = next; + + /* set tick */ + current_tick = snapshots[SNAP_CURRENT]->tick; + + if(snapshots[SNAP_CURRENT] && snapshots[SNAP_PREV]) + { + modc_newsnapshot(); + repredict = 1; + } + } + else + break; + } + else + break; + } + + if(snapshots[SNAP_CURRENT] && snapshots[SNAP_PREV]) + { + int64 curtick_start = (snapshots[SNAP_CURRENT]->tick)*time_freq()/50; + int64 prevtick_start = (snapshots[SNAP_PREV]->tick)*time_freq()/50; + /*tg_add(&predicted_time_graph, pred_now, 0); */ + int prev_pred_tick = (int)(pred_now*50/time_freq()); + int new_pred_tick = prev_pred_tick+1; + static float last_intrapred = 0; + + intratick = (now - prevtick_start) / (float)(curtick_start-prevtick_start); + + graph_add(&intra_graph, intratick*0.25f); + + curtick_start = new_pred_tick*time_freq()/50; + prevtick_start = prev_pred_tick*time_freq()/50; + intrapredtick = (pred_now - prevtick_start) / (float)(curtick_start-prevtick_start); + + + if(new_pred_tick > current_predtick) + { + last_intrapred = intrapredtick; + current_predtick = new_pred_tick; + repredict = 1; + + /* send input */ + client_send_input(); + } + + if(intrapredtick < last_intrapred) + dbg_msg("client", "prediction time goes backwards, that can't be good"); + last_intrapred = intrapredtick; + } + + /* only do sane predictions */ + if(repredict) + { + if(current_predtick > current_tick && current_predtick < current_tick+50) + modc_predict(); + } + } + + /* STRESS TEST: join the server again */ + if(client_state() == CLIENTSTATE_OFFLINE && config.dbg_stress && (frames%100) == 0) + client_connect(config.dbg_stress_server); + + /* pump the network */ + client_pump_network(); + + /* update the server browser */ + client_serverbrowse_update(); +} + +extern int editor_update_and_render(); +extern void editor_init(); + +static void client_run() +{ + NETADDR4 bindaddr; + int64 reporttime = time_get(); + int64 reportinterval = time_freq()*1; + int editor_active = 0; + + local_start_time = time_get(); + snapshot_part = 0; + + /* init graphics and sound */ + if(!gfx_init()) + return; + + /* init the editor */ + editor_init(); + + /* sound is allowed to fail */ + snd_init(); + + /* load data */ + if(!client_load_data()) + return; + + /* init the mod */ + modc_init(); + dbg_msg("client", "version %s", modc_net_version()); + + /* open socket */ + mem_zero(&bindaddr, sizeof(bindaddr)); + net = netclient_open(bindaddr, 0); + + /* connect to the server if wanted */ + /* + if(config.cl_connect[0] != 0) + client_connect(config.cl_connect); + config.cl_connect[0] = 0; + */ + + inp_mouse_mode_relative(); + + while (1) + { + int64 frame_start_time = time_get(); + frames++; + + /* update input */ + inp_update(); + + /* refocus */ + if(!gfx_window_active()) + { + if(window_must_refocus == 0) + inp_mouse_mode_absolute(); + window_must_refocus = 1; + } + + if(window_must_refocus && gfx_window_active()) + { + if(window_must_refocus < 3) + { + inp_mouse_mode_absolute(); + window_must_refocus++; + } + + if(inp_key_pressed(KEY_MOUSE_1)) + { + inp_mouse_mode_relative(); + window_must_refocus = 0; + } + } + + /* screenshot button */ + if(inp_key_down(config.key_screenshot)) + gfx_screenshot(); + + /* some debug keys */ + /* + if(config.debug) + { + if(inp_key_pressed(KEY_F1)) + inp_mouse_mode_absolute(); + if(inp_key_pressed(KEY_F2)) + inp_mouse_mode_relative(); + + if(inp_key_pressed(KEY_F5)) + { + ack_game_tick = -1; + client_send_input(); + } + }*/ + + /* panic quit button */ + if(inp_key_pressed(KEY_LCTRL) && inp_key_pressed(KEY_LSHIFT) && inp_key_pressed('Q')) + break; + + if(inp_key_pressed(KEY_LCTRL) && inp_key_pressed(KEY_LSHIFT) && inp_key_down('E')) + editor_active = editor_active^1; + + if(!gfx_window_open()) + break; + + /* render */ + if(editor_active) + { + client_update(); + editor_update_and_render(); + gfx_swap(); + } + else + { + client_update(); + + if(config.dbg_stress) + { + if((frames%10) == 0) + { + client_render(); + gfx_swap(); + } + } + else + { + client_render(); + gfx_swap(); + } + } + + /* check conditions */ + if(client_state() == CLIENTSTATE_QUITING) + break; + + /* be nice */ + if(config.cl_cpu_throttle || !gfx_window_active()) + thread_sleep(1); + + if(reporttime < time_get()) + { + if(config.debug) + { + dbg_msg("client/report", "fps=%.02f (%.02f %.02f) netstate=%d", + frames/(float)(reportinterval/time_freq()), + 1.0f/frametime_high, + 1.0f/frametime_low, + netclient_state(net)); + } + frametime_low = 1; + frametime_high = 0; + frames = 0; + reporttime += reportinterval; + } + + /* update frametime */ + frametime = (time_get()-frame_start_time)/(float)time_freq(); + if(frametime < frametime_low) + frametime_low = frametime; + if(frametime > frametime_high) + frametime_high = frametime; + } + + modc_shutdown(); + client_disconnect(); + + gfx_shutdown(); + snd_shutdown(); +} + + +int editor_main(int argc, char **argv); + +int main(int argc, char **argv) +{ + /* init the engine */ + dbg_msg("client", "starting..."); + engine_init("Teewars", argc, argv); + + client_run(); + + engine_writeconfig(); + return 0; +} diff --git a/src/engine/client/ec_gfx.c b/src/engine/client/ec_gfx.c new file mode 100644 index 00000000..cd1f690b --- /dev/null +++ b/src/engine/client/ec_gfx.c @@ -0,0 +1,1018 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +/* compressed textures */ +#define GL_COMPRESSED_RGB_ARB 0x84ED +#define GL_COMPRESSED_RGBA_ARB 0x84EE + +enum +{ + DRAWING_QUADS=1, + DRAWING_LINES=2 +}; + +/* */ +typedef struct { float x, y, z; } VEC3; +typedef struct { float u, v; } TEXCOORD; +typedef struct { float r, g, b, a; } COLOR; + +typedef struct +{ + VEC3 pos; + TEXCOORD tex; + COLOR color; +} VERTEX; + +const int vertex_buffer_size = 32*1024; +static VERTEX *vertices = 0; +static int num_vertices = 0; + +static COLOR color[4]; +static TEXCOORD texture[4]; + +static int do_screenshot = 0; + +static int screen_width = -1; +static int screen_height = -1; +static float rotation = 0; +static int drawing = 0; + +static float screen_x0 = 0; +static float screen_y0 = 0; +static float screen_x1 = 0; +static float screen_y1 = 0; + +typedef struct +{ + GLuint tex; + int memsize; + int flags; + int next; +} TEXTURE; + + +enum +{ + MAX_TEXTURES = 128 +}; + +static TEXTURE textures[MAX_TEXTURES]; +static int first_free_texture; +static int memory_usage = 0; + +static const unsigned char null_texture_data[] = { + 0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, 0x00,0xff,0x00,0xff, 0x00,0xff,0x00,0xff, + 0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, 0x00,0xff,0x00,0xff, 0x00,0xff,0x00,0xff, + 0x00,0x00,0xff,0xff, 0x00,0x00,0xff,0xff, 0xff,0xff,0x00,0xff, 0xff,0xff,0x00,0xff, + 0x00,0x00,0xff,0xff, 0x00,0x00,0xff,0xff, 0xff,0xff,0x00,0xff, 0xff,0xff,0x00,0xff, +}; + + +typedef struct +{ + float m_CharStartTable[256]; + float m_CharEndTable[256]; + int font_texture; +} pretty_font; + +pretty_font default_font = +{ +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0.421875, 0.359375, 0.265625, 0.25, 0.1875, 0.25, 0.4375, 0.390625, 0.390625, 0.34375, 0.28125, 0.421875, 0.390625, 0.4375, 0.203125, + 0.265625, 0.28125, 0.28125, 0.265625, 0.25, 0.28125, 0.28125, 0.265625, 0.28125, 0.265625, 0.4375, 0.421875, 0.3125, 0.28125, 0.3125, 0.3125, + 0.25, 0.234375, 0.28125, 0.265625, 0.265625, 0.296875, 0.3125, 0.25, 0.25, 0.421875, 0.28125, 0.265625, 0.328125, 0.171875, 0.234375, 0.25, + 0.28125, 0.234375, 0.265625, 0.265625, 0.28125, 0.265625, 0.234375, 0.09375, 0.234375, 0.234375, 0.265625, 0.390625, 0.203125, 0.390625, 0.296875, 0.28125, + 0.375, 0.3125, 0.3125, 0.3125, 0.296875, 0.3125, 0.359375, 0.296875, 0.3125, 0.4375, 0.390625, 0.328125, 0.4375, 0.203125, 0.3125, 0.296875, + 0.3125, 0.296875, 0.359375, 0.3125, 0.328125, 0.3125, 0.296875, 0.203125, 0.296875, 0.296875, 0.328125, 0.375, 0.421875, 0.375, 0.28125, 0.3125, + 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, + 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, + 0, 0.421875, 0.3125, 0.265625, 0.25, 0.25, 0.421875, 0.265625, 0.375, 0.21875, 0.375, 0.328125, 0.3125, 0, 0.21875, 0.28125, + 0.359375, 0.28125, 0.34375, 0.34375, 0.421875, 0.3125, 0.265625, 0.421875, 0.421875, 0.34375, 0.375, 0.328125, 0.125, 0.125, 0.125, 0.296875, + 0.234375, 0.234375, 0.234375, 0.234375, 0.234375, 0.234375, 0.109375, 0.265625, 0.296875, 0.296875, 0.296875, 0.296875, 0.375, 0.421875, 0.359375, 0.390625, + 0.21875, 0.234375, 0.25, 0.25, 0.25, 0.25, 0.25, 0.296875, 0.21875, 0.265625, 0.265625, 0.265625, 0.265625, 0.234375, 0.28125, 0.3125, + 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.1875, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.375, 0.421875, 0.359375, 0.390625, + 0.3125, 0.3125, 0.296875, 0.296875, 0.296875, 0.296875, 0.296875, 0.28125, 0.28125, 0.3125, 0.3125, 0.3125, 0.3125, 0.296875, 0.3125, 0.296875, +}, +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0.2, 0.5625, 0.625, 0.71875, 0.734375, 0.796875, 0.765625, 0.546875, 0.59375, 0.59375, 0.65625, 0.703125, 0.546875, 0.59375, 0.5625, 0.6875, + 0.71875, 0.609375, 0.703125, 0.703125, 0.71875, 0.703125, 0.703125, 0.6875, 0.703125, 0.703125, 0.5625, 0.546875, 0.671875, 0.703125, 0.671875, 0.671875, + 0.734375, 0.75, 0.734375, 0.734375, 0.734375, 0.6875, 0.6875, 0.734375, 0.71875, 0.5625, 0.65625, 0.765625, 0.703125, 0.8125, 0.75, 0.734375, + 0.734375, 0.765625, 0.71875, 0.71875, 0.703125, 0.71875, 0.75, 0.890625, 0.75, 0.75, 0.71875, 0.59375, 0.6875, 0.59375, 0.6875, 0.703125, + 0.5625, 0.671875, 0.6875, 0.671875, 0.671875, 0.671875, 0.625, 0.671875, 0.671875, 0.5625, 0.546875, 0.703125, 0.5625, 0.78125, 0.671875, 0.671875, + 0.6875, 0.671875, 0.65625, 0.671875, 0.65625, 0.671875, 0.6875, 0.78125, 0.6875, 0.671875, 0.65625, 0.609375, 0.546875, 0.609375, 0.703125, 0.671875, + 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, + 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, + 0, 0.5625, 0.671875, 0.734375, 0.734375, 0.734375, 0.546875, 0.71875, 0.609375, 0.765625, 0.609375, 0.65625, 0.671875, 0, 0.765625, 0.703125, + 0.625, 0.703125, 0.640625, 0.640625, 0.609375, 0.671875, 0.703125, 0.546875, 0.5625, 0.578125, 0.609375, 0.65625, 0.859375, 0.859375, 0.859375, 0.671875, + 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, 0.84375, 0.734375, 0.6875, 0.6875, 0.6875, 0.6875, 0.5625, 0.609375, 0.640625, 0.59375, + 0.734375, 0.75, 0.734375, 0.734375, 0.734375, 0.734375, 0.734375, 0.6875, 0.765625, 0.71875, 0.71875, 0.71875, 0.71875, 0.75, 0.734375, 0.6875, + 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.796875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.5625, 0.609375, 0.625, 0.59375, + 0.6875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.703125, 0.703125, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.6875, 0.671875, +}, +0 +}; + +double extra_kerning[256*256] = {0}; + +pretty_font *current_font = &default_font; + +static void flush() +{ + if(num_vertices == 0) + return; + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glVertexPointer(3, GL_FLOAT, + sizeof(VERTEX), + (char*)vertices); + glTexCoordPointer(2, GL_FLOAT, + sizeof(VERTEX), + (char*)vertices + sizeof(float)*3); + glColorPointer(4, GL_FLOAT, + sizeof(VERTEX), + (char*)vertices + sizeof(float)*5); + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + + if(drawing == DRAWING_QUADS) + glDrawArrays(GL_QUADS, 0, num_vertices); + else if(drawing == DRAWING_LINES) + glDrawArrays(GL_LINES, 0, num_vertices); + + /* Reset pointer */ + num_vertices = 0; +} + +static void draw_line() +{ + num_vertices += 2; + if((num_vertices + 2) >= vertex_buffer_size) + flush(); +} + +static void draw_quad() +{ + num_vertices += 4; + if((num_vertices + 4) >= vertex_buffer_size) + flush(); +} + + +int gfx_init() +{ + int i; + + screen_width = config.gfx_screen_width; + screen_height = config.gfx_screen_height; + + glfwInit(); + + if(config.dbg_stress) + { + screen_width = 320; + screen_height = 240; + } + + /* set antialiasing */ + if(config.gfx_fsaa_samples) + glfwOpenWindowHint(GLFW_FSAA_SAMPLES, config.gfx_fsaa_samples); + + /* set refresh rate */ + if(config.gfx_refresh_rate) + glfwOpenWindowHint(GLFW_REFRESH_RATE, config.gfx_refresh_rate); + + /* no resizing allowed */ + glfwOpenWindowHint(GLFW_WINDOW_NO_RESIZE, 1); + + /* open window */ + if(config.gfx_fullscreen) + { + int result = glfwOpenWindow(screen_width, screen_height, 8, 8, 8, 0, 24, 0, GLFW_FULLSCREEN); + if(result != GL_TRUE) + { + dbg_msg("game", "failed to create gl context"); + return 0; + } + } + else + { + int result = glfwOpenWindow(screen_width, screen_height, 0, 0, 0, 0, 24, 0, GLFW_WINDOW); + if(result != GL_TRUE) + { + dbg_msg("game", "failed to create gl context"); + return 0; + } + } + + glGetIntegerv(GL_DEPTH_BITS, &i); + dbg_msg("gfx", "depthbits = %d", i); + + glfwSetWindowTitle("Teewars"); + + /* We don't want to see the window when we run the stress testing */ + if(config.dbg_stress) + glfwIconifyWindow(); + + /* Init vertices */ + if (vertices) + mem_free(vertices); + vertices = (VERTEX*)mem_alloc(sizeof(VERTEX) * vertex_buffer_size, 1); + num_vertices = 0; + + + /* + dbg_msg("gfx", "OpenGL version %d.%d.%d", context.version_major(), + context.version_minor(), + context.version_rev());*/ + + gfx_mapscreen(0,0,config.gfx_screen_width, config.gfx_screen_height); + + /* set some default settings */ + glEnable(GL_BLEND); + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + gfx_mask_op(MASK_NONE, 0); + + /* Set all z to -5.0f */ + for (i = 0; i < vertex_buffer_size; i++) + vertices[i].pos.z = -5.0f; + + /* init textures */ + first_free_texture = 0; + for(i = 0; i < MAX_TEXTURES; i++) + textures[i].next = i+1; + textures[MAX_TEXTURES-1].next = -1; + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + /* init input */ + inp_init(); + + /* create null texture, will get id=0 */ + gfx_load_texture_raw(4,4,IMG_RGBA,null_texture_data); + + /* set vsync as needed */ + gfx_set_vsync(config.gfx_vsync); + + /* UGLY as hell, please remove */ + current_font->font_texture = gfx_load_texture("data/big_font.png"); + + return 1; +} + +float gfx_screenaspect() +{ + return gfx_screenwidth()/(float)gfx_screenheight(); +} + +void gfx_clear_mask(int fill) +{ + /*if(fill) + glClearDepth(0.0f); + else*/ + + int i; + glGetIntegerv(GL_DEPTH_WRITEMASK, &i); + glDepthMask(GL_TRUE); + glClearDepth(0.0f); + glClear(GL_DEPTH_BUFFER_BIT); + glDepthMask(i); +} + +void gfx_mask_op(int mask, int write) +{ + glEnable(GL_DEPTH_TEST); + + if(write) + glDepthMask(GL_TRUE); + else + glDepthMask(GL_FALSE); + + if(mask == MASK_NONE) + glDepthFunc(GL_ALWAYS); + else if(mask == MASK_SET) + glDepthFunc(GL_LEQUAL); + else if(mask == MASK_ZERO) + glDepthFunc(GL_NOTEQUAL); +} + +int gfx_window_active() +{ + return glfwGetWindowParam(GLFW_ACTIVE) == GL_TRUE ? 1 : 0; +} + +int gfx_window_open() +{ + return glfwGetWindowParam(GLFW_OPENED) == GL_TRUE ? 1 : 0; +} + +VIDEO_MODE fakemodes[] = { + {320,240,8,8,8}, {400,300,8,8,8}, {640,480,8,8,8}, + {720,400,8,8,8}, {768,576,8,8,8}, {800,600,8,8,8}, + {1024,600,8,8,8}, {1024,768,8,8,8}, {1152,864,8,8,8}, + {1280,768,8,8,8}, {1280,800,8,8,8}, {1280,960,8,8,8}, + {1280,1024,8,8,8}, {1368,768,8,8,8}, {1400,1050,8,8,8}, + {1440,900,8,8,8}, {1440,1050,8,8,8}, {1600,1000,8,8,8}, + {1600,1200,8,8,8}, {1680,1050,8,8,8}, {1792,1344,8,8,8}, + {1800,1440,8,8,8}, {1856,1392,8,8,8}, {1920,1080,8,8,8}, + {1920,1200,8,8,8}, {1920,1440,8,8,8}, {1920,2400,8,8,8}, + {2048,1536,8,8,8}, + + {320,240,5,6,5}, {400,300,5,6,5}, {640,480,5,6,5}, + {720,400,5,6,5}, {768,576,5,6,5}, {800,600,5,6,5}, + {1024,600,5,6,5}, {1024,768,5,6,5}, {1152,864,5,6,5}, + {1280,768,5,6,5}, {1280,800,5,6,5}, {1280,960,5,6,5}, + {1280,1024,5,6,5}, {1368,768,5,6,5}, {1400,1050,5,6,5}, + {1440,900,5,6,5}, {1440,1050,5,6,5}, {1600,1000,5,6,5}, + {1600,1200,5,6,5}, {1680,1050,5,6,5}, {1792,1344,5,6,5}, + {1800,1440,5,6,5}, {1856,1392,5,6,5}, {1920,1080,5,6,5}, + {1920,1200,5,6,5}, {1920,1440,5,6,5}, {1920,2400,5,6,5}, + {2048,1536,5,6,5} +}; + +int gfx_get_video_modes(VIDEO_MODE *list, int maxcount) +{ + if(config.gfx_display_all_modes) + { + int count = sizeof(fakemodes)/sizeof(VIDEO_MODE); + mem_copy(list, fakemodes, sizeof(fakemodes)); + if(maxcount < count) + count = maxcount; + return count; + } + + return glfwGetVideoModes((GLFWvidmode *)list, maxcount); +} + +void gfx_set_vsync(int val) +{ + glfwSwapInterval(val); +} + +int gfx_unload_texture(int index) +{ + glDeleteTextures(1, &textures[index].tex); + textures[index].next = first_free_texture; + memory_usage -= textures[index].memsize; + first_free_texture = index; + return 0; +} + +void gfx_blend_normal() +{ + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +} + +void gfx_blend_additive() +{ + glBlendFunc(GL_SRC_ALPHA, GL_ONE); +} + +int gfx_memory_usage() { return memory_usage; } + +static unsigned char sample(int w, int h, const unsigned char *data, int u, int v, int offset) +{ + return (data[(v*w+u)*4+offset]+ + data[(v*w+u+1)*4+offset]+ + data[((v+1)*w+u)*4+offset]+ + data[((v+1)*w+u+1)*4+offset])/4; +} + +int gfx_load_texture_raw(int w, int h, int format, const void *data) +{ + int mipmap = 1; + unsigned char *texdata = (unsigned char *)data; + unsigned char *tmpdata = 0; + int oglformat = 0; + int tex = 0; + + /* don't waste memory on texture if we are stress testing */ + if(config.dbg_stress) + return -1; + + /* grab texture */ + tex = first_free_texture; + first_free_texture = textures[tex].next; + textures[tex].next = -1; + + /* resample if needed */ + if(config.gfx_texture_quality==0) + { + if(w > 16 && h > 16 && format == IMG_RGBA) + { + unsigned char *tmpdata; + int c = 0; + int x, y; + + tmpdata = (unsigned char *)mem_alloc(w*h*4, 1); + + w/=2; + h/=2; + + for(y = 0; y < h; y++) + for(x = 0; x < w; x++, c++) + { + tmpdata[c*4] = sample(w*2, h*2, texdata, x*2,y*2, 0); + tmpdata[c*4+1] = sample(w*2, h*2, texdata, x*2,y*2, 1); + tmpdata[c*4+2] = sample(w*2, h*2, texdata, x*2,y*2, 2); + tmpdata[c*4+3] = sample(w*2, h*2, texdata, x*2,y*2, 3); + } + texdata = tmpdata; + } + } + + if(config.debug) + dbg_msg("gfx", "%d = %dx%d", tex, w, h); + + /* upload texture */ + if(config.gfx_texture_compression) + { + oglformat = GL_COMPRESSED_RGBA_ARB; + if(format == IMG_RGB) + oglformat = GL_COMPRESSED_RGB_ARB; + } + else + { + oglformat = GL_RGBA; + if(format == IMG_RGB) + oglformat = GL_RGB; + } + + glGenTextures(1, &textures[tex].tex); + glBindTexture(GL_TEXTURE_2D, textures[tex].tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + gluBuild2DMipmaps(GL_TEXTURE_2D, oglformat, w, h, oglformat, GL_UNSIGNED_BYTE, texdata); + + /* calculate memory usage */ + textures[tex].memsize = w*h*4; + if(mipmap) + { + while(w > 2 && h > 2) + { + w>>=1; + h>>=1; + textures[tex].memsize += w*h*4; + } + } + + memory_usage += textures[tex].memsize; + mem_free(tmpdata); + return tex; +} +/* +int gfx_load_mip_texture_raw(int w, int h, int format, const void *data) +{ + // grab texture + int tex = first_free_texture; + first_free_texture = textures[tex].next; + textures[tex].next = -1; + + // set data and return + // TODO: should be RGBA, not BGRA + dbg_msg("gfx", "%d = %dx%d", tex, w, h); + dbg_assert(format == IMG_RGBA, "not an RGBA image"); + + unsigned mip_w = w; + unsigned mip_h = h; + unsigned level = 0; + const unsigned char *ptr = (const unsigned char*)data; + while(mip_w > 0 && mip_h > 0) + { + dbg_msg("gfx mip", "%d = %dx%d", level, mip_w, mip_h); + textures[tex].tex.data2d_mip(mip_w, mip_h, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, level, ptr); + level++; + ptr = ptr + mip_w*mip_h*4; + mip_w = mip_w>>1; + mip_h = mip_h>>1; + } + return tex; +} +*/ + +/* simple uncompressed RGBA loaders */ +int gfx_load_texture(const char *filename) +{ + int l = strlen(filename); + IMAGE_INFO img; + if(l < 3) + return 0; + if(gfx_load_png(&img, filename)) + { + int id = gfx_load_texture_raw(img.width, img.height, img.format, img.data); + mem_free(img.data); + return id; + } + + return 0; +} + +int gfx_load_png(IMAGE_INFO *img, const char *filename) +{ + unsigned char *buffer; + png_t png; + + /* open file for reading */ + png_init(0,0); + + if(png_open_file(&png, filename) != PNG_NO_ERROR) + { + dbg_msg("game/png", "failed to open file. filename='%s'", filename); + return 0; + } + + if(png.depth != 8 || (png.color_type != PNG_TRUECOLOR && png.color_type != PNG_TRUECOLOR_ALPHA)) + { + dbg_msg("game/png", "invalid format. filename='%s'", filename); + png_close_file(&png); + } + + buffer = (unsigned char *)mem_alloc(png.width * png.height * png.bpp, 1); + png_get_data(&png, buffer); + png_close_file(&png); + + img->width = png.width; + img->height = png.height; + if(png.color_type == PNG_TRUECOLOR) + img->format = IMG_RGB; + else if(png.color_type == PNG_TRUECOLOR_ALPHA) + img->format = IMG_RGBA; + img->data = buffer; + return 1; +} + +void gfx_shutdown() +{ + if (vertices) + mem_free(vertices); + glfwCloseWindow(); + glfwTerminate(); +} + +void gfx_screenshot() +{ + do_screenshot = 1; +} + +void gfx_swap() +{ + if(do_screenshot) + { + /* fetch image data */ + int y; + int w = screen_width; + int h = screen_height; + unsigned char *pixel_data = (unsigned char *)mem_alloc(w*(h+1)*3, 1); + unsigned char *temp_row = pixel_data+w*h*3; + glReadPixels(0,0, w, h, GL_RGB, GL_UNSIGNED_BYTE, pixel_data); + + /* flip the pixel because opengl works from bottom left corner */ + for(y = 0; y < h/2; y++) + { + mem_copy(temp_row, pixel_data+y*w*3, w*3); + mem_copy(pixel_data+y*w*3, pixel_data+(h-y-1)*w*3, w*3); + mem_copy(pixel_data+(h-y-1)*w*3, temp_row,w*3); + } + + /* find filename */ + { + char wholepath[1024]; + char filename[128]; + static int index = 1; + png_t png; + + for(; index < 1000; index++) + { + IOHANDLE io; + sprintf(filename, "screenshots/screenshot%04d.png", index); + engine_savepath(filename, wholepath, sizeof(wholepath)); + + io = io_open(wholepath, IOFLAG_READ); + if(io) + io_close(io); + else + break; + } + + /* save png */ + dbg_msg("client", "saved screenshot to '%s'", wholepath); + png_open_file_write(&png, wholepath); + png_set_data(&png, w, h, 8, PNG_TRUECOLOR, (unsigned char *)pixel_data); + png_close_file(&png); + } + + /* clean up */ + mem_free(pixel_data); + do_screenshot = 0; + } + + glfwSwapBuffers(); + glFinish(); + glfwPollEvents(); +} + +int gfx_screenwidth() +{ + return screen_width; +} + +int gfx_screenheight() +{ + return screen_height; +} + +void gfx_texture_set(int slot) +{ + dbg_assert(drawing == 0, "called gfx_texture_set within begin"); + if(slot == -1) + glDisable(GL_TEXTURE_2D); + else + { + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, textures[slot].tex); + } +} + +void gfx_clear(float r, float g, float b) +{ + glClearColor(r,g,b,1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} + +void gfx_mapscreen(float tl_x, float tl_y, float br_x, float br_y) +{ + screen_x0 = tl_x; + screen_y0 = tl_y; + screen_x1 = br_x; + screen_y1 = br_y; + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(tl_x, br_x, br_y, tl_y, 1.0f, 10.f); +} + +void gfx_getscreen(float *tl_x, float *tl_y, float *br_x, float *br_y) +{ + *tl_x = screen_x0; + *tl_y = screen_y0; + *br_x = screen_x1; + *br_y = screen_y1; +} + +void gfx_setoffset(float x, float y) +{ + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(x, y, 0); +} + + +void gfx_quads_begin() +{ + dbg_assert(drawing == 0, "called quads_begin twice"); + drawing = DRAWING_QUADS; + + gfx_quads_setsubset(0,0,1,1); + gfx_quads_setrotation(0); + gfx_setcolor(1,1,1,1); +} + +void gfx_quads_end() +{ + dbg_assert(drawing == DRAWING_QUADS, "called quads_end without begin"); + flush(); + drawing = 0; +} + + +void gfx_quads_setrotation(float angle) +{ + dbg_assert(drawing == DRAWING_QUADS, "called gfx_quads_setrotation without begin"); + rotation = angle; +} + +void gfx_setcolorvertex(int i, float r, float g, float b, float a) +{ + dbg_assert(drawing != 0, "called gfx_quads_setcolorvertex without begin"); + color[i].r = r; + color[i].g = g; + color[i].b = b; + color[i].a = a; +} + +void gfx_setcolor(float r, float g, float b, float a) +{ + dbg_assert(drawing != 0, "called gfx_quads_setcolor without begin"); + gfx_setcolorvertex(0, r, g, b, a); + gfx_setcolorvertex(1, r, g, b, a); + gfx_setcolorvertex(2, r, g, b, a); + gfx_setcolorvertex(3, r, g, b, a); +} + +void gfx_quads_setsubset(float tl_u, float tl_v, float br_u, float br_v) +{ + dbg_assert(drawing == DRAWING_QUADS, "called gfx_quads_setsubset without begin"); + + texture[0].u = tl_u; + texture[0].v = tl_v; + + texture[1].u = br_u; + texture[1].v = tl_v; + + texture[2].u = br_u; + texture[2].v = br_v; + + texture[3].u = tl_u; + texture[3].v = br_v; +} + +static void rotate(VEC3 *center, VEC3 *point) +{ + float x = point->x - center->x; + float y = point->y - center->y; + point->x = x * cosf(rotation) - y * sinf(rotation) + center->x; + point->y = x * sinf(rotation) + y * cosf(rotation) + center->y; +} + +void gfx_quads_draw(float x, float y, float w, float h) +{ + gfx_quads_drawTL(x-w/2, y-h/2,w,h); +} + +void gfx_quads_drawTL(float x, float y, float width, float height) +{ + VEC3 center; + + dbg_assert(drawing == DRAWING_QUADS, "called quads_draw without begin"); + + center.x = x + width/2; + center.y = y + height/2; + center.z = 0; + + vertices[num_vertices].pos.x = x; + vertices[num_vertices].pos.y = y; + vertices[num_vertices].tex = texture[0]; + vertices[num_vertices].color = color[0]; + rotate(¢er, &vertices[num_vertices].pos); + + vertices[num_vertices + 1].pos.x = x+width; + vertices[num_vertices + 1].pos.y = y; + vertices[num_vertices + 1].tex = texture[1]; + vertices[num_vertices + 1].color = color[1]; + rotate(¢er, &vertices[num_vertices + 1].pos); + + vertices[num_vertices + 2].pos.x = x + width; + vertices[num_vertices + 2].pos.y = y+height; + vertices[num_vertices + 2].tex = texture[2]; + vertices[num_vertices + 2].color = color[2]; + rotate(¢er, &vertices[num_vertices + 2].pos); + + vertices[num_vertices + 3].pos.x = x; + vertices[num_vertices + 3].pos.y = y+height; + vertices[num_vertices + 3].tex = texture[3]; + vertices[num_vertices + 3].color = color[3]; + rotate(¢er, &vertices[num_vertices + 3].pos); + + draw_quad(); +} + +void gfx_quads_draw_freeform( + float x0, float y0, + float x1, float y1, + float x2, float y2, + float x3, float y3) +{ + dbg_assert(drawing == DRAWING_QUADS, "called quads_draw_freeform without begin"); + + vertices[num_vertices].pos.x = x0; + vertices[num_vertices].pos.y = y0; + vertices[num_vertices].tex = texture[0]; + vertices[num_vertices].color = color[0]; + + vertices[num_vertices + 1].pos.x = x1; + vertices[num_vertices + 1].pos.y = y1; + vertices[num_vertices + 1].tex = texture[1]; + vertices[num_vertices + 1].color = color[1]; + + vertices[num_vertices + 2].pos.x = x3; + vertices[num_vertices + 2].pos.y = y3; + vertices[num_vertices + 2].tex = texture[3]; + vertices[num_vertices + 2].color = color[3]; + + vertices[num_vertices + 3].pos.x = x2; + vertices[num_vertices + 3].pos.y = y2; + vertices[num_vertices + 3].tex = texture[2]; + vertices[num_vertices + 3].color = color[2]; + + draw_quad(); +} + +void gfx_quads_text(float x, float y, float size, const char *text) +{ + float startx = x; + + gfx_quads_begin(); + + while(*text) + { + char c = *text; + text++; + + if(c == '\n') + { + x = startx; + y += size; + } + else + { + gfx_quads_setsubset( + (c%16)/16.0f, + (c/16)/16.0f, + (c%16)/16.0f+1.0f/16.0f, + (c/16)/16.0f+1.0f/16.0f); + + gfx_quads_drawTL(x,y,size,size); + x += size/2; + } + } + + gfx_quads_end(); +} + +static int word_length(const char *text) +{ + int s = 1; + while(1) + { + if(*text == 0) + return s-1; + if(*text == '\n' || *text == '\t' || *text == ' ') + return s; + text++; + s++; + } +} + + + +static float pretty_r=1; +static float pretty_g=1; +static float pretty_b=1; +static float pretty_a=1; + +void gfx_pretty_text_color(float r, float g, float b, float a) +{ + pretty_r = r; + pretty_g = g; + pretty_b = b; + pretty_a = a; +} + +float gfx_pretty_text_raw(float x, float y, float size, const char *text_, int length) +{ + const unsigned char *text = (unsigned char *)text_; + const float spacing = 0.05f; + gfx_texture_set(current_font->font_texture); + gfx_quads_begin(); + gfx_setcolor(pretty_r, pretty_g, pretty_b, pretty_a); + + if(length < 0) + length = strlen(text_); + + while(length) + { + const int c = *text; + const float width = current_font->m_CharEndTable[c] - current_font->m_CharStartTable[c]; + double x_nudge = 0; + + text++; + + x -= size * current_font->m_CharStartTable[c]; + + gfx_quads_setsubset( + (c%16)/16.0f, /* startx */ + (c/16)/16.0f, /* starty */ + (c%16)/16.0f+1.0f/16.0f, /* endx */ + (c/16)/16.0f+1.0f/16.0f); /* endy */ + + gfx_quads_drawTL(x, y, size, size); + + if(length > 1 && text[1]) + x_nudge = extra_kerning[text[0] + text[1] * 256]; + + x += (width + current_font->m_CharStartTable[c] + spacing + x_nudge) * size; + length--; + } + + gfx_quads_end(); + + return x; +} + + + +void gfx_pretty_text(float x, float y, float size, const char *text, int max_width) +{ + if(max_width == -1) + gfx_pretty_text_raw(x, y, size, text, -1); + else + { + float startx = x; + while(*text) + { + int wlen = word_length(text); + float w = gfx_pretty_text_width(size, text, wlen); + if(x+w-startx > max_width) + { + y += size-2; + x = startx; + } + + x = gfx_pretty_text_raw(x, y, size, text, wlen); + + text += wlen; + } + } +} + +float gfx_pretty_text_width(float size, const char *text_, int length) +{ + const float spacing = 0.05f; + float w = 0.0f; + const unsigned char *text = (unsigned char *)text_; + const unsigned char *stop; + + if (length == -1) + stop = text + strlen((char*)text); + else + stop = text + length; + + while (text < stop) + { + const int c = *text; + const float width = current_font->m_CharEndTable[c] - current_font->m_CharStartTable[c]; + + double x_nudge = 0; + if (text[1]) + x_nudge = extra_kerning[text[0] + text[1] * 256]; + + w += (width + spacing + x_nudge) * size; + + text++; + } + + return w; +} + + + +void gfx_lines_begin() +{ + dbg_assert(drawing == 0, "called begin twice"); + drawing = DRAWING_LINES; + gfx_setcolor(1,1,1,1); +} + +void gfx_lines_end() +{ + dbg_assert(drawing == DRAWING_LINES, "called end without begin"); + flush(); + drawing = 0; +} + +void gfx_lines_draw(float x0, float y0, float x1, float y1) +{ + dbg_assert(drawing == DRAWING_LINES, "called draw without begin"); + + vertices[num_vertices].pos.x = x0; + vertices[num_vertices].pos.y = y0; + vertices[num_vertices].tex = texture[0]; + vertices[num_vertices].color = color[0]; + + vertices[num_vertices + 1].pos.x = x1; + vertices[num_vertices + 1].pos.y = y1; + vertices[num_vertices + 1].tex = texture[1]; + vertices[num_vertices + 1].color = color[1]; + + draw_line(); +} diff --git a/src/engine/client/ec_inp.c b/src/engine/client/ec_inp.c new file mode 100644 index 00000000..0366cff5 --- /dev/null +++ b/src/engine/client/ec_inp.c @@ -0,0 +1,210 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include +#include + +#include +#include +#include + +static int keyboard_state[2][1024]; /* TODO: fix this!! */ +static int keyboard_current = 0; +static int keyboard_first = 1; + + +static struct +{ + unsigned char presses; + unsigned char releases; +} input_count[2][1024] = {{{0}}, {{0}}}; + +static unsigned char input_state[2][1024] = {{0}, {0}}; + +static int input_current = 0; +static unsigned int last_release = 0; +static unsigned int release_delta = -1; + +void inp_mouse_relative(int *x, int *y) +{ + static int last_x = 0, last_y = 0; + static int last_sens = 100.0f; + int nx, ny; + float sens = config.inp_mousesens/100.0f; + + if(last_sens != config.inp_mousesens) + { + last_x = (last_x/(float)last_sens)*(float)config.inp_mousesens; + last_y = (last_y/(float)last_sens)*(float)config.inp_mousesens; + last_sens = config.inp_mousesens; + } + + + glfwGetMousePos(&nx, &ny); + nx *= sens; + ny *= sens; + + *x = nx-last_x; + *y = ny-last_y; + last_x = nx; + last_y = ny; +} + +static char last_c = 0; +static int last_k = 0; + +static void char_callback(int character, int action) +{ + if(action == GLFW_PRESS && character < 256) + last_c = (char)character; +} + +static void key_callback(int key, int action) +{ + if(action == GLFW_PRESS) + last_k = key; + + if(action == GLFW_PRESS) + input_count[input_current^1][key].presses++; + if(action == GLFW_RELEASE) + input_count[input_current^1][key].releases++; + input_state[input_current^1][key] = action; +} + +static void mousebutton_callback(int button, int action) +{ + if(action == GLFW_PRESS) + last_k = KEY_MOUSE_FIRST+button; + + if(action == GLFW_PRESS) + input_count[input_current^1][KEY_MOUSE_FIRST+button].presses++; + if(action == GLFW_RELEASE) + { + if(button == 0) + { + release_delta = time_get() - last_release; + last_release = time_get(); + } + input_count[input_current^1][KEY_MOUSE_FIRST+button].releases++; + } + input_state[input_current^1][KEY_MOUSE_FIRST+button] = action; +} + + +static void mousewheel_callback(int pos) +{ + if(pos > 0) + { + while(pos-- != 0) + { + input_count[input_current^1][KEY_MOUSE_WHEEL_UP].presses++; + input_count[input_current^1][KEY_MOUSE_WHEEL_UP].releases++; + } + } + else if(pos < 0) + { + while(pos++ != 0) + { + input_count[input_current^1][KEY_MOUSE_WHEEL_DOWN].presses++; + input_count[input_current^1][KEY_MOUSE_WHEEL_DOWN].releases++; + } + } + glfwSetMouseWheel(0); +} + + +void inp_init() +{ + glfwEnable(GLFW_KEY_REPEAT); + glfwSetCharCallback(char_callback); + glfwSetKeyCallback(key_callback); + glfwSetMouseButtonCallback(mousebutton_callback); + glfwSetMouseWheelCallback(mousewheel_callback); +} + +char inp_last_char() +{ + return last_c; +} + +int inp_last_key() +{ + return last_k; +} + +void inp_clear() +{ + last_k = 0; + last_c = 0; +} + +void inp_mouse_mode_absolute() +{ + glfwEnable(GLFW_MOUSE_CURSOR); +} + +void inp_mouse_mode_relative() +{ + glfwDisable(GLFW_MOUSE_CURSOR); +} + +int inp_mouse_doubleclick() +{ + return release_delta < (time_freq() >> 2); +} + +int inp_key_presses(int key) +{ + return input_count[input_current][key].presses; +} + +int inp_key_releases(int key) +{ + return input_count[input_current][key].releases; +} + +int inp_key_state(int key) +{ + return input_state[input_current][key]; +} + +int inp_key_pressed(int key) { return keyboard_state[keyboard_current][key]; } +int inp_key_was_pressed(int key) { return keyboard_state[keyboard_current^1][key]; } +int inp_key_down(int key) { return inp_key_pressed(key)&&!inp_key_was_pressed(key); } +int inp_button_pressed(int button) { return keyboard_state[keyboard_current][button]; } + +void inp_update() +{ + int i, v; + + /* clear and begin count on the other one */ + mem_zero(&input_count[input_current], sizeof(input_count[input_current])); + memcpy(input_state[input_current], input_state[input_current^1], sizeof(input_state[input_current])); + input_current^=1; + + if(keyboard_first) + { + /* make sure to reset */ + keyboard_first = 0; + inp_update(); + } + + keyboard_current = keyboard_current^1; + for(i = 0; i < KEY_LAST; i++) + { + if (i >= KEY_MOUSE_FIRST) + v = glfwGetMouseButton(i-KEY_MOUSE_FIRST) == GLFW_PRESS ? 1 : 0; + else + v = glfwGetKey(i) == GLFW_PRESS ? 1 : 0; + keyboard_state[keyboard_current][i] = v; + } + + /* handle mouse wheel */ + /* + i = glfwGetMouseWheel(); + keyboard_state[keyboard_current][KEY_MOUSE_WHEEL_UP] = 0; + keyboard_state[keyboard_current][KEY_MOUSE_WHEEL_DOWN] = 0; + if(w > 0) + keyboard_state[keyboard_current][KEY_MOUSE_WHEEL_UP] = 1; + if(w < 0) + keyboard_state[keyboard_current][KEY_MOUSE_WHEEL_DOWN] = 1; + glfwSetMouseWheel(0);*/ +} diff --git a/src/engine/client/ec_snd.c b/src/engine/client/ec_snd.c new file mode 100644 index 00000000..89e18cb8 --- /dev/null +++ b/src/engine/client/ec_snd.c @@ -0,0 +1,441 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include +#include +#include + +#include +#include +#include +#include +#include + +enum +{ + NUM_SAMPLES = 512, + NUM_VOICES = 64, + NUM_CHANNELS = 16, + + MAX_FRAMES = 1024 +}; + +typedef struct +{ + short *data; + int num_frames; + int rate; + int channels; + int loop_start; + int loop_end; +} SAMPLE; + +typedef struct +{ + int vol; + int pan; +} CHANNEL; + +typedef struct VOICE_t +{ + SAMPLE *snd; + CHANNEL *channel; + int tick; + int vol; /* 0 - 255 */ + int flags; + int x, y; +} VOICE; + +static SAMPLE samples[NUM_SAMPLES] = { {0} }; +static VOICE voices[NUM_VOICES] = { {0} }; +static CHANNEL channels[NUM_CHANNELS] = { {255, 0} }; + +static LOCK sound_lock = 0; +static int sound_enabled = 0; + +static int center_x = 0; +static int center_y = 0; + +static int mixing_rate = 48000; + +void snd_set_channel(int cid, float vol, float pan) +{ + channels[cid].vol = (int)(vol*255.0f); + channels[cid].pan = (int)(pan*255.0f); /* TODO: this is only on and off right now */ +} + +static int play(int cid, int sid, int flags, float x, float y) +{ + int vid = -1; + int i; + + lock_wait(sound_lock); + + /* search for voice */ + /* TODO: fix this linear search */ + for(i = 0; i < NUM_VOICES; i++) + { + if(!voices[i].snd) + { + vid = i; + break; + } + } + + /* voice found, use it */ + if(vid != -1) + { + voices[vid].snd = &samples[sid]; + voices[vid].channel = &channels[cid]; + voices[vid].tick = 0; + voices[vid].vol = 255; + voices[vid].flags = flags; + voices[vid].x = (int)x; + voices[vid].y = (int)y; + } + + lock_release(sound_lock); + return vid; +} + +int snd_play_at(int cid, int sid, int flags, float x, float y) +{ + return play(cid, sid, flags|SNDFLAG_POS, x, y); +} + +int snd_play(int cid, int sid, int flags) +{ + return play(cid, sid, flags, 0, 0); +} + +void snd_stop(int vid) +{ + /* TODO: a nice fade out */ + lock_wait(sound_lock); + voices[vid].snd = 0; + lock_release(sound_lock); +} + +/* TODO: there should be a faster way todo this */ +static short int2short(int i) +{ + if(i > 0x7fff) + return 0x7fff; + else if(i < -0x7fff) + return -0x7fff; + return i; +} + +static int iabs(int i) +{ + if(i<0) + return -i; + return i; +} + +static void mix(short *final_out, unsigned frames) +{ + int mix_buffer[MAX_FRAMES*2] = {0}; + int i, s; + + /* aquire lock while we are mixing */ + lock_wait(sound_lock); + + for(i = 0; i < NUM_VOICES; i++) + { + if(voices[i].snd) + { + /* mix voice */ + VOICE *v = &voices[i]; + int *out = mix_buffer; + + int step = v->snd->channels; /* setup input sources */ + short *in_l = &v->snd->data[v->tick*step]; + short *in_r = &v->snd->data[v->tick*step+1]; + + int end = v->snd->num_frames-v->tick; + + int rvol = v->channel->vol; + int lvol = v->channel->vol; + + /* make sure that we don't go outside the sound data */ + if(frames < end) + end = frames; + + /* check if we have a mono sound */ + if(v->snd->channels == 1) + in_r = in_l; + + /* volume calculation */ + if(v->flags&SNDFLAG_POS && v->channel->pan) + { + /* TODO: we should respect the channel panning value */ + const int range = 1500; /* magic value, remove */ + int dx = v->x - center_x; + int dy = v->y - center_y; + int dist = sqrt(dx*dx+dy*dy); /* double here. nasty */ + int p = iabs(dx); + if(dist < range) + { + /* panning */ + if(dx > 0) + lvol = ((range-p)*lvol)/range; + else + rvol = ((range-p)*rvol)/range; + + /* falloff */ + lvol = (lvol*(range-dist))/range; + rvol = (rvol*(range-dist))/range; + } + else + { + lvol = 0; + rvol = 0; + } + } + + /* process all frames */ + for(s = 0; s < end; s++) + { + *out++ += (*in_l)*lvol; + *out++ += (*in_r)*rvol; + in_l += step; + in_r += step; + v->tick++; + } + + /* free voice if not used any more */ + if(v->tick == v->snd->num_frames) + v->snd = 0; + + } + } + + /* release the lock */ + lock_release(sound_lock); + + { + int master_vol = config.snd_volume; + + /* clamp accumulated values */ + /* TODO: this seams slow */ + for(i = 0; i < frames; i++) + { + int j = i<<1; + int vl = ((mix_buffer[j]*master_vol)/101)>>8; + int vr = ((mix_buffer[j+1]*master_vol)/101)>>8; + + final_out[j] = int2short(vl); + final_out[j+1] = int2short(vr); + } + } +} + +static int pacallback(const void *in, void *out, unsigned long frames, const PaStreamCallbackTimeInfo* time, PaStreamCallbackFlags status, void *user) +{ + mix(out, frames); + return 0; +} + +static PaStream *stream; + +int snd_init() +{ + PaStreamParameters params; + PaError err = Pa_Initialize(); + + sound_lock = lock_create(); + + if(!config.snd_enable) + return 0; + + mixing_rate = config.snd_rate; + + params.device = Pa_GetDefaultOutputDevice(); + if(params.device < 0) + return 1; + params.channelCount = 2; + params.sampleFormat = paInt16; + params.suggestedLatency = Pa_GetDeviceInfo(params.device)->defaultLowOutputLatency; + params.hostApiSpecificStreamInfo = 0x0; + + err = Pa_OpenStream( + &stream, /* passes back stream pointer */ + 0, /* no input channels */ + ¶ms, /* pointer to parameters */ + mixing_rate, /* sample rate */ + 128, /* frames per buffer */ + paClipOff, /* no clamping */ + pacallback, /* specify our custom callback */ + 0x0); /* pass our data through to callback */ + err = Pa_StartStream(stream); + + sound_enabled = 1; + return 0; +} + +int snd_shutdown() +{ + Pa_StopStream(stream); + Pa_Terminate(); + + lock_destroy(sound_lock); + + return 0; +} + +int snd_alloc_id() +{ + /* TODO: linear search, get rid of it */ + unsigned sid; + for(sid = 0; sid < NUM_SAMPLES; sid++) + { + if(samples[sid].data == 0x0) + return sid; + } + + return -1; +} + +static void rate_convert(int sid) +{ + SAMPLE *snd = &samples[sid]; + int num_frames = 0; + short *new_data = 0; + int i; + + /* make sure that we need to convert this sound */ + if(!snd->data || snd->rate == mixing_rate) + return; + + /* allocate new data */ + num_frames = (int)((snd->num_frames/(float)snd->rate)*mixing_rate); + new_data = mem_alloc(num_frames*snd->channels*sizeof(short), 1); + + for(i = 0; i < num_frames; i++) + { + /* resample TODO: this should be done better, like linear atleast */ + float a = i/(float)num_frames; + int f = (int)(a*snd->num_frames); + if(f >= snd->num_frames) + f = snd->num_frames-1; + + /* set new data */ + if(snd->channels == 1) + new_data[i] = snd->data[f]; + else if(snd->channels == 2) + { + new_data[i*2] = snd->data[f*2]; + new_data[i*2+1] = snd->data[f*2+1]; + } + } + + /* free old data and apply new */ + mem_free(snd->data); + snd->data = new_data; + snd->num_frames = num_frames; +} + + +static FILE *file = NULL; + +static int read_data(void *buffer, int size) +{ + return fread(buffer, 1, size, file); +} + +int snd_load_wv(const char *filename) +{ + SAMPLE *snd; + int sid = -1; + char error[100]; + WavpackContext *context; + + /* don't waste memory on sound when we are stress testing */ + if(config.dbg_stress) + return -1; + + /* no need to load sound when we are running with no sound */ + if(!sound_enabled) + return 1; + + file = fopen(filename, "rb"); /* TODO: use system.h stuff for this */ + if(!file) + { + dbg_msg("sound/wv", "failed to open %s", filename); + return -1; + } + + sid = snd_alloc_id(); + if(sid < 0) + return -1; + snd = &samples[sid]; + + context = WavpackOpenFileInput(read_data, error); + if (context) + { + int samples = WavpackGetNumSamples(context); + int bitspersample = WavpackGetBitsPerSample(context); + unsigned int samplerate = WavpackGetSampleRate(context); + int channels = WavpackGetNumChannels(context); + int *data; + int *src; + short *dst; + int i; + + snd->channels = channels; + snd->rate = samplerate; + + if(snd->channels > 2) + { + dbg_msg("sound/wv", "file is not mono or stereo. filename='%s'", filename); + return -1; + } + + /* + if(snd->rate != 44100) + { + dbg_msg("sound/wv", "file is %d Hz, not 44100 Hz. filename='%s'", snd->rate, filename); + return -1; + }*/ + + if(bitspersample != 16) + { + dbg_msg("sound/wv", "bps is %d, not 16, filname='%s'", bitspersample, filename); + return -1; + } + + data = (int *)mem_alloc(4*samples*channels, 1); + WavpackUnpackSamples(context, data, samples); /* TODO: check return value */ + src = data; + + snd->data = (short *)mem_alloc(2*samples*channels, 1); + dst = snd->data; + + for (i = 0; i < samples*channels; i++) + *dst++ = (short)*src++; + + mem_free(data); + + snd->num_frames = samples; + snd->loop_start = -1; + snd->loop_end = -1; + } + else + { + dbg_msg("sound/wv", "failed to open %s: %s", filename, error); + } + + fclose(file); + file = NULL; + + if(config.debug) + dbg_msg("sound/wv", "loaded %s", filename); + + rate_convert(sid); + return sid; +} + +void snd_set_listener_pos(float x, float y) +{ + center_x = (int)x; + center_y = (int)y; +} diff --git a/src/engine/client/ec_srvbrowse.c b/src/engine/client/ec_srvbrowse.c new file mode 100644 index 00000000..9fc5ec02 --- /dev/null +++ b/src/engine/client/ec_srvbrowse.c @@ -0,0 +1,404 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include +#include +#include +#include +#include + +#include + +#include +#include + +extern NETCLIENT *net; + + +/* ------ server browse ---- */ +/* TODO: move all this to a separate file */ + +typedef struct SERVERENTRY_t SERVERENTRY; +struct SERVERENTRY_t +{ + NETADDR4 addr; + int64 request_time; + int got_info; + SERVER_INFO info; + + SERVERENTRY *next_ip; /* ip hashed list */ + + SERVERENTRY *prev_req; /* request list */ + SERVERENTRY *next_req; +}; + +static HEAP *serverlist_heap = 0; +static SERVERENTRY **serverlist = 0; +static int *sorted_serverlist = 0; + +static SERVERENTRY *serverlist_ip[256] = {0}; /* ip hash list */ + +static SERVERENTRY *first_req_server = 0; /* request list */ +static SERVERENTRY *last_req_server = 0; +static int num_requests = 0; + +static int num_sorted_servers = 0; +static int num_sorted_servers_capacity = 0; +static int num_servers = 0; +static int num_server_capacity = 0; + +static int sorthash = 0; +static char filterstring[64] = {0}; + +static int serverlist_lan = 1; + +int client_serverbrowse_num() { return num_servers; } + +SERVER_INFO *client_serverbrowse_get(int index) +{ + if(index < 0 || index >= num_servers) + return 0; + return &serverlist[index]->info; +} + +int client_serverbrowse_sorted_num() { return num_sorted_servers; } + +SERVER_INFO *client_serverbrowse_sorted_get(int index) +{ + if(index < 0 || index >= num_sorted_servers) + return 0; + return &serverlist[sorted_serverlist[index]]->info; +} + + +int client_serverbrowse_num_requests() +{ + return num_requests; +} + +static int client_serverbrowse_sort_compare_name(const void *ai, const void *bi) +{ + SERVERENTRY *a = serverlist[*(const int*)ai]; + SERVERENTRY *b = serverlist[*(const int*)bi]; + return strcmp(a->info.name, b->info.name); +} + +static int client_serverbrowse_sort_compare_map(const void *ai, const void *bi) +{ + SERVERENTRY *a = serverlist[*(const int*)ai]; + SERVERENTRY *b = serverlist[*(const int*)bi]; + return strcmp(a->info.map, b->info.map); +} + +static int client_serverbrowse_sort_compare_ping(const void *ai, const void *bi) +{ + SERVERENTRY *a = serverlist[*(const int*)ai]; + SERVERENTRY *b = serverlist[*(const int*)bi]; + return a->info.latency > b->info.latency; +} + +static int client_serverbrowse_sort_compare_gametype(const void *ai, const void *bi) +{ + SERVERENTRY *a = serverlist[*(const int*)ai]; + SERVERENTRY *b = serverlist[*(const int*)bi]; + return a->info.game_type > b->info.game_type; +} + +static int client_serverbrowse_sort_compare_progression(const void *ai, const void *bi) +{ + SERVERENTRY *a = serverlist[*(const int*)ai]; + SERVERENTRY *b = serverlist[*(const int*)bi]; + return a->info.progression > b->info.progression; +} + +static int client_serverbrowse_sort_compare_numplayers(const void *ai, const void *bi) +{ + SERVERENTRY *a = serverlist[*(const int*)ai]; + SERVERENTRY *b = serverlist[*(const int*)bi]; + return a->info.num_players > b->info.num_players; +} + +static void client_serverbrowse_filter() +{ + int i = 0; + num_sorted_servers = 0; + + /* allocate the sorted list */ + if(num_sorted_servers_capacity < num_servers) + { + if(sorted_serverlist) + mem_free(sorted_serverlist); + num_sorted_servers_capacity = num_servers; + sorted_serverlist = mem_alloc(num_sorted_servers_capacity*sizeof(int), 1); + } + + /* filter the servers */ + for(i = 0; i < num_servers; i++) + { + int filtered = 0; + + if(config.b_filter_empty && serverlist[i]->info.num_players == 0) + filtered = 1; + else if(config.b_filter_full && serverlist[i]->info.num_players == serverlist[i]->info.max_players) + filtered = 1; + else if(config.b_filter_pw && serverlist[i]->info.flags&1) + filtered = 1; + else if(config.b_filter_string[0] != 0) + { + if(strstr(serverlist[i]->info.name, config.b_filter_string) == 0) + filtered = 1; + } + + if(filtered == 0) + sorted_serverlist[num_sorted_servers++] = i; + } +} + +static int client_serverbrowse_sorthash() +{ + int i = config.b_sort&0xf; + i |= config.b_filter_empty<<4; + i |= config.b_filter_full<<5; + i |= config.b_filter_pw<<6; + return i; +} + +static void client_serverbrowse_sort() +{ + int i; + + /* create filtered list */ + client_serverbrowse_filter(); + + /* sort */ + if(config.b_sort == BROWSESORT_NAME) + qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_name); + else if(config.b_sort == BROWSESORT_PING) + qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_ping); + else if(config.b_sort == BROWSESORT_MAP) + qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_map); + else if(config.b_sort == BROWSESORT_NUMPLAYERS) + qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_numplayers); + else if(config.b_sort == BROWSESORT_GAMETYPE) + qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_gametype); + else if(config.b_sort == BROWSESORT_PROGRESSION) + qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_progression); + + /* set indexes */ + for(i = 0; i < num_sorted_servers; i++) + serverlist[sorted_serverlist[i]]->info.sorted_index = i; + + strncpy(filterstring, config.b_filter_string, sizeof(filterstring)-1); + sorthash = client_serverbrowse_sorthash(); +} + +static void client_serverbrowse_remove_request(SERVERENTRY *entry) +{ + if(entry->prev_req || entry->next_req || first_req_server == entry) + { + if(entry->prev_req) + entry->prev_req->next_req = entry->next_req; + else + first_req_server = entry->next_req; + + if(entry->next_req) + entry->next_req->prev_req = entry->prev_req; + else + last_req_server = entry->prev_req; + + entry->prev_req = 0; + entry->next_req = 0; + num_requests--; + } +} + +void client_serverbrowse_set(NETADDR4 *addr, int request, SERVER_INFO *info) +{ + int hash = addr->ip[0]; + SERVERENTRY *entry = serverlist_ip[hash]; + while(entry) + { + if(net_addr4_cmp(&entry->addr, addr) == 0) + { + /* update the server that we already have */ + entry->info = *info; + if(!request) + { + entry->info.latency = (time_get()-entry->request_time)*1000/time_freq(); + client_serverbrowse_remove_request(entry); + } + + entry->got_info = 1; + client_serverbrowse_sort(); + return; + } + entry = entry->next_ip; + } + + /* create new entry */ + entry = (SERVERENTRY *)memheap_allocate(serverlist_heap, sizeof(SERVERENTRY)); + mem_zero(entry, sizeof(SERVERENTRY)); + + /* set the info */ + entry->addr = *addr; + entry->info = *info; + + /* add to the hash list */ + entry->next_ip = serverlist_ip[hash]; + serverlist_ip[hash] = entry; + + if(num_servers == num_server_capacity) + { + SERVERENTRY **newlist; + num_server_capacity += 100; + newlist = mem_alloc(num_server_capacity*sizeof(SERVERENTRY*), 1); + memcpy(newlist, serverlist, num_servers*sizeof(SERVERENTRY*)); + mem_free(serverlist); + serverlist = newlist; + } + + /* add to list */ + serverlist[num_servers] = entry; + entry->info.server_index = num_servers; + num_servers++; + + /* */ + if(request) + { + /* add it to the list of servers that we should request info from */ + entry->prev_req = last_req_server; + if(last_req_server) + last_req_server->next_req = entry; + else + first_req_server = entry; + last_req_server = entry; + + num_requests++; + } + + client_serverbrowse_sort(); +} + +void client_serverbrowse_refresh(int lan) +{ + /* clear out everything */ + if(serverlist_heap) + memheap_destroy(serverlist_heap); + serverlist_heap = memheap_create(); + num_servers = 0; + num_sorted_servers = 0; + mem_zero(serverlist_ip, sizeof(serverlist_ip)); + first_req_server = 0; + last_req_server = 0; + num_requests = 0; + + + /* */ + serverlist_lan = lan; + + if(serverlist_lan) + { + NETPACKET packet; + packet.client_id = -1; + mem_zero(&packet, sizeof(packet)); + packet.address.ip[0] = 255; + packet.address.ip[1] = 255; + packet.address.ip[2] = 255; + packet.address.ip[3] = 255; + packet.address.port = 8303; + packet.flags = PACKETFLAG_CONNLESS; + packet.data_size = sizeof(SERVERBROWSE_GETINFO); + packet.data = SERVERBROWSE_GETINFO; + netclient_send(net, &packet); + + if(config.debug) + dbg_msg("client", "broadcasting for servers"); + } + else + { + NETADDR4 master_server; + NETPACKET p; + + net_host_lookup(config.masterserver, MASTERSERVER_PORT, &master_server); + + mem_zero(&p, sizeof(p)); + p.client_id = -1; + p.address = master_server; + p.flags = PACKETFLAG_CONNLESS; + p.data_size = sizeof(SERVERBROWSE_GETLIST); + p.data = SERVERBROWSE_GETLIST; + netclient_send(net, &p); + + if(config.debug) + dbg_msg("client", "requesting server list"); + } +} + +static void client_serverbrowse_request(SERVERENTRY *entry) +{ + NETPACKET p; + + if(config.debug) + { + dbg_msg("client", "requesting server info from %d.%d.%d.%d:%d", + entry->addr.ip[0], entry->addr.ip[1], entry->addr.ip[2], + entry->addr.ip[3], entry->addr.port); + } + + p.client_id = -1; + p.address = entry->addr; + p.flags = PACKETFLAG_CONNLESS; + p.data_size = sizeof(SERVERBROWSE_GETINFO); + p.data = SERVERBROWSE_GETINFO; + netclient_send(net, &p); + entry->request_time = time_get(); +} + +void client_serverbrowse_update() +{ + int64 timeout = time_freq(); + int64 now = time_get(); + int count; + SERVERENTRY *entry, *next; + + /* do timeouts */ + entry = first_req_server; + while(1) + { + if(!entry) /* no more entries */ + break; + + next = entry->next_req; + + if(entry->request_time && entry->request_time+timeout < now) + { + /* timeout */ + client_serverbrowse_remove_request(entry); + num_requests--; + } + + entry = next; + } + + /* do timeouts */ + entry = first_req_server; + count = 0; + while(1) + { + if(!entry) /* no more entries */ + break; + + /* no more then 10 concurrent requests */ + if(count == config.b_max_requests) + break; + + if(entry->request_time == 0) + client_serverbrowse_request(entry); + + count++; + entry = entry->next_req; + } + + /* check if we need to resort */ + /* TODO: remove the strcmp */ + if(sorthash != client_serverbrowse_sorthash() || strcmp(filterstring, config.b_filter_string) != 0) + client_serverbrowse_sort(); +} diff --git a/src/engine/client/ec_ui.c b/src/engine/client/ec_ui.c new file mode 100644 index 00000000..5ba9c0d9 --- /dev/null +++ b/src/engine/client/ec_ui.c @@ -0,0 +1,277 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include +#include +#include +#include "ec_ui.h" + +/******************************************************** + UI +*********************************************************/ + +struct pretty_font +{ + float m_CharStartTable[256]; + float m_CharEndTable[256]; + int font_texture; +}; + +extern struct pretty_font *current_font; + +static const void *hot_item = 0; +static const void *active_item = 0; +static const void *last_active_item = 0; +static const void *becomming_hot_item = 0; +static float mouse_x, mouse_y; /* in gui space */ +static float mouse_wx, mouse_wy; /* in world space */ +static unsigned mouse_buttons = 0; + +float ui_mouse_x() { return mouse_x; } +float ui_mouse_y() { return mouse_y; } +float ui_mouse_world_x() { return mouse_wx; } +float ui_mouse_world_y() { return mouse_wy; } +int ui_mouse_button(int index) { return (mouse_buttons>>index)&1; } + +void ui_set_hot_item(const void *id) { becomming_hot_item = id; } +void ui_set_active_item(const void *id) { active_item = id; if (id) last_active_item = id; } +void ui_clear_last_active_item() { last_active_item = 0; } +const void *ui_hot_item() { return hot_item; } +const void *ui_active_item() { return active_item; } +const void *ui_last_active_item() { return last_active_item; } + +int ui_update(float mx, float my, float mwx, float mwy, int buttons) +{ + mouse_x = mx; + mouse_y = my; + mouse_wx = mwx; + mouse_wy = mwy; + mouse_buttons = buttons; + hot_item = becomming_hot_item; + if(active_item) + hot_item = active_item; + becomming_hot_item = 0; + return 0; +} + +int ui_mouse_inside(float x, float y, float w, float h) +{ + if(mouse_x >= x && mouse_x <= x+w && mouse_y >= y && mouse_y <= y+h) + return 1; + return 0; +} + +void ui_do_image(int texture, float x, float y, float w, float h) +{ + gfx_blend_normal(); + gfx_texture_set(texture); + gfx_quads_begin(); + gfx_setcolor(1,1,1,1); + gfx_quads_setsubset( + 0.0f, /* startx */ + 0.0f, /* starty */ + 1.0f, /* endx */ + 1.0f); /* endy */ + gfx_quads_drawTL(x,y,w,h); + gfx_quads_end(); +} + +void ui_do_label(float x, float y, const char *text, float size) +{ + gfx_blend_normal(); + gfx_texture_set(current_font->font_texture); + gfx_pretty_text(x, y, size, text, -1); +} + +int ui_do_button(const void *id, const char *text, int checked, float x, float y, float w, float h, draw_button_callback draw_func, void *extra) +{ + /* logic */ + int r = 0; + int inside = ui_mouse_inside(x,y,w,h); + + if(ui_active_item() == id) + { + if(!ui_mouse_button(0)) + { + if(inside) + r = 1; + ui_set_active_item(0); + } + } + else if(ui_hot_item() == id) + { + if(ui_mouse_button(0)) + ui_set_active_item(id); + } + + if(inside) + ui_set_hot_item(id); + + draw_func(id, text, checked, x, y, w, h, extra); + return r; +} + +static float scale = 1.0f; +#define MEMORY_SIZE 10*1024 +static struct rect memory[MEMORY_SIZE]; +static int memory_used = 0; +static struct rect screen = { 0.0f, 0.0f, 848.0f, 480.0f }; + +void ui_foreach_rect(rect_fun fun) +{ + int hrm; + for (hrm = 0; hrm < memory_used; hrm++) + fun(&memory[hrm]); +} + +static void add_rect(struct rect *r) +{ + if (memory_used < MEMORY_SIZE) + memory[memory_used++] = *r; + else + dbg_msg("ui", "rect memory full."); +} + +struct rect *ui_screen() +{ + if (config.debug) + { + memory_used = 0; + } + + return &screen; +} + +void ui_scale(float s) +{ + scale = s; +} + +void ui_hsplit_t(const struct rect *original, int pixels, struct rect *top, struct rect *bottom) +{ + struct rect r = *original; + pixels *= scale; + + if (top) + { + top->x = r.x; + top->y = r.y; + top->w = r.w; + top->h = pixels; + } + + if (bottom) + { + bottom->x = r.x; + bottom->y = r.y + pixels; + bottom->w = r.w; + bottom->h = r.h - pixels; + } + + if (config.debug) + { + if (top) + add_rect(top); + if (bottom) + add_rect(bottom); + } +} + +void ui_hsplit_b(const struct rect *original, int pixels, struct rect *top, struct rect *bottom) +{ + struct rect r = *original; + pixels *= scale; + + if (top) + { + top->x = r.x; + top->y = r.y; + top->w = r.w; + top->h = r.h - pixels; + } + + if (bottom) + { + bottom->x = r.x; + bottom->y = r.y + r.h - pixels; + bottom->w = r.w; + bottom->h = pixels; + } + + if (config.debug) + { + if (top) + add_rect(top); + if (bottom) + add_rect(bottom); + } +} + +void ui_vsplit_l(const struct rect *original, int pixels, struct rect *left, struct rect *right) +{ + struct rect r = *original; + pixels *= scale; + + if (left) + { + left->x = r.x; + left->y = r.y; + left->w = pixels; + left->h = r.h; + } + + if (right) + { + right->x = r.x + pixels; + right->y = r.y; + right->w = r.w - pixels; + right->h = r.h; + } + + if (config.debug) + { + if (left) + add_rect(left); + if (right) + add_rect(right); + } +} + +void ui_vsplit_r(const struct rect *original, int pixels, struct rect *left, struct rect *right) +{ + struct rect r = *original; + pixels *= scale; + + if (left) + { + left->x = r.x; + left->y = r.y; + left->w = r.w - pixels; + left->h = r.h; + } + + if (right) + { + right->x = r.x + r.w - pixels; + right->y = r.y; + right->w = pixels; + right->h = r.h; + } + + if (config.debug) + { + if (left) + add_rect(left); + if (right) + add_rect(right); + } +} + +void ui_margin(const struct rect *original, int pixels, struct rect *other_rect) +{ + struct rect r = *original; + pixels *= scale; + + other_rect->x = r.x + pixels; + other_rect->y = r.y + pixels; + other_rect->w = r.w - 2*pixels; + other_rect->h = r.h - 2*pixels; +} diff --git a/src/engine/client/ec_ui.h b/src/engine/client/ec_ui.h new file mode 100644 index 00000000..15c63b90 --- /dev/null +++ b/src/engine/client/ec_ui.h @@ -0,0 +1,36 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#ifndef _UI_H +#define _UI_H + +#ifdef __cplusplus +extern "C" { +#endif + +int ui_update(float mx, float my, float mwx, float mwy, int buttons); + +float ui_mouse_x(); +float ui_mouse_y(); +float ui_mouse_world_x(); +float ui_mouse_world_y(); +int ui_mouse_button(int index); + +void ui_set_hot_item(const void *id); +void ui_set_active_item(const void *id); +void ui_clear_last_active_item(); +const void *ui_hot_item(); +const void *ui_active_item(); +const void *ui_last_active_item(); + +int ui_mouse_inside(float x, float y, float w, float h); + +typedef void (*draw_button_callback)(const void *id, const char *text, int checked, float x, float y, float w, float h, void *extra); + +void ui_do_image(int texture, float x, float y, float w, float h); +void ui_do_label(float x, float y, const char *text, float size); +int ui_do_button(const void *id, const char *text, int checked, float x, float y, float w, float h, draw_button_callback draw_func, void *extra); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/engine/client/gfx.c b/src/engine/client/gfx.c deleted file mode 100644 index c61f0cb1..00000000 --- a/src/engine/client/gfx.c +++ /dev/null @@ -1,1018 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include - -/* compressed textures */ -#define GL_COMPRESSED_RGB_ARB 0x84ED -#define GL_COMPRESSED_RGBA_ARB 0x84EE - -enum -{ - DRAWING_QUADS=1, - DRAWING_LINES=2 -}; - -/* */ -typedef struct { float x, y, z; } VEC3; -typedef struct { float u, v; } TEXCOORD; -typedef struct { float r, g, b, a; } COLOR; - -typedef struct -{ - VEC3 pos; - TEXCOORD tex; - COLOR color; -} VERTEX; - -const int vertex_buffer_size = 32*1024; -static VERTEX *vertices = 0; -static int num_vertices = 0; - -static COLOR color[4]; -static TEXCOORD texture[4]; - -static int do_screenshot = 0; - -static int screen_width = -1; -static int screen_height = -1; -static float rotation = 0; -static int drawing = 0; - -static float screen_x0 = 0; -static float screen_y0 = 0; -static float screen_x1 = 0; -static float screen_y1 = 0; - -typedef struct -{ - GLuint tex; - int memsize; - int flags; - int next; -} TEXTURE; - - -enum -{ - MAX_TEXTURES = 128 -}; - -static TEXTURE textures[MAX_TEXTURES]; -static int first_free_texture; -static int memory_usage = 0; - -static const unsigned char null_texture_data[] = { - 0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, 0x00,0xff,0x00,0xff, 0x00,0xff,0x00,0xff, - 0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, 0x00,0xff,0x00,0xff, 0x00,0xff,0x00,0xff, - 0x00,0x00,0xff,0xff, 0x00,0x00,0xff,0xff, 0xff,0xff,0x00,0xff, 0xff,0xff,0x00,0xff, - 0x00,0x00,0xff,0xff, 0x00,0x00,0xff,0xff, 0xff,0xff,0x00,0xff, 0xff,0xff,0x00,0xff, -}; - - -typedef struct -{ - float m_CharStartTable[256]; - float m_CharEndTable[256]; - int font_texture; -} pretty_font; - -pretty_font default_font = -{ -{ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0.421875, 0.359375, 0.265625, 0.25, 0.1875, 0.25, 0.4375, 0.390625, 0.390625, 0.34375, 0.28125, 0.421875, 0.390625, 0.4375, 0.203125, - 0.265625, 0.28125, 0.28125, 0.265625, 0.25, 0.28125, 0.28125, 0.265625, 0.28125, 0.265625, 0.4375, 0.421875, 0.3125, 0.28125, 0.3125, 0.3125, - 0.25, 0.234375, 0.28125, 0.265625, 0.265625, 0.296875, 0.3125, 0.25, 0.25, 0.421875, 0.28125, 0.265625, 0.328125, 0.171875, 0.234375, 0.25, - 0.28125, 0.234375, 0.265625, 0.265625, 0.28125, 0.265625, 0.234375, 0.09375, 0.234375, 0.234375, 0.265625, 0.390625, 0.203125, 0.390625, 0.296875, 0.28125, - 0.375, 0.3125, 0.3125, 0.3125, 0.296875, 0.3125, 0.359375, 0.296875, 0.3125, 0.4375, 0.390625, 0.328125, 0.4375, 0.203125, 0.3125, 0.296875, - 0.3125, 0.296875, 0.359375, 0.3125, 0.328125, 0.3125, 0.296875, 0.203125, 0.296875, 0.296875, 0.328125, 0.375, 0.421875, 0.375, 0.28125, 0.3125, - 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, - 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, - 0, 0.421875, 0.3125, 0.265625, 0.25, 0.25, 0.421875, 0.265625, 0.375, 0.21875, 0.375, 0.328125, 0.3125, 0, 0.21875, 0.28125, - 0.359375, 0.28125, 0.34375, 0.34375, 0.421875, 0.3125, 0.265625, 0.421875, 0.421875, 0.34375, 0.375, 0.328125, 0.125, 0.125, 0.125, 0.296875, - 0.234375, 0.234375, 0.234375, 0.234375, 0.234375, 0.234375, 0.109375, 0.265625, 0.296875, 0.296875, 0.296875, 0.296875, 0.375, 0.421875, 0.359375, 0.390625, - 0.21875, 0.234375, 0.25, 0.25, 0.25, 0.25, 0.25, 0.296875, 0.21875, 0.265625, 0.265625, 0.265625, 0.265625, 0.234375, 0.28125, 0.3125, - 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.1875, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.375, 0.421875, 0.359375, 0.390625, - 0.3125, 0.3125, 0.296875, 0.296875, 0.296875, 0.296875, 0.296875, 0.28125, 0.28125, 0.3125, 0.3125, 0.3125, 0.3125, 0.296875, 0.3125, 0.296875, -}, -{ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0.2, 0.5625, 0.625, 0.71875, 0.734375, 0.796875, 0.765625, 0.546875, 0.59375, 0.59375, 0.65625, 0.703125, 0.546875, 0.59375, 0.5625, 0.6875, - 0.71875, 0.609375, 0.703125, 0.703125, 0.71875, 0.703125, 0.703125, 0.6875, 0.703125, 0.703125, 0.5625, 0.546875, 0.671875, 0.703125, 0.671875, 0.671875, - 0.734375, 0.75, 0.734375, 0.734375, 0.734375, 0.6875, 0.6875, 0.734375, 0.71875, 0.5625, 0.65625, 0.765625, 0.703125, 0.8125, 0.75, 0.734375, - 0.734375, 0.765625, 0.71875, 0.71875, 0.703125, 0.71875, 0.75, 0.890625, 0.75, 0.75, 0.71875, 0.59375, 0.6875, 0.59375, 0.6875, 0.703125, - 0.5625, 0.671875, 0.6875, 0.671875, 0.671875, 0.671875, 0.625, 0.671875, 0.671875, 0.5625, 0.546875, 0.703125, 0.5625, 0.78125, 0.671875, 0.671875, - 0.6875, 0.671875, 0.65625, 0.671875, 0.65625, 0.671875, 0.6875, 0.78125, 0.6875, 0.671875, 0.65625, 0.609375, 0.546875, 0.609375, 0.703125, 0.671875, - 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, - 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, - 0, 0.5625, 0.671875, 0.734375, 0.734375, 0.734375, 0.546875, 0.71875, 0.609375, 0.765625, 0.609375, 0.65625, 0.671875, 0, 0.765625, 0.703125, - 0.625, 0.703125, 0.640625, 0.640625, 0.609375, 0.671875, 0.703125, 0.546875, 0.5625, 0.578125, 0.609375, 0.65625, 0.859375, 0.859375, 0.859375, 0.671875, - 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, 0.84375, 0.734375, 0.6875, 0.6875, 0.6875, 0.6875, 0.5625, 0.609375, 0.640625, 0.59375, - 0.734375, 0.75, 0.734375, 0.734375, 0.734375, 0.734375, 0.734375, 0.6875, 0.765625, 0.71875, 0.71875, 0.71875, 0.71875, 0.75, 0.734375, 0.6875, - 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.796875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.5625, 0.609375, 0.625, 0.59375, - 0.6875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.703125, 0.703125, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.6875, 0.671875, -}, -0 -}; - -double extra_kerning[256*256] = {0}; - -pretty_font *current_font = &default_font; - -static void flush() -{ - if(num_vertices == 0) - return; - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - glVertexPointer(3, GL_FLOAT, - sizeof(VERTEX), - (char*)vertices); - glTexCoordPointer(2, GL_FLOAT, - sizeof(VERTEX), - (char*)vertices + sizeof(float)*3); - glColorPointer(4, GL_FLOAT, - sizeof(VERTEX), - (char*)vertices + sizeof(float)*5); - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glEnableClientState(GL_COLOR_ARRAY); - - if(drawing == DRAWING_QUADS) - glDrawArrays(GL_QUADS, 0, num_vertices); - else if(drawing == DRAWING_LINES) - glDrawArrays(GL_LINES, 0, num_vertices); - - /* Reset pointer */ - num_vertices = 0; -} - -static void draw_line() -{ - num_vertices += 2; - if((num_vertices + 2) >= vertex_buffer_size) - flush(); -} - -static void draw_quad() -{ - num_vertices += 4; - if((num_vertices + 4) >= vertex_buffer_size) - flush(); -} - - -int gfx_init() -{ - int i; - - screen_width = config.gfx_screen_width; - screen_height = config.gfx_screen_height; - - glfwInit(); - - if(config.dbg_stress) - { - screen_width = 320; - screen_height = 240; - } - - /* set antialiasing */ - if(config.gfx_fsaa_samples) - glfwOpenWindowHint(GLFW_FSAA_SAMPLES, config.gfx_fsaa_samples); - - /* set refresh rate */ - if(config.gfx_refresh_rate) - glfwOpenWindowHint(GLFW_REFRESH_RATE, config.gfx_refresh_rate); - - /* no resizing allowed */ - glfwOpenWindowHint(GLFW_WINDOW_NO_RESIZE, 1); - - /* open window */ - if(config.gfx_fullscreen) - { - int result = glfwOpenWindow(screen_width, screen_height, 8, 8, 8, 0, 24, 0, GLFW_FULLSCREEN); - if(result != GL_TRUE) - { - dbg_msg("game", "failed to create gl context"); - return 0; - } - } - else - { - int result = glfwOpenWindow(screen_width, screen_height, 0, 0, 0, 0, 24, 0, GLFW_WINDOW); - if(result != GL_TRUE) - { - dbg_msg("game", "failed to create gl context"); - return 0; - } - } - - glGetIntegerv(GL_DEPTH_BITS, &i); - dbg_msg("gfx", "depthbits = %d", i); - - glfwSetWindowTitle("Teewars"); - - /* We don't want to see the window when we run the stress testing */ - if(config.dbg_stress) - glfwIconifyWindow(); - - /* Init vertices */ - if (vertices) - mem_free(vertices); - vertices = (VERTEX*)mem_alloc(sizeof(VERTEX) * vertex_buffer_size, 1); - num_vertices = 0; - - - /* - dbg_msg("gfx", "OpenGL version %d.%d.%d", context.version_major(), - context.version_minor(), - context.version_rev());*/ - - gfx_mapscreen(0,0,config.gfx_screen_width, config.gfx_screen_height); - - /* set some default settings */ - glEnable(GL_BLEND); - glDisable(GL_CULL_FACE); - glDisable(GL_DEPTH_TEST); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - gfx_mask_op(MASK_NONE, 0); - - /* Set all z to -5.0f */ - for (i = 0; i < vertex_buffer_size; i++) - vertices[i].pos.z = -5.0f; - - /* init textures */ - first_free_texture = 0; - for(i = 0; i < MAX_TEXTURES; i++) - textures[i].next = i+1; - textures[MAX_TEXTURES-1].next = -1; - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - /* init input */ - inp_init(); - - /* create null texture, will get id=0 */ - gfx_load_texture_raw(4,4,IMG_RGBA,null_texture_data); - - /* set vsync as needed */ - gfx_set_vsync(config.gfx_vsync); - - /* UGLY as hell, please remove */ - current_font->font_texture = gfx_load_texture("data/big_font.png"); - - return 1; -} - -float gfx_screenaspect() -{ - return gfx_screenwidth()/(float)gfx_screenheight(); -} - -void gfx_clear_mask(int fill) -{ - /*if(fill) - glClearDepth(0.0f); - else*/ - - int i; - glGetIntegerv(GL_DEPTH_WRITEMASK, &i); - glDepthMask(GL_TRUE); - glClearDepth(0.0f); - glClear(GL_DEPTH_BUFFER_BIT); - glDepthMask(i); -} - -void gfx_mask_op(int mask, int write) -{ - glEnable(GL_DEPTH_TEST); - - if(write) - glDepthMask(GL_TRUE); - else - glDepthMask(GL_FALSE); - - if(mask == MASK_NONE) - glDepthFunc(GL_ALWAYS); - else if(mask == MASK_SET) - glDepthFunc(GL_LEQUAL); - else if(mask == MASK_ZERO) - glDepthFunc(GL_NOTEQUAL); -} - -int gfx_window_active() -{ - return glfwGetWindowParam(GLFW_ACTIVE) == GL_TRUE ? 1 : 0; -} - -int gfx_window_open() -{ - return glfwGetWindowParam(GLFW_OPENED) == GL_TRUE ? 1 : 0; -} - -VIDEO_MODE fakemodes[] = { - {320,240,8,8,8}, {400,300,8,8,8}, {640,480,8,8,8}, - {720,400,8,8,8}, {768,576,8,8,8}, {800,600,8,8,8}, - {1024,600,8,8,8}, {1024,768,8,8,8}, {1152,864,8,8,8}, - {1280,768,8,8,8}, {1280,800,8,8,8}, {1280,960,8,8,8}, - {1280,1024,8,8,8}, {1368,768,8,8,8}, {1400,1050,8,8,8}, - {1440,900,8,8,8}, {1440,1050,8,8,8}, {1600,1000,8,8,8}, - {1600,1200,8,8,8}, {1680,1050,8,8,8}, {1792,1344,8,8,8}, - {1800,1440,8,8,8}, {1856,1392,8,8,8}, {1920,1080,8,8,8}, - {1920,1200,8,8,8}, {1920,1440,8,8,8}, {1920,2400,8,8,8}, - {2048,1536,8,8,8}, - - {320,240,5,6,5}, {400,300,5,6,5}, {640,480,5,6,5}, - {720,400,5,6,5}, {768,576,5,6,5}, {800,600,5,6,5}, - {1024,600,5,6,5}, {1024,768,5,6,5}, {1152,864,5,6,5}, - {1280,768,5,6,5}, {1280,800,5,6,5}, {1280,960,5,6,5}, - {1280,1024,5,6,5}, {1368,768,5,6,5}, {1400,1050,5,6,5}, - {1440,900,5,6,5}, {1440,1050,5,6,5}, {1600,1000,5,6,5}, - {1600,1200,5,6,5}, {1680,1050,5,6,5}, {1792,1344,5,6,5}, - {1800,1440,5,6,5}, {1856,1392,5,6,5}, {1920,1080,5,6,5}, - {1920,1200,5,6,5}, {1920,1440,5,6,5}, {1920,2400,5,6,5}, - {2048,1536,5,6,5} -}; - -int gfx_get_video_modes(VIDEO_MODE *list, int maxcount) -{ - if(config.gfx_display_all_modes) - { - int count = sizeof(fakemodes)/sizeof(VIDEO_MODE); - mem_copy(list, fakemodes, sizeof(fakemodes)); - if(maxcount < count) - count = maxcount; - return count; - } - - return glfwGetVideoModes((GLFWvidmode *)list, maxcount); -} - -void gfx_set_vsync(int val) -{ - glfwSwapInterval(val); -} - -int gfx_unload_texture(int index) -{ - glDeleteTextures(1, &textures[index].tex); - textures[index].next = first_free_texture; - memory_usage -= textures[index].memsize; - first_free_texture = index; - return 0; -} - -void gfx_blend_normal() -{ - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); -} - -void gfx_blend_additive() -{ - glBlendFunc(GL_SRC_ALPHA, GL_ONE); -} - -int gfx_memory_usage() { return memory_usage; } - -static unsigned char sample(int w, int h, const unsigned char *data, int u, int v, int offset) -{ - return (data[(v*w+u)*4+offset]+ - data[(v*w+u+1)*4+offset]+ - data[((v+1)*w+u)*4+offset]+ - data[((v+1)*w+u+1)*4+offset])/4; -} - -int gfx_load_texture_raw(int w, int h, int format, const void *data) -{ - int mipmap = 1; - unsigned char *texdata = (unsigned char *)data; - unsigned char *tmpdata = 0; - int oglformat = 0; - int tex = 0; - - /* don't waste memory on texture if we are stress testing */ - if(config.dbg_stress) - return -1; - - /* grab texture */ - tex = first_free_texture; - first_free_texture = textures[tex].next; - textures[tex].next = -1; - - /* resample if needed */ - if(config.gfx_texture_quality==0) - { - if(w > 16 && h > 16 && format == IMG_RGBA) - { - unsigned char *tmpdata; - int c = 0; - int x, y; - - tmpdata = (unsigned char *)mem_alloc(w*h*4, 1); - - w/=2; - h/=2; - - for(y = 0; y < h; y++) - for(x = 0; x < w; x++, c++) - { - tmpdata[c*4] = sample(w*2, h*2, texdata, x*2,y*2, 0); - tmpdata[c*4+1] = sample(w*2, h*2, texdata, x*2,y*2, 1); - tmpdata[c*4+2] = sample(w*2, h*2, texdata, x*2,y*2, 2); - tmpdata[c*4+3] = sample(w*2, h*2, texdata, x*2,y*2, 3); - } - texdata = tmpdata; - } - } - - if(config.debug) - dbg_msg("gfx", "%d = %dx%d", tex, w, h); - - /* upload texture */ - if(config.gfx_texture_compression) - { - oglformat = GL_COMPRESSED_RGBA_ARB; - if(format == IMG_RGB) - oglformat = GL_COMPRESSED_RGB_ARB; - } - else - { - oglformat = GL_RGBA; - if(format == IMG_RGB) - oglformat = GL_RGB; - } - - glGenTextures(1, &textures[tex].tex); - glBindTexture(GL_TEXTURE_2D, textures[tex].tex); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - gluBuild2DMipmaps(GL_TEXTURE_2D, oglformat, w, h, oglformat, GL_UNSIGNED_BYTE, texdata); - - /* calculate memory usage */ - textures[tex].memsize = w*h*4; - if(mipmap) - { - while(w > 2 && h > 2) - { - w>>=1; - h>>=1; - textures[tex].memsize += w*h*4; - } - } - - memory_usage += textures[tex].memsize; - mem_free(tmpdata); - return tex; -} -/* -int gfx_load_mip_texture_raw(int w, int h, int format, const void *data) -{ - // grab texture - int tex = first_free_texture; - first_free_texture = textures[tex].next; - textures[tex].next = -1; - - // set data and return - // TODO: should be RGBA, not BGRA - dbg_msg("gfx", "%d = %dx%d", tex, w, h); - dbg_assert(format == IMG_RGBA, "not an RGBA image"); - - unsigned mip_w = w; - unsigned mip_h = h; - unsigned level = 0; - const unsigned char *ptr = (const unsigned char*)data; - while(mip_w > 0 && mip_h > 0) - { - dbg_msg("gfx mip", "%d = %dx%d", level, mip_w, mip_h); - textures[tex].tex.data2d_mip(mip_w, mip_h, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, level, ptr); - level++; - ptr = ptr + mip_w*mip_h*4; - mip_w = mip_w>>1; - mip_h = mip_h>>1; - } - return tex; -} -*/ - -/* simple uncompressed RGBA loaders */ -int gfx_load_texture(const char *filename) -{ - int l = strlen(filename); - IMAGE_INFO img; - if(l < 3) - return 0; - if(gfx_load_png(&img, filename)) - { - int id = gfx_load_texture_raw(img.width, img.height, img.format, img.data); - mem_free(img.data); - return id; - } - - return 0; -} - -int gfx_load_png(IMAGE_INFO *img, const char *filename) -{ - unsigned char *buffer; - png_t png; - - /* open file for reading */ - png_init(0,0); - - if(png_open_file(&png, filename) != PNG_NO_ERROR) - { - dbg_msg("game/png", "failed to open file. filename='%s'", filename); - return 0; - } - - if(png.depth != 8 || (png.color_type != PNG_TRUECOLOR && png.color_type != PNG_TRUECOLOR_ALPHA)) - { - dbg_msg("game/png", "invalid format. filename='%s'", filename); - png_close_file(&png); - } - - buffer = (unsigned char *)mem_alloc(png.width * png.height * png.bpp, 1); - png_get_data(&png, buffer); - png_close_file(&png); - - img->width = png.width; - img->height = png.height; - if(png.color_type == PNG_TRUECOLOR) - img->format = IMG_RGB; - else if(png.color_type == PNG_TRUECOLOR_ALPHA) - img->format = IMG_RGBA; - img->data = buffer; - return 1; -} - -void gfx_shutdown() -{ - if (vertices) - mem_free(vertices); - glfwCloseWindow(); - glfwTerminate(); -} - -void gfx_screenshot() -{ - do_screenshot = 1; -} - -void gfx_swap() -{ - if(do_screenshot) - { - /* fetch image data */ - int y; - int w = screen_width; - int h = screen_height; - unsigned char *pixel_data = (unsigned char *)mem_alloc(w*(h+1)*3, 1); - unsigned char *temp_row = pixel_data+w*h*3; - glReadPixels(0,0, w, h, GL_RGB, GL_UNSIGNED_BYTE, pixel_data); - - /* flip the pixel because opengl works from bottom left corner */ - for(y = 0; y < h/2; y++) - { - mem_copy(temp_row, pixel_data+y*w*3, w*3); - mem_copy(pixel_data+y*w*3, pixel_data+(h-y-1)*w*3, w*3); - mem_copy(pixel_data+(h-y-1)*w*3, temp_row,w*3); - } - - /* find filename */ - { - char wholepath[1024]; - char filename[128]; - static int index = 1; - png_t png; - - for(; index < 1000; index++) - { - IOHANDLE io; - sprintf(filename, "screenshots/screenshot%04d.png", index); - engine_savepath(filename, wholepath, sizeof(wholepath)); - - io = io_open(wholepath, IOFLAG_READ); - if(io) - io_close(io); - else - break; - } - - /* save png */ - dbg_msg("client", "saved screenshot to '%s'", wholepath); - png_open_file_write(&png, wholepath); - png_set_data(&png, w, h, 8, PNG_TRUECOLOR, (unsigned char *)pixel_data); - png_close_file(&png); - } - - /* clean up */ - mem_free(pixel_data); - do_screenshot = 0; - } - - glfwSwapBuffers(); - glFinish(); - glfwPollEvents(); -} - -int gfx_screenwidth() -{ - return screen_width; -} - -int gfx_screenheight() -{ - return screen_height; -} - -void gfx_texture_set(int slot) -{ - dbg_assert(drawing == 0, "called gfx_texture_set within begin"); - if(slot == -1) - glDisable(GL_TEXTURE_2D); - else - { - glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, textures[slot].tex); - } -} - -void gfx_clear(float r, float g, float b) -{ - glClearColor(r,g,b,1.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); -} - -void gfx_mapscreen(float tl_x, float tl_y, float br_x, float br_y) -{ - screen_x0 = tl_x; - screen_y0 = tl_y; - screen_x1 = br_x; - screen_y1 = br_y; - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(tl_x, br_x, br_y, tl_y, 1.0f, 10.f); -} - -void gfx_getscreen(float *tl_x, float *tl_y, float *br_x, float *br_y) -{ - *tl_x = screen_x0; - *tl_y = screen_y0; - *br_x = screen_x1; - *br_y = screen_y1; -} - -void gfx_setoffset(float x, float y) -{ - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - glTranslatef(x, y, 0); -} - - -void gfx_quads_begin() -{ - dbg_assert(drawing == 0, "called quads_begin twice"); - drawing = DRAWING_QUADS; - - gfx_quads_setsubset(0,0,1,1); - gfx_quads_setrotation(0); - gfx_setcolor(1,1,1,1); -} - -void gfx_quads_end() -{ - dbg_assert(drawing == DRAWING_QUADS, "called quads_end without begin"); - flush(); - drawing = 0; -} - - -void gfx_quads_setrotation(float angle) -{ - dbg_assert(drawing == DRAWING_QUADS, "called gfx_quads_setrotation without begin"); - rotation = angle; -} - -void gfx_setcolorvertex(int i, float r, float g, float b, float a) -{ - dbg_assert(drawing != 0, "called gfx_quads_setcolorvertex without begin"); - color[i].r = r; - color[i].g = g; - color[i].b = b; - color[i].a = a; -} - -void gfx_setcolor(float r, float g, float b, float a) -{ - dbg_assert(drawing != 0, "called gfx_quads_setcolor without begin"); - gfx_setcolorvertex(0, r, g, b, a); - gfx_setcolorvertex(1, r, g, b, a); - gfx_setcolorvertex(2, r, g, b, a); - gfx_setcolorvertex(3, r, g, b, a); -} - -void gfx_quads_setsubset(float tl_u, float tl_v, float br_u, float br_v) -{ - dbg_assert(drawing == DRAWING_QUADS, "called gfx_quads_setsubset without begin"); - - texture[0].u = tl_u; - texture[0].v = tl_v; - - texture[1].u = br_u; - texture[1].v = tl_v; - - texture[2].u = br_u; - texture[2].v = br_v; - - texture[3].u = tl_u; - texture[3].v = br_v; -} - -static void rotate(VEC3 *center, VEC3 *point) -{ - float x = point->x - center->x; - float y = point->y - center->y; - point->x = x * cosf(rotation) - y * sinf(rotation) + center->x; - point->y = x * sinf(rotation) + y * cosf(rotation) + center->y; -} - -void gfx_quads_draw(float x, float y, float w, float h) -{ - gfx_quads_drawTL(x-w/2, y-h/2,w,h); -} - -void gfx_quads_drawTL(float x, float y, float width, float height) -{ - VEC3 center; - - dbg_assert(drawing == DRAWING_QUADS, "called quads_draw without begin"); - - center.x = x + width/2; - center.y = y + height/2; - center.z = 0; - - vertices[num_vertices].pos.x = x; - vertices[num_vertices].pos.y = y; - vertices[num_vertices].tex = texture[0]; - vertices[num_vertices].color = color[0]; - rotate(¢er, &vertices[num_vertices].pos); - - vertices[num_vertices + 1].pos.x = x+width; - vertices[num_vertices + 1].pos.y = y; - vertices[num_vertices + 1].tex = texture[1]; - vertices[num_vertices + 1].color = color[1]; - rotate(¢er, &vertices[num_vertices + 1].pos); - - vertices[num_vertices + 2].pos.x = x + width; - vertices[num_vertices + 2].pos.y = y+height; - vertices[num_vertices + 2].tex = texture[2]; - vertices[num_vertices + 2].color = color[2]; - rotate(¢er, &vertices[num_vertices + 2].pos); - - vertices[num_vertices + 3].pos.x = x; - vertices[num_vertices + 3].pos.y = y+height; - vertices[num_vertices + 3].tex = texture[3]; - vertices[num_vertices + 3].color = color[3]; - rotate(¢er, &vertices[num_vertices + 3].pos); - - draw_quad(); -} - -void gfx_quads_draw_freeform( - float x0, float y0, - float x1, float y1, - float x2, float y2, - float x3, float y3) -{ - dbg_assert(drawing == DRAWING_QUADS, "called quads_draw_freeform without begin"); - - vertices[num_vertices].pos.x = x0; - vertices[num_vertices].pos.y = y0; - vertices[num_vertices].tex = texture[0]; - vertices[num_vertices].color = color[0]; - - vertices[num_vertices + 1].pos.x = x1; - vertices[num_vertices + 1].pos.y = y1; - vertices[num_vertices + 1].tex = texture[1]; - vertices[num_vertices + 1].color = color[1]; - - vertices[num_vertices + 2].pos.x = x3; - vertices[num_vertices + 2].pos.y = y3; - vertices[num_vertices + 2].tex = texture[3]; - vertices[num_vertices + 2].color = color[3]; - - vertices[num_vertices + 3].pos.x = x2; - vertices[num_vertices + 3].pos.y = y2; - vertices[num_vertices + 3].tex = texture[2]; - vertices[num_vertices + 3].color = color[2]; - - draw_quad(); -} - -void gfx_quads_text(float x, float y, float size, const char *text) -{ - float startx = x; - - gfx_quads_begin(); - - while(*text) - { - char c = *text; - text++; - - if(c == '\n') - { - x = startx; - y += size; - } - else - { - gfx_quads_setsubset( - (c%16)/16.0f, - (c/16)/16.0f, - (c%16)/16.0f+1.0f/16.0f, - (c/16)/16.0f+1.0f/16.0f); - - gfx_quads_drawTL(x,y,size,size); - x += size/2; - } - } - - gfx_quads_end(); -} - -static int word_length(const char *text) -{ - int s = 1; - while(1) - { - if(*text == 0) - return s-1; - if(*text == '\n' || *text == '\t' || *text == ' ') - return s; - text++; - s++; - } -} - - - -static float pretty_r=1; -static float pretty_g=1; -static float pretty_b=1; -static float pretty_a=1; - -void gfx_pretty_text_color(float r, float g, float b, float a) -{ - pretty_r = r; - pretty_g = g; - pretty_b = b; - pretty_a = a; -} - -float gfx_pretty_text_raw(float x, float y, float size, const char *text_, int length) -{ - const unsigned char *text = (unsigned char *)text_; - const float spacing = 0.05f; - gfx_texture_set(current_font->font_texture); - gfx_quads_begin(); - gfx_setcolor(pretty_r, pretty_g, pretty_b, pretty_a); - - if(length < 0) - length = strlen(text_); - - while(length) - { - const int c = *text; - const float width = current_font->m_CharEndTable[c] - current_font->m_CharStartTable[c]; - double x_nudge = 0; - - text++; - - x -= size * current_font->m_CharStartTable[c]; - - gfx_quads_setsubset( - (c%16)/16.0f, /* startx */ - (c/16)/16.0f, /* starty */ - (c%16)/16.0f+1.0f/16.0f, /* endx */ - (c/16)/16.0f+1.0f/16.0f); /* endy */ - - gfx_quads_drawTL(x, y, size, size); - - if(length > 1 && text[1]) - x_nudge = extra_kerning[text[0] + text[1] * 256]; - - x += (width + current_font->m_CharStartTable[c] + spacing + x_nudge) * size; - length--; - } - - gfx_quads_end(); - - return x; -} - - - -void gfx_pretty_text(float x, float y, float size, const char *text, int max_width) -{ - if(max_width == -1) - gfx_pretty_text_raw(x, y, size, text, -1); - else - { - float startx = x; - while(*text) - { - int wlen = word_length(text); - float w = gfx_pretty_text_width(size, text, wlen); - if(x+w-startx > max_width) - { - y += size-2; - x = startx; - } - - x = gfx_pretty_text_raw(x, y, size, text, wlen); - - text += wlen; - } - } -} - -float gfx_pretty_text_width(float size, const char *text_, int length) -{ - const float spacing = 0.05f; - float w = 0.0f; - const unsigned char *text = (unsigned char *)text_; - const unsigned char *stop; - - if (length == -1) - stop = text + strlen((char*)text); - else - stop = text + length; - - while (text < stop) - { - const int c = *text; - const float width = current_font->m_CharEndTable[c] - current_font->m_CharStartTable[c]; - - double x_nudge = 0; - if (text[1]) - x_nudge = extra_kerning[text[0] + text[1] * 256]; - - w += (width + spacing + x_nudge) * size; - - text++; - } - - return w; -} - - - -void gfx_lines_begin() -{ - dbg_assert(drawing == 0, "called begin twice"); - drawing = DRAWING_LINES; - gfx_setcolor(1,1,1,1); -} - -void gfx_lines_end() -{ - dbg_assert(drawing == DRAWING_LINES, "called end without begin"); - flush(); - drawing = 0; -} - -void gfx_lines_draw(float x0, float y0, float x1, float y1) -{ - dbg_assert(drawing == DRAWING_LINES, "called draw without begin"); - - vertices[num_vertices].pos.x = x0; - vertices[num_vertices].pos.y = y0; - vertices[num_vertices].tex = texture[0]; - vertices[num_vertices].color = color[0]; - - vertices[num_vertices + 1].pos.x = x1; - vertices[num_vertices + 1].pos.y = y1; - vertices[num_vertices + 1].tex = texture[1]; - vertices[num_vertices + 1].color = color[1]; - - draw_line(); -} diff --git a/src/engine/client/inp.c b/src/engine/client/inp.c deleted file mode 100644 index 7ffb7f3f..00000000 --- a/src/engine/client/inp.c +++ /dev/null @@ -1,210 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include -#include - -#include -#include -#include - -static int keyboard_state[2][1024]; /* TODO: fix this!! */ -static int keyboard_current = 0; -static int keyboard_first = 1; - - -static struct -{ - unsigned char presses; - unsigned char releases; -} input_count[2][1024] = {{{0}}, {{0}}}; - -static unsigned char input_state[2][1024] = {{0}, {0}}; - -static int input_current = 0; -static unsigned int last_release = 0; -static unsigned int release_delta = -1; - -void inp_mouse_relative(int *x, int *y) -{ - static int last_x = 0, last_y = 0; - static int last_sens = 100.0f; - int nx, ny; - float sens = config.inp_mousesens/100.0f; - - if(last_sens != config.inp_mousesens) - { - last_x = (last_x/(float)last_sens)*(float)config.inp_mousesens; - last_y = (last_y/(float)last_sens)*(float)config.inp_mousesens; - last_sens = config.inp_mousesens; - } - - - glfwGetMousePos(&nx, &ny); - nx *= sens; - ny *= sens; - - *x = nx-last_x; - *y = ny-last_y; - last_x = nx; - last_y = ny; -} - -static char last_c = 0; -static int last_k = 0; - -static void char_callback(int character, int action) -{ - if(action == GLFW_PRESS && character < 256) - last_c = (char)character; -} - -static void key_callback(int key, int action) -{ - if(action == GLFW_PRESS) - last_k = key; - - if(action == GLFW_PRESS) - input_count[input_current^1][key].presses++; - if(action == GLFW_RELEASE) - input_count[input_current^1][key].releases++; - input_state[input_current^1][key] = action; -} - -static void mousebutton_callback(int button, int action) -{ - if(action == GLFW_PRESS) - last_k = KEY_MOUSE_FIRST+button; - - if(action == GLFW_PRESS) - input_count[input_current^1][KEY_MOUSE_FIRST+button].presses++; - if(action == GLFW_RELEASE) - { - if(button == 0) - { - release_delta = time_get() - last_release; - last_release = time_get(); - } - input_count[input_current^1][KEY_MOUSE_FIRST+button].releases++; - } - input_state[input_current^1][KEY_MOUSE_FIRST+button] = action; -} - - -static void mousewheel_callback(int pos) -{ - if(pos > 0) - { - while(pos-- != 0) - { - input_count[input_current^1][KEY_MOUSE_WHEEL_UP].presses++; - input_count[input_current^1][KEY_MOUSE_WHEEL_UP].releases++; - } - } - else if(pos < 0) - { - while(pos++ != 0) - { - input_count[input_current^1][KEY_MOUSE_WHEEL_DOWN].presses++; - input_count[input_current^1][KEY_MOUSE_WHEEL_DOWN].releases++; - } - } - glfwSetMouseWheel(0); -} - - -void inp_init() -{ - glfwEnable(GLFW_KEY_REPEAT); - glfwSetCharCallback(char_callback); - glfwSetKeyCallback(key_callback); - glfwSetMouseButtonCallback(mousebutton_callback); - glfwSetMouseWheelCallback(mousewheel_callback); -} - -char inp_last_char() -{ - return last_c; -} - -int inp_last_key() -{ - return last_k; -} - -void inp_clear() -{ - last_k = 0; - last_c = 0; -} - -void inp_mouse_mode_absolute() -{ - glfwEnable(GLFW_MOUSE_CURSOR); -} - -void inp_mouse_mode_relative() -{ - glfwDisable(GLFW_MOUSE_CURSOR); -} - -int inp_mouse_doubleclick() -{ - return release_delta < (time_freq() >> 2); -} - -int inp_key_presses(int key) -{ - return input_count[input_current][key].presses; -} - -int inp_key_releases(int key) -{ - return input_count[input_current][key].releases; -} - -int inp_key_state(int key) -{ - return input_state[input_current][key]; -} - -int inp_key_pressed(int key) { return keyboard_state[keyboard_current][key]; } -int inp_key_was_pressed(int key) { return keyboard_state[keyboard_current^1][key]; } -int inp_key_down(int key) { return inp_key_pressed(key)&&!inp_key_was_pressed(key); } -int inp_button_pressed(int button) { return keyboard_state[keyboard_current][button]; } - -void inp_update() -{ - int i, v; - - /* clear and begin count on the other one */ - mem_zero(&input_count[input_current], sizeof(input_count[input_current])); - memcpy(input_state[input_current], input_state[input_current^1], sizeof(input_state[input_current])); - input_current^=1; - - if(keyboard_first) - { - /* make sure to reset */ - keyboard_first = 0; - inp_update(); - } - - keyboard_current = keyboard_current^1; - for(i = 0; i < KEY_LAST; i++) - { - if (i >= KEY_MOUSE_FIRST) - v = glfwGetMouseButton(i-KEY_MOUSE_FIRST) == GLFW_PRESS ? 1 : 0; - else - v = glfwGetKey(i) == GLFW_PRESS ? 1 : 0; - keyboard_state[keyboard_current][i] = v; - } - - /* handle mouse wheel */ - /* - i = glfwGetMouseWheel(); - keyboard_state[keyboard_current][KEY_MOUSE_WHEEL_UP] = 0; - keyboard_state[keyboard_current][KEY_MOUSE_WHEEL_DOWN] = 0; - if(w > 0) - keyboard_state[keyboard_current][KEY_MOUSE_WHEEL_UP] = 1; - if(w < 0) - keyboard_state[keyboard_current][KEY_MOUSE_WHEEL_DOWN] = 1; - glfwSetMouseWheel(0);*/ -} diff --git a/src/engine/client/snd.c b/src/engine/client/snd.c deleted file mode 100644 index aa155c0c..00000000 --- a/src/engine/client/snd.c +++ /dev/null @@ -1,441 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include -#include -#include - -#include -#include -#include -#include -#include - -enum -{ - NUM_SAMPLES = 512, - NUM_VOICES = 64, - NUM_CHANNELS = 16, - - MAX_FRAMES = 1024 -}; - -typedef struct -{ - short *data; - int num_frames; - int rate; - int channels; - int loop_start; - int loop_end; -} SAMPLE; - -typedef struct -{ - int vol; - int pan; -} CHANNEL; - -typedef struct VOICE_t -{ - SAMPLE *snd; - CHANNEL *channel; - int tick; - int vol; /* 0 - 255 */ - int flags; - int x, y; -} VOICE; - -static SAMPLE samples[NUM_SAMPLES] = { {0} }; -static VOICE voices[NUM_VOICES] = { {0} }; -static CHANNEL channels[NUM_CHANNELS] = { {255, 0} }; - -static LOCK sound_lock = 0; -static int sound_enabled = 0; - -static int center_x = 0; -static int center_y = 0; - -static int mixing_rate = 48000; - -void snd_set_channel(int cid, float vol, float pan) -{ - channels[cid].vol = (int)(vol*255.0f); - channels[cid].pan = (int)(pan*255.0f); /* TODO: this is only on and off right now */ -} - -static int play(int cid, int sid, int flags, float x, float y) -{ - int vid = -1; - int i; - - lock_wait(sound_lock); - - /* search for voice */ - /* TODO: fix this linear search */ - for(i = 0; i < NUM_VOICES; i++) - { - if(!voices[i].snd) - { - vid = i; - break; - } - } - - /* voice found, use it */ - if(vid != -1) - { - voices[vid].snd = &samples[sid]; - voices[vid].channel = &channels[cid]; - voices[vid].tick = 0; - voices[vid].vol = 255; - voices[vid].flags = flags; - voices[vid].x = (int)x; - voices[vid].y = (int)y; - } - - lock_release(sound_lock); - return vid; -} - -int snd_play_at(int cid, int sid, int flags, float x, float y) -{ - return play(cid, sid, flags|SNDFLAG_POS, x, y); -} - -int snd_play(int cid, int sid, int flags) -{ - return play(cid, sid, flags, 0, 0); -} - -void snd_stop(int vid) -{ - /* TODO: a nice fade out */ - lock_wait(sound_lock); - voices[vid].snd = 0; - lock_release(sound_lock); -} - -/* TODO: there should be a faster way todo this */ -static short int2short(int i) -{ - if(i > 0x7fff) - return 0x7fff; - else if(i < -0x7fff) - return -0x7fff; - return i; -} - -static int iabs(int i) -{ - if(i<0) - return -i; - return i; -} - -static void mix(short *final_out, unsigned frames) -{ - int mix_buffer[MAX_FRAMES*2] = {0}; - int i, s; - - /* aquire lock while we are mixing */ - lock_wait(sound_lock); - - for(i = 0; i < NUM_VOICES; i++) - { - if(voices[i].snd) - { - /* mix voice */ - VOICE *v = &voices[i]; - int *out = mix_buffer; - - int step = v->snd->channels; /* setup input sources */ - short *in_l = &v->snd->data[v->tick*step]; - short *in_r = &v->snd->data[v->tick*step+1]; - - int end = v->snd->num_frames-v->tick; - - int rvol = v->channel->vol; - int lvol = v->channel->vol; - - /* make sure that we don't go outside the sound data */ - if(frames < end) - end = frames; - - /* check if we have a mono sound */ - if(v->snd->channels == 1) - in_r = in_l; - - /* volume calculation */ - if(v->flags&SNDFLAG_POS && v->channel->pan) - { - /* TODO: we should respect the channel panning value */ - const int range = 1500; /* magic value, remove */ - int dx = v->x - center_x; - int dy = v->y - center_y; - int dist = sqrt(dx*dx+dy*dy); /* double here. nasty */ - int p = iabs(dx); - if(dist < range) - { - /* panning */ - if(dx > 0) - lvol = ((range-p)*lvol)/range; - else - rvol = ((range-p)*rvol)/range; - - /* falloff */ - lvol = (lvol*(range-dist))/range; - rvol = (rvol*(range-dist))/range; - } - else - { - lvol = 0; - rvol = 0; - } - } - - /* process all frames */ - for(s = 0; s < end; s++) - { - *out++ += (*in_l)*lvol; - *out++ += (*in_r)*rvol; - in_l += step; - in_r += step; - v->tick++; - } - - /* free voice if not used any more */ - if(v->tick == v->snd->num_frames) - v->snd = 0; - - } - } - - /* release the lock */ - lock_release(sound_lock); - - { - int master_vol = config.snd_volume; - - /* clamp accumulated values */ - /* TODO: this seams slow */ - for(i = 0; i < frames; i++) - { - int j = i<<1; - int vl = ((mix_buffer[j]*master_vol)/101)>>8; - int vr = ((mix_buffer[j+1]*master_vol)/101)>>8; - - final_out[j] = int2short(vl); - final_out[j+1] = int2short(vr); - } - } -} - -static int pacallback(const void *in, void *out, unsigned long frames, const PaStreamCallbackTimeInfo* time, PaStreamCallbackFlags status, void *user) -{ - mix(out, frames); - return 0; -} - -static PaStream *stream; - -int snd_init() -{ - PaStreamParameters params; - PaError err = Pa_Initialize(); - - sound_lock = lock_create(); - - if(!config.snd_enable) - return 0; - - mixing_rate = config.snd_rate; - - params.device = Pa_GetDefaultOutputDevice(); - if(params.device < 0) - return 1; - params.channelCount = 2; - params.sampleFormat = paInt16; - params.suggestedLatency = Pa_GetDeviceInfo(params.device)->defaultLowOutputLatency; - params.hostApiSpecificStreamInfo = 0x0; - - err = Pa_OpenStream( - &stream, /* passes back stream pointer */ - 0, /* no input channels */ - ¶ms, /* pointer to parameters */ - mixing_rate, /* sample rate */ - 128, /* frames per buffer */ - paClipOff, /* no clamping */ - pacallback, /* specify our custom callback */ - 0x0); /* pass our data through to callback */ - err = Pa_StartStream(stream); - - sound_enabled = 1; - return 0; -} - -int snd_shutdown() -{ - Pa_StopStream(stream); - Pa_Terminate(); - - lock_destroy(sound_lock); - - return 0; -} - -int snd_alloc_id() -{ - /* TODO: linear search, get rid of it */ - unsigned sid; - for(sid = 0; sid < NUM_SAMPLES; sid++) - { - if(samples[sid].data == 0x0) - return sid; - } - - return -1; -} - -static void rate_convert(int sid) -{ - SAMPLE *snd = &samples[sid]; - int num_frames = 0; - short *new_data = 0; - int i; - - /* make sure that we need to convert this sound */ - if(!snd->data || snd->rate == mixing_rate) - return; - - /* allocate new data */ - num_frames = (int)((snd->num_frames/(float)snd->rate)*mixing_rate); - new_data = mem_alloc(num_frames*snd->channels*sizeof(short), 1); - - for(i = 0; i < num_frames; i++) - { - /* resample TODO: this should be done better, like linear atleast */ - float a = i/(float)num_frames; - int f = (int)(a*snd->num_frames); - if(f >= snd->num_frames) - f = snd->num_frames-1; - - /* set new data */ - if(snd->channels == 1) - new_data[i] = snd->data[f]; - else if(snd->channels == 2) - { - new_data[i*2] = snd->data[f*2]; - new_data[i*2+1] = snd->data[f*2+1]; - } - } - - /* free old data and apply new */ - mem_free(snd->data); - snd->data = new_data; - snd->num_frames = num_frames; -} - - -static FILE *file = NULL; - -static int read_data(void *buffer, int size) -{ - return fread(buffer, 1, size, file); -} - -int snd_load_wv(const char *filename) -{ - SAMPLE *snd; - int sid = -1; - char error[100]; - WavpackContext *context; - - /* don't waste memory on sound when we are stress testing */ - if(config.dbg_stress) - return -1; - - /* no need to load sound when we are running with no sound */ - if(!sound_enabled) - return 1; - - file = fopen(filename, "rb"); /* TODO: use system.h stuff for this */ - if(!file) - { - dbg_msg("sound/wv", "failed to open %s", filename); - return -1; - } - - sid = snd_alloc_id(); - if(sid < 0) - return -1; - snd = &samples[sid]; - - context = WavpackOpenFileInput(read_data, error); - if (context) - { - int samples = WavpackGetNumSamples(context); - int bitspersample = WavpackGetBitsPerSample(context); - unsigned int samplerate = WavpackGetSampleRate(context); - int channels = WavpackGetNumChannels(context); - int *data; - int *src; - short *dst; - int i; - - snd->channels = channels; - snd->rate = samplerate; - - if(snd->channels > 2) - { - dbg_msg("sound/wv", "file is not mono or stereo. filename='%s'", filename); - return -1; - } - - /* - if(snd->rate != 44100) - { - dbg_msg("sound/wv", "file is %d Hz, not 44100 Hz. filename='%s'", snd->rate, filename); - return -1; - }*/ - - if(bitspersample != 16) - { - dbg_msg("sound/wv", "bps is %d, not 16, filname='%s'", bitspersample, filename); - return -1; - } - - data = (int *)mem_alloc(4*samples*channels, 1); - WavpackUnpackSamples(context, data, samples); /* TODO: check return value */ - src = data; - - snd->data = (short *)mem_alloc(2*samples*channels, 1); - dst = snd->data; - - for (i = 0; i < samples*channels; i++) - *dst++ = (short)*src++; - - mem_free(data); - - snd->num_frames = samples; - snd->loop_start = -1; - snd->loop_end = -1; - } - else - { - dbg_msg("sound/wv", "failed to open %s: %s", filename, error); - } - - fclose(file); - file = NULL; - - if(config.debug) - dbg_msg("sound/wv", "loaded %s", filename); - - rate_convert(sid); - return sid; -} - -void snd_set_listener_pos(float x, float y) -{ - center_x = (int)x; - center_y = (int)y; -} diff --git a/src/engine/client/srvbrowse.c b/src/engine/client/srvbrowse.c deleted file mode 100644 index 9fbbc2a0..00000000 --- a/src/engine/client/srvbrowse.c +++ /dev/null @@ -1,404 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include -#include -#include -#include -#include - -#include - -#include -#include - -extern NETCLIENT *net; - - -/* ------ server browse ---- */ -/* TODO: move all this to a separate file */ - -typedef struct SERVERENTRY_t SERVERENTRY; -struct SERVERENTRY_t -{ - NETADDR4 addr; - int64 request_time; - int got_info; - SERVER_INFO info; - - SERVERENTRY *next_ip; /* ip hashed list */ - - SERVERENTRY *prev_req; /* request list */ - SERVERENTRY *next_req; -}; - -static HEAP *serverlist_heap = 0; -static SERVERENTRY **serverlist = 0; -static int *sorted_serverlist = 0; - -static SERVERENTRY *serverlist_ip[256] = {0}; /* ip hash list */ - -static SERVERENTRY *first_req_server = 0; /* request list */ -static SERVERENTRY *last_req_server = 0; -static int num_requests = 0; - -static int num_sorted_servers = 0; -static int num_sorted_servers_capacity = 0; -static int num_servers = 0; -static int num_server_capacity = 0; - -static int sorthash = 0; -static char filterstring[64] = {0}; - -static int serverlist_lan = 1; - -int client_serverbrowse_num() { return num_servers; } - -SERVER_INFO *client_serverbrowse_get(int index) -{ - if(index < 0 || index >= num_servers) - return 0; - return &serverlist[index]->info; -} - -int client_serverbrowse_sorted_num() { return num_sorted_servers; } - -SERVER_INFO *client_serverbrowse_sorted_get(int index) -{ - if(index < 0 || index >= num_sorted_servers) - return 0; - return &serverlist[sorted_serverlist[index]]->info; -} - - -int client_serverbrowse_num_requests() -{ - return num_requests; -} - -static int client_serverbrowse_sort_compare_name(const void *ai, const void *bi) -{ - SERVERENTRY *a = serverlist[*(const int*)ai]; - SERVERENTRY *b = serverlist[*(const int*)bi]; - return strcmp(a->info.name, b->info.name); -} - -static int client_serverbrowse_sort_compare_map(const void *ai, const void *bi) -{ - SERVERENTRY *a = serverlist[*(const int*)ai]; - SERVERENTRY *b = serverlist[*(const int*)bi]; - return strcmp(a->info.map, b->info.map); -} - -static int client_serverbrowse_sort_compare_ping(const void *ai, const void *bi) -{ - SERVERENTRY *a = serverlist[*(const int*)ai]; - SERVERENTRY *b = serverlist[*(const int*)bi]; - return a->info.latency > b->info.latency; -} - -static int client_serverbrowse_sort_compare_gametype(const void *ai, const void *bi) -{ - SERVERENTRY *a = serverlist[*(const int*)ai]; - SERVERENTRY *b = serverlist[*(const int*)bi]; - return a->info.game_type > b->info.game_type; -} - -static int client_serverbrowse_sort_compare_progression(const void *ai, const void *bi) -{ - SERVERENTRY *a = serverlist[*(const int*)ai]; - SERVERENTRY *b = serverlist[*(const int*)bi]; - return a->info.progression > b->info.progression; -} - -static int client_serverbrowse_sort_compare_numplayers(const void *ai, const void *bi) -{ - SERVERENTRY *a = serverlist[*(const int*)ai]; - SERVERENTRY *b = serverlist[*(const int*)bi]; - return a->info.num_players > b->info.num_players; -} - -static void client_serverbrowse_filter() -{ - int i = 0; - num_sorted_servers = 0; - - /* allocate the sorted list */ - if(num_sorted_servers_capacity < num_servers) - { - if(sorted_serverlist) - mem_free(sorted_serverlist); - num_sorted_servers_capacity = num_servers; - sorted_serverlist = mem_alloc(num_sorted_servers_capacity*sizeof(int), 1); - } - - /* filter the servers */ - for(i = 0; i < num_servers; i++) - { - int filtered = 0; - - if(config.b_filter_empty && serverlist[i]->info.num_players == 0) - filtered = 1; - else if(config.b_filter_full && serverlist[i]->info.num_players == serverlist[i]->info.max_players) - filtered = 1; - else if(config.b_filter_pw && serverlist[i]->info.flags&1) - filtered = 1; - else if(config.b_filter_string[0] != 0) - { - if(strstr(serverlist[i]->info.name, config.b_filter_string) == 0) - filtered = 1; - } - - if(filtered == 0) - sorted_serverlist[num_sorted_servers++] = i; - } -} - -static int client_serverbrowse_sorthash() -{ - int i = config.b_sort&0xf; - i |= config.b_filter_empty<<4; - i |= config.b_filter_full<<5; - i |= config.b_filter_pw<<6; - return i; -} - -static void client_serverbrowse_sort() -{ - int i; - - /* create filtered list */ - client_serverbrowse_filter(); - - /* sort */ - if(config.b_sort == BROWSESORT_NAME) - qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_name); - else if(config.b_sort == BROWSESORT_PING) - qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_ping); - else if(config.b_sort == BROWSESORT_MAP) - qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_map); - else if(config.b_sort == BROWSESORT_NUMPLAYERS) - qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_numplayers); - else if(config.b_sort == BROWSESORT_GAMETYPE) - qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_gametype); - else if(config.b_sort == BROWSESORT_PROGRESSION) - qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_progression); - - /* set indexes */ - for(i = 0; i < num_sorted_servers; i++) - serverlist[sorted_serverlist[i]]->info.sorted_index = i; - - strncpy(filterstring, config.b_filter_string, sizeof(filterstring)-1); - sorthash = client_serverbrowse_sorthash(); -} - -static void client_serverbrowse_remove_request(SERVERENTRY *entry) -{ - if(entry->prev_req || entry->next_req || first_req_server == entry) - { - if(entry->prev_req) - entry->prev_req->next_req = entry->next_req; - else - first_req_server = entry->next_req; - - if(entry->next_req) - entry->next_req->prev_req = entry->prev_req; - else - last_req_server = entry->prev_req; - - entry->prev_req = 0; - entry->next_req = 0; - num_requests--; - } -} - -void client_serverbrowse_set(NETADDR4 *addr, int request, SERVER_INFO *info) -{ - int hash = addr->ip[0]; - SERVERENTRY *entry = serverlist_ip[hash]; - while(entry) - { - if(net_addr4_cmp(&entry->addr, addr) == 0) - { - /* update the server that we already have */ - entry->info = *info; - if(!request) - { - entry->info.latency = (time_get()-entry->request_time)*1000/time_freq(); - client_serverbrowse_remove_request(entry); - } - - entry->got_info = 1; - client_serverbrowse_sort(); - return; - } - entry = entry->next_ip; - } - - /* create new entry */ - entry = (SERVERENTRY *)memheap_allocate(serverlist_heap, sizeof(SERVERENTRY)); - mem_zero(entry, sizeof(SERVERENTRY)); - - /* set the info */ - entry->addr = *addr; - entry->info = *info; - - /* add to the hash list */ - entry->next_ip = serverlist_ip[hash]; - serverlist_ip[hash] = entry; - - if(num_servers == num_server_capacity) - { - SERVERENTRY **newlist; - num_server_capacity += 100; - newlist = mem_alloc(num_server_capacity*sizeof(SERVERENTRY*), 1); - memcpy(newlist, serverlist, num_servers*sizeof(SERVERENTRY*)); - mem_free(serverlist); - serverlist = newlist; - } - - /* add to list */ - serverlist[num_servers] = entry; - entry->info.server_index = num_servers; - num_servers++; - - /* */ - if(request) - { - /* add it to the list of servers that we should request info from */ - entry->prev_req = last_req_server; - if(last_req_server) - last_req_server->next_req = entry; - else - first_req_server = entry; - last_req_server = entry; - - num_requests++; - } - - client_serverbrowse_sort(); -} - -void client_serverbrowse_refresh(int lan) -{ - /* clear out everything */ - if(serverlist_heap) - memheap_destroy(serverlist_heap); - serverlist_heap = memheap_create(); - num_servers = 0; - num_sorted_servers = 0; - mem_zero(serverlist_ip, sizeof(serverlist_ip)); - first_req_server = 0; - last_req_server = 0; - num_requests = 0; - - - /* */ - serverlist_lan = lan; - - if(serverlist_lan) - { - NETPACKET packet; - packet.client_id = -1; - mem_zero(&packet, sizeof(packet)); - packet.address.ip[0] = 255; - packet.address.ip[1] = 255; - packet.address.ip[2] = 255; - packet.address.ip[3] = 255; - packet.address.port = 8303; - packet.flags = PACKETFLAG_CONNLESS; - packet.data_size = sizeof(SERVERBROWSE_GETINFO); - packet.data = SERVERBROWSE_GETINFO; - netclient_send(net, &packet); - - if(config.debug) - dbg_msg("client", "broadcasting for servers"); - } - else - { - NETADDR4 master_server; - NETPACKET p; - - net_host_lookup(config.masterserver, MASTERSERVER_PORT, &master_server); - - mem_zero(&p, sizeof(p)); - p.client_id = -1; - p.address = master_server; - p.flags = PACKETFLAG_CONNLESS; - p.data_size = sizeof(SERVERBROWSE_GETLIST); - p.data = SERVERBROWSE_GETLIST; - netclient_send(net, &p); - - if(config.debug) - dbg_msg("client", "requesting server list"); - } -} - -static void client_serverbrowse_request(SERVERENTRY *entry) -{ - NETPACKET p; - - if(config.debug) - { - dbg_msg("client", "requesting server info from %d.%d.%d.%d:%d", - entry->addr.ip[0], entry->addr.ip[1], entry->addr.ip[2], - entry->addr.ip[3], entry->addr.port); - } - - p.client_id = -1; - p.address = entry->addr; - p.flags = PACKETFLAG_CONNLESS; - p.data_size = sizeof(SERVERBROWSE_GETINFO); - p.data = SERVERBROWSE_GETINFO; - netclient_send(net, &p); - entry->request_time = time_get(); -} - -void client_serverbrowse_update() -{ - int64 timeout = time_freq(); - int64 now = time_get(); - int count; - SERVERENTRY *entry, *next; - - /* do timeouts */ - entry = first_req_server; - while(1) - { - if(!entry) /* no more entries */ - break; - - next = entry->next_req; - - if(entry->request_time && entry->request_time+timeout < now) - { - /* timeout */ - client_serverbrowse_remove_request(entry); - num_requests--; - } - - entry = next; - } - - /* do timeouts */ - entry = first_req_server; - count = 0; - while(1) - { - if(!entry) /* no more entries */ - break; - - /* no more then 10 concurrent requests */ - if(count == config.b_max_requests) - break; - - if(entry->request_time == 0) - client_serverbrowse_request(entry); - - count++; - entry = entry->next_req; - } - - /* check if we need to resort */ - /* TODO: remove the strcmp */ - if(sorthash != client_serverbrowse_sorthash() || strcmp(filterstring, config.b_filter_string) != 0) - client_serverbrowse_sort(); -} diff --git a/src/engine/client/ui.c b/src/engine/client/ui.c deleted file mode 100644 index 9486b2c1..00000000 --- a/src/engine/client/ui.c +++ /dev/null @@ -1,277 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include -#include -#include -#include "ui.h" - -/******************************************************** - UI -*********************************************************/ - -struct pretty_font -{ - float m_CharStartTable[256]; - float m_CharEndTable[256]; - int font_texture; -}; - -extern struct pretty_font *current_font; - -static const void *hot_item = 0; -static const void *active_item = 0; -static const void *last_active_item = 0; -static const void *becomming_hot_item = 0; -static float mouse_x, mouse_y; /* in gui space */ -static float mouse_wx, mouse_wy; /* in world space */ -static unsigned mouse_buttons = 0; - -float ui_mouse_x() { return mouse_x; } -float ui_mouse_y() { return mouse_y; } -float ui_mouse_world_x() { return mouse_wx; } -float ui_mouse_world_y() { return mouse_wy; } -int ui_mouse_button(int index) { return (mouse_buttons>>index)&1; } - -void ui_set_hot_item(const void *id) { becomming_hot_item = id; } -void ui_set_active_item(const void *id) { active_item = id; if (id) last_active_item = id; } -void ui_clear_last_active_item() { last_active_item = 0; } -const void *ui_hot_item() { return hot_item; } -const void *ui_active_item() { return active_item; } -const void *ui_last_active_item() { return last_active_item; } - -int ui_update(float mx, float my, float mwx, float mwy, int buttons) -{ - mouse_x = mx; - mouse_y = my; - mouse_wx = mwx; - mouse_wy = mwy; - mouse_buttons = buttons; - hot_item = becomming_hot_item; - if(active_item) - hot_item = active_item; - becomming_hot_item = 0; - return 0; -} - -int ui_mouse_inside(float x, float y, float w, float h) -{ - if(mouse_x >= x && mouse_x <= x+w && mouse_y >= y && mouse_y <= y+h) - return 1; - return 0; -} - -void ui_do_image(int texture, float x, float y, float w, float h) -{ - gfx_blend_normal(); - gfx_texture_set(texture); - gfx_quads_begin(); - gfx_setcolor(1,1,1,1); - gfx_quads_setsubset( - 0.0f, /* startx */ - 0.0f, /* starty */ - 1.0f, /* endx */ - 1.0f); /* endy */ - gfx_quads_drawTL(x,y,w,h); - gfx_quads_end(); -} - -void ui_do_label(float x, float y, const char *text, float size) -{ - gfx_blend_normal(); - gfx_texture_set(current_font->font_texture); - gfx_pretty_text(x, y, size, text, -1); -} - -int ui_do_button(const void *id, const char *text, int checked, float x, float y, float w, float h, draw_button_callback draw_func, void *extra) -{ - /* logic */ - int r = 0; - int inside = ui_mouse_inside(x,y,w,h); - - if(ui_active_item() == id) - { - if(!ui_mouse_button(0)) - { - if(inside) - r = 1; - ui_set_active_item(0); - } - } - else if(ui_hot_item() == id) - { - if(ui_mouse_button(0)) - ui_set_active_item(id); - } - - if(inside) - ui_set_hot_item(id); - - draw_func(id, text, checked, x, y, w, h, extra); - return r; -} - -static float scale = 1.0f; -#define MEMORY_SIZE 10*1024 -static struct rect memory[MEMORY_SIZE]; -static int memory_used = 0; -static struct rect screen = { 0.0f, 0.0f, 848.0f, 480.0f }; - -void ui_foreach_rect(rect_fun fun) -{ - int hrm; - for (hrm = 0; hrm < memory_used; hrm++) - fun(&memory[hrm]); -} - -static void add_rect(struct rect *r) -{ - if (memory_used < MEMORY_SIZE) - memory[memory_used++] = *r; - else - dbg_msg("ui", "rect memory full."); -} - -struct rect *ui_screen() -{ - if (config.debug) - { - memory_used = 0; - } - - return &screen; -} - -void ui_scale(float s) -{ - scale = s; -} - -void ui_hsplit_t(const struct rect *original, int pixels, struct rect *top, struct rect *bottom) -{ - struct rect r = *original; - pixels *= scale; - - if (top) - { - top->x = r.x; - top->y = r.y; - top->w = r.w; - top->h = pixels; - } - - if (bottom) - { - bottom->x = r.x; - bottom->y = r.y + pixels; - bottom->w = r.w; - bottom->h = r.h - pixels; - } - - if (config.debug) - { - if (top) - add_rect(top); - if (bottom) - add_rect(bottom); - } -} - -void ui_hsplit_b(const struct rect *original, int pixels, struct rect *top, struct rect *bottom) -{ - struct rect r = *original; - pixels *= scale; - - if (top) - { - top->x = r.x; - top->y = r.y; - top->w = r.w; - top->h = r.h - pixels; - } - - if (bottom) - { - bottom->x = r.x; - bottom->y = r.y + r.h - pixels; - bottom->w = r.w; - bottom->h = pixels; - } - - if (config.debug) - { - if (top) - add_rect(top); - if (bottom) - add_rect(bottom); - } -} - -void ui_vsplit_l(const struct rect *original, int pixels, struct rect *left, struct rect *right) -{ - struct rect r = *original; - pixels *= scale; - - if (left) - { - left->x = r.x; - left->y = r.y; - left->w = pixels; - left->h = r.h; - } - - if (right) - { - right->x = r.x + pixels; - right->y = r.y; - right->w = r.w - pixels; - right->h = r.h; - } - - if (config.debug) - { - if (left) - add_rect(left); - if (right) - add_rect(right); - } -} - -void ui_vsplit_r(const struct rect *original, int pixels, struct rect *left, struct rect *right) -{ - struct rect r = *original; - pixels *= scale; - - if (left) - { - left->x = r.x; - left->y = r.y; - left->w = r.w - pixels; - left->h = r.h; - } - - if (right) - { - right->x = r.x + r.w - pixels; - right->y = r.y; - right->w = pixels; - right->h = r.h; - } - - if (config.debug) - { - if (left) - add_rect(left); - if (right) - add_rect(right); - } -} - -void ui_margin(const struct rect *original, int pixels, struct rect *other_rect) -{ - struct rect r = *original; - pixels *= scale; - - other_rect->x = r.x + pixels; - other_rect->y = r.y + pixels; - other_rect->w = r.w - 2*pixels; - other_rect->h = r.h - 2*pixels; -} diff --git a/src/engine/client/ui.h b/src/engine/client/ui.h deleted file mode 100644 index 15c63b90..00000000 --- a/src/engine/client/ui.h +++ /dev/null @@ -1,36 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#ifndef _UI_H -#define _UI_H - -#ifdef __cplusplus -extern "C" { -#endif - -int ui_update(float mx, float my, float mwx, float mwy, int buttons); - -float ui_mouse_x(); -float ui_mouse_y(); -float ui_mouse_world_x(); -float ui_mouse_world_y(); -int ui_mouse_button(int index); - -void ui_set_hot_item(const void *id); -void ui_set_active_item(const void *id); -void ui_clear_last_active_item(); -const void *ui_hot_item(); -const void *ui_active_item(); -const void *ui_last_active_item(); - -int ui_mouse_inside(float x, float y, float w, float h); - -typedef void (*draw_button_callback)(const void *id, const char *text, int checked, float x, float y, float w, float h, void *extra); - -void ui_do_image(int texture, float x, float y, float w, float h); -void ui_do_label(float x, float y, const char *text, float size); -int ui_do_button(const void *id, const char *text, int checked, float x, float y, float w, float h, draw_button_callback draw_func, void *extra); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/engine/compression.c b/src/engine/compression.c deleted file mode 100644 index d99624c0..00000000 --- a/src/engine/compression.c +++ /dev/null @@ -1,147 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include "system.h" -#include - -/* Format: ESDDDDDD EDDDDDDD EDD... Extended, Data, Sign */ -unsigned char *vint_pack(unsigned char *dst, int i) -{ - *dst = (i>>25)&0x40; /* set sign bit if i<0 */ - i = i^(i>>31); /* if(i<0) i = ~i */ - - *dst |= i&0x3F; /* pack 6bit into dst */ - i >>= 6; /* discard 6 bits */ - if(i) - { - *dst |= 0x80; /* set extend bit */ - while(1) - { - dst++; - *dst = i&(0x7F); /* pack 7bit */ - i >>= 7; /* discard 7 bits */ - *dst |= (i!=0)<<7; /* set extend bit (may branch) */ - if(!i) - break; - } - } - - dst++; - return dst; -} - -const unsigned char *vint_unpack(const unsigned char *src, int *i) -{ - int sign = (*src>>6)&1; - *i = *src&0x3F; - - do - { - if(!(*src&0x80)) break; - src++; - *i |= (*src&(0x7F))<<(6); - - if(!(*src&0x80)) break; - src++; - *i |= (*src&(0x7F))<<(6+7); - - if(!(*src&0x80)) break; - src++; - *i |= (*src&(0x7F))<<(6+7+7); - - if(!(*src&0x80)) break; - src++; - *i |= (*src&(0x7F))<<(6+7+7+7); - } while(0); - - src++; - *i ^= -sign; /* if(sign) *i = ~(*i) */ - return src; -} - - -long intpack_decompress(const void *src_, int size, void *dst_) -{ - const unsigned char *src = (unsigned char *)src_; - const unsigned char *end = src + size; - int *dst = (int *)dst_; - while(src < end) - { - src = vint_unpack(src, dst); - dst++; - } - return (long)((unsigned char *)dst-(unsigned char *)dst_); -} - -long intpack_compress(const void *src_, int size, void *dst_) -{ - int *src = (int *)src_; - unsigned char *dst = (unsigned char *)dst_; - size /= 4; - while(size) - { - dst = vint_pack(dst, *src); - size--; - src++; - } - return (long)(dst-(unsigned char *)dst_); -} - - -/* */ -long zerobit_compress(const void *src_, int size, void *dst_) -{ - unsigned char *src = (unsigned char *)src_; - unsigned char *dst = (unsigned char *)dst_; - - while(size) - { - unsigned char bit = 0x80; - unsigned char mask = 0; - int dst_move = 1; - int chunk = size < 8 ? size : 8; - int b; - size -= chunk; - - for(b = 0; b < chunk; b++, bit>>=1) - { - if(*src) - { - dst[dst_move] = *src; - mask |= bit; - dst_move++; - } - - src++; - } - - *dst = mask; - dst += dst_move; - } - - return (long)(dst-(unsigned char *)dst_); -} - -long zerobit_decompress(const void *src_, int size, void *dst_) -{ - unsigned char *src = (unsigned char *)src_; - unsigned char *dst = (unsigned char *)dst_; - unsigned char *end = src + size; - - - while(src != end) - { - unsigned char bit = 0x80; - unsigned char mask = *src++; - int b; - - for(b = 0; b < 8; b++, bit>>=1) - { - if(mask&bit) - *dst++ = *src++; - else - *dst++ = 0; - } - } - - return (long)(dst-(unsigned char *)dst_); -} - diff --git a/src/engine/compression.h b/src/engine/compression.h deleted file mode 100644 index 39ffd596..00000000 --- a/src/engine/compression.h +++ /dev/null @@ -1,11 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -/* variable int packing */ -unsigned char *vint_pack(unsigned char *dst, int i); -const unsigned char *vint_unpack(const unsigned char *src, int *inout); -long intpack_compress(const void *src, int size, void *dst); -long intpack_decompress(const void *src, int size, void *dst); - -/* zerobit packing */ -long zerobit_compress(const void *src, int size, void *dst); -long zerobit_decompress(const void *src, int size, void *dst); diff --git a/src/engine/config.c b/src/engine/config.c deleted file mode 100644 index 3fa6f98d..00000000 --- a/src/engine/config.c +++ /dev/null @@ -1,196 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -#include -#include -#include - -#include "system.h" -#include "config.h" - - -/* buffered stream for reading lines, should perhaps be something smaller */ -typedef struct -{ - char buffer[4*1024]; - unsigned buffer_pos; - unsigned buffer_size; - unsigned buffer_max_size; - IOHANDLE io; -} LINEREADER; - -void linereader_init(LINEREADER *lr, IOHANDLE io) -{ - lr->buffer_max_size = 4*1024; - lr->buffer_size = 0; - lr->buffer_pos = 0; - lr->io = io; -} - -char *linereader_get(LINEREADER *lr) -{ - unsigned line_start = lr->buffer_pos; - - while(1) - { - if(lr->buffer_pos >= lr->buffer_size) - { - /* fetch more */ - - /* move the remaining part to the front */ - unsigned read; - unsigned left = lr->buffer_size - line_start; - - if(line_start > lr->buffer_size) - left = 0; - if(left) - mem_move(lr->buffer, &lr->buffer[line_start], left); - lr->buffer_pos = left; - - /* fill the buffer */ - read = io_read(lr->io, &lr->buffer[lr->buffer_pos], lr->buffer_max_size-lr->buffer_pos); - lr->buffer_size = left + read; - line_start = 0; - - if(!read) - { - if(left) - { - lr->buffer[left] = 0; /* return the last line */ - lr->buffer_pos = left; - lr->buffer_size = left; - return lr->buffer; - } - else - return 0x0; /* we are done! */ - } - } - else - { - if(lr->buffer[lr->buffer_pos] == '\n' || lr->buffer[lr->buffer_pos] == '\r') - { - /* line found */ - lr->buffer[lr->buffer_pos] = 0; - lr->buffer_pos++; - return &lr->buffer[line_start]; - } - else - lr->buffer_pos++; - } - } -} - -CONFIGURATION config; - -void config_reset() -{ - #define MACRO_CONFIG_INT(name,def,min,max) config.name = def; - #define MACRO_CONFIG_STR(name,len,def) strncpy(config.name, def, len); - - #include "config_variables.h" - - #undef MACRO_CONFIG_INT - #undef MACRO_CONFIG_STR -} - -void strip_spaces(char **p) -{ - char *s = *p; - char *end; - - while (*s == ' ') - ++s; - - end = s + strlen(s); - while (end > s && *(end - 1) == ' ') - *--end = 0; -} - -void config_set(const char *line) -{ - const char *c = strchr(line, '='); - if (c) - { - char var[256]; - char val[256]; - char *var_str = var; - char *val_str = val; - - strcpy(val, c+1); - - mem_copy(var, line, c - line); - var[c - line] = 0; - - - strip_spaces(&var_str); - strip_spaces(&val_str); - - #define MACRO_CONFIG_INT(name,def,min,max) { if (strcmp(#name, var_str) == 0) config_set_ ## name (&config, atoi(val_str)); } - #define MACRO_CONFIG_STR(name,len,def) { if (strcmp(#name, var_str) == 0) { config_set_ ## name (&config, val_str); } } - - #include "config_variables.h" - - #undef MACRO_CONFIG_INT - #undef MACRO_CONFIG_STR - } -} - -void config_load(const char *filename) -{ - IOHANDLE file; - dbg_msg("config/load", "loading %s", filename); - file = io_open(filename, IOFLAG_READ); - - if(file) - { - char *line; - LINEREADER lr; - linereader_init(&lr, file); - - while ((line = linereader_get(&lr))) - config_set(line); - - io_close(file); - } -} - -void config_save(const char *filename) -{ - IOHANDLE file; - dbg_msg("config/save", "saving config to %s", filename); - - file = io_open(filename, IOFLAG_WRITE); - - if(file) - { -#if defined(CONF_FAMILY_WINDOWS) - const char newline[] = "\r\n"; -#else - const char newline[] = "\n"; -#endif - const int newline_len = sizeof(newline)-1; - - #define MACRO_CONFIG_INT(name,def,min,max) { char str[256]; sprintf(str, "%s=%i%s", #name, config.name, newline); io_write(file, str, strlen(str)); } - #define MACRO_CONFIG_STR(name,len,def) { io_write(file, #name, strlen(#name)); io_write(file, "=", 1); io_write(file, config.name, strlen(config.name)); io_write(file, newline, newline_len); } - - #include "config_variables.h" - - #undef MACRO_CONFIG_INT - #undef MACRO_CONFIG_STR - - io_close(file); - } - else - dbg_msg("config/save", "couldn't open %s for writing. :(", filename); -} - -#define MACRO_CONFIG_INT(name,def,min,max) int config_get_ ## name (CONFIGURATION *c) { return c->name; } -#define MACRO_CONFIG_STR(name,len,def) const char *config_get_ ## name (CONFIGURATION *c) { return c->name; } -#include "config_variables.h" -#undef MACRO_CONFIG_INT -#undef MACRO_CONFIG_STR - -#define MACRO_CONFIG_INT(name,def,min,max) void config_set_ ## name (CONFIGURATION *c, int val) { if(min != max) { if (val < min) val = min; if (max != 0 && val > max) val = max; } c->name = val; } -#define MACRO_CONFIG_STR(name,len,def) void config_set_ ## name (CONFIGURATION *c, const char *str) { strncpy(c->name, str, len-1); c->name[sizeof(c->name)-1] = 0; } -#include "config_variables.h" -#undef MACRO_CONFIG_INT -#undef MACRO_CONFIG_STR diff --git a/src/engine/config.h b/src/engine/config.h deleted file mode 100644 index e01a7ec8..00000000 --- a/src/engine/config.h +++ /dev/null @@ -1,41 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#ifndef _CONFIG_H -#define _CONFIG_H - -#ifdef __cplusplus -extern "C"{ -#endif - -typedef struct -{ - #define MACRO_CONFIG_INT(name,def,min,max) int name; - #define MACRO_CONFIG_STR(name,len,def) char name[len]; - #include "config_variables.h" - #undef MACRO_CONFIG_INT - #undef MACRO_CONFIG_STR -} CONFIGURATION; - -extern CONFIGURATION config; - -void config_set(const char *line); -void config_reset(); -void config_load(const char *filename); -void config_save(const char *filename); - -#define MACRO_CONFIG_INT(name,def,min,max) int config_get_ ## name (CONFIGURATION *c); -#define MACRO_CONFIG_STR(name,len,def) const char *config_get_ ## name (CONFIGURATION *c); -#include "config_variables.h" -#undef MACRO_CONFIG_INT -#undef MACRO_CONFIG_STR - -#define MACRO_CONFIG_INT(name,def,min,max) void config_set_ ## name (CONFIGURATION *c, int val); -#define MACRO_CONFIG_STR(name,len,def) void config_set_ ## name (CONFIGURATION *c, const char *str); -#include "config_variables.h" -#undef MACRO_CONFIG_INT -#undef MACRO_CONFIG_STR - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/engine/config_variables.h b/src/engine/config_variables.h deleted file mode 100644 index 4a63e731..00000000 --- a/src/engine/config_variables.h +++ /dev/null @@ -1,56 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include "../game/game_variables.h" - - -MACRO_CONFIG_STR(player_name, 32, "nameless tee") -MACRO_CONFIG_STR(clan_name, 32, "") -MACRO_CONFIG_STR(password, 32, "") -MACRO_CONFIG_STR(rcon_password, 32, "") - -MACRO_CONFIG_INT(cl_cpu_throttle, 0, 0, 1) -/*MACRO_CONFIG_STR(cl_connect, 32, "")*/ -MACRO_CONFIG_INT(cl_editor, 0, 0, 1) - -MACRO_CONFIG_STR(b_filter_string, 64, "") - -MACRO_CONFIG_INT(b_filter_full, 0, 0, 1) -MACRO_CONFIG_INT(b_filter_empty, 0, 0, 1) -MACRO_CONFIG_INT(b_filter_pw, 0, 0, 1) -MACRO_CONFIG_INT(b_sort, 0, 0, 256) -MACRO_CONFIG_INT(b_max_requests, 10, 0, 1000) - -MACRO_CONFIG_INT(snd_rate, 48000, 0, 0) -MACRO_CONFIG_INT(snd_enable, 1, 0, 1) -MACRO_CONFIG_INT(snd_volume, 100, 0, 100) - -MACRO_CONFIG_INT(gfx_screen_width, 800, 0, 0) -MACRO_CONFIG_INT(gfx_screen_height, 600, 0, 0) -MACRO_CONFIG_INT(gfx_fullscreen, 1, 0, 1) -MACRO_CONFIG_INT(gfx_color_depth, 24, 16, 24) -MACRO_CONFIG_INT(gfx_vsync, 1, 0, 1) -MACRO_CONFIG_INT(gfx_display_all_modes, 0, 0, 1) -MACRO_CONFIG_INT(gfx_texture_compression, 0, 0, 1) -MACRO_CONFIG_INT(gfx_high_detail, 1, 0, 1) -MACRO_CONFIG_INT(gfx_texture_quality, 1, 0, 1) -MACRO_CONFIG_INT(gfx_fsaa_samples, 0, 0, 16) -MACRO_CONFIG_INT(gfx_refresh_rate, 0, 0, 0) - -MACRO_CONFIG_INT(key_screenshot, 267, 32, 512) -MACRO_CONFIG_INT(inp_mousesens, 100, 5, 100000) - -MACRO_CONFIG_STR(masterserver, 128, "master.teewars.com") - -MACRO_CONFIG_STR(sv_name, 128, "unnamed server") -MACRO_CONFIG_STR(sv_bindaddr, 128, "") -MACRO_CONFIG_INT(sv_port, 8303, 0, 0) -MACRO_CONFIG_INT(sv_external_port, 0, 0, 0) -MACRO_CONFIG_INT(sv_sendheartbeats, 1, 0, 1) -MACRO_CONFIG_STR(sv_map, 128, "dm1") -MACRO_CONFIG_INT(sv_map_reload, 0, 0, 1) -MACRO_CONFIG_INT(sv_max_clients, 8, 1, 12) -MACRO_CONFIG_INT(sv_bandwidth_mode, 0, 0, 2) - -MACRO_CONFIG_INT(debug, 0, 0, 1) -MACRO_CONFIG_INT(dbg_stress, 0, 0, 0) -MACRO_CONFIG_STR(dbg_stress_server, 32, "localhost") - diff --git a/src/engine/datafile.c b/src/engine/datafile.c deleted file mode 100644 index 7069044c..00000000 --- a/src/engine/datafile.c +++ /dev/null @@ -1,630 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include "system.h" -#include "datafile.h" -#include "external/zlib/zlib.h" - -static const int DEBUG=0; - -typedef struct -{ - int type; - int start; - int num; -} DATAFILE_ITEM_TYPE; - -typedef struct -{ - int type_and_id; - int size; -} DATAFILE_ITEM; - -typedef struct -{ - char id[4]; - int version; - int size; - int swaplen; - int num_item_types; - int num_items; - int num_raw_data; - int item_size; - int data_size; -} DATAFILE_HEADER; - -typedef struct -{ - int num_item_types; - int num_items; - int num_raw_data; - int item_size; - int data_size; - char start[4]; -} DATAFILE_DATA; - -typedef struct -{ - DATAFILE_ITEM_TYPE *item_types; - int *item_offsets; - int *data_offsets; - int *data_sizes; - - char *item_start; - char *data_start; -} DATAFILE_INFO; - -struct DATAFILE_t -{ - IOHANDLE file; - DATAFILE_INFO info; - DATAFILE_HEADER header; - int data_start_offset; - char **data_ptrs; - char *data; -}; - -DATAFILE *datafile_load(const char *filename) -{ - DATAFILE *df; - IOHANDLE file; - DATAFILE_HEADER header; - unsigned readsize; - - unsigned *dst; - unsigned char *src; - unsigned j; - int size = 0; - int allocsize = 0; - - (void)dst; - (void)src; - (void)j; - - - dbg_msg("datafile", "datafile loading. filename='%s'", filename); - - file = io_open(filename, IOFLAG_READ); - if(!file) - return 0; - - /* TODO: change this header */ - io_read(file, &header, sizeof(header)); - if(header.id[0] != 'A' || header.id[1] != 'T' || header.id[2] != 'A' || header.id[3] != 'D') - { - if(header.id[0] != 'D' || header.id[1] != 'A' || header.id[2] != 'T' || header.id[3] != 'A') - { - dbg_msg("datafile", "wrong signature. %x %x %x %x", header.id[0], header.id[1], header.id[2], header.id[3]); - return 0; - } - } - -#if defined(CONF_ARCH_ENDIAN_BIG) - swap_endian(&header, sizeof(int), sizeof(header)/sizeof(int)); -#endif - if(header.version != 3 && header.version != 4) - { - dbg_msg("datafile", "wrong version. version=%x", header.version); - return 0; - } - - /* read in the rest except the data */ - size = 0; - size += header.num_item_types*sizeof(DATAFILE_ITEM_TYPE); - size += (header.num_items+header.num_raw_data)*sizeof(int); - if(header.version == 4) - size += header.num_raw_data*sizeof(int); /* v4 has uncompressed data sizes aswell */ - size += header.item_size; - - allocsize = size; - allocsize += sizeof(DATAFILE); /* add space for info structure */ - allocsize += header.num_raw_data*sizeof(void*); /* add space for data pointers */ - - df = (DATAFILE*)mem_alloc(allocsize, 1); - df->header = header; - df->data_start_offset = sizeof(DATAFILE_HEADER) + size; - df->data_ptrs = (char**)(df+1); - df->data = (char *)(df+1)+header.num_raw_data*sizeof(char *); - df->file = file; - - /* clear the data pointers */ - mem_zero(df->data_ptrs, header.num_raw_data*sizeof(void*)); - - /* read types, offsets, sizes and item data */ - readsize = io_read(file, df->data, size); - if(readsize != size) - { - dbg_msg("datafile", "couldn't load the whole thing, wanted=%d got=%d", size, readsize); - return 0; - } - -#if defined(CONF_ARCH_ENDIAN_BIG) - swap_endian(df->data, sizeof(int), header.swaplen); -#endif - - if(DEBUG) - dbg_msg("datafile", "item_size=%d", df->header.item_size); - - df->info.item_types = (DATAFILE_ITEM_TYPE *)df->data; - df->info.item_offsets = (int *)&df->info.item_types[df->header.num_item_types]; - df->info.data_offsets = (int *)&df->info.item_offsets[df->header.num_items]; - df->info.data_sizes = (int *)&df->info.data_offsets[df->header.num_raw_data]; - - if(header.version == 4) - df->info.item_start = (char *)&df->info.data_sizes[df->header.num_raw_data]; - else - df->info.item_start = (char *)&df->info.data_offsets[df->header.num_raw_data]; - df->info.data_start = df->info.item_start + df->header.item_size; - - if(DEBUG) - dbg_msg("datafile", "datafile loading done. datafile='%s'", filename); - - if(DEBUG) - { - /* - for(int i = 0; i < df->data.num_raw_data; i++) - { - void *p = datafile_get_data(df, i); - dbg_msg("datafile", "%d %d", (int)((char*)p - (char*)(&df->data)), size); - } - - for(int i = 0; i < datafile_num_items(df); i++) - { - int type, id; - void *data = datafile_get_item(df, i, &type, &id); - dbg_msg("map", "\t%d: type=%x id=%x p=%p offset=%d", i, type, id, data, df->info.item_offsets[i]); - int *idata = (int*)data; - for(int k = 0; k < 3; k++) - dbg_msg("datafile", "\t\t%d=%d (%x)", k, idata[k], idata[k]); - } - - for(int i = 0; i < df->data.num_item_types; i++) - { - dbg_msg("map", "\t%d: type=%x start=%d num=%d", i, - df->info.item_types[i].type, - df->info.item_types[i].start, - df->info.item_types[i].num); - for(int k = 0; k < df->info.item_types[i].num; k++) - { - int type, id; - datafile_get_item(df, df->info.item_types[i].start+k, &type, &id); - if(type != df->info.item_types[i].type) - dbg_msg("map", "\tERROR"); - } - } - */ - } - - return df; -} - -int datafile_num_data(DATAFILE *df) -{ - return df->header.num_raw_data; -} - -/* always returns the size in the file */ -int datafile_get_datasize(DATAFILE *df, int index) -{ - if(index == df->header.num_raw_data-1) - return df->header.data_size-df->info.data_offsets[index]; - return df->info.data_offsets[index+1]-df->info.data_offsets[index]; -} - -void *datafile_get_data(DATAFILE *df, int index) -{ - /* load it if needed */ - if(!df->data_ptrs[index]) - { - /* fetch the data size */ - int datasize = datafile_get_datasize(df, index); - - if(df->header.version == 4) - { - /* v4 has compressed data */ - void *temp = (char *)mem_alloc(datasize, 1); - unsigned long uncompressed_size = df->info.data_sizes[index]; - unsigned long s; - - dbg_msg("datafile", "loading data index=%d size=%d", index, datasize); - df->data_ptrs[index] = (char *)mem_alloc(uncompressed_size, 1); - - /* read the compressed data */ - io_seek(df->file, df->data_start_offset+df->info.data_offsets[index], IOSEEK_START); - io_read(df->file, temp, datasize); - - /* decompress the data, TODO: check for errors */ - s = uncompressed_size; - uncompress((Bytef*)df->data_ptrs[index], &s, (Bytef*)temp, datasize); - - /* clean up the temporary buffers */ - mem_free(temp); - } - else - { - /* load the data */ - dbg_msg("datafile", "loading data index=%d size=%d", index, datasize); - df->data_ptrs[index] = (char *)mem_alloc(datasize, 1); - io_seek(df->file, df->data_start_offset+df->info.data_offsets[index], IOSEEK_START); - io_read(df->file, df->data_ptrs[index], datasize); - } - } - - return df->data_ptrs[index]; -} - -void datafile_unload_data(DATAFILE *df, int index) -{ - /* */ - mem_free(df->data_ptrs[index]); - df->data_ptrs[index] = 0x0; -} - -int datafile_get_itemsize(DATAFILE *df, int index) -{ - if(index == df->header.num_items-1) - return df->header.item_size-df->info.item_offsets[index]; - return df->info.item_offsets[index+1]-df->info.item_offsets[index]; -} - -void *datafile_get_item(DATAFILE *df, int index, int *type, int *id) -{ - DATAFILE_ITEM *i = (DATAFILE_ITEM *)(df->info.item_start+df->info.item_offsets[index]); - if(type) - *type = (i->type_and_id>>16)&0xffff; /* remove sign extention */ - if(id) - *id = i->type_and_id&0xffff; - return (void *)(i+1); -} - -void datafile_get_type(DATAFILE *df, int type, int *start, int *num) -{ - int i; - for(i = 0; i < df->header.num_item_types; i++) - { - if(df->info.item_types[i].type == type) - { - *start = df->info.item_types[i].start; - *num = df->info.item_types[i].num; - return; - } - } - - *start = 0; - *num = 0; -} - -void *datafile_find_item(DATAFILE *df, int type, int id) -{ - int start, num,i ; - int item_id; - void *item; - - datafile_get_type(df, type, &start, &num); - for(i = 0; i < num; i++) - { - item = datafile_get_item(df, start+i,0, &item_id); - if(id == item_id) - return item; - } - return 0; -} - -int datafile_num_items(DATAFILE *df) -{ - return df->header.num_items; -} - -void datafile_unload(DATAFILE *df) -{ - if(df) - { - /* free the data that is loaded */ - int i; - for(i = 0; i < df->header.num_raw_data; i++) - mem_free(df->data_ptrs[i]); - - io_close(df->file); - mem_free(df); - } -} - -/* DATAFILE output */ -typedef struct -{ - int uncompressed_size; - int compressed_size; - void *compressed_data; -} DATA_INFO; - -typedef struct -{ - int type; - int id; - int size; - int next; - int prev; - void *data; -} ITEM_INFO; - -typedef struct -{ - int num; - int first; - int last; -} ITEMTYPE_INFO; - -/* */ -struct DATAFILE_OUT_t -{ - IOHANDLE file; - int num_items; - int num_datas; - int num_item_types; - ITEMTYPE_INFO item_types[0xffff]; - ITEM_INFO items[1024]; - DATA_INFO datas[1024]; -}; - -DATAFILE_OUT *datafile_create(const char *filename) -{ - int i; - DATAFILE_OUT *df = (DATAFILE_OUT*)mem_alloc(sizeof(DATAFILE_OUT), 1); - df->file = io_open(filename, IOFLAG_WRITE); - if(!df->file) - { - mem_free(df); - return 0; - } - - df->num_items = 0; - df->num_datas = 0; - df->num_item_types = 0; - mem_zero(&df->item_types, sizeof(df->item_types)); - - for(i = 0; i < 0xffff; i++) - { - df->item_types[i].first = -1; - df->item_types[i].last = -1; - } - - return df; -} - -int datafile_add_item(DATAFILE_OUT *df, int type, int id, int size, void *data) -{ - df->items[df->num_items].type = type; - df->items[df->num_items].id = id; - df->items[df->num_items].size = size; - - /* - dbg_msg("datafile", "added item type=%d id=%d size=%d", type, id, size); - int i; - for(i = 0; i < size/4; i++) - dbg_msg("datafile", "\t%d: %08x %d", i, ((int*)data)[i], ((int*)data)[i]); - */ - - /* copy data */ - df->items[df->num_items].data = mem_alloc(size, 1); - mem_copy(df->items[df->num_items].data, data, size); - - if(!df->item_types[type].num) /* count item types */ - df->num_item_types++; - - /* link */ - df->items[df->num_items].prev = df->item_types[type].last; - df->items[df->num_items].next = -1; - - if(df->item_types[type].last != -1) - df->items[df->item_types[type].last].next = df->num_items; - df->item_types[type].last = df->num_items; - - if(df->item_types[type].first == -1) - df->item_types[type].first = df->num_items; - - df->item_types[type].num++; - - df->num_items++; - return df->num_items-1; -} - -int datafile_add_data(DATAFILE_OUT *df, int size, void *data) -{ - DATA_INFO *info = &df->datas[df->num_datas]; - void *compdata = mem_alloc(size, 1); /* temporary buffer that we use duing compression */ - unsigned long s = size; - - info->uncompressed_size = size; - if(compress((Bytef*)compdata, &s, (Bytef*)data, size) != Z_OK) - dbg_assert(0, "zlib error"); - info->compressed_size = (int)s; - info->compressed_data = mem_alloc(info->compressed_size, 1); - mem_copy(info->compressed_data, compdata, info->compressed_size); - mem_free(compdata); - - df->num_datas++; - return df->num_datas-1; -} - -int datafile_finish(DATAFILE_OUT *df) -{ - int itemsize = 0; - int i, count, offset; - int typessize, headersize, offsetsize, filesize, swapsize; - int datasize = 0; - DATAFILE_ITEM_TYPE info; - DATAFILE_ITEM itm; - DATAFILE_HEADER header; - - /* we should now write this file! */ - if(DEBUG) - dbg_msg("datafile", "writing"); - - /* calculate sizes */ - for(i = 0; i < df->num_items; i++) - { - if(DEBUG) - dbg_msg("datafile", "item=%d size=%d (%d)", i, df->items[i].size, df->items[i].size+sizeof(DATAFILE_ITEM)); - itemsize += df->items[i].size + sizeof(DATAFILE_ITEM); - } - - - for(i = 0; i < df->num_datas; i++) - datasize += df->datas[i].compressed_size; - - /* calculate the complete size */ - typessize = df->num_item_types*sizeof(DATAFILE_ITEM_TYPE); - headersize = sizeof(DATAFILE_HEADER); - offsetsize = df->num_items*sizeof(int) + df->num_datas*sizeof(int); - filesize = headersize + typessize + offsetsize + itemsize + datasize; - swapsize = filesize - datasize; - - (void)swapsize; - - if(DEBUG) - dbg_msg("datafile", "num_item_types=%d typessize=%d itemsize=%d datasize=%d", df->num_item_types, typessize, itemsize, datasize); - - /* construct header */ - { - header.id[0] = 'D'; - header.id[1] = 'A'; - header.id[2] = 'T'; - header.id[3] = 'A'; - header.version = 4; - header.size = filesize - 16; - header.swaplen = swapsize - 16; - header.num_item_types = df->num_item_types; - header.num_items = df->num_items; - header.num_raw_data = df->num_datas; - header.item_size = itemsize; - header.data_size = datasize; - - /* TODO: apply swapping */ - /* write header */ - if(DEBUG) - dbg_msg("datafile", "headersize=%d", sizeof(header)); - io_write(df->file, &header, sizeof(header)); - } - - /* write types */ - for(i = 0, count = 0; i < 0xffff; i++) - { - if(df->item_types[i].num) - { - /* write info */ - info.type = i; - info.start = count; - info.num = df->item_types[i].num; - if(DEBUG) - dbg_msg("datafile", "writing type=%x start=%d num=%d", info.type, info.start, info.num); - io_write(df->file, &info, sizeof(info)); - count += df->item_types[i].num; - } - } - - /* write item offsets */ - for(i = 0, offset = 0; i < 0xffff; i++) - { - if(df->item_types[i].num) - { - /* write all items in of this type */ - int k = df->item_types[i].first; - while(k != -1) - { - if(DEBUG) - dbg_msg("datafile", "writing item offset num=%d offset=%d", k, offset); - io_write(df->file, &offset, sizeof(offset)); - offset += df->items[k].size + sizeof(DATAFILE_ITEM); - - /* next */ - k = df->items[k].next; - } - } - } - - /* write data offsets */ - for(i = 0, offset = 0; i < df->num_datas; i++) - { - if(DEBUG) - dbg_msg("datafile", "writing data offset num=%d offset=%d", i, offset); - io_write(df->file, &offset, sizeof(offset)); - offset += df->datas[i].compressed_size; - } - - /* write data uncompressed sizes */ - for(i = 0, offset = 0; i < df->num_datas; i++) - { - /* - if(DEBUG) - dbg_msg("datafile", "writing data offset num=%d offset=%d", i, offset); - */ - io_write(df->file, &df->datas[i].uncompressed_size, sizeof(int)); - } - - /* write items */ - for(i = 0; i < 0xffff; i++) - { - if(df->item_types[i].num) - { - /* write all items in of this type */ - int k = df->item_types[i].first; - while(k != -1) - { - itm.type_and_id = (i<<16)|df->items[k].id; - itm.size = df->items[k].size; - if(DEBUG) - dbg_msg("datafile", "writing item type=%x idx=%d id=%d size=%d", i, k, df->items[k].id, df->items[k].size); - - io_write(df->file, &itm, sizeof(itm)); - io_write(df->file, df->items[k].data, df->items[k].size); - - /* next */ - k = df->items[k].next; - } - } - } - - /* write data */ - for(i = 0; i < df->num_datas; i++) - { - if(DEBUG) - dbg_msg("datafile", "writing data id=%d size=%d", i, df->datas[i].compressed_size); - io_write(df->file, df->datas[i].compressed_data, df->datas[i].compressed_size); - } - - /* free data */ - for(i = 0; i < df->num_items; i++) - mem_free(df->items[i].data); - - - io_close(df->file); - mem_free(df); - - if(DEBUG) - dbg_msg("datafile", "done"); - return 0; -} - -#define BUFFER_SIZE 64*1024 - -int datafile_crc(const char *filename) -{ - unsigned char buffer[BUFFER_SIZE]; - IOHANDLE file; - int crc = 0; - unsigned bytes = 0; - - file = io_open(filename, IOFLAG_READ); - if(!file) - return 0; - - while(1) - { - bytes = io_read(file, buffer, BUFFER_SIZE); - if(bytes <= 0) - break; - crc = crc32(crc, buffer, bytes); - } - - io_close(file); - - return crc; -} diff --git a/src/engine/datafile.h b/src/engine/datafile.h deleted file mode 100644 index 7cc7b209..00000000 --- a/src/engine/datafile.h +++ /dev/null @@ -1,27 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -/* raw datafile access */ -typedef struct DATAFILE_t DATAFILE; - -/* read access */ -DATAFILE *datafile_load(const char *filename); -DATAFILE *datafile_load_old(const char *filename); -void *datafile_get_data(DATAFILE *df, int index); -int datafile_get_datasize(DATAFILE *df, int index); -void datafile_unload_data(DATAFILE *df, int index); -void *datafile_get_item(DATAFILE *df, int index, int *type, int *id); -int datafile_get_itemsize(DATAFILE *df, int index); -void datafile_get_type(DATAFILE *df, int type, int *start, int *num); -void *datafile_find_item(DATAFILE *df, int type, int id); -int datafile_num_items(DATAFILE *df); -int datafile_num_data(DATAFILE *df); -void datafile_unload(DATAFILE *df); - -int datafile_crc(const char *filename); - -/* write access */ -typedef struct DATAFILE_OUT_t DATAFILE_OUT; -DATAFILE_OUT *datafile_create(const char *filename); -int datafile_add_data(DATAFILE_OUT *df, int size, void *data); -int datafile_add_item(DATAFILE_OUT *df, int type, int id, int size, void *data); -int datafile_finish(DATAFILE_OUT *df); diff --git a/src/engine/detect.h b/src/engine/detect.h deleted file mode 100644 index 04cca2d9..00000000 --- a/src/engine/detect.h +++ /dev/null @@ -1,115 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#ifndef BASELIB_FILE_CONFIG_H -#define BASELIB_FILE_CONFIG_H - -/* - this file detected the family, platform and architecture - to compile for. -*/ - -/* platforms */ - -/* windows Family */ -#if defined(WIN64) || defined(_WIN64) - /* Hmm, is this IA64 or x86-64? */ - #define CONF_FAMILY_WINDOWS 1 - #define CONF_FAMILY_STRING "windows" - #define CONF_PLATFORM_WIN64 1 - #define CONF_PLATFORM_STRING "win64" -#elif defined(WIN32) || defined(_WIN32) || defined(__CYGWIN32__) || defined(__MINGW32__) - #define CONF_FAMILY_WINDOWS 1 - #define CONF_FAMILY_STRING "windows" - #define CONF_PLATFORM_WIN32 1 - #define CONF_PLATFORM_STRING "win32" -#endif - -/* unix family */ -#if defined(__FreeBSD__) - #define CONF_FAMILY_UNIX 1 - #define CONF_FAMILY_STRING "unix" - #define CONF_PLATFORM_FREEBSD 1 - #define CONF_PLATFORM_STRING "freebsd" -#endif - -#if defined(__OpenBSD__) - #define CONF_FAMILY_UNIX 1 - #define CONF_FAMILY_STRING "unix" - #define CONF_PLATFORM_OPENBSD 1 - #define CONF_PLATFORM_STRING "openbsd" -#endif - -#if defined(__LINUX__) || defined(__linux__) - #define CONF_FAMILY_UNIX 1 - #define CONF_FAMILY_STRING "unix" - #define CONF_PLATFORM_LINUX 1 - #define CONF_PLATFORM_STRING "linux" -#endif - -#if defined(MACOSX) || defined(__APPLE__) || defined(__DARWIN__) - #define CONF_FAMILY_UNIX 1 - #define CONF_FAMILY_STRING "unix" - #define CONF_PLATFORM_MACOSX 1 - #define CONF_PLATFORM_STRING "macosx" -#endif - -#if defined(__sun) - #define CONF_FAMILY_UNIX 1 - #define CONF_FAMILY_STRING "unix" - #define CONF_PLATFROM_SOLARIS 1 - #define CONF_PLATFORM_STRING "solaris" -#endif - -/* beos family */ -#if defined(__BeOS) || defined(__BEOS__) - #define CONF_FAMILY_BEOS 1 - #define CONF_FAMILY_STRING "beos" - #define CONF_PLATFORM_BEOS 1 - #define CONF_PLATFORM_STRING "beos" -#endif - - -/* architectures */ -#if defined(i386) || defined(__i386__) || defined(__x86__) || defined(CONF_PLATFORM_WIN32) - #define CONF_ARCH_IA32 1 - #define CONF_ARCH_STRING "ia32" - #define CONF_ARCH_ENDIAN_LITTLE 1 -#endif - -#if defined(__ia64__) - #define CONF_ARCH_IA64 1 - #define CONF_ARCH_STRING "ia64" - #define CONF_ARCH_ENDIAN_LITTLE 1 -#endif - -#if defined(__amd64__) || defined(__x86_64__) - #define CONF_ARCH_AMD64 1 - #define CONF_ARCH_STRING "amd64" - #define CONF_ARCH_ENDIAN_LITTLE 1 -#endif - -#if defined(__powerpc__) || defined(__ppc__) - #define CONF_ARCH_PPC 1 - #define CONF_ARCH_STRING "ppc" - #define CONF_ARCH_ENDIAN_BIG 1 -#endif - -#if defined(__sparc__) - #define CONF_ARCH_SPARC 1 - #define CONF_ARCH_STRING "sparc" - #define CONF_ARCH_ENDIAN_BIG 1 -#endif - - -#ifndef CONF_FAMILY_STRING -#define CONF_FAMILY_STRING "unknown" -#endif - -#ifndef CONF_PLATFORM_STRING -#define CONF_PLATFORM_STRING "unknown" -#endif - -#ifndef CONF_ARCH_STRING -#define CONF_ARCH_STRING "unknown" -#endif - -#endif diff --git a/src/engine/e_compression.c b/src/engine/e_compression.c new file mode 100644 index 00000000..fa7d7866 --- /dev/null +++ b/src/engine/e_compression.c @@ -0,0 +1,147 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include "e_system.h" +#include + +/* Format: ESDDDDDD EDDDDDDD EDD... Extended, Data, Sign */ +unsigned char *vint_pack(unsigned char *dst, int i) +{ + *dst = (i>>25)&0x40; /* set sign bit if i<0 */ + i = i^(i>>31); /* if(i<0) i = ~i */ + + *dst |= i&0x3F; /* pack 6bit into dst */ + i >>= 6; /* discard 6 bits */ + if(i) + { + *dst |= 0x80; /* set extend bit */ + while(1) + { + dst++; + *dst = i&(0x7F); /* pack 7bit */ + i >>= 7; /* discard 7 bits */ + *dst |= (i!=0)<<7; /* set extend bit (may branch) */ + if(!i) + break; + } + } + + dst++; + return dst; +} + +const unsigned char *vint_unpack(const unsigned char *src, int *i) +{ + int sign = (*src>>6)&1; + *i = *src&0x3F; + + do + { + if(!(*src&0x80)) break; + src++; + *i |= (*src&(0x7F))<<(6); + + if(!(*src&0x80)) break; + src++; + *i |= (*src&(0x7F))<<(6+7); + + if(!(*src&0x80)) break; + src++; + *i |= (*src&(0x7F))<<(6+7+7); + + if(!(*src&0x80)) break; + src++; + *i |= (*src&(0x7F))<<(6+7+7+7); + } while(0); + + src++; + *i ^= -sign; /* if(sign) *i = ~(*i) */ + return src; +} + + +long intpack_decompress(const void *src_, int size, void *dst_) +{ + const unsigned char *src = (unsigned char *)src_; + const unsigned char *end = src + size; + int *dst = (int *)dst_; + while(src < end) + { + src = vint_unpack(src, dst); + dst++; + } + return (long)((unsigned char *)dst-(unsigned char *)dst_); +} + +long intpack_compress(const void *src_, int size, void *dst_) +{ + int *src = (int *)src_; + unsigned char *dst = (unsigned char *)dst_; + size /= 4; + while(size) + { + dst = vint_pack(dst, *src); + size--; + src++; + } + return (long)(dst-(unsigned char *)dst_); +} + + +/* */ +long zerobit_compress(const void *src_, int size, void *dst_) +{ + unsigned char *src = (unsigned char *)src_; + unsigned char *dst = (unsigned char *)dst_; + + while(size) + { + unsigned char bit = 0x80; + unsigned char mask = 0; + int dst_move = 1; + int chunk = size < 8 ? size : 8; + int b; + size -= chunk; + + for(b = 0; b < chunk; b++, bit>>=1) + { + if(*src) + { + dst[dst_move] = *src; + mask |= bit; + dst_move++; + } + + src++; + } + + *dst = mask; + dst += dst_move; + } + + return (long)(dst-(unsigned char *)dst_); +} + +long zerobit_decompress(const void *src_, int size, void *dst_) +{ + unsigned char *src = (unsigned char *)src_; + unsigned char *dst = (unsigned char *)dst_; + unsigned char *end = src + size; + + + while(src != end) + { + unsigned char bit = 0x80; + unsigned char mask = *src++; + int b; + + for(b = 0; b < 8; b++, bit>>=1) + { + if(mask&bit) + *dst++ = *src++; + else + *dst++ = 0; + } + } + + return (long)(dst-(unsigned char *)dst_); +} + diff --git a/src/engine/e_compression.h b/src/engine/e_compression.h new file mode 100644 index 00000000..39ffd596 --- /dev/null +++ b/src/engine/e_compression.h @@ -0,0 +1,11 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ + +/* variable int packing */ +unsigned char *vint_pack(unsigned char *dst, int i); +const unsigned char *vint_unpack(const unsigned char *src, int *inout); +long intpack_compress(const void *src, int size, void *dst); +long intpack_decompress(const void *src, int size, void *dst); + +/* zerobit packing */ +long zerobit_compress(const void *src, int size, void *dst); +long zerobit_decompress(const void *src, int size, void *dst); diff --git a/src/engine/e_config.c b/src/engine/e_config.c new file mode 100644 index 00000000..a4c47ec1 --- /dev/null +++ b/src/engine/e_config.c @@ -0,0 +1,196 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ + +#include +#include +#include + +#include "e_system.h" +#include "e_config.h" + + +/* buffered stream for reading lines, should perhaps be something smaller */ +typedef struct +{ + char buffer[4*1024]; + unsigned buffer_pos; + unsigned buffer_size; + unsigned buffer_max_size; + IOHANDLE io; +} LINEREADER; + +void linereader_init(LINEREADER *lr, IOHANDLE io) +{ + lr->buffer_max_size = 4*1024; + lr->buffer_size = 0; + lr->buffer_pos = 0; + lr->io = io; +} + +char *linereader_get(LINEREADER *lr) +{ + unsigned line_start = lr->buffer_pos; + + while(1) + { + if(lr->buffer_pos >= lr->buffer_size) + { + /* fetch more */ + + /* move the remaining part to the front */ + unsigned read; + unsigned left = lr->buffer_size - line_start; + + if(line_start > lr->buffer_size) + left = 0; + if(left) + mem_move(lr->buffer, &lr->buffer[line_start], left); + lr->buffer_pos = left; + + /* fill the buffer */ + read = io_read(lr->io, &lr->buffer[lr->buffer_pos], lr->buffer_max_size-lr->buffer_pos); + lr->buffer_size = left + read; + line_start = 0; + + if(!read) + { + if(left) + { + lr->buffer[left] = 0; /* return the last line */ + lr->buffer_pos = left; + lr->buffer_size = left; + return lr->buffer; + } + else + return 0x0; /* we are done! */ + } + } + else + { + if(lr->buffer[lr->buffer_pos] == '\n' || lr->buffer[lr->buffer_pos] == '\r') + { + /* line found */ + lr->buffer[lr->buffer_pos] = 0; + lr->buffer_pos++; + return &lr->buffer[line_start]; + } + else + lr->buffer_pos++; + } + } +} + +CONFIGURATION config; + +void config_reset() +{ + #define MACRO_CONFIG_INT(name,def,min,max) config.name = def; + #define MACRO_CONFIG_STR(name,len,def) strncpy(config.name, def, len); + + #include "e_config_variables.h" + + #undef MACRO_CONFIG_INT + #undef MACRO_CONFIG_STR +} + +void strip_spaces(char **p) +{ + char *s = *p; + char *end; + + while (*s == ' ') + ++s; + + end = s + strlen(s); + while (end > s && *(end - 1) == ' ') + *--end = 0; +} + +void config_set(const char *line) +{ + const char *c = strchr(line, '='); + if (c) + { + char var[256]; + char val[256]; + char *var_str = var; + char *val_str = val; + + strcpy(val, c+1); + + mem_copy(var, line, c - line); + var[c - line] = 0; + + + strip_spaces(&var_str); + strip_spaces(&val_str); + + #define MACRO_CONFIG_INT(name,def,min,max) { if (strcmp(#name, var_str) == 0) config_set_ ## name (&config, atoi(val_str)); } + #define MACRO_CONFIG_STR(name,len,def) { if (strcmp(#name, var_str) == 0) { config_set_ ## name (&config, val_str); } } + + #include "e_config_variables.h" + + #undef MACRO_CONFIG_INT + #undef MACRO_CONFIG_STR + } +} + +void config_load(const char *filename) +{ + IOHANDLE file; + dbg_msg("config/load", "loading %s", filename); + file = io_open(filename, IOFLAG_READ); + + if(file) + { + char *line; + LINEREADER lr; + linereader_init(&lr, file); + + while ((line = linereader_get(&lr))) + config_set(line); + + io_close(file); + } +} + +void config_save(const char *filename) +{ + IOHANDLE file; + dbg_msg("config/save", "saving config to %s", filename); + + file = io_open(filename, IOFLAG_WRITE); + + if(file) + { +#if defined(CONF_FAMILY_WINDOWS) + const char newline[] = "\r\n"; +#else + const char newline[] = "\n"; +#endif + const int newline_len = sizeof(newline)-1; + + #define MACRO_CONFIG_INT(name,def,min,max) { char str[256]; sprintf(str, "%s=%i%s", #name, config.name, newline); io_write(file, str, strlen(str)); } + #define MACRO_CONFIG_STR(name,len,def) { io_write(file, #name, strlen(#name)); io_write(file, "=", 1); io_write(file, config.name, strlen(config.name)); io_write(file, newline, newline_len); } + + #include "e_config_variables.h" + + #undef MACRO_CONFIG_INT + #undef MACRO_CONFIG_STR + + io_close(file); + } + else + dbg_msg("config/save", "couldn't open %s for writing. :(", filename); +} + +#define MACRO_CONFIG_INT(name,def,min,max) int config_get_ ## name (CONFIGURATION *c) { return c->name; } +#define MACRO_CONFIG_STR(name,len,def) const char *config_get_ ## name (CONFIGURATION *c) { return c->name; } +#include "e_config_variables.h" +#undef MACRO_CONFIG_INT +#undef MACRO_CONFIG_STR + +#define MACRO_CONFIG_INT(name,def,min,max) void config_set_ ## name (CONFIGURATION *c, int val) { if(min != max) { if (val < min) val = min; if (max != 0 && val > max) val = max; } c->name = val; } +#define MACRO_CONFIG_STR(name,len,def) void config_set_ ## name (CONFIGURATION *c, const char *str) { strncpy(c->name, str, len-1); c->name[sizeof(c->name)-1] = 0; } +#include "e_config_variables.h" +#undef MACRO_CONFIG_INT +#undef MACRO_CONFIG_STR diff --git a/src/engine/e_config.h b/src/engine/e_config.h new file mode 100644 index 00000000..63933660 --- /dev/null +++ b/src/engine/e_config.h @@ -0,0 +1,41 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#ifndef _CONFIG_H +#define _CONFIG_H + +#ifdef __cplusplus +extern "C"{ +#endif + +typedef struct +{ + #define MACRO_CONFIG_INT(name,def,min,max) int name; + #define MACRO_CONFIG_STR(name,len,def) char name[len]; + #include "e_config_variables.h" + #undef MACRO_CONFIG_INT + #undef MACRO_CONFIG_STR +} CONFIGURATION; + +extern CONFIGURATION config; + +void config_set(const char *line); +void config_reset(); +void config_load(const char *filename); +void config_save(const char *filename); + +#define MACRO_CONFIG_INT(name,def,min,max) int config_get_ ## name (CONFIGURATION *c); +#define MACRO_CONFIG_STR(name,len,def) const char *config_get_ ## name (CONFIGURATION *c); +#include "e_config_variables.h" +#undef MACRO_CONFIG_INT +#undef MACRO_CONFIG_STR + +#define MACRO_CONFIG_INT(name,def,min,max) void config_set_ ## name (CONFIGURATION *c, int val); +#define MACRO_CONFIG_STR(name,len,def) void config_set_ ## name (CONFIGURATION *c, const char *str); +#include "e_config_variables.h" +#undef MACRO_CONFIG_INT +#undef MACRO_CONFIG_STR + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/engine/e_config_variables.h b/src/engine/e_config_variables.h new file mode 100644 index 00000000..51ee7376 --- /dev/null +++ b/src/engine/e_config_variables.h @@ -0,0 +1,56 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include "../game/g_variables.h" + + +MACRO_CONFIG_STR(player_name, 32, "nameless tee") +MACRO_CONFIG_STR(clan_name, 32, "") +MACRO_CONFIG_STR(password, 32, "") +MACRO_CONFIG_STR(rcon_password, 32, "") + +MACRO_CONFIG_INT(cl_cpu_throttle, 0, 0, 1) +/*MACRO_CONFIG_STR(cl_connect, 32, "")*/ +MACRO_CONFIG_INT(cl_editor, 0, 0, 1) + +MACRO_CONFIG_STR(b_filter_string, 64, "") + +MACRO_CONFIG_INT(b_filter_full, 0, 0, 1) +MACRO_CONFIG_INT(b_filter_empty, 0, 0, 1) +MACRO_CONFIG_INT(b_filter_pw, 0, 0, 1) +MACRO_CONFIG_INT(b_sort, 0, 0, 256) +MACRO_CONFIG_INT(b_max_requests, 10, 0, 1000) + +MACRO_CONFIG_INT(snd_rate, 48000, 0, 0) +MACRO_CONFIG_INT(snd_enable, 1, 0, 1) +MACRO_CONFIG_INT(snd_volume, 100, 0, 100) + +MACRO_CONFIG_INT(gfx_screen_width, 800, 0, 0) +MACRO_CONFIG_INT(gfx_screen_height, 600, 0, 0) +MACRO_CONFIG_INT(gfx_fullscreen, 1, 0, 1) +MACRO_CONFIG_INT(gfx_color_depth, 24, 16, 24) +MACRO_CONFIG_INT(gfx_vsync, 1, 0, 1) +MACRO_CONFIG_INT(gfx_display_all_modes, 0, 0, 1) +MACRO_CONFIG_INT(gfx_texture_compression, 0, 0, 1) +MACRO_CONFIG_INT(gfx_high_detail, 1, 0, 1) +MACRO_CONFIG_INT(gfx_texture_quality, 1, 0, 1) +MACRO_CONFIG_INT(gfx_fsaa_samples, 0, 0, 16) +MACRO_CONFIG_INT(gfx_refresh_rate, 0, 0, 0) + +MACRO_CONFIG_INT(key_screenshot, 267, 32, 512) +MACRO_CONFIG_INT(inp_mousesens, 100, 5, 100000) + +MACRO_CONFIG_STR(masterserver, 128, "master.teewars.com") + +MACRO_CONFIG_STR(sv_name, 128, "unnamed server") +MACRO_CONFIG_STR(sv_bindaddr, 128, "") +MACRO_CONFIG_INT(sv_port, 8303, 0, 0) +MACRO_CONFIG_INT(sv_external_port, 0, 0, 0) +MACRO_CONFIG_INT(sv_sendheartbeats, 1, 0, 1) +MACRO_CONFIG_STR(sv_map, 128, "dm1") +MACRO_CONFIG_INT(sv_map_reload, 0, 0, 1) +MACRO_CONFIG_INT(sv_max_clients, 8, 1, 12) +MACRO_CONFIG_INT(sv_bandwidth_mode, 0, 0, 2) + +MACRO_CONFIG_INT(debug, 0, 0, 1) +MACRO_CONFIG_INT(dbg_stress, 0, 0, 0) +MACRO_CONFIG_STR(dbg_stress_server, 32, "localhost") + diff --git a/src/engine/e_datafile.c b/src/engine/e_datafile.c new file mode 100644 index 00000000..14138ed9 --- /dev/null +++ b/src/engine/e_datafile.c @@ -0,0 +1,630 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include "e_system.h" +#include "e_datafile.h" +#include "external/zlib/zlib.h" + +static const int DEBUG=0; + +typedef struct +{ + int type; + int start; + int num; +} DATAFILE_ITEM_TYPE; + +typedef struct +{ + int type_and_id; + int size; +} DATAFILE_ITEM; + +typedef struct +{ + char id[4]; + int version; + int size; + int swaplen; + int num_item_types; + int num_items; + int num_raw_data; + int item_size; + int data_size; +} DATAFILE_HEADER; + +typedef struct +{ + int num_item_types; + int num_items; + int num_raw_data; + int item_size; + int data_size; + char start[4]; +} DATAFILE_DATA; + +typedef struct +{ + DATAFILE_ITEM_TYPE *item_types; + int *item_offsets; + int *data_offsets; + int *data_sizes; + + char *item_start; + char *data_start; +} DATAFILE_INFO; + +struct DATAFILE_t +{ + IOHANDLE file; + DATAFILE_INFO info; + DATAFILE_HEADER header; + int data_start_offset; + char **data_ptrs; + char *data; +}; + +DATAFILE *datafile_load(const char *filename) +{ + DATAFILE *df; + IOHANDLE file; + DATAFILE_HEADER header; + unsigned readsize; + + unsigned *dst; + unsigned char *src; + unsigned j; + int size = 0; + int allocsize = 0; + + (void)dst; + (void)src; + (void)j; + + + dbg_msg("datafile", "datafile loading. filename='%s'", filename); + + file = io_open(filename, IOFLAG_READ); + if(!file) + return 0; + + /* TODO: change this header */ + io_read(file, &header, sizeof(header)); + if(header.id[0] != 'A' || header.id[1] != 'T' || header.id[2] != 'A' || header.id[3] != 'D') + { + if(header.id[0] != 'D' || header.id[1] != 'A' || header.id[2] != 'T' || header.id[3] != 'A') + { + dbg_msg("datafile", "wrong signature. %x %x %x %x", header.id[0], header.id[1], header.id[2], header.id[3]); + return 0; + } + } + +#if defined(CONF_ARCH_ENDIAN_BIG) + swap_endian(&header, sizeof(int), sizeof(header)/sizeof(int)); +#endif + if(header.version != 3 && header.version != 4) + { + dbg_msg("datafile", "wrong version. version=%x", header.version); + return 0; + } + + /* read in the rest except the data */ + size = 0; + size += header.num_item_types*sizeof(DATAFILE_ITEM_TYPE); + size += (header.num_items+header.num_raw_data)*sizeof(int); + if(header.version == 4) + size += header.num_raw_data*sizeof(int); /* v4 has uncompressed data sizes aswell */ + size += header.item_size; + + allocsize = size; + allocsize += sizeof(DATAFILE); /* add space for info structure */ + allocsize += header.num_raw_data*sizeof(void*); /* add space for data pointers */ + + df = (DATAFILE*)mem_alloc(allocsize, 1); + df->header = header; + df->data_start_offset = sizeof(DATAFILE_HEADER) + size; + df->data_ptrs = (char**)(df+1); + df->data = (char *)(df+1)+header.num_raw_data*sizeof(char *); + df->file = file; + + /* clear the data pointers */ + mem_zero(df->data_ptrs, header.num_raw_data*sizeof(void*)); + + /* read types, offsets, sizes and item data */ + readsize = io_read(file, df->data, size); + if(readsize != size) + { + dbg_msg("datafile", "couldn't load the whole thing, wanted=%d got=%d", size, readsize); + return 0; + } + +#if defined(CONF_ARCH_ENDIAN_BIG) + swap_endian(df->data, sizeof(int), header.swaplen); +#endif + + if(DEBUG) + dbg_msg("datafile", "item_size=%d", df->header.item_size); + + df->info.item_types = (DATAFILE_ITEM_TYPE *)df->data; + df->info.item_offsets = (int *)&df->info.item_types[df->header.num_item_types]; + df->info.data_offsets = (int *)&df->info.item_offsets[df->header.num_items]; + df->info.data_sizes = (int *)&df->info.data_offsets[df->header.num_raw_data]; + + if(header.version == 4) + df->info.item_start = (char *)&df->info.data_sizes[df->header.num_raw_data]; + else + df->info.item_start = (char *)&df->info.data_offsets[df->header.num_raw_data]; + df->info.data_start = df->info.item_start + df->header.item_size; + + if(DEBUG) + dbg_msg("datafile", "datafile loading done. datafile='%s'", filename); + + if(DEBUG) + { + /* + for(int i = 0; i < df->data.num_raw_data; i++) + { + void *p = datafile_get_data(df, i); + dbg_msg("datafile", "%d %d", (int)((char*)p - (char*)(&df->data)), size); + } + + for(int i = 0; i < datafile_num_items(df); i++) + { + int type, id; + void *data = datafile_get_item(df, i, &type, &id); + dbg_msg("map", "\t%d: type=%x id=%x p=%p offset=%d", i, type, id, data, df->info.item_offsets[i]); + int *idata = (int*)data; + for(int k = 0; k < 3; k++) + dbg_msg("datafile", "\t\t%d=%d (%x)", k, idata[k], idata[k]); + } + + for(int i = 0; i < df->data.num_item_types; i++) + { + dbg_msg("map", "\t%d: type=%x start=%d num=%d", i, + df->info.item_types[i].type, + df->info.item_types[i].start, + df->info.item_types[i].num); + for(int k = 0; k < df->info.item_types[i].num; k++) + { + int type, id; + datafile_get_item(df, df->info.item_types[i].start+k, &type, &id); + if(type != df->info.item_types[i].type) + dbg_msg("map", "\tERROR"); + } + } + */ + } + + return df; +} + +int datafile_num_data(DATAFILE *df) +{ + return df->header.num_raw_data; +} + +/* always returns the size in the file */ +int datafile_get_datasize(DATAFILE *df, int index) +{ + if(index == df->header.num_raw_data-1) + return df->header.data_size-df->info.data_offsets[index]; + return df->info.data_offsets[index+1]-df->info.data_offsets[index]; +} + +void *datafile_get_data(DATAFILE *df, int index) +{ + /* load it if needed */ + if(!df->data_ptrs[index]) + { + /* fetch the data size */ + int datasize = datafile_get_datasize(df, index); + + if(df->header.version == 4) + { + /* v4 has compressed data */ + void *temp = (char *)mem_alloc(datasize, 1); + unsigned long uncompressed_size = df->info.data_sizes[index]; + unsigned long s; + + dbg_msg("datafile", "loading data index=%d size=%d", index, datasize); + df->data_ptrs[index] = (char *)mem_alloc(uncompressed_size, 1); + + /* read the compressed data */ + io_seek(df->file, df->data_start_offset+df->info.data_offsets[index], IOSEEK_START); + io_read(df->file, temp, datasize); + + /* decompress the data, TODO: check for errors */ + s = uncompressed_size; + uncompress((Bytef*)df->data_ptrs[index], &s, (Bytef*)temp, datasize); + + /* clean up the temporary buffers */ + mem_free(temp); + } + else + { + /* load the data */ + dbg_msg("datafile", "loading data index=%d size=%d", index, datasize); + df->data_ptrs[index] = (char *)mem_alloc(datasize, 1); + io_seek(df->file, df->data_start_offset+df->info.data_offsets[index], IOSEEK_START); + io_read(df->file, df->data_ptrs[index], datasize); + } + } + + return df->data_ptrs[index]; +} + +void datafile_unload_data(DATAFILE *df, int index) +{ + /* */ + mem_free(df->data_ptrs[index]); + df->data_ptrs[index] = 0x0; +} + +int datafile_get_itemsize(DATAFILE *df, int index) +{ + if(index == df->header.num_items-1) + return df->header.item_size-df->info.item_offsets[index]; + return df->info.item_offsets[index+1]-df->info.item_offsets[index]; +} + +void *datafile_get_item(DATAFILE *df, int index, int *type, int *id) +{ + DATAFILE_ITEM *i = (DATAFILE_ITEM *)(df->info.item_start+df->info.item_offsets[index]); + if(type) + *type = (i->type_and_id>>16)&0xffff; /* remove sign extention */ + if(id) + *id = i->type_and_id&0xffff; + return (void *)(i+1); +} + +void datafile_get_type(DATAFILE *df, int type, int *start, int *num) +{ + int i; + for(i = 0; i < df->header.num_item_types; i++) + { + if(df->info.item_types[i].type == type) + { + *start = df->info.item_types[i].start; + *num = df->info.item_types[i].num; + return; + } + } + + *start = 0; + *num = 0; +} + +void *datafile_find_item(DATAFILE *df, int type, int id) +{ + int start, num,i ; + int item_id; + void *item; + + datafile_get_type(df, type, &start, &num); + for(i = 0; i < num; i++) + { + item = datafile_get_item(df, start+i,0, &item_id); + if(id == item_id) + return item; + } + return 0; +} + +int datafile_num_items(DATAFILE *df) +{ + return df->header.num_items; +} + +void datafile_unload(DATAFILE *df) +{ + if(df) + { + /* free the data that is loaded */ + int i; + for(i = 0; i < df->header.num_raw_data; i++) + mem_free(df->data_ptrs[i]); + + io_close(df->file); + mem_free(df); + } +} + +/* DATAFILE output */ +typedef struct +{ + int uncompressed_size; + int compressed_size; + void *compressed_data; +} DATA_INFO; + +typedef struct +{ + int type; + int id; + int size; + int next; + int prev; + void *data; +} ITEM_INFO; + +typedef struct +{ + int num; + int first; + int last; +} ITEMTYPE_INFO; + +/* */ +struct DATAFILE_OUT_t +{ + IOHANDLE file; + int num_items; + int num_datas; + int num_item_types; + ITEMTYPE_INFO item_types[0xffff]; + ITEM_INFO items[1024]; + DATA_INFO datas[1024]; +}; + +DATAFILE_OUT *datafile_create(const char *filename) +{ + int i; + DATAFILE_OUT *df = (DATAFILE_OUT*)mem_alloc(sizeof(DATAFILE_OUT), 1); + df->file = io_open(filename, IOFLAG_WRITE); + if(!df->file) + { + mem_free(df); + return 0; + } + + df->num_items = 0; + df->num_datas = 0; + df->num_item_types = 0; + mem_zero(&df->item_types, sizeof(df->item_types)); + + for(i = 0; i < 0xffff; i++) + { + df->item_types[i].first = -1; + df->item_types[i].last = -1; + } + + return df; +} + +int datafile_add_item(DATAFILE_OUT *df, int type, int id, int size, void *data) +{ + df->items[df->num_items].type = type; + df->items[df->num_items].id = id; + df->items[df->num_items].size = size; + + /* + dbg_msg("datafile", "added item type=%d id=%d size=%d", type, id, size); + int i; + for(i = 0; i < size/4; i++) + dbg_msg("datafile", "\t%d: %08x %d", i, ((int*)data)[i], ((int*)data)[i]); + */ + + /* copy data */ + df->items[df->num_items].data = mem_alloc(size, 1); + mem_copy(df->items[df->num_items].data, data, size); + + if(!df->item_types[type].num) /* count item types */ + df->num_item_types++; + + /* link */ + df->items[df->num_items].prev = df->item_types[type].last; + df->items[df->num_items].next = -1; + + if(df->item_types[type].last != -1) + df->items[df->item_types[type].last].next = df->num_items; + df->item_types[type].last = df->num_items; + + if(df->item_types[type].first == -1) + df->item_types[type].first = df->num_items; + + df->item_types[type].num++; + + df->num_items++; + return df->num_items-1; +} + +int datafile_add_data(DATAFILE_OUT *df, int size, void *data) +{ + DATA_INFO *info = &df->datas[df->num_datas]; + void *compdata = mem_alloc(size, 1); /* temporary buffer that we use duing compression */ + unsigned long s = size; + + info->uncompressed_size = size; + if(compress((Bytef*)compdata, &s, (Bytef*)data, size) != Z_OK) + dbg_assert(0, "zlib error"); + info->compressed_size = (int)s; + info->compressed_data = mem_alloc(info->compressed_size, 1); + mem_copy(info->compressed_data, compdata, info->compressed_size); + mem_free(compdata); + + df->num_datas++; + return df->num_datas-1; +} + +int datafile_finish(DATAFILE_OUT *df) +{ + int itemsize = 0; + int i, count, offset; + int typessize, headersize, offsetsize, filesize, swapsize; + int datasize = 0; + DATAFILE_ITEM_TYPE info; + DATAFILE_ITEM itm; + DATAFILE_HEADER header; + + /* we should now write this file! */ + if(DEBUG) + dbg_msg("datafile", "writing"); + + /* calculate sizes */ + for(i = 0; i < df->num_items; i++) + { + if(DEBUG) + dbg_msg("datafile", "item=%d size=%d (%d)", i, df->items[i].size, df->items[i].size+sizeof(DATAFILE_ITEM)); + itemsize += df->items[i].size + sizeof(DATAFILE_ITEM); + } + + + for(i = 0; i < df->num_datas; i++) + datasize += df->datas[i].compressed_size; + + /* calculate the complete size */ + typessize = df->num_item_types*sizeof(DATAFILE_ITEM_TYPE); + headersize = sizeof(DATAFILE_HEADER); + offsetsize = df->num_items*sizeof(int) + df->num_datas*sizeof(int); + filesize = headersize + typessize + offsetsize + itemsize + datasize; + swapsize = filesize - datasize; + + (void)swapsize; + + if(DEBUG) + dbg_msg("datafile", "num_item_types=%d typessize=%d itemsize=%d datasize=%d", df->num_item_types, typessize, itemsize, datasize); + + /* construct header */ + { + header.id[0] = 'D'; + header.id[1] = 'A'; + header.id[2] = 'T'; + header.id[3] = 'A'; + header.version = 4; + header.size = filesize - 16; + header.swaplen = swapsize - 16; + header.num_item_types = df->num_item_types; + header.num_items = df->num_items; + header.num_raw_data = df->num_datas; + header.item_size = itemsize; + header.data_size = datasize; + + /* TODO: apply swapping */ + /* write header */ + if(DEBUG) + dbg_msg("datafile", "headersize=%d", sizeof(header)); + io_write(df->file, &header, sizeof(header)); + } + + /* write types */ + for(i = 0, count = 0; i < 0xffff; i++) + { + if(df->item_types[i].num) + { + /* write info */ + info.type = i; + info.start = count; + info.num = df->item_types[i].num; + if(DEBUG) + dbg_msg("datafile", "writing type=%x start=%d num=%d", info.type, info.start, info.num); + io_write(df->file, &info, sizeof(info)); + count += df->item_types[i].num; + } + } + + /* write item offsets */ + for(i = 0, offset = 0; i < 0xffff; i++) + { + if(df->item_types[i].num) + { + /* write all items in of this type */ + int k = df->item_types[i].first; + while(k != -1) + { + if(DEBUG) + dbg_msg("datafile", "writing item offset num=%d offset=%d", k, offset); + io_write(df->file, &offset, sizeof(offset)); + offset += df->items[k].size + sizeof(DATAFILE_ITEM); + + /* next */ + k = df->items[k].next; + } + } + } + + /* write data offsets */ + for(i = 0, offset = 0; i < df->num_datas; i++) + { + if(DEBUG) + dbg_msg("datafile", "writing data offset num=%d offset=%d", i, offset); + io_write(df->file, &offset, sizeof(offset)); + offset += df->datas[i].compressed_size; + } + + /* write data uncompressed sizes */ + for(i = 0, offset = 0; i < df->num_datas; i++) + { + /* + if(DEBUG) + dbg_msg("datafile", "writing data offset num=%d offset=%d", i, offset); + */ + io_write(df->file, &df->datas[i].uncompressed_size, sizeof(int)); + } + + /* write items */ + for(i = 0; i < 0xffff; i++) + { + if(df->item_types[i].num) + { + /* write all items in of this type */ + int k = df->item_types[i].first; + while(k != -1) + { + itm.type_and_id = (i<<16)|df->items[k].id; + itm.size = df->items[k].size; + if(DEBUG) + dbg_msg("datafile", "writing item type=%x idx=%d id=%d size=%d", i, k, df->items[k].id, df->items[k].size); + + io_write(df->file, &itm, sizeof(itm)); + io_write(df->file, df->items[k].data, df->items[k].size); + + /* next */ + k = df->items[k].next; + } + } + } + + /* write data */ + for(i = 0; i < df->num_datas; i++) + { + if(DEBUG) + dbg_msg("datafile", "writing data id=%d size=%d", i, df->datas[i].compressed_size); + io_write(df->file, df->datas[i].compressed_data, df->datas[i].compressed_size); + } + + /* free data */ + for(i = 0; i < df->num_items; i++) + mem_free(df->items[i].data); + + + io_close(df->file); + mem_free(df); + + if(DEBUG) + dbg_msg("datafile", "done"); + return 0; +} + +#define BUFFER_SIZE 64*1024 + +int datafile_crc(const char *filename) +{ + unsigned char buffer[BUFFER_SIZE]; + IOHANDLE file; + int crc = 0; + unsigned bytes = 0; + + file = io_open(filename, IOFLAG_READ); + if(!file) + return 0; + + while(1) + { + bytes = io_read(file, buffer, BUFFER_SIZE); + if(bytes <= 0) + break; + crc = crc32(crc, buffer, bytes); + } + + io_close(file); + + return crc; +} diff --git a/src/engine/e_datafile.h b/src/engine/e_datafile.h new file mode 100644 index 00000000..7cc7b209 --- /dev/null +++ b/src/engine/e_datafile.h @@ -0,0 +1,27 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ + +/* raw datafile access */ +typedef struct DATAFILE_t DATAFILE; + +/* read access */ +DATAFILE *datafile_load(const char *filename); +DATAFILE *datafile_load_old(const char *filename); +void *datafile_get_data(DATAFILE *df, int index); +int datafile_get_datasize(DATAFILE *df, int index); +void datafile_unload_data(DATAFILE *df, int index); +void *datafile_get_item(DATAFILE *df, int index, int *type, int *id); +int datafile_get_itemsize(DATAFILE *df, int index); +void datafile_get_type(DATAFILE *df, int type, int *start, int *num); +void *datafile_find_item(DATAFILE *df, int type, int id); +int datafile_num_items(DATAFILE *df); +int datafile_num_data(DATAFILE *df); +void datafile_unload(DATAFILE *df); + +int datafile_crc(const char *filename); + +/* write access */ +typedef struct DATAFILE_OUT_t DATAFILE_OUT; +DATAFILE_OUT *datafile_create(const char *filename); +int datafile_add_data(DATAFILE_OUT *df, int size, void *data); +int datafile_add_item(DATAFILE_OUT *df, int type, int id, int size, void *data); +int datafile_finish(DATAFILE_OUT *df); diff --git a/src/engine/e_detect.h b/src/engine/e_detect.h new file mode 100644 index 00000000..04cca2d9 --- /dev/null +++ b/src/engine/e_detect.h @@ -0,0 +1,115 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#ifndef BASELIB_FILE_CONFIG_H +#define BASELIB_FILE_CONFIG_H + +/* + this file detected the family, platform and architecture + to compile for. +*/ + +/* platforms */ + +/* windows Family */ +#if defined(WIN64) || defined(_WIN64) + /* Hmm, is this IA64 or x86-64? */ + #define CONF_FAMILY_WINDOWS 1 + #define CONF_FAMILY_STRING "windows" + #define CONF_PLATFORM_WIN64 1 + #define CONF_PLATFORM_STRING "win64" +#elif defined(WIN32) || defined(_WIN32) || defined(__CYGWIN32__) || defined(__MINGW32__) + #define CONF_FAMILY_WINDOWS 1 + #define CONF_FAMILY_STRING "windows" + #define CONF_PLATFORM_WIN32 1 + #define CONF_PLATFORM_STRING "win32" +#endif + +/* unix family */ +#if defined(__FreeBSD__) + #define CONF_FAMILY_UNIX 1 + #define CONF_FAMILY_STRING "unix" + #define CONF_PLATFORM_FREEBSD 1 + #define CONF_PLATFORM_STRING "freebsd" +#endif + +#if defined(__OpenBSD__) + #define CONF_FAMILY_UNIX 1 + #define CONF_FAMILY_STRING "unix" + #define CONF_PLATFORM_OPENBSD 1 + #define CONF_PLATFORM_STRING "openbsd" +#endif + +#if defined(__LINUX__) || defined(__linux__) + #define CONF_FAMILY_UNIX 1 + #define CONF_FAMILY_STRING "unix" + #define CONF_PLATFORM_LINUX 1 + #define CONF_PLATFORM_STRING "linux" +#endif + +#if defined(MACOSX) || defined(__APPLE__) || defined(__DARWIN__) + #define CONF_FAMILY_UNIX 1 + #define CONF_FAMILY_STRING "unix" + #define CONF_PLATFORM_MACOSX 1 + #define CONF_PLATFORM_STRING "macosx" +#endif + +#if defined(__sun) + #define CONF_FAMILY_UNIX 1 + #define CONF_FAMILY_STRING "unix" + #define CONF_PLATFROM_SOLARIS 1 + #define CONF_PLATFORM_STRING "solaris" +#endif + +/* beos family */ +#if defined(__BeOS) || defined(__BEOS__) + #define CONF_FAMILY_BEOS 1 + #define CONF_FAMILY_STRING "beos" + #define CONF_PLATFORM_BEOS 1 + #define CONF_PLATFORM_STRING "beos" +#endif + + +/* architectures */ +#if defined(i386) || defined(__i386__) || defined(__x86__) || defined(CONF_PLATFORM_WIN32) + #define CONF_ARCH_IA32 1 + #define CONF_ARCH_STRING "ia32" + #define CONF_ARCH_ENDIAN_LITTLE 1 +#endif + +#if defined(__ia64__) + #define CONF_ARCH_IA64 1 + #define CONF_ARCH_STRING "ia64" + #define CONF_ARCH_ENDIAN_LITTLE 1 +#endif + +#if defined(__amd64__) || defined(__x86_64__) + #define CONF_ARCH_AMD64 1 + #define CONF_ARCH_STRING "amd64" + #define CONF_ARCH_ENDIAN_LITTLE 1 +#endif + +#if defined(__powerpc__) || defined(__ppc__) + #define CONF_ARCH_PPC 1 + #define CONF_ARCH_STRING "ppc" + #define CONF_ARCH_ENDIAN_BIG 1 +#endif + +#if defined(__sparc__) + #define CONF_ARCH_SPARC 1 + #define CONF_ARCH_STRING "sparc" + #define CONF_ARCH_ENDIAN_BIG 1 +#endif + + +#ifndef CONF_FAMILY_STRING +#define CONF_FAMILY_STRING "unknown" +#endif + +#ifndef CONF_PLATFORM_STRING +#define CONF_PLATFORM_STRING "unknown" +#endif + +#ifndef CONF_ARCH_STRING +#define CONF_ARCH_STRING "unknown" +#endif + +#endif diff --git a/src/engine/e_engine.c b/src/engine/e_engine.c new file mode 100644 index 00000000..341bf936 --- /dev/null +++ b/src/engine/e_engine.c @@ -0,0 +1,66 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include +#include + +#include +#include +#include + +static char application_save_path[512] = {0}; + +const char *engine_savepath(const char *filename, char *buffer, int max) +{ + sprintf(buffer, "%s/%s", application_save_path, filename); + return buffer; +} + +void engine_init(const char *appname, int argc, char **argv) +{ + /* init the network */ + net_init(); + + /* create storage location */ + { + char path[1024] = {0}; + fs_storage_path(appname, application_save_path, sizeof(application_save_path)); + if(fs_makedir(application_save_path) == 0) + { + strcpy(path, application_save_path); + strcat(path, "/screenshots"); + fs_makedir(path); + } + } + + /* reset the config */ + config_reset(); + + /* load the configuration */ + { + int i; + const char *config_filename = "default.cfg"; + char buf[1024]; + for(i = 1; i < argc; i++) + { + if(argv[i][0] == '-' && argv[i][1] == 'f' && argv[i][2] == 0 && argc - i > 1) + { + config_filename = argv[i+1]; + i++; + } + } + + config_load(engine_savepath(config_filename, buf, sizeof(buf))); + } + + /* search arguments for overrides */ + { + int i; + for(i = 1; i < argc; i++) + config_set(argv[i]); + } +} + +void engine_writeconfig() +{ + char buf[1024]; + config_save(engine_savepath("default.cfg", buf, sizeof(buf))); +} diff --git a/src/engine/e_engine.h b/src/engine/e_engine.h new file mode 100644 index 00000000..56fec20e --- /dev/null +++ b/src/engine/e_engine.h @@ -0,0 +1,5 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ + +const char *engine_savepath(const char *filename, char *buffer, int max); +void engine_init(const char *appname, int argc, char **argv); +void engine_writeconfig(); diff --git a/src/engine/e_interface.h b/src/engine/e_interface.h new file mode 100644 index 00000000..67f8c78c --- /dev/null +++ b/src/engine/e_interface.h @@ -0,0 +1,858 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#ifndef ENGINE_INTERFACE_H +#define ENGINE_INTERFACE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + Title: Engine Interface +*/ + +#include "e_keys.h" + +enum +{ + MAX_CLIENTS=12, + SERVER_TICK_SPEED=50, /* TODO: this should be removed */ + SNAP_CURRENT=0, + SNAP_PREV=1, + + IMG_RGB=0, + IMG_RGBA=1, + + MASK_NONE=0, + MASK_SET, + MASK_ZERO, + + SNDFLAG_LOOP=1, + SNDFLAG_POS=2, + SNDFLAG_ALL=3, + + CLIENTSTATE_OFFLINE=0, + CLIENTSTATE_CONNECTING, + CLIENTSTATE_LOADING, + CLIENTSTATE_ONLINE, + CLIENTSTATE_QUITING, + + BROWSESORT_NAME = 0, + BROWSESORT_PING, + BROWSESORT_MAP, + BROWSESORT_GAMETYPE, + BROWSESORT_PROGRESSION, + BROWSESORT_NUMPLAYERS +}; + +typedef struct +{ + int type; + int id; +} SNAP_ITEM; + +typedef struct +{ + const char *name; + int latency; +} CLIENT_INFO; + +typedef struct +{ + int width, height; + int format; + void *data; +} IMAGE_INFO; + +typedef struct +{ + int width, height; + int red, green, blue; +} VIDEO_MODE; + +typedef struct +{ + int sorted_index; + int server_index; + + int progression; + int game_type; + int max_players; + int num_players; + int flags; + int latency; /* in ms */ + char name[64]; + char map[32]; + char version[32]; + char address[24]; + char player_names[16][48]; + int player_scores[16]; +} SERVER_INFO; + +struct rect +{ + float x, y, w, h; +}; + +struct rect *ui_screen(); +typedef void (*rect_fun)(const struct rect *r); +void ui_foreach_rect(rect_fun fun); +void ui_scale(float scale); +void ui_hsplit_t(const struct rect *original, int pixels, struct rect *top, struct rect *bottom); +void ui_hsplit_b(const struct rect *original, int pixels, struct rect *top, struct rect *bottom); +void ui_vsplit_l(const struct rect *original, int pixels, struct rect *left, struct rect *right); +void ui_vsplit_r(const struct rect *original, int pixels, struct rect *left, struct rect *right); +void ui_margin(const struct rect *original, int pixels, struct rect *new_rect); + +/* image loaders */ +int gfx_load_png(IMAGE_INFO *img, const char *filename); + +/* + Group: Graphics +*/ + +int gfx_init(); +void gfx_shutdown(); +void gfx_swap(); + +float gfx_screenaspect(); + +int gfx_get_video_modes(VIDEO_MODE *list, int maxcount); +void gfx_set_vsync(int val); + +int gfx_window_active(); +int gfx_window_open(); + +/* textures */ +/* + Function: gfx_load_texture + Loads a texture from a file. TGA and PNG supported. + + Arguments: + filename - Null terminated string to the file to load. + + Returns: + An ID to the texture. -1 on failure. + + See Also: + +*/ +int gfx_load_texture(const char *filename); + +/* + Function: gfx_load_texture_raw + Loads a texture from memory. + + Arguments: + w - Width of the texture. + h - Height of the texture. + data - Pointer to the pixel data. + + Returns: + An ID to the texture. -1 on failure. + + Remarks: + The pixel data should be in RGBA format with 8 bit per component. + So the total size of the data should be w*h*4. + + See Also: + +*/ +int gfx_load_texture_raw(int w, int h, int format, const void *data); +/*int gfx_load_mip_texture_raw(int w, int h, int format, const void *data);*/ + +/* + Function: gfx_texture_set + Sets the active texture. + + Arguments: + id - ID to the texture to set. +*/ +void gfx_texture_set(int id); + +/* + Function: gfx_unload_texture + Unloads a texture. + + Arguments: + id - ID to the texture to unload. + + See Also: + , + + Remarks: + NOT IMPLEMENTED +*/ +int gfx_unload_texture(int id); + +void gfx_clear(float r, float g, float b); + +/* + Function: gfx_screenwidth + Returns the screen width. + + See Also: + +*/ +int gfx_screenwidth(); + +/* + Function: gfx_screenheight + Returns the screen height. + + See Also: + +*/ +int gfx_screenheight(); + +/* + Function: gfx_mapscreen + Specifies the coordinate system for the screen. + + Arguments: + tl_x - Top-left X + tl_y - Top-left Y + br_x - Bottom-right X + br_y - Bottom-right y +*/ +void gfx_mapscreen(float tl_x, float tl_y, float br_x, float br_y); + +/* + Function: gfx_blend_normal + Set the active blending mode to normal (src, 1-src). + + Remarks: + This must be used before calling . + This is equal to glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA). + + See Also: + +*/ +void gfx_blend_normal(); + +/* + Function: gfx_blend_additive + Set the active blending mode to additive (src, one). + + Remarks: + This must be used before calling . + This is equal to glBlendFunc(GL_SRC_ALPHA, GL_ONE). + + See Also: + +*/ +void gfx_blend_additive(); + +/* + Function: gfx_quads_begin + Begins a quad drawing session. + + Remarks: + This functions resets the rotation, color and subset. + End the session by using . + You can't change texture or blending mode during a session. + + See Also: + +*/ +void gfx_quads_begin(); + +/* + Function: gfx_quads_end + Ends a quad session. + + See Also: + +*/ +void gfx_quads_end(); + +/* + Function: gfx_quads_setrotation + Sets the rotation to use when drawing a quad. + + Arguments: + angle - Angle in radians. + + Remarks: + The angle is reset when is called. +*/ +void gfx_quads_setrotation(float angle); + +/* + Function: gfx_quads_setcolorvertex + Sets the color of a vertex. + + Arguments: + i - Index to the vertex. + r - Red value. + g - Green value. + b - Blue value. + a - Alpha value. + + Remarks: + The color values are from 0.0 to 1.0. + The color is reset when is called. +*/ +void gfx_setcolorvertex(int i, float r, float g, float b, float a); + +/* + Function: gfx_quads_setcolor + Sets the color of all the vertices. + + Arguments: + r - Red value. + g - Green value. + b - Blue value. + a - Alpha value. + + Remarks: + The color values are from 0.0 to 1.0. + The color is reset when is called. +*/ +void gfx_setcolor(float r, float g, float b, float a); + +/* + Function: gfx_quads_setsubset + Sets the uv coordinates to use. + + Arguments: + tl_u - Top-left U value. + tl_v - Top-left V value. + br_u - Bottom-right U value. + br_v - Bottom-right V value. + + Remarks: + O,0 is top-left of the texture and 1,1 is bottom-right. + The color is reset when is called. +*/ +void gfx_quads_setsubset(float tl_u, float tl_v, float br_u, float br_v); + +/* + Function: gfx_quads_drawTL + Draws a quad by specifying the top-left point. + + Arguments: + x - X coordinate of the top-left corner. + y - Y coordinate of the top-left corner. + width - Width of the quad. + height - Height of the quad. + + Remarks: + Rotation still occurs from the center of the quad. + You must call before calling this function. + + See Also: + +*/ +void gfx_quads_drawTL(float x, float y, float width, float height); + +/* + Function: gfx_quads_draw + Draws a quad by specifying the center point. + + Arguments: + x - X coordinate of the center. + y - Y coordinate of the center. + width - Width of the quad. + height - Height of the quad. + + Remarks: + You must call before calling this function. + + See Also: + +*/ +void gfx_quads_draw(float x, float y, float w, float h); + +void gfx_quads_draw_freeform( + float x0, float y0, + float x1, float y1, + float x2, float y2, + float x3, float y3); + +void gfx_quads_text(float x, float y, float size, const char *text); + +/* sound (client) */ +int snd_init(); + +void snd_set_channel(int cid, float vol, float pan); + +int snd_load_wv(const char *filename); + +int snd_play_at(int cid, int sid, int flags, float x, float y); +int snd_play(int cid, int sid, int flags); + +void snd_stop(int id); +void snd_set_listener_pos(float x, float y); + +int snd_shutdown(); + +/* + Group: Input +*/ + +/* + Function: inp_mouse_relative + Fetches the mouse movements. + + Arguments: + x - Pointer to the variable that should get the X movement. + y - Pointer to the variable that should get the Y movement. +*/ +void inp_mouse_relative(int *x, int *y); + +int inp_mouse_scroll(); + +/* + Function: inp_key_pressed + Checks if a key is pressed. + + Arguments: + key - Index to the key to check + + Returns: + Returns 1 if the button is pressed, otherwise 0. + + Remarks: + Check keys.h for the keys. +*/ +int inp_key_pressed(int key); + +/* + Group: Map +*/ + +int map_load(const char *mapname); +void map_unload(); + +/* + Function: map_is_loaded + Checks if a map is loaded. + + Returns: + Returns 1 if the button is pressed, otherwise 0. +*/ +int map_is_loaded(); + +/* + Function: map_num_items + Checks the number of items in the loaded map. + + Returns: + Returns the number of items. 0 if no map is loaded. +*/ +int map_num_items(); + +/* + Function: map_find_item + Searches the map for an item. + + Arguments: + type - Item type. + id - Item ID. + + Returns: + Returns a pointer to the item if it exists, otherwise it returns NULL. +*/ +void *map_find_item(int type, int id); + +/* + Function: map_get_item + Gets an item from the loaded map from index. + + Arguments: + index - Item index. + type - Pointer that recives the item type (can be NULL). + id - Pointer that recives the item id (can be NULL). + + Returns: + Returns a pointer to the item if it exists, otherwise it returns NULL. +*/ +void *map_get_item(int index, int *type, int *id); + +/* + Function: map_get_type + Gets the index range of an item type. + + Arguments: + type - Item type to search for. + start - Pointer that recives the starting index. + num - Pointer that recives the number of items. + + Returns: + If the item type is not in the map, start and num will be set to 0. +*/ +void map_get_type(int type, int *start, int *num); + +/* + Function: map_get_data + Fetches a pointer to a raw data chunk in the map. + + Arguments: + index - Index to the data to fetch. + + Returns: + A pointer to the raw data, otherwise 0. +*/ +void *map_get_data(int index); + +/* + Group: Network (Server) +*/ +/* + Function: snap_new_item + Creates a new item that should be sent. + + Arguments: + type - Type of the item. + id - ID of the item. + size - Size of the item. + + Returns: + A pointer to the item data, otherwise 0. + + Remarks: + The item data should only consist pf 4 byte integers as + they are subject to byte swapping. This means that the size + argument should be dividable by 4. +*/ +void *snap_new_item(int type, int id, int size); + +/* + Group: Network (Client) +*/ +/* + Function: snap_num_items + Check the number of items in a snapshot. + + Arguments: + snapid - Snapshot ID to the data to fetch. + * SNAP_PREV for previous snapshot. + * SNAP_CUR for current snapshot. + + Returns: + The number of items in the snapshot. +*/ +int snap_num_items(int snapid); + +/* + Function: snap_get_item + Gets an item from a snapshot. + + Arguments: + snapid - Snapshot ID to the data to fetch. + * SNAP_PREV for previous snapshot. + * SNAP_CUR for current snapshot. + index - Index of the item. + item - Pointer that recives the item info. + + Returns: + Returns a pointer to the item if it exists, otherwise NULL. +*/ +const void *snap_get_item(int snapid, int index, SNAP_ITEM *item); + +/* + Function: snap_find_item + Searches a snapshot for an item. + + Arguments: + snapid - Snapshot ID to the data to fetch. + * SNAP_PREV for previous snapshot. + * SNAP_CUR for current snapshot. + type - Type of the item. + id - ID of the item. + + Returns: + Returns a pointer to the item if it exists, otherwise NULL. +*/ +const void *snap_find_item(int snapid, int type, int id); + +/* + Function: snap_input + Sets the input data to send to the server. + + Arguments: + data - Pointer to the data. + size - Size of the data. + + Remarks: + The data should only consist of 4 bytes integer as they are + subject to byte swapping. +*/ +void snap_input(void *data, int size); + +/* + Group: Server Callbacks +*/ +/* + Function: mods_init + Called when the server is started. + + Remarks: + It's called after the map is loaded so all map items are available. +*/ +void mods_init(); + +/* + Function: mods_shutdown + Called when the server quits. + + Remarks: + Should be used to clean up all resources used. +*/ +void mods_shutdown(); + +/* + Function: mods_client_enter + Called when a client has joined the game. + + Arguments: + cid - Client ID. Is 0 - MAX_CLIENTS. + + Remarks: + It's called when the client is finished loading and should enter gameplay. +*/ +void mods_client_enter(int cid); + +/* + Function: mods_client_drop + Called when a client drops from the server. + + Arguments: + cid - Client ID. Is 0 - MAX_CLIENTS +*/ +void mods_client_drop(int cid); + +/* + Function: mods_client_input + Called when the server recives new input from a client. + + Arguments: + cid - Client ID. Is 0 - MAX_CLIENTS. + input - Pointer to the input data. + size - Size of the data. (NOT IMPLEMENTED YET) +*/ +void mods_client_input(int cid, void *input); + +/* + Function: mods_tick + Called with a regular interval to progress the gameplay. + + Remarks: + The SERVER_TICK_SPEED tells the number of ticks per second. +*/ +void mods_tick(); + +/* + Function: mods_presnap + Called before the server starts to construct snapshots for the clients. +*/ +void mods_presnap(); + +/* + Function: mods_snap + Called to create the snapshot for a client. + + Arguments: + cid - Client ID. Is 0 - MAX_CLIENTS. + + Remarks: + The game should make a series of calls to to construct + the snapshot for the client. +*/ +void mods_snap(int cid); + +/* + Function: mods_postsnap + Called after the server is done sending the snapshots. +*/ +void mods_postsnap(); + +/* + Group: Client Callbacks +*/ +/* + Function: modc_init + Called when the client starts. + + Remarks: + The game should load resources that are used during the entire + time of the game. No map is loaded. +*/ +void modc_init(); + +/* + Function: modc_newsnapshot + Called when the client progressed to a new snapshot. + + Remarks: + The client can check for items in the snapshot and perform one time + events like playing sounds, spawning client side effects etc. +*/ +void modc_newsnapshot(); + +/* + Function: modc_entergame + Called when the client has successfully connect to a server and + loaded a map. + + Remarks: + The client can check for items in the map and load them. +*/ +void modc_entergame(); + +/* + Function: modc_shutdown + Called when the client closes down. +*/ +void modc_shutdown(); + +/* + Function: modc_render + Called every frame to let the game render it self. +*/ +void modc_render(); + +/* + Function: modc_statechange + Called every time client changes state. +*/ +void modc_statechange(int new_state, int old_state); + +/* undocumented callbacks */ +void modc_connected(); +void modc_message(int msg); +void modc_predict(); + +void mods_message(int msg, int client_id); +void mods_connected(int client_id); + + +const char *modc_net_version(); +const char *mods_net_version(); + +/* server */ +int server_getclientinfo(int client_id, CLIENT_INFO *info); +const char *server_clientname(int client_id); +void server_setclientname(int client_id, const char *name); + +void server_setbrowseinfo(int game_type, int progression); + +int server_tick(); +int server_tickspeed(); + +/* input */ +int inp_key_was_pressed(int key); +int inp_key_down(int key); +char inp_last_char(); +int inp_last_key(); +void inp_clear(); +void inp_update(); +void inp_init(); +void inp_mouse_mode_absolute(); +void inp_mouse_mode_relative(); +int inp_mouse_doubleclick(); + +int inp_key_presses(int key); +int inp_key_releases(int key); +int inp_key_state(int key); + +const char *inp_key_name(int k); +int inp_key_code(const char *key_name); + +/* message packing */ +enum +{ + MSGFLAG_VITAL=1 +}; + +void msg_pack_start_system(int msg, int flags); +void msg_pack_start(int msg, int flags); +void msg_pack_int(int i); +void msg_pack_string(const char *p, int limit); +void msg_pack_raw(const void *data, int size); +void msg_pack_end(); + +typedef struct +{ + int msg; + int flags; + const unsigned char *data; + int size; +} MSG_INFO; + +const MSG_INFO *msg_get_info(); + +/* message unpacking */ +int msg_unpack_start(const void *data, int data_size, int *system); +int msg_unpack_int(); +const char *msg_unpack_string(); +const unsigned char *msg_unpack_raw(int size); + +/* message sending */ +int server_send_msg(int client_id); /* client_id == -1 == broadcast */ +int client_send_msg(); + +/* client */ +int client_tick(); +int client_predtick(); +float client_intratick(); +float client_intrapredtick(); +int client_tickspeed(); +float client_frametime(); +float client_localtime(); + +int client_state(); +const char *client_error_string(); +int *client_get_input(int tick); +int client_connection_problems(); + +void client_connect(const char *address); +void client_disconnect(); +void client_quit(); +void client_entergame(); + +void client_rcon(const char *cmd); + + +void client_serverbrowse_refresh(int lan); + +SERVER_INFO *client_serverbrowse_sorted_get(int index); +int client_serverbrowse_sorted_num(); + +SERVER_INFO *client_serverbrowse_get(int index); +int client_serverbrowse_num(); + +int client_serverbrowse_num_requests(); + +void client_serverbrowse_update(); + +/* undocumented graphics stuff */ + +void gfx_pretty_text_color(float r, float g, float b, float a); +void gfx_pretty_text(float x, float y, float size, const char *text, int max_width); +float gfx_pretty_text_width(float size, const char *text, int length); + +void gfx_getscreen(float *tl_x, float *tl_y, float *br_x, float *br_y); +int gfx_memory_usage(); +void gfx_screenshot(); + +void gfx_lines_begin(); +void gfx_lines_draw(float x0, float y0, float x1, float y1); +void gfx_lines_end(); + +void gfx_mask_op(int mask, int write); +void gfx_clear_mask(int fill); + +/* server snap id */ +int snap_new_id(); +void snap_free_id(int id); + +/* other */ +void map_unload_data(int index); +void map_set(void *m); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/engine/e_keynames.c b/src/engine/e_keynames.c new file mode 100644 index 00000000..fa87467d --- /dev/null +++ b/src/engine/e_keynames.c @@ -0,0 +1,524 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +/* AUTO GENERATED! DO NOT EDIT MANUALLY! */ + +#include + +static const char key_strings[512][16] = +{ + "#0", + "#1", + "#2", + "#3", + "#4", + "#5", + "#6", + "#7", + "#8", + "#9", + "#10", + "#11", + "#12", + "#13", + "#14", + "#15", + "#16", + "#17", + "#18", + "#19", + "#20", + "#21", + "#22", + "#23", + "#24", + "#25", + "#26", + "#27", + "#28", + "#29", + "#30", + "#31", + "space", + "#33", + "#34", + "#35", + "#36", + "#37", + "#38", + "#39", + "#40", + "#41", + "#42", + "#43", + "#44", + "#45", + "#46", + "#47", + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "#58", + "#59", + "#60", + "#61", + "#62", + "#63", + "#64", + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "#91", + "#92", + "#93", + "#94", + "#95", + "#96", + "#97", + "#98", + "#99", + "#100", + "#101", + "#102", + "#103", + "#104", + "#105", + "#106", + "#107", + "#108", + "#109", + "#110", + "#111", + "#112", + "#113", + "#114", + "#115", + "#116", + "#117", + "#118", + "#119", + "#120", + "#121", + "#122", + "#123", + "#124", + "#125", + "#126", + "#127", + "#128", + "#129", + "#130", + "#131", + "#132", + "#133", + "#134", + "#135", + "#136", + "#137", + "#138", + "#139", + "#140", + "#141", + "#142", + "#143", + "#144", + "#145", + "#146", + "#147", + "#148", + "#149", + "#150", + "#151", + "#152", + "#153", + "#154", + "#155", + "#156", + "#157", + "#158", + "#159", + "#160", + "#161", + "#162", + "#163", + "#164", + "#165", + "#166", + "#167", + "#168", + "#169", + "#170", + "#171", + "#172", + "#173", + "#174", + "#175", + "#176", + "#177", + "#178", + "#179", + "#180", + "#181", + "#182", + "#183", + "#184", + "#185", + "#186", + "#187", + "#188", + "#189", + "#190", + "#191", + "#192", + "#193", + "#194", + "#195", + "#196", + "#197", + "#198", + "#199", + "#200", + "#201", + "#202", + "#203", + "#204", + "#205", + "#206", + "#207", + "#208", + "#209", + "#210", + "#211", + "#212", + "#213", + "#214", + "#215", + "#216", + "#217", + "#218", + "#219", + "#220", + "#221", + "#222", + "#223", + "#224", + "#225", + "#226", + "#227", + "#228", + "#229", + "#230", + "#231", + "#232", + "#233", + "#234", + "#235", + "#236", + "#237", + "#238", + "#239", + "#240", + "#241", + "#242", + "#243", + "#244", + "#245", + "#246", + "#247", + "#248", + "#249", + "#250", + "#251", + "#252", + "#253", + "#254", + "#255", + "special", + "esc", + "f1", + "f2", + "f3", + "f4", + "f5", + "f6", + "f7", + "f8", + "f9", + "f10", + "f11", + "f12", + "f13", + "f14", + "f15", + "f16", + "f17", + "f18", + "f19", + "f20", + "f21", + "f22", + "f23", + "f24", + "f25", + "up", + "down", + "left", + "right", + "lshift", + "rshift", + "lctrl", + "rctrl", + "lalt", + "ralt", + "tab", + "enter", + "backspace", + "insert", + "del", + "pageup", + "pagedown", + "home", + "end", + "kp0", + "kp1", + "kp2", + "kp3", + "kp4", + "kp5", + "kp6", + "kp7", + "kp8", + "kp9", + "kpdivide", + "kpmultiply", + "kpsubtract", + "kpadd", + "kpdecimal", + "kpequal", + "kpenter", + "#319", + "#320", + "#321", + "#322", + "#323", + "#324", + "#325", + "#326", + "#327", + "#328", + "#329", + "#330", + "#331", + "#332", + "#333", + "#334", + "#335", + "#336", + "#337", + "#338", + "#339", + "#340", + "#341", + "#342", + "#343", + "#344", + "#345", + "#346", + "#347", + "#348", + "#349", + "#350", + "#351", + "#352", + "#353", + "#354", + "#355", + "#356", + "#357", + "#358", + "#359", + "#360", + "#361", + "#362", + "#363", + "#364", + "#365", + "#366", + "#367", + "#368", + "#369", + "#370", + "#371", + "#372", + "#373", + "#374", + "#375", + "#376", + "#377", + "#378", + "#379", + "#380", + "#381", + "mousewheeldown", + "mousewheelup", + "mouse1", + "mouse2", + "mouse3", + "mouse4", + "mouse5", + "mouse6", + "mouse7", + "mouse8", + "#392", + "#393", + "#394", + "#395", + "#396", + "#397", + "#398", + "#399", + "#400", + "#401", + "#402", + "#403", + "#404", + "#405", + "#406", + "#407", + "#408", + "#409", + "#410", + "#411", + "#412", + "#413", + "#414", + "#415", + "#416", + "#417", + "#418", + "#419", + "#420", + "#421", + "#422", + "#423", + "#424", + "#425", + "#426", + "#427", + "#428", + "#429", + "#430", + "#431", + "#432", + "#433", + "#434", + "#435", + "#436", + "#437", + "#438", + "#439", + "#440", + "#441", + "#442", + "#443", + "#444", + "#445", + "#446", + "#447", + "#448", + "#449", + "#450", + "#451", + "#452", + "#453", + "#454", + "#455", + "#456", + "#457", + "#458", + "#459", + "#460", + "#461", + "#462", + "#463", + "#464", + "#465", + "#466", + "#467", + "#468", + "#469", + "#470", + "#471", + "#472", + "#473", + "#474", + "#475", + "#476", + "#477", + "#478", + "#479", + "#480", + "#481", + "#482", + "#483", + "#484", + "#485", + "#486", + "#487", + "#488", + "#489", + "#490", + "#491", + "#492", + "#493", + "#494", + "#495", + "#496", + "#497", + "#498", + "#499", + "#500", + "#501", + "#502", + "#503", + "#504", + "#505", + "#506", + "#507", + "#508", + "#509", + "#510", + "#511", +}; + +const char *inp_key_name(int k) { if (k >= 0 && k < 512) return key_strings[k]; else return key_strings[0]; } +int inp_key_code(const char *key_name) { int i; if (!strcmp(key_name, "-?-")) return -1; else for (i = 0; i < 512; i++) if (!strcmp(key_strings[i], key_name)) return i; return -1; } + diff --git a/src/engine/e_keys.h b/src/engine/e_keys.h new file mode 100644 index 00000000..77a52f58 --- /dev/null +++ b/src/engine/e_keys.h @@ -0,0 +1,88 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#ifndef ENGINE_KEYS_H +#define ENGINE_KEYS_H +/* AUTO GENERATED! DO NOT EDIT MANUALLY! */ + +enum +{ + KEY_UNKNOWN = -1, + KEY_SPACE = 32, + KEY_SPECIAL = 256, + KEY_ESC = (KEY_SPECIAL+1), + KEY_F1 = (KEY_SPECIAL+2), + KEY_F2 = (KEY_SPECIAL+3), + KEY_F3 = (KEY_SPECIAL+4), + KEY_F4 = (KEY_SPECIAL+5), + KEY_F5 = (KEY_SPECIAL+6), + KEY_F6 = (KEY_SPECIAL+7), + KEY_F7 = (KEY_SPECIAL+8), + KEY_F8 = (KEY_SPECIAL+9), + KEY_F9 = (KEY_SPECIAL+10), + KEY_F10 = (KEY_SPECIAL+11), + KEY_F11 = (KEY_SPECIAL+12), + KEY_F12 = (KEY_SPECIAL+13), + KEY_F13 = (KEY_SPECIAL+14), + KEY_F14 = (KEY_SPECIAL+15), + KEY_F15 = (KEY_SPECIAL+16), + KEY_F16 = (KEY_SPECIAL+17), + KEY_F17 = (KEY_SPECIAL+18), + KEY_F18 = (KEY_SPECIAL+19), + KEY_F19 = (KEY_SPECIAL+20), + KEY_F20 = (KEY_SPECIAL+21), + KEY_F21 = (KEY_SPECIAL+22), + KEY_F22 = (KEY_SPECIAL+23), + KEY_F23 = (KEY_SPECIAL+24), + KEY_F24 = (KEY_SPECIAL+25), + KEY_F25 = (KEY_SPECIAL+26), + KEY_UP = (KEY_SPECIAL+27), + KEY_DOWN = (KEY_SPECIAL+28), + KEY_LEFT = (KEY_SPECIAL+29), + KEY_RIGHT = (KEY_SPECIAL+30), + KEY_LSHIFT = (KEY_SPECIAL+31), + KEY_RSHIFT = (KEY_SPECIAL+32), + KEY_LCTRL = (KEY_SPECIAL+33), + KEY_RCTRL = (KEY_SPECIAL+34), + KEY_LALT = (KEY_SPECIAL+35), + KEY_RALT = (KEY_SPECIAL+36), + KEY_TAB = (KEY_SPECIAL+37), + KEY_ENTER = (KEY_SPECIAL+38), + KEY_BACKSPACE = (KEY_SPECIAL+39), + KEY_INSERT = (KEY_SPECIAL+40), + KEY_DEL = (KEY_SPECIAL+41), + KEY_PAGEUP = (KEY_SPECIAL+42), + KEY_PAGEDOWN = (KEY_SPECIAL+43), + KEY_HOME = (KEY_SPECIAL+44), + KEY_END = (KEY_SPECIAL+45), + KEY_KP_0 = (KEY_SPECIAL+46), + KEY_KP_1 = (KEY_SPECIAL+47), + KEY_KP_2 = (KEY_SPECIAL+48), + KEY_KP_3 = (KEY_SPECIAL+49), + KEY_KP_4 = (KEY_SPECIAL+50), + KEY_KP_5 = (KEY_SPECIAL+51), + KEY_KP_6 = (KEY_SPECIAL+52), + KEY_KP_7 = (KEY_SPECIAL+53), + KEY_KP_8 = (KEY_SPECIAL+54), + KEY_KP_9 = (KEY_SPECIAL+55), + KEY_KP_DIVIDE = (KEY_SPECIAL+56), + KEY_KP_MULTIPLY = (KEY_SPECIAL+57), + KEY_KP_SUBTRACT = (KEY_SPECIAL+58), + KEY_KP_ADD = (KEY_SPECIAL+59), + KEY_KP_DECIMAL = (KEY_SPECIAL+60), + KEY_KP_EQUAL = (KEY_SPECIAL+61), + KEY_KP_ENTER = (KEY_SPECIAL+62), + KEY_REPEAT = 0x00030005, + KEY_MOUSE_WHEEL_DOWN = 382, + KEY_MOUSE_WHEEL_UP = 383, + KEY_MOUSE_FIRST = 384, + KEY_MOUSE_1 = KEY_MOUSE_FIRST+0, + KEY_MOUSE_2 = KEY_MOUSE_FIRST+1, + KEY_MOUSE_3 = KEY_MOUSE_FIRST+2, + KEY_MOUSE_4 = KEY_MOUSE_FIRST+3, + KEY_MOUSE_5 = KEY_MOUSE_FIRST+4, + KEY_MOUSE_6 = KEY_MOUSE_FIRST+5, + KEY_MOUSE_7 = KEY_MOUSE_FIRST+6, + KEY_MOUSE_8 = KEY_MOUSE_FIRST+7, + KEY_LAST +}; + +#endif diff --git a/src/engine/e_map.c b/src/engine/e_map.c new file mode 100644 index 00000000..6dfa4aa6 --- /dev/null +++ b/src/engine/e_map.c @@ -0,0 +1,61 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include +#include "e_datafile.h" + +static DATAFILE *map = 0; + +void *map_get_data(int index) +{ + return datafile_get_data(map, index); +} + +void map_unload_data(int index) +{ + datafile_unload_data(map, index); +} + +void *map_get_item(int index, int *type, int *id) +{ + return datafile_get_item(map, index, type, id); +} + +void map_get_type(int type, int *start, int *num) +{ + datafile_get_type(map, type, start, num); +} + +void *map_find_item(int type, int id) +{ + return datafile_find_item(map, type, id); +} + +int map_num_items() +{ + return datafile_num_items(map); +} + +void map_unload() +{ + datafile_unload(map); + map = 0x0; +} + +int map_is_loaded() +{ + return map != 0; +} + +int map_load(const char *mapname) +{ + char buf[512]; + sprintf(buf, "data/maps/%s.map", mapname); + map = datafile_load(buf); + return map != 0; +} + +void map_set(void *m) +{ + if(map) + map_unload(); + map = (DATAFILE*)m; +} diff --git a/src/engine/e_memheap.c b/src/engine/e_memheap.c new file mode 100644 index 00000000..6d117631 --- /dev/null +++ b/src/engine/e_memheap.c @@ -0,0 +1,102 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include "e_system.h" + +typedef struct CHUNK_t +{ + char *memory; + char *current; + char *end; + struct CHUNK_t *next; +} CHUNK; + +typedef struct +{ + CHUNK *current; +} HEAP; + +/* how large each chunk should be */ +static const int chunksize = 1024*64; + +/* allocates a new chunk to be used */ +static CHUNK *memheap_newchunk() +{ + CHUNK *chunk; + char *mem; + + /* allocate memory */ + mem = (char*)mem_alloc(sizeof(CHUNK)+chunksize, 1); + if(!mem) + return 0x0; + + /* the chunk structure is located in the begining of the chunk */ + /* init it and return the chunk */ + chunk = (CHUNK*)mem; + chunk->memory = (char*)(chunk+1); + chunk->current = chunk->memory; + chunk->end = chunk->memory + chunksize; + chunk->next = (CHUNK *)0x0; + return chunk; +} + +/******************/ +static void *memheap_allocate_from_chunk(CHUNK *chunk, int size) +{ + char *mem; + + /* check if we need can fit the allocation */ + if(chunk->current + size >= chunk->end) + return (void*)0x0; + + /* get memory and move the pointer forward */ + mem = chunk->current; + chunk->current += size; + return mem; +} + +/* creates a heap */ +HEAP *memheap_create() +{ + CHUNK *chunk; + HEAP *heap; + + /* allocate a chunk and allocate the heap structure on that chunk */ + chunk = memheap_newchunk(); + heap = (HEAP *)memheap_allocate_from_chunk(chunk, sizeof(HEAP)); + heap->current = chunk; + return heap; +} + +/* destroys the heap */ +void memheap_destroy(HEAP *heap) +{ + CHUNK *chunk = heap->current; + CHUNK *next; + + while(chunk) + { + next = chunk->next; + mem_free(chunk); + chunk = next; + } +} + +/* */ +void *memheap_allocate(HEAP *heap, int size) +{ + char *mem; + + /* try to allocate from current chunk */ + mem = (char *)memheap_allocate_from_chunk(heap->current, size); + if(!mem) + { + /* allocate new chunk and add it to the heap */ + CHUNK *chunk = memheap_newchunk(); + chunk->next = heap->current; + heap->current = chunk; + + /* try to allocate again */ + mem = (char *)memheap_allocate_from_chunk(heap->current, size); + } + + return mem; +} diff --git a/src/engine/e_memheap.h b/src/engine/e_memheap.h new file mode 100644 index 00000000..83082046 --- /dev/null +++ b/src/engine/e_memheap.h @@ -0,0 +1,6 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ + +typedef struct HEAP_t HEAP; +HEAP *memheap_create(); +void memheap_destroy(HEAP *heap); +void *memheap_allocate(HEAP *heap, int size); diff --git a/src/engine/e_msg.c b/src/engine/e_msg.c new file mode 100644 index 00000000..adfb1cd3 --- /dev/null +++ b/src/engine/e_msg.c @@ -0,0 +1,55 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include "e_interface.h" +#include "e_packer.h" + +/* message packing */ +static PACKER msg_packer; +static MSG_INFO pack_info; + +void msg_pack_int(int i) { packer_add_int(&msg_packer, i); } +void msg_pack_string(const char *p, int limit) { packer_add_string(&msg_packer, p, limit); } +void msg_pack_raw(const void *data, int size) { packer_add_raw(&msg_packer, (const unsigned char *)data, size); } + +void msg_pack_start_system(int msg, int flags) +{ + packer_reset(&msg_packer); + pack_info.msg = (msg<<1)|1; + pack_info.flags = flags; + + msg_pack_int(pack_info.msg); +} + +void msg_pack_start(int msg, int flags) +{ + packer_reset(&msg_packer); + pack_info.msg = msg<<1; + pack_info.flags = flags; + + msg_pack_int(pack_info.msg); +} + +void msg_pack_end() +{ + pack_info.size = packer_size(&msg_packer); + pack_info.data = packer_data(&msg_packer); +} + +const MSG_INFO *msg_get_info() +{ + return &pack_info; +} + +/* message unpacking */ +static UNPACKER msg_unpacker; +int msg_unpack_start(const void *data, int data_size, int *system) +{ + int msg; + unpacker_reset(&msg_unpacker, (const unsigned char *)data, data_size); + msg = msg_unpack_int(); + *system = msg&1; + return msg>>1; +} + +int msg_unpack_int() { return unpacker_get_int(&msg_unpacker); } +const char *msg_unpack_string() { return unpacker_get_string(&msg_unpacker); } +const unsigned char *msg_unpack_raw(int size) { return unpacker_get_raw(&msg_unpacker, size); } diff --git a/src/engine/e_network.c b/src/engine/e_network.c new file mode 100644 index 00000000..b26e75f0 --- /dev/null +++ b/src/engine/e_network.c @@ -0,0 +1,937 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include +#include + +#include "e_system.h" +#include "e_config.h" +#include "e_network.h" + +/* + header (6 bytes) + unsigned char flags; 1 + unsigned char seq_ack[3]; 4 + unsigned char token[2]; 6 +*/ + +enum +{ + NETWORK_VERSION = 1, + + NETWORK_HEADER_SIZE = 6, + NETWORK_MAX_PAYLOAD = 1024, + NETWORK_MAX_PACKET_SIZE = NETWORK_HEADER_SIZE+NETWORK_MAX_PAYLOAD, + NETWORK_MAX_CLIENTS = 16, + + NETWORK_CONNSTATE_OFFLINE=0, + NETWORK_CONNSTATE_CONNECT=1, + NETWORK_CONNSTATE_CONNECTACCEPTED=2, + NETWORK_CONNSTATE_ONLINE=3, + NETWORK_CONNSTATE_ERROR=4, + + NETWORK_PACKETFLAG_CONNECT=0x01, + NETWORK_PACKETFLAG_ACCEPT=0x02, + NETWORK_PACKETFLAG_CLOSE=0x04, + NETWORK_PACKETFLAG_VITAL=0x08, + NETWORK_PACKETFLAG_RESEND=0x10, + NETWORK_PACKETFLAG_CONNLESS=0x20, + + NETWORK_MAX_SEQACK=0x1000 +}; + +static int current_token = 1; + +typedef struct +{ + unsigned char ID[2]; + unsigned char version; + unsigned char flags; + unsigned short seq; + unsigned short ack; + unsigned crc; + int token; + unsigned data_size; + int64 first_send_time; + unsigned char *data; +} NETPACKETDATA; + + +static void send_packet(NETSOCKET socket, NETADDR4 *addr, NETPACKETDATA *packet) +{ + unsigned char buffer[NETWORK_MAX_PACKET_SIZE]; + int send_size = NETWORK_HEADER_SIZE+packet->data_size; + + buffer[0] = packet->flags; + buffer[1] = ((packet->seq>>4)&0xf0) | ((packet->ack>>8)&0x0f); + buffer[2] = packet->seq; + buffer[3] = packet->ack; + buffer[4] = packet->token>>8; + buffer[5] = packet->token&0xff; + mem_copy(buffer+NETWORK_HEADER_SIZE, packet->data, packet->data_size); + net_udp4_send(socket, addr, buffer, send_size); +} + +typedef struct RINGBUFFER_ITEM_t +{ + struct RINGBUFFER_ITEM_t *next; + struct RINGBUFFER_ITEM_t *prev; + int size; +} RINGBUFFER_ITEM; + +typedef struct +{ + RINGBUFFER_ITEM *first; + RINGBUFFER_ITEM *last; + unsigned buffer_size; +} RINGBUFFER; + +static void rb_init(RINGBUFFER *rb) +{ + rb->first = 0; + rb->last = 0; + rb->buffer_size = 0; +} + +static void *rb_item_data(RINGBUFFER_ITEM *item) +{ + return (void*)(item+1); +} + +static void *rb_alloc(RINGBUFFER *rb, int size) +{ + RINGBUFFER_ITEM *item = (RINGBUFFER_ITEM*)mem_alloc(sizeof(RINGBUFFER_ITEM)+size, 1); + item->size = size; + + item->prev = rb->last; + item->next = 0; + if(rb->last) + rb->last->next = item; + else + rb->first = item; + rb->last = item; + + rb->buffer_size += size; + return rb_item_data(item); +} + +static void rb_pop_first(RINGBUFFER *rb) +{ + if(rb->first) + { + RINGBUFFER_ITEM *next = rb->first->next; + rb->buffer_size -= rb->first->size; + mem_free(rb->first); + rb->first = next; + if(rb->first) + rb->first->prev = NULL; + else + rb->last = NULL; + } +} + +static void rb_clear(RINGBUFFER *rb) +{ + while(rb->first) + rb_pop_first(rb); +} + + +typedef struct +{ + unsigned short seq; + unsigned short ack; + unsigned state; + + int token; + + int remote_closed; + + int connected; + int disconnected; + + RINGBUFFER buffer; + + int64 last_update_time; + int64 last_recv_time; + int64 last_send_time; + + char error_string[256]; + + NETADDR4 peeraddr; + NETSOCKET socket; + NETSTATS stats; +} NETCONNECTION; + +typedef struct +{ + NETCONNECTION conn; +} NETSLOT; + +struct NETSERVER_t +{ + NETSOCKET socket; + NETSLOT slots[NETWORK_MAX_CLIENTS]; + int max_clients; + NETFUNC_NEWCLIENT new_client; + NETFUNC_NEWCLIENT del_client; + void *user_ptr; + unsigned char recv_buffer[NETWORK_MAX_PACKET_SIZE]; +} ; + +struct NETCLIENT_t +{ + NETADDR4 server_addr; + NETSOCKET socket; + unsigned char recv_buffer[NETWORK_MAX_PACKET_SIZE]; + + NETCONNECTION conn; +}; + +static void conn_reset_stats(NETCONNECTION *conn) +{ + mem_zero(&conn->stats, sizeof(conn->stats)); +} + +static void conn_reset(NETCONNECTION *conn) +{ + conn->seq = 0; + conn->ack = 0; + conn->remote_closed = 0; + + if(conn->state == NETWORK_CONNSTATE_ONLINE || + conn->state == NETWORK_CONNSTATE_ERROR) + { + conn->disconnected++; + } + + conn->state = NETWORK_CONNSTATE_OFFLINE; + conn->last_send_time = 0; + conn->last_recv_time = 0; + conn->last_update_time = 0; + conn->token = -1; + + rb_clear(&conn->buffer); +} + + +static const char *conn_error(NETCONNECTION *conn) +{ + return conn->error_string; +} + +static void conn_set_error(NETCONNECTION *conn, const char *str) +{ + strcpy(conn->error_string, str); +} + +/* +static int conn_state(NETCONNECTION *conn) +{ + return conn->state; +}*/ + +static void conn_init(NETCONNECTION *conn, NETSOCKET socket) +{ + conn_reset(conn); + conn_reset_stats(conn); + conn->socket = socket; + conn->connected = 0; + conn->disconnected = 0; + rb_init(&conn->buffer); + mem_zero(conn->error_string, sizeof(conn->error_string)); +} + +static void conn_ack(NETCONNECTION *conn, int ack) +{ + while(1) + { + RINGBUFFER_ITEM *item = conn->buffer.first; + NETPACKETDATA *resend; + if(!item) + break; + + resend = (NETPACKETDATA *)rb_item_data(item); + if(resend->seq <= ack || (ack < NETWORK_MAX_SEQACK/3 && resend->seq > NETWORK_MAX_SEQACK/2)) + rb_pop_first(&conn->buffer); + else + break; + } +} + +static void conn_send_raw(NETCONNECTION *conn, NETPACKETDATA *data) +{ + conn->last_send_time = time_get(); + conn->stats.send_packets++; + conn->stats.send_bytes += data->data_size + NETWORK_HEADER_SIZE; + send_packet(conn->socket, &conn->peeraddr, data); +} + +static void conn_resend(NETCONNECTION *conn) +{ + RINGBUFFER_ITEM *item = conn->buffer.first; + while(item) + { + NETPACKETDATA *resend = (NETPACKETDATA *)rb_item_data(item); + conn->stats.resend_packets++; + conn->stats.resend_bytes += resend->data_size + NETWORK_HEADER_SIZE; + conn_send_raw(conn, resend); + item = item->next; + } +} + +static void conn_send(NETCONNECTION *conn, int flags, int data_size, const void *data) +{ + NETPACKETDATA p; + + if(flags&NETWORK_PACKETFLAG_VITAL) + conn->seq = (conn->seq+1)%NETWORK_MAX_SEQACK; + + p.ID[0] = 'T'; + p.ID[1] = 'W'; + p.version = NETWORK_VERSION; + p.flags = flags; + p.seq = conn->seq; + p.ack = conn->ack; + p.crc = 0; + p.token = conn->token; + p.data_size = data_size; + p.data = (unsigned char *)data; + p.first_send_time = time_get(); + + if(flags&NETWORK_PACKETFLAG_VITAL) + { + /* save packet if we need to resend */ + NETPACKETDATA *resend = (NETPACKETDATA *)rb_alloc(&conn->buffer, sizeof(NETPACKETDATA)+data_size); + *resend = p; + resend->data = (unsigned char *)(resend+1); + mem_copy(resend->data, p.data, p.data_size); + } + + /* TODO: calc crc */ + conn_send_raw(conn, &p); +} + +static int conn_connect(NETCONNECTION *conn, NETADDR4 *addr) +{ + if(conn->state != NETWORK_CONNSTATE_OFFLINE) + return -1; + + /* init connection */ + conn_reset(conn); + conn->peeraddr = *addr; + conn->token = current_token++; + mem_zero(conn->error_string, sizeof(conn->error_string)); + conn->state = NETWORK_CONNSTATE_CONNECT; + conn_send(conn, NETWORK_PACKETFLAG_CONNECT, 0, 0); + return 0; +} + +static void conn_disconnect(NETCONNECTION *conn, const char *reason) +{ + if(conn->remote_closed == 0) + { + if(reason) + conn_send(conn, NETWORK_PACKETFLAG_CLOSE, strlen(reason)+1, reason); + else + conn_send(conn, NETWORK_PACKETFLAG_CLOSE, 0, 0); + + conn->error_string[0] = 0; + if(reason) + strcpy(conn->error_string, reason); + } + + conn_reset(conn); +} + +static int conn_feed(NETCONNECTION *conn, NETPACKETDATA *p, NETADDR4 *addr) +{ + conn->last_recv_time = time_get(); + conn->stats.recv_packets++; + conn->stats.recv_bytes += p->data_size + NETWORK_HEADER_SIZE; + + if(p->flags&NETWORK_PACKETFLAG_CLOSE) + { + conn->state = NETWORK_CONNSTATE_ERROR; + conn->remote_closed = 1; + + if(p->data_size) + conn_set_error(conn, (char *)p->data); + else + conn_set_error(conn, "no reason given"); + if(config.debug) + dbg_msg("conn", "closed reason='%s'", conn_error(conn)); + return 0; + } + + if(conn->state == NETWORK_CONNSTATE_OFFLINE) + { + if(p->flags == NETWORK_PACKETFLAG_CONNECT) + { + /* send response and init connection */ + conn->state = NETWORK_CONNSTATE_ONLINE; + conn->connected++; + conn->peeraddr = *addr; + conn->token = p->token; + conn_send(conn, NETWORK_PACKETFLAG_CONNECT|NETWORK_PACKETFLAG_ACCEPT, 0, 0); + if(config.debug) + dbg_msg("connection", "got connection, sending connect+accept"); + } + } + else if(net_addr4_cmp(&conn->peeraddr, addr) == 0) + { + if(p->token != conn->token) + return 0; + + if(conn->state == NETWORK_CONNSTATE_ONLINE) + { + /* remove packages that are acked */ + conn_ack(conn, p->ack); + + /* check if resend is requested */ + if(p->flags&NETWORK_PACKETFLAG_RESEND) + conn_resend(conn); + + if(p->flags&NETWORK_PACKETFLAG_VITAL) + { + if(p->seq == (conn->ack+1)%NETWORK_MAX_SEQACK) + { + /* in sequence */ + conn->ack = (conn->ack+1)%NETWORK_MAX_SEQACK; + } + else + { + /* out of sequence, request resend */ + dbg_msg("conn", "asking for resend %d %d", p->seq, (conn->ack+1)%NETWORK_MAX_SEQACK); + conn_send(conn, NETWORK_PACKETFLAG_RESEND, 0, 0); + return 0; + } + } + else + { + if(p->seq > conn->ack) + conn_send(conn, NETWORK_PACKETFLAG_RESEND, 0, 0); + } + + if(p->data_size == 0) + return 0; + + return 1; + } + else if(conn->state == NETWORK_CONNSTATE_CONNECT) + { + /* connection made */ + if(p->flags == (NETWORK_PACKETFLAG_CONNECT|NETWORK_PACKETFLAG_ACCEPT)) + { + conn_send(conn, NETWORK_PACKETFLAG_ACCEPT, 0, 0); + conn->state = NETWORK_CONNSTATE_ONLINE; + conn->connected++; + dbg_msg("connection", "got connect+accept, sending accept. connection online"); + } + } + /* + else if(conn->state == NETWORK_CONNSTATE_CONNECTACCEPTED) + { + // connection made + if(p->flags == NETWORK_PACKETFLAG_ACCEPT) + { + conn->state = NETWORK_CONNSTATE_ONLINE; + dbg_msg("connection", "got accept. connection online"); + } + }*/ + else + { + /* strange packet, wrong state */ + conn->state = NETWORK_CONNSTATE_ERROR; + conn_set_error(conn, "strange state and packet"); + } + } + else + { + /* strange packet, not ment for me */ + } + + return 0; +} + + + +static int conn_update(NETCONNECTION *conn) +{ + int64 now = time_get(); + + if(conn->state == NETWORK_CONNSTATE_OFFLINE || conn->state == NETWORK_CONNSTATE_ERROR) + return 0; + + /* watch out for major hitches */ + { + int64 delta = now-conn->last_update_time; + if(conn->last_update_time && delta > time_freq()/2) + { + RINGBUFFER_ITEM *item = conn->buffer.first; + + dbg_msg("conn", "hitch %d", (int)((delta*1000)/time_freq())); + conn->last_recv_time += delta; + + while(item) + { + NETPACKETDATA *resend = (NETPACKETDATA *)rb_item_data(item); + resend->first_send_time += delta; + item = item->next; + } + } + + conn->last_update_time = now; + } + + + /* check for timeout */ + if(conn->state != NETWORK_CONNSTATE_OFFLINE && + conn->state != NETWORK_CONNSTATE_CONNECT && + (now-conn->last_recv_time) > time_freq()*10) + { + conn->state = NETWORK_CONNSTATE_ERROR; + conn_set_error(conn, "timeout"); + } + + /* check for large buffer errors */ + if(conn->buffer.buffer_size > 1024*64) + { + conn->state = NETWORK_CONNSTATE_ERROR; + conn_set_error(conn, "too weak connection (out of buffer)"); + } + + if(conn->buffer.first) + { + NETPACKETDATA *resend = (NETPACKETDATA *)(conn->buffer.first+1); + if(now-resend->first_send_time > time_freq()*10) + { + conn->state = NETWORK_CONNSTATE_ERROR; + conn_set_error(conn, "too weak connection (not acked for 10 seconds)"); + } + } + + /* send keep alives if nothing has happend for 250ms */ + if(conn->state == NETWORK_CONNSTATE_ONLINE) + { + if(time_get()-conn->last_send_time> time_freq()/4) + conn_send(conn, NETWORK_PACKETFLAG_VITAL, 0, 0); + } + else if(conn->state == NETWORK_CONNSTATE_CONNECT) + { + if(time_get()-conn->last_send_time > time_freq()/2) /* send a new connect every 500ms */ + conn_send(conn, NETWORK_PACKETFLAG_CONNECT, 0, 0); + } + else if(conn->state == NETWORK_CONNSTATE_CONNECTACCEPTED) + { + if(time_get()-conn->last_send_time > time_freq()/2) /* send a new connect/accept every 500ms */ + conn_send(conn, NETWORK_PACKETFLAG_CONNECT|NETWORK_PACKETFLAG_ACCEPT, 0, 0); + } + + return 0; +} + + +static int check_packet(unsigned char *buffer, int size, NETPACKETDATA *packet) +{ + /* check the size */ + if(size < NETWORK_HEADER_SIZE || size > NETWORK_MAX_PACKET_SIZE) + return -1; + + /* read the packet */ + packet->ID[0] = 'T'; + packet->ID[1] = 'W'; + packet->version = NETWORK_VERSION; + packet->flags = buffer[0]; + packet->seq = ((buffer[1]&0xf0)<<4)|buffer[2]; + packet->ack = ((buffer[1]&0x0f)<<8)|buffer[3]; + packet->crc = 0; + packet->token = (buffer[4]<<8)|buffer[5]; + packet->data_size = size - NETWORK_HEADER_SIZE; + packet->data = buffer+NETWORK_HEADER_SIZE; + + /* check the packet */ + if(packet->ID[0] != 'T' || packet->ID[1] != 'W') + return 1; + + if(packet->version != NETWORK_VERSION) + return 1; + + /* TODO: perform crc check */ + + /* return success */ + return 0; +} + +NETSERVER *netserver_open(NETADDR4 bindaddr, int max_clients, int flags) +{ + int i; + NETSERVER *server; + NETSOCKET socket = net_udp4_create(bindaddr); + if(socket == NETSOCKET_INVALID) + return 0; + + server = (NETSERVER *)mem_alloc(sizeof(NETSERVER), 1); + mem_zero(server, sizeof(NETSERVER)); + server->socket = socket; + server->max_clients = max_clients; + if(server->max_clients > NETWORK_MAX_CLIENTS) + server->max_clients = NETWORK_MAX_CLIENTS; + if(server->max_clients < 1) + server->max_clients = 1; + + for(i = 0; i < NETWORK_MAX_CLIENTS; i++) + conn_init(&server->slots[i].conn, server->socket); + + return server; +} + +int netserver_set_callbacks(NETSERVER *s, NETFUNC_NEWCLIENT new_client, NETFUNC_DELCLIENT del_client, void *user) +{ + s->new_client = new_client; + s->del_client = del_client; + s->user_ptr = user; + return 0; +} + +int netserver_max_clients(NETSERVER *s) +{ + return s->max_clients; +} + +int netserver_close(NETSERVER *s) +{ + /* TODO: implement me */ + return 0; +} + +int netserver_drop(NETSERVER *s, int client_id, const char *reason) +{ + /* TODO: insert lots of checks here */ + dbg_msg("net_server", "client dropped. cid=%d reason=\"%s\"", client_id, reason); + conn_disconnect(&s->slots[client_id].conn, reason); + + if(s->del_client) + s->del_client(client_id, s->user_ptr); + + return 0; +} + +int netserver_update(NETSERVER *s) +{ + int i; + for(i = 0; i < s->max_clients; i++) + { + conn_update(&s->slots[i].conn); + if(s->slots[i].conn.state == NETWORK_CONNSTATE_ERROR) + netserver_drop(s, i, conn_error(&s->slots[i].conn)); + } + return 0; +} + +int netserver_recv(NETSERVER *s, NETPACKET *packet) +{ + NETPACKETDATA data; + int i, r, bytes, found; + NETADDR4 addr; + + while(1) + { + bytes = net_udp4_recv(s->socket, &addr, s->recv_buffer, NETWORK_MAX_PACKET_SIZE); + + /* no more packets for now */ + if(bytes <= 0) + break; + + r = check_packet(s->recv_buffer, bytes, &data); + if(r == 0) + { + if(data.flags&NETWORK_PACKETFLAG_CONNLESS) + { + /* connection less packets */ + packet->client_id = -1; + packet->address = addr; + packet->flags = PACKETFLAG_CONNLESS; + packet->data_size = data.data_size; + packet->data = data.data; + return 1; + } + else + { + /* ok packet, process it */ + if(data.flags == NETWORK_PACKETFLAG_CONNECT) + { + found = 0; + + /* check if we already got this client */ + for(i = 0; i < s->max_clients; i++) + { + if(s->slots[i].conn.state != NETWORK_CONNSTATE_OFFLINE && + net_addr4_cmp(&s->slots[i].conn.peeraddr, &addr) == 0) + { + found = 1; /* silent ignore.. we got this client already */ + break; + } + } + + /* client that wants to connect */ + if(!found) + { + for(i = 0; i < s->max_clients; i++) + { + if(s->slots[i].conn.state == NETWORK_CONNSTATE_OFFLINE) + { + found = 1; + conn_feed(&s->slots[i].conn, &data, &addr); + if(s->new_client) + s->new_client(i, s->user_ptr); + break; + } + } + } + + if(!found) + { + /* send connectionless packet */ + const char errstring[] = "server full"; + NETPACKETDATA p; + p.ID[0] = 'T'; + p.ID[1] = 'W'; + p.version = NETWORK_VERSION; + p.flags = NETWORK_PACKETFLAG_CLOSE; + p.seq = 0; + p.ack = 0; + p.crc = 0; + p.token = data.token; + p.data_size = sizeof(errstring); + p.data = (unsigned char *)errstring; + send_packet(s->socket, &addr, &p); + } + } + else + { + /* find matching slot */ + for(i = 0; i < s->max_clients; i++) + { + if(net_addr4_cmp(&s->slots[i].conn.peeraddr, &addr) == 0) + { + if(conn_feed(&s->slots[i].conn, &data, &addr)) + { + if(data.data_size) + { + packet->client_id = i; + packet->address = addr; + packet->flags = 0; + packet->data_size = data.data_size; + packet->data = data.data; + return 1; + } + } + } + } + } + } + } + else + { + /* errornous packet, drop it */ + dbg_msg("server", "crazy packet"); + } + + /* read header */ + /* do checksum */ + } + + return 0; +} + +int netserver_send(NETSERVER *s, NETPACKET *packet) +{ + dbg_assert(packet->data_size < NETWORK_MAX_PAYLOAD, "packet payload too big"); + + if(packet->flags&PACKETFLAG_CONNLESS) + { + /* send connectionless packet */ + NETPACKETDATA p; + p.ID[0] = 'T'; + p.ID[1] = 'W'; + p.version = NETWORK_VERSION; + p.flags = NETWORK_PACKETFLAG_CONNLESS; + p.seq = 0; + p.ack = 0; + p.crc = 0; + p.data_size = packet->data_size; + p.data = (unsigned char *)packet->data; + send_packet(s->socket, &packet->address, &p); + } + else + { + int flags = 0; + dbg_assert(packet->client_id >= 0, "errornous client id"); + dbg_assert(packet->client_id < s->max_clients, "errornous client id"); + if(packet->flags&PACKETFLAG_VITAL) + flags |= NETWORK_PACKETFLAG_VITAL; + conn_send(&s->slots[packet->client_id].conn, flags, packet->data_size, packet->data); + } + return 0; +} + +void netserver_stats(NETSERVER *s, NETSTATS *stats) +{ + int num_stats = sizeof(NETSTATS)/sizeof(int); + int *istats = (int *)stats; + int c, i; + + mem_zero(stats, sizeof(NETSTATS)); + + for(c = 0; c < s->max_clients; c++) + { + if(s->slots[c].conn.state != NETWORK_CONNSTATE_OFFLINE) + { + int *sstats = (int *)(&(s->slots[c].conn.stats)); + for(i = 0; i < num_stats; i++) + istats[i] += sstats[i]; + } + } +} + +NETCLIENT *netclient_open(NETADDR4 bindaddr, int flags) +{ + NETCLIENT *client = (NETCLIENT *)mem_alloc(sizeof(NETCLIENT), 1); + mem_zero(client, sizeof(NETCLIENT)); + client->socket = net_udp4_create(bindaddr); + conn_init(&client->conn, client->socket); + return client; +} + +int netclient_close(NETCLIENT *c) +{ + /* TODO: implement me */ + return 0; +} + +int netclient_update(NETCLIENT *c) +{ + conn_update(&c->conn); + if(c->conn.state == NETWORK_CONNSTATE_ERROR) + netclient_disconnect(c, conn_error(&c->conn)); + return 0; +} + +int netclient_disconnect(NETCLIENT *c, const char *reason) +{ + dbg_msg("netclient", "disconnected. reason=\"%s\"", reason); + conn_disconnect(&c->conn, reason); + return 0; +} + +int netclient_connect(NETCLIENT *c, NETADDR4 *addr) +{ + conn_connect(&c->conn, addr); + return 0; +} + +int netclient_recv(NETCLIENT *c, NETPACKET *packet) +{ + while(1) + { + NETADDR4 addr; + NETPACKETDATA data; + int r; + int bytes = net_udp4_recv(c->socket, &addr, c->recv_buffer, NETWORK_MAX_PACKET_SIZE); + + /* no more packets for now */ + if(bytes <= 0) + break; + + r = check_packet(c->recv_buffer, bytes, &data); + + if(r == 0) + { + if(data.flags&NETWORK_PACKETFLAG_CONNLESS) + { + /* connection less packets */ + packet->client_id = -1; + packet->address = addr; + packet->flags = PACKETFLAG_CONNLESS; + packet->data_size = data.data_size; + packet->data = data.data; + return 1; + } + else + { + if(conn_feed(&c->conn, &data, &addr)) + { + /* fill in packet */ + packet->client_id = 0; + packet->address = addr; + packet->flags = 0; + packet->data_size = data.data_size; + packet->data = data.data; + return 1; + } + else + { + /* errornous packet, drop it */ + } + } + } + } + + return 0; +} + +int netclient_send(NETCLIENT *c, NETPACKET *packet) +{ + dbg_assert(packet->data_size < NETWORK_MAX_PAYLOAD, "packet payload too big"); + + if(packet->flags&PACKETFLAG_CONNLESS) + { + /* send connectionless packet */ + NETPACKETDATA p; + p.ID[0] = 'T'; + p.ID[1] = 'W'; + p.version = NETWORK_VERSION; + p.flags = NETWORK_PACKETFLAG_CONNLESS; + p.seq = 0; + p.ack = 0; + p.crc = 0; + p.token = 0; + p.data_size = packet->data_size; + p.data = (unsigned char *)packet->data; + send_packet(c->socket, &packet->address, &p); + } + else + { + int flags = 0; + dbg_assert(packet->client_id == 0, "errornous client id"); + if(packet->flags&PACKETFLAG_VITAL) + flags |= NETWORK_PACKETFLAG_VITAL; + conn_send(&c->conn, flags, packet->data_size, packet->data); + } + return 0; +} + +int netclient_state(NETCLIENT *c) +{ + if(c->conn.state == NETWORK_CONNSTATE_ONLINE) + return NETSTATE_ONLINE; + if(c->conn.state == NETWORK_CONNSTATE_OFFLINE) + return NETSTATE_OFFLINE; + return NETSTATE_CONNECTING; +} + +int netclient_gotproblems(NETCLIENT *c) +{ + if(time_get() - c->conn.last_recv_time > time_freq()) + return 1; + return 0; +} + +void netclient_stats(NETCLIENT *c, NETSTATS *stats) +{ + *stats = c->conn.stats; +} + +const char *netclient_error_string(NETCLIENT *c) +{ + return conn_error(&c->conn); +} diff --git a/src/engine/e_network.h b/src/engine/e_network.h new file mode 100644 index 00000000..338c5982 --- /dev/null +++ b/src/engine/e_network.h @@ -0,0 +1,113 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ + +typedef struct +{ + /* -1 means that it's a stateless packet */ + /* 0 on the client means the server */ + int client_id; + NETADDR4 address; /* only used when client_id == -1 */ + int flags; + int data_size; + const void *data; +} NETPACKET; + +typedef struct +{ + int send_bytes; + int recv_bytes; + int send_packets; + int recv_packets; + + int resend_packets; + int resend_bytes; +} NETSTATS; + +typedef struct NETSERVER_t NETSERVER; +typedef struct NETCLIENT_t NETCLIENT; + +enum +{ + NETFLAG_ALLOWSTATELESS=1, + PACKETFLAG_VITAL=1, + PACKETFLAG_CONNLESS=2, + + NETSTATE_OFFLINE=0, + NETSTATE_CONNECTING, + NETSTATE_ONLINE +}; + +typedef int (*NETFUNC_DELCLIENT)(int cid, void *user); +typedef int (*NETFUNC_NEWCLIENT)(int cid, void *user); + +/* server side */ +NETSERVER *netserver_open(NETADDR4 bindaddr, int max_clients, int flags); +int netserver_set_callbacks(NETSERVER *s, NETFUNC_NEWCLIENT new_client, NETFUNC_DELCLIENT del_client, void *user); +int netserver_recv(NETSERVER *s, NETPACKET *packet); +int netserver_send(NETSERVER *s, NETPACKET *packet); +int netserver_close(NETSERVER *s); +int netserver_update(NETSERVER *s); +int netserver_drop(NETSERVER *s, int client_id, const char *reason); +int netserver_max_clients(NETSERVER *s); +void netserver_stats(NETSERVER *s, NETSTATS *stats); + +/* client side */ +NETCLIENT *netclient_open(NETADDR4 bindaddr, int flags); +int netclient_disconnect(NETCLIENT *c, const char *reason); +int netclient_connect(NETCLIENT *c, NETADDR4 *addr); +int netclient_recv(NETCLIENT *c, NETPACKET *packet); +int netclient_send(NETCLIENT *c, NETPACKET *packet); +int netclient_close(NETCLIENT *c); +int netclient_update(NETCLIENT *c); +int netclient_state(NETCLIENT *c); +int netclient_gotproblems(NETCLIENT *c); +void netclient_stats(NETCLIENT *c, NETSTATS *stats); +const char *netclient_error_string(NETCLIENT *c); + +#ifdef __cplusplus +class net_server +{ + NETSERVER *ptr; +public: + net_server() : ptr(0) {} + ~net_server() { close(); } + + int open(NETADDR4 bindaddr, int max, int flags) { ptr = netserver_open(bindaddr, max, flags); return ptr != 0; } + int close() { int r = netserver_close(ptr); ptr = 0; return r; } + + int set_callbacks(NETFUNC_NEWCLIENT new_client, NETFUNC_DELCLIENT del_client, void *user) + { return netserver_set_callbacks(ptr, new_client, del_client, user); } + + int recv(NETPACKET *packet) { return netserver_recv(ptr, packet); } + int send(NETPACKET *packet) { return netserver_send(ptr, packet); } + int update() { return netserver_update(ptr); } + + int drop(int client_id, const char *reason) { return netserver_drop(ptr, client_id, reason); } + + int max_clients() { return netserver_max_clients(ptr); } + void stats(NETSTATS *stats) { netserver_stats(ptr, stats); } +}; + + +class net_client +{ + NETCLIENT *ptr; +public: + net_client() : ptr(0) {} + ~net_client() { close(); } + + int open(NETADDR4 bindaddr, int flags) { ptr = netclient_open(bindaddr, flags); return ptr != 0; } + int close() { int r = netclient_close(ptr); ptr = 0; return r; } + + int connect(NETADDR4 *addr) { return netclient_connect(ptr, addr); } + int disconnect(const char *reason) { return netclient_disconnect(ptr, reason); } + + int recv(NETPACKET *packet) { return netclient_recv(ptr, packet); } + int send(NETPACKET *packet) { return netclient_send(ptr, packet); } + int update() { return netclient_update(ptr); } + + const char *error_string() { return netclient_error_string(ptr); } + + int state() { return netclient_state(ptr); } + void stats(NETSTATS *stats) { netclient_stats(ptr, stats); } +}; +#endif diff --git a/src/engine/e_packer.c b/src/engine/e_packer.c new file mode 100644 index 00000000..948db4ea --- /dev/null +++ b/src/engine/e_packer.c @@ -0,0 +1,91 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ + +#include "e_packer.h" +#include "e_compression.h" + +void packer_reset(PACKER *p) +{ + p->error = 0; + p->current = p->buffer; + p->end = p->current + PACKER_BUFFER_SIZE; +} + +void packer_add_int(PACKER *p, int i) +{ + p->current = vint_pack(p->current, i); +} + +void packer_add_string(PACKER *p, const char *str, int limit) +{ + if(limit > 0) + { + while(*str && limit != 0) + { + *p->current++ = *str++; + limit--; + } + *p->current++ = 0; + } + else + { + while(*str) + *p->current++ = *str++; + *p->current++ = 0; + } +} + +void packer_add_raw(PACKER *p, const unsigned char *data, int size) +{ + while(size) + { + *p->current++ = *data++; + size--; + } +} + +int packer_size(PACKER *p) +{ + return (const unsigned char *)p->current-(const unsigned char *)p->buffer; +} + +const unsigned char *packer_data(PACKER *p) +{ + return (const unsigned char *)p->buffer; +} + +void unpacker_reset(UNPACKER *p, const unsigned char *data, int size) +{ + p->error = 0; + p->start = data; + p->end = p->start + size; + p->current = p->start; +} + +int unpacker_get_int(UNPACKER *p) +{ + int i; + if(p->current >= p->end) + return 0; + p->current = vint_unpack(p->current, &i); + return i; +} + +const char *unpacker_get_string(UNPACKER *p) +{ + const char *ptr; + if(p->current >= p->end) + return ""; + + ptr = (const char *)p->current; + while(*p->current) /* skip the string */ + p->current++; + p->current++; + return ptr; +} + +const unsigned char *unpacker_get_raw(UNPACKER *p, int size) +{ + const unsigned char *ptr = p->current; + p->current += size; + return ptr; +} diff --git a/src/engine/e_packer.h b/src/engine/e_packer.h new file mode 100644 index 00000000..71b99ff7 --- /dev/null +++ b/src/engine/e_packer.h @@ -0,0 +1,35 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ + +enum +{ + PACKER_BUFFER_SIZE=1024*2 +}; + +typedef struct +{ + + unsigned char buffer[PACKER_BUFFER_SIZE]; + unsigned char *current; + unsigned char *end; + int error; +} PACKER; + +typedef struct +{ + const unsigned char *current; + const unsigned char *start; + const unsigned char *end; + int error; +} UNPACKER; + +void packer_reset(PACKER *p); +void packer_add_int(PACKER *p, int i); +void packer_add_string(PACKER *p, const char *str, int limit); +void packer_add_raw(PACKER *p, const unsigned char *data, int size); +int packer_size(PACKER *p); +const unsigned char *packer_data(PACKER *p); + +void unpacker_reset(UNPACKER *p, const unsigned char *data, int size); +int unpacker_get_int(UNPACKER *p); +const char *unpacker_get_string(UNPACKER *p); +const unsigned char *unpacker_get_raw(UNPACKER *p, int size); diff --git a/src/engine/e_protocol.h b/src/engine/e_protocol.h new file mode 100644 index 00000000..0e8a63f4 --- /dev/null +++ b/src/engine/e_protocol.h @@ -0,0 +1,60 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include "e_system.h" + +/* + Connection diagram - How the initilization works. + + Client -> INFO -> Server + Contains version info, name, and some other info. + + Client <- MAP <- Server + Contains current map. + + Client -> READY -> Server + The client has loaded the map and is ready to go, + but the mod needs to send it's information aswell. + modc_connected is called on the client and + mods_connected is called on the server. + The client should call client_entergame when the + mod has done it's initilization. + + Client -> ENTERGAME -> Server + Tells the server to start sending snapshots. + client_entergame and server_client_enter is called. +*/ + + +enum +{ + NETMSG_NULL=0, + + /* the first thing sent by the client + contains the version info for the client */ + NETMSG_INFO=1, + + /* sent by server */ + NETMSG_MAP, + NETMSG_SNAP, + NETMSG_SNAPEMPTY, + NETMSG_SNAPSINGLE, + NETMSG_SNAPSMALL, + + /* sent by client */ + NETMSG_READY, + NETMSG_ENTERGAME, + NETMSG_INPUT, + NETMSG_CMD, + + /* sent by both */ + NETMSG_ERROR +}; + + +/* this should be revised */ +enum +{ + MAX_NAME_LENGTH=32, + MAX_CLANNAME_LENGTH=32, + MAX_INPUT_SIZE=128, + MAX_SNAPSHOT_PACKSIZE=900 +}; diff --git a/src/engine/e_snapshot.c b/src/engine/e_snapshot.c new file mode 100644 index 00000000..91b7797b --- /dev/null +++ b/src/engine/e_snapshot.c @@ -0,0 +1,448 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include "e_snapshot.h" +#include "e_compression.h" + + +int *snapitem_data(SNAPSHOT_ITEM *item) { return (int *)(item+1); } +int snapitem_type(SNAPSHOT_ITEM *item) { return item->type_and_id>>16; } +int snapitem_id(SNAPSHOT_ITEM *item) { return item->type_and_id&0xffff; } +int snapitem_key(SNAPSHOT_ITEM *item) { return item->type_and_id; } + +int *snapshot_offsets(SNAPSHOT *snap) { return (int *)(snap+1); } +char *snapshot_datastart(SNAPSHOT *snap) { return (char*)(snapshot_offsets(snap)+snap->num_items); } + +SNAPSHOT_ITEM *snapshot_get_item(SNAPSHOT *snap, int index) +{ return (SNAPSHOT_ITEM *)(snapshot_datastart(snap) + snapshot_offsets(snap)[index]); } + +int snapshot_get_item_datasize(SNAPSHOT *snap, int index) +{ + if(index == snap->num_items-1) + return (snap->data_size - snapshot_offsets(snap)[index]) - sizeof(SNAPSHOT_ITEM); + return (snapshot_offsets(snap)[index+1] - snapshot_offsets(snap)[index]) - sizeof(SNAPSHOT_ITEM); +} + +int snapshot_get_item_index(SNAPSHOT *snap, int key) +{ + /* TODO: OPT: this should not be a linear search. very bad */ + int i; + for(i = 0; i < snap->num_items; i++) + { + if(snapitem_key(snapshot_get_item(snap, i)) == key) + return i; + } + return -1; +} + + + +typedef struct +{ + int num_deleted_items; + int num_update_items; + int num_temp_items; /* needed? */ + int data[1]; + + /* + char *data_start() { return (char *)&offsets[num_deleted_items+num_update_items+num_temp_items]; } + + int deleted_item(int index) { return offsets[index]; } + item *update_item(int index) { return (item *)(data_start() + offsets[num_deleted_items+index]); } + item *temp_item(int index) { return (item *)(data_start() + offsets[num_deleted_items+num_update_items+index]); } + */ +} SNAPSHOT_DELTA; + + +static const int MAX_ITEMS = 512; +static SNAPSHOT_DELTA empty = {0,0,0,{0}}; + +void *snapshot_empty_delta() +{ + return ∅ +} + +int snapshot_crc(SNAPSHOT *snap) +{ + int crc = 0; + int i, b; + SNAPSHOT_ITEM *item; + int size; + + for(i = 0; i < snap->num_items; i++) + { + item = snapshot_get_item(snap, i); + size = snapshot_get_item_datasize(snap, i); + + for(b = 0; b < size/4; b++) + crc += snapitem_data(item)[b]; + } + return crc; +} + +void snapshot_debug_dump(SNAPSHOT *snap) +{ + int size, i, b; + SNAPSHOT_ITEM *item; + + dbg_msg("snapshot", "data_size=%d num_items=%d", snap->data_size, snap->num_items); + for(i = 0; i < snap->num_items; i++) + { + item = snapshot_get_item(snap, i); + size = snapshot_get_item_datasize(snap, i); + dbg_msg("snapshot", "\ttype=%d id=%d", snapitem_type(item), snapitem_id(item)); + for(b = 0; b < size/4; b++) + dbg_msg("snapshot", "\t\t%3d %12d\t%08x", b, snapitem_data(item)[b], snapitem_data(item)[b]); + } +} + +static int diff_item(int *past, int *current, int *out, int size) +{ + int needed = 0; + while(size) + { + *out = *current-*past; + needed |= *out; + out++; + past++; + current++; + size--; + } + + return needed; +} + +int snapshot_data_rate[0xffff] = {0}; +int snapshot_data_updates[0xffff] = {0}; +static int snapshot_current = 0; + +static void undiff_item(int *past, int *diff, int *out, int size) +{ + while(size) + { + *out = *past+*diff; + + if(*diff == 0) + snapshot_data_rate[snapshot_current] += 1; + else + { + unsigned char buf[16]; + unsigned char *end = vint_pack(buf, *diff); + snapshot_data_rate[snapshot_current] += (int)(end - (unsigned char*)buf) * 8; + } + + out++; + past++; + diff++; + size--; + } +} + + +/* TODO: OPT: this should be made much faster */ +int snapshot_create_delta(SNAPSHOT *from, SNAPSHOT *to, void *dstdata) +{ + SNAPSHOT_DELTA *delta = (SNAPSHOT_DELTA *)dstdata; + int *data = (int *)delta->data; + int i, itemsize, pastindex; + SNAPSHOT_ITEM *fromitem; + SNAPSHOT_ITEM *curitem; + SNAPSHOT_ITEM *pastitem; + int count = 0; + int size_count = 0; + + delta->num_deleted_items = 0; + delta->num_update_items = 0; + delta->num_temp_items = 0; + + /* pack deleted stuff */ + for(i = 0; i < from->num_items; i++) + { + fromitem = snapshot_get_item(from, i); + if(snapshot_get_item_index(to, (snapitem_key(fromitem))) == -1) + { + /* deleted */ + delta->num_deleted_items++; + *data = snapitem_key(fromitem); + data++; + } + } + + /* pack updated stuff */ + for(i = 0; i < to->num_items; i++) + { + /* do delta */ + itemsize = snapshot_get_item_datasize(to, i); + + curitem = snapshot_get_item(to, i); + pastindex = snapshot_get_item_index(from, snapitem_key(curitem)); + if(pastindex != -1) + { + pastitem = snapshot_get_item(from, pastindex); + if(diff_item((int*)snapitem_data(pastitem), (int*)snapitem_data(curitem), data+3, itemsize/4)) + { + *data++ = itemsize; + *data++ = snapitem_type(curitem); + *data++ = snapitem_id(curitem); + /*data++ = curitem->key();*/ + data += itemsize/4; + delta->num_update_items++; + } + } + else + { + *data++ = itemsize; + *data++ = snapitem_type(curitem); + *data++ = snapitem_id(curitem); + /*data++ = curitem->key();*/ + + mem_copy(data, snapitem_data(curitem), itemsize); + size_count += itemsize; + data += itemsize/4; + delta->num_update_items++; + count++; + } + } + + if(0) + { + dbg_msg("snapshot", "%d %d %d", + delta->num_deleted_items, + delta->num_update_items, + delta->num_temp_items); + } + + /* + // TODO: pack temp stuff + + // finish + //mem_copy(delta->offsets, deleted, delta->num_deleted_items*sizeof(int)); + //mem_copy(&(delta->offsets[delta->num_deleted_items]), update, delta->num_update_items*sizeof(int)); + //mem_copy(&(delta->offsets[delta->num_deleted_items+delta->num_update_items]), temp, delta->num_temp_items*sizeof(int)); + //mem_copy(delta->data_start(), data, data_size); + //delta->data_size = data_size; + * */ + + if(!delta->num_deleted_items && !delta->num_update_items && !delta->num_temp_items) + return 0; + + return (int)((char*)data-(char*)dstdata); +} + +int snapshot_unpack_delta(SNAPSHOT *from, SNAPSHOT *to, void *srcdata, int data_size) +{ + SNAPBUILD builder; + SNAPSHOT_DELTA *delta = (SNAPSHOT_DELTA *)srcdata; + int *data = (int *)delta->data; + SNAPSHOT_ITEM *fromitem; + int i, d, keep, itemsize; + int *deleted; + int id, type, key; + int fromindex; + int *newdata; + + snapbuild_init(&builder); + + /* unpack deleted stuff */ + deleted = data; + data += delta->num_deleted_items; + + /* copy all non deleted stuff */ + for(i = 0; i < from->num_items; i++) + { + /* dbg_assert(0, "fail!"); */ + fromitem = snapshot_get_item(from, i); + itemsize = snapshot_get_item_datasize(from, i); + keep = 1; + for(d = 0; d < delta->num_deleted_items; d++) + { + if(deleted[d] == snapitem_key(fromitem)) + { + keep = 0; + break; + } + } + + if(keep) + { + /* keep it */ + mem_copy( + snapbuild_new_item(&builder, snapitem_type(fromitem), snapitem_id(fromitem), itemsize), + snapitem_data(fromitem), itemsize); + } + } + + /* unpack updated stuff */ + for(i = 0; i < delta->num_update_items; i++) + { + itemsize = *data++; + type = *data++; + id = *data++; + snapshot_current = type; + + key = (type<<16)|id; + + /* create the item if needed */ + newdata = snapbuild_get_item_data(&builder, key); + if(!newdata) + newdata = (int *)snapbuild_new_item(&builder, key>>16, key&0xffff, itemsize); + + fromindex = snapshot_get_item_index(from, key); + if(fromindex != -1) + { + /* we got an update so we need to apply the diff */ + undiff_item((int *)snapitem_data(snapshot_get_item(from, fromindex)), data, newdata, itemsize/4); + snapshot_data_updates[snapshot_current]++; + } + else /* no previous, just copy the data */ + { + mem_copy(newdata, data, itemsize); + snapshot_data_rate[snapshot_current] += itemsize*8; + snapshot_data_updates[snapshot_current]++; + } + + data += itemsize/4; + } + + /* finish up */ + return snapbuild_finish(&builder, to); +} + +/* SNAPSTORAGE */ + +void snapstorage_init(SNAPSTORAGE *ss) +{ + ss->first = 0; +} + +void snapstorage_purge_all(SNAPSTORAGE *ss) +{ + SNAPSTORAGE_HOLDER *h = ss->first; + SNAPSTORAGE_HOLDER *next; + + while(h) + { + next = h->next; + mem_free(h); + h = next; + } + + /* no more snapshots in storage */ + ss->first = 0; + ss->last = 0; +} + +void snapstorage_purge_until(SNAPSTORAGE *ss, int tick) +{ + SNAPSTORAGE_HOLDER *next; + SNAPSTORAGE_HOLDER *h = ss->first; + + while(h) + { + next = h->next; + if(h->tick >= tick) + return; /* no more to remove */ + mem_free(h); + + ss->first = next; + next->prev = 0x0; + + h = next; + } + + /* no more snapshots in storage */ + ss->first = 0; + ss->last = 0; +} + +void snapstorage_add(SNAPSTORAGE *ss, int tick, int64 tagtime, int data_size, void *data) +{ + /* allocate memory for holder + snapshot_data */ + SNAPSTORAGE_HOLDER *h = (SNAPSTORAGE_HOLDER *)mem_alloc(sizeof(SNAPSTORAGE_HOLDER)+data_size, 1); + + /* set data */ + h->tick = tick; + h->tagtime = tagtime; + h->snap_size = data_size; + h->snap = (SNAPSHOT*)(h+1); + mem_copy(h->snap, data, data_size); + + /* link */ + h->next = 0; + h->prev = ss->last; + if(ss->last) + ss->last->next = h; + else + ss->first = h; + ss->last = h; +} + +int snapstorage_get(SNAPSTORAGE *ss, int tick, int64 *tagtime, SNAPSHOT **data) +{ + SNAPSTORAGE_HOLDER *h = ss->first; + + while(h) + { + if(h->tick == tick) + { + if(tagtime) + *tagtime = h->tagtime; + if(data) + *data = h->snap; + return h->snap_size; + } + + h = h->next; + } + + return -1; +} + +/* SNAPBUILD */ + +void snapbuild_init(SNAPBUILD *sb) +{ + sb->data_size = 0; + sb->num_items = 0; +} + +SNAPSHOT_ITEM *snapbuild_get_item(SNAPBUILD *sb, int index) +{ + return (SNAPSHOT_ITEM *)&(sb->data[sb->offsets[index]]); +} + +int *snapbuild_get_item_data(SNAPBUILD *sb, int key) +{ + int i; + for(i = 0; i < sb->num_items; i++) + { + if(snapitem_key(snapbuild_get_item(sb, i)) == key) + return (int *)snapitem_data(snapbuild_get_item(sb, i)); + } + return 0; +} + +int snapbuild_finish(SNAPBUILD *sb, void *snapdata) +{ + /* flattern and make the snapshot */ + SNAPSHOT *snap = (SNAPSHOT *)snapdata; + int offset_size = sizeof(int)*sb->num_items; + snap->data_size = sb->data_size; + snap->num_items = sb->num_items; + mem_copy(snapshot_offsets(snap), sb->offsets, offset_size); + mem_copy(snapshot_datastart(snap), sb->data, sb->data_size); + return sizeof(SNAPSHOT) + offset_size + sb->data_size; +} + +void *snapbuild_new_item(SNAPBUILD *sb, int type, int id, int size) +{ + SNAPSHOT_ITEM *obj = (SNAPSHOT_ITEM *)(sb->data+sb->data_size); + mem_zero(obj, sizeof(SNAPSHOT_ITEM) + size); + obj->type_and_id = (type<<16)|id; + sb->offsets[sb->num_items] = sb->data_size; + sb->data_size += sizeof(SNAPSHOT_ITEM) + size; + sb->num_items++; + + dbg_assert(sb->data_size < MAX_SNAPSHOT_SIZE, "too much data"); + dbg_assert(sb->num_items < SNAPBUILD_MAX_ITEMS, "too many items"); + + return snapitem_data(obj); +} diff --git a/src/engine/e_snapshot.h b/src/engine/e_snapshot.h new file mode 100644 index 00000000..0a74b0dc --- /dev/null +++ b/src/engine/e_snapshot.h @@ -0,0 +1,91 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#ifndef ENGINE_SNAPSHOT_H +#define ENGINE_SNAPSHOT_H + +#include "e_system.h" + +/* SNAPSHOT */ + +enum +{ + MAX_SNAPSHOT_SIZE=64*1024 +}; + +typedef struct +{ + int type_and_id; +} SNAPSHOT_ITEM; + +typedef struct +{ + int data_size; + int num_items; +} SNAPSHOT; + +int *snapitem_data(SNAPSHOT_ITEM *item); +int snapitem_type(SNAPSHOT_ITEM *item); +int snapitem_id(SNAPSHOT_ITEM *item); +int snapitem_key(SNAPSHOT_ITEM *item); + +int *snapshot_offsets(SNAPSHOT *snap); +char *snapshot_datastart(SNAPSHOT *snap); + +SNAPSHOT_ITEM *snapshot_get_item(SNAPSHOT *snap, int index); +int snapshot_get_item_datasize(SNAPSHOT *snap, int index); +int snapshot_get_item_index(SNAPSHOT *snap, int key); + +void *snapshot_empty_delta(); +int snapshot_crc(SNAPSHOT *snap); +void snapshot_debug_dump(SNAPSHOT *snap); +int snapshot_create_delta(SNAPSHOT *from, SNAPSHOT *to, void *data); +int snapshot_unpack_delta(SNAPSHOT *from, SNAPSHOT *to, void *data, int data_size); + +/* SNAPSTORAGE */ + +typedef struct SNAPSTORAGE_HOLDER_t +{ + struct SNAPSTORAGE_HOLDER_t *prev; + struct SNAPSTORAGE_HOLDER_t *next; + + int64 tagtime; + int tick; + + int snap_size; + SNAPSHOT *snap; +} SNAPSTORAGE_HOLDER; + +typedef struct SNAPSTORAGE_t +{ + SNAPSTORAGE_HOLDER *first; + SNAPSTORAGE_HOLDER *last; +} SNAPSTORAGE; + +void snapstorage_init(SNAPSTORAGE *ss); +void snapstorage_purge_all(SNAPSTORAGE *ss); +void snapstorage_purge_until(SNAPSTORAGE *ss, int tick); +void snapstorage_add(SNAPSTORAGE *ss, int tick, int64 tagtime, int data_size, void *data); +int snapstorage_get(SNAPSTORAGE *ss, int tick, int64 *tagtime, SNAPSHOT **data); + +/* SNAPBUILD */ + +enum +{ + SNAPBUILD_MAX_ITEMS = 512 +}; + +typedef struct SNAPBUILD +{ + char data[MAX_SNAPSHOT_SIZE]; + int data_size; + + int offsets[SNAPBUILD_MAX_ITEMS]; + int num_items; +} SNAPBUILD; + +void snapbuild_init(SNAPBUILD *sb); +SNAPSHOT_ITEM *snapbuild_get_item(SNAPBUILD *sb, int index); +int *snapbuild_get_item_data(SNAPBUILD *sb, int key); +int snapbuild_finish(SNAPBUILD *sb, void *snapdata); +void *snapbuild_new_item(SNAPBUILD *sb, int type, int id, int size); + +#endif /* ENGINE_SNAPSHOT_H */ diff --git a/src/engine/e_system.c b/src/engine/e_system.c new file mode 100644 index 00000000..90aeccc5 --- /dev/null +++ b/src/engine/e_system.c @@ -0,0 +1,723 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include +#include +#include +#include +#include +#include + +#include "e_detect.h" +#include "e_system.h" + +#if defined(CONF_FAMILY_UNIX) + #include + #include + + /* unix net includes */ + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #include + #include +#elif defined(CONF_FAMILY_WINDOWS) + #define WIN32_LEAN_AND_MEAN + #define _WIN32_WINNT 0x0400 + #include + #include + #include + #include /* for SHGetFolderPathAndSubDir */ + #include + #include + #include + + #define EWOULDBLOCK WSAEWOULDBLOCK +#else + #error NOT IMPLEMENTED +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +IOHANDLE logfile = 0; + +void dbg_assert_imp(const char *filename, int line, int test, const char *msg) +{ + if(!test) + { + dbg_msg("assert", "%s(%d): %s", filename, line, msg); + dbg_break(); + } +} + +void dbg_break() +{ + *((unsigned*)0) = 0x0; +} + +int dbg_log_to_file(const char *filename) +{ + logfile = io_open(filename, IOFLAG_WRITE); + if(logfile) + return 1; + return 0; +} + +void dbg_msg(const char *sys, const char *fmt, ...) +{ + va_list args; +#if defined(CONF_FAMILY_WINDOWS) + char str[1024]; + va_start(args, fmt); + vsprintf(str, fmt, args); + va_end(args); + OutputDebugString(str); + OutputDebugString("\n"); +#endif + + va_start(args, fmt); + printf("[%08x][%s]: ", (int)time(0), sys); + vprintf(fmt, args); + va_end(args); + printf("\n"); + fflush(stdout); +} + +int memory_alloced = 0; + +struct memheader +{ + const char *filename; + int line; + int size; + struct memheader *prev; + struct memheader *next; +}; + +struct memtail +{ + int guard; +}; + +struct memheader *first = 0; + +int mem_allocated() +{ + return memory_alloced; +} + +void *mem_alloc_debug(const char *filename, int line, unsigned size, unsigned alignment) +{ + /* TODO: fix alignment */ + /* TODO: add debugging */ + struct memheader *header = (struct memheader *)malloc(size+sizeof(struct memheader)+sizeof(struct memtail)); + struct memtail *tail = (struct memtail *)(((char*)(header+1))+size); + header->size = size; + header->filename = filename; + header->line = line; + memory_alloced += header->size; + + tail->guard = 0xbaadc0de; + + header->prev = (struct memheader *)0; + header->next = first; + if(first) + first->prev = header; + first = header; + + /*dbg_msg("mem", "++ %p", header+1); */ + return header+1; +} + +void mem_free(void *p) +{ + if(p) + { + struct memheader *header = (struct memheader *)p - 1; + struct memtail *tail = (struct memtail *)(((char*)(header+1))+header->size); + + if(tail->guard != 0xbaadc0de) + dbg_msg("mem", "!! %p", p); + /* dbg_msg("mem", "-- %p", p); */ + memory_alloced -= header->size; + + if(header->prev) + header->prev->next = header->next; + else + first = header->next; + if(header->next) + header->next->prev = header->prev; + + free(header); + } +} + +void mem_debug_dump() +{ + char buf[1024]; + struct memheader *header = first; + IOHANDLE f = io_open("memory.txt", IOFLAG_WRITE); + + while(header) + { + sprintf(buf, "%s(%d): %d\n", header->filename, header->line, header->size); + io_write(f, buf, strlen(buf)); + header = header->next; + } +} + + +void mem_copy(void *dest, const void *source, unsigned size) +{ + memcpy(dest, source, size); +} + +void mem_move(void *dest, const void *source, unsigned size) +{ + memmove(dest, source, size); +} + +void mem_zero(void *block,unsigned size) +{ + memset(block, 0, size); +} + +IOHANDLE io_open(const char *filename, int flags) +{ + if(flags == IOFLAG_READ) + return (IOHANDLE)fopen(filename, "rb"); + if(flags == IOFLAG_WRITE) + return (IOHANDLE)fopen(filename, "wb"); + return 0x0; +} + +unsigned io_read(IOHANDLE io, void *buffer, unsigned size) +{ + return fread(buffer, 1, size, (FILE*)io); +} + +unsigned io_skip(IOHANDLE io, unsigned size) +{ + fseek((FILE*)io, size, SEEK_CUR); + return size; +} + +int io_seek(IOHANDLE io, int offset, int origin) +{ + int real_origin; + + switch(origin) + { + case IOSEEK_START: + real_origin = SEEK_SET; + break; + case IOSEEK_CUR: + real_origin = SEEK_CUR; + break; + case IOSEEK_END: + real_origin = SEEK_END; + } + + return fseek((FILE*)io, offset, origin); +} + +long int io_tell(IOHANDLE io) +{ + return ftell((FILE*)io); +} + +long int io_length(IOHANDLE io) +{ + long int length; + io_seek(io, 0, IOSEEK_END); + length = io_tell(io); + io_seek(io, 0, IOSEEK_START); + return length; +} + +unsigned io_write(IOHANDLE io, const void *buffer, unsigned size) +{ + return fwrite(buffer, 1, size, (FILE*)io); +} + +int io_close(IOHANDLE io) +{ + fclose((FILE*)io); + return 1; +} + +void thread_sleep(int milliseconds) +{ +#if defined(CONF_FAMILY_UNIX) + usleep(milliseconds*1000); +#elif defined(CONF_FAMILY_WINDOWS) + Sleep(milliseconds); +#else + #error not implemented +#endif +} + +#if defined(CONF_FAMILY_UNIX) +typedef pthread_mutex_t LOCKINTERNAL; +#elif defined(CONF_FAMILY_WINDOWS) +typedef CRITICAL_SECTION LOCKINTERNAL; +#else + #error not implemented on this platform +#endif + +LOCK lock_create() +{ + LOCKINTERNAL *lock = (LOCKINTERNAL*)mem_alloc(sizeof(LOCKINTERNAL), 4); + +#if defined(CONF_FAMILY_UNIX) + pthread_mutex_init(lock, 0x0); +#elif defined(CONF_FAMILY_WINDOWS) + InitializeCriticalSection((LPCRITICAL_SECTION)lock); +#else + #error not implemented on this platform +#endif + return (LOCK)lock; +} + +void lock_destroy(LOCK lock) +{ +#if defined(CONF_FAMILY_UNIX) + pthread_mutex_destroy((LOCKINTERNAL *)lock); +#elif defined(CONF_FAMILY_WINDOWS) + DeleteCriticalSection((LPCRITICAL_SECTION)lock); +#else + #error not implemented on this platform +#endif + mem_free(lock); +} + +int lock_try(LOCK lock) +{ +#if defined(CONF_FAMILY_UNIX) + return pthread_mutex_trylock((LOCKINTERNAL *)lock); +#elif defined(CONF_FAMILY_WINDOWS) + return TryEnterCriticalSection((LPCRITICAL_SECTION)lock); +#else + #error not implemented on this platform +#endif +} + +void lock_wait(LOCK lock) +{ +#if defined(CONF_FAMILY_UNIX) + pthread_mutex_lock((LOCKINTERNAL *)lock); +#elif defined(CONF_FAMILY_WINDOWS) + EnterCriticalSection((LPCRITICAL_SECTION)lock); +#else + #error not implemented on this platform +#endif +} + +void lock_release(LOCK lock) +{ +#if defined(CONF_FAMILY_UNIX) + pthread_mutex_unlock((LOCKINTERNAL *)lock); +#elif defined(CONF_FAMILY_WINDOWS) + LeaveCriticalSection((LPCRITICAL_SECTION)lock); +#else + #error not implemented on this platform +#endif +} + +/* ----- time ----- */ +int64 time_get() +{ +#if defined(CONF_FAMILY_UNIX) + struct timeval val; + gettimeofday(&val, NULL); + return (int64)val.tv_sec*(int64)1000000+(int64)val.tv_usec; +#elif defined(CONF_FAMILY_WINDOWS) + static int64 last = 0; + int64 t; + QueryPerformanceCounter((PLARGE_INTEGER)&t); + if(tsin_family = AF_INET; + p->sin_port = htons(src->port); + p->sin_addr.s_addr = htonl(src->ip[0]<<24|src->ip[1]<<16|src->ip[2]<<8|src->ip[3]); +} + +static void sockaddr_to_netaddr4(const struct sockaddr *src, NETADDR4 *dst) +{ + unsigned int ip = htonl(((struct sockaddr_in*)src)->sin_addr.s_addr); + dst->port = htons(((struct sockaddr_in*)src)->sin_port); + dst->ip[0] = (unsigned char)((ip>>24)&0xFF); + dst->ip[1] = (unsigned char)((ip>>16)&0xFF); + dst->ip[2] = (unsigned char)((ip>>8)&0xFF); + dst->ip[3] = (unsigned char)(ip&0xFF); +} + +int net_addr4_cmp(const NETADDR4 *a, const NETADDR4 *b) +{ + if( a->ip[0] != b->ip[0] || + a->ip[1] != b->ip[1] || + a->ip[2] != b->ip[2] || + a->ip[3] != b->ip[3] || + a->port != b->port + ) + return 1; + return 0; +} + + +int net_host_lookup(const char *hostname, unsigned short port, NETADDR4 *addr) +{ + struct hostent* ip = gethostbyname(hostname); + + if(ip && ip->h_length > 0) + { + addr->ip[0] = ip->h_addr_list[0][0]; + addr->ip[1] = ip->h_addr_list[0][1]; + addr->ip[2] = ip->h_addr_list[0][2]; + addr->ip[3] = ip->h_addr_list[0][3]; + addr->port = port; + return 0; + } + + return -1; +} + +NETSOCKET net_udp4_create(NETADDR4 bindaddr) +{ + struct sockaddr addr; + unsigned int mode = 1; + int broadcast = 1; + + /* create socket */ + int sock = socket(AF_INET, SOCK_DGRAM, 0); + if(sock < 0) + return NETSOCKET_INVALID; + + /* bind, we should check for error */ + netaddr4_to_sockaddr(&bindaddr, &addr); + if(bind(sock, &addr, sizeof(addr)) != 0) + { + net_udp4_close(sock); + return NETSOCKET_INVALID; + } + + /* set non-blocking */ +#if defined(CONF_FAMILY_WINDOWS) + ioctlsocket(sock, FIONBIO, (unsigned long *)&mode); +#else + ioctl(sock, FIONBIO, (unsigned long *)&mode); +#endif + + /* set boardcast */ + setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (const char*)&broadcast, sizeof(broadcast)); + + /* return */ + return sock; +} + +int net_udp4_send(NETSOCKET sock, const NETADDR4 *addr, const void *data, int size) +{ + struct sockaddr sa; + int d; + mem_zero(&sa, sizeof(sa)); + netaddr4_to_sockaddr(addr, &sa); + d = sendto((int)sock, (const char*)data, size, 0, &sa, sizeof(sa)); + if(d < 0) + dbg_msg("net", "sendto error %d %x", d, d); + return d; +} + +int net_udp4_recv(NETSOCKET sock, NETADDR4 *addr, void *data, int maxsize) +{ + struct sockaddr from; + int bytes; + socklen_t fromlen = sizeof(struct sockaddr); + bytes = recvfrom(sock, (char*)data, maxsize, 0, &from, &fromlen); + if(bytes > 0) + { + sockaddr_to_netaddr4(&from, addr); + return bytes; + } + else if(bytes == 0) + return 0; + return -1; /* error */ +} + +int net_udp4_close(NETSOCKET sock) +{ +#if defined(CONF_FAMILY_WINDOWS) + closesocket(sock); +#else + close((int)sock); +#endif + return 0; +} + +NETSOCKET net_tcp4_create(const NETADDR4 *a) +{ + struct sockaddr addr; + + /* create socket */ + int sock = socket(AF_INET, SOCK_STREAM, 0); + if(sock < 0) + return NETSOCKET_INVALID; + + /* bind, we should check for error */ + netaddr4_to_sockaddr(a, &addr); + bind(sock, &addr, sizeof(addr)); + + /* return */ + return sock; +} + +int net_tcp4_set_non_blocking(NETSOCKET sock) +{ + unsigned int mode = 1; +#if defined(CONF_FAMILY_WINDOWS) + return ioctlsocket(sock, FIONBIO, (unsigned long *)&mode); +#else + return ioctl(sock, FIONBIO, (unsigned long *)&mode); +#endif +} + +int net_tcp4_set_blocking(NETSOCKET sock) +{ + unsigned int mode = 0; +#if defined(CONF_FAMILY_WINDOWS) + return ioctlsocket(sock, FIONBIO, (unsigned long *)&mode); +#else + return ioctl(sock, FIONBIO, (unsigned long *)&mode); +#endif +} + +int net_tcp4_listen(NETSOCKET sock, int backlog) +{ + return listen(sock, backlog); +} + +int net_tcp4_accept(NETSOCKET sock, NETSOCKET *new_sock, NETADDR4 *a) +{ + int s; + socklen_t sockaddr_len; + struct sockaddr addr; + + sockaddr_len = sizeof(addr); + + s = accept(sock, &addr, &sockaddr_len); + + if (s != -1) + { + sockaddr_to_netaddr4(&addr, a); + *new_sock = s; + } + return s; +} + +int net_tcp4_connect(NETSOCKET sock, const NETADDR4 *a) +{ + struct sockaddr addr; + + netaddr4_to_sockaddr(a, &addr); + return connect(sock, &addr, sizeof(addr)); +} + +int net_tcp4_connect_non_blocking(NETSOCKET sock, const NETADDR4 *a) +{ + struct sockaddr addr; + int res; + + netaddr4_to_sockaddr(a, &addr); + net_tcp4_set_non_blocking(sock); + res = connect(sock, &addr, sizeof(addr)); + net_tcp4_set_blocking(sock); + + return res; +} + +int net_tcp4_send(NETSOCKET sock, const void *data, int size) +{ + int d; + d = send((int)sock, (const char*)data, size, 0); + return d; +} + +int net_tcp4_recv(NETSOCKET sock, void *data, int maxsize) +{ + int bytes; + bytes = recv((int)sock, (char*)data, maxsize, 0); + return bytes; +} + +int net_tcp4_close(NETSOCKET sock) +{ +#if defined(CONF_FAMILY_WINDOWS) + closesocket(sock); +#else + close((int)sock); +#endif + return 0; +} + +int net_errno() +{ + return errno; +} + +int net_would_block() +{ + return net_errno() == EWOULDBLOCK; +} + +int net_init() +{ +#if defined(CONF_FAMILY_WINDOWS) + WSADATA wsaData; + int err = WSAStartup(MAKEWORD(1, 1), &wsaData); + dbg_assert(err == 0, "network initialization failed."); + return err==0?0:1; +#endif + + return 0; +} + +int fs_listdir(const char *dir, fs_listdir_callback cb, void *user) +{ +#if defined(CONF_FAMILY_WINDOWS) + WIN32_FIND_DATA finddata; + HANDLE handle; + char buffer[1024*2]; + strcpy(buffer, dir); + strcat(buffer, "/*"); + + handle = FindFirstFileA(buffer, &finddata); + + if (handle == INVALID_HANDLE_VALUE) + return 0; + + /* add all the entries */ + do + { + if(finddata.cFileName[0] != '.') + cb(finddata.cFileName, 0, user); + } while (FindNextFileA(handle, &finddata)); + + FindClose(handle); + return 0; +#else + struct dirent *entry; + DIR *d = opendir(dir); + + if(!d) + return 0; + + while((entry = readdir(d)) != NULL) + cb(entry->d_name, 0, user); + + /* close the directory and return */ + closedir(d); + return 0; +#endif +} + +int fs_storage_path(const char *appname, char *path, int max) +{ +#if defined(CONF_FAMILY_WINDOWS) + HRESULT r; + char home[MAX_PATH]; + r = SHGetFolderPath (NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, home); + if(r != 0) + return 1; + _snprintf(path, max, "%s/%s", home, appname); + return 0; +#else + char *home = getenv("HOME"); + int i; + if(!home) + return 0; + + snprintf(path, max, "%s/.%s", home, appname); + for(i = strlen(home)+2; path[i]; i++) + path[i] = tolower(path[i]); + + return 1; +#endif +} + +int fs_makedir(const char *path) +{ +#if defined(CONF_FAMILY_WINDOWS) + if(_mkdir(path) == 0) + return 0; + if(errno == EEXIST) + return 0; + return 1; +#else + if(mkdir(path, 0755) == 0) + return 0; + if(errno == EEXIST) + return 0; + return 1; +#endif +} + +void swap_endian(void *data, unsigned elem_size, unsigned num) +{ + char *src = (char*) data; + char *dst = src + (elem_size - 1); + + while(num) + { + unsigned n = elem_size>>1; + char tmp; + while(n) + { + tmp = *src; + *src = *dst; + *dst = tmp; + + src++; + dst--; + n--; + } + + src = src + (elem_size>>1); + dst = src + (elem_size - 1); + num--; + } +} + +#if defined(__cplusplus) +} +#endif diff --git a/src/engine/e_system.h b/src/engine/e_system.h new file mode 100644 index 00000000..4802111f --- /dev/null +++ b/src/engine/e_system.h @@ -0,0 +1,531 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#ifndef BASE_SYSTEM_H +#define BASE_SYSTEM_H + +#include "e_detect.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Group: Debug */ + +/********** + Function: dbg_assert + + Breaks into the debugger based on a test. + + Parameters: + test - Result of the test. + msg - Message that should be printed if the test fails. + + Remarks: + Does nothing in release version of the library. + + See Also: + +**********/ +void dbg_assert(int test, const char *msg); +#define dbg_assert(test,msg) dbg_assert_imp(__FILE__, __LINE__, test, msg) +void dbg_assert_imp(const char *filename, int line, int test, const char *msg); + +/********** + Function: dbg_break + + Breaks into the debugger. + + Remarks: + Does nothing in release version of the library. + + See Also: + +**********/ +void dbg_break(); + +/********** + Function: dbg_msg + + Prints a debug message. + + Parameters: + sys - A string that describes what system the message belongs to + fmt - A printf styled format string. + + Remarks: + Does nothing in relase version of the library. + + See Also: + +**********/ +void dbg_msg(const char *sys, const char *fmt, ...); + +/* Group: Memory */ + +/********** + Function: mem_alloc + + Allocates memory. + + Parameters: + size - Size of the needed block. + alignment - Alignment for the block. + + Returns: + Returns a pointer to the newly allocated block. Returns a + null pointer if the memory couldn't be allocated. + + Remarks: + - Passing 0 to size will allocated the smallest amount possible + and return a unique pointer. + + See Also: + +**********/ +void *mem_alloc_debug(const char *filename, int line, unsigned size, unsigned alignment); +#define mem_alloc(s,a) mem_alloc_debug(__FILE__, __LINE__, (s), (a)) + +/********** + Function: mem_free + + Frees a block allocated through . + + Remarks: + - In the debug version of the library the function will assert if + a non-valid block is passed, like a null pointer or a block that + isn't allocated. + + See Also: + +**********/ +void mem_free(void *block); + +/********** + Function: mem_copy + Copies a a memory block. + + Parameters: + dest - Destination. + source - Source to copy. + size - Size of the block to copy. + + Remarks: + - This functions DOES NOT handles cases where source and + destination is overlapping. + + See Also: + +**********/ +void mem_copy(void *dest, const void *source, unsigned size); + +/********** + Function: mem_move + Copies a a memory block. + + Parameters: + dest - Destination. + source - Source to copy. + size - Size of the block to copy. + + Remarks: + - This functions handles cases where source and destination is overlapping. + + See Also: + +**********/ +void mem_move(void *dest, const void *source, unsigned size); + +/********** + Function: mem_zero + Sets a complete memory block to 0. + + Parameters: + block - Pointer to the block to zero out. + size - Size of the block. +**********/ +void mem_zero(void *block, unsigned size); + +/* ------- io ------- */ +enum { + IOFLAG_READ = 1, + IOFLAG_WRITE = 2, + IOFLAG_RANDOM = 4, + + IOSEEK_START = 0, + IOSEEK_CUR = 1, + IOSEEK_END = 2 +}; + +typedef struct IOINTERNAL *IOHANDLE; + +/**** Group: File IO ****/ + +/**** + Function: io_open + + Opens a file. + + Parameters: + filename - File to open. + flags - A set of flags. IOFLAG_READ, IOFLAG_WRITE, IOFLAG_RANDOM. + + Returns: + Returns a handle to the file on success and 0 on failure. + +****/ +IOHANDLE io_open(const char *filename, int flags); + +/**** + Function: io_read + + Reads data into a buffer from a file. + + Parameters: + io - Handle to the file to read data from. + buffer - Pointer to the buffer that will recive the data. + size - Number of bytes to read from the file. + + Returns: + Number of bytes read. + +****/ +unsigned io_read(IOHANDLE io, void *buffer, unsigned size); + +/***** + Function: io_skip + + Skips data in a file. + + Parameters: + io - Handle to the file. + size - Number of bytes to skip. + + Returns: + Number of bytes skipped. +****/ +unsigned io_skip(IOHANDLE io, unsigned size); + +/***** + Function: io_write + + Writes data from a buffer to file. + + Parameters: + io - Handle to the file. + buffer - Pointer to the data that should be written. + size - Number of bytes to write. + + Returns: + Number of bytes written. +*****/ +unsigned io_write(IOHANDLE io, const void *buffer, unsigned size); + +/***** + Function: io_seek + + Seeks to a specified offset in the file. + + Parameters: + io - Handle to the file. + offset - Offset from pos to stop. + origin - Position to start searching from. + + Returns: + Returns 0 on success. +*****/ +int io_seek(IOHANDLE io, int offset, int origin); + +/***** + Function: io_tell + + Gets the current position in the file. + + Parameters: + io - Handle to the file. + + Returns: + Returns the current position. -1L if an error occured. +*****/ +long int io_tell(IOHANDLE io); + +/***** + Function: io_length + + Gets the total length of the file. Resetting cursor to the beginning + + Parameters: + io - Handle to the file. + + Returns: + Returns the total size. -1L if an error occured. +*****/ +long int io_length(IOHANDLE io); + +/***** + Function: io_close + + Closes a file. + + Parameters: + io - Handle to the file. + + Returns: + Returns 0 on success. +*****/ +int io_close(IOHANDLE io); + +/**** Group: Threads ****/ + +int thread_create(); /* NOT IMPLEMENTED */ +int thread_destory(); /* NOT IMPLEMENTED */ + +int thread_run(); /* NOT IMPLEMENTED */ +int thread_pause(); /* NOT IMPLEMENTED */ +int thread_wait(); /* NOT IMPLEMENTED */ + +/***** + Function: thread_sleep + + Suspends the current thread for a given period. + + Parameters: + milliseconds - Number of milliseconds to sleep. +*****/ +void thread_sleep(int milliseconds); + +/**** Group: Locks ****/ +typedef void* LOCK; + +LOCK lock_create(); +void lock_destroy(LOCK lock); + +int lock_try(LOCK lock); +void lock_wait(LOCK lock); +void lock_release(LOCK lock); + +/**** Group: Timer ****/ +#ifdef __GNUC__ +/* if compiled with -pedantic-errors it will complain about long + not being a C90 thing. +*/ +__extension__ typedef long long int64; +#else +typedef long long int64; +#endif +/***** + Function: time_get + + Fetches a sample from a high resolution timer. + + Returns: + Current value of the timer. + + Remarks: + To know how fast the timer is ticking, see . +*****/ +int64 time_get(); + +/***** + Function: time_freq + + Returns the frequency of the high resolution timer. + + Returns: + Returns the frequency of the high resolution timer. +*****/ +int64 time_freq(); + +/**** Group: Network General ipv4 ****/ +typedef int NETSOCKET; +enum +{ + NETSOCKET_INVALID = -1 +}; + +typedef struct +{ + unsigned char ip[4]; + unsigned short port; +} NETADDR4; + +/***** + Function: net_host_lookup + + Does a hostname lookup by name and fills out the passed NETADDE4 struct with the recieved details. + + Returns: + 0 on success. +*****/ +int net_host_lookup(const char *hostname, unsigned short port, NETADDR4 *addr); + +/**** Group: Network UDP4 ****/ + +/***** + Function: net_udp4_create + + Creates a UDP4 socket and binds it to a port. + + Parameters: + port - Port to bind the socket to. + + Returns: + On success it returns an handle to the socket. On failure it returns NETSOCKET_INVALID. +*****/ +NETSOCKET net_udp4_create(NETADDR4 bindaddr); + +/***** + Function: net_udp4_send + + Sends a packet over an UDP4 socket. + + Parameters: + sock - Socket to use. + addr - Where to send the packet. + data - Pointer to the packet data to send. + size - Size of the packet. + + Returns: + On success it returns the number of bytes sent. Returns -1 on error. +*****/ +int net_udp4_send(NETSOCKET sock, const NETADDR4 *addr, const void *data, int size); + +/***** + Function: net_udp4_recv + + Recives a packet over an UDP4 socket. + + Parameters: + sock - Socket to use. + addr - Pointer to an NETADDR4 that will recive the address. + data - Pointer to a buffer that will recive the data. + maxsize - Maximum size to recive. + + Returns: + On success it returns the number of bytes recived. Returns -1 on error. +*****/ +int net_udp4_recv(NETSOCKET sock, NETADDR4 *addr, void *data, int maxsize); + +/***** + Function: net_udp4_close + + Closes an UDP4 socket. + + Parameters: + sock - Socket to close. + + Returns: + Returns 0 on success. -1 on error. +*****/ +int net_udp4_close(NETSOCKET sock); + + +/**** Group: Network TCP4 ****/ + +/***** + Function: net_tcp4_create + + DOCTODO: serp +*****/ +NETSOCKET net_tcp4_create(const NETADDR4 *a); + +/***** + Function: net_tcp4_set_non_blocking + + DOCTODO: serp +*****/ +int net_tcp4_set_non_blocking(NETSOCKET sock); + +/***** + Function: net_tcp4_set_non_blocking + + DOCTODO: serp +*****/ +int net_tcp4_set_blocking(NETSOCKET sock); + +/***** + Function: net_tcp4_listen + + DOCTODO: serp +*****/ +int net_tcp4_listen(NETSOCKET sock, int backlog); + +/***** + Function: net_tcp4_accept + + DOCTODO: serp +*****/ +int net_tcp4_accept(NETSOCKET sock, NETSOCKET *new_sock, NETADDR4 *a); + +/***** + Function: net_tcp4_connect + + DOCTODO: serp +*****/ +int net_tcp4_connect(NETSOCKET sock, const NETADDR4 *a); + +/***** + Function: net_tcp4_connect_non_blocking + + DOCTODO: serp +*****/ +int net_tcp4_connect_non_blocking(NETSOCKET sock, const NETADDR4 *a); + +/***** + Function: net_tcp4_send + + DOCTODO: serp +*****/ +int net_tcp4_send(NETSOCKET sock, const void *data, int size); + +/***** + Function: net_tcp4_recv + + DOCTODO: serp +*****/ +int net_tcp4_recv(NETSOCKET sock, void *data, int maxsize); + +/***** + Function: net_tcp4_close + + DOCTODO: serp +*****/ +int net_tcp4_close(NETSOCKET sock); + +/***** + Function: net_errno + + DOCTODO: serp +*****/ +int net_errno(); + +/***** + Function: net_would_block + + DOCTODO: serp +*****/ +int net_would_block(); + +/***** + Function: net_init + + DOCTODO: serp +*****/ +int net_init(); + + + +/* NOT DOCUMENTED */ +typedef void (*fs_listdir_callback)(const char *name, int is_dir, void *user); +int fs_listdir(const char *dir, fs_listdir_callback cb, void *user); +int fs_storage_path(const char *appname, char *path, int max); +int fs_makedir(const char *path); +int net_addr4_cmp(const NETADDR4 *a, const NETADDR4 *b); + +void mem_debug_dump(); +int mem_allocated(); + +void swap_endian(void *data, unsigned elem_size, unsigned num); +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/engine/engine.c b/src/engine/engine.c deleted file mode 100644 index 08f13184..00000000 --- a/src/engine/engine.c +++ /dev/null @@ -1,66 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include -#include - -#include -#include -#include - -static char application_save_path[512] = {0}; - -const char *engine_savepath(const char *filename, char *buffer, int max) -{ - sprintf(buffer, "%s/%s", application_save_path, filename); - return buffer; -} - -void engine_init(const char *appname, int argc, char **argv) -{ - /* init the network */ - net_init(); - - /* create storage location */ - { - char path[1024] = {0}; - fs_storage_path(appname, application_save_path, sizeof(application_save_path)); - if(fs_makedir(application_save_path) == 0) - { - strcpy(path, application_save_path); - strcat(path, "/screenshots"); - fs_makedir(path); - } - } - - /* reset the config */ - config_reset(); - - /* load the configuration */ - { - int i; - const char *config_filename = "default.cfg"; - char buf[1024]; - for(i = 1; i < argc; i++) - { - if(argv[i][0] == '-' && argv[i][1] == 'f' && argv[i][2] == 0 && argc - i > 1) - { - config_filename = argv[i+1]; - i++; - } - } - - config_load(engine_savepath(config_filename, buf, sizeof(buf))); - } - - /* search arguments for overrides */ - { - int i; - for(i = 1; i < argc; i++) - config_set(argv[i]); - } -} - -void engine_writeconfig() -{ - char buf[1024]; - config_save(engine_savepath("default.cfg", buf, sizeof(buf))); -} diff --git a/src/engine/engine.h b/src/engine/engine.h deleted file mode 100644 index 56fec20e..00000000 --- a/src/engine/engine.h +++ /dev/null @@ -1,5 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -const char *engine_savepath(const char *filename, char *buffer, int max); -void engine_init(const char *appname, int argc, char **argv); -void engine_writeconfig(); diff --git a/src/engine/external/pa.c b/src/engine/external/pa.c index a417e9db..71afc065 100644 --- a/src/engine/external/pa.c +++ b/src/engine/external/pa.c @@ -1,5 +1,5 @@ /* Include the auto configuration */ -#include +#include /* common part */ #include "portaudio/pa_converters.c" diff --git a/src/engine/interface.h b/src/engine/interface.h deleted file mode 100644 index 5cd8d604..00000000 --- a/src/engine/interface.h +++ /dev/null @@ -1,858 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#ifndef ENGINE_INTERFACE_H -#define ENGINE_INTERFACE_H - -#ifdef __cplusplus -extern "C" { -#endif - -/* - Title: Engine Interface -*/ - -#include "keys.h" - -enum -{ - MAX_CLIENTS=12, - SERVER_TICK_SPEED=50, /* TODO: this should be removed */ - SNAP_CURRENT=0, - SNAP_PREV=1, - - IMG_RGB=0, - IMG_RGBA=1, - - MASK_NONE=0, - MASK_SET, - MASK_ZERO, - - SNDFLAG_LOOP=1, - SNDFLAG_POS=2, - SNDFLAG_ALL=3, - - CLIENTSTATE_OFFLINE=0, - CLIENTSTATE_CONNECTING, - CLIENTSTATE_LOADING, - CLIENTSTATE_ONLINE, - CLIENTSTATE_QUITING, - - BROWSESORT_NAME = 0, - BROWSESORT_PING, - BROWSESORT_MAP, - BROWSESORT_GAMETYPE, - BROWSESORT_PROGRESSION, - BROWSESORT_NUMPLAYERS -}; - -typedef struct -{ - int type; - int id; -} SNAP_ITEM; - -typedef struct -{ - const char *name; - int latency; -} CLIENT_INFO; - -typedef struct -{ - int width, height; - int format; - void *data; -} IMAGE_INFO; - -typedef struct -{ - int width, height; - int red, green, blue; -} VIDEO_MODE; - -typedef struct -{ - int sorted_index; - int server_index; - - int progression; - int game_type; - int max_players; - int num_players; - int flags; - int latency; /* in ms */ - char name[64]; - char map[32]; - char version[32]; - char address[24]; - char player_names[16][48]; - int player_scores[16]; -} SERVER_INFO; - -struct rect -{ - float x, y, w, h; -}; - -struct rect *ui_screen(); -typedef void (*rect_fun)(const struct rect *r); -void ui_foreach_rect(rect_fun fun); -void ui_scale(float scale); -void ui_hsplit_t(const struct rect *original, int pixels, struct rect *top, struct rect *bottom); -void ui_hsplit_b(const struct rect *original, int pixels, struct rect *top, struct rect *bottom); -void ui_vsplit_l(const struct rect *original, int pixels, struct rect *left, struct rect *right); -void ui_vsplit_r(const struct rect *original, int pixels, struct rect *left, struct rect *right); -void ui_margin(const struct rect *original, int pixels, struct rect *new_rect); - -/* image loaders */ -int gfx_load_png(IMAGE_INFO *img, const char *filename); - -/* - Group: Graphics -*/ - -int gfx_init(); -void gfx_shutdown(); -void gfx_swap(); - -float gfx_screenaspect(); - -int gfx_get_video_modes(VIDEO_MODE *list, int maxcount); -void gfx_set_vsync(int val); - -int gfx_window_active(); -int gfx_window_open(); - -/* textures */ -/* - Function: gfx_load_texture - Loads a texture from a file. TGA and PNG supported. - - Arguments: - filename - Null terminated string to the file to load. - - Returns: - An ID to the texture. -1 on failure. - - See Also: - -*/ -int gfx_load_texture(const char *filename); - -/* - Function: gfx_load_texture_raw - Loads a texture from memory. - - Arguments: - w - Width of the texture. - h - Height of the texture. - data - Pointer to the pixel data. - - Returns: - An ID to the texture. -1 on failure. - - Remarks: - The pixel data should be in RGBA format with 8 bit per component. - So the total size of the data should be w*h*4. - - See Also: - -*/ -int gfx_load_texture_raw(int w, int h, int format, const void *data); -/*int gfx_load_mip_texture_raw(int w, int h, int format, const void *data);*/ - -/* - Function: gfx_texture_set - Sets the active texture. - - Arguments: - id - ID to the texture to set. -*/ -void gfx_texture_set(int id); - -/* - Function: gfx_unload_texture - Unloads a texture. - - Arguments: - id - ID to the texture to unload. - - See Also: - , - - Remarks: - NOT IMPLEMENTED -*/ -int gfx_unload_texture(int id); - -void gfx_clear(float r, float g, float b); - -/* - Function: gfx_screenwidth - Returns the screen width. - - See Also: - -*/ -int gfx_screenwidth(); - -/* - Function: gfx_screenheight - Returns the screen height. - - See Also: - -*/ -int gfx_screenheight(); - -/* - Function: gfx_mapscreen - Specifies the coordinate system for the screen. - - Arguments: - tl_x - Top-left X - tl_y - Top-left Y - br_x - Bottom-right X - br_y - Bottom-right y -*/ -void gfx_mapscreen(float tl_x, float tl_y, float br_x, float br_y); - -/* - Function: gfx_blend_normal - Set the active blending mode to normal (src, 1-src). - - Remarks: - This must be used before calling . - This is equal to glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA). - - See Also: - -*/ -void gfx_blend_normal(); - -/* - Function: gfx_blend_additive - Set the active blending mode to additive (src, one). - - Remarks: - This must be used before calling . - This is equal to glBlendFunc(GL_SRC_ALPHA, GL_ONE). - - See Also: - -*/ -void gfx_blend_additive(); - -/* - Function: gfx_quads_begin - Begins a quad drawing session. - - Remarks: - This functions resets the rotation, color and subset. - End the session by using . - You can't change texture or blending mode during a session. - - See Also: - -*/ -void gfx_quads_begin(); - -/* - Function: gfx_quads_end - Ends a quad session. - - See Also: - -*/ -void gfx_quads_end(); - -/* - Function: gfx_quads_setrotation - Sets the rotation to use when drawing a quad. - - Arguments: - angle - Angle in radians. - - Remarks: - The angle is reset when is called. -*/ -void gfx_quads_setrotation(float angle); - -/* - Function: gfx_quads_setcolorvertex - Sets the color of a vertex. - - Arguments: - i - Index to the vertex. - r - Red value. - g - Green value. - b - Blue value. - a - Alpha value. - - Remarks: - The color values are from 0.0 to 1.0. - The color is reset when is called. -*/ -void gfx_setcolorvertex(int i, float r, float g, float b, float a); - -/* - Function: gfx_quads_setcolor - Sets the color of all the vertices. - - Arguments: - r - Red value. - g - Green value. - b - Blue value. - a - Alpha value. - - Remarks: - The color values are from 0.0 to 1.0. - The color is reset when is called. -*/ -void gfx_setcolor(float r, float g, float b, float a); - -/* - Function: gfx_quads_setsubset - Sets the uv coordinates to use. - - Arguments: - tl_u - Top-left U value. - tl_v - Top-left V value. - br_u - Bottom-right U value. - br_v - Bottom-right V value. - - Remarks: - O,0 is top-left of the texture and 1,1 is bottom-right. - The color is reset when is called. -*/ -void gfx_quads_setsubset(float tl_u, float tl_v, float br_u, float br_v); - -/* - Function: gfx_quads_drawTL - Draws a quad by specifying the top-left point. - - Arguments: - x - X coordinate of the top-left corner. - y - Y coordinate of the top-left corner. - width - Width of the quad. - height - Height of the quad. - - Remarks: - Rotation still occurs from the center of the quad. - You must call before calling this function. - - See Also: - -*/ -void gfx_quads_drawTL(float x, float y, float width, float height); - -/* - Function: gfx_quads_draw - Draws a quad by specifying the center point. - - Arguments: - x - X coordinate of the center. - y - Y coordinate of the center. - width - Width of the quad. - height - Height of the quad. - - Remarks: - You must call before calling this function. - - See Also: - -*/ -void gfx_quads_draw(float x, float y, float w, float h); - -void gfx_quads_draw_freeform( - float x0, float y0, - float x1, float y1, - float x2, float y2, - float x3, float y3); - -void gfx_quads_text(float x, float y, float size, const char *text); - -/* sound (client) */ -int snd_init(); - -void snd_set_channel(int cid, float vol, float pan); - -int snd_load_wv(const char *filename); - -int snd_play_at(int cid, int sid, int flags, float x, float y); -int snd_play(int cid, int sid, int flags); - -void snd_stop(int id); -void snd_set_listener_pos(float x, float y); - -int snd_shutdown(); - -/* - Group: Input -*/ - -/* - Function: inp_mouse_relative - Fetches the mouse movements. - - Arguments: - x - Pointer to the variable that should get the X movement. - y - Pointer to the variable that should get the Y movement. -*/ -void inp_mouse_relative(int *x, int *y); - -int inp_mouse_scroll(); - -/* - Function: inp_key_pressed - Checks if a key is pressed. - - Arguments: - key - Index to the key to check - - Returns: - Returns 1 if the button is pressed, otherwise 0. - - Remarks: - Check keys.h for the keys. -*/ -int inp_key_pressed(int key); - -/* - Group: Map -*/ - -int map_load(const char *mapname); -void map_unload(); - -/* - Function: map_is_loaded - Checks if a map is loaded. - - Returns: - Returns 1 if the button is pressed, otherwise 0. -*/ -int map_is_loaded(); - -/* - Function: map_num_items - Checks the number of items in the loaded map. - - Returns: - Returns the number of items. 0 if no map is loaded. -*/ -int map_num_items(); - -/* - Function: map_find_item - Searches the map for an item. - - Arguments: - type - Item type. - id - Item ID. - - Returns: - Returns a pointer to the item if it exists, otherwise it returns NULL. -*/ -void *map_find_item(int type, int id); - -/* - Function: map_get_item - Gets an item from the loaded map from index. - - Arguments: - index - Item index. - type - Pointer that recives the item type (can be NULL). - id - Pointer that recives the item id (can be NULL). - - Returns: - Returns a pointer to the item if it exists, otherwise it returns NULL. -*/ -void *map_get_item(int index, int *type, int *id); - -/* - Function: map_get_type - Gets the index range of an item type. - - Arguments: - type - Item type to search for. - start - Pointer that recives the starting index. - num - Pointer that recives the number of items. - - Returns: - If the item type is not in the map, start and num will be set to 0. -*/ -void map_get_type(int type, int *start, int *num); - -/* - Function: map_get_data - Fetches a pointer to a raw data chunk in the map. - - Arguments: - index - Index to the data to fetch. - - Returns: - A pointer to the raw data, otherwise 0. -*/ -void *map_get_data(int index); - -/* - Group: Network (Server) -*/ -/* - Function: snap_new_item - Creates a new item that should be sent. - - Arguments: - type - Type of the item. - id - ID of the item. - size - Size of the item. - - Returns: - A pointer to the item data, otherwise 0. - - Remarks: - The item data should only consist pf 4 byte integers as - they are subject to byte swapping. This means that the size - argument should be dividable by 4. -*/ -void *snap_new_item(int type, int id, int size); - -/* - Group: Network (Client) -*/ -/* - Function: snap_num_items - Check the number of items in a snapshot. - - Arguments: - snapid - Snapshot ID to the data to fetch. - * SNAP_PREV for previous snapshot. - * SNAP_CUR for current snapshot. - - Returns: - The number of items in the snapshot. -*/ -int snap_num_items(int snapid); - -/* - Function: snap_get_item - Gets an item from a snapshot. - - Arguments: - snapid - Snapshot ID to the data to fetch. - * SNAP_PREV for previous snapshot. - * SNAP_CUR for current snapshot. - index - Index of the item. - item - Pointer that recives the item info. - - Returns: - Returns a pointer to the item if it exists, otherwise NULL. -*/ -const void *snap_get_item(int snapid, int index, SNAP_ITEM *item); - -/* - Function: snap_find_item - Searches a snapshot for an item. - - Arguments: - snapid - Snapshot ID to the data to fetch. - * SNAP_PREV for previous snapshot. - * SNAP_CUR for current snapshot. - type - Type of the item. - id - ID of the item. - - Returns: - Returns a pointer to the item if it exists, otherwise NULL. -*/ -const void *snap_find_item(int snapid, int type, int id); - -/* - Function: snap_input - Sets the input data to send to the server. - - Arguments: - data - Pointer to the data. - size - Size of the data. - - Remarks: - The data should only consist of 4 bytes integer as they are - subject to byte swapping. -*/ -void snap_input(void *data, int size); - -/* - Group: Server Callbacks -*/ -/* - Function: mods_init - Called when the server is started. - - Remarks: - It's called after the map is loaded so all map items are available. -*/ -void mods_init(); - -/* - Function: mods_shutdown - Called when the server quits. - - Remarks: - Should be used to clean up all resources used. -*/ -void mods_shutdown(); - -/* - Function: mods_client_enter - Called when a client has joined the game. - - Arguments: - cid - Client ID. Is 0 - MAX_CLIENTS. - - Remarks: - It's called when the client is finished loading and should enter gameplay. -*/ -void mods_client_enter(int cid); - -/* - Function: mods_client_drop - Called when a client drops from the server. - - Arguments: - cid - Client ID. Is 0 - MAX_CLIENTS -*/ -void mods_client_drop(int cid); - -/* - Function: mods_client_input - Called when the server recives new input from a client. - - Arguments: - cid - Client ID. Is 0 - MAX_CLIENTS. - input - Pointer to the input data. - size - Size of the data. (NOT IMPLEMENTED YET) -*/ -void mods_client_input(int cid, void *input); - -/* - Function: mods_tick - Called with a regular interval to progress the gameplay. - - Remarks: - The SERVER_TICK_SPEED tells the number of ticks per second. -*/ -void mods_tick(); - -/* - Function: mods_presnap - Called before the server starts to construct snapshots for the clients. -*/ -void mods_presnap(); - -/* - Function: mods_snap - Called to create the snapshot for a client. - - Arguments: - cid - Client ID. Is 0 - MAX_CLIENTS. - - Remarks: - The game should make a series of calls to to construct - the snapshot for the client. -*/ -void mods_snap(int cid); - -/* - Function: mods_postsnap - Called after the server is done sending the snapshots. -*/ -void mods_postsnap(); - -/* - Group: Client Callbacks -*/ -/* - Function: modc_init - Called when the client starts. - - Remarks: - The game should load resources that are used during the entire - time of the game. No map is loaded. -*/ -void modc_init(); - -/* - Function: modc_newsnapshot - Called when the client progressed to a new snapshot. - - Remarks: - The client can check for items in the snapshot and perform one time - events like playing sounds, spawning client side effects etc. -*/ -void modc_newsnapshot(); - -/* - Function: modc_entergame - Called when the client has successfully connect to a server and - loaded a map. - - Remarks: - The client can check for items in the map and load them. -*/ -void modc_entergame(); - -/* - Function: modc_shutdown - Called when the client closes down. -*/ -void modc_shutdown(); - -/* - Function: modc_render - Called every frame to let the game render it self. -*/ -void modc_render(); - -/* - Function: modc_statechange - Called every time client changes state. -*/ -void modc_statechange(int new_state, int old_state); - -/* undocumented callbacks */ -void modc_connected(); -void modc_message(int msg); -void modc_predict(); - -void mods_message(int msg, int client_id); -void mods_connected(int client_id); - - -const char *modc_net_version(); -const char *mods_net_version(); - -/* server */ -int server_getclientinfo(int client_id, CLIENT_INFO *info); -const char *server_clientname(int client_id); -void server_setclientname(int client_id, const char *name); - -void server_setbrowseinfo(int game_type, int progression); - -int server_tick(); -int server_tickspeed(); - -/* input */ -int inp_key_was_pressed(int key); -int inp_key_down(int key); -char inp_last_char(); -int inp_last_key(); -void inp_clear(); -void inp_update(); -void inp_init(); -void inp_mouse_mode_absolute(); -void inp_mouse_mode_relative(); -int inp_mouse_doubleclick(); - -int inp_key_presses(int key); -int inp_key_releases(int key); -int inp_key_state(int key); - -const char *inp_key_name(int k); -int inp_key_code(const char *key_name); - -/* message packing */ -enum -{ - MSGFLAG_VITAL=1 -}; - -void msg_pack_start_system(int msg, int flags); -void msg_pack_start(int msg, int flags); -void msg_pack_int(int i); -void msg_pack_string(const char *p, int limit); -void msg_pack_raw(const void *data, int size); -void msg_pack_end(); - -typedef struct -{ - int msg; - int flags; - const unsigned char *data; - int size; -} MSG_INFO; - -const MSG_INFO *msg_get_info(); - -/* message unpacking */ -int msg_unpack_start(const void *data, int data_size, int *system); -int msg_unpack_int(); -const char *msg_unpack_string(); -const unsigned char *msg_unpack_raw(int size); - -/* message sending */ -int server_send_msg(int client_id); /* client_id == -1 == broadcast */ -int client_send_msg(); - -/* client */ -int client_tick(); -int client_predtick(); -float client_intratick(); -float client_intrapredtick(); -int client_tickspeed(); -float client_frametime(); -float client_localtime(); - -int client_state(); -const char *client_error_string(); -int *client_get_input(int tick); -int client_connection_problems(); - -void client_connect(const char *address); -void client_disconnect(); -void client_quit(); -void client_entergame(); - -void client_rcon(const char *cmd); - - -void client_serverbrowse_refresh(int lan); - -SERVER_INFO *client_serverbrowse_sorted_get(int index); -int client_serverbrowse_sorted_num(); - -SERVER_INFO *client_serverbrowse_get(int index); -int client_serverbrowse_num(); - -int client_serverbrowse_num_requests(); - -void client_serverbrowse_update(); - -/* undocumented graphics stuff */ - -void gfx_pretty_text_color(float r, float g, float b, float a); -void gfx_pretty_text(float x, float y, float size, const char *text, int max_width); -float gfx_pretty_text_width(float size, const char *text, int length); - -void gfx_getscreen(float *tl_x, float *tl_y, float *br_x, float *br_y); -int gfx_memory_usage(); -void gfx_screenshot(); - -void gfx_lines_begin(); -void gfx_lines_draw(float x0, float y0, float x1, float y1); -void gfx_lines_end(); - -void gfx_mask_op(int mask, int write); -void gfx_clear_mask(int fill); - -/* server snap id */ -int snap_new_id(); -void snap_free_id(int id); - -/* other */ -void map_unload_data(int index); -void map_set(void *m); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/engine/keynames.c b/src/engine/keynames.c deleted file mode 100644 index fa87467d..00000000 --- a/src/engine/keynames.c +++ /dev/null @@ -1,524 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -/* AUTO GENERATED! DO NOT EDIT MANUALLY! */ - -#include - -static const char key_strings[512][16] = -{ - "#0", - "#1", - "#2", - "#3", - "#4", - "#5", - "#6", - "#7", - "#8", - "#9", - "#10", - "#11", - "#12", - "#13", - "#14", - "#15", - "#16", - "#17", - "#18", - "#19", - "#20", - "#21", - "#22", - "#23", - "#24", - "#25", - "#26", - "#27", - "#28", - "#29", - "#30", - "#31", - "space", - "#33", - "#34", - "#35", - "#36", - "#37", - "#38", - "#39", - "#40", - "#41", - "#42", - "#43", - "#44", - "#45", - "#46", - "#47", - "0", - "1", - "2", - "3", - "4", - "5", - "6", - "7", - "8", - "9", - "#58", - "#59", - "#60", - "#61", - "#62", - "#63", - "#64", - "a", - "b", - "c", - "d", - "e", - "f", - "g", - "h", - "i", - "j", - "k", - "l", - "m", - "n", - "o", - "p", - "q", - "r", - "s", - "t", - "u", - "v", - "w", - "x", - "y", - "z", - "#91", - "#92", - "#93", - "#94", - "#95", - "#96", - "#97", - "#98", - "#99", - "#100", - "#101", - "#102", - "#103", - "#104", - "#105", - "#106", - "#107", - "#108", - "#109", - "#110", - "#111", - "#112", - "#113", - "#114", - "#115", - "#116", - "#117", - "#118", - "#119", - "#120", - "#121", - "#122", - "#123", - "#124", - "#125", - "#126", - "#127", - "#128", - "#129", - "#130", - "#131", - "#132", - "#133", - "#134", - "#135", - "#136", - "#137", - "#138", - "#139", - "#140", - "#141", - "#142", - "#143", - "#144", - "#145", - "#146", - "#147", - "#148", - "#149", - "#150", - "#151", - "#152", - "#153", - "#154", - "#155", - "#156", - "#157", - "#158", - "#159", - "#160", - "#161", - "#162", - "#163", - "#164", - "#165", - "#166", - "#167", - "#168", - "#169", - "#170", - "#171", - "#172", - "#173", - "#174", - "#175", - "#176", - "#177", - "#178", - "#179", - "#180", - "#181", - "#182", - "#183", - "#184", - "#185", - "#186", - "#187", - "#188", - "#189", - "#190", - "#191", - "#192", - "#193", - "#194", - "#195", - "#196", - "#197", - "#198", - "#199", - "#200", - "#201", - "#202", - "#203", - "#204", - "#205", - "#206", - "#207", - "#208", - "#209", - "#210", - "#211", - "#212", - "#213", - "#214", - "#215", - "#216", - "#217", - "#218", - "#219", - "#220", - "#221", - "#222", - "#223", - "#224", - "#225", - "#226", - "#227", - "#228", - "#229", - "#230", - "#231", - "#232", - "#233", - "#234", - "#235", - "#236", - "#237", - "#238", - "#239", - "#240", - "#241", - "#242", - "#243", - "#244", - "#245", - "#246", - "#247", - "#248", - "#249", - "#250", - "#251", - "#252", - "#253", - "#254", - "#255", - "special", - "esc", - "f1", - "f2", - "f3", - "f4", - "f5", - "f6", - "f7", - "f8", - "f9", - "f10", - "f11", - "f12", - "f13", - "f14", - "f15", - "f16", - "f17", - "f18", - "f19", - "f20", - "f21", - "f22", - "f23", - "f24", - "f25", - "up", - "down", - "left", - "right", - "lshift", - "rshift", - "lctrl", - "rctrl", - "lalt", - "ralt", - "tab", - "enter", - "backspace", - "insert", - "del", - "pageup", - "pagedown", - "home", - "end", - "kp0", - "kp1", - "kp2", - "kp3", - "kp4", - "kp5", - "kp6", - "kp7", - "kp8", - "kp9", - "kpdivide", - "kpmultiply", - "kpsubtract", - "kpadd", - "kpdecimal", - "kpequal", - "kpenter", - "#319", - "#320", - "#321", - "#322", - "#323", - "#324", - "#325", - "#326", - "#327", - "#328", - "#329", - "#330", - "#331", - "#332", - "#333", - "#334", - "#335", - "#336", - "#337", - "#338", - "#339", - "#340", - "#341", - "#342", - "#343", - "#344", - "#345", - "#346", - "#347", - "#348", - "#349", - "#350", - "#351", - "#352", - "#353", - "#354", - "#355", - "#356", - "#357", - "#358", - "#359", - "#360", - "#361", - "#362", - "#363", - "#364", - "#365", - "#366", - "#367", - "#368", - "#369", - "#370", - "#371", - "#372", - "#373", - "#374", - "#375", - "#376", - "#377", - "#378", - "#379", - "#380", - "#381", - "mousewheeldown", - "mousewheelup", - "mouse1", - "mouse2", - "mouse3", - "mouse4", - "mouse5", - "mouse6", - "mouse7", - "mouse8", - "#392", - "#393", - "#394", - "#395", - "#396", - "#397", - "#398", - "#399", - "#400", - "#401", - "#402", - "#403", - "#404", - "#405", - "#406", - "#407", - "#408", - "#409", - "#410", - "#411", - "#412", - "#413", - "#414", - "#415", - "#416", - "#417", - "#418", - "#419", - "#420", - "#421", - "#422", - "#423", - "#424", - "#425", - "#426", - "#427", - "#428", - "#429", - "#430", - "#431", - "#432", - "#433", - "#434", - "#435", - "#436", - "#437", - "#438", - "#439", - "#440", - "#441", - "#442", - "#443", - "#444", - "#445", - "#446", - "#447", - "#448", - "#449", - "#450", - "#451", - "#452", - "#453", - "#454", - "#455", - "#456", - "#457", - "#458", - "#459", - "#460", - "#461", - "#462", - "#463", - "#464", - "#465", - "#466", - "#467", - "#468", - "#469", - "#470", - "#471", - "#472", - "#473", - "#474", - "#475", - "#476", - "#477", - "#478", - "#479", - "#480", - "#481", - "#482", - "#483", - "#484", - "#485", - "#486", - "#487", - "#488", - "#489", - "#490", - "#491", - "#492", - "#493", - "#494", - "#495", - "#496", - "#497", - "#498", - "#499", - "#500", - "#501", - "#502", - "#503", - "#504", - "#505", - "#506", - "#507", - "#508", - "#509", - "#510", - "#511", -}; - -const char *inp_key_name(int k) { if (k >= 0 && k < 512) return key_strings[k]; else return key_strings[0]; } -int inp_key_code(const char *key_name) { int i; if (!strcmp(key_name, "-?-")) return -1; else for (i = 0; i < 512; i++) if (!strcmp(key_strings[i], key_name)) return i; return -1; } - diff --git a/src/engine/keys.h b/src/engine/keys.h deleted file mode 100644 index 77a52f58..00000000 --- a/src/engine/keys.h +++ /dev/null @@ -1,88 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#ifndef ENGINE_KEYS_H -#define ENGINE_KEYS_H -/* AUTO GENERATED! DO NOT EDIT MANUALLY! */ - -enum -{ - KEY_UNKNOWN = -1, - KEY_SPACE = 32, - KEY_SPECIAL = 256, - KEY_ESC = (KEY_SPECIAL+1), - KEY_F1 = (KEY_SPECIAL+2), - KEY_F2 = (KEY_SPECIAL+3), - KEY_F3 = (KEY_SPECIAL+4), - KEY_F4 = (KEY_SPECIAL+5), - KEY_F5 = (KEY_SPECIAL+6), - KEY_F6 = (KEY_SPECIAL+7), - KEY_F7 = (KEY_SPECIAL+8), - KEY_F8 = (KEY_SPECIAL+9), - KEY_F9 = (KEY_SPECIAL+10), - KEY_F10 = (KEY_SPECIAL+11), - KEY_F11 = (KEY_SPECIAL+12), - KEY_F12 = (KEY_SPECIAL+13), - KEY_F13 = (KEY_SPECIAL+14), - KEY_F14 = (KEY_SPECIAL+15), - KEY_F15 = (KEY_SPECIAL+16), - KEY_F16 = (KEY_SPECIAL+17), - KEY_F17 = (KEY_SPECIAL+18), - KEY_F18 = (KEY_SPECIAL+19), - KEY_F19 = (KEY_SPECIAL+20), - KEY_F20 = (KEY_SPECIAL+21), - KEY_F21 = (KEY_SPECIAL+22), - KEY_F22 = (KEY_SPECIAL+23), - KEY_F23 = (KEY_SPECIAL+24), - KEY_F24 = (KEY_SPECIAL+25), - KEY_F25 = (KEY_SPECIAL+26), - KEY_UP = (KEY_SPECIAL+27), - KEY_DOWN = (KEY_SPECIAL+28), - KEY_LEFT = (KEY_SPECIAL+29), - KEY_RIGHT = (KEY_SPECIAL+30), - KEY_LSHIFT = (KEY_SPECIAL+31), - KEY_RSHIFT = (KEY_SPECIAL+32), - KEY_LCTRL = (KEY_SPECIAL+33), - KEY_RCTRL = (KEY_SPECIAL+34), - KEY_LALT = (KEY_SPECIAL+35), - KEY_RALT = (KEY_SPECIAL+36), - KEY_TAB = (KEY_SPECIAL+37), - KEY_ENTER = (KEY_SPECIAL+38), - KEY_BACKSPACE = (KEY_SPECIAL+39), - KEY_INSERT = (KEY_SPECIAL+40), - KEY_DEL = (KEY_SPECIAL+41), - KEY_PAGEUP = (KEY_SPECIAL+42), - KEY_PAGEDOWN = (KEY_SPECIAL+43), - KEY_HOME = (KEY_SPECIAL+44), - KEY_END = (KEY_SPECIAL+45), - KEY_KP_0 = (KEY_SPECIAL+46), - KEY_KP_1 = (KEY_SPECIAL+47), - KEY_KP_2 = (KEY_SPECIAL+48), - KEY_KP_3 = (KEY_SPECIAL+49), - KEY_KP_4 = (KEY_SPECIAL+50), - KEY_KP_5 = (KEY_SPECIAL+51), - KEY_KP_6 = (KEY_SPECIAL+52), - KEY_KP_7 = (KEY_SPECIAL+53), - KEY_KP_8 = (KEY_SPECIAL+54), - KEY_KP_9 = (KEY_SPECIAL+55), - KEY_KP_DIVIDE = (KEY_SPECIAL+56), - KEY_KP_MULTIPLY = (KEY_SPECIAL+57), - KEY_KP_SUBTRACT = (KEY_SPECIAL+58), - KEY_KP_ADD = (KEY_SPECIAL+59), - KEY_KP_DECIMAL = (KEY_SPECIAL+60), - KEY_KP_EQUAL = (KEY_SPECIAL+61), - KEY_KP_ENTER = (KEY_SPECIAL+62), - KEY_REPEAT = 0x00030005, - KEY_MOUSE_WHEEL_DOWN = 382, - KEY_MOUSE_WHEEL_UP = 383, - KEY_MOUSE_FIRST = 384, - KEY_MOUSE_1 = KEY_MOUSE_FIRST+0, - KEY_MOUSE_2 = KEY_MOUSE_FIRST+1, - KEY_MOUSE_3 = KEY_MOUSE_FIRST+2, - KEY_MOUSE_4 = KEY_MOUSE_FIRST+3, - KEY_MOUSE_5 = KEY_MOUSE_FIRST+4, - KEY_MOUSE_6 = KEY_MOUSE_FIRST+5, - KEY_MOUSE_7 = KEY_MOUSE_FIRST+6, - KEY_MOUSE_8 = KEY_MOUSE_FIRST+7, - KEY_LAST -}; - -#endif diff --git a/src/engine/map.c b/src/engine/map.c deleted file mode 100644 index 921c05c7..00000000 --- a/src/engine/map.c +++ /dev/null @@ -1,61 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include -#include "datafile.h" - -static DATAFILE *map = 0; - -void *map_get_data(int index) -{ - return datafile_get_data(map, index); -} - -void map_unload_data(int index) -{ - datafile_unload_data(map, index); -} - -void *map_get_item(int index, int *type, int *id) -{ - return datafile_get_item(map, index, type, id); -} - -void map_get_type(int type, int *start, int *num) -{ - datafile_get_type(map, type, start, num); -} - -void *map_find_item(int type, int id) -{ - return datafile_find_item(map, type, id); -} - -int map_num_items() -{ - return datafile_num_items(map); -} - -void map_unload() -{ - datafile_unload(map); - map = 0x0; -} - -int map_is_loaded() -{ - return map != 0; -} - -int map_load(const char *mapname) -{ - char buf[512]; - sprintf(buf, "data/maps/%s.map", mapname); - map = datafile_load(buf); - return map != 0; -} - -void map_set(void *m) -{ - if(map) - map_unload(); - map = (DATAFILE*)m; -} diff --git a/src/engine/memheap.c b/src/engine/memheap.c deleted file mode 100644 index cb8ca664..00000000 --- a/src/engine/memheap.c +++ /dev/null @@ -1,102 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include "system.h" - -typedef struct CHUNK_t -{ - char *memory; - char *current; - char *end; - struct CHUNK_t *next; -} CHUNK; - -typedef struct -{ - CHUNK *current; -} HEAP; - -/* how large each chunk should be */ -static const int chunksize = 1024*64; - -/* allocates a new chunk to be used */ -static CHUNK *memheap_newchunk() -{ - CHUNK *chunk; - char *mem; - - /* allocate memory */ - mem = (char*)mem_alloc(sizeof(CHUNK)+chunksize, 1); - if(!mem) - return 0x0; - - /* the chunk structure is located in the begining of the chunk */ - /* init it and return the chunk */ - chunk = (CHUNK*)mem; - chunk->memory = (char*)(chunk+1); - chunk->current = chunk->memory; - chunk->end = chunk->memory + chunksize; - chunk->next = (CHUNK *)0x0; - return chunk; -} - -/******************/ -static void *memheap_allocate_from_chunk(CHUNK *chunk, int size) -{ - char *mem; - - /* check if we need can fit the allocation */ - if(chunk->current + size >= chunk->end) - return (void*)0x0; - - /* get memory and move the pointer forward */ - mem = chunk->current; - chunk->current += size; - return mem; -} - -/* creates a heap */ -HEAP *memheap_create() -{ - CHUNK *chunk; - HEAP *heap; - - /* allocate a chunk and allocate the heap structure on that chunk */ - chunk = memheap_newchunk(); - heap = (HEAP *)memheap_allocate_from_chunk(chunk, sizeof(HEAP)); - heap->current = chunk; - return heap; -} - -/* destroys the heap */ -void memheap_destroy(HEAP *heap) -{ - CHUNK *chunk = heap->current; - CHUNK *next; - - while(chunk) - { - next = chunk->next; - mem_free(chunk); - chunk = next; - } -} - -/* */ -void *memheap_allocate(HEAP *heap, int size) -{ - char *mem; - - /* try to allocate from current chunk */ - mem = (char *)memheap_allocate_from_chunk(heap->current, size); - if(!mem) - { - /* allocate new chunk and add it to the heap */ - CHUNK *chunk = memheap_newchunk(); - chunk->next = heap->current; - heap->current = chunk; - - /* try to allocate again */ - mem = (char *)memheap_allocate_from_chunk(heap->current, size); - } - - return mem; -} diff --git a/src/engine/memheap.h b/src/engine/memheap.h deleted file mode 100644 index 83082046..00000000 --- a/src/engine/memheap.h +++ /dev/null @@ -1,6 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -typedef struct HEAP_t HEAP; -HEAP *memheap_create(); -void memheap_destroy(HEAP *heap); -void *memheap_allocate(HEAP *heap, int size); diff --git a/src/engine/msg.c b/src/engine/msg.c deleted file mode 100644 index d3f916fa..00000000 --- a/src/engine/msg.c +++ /dev/null @@ -1,55 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include "interface.h" -#include "packer.h" - -/* message packing */ -static PACKER msg_packer; -static MSG_INFO pack_info; - -void msg_pack_int(int i) { packer_add_int(&msg_packer, i); } -void msg_pack_string(const char *p, int limit) { packer_add_string(&msg_packer, p, limit); } -void msg_pack_raw(const void *data, int size) { packer_add_raw(&msg_packer, (const unsigned char *)data, size); } - -void msg_pack_start_system(int msg, int flags) -{ - packer_reset(&msg_packer); - pack_info.msg = (msg<<1)|1; - pack_info.flags = flags; - - msg_pack_int(pack_info.msg); -} - -void msg_pack_start(int msg, int flags) -{ - packer_reset(&msg_packer); - pack_info.msg = msg<<1; - pack_info.flags = flags; - - msg_pack_int(pack_info.msg); -} - -void msg_pack_end() -{ - pack_info.size = packer_size(&msg_packer); - pack_info.data = packer_data(&msg_packer); -} - -const MSG_INFO *msg_get_info() -{ - return &pack_info; -} - -/* message unpacking */ -static UNPACKER msg_unpacker; -int msg_unpack_start(const void *data, int data_size, int *system) -{ - int msg; - unpacker_reset(&msg_unpacker, (const unsigned char *)data, data_size); - msg = msg_unpack_int(); - *system = msg&1; - return msg>>1; -} - -int msg_unpack_int() { return unpacker_get_int(&msg_unpacker); } -const char *msg_unpack_string() { return unpacker_get_string(&msg_unpacker); } -const unsigned char *msg_unpack_raw(int size) { return unpacker_get_raw(&msg_unpacker, size); } diff --git a/src/engine/network.c b/src/engine/network.c deleted file mode 100644 index 85b4604f..00000000 --- a/src/engine/network.c +++ /dev/null @@ -1,937 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include -#include - -#include "system.h" -#include "config.h" -#include "network.h" - -/* - header (6 bytes) - unsigned char flags; 1 - unsigned char seq_ack[3]; 4 - unsigned char token[2]; 6 -*/ - -enum -{ - NETWORK_VERSION = 1, - - NETWORK_HEADER_SIZE = 6, - NETWORK_MAX_PAYLOAD = 1024, - NETWORK_MAX_PACKET_SIZE = NETWORK_HEADER_SIZE+NETWORK_MAX_PAYLOAD, - NETWORK_MAX_CLIENTS = 16, - - NETWORK_CONNSTATE_OFFLINE=0, - NETWORK_CONNSTATE_CONNECT=1, - NETWORK_CONNSTATE_CONNECTACCEPTED=2, - NETWORK_CONNSTATE_ONLINE=3, - NETWORK_CONNSTATE_ERROR=4, - - NETWORK_PACKETFLAG_CONNECT=0x01, - NETWORK_PACKETFLAG_ACCEPT=0x02, - NETWORK_PACKETFLAG_CLOSE=0x04, - NETWORK_PACKETFLAG_VITAL=0x08, - NETWORK_PACKETFLAG_RESEND=0x10, - NETWORK_PACKETFLAG_CONNLESS=0x20, - - NETWORK_MAX_SEQACK=0x1000 -}; - -static int current_token = 1; - -typedef struct -{ - unsigned char ID[2]; - unsigned char version; - unsigned char flags; - unsigned short seq; - unsigned short ack; - unsigned crc; - int token; - unsigned data_size; - int64 first_send_time; - unsigned char *data; -} NETPACKETDATA; - - -static void send_packet(NETSOCKET socket, NETADDR4 *addr, NETPACKETDATA *packet) -{ - unsigned char buffer[NETWORK_MAX_PACKET_SIZE]; - int send_size = NETWORK_HEADER_SIZE+packet->data_size; - - buffer[0] = packet->flags; - buffer[1] = ((packet->seq>>4)&0xf0) | ((packet->ack>>8)&0x0f); - buffer[2] = packet->seq; - buffer[3] = packet->ack; - buffer[4] = packet->token>>8; - buffer[5] = packet->token&0xff; - mem_copy(buffer+NETWORK_HEADER_SIZE, packet->data, packet->data_size); - net_udp4_send(socket, addr, buffer, send_size); -} - -typedef struct RINGBUFFER_ITEM_t -{ - struct RINGBUFFER_ITEM_t *next; - struct RINGBUFFER_ITEM_t *prev; - int size; -} RINGBUFFER_ITEM; - -typedef struct -{ - RINGBUFFER_ITEM *first; - RINGBUFFER_ITEM *last; - unsigned buffer_size; -} RINGBUFFER; - -static void rb_init(RINGBUFFER *rb) -{ - rb->first = 0; - rb->last = 0; - rb->buffer_size = 0; -} - -static void *rb_item_data(RINGBUFFER_ITEM *item) -{ - return (void*)(item+1); -} - -static void *rb_alloc(RINGBUFFER *rb, int size) -{ - RINGBUFFER_ITEM *item = (RINGBUFFER_ITEM*)mem_alloc(sizeof(RINGBUFFER_ITEM)+size, 1); - item->size = size; - - item->prev = rb->last; - item->next = 0; - if(rb->last) - rb->last->next = item; - else - rb->first = item; - rb->last = item; - - rb->buffer_size += size; - return rb_item_data(item); -} - -static void rb_pop_first(RINGBUFFER *rb) -{ - if(rb->first) - { - RINGBUFFER_ITEM *next = rb->first->next; - rb->buffer_size -= rb->first->size; - mem_free(rb->first); - rb->first = next; - if(rb->first) - rb->first->prev = NULL; - else - rb->last = NULL; - } -} - -static void rb_clear(RINGBUFFER *rb) -{ - while(rb->first) - rb_pop_first(rb); -} - - -typedef struct -{ - unsigned short seq; - unsigned short ack; - unsigned state; - - int token; - - int remote_closed; - - int connected; - int disconnected; - - RINGBUFFER buffer; - - int64 last_update_time; - int64 last_recv_time; - int64 last_send_time; - - char error_string[256]; - - NETADDR4 peeraddr; - NETSOCKET socket; - NETSTATS stats; -} NETCONNECTION; - -typedef struct -{ - NETCONNECTION conn; -} NETSLOT; - -struct NETSERVER_t -{ - NETSOCKET socket; - NETSLOT slots[NETWORK_MAX_CLIENTS]; - int max_clients; - NETFUNC_NEWCLIENT new_client; - NETFUNC_NEWCLIENT del_client; - void *user_ptr; - unsigned char recv_buffer[NETWORK_MAX_PACKET_SIZE]; -} ; - -struct NETCLIENT_t -{ - NETADDR4 server_addr; - NETSOCKET socket; - unsigned char recv_buffer[NETWORK_MAX_PACKET_SIZE]; - - NETCONNECTION conn; -}; - -static void conn_reset_stats(NETCONNECTION *conn) -{ - mem_zero(&conn->stats, sizeof(conn->stats)); -} - -static void conn_reset(NETCONNECTION *conn) -{ - conn->seq = 0; - conn->ack = 0; - conn->remote_closed = 0; - - if(conn->state == NETWORK_CONNSTATE_ONLINE || - conn->state == NETWORK_CONNSTATE_ERROR) - { - conn->disconnected++; - } - - conn->state = NETWORK_CONNSTATE_OFFLINE; - conn->last_send_time = 0; - conn->last_recv_time = 0; - conn->last_update_time = 0; - conn->token = -1; - - rb_clear(&conn->buffer); -} - - -static const char *conn_error(NETCONNECTION *conn) -{ - return conn->error_string; -} - -static void conn_set_error(NETCONNECTION *conn, const char *str) -{ - strcpy(conn->error_string, str); -} - -/* -static int conn_state(NETCONNECTION *conn) -{ - return conn->state; -}*/ - -static void conn_init(NETCONNECTION *conn, NETSOCKET socket) -{ - conn_reset(conn); - conn_reset_stats(conn); - conn->socket = socket; - conn->connected = 0; - conn->disconnected = 0; - rb_init(&conn->buffer); - mem_zero(conn->error_string, sizeof(conn->error_string)); -} - -static void conn_ack(NETCONNECTION *conn, int ack) -{ - while(1) - { - RINGBUFFER_ITEM *item = conn->buffer.first; - NETPACKETDATA *resend; - if(!item) - break; - - resend = (NETPACKETDATA *)rb_item_data(item); - if(resend->seq <= ack || (ack < NETWORK_MAX_SEQACK/3 && resend->seq > NETWORK_MAX_SEQACK/2)) - rb_pop_first(&conn->buffer); - else - break; - } -} - -static void conn_send_raw(NETCONNECTION *conn, NETPACKETDATA *data) -{ - conn->last_send_time = time_get(); - conn->stats.send_packets++; - conn->stats.send_bytes += data->data_size + NETWORK_HEADER_SIZE; - send_packet(conn->socket, &conn->peeraddr, data); -} - -static void conn_resend(NETCONNECTION *conn) -{ - RINGBUFFER_ITEM *item = conn->buffer.first; - while(item) - { - NETPACKETDATA *resend = (NETPACKETDATA *)rb_item_data(item); - conn->stats.resend_packets++; - conn->stats.resend_bytes += resend->data_size + NETWORK_HEADER_SIZE; - conn_send_raw(conn, resend); - item = item->next; - } -} - -static void conn_send(NETCONNECTION *conn, int flags, int data_size, const void *data) -{ - NETPACKETDATA p; - - if(flags&NETWORK_PACKETFLAG_VITAL) - conn->seq = (conn->seq+1)%NETWORK_MAX_SEQACK; - - p.ID[0] = 'T'; - p.ID[1] = 'W'; - p.version = NETWORK_VERSION; - p.flags = flags; - p.seq = conn->seq; - p.ack = conn->ack; - p.crc = 0; - p.token = conn->token; - p.data_size = data_size; - p.data = (unsigned char *)data; - p.first_send_time = time_get(); - - if(flags&NETWORK_PACKETFLAG_VITAL) - { - /* save packet if we need to resend */ - NETPACKETDATA *resend = (NETPACKETDATA *)rb_alloc(&conn->buffer, sizeof(NETPACKETDATA)+data_size); - *resend = p; - resend->data = (unsigned char *)(resend+1); - mem_copy(resend->data, p.data, p.data_size); - } - - /* TODO: calc crc */ - conn_send_raw(conn, &p); -} - -static int conn_connect(NETCONNECTION *conn, NETADDR4 *addr) -{ - if(conn->state != NETWORK_CONNSTATE_OFFLINE) - return -1; - - /* init connection */ - conn_reset(conn); - conn->peeraddr = *addr; - conn->token = current_token++; - mem_zero(conn->error_string, sizeof(conn->error_string)); - conn->state = NETWORK_CONNSTATE_CONNECT; - conn_send(conn, NETWORK_PACKETFLAG_CONNECT, 0, 0); - return 0; -} - -static void conn_disconnect(NETCONNECTION *conn, const char *reason) -{ - if(conn->remote_closed == 0) - { - if(reason) - conn_send(conn, NETWORK_PACKETFLAG_CLOSE, strlen(reason)+1, reason); - else - conn_send(conn, NETWORK_PACKETFLAG_CLOSE, 0, 0); - - conn->error_string[0] = 0; - if(reason) - strcpy(conn->error_string, reason); - } - - conn_reset(conn); -} - -static int conn_feed(NETCONNECTION *conn, NETPACKETDATA *p, NETADDR4 *addr) -{ - conn->last_recv_time = time_get(); - conn->stats.recv_packets++; - conn->stats.recv_bytes += p->data_size + NETWORK_HEADER_SIZE; - - if(p->flags&NETWORK_PACKETFLAG_CLOSE) - { - conn->state = NETWORK_CONNSTATE_ERROR; - conn->remote_closed = 1; - - if(p->data_size) - conn_set_error(conn, (char *)p->data); - else - conn_set_error(conn, "no reason given"); - if(config.debug) - dbg_msg("conn", "closed reason='%s'", conn_error(conn)); - return 0; - } - - if(conn->state == NETWORK_CONNSTATE_OFFLINE) - { - if(p->flags == NETWORK_PACKETFLAG_CONNECT) - { - /* send response and init connection */ - conn->state = NETWORK_CONNSTATE_ONLINE; - conn->connected++; - conn->peeraddr = *addr; - conn->token = p->token; - conn_send(conn, NETWORK_PACKETFLAG_CONNECT|NETWORK_PACKETFLAG_ACCEPT, 0, 0); - if(config.debug) - dbg_msg("connection", "got connection, sending connect+accept"); - } - } - else if(net_addr4_cmp(&conn->peeraddr, addr) == 0) - { - if(p->token != conn->token) - return 0; - - if(conn->state == NETWORK_CONNSTATE_ONLINE) - { - /* remove packages that are acked */ - conn_ack(conn, p->ack); - - /* check if resend is requested */ - if(p->flags&NETWORK_PACKETFLAG_RESEND) - conn_resend(conn); - - if(p->flags&NETWORK_PACKETFLAG_VITAL) - { - if(p->seq == (conn->ack+1)%NETWORK_MAX_SEQACK) - { - /* in sequence */ - conn->ack = (conn->ack+1)%NETWORK_MAX_SEQACK; - } - else - { - /* out of sequence, request resend */ - dbg_msg("conn", "asking for resend %d %d", p->seq, (conn->ack+1)%NETWORK_MAX_SEQACK); - conn_send(conn, NETWORK_PACKETFLAG_RESEND, 0, 0); - return 0; - } - } - else - { - if(p->seq > conn->ack) - conn_send(conn, NETWORK_PACKETFLAG_RESEND, 0, 0); - } - - if(p->data_size == 0) - return 0; - - return 1; - } - else if(conn->state == NETWORK_CONNSTATE_CONNECT) - { - /* connection made */ - if(p->flags == (NETWORK_PACKETFLAG_CONNECT|NETWORK_PACKETFLAG_ACCEPT)) - { - conn_send(conn, NETWORK_PACKETFLAG_ACCEPT, 0, 0); - conn->state = NETWORK_CONNSTATE_ONLINE; - conn->connected++; - dbg_msg("connection", "got connect+accept, sending accept. connection online"); - } - } - /* - else if(conn->state == NETWORK_CONNSTATE_CONNECTACCEPTED) - { - // connection made - if(p->flags == NETWORK_PACKETFLAG_ACCEPT) - { - conn->state = NETWORK_CONNSTATE_ONLINE; - dbg_msg("connection", "got accept. connection online"); - } - }*/ - else - { - /* strange packet, wrong state */ - conn->state = NETWORK_CONNSTATE_ERROR; - conn_set_error(conn, "strange state and packet"); - } - } - else - { - /* strange packet, not ment for me */ - } - - return 0; -} - - - -static int conn_update(NETCONNECTION *conn) -{ - int64 now = time_get(); - - if(conn->state == NETWORK_CONNSTATE_OFFLINE || conn->state == NETWORK_CONNSTATE_ERROR) - return 0; - - /* watch out for major hitches */ - { - int64 delta = now-conn->last_update_time; - if(conn->last_update_time && delta > time_freq()/2) - { - RINGBUFFER_ITEM *item = conn->buffer.first; - - dbg_msg("conn", "hitch %d", (int)((delta*1000)/time_freq())); - conn->last_recv_time += delta; - - while(item) - { - NETPACKETDATA *resend = (NETPACKETDATA *)rb_item_data(item); - resend->first_send_time += delta; - item = item->next; - } - } - - conn->last_update_time = now; - } - - - /* check for timeout */ - if(conn->state != NETWORK_CONNSTATE_OFFLINE && - conn->state != NETWORK_CONNSTATE_CONNECT && - (now-conn->last_recv_time) > time_freq()*10) - { - conn->state = NETWORK_CONNSTATE_ERROR; - conn_set_error(conn, "timeout"); - } - - /* check for large buffer errors */ - if(conn->buffer.buffer_size > 1024*64) - { - conn->state = NETWORK_CONNSTATE_ERROR; - conn_set_error(conn, "too weak connection (out of buffer)"); - } - - if(conn->buffer.first) - { - NETPACKETDATA *resend = (NETPACKETDATA *)(conn->buffer.first+1); - if(now-resend->first_send_time > time_freq()*10) - { - conn->state = NETWORK_CONNSTATE_ERROR; - conn_set_error(conn, "too weak connection (not acked for 10 seconds)"); - } - } - - /* send keep alives if nothing has happend for 250ms */ - if(conn->state == NETWORK_CONNSTATE_ONLINE) - { - if(time_get()-conn->last_send_time> time_freq()/4) - conn_send(conn, NETWORK_PACKETFLAG_VITAL, 0, 0); - } - else if(conn->state == NETWORK_CONNSTATE_CONNECT) - { - if(time_get()-conn->last_send_time > time_freq()/2) /* send a new connect every 500ms */ - conn_send(conn, NETWORK_PACKETFLAG_CONNECT, 0, 0); - } - else if(conn->state == NETWORK_CONNSTATE_CONNECTACCEPTED) - { - if(time_get()-conn->last_send_time > time_freq()/2) /* send a new connect/accept every 500ms */ - conn_send(conn, NETWORK_PACKETFLAG_CONNECT|NETWORK_PACKETFLAG_ACCEPT, 0, 0); - } - - return 0; -} - - -static int check_packet(unsigned char *buffer, int size, NETPACKETDATA *packet) -{ - /* check the size */ - if(size < NETWORK_HEADER_SIZE || size > NETWORK_MAX_PACKET_SIZE) - return -1; - - /* read the packet */ - packet->ID[0] = 'T'; - packet->ID[1] = 'W'; - packet->version = NETWORK_VERSION; - packet->flags = buffer[0]; - packet->seq = ((buffer[1]&0xf0)<<4)|buffer[2]; - packet->ack = ((buffer[1]&0x0f)<<8)|buffer[3]; - packet->crc = 0; - packet->token = (buffer[4]<<8)|buffer[5]; - packet->data_size = size - NETWORK_HEADER_SIZE; - packet->data = buffer+NETWORK_HEADER_SIZE; - - /* check the packet */ - if(packet->ID[0] != 'T' || packet->ID[1] != 'W') - return 1; - - if(packet->version != NETWORK_VERSION) - return 1; - - /* TODO: perform crc check */ - - /* return success */ - return 0; -} - -NETSERVER *netserver_open(NETADDR4 bindaddr, int max_clients, int flags) -{ - int i; - NETSERVER *server; - NETSOCKET socket = net_udp4_create(bindaddr); - if(socket == NETSOCKET_INVALID) - return 0; - - server = (NETSERVER *)mem_alloc(sizeof(NETSERVER), 1); - mem_zero(server, sizeof(NETSERVER)); - server->socket = socket; - server->max_clients = max_clients; - if(server->max_clients > NETWORK_MAX_CLIENTS) - server->max_clients = NETWORK_MAX_CLIENTS; - if(server->max_clients < 1) - server->max_clients = 1; - - for(i = 0; i < NETWORK_MAX_CLIENTS; i++) - conn_init(&server->slots[i].conn, server->socket); - - return server; -} - -int netserver_set_callbacks(NETSERVER *s, NETFUNC_NEWCLIENT new_client, NETFUNC_DELCLIENT del_client, void *user) -{ - s->new_client = new_client; - s->del_client = del_client; - s->user_ptr = user; - return 0; -} - -int netserver_max_clients(NETSERVER *s) -{ - return s->max_clients; -} - -int netserver_close(NETSERVER *s) -{ - /* TODO: implement me */ - return 0; -} - -int netserver_drop(NETSERVER *s, int client_id, const char *reason) -{ - /* TODO: insert lots of checks here */ - dbg_msg("net_server", "client dropped. cid=%d reason=\"%s\"", client_id, reason); - conn_disconnect(&s->slots[client_id].conn, reason); - - if(s->del_client) - s->del_client(client_id, s->user_ptr); - - return 0; -} - -int netserver_update(NETSERVER *s) -{ - int i; - for(i = 0; i < s->max_clients; i++) - { - conn_update(&s->slots[i].conn); - if(s->slots[i].conn.state == NETWORK_CONNSTATE_ERROR) - netserver_drop(s, i, conn_error(&s->slots[i].conn)); - } - return 0; -} - -int netserver_recv(NETSERVER *s, NETPACKET *packet) -{ - NETPACKETDATA data; - int i, r, bytes, found; - NETADDR4 addr; - - while(1) - { - bytes = net_udp4_recv(s->socket, &addr, s->recv_buffer, NETWORK_MAX_PACKET_SIZE); - - /* no more packets for now */ - if(bytes <= 0) - break; - - r = check_packet(s->recv_buffer, bytes, &data); - if(r == 0) - { - if(data.flags&NETWORK_PACKETFLAG_CONNLESS) - { - /* connection less packets */ - packet->client_id = -1; - packet->address = addr; - packet->flags = PACKETFLAG_CONNLESS; - packet->data_size = data.data_size; - packet->data = data.data; - return 1; - } - else - { - /* ok packet, process it */ - if(data.flags == NETWORK_PACKETFLAG_CONNECT) - { - found = 0; - - /* check if we already got this client */ - for(i = 0; i < s->max_clients; i++) - { - if(s->slots[i].conn.state != NETWORK_CONNSTATE_OFFLINE && - net_addr4_cmp(&s->slots[i].conn.peeraddr, &addr) == 0) - { - found = 1; /* silent ignore.. we got this client already */ - break; - } - } - - /* client that wants to connect */ - if(!found) - { - for(i = 0; i < s->max_clients; i++) - { - if(s->slots[i].conn.state == NETWORK_CONNSTATE_OFFLINE) - { - found = 1; - conn_feed(&s->slots[i].conn, &data, &addr); - if(s->new_client) - s->new_client(i, s->user_ptr); - break; - } - } - } - - if(!found) - { - /* send connectionless packet */ - const char errstring[] = "server full"; - NETPACKETDATA p; - p.ID[0] = 'T'; - p.ID[1] = 'W'; - p.version = NETWORK_VERSION; - p.flags = NETWORK_PACKETFLAG_CLOSE; - p.seq = 0; - p.ack = 0; - p.crc = 0; - p.token = data.token; - p.data_size = sizeof(errstring); - p.data = (unsigned char *)errstring; - send_packet(s->socket, &addr, &p); - } - } - else - { - /* find matching slot */ - for(i = 0; i < s->max_clients; i++) - { - if(net_addr4_cmp(&s->slots[i].conn.peeraddr, &addr) == 0) - { - if(conn_feed(&s->slots[i].conn, &data, &addr)) - { - if(data.data_size) - { - packet->client_id = i; - packet->address = addr; - packet->flags = 0; - packet->data_size = data.data_size; - packet->data = data.data; - return 1; - } - } - } - } - } - } - } - else - { - /* errornous packet, drop it */ - dbg_msg("server", "crazy packet"); - } - - /* read header */ - /* do checksum */ - } - - return 0; -} - -int netserver_send(NETSERVER *s, NETPACKET *packet) -{ - dbg_assert(packet->data_size < NETWORK_MAX_PAYLOAD, "packet payload too big"); - - if(packet->flags&PACKETFLAG_CONNLESS) - { - /* send connectionless packet */ - NETPACKETDATA p; - p.ID[0] = 'T'; - p.ID[1] = 'W'; - p.version = NETWORK_VERSION; - p.flags = NETWORK_PACKETFLAG_CONNLESS; - p.seq = 0; - p.ack = 0; - p.crc = 0; - p.data_size = packet->data_size; - p.data = (unsigned char *)packet->data; - send_packet(s->socket, &packet->address, &p); - } - else - { - int flags = 0; - dbg_assert(packet->client_id >= 0, "errornous client id"); - dbg_assert(packet->client_id < s->max_clients, "errornous client id"); - if(packet->flags&PACKETFLAG_VITAL) - flags |= NETWORK_PACKETFLAG_VITAL; - conn_send(&s->slots[packet->client_id].conn, flags, packet->data_size, packet->data); - } - return 0; -} - -void netserver_stats(NETSERVER *s, NETSTATS *stats) -{ - int num_stats = sizeof(NETSTATS)/sizeof(int); - int *istats = (int *)stats; - int c, i; - - mem_zero(stats, sizeof(NETSTATS)); - - for(c = 0; c < s->max_clients; c++) - { - if(s->slots[c].conn.state != NETWORK_CONNSTATE_OFFLINE) - { - int *sstats = (int *)(&(s->slots[c].conn.stats)); - for(i = 0; i < num_stats; i++) - istats[i] += sstats[i]; - } - } -} - -NETCLIENT *netclient_open(NETADDR4 bindaddr, int flags) -{ - NETCLIENT *client = (NETCLIENT *)mem_alloc(sizeof(NETCLIENT), 1); - mem_zero(client, sizeof(NETCLIENT)); - client->socket = net_udp4_create(bindaddr); - conn_init(&client->conn, client->socket); - return client; -} - -int netclient_close(NETCLIENT *c) -{ - /* TODO: implement me */ - return 0; -} - -int netclient_update(NETCLIENT *c) -{ - conn_update(&c->conn); - if(c->conn.state == NETWORK_CONNSTATE_ERROR) - netclient_disconnect(c, conn_error(&c->conn)); - return 0; -} - -int netclient_disconnect(NETCLIENT *c, const char *reason) -{ - dbg_msg("netclient", "disconnected. reason=\"%s\"", reason); - conn_disconnect(&c->conn, reason); - return 0; -} - -int netclient_connect(NETCLIENT *c, NETADDR4 *addr) -{ - conn_connect(&c->conn, addr); - return 0; -} - -int netclient_recv(NETCLIENT *c, NETPACKET *packet) -{ - while(1) - { - NETADDR4 addr; - NETPACKETDATA data; - int r; - int bytes = net_udp4_recv(c->socket, &addr, c->recv_buffer, NETWORK_MAX_PACKET_SIZE); - - /* no more packets for now */ - if(bytes <= 0) - break; - - r = check_packet(c->recv_buffer, bytes, &data); - - if(r == 0) - { - if(data.flags&NETWORK_PACKETFLAG_CONNLESS) - { - /* connection less packets */ - packet->client_id = -1; - packet->address = addr; - packet->flags = PACKETFLAG_CONNLESS; - packet->data_size = data.data_size; - packet->data = data.data; - return 1; - } - else - { - if(conn_feed(&c->conn, &data, &addr)) - { - /* fill in packet */ - packet->client_id = 0; - packet->address = addr; - packet->flags = 0; - packet->data_size = data.data_size; - packet->data = data.data; - return 1; - } - else - { - /* errornous packet, drop it */ - } - } - } - } - - return 0; -} - -int netclient_send(NETCLIENT *c, NETPACKET *packet) -{ - dbg_assert(packet->data_size < NETWORK_MAX_PAYLOAD, "packet payload too big"); - - if(packet->flags&PACKETFLAG_CONNLESS) - { - /* send connectionless packet */ - NETPACKETDATA p; - p.ID[0] = 'T'; - p.ID[1] = 'W'; - p.version = NETWORK_VERSION; - p.flags = NETWORK_PACKETFLAG_CONNLESS; - p.seq = 0; - p.ack = 0; - p.crc = 0; - p.token = 0; - p.data_size = packet->data_size; - p.data = (unsigned char *)packet->data; - send_packet(c->socket, &packet->address, &p); - } - else - { - int flags = 0; - dbg_assert(packet->client_id == 0, "errornous client id"); - if(packet->flags&PACKETFLAG_VITAL) - flags |= NETWORK_PACKETFLAG_VITAL; - conn_send(&c->conn, flags, packet->data_size, packet->data); - } - return 0; -} - -int netclient_state(NETCLIENT *c) -{ - if(c->conn.state == NETWORK_CONNSTATE_ONLINE) - return NETSTATE_ONLINE; - if(c->conn.state == NETWORK_CONNSTATE_OFFLINE) - return NETSTATE_OFFLINE; - return NETSTATE_CONNECTING; -} - -int netclient_gotproblems(NETCLIENT *c) -{ - if(time_get() - c->conn.last_recv_time > time_freq()) - return 1; - return 0; -} - -void netclient_stats(NETCLIENT *c, NETSTATS *stats) -{ - *stats = c->conn.stats; -} - -const char *netclient_error_string(NETCLIENT *c) -{ - return conn_error(&c->conn); -} diff --git a/src/engine/network.h b/src/engine/network.h deleted file mode 100644 index 338c5982..00000000 --- a/src/engine/network.h +++ /dev/null @@ -1,113 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -typedef struct -{ - /* -1 means that it's a stateless packet */ - /* 0 on the client means the server */ - int client_id; - NETADDR4 address; /* only used when client_id == -1 */ - int flags; - int data_size; - const void *data; -} NETPACKET; - -typedef struct -{ - int send_bytes; - int recv_bytes; - int send_packets; - int recv_packets; - - int resend_packets; - int resend_bytes; -} NETSTATS; - -typedef struct NETSERVER_t NETSERVER; -typedef struct NETCLIENT_t NETCLIENT; - -enum -{ - NETFLAG_ALLOWSTATELESS=1, - PACKETFLAG_VITAL=1, - PACKETFLAG_CONNLESS=2, - - NETSTATE_OFFLINE=0, - NETSTATE_CONNECTING, - NETSTATE_ONLINE -}; - -typedef int (*NETFUNC_DELCLIENT)(int cid, void *user); -typedef int (*NETFUNC_NEWCLIENT)(int cid, void *user); - -/* server side */ -NETSERVER *netserver_open(NETADDR4 bindaddr, int max_clients, int flags); -int netserver_set_callbacks(NETSERVER *s, NETFUNC_NEWCLIENT new_client, NETFUNC_DELCLIENT del_client, void *user); -int netserver_recv(NETSERVER *s, NETPACKET *packet); -int netserver_send(NETSERVER *s, NETPACKET *packet); -int netserver_close(NETSERVER *s); -int netserver_update(NETSERVER *s); -int netserver_drop(NETSERVER *s, int client_id, const char *reason); -int netserver_max_clients(NETSERVER *s); -void netserver_stats(NETSERVER *s, NETSTATS *stats); - -/* client side */ -NETCLIENT *netclient_open(NETADDR4 bindaddr, int flags); -int netclient_disconnect(NETCLIENT *c, const char *reason); -int netclient_connect(NETCLIENT *c, NETADDR4 *addr); -int netclient_recv(NETCLIENT *c, NETPACKET *packet); -int netclient_send(NETCLIENT *c, NETPACKET *packet); -int netclient_close(NETCLIENT *c); -int netclient_update(NETCLIENT *c); -int netclient_state(NETCLIENT *c); -int netclient_gotproblems(NETCLIENT *c); -void netclient_stats(NETCLIENT *c, NETSTATS *stats); -const char *netclient_error_string(NETCLIENT *c); - -#ifdef __cplusplus -class net_server -{ - NETSERVER *ptr; -public: - net_server() : ptr(0) {} - ~net_server() { close(); } - - int open(NETADDR4 bindaddr, int max, int flags) { ptr = netserver_open(bindaddr, max, flags); return ptr != 0; } - int close() { int r = netserver_close(ptr); ptr = 0; return r; } - - int set_callbacks(NETFUNC_NEWCLIENT new_client, NETFUNC_DELCLIENT del_client, void *user) - { return netserver_set_callbacks(ptr, new_client, del_client, user); } - - int recv(NETPACKET *packet) { return netserver_recv(ptr, packet); } - int send(NETPACKET *packet) { return netserver_send(ptr, packet); } - int update() { return netserver_update(ptr); } - - int drop(int client_id, const char *reason) { return netserver_drop(ptr, client_id, reason); } - - int max_clients() { return netserver_max_clients(ptr); } - void stats(NETSTATS *stats) { netserver_stats(ptr, stats); } -}; - - -class net_client -{ - NETCLIENT *ptr; -public: - net_client() : ptr(0) {} - ~net_client() { close(); } - - int open(NETADDR4 bindaddr, int flags) { ptr = netclient_open(bindaddr, flags); return ptr != 0; } - int close() { int r = netclient_close(ptr); ptr = 0; return r; } - - int connect(NETADDR4 *addr) { return netclient_connect(ptr, addr); } - int disconnect(const char *reason) { return netclient_disconnect(ptr, reason); } - - int recv(NETPACKET *packet) { return netclient_recv(ptr, packet); } - int send(NETPACKET *packet) { return netclient_send(ptr, packet); } - int update() { return netclient_update(ptr); } - - const char *error_string() { return netclient_error_string(ptr); } - - int state() { return netclient_state(ptr); } - void stats(NETSTATS *stats) { netclient_stats(ptr, stats); } -}; -#endif diff --git a/src/engine/packer.c b/src/engine/packer.c deleted file mode 100644 index 30cb6755..00000000 --- a/src/engine/packer.c +++ /dev/null @@ -1,91 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -#include "packer.h" -#include "compression.h" - -void packer_reset(PACKER *p) -{ - p->error = 0; - p->current = p->buffer; - p->end = p->current + PACKER_BUFFER_SIZE; -} - -void packer_add_int(PACKER *p, int i) -{ - p->current = vint_pack(p->current, i); -} - -void packer_add_string(PACKER *p, const char *str, int limit) -{ - if(limit > 0) - { - while(*str && limit != 0) - { - *p->current++ = *str++; - limit--; - } - *p->current++ = 0; - } - else - { - while(*str) - *p->current++ = *str++; - *p->current++ = 0; - } -} - -void packer_add_raw(PACKER *p, const unsigned char *data, int size) -{ - while(size) - { - *p->current++ = *data++; - size--; - } -} - -int packer_size(PACKER *p) -{ - return (const unsigned char *)p->current-(const unsigned char *)p->buffer; -} - -const unsigned char *packer_data(PACKER *p) -{ - return (const unsigned char *)p->buffer; -} - -void unpacker_reset(UNPACKER *p, const unsigned char *data, int size) -{ - p->error = 0; - p->start = data; - p->end = p->start + size; - p->current = p->start; -} - -int unpacker_get_int(UNPACKER *p) -{ - int i; - if(p->current >= p->end) - return 0; - p->current = vint_unpack(p->current, &i); - return i; -} - -const char *unpacker_get_string(UNPACKER *p) -{ - const char *ptr; - if(p->current >= p->end) - return ""; - - ptr = (const char *)p->current; - while(*p->current) /* skip the string */ - p->current++; - p->current++; - return ptr; -} - -const unsigned char *unpacker_get_raw(UNPACKER *p, int size) -{ - const unsigned char *ptr = p->current; - p->current += size; - return ptr; -} diff --git a/src/engine/packer.h b/src/engine/packer.h deleted file mode 100644 index 71b99ff7..00000000 --- a/src/engine/packer.h +++ /dev/null @@ -1,35 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -enum -{ - PACKER_BUFFER_SIZE=1024*2 -}; - -typedef struct -{ - - unsigned char buffer[PACKER_BUFFER_SIZE]; - unsigned char *current; - unsigned char *end; - int error; -} PACKER; - -typedef struct -{ - const unsigned char *current; - const unsigned char *start; - const unsigned char *end; - int error; -} UNPACKER; - -void packer_reset(PACKER *p); -void packer_add_int(PACKER *p, int i); -void packer_add_string(PACKER *p, const char *str, int limit); -void packer_add_raw(PACKER *p, const unsigned char *data, int size); -int packer_size(PACKER *p); -const unsigned char *packer_data(PACKER *p); - -void unpacker_reset(UNPACKER *p, const unsigned char *data, int size); -int unpacker_get_int(UNPACKER *p); -const char *unpacker_get_string(UNPACKER *p); -const unsigned char *unpacker_get_raw(UNPACKER *p, int size); diff --git a/src/engine/protocol.h b/src/engine/protocol.h deleted file mode 100644 index e85ecf9f..00000000 --- a/src/engine/protocol.h +++ /dev/null @@ -1,60 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include "system.h" - -/* - Connection diagram - How the initilization works. - - Client -> INFO -> Server - Contains version info, name, and some other info. - - Client <- MAP <- Server - Contains current map. - - Client -> READY -> Server - The client has loaded the map and is ready to go, - but the mod needs to send it's information aswell. - modc_connected is called on the client and - mods_connected is called on the server. - The client should call client_entergame when the - mod has done it's initilization. - - Client -> ENTERGAME -> Server - Tells the server to start sending snapshots. - client_entergame and server_client_enter is called. -*/ - - -enum -{ - NETMSG_NULL=0, - - /* the first thing sent by the client - contains the version info for the client */ - NETMSG_INFO=1, - - /* sent by server */ - NETMSG_MAP, - NETMSG_SNAP, - NETMSG_SNAPEMPTY, - NETMSG_SNAPSINGLE, - NETMSG_SNAPSMALL, - - /* sent by client */ - NETMSG_READY, - NETMSG_ENTERGAME, - NETMSG_INPUT, - NETMSG_CMD, - - /* sent by both */ - NETMSG_ERROR -}; - - -/* this should be revised */ -enum -{ - MAX_NAME_LENGTH=32, - MAX_CLANNAME_LENGTH=32, - MAX_INPUT_SIZE=128, - MAX_SNAPSHOT_PACKSIZE=900 -}; diff --git a/src/engine/server/es_server.c b/src/engine/server/es_server.c new file mode 100644 index 00000000..00100b10 --- /dev/null +++ b/src/engine/server/es_server.c @@ -0,0 +1,901 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include + +#include + +static SNAPBUILD builder; + +static int64 game_start_time; +static int current_tick = 0; + +static int browseinfo_gametype = -1; +static int browseinfo_progression = -1; + +static int64 lastheartbeat; +static NETADDR4 master_server; + +static char current_map[64]; +static int current_map_crc; + +void *snap_new_item(int type, int id, int size) +{ + dbg_assert(type >= 0 && type <=0xffff, "incorrect type"); + dbg_assert(id >= 0 && id <=0xffff, "incorrect id"); + return snapbuild_new_item(&builder, type, id, size); +} + +typedef struct +{ + short next; + short state; /* 0 = free, 1 = alloced, 2 = timed */ + int timeout; +} SNAP_ID; + +static const int MAX_IDS = 16*1024; /* should be lowered */ +static SNAP_ID snap_ids[16*1024]; +static int snap_first_free_id; +static int snap_first_timed_id; +static int snap_last_timed_id; +static int snap_id_usage; +static int snap_id_inusage; +static int snap_id_inited = 0; + + +enum +{ + SRVCLIENT_STATE_EMPTY = 0, + SRVCLIENT_STATE_CONNECTING, + SRVCLIENT_STATE_READY, + SRVCLIENT_STATE_INGAME +}; + +typedef struct +{ + int data[MAX_INPUT_SIZE]; + int pred_tick; /* tick that the client predicted for the input */ + int game_tick; /* the tick that was chosen for the input */ + int64 timeleft; /* how much time in ms there were left before this should be applied */ +} CLIENT_INPUT; + +/* */ +typedef struct +{ + /* connection state info */ + int state; + int latency; + + int last_acked_snapshot; + SNAPSTORAGE snapshots; + + CLIENT_INPUT inputs[200]; /* TODO: handle input better */ + int current_input; + + char name[MAX_NAME_LENGTH]; + char clan[MAX_CLANNAME_LENGTH]; +} CLIENT; + +static CLIENT clients[MAX_CLIENTS]; +static NETSERVER *net; + +static void snap_init_id() +{ + int i; + for(i = 0; i < MAX_IDS; i++) + { + snap_ids[i].next = i+1; + snap_ids[i].state = 0; + } + + snap_ids[MAX_IDS-1].next = -1; + snap_first_free_id = 0; + snap_first_timed_id = -1; + snap_last_timed_id = -1; + snap_id_usage = 0; + snap_id_inusage = 0; + + snap_id_inited = 1; +} + +static void snap_remove_first_timeout() +{ + int next_timed = snap_ids[snap_first_timed_id].next; + + /* add it to the free list */ + snap_ids[snap_first_timed_id].next = snap_first_free_id; + snap_ids[snap_first_timed_id].state = 0; + snap_first_free_id = snap_first_timed_id; + + /* remove it from the timed list */ + snap_first_timed_id = next_timed; + if(snap_first_timed_id == -1) + snap_last_timed_id = -1; + + snap_id_usage--; +} + +int snap_new_id() +{ + int id; + int64 now = time_get(); + dbg_assert(snap_id_inited == 1, "requesting id too soon"); + + + /* process timed ids */ + while(snap_first_timed_id != -1 && snap_ids[snap_first_timed_id].timeout < now) + snap_remove_first_timeout(); + + id = snap_first_free_id; + dbg_assert(id != -1, "id error"); + snap_first_free_id = snap_ids[snap_first_free_id].next; + snap_ids[id].state = 1; + snap_id_usage++; + snap_id_inusage++; + return id; +} + +void snap_timeout_ids() +{ + /* process timed ids */ + while(snap_first_timed_id != -1) + snap_remove_first_timeout(); +} + +void snap_free_id(int id) +{ + dbg_assert(snap_ids[id].state == 1, "id is not alloced"); + + snap_id_inusage--; + snap_ids[id].state = 2; + snap_ids[id].timeout = time_get()+time_freq()*5; + snap_ids[id].next = -1; + + if(snap_last_timed_id != -1) + { + snap_ids[snap_last_timed_id].next = id; + snap_last_timed_id = id; + } + else + { + snap_first_timed_id = id; + snap_last_timed_id = id; + } +} + +const char *server_clientname(int client_id) +{ + if(client_id < 0 || client_id > MAX_CLIENTS || clients[client_id].state < SRVCLIENT_STATE_READY) + return "(invalid client)"; + return clients[client_id].name; +} + +void server_setclientname(int client_id, const char *name) +{ + if(client_id < 0 || client_id > MAX_CLIENTS || clients[client_id].state < SRVCLIENT_STATE_READY) + return; + strncpy(clients[client_id].name, name, MAX_NAME_LENGTH); +} + +void server_setbrowseinfo(int game_type, int progression) +{ + browseinfo_gametype = game_type; + browseinfo_progression = progression; +} + +int server_tick() +{ + return current_tick; +} + +int64 server_tick_start_time(int tick) +{ + return game_start_time + (time_freq()*tick)/SERVER_TICK_SPEED; +} + +int server_tickspeed() +{ + return SERVER_TICK_SPEED; +} + +int server_init() +{ + int i; + for(i = 0; i < MAX_CLIENTS; i++) + { + clients[i].state = SRVCLIENT_STATE_EMPTY; + clients[i].name[0] = 0; + clients[i].clan[0] = 0; + snapstorage_init(&clients[i].snapshots); + } + + current_tick = 0; + + return 0; +} + +int server_getclientinfo(int client_id, CLIENT_INFO *info) +{ + dbg_assert(client_id >= 0 && client_id < MAX_CLIENTS, "client_id is not valid"); + dbg_assert(info != 0, "info can not be null"); + + if(clients[client_id].state == SRVCLIENT_STATE_INGAME) + { + info->name = clients[client_id].name; + info->latency = clients[client_id].latency; + return 1; + } + return 0; +} + + +int server_send_msg(int client_id) +{ + const MSG_INFO *info = msg_get_info(); + NETPACKET packet; + mem_zero(&packet, sizeof(NETPACKET)); + + packet.client_id = client_id; + packet.data = info->data; + packet.data_size = info->size; + + if(info->flags&MSGFLAG_VITAL) + packet.flags = PACKETFLAG_VITAL; + + if(client_id == -1) + { + /* broadcast */ + int i; + for(i = 0; i < MAX_CLIENTS; i++) + if(clients[i].state == SRVCLIENT_STATE_INGAME) + { + packet.client_id = i; + netserver_send(net, &packet); + } + } + else + netserver_send(net, &packet); + return 0; +} + +static void server_do_snap() +{ + int i, k; + mods_presnap(); + + for(i = 0; i < MAX_CLIENTS; i++) + { + if(clients[i].state == SRVCLIENT_STATE_INGAME) + { + char data[MAX_SNAPSHOT_SIZE]; + char deltadata[MAX_SNAPSHOT_SIZE]; + char compdata[MAX_SNAPSHOT_SIZE]; + int snapshot_size; + int crc; + static SNAPSHOT emptysnap; + SNAPSHOT *deltashot = &emptysnap; + int deltashot_size; + int delta_tick = -1; + int input_predtick = -1; + int64 timeleft = 0; + int deltasize; + + snapbuild_init(&builder); + mods_snap(i); + + /* finish snapshot */ + snapshot_size = snapbuild_finish(&builder, data); + crc = snapshot_crc((SNAPSHOT*)data); + + /* remove old snapshos */ + /* keep 1 seconds worth of snapshots */ + snapstorage_purge_until(&clients[i].snapshots, current_tick-SERVER_TICK_SPEED); + + /* save it the snapshot */ + snapstorage_add(&clients[i].snapshots, current_tick, time_get(), snapshot_size, data); + + /* find snapshot that we can preform delta against */ + emptysnap.data_size = 0; + emptysnap.num_items = 0; + + { + deltashot_size = snapstorage_get(&clients[i].snapshots, clients[i].last_acked_snapshot, 0, &deltashot); + if(deltashot_size >= 0) + delta_tick = clients[i].last_acked_snapshot; + } + + for(k = 0; k < 200; k++) /* TODO: do this better */ + { + if(clients[i].inputs[k].game_tick == current_tick) + { + timeleft = clients[i].inputs[k].timeleft; + input_predtick = clients[i].inputs[k].pred_tick; + break; + } + } + + /* create delta */ + deltasize = snapshot_create_delta(deltashot, (SNAPSHOT*)data, deltadata); + + if(deltasize) + { + /* compress it */ + unsigned char intdata[MAX_SNAPSHOT_SIZE]; + int intsize = intpack_compress(deltadata, deltasize, intdata); + int snapshot_size = zerobit_compress(intdata, intsize, compdata); + const int max_size = MAX_SNAPSHOT_PACKSIZE; + int numpackets = (snapshot_size+max_size-1)/max_size; + int n, left; + + for(n = 0, left = snapshot_size; left; n++) + { + int chunk = left < max_size ? left : max_size; + left -= chunk; + + if(numpackets == 1) + msg_pack_start_system(NETMSG_SNAPSINGLE, 0); + else + msg_pack_start_system(NETMSG_SNAP, 0); + + msg_pack_int(current_tick); + msg_pack_int(current_tick-delta_tick); /* compressed with */ + msg_pack_int(input_predtick); + msg_pack_int((timeleft*1000)/time_freq()); + + if(numpackets != 1) + { + msg_pack_int(numpackets); + msg_pack_int(n); + } + + msg_pack_int(crc); + msg_pack_int(chunk); + msg_pack_raw(&compdata[n*max_size], chunk); + msg_pack_end(); + server_send_msg(i); + } + } + else + { + msg_pack_start_system(NETMSG_SNAPEMPTY, 0); + msg_pack_int(current_tick); + msg_pack_int(current_tick-delta_tick); /* compressed with */ + msg_pack_int(input_predtick); + msg_pack_int((timeleft*1000)/time_freq()); + msg_pack_end(); + server_send_msg(i); + } + } + } + + mods_postsnap(); +} + + +static int new_client_callback(int cid, void *user) +{ + int i; + clients[cid].state = SRVCLIENT_STATE_CONNECTING; + clients[cid].name[0] = 0; + clients[cid].clan[0] = 0; + + /* reset input */ + for(i = 0; i < 200; i++) + { + clients[cid].inputs[i].game_tick = -1; + clients[cid].inputs[i].pred_tick = -1; + } + clients[cid].current_input = 0; + + snapstorage_purge_all(&clients[cid].snapshots); + clients[cid].last_acked_snapshot = -1; + return 0; +} + +static int del_client_callback(int cid, void *user) +{ + /* notify the mod about the drop */ + if(clients[cid].state == SRVCLIENT_STATE_READY || + clients[cid].state == SRVCLIENT_STATE_INGAME) + { + mods_client_drop(cid); + } + + clients[cid].state = SRVCLIENT_STATE_EMPTY; + clients[cid].name[0] = 0; + clients[cid].clan[0] = 0; + snapstorage_purge_all(&clients[cid].snapshots); + return 0; +} + + +static void server_send_map(int cid) +{ + msg_pack_start_system(NETMSG_MAP, MSGFLAG_VITAL); + msg_pack_string(config.sv_map, 0); + msg_pack_int(current_map_crc); + msg_pack_end(); + server_send_msg(cid); +} + +static void server_send_heartbeat() +{ + static unsigned char data[sizeof(SERVERBROWSE_HEARTBEAT) + 2]; + unsigned short port = config.sv_port; + NETPACKET packet; + + mem_copy(data, SERVERBROWSE_HEARTBEAT, sizeof(SERVERBROWSE_HEARTBEAT)); + + packet.client_id = -1; + packet.address = master_server; + packet.flags = PACKETFLAG_CONNLESS; + packet.data_size = sizeof(SERVERBROWSE_HEARTBEAT) + 2; + packet.data = &data; + + /* supply the set port that the master can use if it has problems */ + if(config.sv_external_port) + port = config.sv_external_port; + data[sizeof(SERVERBROWSE_HEARTBEAT)] = port >> 8; + data[sizeof(SERVERBROWSE_HEARTBEAT)+1] = port&0xff; + + netserver_send(net, &packet); +} + +static void server_process_client_packet(NETPACKET *packet) +{ + int cid = packet->client_id; + int sys; + int msg = msg_unpack_start(packet->data, packet->data_size, &sys); + if(sys) + { + /* system message */ + if(msg == NETMSG_INFO) + { + char version[64]; + const char *password; + strncpy(version, msg_unpack_string(), 64); + if(strcmp(version, mods_net_version()) != 0) + { + /* OH FUCK! wrong version, drop him */ + char reason[256]; + sprintf(reason, "wrong version. server is running %s.", mods_net_version()); + netserver_drop(net, cid, reason); + return; + } + + strncpy(clients[cid].name, msg_unpack_string(), MAX_NAME_LENGTH); + strncpy(clients[cid].clan, msg_unpack_string(), MAX_CLANNAME_LENGTH); + password = msg_unpack_string(); + + if(config.password[0] != 0 && strcmp(config.password, password) != 0) + { + /* wrong password */ + netserver_drop(net, cid, "wrong password"); + return; + } + + server_send_map(cid); + } + else if(msg == NETMSG_READY) + { + if(clients[cid].state == SRVCLIENT_STATE_CONNECTING) + { + dbg_msg("server", "player is ready. cid=%x", cid); + clients[cid].state = SRVCLIENT_STATE_READY; + mods_connected(cid); + } + } + else if(msg == NETMSG_ENTERGAME) + { + if(clients[cid].state != SRVCLIENT_STATE_INGAME) + { + dbg_msg("server", "player as entered the game. cid=%x", cid); + clients[cid].state = SRVCLIENT_STATE_INGAME; + mods_client_enter(cid); + } + } + else if(msg == NETMSG_INPUT) + { + int tick, size, i; + CLIENT_INPUT *input; + int64 tagtime; + + clients[cid].last_acked_snapshot = msg_unpack_int(); + if(snapstorage_get(&clients[cid].snapshots, clients[cid].last_acked_snapshot, &tagtime, 0) >= 0) + clients[cid].latency = (int)(((time_get()-tagtime)*1000)/time_freq()); + + tick = msg_unpack_int(); + size = msg_unpack_int(); + + input = &clients[cid].inputs[clients[cid].current_input]; + input->timeleft = server_tick_start_time(tick)-time_get(); + input->pred_tick = tick; + + if(tick <= server_tick()) + { + /* TODO: how should we handle this */ + dbg_msg("server", "input got in late for=%d cur=%d", tick, server_tick()); + tick = server_tick()+1; + } + + input->game_tick = tick; + + for(i = 0; i < size/4; i++) + input->data[i] = msg_unpack_int(); + + clients[cid].current_input++; + clients[cid].current_input %= 200; + } + else if(msg == NETMSG_CMD) + { + const char *pw = msg_unpack_string(); + const char *cmd = msg_unpack_string(); + if(config.rcon_password[0] != 0 && strcmp(pw, config.rcon_password) == 0) + { + dbg_msg("server", "cid=%d rcon='%s'", cid, cmd); + config_set(cmd); + } + } + else + { + dbg_msg("server", "strange message cid=%d msg=%d data_size=%d", cid, msg, packet->data_size); + } + } + else + { + /* game message */ + mods_message(msg, cid); + } +} + +static void server_send_serverinfo(NETADDR4 *addr) +{ + NETPACKET packet; + PACKER p; + char buf[128]; + + /* count the players */ + int c = 0; + int i; + for(i = 0; i < MAX_CLIENTS; i++) + { + if(clients[i].state != SRVCLIENT_STATE_EMPTY) + c++; + } + + packer_reset(&p); + packer_add_raw(&p, SERVERBROWSE_INFO, sizeof(SERVERBROWSE_INFO)); + packer_add_string(&p, mods_net_version(), 32); + packer_add_string(&p, config.sv_name, 64); + packer_add_string(&p, config.sv_map, 32); + + /* gametype */ + sprintf(buf, "%d", browseinfo_gametype); + packer_add_string(&p, buf, 2); + + /* flags */ + i = 0; + if(strlen(config.password)) + i |= 1; + sprintf(buf, "%d", i); + packer_add_string(&p, buf, 2); + + /* progression */ + sprintf(buf, "%d", browseinfo_progression); + packer_add_string(&p, buf, 4); + + sprintf(buf, "%d", c); packer_add_string(&p, buf, 3); /* num players */ + sprintf(buf, "%d", netserver_max_clients(net)); packer_add_string(&p, buf, 3); /* max players */ + + for(i = 0; i < MAX_CLIENTS; i++) + { + if(clients[i].state != SRVCLIENT_STATE_EMPTY) + { + packer_add_string(&p, clients[i].name, 48); /* player name */ + packer_add_string(&p, "0", 6); /* score */ + } + } + + + packet.client_id = -1; + packet.address = *addr; + packet.flags = PACKETFLAG_CONNLESS; + packet.data_size = packer_size(&p); + packet.data = packer_data(&p); + netserver_send(net, &packet); +} + + +static void server_send_fwcheckresponse(NETADDR4 *addr) +{ + NETPACKET packet; + packet.client_id = -1; + packet.address = *addr; + packet.flags = PACKETFLAG_CONNLESS; + packet.data_size = sizeof(SERVERBROWSE_FWRESPONSE); + packet.data = SERVERBROWSE_FWRESPONSE; + netserver_send(net, &packet); +} + +static void server_pump_network() +{ + NETPACKET packet; + + netserver_update(net); + + /* process packets */ + while(netserver_recv(net, &packet)) + { + if(packet.client_id == -1) + { + /* stateless */ + if(packet.data_size == sizeof(SERVERBROWSE_GETINFO) && + memcmp(packet.data, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO)) == 0) + { + server_send_serverinfo(&packet.address); + } + else if(packet.data_size == sizeof(SERVERBROWSE_FWCHECK) && + memcmp(packet.data, SERVERBROWSE_FWCHECK, sizeof(SERVERBROWSE_FWCHECK)) == 0) + { + server_send_fwcheckresponse(&packet.address); + } + else if(packet.data_size == sizeof(SERVERBROWSE_FWOK) && + memcmp(packet.data, SERVERBROWSE_FWOK, sizeof(SERVERBROWSE_FWOK)) == 0) + { + if(config.debug) + dbg_msg("server", "no firewall/nat problems detected"); + } + else if(packet.data_size == sizeof(SERVERBROWSE_FWERROR) && + memcmp(packet.data, SERVERBROWSE_FWERROR, sizeof(SERVERBROWSE_FWERROR)) == 0) + { + dbg_msg("server", "ERROR: the master server reports that clients can not connect to this server."); + dbg_msg("server", "ERROR: configure your firewall/nat to let trough udp on port %d.", config.sv_port); + } + } + else + server_process_client_packet(&packet); + } +} + +static int server_load_map(const char *mapname) +{ + DATAFILE *df; + char buf[512]; + sprintf(buf, "data/maps/%s.map", mapname); + df = datafile_load(buf); + if(!df) + return 0; + + /* reinit snapshot ids */ + snap_timeout_ids(); + + /* get the crc of the map */ + current_map_crc = datafile_crc(buf); + dbg_msg("server", "%s crc is %08x", buf, current_map_crc); + + strcpy(current_map, mapname); + map_set(df); + return 1; +} + + +static int server_run() +{ + NETADDR4 bindaddr; + + net_init(); + + snap_init_id(); + + /* load map */ + if(!server_load_map(config.sv_map)) + { + dbg_msg("server", "failed to load map. mapname='%s'", config.sv_map); + return -1; + } + + /* start server */ + if(strlen(config.sv_bindaddr) && net_host_lookup(config.sv_bindaddr, config.sv_port, &bindaddr) != 0) + { + /* sweet! */ + } + else + { + mem_zero(&bindaddr, sizeof(bindaddr)); + bindaddr.port = config.sv_port; + } + + net = netserver_open(bindaddr, config.sv_max_clients, 0); + if(!net) + { + dbg_msg("server", "couldn't open socket. port might already be in use"); + return -1; + } + + netserver_set_callbacks(net, new_client_callback, del_client_callback, 0); + + dbg_msg("server", "server name is '%s'", config.sv_name); + dbg_msg("server", "masterserver is '%s'", config.masterserver); + if(net_host_lookup(config.masterserver, MASTERSERVER_PORT, &master_server) != 0) + { + /* TODO: fix me */ + /*master_server = netaddr4(0, 0, 0, 0, 0); */ + } + + mods_init(); + dbg_msg("server", "version %s", mods_net_version()); + + /* start game */ + { + int64 time_per_heartbeat = time_freq() * 30; + int64 reporttime = time_get(); + int reportinterval = 3; + + int64 simulationtime = 0; + int64 snaptime = 0; + int64 networktime = 0; + int64 totaltime = 0; + + lastheartbeat = 0; + game_start_time = time_get(); + + if(config.debug) + dbg_msg("server", "baseline memory usage %dk", mem_allocated()/1024); + + while(1) + { + int64 t = time_get(); + /* load new map TODO: don't poll this */ + if(strcmp(config.sv_map, current_map) != 0 || config.sv_map_reload) + { + config.sv_map_reload = 0; + + /* load map */ + if(server_load_map(config.sv_map)) + { + int c; + + /* new map loaded */ + mods_shutdown(); + + for(c = 0; c < MAX_CLIENTS; c++) + { + if(clients[c].state == SRVCLIENT_STATE_EMPTY) + continue; + + server_send_map(c); + clients[c].state = SRVCLIENT_STATE_CONNECTING; + clients[c].last_acked_snapshot = -1; + snapstorage_purge_all(&clients[c].snapshots); + } + + game_start_time = time_get(); + current_tick = 0; + mods_init(); + } + else + { + dbg_msg("server", "failed to load map. mapname='%s'", config.sv_map); + config_set_sv_map(&config, current_map); + } + } + + if(t > server_tick_start_time(current_tick+1)) + { + current_tick++; + + /* apply new input */ + { + int c, i; + for(c = 0; c < MAX_CLIENTS; c++) + { + if(clients[c].state == SRVCLIENT_STATE_EMPTY) + continue; + for(i = 0; i < 200; i++) + { + if(clients[c].inputs[i].game_tick == server_tick()) + { + mods_client_input(c, clients[c].inputs[i].data); + break; + } + } + } + } + + /* progress game */ + { + int64 start = time_get(); + mods_tick(); + simulationtime += time_get()-start; + } + + /* snap game */ + if(config.sv_bandwidth_mode == 0 || + (config.sv_bandwidth_mode == 1 && current_tick%2) || + (config.sv_bandwidth_mode == 2 && (current_tick%3) == 0 )) + /* if(current_tick&1) */ + { + int64 start = time_get(); + server_do_snap(); + snaptime += time_get()-start; + } + } + + if(config.sv_sendheartbeats) + { + if (t > lastheartbeat+time_per_heartbeat) + { + server_send_heartbeat(); + lastheartbeat = t+time_per_heartbeat; + } + } + + { + int64 start = time_get(); + server_pump_network(); + networktime += time_get()-start; + } + + if(reporttime < time_get()) + { + if(config.debug) + { + static NETSTATS prev_stats; + NETSTATS stats; + netserver_stats(net, &stats); + dbg_msg("server", "sim=%.02fms snap=%.02fms net=%.02fms tot=%.02fms load=%.02f%%", + (simulationtime/reportinterval)/(double)time_freq()*1000, + (snaptime/reportinterval)/(double)time_freq()*1000, + (networktime/reportinterval)/(double)time_freq()*1000, + (totaltime/reportinterval)/(double)time_freq()*1000, + (totaltime)/reportinterval/(double)time_freq()*100.0f); + + dbg_msg("server", "send=%8d recv=%8d", + (stats.send_bytes - prev_stats.send_bytes)/reportinterval, + (stats.recv_bytes - prev_stats.recv_bytes)/reportinterval); + + prev_stats = stats; + } + + simulationtime = 0; + snaptime = 0; + networktime = 0; + totaltime = 0; + + reporttime += time_freq()*reportinterval; + } + totaltime += time_get()-t; + thread_sleep(1); + } + } + + mods_shutdown(); + map_unload(); + + return 0; +} + + +int main(int argc, char **argv) +{ + dbg_msg("server", "starting..."); + engine_init("Teewars", argc, argv); + server_run(); + return 0; +} + diff --git a/src/engine/server/server.c b/src/engine/server/server.c deleted file mode 100644 index d2ef39ed..00000000 --- a/src/engine/server/server.c +++ /dev/null @@ -1,901 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -#include - -#include -#include -#include -#include - -#include - -static SNAPBUILD builder; - -static int64 game_start_time; -static int current_tick = 0; - -static int browseinfo_gametype = -1; -static int browseinfo_progression = -1; - -static int64 lastheartbeat; -static NETADDR4 master_server; - -static char current_map[64]; -static int current_map_crc; - -void *snap_new_item(int type, int id, int size) -{ - dbg_assert(type >= 0 && type <=0xffff, "incorrect type"); - dbg_assert(id >= 0 && id <=0xffff, "incorrect id"); - return snapbuild_new_item(&builder, type, id, size); -} - -typedef struct -{ - short next; - short state; /* 0 = free, 1 = alloced, 2 = timed */ - int timeout; -} SNAP_ID; - -static const int MAX_IDS = 16*1024; /* should be lowered */ -static SNAP_ID snap_ids[16*1024]; -static int snap_first_free_id; -static int snap_first_timed_id; -static int snap_last_timed_id; -static int snap_id_usage; -static int snap_id_inusage; -static int snap_id_inited = 0; - - -enum -{ - SRVCLIENT_STATE_EMPTY = 0, - SRVCLIENT_STATE_CONNECTING, - SRVCLIENT_STATE_READY, - SRVCLIENT_STATE_INGAME -}; - -typedef struct -{ - int data[MAX_INPUT_SIZE]; - int pred_tick; /* tick that the client predicted for the input */ - int game_tick; /* the tick that was chosen for the input */ - int64 timeleft; /* how much time in ms there were left before this should be applied */ -} CLIENT_INPUT; - -/* */ -typedef struct -{ - /* connection state info */ - int state; - int latency; - - int last_acked_snapshot; - SNAPSTORAGE snapshots; - - CLIENT_INPUT inputs[200]; /* TODO: handle input better */ - int current_input; - - char name[MAX_NAME_LENGTH]; - char clan[MAX_CLANNAME_LENGTH]; -} CLIENT; - -static CLIENT clients[MAX_CLIENTS]; -static NETSERVER *net; - -static void snap_init_id() -{ - int i; - for(i = 0; i < MAX_IDS; i++) - { - snap_ids[i].next = i+1; - snap_ids[i].state = 0; - } - - snap_ids[MAX_IDS-1].next = -1; - snap_first_free_id = 0; - snap_first_timed_id = -1; - snap_last_timed_id = -1; - snap_id_usage = 0; - snap_id_inusage = 0; - - snap_id_inited = 1; -} - -static void snap_remove_first_timeout() -{ - int next_timed = snap_ids[snap_first_timed_id].next; - - /* add it to the free list */ - snap_ids[snap_first_timed_id].next = snap_first_free_id; - snap_ids[snap_first_timed_id].state = 0; - snap_first_free_id = snap_first_timed_id; - - /* remove it from the timed list */ - snap_first_timed_id = next_timed; - if(snap_first_timed_id == -1) - snap_last_timed_id = -1; - - snap_id_usage--; -} - -int snap_new_id() -{ - int id; - int64 now = time_get(); - dbg_assert(snap_id_inited == 1, "requesting id too soon"); - - - /* process timed ids */ - while(snap_first_timed_id != -1 && snap_ids[snap_first_timed_id].timeout < now) - snap_remove_first_timeout(); - - id = snap_first_free_id; - dbg_assert(id != -1, "id error"); - snap_first_free_id = snap_ids[snap_first_free_id].next; - snap_ids[id].state = 1; - snap_id_usage++; - snap_id_inusage++; - return id; -} - -void snap_timeout_ids() -{ - /* process timed ids */ - while(snap_first_timed_id != -1) - snap_remove_first_timeout(); -} - -void snap_free_id(int id) -{ - dbg_assert(snap_ids[id].state == 1, "id is not alloced"); - - snap_id_inusage--; - snap_ids[id].state = 2; - snap_ids[id].timeout = time_get()+time_freq()*5; - snap_ids[id].next = -1; - - if(snap_last_timed_id != -1) - { - snap_ids[snap_last_timed_id].next = id; - snap_last_timed_id = id; - } - else - { - snap_first_timed_id = id; - snap_last_timed_id = id; - } -} - -const char *server_clientname(int client_id) -{ - if(client_id < 0 || client_id > MAX_CLIENTS || clients[client_id].state < SRVCLIENT_STATE_READY) - return "(invalid client)"; - return clients[client_id].name; -} - -void server_setclientname(int client_id, const char *name) -{ - if(client_id < 0 || client_id > MAX_CLIENTS || clients[client_id].state < SRVCLIENT_STATE_READY) - return; - strncpy(clients[client_id].name, name, MAX_NAME_LENGTH); -} - -void server_setbrowseinfo(int game_type, int progression) -{ - browseinfo_gametype = game_type; - browseinfo_progression = progression; -} - -int server_tick() -{ - return current_tick; -} - -int64 server_tick_start_time(int tick) -{ - return game_start_time + (time_freq()*tick)/SERVER_TICK_SPEED; -} - -int server_tickspeed() -{ - return SERVER_TICK_SPEED; -} - -int server_init() -{ - int i; - for(i = 0; i < MAX_CLIENTS; i++) - { - clients[i].state = SRVCLIENT_STATE_EMPTY; - clients[i].name[0] = 0; - clients[i].clan[0] = 0; - snapstorage_init(&clients[i].snapshots); - } - - current_tick = 0; - - return 0; -} - -int server_getclientinfo(int client_id, CLIENT_INFO *info) -{ - dbg_assert(client_id >= 0 && client_id < MAX_CLIENTS, "client_id is not valid"); - dbg_assert(info != 0, "info can not be null"); - - if(clients[client_id].state == SRVCLIENT_STATE_INGAME) - { - info->name = clients[client_id].name; - info->latency = clients[client_id].latency; - return 1; - } - return 0; -} - - -int server_send_msg(int client_id) -{ - const MSG_INFO *info = msg_get_info(); - NETPACKET packet; - mem_zero(&packet, sizeof(NETPACKET)); - - packet.client_id = client_id; - packet.data = info->data; - packet.data_size = info->size; - - if(info->flags&MSGFLAG_VITAL) - packet.flags = PACKETFLAG_VITAL; - - if(client_id == -1) - { - /* broadcast */ - int i; - for(i = 0; i < MAX_CLIENTS; i++) - if(clients[i].state == SRVCLIENT_STATE_INGAME) - { - packet.client_id = i; - netserver_send(net, &packet); - } - } - else - netserver_send(net, &packet); - return 0; -} - -static void server_do_snap() -{ - int i, k; - mods_presnap(); - - for(i = 0; i < MAX_CLIENTS; i++) - { - if(clients[i].state == SRVCLIENT_STATE_INGAME) - { - char data[MAX_SNAPSHOT_SIZE]; - char deltadata[MAX_SNAPSHOT_SIZE]; - char compdata[MAX_SNAPSHOT_SIZE]; - int snapshot_size; - int crc; - static SNAPSHOT emptysnap; - SNAPSHOT *deltashot = &emptysnap; - int deltashot_size; - int delta_tick = -1; - int input_predtick = -1; - int64 timeleft = 0; - int deltasize; - - snapbuild_init(&builder); - mods_snap(i); - - /* finish snapshot */ - snapshot_size = snapbuild_finish(&builder, data); - crc = snapshot_crc((SNAPSHOT*)data); - - /* remove old snapshos */ - /* keep 1 seconds worth of snapshots */ - snapstorage_purge_until(&clients[i].snapshots, current_tick-SERVER_TICK_SPEED); - - /* save it the snapshot */ - snapstorage_add(&clients[i].snapshots, current_tick, time_get(), snapshot_size, data); - - /* find snapshot that we can preform delta against */ - emptysnap.data_size = 0; - emptysnap.num_items = 0; - - { - deltashot_size = snapstorage_get(&clients[i].snapshots, clients[i].last_acked_snapshot, 0, &deltashot); - if(deltashot_size >= 0) - delta_tick = clients[i].last_acked_snapshot; - } - - for(k = 0; k < 200; k++) /* TODO: do this better */ - { - if(clients[i].inputs[k].game_tick == current_tick) - { - timeleft = clients[i].inputs[k].timeleft; - input_predtick = clients[i].inputs[k].pred_tick; - break; - } - } - - /* create delta */ - deltasize = snapshot_create_delta(deltashot, (SNAPSHOT*)data, deltadata); - - if(deltasize) - { - /* compress it */ - unsigned char intdata[MAX_SNAPSHOT_SIZE]; - int intsize = intpack_compress(deltadata, deltasize, intdata); - int snapshot_size = zerobit_compress(intdata, intsize, compdata); - const int max_size = MAX_SNAPSHOT_PACKSIZE; - int numpackets = (snapshot_size+max_size-1)/max_size; - int n, left; - - for(n = 0, left = snapshot_size; left; n++) - { - int chunk = left < max_size ? left : max_size; - left -= chunk; - - if(numpackets == 1) - msg_pack_start_system(NETMSG_SNAPSINGLE, 0); - else - msg_pack_start_system(NETMSG_SNAP, 0); - - msg_pack_int(current_tick); - msg_pack_int(current_tick-delta_tick); /* compressed with */ - msg_pack_int(input_predtick); - msg_pack_int((timeleft*1000)/time_freq()); - - if(numpackets != 1) - { - msg_pack_int(numpackets); - msg_pack_int(n); - } - - msg_pack_int(crc); - msg_pack_int(chunk); - msg_pack_raw(&compdata[n*max_size], chunk); - msg_pack_end(); - server_send_msg(i); - } - } - else - { - msg_pack_start_system(NETMSG_SNAPEMPTY, 0); - msg_pack_int(current_tick); - msg_pack_int(current_tick-delta_tick); /* compressed with */ - msg_pack_int(input_predtick); - msg_pack_int((timeleft*1000)/time_freq()); - msg_pack_end(); - server_send_msg(i); - } - } - } - - mods_postsnap(); -} - - -static int new_client_callback(int cid, void *user) -{ - int i; - clients[cid].state = SRVCLIENT_STATE_CONNECTING; - clients[cid].name[0] = 0; - clients[cid].clan[0] = 0; - - /* reset input */ - for(i = 0; i < 200; i++) - { - clients[cid].inputs[i].game_tick = -1; - clients[cid].inputs[i].pred_tick = -1; - } - clients[cid].current_input = 0; - - snapstorage_purge_all(&clients[cid].snapshots); - clients[cid].last_acked_snapshot = -1; - return 0; -} - -static int del_client_callback(int cid, void *user) -{ - /* notify the mod about the drop */ - if(clients[cid].state == SRVCLIENT_STATE_READY || - clients[cid].state == SRVCLIENT_STATE_INGAME) - { - mods_client_drop(cid); - } - - clients[cid].state = SRVCLIENT_STATE_EMPTY; - clients[cid].name[0] = 0; - clients[cid].clan[0] = 0; - snapstorage_purge_all(&clients[cid].snapshots); - return 0; -} - - -static void server_send_map(int cid) -{ - msg_pack_start_system(NETMSG_MAP, MSGFLAG_VITAL); - msg_pack_string(config.sv_map, 0); - msg_pack_int(current_map_crc); - msg_pack_end(); - server_send_msg(cid); -} - -static void server_send_heartbeat() -{ - static unsigned char data[sizeof(SERVERBROWSE_HEARTBEAT) + 2]; - unsigned short port = config.sv_port; - NETPACKET packet; - - mem_copy(data, SERVERBROWSE_HEARTBEAT, sizeof(SERVERBROWSE_HEARTBEAT)); - - packet.client_id = -1; - packet.address = master_server; - packet.flags = PACKETFLAG_CONNLESS; - packet.data_size = sizeof(SERVERBROWSE_HEARTBEAT) + 2; - packet.data = &data; - - /* supply the set port that the master can use if it has problems */ - if(config.sv_external_port) - port = config.sv_external_port; - data[sizeof(SERVERBROWSE_HEARTBEAT)] = port >> 8; - data[sizeof(SERVERBROWSE_HEARTBEAT)+1] = port&0xff; - - netserver_send(net, &packet); -} - -static void server_process_client_packet(NETPACKET *packet) -{ - int cid = packet->client_id; - int sys; - int msg = msg_unpack_start(packet->data, packet->data_size, &sys); - if(sys) - { - /* system message */ - if(msg == NETMSG_INFO) - { - char version[64]; - const char *password; - strncpy(version, msg_unpack_string(), 64); - if(strcmp(version, mods_net_version()) != 0) - { - /* OH FUCK! wrong version, drop him */ - char reason[256]; - sprintf(reason, "wrong version. server is running %s.", mods_net_version()); - netserver_drop(net, cid, reason); - return; - } - - strncpy(clients[cid].name, msg_unpack_string(), MAX_NAME_LENGTH); - strncpy(clients[cid].clan, msg_unpack_string(), MAX_CLANNAME_LENGTH); - password = msg_unpack_string(); - - if(config.password[0] != 0 && strcmp(config.password, password) != 0) - { - /* wrong password */ - netserver_drop(net, cid, "wrong password"); - return; - } - - server_send_map(cid); - } - else if(msg == NETMSG_READY) - { - if(clients[cid].state == SRVCLIENT_STATE_CONNECTING) - { - dbg_msg("server", "player is ready. cid=%x", cid); - clients[cid].state = SRVCLIENT_STATE_READY; - mods_connected(cid); - } - } - else if(msg == NETMSG_ENTERGAME) - { - if(clients[cid].state != SRVCLIENT_STATE_INGAME) - { - dbg_msg("server", "player as entered the game. cid=%x", cid); - clients[cid].state = SRVCLIENT_STATE_INGAME; - mods_client_enter(cid); - } - } - else if(msg == NETMSG_INPUT) - { - int tick, size, i; - CLIENT_INPUT *input; - int64 tagtime; - - clients[cid].last_acked_snapshot = msg_unpack_int(); - if(snapstorage_get(&clients[cid].snapshots, clients[cid].last_acked_snapshot, &tagtime, 0) >= 0) - clients[cid].latency = (int)(((time_get()-tagtime)*1000)/time_freq()); - - tick = msg_unpack_int(); - size = msg_unpack_int(); - - input = &clients[cid].inputs[clients[cid].current_input]; - input->timeleft = server_tick_start_time(tick)-time_get(); - input->pred_tick = tick; - - if(tick <= server_tick()) - { - /* TODO: how should we handle this */ - dbg_msg("server", "input got in late for=%d cur=%d", tick, server_tick()); - tick = server_tick()+1; - } - - input->game_tick = tick; - - for(i = 0; i < size/4; i++) - input->data[i] = msg_unpack_int(); - - clients[cid].current_input++; - clients[cid].current_input %= 200; - } - else if(msg == NETMSG_CMD) - { - const char *pw = msg_unpack_string(); - const char *cmd = msg_unpack_string(); - if(config.rcon_password[0] != 0 && strcmp(pw, config.rcon_password) == 0) - { - dbg_msg("server", "cid=%d rcon='%s'", cid, cmd); - config_set(cmd); - } - } - else - { - dbg_msg("server", "strange message cid=%d msg=%d data_size=%d", cid, msg, packet->data_size); - } - } - else - { - /* game message */ - mods_message(msg, cid); - } -} - -static void server_send_serverinfo(NETADDR4 *addr) -{ - NETPACKET packet; - PACKER p; - char buf[128]; - - /* count the players */ - int c = 0; - int i; - for(i = 0; i < MAX_CLIENTS; i++) - { - if(clients[i].state != SRVCLIENT_STATE_EMPTY) - c++; - } - - packer_reset(&p); - packer_add_raw(&p, SERVERBROWSE_INFO, sizeof(SERVERBROWSE_INFO)); - packer_add_string(&p, mods_net_version(), 32); - packer_add_string(&p, config.sv_name, 64); - packer_add_string(&p, config.sv_map, 32); - - /* gametype */ - sprintf(buf, "%d", browseinfo_gametype); - packer_add_string(&p, buf, 2); - - /* flags */ - i = 0; - if(strlen(config.password)) - i |= 1; - sprintf(buf, "%d", i); - packer_add_string(&p, buf, 2); - - /* progression */ - sprintf(buf, "%d", browseinfo_progression); - packer_add_string(&p, buf, 4); - - sprintf(buf, "%d", c); packer_add_string(&p, buf, 3); /* num players */ - sprintf(buf, "%d", netserver_max_clients(net)); packer_add_string(&p, buf, 3); /* max players */ - - for(i = 0; i < MAX_CLIENTS; i++) - { - if(clients[i].state != SRVCLIENT_STATE_EMPTY) - { - packer_add_string(&p, clients[i].name, 48); /* player name */ - packer_add_string(&p, "0", 6); /* score */ - } - } - - - packet.client_id = -1; - packet.address = *addr; - packet.flags = PACKETFLAG_CONNLESS; - packet.data_size = packer_size(&p); - packet.data = packer_data(&p); - netserver_send(net, &packet); -} - - -static void server_send_fwcheckresponse(NETADDR4 *addr) -{ - NETPACKET packet; - packet.client_id = -1; - packet.address = *addr; - packet.flags = PACKETFLAG_CONNLESS; - packet.data_size = sizeof(SERVERBROWSE_FWRESPONSE); - packet.data = SERVERBROWSE_FWRESPONSE; - netserver_send(net, &packet); -} - -static void server_pump_network() -{ - NETPACKET packet; - - netserver_update(net); - - /* process packets */ - while(netserver_recv(net, &packet)) - { - if(packet.client_id == -1) - { - /* stateless */ - if(packet.data_size == sizeof(SERVERBROWSE_GETINFO) && - memcmp(packet.data, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO)) == 0) - { - server_send_serverinfo(&packet.address); - } - else if(packet.data_size == sizeof(SERVERBROWSE_FWCHECK) && - memcmp(packet.data, SERVERBROWSE_FWCHECK, sizeof(SERVERBROWSE_FWCHECK)) == 0) - { - server_send_fwcheckresponse(&packet.address); - } - else if(packet.data_size == sizeof(SERVERBROWSE_FWOK) && - memcmp(packet.data, SERVERBROWSE_FWOK, sizeof(SERVERBROWSE_FWOK)) == 0) - { - if(config.debug) - dbg_msg("server", "no firewall/nat problems detected"); - } - else if(packet.data_size == sizeof(SERVERBROWSE_FWERROR) && - memcmp(packet.data, SERVERBROWSE_FWERROR, sizeof(SERVERBROWSE_FWERROR)) == 0) - { - dbg_msg("server", "ERROR: the master server reports that clients can not connect to this server."); - dbg_msg("server", "ERROR: configure your firewall/nat to let trough udp on port %d.", config.sv_port); - } - } - else - server_process_client_packet(&packet); - } -} - -static int server_load_map(const char *mapname) -{ - DATAFILE *df; - char buf[512]; - sprintf(buf, "data/maps/%s.map", mapname); - df = datafile_load(buf); - if(!df) - return 0; - - /* reinit snapshot ids */ - snap_timeout_ids(); - - /* get the crc of the map */ - current_map_crc = datafile_crc(buf); - dbg_msg("server", "%s crc is %08x", buf, current_map_crc); - - strcpy(current_map, mapname); - map_set(df); - return 1; -} - - -static int server_run() -{ - NETADDR4 bindaddr; - - net_init(); - - snap_init_id(); - - /* load map */ - if(!server_load_map(config.sv_map)) - { - dbg_msg("server", "failed to load map. mapname='%s'", config.sv_map); - return -1; - } - - /* start server */ - if(strlen(config.sv_bindaddr) && net_host_lookup(config.sv_bindaddr, config.sv_port, &bindaddr) != 0) - { - /* sweet! */ - } - else - { - mem_zero(&bindaddr, sizeof(bindaddr)); - bindaddr.port = config.sv_port; - } - - net = netserver_open(bindaddr, config.sv_max_clients, 0); - if(!net) - { - dbg_msg("server", "couldn't open socket. port might already be in use"); - return -1; - } - - netserver_set_callbacks(net, new_client_callback, del_client_callback, 0); - - dbg_msg("server", "server name is '%s'", config.sv_name); - dbg_msg("server", "masterserver is '%s'", config.masterserver); - if(net_host_lookup(config.masterserver, MASTERSERVER_PORT, &master_server) != 0) - { - /* TODO: fix me */ - /*master_server = netaddr4(0, 0, 0, 0, 0); */ - } - - mods_init(); - dbg_msg("server", "version %s", mods_net_version()); - - /* start game */ - { - int64 time_per_heartbeat = time_freq() * 30; - int64 reporttime = time_get(); - int reportinterval = 3; - - int64 simulationtime = 0; - int64 snaptime = 0; - int64 networktime = 0; - int64 totaltime = 0; - - lastheartbeat = 0; - game_start_time = time_get(); - - if(config.debug) - dbg_msg("server", "baseline memory usage %dk", mem_allocated()/1024); - - while(1) - { - int64 t = time_get(); - /* load new map TODO: don't poll this */ - if(strcmp(config.sv_map, current_map) != 0 || config.sv_map_reload) - { - config.sv_map_reload = 0; - - /* load map */ - if(server_load_map(config.sv_map)) - { - int c; - - /* new map loaded */ - mods_shutdown(); - - for(c = 0; c < MAX_CLIENTS; c++) - { - if(clients[c].state == SRVCLIENT_STATE_EMPTY) - continue; - - server_send_map(c); - clients[c].state = SRVCLIENT_STATE_CONNECTING; - clients[c].last_acked_snapshot = -1; - snapstorage_purge_all(&clients[c].snapshots); - } - - game_start_time = time_get(); - current_tick = 0; - mods_init(); - } - else - { - dbg_msg("server", "failed to load map. mapname='%s'", config.sv_map); - config_set_sv_map(&config, current_map); - } - } - - if(t > server_tick_start_time(current_tick+1)) - { - current_tick++; - - /* apply new input */ - { - int c, i; - for(c = 0; c < MAX_CLIENTS; c++) - { - if(clients[c].state == SRVCLIENT_STATE_EMPTY) - continue; - for(i = 0; i < 200; i++) - { - if(clients[c].inputs[i].game_tick == server_tick()) - { - mods_client_input(c, clients[c].inputs[i].data); - break; - } - } - } - } - - /* progress game */ - { - int64 start = time_get(); - mods_tick(); - simulationtime += time_get()-start; - } - - /* snap game */ - if(config.sv_bandwidth_mode == 0 || - (config.sv_bandwidth_mode == 1 && current_tick%2) || - (config.sv_bandwidth_mode == 2 && (current_tick%3) == 0 )) - /* if(current_tick&1) */ - { - int64 start = time_get(); - server_do_snap(); - snaptime += time_get()-start; - } - } - - if(config.sv_sendheartbeats) - { - if (t > lastheartbeat+time_per_heartbeat) - { - server_send_heartbeat(); - lastheartbeat = t+time_per_heartbeat; - } - } - - { - int64 start = time_get(); - server_pump_network(); - networktime += time_get()-start; - } - - if(reporttime < time_get()) - { - if(config.debug) - { - static NETSTATS prev_stats; - NETSTATS stats; - netserver_stats(net, &stats); - dbg_msg("server", "sim=%.02fms snap=%.02fms net=%.02fms tot=%.02fms load=%.02f%%", - (simulationtime/reportinterval)/(double)time_freq()*1000, - (snaptime/reportinterval)/(double)time_freq()*1000, - (networktime/reportinterval)/(double)time_freq()*1000, - (totaltime/reportinterval)/(double)time_freq()*1000, - (totaltime)/reportinterval/(double)time_freq()*100.0f); - - dbg_msg("server", "send=%8d recv=%8d", - (stats.send_bytes - prev_stats.send_bytes)/reportinterval, - (stats.recv_bytes - prev_stats.recv_bytes)/reportinterval); - - prev_stats = stats; - } - - simulationtime = 0; - snaptime = 0; - networktime = 0; - totaltime = 0; - - reporttime += time_freq()*reportinterval; - } - totaltime += time_get()-t; - thread_sleep(1); - } - } - - mods_shutdown(); - map_unload(); - - return 0; -} - - -int main(int argc, char **argv) -{ - dbg_msg("server", "starting..."); - engine_init("Teewars", argc, argv); - server_run(); - return 0; -} - diff --git a/src/engine/snapshot.c b/src/engine/snapshot.c deleted file mode 100644 index 4db6ac26..00000000 --- a/src/engine/snapshot.c +++ /dev/null @@ -1,448 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include "snapshot.h" -#include "compression.h" - - -int *snapitem_data(SNAPSHOT_ITEM *item) { return (int *)(item+1); } -int snapitem_type(SNAPSHOT_ITEM *item) { return item->type_and_id>>16; } -int snapitem_id(SNAPSHOT_ITEM *item) { return item->type_and_id&0xffff; } -int snapitem_key(SNAPSHOT_ITEM *item) { return item->type_and_id; } - -int *snapshot_offsets(SNAPSHOT *snap) { return (int *)(snap+1); } -char *snapshot_datastart(SNAPSHOT *snap) { return (char*)(snapshot_offsets(snap)+snap->num_items); } - -SNAPSHOT_ITEM *snapshot_get_item(SNAPSHOT *snap, int index) -{ return (SNAPSHOT_ITEM *)(snapshot_datastart(snap) + snapshot_offsets(snap)[index]); } - -int snapshot_get_item_datasize(SNAPSHOT *snap, int index) -{ - if(index == snap->num_items-1) - return (snap->data_size - snapshot_offsets(snap)[index]) - sizeof(SNAPSHOT_ITEM); - return (snapshot_offsets(snap)[index+1] - snapshot_offsets(snap)[index]) - sizeof(SNAPSHOT_ITEM); -} - -int snapshot_get_item_index(SNAPSHOT *snap, int key) -{ - /* TODO: OPT: this should not be a linear search. very bad */ - int i; - for(i = 0; i < snap->num_items; i++) - { - if(snapitem_key(snapshot_get_item(snap, i)) == key) - return i; - } - return -1; -} - - - -typedef struct -{ - int num_deleted_items; - int num_update_items; - int num_temp_items; /* needed? */ - int data[1]; - - /* - char *data_start() { return (char *)&offsets[num_deleted_items+num_update_items+num_temp_items]; } - - int deleted_item(int index) { return offsets[index]; } - item *update_item(int index) { return (item *)(data_start() + offsets[num_deleted_items+index]); } - item *temp_item(int index) { return (item *)(data_start() + offsets[num_deleted_items+num_update_items+index]); } - */ -} SNAPSHOT_DELTA; - - -static const int MAX_ITEMS = 512; -static SNAPSHOT_DELTA empty = {0,0,0,{0}}; - -void *snapshot_empty_delta() -{ - return ∅ -} - -int snapshot_crc(SNAPSHOT *snap) -{ - int crc = 0; - int i, b; - SNAPSHOT_ITEM *item; - int size; - - for(i = 0; i < snap->num_items; i++) - { - item = snapshot_get_item(snap, i); - size = snapshot_get_item_datasize(snap, i); - - for(b = 0; b < size/4; b++) - crc += snapitem_data(item)[b]; - } - return crc; -} - -void snapshot_debug_dump(SNAPSHOT *snap) -{ - int size, i, b; - SNAPSHOT_ITEM *item; - - dbg_msg("snapshot", "data_size=%d num_items=%d", snap->data_size, snap->num_items); - for(i = 0; i < snap->num_items; i++) - { - item = snapshot_get_item(snap, i); - size = snapshot_get_item_datasize(snap, i); - dbg_msg("snapshot", "\ttype=%d id=%d", snapitem_type(item), snapitem_id(item)); - for(b = 0; b < size/4; b++) - dbg_msg("snapshot", "\t\t%3d %12d\t%08x", b, snapitem_data(item)[b], snapitem_data(item)[b]); - } -} - -static int diff_item(int *past, int *current, int *out, int size) -{ - int needed = 0; - while(size) - { - *out = *current-*past; - needed |= *out; - out++; - past++; - current++; - size--; - } - - return needed; -} - -int snapshot_data_rate[0xffff] = {0}; -int snapshot_data_updates[0xffff] = {0}; -static int snapshot_current = 0; - -static void undiff_item(int *past, int *diff, int *out, int size) -{ - while(size) - { - *out = *past+*diff; - - if(*diff == 0) - snapshot_data_rate[snapshot_current] += 1; - else - { - unsigned char buf[16]; - unsigned char *end = vint_pack(buf, *diff); - snapshot_data_rate[snapshot_current] += (int)(end - (unsigned char*)buf) * 8; - } - - out++; - past++; - diff++; - size--; - } -} - - -/* TODO: OPT: this should be made much faster */ -int snapshot_create_delta(SNAPSHOT *from, SNAPSHOT *to, void *dstdata) -{ - SNAPSHOT_DELTA *delta = (SNAPSHOT_DELTA *)dstdata; - int *data = (int *)delta->data; - int i, itemsize, pastindex; - SNAPSHOT_ITEM *fromitem; - SNAPSHOT_ITEM *curitem; - SNAPSHOT_ITEM *pastitem; - int count = 0; - int size_count = 0; - - delta->num_deleted_items = 0; - delta->num_update_items = 0; - delta->num_temp_items = 0; - - /* pack deleted stuff */ - for(i = 0; i < from->num_items; i++) - { - fromitem = snapshot_get_item(from, i); - if(snapshot_get_item_index(to, (snapitem_key(fromitem))) == -1) - { - /* deleted */ - delta->num_deleted_items++; - *data = snapitem_key(fromitem); - data++; - } - } - - /* pack updated stuff */ - for(i = 0; i < to->num_items; i++) - { - /* do delta */ - itemsize = snapshot_get_item_datasize(to, i); - - curitem = snapshot_get_item(to, i); - pastindex = snapshot_get_item_index(from, snapitem_key(curitem)); - if(pastindex != -1) - { - pastitem = snapshot_get_item(from, pastindex); - if(diff_item((int*)snapitem_data(pastitem), (int*)snapitem_data(curitem), data+3, itemsize/4)) - { - *data++ = itemsize; - *data++ = snapitem_type(curitem); - *data++ = snapitem_id(curitem); - /*data++ = curitem->key();*/ - data += itemsize/4; - delta->num_update_items++; - } - } - else - { - *data++ = itemsize; - *data++ = snapitem_type(curitem); - *data++ = snapitem_id(curitem); - /*data++ = curitem->key();*/ - - mem_copy(data, snapitem_data(curitem), itemsize); - size_count += itemsize; - data += itemsize/4; - delta->num_update_items++; - count++; - } - } - - if(0) - { - dbg_msg("snapshot", "%d %d %d", - delta->num_deleted_items, - delta->num_update_items, - delta->num_temp_items); - } - - /* - // TODO: pack temp stuff - - // finish - //mem_copy(delta->offsets, deleted, delta->num_deleted_items*sizeof(int)); - //mem_copy(&(delta->offsets[delta->num_deleted_items]), update, delta->num_update_items*sizeof(int)); - //mem_copy(&(delta->offsets[delta->num_deleted_items+delta->num_update_items]), temp, delta->num_temp_items*sizeof(int)); - //mem_copy(delta->data_start(), data, data_size); - //delta->data_size = data_size; - * */ - - if(!delta->num_deleted_items && !delta->num_update_items && !delta->num_temp_items) - return 0; - - return (int)((char*)data-(char*)dstdata); -} - -int snapshot_unpack_delta(SNAPSHOT *from, SNAPSHOT *to, void *srcdata, int data_size) -{ - SNAPBUILD builder; - SNAPSHOT_DELTA *delta = (SNAPSHOT_DELTA *)srcdata; - int *data = (int *)delta->data; - SNAPSHOT_ITEM *fromitem; - int i, d, keep, itemsize; - int *deleted; - int id, type, key; - int fromindex; - int *newdata; - - snapbuild_init(&builder); - - /* unpack deleted stuff */ - deleted = data; - data += delta->num_deleted_items; - - /* copy all non deleted stuff */ - for(i = 0; i < from->num_items; i++) - { - /* dbg_assert(0, "fail!"); */ - fromitem = snapshot_get_item(from, i); - itemsize = snapshot_get_item_datasize(from, i); - keep = 1; - for(d = 0; d < delta->num_deleted_items; d++) - { - if(deleted[d] == snapitem_key(fromitem)) - { - keep = 0; - break; - } - } - - if(keep) - { - /* keep it */ - mem_copy( - snapbuild_new_item(&builder, snapitem_type(fromitem), snapitem_id(fromitem), itemsize), - snapitem_data(fromitem), itemsize); - } - } - - /* unpack updated stuff */ - for(i = 0; i < delta->num_update_items; i++) - { - itemsize = *data++; - type = *data++; - id = *data++; - snapshot_current = type; - - key = (type<<16)|id; - - /* create the item if needed */ - newdata = snapbuild_get_item_data(&builder, key); - if(!newdata) - newdata = (int *)snapbuild_new_item(&builder, key>>16, key&0xffff, itemsize); - - fromindex = snapshot_get_item_index(from, key); - if(fromindex != -1) - { - /* we got an update so we need to apply the diff */ - undiff_item((int *)snapitem_data(snapshot_get_item(from, fromindex)), data, newdata, itemsize/4); - snapshot_data_updates[snapshot_current]++; - } - else /* no previous, just copy the data */ - { - mem_copy(newdata, data, itemsize); - snapshot_data_rate[snapshot_current] += itemsize*8; - snapshot_data_updates[snapshot_current]++; - } - - data += itemsize/4; - } - - /* finish up */ - return snapbuild_finish(&builder, to); -} - -/* SNAPSTORAGE */ - -void snapstorage_init(SNAPSTORAGE *ss) -{ - ss->first = 0; -} - -void snapstorage_purge_all(SNAPSTORAGE *ss) -{ - SNAPSTORAGE_HOLDER *h = ss->first; - SNAPSTORAGE_HOLDER *next; - - while(h) - { - next = h->next; - mem_free(h); - h = next; - } - - /* no more snapshots in storage */ - ss->first = 0; - ss->last = 0; -} - -void snapstorage_purge_until(SNAPSTORAGE *ss, int tick) -{ - SNAPSTORAGE_HOLDER *next; - SNAPSTORAGE_HOLDER *h = ss->first; - - while(h) - { - next = h->next; - if(h->tick >= tick) - return; /* no more to remove */ - mem_free(h); - - ss->first = next; - next->prev = 0x0; - - h = next; - } - - /* no more snapshots in storage */ - ss->first = 0; - ss->last = 0; -} - -void snapstorage_add(SNAPSTORAGE *ss, int tick, int64 tagtime, int data_size, void *data) -{ - /* allocate memory for holder + snapshot_data */ - SNAPSTORAGE_HOLDER *h = (SNAPSTORAGE_HOLDER *)mem_alloc(sizeof(SNAPSTORAGE_HOLDER)+data_size, 1); - - /* set data */ - h->tick = tick; - h->tagtime = tagtime; - h->snap_size = data_size; - h->snap = (SNAPSHOT*)(h+1); - mem_copy(h->snap, data, data_size); - - /* link */ - h->next = 0; - h->prev = ss->last; - if(ss->last) - ss->last->next = h; - else - ss->first = h; - ss->last = h; -} - -int snapstorage_get(SNAPSTORAGE *ss, int tick, int64 *tagtime, SNAPSHOT **data) -{ - SNAPSTORAGE_HOLDER *h = ss->first; - - while(h) - { - if(h->tick == tick) - { - if(tagtime) - *tagtime = h->tagtime; - if(data) - *data = h->snap; - return h->snap_size; - } - - h = h->next; - } - - return -1; -} - -/* SNAPBUILD */ - -void snapbuild_init(SNAPBUILD *sb) -{ - sb->data_size = 0; - sb->num_items = 0; -} - -SNAPSHOT_ITEM *snapbuild_get_item(SNAPBUILD *sb, int index) -{ - return (SNAPSHOT_ITEM *)&(sb->data[sb->offsets[index]]); -} - -int *snapbuild_get_item_data(SNAPBUILD *sb, int key) -{ - int i; - for(i = 0; i < sb->num_items; i++) - { - if(snapitem_key(snapbuild_get_item(sb, i)) == key) - return (int *)snapitem_data(snapbuild_get_item(sb, i)); - } - return 0; -} - -int snapbuild_finish(SNAPBUILD *sb, void *snapdata) -{ - /* flattern and make the snapshot */ - SNAPSHOT *snap = (SNAPSHOT *)snapdata; - int offset_size = sizeof(int)*sb->num_items; - snap->data_size = sb->data_size; - snap->num_items = sb->num_items; - mem_copy(snapshot_offsets(snap), sb->offsets, offset_size); - mem_copy(snapshot_datastart(snap), sb->data, sb->data_size); - return sizeof(SNAPSHOT) + offset_size + sb->data_size; -} - -void *snapbuild_new_item(SNAPBUILD *sb, int type, int id, int size) -{ - SNAPSHOT_ITEM *obj = (SNAPSHOT_ITEM *)(sb->data+sb->data_size); - mem_zero(obj, sizeof(SNAPSHOT_ITEM) + size); - obj->type_and_id = (type<<16)|id; - sb->offsets[sb->num_items] = sb->data_size; - sb->data_size += sizeof(SNAPSHOT_ITEM) + size; - sb->num_items++; - - dbg_assert(sb->data_size < MAX_SNAPSHOT_SIZE, "too much data"); - dbg_assert(sb->num_items < SNAPBUILD_MAX_ITEMS, "too many items"); - - return snapitem_data(obj); -} diff --git a/src/engine/snapshot.h b/src/engine/snapshot.h deleted file mode 100644 index 60131d4f..00000000 --- a/src/engine/snapshot.h +++ /dev/null @@ -1,91 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#ifndef ENGINE_SNAPSHOT_H -#define ENGINE_SNAPSHOT_H - -#include "system.h" - -/* SNAPSHOT */ - -enum -{ - MAX_SNAPSHOT_SIZE=64*1024 -}; - -typedef struct -{ - int type_and_id; -} SNAPSHOT_ITEM; - -typedef struct -{ - int data_size; - int num_items; -} SNAPSHOT; - -int *snapitem_data(SNAPSHOT_ITEM *item); -int snapitem_type(SNAPSHOT_ITEM *item); -int snapitem_id(SNAPSHOT_ITEM *item); -int snapitem_key(SNAPSHOT_ITEM *item); - -int *snapshot_offsets(SNAPSHOT *snap); -char *snapshot_datastart(SNAPSHOT *snap); - -SNAPSHOT_ITEM *snapshot_get_item(SNAPSHOT *snap, int index); -int snapshot_get_item_datasize(SNAPSHOT *snap, int index); -int snapshot_get_item_index(SNAPSHOT *snap, int key); - -void *snapshot_empty_delta(); -int snapshot_crc(SNAPSHOT *snap); -void snapshot_debug_dump(SNAPSHOT *snap); -int snapshot_create_delta(SNAPSHOT *from, SNAPSHOT *to, void *data); -int snapshot_unpack_delta(SNAPSHOT *from, SNAPSHOT *to, void *data, int data_size); - -/* SNAPSTORAGE */ - -typedef struct SNAPSTORAGE_HOLDER_t -{ - struct SNAPSTORAGE_HOLDER_t *prev; - struct SNAPSTORAGE_HOLDER_t *next; - - int64 tagtime; - int tick; - - int snap_size; - SNAPSHOT *snap; -} SNAPSTORAGE_HOLDER; - -typedef struct SNAPSTORAGE_t -{ - SNAPSTORAGE_HOLDER *first; - SNAPSTORAGE_HOLDER *last; -} SNAPSTORAGE; - -void snapstorage_init(SNAPSTORAGE *ss); -void snapstorage_purge_all(SNAPSTORAGE *ss); -void snapstorage_purge_until(SNAPSTORAGE *ss, int tick); -void snapstorage_add(SNAPSTORAGE *ss, int tick, int64 tagtime, int data_size, void *data); -int snapstorage_get(SNAPSTORAGE *ss, int tick, int64 *tagtime, SNAPSHOT **data); - -/* SNAPBUILD */ - -enum -{ - SNAPBUILD_MAX_ITEMS = 512 -}; - -typedef struct SNAPBUILD -{ - char data[MAX_SNAPSHOT_SIZE]; - int data_size; - - int offsets[SNAPBUILD_MAX_ITEMS]; - int num_items; -} SNAPBUILD; - -void snapbuild_init(SNAPBUILD *sb); -SNAPSHOT_ITEM *snapbuild_get_item(SNAPBUILD *sb, int index); -int *snapbuild_get_item_data(SNAPBUILD *sb, int key); -int snapbuild_finish(SNAPBUILD *sb, void *snapdata); -void *snapbuild_new_item(SNAPBUILD *sb, int type, int id, int size); - -#endif /* ENGINE_SNAPSHOT_H */ diff --git a/src/engine/system.c b/src/engine/system.c deleted file mode 100644 index 8e5bd8e5..00000000 --- a/src/engine/system.c +++ /dev/null @@ -1,723 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include -#include -#include -#include -#include -#include - -#include "detect.h" -#include "system.h" - -#if defined(CONF_FAMILY_UNIX) - #include - #include - - /* unix net includes */ - #include - #include - #include - #include - #include - #include - #include - #include - #include - - #include - #include -#elif defined(CONF_FAMILY_WINDOWS) - #define WIN32_LEAN_AND_MEAN - #define _WIN32_WINNT 0x0400 - #include - #include - #include - #include /* for SHGetFolderPathAndSubDir */ - #include - #include - #include - - #define EWOULDBLOCK WSAEWOULDBLOCK -#else - #error NOT IMPLEMENTED -#endif - -#if defined(__cplusplus) -extern "C" { -#endif - -IOHANDLE logfile = 0; - -void dbg_assert_imp(const char *filename, int line, int test, const char *msg) -{ - if(!test) - { - dbg_msg("assert", "%s(%d): %s", filename, line, msg); - dbg_break(); - } -} - -void dbg_break() -{ - *((unsigned*)0) = 0x0; -} - -int dbg_log_to_file(const char *filename) -{ - logfile = io_open(filename, IOFLAG_WRITE); - if(logfile) - return 1; - return 0; -} - -void dbg_msg(const char *sys, const char *fmt, ...) -{ - va_list args; -#if defined(CONF_FAMILY_WINDOWS) - char str[1024]; - va_start(args, fmt); - vsprintf(str, fmt, args); - va_end(args); - OutputDebugString(str); - OutputDebugString("\n"); -#endif - - va_start(args, fmt); - printf("[%08x][%s]: ", (int)time(0), sys); - vprintf(fmt, args); - va_end(args); - printf("\n"); - fflush(stdout); -} - -int memory_alloced = 0; - -struct memheader -{ - const char *filename; - int line; - int size; - struct memheader *prev; - struct memheader *next; -}; - -struct memtail -{ - int guard; -}; - -struct memheader *first = 0; - -int mem_allocated() -{ - return memory_alloced; -} - -void *mem_alloc_debug(const char *filename, int line, unsigned size, unsigned alignment) -{ - /* TODO: fix alignment */ - /* TODO: add debugging */ - struct memheader *header = (struct memheader *)malloc(size+sizeof(struct memheader)+sizeof(struct memtail)); - struct memtail *tail = (struct memtail *)(((char*)(header+1))+size); - header->size = size; - header->filename = filename; - header->line = line; - memory_alloced += header->size; - - tail->guard = 0xbaadc0de; - - header->prev = (struct memheader *)0; - header->next = first; - if(first) - first->prev = header; - first = header; - - /*dbg_msg("mem", "++ %p", header+1); */ - return header+1; -} - -void mem_free(void *p) -{ - if(p) - { - struct memheader *header = (struct memheader *)p - 1; - struct memtail *tail = (struct memtail *)(((char*)(header+1))+header->size); - - if(tail->guard != 0xbaadc0de) - dbg_msg("mem", "!! %p", p); - /* dbg_msg("mem", "-- %p", p); */ - memory_alloced -= header->size; - - if(header->prev) - header->prev->next = header->next; - else - first = header->next; - if(header->next) - header->next->prev = header->prev; - - free(header); - } -} - -void mem_debug_dump() -{ - char buf[1024]; - struct memheader *header = first; - IOHANDLE f = io_open("memory.txt", IOFLAG_WRITE); - - while(header) - { - sprintf(buf, "%s(%d): %d\n", header->filename, header->line, header->size); - io_write(f, buf, strlen(buf)); - header = header->next; - } -} - - -void mem_copy(void *dest, const void *source, unsigned size) -{ - memcpy(dest, source, size); -} - -void mem_move(void *dest, const void *source, unsigned size) -{ - memmove(dest, source, size); -} - -void mem_zero(void *block,unsigned size) -{ - memset(block, 0, size); -} - -IOHANDLE io_open(const char *filename, int flags) -{ - if(flags == IOFLAG_READ) - return (IOHANDLE)fopen(filename, "rb"); - if(flags == IOFLAG_WRITE) - return (IOHANDLE)fopen(filename, "wb"); - return 0x0; -} - -unsigned io_read(IOHANDLE io, void *buffer, unsigned size) -{ - return fread(buffer, 1, size, (FILE*)io); -} - -unsigned io_skip(IOHANDLE io, unsigned size) -{ - fseek((FILE*)io, size, SEEK_CUR); - return size; -} - -int io_seek(IOHANDLE io, int offset, int origin) -{ - int real_origin; - - switch(origin) - { - case IOSEEK_START: - real_origin = SEEK_SET; - break; - case IOSEEK_CUR: - real_origin = SEEK_CUR; - break; - case IOSEEK_END: - real_origin = SEEK_END; - } - - return fseek((FILE*)io, offset, origin); -} - -long int io_tell(IOHANDLE io) -{ - return ftell((FILE*)io); -} - -long int io_length(IOHANDLE io) -{ - long int length; - io_seek(io, 0, IOSEEK_END); - length = io_tell(io); - io_seek(io, 0, IOSEEK_START); - return length; -} - -unsigned io_write(IOHANDLE io, const void *buffer, unsigned size) -{ - return fwrite(buffer, 1, size, (FILE*)io); -} - -int io_close(IOHANDLE io) -{ - fclose((FILE*)io); - return 1; -} - -void thread_sleep(int milliseconds) -{ -#if defined(CONF_FAMILY_UNIX) - usleep(milliseconds*1000); -#elif defined(CONF_FAMILY_WINDOWS) - Sleep(milliseconds); -#else - #error not implemented -#endif -} - -#if defined(CONF_FAMILY_UNIX) -typedef pthread_mutex_t LOCKINTERNAL; -#elif defined(CONF_FAMILY_WINDOWS) -typedef CRITICAL_SECTION LOCKINTERNAL; -#else - #error not implemented on this platform -#endif - -LOCK lock_create() -{ - LOCKINTERNAL *lock = (LOCKINTERNAL*)mem_alloc(sizeof(LOCKINTERNAL), 4); - -#if defined(CONF_FAMILY_UNIX) - pthread_mutex_init(lock, 0x0); -#elif defined(CONF_FAMILY_WINDOWS) - InitializeCriticalSection((LPCRITICAL_SECTION)lock); -#else - #error not implemented on this platform -#endif - return (LOCK)lock; -} - -void lock_destroy(LOCK lock) -{ -#if defined(CONF_FAMILY_UNIX) - pthread_mutex_destroy((LOCKINTERNAL *)lock); -#elif defined(CONF_FAMILY_WINDOWS) - DeleteCriticalSection((LPCRITICAL_SECTION)lock); -#else - #error not implemented on this platform -#endif - mem_free(lock); -} - -int lock_try(LOCK lock) -{ -#if defined(CONF_FAMILY_UNIX) - return pthread_mutex_trylock((LOCKINTERNAL *)lock); -#elif defined(CONF_FAMILY_WINDOWS) - return TryEnterCriticalSection((LPCRITICAL_SECTION)lock); -#else - #error not implemented on this platform -#endif -} - -void lock_wait(LOCK lock) -{ -#if defined(CONF_FAMILY_UNIX) - pthread_mutex_lock((LOCKINTERNAL *)lock); -#elif defined(CONF_FAMILY_WINDOWS) - EnterCriticalSection((LPCRITICAL_SECTION)lock); -#else - #error not implemented on this platform -#endif -} - -void lock_release(LOCK lock) -{ -#if defined(CONF_FAMILY_UNIX) - pthread_mutex_unlock((LOCKINTERNAL *)lock); -#elif defined(CONF_FAMILY_WINDOWS) - LeaveCriticalSection((LPCRITICAL_SECTION)lock); -#else - #error not implemented on this platform -#endif -} - -/* ----- time ----- */ -int64 time_get() -{ -#if defined(CONF_FAMILY_UNIX) - struct timeval val; - gettimeofday(&val, NULL); - return (int64)val.tv_sec*(int64)1000000+(int64)val.tv_usec; -#elif defined(CONF_FAMILY_WINDOWS) - static int64 last = 0; - int64 t; - QueryPerformanceCounter((PLARGE_INTEGER)&t); - if(tsin_family = AF_INET; - p->sin_port = htons(src->port); - p->sin_addr.s_addr = htonl(src->ip[0]<<24|src->ip[1]<<16|src->ip[2]<<8|src->ip[3]); -} - -static void sockaddr_to_netaddr4(const struct sockaddr *src, NETADDR4 *dst) -{ - unsigned int ip = htonl(((struct sockaddr_in*)src)->sin_addr.s_addr); - dst->port = htons(((struct sockaddr_in*)src)->sin_port); - dst->ip[0] = (unsigned char)((ip>>24)&0xFF); - dst->ip[1] = (unsigned char)((ip>>16)&0xFF); - dst->ip[2] = (unsigned char)((ip>>8)&0xFF); - dst->ip[3] = (unsigned char)(ip&0xFF); -} - -int net_addr4_cmp(const NETADDR4 *a, const NETADDR4 *b) -{ - if( a->ip[0] != b->ip[0] || - a->ip[1] != b->ip[1] || - a->ip[2] != b->ip[2] || - a->ip[3] != b->ip[3] || - a->port != b->port - ) - return 1; - return 0; -} - - -int net_host_lookup(const char *hostname, unsigned short port, NETADDR4 *addr) -{ - struct hostent* ip = gethostbyname(hostname); - - if(ip && ip->h_length > 0) - { - addr->ip[0] = ip->h_addr_list[0][0]; - addr->ip[1] = ip->h_addr_list[0][1]; - addr->ip[2] = ip->h_addr_list[0][2]; - addr->ip[3] = ip->h_addr_list[0][3]; - addr->port = port; - return 0; - } - - return -1; -} - -NETSOCKET net_udp4_create(NETADDR4 bindaddr) -{ - struct sockaddr addr; - unsigned int mode = 1; - int broadcast = 1; - - /* create socket */ - int sock = socket(AF_INET, SOCK_DGRAM, 0); - if(sock < 0) - return NETSOCKET_INVALID; - - /* bind, we should check for error */ - netaddr4_to_sockaddr(&bindaddr, &addr); - if(bind(sock, &addr, sizeof(addr)) != 0) - { - net_udp4_close(sock); - return NETSOCKET_INVALID; - } - - /* set non-blocking */ -#if defined(CONF_FAMILY_WINDOWS) - ioctlsocket(sock, FIONBIO, (unsigned long *)&mode); -#else - ioctl(sock, FIONBIO, (unsigned long *)&mode); -#endif - - /* set boardcast */ - setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (const char*)&broadcast, sizeof(broadcast)); - - /* return */ - return sock; -} - -int net_udp4_send(NETSOCKET sock, const NETADDR4 *addr, const void *data, int size) -{ - struct sockaddr sa; - int d; - mem_zero(&sa, sizeof(sa)); - netaddr4_to_sockaddr(addr, &sa); - d = sendto((int)sock, (const char*)data, size, 0, &sa, sizeof(sa)); - if(d < 0) - dbg_msg("net", "sendto error %d %x", d, d); - return d; -} - -int net_udp4_recv(NETSOCKET sock, NETADDR4 *addr, void *data, int maxsize) -{ - struct sockaddr from; - int bytes; - socklen_t fromlen = sizeof(struct sockaddr); - bytes = recvfrom(sock, (char*)data, maxsize, 0, &from, &fromlen); - if(bytes > 0) - { - sockaddr_to_netaddr4(&from, addr); - return bytes; - } - else if(bytes == 0) - return 0; - return -1; /* error */ -} - -int net_udp4_close(NETSOCKET sock) -{ -#if defined(CONF_FAMILY_WINDOWS) - closesocket(sock); -#else - close((int)sock); -#endif - return 0; -} - -NETSOCKET net_tcp4_create(const NETADDR4 *a) -{ - struct sockaddr addr; - - /* create socket */ - int sock = socket(AF_INET, SOCK_STREAM, 0); - if(sock < 0) - return NETSOCKET_INVALID; - - /* bind, we should check for error */ - netaddr4_to_sockaddr(a, &addr); - bind(sock, &addr, sizeof(addr)); - - /* return */ - return sock; -} - -int net_tcp4_set_non_blocking(NETSOCKET sock) -{ - unsigned int mode = 1; -#if defined(CONF_FAMILY_WINDOWS) - return ioctlsocket(sock, FIONBIO, (unsigned long *)&mode); -#else - return ioctl(sock, FIONBIO, (unsigned long *)&mode); -#endif -} - -int net_tcp4_set_blocking(NETSOCKET sock) -{ - unsigned int mode = 0; -#if defined(CONF_FAMILY_WINDOWS) - return ioctlsocket(sock, FIONBIO, (unsigned long *)&mode); -#else - return ioctl(sock, FIONBIO, (unsigned long *)&mode); -#endif -} - -int net_tcp4_listen(NETSOCKET sock, int backlog) -{ - return listen(sock, backlog); -} - -int net_tcp4_accept(NETSOCKET sock, NETSOCKET *new_sock, NETADDR4 *a) -{ - int s; - socklen_t sockaddr_len; - struct sockaddr addr; - - sockaddr_len = sizeof(addr); - - s = accept(sock, &addr, &sockaddr_len); - - if (s != -1) - { - sockaddr_to_netaddr4(&addr, a); - *new_sock = s; - } - return s; -} - -int net_tcp4_connect(NETSOCKET sock, const NETADDR4 *a) -{ - struct sockaddr addr; - - netaddr4_to_sockaddr(a, &addr); - return connect(sock, &addr, sizeof(addr)); -} - -int net_tcp4_connect_non_blocking(NETSOCKET sock, const NETADDR4 *a) -{ - struct sockaddr addr; - int res; - - netaddr4_to_sockaddr(a, &addr); - net_tcp4_set_non_blocking(sock); - res = connect(sock, &addr, sizeof(addr)); - net_tcp4_set_blocking(sock); - - return res; -} - -int net_tcp4_send(NETSOCKET sock, const void *data, int size) -{ - int d; - d = send((int)sock, (const char*)data, size, 0); - return d; -} - -int net_tcp4_recv(NETSOCKET sock, void *data, int maxsize) -{ - int bytes; - bytes = recv((int)sock, (char*)data, maxsize, 0); - return bytes; -} - -int net_tcp4_close(NETSOCKET sock) -{ -#if defined(CONF_FAMILY_WINDOWS) - closesocket(sock); -#else - close((int)sock); -#endif - return 0; -} - -int net_errno() -{ - return errno; -} - -int net_would_block() -{ - return net_errno() == EWOULDBLOCK; -} - -int net_init() -{ -#if defined(CONF_FAMILY_WINDOWS) - WSADATA wsaData; - int err = WSAStartup(MAKEWORD(1, 1), &wsaData); - dbg_assert(err == 0, "network initialization failed."); - return err==0?0:1; -#endif - - return 0; -} - -int fs_listdir(const char *dir, fs_listdir_callback cb, void *user) -{ -#if defined(CONF_FAMILY_WINDOWS) - WIN32_FIND_DATA finddata; - HANDLE handle; - char buffer[1024*2]; - strcpy(buffer, dir); - strcat(buffer, "/*"); - - handle = FindFirstFileA(buffer, &finddata); - - if (handle == INVALID_HANDLE_VALUE) - return 0; - - /* add all the entries */ - do - { - if(finddata.cFileName[0] != '.') - cb(finddata.cFileName, 0, user); - } while (FindNextFileA(handle, &finddata)); - - FindClose(handle); - return 0; -#else - struct dirent *entry; - DIR *d = opendir(dir); - - if(!d) - return 0; - - while((entry = readdir(d)) != NULL) - cb(entry->d_name, 0, user); - - /* close the directory and return */ - closedir(d); - return 0; -#endif -} - -int fs_storage_path(const char *appname, char *path, int max) -{ -#if defined(CONF_FAMILY_WINDOWS) - HRESULT r; - char home[MAX_PATH]; - r = SHGetFolderPath (NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, home); - if(r != 0) - return 1; - _snprintf(path, max, "%s/%s", home, appname); - return 0; -#else - char *home = getenv("HOME"); - int i; - if(!home) - return 0; - - snprintf(path, max, "%s/.%s", home, appname); - for(i = strlen(home)+2; path[i]; i++) - path[i] = tolower(path[i]); - - return 1; -#endif -} - -int fs_makedir(const char *path) -{ -#if defined(CONF_FAMILY_WINDOWS) - if(_mkdir(path) == 0) - return 0; - if(errno == EEXIST) - return 0; - return 1; -#else - if(mkdir(path, 0755) == 0) - return 0; - if(errno == EEXIST) - return 0; - return 1; -#endif -} - -void swap_endian(void *data, unsigned elem_size, unsigned num) -{ - char *src = (char*) data; - char *dst = src + (elem_size - 1); - - while(num) - { - unsigned n = elem_size>>1; - char tmp; - while(n) - { - tmp = *src; - *src = *dst; - *dst = tmp; - - src++; - dst--; - n--; - } - - src = src + (elem_size>>1); - dst = src + (elem_size - 1); - num--; - } -} - -#if defined(__cplusplus) -} -#endif diff --git a/src/engine/system.h b/src/engine/system.h deleted file mode 100644 index e54af0f1..00000000 --- a/src/engine/system.h +++ /dev/null @@ -1,531 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#ifndef BASE_SYSTEM_H -#define BASE_SYSTEM_H - -#include "detect.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* Group: Debug */ - -/********** - Function: dbg_assert - - Breaks into the debugger based on a test. - - Parameters: - test - Result of the test. - msg - Message that should be printed if the test fails. - - Remarks: - Does nothing in release version of the library. - - See Also: - -**********/ -void dbg_assert(int test, const char *msg); -#define dbg_assert(test,msg) dbg_assert_imp(__FILE__, __LINE__, test, msg) -void dbg_assert_imp(const char *filename, int line, int test, const char *msg); - -/********** - Function: dbg_break - - Breaks into the debugger. - - Remarks: - Does nothing in release version of the library. - - See Also: - -**********/ -void dbg_break(); - -/********** - Function: dbg_msg - - Prints a debug message. - - Parameters: - sys - A string that describes what system the message belongs to - fmt - A printf styled format string. - - Remarks: - Does nothing in relase version of the library. - - See Also: - -**********/ -void dbg_msg(const char *sys, const char *fmt, ...); - -/* Group: Memory */ - -/********** - Function: mem_alloc - - Allocates memory. - - Parameters: - size - Size of the needed block. - alignment - Alignment for the block. - - Returns: - Returns a pointer to the newly allocated block. Returns a - null pointer if the memory couldn't be allocated. - - Remarks: - - Passing 0 to size will allocated the smallest amount possible - and return a unique pointer. - - See Also: - -**********/ -void *mem_alloc_debug(const char *filename, int line, unsigned size, unsigned alignment); -#define mem_alloc(s,a) mem_alloc_debug(__FILE__, __LINE__, (s), (a)) - -/********** - Function: mem_free - - Frees a block allocated through . - - Remarks: - - In the debug version of the library the function will assert if - a non-valid block is passed, like a null pointer or a block that - isn't allocated. - - See Also: - -**********/ -void mem_free(void *block); - -/********** - Function: mem_copy - Copies a a memory block. - - Parameters: - dest - Destination. - source - Source to copy. - size - Size of the block to copy. - - Remarks: - - This functions DOES NOT handles cases where source and - destination is overlapping. - - See Also: - -**********/ -void mem_copy(void *dest, const void *source, unsigned size); - -/********** - Function: mem_move - Copies a a memory block. - - Parameters: - dest - Destination. - source - Source to copy. - size - Size of the block to copy. - - Remarks: - - This functions handles cases where source and destination is overlapping. - - See Also: - -**********/ -void mem_move(void *dest, const void *source, unsigned size); - -/********** - Function: mem_zero - Sets a complete memory block to 0. - - Parameters: - block - Pointer to the block to zero out. - size - Size of the block. -**********/ -void mem_zero(void *block, unsigned size); - -/* ------- io ------- */ -enum { - IOFLAG_READ = 1, - IOFLAG_WRITE = 2, - IOFLAG_RANDOM = 4, - - IOSEEK_START = 0, - IOSEEK_CUR = 1, - IOSEEK_END = 2 -}; - -typedef struct IOINTERNAL *IOHANDLE; - -/**** Group: File IO ****/ - -/**** - Function: io_open - - Opens a file. - - Parameters: - filename - File to open. - flags - A set of flags. IOFLAG_READ, IOFLAG_WRITE, IOFLAG_RANDOM. - - Returns: - Returns a handle to the file on success and 0 on failure. - -****/ -IOHANDLE io_open(const char *filename, int flags); - -/**** - Function: io_read - - Reads data into a buffer from a file. - - Parameters: - io - Handle to the file to read data from. - buffer - Pointer to the buffer that will recive the data. - size - Number of bytes to read from the file. - - Returns: - Number of bytes read. - -****/ -unsigned io_read(IOHANDLE io, void *buffer, unsigned size); - -/***** - Function: io_skip - - Skips data in a file. - - Parameters: - io - Handle to the file. - size - Number of bytes to skip. - - Returns: - Number of bytes skipped. -****/ -unsigned io_skip(IOHANDLE io, unsigned size); - -/***** - Function: io_write - - Writes data from a buffer to file. - - Parameters: - io - Handle to the file. - buffer - Pointer to the data that should be written. - size - Number of bytes to write. - - Returns: - Number of bytes written. -*****/ -unsigned io_write(IOHANDLE io, const void *buffer, unsigned size); - -/***** - Function: io_seek - - Seeks to a specified offset in the file. - - Parameters: - io - Handle to the file. - offset - Offset from pos to stop. - origin - Position to start searching from. - - Returns: - Returns 0 on success. -*****/ -int io_seek(IOHANDLE io, int offset, int origin); - -/***** - Function: io_tell - - Gets the current position in the file. - - Parameters: - io - Handle to the file. - - Returns: - Returns the current position. -1L if an error occured. -*****/ -long int io_tell(IOHANDLE io); - -/***** - Function: io_length - - Gets the total length of the file. Resetting cursor to the beginning - - Parameters: - io - Handle to the file. - - Returns: - Returns the total size. -1L if an error occured. -*****/ -long int io_length(IOHANDLE io); - -/***** - Function: io_close - - Closes a file. - - Parameters: - io - Handle to the file. - - Returns: - Returns 0 on success. -*****/ -int io_close(IOHANDLE io); - -/**** Group: Threads ****/ - -int thread_create(); /* NOT IMPLEMENTED */ -int thread_destory(); /* NOT IMPLEMENTED */ - -int thread_run(); /* NOT IMPLEMENTED */ -int thread_pause(); /* NOT IMPLEMENTED */ -int thread_wait(); /* NOT IMPLEMENTED */ - -/***** - Function: thread_sleep - - Suspends the current thread for a given period. - - Parameters: - milliseconds - Number of milliseconds to sleep. -*****/ -void thread_sleep(int milliseconds); - -/**** Group: Locks ****/ -typedef void* LOCK; - -LOCK lock_create(); -void lock_destroy(LOCK lock); - -int lock_try(LOCK lock); -void lock_wait(LOCK lock); -void lock_release(LOCK lock); - -/**** Group: Timer ****/ -#ifdef __GNUC__ -/* if compiled with -pedantic-errors it will complain about long - not being a C90 thing. -*/ -__extension__ typedef long long int64; -#else -typedef long long int64; -#endif -/***** - Function: time_get - - Fetches a sample from a high resolution timer. - - Returns: - Current value of the timer. - - Remarks: - To know how fast the timer is ticking, see . -*****/ -int64 time_get(); - -/***** - Function: time_freq - - Returns the frequency of the high resolution timer. - - Returns: - Returns the frequency of the high resolution timer. -*****/ -int64 time_freq(); - -/**** Group: Network General ipv4 ****/ -typedef int NETSOCKET; -enum -{ - NETSOCKET_INVALID = -1 -}; - -typedef struct -{ - unsigned char ip[4]; - unsigned short port; -} NETADDR4; - -/***** - Function: net_host_lookup - - Does a hostname lookup by name and fills out the passed NETADDE4 struct with the recieved details. - - Returns: - 0 on success. -*****/ -int net_host_lookup(const char *hostname, unsigned short port, NETADDR4 *addr); - -/**** Group: Network UDP4 ****/ - -/***** - Function: net_udp4_create - - Creates a UDP4 socket and binds it to a port. - - Parameters: - port - Port to bind the socket to. - - Returns: - On success it returns an handle to the socket. On failure it returns NETSOCKET_INVALID. -*****/ -NETSOCKET net_udp4_create(NETADDR4 bindaddr); - -/***** - Function: net_udp4_send - - Sends a packet over an UDP4 socket. - - Parameters: - sock - Socket to use. - addr - Where to send the packet. - data - Pointer to the packet data to send. - size - Size of the packet. - - Returns: - On success it returns the number of bytes sent. Returns -1 on error. -*****/ -int net_udp4_send(NETSOCKET sock, const NETADDR4 *addr, const void *data, int size); - -/***** - Function: net_udp4_recv - - Recives a packet over an UDP4 socket. - - Parameters: - sock - Socket to use. - addr - Pointer to an NETADDR4 that will recive the address. - data - Pointer to a buffer that will recive the data. - maxsize - Maximum size to recive. - - Returns: - On success it returns the number of bytes recived. Returns -1 on error. -*****/ -int net_udp4_recv(NETSOCKET sock, NETADDR4 *addr, void *data, int maxsize); - -/***** - Function: net_udp4_close - - Closes an UDP4 socket. - - Parameters: - sock - Socket to close. - - Returns: - Returns 0 on success. -1 on error. -*****/ -int net_udp4_close(NETSOCKET sock); - - -/**** Group: Network TCP4 ****/ - -/***** - Function: net_tcp4_create - - DOCTODO: serp -*****/ -NETSOCKET net_tcp4_create(const NETADDR4 *a); - -/***** - Function: net_tcp4_set_non_blocking - - DOCTODO: serp -*****/ -int net_tcp4_set_non_blocking(NETSOCKET sock); - -/***** - Function: net_tcp4_set_non_blocking - - DOCTODO: serp -*****/ -int net_tcp4_set_blocking(NETSOCKET sock); - -/***** - Function: net_tcp4_listen - - DOCTODO: serp -*****/ -int net_tcp4_listen(NETSOCKET sock, int backlog); - -/***** - Function: net_tcp4_accept - - DOCTODO: serp -*****/ -int net_tcp4_accept(NETSOCKET sock, NETSOCKET *new_sock, NETADDR4 *a); - -/***** - Function: net_tcp4_connect - - DOCTODO: serp -*****/ -int net_tcp4_connect(NETSOCKET sock, const NETADDR4 *a); - -/***** - Function: net_tcp4_connect_non_blocking - - DOCTODO: serp -*****/ -int net_tcp4_connect_non_blocking(NETSOCKET sock, const NETADDR4 *a); - -/***** - Function: net_tcp4_send - - DOCTODO: serp -*****/ -int net_tcp4_send(NETSOCKET sock, const void *data, int size); - -/***** - Function: net_tcp4_recv - - DOCTODO: serp -*****/ -int net_tcp4_recv(NETSOCKET sock, void *data, int maxsize); - -/***** - Function: net_tcp4_close - - DOCTODO: serp -*****/ -int net_tcp4_close(NETSOCKET sock); - -/***** - Function: net_errno - - DOCTODO: serp -*****/ -int net_errno(); - -/***** - Function: net_would_block - - DOCTODO: serp -*****/ -int net_would_block(); - -/***** - Function: net_init - - DOCTODO: serp -*****/ -int net_init(); - - - -/* NOT DOCUMENTED */ -typedef void (*fs_listdir_callback)(const char *name, int is_dir, void *user); -int fs_listdir(const char *dir, fs_listdir_callback cb, void *user); -int fs_storage_path(const char *appname, char *path, int max); -int fs_makedir(const char *path); -int net_addr4_cmp(const NETADDR4 *a, const NETADDR4 *b); - -void mem_debug_dump(); -int mem_allocated(); - -void swap_endian(void *data, unsigned elem_size, unsigned num); -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/game/client/cl_render.h b/src/game/client/cl_render.h deleted file mode 100644 index fc85d49a..00000000 --- a/src/game/client/cl_render.h +++ /dev/null @@ -1,24 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -struct animstate -{ - keyframe body; - keyframe back_foot; - keyframe front_foot; - keyframe attach; -}; - -void anim_seq_eval(sequence *seq, float time, keyframe *frame); -void anim_eval(animation *anim, float time, animstate *state); -void anim_add_keyframe(keyframe *seq, keyframe *added, float amount); -void anim_add(animstate *state, animstate *added, float amount); -void anim_eval_add(animstate *state, animation *anim, float time, float amount); - -struct tee_render_info -{ - int texture; - vec4 color_body; - vec4 color_feet; - float size; -}; - -void render_tee(animstate *anim, tee_render_info *info, int emote, vec2 dir, vec2 pos); diff --git a/src/game/client/cl_skin.cpp b/src/game/client/cl_skin.cpp deleted file mode 100644 index da8fe535..00000000 --- a/src/game/client/cl_skin.cpp +++ /dev/null @@ -1,167 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include -#include -#include -#include -#include "cl_skin.h" -#include "../math.h" - -enum -{ - MAX_SKINS=256, -}; - -static skin skins[MAX_SKINS] = {{0}}; -static int num_skins = 0; - -static void skinscan(const char *name, int is_dir, void *user) -{ - int l = strlen(name); - if(l < 4 || is_dir || num_skins == MAX_SKINS) - return; - if(strcmp(name+l-4, ".png") != 0) - return; - - char buf[512]; - sprintf(buf, "data/skins/%s", name); - IMAGE_INFO info; - if(!gfx_load_png(&info, buf)) - { - dbg_msg("game", "failed to load skin from %s", name); - return; - } - - skins[num_skins].org_texture = gfx_load_texture_raw(info.width, info.height, info.format, info.data); - - // create colorless version - unsigned char *d = (unsigned char *)info.data; - int step = info.format == IMG_RGBA ? 4 : 3; - - for(int i = 0; i < info.width*info.height; i++) - { - int v = (d[i*step]+d[i*step+1]+d[i*step+2])/3; - d[i*step] = v; - d[i*step+1] = v; - d[i*step+2] = v; - } - - if(1) - { - int bs = 96; // body size - int pitch = info.width*4; - int freq[256] = {0}; - - for(int y = 0; y < bs; y++) - for(int x = 0; x < bs; x++) - { - if(d[y*pitch+x*4+3] > 128) - freq[d[y*pitch+x*4]]++; - } - - int org_weight = 0; - int new_weight = 192; - for(int i = 1; i < 256; i++) - { - if(freq[org_weight] < freq[i]) - org_weight = i; - } - - int inv_org_weight = 255-org_weight; - int inv_new_weight = 255-new_weight; - for(int y = 0; y < bs; y++) - for(int x = 0; x < bs; x++) - { - int v = d[y*pitch+x*4]; - if(v <= org_weight) - v = (int)(((v/(float)org_weight) * new_weight)); - else - v = (int)(((v-org_weight)/(float)inv_org_weight)*inv_new_weight + new_weight); - d[y*pitch+x*4] = v; - d[y*pitch+x*4+1] = v; - d[y*pitch+x*4+2] = v; - } - } - - skins[num_skins].color_texture = gfx_load_texture_raw(info.width, info.height, info.format, info.data); - mem_free(info.data); - - // set skin data - strncpy(skins[num_skins].name, name, min((int)sizeof(skins[num_skins].name),l-4)); - dbg_msg("game", "load skin %s", skins[num_skins].name); - num_skins++; -} - - -void skin_init() -{ - // load skins - fs_listdir("data/skins", skinscan, 0); -} - -int skin_num() -{ - return num_skins; -} - -const skin *skin_get(int index) -{ - return &skins[index%num_skins]; -} - -int skin_find(const char *name) -{ - for(int i = 0; i < num_skins; i++) - { - if(strcmp(skins[i].name, name) == 0) - return i; - } - return -1; -} - - - - -// these converter functions were nicked from some random internet pages -static float hue_to_rgb(float v1, float v2, float h) -{ - if(h < 0) h += 1; - if(h > 1) h -= 1; - if((6 * h) < 1) return v1 + ( v2 - v1 ) * 6 * h; - if((2 * h) < 1) return v2; - if((3 * h) < 2) return v1 + ( v2 - v1 ) * ((2.0f/3.0f) - h) * 6; - return v1; -} - -vec3 hsl_to_rgb(vec3 in) -{ - float v1, v2; - vec3 out; - - if(in.s == 0) - { - out.r = in.l; - out.g = in.l; - out.b = in.l; - } - else - { - if(in.l < 0.5f) - v2 = in.l * (1 + in.s); - else - v2 = (in.l+in.s) - (in.s*in.l); - - v1 = 2 * in.l - v2; - - out.r = hue_to_rgb(v1, v2, in.h + (1.0f/3.0f)); - out.g = hue_to_rgb(v1, v2, in.h); - out.b = hue_to_rgb(v1, v2, in.h - (1.0f/3.0f)); - } - - return out; -} - -vec4 skin_get_color(int v) -{ - vec3 r = hsl_to_rgb(vec3((v>>16)/255.0f, ((v>>8)&0xff)/255.0f, 0.5f+(v&0xff)/255.0f*0.5f)); - return vec4(r.r, r.g, r.b, 1.0f); -} diff --git a/src/game/client/cl_skin.h b/src/game/client/cl_skin.h deleted file mode 100644 index bafc76b9..00000000 --- a/src/game/client/cl_skin.h +++ /dev/null @@ -1,20 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include "../vmath.h" - -// do this better and nicer -typedef struct -{ - int org_texture; - int color_texture; - char name[31]; - char term[1]; -} skin; - -vec4 skin_get_color(int v); -void skin_init(); -int skin_num(); -const skin *skin_get(int index); -int skin_find(const char *name); - - -vec3 hsl_to_rgb(vec3 in); diff --git a/src/game/client/data/createdir.txt b/src/game/client/data/createdir.txt deleted file mode 100644 index 258b42d5..00000000 --- a/src/game/client/data/createdir.txt +++ /dev/null @@ -1 +0,0 @@ -this file is here to make sure that this directory gets created diff --git a/src/game/client/game_client.cpp b/src/game/client/game_client.cpp deleted file mode 100644 index 00deea5f..00000000 --- a/src/game/client/game_client.cpp +++ /dev/null @@ -1,3103 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include -#include -#include -#include - -extern "C" { - #include - #include -}; - -#include "../game.h" -#include "../version.h" -#include "../mapres.h" -#include "mapres_image.h" -#include "mapres_tilemap.h" -#include "data.h" -#include "menu.h" -#include "cl_skin.h" -#include "cl_render.h" - -// sound channels -enum -{ - CHN_GUI=0, - CHN_MUSIC, - CHN_WORLD, - CHN_GLOBAL, -}; - -data_container *data = 0x0; - -int gametype = GAMETYPE_DM; - -extern void modmenu_render(); - -enum -{ - CHATMODE_NONE=0, - CHATMODE_ALL, - CHATMODE_TEAM, - CHATMODE_CONSOLE, - CHATMODE_REMOTECONSOLE, -}; - -typedef struct -{ - float x, y, w, h; -} RECT; -RECT *ui2_screen(); - -static int chat_mode = CHATMODE_NONE; -bool menu_active = false; -bool menu_game_active = false; -static bool emoticon_selector_active = false; - -static vec2 mouse_pos; -static vec2 local_character_pos; -static vec2 local_target_pos; -static const obj_player_character *local_character = 0; -static const obj_player_character *local_prev_character = 0; -const obj_player_info *local_info = 0; -static const obj_flag *flags[2] = {0,0}; -static const obj_game *gameobj = 0; - -static int picked_up_weapon = 0; - -static struct client_data -{ - char name[64]; - char skin_name[64]; - int skin_id; - int skin_color; - int team; - int emoticon; - int emoticon_start; - player_core predicted; - - tee_render_info skin_info; - -} client_datas[MAX_CLIENTS]; - -class client_effects -{ -public: - float zoom; - float currentzoom; - float stage; - int lastzoomin; - int lastincrease; - - client_effects() - { - currentzoom = zoom = 3.0f; - stage = 0.0f; - } - - float getorgzoom() { return zoom; } - - float getzoom(int tick, float intratick, const obj_player_character *player) - { - float currentstage = ((float)player->weaponstage) * 0.1f; - if (currentstage < stage) - { - if ((tick - lastincrease) > (client_tickspeed() / 2)) - stage = currentstage; - } - else - { - lastincrease = tick; - stage = currentstage; - } - - float targetzoom = 3.0f + stage; - currentzoom = LERP(currentzoom, targetzoom, 0.1); - return currentzoom; - } -}; - -client_effects cl_effects; - -inline float frandom() { return rand()/(float)(RAND_MAX); } - -void snd_play_random(int chn, int setid, float vol, vec2 pos) -{ - soundset *set = &data->sounds[setid]; - - if(!set->num_sounds) - return; - - if(set->num_sounds == 1) - { - snd_play_at(chn, set->sounds[0].id, 0, pos.x, pos.y); - return; - } - - // play a random one - int id; - do { - id = rand() % set->num_sounds; - } while(id == set->last); - snd_play_at(chn, set->sounds[id].id, 0, pos.x, pos.y); - set->last = id; -} - -// sound volume tweak -static const float stereo_separation = 0.001f; -static const float stereo_separation_deadzone = 200.0f; -static const float volume_distance_falloff = 200.0f; -static const float volume_distance_deadzone = 320.0f; -static const float volume_gun = 0.5f; -static const float volume_tee = 0.5f; -static const float volume_hit = 0.5f; -static const float volume_music = 0.8f; - -void sound_vol_pan(const vec2& p, float *vol, float *pan) -{ - vec2 player_to_ev = p - local_character_pos; - *pan = 0.0f; - *vol = 1.0f; - - if(fabs(player_to_ev.x) > stereo_separation_deadzone) - { - *pan = stereo_separation * (player_to_ev.x - sign(player_to_ev.x)*stereo_separation_deadzone); - if(*pan < -1.0f) *pan = -1.0f; - if(*pan > 1.0f) *pan = 1.0f; - } - - float len = length(player_to_ev); - if(len > volume_distance_deadzone) - { - *vol = volume_distance_falloff / (len - volume_distance_deadzone); - - if(*vol < 0.0f) *vol = 0.0f; - if(*vol > 1.0f) *vol = 1.0f; - } -} - -enum -{ - SPRITE_FLAG_FLIP_Y=1, - SPRITE_FLAG_FLIP_X=2, -}; - -static float sprite_w_scale; -static float sprite_h_scale; - -static void select_sprite(sprite *spr, int flags=0, int sx=0, int sy=0) -{ - int x = spr->x+sx; - int y = spr->y+sy; - int w = spr->w; - int h = spr->h; - int cx = spr->set->gridx; - int cy = spr->set->gridy; - - float f = sqrtf(h*h + w*w); - sprite_w_scale = w/f; - sprite_h_scale = h/f; - - float x1 = x/(float)cx; - float x2 = (x+w)/(float)cx; - float y1 = y/(float)cy; - float y2 = (y+h)/(float)cy; - float temp = 0; - - if(flags&SPRITE_FLAG_FLIP_Y) - { - temp = y1; - y1 = y2; - y2 = temp; - } - - if(flags&SPRITE_FLAG_FLIP_X) - { - temp = x1; - x1 = x2; - x2 = temp; - } - - gfx_quads_setsubset(x1, y1, x2, y2); -} - -void select_sprite(int id, int flags=0, int sx=0, int sy=0) -{ - if(id < 0 || id > data->num_sprites) - return; - select_sprite(&data->sprites[id], flags, sx, sy); -} - -void draw_sprite(float x, float y, float size) -{ - gfx_quads_draw(x, y, size*sprite_w_scale, size*sprite_h_scale); -} - -class damage_indicators -{ -public: - int64 lastupdate; - struct item - { - vec2 pos; - vec2 dir; - float life; - float startangle; - }; - - enum - { - MAX_ITEMS=64, - }; - - damage_indicators() - { - lastupdate = 0; - num_items = 0; - } - - item items[MAX_ITEMS]; - int num_items; - - item *create_i() - { - if (num_items < MAX_ITEMS) - { - item *p = &items[num_items]; - num_items++; - return p; - } - return 0; - } - - void destroy_i(item *i) - { - num_items--; - *i = items[num_items]; - } - - void create(vec2 pos, vec2 dir) - { - item *i = create_i(); - if (i) - { - i->pos = pos; - i->life = 0.75f; - i->dir = dir*-1; - i->startangle = (( (float)rand()/(float)RAND_MAX) - 1.0f) * 2.0f * pi; - } - } - - void render() - { - gfx_texture_set(data->images[IMAGE_GAME].id); - gfx_quads_begin(); - for(int i = 0; i < num_items;) - { - vec2 pos = mix(items[i].pos+items[i].dir*75.0f, items[i].pos, clamp((items[i].life-0.60f)/0.15f, 0.0f, 1.0f)); - - items[i].life -= client_frametime(); - if(items[i].life < 0.0f) - destroy_i(&items[i]); - else - { - gfx_setcolor(1.0f,1.0f,1.0f, items[i].life/0.1f); - gfx_quads_setrotation(items[i].startangle + items[i].life * 2.0f); - select_sprite(SPRITE_STAR1); - draw_sprite(pos.x, pos.y, 48.0f); - i++; - } - } - gfx_quads_end(); - } - -}; - -static damage_indicators damageind; - -class particle_system -{ -public: - struct particle - { - vec2 pos; - vec2 vel; - float life; - float max_life; - float size; - - float rot; - float rotspeed; - - float gravity; - float friction; - int iparticle; - - vec4 color; - }; - - enum - { - MAX_PARTICLES=1024, - }; - - particle particles[MAX_PARTICLES]; - int num_particles; - - particle_system() - { - num_particles = 0; - } - - void new_particle(vec2 pos, vec2 vel, float life, float size, float gravity, float friction, vec4 color=vec4(1,1,1,1)) - { - if (num_particles >= MAX_PARTICLES) - return; - - particles[num_particles].iparticle = rand() % data->num_particles; - particles[num_particles].pos = pos; - particles[num_particles].vel = vel; - particles[num_particles].life = life - (data->particles[particles[num_particles].iparticle].lifemod/100.0f) * life; - particles[num_particles].size = size; - particles[num_particles].max_life = life; - particles[num_particles].gravity = gravity; - particles[num_particles].friction = friction; - particles[num_particles].rot = frandom()*pi*2; - particles[num_particles].rotspeed = frandom() * 10.0f; - particles[num_particles].color = color; - num_particles++; - } - - void update(float time_passed) - { - for(int i = 0; i < num_particles; i++) - { - particles[i].vel.y += particles[i].gravity*time_passed; - particles[i].vel *= particles[i].friction; - vec2 vel = particles[i].vel*time_passed; - move_point(&particles[i].pos, &vel, 0.1f+0.9f*frandom(), NULL); - particles[i].vel = vel* (1.0f/time_passed); - particles[i].life += time_passed; - particles[i].rot += time_passed * particles[i].rotspeed; - - // check particle death - if(particles[i].life > particles[i].max_life) - { - num_particles--; - particles[i] = particles[num_particles]; - i--; - } - } - } - - void render() - { - gfx_blend_additive(); - gfx_texture_set(data->images[IMAGE_GAME].id); - gfx_quads_begin(); - - for(int i = 0; i < num_particles; i++) - { - int type = particles[i].iparticle; - select_sprite(data->particles[type].spr); - float a = 1 - particles[i].life / particles[i].max_life; - vec2 p = particles[i].pos; - - gfx_quads_setrotation(particles[i].rot); - - gfx_setcolor( - data->particles[type].color_r * particles[i].color.r, - data->particles[type].color_g * particles[i].color.g, - data->particles[type].color_b * particles[i].color.b, - pow(a, 0.75f) * particles[i].color.a); - - gfx_quads_draw(p.x, p.y,particles[i].size,particles[i].size); - } - gfx_quads_end(); - gfx_blend_normal(); - } -}; - -static particle_system temp_system; - -class projectile_particles -{ -public: - enum - { - LISTSIZE = 1000, - }; - // meh, just use size % - int lastadd[LISTSIZE]; - projectile_particles() - { - reset(); - } - - void reset() - { - for (int i = 0; i < LISTSIZE; i++) - lastadd[i] = -1000; - } - - void addparticle(int projectiletype, int projectileid, vec2 pos, vec2 vel) - { - int particlespersecond = data->projectileinfo[projectiletype].particlespersecond; - int lastaddtick = lastadd[projectileid % LISTSIZE]; - - if(!particlespersecond) - return; - - if ((client_tick() - lastaddtick) > (client_tickspeed() / particlespersecond)) - { - lastadd[projectileid % LISTSIZE] = client_tick(); - float life = data->projectileinfo[projectiletype].particlelife; - float size = data->projectileinfo[projectiletype].particlesize; - vec2 v = vel * 0.2f + normalize(vec2(frandom()-0.5f, -frandom()))*(32.0f+frandom()*32.0f); - - // add the particle (from projectiletype later on, but meh...) - temp_system.new_particle(pos, v, life, size, 0, 0.95f); - } - } -}; -static projectile_particles proj_particles; - -static char chat_input[512]; -static unsigned chat_input_len; -static const int chat_max_lines = 10; - -struct chatline -{ - int tick; - int client_id; - int team; - char text[512+64]; -}; - -chatline chat_lines[chat_max_lines]; -static int chat_current_line = 0; - -void chat_reset() -{ - for(int i = 0; i < chat_max_lines; i++) - chat_lines[i].tick = -1000000; - chat_current_line = 0; -} - -void chat_add_line(int client_id, int team, const char *line) -{ - chat_current_line = (chat_current_line+1)%chat_max_lines; - chat_lines[chat_current_line].tick = client_tick(); - chat_lines[chat_current_line].client_id = client_id; - chat_lines[chat_current_line].team = team; - - if(client_id == -1) // server message - sprintf(chat_lines[chat_current_line].text, "*** %s", line); - else - { - sprintf(chat_lines[chat_current_line].text, "%s: %s", client_datas[client_id].name, line); - } -} - -struct killmsg -{ - int weapon; - int victim; - int killer; - int mode_special; // for CTF, if the guy is carrying a flag for example - int tick; -}; - -static const int killmsg_max = 5; -killmsg killmsgs[killmsg_max]; -static int killmsg_current = 0; - -extern unsigned char internal_data[]; - -void create_air_jump_effect(vec2 pos) -{ - const int count = 12; - for(int i = 0; i <= count; i++) - { - float a = i/(float)count; - vec2 v = vec2((a-0.5f)*512.0f, 0); - temp_system.new_particle(pos+vec2(0,28), v, 0.4f, 16.0f, 0, 0.985f, vec4(0.25f,0.4f,1,1)); - } -} - - -extern void draw_round_rect(float x, float y, float w, float h, float r); -extern int render_popup(const char *caption, const char *text, const char *button_text); - -static void render_loading(float percent) -{ - gfx_clear(0.65f,0.78f,0.9f); - RECT screen = *ui2_screen(); - gfx_mapscreen(screen.x, screen.y, screen.w, screen.h); - - float tw; - - float w = 700; - float h = 200; - float x = screen.w/2-w/2; - float y = screen.h/2-h/2; - - gfx_blend_normal(); - - gfx_texture_set(-1); - gfx_quads_begin(); - gfx_setcolor(0,0,0,0.50f); - draw_round_rect(x, y, w, h, 40.0f); - gfx_quads_end(); - - const char *caption = "Loading"; - - tw = gfx_pretty_text_width(48.0f, caption, -1); - ui_do_label(x+w/2-tw/2, y+20, caption, 48.0f); - - gfx_texture_set(-1); - gfx_quads_begin(); - gfx_setcolor(1,1,1,1.0f); - draw_round_rect(x+40, y+h-75, (w-80)*percent, 25, 5.0f); - gfx_quads_end(); - - gfx_swap(); -} - -extern "C" void modc_init() -{ - // setup sound channels - snd_set_channel(CHN_GUI, 1.0f, 0.0f); - snd_set_channel(CHN_MUSIC, 1.0f, 0.0f); - snd_set_channel(CHN_WORLD, 0.9f, 1.0f); - snd_set_channel(CHN_GLOBAL, 1.0f, 0.0f); - - // load the data container - data = load_data_from_memory(internal_data); - - // TODO: should be removed - snd_set_listener_pos(0.0f, 0.0f); - - float total = data->num_sounds+data->num_images; - float current = 0; - - // load sounds - for(int s = 0; s < data->num_sounds; s++) - { - render_loading(current/total); - for(int i = 0; i < data->sounds[s].num_sounds; i++) - { - int id; - //if (strcmp(data->sounds[s].sounds[i].filename + strlen(data->sounds[s].sounds[i].filename) - 3, ".wv") == 0) - id = snd_load_wv(data->sounds[s].sounds[i].filename); - //else - // id = snd_load_wav(data->sounds[s].sounds[i].filename); - - data->sounds[s].sounds[i].id = id; - } - - current++; - } - - // load textures - for(int i = 0; i < data->num_images; i++) - { - render_loading(current/total); - data->images[i].id = gfx_load_texture(data->images[i].filename); - current++; - } - - skin_init(); -} - -extern "C" void modc_entergame() -{ -} - -extern "C" void modc_shutdown() -{ - // shutdown the menu -} - -static void process_events(int s) -{ - int num = snap_num_items(s); - for(int index = 0; index < num; index++) - { - SNAP_ITEM item; - const void *data = snap_get_item(s, index, &item); - - if(item.type == EVENT_DAMAGEINDICATION) - { - ev_damageind *ev = (ev_damageind *)data; - damageind.create(vec2(ev->x, ev->y), get_direction(ev->angle)); - } - else if(item.type == EVENT_AIR_JUMP) - { - ev_common *ev = (ev_common *)data; - create_air_jump_effect(vec2(ev->x, ev->y)); - } - else if(item.type == EVENT_EXPLOSION) - { - ev_explosion *ev = (ev_explosion *)data; - vec2 p(ev->x, ev->y); - - // center explosion - temp_system.new_particle(p, vec2(0,0), 0.3f, 96.0f, 0, 0.95f); - temp_system.new_particle(p, vec2(0,0), 0.3f, 64.0f, 0, 0.95f); - temp_system.new_particle(p, vec2(0,0), 0.3f, 32.0f, 0, 0.95f); - temp_system.new_particle(p, vec2(0,0), 0.3f, 16.0f, 0, 0.95f); - - for(int i = 0; i < 16; i++) - { - vec2 v = normalize(vec2(frandom()-0.5f, frandom()-0.5f))*(128.0f+frandom()*128.0f); - temp_system.new_particle(p, v, 0.2f+0.25f*frandom(), 16.0f, 0, 0.985f); - } - - for(int i = 0; i < 16; i++) - { - vec2 v = normalize(vec2(frandom()-0.5f, frandom()-0.5f))*(256.0f+frandom()*512.0f); - temp_system.new_particle(p, v, 0.2f+0.25f*frandom(), 16.0f, 128.0f, 0.985f); - } - - for(int i = 0; i < 64; i++) - { - vec2 v = normalize(vec2(frandom()-0.5f, frandom()-0.5f))*(frandom()*256.0f); - temp_system.new_particle(p, v, 0.2f+0.25f*frandom(), 24.0f, 128.0f, 0.985f); - } - } - else if(item.type == EVENT_SMOKE) - { - ev_explosion *ev = (ev_explosion *)data; - vec2 p(ev->x, ev->y); - - // center explosion - vec2 v = normalize(vec2(frandom()-0.5f, -frandom()))*(32.0f+frandom()*32.0f); - temp_system.new_particle(p, v, 1.2f, 64.0f, 0, 0.95f); - v = normalize(vec2(frandom()-0.5f, -frandom()))*(128.0f+frandom()*128.0f); - temp_system.new_particle(p, v, 1.2f, 32.0f, 0, 0.95f); - v = normalize(vec2(frandom()-0.5f, -frandom()))*(128.0f+frandom()*128.0f); - temp_system.new_particle(p, v, 1.2f, 16.0f, 0, 0.95f); - - for(int i = 0; i < 8; i++) - { - vec2 v = normalize(vec2(frandom()-0.5f, frandom()-0.5f))*(64.0f+frandom()*64.0f); - temp_system.new_particle(p, v, 0.5f+0.5f*frandom(), 16.0f, 0, 0.985f); - } - - for(int i = 0; i < 8; i++) - { - vec2 v = normalize(vec2(frandom()-0.5f, frandom()-0.5f))*(128.0f+frandom()*256.0f); - temp_system.new_particle(p, v, 0.5f+0.5f*frandom(), 16.0f, 128.0f, 0.985f); - } - } - else if(item.type == EVENT_SPAWN) - { - ev_explosion *ev = (ev_explosion *)data; - vec2 p(ev->x, ev->y); - - // center explosion - vec2 v = normalize(vec2(frandom()-0.5f, -frandom()))*(32.0f+frandom()*32.0f); - temp_system.new_particle(p, v, 1.2f, 64.0f, 0, 0.95f); - v = normalize(vec2(frandom()-0.5f, -frandom()))*(128.0f+frandom()*128.0f); - temp_system.new_particle(p, v, 1.2f, 32.0f, 0, 0.95f); - v = normalize(vec2(frandom()-0.5f, -frandom()))*(128.0f+frandom()*128.0f); - temp_system.new_particle(p, v, 1.2f, 16.0f, 0, 0.95f); - - for(int i = 0; i < 8; i++) - { - vec2 v = normalize(vec2(frandom()-0.5f, frandom()-0.5f))*(64.0f+frandom()*64.0f); - temp_system.new_particle(p, v, 0.5f+0.5f*frandom(), 16.0f, 0, 0.985f); - } - - for(int i = 0; i < 8; i++) - { - vec2 v = normalize(vec2(frandom()-0.5f, frandom()-0.5f))*(128.0f+frandom()*256.0f); - temp_system.new_particle(p, v, 0.5f+0.5f*frandom(), 16.0f, 128.0f, 0.985f); - } - } - else if(item.type == EVENT_DEATH) - { - ev_explosion *ev = (ev_explosion *)data; - vec2 p(ev->x, ev->y); - vec4 c(0.5f, 0.1f, 0.1f, 1.0f); - - // center explosion - vec2 v = normalize(vec2(frandom()-0.5f, -frandom()))*(32.0f+frandom()*32.0f); - temp_system.new_particle(p, v, 1.2f, 64.0f, 0, 0.95f, c); - v = normalize(vec2(frandom()-0.5f, -frandom()))*(128.0f+frandom()*128.0f); - temp_system.new_particle(p, v, 1.2f, 32.0f, 0, 0.95f, c); - v = normalize(vec2(frandom()-0.5f, -frandom()))*(128.0f+frandom()*128.0f); - temp_system.new_particle(p, v, 1.2f, 16.0f, 0, 0.95f, c); - - for(int i = 0; i < 8; i++) - { - vec2 v = normalize(vec2(frandom()-0.5f, frandom()-0.5f))*(64.0f+frandom()*64.0f); - temp_system.new_particle(p, v, 0.5f+0.5f*frandom(), 16.0f, 0, 0.985f, c); - } - - for(int i = 0; i < 8; i++) - { - vec2 v = normalize(vec2(frandom()-0.5f, frandom()-0.5f))*(128.0f+frandom()*256.0f); - temp_system.new_particle(p, v, 0.5f+0.5f*frandom(), 16.0f, 128.0f, 0.985f, c); - } - } - else if(item.type == EVENT_SOUND_WORLD) - { - ev_sound *ev = (ev_sound *)data; - if(ev->sound >= 0 && ev->sound < NUM_SOUNDS) - snd_play_random(CHN_WORLD, ev->sound, 1.0f, vec2(ev->x, ev->y)); - } - } -} - -static player_core predicted_prev_player; -static player_core predicted_player; -static int predicted_tick = 0; -static int last_new_predicted_tick = -1; - -extern "C" void modc_predict() -{ - player_core before_prev_player = predicted_prev_player; - player_core before_player = predicted_player; - - - // repredict player - world_core world; - int local_cid = -1; - - // search for players - for(int i = 0; i < snap_num_items(SNAP_CURRENT); i++) - { - SNAP_ITEM item; - const void *data = snap_get_item(SNAP_CURRENT, i, &item); - int client_id = item.id; - - if(item.type == OBJTYPE_PLAYER_CHARACTER) - { - const obj_player_character *character = (const obj_player_character *)data; - client_datas[client_id].predicted.world = &world; - world.players[client_id] = &client_datas[client_id].predicted; - - client_datas[client_id].predicted.read(character); - } - else if(item.type == OBJTYPE_PLAYER_INFO) - { - const obj_player_info *info = (const obj_player_info *)data; - if(info->local) - local_cid = client_id; - } - } - - // predict - for(int tick = client_tick()+1; tick <= client_predtick(); tick++) - { - // fetch the local - if(tick == client_predtick() && world.players[local_cid]) - predicted_prev_player = *world.players[local_cid]; - - // first calculate where everyone should move - for(int c = 0; c < MAX_CLIENTS; c++) - { - if(!world.players[c]) - continue; - - mem_zero(&world.players[c]->input, sizeof(world.players[c]->input)); - if(local_cid == c) - { - // apply player input - int *input = client_get_input(tick); - if(input) - world.players[c]->input = *((player_input*)input); - } - - world.players[c]->tick(); - } - - // move all players and quantize their data - for(int c = 0; c < MAX_CLIENTS; c++) - { - if(!world.players[c]) - continue; - - world.players[c]->move(); - world.players[c]->quantize(); - } - - if(tick > last_new_predicted_tick) - { - last_new_predicted_tick = tick; - - if(local_cid != -1 && world.players[local_cid]) - { - vec2 pos = world.players[local_cid]->pos; - int events = world.players[local_cid]->triggered_events; - if(events&COREEVENT_GROUND_JUMP) snd_play_random(CHN_WORLD, SOUND_PLAYER_JUMP, 1.0f, pos); - if(events&COREEVENT_AIR_JUMP) - { - create_air_jump_effect(pos); - snd_play_random(CHN_WORLD, SOUND_PLAYER_JUMP, 1.0f, pos); - } - //if(events&COREEVENT_HOOK_LAUNCH) snd_play_random(CHN_WORLD, SOUND_HOOK_LOOP, 1.0f, pos); - if(events&COREEVENT_HOOK_ATTACH_PLAYER) snd_play_random(CHN_WORLD, SOUND_HOOK_ATTACH_PLAYER, 1.0f, pos); - if(events&COREEVENT_HOOK_ATTACH_GROUND) snd_play_random(CHN_WORLD, SOUND_HOOK_ATTACH_GROUND, 1.0f, pos); - //if(events&COREEVENT_HOOK_RETRACT) snd_play_random(CHN_WORLD, SOUND_PLAYER_JUMP, 1.0f, pos); - } - - - /* - dbg_msg("predict", "%d %d %d", tick, - (int)world.players[c]->pos.x, (int)world.players[c]->pos.y, - (int)world.players[c]->vel.x, (int)world.players[c]->vel.y);*/ - } - - if(tick == client_predtick() && world.players[local_cid]) - predicted_player = *world.players[local_cid]; - } - - if(config.debug && predicted_tick == client_predtick()) - { - if(predicted_player.pos.x != before_player.pos.x || - predicted_player.pos.y != before_player.pos.y) - { - dbg_msg("client", "prediction error, (%d %d) (%d %d)", - (int)before_player.pos.x, (int)before_player.pos.y, - (int)predicted_player.pos.x, (int)predicted_player.pos.y); - } - - if(predicted_prev_player.pos.x != before_prev_player.pos.x || - predicted_prev_player.pos.y != before_prev_player.pos.y) - { - dbg_msg("client", "prediction error, prev (%d %d) (%d %d)", - (int)before_prev_player.pos.x, (int)before_prev_player.pos.y, - (int)predicted_prev_player.pos.x, (int)predicted_prev_player.pos.y); - } - } - - predicted_tick = client_predtick(); -} - -static void clear_object_pointers() -{ - // clear out the invalid pointers - local_character = 0; - local_prev_character = 0; - local_info = 0; - flags[0] = 0; - flags[1] = 0; - gameobj = 0; -} - -extern "C" void modc_newsnapshot() -{ - process_events(SNAP_CURRENT); - - if(config.dbg_stress) - { - if((client_tick()%250) == 0) - { - msg_pack_start(MSG_SAY, MSGFLAG_VITAL); - msg_pack_int(-1); - msg_pack_string("galenskap!!!!", 512); - msg_pack_end(); - client_send_msg(); - } - } - - clear_object_pointers(); - - // setup world view - { - // 1. fetch local player - // 2. set him to the center - int num = snap_num_items(SNAP_CURRENT); - for(int i = 0; i < num; i++) - { - SNAP_ITEM item; - const void *data = snap_get_item(SNAP_CURRENT, i, &item); - - if(item.type == OBJTYPE_PLAYER_INFO) - { - const obj_player_info *info = (const obj_player_info *)data; - if(info->local) - { - local_info = info; - const void *data = snap_find_item(SNAP_CURRENT, OBJTYPE_PLAYER_CHARACTER, item.id); - if(data) - { - local_character = (const obj_player_character *)data; - local_character_pos = vec2(local_character->x, local_character->y); - - const void *p = snap_find_item(SNAP_PREV, OBJTYPE_PLAYER_CHARACTER, item.id); - if(p) - local_prev_character = (obj_player_character *)p; - } - } - } - else if(item.type == OBJTYPE_GAME) - gameobj = (obj_game *)data; - else if(item.type == OBJTYPE_FLAG) - { - flags[item.id%2] = (const obj_flag *)data; - } - } - } -} - -void send_info(bool start) -{ - if(start) - msg_pack_start(MSG_STARTINFO, MSGFLAG_VITAL); - else - msg_pack_start(MSG_CHANGEINFO, MSGFLAG_VITAL); - msg_pack_string(config.player_name, 64); - msg_pack_string(config.player_skin, 64); - msg_pack_int(config.player_use_custom_color); - msg_pack_int(config.player_color_body); - msg_pack_int(config.player_color_feet); - msg_pack_end(); - client_send_msg(); -} - -void send_emoticon(int emoticon) -{ - msg_pack_start(MSG_EMOTICON, MSGFLAG_VITAL); - msg_pack_int(emoticon); - msg_pack_end(); - client_send_msg(); -} - -static void render_projectile(const obj_projectile *prev, const obj_projectile *current, int itemid) -{ - gfx_texture_set(data->images[IMAGE_GAME].id); - gfx_quads_begin(); - - // get positions - float gravity = -400; - if(current->type != WEAPON_ROCKET) - gravity = -100; - - float ct = (client_tick()-current->start_tick)/(float)SERVER_TICK_SPEED + client_intratick()*1/(float)SERVER_TICK_SPEED; - vec2 startpos(current->x, current->y); - vec2 startvel(current->vx, current->vy); - vec2 pos = calc_pos(startpos, startvel, gravity, ct); - vec2 prevpos = calc_pos(startpos, startvel, gravity, ct-0.001f); - - select_sprite(data->weapons[current->type%data->num_weapons].sprite_proj); - vec2 vel = pos-prevpos; - //vec2 pos = mix(vec2(prev->x, prev->y), vec2(current->x, current->y), client_intratick()); - - // add particle for this projectile - proj_particles.addparticle(current->type, itemid, pos, vel); - - if(length(vel) > 0.00001f) - gfx_quads_setrotation(get_angle(vel)); - else - gfx_quads_setrotation(0); - - // TODO: do this, but nice - //temp_system.new_particle(pos, vec2(0,0), 0.3f, 14.0f, 0, 0.95f); - - gfx_quads_draw(pos.x, pos.y, 32, 32); - gfx_quads_setrotation(0); - gfx_quads_end(); -} - -static void render_powerup(const obj_powerup *prev, const obj_powerup *current) -{ - gfx_texture_set(data->images[IMAGE_GAME].id); - gfx_quads_begin(); - vec2 pos = mix(vec2(prev->x, prev->y), vec2(current->x, current->y), client_intratick()); - float angle = 0.0f; - float size = 64.0f; - if (current->type == POWERUP_WEAPON) - { - angle = 0; //-pi/6;//-0.25f * pi * 2.0f; - select_sprite(data->weapons[current->subtype%data->num_weapons].sprite_body); - size = data->weapons[current->subtype%data->num_weapons].visual_size; - } - else - { - const int c[] = { - SPRITE_POWERUP_HEALTH, - SPRITE_POWERUP_ARMOR, - SPRITE_POWERUP_WEAPON, - SPRITE_POWERUP_NINJA, - SPRITE_POWERUP_TIMEFIELD - }; - select_sprite(c[current->type]); - - if(c[current->type] == SPRITE_POWERUP_NINJA) - { - proj_particles.addparticle(0, 0, - pos+vec2((frandom()-0.5f)*80.0f, (frandom()-0.5f)*20.0f), - vec2((frandom()-0.5f)*10.0f, (frandom()-0.5f)*10.0f)); - size *= 2.0f; - pos.x += 10.0f; - } - } - - gfx_quads_setrotation(angle); - - float offset = pos.y/32.0f + pos.x/32.0f; - pos.x += cosf(client_localtime()*2.0f+offset)*2.5f; - pos.y += sinf(client_localtime()*2.0f+offset)*2.5f; - draw_sprite(pos.x, pos.y, size); - gfx_quads_end(); -} - -static void render_flag(const obj_flag *prev, const obj_flag *current) -{ - float angle = 0.0f; - float size = 42.0f; - - gfx_blend_normal(); - gfx_texture_set(data->images[IMAGE_GAME].id); - gfx_quads_begin(); - - if(current->team == 0) // red team - select_sprite(SPRITE_FLAG_RED); - else - select_sprite(SPRITE_FLAG_BLUE); - - gfx_quads_setrotation(angle); - - vec2 pos = mix(vec2(prev->x, prev->y), vec2(current->x, current->y), client_intratick()); - - if(local_info && current->carried_by == local_info->clientid) - pos = local_character_pos; - - gfx_setcolor(current->team ? 0 : 1,0,current->team ? 1 : 0,1); - //draw_sprite(pos.x, pos.y, size); - gfx_quads_draw(pos.x, pos.y-size*0.75f, size, size*2); - gfx_quads_end(); -} - -void anim_seq_eval(sequence *seq, float time, keyframe *frame) -{ - if(seq->num_frames == 0) - { - frame->time = 0; - frame->x = 0; - frame->y = 0; - frame->angle = 0; - } - else if(seq->num_frames == 1) - { - *frame = seq->frames[0]; - } - else - { - //time = max(0.0f, min(1.0f, time / duration)); // TODO: use clamp - keyframe *frame1 = 0; - keyframe *frame2 = 0; - float blend = 0.0f; - - // TODO: make this smarter.. binary search - for (int i = 1; i < seq->num_frames; i++) - { - if (seq->frames[i-1].time <= time && seq->frames[i].time >= time) - { - frame1 = &seq->frames[i-1]; - frame2 = &seq->frames[i]; - blend = (time - frame1->time) / (frame2->time - frame1->time); - break; - } - } - - if (frame1 && frame2) - { - frame->time = time; - frame->x = mix(frame1->x, frame2->x, blend); - frame->y = mix(frame1->y, frame2->y, blend); - frame->angle = mix(frame1->angle, frame2->angle, blend); - } - } -} - -void anim_eval(animation *anim, float time, animstate *state) -{ - anim_seq_eval(&anim->body, time, &state->body); - anim_seq_eval(&anim->back_foot, time, &state->back_foot); - anim_seq_eval(&anim->front_foot, time, &state->front_foot); - anim_seq_eval(&anim->attach, time, &state->attach); -} - -void anim_add_keyframe(keyframe *seq, keyframe *added, float amount) -{ - seq->x += added->x*amount; - seq->y += added->y*amount; - seq->angle += added->angle*amount; -} - -void anim_add(animstate *state, animstate *added, float amount) -{ - anim_add_keyframe(&state->body, &added->body, amount); - anim_add_keyframe(&state->back_foot, &added->back_foot, amount); - anim_add_keyframe(&state->front_foot, &added->front_foot, amount); - anim_add_keyframe(&state->attach, &added->attach, amount); -} - -void anim_eval_add(animstate *state, animation *anim, float time, float amount) -{ - animstate add; - anim_eval(anim, time, &add); - anim_add(state, &add, amount); -} - -static void render_hand(int skin_id, vec2 center_pos, vec2 dir, float angle_offset, vec2 post_rot_offset) -{ - // for drawing hand - const skin *s = skin_get(skin_id); - - float basesize = 10.0f; - //dir = normalize(hook_pos-pos); - - vec2 hand_pos = center_pos + dir; - float angle = get_angle(dir); - if (dir.x < 0) - angle -= angle_offset; - else - angle += angle_offset; - - vec2 dirx = dir; - vec2 diry(-dir.y,dir.x); - - if (dir.x < 0) - diry = -diry; - - hand_pos += dirx * post_rot_offset.x; - hand_pos += diry * post_rot_offset.y; - - //gfx_texture_set(data->images[IMAGE_CHAR_DEFAULT].id); - gfx_texture_set(s->color_texture); - gfx_quads_begin(); - - // two passes - for (int i = 0; i < 2; i++) - { - bool outline = i == 0; - - select_sprite(outline?SPRITE_TEE_HAND_OUTLINE:SPRITE_TEE_HAND, 0, 0, 0); - gfx_quads_setrotation(angle); - gfx_quads_draw(hand_pos.x, hand_pos.y, 2*basesize, 2*basesize); - } - - gfx_quads_setrotation(0); - gfx_quads_end(); -} - -void render_tee(animstate *anim, tee_render_info *info, int emote, vec2 dir, vec2 pos) -{ - vec2 direction = dir; - vec2 position = pos; - - //gfx_texture_set(data->images[IMAGE_CHAR_DEFAULT].id); - gfx_texture_set(info->texture); - gfx_quads_begin(); - //gfx_quads_draw(pos.x, pos.y-128, 128, 128); - - // first pass we draw the outline - // second pass we draw the filling - for(int p = 0; p < 2; p++) - { - int outline = p==0 ? 1 : 0; - - for(int f = 0; f < 2; f++) - { - float animscale = info->size * 1.0f/64.0f; - float basesize = info->size; - if(f == 1) - { - gfx_quads_setrotation(anim->body.angle*pi*2); - - // draw body - gfx_setcolor(info->color_body.r, info->color_body.g, info->color_body.b, info->color_body.a); - vec2 body_pos = position + vec2(anim->body.x, anim->body.y)*animscale; - select_sprite(outline?SPRITE_TEE_BODY_OUTLINE:SPRITE_TEE_BODY, 0, 0, 0); - gfx_quads_draw(body_pos.x, body_pos.y, basesize, basesize); - - // draw eyes - if(p == 1) - { - switch (emote) - { - case EMOTE_PAIN: - select_sprite(SPRITE_TEE_EYE_PAIN, 0, 0, 0); - break; - case EMOTE_HAPPY: - select_sprite(SPRITE_TEE_EYE_HAPPY, 0, 0, 0); - break; - case EMOTE_SURPRISE: - select_sprite(SPRITE_TEE_EYE_SURPRISE, 0, 0, 0); - break; - case EMOTE_ANGRY: - select_sprite(SPRITE_TEE_EYE_ANGRY, 0, 0, 0); - break; - default: - select_sprite(SPRITE_TEE_EYE_NORMAL, 0, 0, 0); - break; - } - - float eyescale = basesize*0.40f; - float h = emote == EMOTE_BLINK ? basesize*0.15f : eyescale; - float eyeseparation = (0.075f - 0.010f*fabs(direction.x))*basesize; - vec2 offset = vec2(direction.x*0.125f, -0.05f+direction.y*0.10f)*basesize; - gfx_quads_draw(body_pos.x-eyeseparation+offset.x, body_pos.y+offset.y, eyescale, h); - gfx_quads_draw(body_pos.x+eyeseparation+offset.x, body_pos.y+offset.y, -eyescale, h); - } - } - - // draw feet - gfx_setcolor(info->color_feet.r, info->color_feet.g, info->color_feet.b, info->color_feet.a); - select_sprite(outline?SPRITE_TEE_FOOT_OUTLINE:SPRITE_TEE_FOOT, 0, 0, 0); - - keyframe *foot = f ? &anim->front_foot : &anim->back_foot; - - float w = basesize; - float h = basesize/2; - - gfx_quads_setrotation(foot->angle*pi*2); - gfx_quads_draw(position.x+foot->x*animscale, position.y+foot->y*animscale, w, h); - } - } - - gfx_quads_end(); -} - -void draw_circle(float x, float y, float r, int segments) -{ - float f_segments = (float)segments; - for(int i = 0; i < segments; i+=2) - { - float a1 = i/f_segments * 2*pi; - float a2 = (i+1)/f_segments * 2*pi; - float a3 = (i+2)/f_segments * 2*pi; - float ca1 = cosf(a1); - float ca2 = cosf(a2); - float ca3 = cosf(a3); - float sa1 = sinf(a1); - float sa2 = sinf(a2); - float sa3 = sinf(a3); - - gfx_quads_draw_freeform( - x, y, - x+ca1*r, y+sa1*r, - x+ca3*r, y+sa3*r, - x+ca2*r, y+sa2*r); - } -} - -void draw_round_rect_ext(float x, float y, float w, float h, float r, int corners) -{ - int num = 8; - for(int i = 0; i < num; i+=2) - { - float a1 = i/(float)num * pi/2; - float a2 = (i+1)/(float)num * pi/2; - float a3 = (i+2)/(float)num * pi/2; - float ca1 = cosf(a1); - float ca2 = cosf(a2); - float ca3 = cosf(a3); - float sa1 = sinf(a1); - float sa2 = sinf(a2); - float sa3 = sinf(a3); - - if(corners&1) // TL - gfx_quads_draw_freeform( - x+r, y+r, - x+(1-ca1)*r, y+(1-sa1)*r, - x+(1-ca3)*r, y+(1-sa3)*r, - x+(1-ca2)*r, y+(1-sa2)*r); - - if(corners&2) // TR - gfx_quads_draw_freeform( - x+w-r, y+r, - x+w-r+ca1*r, y+(1-sa1)*r, - x+w-r+ca3*r, y+(1-sa3)*r, - x+w-r+ca2*r, y+(1-sa2)*r); - - if(corners&4) // BL - gfx_quads_draw_freeform( - x+r, y+h-r, - x+(1-ca1)*r, y+h-r+sa1*r, - x+(1-ca3)*r, y+h-r+sa3*r, - x+(1-ca2)*r, y+h-r+sa2*r); - - if(corners&8) // BR - gfx_quads_draw_freeform( - x+w-r, y+h-r, - x+w-r+ca1*r, y+h-r+sa1*r, - x+w-r+ca3*r, y+h-r+sa3*r, - x+w-r+ca2*r, y+h-r+sa2*r); - } - - gfx_quads_drawTL(x+r, y+r, w-r*2, h-r*2); // center - gfx_quads_drawTL(x+r, y, w-r*2, r); // top - gfx_quads_drawTL(x+r, y+h-r, w-r*2, r); // bottom - gfx_quads_drawTL(x, y+r, r, h-r*2); // left - gfx_quads_drawTL(x+w-r, y+r, r, h-r*2); // right - - if(!(corners&1)) gfx_quads_drawTL(x, y, r, r); // TL - if(!(corners&2)) gfx_quads_drawTL(x+w, y, -r, r); // TR - if(!(corners&4)) gfx_quads_drawTL(x, y+h, r, -r); // BL - if(!(corners&8)) gfx_quads_drawTL(x+w, y+h, -r, -r); // BR -} - -void draw_round_rect(float x, float y, float w, float h, float r) -{ - draw_round_rect_ext(x,y,w,h,r,0xf); -} - - -static void render_player( - const obj_player_character *prev_char, - const obj_player_character *player_char, - const obj_player_info *prev_info, - const obj_player_info *player_info - ) -{ - obj_player_character prev; - obj_player_character player; - prev = *prev_char; - player = *player_char; - - obj_player_info info = *player_info; - - float intratick = client_intratick(); - - if(player.health < 0) // dont render dead players - return; - - if(info.local && config.cl_predict) - { - if(!local_character || (local_character->health < 0) || (gameobj && gameobj->game_over)) - { - } - else - { - // apply predicted results - predicted_player.write(&player); - predicted_prev_player.write(&prev); - intratick = client_intrapredtick(); - } - } - - // TODO: proper skin selection - int skin_id = client_datas[info.clientid].skin_id; //charids[info.clientid]; - //if(gametype != GAMETYPE_DM) - //skin_id = info.team*9; // 0 or 9 - - vec2 direction = get_direction(player.angle); - float angle = player.angle/256.0f; - vec2 position = mix(vec2(prev.x, prev.y), vec2(player.x, player.y), intratick); - - if(prev.health < 0) // Don't flicker from previous position - position = vec2(player.x, player.y); - - bool stationary = player.vx < 1 && player.vx > -1; - bool inair = col_check_point(player.x, player.y+16) == 0; - - // evaluate animation - float walk_time = fmod(position.x, 100.0f)/100.0f; - animstate state; - anim_eval(&data->animations[ANIM_BASE], 0, &state); - - if(inair) - anim_eval_add(&state, &data->animations[ANIM_INAIR], 0, 1.0f); // TODO: some sort of time here - else if(stationary) - anim_eval_add(&state, &data->animations[ANIM_IDLE], 0, 1.0f); // TODO: some sort of time here - else - anim_eval_add(&state, &data->animations[ANIM_WALK], walk_time, 1.0f); - - if (player.weapon == WEAPON_HAMMER) - { - float a = clamp((client_tick()-player.attacktick+intratick)/10.0f, 0.0f, 1.0f); - anim_eval_add(&state, &data->animations[ANIM_HAMMER_SWING], a, 1.0f); - } - if (player.weapon == WEAPON_NINJA) - { - float a = clamp((client_tick()-player.attacktick+intratick)/40.0f, 0.0f, 1.0f); - anim_eval_add(&state, &data->animations[ANIM_NINJA_SWING], a, 1.0f); - } - - // draw hook - if (prev.hook_state>0 && player.hook_state>0) - { - gfx_texture_set(data->images[IMAGE_GAME].id); - gfx_quads_begin(); - //gfx_quads_begin(); - - vec2 pos = position; - vec2 hook_pos = mix(vec2(prev.hook_x, prev.hook_y), vec2(player.hook_x, player.hook_y), intratick); - - float d = distance(pos, hook_pos); - vec2 dir = normalize(pos-hook_pos); - - gfx_quads_setrotation(get_angle(dir)+pi); - - // render head - select_sprite(SPRITE_HOOK_HEAD); - gfx_quads_draw(hook_pos.x, hook_pos.y, 24,16); - - // render chain - select_sprite(SPRITE_HOOK_CHAIN); - for(float f = 24; f < d; f += 24) - { - vec2 p = hook_pos + dir*f; - gfx_quads_draw(p.x, p.y,24,16); - } - - gfx_quads_setrotation(0); - gfx_quads_end(); - - render_hand(skin_id, position, normalize(hook_pos-pos), -pi/2, vec2(20, 0)); - } - - // draw gun - { - gfx_texture_set(data->images[IMAGE_GAME].id); - gfx_quads_begin(); - gfx_quads_setrotation(state.attach.angle*pi*2+angle); - - // normal weapons - int iw = clamp(player.weapon, 0, NUM_WEAPONS-1); - select_sprite(data->weapons[iw].sprite_body, direction.x < 0 ? SPRITE_FLAG_FLIP_Y : 0); - - vec2 dir = direction; - float recoil = 0.0f; - vec2 p; - if (player.weapon == WEAPON_HAMMER) - { - // Static position for hammer - p = position; - p.y += data->weapons[iw].offsety; - // if attack is under way, bash stuffs - if(direction.x < 0) - { - gfx_quads_setrotation(-pi/2-state.attach.angle*pi*2); - p.x -= data->weapons[iw].offsetx; - } - else - { - gfx_quads_setrotation(-pi/2+state.attach.angle*pi*2); - } - draw_sprite(p.x, p.y, data->weapons[iw].visual_size); - } - else if (player.weapon == WEAPON_NINJA) - { - p = position; - p.y += data->weapons[iw].offsety; - - if(direction.x < 0) - { - gfx_quads_setrotation(-pi/2-state.attach.angle*pi*2); - p.x -= data->weapons[iw].offsetx; - } - else - { - gfx_quads_setrotation(-pi/2+state.attach.angle*pi*2); - } - draw_sprite(p.x, p.y, data->weapons[iw].visual_size); - - // HADOKEN - if ((client_tick()-player.attacktick) <= (SERVER_TICK_SPEED / 6) && data->weapons[iw].nummuzzlesprites) - { - int itex = rand() % data->weapons[iw].nummuzzlesprites; - float alpha = 1.0f; - if (alpha > 0.0f && data->weapons[iw].sprite_muzzle[itex].psprite) - { - vec2 dir = vec2(player_char->x,player_char->y) - vec2(prev_char->x, prev_char->y); - dir = normalize(dir); - float hadokenangle = get_angle(dir); - gfx_quads_setrotation(hadokenangle); - //float offsety = -data->weapons[iw].muzzleoffsety; - select_sprite(data->weapons[iw].sprite_muzzle[itex].psprite, 0); - vec2 diry(-dir.y,dir.x); - p = position; - float offsetx = data->weapons[iw].muzzleoffsetx; - p -= dir * offsetx; - draw_sprite(p.x, p.y, 160.0f); - } - } - } - else - { - // TODO: should be an animation - recoil = 0; - float a = (client_tick()-player.attacktick+intratick)/5.0f; - if(a < 1) - recoil = sinf(a*pi); - p = position + dir * data->weapons[iw].offsetx - dir*recoil*10.0f; - p.y += data->weapons[iw].offsety; - draw_sprite(p.x, p.y, data->weapons[iw].visual_size); - } - - if (player.weapon == WEAPON_GUN || player.weapon == WEAPON_SHOTGUN) - { - // check if we're firing stuff - if (true)//prev.attackticks) - { - float alpha = 0.0f; - int phase1tick = (client_tick() - player.attacktick); - if (phase1tick < (data->weapons[iw].muzzleduration + 3)) - { - float t = ((((float)phase1tick) + intratick)/(float)data->weapons[iw].muzzleduration); - alpha = LERP(2.0, 0.0f, min(1.0f,max(0.0f,t))); - } - - int itex = rand() % data->weapons[iw].nummuzzlesprites; - if (alpha > 0.0f && data->weapons[iw].sprite_muzzle[itex].psprite) - { - float offsety = -data->weapons[iw].muzzleoffsety; - select_sprite(data->weapons[iw].sprite_muzzle[itex].psprite, direction.x < 0 ? SPRITE_FLAG_FLIP_Y : 0); - if(direction.x < 0) - offsety = -offsety; - - vec2 diry(-dir.y,dir.x); - vec2 muzzlepos = p + dir * data->weapons[iw].muzzleoffsetx + diry * offsety; - - draw_sprite(muzzlepos.x, muzzlepos.y, data->weapons[iw].visual_size); - /*gfx_setcolor(1.0f,1.0f,1.0f,alpha); - vec2 diry(-dir.y,dir.x); - p += dir * muzzleparams[player.weapon].offsetx + diry * offsety; - gfx_quads_draw(p.x,p.y,muzzleparams[player.weapon].sizex, muzzleparams[player.weapon].sizey);*/ - } - } - } - gfx_quads_end(); - - switch (player.weapon) - { - case WEAPON_GUN: render_hand(skin_id, p, direction, -3*pi/4, vec2(-15, 4)); break; - case WEAPON_SHOTGUN: render_hand(skin_id, p, direction, -pi/2, vec2(-5, 4)); break; - case WEAPON_ROCKET: render_hand(skin_id, p, direction, -pi/2, vec2(-4, 7)); break; - } - - } - - // render the "shadow" tee - if(info.local && config.debug) - { - vec2 ghost_position = mix(vec2(prev_char->x, prev_char->y), vec2(player_char->x, player_char->y), client_intratick()); - tee_render_info ghost = client_datas[info.clientid].skin_info; - ghost.color_body.a = 0.5f; - ghost.color_feet.a = 0.5f; - render_tee(&state, &ghost, player.emote, direction, ghost_position); // render ghost - } - - // render the tee - render_tee(&state, &client_datas[info.clientid].skin_info, player.emote, direction, position); - - if(player.state == STATE_CHATTING) - { - gfx_texture_set(data->images[IMAGE_EMOTICONS].id); - gfx_quads_begin(); - select_sprite(SPRITE_DOTDOT); - gfx_quads_draw(position.x + 24, position.y - 40, 64,64); - gfx_quads_end(); - } - - if (client_datas[info.clientid].emoticon_start != -1 && client_datas[info.clientid].emoticon_start + 2 * client_tickspeed() > client_tick()) - { - gfx_texture_set(data->images[IMAGE_EMOTICONS].id); - gfx_quads_begin(); - - int since_start = client_tick() - client_datas[info.clientid].emoticon_start; - int from_end = client_datas[info.clientid].emoticon_start + 2 * client_tickspeed() - client_tick(); - - float a = 1; - - if (from_end < client_tickspeed() / 5) - a = from_end / (client_tickspeed() / 5.0); - - float h = 1; - if (since_start < client_tickspeed() / 10) - h = since_start / (client_tickspeed() / 10.0); - - float wiggle = 0; - if (since_start < client_tickspeed() / 5) - wiggle = since_start / (client_tickspeed() / 5.0); - - float wiggle_angle = sin(5*wiggle); - - gfx_quads_setrotation(pi/6*wiggle_angle); - - gfx_setcolor(1.0f,1.0f,1.0f,a); - // client_datas::emoticon is an offset from the first emoticon - select_sprite(SPRITE_OOP + client_datas[info.clientid].emoticon); - gfx_quads_draw(position.x, position.y - 23 - 32*h, 64, 64*h); - gfx_quads_end(); - } - - // render name plate - if(!info.local && config.cl_nameplates) - { - //gfx_pretty_text_color - float a = 1; - if(config.cl_nameplates_always == 0) - a = clamp(1-powf(distance(local_target_pos, position)/200.0f,16.0f), 0.0f, 1.0f); - - const char *name = client_datas[info.clientid].name; - float tw = gfx_pretty_text_width(28.0f, name, -1); - gfx_pretty_text_color(1,1,1,a); - gfx_pretty_text(position.x-tw/2.0f, position.y-60, 28.0f, name, -1); - gfx_pretty_text_color(1,1,1,1); - } -} - -void render_moon(float x, float y) -{ - gfx_texture_set(data->images[IMAGE_MOON].id); - gfx_quads_begin(); - gfx_quads_draw(x, y, 512, 512); - gfx_quads_end(); -} - -void render_stars() -{ - struct star - { - vec2 p; - float tingle; - float rot; - float size; - }; - - static const int NUM_STARS = 100; - static star stars[NUM_STARS]; - static bool init = true; - if(init) - { - for(int i = 0; i < NUM_STARS; i++) - { - stars[i].p.x = (frandom()-0.5f)*800*3; - stars[i].p.y = (frandom()-0.5f)*600*3; - stars[i].tingle = frandom(); - stars[i].rot = frandom()*pi; - stars[i].size = 10.0f+frandom()*10.0f; - } - - init = false; - } - - int64 now = time_get(); - int64 freq = time_freq()*5; - float t = (now%freq)/(float)freq; - gfx_texture_set(data->images[IMAGE_STARS].id); - gfx_quads_begin(); - gfx_quads_setsubset(0,0,0.5f,1); - for(int i = 0; i < NUM_STARS; i++) - { - float a = fmod(t+stars[i].tingle, 1.0f); - gfx_quads_setrotation(stars[i].rot); - gfx_setcolor(1,1,1,0.25f+sinf(a*pi)*0.75f); - gfx_quads_draw(stars[i].p.x, stars[i].p.y, stars[i].size, stars[i].size); - } - - gfx_quads_end(); -} - -void render_snow() -{ - vec2 tl, br; - gfx_getscreen(&tl.x, &tl.y, &br.x, &br.y); - tl.x += 1000; // this is here to fix positions below 0,0 - tl.y += 1000; - br.x += 1000; - br.y += 1000; - - struct flake - { - vec2 p; - float tingle; - float rot; - float size; - float yspeed; - float xspeed; - }; - - static const int NUM_FLAKES = 100; - static flake flakes[NUM_FLAKES]; - static bool init = true; - - float w = br.x-tl.x; - float h = br.y-tl.y; - - if(init) - { - for(int i = 0; i < NUM_FLAKES; i++) - { - flakes[i].p.x = frandom()*w; - flakes[i].p.y = frandom()*h; - flakes[i].tingle = frandom(); - flakes[i].rot = frandom()*pi; - flakes[i].size = 50.0f+frandom()*10.0f; - flakes[i].yspeed = 50+frandom()*20.0f; - flakes[i].xspeed = flakes[i].yspeed * (0.4+frandom()*0.4f); - } - - init = false; - } - - int basex = (int)(tl.x/w); - float splitx = tl.x-basex*w; - int basey = (int)(tl.y/h); - float splity = tl.y-basey*h; - - float f = client_frametime(); - gfx_texture_set(data->images[IMAGE_SNOW].id); - gfx_quads_begin(); - for(int i = 0; i < NUM_FLAKES; i++) - { - flakes[i].p.x -= f*flakes[i].xspeed; - flakes[i].p.y += f*flakes[i].yspeed; - if(flakes[i].p.x < 0) - flakes[i].p.x += w; - if(flakes[i].p.y > h) - flakes[i].p.y -= h; - - float x = flakes[i].p.x + basex*w; - float y = flakes[i].p.y + basey*h; - - if(flakes[i].p.x < splitx) - x += w; - if(flakes[i].p.y < splity) - y += h; - - x -= 1000; - y -= 1000; - - gfx_quads_setrotation(flakes[i].rot); - gfx_quads_draw(x, y, flakes[i].size, flakes[i].size); - } - - gfx_quads_end(); -} - -void render_sun(float x, float y) -{ - vec2 pos(x, y); - - gfx_texture_set(-1); - gfx_blend_additive(); - gfx_quads_begin(); - const int rays = 10; - gfx_setcolor(1.0f,1.0f,1.0f,0.025f); - for(int r = 0; r < rays; r++) - { - float a = r/(float)rays + client_localtime()*0.025f; - float size = (1.0f/(float)rays)*0.25f; - vec2 dir0(sinf((a-size)*pi*2.0f), cosf((a-size)*pi*2.0f)); - vec2 dir1(sinf((a+size)*pi*2.0f), cosf((a+size)*pi*2.0f)); - - gfx_setcolorvertex(0, 1.0f,1.0f,1.0f,0.025f); - gfx_setcolorvertex(1, 1.0f,1.0f,1.0f,0.025f); - gfx_setcolorvertex(2, 1.0f,1.0f,1.0f,0.0f); - gfx_setcolorvertex(3, 1.0f,1.0f,1.0f,0.0f); - const float range = 1000.0f; - gfx_quads_draw_freeform( - pos.x+dir0.x, pos.y+dir0.y, - pos.x+dir1.x, pos.y+dir1.y, - pos.x+dir0.x*range, pos.y+dir0.y*range, - pos.x+dir1.x*range, pos.y+dir1.y*range); - } - gfx_quads_end(); - gfx_blend_normal(); - - gfx_texture_set(data->images[IMAGE_SUN].id); - gfx_quads_begin(); - gfx_quads_draw(pos.x, pos.y, 256, 256); - gfx_quads_end(); -} - -static vec2 emoticon_selector_mouse; - -void emoticon_selector_reset() -{ - emoticon_selector_mouse = vec2(0, 0); -} - -int emoticon_selector_render() -{ - int x, y; - inp_mouse_relative(&x, &y); - - emoticon_selector_mouse.x += x; - emoticon_selector_mouse.y += y; - - if (length(emoticon_selector_mouse) > 140) - emoticon_selector_mouse = normalize(emoticon_selector_mouse) * 140; - - float selected_angle = get_angle(emoticon_selector_mouse) + 2*pi/24; - if (selected_angle < 0) - selected_angle += 2*pi; - - bool return_now = false; - int selected_emoticon = -1; - - if (length(emoticon_selector_mouse) > 100) - selected_emoticon = (int)(selected_angle / (2*pi) * 12.0f); - - if(!inp_key_pressed(config.key_emoticon)) - { - return_now = true; - emoticon_selector_active = false; - } - - RECT screen = *ui2_screen(); - - gfx_mapscreen(screen.x, screen.y, screen.w, screen.h); - - gfx_blend_normal(); - - gfx_texture_set(-1); - gfx_quads_begin(); - gfx_setcolor(0,0,0,0.3f); - draw_circle(screen.w/2, screen.h/2, 160, 64); - gfx_quads_end(); - - gfx_texture_set(data->images[IMAGE_EMOTICONS].id); - gfx_quads_begin(); - - for (int i = 0; i < 12; i++) - { - float angle = 2*pi*i/12.0; - if (angle > pi) - angle -= 2*pi; - - bool selected = selected_emoticon == i; - - float size = selected ? 96 : 64; - - float nudge_x = 120 * cos(angle); - float nudge_y = 120 * sin(angle); - select_sprite(SPRITE_OOP + i); - gfx_quads_draw(screen.w/2 + nudge_x, screen.h/2 + nudge_y, size, size); - } - - gfx_quads_end(); - - gfx_texture_set(data->images[IMAGE_CURSOR].id); - gfx_quads_begin(); - gfx_setcolor(1,1,1,1); - gfx_quads_drawTL(emoticon_selector_mouse.x+screen.w/2,emoticon_selector_mouse.y+screen.h/2,24,24); - gfx_quads_end(); - - return return_now ? selected_emoticon : -1; -} - -void render_goals(float x, float y, float w) -{ - float h = 50.0f; - - gfx_blend_normal(); - gfx_texture_set(-1); - gfx_quads_begin(); - gfx_setcolor(0,0,0,0.5f); - draw_round_rect(x-10.f, y-10.f, w, h, 10.0f); - gfx_quads_end(); - - // render goals - //y = ystart+h-54; - if(gameobj && gameobj->time_limit) - { - char buf[64]; - sprintf(buf, "Time Limit: %d min", gameobj->time_limit); - gfx_pretty_text(x+w/2, y, 32, buf, -1); - } - if(gameobj && gameobj->score_limit) - { - char buf[64]; - sprintf(buf, "Score Limit: %d", gameobj->score_limit); - gfx_pretty_text(x+40, y, 32, buf, -1); - } - -} - -void render_scoreboard(float x, float y, float w, int team, const char *title) -{ - //float w = 550.0f; - //float x = width/2-w/2; - //; - //float y = ystart; - //float w = 550.0f; - - animstate idlestate; - anim_eval(&data->animations[ANIM_BASE], 0, &idlestate); - anim_eval_add(&idlestate, &data->animations[ANIM_IDLE], 0, 1.0f); - - //float ystart = y; - float h = 600.0f; - - gfx_blend_normal(); - gfx_texture_set(-1); - gfx_quads_begin(); - gfx_setcolor(0,0,0,0.5f); - draw_round_rect(x-10.f, y-10.f, w, h, 40.0f); - gfx_quads_end(); - - // render title - if(!title) - { - if(gameobj->game_over) - title = "Game Over"; - else - title = "Score Board"; - } - - float tw = gfx_pretty_text_width( 64, title, -1); - - if(team == -1) - { - gfx_pretty_text(x+w/2-tw/2, y, 64, title, -1); - } - else - { - gfx_pretty_text(x+10, y, 64, title, -1); - - if(gameobj) - { - char buf[128]; - sprintf(buf, "%d", gameobj->teamscore[team&1]); - tw = gfx_pretty_text_width(64, buf, -1); - gfx_pretty_text(x+w-tw-40, y, 64, buf, -1); - } - } - - y += 64.0f; - - /* - if(team) - { - char buf[128]; - sprintf(buf, "%4d", gameobj->teamscore[team&1]); - gfx_pretty_text(x+w/2-tw/2, y, 32, buf, -1); - }*/ - - - // find players - const obj_player_info *players[MAX_CLIENTS] = {0}; - int num_players = 0; - for(int i = 0; i < snap_num_items(SNAP_CURRENT); i++) - { - SNAP_ITEM item; - const void *data = snap_get_item(SNAP_CURRENT, i, &item); - - if(item.type == OBJTYPE_PLAYER_INFO) - { - players[num_players] = (const obj_player_info *)data; - num_players++; - } - } - - // sort players - for(int k = 0; k < num_players; k++) // ffs, bubblesort - { - for(int i = 0; i < num_players-k-1; i++) - { - if(players[i]->score < players[i+1]->score) - { - const obj_player_info *tmp = players[i]; - players[i] = players[i+1]; - players[i+1] = tmp; - } - } - } - - // render headlines - gfx_pretty_text(x+10, y, 32, "Score", -1); - gfx_pretty_text(x+125, y, 32, "Name", -1); - gfx_pretty_text(x+w-70, y, 32, "Ping", -1); - y += 38.0f; - - // render player scores - for(int i = 0; i < num_players; i++) - { - const obj_player_info *info = players[i]; - - // make sure that we render the correct team - if(team != -1 && info->team != team) - continue; - - char buf[128]; - float font_size = 46.0f; - if(info->local) - { - // background so it's easy to find the local player - gfx_texture_set(-1); - gfx_quads_begin(); - gfx_setcolor(1,1,1,0.25f); - draw_round_rect(x, y, w-20, 48, 20.0f); - gfx_quads_end(); - } - - sprintf(buf, "%4d", info->score); - gfx_pretty_text(x+60-gfx_pretty_text_width(font_size,buf,-1), y, font_size, buf, -1); - gfx_pretty_text(x+128, y, font_size, client_datas[info->clientid].name, -1); - - sprintf(buf, "%4d", info->latency); - float tw = gfx_pretty_text_width(font_size, buf, -1); - gfx_pretty_text(x+w-tw-35, y, font_size, buf, -1); - - // render avatar - render_tee(&idlestate, &client_datas[info->clientid].skin_info, EMOTE_NORMAL, vec2(1,0), vec2(x+90, y+28)); - y += 50.0f; - } -} - -void mapscreen_to_world(float center_x, float center_y, float zoom) -{ - RECT screen = *ui2_screen(); - - const float default_zoom = 1.5f; - float width = screen.w*default_zoom*zoom; - float height = screen.h*default_zoom*zoom; - gfx_mapscreen(center_x-width/2, center_y-height/2, center_x+width/2, center_y+height/2); -} - -// renders the complete game world -void render_world(float center_x, float center_y, float zoom) -{ - mapscreen_to_world(center_x, center_y, zoom); - //gfx_mapscreen(center_x-width/2, center_y-height/2, center_x+width/2, center_y+height/2); - - // render background environment - int theme_id = 0; - mapres_theme *t = (mapres_theme *)map_find_item(MAPRES_TEMP_THEME, 0); - if(t) - theme_id = t->id; - - if(config.gfx_high_detail) - { - if(theme_id == 1) - { - // Winter night - gfx_mapscreen(0,0,1,1); - gfx_texture_set(-1); - gfx_quads_begin(); - vec4 top(0x11/(float)0xff, 0x1a/(float)0xff, 0x21/(float)0xff, 1.0f); - vec4 bottom(0x2a/(float)0xff, 0x40/(float)0xff, 0x52/(float)0xff, 1.0f); - gfx_setcolorvertex(0, top.r, top.g, top.b, top.a); - gfx_setcolorvertex(1, top.r, top.g, top.b, top.a); - gfx_setcolorvertex(2, bottom.r, bottom.g, bottom.b, bottom.a); - gfx_setcolorvertex(3, bottom.r, bottom.g, bottom.b, bottom.a); - gfx_quads_drawTL(0, 0, 1, 1); - gfx_quads_end(); - - mapscreen_to_world(center_x*0.1f, center_y*0.1f, zoom); - render_stars(); - - mapscreen_to_world(center_x, center_y, zoom); - - render_moon(center_x*0.8f, center_y*0.8f); - - mapscreen_to_world(center_x, center_y, zoom); - } - else - { - // Summer day - render_sun(20+center_x*0.6f, 20+center_y*0.6f); - - // draw clouds - static vec2 cloud_pos[6] = {vec2(-500,0),vec2(-500,200),vec2(-500,400)}; - static float cloud_speed[6] = {30, 20, 10}; - static int cloud_sprites[6] = {SPRITE_CLOUD1, SPRITE_CLOUD2, SPRITE_CLOUD3}; - - gfx_texture_set(data->images[IMAGE_CLOUDS].id); - gfx_quads_begin(); - for(int i = 0; i < 3; i++) - { - float parallax_amount = 0.55f; - select_sprite(cloud_sprites[i]); - draw_sprite((cloud_pos[i].x+fmod(client_localtime()*cloud_speed[i]+i*100.0f, 3000.0f))+center_x*parallax_amount, - cloud_pos[i].y+center_y*parallax_amount, 300); - } - gfx_quads_end(); - - // draw backdrop - gfx_texture_set(data->images[IMAGE_BACKDROP].id); - gfx_quads_begin(); - float parallax_amount = 0.25f; - for(int x = -1; x < 3; x++) - gfx_quads_drawTL(1024*x+center_x*parallax_amount, (center_y)*parallax_amount+150+512, 1024, 512); - gfx_quads_end(); - } - } - - // render background tilemaps - tilemap_render(32.0f, 0); - - // render items - { - int num = snap_num_items(SNAP_CURRENT); - for(int i = 0; i < num; i++) - { - SNAP_ITEM item; - const void *data = snap_get_item(SNAP_CURRENT, i, &item); - - if(item.type == OBJTYPE_PROJECTILE) - { - const void *prev = snap_find_item(SNAP_PREV, item.type, item.id); - if(prev) - render_projectile((const obj_projectile *)prev, (const obj_projectile *)data, item.id); - } - else if(item.type == OBJTYPE_POWERUP) - { - const void *prev = snap_find_item(SNAP_PREV, item.type, item.id); - if(prev) - render_powerup((const obj_powerup *)prev, (const obj_powerup *)data); - } - else if(item.type == OBJTYPE_FLAG) - { - const void *prev = snap_find_item(SNAP_PREV, item.type, item.id); - if (prev) - render_flag((const obj_flag *)prev, (const obj_flag *)data); - } - } - } - - // render players above all - { - int num = snap_num_items(SNAP_CURRENT); - for(int i = 0; i < num; i++) - { - SNAP_ITEM item; - const void *data = snap_get_item(SNAP_CURRENT, i, &item); - - if(item.type == OBJTYPE_PLAYER_CHARACTER) - { - const void *prev = snap_find_item(SNAP_PREV, item.type, item.id); - const void *prev_info = snap_find_item(SNAP_PREV, OBJTYPE_PLAYER_INFO, item.id); - const void *info = snap_find_item(SNAP_CURRENT, OBJTYPE_PLAYER_INFO, item.id); - if(prev && prev_info && info) - { - client_datas[((const obj_player_info *)info)->clientid].team = ((const obj_player_info *)info)->team; - render_player( - (const obj_player_character *)prev, - (const obj_player_character *)data, - (const obj_player_info *)prev_info, - (const obj_player_info *)info - ); - } - } - } - } - - // render particles - temp_system.update(client_frametime()); - temp_system.render(); - - // render foreground tilemaps - tilemap_render(32.0f, 1); - - // render front environment effects - if(config.gfx_high_detail) - { - if(theme_id == 1) - { - //mapscreen_to_world(center_x, center_y, zoom); - render_snow(); - } - } - - // render damage indications - damageind.render(); -} - -static void do_input(int *v, int key) -{ - *v += inp_key_presses(key) + inp_key_releases(key); - if((*v&1) != inp_key_state(key)) - (*v)++; - *v &= INPUT_STATE_MASK; -} - -void render_game() -{ - float width = 400*3.0f; - float height = 300*3.0f; - - bool spectate = false; - - if(config.cl_predict) - { - if(!local_character || (local_character->health < 0) || (gameobj && gameobj->game_over)) - { - // don't use predicted - } - else - local_character_pos = mix(predicted_prev_player.pos, predicted_player.pos, client_intrapredtick()); - } - else if(local_character && local_prev_character) - { - local_character_pos = mix( - vec2(local_prev_character->x, local_prev_character->y), - vec2(local_character->x, local_character->y), client_intratick()); - } - - if(local_info && local_info->team == -1) - spectate = true; - - animstate idlestate; - anim_eval(&data->animations[ANIM_BASE], 0, &idlestate); - anim_eval_add(&idlestate, &data->animations[ANIM_IDLE], 0, 1.0f); - - if (inp_key_down(KEY_ESC)) - { - if (chat_mode) - chat_mode = CHATMODE_NONE; - else - { - menu_active = !menu_active; - if(menu_active) - menu_game_active = true; - } - } - - // make sure to send our info again if the menu closes - static bool menu_was_active = false; - if(menu_active) - menu_was_active = true; - else if(menu_was_active) - { - send_info(false); - menu_was_active = false; - } - - // handle chat input - if (!menu_active) - { - if(chat_mode != CHATMODE_NONE) - { - if(inp_key_down(KEY_ENTER)) - { - // send message - if(chat_input_len) - { - if(chat_mode == CHATMODE_CONSOLE) - config_set(chat_input); - else if(chat_mode == CHATMODE_REMOTECONSOLE) - client_rcon(chat_input); - else - { - // send chat message - msg_pack_start(MSG_SAY, MSGFLAG_VITAL); - if(chat_mode == CHATMODE_ALL) - msg_pack_int(0); - else - msg_pack_int(1); - msg_pack_string(chat_input, 512); - msg_pack_end(); - client_send_msg(); - } - } - - chat_mode = CHATMODE_NONE; - } - - int c = inp_last_char(); - int k = inp_last_key(); - - if (!(c >= 0 && c < 32)) - { - if (chat_input_len < sizeof(chat_input) - 1) - { - chat_input[chat_input_len] = c; - chat_input[chat_input_len+1] = 0; - chat_input_len++; - } - } - - if(k == KEY_BACKSPACE) - { - if(chat_input_len > 0) - { - chat_input[chat_input_len-1] = 0; - chat_input_len--; - } - } - - } - else - { - if(chat_mode == CHATMODE_NONE) - { - if(inp_key_down(config.key_chat)) - chat_mode = CHATMODE_ALL; - - if(inp_key_down(config.key_teamchat)) - chat_mode = CHATMODE_TEAM; - - if(inp_key_down(config.key_console)) - chat_mode = CHATMODE_CONSOLE; - - if(inp_key_down(config.key_remoteconsole)) - chat_mode = CHATMODE_REMOTECONSOLE; - - if(chat_mode != CHATMODE_NONE) - { - mem_zero(chat_input, sizeof(chat_input)); - chat_input_len = 0; - } - } - } - } - - if (!menu_active) - inp_clear(); - - // fetch new input - if(!menu_active && !emoticon_selector_active) - { - int x, y; - inp_mouse_relative(&x, &y); - mouse_pos += vec2(x, y); - if(!spectate) - { - float l = length(mouse_pos); - if(l > 600.0f) - mouse_pos = normalize(mouse_pos)*600.0f; - } - } - - // set listner pos - if(spectate) - { - local_target_pos = mouse_pos; - snd_set_listener_pos(mouse_pos.x, mouse_pos.y); - } - else - { - local_target_pos = local_character_pos + mouse_pos; - snd_set_listener_pos(local_character_pos.x, local_character_pos.y); - } - - // snap input - { - static player_input input = {0}; - - input.target_x = (int)mouse_pos.x; - input.target_y = (int)mouse_pos.y; - - if(chat_mode != CHATMODE_NONE) - input.state = STATE_CHATTING; - else if(menu_active) - input.state = STATE_IN_MENU; - else - { - input.state = STATE_PLAYING; - input.left = inp_key_state(config.key_move_left); - input.right = inp_key_state(config.key_move_right); - input.hook = inp_key_state(config.key_hook); - input.jump = inp_key_state(config.key_jump); - - if(!emoticon_selector_active) - do_input(&input.fire, config.key_fire); - - // weapon selection - do_input(&input.next_weapon, config.key_next_weapon); - do_input(&input.prev_weapon, config.key_prev_weapon); - - if(inp_key_presses(config.key_next_weapon) || inp_key_presses(config.key_prev_weapon)) - input.wanted_weapon = 0; - else if (config.cl_autoswitch_weapons && picked_up_weapon) - { - input.wanted_weapon = picked_up_weapon; - } - else - { - if(inp_key_presses(config.key_weapon1)) input.wanted_weapon = 1; - if(inp_key_presses(config.key_weapon2)) input.wanted_weapon = 2; - if(inp_key_presses(config.key_weapon3)) input.wanted_weapon = 3; - if(inp_key_presses(config.key_weapon4)) input.wanted_weapon = 4; - if(inp_key_presses(config.key_weapon5)) input.wanted_weapon = 5; - if(inp_key_presses(config.key_weapon6)) input.wanted_weapon = 6; - } - - picked_up_weapon = 0; - } - - // stress testing - if(config.dbg_stress) - { - float t = client_localtime(); - mem_zero(&input, sizeof(input)); - - input.left = ((int)t/2)&1; - input.right = ((int)t/2+1)&1; - input.jump = ((int)t); - input.fire = ((int)(t*10)); - input.hook = ((int)(t*2))&1; - input.wanted_weapon = ((int)t)%NUM_WEAPONS; - input.target_x = (int)(sinf(t*3)*100.0f); - input.target_y = (int)(cosf(t*3)*100.0f); - } - - snap_input(&input, sizeof(input)); - } - - // center at char but can be moved when mouse is far away - float offx = 0, offy = 0; - if (config.cl_dynamic_camera) - { - int deadzone = 300; - if(mouse_pos.x > deadzone) offx = mouse_pos.x-deadzone; - if(mouse_pos.x <-deadzone) offx = mouse_pos.x+deadzone; - if(mouse_pos.y > deadzone) offy = mouse_pos.y-deadzone; - if(mouse_pos.y <-deadzone) offy = mouse_pos.y+deadzone; - offx = offx*2/3; - offy = offy*2/3; - } - - // render the world - gfx_clear(0.65f,0.78f,0.9f); - if(spectate) - render_world(mouse_pos.x, mouse_pos.y, 1.0f); - else - { - render_world(local_character_pos.x+offx, local_character_pos.y+offy, 1.0f); - - // draw screen box - if(0) - { - gfx_texture_set(-1); - gfx_blend_normal(); - gfx_lines_begin(); - float cx = local_character_pos.x+offx; - float cy = local_character_pos.y+offy; - float w = 400*3/2; - float h = 300*3/2; - gfx_lines_draw(cx-w,cy-h,cx+w,cy-h); - gfx_lines_draw(cx+w,cy-h,cx+w,cy+h); - gfx_lines_draw(cx+w,cy+h,cx-w,cy+h); - gfx_lines_draw(cx-w,cy+h,cx-w,cy-h); - gfx_lines_end(); - } - } - - - // pseudo format - // ZOOM ZOOM - float zoom = 3.0; - if(local_character) - cl_effects.getzoom(client_tick(), client_intratick(), local_character); - - // DEBUG TESTING - if(zoom > 3.01f) - { - gfx_clear_mask(0); - - gfx_texture_set(-1); - gfx_blend_normal(); - - gfx_mask_op(MASK_NONE, 1); - - gfx_quads_begin(); - gfx_setcolor(0.65f,0.78f,0.9f,1.0f); - - float fov; - if (zoom > 3.01f) - fov = pi * (zoom - 3.0f) / 6.0f; - else - fov = pi / 6.0f; - - float fade = 0.7f; - - - float a = get_angle(normalize(vec2(mouse_pos.x, mouse_pos.y))); - vec2 d = get_dir(a); - vec2 d0 = get_dir(a-fov/2.0f); - vec2 d1 = get_dir(a+fov/2.0f); - - vec2 cd0 = get_dir(a-(fov*fade)/2.0f); // center direction - vec2 cd1 = get_dir(a+(fov*fade)/2.0f); - - vec2 p0n = local_character_pos + d0*32.0f; - vec2 p1n = local_character_pos + d1*32.0f; - vec2 p0f = local_character_pos + d0*1000.0f; - vec2 p1f = local_character_pos + d1*1000.0f; - - vec2 cn = local_character_pos + d*32.0f; - vec2 cf = local_character_pos + d*1000.0f; - - vec2 cp0n = local_character_pos + cd0*32.0f; - vec2 cp0f = local_character_pos + cd0*1000.0f; - vec2 cp1n = local_character_pos + cd1*32.0f; - vec2 cp1f = local_character_pos + cd1*1000.0f; - - gfx_quads_draw_freeform( - p0n.x,p0n.y, - p1n.x,p1n.y, - p0f.x,p0f.y, - p1f.x,p1f.y); - gfx_quads_end(); - - gfx_mask_op(MASK_SET, 0); - - render_world(local_character_pos.x+offx, local_character_pos.y+offy, 2.0f); - - gfx_mask_op(MASK_NONE, 0); - - mapscreen_to_world(local_character_pos.x+offx, local_character_pos.y+offy, 1.0f); - - gfx_texture_set(-1); - gfx_blend_normal(); - gfx_quads_begin(); - gfx_setcolor(0.5f,0.9f,0.5f,0.25f); - float r=0.5f, g=1.0f, b=0.5f; - float r2=r*0.25f, g2=g*0.25f, b2=b*0.25f; - - gfx_setcolor(r,g,b,0.2f); - gfx_quads_draw_freeform( - cn.x,cn.y, - cn.x,cn.y, - cp0f.x,cp0f.y, - cp1f.x,cp1f.y); - - gfx_setcolorvertex(0, r, g, b, 0.2f); - gfx_setcolorvertex(1, r2, g2, b2, 0.9f); - gfx_setcolorvertex(2, r, g, b, 0.2f); - gfx_setcolorvertex(3, r2, g2, b2, 0.9f); - gfx_quads_draw_freeform( - cn.x,cn.y, - p0n.x,p0n.y, - cp0f.x,cp0f.y, - p0f.x,p0f.y); - - gfx_quads_draw_freeform( - cn.x,cn.y, - p1n.x,p1n.y, - cp1f.x,cp1f.y, - p1f.x,p1f.y); - - gfx_quads_end(); - } - - if(local_character && !spectate && !(gameobj && gameobj->game_over)) - { - gfx_texture_set(data->images[IMAGE_GAME].id); - gfx_quads_begin(); - - // render cursor - if (!menu_active && !emoticon_selector_active) - { - select_sprite(data->weapons[local_character->weapon%data->num_weapons].sprite_cursor); - float cursorsize = 64; - draw_sprite(local_target_pos.x, local_target_pos.y, cursorsize); - } - - float x = 5; - float y = 5; - - // render ammo count - // render gui stuff - gfx_quads_end(); - gfx_quads_begin(); - gfx_mapscreen(0,0,400,300); - // if weaponstage is active, put a "glow" around the stage ammo - select_sprite(SPRITE_TEE_BODY); - for (int i = 0; i < local_character->weaponstage; i++) - gfx_quads_drawTL(x+local_character->ammocount * 12 -i*12, y+22, 11, 11); - select_sprite(data->weapons[local_character->weapon%data->num_weapons].sprite_proj); - for (int i = 0; i < local_character->ammocount; i++) - gfx_quads_drawTL(x+i*12,y+24,10,10); - - gfx_quads_end(); - - gfx_texture_set(data->images[IMAGE_GAME].id); - gfx_quads_begin(); - int h = 0; - - // render health - select_sprite(SPRITE_HEALTH_FULL); - for(; h < local_character->health; h++) - gfx_quads_drawTL(x+h*12,y,10,10); - - select_sprite(SPRITE_HEALTH_EMPTY); - for(; h < 10; h++) - gfx_quads_drawTL(x+h*12,y,10,10); - - // render armor meter - h = 0; - select_sprite(SPRITE_ARMOR_FULL); - for(; h < local_character->armor; h++) - gfx_quads_drawTL(x+h*12,y+12,10,10); - - select_sprite(SPRITE_ARMOR_EMPTY); - for(; h < 10; h++) - gfx_quads_drawTL(x+h*12,y+12,10,10); - gfx_quads_end(); - } - - // render kill messages - { - gfx_mapscreen(0, 0, width*1.5f, height*1.5f); - float startx = width*1.5f-10.0f; - float y = 20.0f; - - for(int i = 0; i < killmsg_max; i++) - { - - int r = (killmsg_current+i+1)%killmsg_max; - if(client_tick() > killmsgs[r].tick+50*10) - continue; - - float font_size = 48.0f; - float killername_w = gfx_pretty_text_width(font_size, client_datas[killmsgs[r].killer].name, -1); - float victimname_w = gfx_pretty_text_width(font_size, client_datas[killmsgs[r].victim].name, -1); - - float x = startx; - - // render victim name - x -= victimname_w; - gfx_pretty_text(x, y, font_size, client_datas[killmsgs[r].victim].name, -1); - - // render victim tee - x -= 24.0f; - - if(gameobj && gameobj->gametype == GAMETYPE_CTF) - { - if(killmsgs[r].mode_special&1) - { - gfx_blend_normal(); - gfx_texture_set(data->images[IMAGE_GAME].id); - gfx_quads_begin(); - - if(client_datas[killmsgs[r].victim].team == 0) select_sprite(SPRITE_FLAG_BLUE); - else select_sprite(SPRITE_FLAG_RED); - - float size = 56.0f; - gfx_quads_drawTL(x, y-16, size/2, size); - gfx_quads_end(); - } - } - - render_tee(&idlestate, &client_datas[killmsgs[r].victim].skin_info, EMOTE_PAIN, vec2(-1,0), vec2(x, y+28)); - x -= 32.0f; - - // render weapon - x -= 44.0f; - if (killmsgs[r].weapon >= 0) - { - gfx_texture_set(data->images[IMAGE_GAME].id); - gfx_quads_begin(); - select_sprite(data->weapons[killmsgs[r].weapon].sprite_body); - draw_sprite(x, y+28, 96); - gfx_quads_end(); - } - x -= 52.0f; - - if(killmsgs[r].victim != killmsgs[r].killer) - { - if(gameobj && gameobj->gametype == GAMETYPE_CTF) - { - if(killmsgs[r].mode_special&2) - { - gfx_blend_normal(); - gfx_texture_set(data->images[IMAGE_GAME].id); - gfx_quads_begin(); - - if(client_datas[killmsgs[r].killer].team == 0) select_sprite(SPRITE_FLAG_BLUE, SPRITE_FLAG_FLIP_X); - else select_sprite(SPRITE_FLAG_RED, SPRITE_FLAG_FLIP_X); - - float size = 56.0f; - gfx_quads_drawTL(x-56, y-16, size/2, size); - gfx_quads_end(); - } - } - - // render killer tee - x -= 24.0f; - render_tee(&idlestate, &client_datas[killmsgs[r].killer].skin_info, EMOTE_ANGRY, vec2(1,0), vec2(x, y+28)); - x -= 32.0f; - - // render killer name - x -= killername_w; - gfx_pretty_text(x, y, font_size, client_datas[killmsgs[r].killer].name, -1); - } - - y += 44; - } - } - - // render chat - { - gfx_mapscreen(0,0,400,300); - float x = 10.0f; - float y = 300.0f-50.0f; - float starty = -1; - if(chat_mode != CHATMODE_NONE) - { - // render chat input - char buf[sizeof(chat_input)+16]; - if(chat_mode == CHATMODE_ALL) - sprintf(buf, "All: %s_", chat_input); - else if(chat_mode == CHATMODE_TEAM) - sprintf(buf, "Team: %s_", chat_input); - else if(chat_mode == CHATMODE_CONSOLE) - sprintf(buf, "Console: %s_", chat_input); - else if(chat_mode == CHATMODE_REMOTECONSOLE) - sprintf(buf, "Rcon: %s_", chat_input); - else - sprintf(buf, "Chat: %s_", chat_input); - gfx_pretty_text(x, y, 10.0f, buf, 380); - starty = y; - } - - y -= 10; - - int i; - for(i = 0; i < chat_max_lines; i++) - { - int r = ((chat_current_line-i)+chat_max_lines)%chat_max_lines; - if(client_tick() > chat_lines[r].tick+50*15) - break; - - int lines = int(gfx_pretty_text_width(10, chat_lines[r].text, -1)) / 380 + 1; - - gfx_pretty_text_color(1,1,1,1); - if(chat_lines[r].client_id == -1) - gfx_pretty_text_color(1,1,0.5f,1); // system - else if(chat_lines[r].team) - gfx_pretty_text_color(0.5f,1,0.5f,1); // team message - - gfx_pretty_text(x, y - 8 * (lines - 1), 10, chat_lines[r].text, 380); - y -= 8 * lines; - } - - gfx_pretty_text_color(1,1,1,1); - } - - // render goals - if(gameobj) - { - gametype = gameobj->gametype; - gfx_mapscreen(0,0,400,300); - if(!gameobj->sudden_death) - { - char buf[32]; - int time = 0; - if(gameobj->time_limit) - { - time = gameobj->time_limit*60 - ((client_tick()-gameobj->round_start_tick)/client_tickspeed()); - - if(gameobj->game_over) - time = 0; - } - else - time = (client_tick()-gameobj->round_start_tick)/client_tickspeed(); - - sprintf(buf, "%d:%02d", time /60, time %60); - float w = gfx_pretty_text_width(16, buf, -1); - gfx_pretty_text(200-w/2, 2, 16, buf, -1); - } - - if(gameobj->sudden_death) - { - const char *text = "Sudden Death"; - float w = gfx_pretty_text_width(16, text, -1); - gfx_pretty_text(200-w/2, 2, 16, text, -1); - } - - // render small score hud - if(!(gameobj && gameobj->game_over) && (gametype == GAMETYPE_TDM || gametype == GAMETYPE_CTF)) - { - for(int t = 0; t < 2; t++) - { - gfx_blend_normal(); - gfx_texture_set(-1); - gfx_quads_begin(); - if(t == 0) - gfx_setcolor(1,0,0,0.25f); - else - gfx_setcolor(0,0,1,0.25f); - draw_round_rect(400-40, 300-40-15+t*20, 50, 18, 5.0f); - gfx_quads_end(); - - char buf[32]; - sprintf(buf, "%d", gameobj->teamscore[t]); - float w = gfx_pretty_text_width(14, buf, -1); - - if(gametype == GAMETYPE_CTF) - { - gfx_pretty_text(400-20-w/2+5, 300-40-15+t*20+2, 14, buf, -1); - if(flags[t]) - { - if(flags[t]->carried_by == -2 || (flags[t]->carried_by == -1 && ((client_tick()/10)&1))) - { - gfx_blend_normal(); - gfx_texture_set(data->images[IMAGE_GAME].id); - gfx_quads_begin(); - - if(t == 0) select_sprite(SPRITE_FLAG_RED); - else select_sprite(SPRITE_FLAG_BLUE); - - float size = 16; - gfx_quads_drawTL(400-40+5, 300-40-15+t*20+1, size/2, size); - gfx_quads_end(); - } - else if(flags[t]->carried_by >= 0) - { - int id = flags[t]->carried_by%MAX_CLIENTS; - const char *name = client_datas[id].name; - float w = gfx_pretty_text_width(10, name, -1); - gfx_pretty_text(400-40-5-w, 300-40-15+t*20+2, 10, name, -1); - tee_render_info info = client_datas[id].skin_info; - info.size = 18.0f; - - render_tee(&idlestate, &info, EMOTE_NORMAL, vec2(1,0), - vec2(400-40+10, 300-40-15+9+t*20+1)); - } - } - } - else - gfx_pretty_text(400-20-w/2, 300-40-15+t*20+2, 14, buf, -1); - } - } - - // render warmup timer - if(gameobj->warmup) - { - char buf[256]; - float w = gfx_pretty_text_width(24, "Warmup", -1); - gfx_pretty_text(200+-w/2, 50, 24, "Warmup", -1); - - int seconds = gameobj->warmup/SERVER_TICK_SPEED; - if(seconds < 5) - sprintf(buf, "%d.%d", seconds, (gameobj->warmup*10/SERVER_TICK_SPEED)%10); - else - sprintf(buf, "%d", seconds); - w = gfx_pretty_text_width(24, buf, -1); - gfx_pretty_text(200+-w/2, 75, 24, buf, -1); - } - } - - if (menu_active) - { - modmenu_render(); - return; - } - - if(chat_mode == CHATMODE_NONE && !menu_active && !spectate) - { - if(!emoticon_selector_active && inp_key_pressed(config.key_emoticon)) - { - emoticon_selector_active = true; - emoticon_selector_reset(); - } - } - else - emoticon_selector_active = false; - - if(emoticon_selector_active) - { - int emoticon = emoticon_selector_render(); - if (emoticon != -1) - { - send_emoticon(emoticon); - emoticon_selector_active = false; - } - } - - if(client_connection_problems()) - { - gfx_mapscreen(0, 0, 400, 300); - const char *text = "Connection Problems..."; - float w = gfx_pretty_text_width(24, text, -1); - gfx_pretty_text(200-w/2, 50, 24, text, -1); - } - - // render score board - if(inp_key_pressed(KEY_TAB) || // user requested - (!spectate && (!local_character || local_character->health < 0)) || // not spectating and is dead - (gameobj && gameobj->game_over) // game over - ) - { - gfx_mapscreen(0, 0, width, height); - - float w = 550.0f; - - if (gameobj && gameobj->gametype == GAMETYPE_DM) - { - render_scoreboard(width/2-w/2, 150.0f, w, -1, 0); - //render_scoreboard(gameobj, 0, 0, -1, 0); - } - else - { - - if(gameobj && gameobj->game_over) - { - const char *text = "DRAW!"; - if(gameobj->teamscore[0] > gameobj->teamscore[1]) - text = "Red Team Wins!"; - else if(gameobj->teamscore[1] > gameobj->teamscore[0]) - text = "Blue Team Wins!"; - - float w = gfx_pretty_text_width(92.0f, text, -1); - gfx_pretty_text(width/2-w/2, 45, 92.0f, text, -1); - } - - render_scoreboard(width/2-w-20, 150.0f, w, 0, "Red Team"); - render_scoreboard(width/2 + 20, 150.0f, w, 1, "Blue Team"); - } - - render_goals(width/2-w/2, 150+600+25, w); - - } -} - -extern "C" void modc_render() -{ - // this should be moved around abit - if(client_state() == CLIENTSTATE_ONLINE) - { - render_game(); - - // handle team switching - // TODO: FUGLY!!! - if(config.cl_team != -10) - { - msg_pack_start(MSG_SETTEAM, MSGFLAG_VITAL); - msg_pack_int(config.cl_team); - msg_pack_end(); - client_send_msg(); - } - } - else // if (client_state() != CLIENTSTATE_CONNECTING && client_state() != CLIENTSTATE_LOADING) - { - modmenu_render(); - } - - // - config.cl_team = -10; -} - - -void menu_do_disconnected(); -void menu_do_connecting(); -void menu_do_connected(); - -extern "C" void modc_statechange(int state, int old) -{ - clear_object_pointers(); - - if(state == CLIENTSTATE_OFFLINE) - { - menu_do_disconnected(); - menu_game_active = false; - } - else if(state == CLIENTSTATE_CONNECTING) - menu_do_connecting(); - else if (state == CLIENTSTATE_ONLINE) - { - menu_active = false; - menu_game_active = true; - menu_do_connected(); - } -} - -extern "C" void modc_message(int msg) -{ - if(msg == MSG_CHAT) - { - int cid = msg_unpack_int(); - int team = msg_unpack_int(); - const char *message = msg_unpack_string(); - dbg_msg("message", "chat cid=%d team=%d msg='%s'", cid, team, message); - chat_add_line(cid, team, message); - - if(cid >= 0) - snd_play(CHN_GUI, data->sounds[SOUND_CHAT_CLIENT].sounds[0].id, 0); - else - snd_play(CHN_GUI, data->sounds[SOUND_CHAT_SERVER].sounds[0].id, 0); - } - else if(msg == MSG_SETINFO) - { - int cid = msg_unpack_int(); - const char *name = msg_unpack_string(); - const char *skinname = msg_unpack_string(); - - strncpy(client_datas[cid].name, name, 64); - strncpy(client_datas[cid].skin_name, skinname, 64); - - int use_custom_color = msg_unpack_int(); - client_datas[cid].skin_info.color_body = skin_get_color(msg_unpack_int()); - client_datas[cid].skin_info.color_feet = skin_get_color(msg_unpack_int()); - client_datas[cid].skin_info.size = 64; - - // find new skin - client_datas[cid].skin_id = skin_find(client_datas[cid].skin_name); - if(client_datas[cid].skin_id < 0) - client_datas[cid].skin_id = 0; - - if(use_custom_color) - client_datas[cid].skin_info.texture = skin_get(client_datas[cid].skin_id)->color_texture; - else - { - client_datas[cid].skin_info.texture = skin_get(client_datas[cid].skin_id)->org_texture; - client_datas[cid].skin_info.color_body = vec4(1,1,1,1); - client_datas[cid].skin_info.color_feet = vec4(1,1,1,1); - } - } - else if(msg == MSG_WEAPON_PICKUP) - { - int weapon = msg_unpack_int(); - - picked_up_weapon = weapon+1; - } - else if(msg == MSG_READY_TO_ENTER) - { - client_entergame(); - } - else if(msg == MSG_KILLMSG) - { - killmsg_current = (killmsg_current+1)%killmsg_max; - killmsgs[killmsg_current].killer = msg_unpack_int(); - killmsgs[killmsg_current].victim = msg_unpack_int(); - killmsgs[killmsg_current].weapon = msg_unpack_int(); - killmsgs[killmsg_current].mode_special = msg_unpack_int(); - killmsgs[killmsg_current].tick = client_tick(); - } - else if (msg == MSG_EMOTICON) - { - int cid = msg_unpack_int(); - int emoticon = msg_unpack_int(); - client_datas[cid].emoticon = emoticon; - client_datas[cid].emoticon_start = client_tick(); - } - else if(msg == MSG_SOUND_GLOBAL) - { - int soundid = msg_unpack_int(); - snd_play_random(CHN_GLOBAL, soundid, 1.0f, vec2(0,0)); - } -} - -extern "C" void modc_connected() -{ - // init some stuff - col_init(32); - img_init(); - tilemap_init(); - chat_reset(); - - proj_particles.reset(); - - clear_object_pointers(); - last_new_predicted_tick = -1; - - for(int i = 0; i < MAX_CLIENTS; i++) - { - client_datas[i].name[0] = 0; - client_datas[i].team = 0; - client_datas[i].emoticon = 0; - client_datas[i].emoticon_start = -1; - } - - for(int i = 0; i < killmsg_max; i++) - killmsgs[i].tick = -100000; - - send_info(true); -} - -extern "C" const char *modc_net_version() { return TEEWARS_NETVERSION; } diff --git a/src/game/client/gc_client.cpp b/src/game/client/gc_client.cpp new file mode 100644 index 00000000..98275188 --- /dev/null +++ b/src/game/client/gc_client.cpp @@ -0,0 +1,3103 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include +#include +#include +#include + +extern "C" { + #include + #include +}; + +#include "../g_game.h" +#include "../g_version.h" +#include "../g_mapres.h" +#include "gc_mapres_image.h" +#include "gc_mapres_tilemap.h" +#include "../generated/gc_data.h" +#include "gc_menu.h" +#include "gc_skin.h" +#include "gc_render.h" + +// sound channels +enum +{ + CHN_GUI=0, + CHN_MUSIC, + CHN_WORLD, + CHN_GLOBAL, +}; + +data_container *data = 0x0; + +int gametype = GAMETYPE_DM; + +extern void modmenu_render(); + +enum +{ + CHATMODE_NONE=0, + CHATMODE_ALL, + CHATMODE_TEAM, + CHATMODE_CONSOLE, + CHATMODE_REMOTECONSOLE, +}; + +typedef struct +{ + float x, y, w, h; +} RECT; +RECT *ui2_screen(); + +static int chat_mode = CHATMODE_NONE; +bool menu_active = false; +bool menu_game_active = false; +static bool emoticon_selector_active = false; + +static vec2 mouse_pos; +static vec2 local_character_pos; +static vec2 local_target_pos; +static const obj_player_character *local_character = 0; +static const obj_player_character *local_prev_character = 0; +const obj_player_info *local_info = 0; +static const obj_flag *flags[2] = {0,0}; +static const obj_game *gameobj = 0; + +static int picked_up_weapon = 0; + +static struct client_data +{ + char name[64]; + char skin_name[64]; + int skin_id; + int skin_color; + int team; + int emoticon; + int emoticon_start; + player_core predicted; + + tee_render_info skin_info; + +} client_datas[MAX_CLIENTS]; + +class client_effects +{ +public: + float zoom; + float currentzoom; + float stage; + int lastzoomin; + int lastincrease; + + client_effects() + { + currentzoom = zoom = 3.0f; + stage = 0.0f; + } + + float getorgzoom() { return zoom; } + + float getzoom(int tick, float intratick, const obj_player_character *player) + { + float currentstage = ((float)player->weaponstage) * 0.1f; + if (currentstage < stage) + { + if ((tick - lastincrease) > (client_tickspeed() / 2)) + stage = currentstage; + } + else + { + lastincrease = tick; + stage = currentstage; + } + + float targetzoom = 3.0f + stage; + currentzoom = LERP(currentzoom, targetzoom, 0.1); + return currentzoom; + } +}; + +client_effects cl_effects; + +inline float frandom() { return rand()/(float)(RAND_MAX); } + +void snd_play_random(int chn, int setid, float vol, vec2 pos) +{ + soundset *set = &data->sounds[setid]; + + if(!set->num_sounds) + return; + + if(set->num_sounds == 1) + { + snd_play_at(chn, set->sounds[0].id, 0, pos.x, pos.y); + return; + } + + // play a random one + int id; + do { + id = rand() % set->num_sounds; + } while(id == set->last); + snd_play_at(chn, set->sounds[id].id, 0, pos.x, pos.y); + set->last = id; +} + +// sound volume tweak +static const float stereo_separation = 0.001f; +static const float stereo_separation_deadzone = 200.0f; +static const float volume_distance_falloff = 200.0f; +static const float volume_distance_deadzone = 320.0f; +static const float volume_gun = 0.5f; +static const float volume_tee = 0.5f; +static const float volume_hit = 0.5f; +static const float volume_music = 0.8f; + +void sound_vol_pan(const vec2& p, float *vol, float *pan) +{ + vec2 player_to_ev = p - local_character_pos; + *pan = 0.0f; + *vol = 1.0f; + + if(fabs(player_to_ev.x) > stereo_separation_deadzone) + { + *pan = stereo_separation * (player_to_ev.x - sign(player_to_ev.x)*stereo_separation_deadzone); + if(*pan < -1.0f) *pan = -1.0f; + if(*pan > 1.0f) *pan = 1.0f; + } + + float len = length(player_to_ev); + if(len > volume_distance_deadzone) + { + *vol = volume_distance_falloff / (len - volume_distance_deadzone); + + if(*vol < 0.0f) *vol = 0.0f; + if(*vol > 1.0f) *vol = 1.0f; + } +} + +enum +{ + SPRITE_FLAG_FLIP_Y=1, + SPRITE_FLAG_FLIP_X=2, +}; + +static float sprite_w_scale; +static float sprite_h_scale; + +static void select_sprite(sprite *spr, int flags=0, int sx=0, int sy=0) +{ + int x = spr->x+sx; + int y = spr->y+sy; + int w = spr->w; + int h = spr->h; + int cx = spr->set->gridx; + int cy = spr->set->gridy; + + float f = sqrtf(h*h + w*w); + sprite_w_scale = w/f; + sprite_h_scale = h/f; + + float x1 = x/(float)cx; + float x2 = (x+w)/(float)cx; + float y1 = y/(float)cy; + float y2 = (y+h)/(float)cy; + float temp = 0; + + if(flags&SPRITE_FLAG_FLIP_Y) + { + temp = y1; + y1 = y2; + y2 = temp; + } + + if(flags&SPRITE_FLAG_FLIP_X) + { + temp = x1; + x1 = x2; + x2 = temp; + } + + gfx_quads_setsubset(x1, y1, x2, y2); +} + +void select_sprite(int id, int flags=0, int sx=0, int sy=0) +{ + if(id < 0 || id > data->num_sprites) + return; + select_sprite(&data->sprites[id], flags, sx, sy); +} + +void draw_sprite(float x, float y, float size) +{ + gfx_quads_draw(x, y, size*sprite_w_scale, size*sprite_h_scale); +} + +class damage_indicators +{ +public: + int64 lastupdate; + struct item + { + vec2 pos; + vec2 dir; + float life; + float startangle; + }; + + enum + { + MAX_ITEMS=64, + }; + + damage_indicators() + { + lastupdate = 0; + num_items = 0; + } + + item items[MAX_ITEMS]; + int num_items; + + item *create_i() + { + if (num_items < MAX_ITEMS) + { + item *p = &items[num_items]; + num_items++; + return p; + } + return 0; + } + + void destroy_i(item *i) + { + num_items--; + *i = items[num_items]; + } + + void create(vec2 pos, vec2 dir) + { + item *i = create_i(); + if (i) + { + i->pos = pos; + i->life = 0.75f; + i->dir = dir*-1; + i->startangle = (( (float)rand()/(float)RAND_MAX) - 1.0f) * 2.0f * pi; + } + } + + void render() + { + gfx_texture_set(data->images[IMAGE_GAME].id); + gfx_quads_begin(); + for(int i = 0; i < num_items;) + { + vec2 pos = mix(items[i].pos+items[i].dir*75.0f, items[i].pos, clamp((items[i].life-0.60f)/0.15f, 0.0f, 1.0f)); + + items[i].life -= client_frametime(); + if(items[i].life < 0.0f) + destroy_i(&items[i]); + else + { + gfx_setcolor(1.0f,1.0f,1.0f, items[i].life/0.1f); + gfx_quads_setrotation(items[i].startangle + items[i].life * 2.0f); + select_sprite(SPRITE_STAR1); + draw_sprite(pos.x, pos.y, 48.0f); + i++; + } + } + gfx_quads_end(); + } + +}; + +static damage_indicators damageind; + +class particle_system +{ +public: + struct particle + { + vec2 pos; + vec2 vel; + float life; + float max_life; + float size; + + float rot; + float rotspeed; + + float gravity; + float friction; + int iparticle; + + vec4 color; + }; + + enum + { + MAX_PARTICLES=1024, + }; + + particle particles[MAX_PARTICLES]; + int num_particles; + + particle_system() + { + num_particles = 0; + } + + void new_particle(vec2 pos, vec2 vel, float life, float size, float gravity, float friction, vec4 color=vec4(1,1,1,1)) + { + if (num_particles >= MAX_PARTICLES) + return; + + particles[num_particles].iparticle = rand() % data->num_particles; + particles[num_particles].pos = pos; + particles[num_particles].vel = vel; + particles[num_particles].life = life - (data->particles[particles[num_particles].iparticle].lifemod/100.0f) * life; + particles[num_particles].size = size; + particles[num_particles].max_life = life; + particles[num_particles].gravity = gravity; + particles[num_particles].friction = friction; + particles[num_particles].rot = frandom()*pi*2; + particles[num_particles].rotspeed = frandom() * 10.0f; + particles[num_particles].color = color; + num_particles++; + } + + void update(float time_passed) + { + for(int i = 0; i < num_particles; i++) + { + particles[i].vel.y += particles[i].gravity*time_passed; + particles[i].vel *= particles[i].friction; + vec2 vel = particles[i].vel*time_passed; + move_point(&particles[i].pos, &vel, 0.1f+0.9f*frandom(), NULL); + particles[i].vel = vel* (1.0f/time_passed); + particles[i].life += time_passed; + particles[i].rot += time_passed * particles[i].rotspeed; + + // check particle death + if(particles[i].life > particles[i].max_life) + { + num_particles--; + particles[i] = particles[num_particles]; + i--; + } + } + } + + void render() + { + gfx_blend_additive(); + gfx_texture_set(data->images[IMAGE_GAME].id); + gfx_quads_begin(); + + for(int i = 0; i < num_particles; i++) + { + int type = particles[i].iparticle; + select_sprite(data->particles[type].spr); + float a = 1 - particles[i].life / particles[i].max_life; + vec2 p = particles[i].pos; + + gfx_quads_setrotation(particles[i].rot); + + gfx_setcolor( + data->particles[type].color_r * particles[i].color.r, + data->particles[type].color_g * particles[i].color.g, + data->particles[type].color_b * particles[i].color.b, + pow(a, 0.75f) * particles[i].color.a); + + gfx_quads_draw(p.x, p.y,particles[i].size,particles[i].size); + } + gfx_quads_end(); + gfx_blend_normal(); + } +}; + +static particle_system temp_system; + +class projectile_particles +{ +public: + enum + { + LISTSIZE = 1000, + }; + // meh, just use size % + int lastadd[LISTSIZE]; + projectile_particles() + { + reset(); + } + + void reset() + { + for (int i = 0; i < LISTSIZE; i++) + lastadd[i] = -1000; + } + + void addparticle(int projectiletype, int projectileid, vec2 pos, vec2 vel) + { + int particlespersecond = data->projectileinfo[projectiletype].particlespersecond; + int lastaddtick = lastadd[projectileid % LISTSIZE]; + + if(!particlespersecond) + return; + + if ((client_tick() - lastaddtick) > (client_tickspeed() / particlespersecond)) + { + lastadd[projectileid % LISTSIZE] = client_tick(); + float life = data->projectileinfo[projectiletype].particlelife; + float size = data->projectileinfo[projectiletype].particlesize; + vec2 v = vel * 0.2f + normalize(vec2(frandom()-0.5f, -frandom()))*(32.0f+frandom()*32.0f); + + // add the particle (from projectiletype later on, but meh...) + temp_system.new_particle(pos, v, life, size, 0, 0.95f); + } + } +}; +static projectile_particles proj_particles; + +static char chat_input[512]; +static unsigned chat_input_len; +static const int chat_max_lines = 10; + +struct chatline +{ + int tick; + int client_id; + int team; + char text[512+64]; +}; + +chatline chat_lines[chat_max_lines]; +static int chat_current_line = 0; + +void chat_reset() +{ + for(int i = 0; i < chat_max_lines; i++) + chat_lines[i].tick = -1000000; + chat_current_line = 0; +} + +void chat_add_line(int client_id, int team, const char *line) +{ + chat_current_line = (chat_current_line+1)%chat_max_lines; + chat_lines[chat_current_line].tick = client_tick(); + chat_lines[chat_current_line].client_id = client_id; + chat_lines[chat_current_line].team = team; + + if(client_id == -1) // server message + sprintf(chat_lines[chat_current_line].text, "*** %s", line); + else + { + sprintf(chat_lines[chat_current_line].text, "%s: %s", client_datas[client_id].name, line); + } +} + +struct killmsg +{ + int weapon; + int victim; + int killer; + int mode_special; // for CTF, if the guy is carrying a flag for example + int tick; +}; + +static const int killmsg_max = 5; +killmsg killmsgs[killmsg_max]; +static int killmsg_current = 0; + +extern unsigned char internal_data[]; + +void create_air_jump_effect(vec2 pos) +{ + const int count = 12; + for(int i = 0; i <= count; i++) + { + float a = i/(float)count; + vec2 v = vec2((a-0.5f)*512.0f, 0); + temp_system.new_particle(pos+vec2(0,28), v, 0.4f, 16.0f, 0, 0.985f, vec4(0.25f,0.4f,1,1)); + } +} + + +extern void draw_round_rect(float x, float y, float w, float h, float r); +extern int render_popup(const char *caption, const char *text, const char *button_text); + +static void render_loading(float percent) +{ + gfx_clear(0.65f,0.78f,0.9f); + RECT screen = *ui2_screen(); + gfx_mapscreen(screen.x, screen.y, screen.w, screen.h); + + float tw; + + float w = 700; + float h = 200; + float x = screen.w/2-w/2; + float y = screen.h/2-h/2; + + gfx_blend_normal(); + + gfx_texture_set(-1); + gfx_quads_begin(); + gfx_setcolor(0,0,0,0.50f); + draw_round_rect(x, y, w, h, 40.0f); + gfx_quads_end(); + + const char *caption = "Loading"; + + tw = gfx_pretty_text_width(48.0f, caption, -1); + ui_do_label(x+w/2-tw/2, y+20, caption, 48.0f); + + gfx_texture_set(-1); + gfx_quads_begin(); + gfx_setcolor(1,1,1,1.0f); + draw_round_rect(x+40, y+h-75, (w-80)*percent, 25, 5.0f); + gfx_quads_end(); + + gfx_swap(); +} + +extern "C" void modc_init() +{ + // setup sound channels + snd_set_channel(CHN_GUI, 1.0f, 0.0f); + snd_set_channel(CHN_MUSIC, 1.0f, 0.0f); + snd_set_channel(CHN_WORLD, 0.9f, 1.0f); + snd_set_channel(CHN_GLOBAL, 1.0f, 0.0f); + + // load the data container + data = load_data_from_memory(internal_data); + + // TODO: should be removed + snd_set_listener_pos(0.0f, 0.0f); + + float total = data->num_sounds+data->num_images; + float current = 0; + + // load sounds + for(int s = 0; s < data->num_sounds; s++) + { + render_loading(current/total); + for(int i = 0; i < data->sounds[s].num_sounds; i++) + { + int id; + //if (strcmp(data->sounds[s].sounds[i].filename + strlen(data->sounds[s].sounds[i].filename) - 3, ".wv") == 0) + id = snd_load_wv(data->sounds[s].sounds[i].filename); + //else + // id = snd_load_wav(data->sounds[s].sounds[i].filename); + + data->sounds[s].sounds[i].id = id; + } + + current++; + } + + // load textures + for(int i = 0; i < data->num_images; i++) + { + render_loading(current/total); + data->images[i].id = gfx_load_texture(data->images[i].filename); + current++; + } + + skin_init(); +} + +extern "C" void modc_entergame() +{ +} + +extern "C" void modc_shutdown() +{ + // shutdown the menu +} + +static void process_events(int s) +{ + int num = snap_num_items(s); + for(int index = 0; index < num; index++) + { + SNAP_ITEM item; + const void *data = snap_get_item(s, index, &item); + + if(item.type == EVENT_DAMAGEINDICATION) + { + ev_damageind *ev = (ev_damageind *)data; + damageind.create(vec2(ev->x, ev->y), get_direction(ev->angle)); + } + else if(item.type == EVENT_AIR_JUMP) + { + ev_common *ev = (ev_common *)data; + create_air_jump_effect(vec2(ev->x, ev->y)); + } + else if(item.type == EVENT_EXPLOSION) + { + ev_explosion *ev = (ev_explosion *)data; + vec2 p(ev->x, ev->y); + + // center explosion + temp_system.new_particle(p, vec2(0,0), 0.3f, 96.0f, 0, 0.95f); + temp_system.new_particle(p, vec2(0,0), 0.3f, 64.0f, 0, 0.95f); + temp_system.new_particle(p, vec2(0,0), 0.3f, 32.0f, 0, 0.95f); + temp_system.new_particle(p, vec2(0,0), 0.3f, 16.0f, 0, 0.95f); + + for(int i = 0; i < 16; i++) + { + vec2 v = normalize(vec2(frandom()-0.5f, frandom()-0.5f))*(128.0f+frandom()*128.0f); + temp_system.new_particle(p, v, 0.2f+0.25f*frandom(), 16.0f, 0, 0.985f); + } + + for(int i = 0; i < 16; i++) + { + vec2 v = normalize(vec2(frandom()-0.5f, frandom()-0.5f))*(256.0f+frandom()*512.0f); + temp_system.new_particle(p, v, 0.2f+0.25f*frandom(), 16.0f, 128.0f, 0.985f); + } + + for(int i = 0; i < 64; i++) + { + vec2 v = normalize(vec2(frandom()-0.5f, frandom()-0.5f))*(frandom()*256.0f); + temp_system.new_particle(p, v, 0.2f+0.25f*frandom(), 24.0f, 128.0f, 0.985f); + } + } + else if(item.type == EVENT_SMOKE) + { + ev_explosion *ev = (ev_explosion *)data; + vec2 p(ev->x, ev->y); + + // center explosion + vec2 v = normalize(vec2(frandom()-0.5f, -frandom()))*(32.0f+frandom()*32.0f); + temp_system.new_particle(p, v, 1.2f, 64.0f, 0, 0.95f); + v = normalize(vec2(frandom()-0.5f, -frandom()))*(128.0f+frandom()*128.0f); + temp_system.new_particle(p, v, 1.2f, 32.0f, 0, 0.95f); + v = normalize(vec2(frandom()-0.5f, -frandom()))*(128.0f+frandom()*128.0f); + temp_system.new_particle(p, v, 1.2f, 16.0f, 0, 0.95f); + + for(int i = 0; i < 8; i++) + { + vec2 v = normalize(vec2(frandom()-0.5f, frandom()-0.5f))*(64.0f+frandom()*64.0f); + temp_system.new_particle(p, v, 0.5f+0.5f*frandom(), 16.0f, 0, 0.985f); + } + + for(int i = 0; i < 8; i++) + { + vec2 v = normalize(vec2(frandom()-0.5f, frandom()-0.5f))*(128.0f+frandom()*256.0f); + temp_system.new_particle(p, v, 0.5f+0.5f*frandom(), 16.0f, 128.0f, 0.985f); + } + } + else if(item.type == EVENT_SPAWN) + { + ev_explosion *ev = (ev_explosion *)data; + vec2 p(ev->x, ev->y); + + // center explosion + vec2 v = normalize(vec2(frandom()-0.5f, -frandom()))*(32.0f+frandom()*32.0f); + temp_system.new_particle(p, v, 1.2f, 64.0f, 0, 0.95f); + v = normalize(vec2(frandom()-0.5f, -frandom()))*(128.0f+frandom()*128.0f); + temp_system.new_particle(p, v, 1.2f, 32.0f, 0, 0.95f); + v = normalize(vec2(frandom()-0.5f, -frandom()))*(128.0f+frandom()*128.0f); + temp_system.new_particle(p, v, 1.2f, 16.0f, 0, 0.95f); + + for(int i = 0; i < 8; i++) + { + vec2 v = normalize(vec2(frandom()-0.5f, frandom()-0.5f))*(64.0f+frandom()*64.0f); + temp_system.new_particle(p, v, 0.5f+0.5f*frandom(), 16.0f, 0, 0.985f); + } + + for(int i = 0; i < 8; i++) + { + vec2 v = normalize(vec2(frandom()-0.5f, frandom()-0.5f))*(128.0f+frandom()*256.0f); + temp_system.new_particle(p, v, 0.5f+0.5f*frandom(), 16.0f, 128.0f, 0.985f); + } + } + else if(item.type == EVENT_DEATH) + { + ev_explosion *ev = (ev_explosion *)data; + vec2 p(ev->x, ev->y); + vec4 c(0.5f, 0.1f, 0.1f, 1.0f); + + // center explosion + vec2 v = normalize(vec2(frandom()-0.5f, -frandom()))*(32.0f+frandom()*32.0f); + temp_system.new_particle(p, v, 1.2f, 64.0f, 0, 0.95f, c); + v = normalize(vec2(frandom()-0.5f, -frandom()))*(128.0f+frandom()*128.0f); + temp_system.new_particle(p, v, 1.2f, 32.0f, 0, 0.95f, c); + v = normalize(vec2(frandom()-0.5f, -frandom()))*(128.0f+frandom()*128.0f); + temp_system.new_particle(p, v, 1.2f, 16.0f, 0, 0.95f, c); + + for(int i = 0; i < 8; i++) + { + vec2 v = normalize(vec2(frandom()-0.5f, frandom()-0.5f))*(64.0f+frandom()*64.0f); + temp_system.new_particle(p, v, 0.5f+0.5f*frandom(), 16.0f, 0, 0.985f, c); + } + + for(int i = 0; i < 8; i++) + { + vec2 v = normalize(vec2(frandom()-0.5f, frandom()-0.5f))*(128.0f+frandom()*256.0f); + temp_system.new_particle(p, v, 0.5f+0.5f*frandom(), 16.0f, 128.0f, 0.985f, c); + } + } + else if(item.type == EVENT_SOUND_WORLD) + { + ev_sound *ev = (ev_sound *)data; + if(ev->sound >= 0 && ev->sound < NUM_SOUNDS) + snd_play_random(CHN_WORLD, ev->sound, 1.0f, vec2(ev->x, ev->y)); + } + } +} + +static player_core predicted_prev_player; +static player_core predicted_player; +static int predicted_tick = 0; +static int last_new_predicted_tick = -1; + +extern "C" void modc_predict() +{ + player_core before_prev_player = predicted_prev_player; + player_core before_player = predicted_player; + + + // repredict player + world_core world; + int local_cid = -1; + + // search for players + for(int i = 0; i < snap_num_items(SNAP_CURRENT); i++) + { + SNAP_ITEM item; + const void *data = snap_get_item(SNAP_CURRENT, i, &item); + int client_id = item.id; + + if(item.type == OBJTYPE_PLAYER_CHARACTER) + { + const obj_player_character *character = (const obj_player_character *)data; + client_datas[client_id].predicted.world = &world; + world.players[client_id] = &client_datas[client_id].predicted; + + client_datas[client_id].predicted.read(character); + } + else if(item.type == OBJTYPE_PLAYER_INFO) + { + const obj_player_info *info = (const obj_player_info *)data; + if(info->local) + local_cid = client_id; + } + } + + // predict + for(int tick = client_tick()+1; tick <= client_predtick(); tick++) + { + // fetch the local + if(tick == client_predtick() && world.players[local_cid]) + predicted_prev_player = *world.players[local_cid]; + + // first calculate where everyone should move + for(int c = 0; c < MAX_CLIENTS; c++) + { + if(!world.players[c]) + continue; + + mem_zero(&world.players[c]->input, sizeof(world.players[c]->input)); + if(local_cid == c) + { + // apply player input + int *input = client_get_input(tick); + if(input) + world.players[c]->input = *((player_input*)input); + } + + world.players[c]->tick(); + } + + // move all players and quantize their data + for(int c = 0; c < MAX_CLIENTS; c++) + { + if(!world.players[c]) + continue; + + world.players[c]->move(); + world.players[c]->quantize(); + } + + if(tick > last_new_predicted_tick) + { + last_new_predicted_tick = tick; + + if(local_cid != -1 && world.players[local_cid]) + { + vec2 pos = world.players[local_cid]->pos; + int events = world.players[local_cid]->triggered_events; + if(events&COREEVENT_GROUND_JUMP) snd_play_random(CHN_WORLD, SOUND_PLAYER_JUMP, 1.0f, pos); + if(events&COREEVENT_AIR_JUMP) + { + create_air_jump_effect(pos); + snd_play_random(CHN_WORLD, SOUND_PLAYER_JUMP, 1.0f, pos); + } + //if(events&COREEVENT_HOOK_LAUNCH) snd_play_random(CHN_WORLD, SOUND_HOOK_LOOP, 1.0f, pos); + if(events&COREEVENT_HOOK_ATTACH_PLAYER) snd_play_random(CHN_WORLD, SOUND_HOOK_ATTACH_PLAYER, 1.0f, pos); + if(events&COREEVENT_HOOK_ATTACH_GROUND) snd_play_random(CHN_WORLD, SOUND_HOOK_ATTACH_GROUND, 1.0f, pos); + //if(events&COREEVENT_HOOK_RETRACT) snd_play_random(CHN_WORLD, SOUND_PLAYER_JUMP, 1.0f, pos); + } + + + /* + dbg_msg("predict", "%d %d %d", tick, + (int)world.players[c]->pos.x, (int)world.players[c]->pos.y, + (int)world.players[c]->vel.x, (int)world.players[c]->vel.y);*/ + } + + if(tick == client_predtick() && world.players[local_cid]) + predicted_player = *world.players[local_cid]; + } + + if(config.debug && predicted_tick == client_predtick()) + { + if(predicted_player.pos.x != before_player.pos.x || + predicted_player.pos.y != before_player.pos.y) + { + dbg_msg("client", "prediction error, (%d %d) (%d %d)", + (int)before_player.pos.x, (int)before_player.pos.y, + (int)predicted_player.pos.x, (int)predicted_player.pos.y); + } + + if(predicted_prev_player.pos.x != before_prev_player.pos.x || + predicted_prev_player.pos.y != before_prev_player.pos.y) + { + dbg_msg("client", "prediction error, prev (%d %d) (%d %d)", + (int)before_prev_player.pos.x, (int)before_prev_player.pos.y, + (int)predicted_prev_player.pos.x, (int)predicted_prev_player.pos.y); + } + } + + predicted_tick = client_predtick(); +} + +static void clear_object_pointers() +{ + // clear out the invalid pointers + local_character = 0; + local_prev_character = 0; + local_info = 0; + flags[0] = 0; + flags[1] = 0; + gameobj = 0; +} + +extern "C" void modc_newsnapshot() +{ + process_events(SNAP_CURRENT); + + if(config.dbg_stress) + { + if((client_tick()%250) == 0) + { + msg_pack_start(MSG_SAY, MSGFLAG_VITAL); + msg_pack_int(-1); + msg_pack_string("galenskap!!!!", 512); + msg_pack_end(); + client_send_msg(); + } + } + + clear_object_pointers(); + + // setup world view + { + // 1. fetch local player + // 2. set him to the center + int num = snap_num_items(SNAP_CURRENT); + for(int i = 0; i < num; i++) + { + SNAP_ITEM item; + const void *data = snap_get_item(SNAP_CURRENT, i, &item); + + if(item.type == OBJTYPE_PLAYER_INFO) + { + const obj_player_info *info = (const obj_player_info *)data; + if(info->local) + { + local_info = info; + const void *data = snap_find_item(SNAP_CURRENT, OBJTYPE_PLAYER_CHARACTER, item.id); + if(data) + { + local_character = (const obj_player_character *)data; + local_character_pos = vec2(local_character->x, local_character->y); + + const void *p = snap_find_item(SNAP_PREV, OBJTYPE_PLAYER_CHARACTER, item.id); + if(p) + local_prev_character = (obj_player_character *)p; + } + } + } + else if(item.type == OBJTYPE_GAME) + gameobj = (obj_game *)data; + else if(item.type == OBJTYPE_FLAG) + { + flags[item.id%2] = (const obj_flag *)data; + } + } + } +} + +void send_info(bool start) +{ + if(start) + msg_pack_start(MSG_STARTINFO, MSGFLAG_VITAL); + else + msg_pack_start(MSG_CHANGEINFO, MSGFLAG_VITAL); + msg_pack_string(config.player_name, 64); + msg_pack_string(config.player_skin, 64); + msg_pack_int(config.player_use_custom_color); + msg_pack_int(config.player_color_body); + msg_pack_int(config.player_color_feet); + msg_pack_end(); + client_send_msg(); +} + +void send_emoticon(int emoticon) +{ + msg_pack_start(MSG_EMOTICON, MSGFLAG_VITAL); + msg_pack_int(emoticon); + msg_pack_end(); + client_send_msg(); +} + +static void render_projectile(const obj_projectile *prev, const obj_projectile *current, int itemid) +{ + gfx_texture_set(data->images[IMAGE_GAME].id); + gfx_quads_begin(); + + // get positions + float gravity = -400; + if(current->type != WEAPON_ROCKET) + gravity = -100; + + float ct = (client_tick()-current->start_tick)/(float)SERVER_TICK_SPEED + client_intratick()*1/(float)SERVER_TICK_SPEED; + vec2 startpos(current->x, current->y); + vec2 startvel(current->vx, current->vy); + vec2 pos = calc_pos(startpos, startvel, gravity, ct); + vec2 prevpos = calc_pos(startpos, startvel, gravity, ct-0.001f); + + select_sprite(data->weapons[current->type%data->num_weapons].sprite_proj); + vec2 vel = pos-prevpos; + //vec2 pos = mix(vec2(prev->x, prev->y), vec2(current->x, current->y), client_intratick()); + + // add particle for this projectile + proj_particles.addparticle(current->type, itemid, pos, vel); + + if(length(vel) > 0.00001f) + gfx_quads_setrotation(get_angle(vel)); + else + gfx_quads_setrotation(0); + + // TODO: do this, but nice + //temp_system.new_particle(pos, vec2(0,0), 0.3f, 14.0f, 0, 0.95f); + + gfx_quads_draw(pos.x, pos.y, 32, 32); + gfx_quads_setrotation(0); + gfx_quads_end(); +} + +static void render_powerup(const obj_powerup *prev, const obj_powerup *current) +{ + gfx_texture_set(data->images[IMAGE_GAME].id); + gfx_quads_begin(); + vec2 pos = mix(vec2(prev->x, prev->y), vec2(current->x, current->y), client_intratick()); + float angle = 0.0f; + float size = 64.0f; + if (current->type == POWERUP_WEAPON) + { + angle = 0; //-pi/6;//-0.25f * pi * 2.0f; + select_sprite(data->weapons[current->subtype%data->num_weapons].sprite_body); + size = data->weapons[current->subtype%data->num_weapons].visual_size; + } + else + { + const int c[] = { + SPRITE_POWERUP_HEALTH, + SPRITE_POWERUP_ARMOR, + SPRITE_POWERUP_WEAPON, + SPRITE_POWERUP_NINJA, + SPRITE_POWERUP_TIMEFIELD + }; + select_sprite(c[current->type]); + + if(c[current->type] == SPRITE_POWERUP_NINJA) + { + proj_particles.addparticle(0, 0, + pos+vec2((frandom()-0.5f)*80.0f, (frandom()-0.5f)*20.0f), + vec2((frandom()-0.5f)*10.0f, (frandom()-0.5f)*10.0f)); + size *= 2.0f; + pos.x += 10.0f; + } + } + + gfx_quads_setrotation(angle); + + float offset = pos.y/32.0f + pos.x/32.0f; + pos.x += cosf(client_localtime()*2.0f+offset)*2.5f; + pos.y += sinf(client_localtime()*2.0f+offset)*2.5f; + draw_sprite(pos.x, pos.y, size); + gfx_quads_end(); +} + +static void render_flag(const obj_flag *prev, const obj_flag *current) +{ + float angle = 0.0f; + float size = 42.0f; + + gfx_blend_normal(); + gfx_texture_set(data->images[IMAGE_GAME].id); + gfx_quads_begin(); + + if(current->team == 0) // red team + select_sprite(SPRITE_FLAG_RED); + else + select_sprite(SPRITE_FLAG_BLUE); + + gfx_quads_setrotation(angle); + + vec2 pos = mix(vec2(prev->x, prev->y), vec2(current->x, current->y), client_intratick()); + + if(local_info && current->carried_by == local_info->clientid) + pos = local_character_pos; + + gfx_setcolor(current->team ? 0 : 1,0,current->team ? 1 : 0,1); + //draw_sprite(pos.x, pos.y, size); + gfx_quads_draw(pos.x, pos.y-size*0.75f, size, size*2); + gfx_quads_end(); +} + +void anim_seq_eval(sequence *seq, float time, keyframe *frame) +{ + if(seq->num_frames == 0) + { + frame->time = 0; + frame->x = 0; + frame->y = 0; + frame->angle = 0; + } + else if(seq->num_frames == 1) + { + *frame = seq->frames[0]; + } + else + { + //time = max(0.0f, min(1.0f, time / duration)); // TODO: use clamp + keyframe *frame1 = 0; + keyframe *frame2 = 0; + float blend = 0.0f; + + // TODO: make this smarter.. binary search + for (int i = 1; i < seq->num_frames; i++) + { + if (seq->frames[i-1].time <= time && seq->frames[i].time >= time) + { + frame1 = &seq->frames[i-1]; + frame2 = &seq->frames[i]; + blend = (time - frame1->time) / (frame2->time - frame1->time); + break; + } + } + + if (frame1 && frame2) + { + frame->time = time; + frame->x = mix(frame1->x, frame2->x, blend); + frame->y = mix(frame1->y, frame2->y, blend); + frame->angle = mix(frame1->angle, frame2->angle, blend); + } + } +} + +void anim_eval(animation *anim, float time, animstate *state) +{ + anim_seq_eval(&anim->body, time, &state->body); + anim_seq_eval(&anim->back_foot, time, &state->back_foot); + anim_seq_eval(&anim->front_foot, time, &state->front_foot); + anim_seq_eval(&anim->attach, time, &state->attach); +} + +void anim_add_keyframe(keyframe *seq, keyframe *added, float amount) +{ + seq->x += added->x*amount; + seq->y += added->y*amount; + seq->angle += added->angle*amount; +} + +void anim_add(animstate *state, animstate *added, float amount) +{ + anim_add_keyframe(&state->body, &added->body, amount); + anim_add_keyframe(&state->back_foot, &added->back_foot, amount); + anim_add_keyframe(&state->front_foot, &added->front_foot, amount); + anim_add_keyframe(&state->attach, &added->attach, amount); +} + +void anim_eval_add(animstate *state, animation *anim, float time, float amount) +{ + animstate add; + anim_eval(anim, time, &add); + anim_add(state, &add, amount); +} + +static void render_hand(int skin_id, vec2 center_pos, vec2 dir, float angle_offset, vec2 post_rot_offset) +{ + // for drawing hand + const skin *s = skin_get(skin_id); + + float basesize = 10.0f; + //dir = normalize(hook_pos-pos); + + vec2 hand_pos = center_pos + dir; + float angle = get_angle(dir); + if (dir.x < 0) + angle -= angle_offset; + else + angle += angle_offset; + + vec2 dirx = dir; + vec2 diry(-dir.y,dir.x); + + if (dir.x < 0) + diry = -diry; + + hand_pos += dirx * post_rot_offset.x; + hand_pos += diry * post_rot_offset.y; + + //gfx_texture_set(data->images[IMAGE_CHAR_DEFAULT].id); + gfx_texture_set(s->color_texture); + gfx_quads_begin(); + + // two passes + for (int i = 0; i < 2; i++) + { + bool outline = i == 0; + + select_sprite(outline?SPRITE_TEE_HAND_OUTLINE:SPRITE_TEE_HAND, 0, 0, 0); + gfx_quads_setrotation(angle); + gfx_quads_draw(hand_pos.x, hand_pos.y, 2*basesize, 2*basesize); + } + + gfx_quads_setrotation(0); + gfx_quads_end(); +} + +void render_tee(animstate *anim, tee_render_info *info, int emote, vec2 dir, vec2 pos) +{ + vec2 direction = dir; + vec2 position = pos; + + //gfx_texture_set(data->images[IMAGE_CHAR_DEFAULT].id); + gfx_texture_set(info->texture); + gfx_quads_begin(); + //gfx_quads_draw(pos.x, pos.y-128, 128, 128); + + // first pass we draw the outline + // second pass we draw the filling + for(int p = 0; p < 2; p++) + { + int outline = p==0 ? 1 : 0; + + for(int f = 0; f < 2; f++) + { + float animscale = info->size * 1.0f/64.0f; + float basesize = info->size; + if(f == 1) + { + gfx_quads_setrotation(anim->body.angle*pi*2); + + // draw body + gfx_setcolor(info->color_body.r, info->color_body.g, info->color_body.b, info->color_body.a); + vec2 body_pos = position + vec2(anim->body.x, anim->body.y)*animscale; + select_sprite(outline?SPRITE_TEE_BODY_OUTLINE:SPRITE_TEE_BODY, 0, 0, 0); + gfx_quads_draw(body_pos.x, body_pos.y, basesize, basesize); + + // draw eyes + if(p == 1) + { + switch (emote) + { + case EMOTE_PAIN: + select_sprite(SPRITE_TEE_EYE_PAIN, 0, 0, 0); + break; + case EMOTE_HAPPY: + select_sprite(SPRITE_TEE_EYE_HAPPY, 0, 0, 0); + break; + case EMOTE_SURPRISE: + select_sprite(SPRITE_TEE_EYE_SURPRISE, 0, 0, 0); + break; + case EMOTE_ANGRY: + select_sprite(SPRITE_TEE_EYE_ANGRY, 0, 0, 0); + break; + default: + select_sprite(SPRITE_TEE_EYE_NORMAL, 0, 0, 0); + break; + } + + float eyescale = basesize*0.40f; + float h = emote == EMOTE_BLINK ? basesize*0.15f : eyescale; + float eyeseparation = (0.075f - 0.010f*fabs(direction.x))*basesize; + vec2 offset = vec2(direction.x*0.125f, -0.05f+direction.y*0.10f)*basesize; + gfx_quads_draw(body_pos.x-eyeseparation+offset.x, body_pos.y+offset.y, eyescale, h); + gfx_quads_draw(body_pos.x+eyeseparation+offset.x, body_pos.y+offset.y, -eyescale, h); + } + } + + // draw feet + gfx_setcolor(info->color_feet.r, info->color_feet.g, info->color_feet.b, info->color_feet.a); + select_sprite(outline?SPRITE_TEE_FOOT_OUTLINE:SPRITE_TEE_FOOT, 0, 0, 0); + + keyframe *foot = f ? &anim->front_foot : &anim->back_foot; + + float w = basesize; + float h = basesize/2; + + gfx_quads_setrotation(foot->angle*pi*2); + gfx_quads_draw(position.x+foot->x*animscale, position.y+foot->y*animscale, w, h); + } + } + + gfx_quads_end(); +} + +void draw_circle(float x, float y, float r, int segments) +{ + float f_segments = (float)segments; + for(int i = 0; i < segments; i+=2) + { + float a1 = i/f_segments * 2*pi; + float a2 = (i+1)/f_segments * 2*pi; + float a3 = (i+2)/f_segments * 2*pi; + float ca1 = cosf(a1); + float ca2 = cosf(a2); + float ca3 = cosf(a3); + float sa1 = sinf(a1); + float sa2 = sinf(a2); + float sa3 = sinf(a3); + + gfx_quads_draw_freeform( + x, y, + x+ca1*r, y+sa1*r, + x+ca3*r, y+sa3*r, + x+ca2*r, y+sa2*r); + } +} + +void draw_round_rect_ext(float x, float y, float w, float h, float r, int corners) +{ + int num = 8; + for(int i = 0; i < num; i+=2) + { + float a1 = i/(float)num * pi/2; + float a2 = (i+1)/(float)num * pi/2; + float a3 = (i+2)/(float)num * pi/2; + float ca1 = cosf(a1); + float ca2 = cosf(a2); + float ca3 = cosf(a3); + float sa1 = sinf(a1); + float sa2 = sinf(a2); + float sa3 = sinf(a3); + + if(corners&1) // TL + gfx_quads_draw_freeform( + x+r, y+r, + x+(1-ca1)*r, y+(1-sa1)*r, + x+(1-ca3)*r, y+(1-sa3)*r, + x+(1-ca2)*r, y+(1-sa2)*r); + + if(corners&2) // TR + gfx_quads_draw_freeform( + x+w-r, y+r, + x+w-r+ca1*r, y+(1-sa1)*r, + x+w-r+ca3*r, y+(1-sa3)*r, + x+w-r+ca2*r, y+(1-sa2)*r); + + if(corners&4) // BL + gfx_quads_draw_freeform( + x+r, y+h-r, + x+(1-ca1)*r, y+h-r+sa1*r, + x+(1-ca3)*r, y+h-r+sa3*r, + x+(1-ca2)*r, y+h-r+sa2*r); + + if(corners&8) // BR + gfx_quads_draw_freeform( + x+w-r, y+h-r, + x+w-r+ca1*r, y+h-r+sa1*r, + x+w-r+ca3*r, y+h-r+sa3*r, + x+w-r+ca2*r, y+h-r+sa2*r); + } + + gfx_quads_drawTL(x+r, y+r, w-r*2, h-r*2); // center + gfx_quads_drawTL(x+r, y, w-r*2, r); // top + gfx_quads_drawTL(x+r, y+h-r, w-r*2, r); // bottom + gfx_quads_drawTL(x, y+r, r, h-r*2); // left + gfx_quads_drawTL(x+w-r, y+r, r, h-r*2); // right + + if(!(corners&1)) gfx_quads_drawTL(x, y, r, r); // TL + if(!(corners&2)) gfx_quads_drawTL(x+w, y, -r, r); // TR + if(!(corners&4)) gfx_quads_drawTL(x, y+h, r, -r); // BL + if(!(corners&8)) gfx_quads_drawTL(x+w, y+h, -r, -r); // BR +} + +void draw_round_rect(float x, float y, float w, float h, float r) +{ + draw_round_rect_ext(x,y,w,h,r,0xf); +} + + +static void render_player( + const obj_player_character *prev_char, + const obj_player_character *player_char, + const obj_player_info *prev_info, + const obj_player_info *player_info + ) +{ + obj_player_character prev; + obj_player_character player; + prev = *prev_char; + player = *player_char; + + obj_player_info info = *player_info; + + float intratick = client_intratick(); + + if(player.health < 0) // dont render dead players + return; + + if(info.local && config.cl_predict) + { + if(!local_character || (local_character->health < 0) || (gameobj && gameobj->game_over)) + { + } + else + { + // apply predicted results + predicted_player.write(&player); + predicted_prev_player.write(&prev); + intratick = client_intrapredtick(); + } + } + + // TODO: proper skin selection + int skin_id = client_datas[info.clientid].skin_id; //charids[info.clientid]; + //if(gametype != GAMETYPE_DM) + //skin_id = info.team*9; // 0 or 9 + + vec2 direction = get_direction(player.angle); + float angle = player.angle/256.0f; + vec2 position = mix(vec2(prev.x, prev.y), vec2(player.x, player.y), intratick); + + if(prev.health < 0) // Don't flicker from previous position + position = vec2(player.x, player.y); + + bool stationary = player.vx < 1 && player.vx > -1; + bool inair = col_check_point(player.x, player.y+16) == 0; + + // evaluate animation + float walk_time = fmod(position.x, 100.0f)/100.0f; + animstate state; + anim_eval(&data->animations[ANIM_BASE], 0, &state); + + if(inair) + anim_eval_add(&state, &data->animations[ANIM_INAIR], 0, 1.0f); // TODO: some sort of time here + else if(stationary) + anim_eval_add(&state, &data->animations[ANIM_IDLE], 0, 1.0f); // TODO: some sort of time here + else + anim_eval_add(&state, &data->animations[ANIM_WALK], walk_time, 1.0f); + + if (player.weapon == WEAPON_HAMMER) + { + float a = clamp((client_tick()-player.attacktick+intratick)/10.0f, 0.0f, 1.0f); + anim_eval_add(&state, &data->animations[ANIM_HAMMER_SWING], a, 1.0f); + } + if (player.weapon == WEAPON_NINJA) + { + float a = clamp((client_tick()-player.attacktick+intratick)/40.0f, 0.0f, 1.0f); + anim_eval_add(&state, &data->animations[ANIM_NINJA_SWING], a, 1.0f); + } + + // draw hook + if (prev.hook_state>0 && player.hook_state>0) + { + gfx_texture_set(data->images[IMAGE_GAME].id); + gfx_quads_begin(); + //gfx_quads_begin(); + + vec2 pos = position; + vec2 hook_pos = mix(vec2(prev.hook_x, prev.hook_y), vec2(player.hook_x, player.hook_y), intratick); + + float d = distance(pos, hook_pos); + vec2 dir = normalize(pos-hook_pos); + + gfx_quads_setrotation(get_angle(dir)+pi); + + // render head + select_sprite(SPRITE_HOOK_HEAD); + gfx_quads_draw(hook_pos.x, hook_pos.y, 24,16); + + // render chain + select_sprite(SPRITE_HOOK_CHAIN); + for(float f = 24; f < d; f += 24) + { + vec2 p = hook_pos + dir*f; + gfx_quads_draw(p.x, p.y,24,16); + } + + gfx_quads_setrotation(0); + gfx_quads_end(); + + render_hand(skin_id, position, normalize(hook_pos-pos), -pi/2, vec2(20, 0)); + } + + // draw gun + { + gfx_texture_set(data->images[IMAGE_GAME].id); + gfx_quads_begin(); + gfx_quads_setrotation(state.attach.angle*pi*2+angle); + + // normal weapons + int iw = clamp(player.weapon, 0, NUM_WEAPONS-1); + select_sprite(data->weapons[iw].sprite_body, direction.x < 0 ? SPRITE_FLAG_FLIP_Y : 0); + + vec2 dir = direction; + float recoil = 0.0f; + vec2 p; + if (player.weapon == WEAPON_HAMMER) + { + // Static position for hammer + p = position; + p.y += data->weapons[iw].offsety; + // if attack is under way, bash stuffs + if(direction.x < 0) + { + gfx_quads_setrotation(-pi/2-state.attach.angle*pi*2); + p.x -= data->weapons[iw].offsetx; + } + else + { + gfx_quads_setrotation(-pi/2+state.attach.angle*pi*2); + } + draw_sprite(p.x, p.y, data->weapons[iw].visual_size); + } + else if (player.weapon == WEAPON_NINJA) + { + p = position; + p.y += data->weapons[iw].offsety; + + if(direction.x < 0) + { + gfx_quads_setrotation(-pi/2-state.attach.angle*pi*2); + p.x -= data->weapons[iw].offsetx; + } + else + { + gfx_quads_setrotation(-pi/2+state.attach.angle*pi*2); + } + draw_sprite(p.x, p.y, data->weapons[iw].visual_size); + + // HADOKEN + if ((client_tick()-player.attacktick) <= (SERVER_TICK_SPEED / 6) && data->weapons[iw].nummuzzlesprites) + { + int itex = rand() % data->weapons[iw].nummuzzlesprites; + float alpha = 1.0f; + if (alpha > 0.0f && data->weapons[iw].sprite_muzzle[itex].psprite) + { + vec2 dir = vec2(player_char->x,player_char->y) - vec2(prev_char->x, prev_char->y); + dir = normalize(dir); + float hadokenangle = get_angle(dir); + gfx_quads_setrotation(hadokenangle); + //float offsety = -data->weapons[iw].muzzleoffsety; + select_sprite(data->weapons[iw].sprite_muzzle[itex].psprite, 0); + vec2 diry(-dir.y,dir.x); + p = position; + float offsetx = data->weapons[iw].muzzleoffsetx; + p -= dir * offsetx; + draw_sprite(p.x, p.y, 160.0f); + } + } + } + else + { + // TODO: should be an animation + recoil = 0; + float a = (client_tick()-player.attacktick+intratick)/5.0f; + if(a < 1) + recoil = sinf(a*pi); + p = position + dir * data->weapons[iw].offsetx - dir*recoil*10.0f; + p.y += data->weapons[iw].offsety; + draw_sprite(p.x, p.y, data->weapons[iw].visual_size); + } + + if (player.weapon == WEAPON_GUN || player.weapon == WEAPON_SHOTGUN) + { + // check if we're firing stuff + if (true)//prev.attackticks) + { + float alpha = 0.0f; + int phase1tick = (client_tick() - player.attacktick); + if (phase1tick < (data->weapons[iw].muzzleduration + 3)) + { + float t = ((((float)phase1tick) + intratick)/(float)data->weapons[iw].muzzleduration); + alpha = LERP(2.0, 0.0f, min(1.0f,max(0.0f,t))); + } + + int itex = rand() % data->weapons[iw].nummuzzlesprites; + if (alpha > 0.0f && data->weapons[iw].sprite_muzzle[itex].psprite) + { + float offsety = -data->weapons[iw].muzzleoffsety; + select_sprite(data->weapons[iw].sprite_muzzle[itex].psprite, direction.x < 0 ? SPRITE_FLAG_FLIP_Y : 0); + if(direction.x < 0) + offsety = -offsety; + + vec2 diry(-dir.y,dir.x); + vec2 muzzlepos = p + dir * data->weapons[iw].muzzleoffsetx + diry * offsety; + + draw_sprite(muzzlepos.x, muzzlepos.y, data->weapons[iw].visual_size); + /*gfx_setcolor(1.0f,1.0f,1.0f,alpha); + vec2 diry(-dir.y,dir.x); + p += dir * muzzleparams[player.weapon].offsetx + diry * offsety; + gfx_quads_draw(p.x,p.y,muzzleparams[player.weapon].sizex, muzzleparams[player.weapon].sizey);*/ + } + } + } + gfx_quads_end(); + + switch (player.weapon) + { + case WEAPON_GUN: render_hand(skin_id, p, direction, -3*pi/4, vec2(-15, 4)); break; + case WEAPON_SHOTGUN: render_hand(skin_id, p, direction, -pi/2, vec2(-5, 4)); break; + case WEAPON_ROCKET: render_hand(skin_id, p, direction, -pi/2, vec2(-4, 7)); break; + } + + } + + // render the "shadow" tee + if(info.local && config.debug) + { + vec2 ghost_position = mix(vec2(prev_char->x, prev_char->y), vec2(player_char->x, player_char->y), client_intratick()); + tee_render_info ghost = client_datas[info.clientid].skin_info; + ghost.color_body.a = 0.5f; + ghost.color_feet.a = 0.5f; + render_tee(&state, &ghost, player.emote, direction, ghost_position); // render ghost + } + + // render the tee + render_tee(&state, &client_datas[info.clientid].skin_info, player.emote, direction, position); + + if(player.state == STATE_CHATTING) + { + gfx_texture_set(data->images[IMAGE_EMOTICONS].id); + gfx_quads_begin(); + select_sprite(SPRITE_DOTDOT); + gfx_quads_draw(position.x + 24, position.y - 40, 64,64); + gfx_quads_end(); + } + + if (client_datas[info.clientid].emoticon_start != -1 && client_datas[info.clientid].emoticon_start + 2 * client_tickspeed() > client_tick()) + { + gfx_texture_set(data->images[IMAGE_EMOTICONS].id); + gfx_quads_begin(); + + int since_start = client_tick() - client_datas[info.clientid].emoticon_start; + int from_end = client_datas[info.clientid].emoticon_start + 2 * client_tickspeed() - client_tick(); + + float a = 1; + + if (from_end < client_tickspeed() / 5) + a = from_end / (client_tickspeed() / 5.0); + + float h = 1; + if (since_start < client_tickspeed() / 10) + h = since_start / (client_tickspeed() / 10.0); + + float wiggle = 0; + if (since_start < client_tickspeed() / 5) + wiggle = since_start / (client_tickspeed() / 5.0); + + float wiggle_angle = sin(5*wiggle); + + gfx_quads_setrotation(pi/6*wiggle_angle); + + gfx_setcolor(1.0f,1.0f,1.0f,a); + // client_datas::emoticon is an offset from the first emoticon + select_sprite(SPRITE_OOP + client_datas[info.clientid].emoticon); + gfx_quads_draw(position.x, position.y - 23 - 32*h, 64, 64*h); + gfx_quads_end(); + } + + // render name plate + if(!info.local && config.cl_nameplates) + { + //gfx_pretty_text_color + float a = 1; + if(config.cl_nameplates_always == 0) + a = clamp(1-powf(distance(local_target_pos, position)/200.0f,16.0f), 0.0f, 1.0f); + + const char *name = client_datas[info.clientid].name; + float tw = gfx_pretty_text_width(28.0f, name, -1); + gfx_pretty_text_color(1,1,1,a); + gfx_pretty_text(position.x-tw/2.0f, position.y-60, 28.0f, name, -1); + gfx_pretty_text_color(1,1,1,1); + } +} + +void render_moon(float x, float y) +{ + gfx_texture_set(data->images[IMAGE_MOON].id); + gfx_quads_begin(); + gfx_quads_draw(x, y, 512, 512); + gfx_quads_end(); +} + +void render_stars() +{ + struct star + { + vec2 p; + float tingle; + float rot; + float size; + }; + + static const int NUM_STARS = 100; + static star stars[NUM_STARS]; + static bool init = true; + if(init) + { + for(int i = 0; i < NUM_STARS; i++) + { + stars[i].p.x = (frandom()-0.5f)*800*3; + stars[i].p.y = (frandom()-0.5f)*600*3; + stars[i].tingle = frandom(); + stars[i].rot = frandom()*pi; + stars[i].size = 10.0f+frandom()*10.0f; + } + + init = false; + } + + int64 now = time_get(); + int64 freq = time_freq()*5; + float t = (now%freq)/(float)freq; + gfx_texture_set(data->images[IMAGE_STARS].id); + gfx_quads_begin(); + gfx_quads_setsubset(0,0,0.5f,1); + for(int i = 0; i < NUM_STARS; i++) + { + float a = fmod(t+stars[i].tingle, 1.0f); + gfx_quads_setrotation(stars[i].rot); + gfx_setcolor(1,1,1,0.25f+sinf(a*pi)*0.75f); + gfx_quads_draw(stars[i].p.x, stars[i].p.y, stars[i].size, stars[i].size); + } + + gfx_quads_end(); +} + +void render_snow() +{ + vec2 tl, br; + gfx_getscreen(&tl.x, &tl.y, &br.x, &br.y); + tl.x += 1000; // this is here to fix positions below 0,0 + tl.y += 1000; + br.x += 1000; + br.y += 1000; + + struct flake + { + vec2 p; + float tingle; + float rot; + float size; + float yspeed; + float xspeed; + }; + + static const int NUM_FLAKES = 100; + static flake flakes[NUM_FLAKES]; + static bool init = true; + + float w = br.x-tl.x; + float h = br.y-tl.y; + + if(init) + { + for(int i = 0; i < NUM_FLAKES; i++) + { + flakes[i].p.x = frandom()*w; + flakes[i].p.y = frandom()*h; + flakes[i].tingle = frandom(); + flakes[i].rot = frandom()*pi; + flakes[i].size = 50.0f+frandom()*10.0f; + flakes[i].yspeed = 50+frandom()*20.0f; + flakes[i].xspeed = flakes[i].yspeed * (0.4+frandom()*0.4f); + } + + init = false; + } + + int basex = (int)(tl.x/w); + float splitx = tl.x-basex*w; + int basey = (int)(tl.y/h); + float splity = tl.y-basey*h; + + float f = client_frametime(); + gfx_texture_set(data->images[IMAGE_SNOW].id); + gfx_quads_begin(); + for(int i = 0; i < NUM_FLAKES; i++) + { + flakes[i].p.x -= f*flakes[i].xspeed; + flakes[i].p.y += f*flakes[i].yspeed; + if(flakes[i].p.x < 0) + flakes[i].p.x += w; + if(flakes[i].p.y > h) + flakes[i].p.y -= h; + + float x = flakes[i].p.x + basex*w; + float y = flakes[i].p.y + basey*h; + + if(flakes[i].p.x < splitx) + x += w; + if(flakes[i].p.y < splity) + y += h; + + x -= 1000; + y -= 1000; + + gfx_quads_setrotation(flakes[i].rot); + gfx_quads_draw(x, y, flakes[i].size, flakes[i].size); + } + + gfx_quads_end(); +} + +void render_sun(float x, float y) +{ + vec2 pos(x, y); + + gfx_texture_set(-1); + gfx_blend_additive(); + gfx_quads_begin(); + const int rays = 10; + gfx_setcolor(1.0f,1.0f,1.0f,0.025f); + for(int r = 0; r < rays; r++) + { + float a = r/(float)rays + client_localtime()*0.025f; + float size = (1.0f/(float)rays)*0.25f; + vec2 dir0(sinf((a-size)*pi*2.0f), cosf((a-size)*pi*2.0f)); + vec2 dir1(sinf((a+size)*pi*2.0f), cosf((a+size)*pi*2.0f)); + + gfx_setcolorvertex(0, 1.0f,1.0f,1.0f,0.025f); + gfx_setcolorvertex(1, 1.0f,1.0f,1.0f,0.025f); + gfx_setcolorvertex(2, 1.0f,1.0f,1.0f,0.0f); + gfx_setcolorvertex(3, 1.0f,1.0f,1.0f,0.0f); + const float range = 1000.0f; + gfx_quads_draw_freeform( + pos.x+dir0.x, pos.y+dir0.y, + pos.x+dir1.x, pos.y+dir1.y, + pos.x+dir0.x*range, pos.y+dir0.y*range, + pos.x+dir1.x*range, pos.y+dir1.y*range); + } + gfx_quads_end(); + gfx_blend_normal(); + + gfx_texture_set(data->images[IMAGE_SUN].id); + gfx_quads_begin(); + gfx_quads_draw(pos.x, pos.y, 256, 256); + gfx_quads_end(); +} + +static vec2 emoticon_selector_mouse; + +void emoticon_selector_reset() +{ + emoticon_selector_mouse = vec2(0, 0); +} + +int emoticon_selector_render() +{ + int x, y; + inp_mouse_relative(&x, &y); + + emoticon_selector_mouse.x += x; + emoticon_selector_mouse.y += y; + + if (length(emoticon_selector_mouse) > 140) + emoticon_selector_mouse = normalize(emoticon_selector_mouse) * 140; + + float selected_angle = get_angle(emoticon_selector_mouse) + 2*pi/24; + if (selected_angle < 0) + selected_angle += 2*pi; + + bool return_now = false; + int selected_emoticon = -1; + + if (length(emoticon_selector_mouse) > 100) + selected_emoticon = (int)(selected_angle / (2*pi) * 12.0f); + + if(!inp_key_pressed(config.key_emoticon)) + { + return_now = true; + emoticon_selector_active = false; + } + + RECT screen = *ui2_screen(); + + gfx_mapscreen(screen.x, screen.y, screen.w, screen.h); + + gfx_blend_normal(); + + gfx_texture_set(-1); + gfx_quads_begin(); + gfx_setcolor(0,0,0,0.3f); + draw_circle(screen.w/2, screen.h/2, 160, 64); + gfx_quads_end(); + + gfx_texture_set(data->images[IMAGE_EMOTICONS].id); + gfx_quads_begin(); + + for (int i = 0; i < 12; i++) + { + float angle = 2*pi*i/12.0; + if (angle > pi) + angle -= 2*pi; + + bool selected = selected_emoticon == i; + + float size = selected ? 96 : 64; + + float nudge_x = 120 * cos(angle); + float nudge_y = 120 * sin(angle); + select_sprite(SPRITE_OOP + i); + gfx_quads_draw(screen.w/2 + nudge_x, screen.h/2 + nudge_y, size, size); + } + + gfx_quads_end(); + + gfx_texture_set(data->images[IMAGE_CURSOR].id); + gfx_quads_begin(); + gfx_setcolor(1,1,1,1); + gfx_quads_drawTL(emoticon_selector_mouse.x+screen.w/2,emoticon_selector_mouse.y+screen.h/2,24,24); + gfx_quads_end(); + + return return_now ? selected_emoticon : -1; +} + +void render_goals(float x, float y, float w) +{ + float h = 50.0f; + + gfx_blend_normal(); + gfx_texture_set(-1); + gfx_quads_begin(); + gfx_setcolor(0,0,0,0.5f); + draw_round_rect(x-10.f, y-10.f, w, h, 10.0f); + gfx_quads_end(); + + // render goals + //y = ystart+h-54; + if(gameobj && gameobj->time_limit) + { + char buf[64]; + sprintf(buf, "Time Limit: %d min", gameobj->time_limit); + gfx_pretty_text(x+w/2, y, 32, buf, -1); + } + if(gameobj && gameobj->score_limit) + { + char buf[64]; + sprintf(buf, "Score Limit: %d", gameobj->score_limit); + gfx_pretty_text(x+40, y, 32, buf, -1); + } + +} + +void render_scoreboard(float x, float y, float w, int team, const char *title) +{ + //float w = 550.0f; + //float x = width/2-w/2; + //; + //float y = ystart; + //float w = 550.0f; + + animstate idlestate; + anim_eval(&data->animations[ANIM_BASE], 0, &idlestate); + anim_eval_add(&idlestate, &data->animations[ANIM_IDLE], 0, 1.0f); + + //float ystart = y; + float h = 600.0f; + + gfx_blend_normal(); + gfx_texture_set(-1); + gfx_quads_begin(); + gfx_setcolor(0,0,0,0.5f); + draw_round_rect(x-10.f, y-10.f, w, h, 40.0f); + gfx_quads_end(); + + // render title + if(!title) + { + if(gameobj->game_over) + title = "Game Over"; + else + title = "Score Board"; + } + + float tw = gfx_pretty_text_width( 64, title, -1); + + if(team == -1) + { + gfx_pretty_text(x+w/2-tw/2, y, 64, title, -1); + } + else + { + gfx_pretty_text(x+10, y, 64, title, -1); + + if(gameobj) + { + char buf[128]; + sprintf(buf, "%d", gameobj->teamscore[team&1]); + tw = gfx_pretty_text_width(64, buf, -1); + gfx_pretty_text(x+w-tw-40, y, 64, buf, -1); + } + } + + y += 64.0f; + + /* + if(team) + { + char buf[128]; + sprintf(buf, "%4d", gameobj->teamscore[team&1]); + gfx_pretty_text(x+w/2-tw/2, y, 32, buf, -1); + }*/ + + + // find players + const obj_player_info *players[MAX_CLIENTS] = {0}; + int num_players = 0; + for(int i = 0; i < snap_num_items(SNAP_CURRENT); i++) + { + SNAP_ITEM item; + const void *data = snap_get_item(SNAP_CURRENT, i, &item); + + if(item.type == OBJTYPE_PLAYER_INFO) + { + players[num_players] = (const obj_player_info *)data; + num_players++; + } + } + + // sort players + for(int k = 0; k < num_players; k++) // ffs, bubblesort + { + for(int i = 0; i < num_players-k-1; i++) + { + if(players[i]->score < players[i+1]->score) + { + const obj_player_info *tmp = players[i]; + players[i] = players[i+1]; + players[i+1] = tmp; + } + } + } + + // render headlines + gfx_pretty_text(x+10, y, 32, "Score", -1); + gfx_pretty_text(x+125, y, 32, "Name", -1); + gfx_pretty_text(x+w-70, y, 32, "Ping", -1); + y += 38.0f; + + // render player scores + for(int i = 0; i < num_players; i++) + { + const obj_player_info *info = players[i]; + + // make sure that we render the correct team + if(team != -1 && info->team != team) + continue; + + char buf[128]; + float font_size = 46.0f; + if(info->local) + { + // background so it's easy to find the local player + gfx_texture_set(-1); + gfx_quads_begin(); + gfx_setcolor(1,1,1,0.25f); + draw_round_rect(x, y, w-20, 48, 20.0f); + gfx_quads_end(); + } + + sprintf(buf, "%4d", info->score); + gfx_pretty_text(x+60-gfx_pretty_text_width(font_size,buf,-1), y, font_size, buf, -1); + gfx_pretty_text(x+128, y, font_size, client_datas[info->clientid].name, -1); + + sprintf(buf, "%4d", info->latency); + float tw = gfx_pretty_text_width(font_size, buf, -1); + gfx_pretty_text(x+w-tw-35, y, font_size, buf, -1); + + // render avatar + render_tee(&idlestate, &client_datas[info->clientid].skin_info, EMOTE_NORMAL, vec2(1,0), vec2(x+90, y+28)); + y += 50.0f; + } +} + +void mapscreen_to_world(float center_x, float center_y, float zoom) +{ + RECT screen = *ui2_screen(); + + const float default_zoom = 1.5f; + float width = screen.w*default_zoom*zoom; + float height = screen.h*default_zoom*zoom; + gfx_mapscreen(center_x-width/2, center_y-height/2, center_x+width/2, center_y+height/2); +} + +// renders the complete game world +void render_world(float center_x, float center_y, float zoom) +{ + mapscreen_to_world(center_x, center_y, zoom); + //gfx_mapscreen(center_x-width/2, center_y-height/2, center_x+width/2, center_y+height/2); + + // render background environment + int theme_id = 0; + mapres_theme *t = (mapres_theme *)map_find_item(MAPRES_TEMP_THEME, 0); + if(t) + theme_id = t->id; + + if(config.gfx_high_detail) + { + if(theme_id == 1) + { + // Winter night + gfx_mapscreen(0,0,1,1); + gfx_texture_set(-1); + gfx_quads_begin(); + vec4 top(0x11/(float)0xff, 0x1a/(float)0xff, 0x21/(float)0xff, 1.0f); + vec4 bottom(0x2a/(float)0xff, 0x40/(float)0xff, 0x52/(float)0xff, 1.0f); + gfx_setcolorvertex(0, top.r, top.g, top.b, top.a); + gfx_setcolorvertex(1, top.r, top.g, top.b, top.a); + gfx_setcolorvertex(2, bottom.r, bottom.g, bottom.b, bottom.a); + gfx_setcolorvertex(3, bottom.r, bottom.g, bottom.b, bottom.a); + gfx_quads_drawTL(0, 0, 1, 1); + gfx_quads_end(); + + mapscreen_to_world(center_x*0.1f, center_y*0.1f, zoom); + render_stars(); + + mapscreen_to_world(center_x, center_y, zoom); + + render_moon(center_x*0.8f, center_y*0.8f); + + mapscreen_to_world(center_x, center_y, zoom); + } + else + { + // Summer day + render_sun(20+center_x*0.6f, 20+center_y*0.6f); + + // draw clouds + static vec2 cloud_pos[6] = {vec2(-500,0),vec2(-500,200),vec2(-500,400)}; + static float cloud_speed[6] = {30, 20, 10}; + static int cloud_sprites[6] = {SPRITE_CLOUD1, SPRITE_CLOUD2, SPRITE_CLOUD3}; + + gfx_texture_set(data->images[IMAGE_CLOUDS].id); + gfx_quads_begin(); + for(int i = 0; i < 3; i++) + { + float parallax_amount = 0.55f; + select_sprite(cloud_sprites[i]); + draw_sprite((cloud_pos[i].x+fmod(client_localtime()*cloud_speed[i]+i*100.0f, 3000.0f))+center_x*parallax_amount, + cloud_pos[i].y+center_y*parallax_amount, 300); + } + gfx_quads_end(); + + // draw backdrop + gfx_texture_set(data->images[IMAGE_BACKDROP].id); + gfx_quads_begin(); + float parallax_amount = 0.25f; + for(int x = -1; x < 3; x++) + gfx_quads_drawTL(1024*x+center_x*parallax_amount, (center_y)*parallax_amount+150+512, 1024, 512); + gfx_quads_end(); + } + } + + // render background tilemaps + tilemap_render(32.0f, 0); + + // render items + { + int num = snap_num_items(SNAP_CURRENT); + for(int i = 0; i < num; i++) + { + SNAP_ITEM item; + const void *data = snap_get_item(SNAP_CURRENT, i, &item); + + if(item.type == OBJTYPE_PROJECTILE) + { + const void *prev = snap_find_item(SNAP_PREV, item.type, item.id); + if(prev) + render_projectile((const obj_projectile *)prev, (const obj_projectile *)data, item.id); + } + else if(item.type == OBJTYPE_POWERUP) + { + const void *prev = snap_find_item(SNAP_PREV, item.type, item.id); + if(prev) + render_powerup((const obj_powerup *)prev, (const obj_powerup *)data); + } + else if(item.type == OBJTYPE_FLAG) + { + const void *prev = snap_find_item(SNAP_PREV, item.type, item.id); + if (prev) + render_flag((const obj_flag *)prev, (const obj_flag *)data); + } + } + } + + // render players above all + { + int num = snap_num_items(SNAP_CURRENT); + for(int i = 0; i < num; i++) + { + SNAP_ITEM item; + const void *data = snap_get_item(SNAP_CURRENT, i, &item); + + if(item.type == OBJTYPE_PLAYER_CHARACTER) + { + const void *prev = snap_find_item(SNAP_PREV, item.type, item.id); + const void *prev_info = snap_find_item(SNAP_PREV, OBJTYPE_PLAYER_INFO, item.id); + const void *info = snap_find_item(SNAP_CURRENT, OBJTYPE_PLAYER_INFO, item.id); + if(prev && prev_info && info) + { + client_datas[((const obj_player_info *)info)->clientid].team = ((const obj_player_info *)info)->team; + render_player( + (const obj_player_character *)prev, + (const obj_player_character *)data, + (const obj_player_info *)prev_info, + (const obj_player_info *)info + ); + } + } + } + } + + // render particles + temp_system.update(client_frametime()); + temp_system.render(); + + // render foreground tilemaps + tilemap_render(32.0f, 1); + + // render front environment effects + if(config.gfx_high_detail) + { + if(theme_id == 1) + { + //mapscreen_to_world(center_x, center_y, zoom); + render_snow(); + } + } + + // render damage indications + damageind.render(); +} + +static void do_input(int *v, int key) +{ + *v += inp_key_presses(key) + inp_key_releases(key); + if((*v&1) != inp_key_state(key)) + (*v)++; + *v &= INPUT_STATE_MASK; +} + +void render_game() +{ + float width = 400*3.0f; + float height = 300*3.0f; + + bool spectate = false; + + if(config.cl_predict) + { + if(!local_character || (local_character->health < 0) || (gameobj && gameobj->game_over)) + { + // don't use predicted + } + else + local_character_pos = mix(predicted_prev_player.pos, predicted_player.pos, client_intrapredtick()); + } + else if(local_character && local_prev_character) + { + local_character_pos = mix( + vec2(local_prev_character->x, local_prev_character->y), + vec2(local_character->x, local_character->y), client_intratick()); + } + + if(local_info && local_info->team == -1) + spectate = true; + + animstate idlestate; + anim_eval(&data->animations[ANIM_BASE], 0, &idlestate); + anim_eval_add(&idlestate, &data->animations[ANIM_IDLE], 0, 1.0f); + + if (inp_key_down(KEY_ESC)) + { + if (chat_mode) + chat_mode = CHATMODE_NONE; + else + { + menu_active = !menu_active; + if(menu_active) + menu_game_active = true; + } + } + + // make sure to send our info again if the menu closes + static bool menu_was_active = false; + if(menu_active) + menu_was_active = true; + else if(menu_was_active) + { + send_info(false); + menu_was_active = false; + } + + // handle chat input + if (!menu_active) + { + if(chat_mode != CHATMODE_NONE) + { + if(inp_key_down(KEY_ENTER)) + { + // send message + if(chat_input_len) + { + if(chat_mode == CHATMODE_CONSOLE) + config_set(chat_input); + else if(chat_mode == CHATMODE_REMOTECONSOLE) + client_rcon(chat_input); + else + { + // send chat message + msg_pack_start(MSG_SAY, MSGFLAG_VITAL); + if(chat_mode == CHATMODE_ALL) + msg_pack_int(0); + else + msg_pack_int(1); + msg_pack_string(chat_input, 512); + msg_pack_end(); + client_send_msg(); + } + } + + chat_mode = CHATMODE_NONE; + } + + int c = inp_last_char(); + int k = inp_last_key(); + + if (!(c >= 0 && c < 32)) + { + if (chat_input_len < sizeof(chat_input) - 1) + { + chat_input[chat_input_len] = c; + chat_input[chat_input_len+1] = 0; + chat_input_len++; + } + } + + if(k == KEY_BACKSPACE) + { + if(chat_input_len > 0) + { + chat_input[chat_input_len-1] = 0; + chat_input_len--; + } + } + + } + else + { + if(chat_mode == CHATMODE_NONE) + { + if(inp_key_down(config.key_chat)) + chat_mode = CHATMODE_ALL; + + if(inp_key_down(config.key_teamchat)) + chat_mode = CHATMODE_TEAM; + + if(inp_key_down(config.key_console)) + chat_mode = CHATMODE_CONSOLE; + + if(inp_key_down(config.key_remoteconsole)) + chat_mode = CHATMODE_REMOTECONSOLE; + + if(chat_mode != CHATMODE_NONE) + { + mem_zero(chat_input, sizeof(chat_input)); + chat_input_len = 0; + } + } + } + } + + if (!menu_active) + inp_clear(); + + // fetch new input + if(!menu_active && !emoticon_selector_active) + { + int x, y; + inp_mouse_relative(&x, &y); + mouse_pos += vec2(x, y); + if(!spectate) + { + float l = length(mouse_pos); + if(l > 600.0f) + mouse_pos = normalize(mouse_pos)*600.0f; + } + } + + // set listner pos + if(spectate) + { + local_target_pos = mouse_pos; + snd_set_listener_pos(mouse_pos.x, mouse_pos.y); + } + else + { + local_target_pos = local_character_pos + mouse_pos; + snd_set_listener_pos(local_character_pos.x, local_character_pos.y); + } + + // snap input + { + static player_input input = {0}; + + input.target_x = (int)mouse_pos.x; + input.target_y = (int)mouse_pos.y; + + if(chat_mode != CHATMODE_NONE) + input.state = STATE_CHATTING; + else if(menu_active) + input.state = STATE_IN_MENU; + else + { + input.state = STATE_PLAYING; + input.left = inp_key_state(config.key_move_left); + input.right = inp_key_state(config.key_move_right); + input.hook = inp_key_state(config.key_hook); + input.jump = inp_key_state(config.key_jump); + + if(!emoticon_selector_active) + do_input(&input.fire, config.key_fire); + + // weapon selection + do_input(&input.next_weapon, config.key_next_weapon); + do_input(&input.prev_weapon, config.key_prev_weapon); + + if(inp_key_presses(config.key_next_weapon) || inp_key_presses(config.key_prev_weapon)) + input.wanted_weapon = 0; + else if (config.cl_autoswitch_weapons && picked_up_weapon) + { + input.wanted_weapon = picked_up_weapon; + } + else + { + if(inp_key_presses(config.key_weapon1)) input.wanted_weapon = 1; + if(inp_key_presses(config.key_weapon2)) input.wanted_weapon = 2; + if(inp_key_presses(config.key_weapon3)) input.wanted_weapon = 3; + if(inp_key_presses(config.key_weapon4)) input.wanted_weapon = 4; + if(inp_key_presses(config.key_weapon5)) input.wanted_weapon = 5; + if(inp_key_presses(config.key_weapon6)) input.wanted_weapon = 6; + } + + picked_up_weapon = 0; + } + + // stress testing + if(config.dbg_stress) + { + float t = client_localtime(); + mem_zero(&input, sizeof(input)); + + input.left = ((int)t/2)&1; + input.right = ((int)t/2+1)&1; + input.jump = ((int)t); + input.fire = ((int)(t*10)); + input.hook = ((int)(t*2))&1; + input.wanted_weapon = ((int)t)%NUM_WEAPONS; + input.target_x = (int)(sinf(t*3)*100.0f); + input.target_y = (int)(cosf(t*3)*100.0f); + } + + snap_input(&input, sizeof(input)); + } + + // center at char but can be moved when mouse is far away + float offx = 0, offy = 0; + if (config.cl_dynamic_camera) + { + int deadzone = 300; + if(mouse_pos.x > deadzone) offx = mouse_pos.x-deadzone; + if(mouse_pos.x <-deadzone) offx = mouse_pos.x+deadzone; + if(mouse_pos.y > deadzone) offy = mouse_pos.y-deadzone; + if(mouse_pos.y <-deadzone) offy = mouse_pos.y+deadzone; + offx = offx*2/3; + offy = offy*2/3; + } + + // render the world + gfx_clear(0.65f,0.78f,0.9f); + if(spectate) + render_world(mouse_pos.x, mouse_pos.y, 1.0f); + else + { + render_world(local_character_pos.x+offx, local_character_pos.y+offy, 1.0f); + + // draw screen box + if(0) + { + gfx_texture_set(-1); + gfx_blend_normal(); + gfx_lines_begin(); + float cx = local_character_pos.x+offx; + float cy = local_character_pos.y+offy; + float w = 400*3/2; + float h = 300*3/2; + gfx_lines_draw(cx-w,cy-h,cx+w,cy-h); + gfx_lines_draw(cx+w,cy-h,cx+w,cy+h); + gfx_lines_draw(cx+w,cy+h,cx-w,cy+h); + gfx_lines_draw(cx-w,cy+h,cx-w,cy-h); + gfx_lines_end(); + } + } + + + // pseudo format + // ZOOM ZOOM + float zoom = 3.0; + if(local_character) + cl_effects.getzoom(client_tick(), client_intratick(), local_character); + + // DEBUG TESTING + if(zoom > 3.01f) + { + gfx_clear_mask(0); + + gfx_texture_set(-1); + gfx_blend_normal(); + + gfx_mask_op(MASK_NONE, 1); + + gfx_quads_begin(); + gfx_setcolor(0.65f,0.78f,0.9f,1.0f); + + float fov; + if (zoom > 3.01f) + fov = pi * (zoom - 3.0f) / 6.0f; + else + fov = pi / 6.0f; + + float fade = 0.7f; + + + float a = get_angle(normalize(vec2(mouse_pos.x, mouse_pos.y))); + vec2 d = get_dir(a); + vec2 d0 = get_dir(a-fov/2.0f); + vec2 d1 = get_dir(a+fov/2.0f); + + vec2 cd0 = get_dir(a-(fov*fade)/2.0f); // center direction + vec2 cd1 = get_dir(a+(fov*fade)/2.0f); + + vec2 p0n = local_character_pos + d0*32.0f; + vec2 p1n = local_character_pos + d1*32.0f; + vec2 p0f = local_character_pos + d0*1000.0f; + vec2 p1f = local_character_pos + d1*1000.0f; + + vec2 cn = local_character_pos + d*32.0f; + vec2 cf = local_character_pos + d*1000.0f; + + vec2 cp0n = local_character_pos + cd0*32.0f; + vec2 cp0f = local_character_pos + cd0*1000.0f; + vec2 cp1n = local_character_pos + cd1*32.0f; + vec2 cp1f = local_character_pos + cd1*1000.0f; + + gfx_quads_draw_freeform( + p0n.x,p0n.y, + p1n.x,p1n.y, + p0f.x,p0f.y, + p1f.x,p1f.y); + gfx_quads_end(); + + gfx_mask_op(MASK_SET, 0); + + render_world(local_character_pos.x+offx, local_character_pos.y+offy, 2.0f); + + gfx_mask_op(MASK_NONE, 0); + + mapscreen_to_world(local_character_pos.x+offx, local_character_pos.y+offy, 1.0f); + + gfx_texture_set(-1); + gfx_blend_normal(); + gfx_quads_begin(); + gfx_setcolor(0.5f,0.9f,0.5f,0.25f); + float r=0.5f, g=1.0f, b=0.5f; + float r2=r*0.25f, g2=g*0.25f, b2=b*0.25f; + + gfx_setcolor(r,g,b,0.2f); + gfx_quads_draw_freeform( + cn.x,cn.y, + cn.x,cn.y, + cp0f.x,cp0f.y, + cp1f.x,cp1f.y); + + gfx_setcolorvertex(0, r, g, b, 0.2f); + gfx_setcolorvertex(1, r2, g2, b2, 0.9f); + gfx_setcolorvertex(2, r, g, b, 0.2f); + gfx_setcolorvertex(3, r2, g2, b2, 0.9f); + gfx_quads_draw_freeform( + cn.x,cn.y, + p0n.x,p0n.y, + cp0f.x,cp0f.y, + p0f.x,p0f.y); + + gfx_quads_draw_freeform( + cn.x,cn.y, + p1n.x,p1n.y, + cp1f.x,cp1f.y, + p1f.x,p1f.y); + + gfx_quads_end(); + } + + if(local_character && !spectate && !(gameobj && gameobj->game_over)) + { + gfx_texture_set(data->images[IMAGE_GAME].id); + gfx_quads_begin(); + + // render cursor + if (!menu_active && !emoticon_selector_active) + { + select_sprite(data->weapons[local_character->weapon%data->num_weapons].sprite_cursor); + float cursorsize = 64; + draw_sprite(local_target_pos.x, local_target_pos.y, cursorsize); + } + + float x = 5; + float y = 5; + + // render ammo count + // render gui stuff + gfx_quads_end(); + gfx_quads_begin(); + gfx_mapscreen(0,0,400,300); + // if weaponstage is active, put a "glow" around the stage ammo + select_sprite(SPRITE_TEE_BODY); + for (int i = 0; i < local_character->weaponstage; i++) + gfx_quads_drawTL(x+local_character->ammocount * 12 -i*12, y+22, 11, 11); + select_sprite(data->weapons[local_character->weapon%data->num_weapons].sprite_proj); + for (int i = 0; i < local_character->ammocount; i++) + gfx_quads_drawTL(x+i*12,y+24,10,10); + + gfx_quads_end(); + + gfx_texture_set(data->images[IMAGE_GAME].id); + gfx_quads_begin(); + int h = 0; + + // render health + select_sprite(SPRITE_HEALTH_FULL); + for(; h < local_character->health; h++) + gfx_quads_drawTL(x+h*12,y,10,10); + + select_sprite(SPRITE_HEALTH_EMPTY); + for(; h < 10; h++) + gfx_quads_drawTL(x+h*12,y,10,10); + + // render armor meter + h = 0; + select_sprite(SPRITE_ARMOR_FULL); + for(; h < local_character->armor; h++) + gfx_quads_drawTL(x+h*12,y+12,10,10); + + select_sprite(SPRITE_ARMOR_EMPTY); + for(; h < 10; h++) + gfx_quads_drawTL(x+h*12,y+12,10,10); + gfx_quads_end(); + } + + // render kill messages + { + gfx_mapscreen(0, 0, width*1.5f, height*1.5f); + float startx = width*1.5f-10.0f; + float y = 20.0f; + + for(int i = 0; i < killmsg_max; i++) + { + + int r = (killmsg_current+i+1)%killmsg_max; + if(client_tick() > killmsgs[r].tick+50*10) + continue; + + float font_size = 48.0f; + float killername_w = gfx_pretty_text_width(font_size, client_datas[killmsgs[r].killer].name, -1); + float victimname_w = gfx_pretty_text_width(font_size, client_datas[killmsgs[r].victim].name, -1); + + float x = startx; + + // render victim name + x -= victimname_w; + gfx_pretty_text(x, y, font_size, client_datas[killmsgs[r].victim].name, -1); + + // render victim tee + x -= 24.0f; + + if(gameobj && gameobj->gametype == GAMETYPE_CTF) + { + if(killmsgs[r].mode_special&1) + { + gfx_blend_normal(); + gfx_texture_set(data->images[IMAGE_GAME].id); + gfx_quads_begin(); + + if(client_datas[killmsgs[r].victim].team == 0) select_sprite(SPRITE_FLAG_BLUE); + else select_sprite(SPRITE_FLAG_RED); + + float size = 56.0f; + gfx_quads_drawTL(x, y-16, size/2, size); + gfx_quads_end(); + } + } + + render_tee(&idlestate, &client_datas[killmsgs[r].victim].skin_info, EMOTE_PAIN, vec2(-1,0), vec2(x, y+28)); + x -= 32.0f; + + // render weapon + x -= 44.0f; + if (killmsgs[r].weapon >= 0) + { + gfx_texture_set(data->images[IMAGE_GAME].id); + gfx_quads_begin(); + select_sprite(data->weapons[killmsgs[r].weapon].sprite_body); + draw_sprite(x, y+28, 96); + gfx_quads_end(); + } + x -= 52.0f; + + if(killmsgs[r].victim != killmsgs[r].killer) + { + if(gameobj && gameobj->gametype == GAMETYPE_CTF) + { + if(killmsgs[r].mode_special&2) + { + gfx_blend_normal(); + gfx_texture_set(data->images[IMAGE_GAME].id); + gfx_quads_begin(); + + if(client_datas[killmsgs[r].killer].team == 0) select_sprite(SPRITE_FLAG_BLUE, SPRITE_FLAG_FLIP_X); + else select_sprite(SPRITE_FLAG_RED, SPRITE_FLAG_FLIP_X); + + float size = 56.0f; + gfx_quads_drawTL(x-56, y-16, size/2, size); + gfx_quads_end(); + } + } + + // render killer tee + x -= 24.0f; + render_tee(&idlestate, &client_datas[killmsgs[r].killer].skin_info, EMOTE_ANGRY, vec2(1,0), vec2(x, y+28)); + x -= 32.0f; + + // render killer name + x -= killername_w; + gfx_pretty_text(x, y, font_size, client_datas[killmsgs[r].killer].name, -1); + } + + y += 44; + } + } + + // render chat + { + gfx_mapscreen(0,0,400,300); + float x = 10.0f; + float y = 300.0f-50.0f; + float starty = -1; + if(chat_mode != CHATMODE_NONE) + { + // render chat input + char buf[sizeof(chat_input)+16]; + if(chat_mode == CHATMODE_ALL) + sprintf(buf, "All: %s_", chat_input); + else if(chat_mode == CHATMODE_TEAM) + sprintf(buf, "Team: %s_", chat_input); + else if(chat_mode == CHATMODE_CONSOLE) + sprintf(buf, "Console: %s_", chat_input); + else if(chat_mode == CHATMODE_REMOTECONSOLE) + sprintf(buf, "Rcon: %s_", chat_input); + else + sprintf(buf, "Chat: %s_", chat_input); + gfx_pretty_text(x, y, 10.0f, buf, 380); + starty = y; + } + + y -= 10; + + int i; + for(i = 0; i < chat_max_lines; i++) + { + int r = ((chat_current_line-i)+chat_max_lines)%chat_max_lines; + if(client_tick() > chat_lines[r].tick+50*15) + break; + + int lines = int(gfx_pretty_text_width(10, chat_lines[r].text, -1)) / 380 + 1; + + gfx_pretty_text_color(1,1,1,1); + if(chat_lines[r].client_id == -1) + gfx_pretty_text_color(1,1,0.5f,1); // system + else if(chat_lines[r].team) + gfx_pretty_text_color(0.5f,1,0.5f,1); // team message + + gfx_pretty_text(x, y - 8 * (lines - 1), 10, chat_lines[r].text, 380); + y -= 8 * lines; + } + + gfx_pretty_text_color(1,1,1,1); + } + + // render goals + if(gameobj) + { + gametype = gameobj->gametype; + gfx_mapscreen(0,0,400,300); + if(!gameobj->sudden_death) + { + char buf[32]; + int time = 0; + if(gameobj->time_limit) + { + time = gameobj->time_limit*60 - ((client_tick()-gameobj->round_start_tick)/client_tickspeed()); + + if(gameobj->game_over) + time = 0; + } + else + time = (client_tick()-gameobj->round_start_tick)/client_tickspeed(); + + sprintf(buf, "%d:%02d", time /60, time %60); + float w = gfx_pretty_text_width(16, buf, -1); + gfx_pretty_text(200-w/2, 2, 16, buf, -1); + } + + if(gameobj->sudden_death) + { + const char *text = "Sudden Death"; + float w = gfx_pretty_text_width(16, text, -1); + gfx_pretty_text(200-w/2, 2, 16, text, -1); + } + + // render small score hud + if(!(gameobj && gameobj->game_over) && (gametype == GAMETYPE_TDM || gametype == GAMETYPE_CTF)) + { + for(int t = 0; t < 2; t++) + { + gfx_blend_normal(); + gfx_texture_set(-1); + gfx_quads_begin(); + if(t == 0) + gfx_setcolor(1,0,0,0.25f); + else + gfx_setcolor(0,0,1,0.25f); + draw_round_rect(400-40, 300-40-15+t*20, 50, 18, 5.0f); + gfx_quads_end(); + + char buf[32]; + sprintf(buf, "%d", gameobj->teamscore[t]); + float w = gfx_pretty_text_width(14, buf, -1); + + if(gametype == GAMETYPE_CTF) + { + gfx_pretty_text(400-20-w/2+5, 300-40-15+t*20+2, 14, buf, -1); + if(flags[t]) + { + if(flags[t]->carried_by == -2 || (flags[t]->carried_by == -1 && ((client_tick()/10)&1))) + { + gfx_blend_normal(); + gfx_texture_set(data->images[IMAGE_GAME].id); + gfx_quads_begin(); + + if(t == 0) select_sprite(SPRITE_FLAG_RED); + else select_sprite(SPRITE_FLAG_BLUE); + + float size = 16; + gfx_quads_drawTL(400-40+5, 300-40-15+t*20+1, size/2, size); + gfx_quads_end(); + } + else if(flags[t]->carried_by >= 0) + { + int id = flags[t]->carried_by%MAX_CLIENTS; + const char *name = client_datas[id].name; + float w = gfx_pretty_text_width(10, name, -1); + gfx_pretty_text(400-40-5-w, 300-40-15+t*20+2, 10, name, -1); + tee_render_info info = client_datas[id].skin_info; + info.size = 18.0f; + + render_tee(&idlestate, &info, EMOTE_NORMAL, vec2(1,0), + vec2(400-40+10, 300-40-15+9+t*20+1)); + } + } + } + else + gfx_pretty_text(400-20-w/2, 300-40-15+t*20+2, 14, buf, -1); + } + } + + // render warmup timer + if(gameobj->warmup) + { + char buf[256]; + float w = gfx_pretty_text_width(24, "Warmup", -1); + gfx_pretty_text(200+-w/2, 50, 24, "Warmup", -1); + + int seconds = gameobj->warmup/SERVER_TICK_SPEED; + if(seconds < 5) + sprintf(buf, "%d.%d", seconds, (gameobj->warmup*10/SERVER_TICK_SPEED)%10); + else + sprintf(buf, "%d", seconds); + w = gfx_pretty_text_width(24, buf, -1); + gfx_pretty_text(200+-w/2, 75, 24, buf, -1); + } + } + + if (menu_active) + { + modmenu_render(); + return; + } + + if(chat_mode == CHATMODE_NONE && !menu_active && !spectate) + { + if(!emoticon_selector_active && inp_key_pressed(config.key_emoticon)) + { + emoticon_selector_active = true; + emoticon_selector_reset(); + } + } + else + emoticon_selector_active = false; + + if(emoticon_selector_active) + { + int emoticon = emoticon_selector_render(); + if (emoticon != -1) + { + send_emoticon(emoticon); + emoticon_selector_active = false; + } + } + + if(client_connection_problems()) + { + gfx_mapscreen(0, 0, 400, 300); + const char *text = "Connection Problems..."; + float w = gfx_pretty_text_width(24, text, -1); + gfx_pretty_text(200-w/2, 50, 24, text, -1); + } + + // render score board + if(inp_key_pressed(KEY_TAB) || // user requested + (!spectate && (!local_character || local_character->health < 0)) || // not spectating and is dead + (gameobj && gameobj->game_over) // game over + ) + { + gfx_mapscreen(0, 0, width, height); + + float w = 550.0f; + + if (gameobj && gameobj->gametype == GAMETYPE_DM) + { + render_scoreboard(width/2-w/2, 150.0f, w, -1, 0); + //render_scoreboard(gameobj, 0, 0, -1, 0); + } + else + { + + if(gameobj && gameobj->game_over) + { + const char *text = "DRAW!"; + if(gameobj->teamscore[0] > gameobj->teamscore[1]) + text = "Red Team Wins!"; + else if(gameobj->teamscore[1] > gameobj->teamscore[0]) + text = "Blue Team Wins!"; + + float w = gfx_pretty_text_width(92.0f, text, -1); + gfx_pretty_text(width/2-w/2, 45, 92.0f, text, -1); + } + + render_scoreboard(width/2-w-20, 150.0f, w, 0, "Red Team"); + render_scoreboard(width/2 + 20, 150.0f, w, 1, "Blue Team"); + } + + render_goals(width/2-w/2, 150+600+25, w); + + } +} + +extern "C" void modc_render() +{ + // this should be moved around abit + if(client_state() == CLIENTSTATE_ONLINE) + { + render_game(); + + // handle team switching + // TODO: FUGLY!!! + if(config.cl_team != -10) + { + msg_pack_start(MSG_SETTEAM, MSGFLAG_VITAL); + msg_pack_int(config.cl_team); + msg_pack_end(); + client_send_msg(); + } + } + else // if (client_state() != CLIENTSTATE_CONNECTING && client_state() != CLIENTSTATE_LOADING) + { + modmenu_render(); + } + + // + config.cl_team = -10; +} + + +void menu_do_disconnected(); +void menu_do_connecting(); +void menu_do_connected(); + +extern "C" void modc_statechange(int state, int old) +{ + clear_object_pointers(); + + if(state == CLIENTSTATE_OFFLINE) + { + menu_do_disconnected(); + menu_game_active = false; + } + else if(state == CLIENTSTATE_CONNECTING) + menu_do_connecting(); + else if (state == CLIENTSTATE_ONLINE) + { + menu_active = false; + menu_game_active = true; + menu_do_connected(); + } +} + +extern "C" void modc_message(int msg) +{ + if(msg == MSG_CHAT) + { + int cid = msg_unpack_int(); + int team = msg_unpack_int(); + const char *message = msg_unpack_string(); + dbg_msg("message", "chat cid=%d team=%d msg='%s'", cid, team, message); + chat_add_line(cid, team, message); + + if(cid >= 0) + snd_play(CHN_GUI, data->sounds[SOUND_CHAT_CLIENT].sounds[0].id, 0); + else + snd_play(CHN_GUI, data->sounds[SOUND_CHAT_SERVER].sounds[0].id, 0); + } + else if(msg == MSG_SETINFO) + { + int cid = msg_unpack_int(); + const char *name = msg_unpack_string(); + const char *skinname = msg_unpack_string(); + + strncpy(client_datas[cid].name, name, 64); + strncpy(client_datas[cid].skin_name, skinname, 64); + + int use_custom_color = msg_unpack_int(); + client_datas[cid].skin_info.color_body = skin_get_color(msg_unpack_int()); + client_datas[cid].skin_info.color_feet = skin_get_color(msg_unpack_int()); + client_datas[cid].skin_info.size = 64; + + // find new skin + client_datas[cid].skin_id = skin_find(client_datas[cid].skin_name); + if(client_datas[cid].skin_id < 0) + client_datas[cid].skin_id = 0; + + if(use_custom_color) + client_datas[cid].skin_info.texture = skin_get(client_datas[cid].skin_id)->color_texture; + else + { + client_datas[cid].skin_info.texture = skin_get(client_datas[cid].skin_id)->org_texture; + client_datas[cid].skin_info.color_body = vec4(1,1,1,1); + client_datas[cid].skin_info.color_feet = vec4(1,1,1,1); + } + } + else if(msg == MSG_WEAPON_PICKUP) + { + int weapon = msg_unpack_int(); + + picked_up_weapon = weapon+1; + } + else if(msg == MSG_READY_TO_ENTER) + { + client_entergame(); + } + else if(msg == MSG_KILLMSG) + { + killmsg_current = (killmsg_current+1)%killmsg_max; + killmsgs[killmsg_current].killer = msg_unpack_int(); + killmsgs[killmsg_current].victim = msg_unpack_int(); + killmsgs[killmsg_current].weapon = msg_unpack_int(); + killmsgs[killmsg_current].mode_special = msg_unpack_int(); + killmsgs[killmsg_current].tick = client_tick(); + } + else if (msg == MSG_EMOTICON) + { + int cid = msg_unpack_int(); + int emoticon = msg_unpack_int(); + client_datas[cid].emoticon = emoticon; + client_datas[cid].emoticon_start = client_tick(); + } + else if(msg == MSG_SOUND_GLOBAL) + { + int soundid = msg_unpack_int(); + snd_play_random(CHN_GLOBAL, soundid, 1.0f, vec2(0,0)); + } +} + +extern "C" void modc_connected() +{ + // init some stuff + col_init(32); + img_init(); + tilemap_init(); + chat_reset(); + + proj_particles.reset(); + + clear_object_pointers(); + last_new_predicted_tick = -1; + + for(int i = 0; i < MAX_CLIENTS; i++) + { + client_datas[i].name[0] = 0; + client_datas[i].team = 0; + client_datas[i].emoticon = 0; + client_datas[i].emoticon_start = -1; + } + + for(int i = 0; i < killmsg_max; i++) + killmsgs[i].tick = -100000; + + send_info(true); +} + +extern "C" const char *modc_net_version() { return TEEWARS_NETVERSION; } diff --git a/src/game/client/gc_mapres_image.cpp b/src/game/client/gc_mapres_image.cpp new file mode 100644 index 00000000..cc9a8143 --- /dev/null +++ b/src/game/client/gc_mapres_image.cpp @@ -0,0 +1,125 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include +#include +#include "gc_mapres_image.h" +#include "../g_mapres.h" + +static int map_textures[64] = {0}; +static int count = 0; +/* +static void calc_mipmaps(void *data_in, unsigned width, unsigned height, void *data_out) +{ + unsigned char *src = (unsigned char*)data_in; + unsigned char *dst = (unsigned char*)data_out; + unsigned mip_w = width; + unsigned mip_h = height; + unsigned prev_w; + unsigned prev_h; + + // Highest level - no mod + for(unsigned x = 0; x < mip_w; x++) + { + for(unsigned y = 0; y < mip_h; y++) + { + unsigned i = (y * mip_w + x)<<2; + for(unsigned j = 0; j < 4; j++) + dst[i+j] = src[i+j]; + } + } + + src = dst; + dst += mip_w * mip_h * 4; + prev_w = mip_w; + prev_h = mip_h; + mip_w = mip_w>>1; + mip_h = mip_h>>1; + + while(mip_w > 0 && mip_h > 0) + { + for(unsigned x = 0; x < mip_w; x++) + { + for(unsigned y = 0; y < mip_h; y++) + { + unsigned i = (y * mip_w + x)<<2; + + unsigned r = 0; + unsigned g = 0; + unsigned b = 0; + unsigned a = 0; + + + r += src[(((y<<1) * prev_w + (x<<1))<<2)]; + g += src[(((y<<1) * prev_w + (x<<1))<<2)+1]; + b += src[(((y<<1) * prev_w + (x<<1))<<2)+2]; + a += src[(((y<<1) * prev_w + (x<<1))<<2)+3]; + + r += src[(((y<<1) * prev_w + ((x+1)<<1))<<2)]; + g += src[(((y<<1) * prev_w + ((x+1)<<1))<<2)+1]; + b += src[(((y<<1) * prev_w + ((x+1)<<1))<<2)+2]; + a += src[(((y<<1) * prev_w + ((x+1)<<1))<<2)+3]; + + r += src[((((y+1)<<1) * prev_w + (x<<1))<<2)]; + g += src[((((y+1)<<1) * prev_w + (x<<1))<<2)+1]; + b += src[((((y+1)<<1) * prev_w + (x<<1))<<2)+2]; + a += src[((((y+1)<<1) * prev_w + (x<<1))<<2)+3]; + + r += src[((((y+1)<<1) * prev_w + ((x+1)<<1))<<2)]; + g += src[((((y+1)<<1) * prev_w + ((x+1)<<1))<<2)+1]; + b += src[((((y+1)<<1) * prev_w + ((x+1)<<1))<<2)+2]; + a += src[((((y+1)<<1) * prev_w + ((x+1)<<1))<<2)+3]; + + dst[i] = r>>2; + dst[i+1] = g>>2; + dst[i+2] = b>>2; + dst[i+3] = a>>2; + } + } + + src = dst; + dst = dst + mip_w*mip_h*4; + prev_w = mip_w; + prev_h = mip_h; + mip_w = mip_w>>1; + mip_h = mip_h>>1; + } +} +*/ +extern int DEBUGTEST_MAPIMAGE; + +int img_init() +{ + int start, count; + map_get_type(MAPRES_IMAGE, &start, &count); + dbg_msg("mapres_image", "start=%d count=%d", start, count); + for(int i = 0; i < 64; i++) + { + if(map_textures[i]) + { + gfx_unload_texture(map_textures[i]); + map_textures[i] = 0; + } + } + + //void *data_res = (void*)mem_alloc(1024*1024*4*2, 16); + for(int i = 0; i < count; i++) + { + mapres_image *img = (mapres_image *)map_get_item(start+i, 0, 0); + void *data = map_get_data(img->image_data); + //calc_mipmaps(data, img->width, img->height, data_res); + map_textures[i] = gfx_load_texture_raw(img->width, img->height, IMG_RGBA, data); + map_unload_data(img->image_data); + } + + //mem_free(data_res); + return count; +} + +int img_num() +{ + return count; +} + +int img_get(int index) +{ + return map_textures[index]; +} diff --git a/src/game/client/gc_mapres_image.h b/src/game/client/gc_mapres_image.h new file mode 100644 index 00000000..f841ca53 --- /dev/null +++ b/src/game/client/gc_mapres_image.h @@ -0,0 +1,19 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ + +// loads images from the map to textures +int img_init(); + +// returns the number of images in the map +int img_num(); + +// fetches the texture id for the image +int img_get(int index); + + +class mapres_image +{ +public: + int width; + int height; + int image_data; +}; diff --git a/src/game/client/gc_mapres_tilemap.cpp b/src/game/client/gc_mapres_tilemap.cpp new file mode 100644 index 00000000..6e687495 --- /dev/null +++ b/src/game/client/gc_mapres_tilemap.cpp @@ -0,0 +1,102 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include +#include +#include "gc_mapres_tilemap.h" +#include "gc_mapres_image.h" +#include "../g_mapres.h" + +int tilemap_init() +{ + return 0; +} + +void tilemap_render(float scale, int fg) +{ + if(!map_is_loaded()) + return; + + float screen_x0, screen_y0, screen_x1, screen_y1; + gfx_getscreen(&screen_x0, &screen_y0, &screen_x1, &screen_y1); + + // fetch indecies + int start, num; + map_get_type(MAPRES_TILEMAP, &start, &num); + + // render tilemaps + int passed_main = 0; + for(int t = 0; t < num; t++) + { + mapres_tilemap *tmap = (mapres_tilemap *)map_get_item(start+t,0,0); + unsigned char *data = (unsigned char *)map_get_data(tmap->data); + + if(tmap->main) + passed_main = 1; + + if((fg && passed_main) || (!fg && !passed_main)) + { + if(!config.gfx_high_detail && !tmap->main) + continue; + gfx_texture_set(img_get(tmap->image)); + + gfx_quads_begin(); + + int starty = (int)(screen_y0/scale)-1; + int startx = (int)(screen_x0/scale)-1; + int endy = (int)(screen_y1/scale)+1; + int endx = (int)(screen_x1/scale)+1; + + float frac = (1.25f/1024.0f);//2.0f; //2.0f; + float texsize = 1024.0f; + float nudge = 0.5f/texsize; + for(int y = starty; y < endy; y++) + for(int x = startx; x < endx; x++) + { + int mx = x; + int my = y; + if(mx<0) mx = 0; + if(mx>=tmap->width) mx = tmap->width-1; + if(my<0) my = 0; + if(my>=tmap->height) my = tmap->height-1; + + int c = mx + my*tmap->width; + + unsigned char d = data[c*2]; + unsigned char f = data[c*2+1]; + if(d) + { + int tx = d%16; + int ty = d/16; + int px0 = tx*(1024/16); + int py0 = ty*(1024/16); + int px1 = (tx+1)*(1024/16)-1; + int py1 = (ty+1)*(1024/16)-1; + + float u0 = nudge + px0/texsize+frac; + float v0 = nudge + py0/texsize+frac; + float u1 = nudge + px1/texsize-frac; + float v1 = nudge + py1/texsize-frac; + + if(f&TILEFLAG_VFLIP) + { + float tmp = u0; + u0 = u1; + u1 = tmp; + } + + if(f&TILEFLAG_HFLIP) + { + float tmp = v0; + v0 = v1; + v1 = tmp; + } + + gfx_quads_setsubset(u0,v0,u1,v1); + + gfx_quads_drawTL(x*scale, y*scale, scale, scale); + } + } + + gfx_quads_end(); + } + } +} diff --git a/src/game/client/gc_mapres_tilemap.h b/src/game/client/gc_mapres_tilemap.h new file mode 100644 index 00000000..a13495ed --- /dev/null +++ b/src/game/client/gc_mapres_tilemap.h @@ -0,0 +1,26 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ + +// dependencies: image + +// +int tilemap_init(); + +// renders the tilemaps +void tilemap_render(float scale, int fg); + +enum +{ + TILEFLAG_VFLIP=1, + TILEFLAG_HFLIP=2, +}; + +struct mapres_tilemap +{ + int image; + int width; + int height; + int x, y; + int scale; + int data; + int main; +}; diff --git a/src/game/client/gc_menu.cpp b/src/game/client/gc_menu.cpp new file mode 100644 index 00000000..de9047df --- /dev/null +++ b/src/game/client/gc_menu.cpp @@ -0,0 +1,1893 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include +#include +#include +#include + +#include +#include + +extern "C" { + #include + #include + #include + #include +} + +#include "../g_mapres.h" +#include "../g_version.h" +#include "../g_protocol.h" + +#include "gc_mapres_image.h" +#include "gc_mapres_tilemap.h" + +#include "../generated/gc_data.h" +#include "gc_render.h" +#include "gc_skin.h" +#include + +extern data_container *data; + +// abit uglyness +extern const obj_player_info *local_info; +extern bool menu_active; +extern bool menu_game_active; + +enum +{ + POPUP_NONE=0, + POPUP_CONNECTING, + POPUP_DISCONNECTED, + POPUP_PASSWORD, + POPUP_QUIT, +}; + +static int popup = POPUP_NONE; + +//static vec4 gui_color(0.9f,0.78f,0.65f, 0.5f); +//static vec4 gui_color(0.78f,0.9f,0.65f, 0.5f); + +static vec4 gui_color(0.65f,0.78f,0.9f, 0.5f); + +static vec4 color_tabbar_inactive_outgame(0,0,0,0.25f); +static vec4 color_tabbar_active_outgame(0,0,0,0.5f); + +static float color_ingame_scale_i = 0.5f; +static float color_ingame_scale_a = 0.2f; +static vec4 color_tabbar_inactive_ingame(gui_color.r*color_ingame_scale_i, gui_color.g*color_ingame_scale_i, gui_color.b*color_ingame_scale_i,0.75f); +static vec4 color_tabbar_active_ingame(gui_color.r*color_ingame_scale_a, gui_color.g*color_ingame_scale_a, gui_color.b*color_ingame_scale_a,0.85f); +//static vec4 color_tabbar_inactive_ingame(0.2f,0.2f,0.2f,0.5f); +//static vec4 color_tabbar_active_ingame(0.2f,0.2f,0.2f,0.75f); + +static vec4 color_tabbar_inactive = color_tabbar_inactive_outgame; +static vec4 color_tabbar_active = color_tabbar_active_outgame; + +enum +{ + CORNER_TL=1, + CORNER_TR=2, + CORNER_BL=4, + CORNER_BR=8, + + CORNER_T=CORNER_TL|CORNER_TR, + CORNER_B=CORNER_BL|CORNER_BR, + CORNER_R=CORNER_TR|CORNER_BR, + CORNER_L=CORNER_TL|CORNER_BL, + + CORNER_ALL=CORNER_T|CORNER_B, + + PAGE_NEWS=0, + PAGE_INTERNET, + PAGE_LAN, + PAGE_FAVORITES, + PAGE_SETTINGS, + //PAGE_GAME, // not a real page + PAGE_SYSTEM, +}; + +typedef struct +{ + float x, y, w, h; +} RECT; + +static RECT screen = { 0.0f, 0.0f, 848.0f, 480.0f }; + +extern void select_sprite(int id, int flags=0, int sx=0, int sy=0); + +RECT *ui2_screen() +{ + float aspect = gfx_screenaspect(); + float w, h; + + h = 600; + w = aspect*h; + + screen.w = w; + screen.h = h; + + return &screen; +} + +void ui2_set_scale(float s) +{ + config.ui_scale = (int)(s*100.0f); +} + +float ui2_scale() +{ + return config.ui_scale/100.0f; +} + +void ui2_hsplit_t(const RECT *original, float cut, RECT *top, RECT *bottom) +{ + RECT r = *original; + cut *= ui2_scale(); + + if (top) + { + top->x = r.x; + top->y = r.y; + top->w = r.w; + top->h = cut; + } + + if (bottom) + { + bottom->x = r.x; + bottom->y = r.y + cut; + bottom->w = r.w; + bottom->h = r.h - cut; + } +} + +void ui2_hsplit_b(const RECT *original, float cut, RECT *top, RECT *bottom) +{ + RECT r = *original; + cut *= ui2_scale(); + + if (top) + { + top->x = r.x; + top->y = r.y; + top->w = r.w; + top->h = r.h - cut; + } + + if (bottom) + { + bottom->x = r.x; + bottom->y = r.y + r.h - cut; + bottom->w = r.w; + bottom->h = cut; + } +} + +void ui2_vsplit_l(const RECT *original, float cut, RECT *left, RECT *right) +{ + RECT r = *original; + cut *= ui2_scale(); + + if (left) + { + left->x = r.x; + left->y = r.y; + left->w = cut; + left->h = r.h; + } + + if (right) + { + right->x = r.x + cut; + right->y = r.y; + right->w = r.w - cut; + right->h = r.h; + } +} + +void ui2_vsplit_r(const RECT *original, float cut, RECT *left, RECT *right) +{ + RECT r = *original; + cut *= ui2_scale(); + + if (left) + { + left->x = r.x; + left->y = r.y; + left->w = r.w - cut; + left->h = r.h; + } + + if (right) + { + right->x = r.x + r.w - cut; + right->y = r.y; + right->w = cut; + right->h = r.h; + } +} + +void ui2_margin(const RECT *original, float cut, RECT *other_rect) +{ + RECT r = *original; + cut *= ui2_scale(); + + other_rect->x = r.x + cut; + other_rect->y = r.y + cut; + other_rect->w = r.w - 2*cut; + other_rect->h = r.h - 2*cut; +} + +void ui2_vmargin(const RECT *original, float cut, RECT *other_rect) +{ + RECT r = *original; + cut *= ui2_scale(); + + other_rect->x = r.x + cut; + other_rect->y = r.y; + other_rect->w = r.w - 2*cut; + other_rect->h = r.h; +} + +void ui2_hmargin(const RECT *original, float cut, RECT *other_rect) +{ + RECT r = *original; + cut *= ui2_scale(); + + other_rect->x = r.x; + other_rect->y = r.y + cut; + other_rect->w = r.w; + other_rect->h = r.h - 2*cut; +} + +typedef void (*ui2_draw_button_func)(const void *id, const char *text, int checked, const RECT *r, void *extra); + +int ui2_do_button(const void *id, const char *text, int checked, const RECT *r, ui2_draw_button_func draw_func, void *extra) +{ + /* logic */ + int ret = 0; + int inside = ui_mouse_inside(r->x,r->y,r->w,r->h); + + if(ui_active_item() == id) + { + if(!ui_mouse_button(0)) + { + if(inside) + ret = 1; + ui_set_active_item(0); + } + } + else if(ui_hot_item() == id) + { + if(ui_mouse_button(0)) + ui_set_active_item(id); + } + + if(inside) + ui_set_hot_item(id); + + if(draw_func) + draw_func(id, text, checked, r, extra); + return ret; +} + + +void ui2_do_label(const RECT *r, const char *text, float size, int align) +{ + gfx_blend_normal(); + size *= ui2_scale(); + if(align == 0) + { + float tw = gfx_pretty_text_width(size, text, -1); + gfx_pretty_text(r->x + r->w/2-tw/2, r->y, size, text, -1); + } + else if(align < 0) + gfx_pretty_text(r->x, r->y, size, text, -1); + else if(align > 0) + { + float tw = gfx_pretty_text_width(size, text, -1); + gfx_pretty_text(r->x + r->w-tw, r->y, size, text, -1); + } +} + + +extern void draw_round_rect_ext(float x, float y, float w, float h, float r, int corners); +extern void draw_round_rect(float x, float y, float w, float h, float r); + +static void ui2_draw_rect(const RECT *r, vec4 color, int corners, float rounding) +{ + gfx_texture_set(-1); + gfx_quads_begin(); + gfx_setcolor(color.r, color.g, color.b, color.a); + draw_round_rect_ext(r->x,r->y,r->w,r->h,rounding*ui2_scale(), corners); + gfx_quads_end(); +} + +static void ui2_draw_browse_icon(int what, const RECT *r) +{ + gfx_texture_set(data->images[IMAGE_BROWSEICONS].id); + gfx_quads_begin(); + select_sprite(SPRITE_BROWSE_PROGRESS1); // default + if(what <= 100) + { + if(what < 66) + select_sprite(SPRITE_BROWSE_PROGRESS2); + else + select_sprite(SPRITE_BROWSE_PROGRESS3); + } + else if(what&0x100) + { + select_sprite(SPRITE_BROWSE_LOCK); + } + gfx_quads_drawTL(r->x,r->y,r->w,r->h); + gfx_quads_end(); +} + +static void ui2_draw_menu_button(const void *id, const char *text, int checked, const RECT *r, void *extra) +{ + ui2_draw_rect(r, vec4(1,1,1,0.5f), CORNER_ALL, 5.0f); + ui2_do_label(r, text, 24, 0); +} + + +static void ui2_draw_keyselect_button(const void *id, const char *text, int checked, const RECT *r, void *extra) +{ + ui2_draw_rect(r, vec4(1,1,1,0.5f), CORNER_ALL, 5.0f); + ui2_do_label(r, text, 18, 0); +} + +static void ui2_draw_menu_tab_button(const void *id, const char *text, int checked, const RECT *r, void *extra) +{ + if(checked) + ui2_draw_rect(r, color_tabbar_active, CORNER_T, 10.0f); + else + ui2_draw_rect(r, color_tabbar_inactive, CORNER_T, 10.0f); + ui2_do_label(r, text, 26, 0); +} + + +static void ui2_draw_settings_tab_button(const void *id, const char *text, int checked, const RECT *r, void *extra) +{ + if(checked) + ui2_draw_rect(r, color_tabbar_active, CORNER_R, 10.0f); + else + ui2_draw_rect(r, color_tabbar_inactive, CORNER_R, 10.0f); + ui2_do_label(r, text, 24, 0); +} + +static void ui2_draw_grid_header(const void *id, const char *text, int checked, const RECT *r, void *extra) +{ + if(checked) + ui2_draw_rect(r, vec4(1,1,1,0.5f), CORNER_T, 5.0f); + //else + // ui2_draw_rect(r, vec4(1,1,1,0.1f), CORNER_T, 5.0f); + RECT t; + ui2_vsplit_l(r, 5.0f, 0, &t); + ui2_do_label(&t, text, 18, -1); +} +/* +static void ui2_draw_grid_cell_l(const void *id, const char *text, int checked, const RECT *r, void *extra) +{ + ui2_do_label(r, text, 18, -1); +} + +static void ui2_draw_grid_cell_r(const void *id, const char *text, int checked, const RECT *r, void *extra) +{ + ui2_do_label(r, text, 18, 1); +}*/ + +static void ui2_draw_list_row(const void *id, const char *text, int checked, const RECT *r, void *extra) +{ + if(checked) + { + RECT sr = *r; + ui2_margin(&sr, 1.5f, &sr); + ui2_draw_rect(&sr, vec4(1,1,1,0.5f), CORNER_ALL, 4.0f); + } + ui2_do_label(r, text, 18, -1); +} + +static void ui2_draw_checkbox_common(const void *id, const char *text, const char *boxtext, const RECT *r) +{ + RECT c = *r; + RECT t = *r; + c.w = c.h; + t.x += c.w; + t.w -= c.w; + ui2_vsplit_l(&t, 5.0f, 0, &t); + + ui2_margin(&c, 2.0f, &c); + ui2_draw_rect(&c, vec4(1,1,1,0.25f), CORNER_ALL, 3.0f); + ui2_do_label(&c, boxtext, 16, 0); + ui2_do_label(&t, text, 18, -1); +} + +static void ui2_draw_checkbox(const void *id, const char *text, int checked, const RECT *r, void *extra) +{ + ui2_draw_checkbox_common(id, text, checked?"X":"", r); +} + +static void ui2_draw_checkbox_number(const void *id, const char *text, int checked, const RECT *r, void *extra) +{ + char buf[16]; + sprintf(buf, "%d", checked); + ui2_draw_checkbox_common(id, text, buf, r); +} + +int ui2_do_edit_box(void *id, const RECT *rect, char *str, int str_size, bool hidden=false) +{ + int inside = ui_mouse_inside(rect->x,rect->y,rect->w,rect->h); + int r = 0; + static int at_index = 0; + + if(ui_last_active_item() == id) + { + int c = inp_last_char(); + int k = inp_last_key(); + int len = strlen(str); + + if (inside && ui_mouse_button(0)) + { + int mx_rel = (int)(ui_mouse_x() - rect->x); + + for (int i = 1; i <= len; i++) + { + if (gfx_pretty_text_width(18.0f, str, i) + 10 > mx_rel) + { + at_index = i - 1; + break; + } + + if (i == len) + at_index = len; + } + } + + if (at_index > len) + at_index = len; + + if (!(c >= 0 && c < 32)) + { + if (len < str_size - 1 && at_index < str_size - 1) + { + memmove(str + at_index + 1, str + at_index, len - at_index + 1); + str[at_index] = c; + at_index++; + } + } + + if (k == KEY_BACKSPACE && at_index > 0) + { + memmove(str + at_index - 1, str + at_index, len - at_index + 1); + at_index--; + } + else if (k == KEY_DEL && at_index < len) + memmove(str + at_index, str + at_index + 1, len - at_index); + else if (k == KEY_ENTER) + ui_clear_last_active_item(); + else if (k == KEY_LEFT && at_index > 0) + at_index--; + else if (k == KEY_RIGHT && at_index < len) + at_index++; + else if (k == KEY_HOME) + at_index = 0; + else if (k == KEY_END) + at_index = len; + + r = 1; + } + + int box_type; + if (ui_active_item() == id || ui_hot_item() == id || ui_last_active_item() == id) + box_type = GUI_BOX_SCREEN_INFO; + else + box_type = GUI_BOX_SCREEN_TEXTBOX; + + bool just_got_active = false; + + if(ui_active_item() == id) + { + if(!ui_mouse_button(0)) + ui_set_active_item(0); + } + else if(ui_hot_item() == id) + { + if(ui_mouse_button(0)) + { + if (ui_last_active_item() != id) + just_got_active = true; + ui_set_active_item(id); + } + } + + if(inside) + ui_set_hot_item(id); + + RECT textbox = *rect; + ui2_draw_rect(&textbox, vec4(1,1,1,0.5f), CORNER_ALL, 5.0f); + ui2_vmargin(&textbox, 5.0f, &textbox); + + const char *display_str = str; + char stars[128]; + + if(hidden) + { + unsigned s = strlen(str); + if(s >= sizeof(stars)) + s = sizeof(stars)-1; + memset(stars, '*', s); + stars[s] = 0; + display_str = stars; + } + + ui2_do_label(&textbox, display_str, 18, -1); + + if (ui_last_active_item() == id && !just_got_active) + { + float w = gfx_pretty_text_width(18.0f, display_str, at_index); + textbox.x += w*ui2_scale(); + ui2_do_label(&textbox, "_", 18, -1); + } + + return r; +} + +float ui2_do_scrollbar_v(const void *id, const RECT *rect, float current) +{ + RECT handle; + static float offset_y; + ui2_hsplit_t(rect, 33, &handle, 0); + + handle.y += (rect->h-handle.h)*current; + + /* logic */ + float ret = current; + int inside = ui_mouse_inside(handle.x,handle.y,handle.w,handle.h); + + if(ui_active_item() == id) + { + if(!ui_mouse_button(0)) + ui_set_active_item(0); + + float min = rect->y; + float max = rect->h-handle.h; + float cur = ui_mouse_y()-offset_y; + ret = (cur-min)/max; + if(ret < 0.0f) ret = 0.0f; + if(ret > 1.0f) ret = 1.0f; + } + else if(ui_hot_item() == id) + { + if(ui_mouse_button(0)) + { + ui_set_active_item(id); + offset_y = ui_mouse_y()-handle.y; + } + } + + if(inside) + ui_set_hot_item(id); + + // render + RECT rail; + ui2_vmargin(rect, 5.0f, &rail); + ui2_draw_rect(&rail, vec4(1,1,1,0.25f), 0, 0.0f); + + RECT slider = handle; + slider.w = rail.x-slider.x; + ui2_draw_rect(&slider, vec4(1,1,1,0.25f), CORNER_L, 2.5f); + slider.x = rail.x+rail.w; + ui2_draw_rect(&slider, vec4(1,1,1,0.25f), CORNER_R, 2.5f); + + slider = handle; + ui2_margin(&slider, 5.0f, &slider); + ui2_draw_rect(&slider, vec4(1,1,1,0.25f), CORNER_ALL, 2.5f); + + return ret; +} + + + +float ui2_do_scrollbar_h(const void *id, const RECT *rect, float current) +{ + RECT handle; + static float offset_x; + ui2_vsplit_l(rect, 33, &handle, 0); + + handle.x += (rect->w-handle.w)*current; + + /* logic */ + float ret = current; + int inside = ui_mouse_inside(handle.x,handle.y,handle.w,handle.h); + + if(ui_active_item() == id) + { + if(!ui_mouse_button(0)) + ui_set_active_item(0); + + float min = rect->x; + float max = rect->w-handle.w; + float cur = ui_mouse_x()-offset_x; + ret = (cur-min)/max; + if(ret < 0.0f) ret = 0.0f; + if(ret > 1.0f) ret = 1.0f; + } + else if(ui_hot_item() == id) + { + if(ui_mouse_button(0)) + { + ui_set_active_item(id); + offset_x = ui_mouse_x()-handle.x; + } + } + + if(inside) + ui_set_hot_item(id); + + // render + RECT rail; + ui2_hmargin(rect, 5.0f, &rail); + ui2_draw_rect(&rail, vec4(1,1,1,0.25f), 0, 0.0f); + + RECT slider = handle; + slider.h = rail.y-slider.y; + ui2_draw_rect(&slider, vec4(1,1,1,0.25f), CORNER_T, 2.5f); + slider.y = rail.y+rail.h; + ui2_draw_rect(&slider, vec4(1,1,1,0.25f), CORNER_B, 2.5f); + + slider = handle; + ui2_margin(&slider, 5.0f, &slider); + ui2_draw_rect(&slider, vec4(1,1,1,0.25f), CORNER_ALL, 2.5f); + + return ret; +} + +int ui2_do_key_reader(void *id, const RECT *rect, int key) +{ + // process + static bool mouse_released = true; + int inside = ui_mouse_inside(rect->x, rect->y, rect->w, rect->h); + int new_key = key; + + if(!ui_mouse_button(0)) + mouse_released = true; + + if(ui_active_item() == id) + { + int k = inp_last_key(); + if (k) + { + new_key = k; + ui_set_active_item(0); + mouse_released = false; + } + } + else if(ui_hot_item() == id) + { + if(ui_mouse_button(0) && mouse_released) + ui_set_active_item(id); + } + + if(inside) + ui_set_hot_item(id); + + // draw + if (ui_active_item() == id) + ui2_draw_keyselect_button(id, "???", 0, rect, 0); + else + ui2_draw_keyselect_button(id, inp_key_name(key), 0, rect, 0); + return new_key; +} + + +static int menu2_render_menubar(RECT r) +{ + RECT box = r; + RECT button; + + int active_page = config.ui_page; + int new_page = -1; + if(menu_game_active) + active_page = -1; + + if(client_state() == CLIENTSTATE_OFFLINE) + { + if(0) // this is not done yet + { + ui2_vsplit_l(&box, 90.0f, &button, &box); + static int news_button=0; + if (ui2_do_button(&news_button, "News", active_page==PAGE_NEWS, &button, ui2_draw_menu_tab_button, 0)) + new_page = PAGE_NEWS; + ui2_vsplit_l(&box, 30.0f, 0, &box); + } + } + else + { + ui2_vsplit_l(&box, 90.0f, &button, &box); + static int game_button=0; + if (ui2_do_button(&game_button, "Game", menu_game_active, &button, ui2_draw_menu_tab_button, 0)) + menu_game_active = true; + + ui2_vsplit_l(&box, 30.0f, 0, &box); + } + + ui2_vsplit_l(&box, 110.0f, &button, &box); + static int internet_button=0; + if (ui2_do_button(&internet_button, "Internet", active_page==PAGE_INTERNET, &button, ui2_draw_menu_tab_button, 0)) + { + client_serverbrowse_refresh(0); + new_page = PAGE_INTERNET; + } + + ui2_vsplit_l(&box, 4.0f, 0, &box); + ui2_vsplit_l(&box, 90.0f, &button, &box); + static int lan_button=0; + if (ui2_do_button(&lan_button, "LAN", active_page==PAGE_LAN, &button, ui2_draw_menu_tab_button, 0)) + { + client_serverbrowse_refresh(1); + new_page = PAGE_LAN; + } + + if(0) // this one is not done yet + { + ui2_vsplit_l(&box, 4.0f, 0, &box); + ui2_vsplit_l(&box, 120.0f, &button, &box); + static int favorites_button=0; + if (ui2_do_button(&favorites_button, "Favorites", active_page==PAGE_FAVORITES, &button, ui2_draw_menu_tab_button, 0)) + new_page = PAGE_FAVORITES; + } + + /* + ui2_vsplit_r(&box, 110.0f, &box, &button); + static int system_button=0; + if (ui2_do_button(&system_button, "System", config.ui_page==PAGE_SYSTEM, &button, ui2_draw_menu_tab_button, 0)) + config.ui_page = PAGE_SYSTEM; + + ui2_vsplit_r(&box, 30.0f, &box, 0); + */ + + ui2_vsplit_r(&box, 110.0f, &box, &button); + static int quit_button=0; + if (ui2_do_button(&quit_button, "Quit", 0, &button, ui2_draw_menu_tab_button, 0)) + popup = POPUP_QUIT; + + ui2_vsplit_r(&box, 10.0f, &box, &button); + ui2_vsplit_r(&box, 110.0f, &box, &button); + static int settings_button=0; + if (ui2_do_button(&settings_button, "Settings", active_page==PAGE_SETTINGS, &button, ui2_draw_menu_tab_button, 0)) + new_page = PAGE_SETTINGS; + + if(new_page != -1) + { + config.ui_page = new_page; + menu_game_active = false; + } + + return 0; +} + +static void menu2_render_background() +{ + //gfx_clear(0.65f,0.78f,0.9f); + gfx_clear(gui_color.r, gui_color.g, gui_color.b); + //gfx_clear(0.78f,0.9f,0.65f); + + gfx_texture_set(data->images[IMAGE_BANNER].id); + gfx_quads_begin(); + gfx_setcolor(0,0,0,0.05f); + gfx_quads_setrotation(-pi/4+0.15f); + gfx_quads_draw(400, 300, 1000, 250); + gfx_quads_end(); +} + +static void menu2_render_serverbrowser(RECT main_view) +{ + ui2_draw_rect(&main_view, color_tabbar_active, CORNER_ALL, 10.0f); + + RECT view; + ui2_margin(&main_view, 10.0f, &view); + + RECT headers; + RECT filters; + RECT status; + RECT toolbox; + + //ui2_hsplit_t(&view, 20.0f, &status, &view); + ui2_hsplit_t(&view, 20.0f, &headers, &view); + ui2_hsplit_b(&view, 90.0f, &view, &filters); + ui2_hsplit_b(&view, 5.0f, &view, 0); + ui2_hsplit_b(&view, 20.0f, &view, &status); + + ui2_vsplit_r(&filters, 300.0f, &filters, &toolbox); + ui2_vsplit_r(&filters, 150.0f, &filters, 0); + + // split of the scrollbar + ui2_draw_rect(&headers, vec4(1,1,1,0.25f), CORNER_T, 5.0f); + ui2_vsplit_r(&headers, 20.0f, &headers, 0); + + struct column + { + int id; + int sort; + const char *caption; + int direction; + float width; + int flags; + RECT rect; + RECT spacer; + }; + + enum + { + FIXED=1, + SPACER=2, + + COL_FLAGS=0, + COL_NAME, + COL_GAMETYPE, + COL_MAP, + COL_PLAYERS, + COL_PING, + COL_PROGRESS, + }; + + static column cols[] = { + {-1, -1, " ", -1, 10.0f, 0, {0}, {0}}, + {COL_FLAGS, -1, " ", -1, 20.0f, 0, {0}, {0}}, + {COL_NAME, BROWSESORT_NAME, "Name", 0, 300.0f, 0, {0}, {0}}, + {COL_GAMETYPE, BROWSESORT_GAMETYPE, "Type", 1, 50.0f, 0, {0}, {0}}, + {COL_MAP, BROWSESORT_MAP, "Map", 1, 100.0f, 0, {0}, {0}}, + {COL_PLAYERS, BROWSESORT_NUMPLAYERS, "Players", 1, 60.0f, 0, {0}, {0}}, + {-1, -1, " ", 1, 10.0f, 0, {0}, {0}}, + {COL_PROGRESS, BROWSESORT_PROGRESSION, "%", 1, 20.0f, FIXED, {0}, {0}}, + {COL_PING, BROWSESORT_PING, "Ping", 1, 40.0f, FIXED, {0}, {0}}, + }; + + int num_cols = sizeof(cols)/sizeof(column); + + // do layout + for(int i = 0; i < num_cols; i++) + { + if(cols[i].direction == -1) + { + ui2_vsplit_l(&headers, cols[i].width, &cols[i].rect, &headers); + + if(i+1 < num_cols) + { + //cols[i].flags |= SPACER; + ui2_vsplit_l(&headers, 2, &cols[i].spacer, &headers); + } + } + } + + for(int i = num_cols-1; i >= 0; i--) + { + if(cols[i].direction == 1) + { + ui2_vsplit_r(&headers, cols[i].width, &headers, &cols[i].rect); + ui2_vsplit_r(&headers, 2, &headers, &cols[i].spacer); + } + } + + for(int i = 0; i < num_cols; i++) + { + if(cols[i].direction == 0) + cols[i].rect = headers; + } + + // do headers + for(int i = 0; i < num_cols; i++) + { + if(ui2_do_button(cols[i].caption, cols[i].caption, config.b_sort == cols[i].sort, &cols[i].rect, ui2_draw_grid_header, 0)) + { + if(cols[i].sort != -1) + config.b_sort = cols[i].sort; + } + } + + ui2_draw_rect(&view, vec4(0,0,0,0.15f), 0, 0); + + RECT scroll; + ui2_vsplit_r(&view, 15, &view, &scroll); + + int num_servers = client_serverbrowse_sorted_num(); + + int num = (int)(view.h/cols[0].rect.h); + static int scrollbar = 0; + static float scrollvalue = 0; + static int selected_index = -1; + ui2_hmargin(&scroll, 5.0f, &scroll); + scrollvalue = ui2_do_scrollbar_v(&scrollbar, &scroll, scrollvalue); + + int start = (int)((num_servers-num)*scrollvalue); + if(start < 0) + start = 0; + + //int r = -1; + int new_selected = selected_index; + + for (int i = start, k = 0; i < num_servers && k < num; i++, k++) + { + int item_index = i; + SERVER_INFO *item = client_serverbrowse_sorted_get(item_index); + RECT row; + + int l = selected_index==item_index; + + if(l) + { + // selected server, draw the players on it + RECT whole; + int h = (item->num_players+2)/3; + + ui2_hsplit_t(&view, 25.0f+h*15.0f, &whole, &view); + + RECT r = whole; + ui2_margin(&r, 1.5f, &r); + ui2_draw_rect(&r, vec4(1,1,1,0.5f), CORNER_ALL, 4.0f); + + ui2_hsplit_t(&whole, 20.0f, &row, &whole); + ui2_vsplit_l(&whole, 50.0f, 0, &whole); + + for(int p = 0; p < item->num_players; p+=3) + { + RECT player_row; + RECT player_rect; + RECT player_score; + RECT player_name; + ui2_hsplit_t(&whole, 15.0f, &player_row, &whole); + + for(int a = 0; a < 3; a++) + { + if(p+a >= item->num_players) + break; + + ui2_vsplit_l(&player_row, 170.0f, &player_rect, &player_row); + ui2_vsplit_l(&player_rect, 30.0f, &player_score, &player_name); + ui2_vsplit_l(&player_name, 10.0f, 0, &player_name); + char buf[32]; + sprintf(buf, "%d", item->player_scores[p+a]); + ui2_do_label(&player_score, buf, 16.0f, 1); + ui2_do_label(&player_name, item->player_names[p+a], 16.0f, -1); + } + } + + k += h*3/4; + } + else + ui2_hsplit_t(&view, 20.0f, &row, &view); + + if(ui2_do_button(item, "", l, &row, 0, 0)) + { + new_selected = item_index; + dbg_msg("dbg", "addr = %s", item->address); + strncpy(config.ui_server_address, item->address, sizeof(config.ui_server_address)); + if(inp_mouse_doubleclick()) + client_connect(config.ui_server_address); + } + + for(int c = 0; c < num_cols; c++) + { + RECT button; + char temp[64]; + button.x = cols[c].rect.x; + button.y = row.y; + button.h = row.h; + button.w = cols[c].rect.w; + + //int s = 0; + int id = cols[c].id; + + //s = ui2_do_button(item, "L", l, &button, ui2_draw_browse_icon, 0); + + if(id == COL_FLAGS) + { + if(item->flags&1) + ui2_draw_browse_icon(0x100, &button); + } + else if(id == COL_NAME) + ui2_do_label(&button, item->name, 20.0f, -1); + else if(id == COL_MAP) + ui2_do_label(&button, item->map, 20.0f, -1); + else if(id == COL_PLAYERS) + { + sprintf(temp, "%i/%i", item->num_players, item->max_players); + ui2_do_label(&button, temp, 20.0f, 1); + } + else if(id == COL_PING) + { + sprintf(temp, "%i", item->latency); + ui2_do_label(&button, temp, 20.0f, 1); + } + else if(id == COL_PROGRESS) + { + ui2_draw_browse_icon(item->progression, &button); + } + else if(id == COL_GAMETYPE) + { + const char *type = "???"; + if(item->game_type == GAMETYPE_DM) type = "DM"; + else if(item->game_type == GAMETYPE_TDM) type = "TDM"; + else if(item->game_type == GAMETYPE_CTF) type = "CTF"; + ui2_do_label(&button, type, 20.0f, 0); + } + /* + if(s) + { + new_selected = item_index; + dbg_msg("dbg", "addr = %s", item->address); + strncpy(config.ui_server_address, item->address, sizeof(config.ui_server_address)); + }*/ + } + } + + selected_index = new_selected; + + + // render quick search + RECT button; + ui2_hsplit_t(&filters, 20.0f, &button, &filters); + ui2_do_label(&button, "Quick search: ", 18, -1); + ui2_vsplit_l(&button, 95.0f, 0, &button); + ui2_do_edit_box(&config.b_filter_string, &button, config.b_filter_string, sizeof(config.b_filter_string)); + + // render filters + ui2_hsplit_t(&filters, 20.0f, &button, &filters); + if (ui2_do_button(&config.b_filter_empty, "Has people playing", config.b_filter_empty, &button, ui2_draw_checkbox, 0)) + config.b_filter_empty ^= 1; + + ui2_hsplit_t(&filters, 20.0f, &button, &filters); + if (ui2_do_button(&config.b_filter_full, "Server not full", config.b_filter_full, &button, ui2_draw_checkbox, 0)) + config.b_filter_full ^= 1; + + ui2_hsplit_t(&filters, 20.0f, &button, &filters); + if (ui2_do_button(&config.b_filter_pw, "Is not password protected", config.b_filter_pw, &button, ui2_draw_checkbox, 0)) + config.b_filter_pw ^= 1; + + + // render status + ui2_draw_rect(&status, vec4(1,1,1,0.25f), CORNER_B, 5.0f); + ui2_vmargin(&status, 50.0f, &status); + char buf[128]; + sprintf(buf, "%d of %d servers", client_serverbrowse_sorted_num(), client_serverbrowse_num()); + ui2_do_label(&status, buf, 18.0f, -1); + + // render toolbox + { + RECT buttons, button; + ui2_hsplit_b(&toolbox, 25.0f, &toolbox, &buttons); + + ui2_vsplit_r(&buttons, 100.0f, &buttons, &button); + ui2_vmargin(&button, 2.0f, &button); + static int join_button = 0; + if(ui2_do_button(&join_button, "Connect", 0, &button, ui2_draw_menu_button, 0)) + client_connect(config.ui_server_address); + + ui2_vsplit_r(&buttons, 20.0f, &buttons, &button); + ui2_vsplit_r(&buttons, 100.0f, &buttons, &button); + ui2_vmargin(&button, 2.0f, &button); + static int refresh_button = 0; + if(ui2_do_button(&refresh_button, "Refresh", 0, &button, ui2_draw_menu_button, 0)) + { + if(config.ui_page == PAGE_INTERNET) + client_serverbrowse_refresh(0); + else if(config.ui_page == PAGE_LAN) + client_serverbrowse_refresh(1); + } + + ui2_hsplit_t(&toolbox, 20.0f, &button, &toolbox); + ui2_do_label(&button, "Host address:", 18, -1); + ui2_vsplit_l(&button, 100.0f, 0, &button); + ui2_do_edit_box(&config.ui_server_address, &button, config.ui_server_address, sizeof(config.ui_server_address)); + } +} + +static void menu2_render_settings_player(RECT main_view) +{ + RECT button; + RECT skinselection; + ui2_vsplit_l(&main_view, 300.0f, &main_view, &skinselection); + + + ui2_hsplit_t(&main_view, 20.0f, &button, &main_view); + + // render settings + { + ui2_hsplit_t(&main_view, 20.0f, &button, &main_view); + ui2_do_label(&button, "Name:", 18.0, -1); + ui2_vsplit_l(&button, 80.0f, 0, &button); + ui2_vsplit_l(&button, 180.0f, &button, 0); + ui2_do_edit_box(config.player_name, &button, config.player_name, sizeof(config.player_name)); + + ui2_hsplit_t(&main_view, 20.0f, &button, &main_view); + if (ui2_do_button(&config.cl_dynamic_camera, "Dynamic camera", config.cl_dynamic_camera, &button, ui2_draw_checkbox, 0)) + config.cl_dynamic_camera ^= 1; + + ui2_hsplit_t(&main_view, 20.0f, &button, &main_view); + if (ui2_do_button(&config.cl_autoswitch_weapons, "Switch weapon on pickup", config.cl_autoswitch_weapons, &button, ui2_draw_checkbox, 0)) + config.cl_autoswitch_weapons ^= 1; + + ui2_hsplit_t(&main_view, 20.0f, &button, &main_view); + if (ui2_do_button(&config.cl_nameplates, "Show name plates", config.cl_nameplates, &button, ui2_draw_checkbox, 0)) + config.cl_nameplates ^= 1; + + if(config.cl_nameplates) + { + ui2_hsplit_t(&main_view, 20.0f, &button, &main_view); + ui2_vsplit_l(&button, 15.0f, 0, &button); + if (ui2_do_button(&config.cl_nameplates_always, "Always show name plates", config.cl_nameplates_always, &button, ui2_draw_checkbox, 0)) + config.cl_nameplates_always ^= 1; + } + + ui2_hsplit_t(&main_view, 20.0f, &button, &main_view); + + ui2_hsplit_t(&main_view, 20.0f, &button, &main_view); + if (ui2_do_button(&config.player_color_body, "Custom colors", config.player_use_custom_color, &button, ui2_draw_checkbox, 0)) + config.player_use_custom_color = config.player_use_custom_color?0:1; + + if(config.player_use_custom_color) + { + int *colors[2]; + colors[0] = &config.player_color_body; + colors[1] = &config.player_color_feet; + + const char *parts[] = {"Body", "Feet"}; + const char *labels[] = {"Hue", "Sat.", "Lht."}; + static int color_slider[2][3] = {{0}}; + //static float v[2][3] = {{0, 0.5f, 0.25f}, {0, 0.5f, 0.25f}}; + + for(int i = 0; i < 2; i++) + { + RECT text; + ui2_hsplit_t(&main_view, 20.0f, &text, &main_view); + ui2_vsplit_l(&text, 15.0f, 0, &text); + ui2_do_label(&text, parts[i], 18, -1); + + int prevcolor = *colors[i]; + int color = 0; + for(int s = 0; s < 3; s++) + { + RECT text; + ui2_hsplit_t(&main_view, 19.0f, &button, &main_view); + ui2_vsplit_l(&button, 30.0f, 0, &button); + ui2_vsplit_l(&button, 30.0f, &text, &button); + ui2_vsplit_r(&button, 5.0f, &button, 0); + ui2_hsplit_t(&button, 4.0f, 0, &button); + + float k = ((prevcolor>>((2-s)*8))&0xff) / 255.0f; + k = ui2_do_scrollbar_h(&color_slider[i][s], &button, k); + color <<= 8; + color += clamp((int)(k*255), 0, 255); + ui2_do_label(&text, labels[s], 20, -1); + + } + + *colors[i] = color; + ui2_hsplit_t(&main_view, 5.0f, 0, &main_view); + } + } + } + + // draw header + RECT header, footer; + ui2_hsplit_t(&skinselection, 20, &header, &skinselection); + ui2_draw_rect(&header, vec4(1,1,1,0.25f), CORNER_T, 5.0f); + ui2_do_label(&header, "Skins", 18.0f, 0); + + // draw footers + ui2_hsplit_b(&skinselection, 20, &skinselection, &footer); + ui2_draw_rect(&footer, vec4(1,1,1,0.25f), CORNER_B, 5.0f); + ui2_vsplit_l(&footer, 10.0f, 0, &footer); + + // modes + ui2_draw_rect(&skinselection, vec4(0,0,0,0.15f), 0, 0); + + RECT scroll; + ui2_vsplit_r(&skinselection, 15, &skinselection, &scroll); + + RECT list = skinselection; + ui2_hsplit_t(&list, 50, &button, &list); + + int num = (int)(skinselection.h/button.h); + static float scrollvalue = 0; + static int scrollbar = 0; + ui2_hmargin(&scroll, 5.0f, &scroll); + scrollvalue = ui2_do_scrollbar_v(&scrollbar, &scroll, scrollvalue); + + int start = (int)((skin_num()-num)*scrollvalue); + if(start < 0) + start = 0; + + animstate state; + anim_eval(&data->animations[ANIM_BASE], 0, &state); + anim_eval_add(&state, &data->animations[ANIM_IDLE], 0, 1.0f); + //anim_eval_add(&state, &data->animations[ANIM_WALK], fmod(client_localtime(), 1.0f), 1.0f); + + for(int i = start; i < start+num && i < skin_num(); i++) + { + const skin *s = skin_get(i); + char buf[128]; + sprintf(buf, "%s", s->name); + int selected = 0; + if(strcmp(s->name, config.player_skin) == 0) + selected = 1; + + tee_render_info info; + info.texture = s->org_texture; + info.color_body = vec4(1,1,1,1); + info.color_feet = vec4(1,1,1,1); + if(config.player_use_custom_color) + { + info.color_body = skin_get_color(config.player_color_body); + info.color_feet = skin_get_color(config.player_color_feet); + info.texture = s->color_texture; + } + + info.size = ui2_scale()*50.0f; + + RECT icon; + RECT text; + ui2_vsplit_l(&button, 50.0f, &icon, &text); + + if(ui2_do_button(s, "", selected, &button, ui2_draw_list_row, 0)) + config_set_player_skin(&config, s->name); + + ui2_hsplit_t(&text, 12.0f, 0, &text); // some margin from the top + ui2_do_label(&text, buf, 24, 0); + + ui2_hsplit_t(&icon, 5.0f, 0, &icon); // some margin from the top + render_tee(&state, &info, 0, vec2(1, 0), vec2(icon.x+icon.w/2, icon.y+icon.h/2)); + + ui2_hsplit_t(&list, 50, &button, &list); + } +} + +typedef void (*assign_func_callback)(CONFIGURATION *config, int value); + +static void menu2_render_settings_controls(RECT main_view) +{ + ui2_vsplit_l(&main_view, 300.0f, &main_view, 0); + + { + RECT button, label; + ui2_hsplit_t(&main_view, 20.0f, &button, &main_view); + ui2_vsplit_l(&button, 110.0f, &label, &button); + ui2_do_label(&label, "Mouse sens.", 18.0f, -1); + ui2_hmargin(&button, 2.0f, &button); + config.inp_mousesens = (int)(ui2_do_scrollbar_h(&config.inp_mousesens, &button, config.inp_mousesens/500.0f)*500.0f); + //*key.key = ui2_do_key_reader(key.key, &button, *key.key); + ui2_hsplit_t(&main_view, 20.0f, 0, &main_view); + } + + typedef struct + { + char name[32]; + int *key; + } KEYINFO; + + const KEYINFO keys[] = + { + { "Move Left:", &config.key_move_left }, + { "Move Right:", &config.key_move_right }, + { "Jump:", &config.key_jump }, + { "Fire:", &config.key_fire }, + { "Hook:", &config.key_hook }, + { "Hammer:", &config.key_weapon1 }, + { "Pistol:", &config.key_weapon2 }, + { "Shotgun:", &config.key_weapon3 }, + { "Grenade:", &config.key_weapon4 }, + { "Next Weapon:", &config.key_next_weapon }, + { "Prev. Weapon:", &config.key_prev_weapon }, + { "Emoticon:", &config.key_emoticon }, + { "Chat:", &config.key_chat }, + { "Team Chat:", &config.key_teamchat }, + { "Console:", &config.key_console }, + { "Remote Console:", &config.key_remoteconsole }, + { "Screenshot:", &config.key_screenshot }, + }; + + const int key_count = sizeof(keys) / sizeof(KEYINFO); + + for (int i = 0; i < key_count; i++) + { + KEYINFO key = keys[i]; + RECT button, label; + ui2_hsplit_t(&main_view, 20.0f, &button, &main_view); + ui2_vsplit_l(&button, 110.0f, &label, &button); + + ui2_do_label(&label, key.name, 18.0f, -1); + *key.key = ui2_do_key_reader(key.key, &button, *key.key); + ui2_hsplit_t(&main_view, 5.0f, 0, &main_view); + } +} + +static void menu2_render_settings_graphics(RECT main_view) +{ + RECT button; + char buf[128]; + + static const int MAX_RESOLUTIONS = 256; + static VIDEO_MODE modes[MAX_RESOLUTIONS]; + static int num_modes = -1; + + if(num_modes == -1) + num_modes = gfx_get_video_modes(modes, MAX_RESOLUTIONS); + + RECT modelist; + ui2_vsplit_l(&main_view, 300.0f, &main_view, &modelist); + + // draw allmodes switch + RECT header, footer; + ui2_hsplit_t(&modelist, 20, &button, &modelist); + if (ui2_do_button(&config.gfx_display_all_modes, "Show only supported", config.gfx_display_all_modes^1, &button, ui2_draw_checkbox, 0)) + { + config.gfx_display_all_modes ^= 1; + num_modes = gfx_get_video_modes(modes, MAX_RESOLUTIONS); + } + + // draw header + ui2_hsplit_t(&modelist, 20, &header, &modelist); + ui2_draw_rect(&header, vec4(1,1,1,0.25f), CORNER_T, 5.0f); + ui2_do_label(&header, "Display Modes", 18.0f, 0); + + // draw footers + ui2_hsplit_b(&modelist, 20, &modelist, &footer); + sprintf(buf, "Current: %dx%d %d bit", config.gfx_screen_width, config.gfx_screen_height, config.gfx_color_depth); + ui2_draw_rect(&footer, vec4(1,1,1,0.25f), CORNER_B, 5.0f); + ui2_vsplit_l(&footer, 10.0f, 0, &footer); + ui2_do_label(&footer, buf, 18.0f, -1); + + // modes + ui2_draw_rect(&modelist, vec4(0,0,0,0.15f), 0, 0); + + RECT scroll; + ui2_vsplit_r(&modelist, 15, &modelist, &scroll); + + RECT list = modelist; + ui2_hsplit_t(&list, 20, &button, &list); + + int num = (int)(modelist.h/button.h); + static float scrollvalue = 0; + static int scrollbar = 0; + ui2_hmargin(&scroll, 5.0f, &scroll); + scrollvalue = ui2_do_scrollbar_v(&scrollbar, &scroll, scrollvalue); + + int start = (int)((num_modes-num)*scrollvalue); + if(start < 0) + start = 0; + + for(int i = start; i < start+num && i < num_modes; i++) + { + int depth = modes[i].red+modes[i].green+modes[i].blue; + if(depth < 16) + depth = 16; + else if(depth > 16) + depth = 24; + + int selected = 0; + if(config.gfx_color_depth == depth && + config.gfx_screen_width == modes[i].width && + config.gfx_screen_height == modes[i].height) + { + selected = 1; + } + + sprintf(buf, " %dx%d %d bit", modes[i].width, modes[i].height, depth); + if(ui2_do_button(&modes[i], buf, selected, &button, ui2_draw_list_row, 0)) + { + config.gfx_color_depth = depth; + config.gfx_screen_width = modes[i].width; + config.gfx_screen_height = modes[i].height; + } + + ui2_hsplit_t(&list, 20, &button, &list); + } + + + // switches + ui2_hsplit_t(&main_view, 20.0f, &button, &main_view); + if (ui2_do_button(&config.gfx_fullscreen, "Fullscreen", config.gfx_fullscreen, &button, ui2_draw_checkbox, 0)) + config.gfx_fullscreen ^= 1; + + ui2_hsplit_t(&main_view, 20.0f, &button, &main_view); + if (ui2_do_button(&config.gfx_vsync, "V-Sync", config.gfx_vsync, &button, ui2_draw_checkbox, 0)) + config.gfx_vsync ^= 1; + + ui2_hsplit_t(&main_view, 20.0f, &button, &main_view); + if (ui2_do_button(&config.gfx_fsaa_samples, "FSAA samples", config.gfx_fsaa_samples, &button, ui2_draw_checkbox_number, 0)) + { + if(config.gfx_fsaa_samples < 2) config.gfx_fsaa_samples = 2; + else if(config.gfx_fsaa_samples < 4) config.gfx_fsaa_samples = 4; + else if(config.gfx_fsaa_samples < 6) config.gfx_fsaa_samples = 6; + else if(config.gfx_fsaa_samples < 8) config.gfx_fsaa_samples = 8; + else if(config.gfx_fsaa_samples < 16) config.gfx_fsaa_samples = 16; + else if(config.gfx_fsaa_samples >= 16) config.gfx_fsaa_samples = 0; + } + + ui2_hsplit_t(&main_view, 40.0f, &button, &main_view); + ui2_hsplit_t(&main_view, 20.0f, &button, &main_view); + if (ui2_do_button(&config.gfx_texture_quality, "Quality Textures", config.gfx_texture_quality, &button, ui2_draw_checkbox, 0)) + config.gfx_texture_quality ^= 1; + + ui2_hsplit_t(&main_view, 20.0f, &button, &main_view); + if (ui2_do_button(&config.gfx_texture_compression, "Texture Compression", config.gfx_texture_compression, &button, ui2_draw_checkbox, 0)) + config.gfx_texture_compression ^= 1; + + ui2_hsplit_t(&main_view, 20.0f, &button, &main_view); + if (ui2_do_button(&config.gfx_high_detail, "High Detail", config.gfx_high_detail, &button, ui2_draw_checkbox, 0)) + config.gfx_high_detail ^= 1; + + // + + RECT text; + ui2_hsplit_t(&main_view, 20.0f, 0, &main_view); + ui2_hsplit_t(&main_view, 20.0f, &text, &main_view); + //ui2_vsplit_l(&text, 15.0f, 0, &text); + ui2_do_label(&text, "UI Color", 18, -1); + + const char *labels[] = {"Hue", "Sat.", "Lht.", "Alpha"}; + int *color_slider[4] = {&config.ui_color_hue, &config.ui_color_sat, &config.ui_color_lht, &config.ui_color_alpha}; + for(int s = 0; s < 4; s++) + { + RECT text; + ui2_hsplit_t(&main_view, 19.0f, &button, &main_view); + ui2_vmargin(&button, 15.0f, &button); + ui2_vsplit_l(&button, 30.0f, &text, &button); + ui2_vsplit_r(&button, 5.0f, &button, 0); + ui2_hsplit_t(&button, 4.0f, 0, &button); + + float k = (*color_slider[s]) / 255.0f; + k = ui2_do_scrollbar_h(color_slider[s], &button, k); + *color_slider[s] = (int)(k*255.0f); + ui2_do_label(&text, labels[s], 20, -1); + } +} + +static void menu2_render_settings_sound(RECT main_view) +{ + RECT button; + ui2_vsplit_l(&main_view, 300.0f, &main_view, 0); + + ui2_hsplit_t(&main_view, 20.0f, &button, &main_view); + if (ui2_do_button(&config.snd_enable, "Use Sounds", config.snd_enable, &button, ui2_draw_checkbox, 0)) + config.snd_enable ^= 1; + + if(!config.snd_enable) + return; + + // sample rate box + { + char buf[64]; + sprintf(buf, "%d", config.snd_rate); + ui2_hsplit_t(&main_view, 20.0f, &button, &main_view); + ui2_do_label(&button, "Sample Rate", 18.0, -1); + ui2_vsplit_l(&button, 110.0f, 0, &button); + ui2_vsplit_l(&button, 180.0f, &button, 0); + ui2_do_edit_box(&config.snd_rate, &button, buf, sizeof(buf)); + config.snd_rate = atoi(buf); + + if(config.snd_rate < 1) + config.snd_rate = 1; + } + + // volume slider + { + RECT button, label; + ui2_hsplit_t(&main_view, 5.0f, &button, &main_view); + ui2_hsplit_t(&main_view, 20.0f, &button, &main_view); + ui2_vsplit_l(&button, 110.0f, &label, &button); + ui2_hmargin(&button, 2.0f, &button); + ui2_do_label(&label, "Sound Volume", 18.0f, -1); + config.snd_volume = (int)(ui2_do_scrollbar_h(&config.snd_volume, &button, config.snd_volume/100.0f)*100.0f); + ui2_hsplit_t(&main_view, 20.0f, 0, &main_view); + } +} + + +static void menu2_render_settings_network(RECT main_view) +{ + RECT button; + ui2_vsplit_l(&main_view, 300.0f, &main_view, 0); + + { + ui2_hsplit_t(&main_view, 20.0f, &button, &main_view); + ui2_do_label(&button, "Rcon Password", 18.0, -1); + ui2_vsplit_l(&button, 110.0f, 0, &button); + ui2_vsplit_l(&button, 180.0f, &button, 0); + ui2_do_edit_box(&config.rcon_password, &button, config.rcon_password, sizeof(config.rcon_password), true); + } +} + +static void menu2_render_settings(RECT main_view) +{ + static int settings_page = 0; + + // render background + RECT temp, tabbar; + ui2_vsplit_r(&main_view, 120.0f, &main_view, &tabbar); + ui2_draw_rect(&main_view, color_tabbar_active, CORNER_B|CORNER_TL, 10.0f); + ui2_hsplit_t(&tabbar, 50.0f, &temp, &tabbar); + ui2_draw_rect(&temp, color_tabbar_active, CORNER_R, 10.0f); + + ui2_hsplit_t(&main_view, 10.0f, 0, &main_view); + + RECT button; + + const char *tabs[] = {"Player", "Controls", "Network", "Graphics", "Sound"}; + int num_tabs = (int)(sizeof(tabs)/sizeof(*tabs)); + + for(int i = 0; i < num_tabs; i++) + { + ui2_hsplit_t(&tabbar, 10, &button, &tabbar); + ui2_hsplit_t(&tabbar, 26, &button, &tabbar); + if(ui2_do_button(tabs[i], tabs[i], settings_page == i, &button, ui2_draw_settings_tab_button, 0)) + settings_page = i; + } + + ui2_margin(&main_view, 10.0f, &main_view); + + if(settings_page == 0) + menu2_render_settings_player(main_view); + else if(settings_page == 1) + menu2_render_settings_controls(main_view); + else if(settings_page == 2) + menu2_render_settings_network(main_view); + else if(settings_page == 3) + menu2_render_settings_graphics(main_view); + else if(settings_page == 4) + menu2_render_settings_sound(main_view); +} + +static void menu2_render_news(RECT main_view) +{ + ui2_draw_rect(&main_view, color_tabbar_active, CORNER_ALL, 10.0f); +} + + +static void menu2_render_game(RECT main_view) +{ + RECT button; + ui2_hsplit_t(&main_view, 45.0f, &main_view, 0); + ui2_draw_rect(&main_view, color_tabbar_active, CORNER_ALL, 10.0f); + + ui2_hsplit_t(&main_view, 10.0f, 0, &main_view); + ui2_hsplit_t(&main_view, 25.0f, &main_view, 0); + ui2_vmargin(&main_view, 10.0f, &main_view); + + ui2_vsplit_r(&main_view, 120.0f, &main_view, &button); + static int disconnect_button = 0; + if(ui2_do_button(&disconnect_button, "Disconnect", 0, &button, ui2_draw_menu_button, 0)) + client_disconnect(); + + if(local_info) + { + if(local_info->team != -1) + { + ui2_vsplit_l(&main_view, 10.0f, &button, &main_view); + ui2_vsplit_l(&main_view, 120.0f, &button, &main_view); + static int spectate_button = 0; + if(ui2_do_button(&spectate_button, "Spectate", 0, &button, ui2_draw_menu_button, 0)) + { + config.cl_team = -1; + menu_active = false; + } + } + + if(local_info->team != 0) + { + ui2_vsplit_l(&main_view, 10.0f, &button, &main_view); + ui2_vsplit_l(&main_view, 120.0f, &button, &main_view); + static int spectate_button = 0; + if(ui2_do_button(&spectate_button, "Join Red", 0, &button, ui2_draw_menu_button, 0)) + { + config.cl_team = 0; + menu_active = false; + } + } + + if(local_info->team != 1) + { + ui2_vsplit_l(&main_view, 10.0f, &button, &main_view); + ui2_vsplit_l(&main_view, 120.0f, &button, &main_view); + static int spectate_button = 0; + if(ui2_do_button(&spectate_button, "Join Blue", 0, &button, ui2_draw_menu_button, 0)) + { + config.cl_team = 1; + menu_active = false; + } + } + } +} + +void menu_do_disconnected() +{ + popup = POPUP_NONE; + if(client_error_string() && client_error_string()[0] != 0) + { + if(strstr(client_error_string(), "password")) + { + popup = POPUP_PASSWORD; + ui_set_hot_item(&config.password); + ui_set_active_item(&config.password); + } + else + popup = POPUP_DISCONNECTED; + } +} + +void menu_do_connecting() +{ + popup = POPUP_CONNECTING; +} + +void menu_do_connected() +{ + popup = POPUP_NONE; +} + +int menu2_render() +{ + if(0) + { + gfx_mapscreen(0,0,10*4/3.0f,10); + gfx_clear(gui_color.r, gui_color.g, gui_color.b); + + animstate state; + anim_eval(&data->animations[ANIM_BASE], 0, &state); + anim_eval_add(&state, &data->animations[ANIM_IDLE], 0, 1.0f); + //anim_eval_add(&state, &data->animations[ANIM_WALK], fmod(client_localtime(), 1.0f), 1.0f); + + for(int i = 0; i < skin_num(); i++) + { + float x = (i/8)*3; + float y = (i%8); + for(int c = 0; c < 2; c++) + { + //int colors[2] = {54090, 10998628}; + //int colors[2] = {65432, 9895832}; // NEW + int colors[2] = {65387, 10223467}; // NEW + + tee_render_info info; + info.texture = skin_get(i)->color_texture; + info.color_feet = info.color_body = skin_get_color(colors[c]); + //info.color_feet = info.color_body = vec4(1,1,1,1); + info.size = 1.0f; //ui2_scale()*16.0f; + //render_tee(&state, &info, 0, vec2(sinf(client_localtime()*3), cosf(client_localtime()*3)), vec2(1+x+c,1+y)); + render_tee(&state, &info, 0, vec2(1,0), vec2(1+x+c,1+y)); + } + } + + return 0; + } + + RECT screen = *ui2_screen(); + gfx_mapscreen(screen.x, screen.y, screen.w, screen.h); + + static bool first = true; + if(first) + { + if(config.ui_page == PAGE_INTERNET) + client_serverbrowse_refresh(0); + else if(config.ui_page == PAGE_LAN) + client_serverbrowse_refresh(1); + first = false; + } + + if(client_state() == CLIENTSTATE_ONLINE) + { + color_tabbar_inactive = color_tabbar_inactive_ingame; + color_tabbar_active = color_tabbar_active_ingame; + } + else + { + menu2_render_background(); + color_tabbar_inactive = color_tabbar_inactive_outgame; + color_tabbar_active = color_tabbar_active_outgame; + } + + RECT tab_bar; + RECT main_view; + + // some margin around the screen + ui2_margin(&screen, 10.0f, &screen); + + if(popup == POPUP_NONE) + { + // do tab bar + ui2_hsplit_t(&screen, 26.0f, &tab_bar, &main_view); + ui2_vmargin(&tab_bar, 20.0f, &tab_bar); + menu2_render_menubar(tab_bar); + + // render current page + if(menu_game_active) + menu2_render_game(main_view); + else if(config.ui_page == PAGE_NEWS) + menu2_render_news(main_view); + else if(config.ui_page == PAGE_INTERNET) + menu2_render_serverbrowser(main_view); + else if(config.ui_page == PAGE_LAN) + menu2_render_serverbrowser(main_view); + else if(config.ui_page == PAGE_FAVORITES) + menu2_render_serverbrowser(main_view); + else if(config.ui_page == PAGE_SETTINGS) + menu2_render_settings(main_view); + } + else + { + // make sure that other windows doesn't do anything funnay! + //ui_set_hot_item(0); + //ui_set_active_item(0); + const char *title = ""; + const char *extra_text = ""; + const char *button_text = ""; + + if(popup == POPUP_CONNECTING) + { + title = "Connecting to"; + extra_text = config.ui_server_address; // TODO: query the client about the address + button_text = "Abort"; + } + else if(popup == POPUP_DISCONNECTED) + { + title = "Disconnected"; + extra_text = client_error_string(); + button_text = "Ok"; + } + else if(popup == POPUP_PASSWORD) + { + title = "Password Error"; + extra_text = client_error_string(); + button_text = "Try Again"; + } + else if(popup == POPUP_QUIT) + { + title = "Quit"; + extra_text = "Are you sure that you want to quit?"; + } + + + RECT box, part; + box = screen; + ui2_vmargin(&box, 150.0f, &box); + ui2_hmargin(&box, 150.0f, &box); + + // render the box + ui2_draw_rect(&box, vec4(0,0,0,0.5f), CORNER_ALL, 15.0f); + + ui2_hsplit_t(&box, 20.f, &part, &box); + ui2_hsplit_t(&box, 24.f, &part, &box); + ui2_do_label(&part, title, 24.f, 0); + ui2_hsplit_t(&box, 20.f, &part, &box); + ui2_hsplit_t(&box, 24.f, &part, &box); + ui2_do_label(&part, extra_text, 20.f, 0); + + if(popup == POPUP_QUIT) + { + RECT tryagain, abort; + ui2_hsplit_b(&box, 20.f, &box, &part); + ui2_hsplit_b(&box, 24.f, &box, &part); + ui2_vmargin(&part, 120.0f, &part); + + ui2_vsplit_l(&part, 100.0f, &abort, &part); + ui2_vsplit_r(&part, 100.0f, 0, &tryagain); + + static int button_abort = 0; + if(ui2_do_button(&button_abort, "No", 0, &abort, ui2_draw_menu_button, 0) || inp_key_down(KEY_ESC)) + popup = POPUP_NONE; + + static int button_tryagain = 0; + if(ui2_do_button(&button_tryagain, "Yes", 0, &tryagain, ui2_draw_menu_button, 0) || inp_key_down(KEY_ENTER)) + client_quit(); + } + else if(popup == POPUP_PASSWORD) + { + RECT label, textbox, tryagain, abort; + + ui2_hsplit_b(&box, 20.f, &box, &part); + ui2_hsplit_b(&box, 24.f, &box, &part); + ui2_vmargin(&part, 120.0f, &part); + + ui2_vsplit_l(&part, 100.0f, &abort, &part); + ui2_vsplit_r(&part, 100.0f, 0, &tryagain); + + static int button_abort = 0; + if(ui2_do_button(&button_abort, "Abort", 0, &abort, ui2_draw_menu_button, 0) || inp_key_down(KEY_ESC)) + popup = POPUP_NONE; + + static int button_tryagain = 0; + if(ui2_do_button(&button_tryagain, "Try again", 0, &tryagain, ui2_draw_menu_button, 0) || inp_key_down(KEY_ENTER)) + { + client_connect(config.ui_server_address); + } + + ui2_hsplit_b(&box, 60.f, &box, &part); + ui2_hsplit_b(&box, 24.f, &box, &part); + + ui2_vsplit_l(&part, 60.0f, 0, &label); + ui2_vsplit_l(&label, 100.0f, 0, &textbox); + ui2_vsplit_l(&textbox, 20.0f, 0, &textbox); + ui2_vsplit_r(&textbox, 60.0f, &textbox, 0); + ui2_do_label(&label, "Password:", 20, -1); + ui2_do_edit_box(&config.password, &textbox, config.password, sizeof(config.password), true); + } + else + { + ui2_hsplit_b(&box, 20.f, &box, &part); + ui2_hsplit_b(&box, 24.f, &box, &part); + ui2_vmargin(&part, 120.0f, &part); + + static int button = 0; + if(ui2_do_button(&button, button_text, 0, &part, ui2_draw_menu_button, 0) || inp_key_down(KEY_ESC) || inp_key_down(KEY_ENTER)) + { + if(popup == POPUP_CONNECTING) + client_disconnect(); + popup = POPUP_NONE; + } + } + } + + return 0; +} + +void modmenu_render() +{ + static int mouse_x = 0; + static int mouse_y = 0; + + // update colors + + vec3 rgb = hsl_to_rgb(vec3(config.ui_color_hue/255.0f, config.ui_color_sat/255.0f, config.ui_color_lht/255.0f)); + gui_color = vec4(rgb.r, rgb.g, rgb.b, config.ui_color_alpha/255.0f); + + color_tabbar_inactive_outgame = vec4(0,0,0,0.25f); + color_tabbar_active_outgame = vec4(0,0,0,0.5f); + + color_ingame_scale_i = 0.5f; + color_ingame_scale_a = 0.2f; + color_tabbar_inactive_ingame = vec4( + gui_color.r*color_ingame_scale_i, + gui_color.g*color_ingame_scale_i, + gui_color.b*color_ingame_scale_i, + gui_color.a*0.8f); + + color_tabbar_active_ingame = vec4( + gui_color.r*color_ingame_scale_a, + gui_color.g*color_ingame_scale_a, + gui_color.b*color_ingame_scale_a, + gui_color.a); + + + // handle mouse movement + float mx, my; + { + int rx, ry; + inp_mouse_relative(&rx, &ry); + mouse_x += rx; + mouse_y += ry; + if(mouse_x < 0) mouse_x = 0; + if(mouse_y < 0) mouse_y = 0; + if(mouse_x > gfx_screenwidth()) mouse_x = gfx_screenwidth(); + if(mouse_y > gfx_screenheight()) mouse_y = gfx_screenheight(); + + // update the ui + RECT *screen = ui2_screen(); + mx = (mouse_x/(float)gfx_screenwidth())*screen->w; + my = (mouse_y/(float)gfx_screenheight())*screen->h; + + int buttons = 0; + if(inp_key_pressed(KEY_MOUSE_1)) buttons |= 1; + if(inp_key_pressed(KEY_MOUSE_2)) buttons |= 2; + if(inp_key_pressed(KEY_MOUSE_3)) buttons |= 4; + + ui_update(mx,my,mx*3.0f,my*3.0f,buttons); + } + + menu2_render(); + + gfx_texture_set(data->images[IMAGE_CURSOR].id); + gfx_quads_begin(); + gfx_setcolor(1,1,1,1); + gfx_quads_drawTL(mx,my,24,24); + gfx_quads_end(); + + inp_clear(); +} diff --git a/src/game/client/gc_menu.h b/src/game/client/gc_menu.h new file mode 100644 index 00000000..5c68c53d --- /dev/null +++ b/src/game/client/gc_menu.h @@ -0,0 +1,16 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#ifndef __MENU_H +#define __MENU_H + +void draw_image_button(void *id, const char *text, int checked, float x, float y, float w, float h, void *extra); +void draw_single_part_button(void *id, const char *text, int checked, float x, float y, float w, float h, void *extra); +void draw_menu_button(void *id, const char *text, int checked, float x, float y, float w, float h, void *extra); +void draw_teewars_button(void *id, const char *text, int checked, float x, float y, float w, float h, void *extra); +int ui_do_key_reader(void *id, float x, float y, float w, float h, int key); +int ui_do_combo_box(void *id, float x, float y, float w, char *lines, int line_count, int selected_index); +int ui_do_edit_box(void *id, float x, float y, float w, float h, char *str, int str_size); +int ui_do_check_box(void *id, float x, float y, float w, float h, int value); +int do_scroll_bar_horiz(void *id, float x, float y, float width, int steps, int last_index); +int do_scroll_bar_vert(void *id, float x, float y, float height, int steps, int last_index); + +#endif diff --git a/src/game/client/gc_render.h b/src/game/client/gc_render.h new file mode 100644 index 00000000..fc85d49a --- /dev/null +++ b/src/game/client/gc_render.h @@ -0,0 +1,24 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +struct animstate +{ + keyframe body; + keyframe back_foot; + keyframe front_foot; + keyframe attach; +}; + +void anim_seq_eval(sequence *seq, float time, keyframe *frame); +void anim_eval(animation *anim, float time, animstate *state); +void anim_add_keyframe(keyframe *seq, keyframe *added, float amount); +void anim_add(animstate *state, animstate *added, float amount); +void anim_eval_add(animstate *state, animation *anim, float time, float amount); + +struct tee_render_info +{ + int texture; + vec4 color_body; + vec4 color_feet; + float size; +}; + +void render_tee(animstate *anim, tee_render_info *info, int emote, vec2 dir, vec2 pos); diff --git a/src/game/client/gc_skin.cpp b/src/game/client/gc_skin.cpp new file mode 100644 index 00000000..cc5699e4 --- /dev/null +++ b/src/game/client/gc_skin.cpp @@ -0,0 +1,167 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include +#include +#include +#include +#include "gc_skin.h" +#include "../g_math.h" + +enum +{ + MAX_SKINS=256, +}; + +static skin skins[MAX_SKINS] = {{0}}; +static int num_skins = 0; + +static void skinscan(const char *name, int is_dir, void *user) +{ + int l = strlen(name); + if(l < 4 || is_dir || num_skins == MAX_SKINS) + return; + if(strcmp(name+l-4, ".png") != 0) + return; + + char buf[512]; + sprintf(buf, "data/skins/%s", name); + IMAGE_INFO info; + if(!gfx_load_png(&info, buf)) + { + dbg_msg("game", "failed to load skin from %s", name); + return; + } + + skins[num_skins].org_texture = gfx_load_texture_raw(info.width, info.height, info.format, info.data); + + // create colorless version + unsigned char *d = (unsigned char *)info.data; + int step = info.format == IMG_RGBA ? 4 : 3; + + for(int i = 0; i < info.width*info.height; i++) + { + int v = (d[i*step]+d[i*step+1]+d[i*step+2])/3; + d[i*step] = v; + d[i*step+1] = v; + d[i*step+2] = v; + } + + if(1) + { + int bs = 96; // body size + int pitch = info.width*4; + int freq[256] = {0}; + + for(int y = 0; y < bs; y++) + for(int x = 0; x < bs; x++) + { + if(d[y*pitch+x*4+3] > 128) + freq[d[y*pitch+x*4]]++; + } + + int org_weight = 0; + int new_weight = 192; + for(int i = 1; i < 256; i++) + { + if(freq[org_weight] < freq[i]) + org_weight = i; + } + + int inv_org_weight = 255-org_weight; + int inv_new_weight = 255-new_weight; + for(int y = 0; y < bs; y++) + for(int x = 0; x < bs; x++) + { + int v = d[y*pitch+x*4]; + if(v <= org_weight) + v = (int)(((v/(float)org_weight) * new_weight)); + else + v = (int)(((v-org_weight)/(float)inv_org_weight)*inv_new_weight + new_weight); + d[y*pitch+x*4] = v; + d[y*pitch+x*4+1] = v; + d[y*pitch+x*4+2] = v; + } + } + + skins[num_skins].color_texture = gfx_load_texture_raw(info.width, info.height, info.format, info.data); + mem_free(info.data); + + // set skin data + strncpy(skins[num_skins].name, name, min((int)sizeof(skins[num_skins].name),l-4)); + dbg_msg("game", "load skin %s", skins[num_skins].name); + num_skins++; +} + + +void skin_init() +{ + // load skins + fs_listdir("data/skins", skinscan, 0); +} + +int skin_num() +{ + return num_skins; +} + +const skin *skin_get(int index) +{ + return &skins[index%num_skins]; +} + +int skin_find(const char *name) +{ + for(int i = 0; i < num_skins; i++) + { + if(strcmp(skins[i].name, name) == 0) + return i; + } + return -1; +} + + + + +// these converter functions were nicked from some random internet pages +static float hue_to_rgb(float v1, float v2, float h) +{ + if(h < 0) h += 1; + if(h > 1) h -= 1; + if((6 * h) < 1) return v1 + ( v2 - v1 ) * 6 * h; + if((2 * h) < 1) return v2; + if((3 * h) < 2) return v1 + ( v2 - v1 ) * ((2.0f/3.0f) - h) * 6; + return v1; +} + +vec3 hsl_to_rgb(vec3 in) +{ + float v1, v2; + vec3 out; + + if(in.s == 0) + { + out.r = in.l; + out.g = in.l; + out.b = in.l; + } + else + { + if(in.l < 0.5f) + v2 = in.l * (1 + in.s); + else + v2 = (in.l+in.s) - (in.s*in.l); + + v1 = 2 * in.l - v2; + + out.r = hue_to_rgb(v1, v2, in.h + (1.0f/3.0f)); + out.g = hue_to_rgb(v1, v2, in.h); + out.b = hue_to_rgb(v1, v2, in.h - (1.0f/3.0f)); + } + + return out; +} + +vec4 skin_get_color(int v) +{ + vec3 r = hsl_to_rgb(vec3((v>>16)/255.0f, ((v>>8)&0xff)/255.0f, 0.5f+(v&0xff)/255.0f*0.5f)); + return vec4(r.r, r.g, r.b, 1.0f); +} diff --git a/src/game/client/gc_skin.h b/src/game/client/gc_skin.h new file mode 100644 index 00000000..3742d04e --- /dev/null +++ b/src/game/client/gc_skin.h @@ -0,0 +1,20 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include "../g_vmath.h" + +// do this better and nicer +typedef struct +{ + int org_texture; + int color_texture; + char name[31]; + char term[1]; +} skin; + +vec4 skin_get_color(int v); +void skin_init(); +int skin_num(); +const skin *skin_get(int index); +int skin_find(const char *name); + + +vec3 hsl_to_rgb(vec3 in); diff --git a/src/game/client/mapres_image.cpp b/src/game/client/mapres_image.cpp deleted file mode 100644 index 1ef1617c..00000000 --- a/src/game/client/mapres_image.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include -#include -#include "mapres_image.h" -#include "../mapres.h" - -static int map_textures[64] = {0}; -static int count = 0; -/* -static void calc_mipmaps(void *data_in, unsigned width, unsigned height, void *data_out) -{ - unsigned char *src = (unsigned char*)data_in; - unsigned char *dst = (unsigned char*)data_out; - unsigned mip_w = width; - unsigned mip_h = height; - unsigned prev_w; - unsigned prev_h; - - // Highest level - no mod - for(unsigned x = 0; x < mip_w; x++) - { - for(unsigned y = 0; y < mip_h; y++) - { - unsigned i = (y * mip_w + x)<<2; - for(unsigned j = 0; j < 4; j++) - dst[i+j] = src[i+j]; - } - } - - src = dst; - dst += mip_w * mip_h * 4; - prev_w = mip_w; - prev_h = mip_h; - mip_w = mip_w>>1; - mip_h = mip_h>>1; - - while(mip_w > 0 && mip_h > 0) - { - for(unsigned x = 0; x < mip_w; x++) - { - for(unsigned y = 0; y < mip_h; y++) - { - unsigned i = (y * mip_w + x)<<2; - - unsigned r = 0; - unsigned g = 0; - unsigned b = 0; - unsigned a = 0; - - - r += src[(((y<<1) * prev_w + (x<<1))<<2)]; - g += src[(((y<<1) * prev_w + (x<<1))<<2)+1]; - b += src[(((y<<1) * prev_w + (x<<1))<<2)+2]; - a += src[(((y<<1) * prev_w + (x<<1))<<2)+3]; - - r += src[(((y<<1) * prev_w + ((x+1)<<1))<<2)]; - g += src[(((y<<1) * prev_w + ((x+1)<<1))<<2)+1]; - b += src[(((y<<1) * prev_w + ((x+1)<<1))<<2)+2]; - a += src[(((y<<1) * prev_w + ((x+1)<<1))<<2)+3]; - - r += src[((((y+1)<<1) * prev_w + (x<<1))<<2)]; - g += src[((((y+1)<<1) * prev_w + (x<<1))<<2)+1]; - b += src[((((y+1)<<1) * prev_w + (x<<1))<<2)+2]; - a += src[((((y+1)<<1) * prev_w + (x<<1))<<2)+3]; - - r += src[((((y+1)<<1) * prev_w + ((x+1)<<1))<<2)]; - g += src[((((y+1)<<1) * prev_w + ((x+1)<<1))<<2)+1]; - b += src[((((y+1)<<1) * prev_w + ((x+1)<<1))<<2)+2]; - a += src[((((y+1)<<1) * prev_w + ((x+1)<<1))<<2)+3]; - - dst[i] = r>>2; - dst[i+1] = g>>2; - dst[i+2] = b>>2; - dst[i+3] = a>>2; - } - } - - src = dst; - dst = dst + mip_w*mip_h*4; - prev_w = mip_w; - prev_h = mip_h; - mip_w = mip_w>>1; - mip_h = mip_h>>1; - } -} -*/ -extern int DEBUGTEST_MAPIMAGE; - -int img_init() -{ - int start, count; - map_get_type(MAPRES_IMAGE, &start, &count); - dbg_msg("mapres_image", "start=%d count=%d", start, count); - for(int i = 0; i < 64; i++) - { - if(map_textures[i]) - { - gfx_unload_texture(map_textures[i]); - map_textures[i] = 0; - } - } - - //void *data_res = (void*)mem_alloc(1024*1024*4*2, 16); - for(int i = 0; i < count; i++) - { - mapres_image *img = (mapres_image *)map_get_item(start+i, 0, 0); - void *data = map_get_data(img->image_data); - //calc_mipmaps(data, img->width, img->height, data_res); - map_textures[i] = gfx_load_texture_raw(img->width, img->height, IMG_RGBA, data); - map_unload_data(img->image_data); - } - - //mem_free(data_res); - return count; -} - -int img_num() -{ - return count; -} - -int img_get(int index) -{ - return map_textures[index]; -} diff --git a/src/game/client/mapres_image.h b/src/game/client/mapres_image.h deleted file mode 100644 index f841ca53..00000000 --- a/src/game/client/mapres_image.h +++ /dev/null @@ -1,19 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -// loads images from the map to textures -int img_init(); - -// returns the number of images in the map -int img_num(); - -// fetches the texture id for the image -int img_get(int index); - - -class mapres_image -{ -public: - int width; - int height; - int image_data; -}; diff --git a/src/game/client/mapres_tilemap.cpp b/src/game/client/mapres_tilemap.cpp deleted file mode 100644 index 6bee4081..00000000 --- a/src/game/client/mapres_tilemap.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include -#include -#include "mapres_tilemap.h" -#include "mapres_image.h" -#include "../mapres.h" - -int tilemap_init() -{ - return 0; -} - -void tilemap_render(float scale, int fg) -{ - if(!map_is_loaded()) - return; - - float screen_x0, screen_y0, screen_x1, screen_y1; - gfx_getscreen(&screen_x0, &screen_y0, &screen_x1, &screen_y1); - - // fetch indecies - int start, num; - map_get_type(MAPRES_TILEMAP, &start, &num); - - // render tilemaps - int passed_main = 0; - for(int t = 0; t < num; t++) - { - mapres_tilemap *tmap = (mapres_tilemap *)map_get_item(start+t,0,0); - unsigned char *data = (unsigned char *)map_get_data(tmap->data); - - if(tmap->main) - passed_main = 1; - - if((fg && passed_main) || (!fg && !passed_main)) - { - if(!config.gfx_high_detail && !tmap->main) - continue; - gfx_texture_set(img_get(tmap->image)); - - gfx_quads_begin(); - - int starty = (int)(screen_y0/scale)-1; - int startx = (int)(screen_x0/scale)-1; - int endy = (int)(screen_y1/scale)+1; - int endx = (int)(screen_x1/scale)+1; - - float frac = (1.25f/1024.0f);//2.0f; //2.0f; - float texsize = 1024.0f; - float nudge = 0.5f/texsize; - for(int y = starty; y < endy; y++) - for(int x = startx; x < endx; x++) - { - int mx = x; - int my = y; - if(mx<0) mx = 0; - if(mx>=tmap->width) mx = tmap->width-1; - if(my<0) my = 0; - if(my>=tmap->height) my = tmap->height-1; - - int c = mx + my*tmap->width; - - unsigned char d = data[c*2]; - unsigned char f = data[c*2+1]; - if(d) - { - int tx = d%16; - int ty = d/16; - int px0 = tx*(1024/16); - int py0 = ty*(1024/16); - int px1 = (tx+1)*(1024/16)-1; - int py1 = (ty+1)*(1024/16)-1; - - float u0 = nudge + px0/texsize+frac; - float v0 = nudge + py0/texsize+frac; - float u1 = nudge + px1/texsize-frac; - float v1 = nudge + py1/texsize-frac; - - if(f&TILEFLAG_VFLIP) - { - float tmp = u0; - u0 = u1; - u1 = tmp; - } - - if(f&TILEFLAG_HFLIP) - { - float tmp = v0; - v0 = v1; - v1 = tmp; - } - - gfx_quads_setsubset(u0,v0,u1,v1); - - gfx_quads_drawTL(x*scale, y*scale, scale, scale); - } - } - - gfx_quads_end(); - } - } -} diff --git a/src/game/client/mapres_tilemap.h b/src/game/client/mapres_tilemap.h deleted file mode 100644 index a13495ed..00000000 --- a/src/game/client/mapres_tilemap.h +++ /dev/null @@ -1,26 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -// dependencies: image - -// -int tilemap_init(); - -// renders the tilemaps -void tilemap_render(float scale, int fg); - -enum -{ - TILEFLAG_VFLIP=1, - TILEFLAG_HFLIP=2, -}; - -struct mapres_tilemap -{ - int image; - int width; - int height; - int x, y; - int scale; - int data; - int main; -}; diff --git a/src/game/client/menu.cpp b/src/game/client/menu.cpp deleted file mode 100644 index 10dafc39..00000000 --- a/src/game/client/menu.cpp +++ /dev/null @@ -1,1865 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#if 0 - -#include -#include -#include -#include - -#include - -extern "C" { - #include - #include - #include - #include -} - -#include "../mapres.h" -#include "../version.h" - -#include "mapres_image.h" -#include "mapres_tilemap.h" - -#include "data.h" -#include - -extern data_container *data; - -/******************************************************** - MENU -*********************************************************/ - -enum gui_tileset_enum -{ - tileset_regular, - tileset_hot, - tileset_active, - tileset_inactive -}; - -void draw_area(gui_tileset_enum tileset, int areax, int areay, int areaw, int areah, float x, float y, float w, float h) -{ -/* - const float tex_w = 512.0, tex_h = 512.0; - - switch (tileset) - { - case tileset_regular: - break; - case tileset_hot: - areax += 192; areay += 192; break; - case tileset_active: - areay += 192; break; - case tileset_inactive: - areax += 192; break; - default: - dbg_msg("menu", "invalid tileset given to draw_part"); - } - - float ts_x = areax / tex_w; - float ts_y = areay / tex_h; - float te_x = (areax + areaw) / tex_w; - float te_y = (areay + areah) / tex_h; - - gfx_blend_normal(); - gfx_texture_set(data->images[IMAGE_GUI_WIDGETS].id); - gfx_quads_begin(); - gfx_setcolor(1,1,1,1); - gfx_quads_setsubset( - ts_x, // startx - ts_y, // starty - te_x, // endx - te_y); // endy - gfx_quads_drawTL(x,y,w,h); - gfx_quads_end(); - */ -} - -void draw_part(int part_type, gui_tileset_enum tileset, float x, float y, float w, float h) -{ - gui_box part = data->gui.misc[part_type]; - - draw_area(tileset, part.x, part.y, part.w, part.h, x, y, w, h); - - //draw_part(parts[part], tileset, x, y, w, h); -} - -void draw_part(int part_type, gui_tileset_enum tileset, float x, float y) -{ - gui_box part = data->gui.misc[part_type]; - - draw_part(part_type, tileset, x, y, part.w, part.h); -} - -void draw_box(int box_type, gui_tileset_enum tileset, float x, float y, float w, float h) -{ - gui_compositebox box = data->gui.boxes[box_type]; - - /* A composite box consists of 9 parts. To get the coordinates for all corners, we need A, B, C and D: - * A----+----+----+ - * | tl | tm | tr | - * +----B----+----+ - * | ml | mm | mr | - * +----+----C----+ - * | bl | bm | br | - * +----+----+----D - */ - - int ax = box.rect.x; - int ay = box.rect.y; - - int bx = box.center.x; - int by = box.center.y; - - int cx = box.center.x + box.center.w; - int cy = box.center.y + box.center.h; - - int dx = box.rect.x + box.rect.w; - int dy = box.rect.y + box.rect.h; - - draw_area(tileset, ax, ay, bx-ax, by-ay, x, y, bx-ax, by-ay); - draw_area(tileset, bx, ay, cx-bx, by-ay, x+bx-ax, y, w-(bx-ax)-(dx-cx), by-ay); - draw_area(tileset, cx, ay, dx-cx, by-ay, x+w-(dx-cx), y, dx-cx, by-ay); - - draw_area(tileset, ax, by, bx-ax, min(int(h-(by-ay)-(dy-cy)), cy-by), x, y+(by-ay), bx-ax, h-(by-ay)-(dy-cy)); - draw_area(tileset, bx, by, cx-bx, cy-by, x+bx-ax, y+(by-ay), w-(bx-ax)-(dx-cx), h-(by-ay)-(dy-cy)); - draw_area(tileset, cx, by, dx-cx, min(int(h-(by-ay)-(dy-cy)), cy-by), x+w-(dx-cx), y+(by-ay), dx-cx, h-(by-ay)-(dy-cy)); - - draw_area(tileset, ax, cy, bx-ax, dy-cy, x, y+h-(dy-cy), bx-ax, dy-cy); - draw_area(tileset, bx, cy, cx-bx, dy-cy, x+bx-ax, y+h-(dy-cy), w-(bx-ax)-(dx-cx), dy-cy); - draw_area(tileset, cx, cy, dx-cx, dy-cy, x+w-(dx-cx), y+h-(dy-cy), dx-cx, dy-cy); -} - -extern "C" -{ - -struct pretty_font -{ - float m_CharStartTable[256]; - float m_CharEndTable[256]; - int font_texture; -}; - -extern pretty_font *current_font; - -} - -extern void render_sun(float x, float y); -extern void select_sprite(int id, int flags=0, int sx=0, int sy=0); -extern void draw_sprite(float x, float y, float size); - -void draw_background(float t) -{ - // background color - gfx_clear(0.65f,0.78f,0.9f); - - gfx_blend_normal(); - - render_sun(170, 170); - - gfx_texture_set(data->images[IMAGE_CLOUDS].id); - gfx_quads_begin(); - select_sprite(SPRITE_CLOUD1); - draw_sprite(3500 - fmod(t * 20 + 2000, 4524), 250, 512); - select_sprite(SPRITE_CLOUD2); - draw_sprite(3000 - fmod(t * 50 + 2000, 4024), 150+250, 512); - select_sprite(SPRITE_CLOUD3); - draw_sprite(4000 - fmod(t * 60 + 500, 4512), 300+130, 256); - gfx_quads_end(); - - /* - gfx_texture_set(data->images[IMAGE_MENU_BACKGROUND].id); - gfx_quads_begin(); - gfx_quads_drawTL(0, 430, 1600, 1600/2); - gfx_quads_end(); - */ - -/* - int frame = int(t * 10) % 3; - - //float x_path = -t - - float x_nudge = 3*cos(t*10); - float y_nudge = 8*sin(t*3); - - x_nudge += 150 * cos(t/3); - y_nudge += 30 * sin(t/3); - - float angl = t/3; - angl = fmod(angl, 2*pi); - - bool flip = angl > pi; - - gfx_texture_set(data->images[IMAGE_MENU_BUTTERFLY].id); - gfx_quads_begin(); - gfx_setcolor(1, 1, 1, 1); - gfx_quads_setsubset( - flip ? (frame + 1) * 0.25f : frame * 0.25f, // startx - 0.0f, // starty - flip ? frame * 0.25f : (frame + 1) * 0.25f, // endx - 0.5f); // endy - gfx_quads_drawTL(1250 + x_nudge, 480 + y_nudge, 64, 64); - gfx_quads_end(); - */ -} - -void draw_image_button(const void *id, const char *text, int checked, float x, float y, float w, float h, void *extra) -{ - ui_do_image(*(int *)id, x, y, w, h); -} - -void draw_single_part_button(const void *id, const char *text, int checked, float x, float y, float w, float h, void *extra) -{ - gui_tileset_enum tileset; - - if (ui_active_item() == id && ui_hot_item() == id) - tileset = tileset_active; - else if (ui_hot_item() == id) - tileset = tileset_hot; - else - tileset = tileset_regular; - - - draw_part((int)((char*)extra-(char*)0), tileset, x, y, w, h); -} - -void draw_menu_button(const void *id, const char *text, int checked, float x, float y, float w, float h, void *extra) -{ - int box_type; - if ((int)((char*)extra-(char*)0)) - box_type = GUI_BOX_SCREEN_INFO; - else - box_type = GUI_BOX_SCREEN_LIST; - draw_box(box_type, tileset_regular, x, y, w, h); - - ui_do_label(x + 10, y, text, 28); -} - -void draw_teewars_button(const void *id, const char *text, int checked, float x, float y, float w, float h, void *extra) -{ - const float font_size = h-6.0f;//42.0f; - - float text_width = gfx_pretty_text_width(font_size, text, -1); - gui_tileset_enum tileset; - - if (ui_active_item() == id) - { - int inside = ui_mouse_inside(x, y, w, h); - tileset = inside ? tileset_active : tileset_hot; - } - else if (ui_hot_item() == id) - tileset = tileset_hot; - else - tileset = tileset_regular; - - if ((int)((char*)extra-(char*)0) == 1) - tileset = tileset_inactive; - - draw_box(GUI_BOX_BUTTON, tileset, x, y, w, h); - - ui_do_label(x + w/2 - text_width/2, y + 2, text, font_size); -} - -/* -struct server_info -{ - int version; - int players; - int max_players; - netaddr4 address; - char name[129]; - char map[65]; -};*/ - -struct server_list -{ - int active_count, info_count; - int scroll_index; - int selected_index; -}; - -int ui_do_key_reader(void *id, float x, float y, float w, float h, int key) -{ - // process - static bool mouse_released = true; - int inside = ui_mouse_inside(x, y, w, h); - int new_key = key; - - if(!ui_mouse_button(0)) - mouse_released = true; - - if(ui_active_item() == id) - { - int k = inp_last_key(); - if (k) - { - new_key = k; - ui_set_active_item(0); - mouse_released = false; - } - } - else if(ui_hot_item() == id) - { - if(ui_mouse_button(0) && mouse_released) - ui_set_active_item(id); - } - - if(inside) - ui_set_hot_item(id); - - // draw - int box_type; - if (ui_active_item() == id || ui_hot_item() == id) - box_type = GUI_BOX_SCREEN_INFO; - else - box_type = GUI_BOX_SCREEN_TEXTBOX; - draw_box(box_type, tileset_regular, x, y, w, h); - - const char *str = inp_key_name(key); - ui_do_label(x + 10, y, str, 36); - if (ui_active_item() == id) - { - float w = gfx_pretty_text_width(36.0f, str, -1); - ui_do_label(x + 10 + w, y, "_", 36); - } - - return new_key; -} - -int ui_do_combo_box(void *id, float x, float y, float w, const char **lines, int line_count, int selected_index) -{ - float line_height = 36.0f; - float height = line_count * line_height; - - int inside = (ui_active_item() == id) ? ui_mouse_inside(x, y, w, height) : ui_mouse_inside(x, y, w, line_height); - int hover_index = (int)((ui_mouse_y() - y) / line_height); - - if (ui_active_item() == id) - { - if (!ui_mouse_button(0)) - { - ui_set_active_item(0); - - if (inside) - selected_index = hover_index; - } - } - else if(ui_hot_item() == id) - { - if (ui_mouse_button(0)) - { - ui_set_active_item(id); - } - } - - if (inside) - { - ui_set_hot_item(id); - } - - if (ui_active_item() == id) - { - for (int i = 0; i < line_count; i++) - { - int box_type; - if (inside && hover_index == i) - box_type = GUI_BOX_SCREEN_INFO; - else - box_type = GUI_BOX_SCREEN_LIST; - - draw_box(box_type, tileset_regular, x, y + i * line_height, w, line_height); - ui_do_label(x + 10 + 10, y + i * line_height, lines[i], 36); - if (selected_index == i) - ui_do_label(x + 10, y + i * line_height, "-", 36); - } - } - else - { - int box_type; - if (ui_active_item() == id || ui_hot_item() == id) - box_type = GUI_BOX_SCREEN_INFO; - else - box_type = GUI_BOX_SCREEN_TEXTBOX; - draw_box(box_type, tileset_regular, x, y, w, line_height); - ui_do_label(x + 10, y, lines[selected_index], 36); - } - - return selected_index; -} - -int ui_do_edit_box(void *id, float x, float y, float w, float h, char *str, int str_size) -{ - int inside = ui_mouse_inside(x, y, w, h); - int r = 0; - static int at_index = 0; - - if(ui_last_active_item() == id) - { - int c = inp_last_char(); - int k = inp_last_key(); - int len = strlen(str); - - if (inside && ui_mouse_button(0)) - { - int mx_rel = (int)(ui_mouse_x() - x); - - for (int i = 1; i <= len; i++) - { - if (gfx_pretty_text_width(36.0f, str, i) + 10 > mx_rel) - { - at_index = i - 1; - break; - } - - if (i == len) - at_index = len; - } - } - - if (at_index > len) - at_index = len; - - if (!(c >= 0 && c < 32)) - { - if (len < str_size - 1 && at_index < str_size - 1) - { - memmove(str + at_index + 1, str + at_index, len - at_index + 1); - str[at_index] = c; - at_index++; - } - } - - if (k == KEY_BACKSPACE && at_index > 0) - { - memmove(str + at_index - 1, str + at_index, len - at_index + 1); - at_index--; - } - else if (k == KEY_DEL && at_index < len) - memmove(str + at_index, str + at_index + 1, len - at_index); - else if (k == KEY_ENTER) - ui_clear_last_active_item(); - else if (k == KEY_LEFT && at_index > 0) - at_index--; - else if (k == KEY_RIGHT && at_index < len) - at_index++; - else if (k == KEY_HOME) - at_index = 0; - else if (k == KEY_END) - at_index = len; - - r = 1; - } - - int box_type; - if (ui_active_item() == id || ui_hot_item() == id || ui_last_active_item() == id) - box_type = GUI_BOX_SCREEN_INFO; - else - box_type = GUI_BOX_SCREEN_TEXTBOX; - - bool just_got_active = false; - - if(ui_active_item() == id) - { - if(!ui_mouse_button(0)) - ui_set_active_item(0); - } - else if(ui_hot_item() == id) - { - if(ui_mouse_button(0)) - { - if (ui_last_active_item() != id) - just_got_active = true; - ui_set_active_item(id); - } - } - - if(inside) - ui_set_hot_item(id); - - draw_box(box_type, tileset_regular, x, y, w, h); - - ui_do_label(x + 10, y, str, 36); - - if (ui_last_active_item() == id && !just_got_active) - { - float w = gfx_pretty_text_width(36.0f, str, at_index); - ui_do_label(x + 10 + w, y, "_", 36); - } - - return r; -} - -int ui_do_check_box(void *id, float x, float y, float w, float h, int value) -{ - int inside = ui_mouse_inside(x, y, w, h); - int r = value; - - if(ui_active_item() == id) - { - if(!ui_mouse_button(0)) - { - ui_set_active_item(0); - r = r ? 0 : 1; - } - } - else if(ui_hot_item() == id) - { - if(ui_mouse_button(0)) - ui_set_active_item(id); - } - - if(inside) - ui_set_hot_item(id); - - // render - gui_tileset_enum tileset; - int part_type; - if (ui_active_item() == id) - tileset = tileset_active; - else if (ui_hot_item() == id) - tileset = tileset_hot; - else - tileset = tileset_regular; - - part_type = r ? GUI_MISC_RADIO_CHECKED : GUI_MISC_RADIO_UNCHECKED; - - draw_part(part_type, tileset, x, y, w, h); - - return r; -} - -int do_scroll_bar_horiz(void *id, float x, float y, float width, int steps, int last_index) -{ - int r = last_index; - - static int up_button; - static int down_button; - - if (ui_do_button(&up_button, "", 0, x, y + 8, 16, 16, draw_single_part_button, (void *)GUI_MISC_SLIDER_BIG_ARROW_LEFT)) - { - if (r > 0) - --r; - } - if (ui_do_button(&down_button, "", 0, x + width - 16, y + 8, 16, 16, draw_single_part_button, (void *)GUI_MISC_SLIDER_BIG_ARROW_RIGHT)) - { - if (r < steps) - ++r; - } - if (steps > 0) // only if there's actually stuff to scroll through - { - int inside = ui_mouse_inside(x + 16, y, width - 32, 32); - if (inside && (!ui_active_item() || ui_active_item() == id)) - ui_set_hot_item(id); - - if(ui_active_item() == id) - { - if (ui_mouse_button(0)) - { - float pos = ui_mouse_x() - x - 16; - float perc = pos / (width - 32); - - r = (int)((steps + 1) * perc); - if (r < 0) - r = 0; - else if (r > steps) - r = steps; - } - else - ui_set_active_item(0); - } - else if (ui_hot_item() == id && ui_mouse_button(0)) - ui_set_active_item(id); - else if (inside && (!ui_active_item() || ui_active_item() == id)) - ui_set_hot_item(id); - } - - draw_part(GUI_MISC_SLIDER_BIG_HORIZ_BEGIN, tileset_regular, x + 16, y + 8, 16, 16); - draw_part(GUI_MISC_SLIDER_BIG_HORIZ_MID, tileset_regular, x + 32, y + 8, width - 32 - 32, 16); - draw_part(GUI_MISC_SLIDER_BIG_HORIZ_END, tileset_regular, x + width - 32, y + 8, 16, 16); - - draw_part(GUI_MISC_SLIDER_BIG_HANDLE_HORIZ, tileset_regular, x + 16 + r * ((width - 64) / steps), y + 8, 32, 16); - - return r; -} - -int do_scroll_bar_vert(void *id, float x, float y, float height, int steps, int last_index) -{ - int r = last_index; - - static int up_button; - static int down_button; - - if (ui_do_button(&up_button, "", 0, x + 8, y, 16, 16, draw_single_part_button, (void *)GUI_MISC_SLIDER_BIG_ARROW_UP)) - { - if (r > 0) - --r; - } - if (ui_do_button(&down_button, "", 0, x + 8, y + height - 16, 16, 16, draw_single_part_button, (void *)GUI_MISC_SLIDER_BIG_ARROW_DOWN)) - { - if (r < steps) - ++r; - } - if (steps > 0) // only if there's actually stuff to scroll through - { - int inside = ui_mouse_inside(x, y + 16, 16, height - 32); - if (inside && (!ui_active_item() || ui_active_item() == id)) - ui_set_hot_item(id); - - if(ui_active_item() == id) - { - if (ui_mouse_button(0)) - { - float pos = ui_mouse_y() - y - 32; - float perc = pos / (height - 32); - - r = (int)((steps + 1) * perc); - if (r < 0) - r = 0; - else if (r > steps) - r = steps; - } - else - ui_set_active_item(0); - } - else if (ui_hot_item() == id && ui_mouse_button(0)) - ui_set_active_item(id); - else if (inside && (!ui_active_item() || ui_active_item() == id)) - ui_set_hot_item(id); - } - - draw_part(GUI_MISC_SLIDER_BIG_VERT_BEGIN, tileset_regular, x + 8, y + 16, 16, 16); - draw_part(GUI_MISC_SLIDER_BIG_VERT_MID, tileset_regular, x + 8, y + 32, 16, height - 32 - 32); - draw_part(GUI_MISC_SLIDER_BIG_VERT_END, tileset_regular, x + 8, y + height - 32, 16, 16); - - draw_part(GUI_MISC_SLIDER_BIG_HANDLE_VERT, tileset_regular, x + 8, y + 16 + r * ((height - 64) / steps), 16, 32); - - return r; -} - -int ui_do_button_rect(const void *id, const char *text, int checked, const struct rect *r, draw_button_callback draw_func, void *extra) -{ - return ui_do_button((void *)id, text, checked, r->x, r->y, r->w, r->h, draw_func, extra); -} - -void ui_do_label_rect(const struct rect *r, char *str) -{ - float size = r->h; - ui_do_label(r->x + 3, r->y + r->h/2 - size/2, str, size); -} - -void ui_do_edit_box_rect(const void *id, const struct rect *r, char *buffer, int bufferSize) -{ - ui_do_edit_box((void *)id, r->x, r->y, r->w, r->h, buffer, bufferSize); -} - -int ui_do_check_box_rect(const void *id, const struct rect *r, int checked) -{ - return ui_do_check_box((void *)id, r->x, r->y, 32, 32, checked); -} - -int ui_do_key_reader_rect(const void *id, const struct rect *r, int key) -{ - return ui_do_key_reader((void *)id, r->x, r->y, r->w, r->h, key); -} - -int do_scroll_bar_horiz_rect(const void *id, const struct rect *r, int steps, int last_index) -{ - return do_scroll_bar_horiz((void *)id, r->x, r->y, r->w, steps, last_index); - //return do_scroll_bar_horiz((void *)id, r->x, r->y, r->w, r->h, steps, last_index); -} - - -static int do_server_list(float x, float y, int *scroll_index, int *selected_index, int visible_items) -{ - const float spacing = 3.f; - const float item_height = 28; - const float item_width = 728; - const float real_width = item_width + 20; - const float real_height = item_height * visible_items + spacing * (visible_items - 1); - - int num_servers = client_serverbrowse_sorted_num(); - - int r = -1; - - for (int i = 0; i < visible_items; i++) - { - int item_index = i + *scroll_index; - if (item_index >= num_servers) - ; - //ui_do_image(empty_item_texture, x, y + i * item_height + i * spacing, item_width, item_height); - else - { - SERVER_INFO *item = client_serverbrowse_sorted_get(item_index); - - bool clicked = false; - clicked = ui_do_button(item, item->name, 0, x, y + i * item_height + i * spacing, item_width, item_height, - draw_menu_button, (*selected_index == item_index) ? (void *)1 : 0); - - char temp[64]; // plenty of extra room so we don't get sad :o - sprintf(temp, "%i/%i %3d", item->num_players, item->max_players, item->latency); - - ui_do_label(x + 600, y + i * item_height + i * spacing, temp, item_height); - ui_do_label(x + 360, y + i * item_height + i * spacing, item->map, item_height); - - if (clicked) - { - r = item_index; - *selected_index = item_index; - } - } - } - - *scroll_index = do_scroll_bar_vert(scroll_index, x + real_width - 16, y, real_height, - max(num_servers - visible_items, 0), *scroll_index); - - return r; -} - -enum -{ - SCREEN_SERVERS, - SCREEN_NEWS, - SCREEN_HOST, - SCREEN_DISCONNECTED, - SCREEN_CONNECTING, - SCREEN_SETTINGS_GENERAL, - SCREEN_SETTINGS_CONTROLS, - SCREEN_SETTINGS_VIDEO, - SCREEN_SETTINGS_VIDEO_SELECT_MODE, - SCREEN_SETTINGS_VIDEO_CUSTOM, - SCREEN_SETTINGS_SOUND, - SCREEN_KERNING -}; - -static int screen = SCREEN_SERVERS; -static CONFIGURATION config_copy; - -const float column1_x = 250; -const float column2_x = column1_x + 170; -const float column3_x = column2_x + 170; -const float row1_y = 180; -const float row2_y = row1_y + 40; -const float row3_y = row2_y + 40; -const float row4_y = row3_y + 40; -const float row5_y = row4_y + 40; -const float row6_y = row5_y + 40; -const float row7_y = row6_y + 40; - -static char address[128] = "localhost:8303"; - -static float colors[7][3] = -{ - { 0, 0, 1 }, - { 0, 1, 0 }, - { 0, 1, 1 }, - { 1, 0, 0 }, - { 1, 0, 1 }, - { 1, 1, 0 }, - { 1, 1, 1 }, -}; - -static void draw_rect(const struct rect *r) -{ - float *color = colors[rand()%7]; - gfx_setcolor(color[0], color[1], color[2], 1); - - gfx_lines_draw(r->x, r->y, r->x+r->w, r->y); - gfx_lines_draw(r->x+r->w, r->y, r->x+r->w, r->y+r->h); - gfx_lines_draw(r->x+r->w, r->y+r->h, r->x, r->y+r->h); - gfx_lines_draw(r->x, r->y+r->h, r->x, r->y); -} - -void ui_settings_general_render(const struct rect *r) -{ - static struct rect row1; - static struct rect row2; - static struct rect rest; - - static struct rect cells[4]; - - ui_hsplit_t(r, 50, &row1, &rest); - ui_hsplit_t(&rest, 50, &row2, &rest); - - ui_vsplit_l(&row1, 100, &cells[0], &cells[1]); - ui_vsplit_l(&row2, 100, &cells[2], &cells[3]); - - // NAME - ui_do_label_rect(&cells[0], "Name:"); - ui_do_edit_box_rect(config_copy.player_name, &cells[1], config_copy.player_name, sizeof(config_copy.player_name)); - - // Dynamic camera - ui_do_label_rect(&cells[2], "Dynamic Camera:"); - config_set_dynamic_camera(&config_copy, ui_do_check_box_rect(&config_copy.dynamic_camera, &cells[3], config_copy.dynamic_camera)); -} - -typedef void (*assign_func_callback)(CONFIGURATION *config, int value); - -struct key_thing -{ - char name[32]; - int *key; - assign_func_callback assign_func; -}; - -static void ui_settings_controls_render(const struct rect *r) -{ - static int scroll_index = 0; - - const key_thing keys[] = - { - { "Move Left:", &config_copy.key_move_left, config_set_key_move_left }, - { "Move Right:", &config_copy.key_move_right, config_set_key_move_right }, - { "Jump:", &config_copy.key_jump, config_set_key_jump }, - { "Fire:", &config_copy.key_fire, config_set_key_fire }, - { "Hook:", &config_copy.key_hook, config_set_key_hook }, - { "Hammer:", &config_copy.key_weapon1, config_set_key_weapon1 }, - { "Pistol:", &config_copy.key_weapon2, config_set_key_weapon2 }, - { "Shotgun:", &config_copy.key_weapon3, config_set_key_weapon3 }, - { "Grenade:", &config_copy.key_weapon4, config_set_key_weapon4 }, - { "Next Weapon:", &config_copy.key_next_weapon, config_set_key_next_weapon }, - { "Prev. Weapon:", &config_copy.key_prev_weapon, config_set_key_prev_weapon }, - { "Emoticon:", &config_copy.key_emoticon, config_set_key_emoticon }, - { "Screenshot:", &config_copy.key_screenshot, config_set_key_screenshot }, - }; - - const int key_count = sizeof(keys) / sizeof(key_thing); - - struct rect rest = *r; - - for (int i = 0; i < key_count; i++) - { - struct rect row; - struct rect left, right; - - ui_hsplit_t(&rest, 32, &row, &rest); - ui_vsplit_l(&row, 128, &left, &right); - - key_thing key = keys[i + scroll_index]; - - ui_do_label_rect(&left, key.name); - key.assign_func(&config_copy, ui_do_key_reader_rect(key.key, &right, *key.key)); - } -} - -void ui_settings_video_render(const struct rect *r) -{ -} - -void ui_settings_video_render_select_mode(const struct rect *r) -{ -} - -void ui_settings_video_render_custom(const struct rect *r) -{ -} - -void ui_settings_sound_render(const struct rect *r) -{ - struct rect row; - struct rect left, right; - - ui_hsplit_t(r, 32, &row, 0x0); - ui_vsplit_l(&row, 128, &left, &right); - - ui_do_label_rect(&left, "Volume:"); - - config_set_volume(&config_copy, do_scroll_bar_horiz_rect(&config_copy.volume, &right, 256, config_copy.volume)); - snd_set_master_volume(config_copy.volume / 255.0f); -} - -static void tab_menu_button_render(const struct rect *r, char *name, int s) -{ - if (ui_do_button_rect(r, name, 0, r, draw_teewars_button, 0)) - screen = s; -} - -static void tab_menu_render(const struct rect *r) -{ - static struct rect button_news; - static struct rect button_servers; - static struct rect button_host; - static struct rect button_settings; - static struct rect rest; - - ui_vsplit_l(r, 130, &button_news, &rest); - ui_vsplit_l(&rest, 130, &button_servers, &rest); - ui_vsplit_l(&rest, 130, &button_host, &rest); - ui_vsplit_l(&rest, 130, &button_settings, &rest); - - tab_menu_button_render(&button_news, "News", SCREEN_NEWS); - tab_menu_button_render(&button_servers, "Servers", SCREEN_SERVERS); - tab_menu_button_render(&button_host, "Host", SCREEN_HOST); - tab_menu_button_render(&button_settings, "Settings", SCREEN_SETTINGS_GENERAL); -} - -static void settings_tab_menu_render(const struct rect *r) -{ - static struct rect button_general; - static struct rect button_controls; - static struct rect button_video; - static struct rect button_sound; - static struct rect rest; - - ui_hsplit_t(r, 60, &button_general, &rest); - ui_hsplit_t(&rest, 60, &button_controls, &rest); - ui_hsplit_t(&rest, 60, &button_video, &rest); - ui_hsplit_t(&rest, 60, &button_sound, &rest); - - tab_menu_button_render(&button_general, "General", SCREEN_SETTINGS_GENERAL); - tab_menu_button_render(&button_controls, "Controls", SCREEN_SETTINGS_CONTROLS); - tab_menu_button_render(&button_video, "Video", SCREEN_SETTINGS_VIDEO); - tab_menu_button_render(&button_sound, "Sound", SCREEN_SETTINGS_SOUND); -} - -static void ui_settings_render(const struct rect *r) -{ - static struct rect sub_menu_selector; - static struct rect center; - - ui_vsplit_l(r, 100, &sub_menu_selector, ¢er); - - settings_tab_menu_render(&sub_menu_selector); - - switch (screen) - { - case SCREEN_SETTINGS_GENERAL: ui_settings_general_render(¢er); break; - case SCREEN_SETTINGS_CONTROLS: ui_settings_controls_render(¢er); break; - case SCREEN_SETTINGS_VIDEO: ui_settings_video_render(¢er); break; - case SCREEN_SETTINGS_VIDEO_SELECT_MODE: ui_settings_video_render_select_mode(¢er); break; - case SCREEN_SETTINGS_VIDEO_CUSTOM: ui_settings_video_render_custom(¢er); break; - case SCREEN_SETTINGS_SOUND: ui_settings_sound_render(¢er); break; - } -} - -static void middle_render(const struct rect *r) -{ - bool ingame = false; - - switch (screen) - { - case SCREEN_SERVERS: - { - if (ingame) - {} - //return ingame_main_render(); - else - { - static struct rect browser; - static bool inited = false; - - ui_margin(r, 5, &browser); - - { - struct rect button_row; - static struct rect button_refresh, button_connect; - - ui_hsplit_b(&browser, 30, &browser, &button_row); - ui_vsplit_l(&button_row, 100, &button_refresh, &button_row); - ui_vsplit_l(&button_row, 100, &button_connect, &button_row); - - if (ui_do_button_rect(&button_refresh, "Refresh", 0, &button_refresh, draw_teewars_button, 0)) - client_serverbrowse_refresh(0); - - if (ui_do_button_rect(&button_connect, "Connect", 0, &button_connect, draw_teewars_button, 0)) - {} - - } - - if (!inited) - { - client_serverbrowse_refresh(0); - inited = true; - } - - { - int server_count = client_serverbrowse_sorted_num(); - int i; - - struct rect rest = browser; - - struct rect button_row; - - static struct rect button_name, button_players, button_players_max, button_map, button_latency, button_progression; - - ui_hsplit_t(&rest, 32, &button_row, &rest); - ui_margin(&button_row, 1, &button_row); - - ui_vsplit_l(&button_row, 400, &button_name, &button_row); - ui_vsplit_l(&button_row, 40, &button_players, &button_row); - ui_vsplit_l(&button_row, 40, &button_players_max, &button_row); - ui_vsplit_l(&button_row, 80, &button_map, &button_row); - ui_vsplit_l(&button_row, 40, &button_latency, &button_row); - ui_vsplit_l(&button_row, 40, &button_progression, &button_row); - - if (ui_do_button_rect(&button_name, "Name", 0, &button_name, draw_teewars_button, 0)) - config.b_sort = BROWSESORT_NAME; - if (ui_do_button_rect(&button_players, "Players", 0, &button_players, draw_teewars_button, 0)) - config.b_sort = BROWSESORT_NUMPLAYERS; - if (ui_do_button_rect(&button_players_max, "Max Players", 0, &button_players_max, draw_teewars_button, 0)) - config.b_sort = BROWSESORT_NUMPLAYERS; // TODO: real enum here - if (ui_do_button_rect(&button_map, "Map", 0, &button_map, draw_teewars_button, 0)) - config.b_sort = BROWSESORT_MAP; - if (ui_do_button_rect(&button_latency, "Ping", 0, &button_latency, draw_teewars_button, 0)) - config.b_sort = BROWSESORT_PING; - if (ui_do_button_rect(&button_progression, "Progression", 0, &button_progression, draw_teewars_button, 0)) - config.b_sort = BROWSESORT_PING; // TODO: real enum here - - for (i = 0; i < server_count; i++) - { - SERVER_INFO *info = client_serverbrowse_sorted_get(i); - struct rect row; - struct rect col_name, col_players, col_players_max, col_map, col_latency, col_progression; - char temp[16]; - - - ui_hsplit_t(&rest, 32, &row, &rest); - - if (rest.h < 0) - break; - - ui_margin(&row, 1, &row); - - ui_vsplit_l(&row, 400, &col_name, &row); - ui_vsplit_l(&row, 40, &col_players, &row); - ui_vsplit_l(&row, 40, &col_players_max, &row); - ui_vsplit_l(&row, 80, &col_map, &row); - ui_vsplit_l(&row, 40, &col_latency, &row); - ui_vsplit_l(&row, 40, &col_progression, &row); - - ui_do_label_rect(&col_name, info->name); - - sprintf(temp, "%i", info->num_players); - ui_do_label_rect(&col_players, temp); - sprintf(temp, "%i", info->max_players); - ui_do_label_rect(&col_players_max, temp); - ui_do_label_rect(&col_map, info->map); - - sprintf(temp, "%i", info->latency); - ui_do_label_rect(&col_latency, temp); - sprintf(temp, "%i", info->progression); - ui_do_label_rect(&col_progression, temp); - } - } - } - break; - } - case SCREEN_NEWS: - { - break; - } - case SCREEN_HOST: - { - break; - //return disconnected_render(); - } - case SCREEN_CONNECTING: - { - break; - //return connecting_render(); - } - case SCREEN_SETTINGS_GENERAL: - case SCREEN_SETTINGS_CONTROLS: - case SCREEN_SETTINGS_VIDEO: - case SCREEN_SETTINGS_VIDEO_SELECT_MODE: - case SCREEN_SETTINGS_VIDEO_CUSTOM: - case SCREEN_SETTINGS_SOUND: - { - ui_settings_render(r); - break; - } - case SCREEN_KERNING: - { - break; - } - default: dbg_msg("menu", "invalid screen selected..."); break; - } -} - -static int ui_menu_render(const struct rect *r) -{ - static struct rect top; - static struct rect middle; - static struct rect bottom; - - ui_hsplit_t(r, 48, &top, &middle); - ui_hsplit_b(&middle, 32, &middle, &bottom); - tab_menu_render(&top); - middle_render(&middle); - - return -ui_do_button_rect(&bottom, "Quit", 0, &bottom, draw_teewars_button, 0); -} - -static int main_render() -{ - static bool inited = false; - - if (!inited) - { - inited = true; - client_serverbrowse_refresh(0); - } - - static int scoll_index = 0, selected_index = -1; - int last_selected_index = selected_index; - do_server_list(20, 160, &scoll_index, &selected_index, 8); - - ui_do_edit_box(address, 280, 425, 300, 36, address, sizeof(address)); - - if (last_selected_index != selected_index && selected_index != -1) - { - SERVER_INFO *server; - server = client_serverbrowse_get(selected_index); - - strcpy(address, server->address); - } - - static int refresh_button, join_button, quit_button; - static int use_lan = 0; - - - if (ui_do_button(&refresh_button, "Refresh", 0, 20, 460, 170, 48, draw_teewars_button, 0)) - client_serverbrowse_refresh(use_lan); - - ui_do_label(60, 420, "Search LAN ", 36); - int last_lan = use_lan; - use_lan = ui_do_check_box(&use_lan, 20, 424, 32, 32, use_lan); - if (use_lan != last_lan) - client_serverbrowse_refresh(use_lan); - - if (ui_do_button(&join_button, "Join", 0, 620, 420, 128, 48, draw_teewars_button, 0)) - { - client_connect(address); - - return 1; - } - - if (ui_do_button(&quit_button, "Quit", 0, 620, 490, 128, 48, draw_teewars_button, 0)) - return -1; - - static int settings_button; - if (ui_do_button(&settings_button, "Settings", 0, 400, 490, 170, 48, draw_teewars_button, 0)) - { - config_copy = config; - screen = SCREEN_SETTINGS_GENERAL; - } - - // render status text - if(client_serverbrowse_num_requests()) - { - char buf[512]; - sprintf(buf, "Refreshing %d servers...", client_serverbrowse_num_requests()); - ui_do_label(20, 400, buf, 28); - } - else - { - char buf[512]; - sprintf(buf, "%d of %d servers", client_serverbrowse_sorted_num(), client_serverbrowse_num()); - ui_do_label(20, 400, buf, 28); - } - - ui_do_edit_box(&config.b_filter_string, 20+150, 600-80, 200, 36, config.b_filter_string, sizeof(config.b_filter_string)); - config.b_filter_empty = ui_do_check_box(&config.b_filter_empty, 20, 600-80, 32, 32, config.b_filter_empty); - config.b_filter_full = ui_do_check_box(&config.b_filter_full, 20+50, 600-80, 32, 32, config.b_filter_full); - config.b_filter_pw = ui_do_check_box(&config.b_filter_pw, 20+100, 600-80, 32, 32, config.b_filter_pw); - - - return 0; -} - - -static int settings_general_render() -{ - // NAME - ui_do_label(column1_x, row1_y, "Name:", 36); - ui_do_edit_box(config_copy.player_name, column2_x, row1_y, 300, 36, config_copy.player_name, sizeof(config_copy.player_name)); - - // Dynamic camera - ui_do_label(column1_x, row2_y, "Dynamic Camera:", 36); - config_set_dynamic_camera(&config_copy, ui_do_check_box(&config_copy.dynamic_camera, column2_x + 55, row2_y + 4, 32, 32, config_copy.dynamic_camera)); - - return 0; -} - -static int settings_controls_render() -{ - static int scroll_index = 0; - - const key_thing keys[] = - { - { "Move Left:", &config_copy.key_move_left, config_set_key_move_left }, - { "Move Right:", &config_copy.key_move_right, config_set_key_move_right }, - { "Jump:", &config_copy.key_jump, config_set_key_jump }, - { "Fire:", &config_copy.key_fire, config_set_key_fire }, - { "Hook:", &config_copy.key_hook, config_set_key_hook }, - { "Hammer:", &config_copy.key_weapon1, config_set_key_weapon1 }, - { "Pistol:", &config_copy.key_weapon2, config_set_key_weapon2 }, - { "Shotgun:", &config_copy.key_weapon3, config_set_key_weapon3 }, - { "Grenade:", &config_copy.key_weapon4, config_set_key_weapon4 }, - { "Next Weapon:", &config_copy.key_next_weapon, config_set_key_next_weapon }, - { "Prev. Weapon:", &config_copy.key_prev_weapon, config_set_key_prev_weapon }, - { "Emoticon:", &config_copy.key_emoticon, config_set_key_emoticon }, - { "Screenshot:", &config_copy.key_screenshot, config_set_key_screenshot }, - }; - - const int key_count = sizeof(keys) / sizeof(key_thing); - - for (int i = 0; i < 6; i++) - { - key_thing key = keys[i + scroll_index]; - - ui_do_label(column1_x, row1_y + 40 * i, key.name, 36); - key.assign_func(&config_copy, ui_do_key_reader(key.key, column2_x, row1_y + 40 * i, 150, 36, *key.key)); - } - - scroll_index = do_scroll_bar_vert(&scroll_index, 600, row1_y, 40 * 6, key_count - 6, scroll_index); - - ui_do_label(column1_x, row1_y + 40 * 6, "Scroll wheel weapon switch", 22); - config_set_scroll_weapon(&config_copy, ui_do_check_box(&config_copy.scroll_weapon, column2_x + 50, row1_y + 40 * 6, 22, 22, config_copy.scroll_weapon)); - - return 0; -} - -static const int MAX_RESOLUTIONS = 128; -static int settings_video_render_select_mode() -{ - static VIDEO_MODE modes[MAX_RESOLUTIONS]; - static int num_modes = -1; - - if(num_modes == -1) - num_modes = gfx_get_video_modes(modes, MAX_RESOLUTIONS); - - static int scroll_index = 0; - scroll_index = do_scroll_bar_vert(&scroll_index, 500, row1_y, 40 * 7, num_modes - 7, scroll_index); - - for (int i = 0; i < 7; i++) - { - int index = i + scroll_index; - if(index >= num_modes) - break; - - //key_thing key = keys[i + scroll_index]; - int depth = modes[index].red+modes[index].green+modes[index].blue; - if(depth < 16) - depth = 16; - else if(depth > 16) - depth = 24; - - char buf[128]; - int s = 0; - if(modes[index].width == config_copy.gfx_screen_width && - modes[index].height == config_copy.gfx_screen_height && - depth == config_copy.gfx_color_depth) - { - s = 1; - } - - sprintf(buf, "%c %dx%d %d bit %c", s?'>':' ', modes[index].width, modes[index].height, depth, s?'<':' '); - - if(ui_do_button((void*)&modes[index], buf, 0, - column1_x, row1_y + 40 * i, 250, 32.0f, draw_teewars_button, 0)) - { - // select - config_set_gfx_color_depth(&config_copy, depth); - config_set_gfx_screen_width(&config_copy, modes[index].width); - config_set_gfx_screen_height(&config_copy, modes[index].height); - screen = SCREEN_SETTINGS_VIDEO; - } - } - - static int back_button = 0; - if(ui_do_button(&back_button, "Back", 0, column3_x, row7_y, 150, 32, draw_teewars_button, 0)) - screen = SCREEN_SETTINGS_VIDEO; - - return 0; -} - -static int settings_video_render_custom() -{ - ui_do_label(column1_x, row1_y, "Quality Textures:", 36); - config_set_gfx_texture_quality(&config_copy, ui_do_check_box(&config_copy.gfx_texture_quality, column3_x, row1_y + 5, 32, 32, config_copy.gfx_texture_quality)); - - ui_do_label(column1_x, row2_y, "Texture Compression:", 36); - config_set_gfx_texture_compression(&config_copy, ui_do_check_box(&config_copy.gfx_texture_compression, column3_x, row2_y + 5, 32, 32, config_copy.gfx_texture_compression)); - - ui_do_label(column1_x, row3_y, "High Detail:", 36); - config_set_gfx_high_detail(&config_copy, ui_do_check_box(&config_copy.gfx_high_detail, column3_x, row3_y + 5, 32, 32, config_copy.gfx_high_detail)); - - static int back_button = 0; - if(ui_do_button(&back_button, "Back", 0, column3_x, row7_y, 150, 32, draw_teewars_button, 0)) - screen = SCREEN_SETTINGS_VIDEO; - - return 0; -} - -static int settings_video_render() -{ - - ui_do_label(column1_x, row1_y, "Mode:", 36); - - char buf[128]; - sprintf(buf, "%dx%d %d bit", config_copy.gfx_screen_width, config_copy.gfx_screen_height, config_copy.gfx_color_depth); - static int select_button = 0; - if(ui_do_button(&select_button, buf, 0, column2_x, row1_y, 300, 32, draw_teewars_button, 0)) - screen = SCREEN_SETTINGS_VIDEO_SELECT_MODE; - - // we need to draw these bottom up, to make overlapping work correctly - - ui_do_label(column1_x, row2_y, "Fullscreen:", 36); - config_set_gfx_fullscreen(&config_copy, ui_do_check_box(&config_copy.gfx_fullscreen, column2_x, row2_y + 5, 32, 32, config_copy.gfx_fullscreen)); - - ui_do_label(column1_x, row3_y, "V-sync:", 36); - config_set_gfx_vsync(&config_copy, ui_do_check_box(&config_copy.gfx_vsync, column2_x, row3_y + 5, 32, 32, config_copy.gfx_vsync)); - - - int current_level = - (config_copy.gfx_texture_quality<<8)| - (config_copy.gfx_texture_compression<<4)| - (config_copy.gfx_high_detail); - - static const int opt_levels[3] = {0x101,0x001,0x010}; - static const char *opts[] = { - "High", - "Medium", - "Low", - "Custom"}; - - int selected = 0; // custom per default - for(; selected < 3; selected++) - { - if(current_level == opt_levels[selected]) - break; - } - - ui_do_label(column1_x, row4_y, "Quality:", 36); - int new_level = ui_do_combo_box(&config_copy.gfx_texture_quality, column2_x, row4_y, 150, opts, 4, selected); - if(new_level < 3) - { - config_set_gfx_texture_quality(&config_copy, (opt_levels[new_level]>>8)&1); - config_set_gfx_texture_compression(&config_copy, (opt_levels[new_level]>>4)&1); - config_set_gfx_high_detail(&config_copy, opt_levels[new_level]&1); - } - - static int custom_button=0; - if(ui_do_button(&custom_button, "Customize", 0, column3_x, row4_y, 130, 32, draw_teewars_button, 0)) - screen = SCREEN_SETTINGS_VIDEO_CUSTOM; - - ui_do_label(column1_x, row6_y + 50, "(A restart of the game is required for these settings to take effect.)", 20); - - return 0; -} - -static int settings_sound_render() -{ - ui_do_label(column1_x, row1_y, "Volume:", 36); - - config_set_volume(&config_copy, do_scroll_bar_horiz(&config_copy.volume, column2_x, row1_y, 200, 255, config_copy.volume)); - snd_set_master_volume(config_copy.volume / 255.0f); - - return 0; -} - -extern void draw_round_rect(float x, float y, float w, float h, float r); -extern void send_info(bool); - -static int settings_render(bool ingame) -{ - if (ingame) - { - gfx_blend_normal(); - - gfx_texture_set(-1); - gfx_quads_begin(); - gfx_setcolor(0,0,0,0.5f); - draw_round_rect(10, 120, 780, 460, 30.0f); - gfx_quads_end(); - } - - static int general_button, controls_button, video_button, sound_button; - - if (ui_do_button(&general_button, "General", 0, 30, 200, 170, 48, draw_teewars_button, 0)) - screen = SCREEN_SETTINGS_GENERAL; - if (ui_do_button(&controls_button, "Controls", 0, 30, 250, 170, 48, draw_teewars_button, 0)) - screen = SCREEN_SETTINGS_CONTROLS; - if (ui_do_button(&video_button, "Video", 0, 30, 300, 170, 48, draw_teewars_button, 0)) - screen = SCREEN_SETTINGS_VIDEO; - if (ui_do_button(&sound_button, "Sound", 0, 30, 350, 170, 48, draw_teewars_button, 0)) - screen = SCREEN_SETTINGS_SOUND; - - switch (screen) - { - case SCREEN_SETTINGS_GENERAL: settings_general_render(); break; - case SCREEN_SETTINGS_CONTROLS: settings_controls_render(); break; - case SCREEN_SETTINGS_VIDEO: settings_video_render(); break; - case SCREEN_SETTINGS_VIDEO_SELECT_MODE: settings_video_render_select_mode(); break; - case SCREEN_SETTINGS_VIDEO_CUSTOM: settings_video_render_custom(); break; - case SCREEN_SETTINGS_SOUND: settings_sound_render(); break; - } - - // SAVE BUTTON - static int save_button; - if (ui_do_button(&save_button, "Save", 0, 482, 490, 128, 48, draw_teewars_button, 0)) - { - // did we change our name? - bool name_changed = strcmp(config.player_name, config_copy.player_name) != 0; - - config = config_copy; - - if (ingame && name_changed) - send_info(false); - -#ifdef CONF_PLATFORM_MACOSX - config_save("~/.teewars"); -#else - config_save("default.cfg"); -#endif - screen = SCREEN_SERVERS; - } - - // CANCEL BUTTON - static int cancel_button; - if (ui_do_button(&cancel_button, "Cancel", 0, 620, 490, 150, 48, draw_teewars_button, 0)) - { - snd_set_master_volume(config.volume / 255.0f); - screen = SCREEN_SERVERS; - } - - return 0; -} - -extern int gametype; -static int ingame_main_render() -{ - static int menu_resume, menu_quit, menu_settings; - /*if (gametype == GAMETYPE_TDM) - { - char buf[128]; - // Switch team - ui_do_label(100,100,"Switch Team",40); - sprintf(buf,"Team: %s",local_player->team ? "A" : "B"); - if (ui_do_button(&menu_team, buf, 0, 30, 150, 170, 48, draw_teewars_button)) - { - msg_pack_start(MSG_SWITCHTEAM, MSGFLAG_VITAL); - msg_pack_end(); - client_send_msg(); - menu_active = false; - } - }*/ - - const int column1_x = 275; - const int row1_y = 200; - const int row2_y = row1_y + 60; - const int row3_y = row2_y + 60; - const int row4_y = row3_y + 60; - - gfx_blend_normal(); - - gfx_texture_set(-1); - gfx_quads_begin(); - gfx_setcolor(0,0,0,0.5f); - draw_round_rect(170, 120, 460, 360, 30.0f); - gfx_quads_end(); - - ui_do_image(data->images[IMAGE_BANNER].id, 214, 150, 384, 96); - - if (ui_do_button(&menu_resume, "Resume Game", 0, column1_x, row2_y, 250, 48, draw_teewars_button, 0)) - { - return 1; - } - - if (ui_do_button(&menu_quit, "Disconnect", 0, column1_x, row4_y, 250, 48, draw_teewars_button, 0)) - { - client_disconnect(); - return 1; - } - - if (ui_do_button(&menu_settings, "Settings", 0, column1_x, row3_y, 250, 48, draw_teewars_button, 0)) - { - config_copy = config; - screen = SCREEN_SETTINGS_GENERAL; - } - - return 0; -} - -extern "C" double extra_kerning[256*256]; - -static int kerning_render() -{ - static bool loaded = false; - static char text[32] = {0}; - - if (!loaded) - { - // TODO: fix me - /* - file_stream file; - - if (file.open_r("kerning.txt")) - { - line_stream lstream(&file); - int i = 0; - char *line; - - while ((line = lstream.get_line())) - extra_kerning[i++] = atof(line); - - file.close(); - } - - if (file.open_r("tracking.txt")) - { - line_stream lstream(&file); - char *line; - - for (int i = 0; i < 256; i++) - { - line = lstream.get_line(); - current_font->m_CharStartTable[i] = atof(line); - line = lstream.get_line(); - current_font->m_CharEndTable[i] = atof(line); - } - - file.close(); - } - */ - - loaded = true; - } - - ui_do_edit_box(text, 160, 20, 300, 36, text, sizeof(text)); - - ui_do_label(160, 250, text, 70); - - int len = strlen(text); - - for (int i = 0; i < len-1; i++) - { - char s[3] = {0}; - s[0] = text[i]; - s[1] = text[i+1]; - ui_do_label(10, 30 * i + 10, s, 45); - - int index = s[0] + s[1] * 256; - - // less - if (ui_do_button((void *)(100 + i * 2), "", 0, 50, 30 * i + 10 + 20, 16, 16, draw_single_part_button, (void *)GUI_MISC_SLIDER_BIG_ARROW_LEFT)) - { - extra_kerning[index] -= 0.01; - } - - // more - if (ui_do_button((void *)(100 + i * 2 + 1), "", 0, 66, 30 * i + 10 + 20, 16, 16, draw_single_part_button, (void *)GUI_MISC_SLIDER_BIG_ARROW_RIGHT)) - { - extra_kerning[index] += 0.01; - } - - char num[16]; - sprintf(num, "(%f)", extra_kerning[index]); - ui_do_label(84, 30 * i + 30, num, 12); - } - - for (int i = 0; i < len; i++) - { - char s[2] = {0}; - s[0] = text[i]; - - ui_do_label(700, 35 * i + 10, s, 45); - - gfx_blend_normal(); - gfx_texture_set(-1); - gfx_quads_begin(); - gfx_setcolor(0,0,0,0.5); - gfx_quads_drawTL(700,35*i+20,1,30); - gfx_quads_drawTL(700+45*(current_font->m_CharEndTable[(int)s[0]]-current_font->m_CharStartTable[(int)s[0]]),35*i+20,1,30); - gfx_quads_end(); - // less - if (ui_do_button((void *)(200 + i * 2), "", 0, 650, 35 * i + 10 + 15, 16, 16, draw_single_part_button, (void *)GUI_MISC_SLIDER_BIG_ARROW_LEFT)) - { - current_font->m_CharStartTable[(int)s[0]] -= 0.01f; - } - - // more - if (ui_do_button((void *)(200 + i * 2 + 1), "", 0, 666, 35 * i + 10 + 15, 16, 16, draw_single_part_button, (void *)GUI_MISC_SLIDER_BIG_ARROW_RIGHT)) - { - current_font->m_CharStartTable[(int)s[0]] += 0.01f; - } - - char num[16]; - sprintf(num, "(%f)", current_font->m_CharStartTable[(int)s[0]]); - ui_do_label(645, 35 * i + 40, num, 12); - - - - - // less - if (ui_do_button((void *)(300 + i * 2), "", 0, 750, 35 * i + 10 + 15, 16, 16, draw_single_part_button, (void *)GUI_MISC_SLIDER_BIG_ARROW_LEFT)) - { - current_font->m_CharEndTable[(int)s[0]] -= 0.01f; - } - - // more - if (ui_do_button((void *)(300 + i * 2 + 1), "", 0, 766, 35 * i + 10 + 15, 16, 16, draw_single_part_button, (void *)GUI_MISC_SLIDER_BIG_ARROW_RIGHT)) - { - current_font->m_CharEndTable[(int)s[0]] += 0.01f; - } - - sprintf(num, "(%f)", current_font->m_CharEndTable[(int)s[0]]); - ui_do_label(745, 35 * i + 40, num, 12); - - } - - // SAVE BUTTON - static int save_button; - if (ui_do_button(&save_button, "Save", 0, 482, 520, 128, 48, draw_teewars_button, 0)) - { - // TODO: fix or remove me - /* - file_stream file; - - if (file.open_w("kerning.txt")) - { - char t[16]; - - for (int i = 0; i < 256*256; i++) - { - sprintf(t, "%f\n", extra_kerning[i]); - file.write(t, strlen(t)); - } - - file.close(); - } - - if (file.open_w("tracking.txt")) - { - char t[16]; - - for (int i = 0; i < 256; i++) - { - sprintf(t, "%f\n", current_font->m_CharStartTable[i]); - file.write(t, strlen(t)); - sprintf(t, "%f\n", current_font->m_CharEndTable[i]); - file.write(t, strlen(t)); - } - - file.close(); - } - */ - - //screen = 0; - } - - // CANCEL BUTTON - static int cancel_button; - if (ui_do_button(&cancel_button, "Cancel", 0, 620, 520, 150, 48, draw_teewars_button, 0)) - screen = SCREEN_SERVERS; - - return 0; -} - - -int render_popup(const char *caption, const char *text, const char *button_text) -{ - float tw; - - float w = 700; - float h = 300; - float x = 800/2-w/2; - float y = 600/2-h/2; - - gfx_blend_normal(); - - gfx_texture_set(-1); - gfx_quads_begin(); - gfx_setcolor(0,0,0,0.50f); - draw_round_rect(x, y, w, h, 40.0f); - gfx_quads_end(); - - tw = gfx_pretty_text_width(48.0f, caption, -1); - ui_do_label(x+w/2-tw/2, y+20, caption, 48.0f); - - tw = gfx_pretty_text_width(32.0f, text, -1); - gfx_pretty_text(x+w/2-tw/2, y+130, 32.0f, text, -1); - - if(button_text) - { - static int back_button = 0; - if(ui_do_button(&back_button, button_text, 0, x+w/2-100, y+220, 200, 48, draw_teewars_button, 0)) - return 1; - if(inp_key_down(KEY_ESC) || inp_key_down(KEY_ENTER)) - return 1; - } - - return 0; -} - -static int disconnected_render() -{ - if(strlen(client_error_string()) == 0) - screen = SCREEN_SERVERS; - else - { - if(render_popup("Disconnected", client_error_string(), "Back")) - screen = SCREEN_SERVERS; - } - return 0; -} - -static int connecting_render() -{ - char buf[256]; - sprintf(buf, "Server: %s", address); - if(render_popup("Connecting", buf, "Abort")) - { - client_disconnect(); - screen = SCREEN_SERVERS; - } - return 0; -} - - -void menu_do_disconnected() -{ - screen = SCREEN_DISCONNECTED; -} - - -void menu_do_connecting() -{ - screen = SCREEN_CONNECTING; -} - -void menu_do_connected() -{ - screen = SCREEN_SERVERS; -} - -static int menu_render(bool ingame) -{ - if (!ingame) - { - // background color - gfx_clear(0.65f,0.78f,0.9f); - //gfx_clear(89/255.f,122/255.f,0.0); - - // GUI coordsys - gfx_mapscreen(0,0,800.0f,600.0f); - - static int64 start = time_get(); - - float t = double(time_get() - start) / double(time_freq()); - gfx_mapscreen(0,0,1600.0f,1200.0f); - draw_background(t); - gfx_mapscreen(0,0,800.0f,600.0f); - - if (screen != SCREEN_KERNING) - { - ui_do_image(data->images[IMAGE_BANNER].id, 200, 20, 512, 128); - ui_do_label(20.0f, 600.0f-40.0f, "Version: " TEEWARS_VERSION, 36); - if(config.debug) - ui_do_label(20.0f, 600.0f-60.0f, "Nethash: " TEEWARS_NETVERSION_HASH, 24); - } - } - else - { - gfx_mapscreen(0, 0, 800, 600); - } - - if(config.dbg_new_gui) - { - struct rect *screen = ui_screen(); - - static float scale = 1.0f; - - if (inp_key_pressed('I')) - scale += 0.01; - if (inp_key_pressed('O')) - scale -= 0.01; - - ui_scale(scale); - int retn = ui_menu_render(screen); - - /*gfx_texture_set(-1); - gfx_lines_begin(); - ui_foreach_rect(draw_rect); - gfx_lines_end();*/ - - return retn; - } - else - { - switch (screen) - { - case SCREEN_SERVERS: return ingame ? ingame_main_render() : main_render(); - case SCREEN_DISCONNECTED: return disconnected_render(); - case SCREEN_CONNECTING: return connecting_render(); - case SCREEN_SETTINGS_GENERAL: - case SCREEN_SETTINGS_CONTROLS: - case SCREEN_SETTINGS_VIDEO: - case SCREEN_SETTINGS_VIDEO_SELECT_MODE: - case SCREEN_SETTINGS_VIDEO_CUSTOM: - case SCREEN_SETTINGS_SOUND: return settings_render(ingame); - case SCREEN_KERNING: return kerning_render(); - default: dbg_msg("menu", "invalid screen selected..."); return 0; - } - } -} - -extern "C" void modmenu_init() // TODO: nastyness -{ - // TODO: should be removed - current_font->font_texture = gfx_load_texture("data/big_font.png"); -} - -void modmenu_shutdown() -{ -} - -extern int menu2_render(); - -extern "C" int modmenu_render(int ingame) // TODO: nastyness -{ - static int mouse_x = 0; - static int mouse_y = 0; - - // handle mouse movement - float mx, my; - { - int rx, ry; - inp_mouse_relative(&rx, &ry); - mouse_x += rx; - mouse_y += ry; - if(mouse_x < 0) mouse_x = 0; - if(mouse_y < 0) mouse_y = 0; - if(mouse_x > gfx_screenwidth()) mouse_x = gfx_screenwidth(); - if(mouse_y > gfx_screenheight()) mouse_y = gfx_screenheight(); - - // update the ui - mx = (mouse_x/(float)gfx_screenwidth())*800.0f; - my = (mouse_y/(float)gfx_screenheight())*600.0f; - - int buttons = 0; - if(inp_key_pressed(KEY_MOUSE_1)) buttons |= 1; - if(inp_key_pressed(KEY_MOUSE_2)) buttons |= 2; - if(inp_key_pressed(KEY_MOUSE_3)) buttons |= 4; - - ui_update(mx,my,mx*3.0f,my*3.0f,buttons); - } - - //int r = menu_render(server_address, str, max_len); - //int r = menu2_render(ingame); - (void)menu_render; - int r = menu2_render(); - - gfx_texture_set(data->images[IMAGE_CURSOR].id); - gfx_quads_begin(); - gfx_setcolor(1,1,1,1); - gfx_quads_drawTL(mx,my,24,24); - gfx_quads_end(); - - inp_clear(); - - return r; -} - -#endif diff --git a/src/game/client/menu.h b/src/game/client/menu.h deleted file mode 100644 index 5c68c53d..00000000 --- a/src/game/client/menu.h +++ /dev/null @@ -1,16 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#ifndef __MENU_H -#define __MENU_H - -void draw_image_button(void *id, const char *text, int checked, float x, float y, float w, float h, void *extra); -void draw_single_part_button(void *id, const char *text, int checked, float x, float y, float w, float h, void *extra); -void draw_menu_button(void *id, const char *text, int checked, float x, float y, float w, float h, void *extra); -void draw_teewars_button(void *id, const char *text, int checked, float x, float y, float w, float h, void *extra); -int ui_do_key_reader(void *id, float x, float y, float w, float h, int key); -int ui_do_combo_box(void *id, float x, float y, float w, char *lines, int line_count, int selected_index); -int ui_do_edit_box(void *id, float x, float y, float w, float h, char *str, int str_size); -int ui_do_check_box(void *id, float x, float y, float w, float h, int value); -int do_scroll_bar_horiz(void *id, float x, float y, float width, int steps, int last_index); -int do_scroll_bar_vert(void *id, float x, float y, float height, int steps, int last_index); - -#endif diff --git a/src/game/client/menu2.cpp b/src/game/client/menu2.cpp deleted file mode 100644 index 04ed48b0..00000000 --- a/src/game/client/menu2.cpp +++ /dev/null @@ -1,1893 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include -#include -#include -#include - -#include -#include - -extern "C" { - #include - #include - #include - #include -} - -#include "../mapres.h" -#include "../version.h" -#include "../game_protocol.h" - -#include "mapres_image.h" -#include "mapres_tilemap.h" - -#include "data.h" -#include "cl_render.h" -#include "cl_skin.h" -#include - -extern data_container *data; - -// abit uglyness -extern const obj_player_info *local_info; -extern bool menu_active; -extern bool menu_game_active; - -enum -{ - POPUP_NONE=0, - POPUP_CONNECTING, - POPUP_DISCONNECTED, - POPUP_PASSWORD, - POPUP_QUIT, -}; - -static int popup = POPUP_NONE; - -//static vec4 gui_color(0.9f,0.78f,0.65f, 0.5f); -//static vec4 gui_color(0.78f,0.9f,0.65f, 0.5f); - -static vec4 gui_color(0.65f,0.78f,0.9f, 0.5f); - -static vec4 color_tabbar_inactive_outgame(0,0,0,0.25f); -static vec4 color_tabbar_active_outgame(0,0,0,0.5f); - -static float color_ingame_scale_i = 0.5f; -static float color_ingame_scale_a = 0.2f; -static vec4 color_tabbar_inactive_ingame(gui_color.r*color_ingame_scale_i, gui_color.g*color_ingame_scale_i, gui_color.b*color_ingame_scale_i,0.75f); -static vec4 color_tabbar_active_ingame(gui_color.r*color_ingame_scale_a, gui_color.g*color_ingame_scale_a, gui_color.b*color_ingame_scale_a,0.85f); -//static vec4 color_tabbar_inactive_ingame(0.2f,0.2f,0.2f,0.5f); -//static vec4 color_tabbar_active_ingame(0.2f,0.2f,0.2f,0.75f); - -static vec4 color_tabbar_inactive = color_tabbar_inactive_outgame; -static vec4 color_tabbar_active = color_tabbar_active_outgame; - -enum -{ - CORNER_TL=1, - CORNER_TR=2, - CORNER_BL=4, - CORNER_BR=8, - - CORNER_T=CORNER_TL|CORNER_TR, - CORNER_B=CORNER_BL|CORNER_BR, - CORNER_R=CORNER_TR|CORNER_BR, - CORNER_L=CORNER_TL|CORNER_BL, - - CORNER_ALL=CORNER_T|CORNER_B, - - PAGE_NEWS=0, - PAGE_INTERNET, - PAGE_LAN, - PAGE_FAVORITES, - PAGE_SETTINGS, - //PAGE_GAME, // not a real page - PAGE_SYSTEM, -}; - -typedef struct -{ - float x, y, w, h; -} RECT; - -static RECT screen = { 0.0f, 0.0f, 848.0f, 480.0f }; - -extern void select_sprite(int id, int flags=0, int sx=0, int sy=0); - -RECT *ui2_screen() -{ - float aspect = gfx_screenaspect(); - float w, h; - - h = 600; - w = aspect*h; - - screen.w = w; - screen.h = h; - - return &screen; -} - -void ui2_set_scale(float s) -{ - config.ui_scale = (int)(s*100.0f); -} - -float ui2_scale() -{ - return config.ui_scale/100.0f; -} - -void ui2_hsplit_t(const RECT *original, float cut, RECT *top, RECT *bottom) -{ - RECT r = *original; - cut *= ui2_scale(); - - if (top) - { - top->x = r.x; - top->y = r.y; - top->w = r.w; - top->h = cut; - } - - if (bottom) - { - bottom->x = r.x; - bottom->y = r.y + cut; - bottom->w = r.w; - bottom->h = r.h - cut; - } -} - -void ui2_hsplit_b(const RECT *original, float cut, RECT *top, RECT *bottom) -{ - RECT r = *original; - cut *= ui2_scale(); - - if (top) - { - top->x = r.x; - top->y = r.y; - top->w = r.w; - top->h = r.h - cut; - } - - if (bottom) - { - bottom->x = r.x; - bottom->y = r.y + r.h - cut; - bottom->w = r.w; - bottom->h = cut; - } -} - -void ui2_vsplit_l(const RECT *original, float cut, RECT *left, RECT *right) -{ - RECT r = *original; - cut *= ui2_scale(); - - if (left) - { - left->x = r.x; - left->y = r.y; - left->w = cut; - left->h = r.h; - } - - if (right) - { - right->x = r.x + cut; - right->y = r.y; - right->w = r.w - cut; - right->h = r.h; - } -} - -void ui2_vsplit_r(const RECT *original, float cut, RECT *left, RECT *right) -{ - RECT r = *original; - cut *= ui2_scale(); - - if (left) - { - left->x = r.x; - left->y = r.y; - left->w = r.w - cut; - left->h = r.h; - } - - if (right) - { - right->x = r.x + r.w - cut; - right->y = r.y; - right->w = cut; - right->h = r.h; - } -} - -void ui2_margin(const RECT *original, float cut, RECT *other_rect) -{ - RECT r = *original; - cut *= ui2_scale(); - - other_rect->x = r.x + cut; - other_rect->y = r.y + cut; - other_rect->w = r.w - 2*cut; - other_rect->h = r.h - 2*cut; -} - -void ui2_vmargin(const RECT *original, float cut, RECT *other_rect) -{ - RECT r = *original; - cut *= ui2_scale(); - - other_rect->x = r.x + cut; - other_rect->y = r.y; - other_rect->w = r.w - 2*cut; - other_rect->h = r.h; -} - -void ui2_hmargin(const RECT *original, float cut, RECT *other_rect) -{ - RECT r = *original; - cut *= ui2_scale(); - - other_rect->x = r.x; - other_rect->y = r.y + cut; - other_rect->w = r.w; - other_rect->h = r.h - 2*cut; -} - -typedef void (*ui2_draw_button_func)(const void *id, const char *text, int checked, const RECT *r, void *extra); - -int ui2_do_button(const void *id, const char *text, int checked, const RECT *r, ui2_draw_button_func draw_func, void *extra) -{ - /* logic */ - int ret = 0; - int inside = ui_mouse_inside(r->x,r->y,r->w,r->h); - - if(ui_active_item() == id) - { - if(!ui_mouse_button(0)) - { - if(inside) - ret = 1; - ui_set_active_item(0); - } - } - else if(ui_hot_item() == id) - { - if(ui_mouse_button(0)) - ui_set_active_item(id); - } - - if(inside) - ui_set_hot_item(id); - - if(draw_func) - draw_func(id, text, checked, r, extra); - return ret; -} - - -void ui2_do_label(const RECT *r, const char *text, float size, int align) -{ - gfx_blend_normal(); - size *= ui2_scale(); - if(align == 0) - { - float tw = gfx_pretty_text_width(size, text, -1); - gfx_pretty_text(r->x + r->w/2-tw/2, r->y, size, text, -1); - } - else if(align < 0) - gfx_pretty_text(r->x, r->y, size, text, -1); - else if(align > 0) - { - float tw = gfx_pretty_text_width(size, text, -1); - gfx_pretty_text(r->x + r->w-tw, r->y, size, text, -1); - } -} - - -extern void draw_round_rect_ext(float x, float y, float w, float h, float r, int corners); -extern void draw_round_rect(float x, float y, float w, float h, float r); - -static void ui2_draw_rect(const RECT *r, vec4 color, int corners, float rounding) -{ - gfx_texture_set(-1); - gfx_quads_begin(); - gfx_setcolor(color.r, color.g, color.b, color.a); - draw_round_rect_ext(r->x,r->y,r->w,r->h,rounding*ui2_scale(), corners); - gfx_quads_end(); -} - -static void ui2_draw_browse_icon(int what, const RECT *r) -{ - gfx_texture_set(data->images[IMAGE_BROWSEICONS].id); - gfx_quads_begin(); - select_sprite(SPRITE_BROWSE_PROGRESS1); // default - if(what <= 100) - { - if(what < 66) - select_sprite(SPRITE_BROWSE_PROGRESS2); - else - select_sprite(SPRITE_BROWSE_PROGRESS3); - } - else if(what&0x100) - { - select_sprite(SPRITE_BROWSE_LOCK); - } - gfx_quads_drawTL(r->x,r->y,r->w,r->h); - gfx_quads_end(); -} - -static void ui2_draw_menu_button(const void *id, const char *text, int checked, const RECT *r, void *extra) -{ - ui2_draw_rect(r, vec4(1,1,1,0.5f), CORNER_ALL, 5.0f); - ui2_do_label(r, text, 24, 0); -} - - -static void ui2_draw_keyselect_button(const void *id, const char *text, int checked, const RECT *r, void *extra) -{ - ui2_draw_rect(r, vec4(1,1,1,0.5f), CORNER_ALL, 5.0f); - ui2_do_label(r, text, 18, 0); -} - -static void ui2_draw_menu_tab_button(const void *id, const char *text, int checked, const RECT *r, void *extra) -{ - if(checked) - ui2_draw_rect(r, color_tabbar_active, CORNER_T, 10.0f); - else - ui2_draw_rect(r, color_tabbar_inactive, CORNER_T, 10.0f); - ui2_do_label(r, text, 26, 0); -} - - -static void ui2_draw_settings_tab_button(const void *id, const char *text, int checked, const RECT *r, void *extra) -{ - if(checked) - ui2_draw_rect(r, color_tabbar_active, CORNER_R, 10.0f); - else - ui2_draw_rect(r, color_tabbar_inactive, CORNER_R, 10.0f); - ui2_do_label(r, text, 24, 0); -} - -static void ui2_draw_grid_header(const void *id, const char *text, int checked, const RECT *r, void *extra) -{ - if(checked) - ui2_draw_rect(r, vec4(1,1,1,0.5f), CORNER_T, 5.0f); - //else - // ui2_draw_rect(r, vec4(1,1,1,0.1f), CORNER_T, 5.0f); - RECT t; - ui2_vsplit_l(r, 5.0f, 0, &t); - ui2_do_label(&t, text, 18, -1); -} -/* -static void ui2_draw_grid_cell_l(const void *id, const char *text, int checked, const RECT *r, void *extra) -{ - ui2_do_label(r, text, 18, -1); -} - -static void ui2_draw_grid_cell_r(const void *id, const char *text, int checked, const RECT *r, void *extra) -{ - ui2_do_label(r, text, 18, 1); -}*/ - -static void ui2_draw_list_row(const void *id, const char *text, int checked, const RECT *r, void *extra) -{ - if(checked) - { - RECT sr = *r; - ui2_margin(&sr, 1.5f, &sr); - ui2_draw_rect(&sr, vec4(1,1,1,0.5f), CORNER_ALL, 4.0f); - } - ui2_do_label(r, text, 18, -1); -} - -static void ui2_draw_checkbox_common(const void *id, const char *text, const char *boxtext, const RECT *r) -{ - RECT c = *r; - RECT t = *r; - c.w = c.h; - t.x += c.w; - t.w -= c.w; - ui2_vsplit_l(&t, 5.0f, 0, &t); - - ui2_margin(&c, 2.0f, &c); - ui2_draw_rect(&c, vec4(1,1,1,0.25f), CORNER_ALL, 3.0f); - ui2_do_label(&c, boxtext, 16, 0); - ui2_do_label(&t, text, 18, -1); -} - -static void ui2_draw_checkbox(const void *id, const char *text, int checked, const RECT *r, void *extra) -{ - ui2_draw_checkbox_common(id, text, checked?"X":"", r); -} - -static void ui2_draw_checkbox_number(const void *id, const char *text, int checked, const RECT *r, void *extra) -{ - char buf[16]; - sprintf(buf, "%d", checked); - ui2_draw_checkbox_common(id, text, buf, r); -} - -int ui2_do_edit_box(void *id, const RECT *rect, char *str, int str_size, bool hidden=false) -{ - int inside = ui_mouse_inside(rect->x,rect->y,rect->w,rect->h); - int r = 0; - static int at_index = 0; - - if(ui_last_active_item() == id) - { - int c = inp_last_char(); - int k = inp_last_key(); - int len = strlen(str); - - if (inside && ui_mouse_button(0)) - { - int mx_rel = (int)(ui_mouse_x() - rect->x); - - for (int i = 1; i <= len; i++) - { - if (gfx_pretty_text_width(18.0f, str, i) + 10 > mx_rel) - { - at_index = i - 1; - break; - } - - if (i == len) - at_index = len; - } - } - - if (at_index > len) - at_index = len; - - if (!(c >= 0 && c < 32)) - { - if (len < str_size - 1 && at_index < str_size - 1) - { - memmove(str + at_index + 1, str + at_index, len - at_index + 1); - str[at_index] = c; - at_index++; - } - } - - if (k == KEY_BACKSPACE && at_index > 0) - { - memmove(str + at_index - 1, str + at_index, len - at_index + 1); - at_index--; - } - else if (k == KEY_DEL && at_index < len) - memmove(str + at_index, str + at_index + 1, len - at_index); - else if (k == KEY_ENTER) - ui_clear_last_active_item(); - else if (k == KEY_LEFT && at_index > 0) - at_index--; - else if (k == KEY_RIGHT && at_index < len) - at_index++; - else if (k == KEY_HOME) - at_index = 0; - else if (k == KEY_END) - at_index = len; - - r = 1; - } - - int box_type; - if (ui_active_item() == id || ui_hot_item() == id || ui_last_active_item() == id) - box_type = GUI_BOX_SCREEN_INFO; - else - box_type = GUI_BOX_SCREEN_TEXTBOX; - - bool just_got_active = false; - - if(ui_active_item() == id) - { - if(!ui_mouse_button(0)) - ui_set_active_item(0); - } - else if(ui_hot_item() == id) - { - if(ui_mouse_button(0)) - { - if (ui_last_active_item() != id) - just_got_active = true; - ui_set_active_item(id); - } - } - - if(inside) - ui_set_hot_item(id); - - RECT textbox = *rect; - ui2_draw_rect(&textbox, vec4(1,1,1,0.5f), CORNER_ALL, 5.0f); - ui2_vmargin(&textbox, 5.0f, &textbox); - - const char *display_str = str; - char stars[128]; - - if(hidden) - { - unsigned s = strlen(str); - if(s >= sizeof(stars)) - s = sizeof(stars)-1; - memset(stars, '*', s); - stars[s] = 0; - display_str = stars; - } - - ui2_do_label(&textbox, display_str, 18, -1); - - if (ui_last_active_item() == id && !just_got_active) - { - float w = gfx_pretty_text_width(18.0f, display_str, at_index); - textbox.x += w*ui2_scale(); - ui2_do_label(&textbox, "_", 18, -1); - } - - return r; -} - -float ui2_do_scrollbar_v(const void *id, const RECT *rect, float current) -{ - RECT handle; - static float offset_y; - ui2_hsplit_t(rect, 33, &handle, 0); - - handle.y += (rect->h-handle.h)*current; - - /* logic */ - float ret = current; - int inside = ui_mouse_inside(handle.x,handle.y,handle.w,handle.h); - - if(ui_active_item() == id) - { - if(!ui_mouse_button(0)) - ui_set_active_item(0); - - float min = rect->y; - float max = rect->h-handle.h; - float cur = ui_mouse_y()-offset_y; - ret = (cur-min)/max; - if(ret < 0.0f) ret = 0.0f; - if(ret > 1.0f) ret = 1.0f; - } - else if(ui_hot_item() == id) - { - if(ui_mouse_button(0)) - { - ui_set_active_item(id); - offset_y = ui_mouse_y()-handle.y; - } - } - - if(inside) - ui_set_hot_item(id); - - // render - RECT rail; - ui2_vmargin(rect, 5.0f, &rail); - ui2_draw_rect(&rail, vec4(1,1,1,0.25f), 0, 0.0f); - - RECT slider = handle; - slider.w = rail.x-slider.x; - ui2_draw_rect(&slider, vec4(1,1,1,0.25f), CORNER_L, 2.5f); - slider.x = rail.x+rail.w; - ui2_draw_rect(&slider, vec4(1,1,1,0.25f), CORNER_R, 2.5f); - - slider = handle; - ui2_margin(&slider, 5.0f, &slider); - ui2_draw_rect(&slider, vec4(1,1,1,0.25f), CORNER_ALL, 2.5f); - - return ret; -} - - - -float ui2_do_scrollbar_h(const void *id, const RECT *rect, float current) -{ - RECT handle; - static float offset_x; - ui2_vsplit_l(rect, 33, &handle, 0); - - handle.x += (rect->w-handle.w)*current; - - /* logic */ - float ret = current; - int inside = ui_mouse_inside(handle.x,handle.y,handle.w,handle.h); - - if(ui_active_item() == id) - { - if(!ui_mouse_button(0)) - ui_set_active_item(0); - - float min = rect->x; - float max = rect->w-handle.w; - float cur = ui_mouse_x()-offset_x; - ret = (cur-min)/max; - if(ret < 0.0f) ret = 0.0f; - if(ret > 1.0f) ret = 1.0f; - } - else if(ui_hot_item() == id) - { - if(ui_mouse_button(0)) - { - ui_set_active_item(id); - offset_x = ui_mouse_x()-handle.x; - } - } - - if(inside) - ui_set_hot_item(id); - - // render - RECT rail; - ui2_hmargin(rect, 5.0f, &rail); - ui2_draw_rect(&rail, vec4(1,1,1,0.25f), 0, 0.0f); - - RECT slider = handle; - slider.h = rail.y-slider.y; - ui2_draw_rect(&slider, vec4(1,1,1,0.25f), CORNER_T, 2.5f); - slider.y = rail.y+rail.h; - ui2_draw_rect(&slider, vec4(1,1,1,0.25f), CORNER_B, 2.5f); - - slider = handle; - ui2_margin(&slider, 5.0f, &slider); - ui2_draw_rect(&slider, vec4(1,1,1,0.25f), CORNER_ALL, 2.5f); - - return ret; -} - -int ui2_do_key_reader(void *id, const RECT *rect, int key) -{ - // process - static bool mouse_released = true; - int inside = ui_mouse_inside(rect->x, rect->y, rect->w, rect->h); - int new_key = key; - - if(!ui_mouse_button(0)) - mouse_released = true; - - if(ui_active_item() == id) - { - int k = inp_last_key(); - if (k) - { - new_key = k; - ui_set_active_item(0); - mouse_released = false; - } - } - else if(ui_hot_item() == id) - { - if(ui_mouse_button(0) && mouse_released) - ui_set_active_item(id); - } - - if(inside) - ui_set_hot_item(id); - - // draw - if (ui_active_item() == id) - ui2_draw_keyselect_button(id, "???", 0, rect, 0); - else - ui2_draw_keyselect_button(id, inp_key_name(key), 0, rect, 0); - return new_key; -} - - -static int menu2_render_menubar(RECT r) -{ - RECT box = r; - RECT button; - - int active_page = config.ui_page; - int new_page = -1; - if(menu_game_active) - active_page = -1; - - if(client_state() == CLIENTSTATE_OFFLINE) - { - if(0) // this is not done yet - { - ui2_vsplit_l(&box, 90.0f, &button, &box); - static int news_button=0; - if (ui2_do_button(&news_button, "News", active_page==PAGE_NEWS, &button, ui2_draw_menu_tab_button, 0)) - new_page = PAGE_NEWS; - ui2_vsplit_l(&box, 30.0f, 0, &box); - } - } - else - { - ui2_vsplit_l(&box, 90.0f, &button, &box); - static int game_button=0; - if (ui2_do_button(&game_button, "Game", menu_game_active, &button, ui2_draw_menu_tab_button, 0)) - menu_game_active = true; - - ui2_vsplit_l(&box, 30.0f, 0, &box); - } - - ui2_vsplit_l(&box, 110.0f, &button, &box); - static int internet_button=0; - if (ui2_do_button(&internet_button, "Internet", active_page==PAGE_INTERNET, &button, ui2_draw_menu_tab_button, 0)) - { - client_serverbrowse_refresh(0); - new_page = PAGE_INTERNET; - } - - ui2_vsplit_l(&box, 4.0f, 0, &box); - ui2_vsplit_l(&box, 90.0f, &button, &box); - static int lan_button=0; - if (ui2_do_button(&lan_button, "LAN", active_page==PAGE_LAN, &button, ui2_draw_menu_tab_button, 0)) - { - client_serverbrowse_refresh(1); - new_page = PAGE_LAN; - } - - if(0) // this one is not done yet - { - ui2_vsplit_l(&box, 4.0f, 0, &box); - ui2_vsplit_l(&box, 120.0f, &button, &box); - static int favorites_button=0; - if (ui2_do_button(&favorites_button, "Favorites", active_page==PAGE_FAVORITES, &button, ui2_draw_menu_tab_button, 0)) - new_page = PAGE_FAVORITES; - } - - /* - ui2_vsplit_r(&box, 110.0f, &box, &button); - static int system_button=0; - if (ui2_do_button(&system_button, "System", config.ui_page==PAGE_SYSTEM, &button, ui2_draw_menu_tab_button, 0)) - config.ui_page = PAGE_SYSTEM; - - ui2_vsplit_r(&box, 30.0f, &box, 0); - */ - - ui2_vsplit_r(&box, 110.0f, &box, &button); - static int quit_button=0; - if (ui2_do_button(&quit_button, "Quit", 0, &button, ui2_draw_menu_tab_button, 0)) - popup = POPUP_QUIT; - - ui2_vsplit_r(&box, 10.0f, &box, &button); - ui2_vsplit_r(&box, 110.0f, &box, &button); - static int settings_button=0; - if (ui2_do_button(&settings_button, "Settings", active_page==PAGE_SETTINGS, &button, ui2_draw_menu_tab_button, 0)) - new_page = PAGE_SETTINGS; - - if(new_page != -1) - { - config.ui_page = new_page; - menu_game_active = false; - } - - return 0; -} - -static void menu2_render_background() -{ - //gfx_clear(0.65f,0.78f,0.9f); - gfx_clear(gui_color.r, gui_color.g, gui_color.b); - //gfx_clear(0.78f,0.9f,0.65f); - - gfx_texture_set(data->images[IMAGE_BANNER].id); - gfx_quads_begin(); - gfx_setcolor(0,0,0,0.05f); - gfx_quads_setrotation(-pi/4+0.15f); - gfx_quads_draw(400, 300, 1000, 250); - gfx_quads_end(); -} - -static void menu2_render_serverbrowser(RECT main_view) -{ - ui2_draw_rect(&main_view, color_tabbar_active, CORNER_ALL, 10.0f); - - RECT view; - ui2_margin(&main_view, 10.0f, &view); - - RECT headers; - RECT filters; - RECT status; - RECT toolbox; - - //ui2_hsplit_t(&view, 20.0f, &status, &view); - ui2_hsplit_t(&view, 20.0f, &headers, &view); - ui2_hsplit_b(&view, 90.0f, &view, &filters); - ui2_hsplit_b(&view, 5.0f, &view, 0); - ui2_hsplit_b(&view, 20.0f, &view, &status); - - ui2_vsplit_r(&filters, 300.0f, &filters, &toolbox); - ui2_vsplit_r(&filters, 150.0f, &filters, 0); - - // split of the scrollbar - ui2_draw_rect(&headers, vec4(1,1,1,0.25f), CORNER_T, 5.0f); - ui2_vsplit_r(&headers, 20.0f, &headers, 0); - - struct column - { - int id; - int sort; - const char *caption; - int direction; - float width; - int flags; - RECT rect; - RECT spacer; - }; - - enum - { - FIXED=1, - SPACER=2, - - COL_FLAGS=0, - COL_NAME, - COL_GAMETYPE, - COL_MAP, - COL_PLAYERS, - COL_PING, - COL_PROGRESS, - }; - - static column cols[] = { - {-1, -1, " ", -1, 10.0f, 0, {0}, {0}}, - {COL_FLAGS, -1, " ", -1, 20.0f, 0, {0}, {0}}, - {COL_NAME, BROWSESORT_NAME, "Name", 0, 300.0f, 0, {0}, {0}}, - {COL_GAMETYPE, BROWSESORT_GAMETYPE, "Type", 1, 50.0f, 0, {0}, {0}}, - {COL_MAP, BROWSESORT_MAP, "Map", 1, 100.0f, 0, {0}, {0}}, - {COL_PLAYERS, BROWSESORT_NUMPLAYERS, "Players", 1, 60.0f, 0, {0}, {0}}, - {-1, -1, " ", 1, 10.0f, 0, {0}, {0}}, - {COL_PROGRESS, BROWSESORT_PROGRESSION, "%", 1, 20.0f, FIXED, {0}, {0}}, - {COL_PING, BROWSESORT_PING, "Ping", 1, 40.0f, FIXED, {0}, {0}}, - }; - - int num_cols = sizeof(cols)/sizeof(column); - - // do layout - for(int i = 0; i < num_cols; i++) - { - if(cols[i].direction == -1) - { - ui2_vsplit_l(&headers, cols[i].width, &cols[i].rect, &headers); - - if(i+1 < num_cols) - { - //cols[i].flags |= SPACER; - ui2_vsplit_l(&headers, 2, &cols[i].spacer, &headers); - } - } - } - - for(int i = num_cols-1; i >= 0; i--) - { - if(cols[i].direction == 1) - { - ui2_vsplit_r(&headers, cols[i].width, &headers, &cols[i].rect); - ui2_vsplit_r(&headers, 2, &headers, &cols[i].spacer); - } - } - - for(int i = 0; i < num_cols; i++) - { - if(cols[i].direction == 0) - cols[i].rect = headers; - } - - // do headers - for(int i = 0; i < num_cols; i++) - { - if(ui2_do_button(cols[i].caption, cols[i].caption, config.b_sort == cols[i].sort, &cols[i].rect, ui2_draw_grid_header, 0)) - { - if(cols[i].sort != -1) - config.b_sort = cols[i].sort; - } - } - - ui2_draw_rect(&view, vec4(0,0,0,0.15f), 0, 0); - - RECT scroll; - ui2_vsplit_r(&view, 15, &view, &scroll); - - int num_servers = client_serverbrowse_sorted_num(); - - int num = (int)(view.h/cols[0].rect.h); - static int scrollbar = 0; - static float scrollvalue = 0; - static int selected_index = -1; - ui2_hmargin(&scroll, 5.0f, &scroll); - scrollvalue = ui2_do_scrollbar_v(&scrollbar, &scroll, scrollvalue); - - int start = (int)((num_servers-num)*scrollvalue); - if(start < 0) - start = 0; - - //int r = -1; - int new_selected = selected_index; - - for (int i = start, k = 0; i < num_servers && k < num; i++, k++) - { - int item_index = i; - SERVER_INFO *item = client_serverbrowse_sorted_get(item_index); - RECT row; - - int l = selected_index==item_index; - - if(l) - { - // selected server, draw the players on it - RECT whole; - int h = (item->num_players+2)/3; - - ui2_hsplit_t(&view, 25.0f+h*15.0f, &whole, &view); - - RECT r = whole; - ui2_margin(&r, 1.5f, &r); - ui2_draw_rect(&r, vec4(1,1,1,0.5f), CORNER_ALL, 4.0f); - - ui2_hsplit_t(&whole, 20.0f, &row, &whole); - ui2_vsplit_l(&whole, 50.0f, 0, &whole); - - for(int p = 0; p < item->num_players; p+=3) - { - RECT player_row; - RECT player_rect; - RECT player_score; - RECT player_name; - ui2_hsplit_t(&whole, 15.0f, &player_row, &whole); - - for(int a = 0; a < 3; a++) - { - if(p+a >= item->num_players) - break; - - ui2_vsplit_l(&player_row, 170.0f, &player_rect, &player_row); - ui2_vsplit_l(&player_rect, 30.0f, &player_score, &player_name); - ui2_vsplit_l(&player_name, 10.0f, 0, &player_name); - char buf[32]; - sprintf(buf, "%d", item->player_scores[p+a]); - ui2_do_label(&player_score, buf, 16.0f, 1); - ui2_do_label(&player_name, item->player_names[p+a], 16.0f, -1); - } - } - - k += h*3/4; - } - else - ui2_hsplit_t(&view, 20.0f, &row, &view); - - if(ui2_do_button(item, "", l, &row, 0, 0)) - { - new_selected = item_index; - dbg_msg("dbg", "addr = %s", item->address); - strncpy(config.ui_server_address, item->address, sizeof(config.ui_server_address)); - if(inp_mouse_doubleclick()) - client_connect(config.ui_server_address); - } - - for(int c = 0; c < num_cols; c++) - { - RECT button; - char temp[64]; - button.x = cols[c].rect.x; - button.y = row.y; - button.h = row.h; - button.w = cols[c].rect.w; - - //int s = 0; - int id = cols[c].id; - - //s = ui2_do_button(item, "L", l, &button, ui2_draw_browse_icon, 0); - - if(id == COL_FLAGS) - { - if(item->flags&1) - ui2_draw_browse_icon(0x100, &button); - } - else if(id == COL_NAME) - ui2_do_label(&button, item->name, 20.0f, -1); - else if(id == COL_MAP) - ui2_do_label(&button, item->map, 20.0f, -1); - else if(id == COL_PLAYERS) - { - sprintf(temp, "%i/%i", item->num_players, item->max_players); - ui2_do_label(&button, temp, 20.0f, 1); - } - else if(id == COL_PING) - { - sprintf(temp, "%i", item->latency); - ui2_do_label(&button, temp, 20.0f, 1); - } - else if(id == COL_PROGRESS) - { - ui2_draw_browse_icon(item->progression, &button); - } - else if(id == COL_GAMETYPE) - { - const char *type = "???"; - if(item->game_type == GAMETYPE_DM) type = "DM"; - else if(item->game_type == GAMETYPE_TDM) type = "TDM"; - else if(item->game_type == GAMETYPE_CTF) type = "CTF"; - ui2_do_label(&button, type, 20.0f, 0); - } - /* - if(s) - { - new_selected = item_index; - dbg_msg("dbg", "addr = %s", item->address); - strncpy(config.ui_server_address, item->address, sizeof(config.ui_server_address)); - }*/ - } - } - - selected_index = new_selected; - - - // render quick search - RECT button; - ui2_hsplit_t(&filters, 20.0f, &button, &filters); - ui2_do_label(&button, "Quick search: ", 18, -1); - ui2_vsplit_l(&button, 95.0f, 0, &button); - ui2_do_edit_box(&config.b_filter_string, &button, config.b_filter_string, sizeof(config.b_filter_string)); - - // render filters - ui2_hsplit_t(&filters, 20.0f, &button, &filters); - if (ui2_do_button(&config.b_filter_empty, "Has people playing", config.b_filter_empty, &button, ui2_draw_checkbox, 0)) - config.b_filter_empty ^= 1; - - ui2_hsplit_t(&filters, 20.0f, &button, &filters); - if (ui2_do_button(&config.b_filter_full, "Server not full", config.b_filter_full, &button, ui2_draw_checkbox, 0)) - config.b_filter_full ^= 1; - - ui2_hsplit_t(&filters, 20.0f, &button, &filters); - if (ui2_do_button(&config.b_filter_pw, "Is not password protected", config.b_filter_pw, &button, ui2_draw_checkbox, 0)) - config.b_filter_pw ^= 1; - - - // render status - ui2_draw_rect(&status, vec4(1,1,1,0.25f), CORNER_B, 5.0f); - ui2_vmargin(&status, 50.0f, &status); - char buf[128]; - sprintf(buf, "%d of %d servers", client_serverbrowse_sorted_num(), client_serverbrowse_num()); - ui2_do_label(&status, buf, 18.0f, -1); - - // render toolbox - { - RECT buttons, button; - ui2_hsplit_b(&toolbox, 25.0f, &toolbox, &buttons); - - ui2_vsplit_r(&buttons, 100.0f, &buttons, &button); - ui2_vmargin(&button, 2.0f, &button); - static int join_button = 0; - if(ui2_do_button(&join_button, "Connect", 0, &button, ui2_draw_menu_button, 0)) - client_connect(config.ui_server_address); - - ui2_vsplit_r(&buttons, 20.0f, &buttons, &button); - ui2_vsplit_r(&buttons, 100.0f, &buttons, &button); - ui2_vmargin(&button, 2.0f, &button); - static int refresh_button = 0; - if(ui2_do_button(&refresh_button, "Refresh", 0, &button, ui2_draw_menu_button, 0)) - { - if(config.ui_page == PAGE_INTERNET) - client_serverbrowse_refresh(0); - else if(config.ui_page == PAGE_LAN) - client_serverbrowse_refresh(1); - } - - ui2_hsplit_t(&toolbox, 20.0f, &button, &toolbox); - ui2_do_label(&button, "Host address:", 18, -1); - ui2_vsplit_l(&button, 100.0f, 0, &button); - ui2_do_edit_box(&config.ui_server_address, &button, config.ui_server_address, sizeof(config.ui_server_address)); - } -} - -static void menu2_render_settings_player(RECT main_view) -{ - RECT button; - RECT skinselection; - ui2_vsplit_l(&main_view, 300.0f, &main_view, &skinselection); - - - ui2_hsplit_t(&main_view, 20.0f, &button, &main_view); - - // render settings - { - ui2_hsplit_t(&main_view, 20.0f, &button, &main_view); - ui2_do_label(&button, "Name:", 18.0, -1); - ui2_vsplit_l(&button, 80.0f, 0, &button); - ui2_vsplit_l(&button, 180.0f, &button, 0); - ui2_do_edit_box(config.player_name, &button, config.player_name, sizeof(config.player_name)); - - ui2_hsplit_t(&main_view, 20.0f, &button, &main_view); - if (ui2_do_button(&config.cl_dynamic_camera, "Dynamic camera", config.cl_dynamic_camera, &button, ui2_draw_checkbox, 0)) - config.cl_dynamic_camera ^= 1; - - ui2_hsplit_t(&main_view, 20.0f, &button, &main_view); - if (ui2_do_button(&config.cl_autoswitch_weapons, "Switch weapon on pickup", config.cl_autoswitch_weapons, &button, ui2_draw_checkbox, 0)) - config.cl_autoswitch_weapons ^= 1; - - ui2_hsplit_t(&main_view, 20.0f, &button, &main_view); - if (ui2_do_button(&config.cl_nameplates, "Show name plates", config.cl_nameplates, &button, ui2_draw_checkbox, 0)) - config.cl_nameplates ^= 1; - - if(config.cl_nameplates) - { - ui2_hsplit_t(&main_view, 20.0f, &button, &main_view); - ui2_vsplit_l(&button, 15.0f, 0, &button); - if (ui2_do_button(&config.cl_nameplates_always, "Always show name plates", config.cl_nameplates_always, &button, ui2_draw_checkbox, 0)) - config.cl_nameplates_always ^= 1; - } - - ui2_hsplit_t(&main_view, 20.0f, &button, &main_view); - - ui2_hsplit_t(&main_view, 20.0f, &button, &main_view); - if (ui2_do_button(&config.player_color_body, "Custom colors", config.player_use_custom_color, &button, ui2_draw_checkbox, 0)) - config.player_use_custom_color = config.player_use_custom_color?0:1; - - if(config.player_use_custom_color) - { - int *colors[2]; - colors[0] = &config.player_color_body; - colors[1] = &config.player_color_feet; - - const char *parts[] = {"Body", "Feet"}; - const char *labels[] = {"Hue", "Sat.", "Lht."}; - static int color_slider[2][3] = {{0}}; - //static float v[2][3] = {{0, 0.5f, 0.25f}, {0, 0.5f, 0.25f}}; - - for(int i = 0; i < 2; i++) - { - RECT text; - ui2_hsplit_t(&main_view, 20.0f, &text, &main_view); - ui2_vsplit_l(&text, 15.0f, 0, &text); - ui2_do_label(&text, parts[i], 18, -1); - - int prevcolor = *colors[i]; - int color = 0; - for(int s = 0; s < 3; s++) - { - RECT text; - ui2_hsplit_t(&main_view, 19.0f, &button, &main_view); - ui2_vsplit_l(&button, 30.0f, 0, &button); - ui2_vsplit_l(&button, 30.0f, &text, &button); - ui2_vsplit_r(&button, 5.0f, &button, 0); - ui2_hsplit_t(&button, 4.0f, 0, &button); - - float k = ((prevcolor>>((2-s)*8))&0xff) / 255.0f; - k = ui2_do_scrollbar_h(&color_slider[i][s], &button, k); - color <<= 8; - color += clamp((int)(k*255), 0, 255); - ui2_do_label(&text, labels[s], 20, -1); - - } - - *colors[i] = color; - ui2_hsplit_t(&main_view, 5.0f, 0, &main_view); - } - } - } - - // draw header - RECT header, footer; - ui2_hsplit_t(&skinselection, 20, &header, &skinselection); - ui2_draw_rect(&header, vec4(1,1,1,0.25f), CORNER_T, 5.0f); - ui2_do_label(&header, "Skins", 18.0f, 0); - - // draw footers - ui2_hsplit_b(&skinselection, 20, &skinselection, &footer); - ui2_draw_rect(&footer, vec4(1,1,1,0.25f), CORNER_B, 5.0f); - ui2_vsplit_l(&footer, 10.0f, 0, &footer); - - // modes - ui2_draw_rect(&skinselection, vec4(0,0,0,0.15f), 0, 0); - - RECT scroll; - ui2_vsplit_r(&skinselection, 15, &skinselection, &scroll); - - RECT list = skinselection; - ui2_hsplit_t(&list, 50, &button, &list); - - int num = (int)(skinselection.h/button.h); - static float scrollvalue = 0; - static int scrollbar = 0; - ui2_hmargin(&scroll, 5.0f, &scroll); - scrollvalue = ui2_do_scrollbar_v(&scrollbar, &scroll, scrollvalue); - - int start = (int)((skin_num()-num)*scrollvalue); - if(start < 0) - start = 0; - - animstate state; - anim_eval(&data->animations[ANIM_BASE], 0, &state); - anim_eval_add(&state, &data->animations[ANIM_IDLE], 0, 1.0f); - //anim_eval_add(&state, &data->animations[ANIM_WALK], fmod(client_localtime(), 1.0f), 1.0f); - - for(int i = start; i < start+num && i < skin_num(); i++) - { - const skin *s = skin_get(i); - char buf[128]; - sprintf(buf, "%s", s->name); - int selected = 0; - if(strcmp(s->name, config.player_skin) == 0) - selected = 1; - - tee_render_info info; - info.texture = s->org_texture; - info.color_body = vec4(1,1,1,1); - info.color_feet = vec4(1,1,1,1); - if(config.player_use_custom_color) - { - info.color_body = skin_get_color(config.player_color_body); - info.color_feet = skin_get_color(config.player_color_feet); - info.texture = s->color_texture; - } - - info.size = ui2_scale()*50.0f; - - RECT icon; - RECT text; - ui2_vsplit_l(&button, 50.0f, &icon, &text); - - if(ui2_do_button(s, "", selected, &button, ui2_draw_list_row, 0)) - config_set_player_skin(&config, s->name); - - ui2_hsplit_t(&text, 12.0f, 0, &text); // some margin from the top - ui2_do_label(&text, buf, 24, 0); - - ui2_hsplit_t(&icon, 5.0f, 0, &icon); // some margin from the top - render_tee(&state, &info, 0, vec2(1, 0), vec2(icon.x+icon.w/2, icon.y+icon.h/2)); - - ui2_hsplit_t(&list, 50, &button, &list); - } -} - -typedef void (*assign_func_callback)(CONFIGURATION *config, int value); - -static void menu2_render_settings_controls(RECT main_view) -{ - ui2_vsplit_l(&main_view, 300.0f, &main_view, 0); - - { - RECT button, label; - ui2_hsplit_t(&main_view, 20.0f, &button, &main_view); - ui2_vsplit_l(&button, 110.0f, &label, &button); - ui2_do_label(&label, "Mouse sens.", 18.0f, -1); - ui2_hmargin(&button, 2.0f, &button); - config.inp_mousesens = (int)(ui2_do_scrollbar_h(&config.inp_mousesens, &button, config.inp_mousesens/500.0f)*500.0f); - //*key.key = ui2_do_key_reader(key.key, &button, *key.key); - ui2_hsplit_t(&main_view, 20.0f, 0, &main_view); - } - - typedef struct - { - char name[32]; - int *key; - } KEYINFO; - - const KEYINFO keys[] = - { - { "Move Left:", &config.key_move_left }, - { "Move Right:", &config.key_move_right }, - { "Jump:", &config.key_jump }, - { "Fire:", &config.key_fire }, - { "Hook:", &config.key_hook }, - { "Hammer:", &config.key_weapon1 }, - { "Pistol:", &config.key_weapon2 }, - { "Shotgun:", &config.key_weapon3 }, - { "Grenade:", &config.key_weapon4 }, - { "Next Weapon:", &config.key_next_weapon }, - { "Prev. Weapon:", &config.key_prev_weapon }, - { "Emoticon:", &config.key_emoticon }, - { "Chat:", &config.key_chat }, - { "Team Chat:", &config.key_teamchat }, - { "Console:", &config.key_console }, - { "Remote Console:", &config.key_remoteconsole }, - { "Screenshot:", &config.key_screenshot }, - }; - - const int key_count = sizeof(keys) / sizeof(KEYINFO); - - for (int i = 0; i < key_count; i++) - { - KEYINFO key = keys[i]; - RECT button, label; - ui2_hsplit_t(&main_view, 20.0f, &button, &main_view); - ui2_vsplit_l(&button, 110.0f, &label, &button); - - ui2_do_label(&label, key.name, 18.0f, -1); - *key.key = ui2_do_key_reader(key.key, &button, *key.key); - ui2_hsplit_t(&main_view, 5.0f, 0, &main_view); - } -} - -static void menu2_render_settings_graphics(RECT main_view) -{ - RECT button; - char buf[128]; - - static const int MAX_RESOLUTIONS = 256; - static VIDEO_MODE modes[MAX_RESOLUTIONS]; - static int num_modes = -1; - - if(num_modes == -1) - num_modes = gfx_get_video_modes(modes, MAX_RESOLUTIONS); - - RECT modelist; - ui2_vsplit_l(&main_view, 300.0f, &main_view, &modelist); - - // draw allmodes switch - RECT header, footer; - ui2_hsplit_t(&modelist, 20, &button, &modelist); - if (ui2_do_button(&config.gfx_display_all_modes, "Show only supported", config.gfx_display_all_modes^1, &button, ui2_draw_checkbox, 0)) - { - config.gfx_display_all_modes ^= 1; - num_modes = gfx_get_video_modes(modes, MAX_RESOLUTIONS); - } - - // draw header - ui2_hsplit_t(&modelist, 20, &header, &modelist); - ui2_draw_rect(&header, vec4(1,1,1,0.25f), CORNER_T, 5.0f); - ui2_do_label(&header, "Display Modes", 18.0f, 0); - - // draw footers - ui2_hsplit_b(&modelist, 20, &modelist, &footer); - sprintf(buf, "Current: %dx%d %d bit", config.gfx_screen_width, config.gfx_screen_height, config.gfx_color_depth); - ui2_draw_rect(&footer, vec4(1,1,1,0.25f), CORNER_B, 5.0f); - ui2_vsplit_l(&footer, 10.0f, 0, &footer); - ui2_do_label(&footer, buf, 18.0f, -1); - - // modes - ui2_draw_rect(&modelist, vec4(0,0,0,0.15f), 0, 0); - - RECT scroll; - ui2_vsplit_r(&modelist, 15, &modelist, &scroll); - - RECT list = modelist; - ui2_hsplit_t(&list, 20, &button, &list); - - int num = (int)(modelist.h/button.h); - static float scrollvalue = 0; - static int scrollbar = 0; - ui2_hmargin(&scroll, 5.0f, &scroll); - scrollvalue = ui2_do_scrollbar_v(&scrollbar, &scroll, scrollvalue); - - int start = (int)((num_modes-num)*scrollvalue); - if(start < 0) - start = 0; - - for(int i = start; i < start+num && i < num_modes; i++) - { - int depth = modes[i].red+modes[i].green+modes[i].blue; - if(depth < 16) - depth = 16; - else if(depth > 16) - depth = 24; - - int selected = 0; - if(config.gfx_color_depth == depth && - config.gfx_screen_width == modes[i].width && - config.gfx_screen_height == modes[i].height) - { - selected = 1; - } - - sprintf(buf, " %dx%d %d bit", modes[i].width, modes[i].height, depth); - if(ui2_do_button(&modes[i], buf, selected, &button, ui2_draw_list_row, 0)) - { - config.gfx_color_depth = depth; - config.gfx_screen_width = modes[i].width; - config.gfx_screen_height = modes[i].height; - } - - ui2_hsplit_t(&list, 20, &button, &list); - } - - - // switches - ui2_hsplit_t(&main_view, 20.0f, &button, &main_view); - if (ui2_do_button(&config.gfx_fullscreen, "Fullscreen", config.gfx_fullscreen, &button, ui2_draw_checkbox, 0)) - config.gfx_fullscreen ^= 1; - - ui2_hsplit_t(&main_view, 20.0f, &button, &main_view); - if (ui2_do_button(&config.gfx_vsync, "V-Sync", config.gfx_vsync, &button, ui2_draw_checkbox, 0)) - config.gfx_vsync ^= 1; - - ui2_hsplit_t(&main_view, 20.0f, &button, &main_view); - if (ui2_do_button(&config.gfx_fsaa_samples, "FSAA samples", config.gfx_fsaa_samples, &button, ui2_draw_checkbox_number, 0)) - { - if(config.gfx_fsaa_samples < 2) config.gfx_fsaa_samples = 2; - else if(config.gfx_fsaa_samples < 4) config.gfx_fsaa_samples = 4; - else if(config.gfx_fsaa_samples < 6) config.gfx_fsaa_samples = 6; - else if(config.gfx_fsaa_samples < 8) config.gfx_fsaa_samples = 8; - else if(config.gfx_fsaa_samples < 16) config.gfx_fsaa_samples = 16; - else if(config.gfx_fsaa_samples >= 16) config.gfx_fsaa_samples = 0; - } - - ui2_hsplit_t(&main_view, 40.0f, &button, &main_view); - ui2_hsplit_t(&main_view, 20.0f, &button, &main_view); - if (ui2_do_button(&config.gfx_texture_quality, "Quality Textures", config.gfx_texture_quality, &button, ui2_draw_checkbox, 0)) - config.gfx_texture_quality ^= 1; - - ui2_hsplit_t(&main_view, 20.0f, &button, &main_view); - if (ui2_do_button(&config.gfx_texture_compression, "Texture Compression", config.gfx_texture_compression, &button, ui2_draw_checkbox, 0)) - config.gfx_texture_compression ^= 1; - - ui2_hsplit_t(&main_view, 20.0f, &button, &main_view); - if (ui2_do_button(&config.gfx_high_detail, "High Detail", config.gfx_high_detail, &button, ui2_draw_checkbox, 0)) - config.gfx_high_detail ^= 1; - - // - - RECT text; - ui2_hsplit_t(&main_view, 20.0f, 0, &main_view); - ui2_hsplit_t(&main_view, 20.0f, &text, &main_view); - //ui2_vsplit_l(&text, 15.0f, 0, &text); - ui2_do_label(&text, "UI Color", 18, -1); - - const char *labels[] = {"Hue", "Sat.", "Lht.", "Alpha"}; - int *color_slider[4] = {&config.ui_color_hue, &config.ui_color_sat, &config.ui_color_lht, &config.ui_color_alpha}; - for(int s = 0; s < 4; s++) - { - RECT text; - ui2_hsplit_t(&main_view, 19.0f, &button, &main_view); - ui2_vmargin(&button, 15.0f, &button); - ui2_vsplit_l(&button, 30.0f, &text, &button); - ui2_vsplit_r(&button, 5.0f, &button, 0); - ui2_hsplit_t(&button, 4.0f, 0, &button); - - float k = (*color_slider[s]) / 255.0f; - k = ui2_do_scrollbar_h(color_slider[s], &button, k); - *color_slider[s] = (int)(k*255.0f); - ui2_do_label(&text, labels[s], 20, -1); - } -} - -static void menu2_render_settings_sound(RECT main_view) -{ - RECT button; - ui2_vsplit_l(&main_view, 300.0f, &main_view, 0); - - ui2_hsplit_t(&main_view, 20.0f, &button, &main_view); - if (ui2_do_button(&config.snd_enable, "Use Sounds", config.snd_enable, &button, ui2_draw_checkbox, 0)) - config.snd_enable ^= 1; - - if(!config.snd_enable) - return; - - // sample rate box - { - char buf[64]; - sprintf(buf, "%d", config.snd_rate); - ui2_hsplit_t(&main_view, 20.0f, &button, &main_view); - ui2_do_label(&button, "Sample Rate", 18.0, -1); - ui2_vsplit_l(&button, 110.0f, 0, &button); - ui2_vsplit_l(&button, 180.0f, &button, 0); - ui2_do_edit_box(&config.snd_rate, &button, buf, sizeof(buf)); - config.snd_rate = atoi(buf); - - if(config.snd_rate < 1) - config.snd_rate = 1; - } - - // volume slider - { - RECT button, label; - ui2_hsplit_t(&main_view, 5.0f, &button, &main_view); - ui2_hsplit_t(&main_view, 20.0f, &button, &main_view); - ui2_vsplit_l(&button, 110.0f, &label, &button); - ui2_hmargin(&button, 2.0f, &button); - ui2_do_label(&label, "Sound Volume", 18.0f, -1); - config.snd_volume = (int)(ui2_do_scrollbar_h(&config.snd_volume, &button, config.snd_volume/100.0f)*100.0f); - ui2_hsplit_t(&main_view, 20.0f, 0, &main_view); - } -} - - -static void menu2_render_settings_network(RECT main_view) -{ - RECT button; - ui2_vsplit_l(&main_view, 300.0f, &main_view, 0); - - { - ui2_hsplit_t(&main_view, 20.0f, &button, &main_view); - ui2_do_label(&button, "Rcon Password", 18.0, -1); - ui2_vsplit_l(&button, 110.0f, 0, &button); - ui2_vsplit_l(&button, 180.0f, &button, 0); - ui2_do_edit_box(&config.rcon_password, &button, config.rcon_password, sizeof(config.rcon_password), true); - } -} - -static void menu2_render_settings(RECT main_view) -{ - static int settings_page = 0; - - // render background - RECT temp, tabbar; - ui2_vsplit_r(&main_view, 120.0f, &main_view, &tabbar); - ui2_draw_rect(&main_view, color_tabbar_active, CORNER_B|CORNER_TL, 10.0f); - ui2_hsplit_t(&tabbar, 50.0f, &temp, &tabbar); - ui2_draw_rect(&temp, color_tabbar_active, CORNER_R, 10.0f); - - ui2_hsplit_t(&main_view, 10.0f, 0, &main_view); - - RECT button; - - const char *tabs[] = {"Player", "Controls", "Network", "Graphics", "Sound"}; - int num_tabs = (int)(sizeof(tabs)/sizeof(*tabs)); - - for(int i = 0; i < num_tabs; i++) - { - ui2_hsplit_t(&tabbar, 10, &button, &tabbar); - ui2_hsplit_t(&tabbar, 26, &button, &tabbar); - if(ui2_do_button(tabs[i], tabs[i], settings_page == i, &button, ui2_draw_settings_tab_button, 0)) - settings_page = i; - } - - ui2_margin(&main_view, 10.0f, &main_view); - - if(settings_page == 0) - menu2_render_settings_player(main_view); - else if(settings_page == 1) - menu2_render_settings_controls(main_view); - else if(settings_page == 2) - menu2_render_settings_network(main_view); - else if(settings_page == 3) - menu2_render_settings_graphics(main_view); - else if(settings_page == 4) - menu2_render_settings_sound(main_view); -} - -static void menu2_render_news(RECT main_view) -{ - ui2_draw_rect(&main_view, color_tabbar_active, CORNER_ALL, 10.0f); -} - - -static void menu2_render_game(RECT main_view) -{ - RECT button; - ui2_hsplit_t(&main_view, 45.0f, &main_view, 0); - ui2_draw_rect(&main_view, color_tabbar_active, CORNER_ALL, 10.0f); - - ui2_hsplit_t(&main_view, 10.0f, 0, &main_view); - ui2_hsplit_t(&main_view, 25.0f, &main_view, 0); - ui2_vmargin(&main_view, 10.0f, &main_view); - - ui2_vsplit_r(&main_view, 120.0f, &main_view, &button); - static int disconnect_button = 0; - if(ui2_do_button(&disconnect_button, "Disconnect", 0, &button, ui2_draw_menu_button, 0)) - client_disconnect(); - - if(local_info) - { - if(local_info->team != -1) - { - ui2_vsplit_l(&main_view, 10.0f, &button, &main_view); - ui2_vsplit_l(&main_view, 120.0f, &button, &main_view); - static int spectate_button = 0; - if(ui2_do_button(&spectate_button, "Spectate", 0, &button, ui2_draw_menu_button, 0)) - { - config.cl_team = -1; - menu_active = false; - } - } - - if(local_info->team != 0) - { - ui2_vsplit_l(&main_view, 10.0f, &button, &main_view); - ui2_vsplit_l(&main_view, 120.0f, &button, &main_view); - static int spectate_button = 0; - if(ui2_do_button(&spectate_button, "Join Red", 0, &button, ui2_draw_menu_button, 0)) - { - config.cl_team = 0; - menu_active = false; - } - } - - if(local_info->team != 1) - { - ui2_vsplit_l(&main_view, 10.0f, &button, &main_view); - ui2_vsplit_l(&main_view, 120.0f, &button, &main_view); - static int spectate_button = 0; - if(ui2_do_button(&spectate_button, "Join Blue", 0, &button, ui2_draw_menu_button, 0)) - { - config.cl_team = 1; - menu_active = false; - } - } - } -} - -void menu_do_disconnected() -{ - popup = POPUP_NONE; - if(client_error_string() && client_error_string()[0] != 0) - { - if(strstr(client_error_string(), "password")) - { - popup = POPUP_PASSWORD; - ui_set_hot_item(&config.password); - ui_set_active_item(&config.password); - } - else - popup = POPUP_DISCONNECTED; - } -} - -void menu_do_connecting() -{ - popup = POPUP_CONNECTING; -} - -void menu_do_connected() -{ - popup = POPUP_NONE; -} - -int menu2_render() -{ - if(0) - { - gfx_mapscreen(0,0,10*4/3.0f,10); - gfx_clear(gui_color.r, gui_color.g, gui_color.b); - - animstate state; - anim_eval(&data->animations[ANIM_BASE], 0, &state); - anim_eval_add(&state, &data->animations[ANIM_IDLE], 0, 1.0f); - //anim_eval_add(&state, &data->animations[ANIM_WALK], fmod(client_localtime(), 1.0f), 1.0f); - - for(int i = 0; i < skin_num(); i++) - { - float x = (i/8)*3; - float y = (i%8); - for(int c = 0; c < 2; c++) - { - //int colors[2] = {54090, 10998628}; - //int colors[2] = {65432, 9895832}; // NEW - int colors[2] = {65387, 10223467}; // NEW - - tee_render_info info; - info.texture = skin_get(i)->color_texture; - info.color_feet = info.color_body = skin_get_color(colors[c]); - //info.color_feet = info.color_body = vec4(1,1,1,1); - info.size = 1.0f; //ui2_scale()*16.0f; - //render_tee(&state, &info, 0, vec2(sinf(client_localtime()*3), cosf(client_localtime()*3)), vec2(1+x+c,1+y)); - render_tee(&state, &info, 0, vec2(1,0), vec2(1+x+c,1+y)); - } - } - - return 0; - } - - RECT screen = *ui2_screen(); - gfx_mapscreen(screen.x, screen.y, screen.w, screen.h); - - static bool first = true; - if(first) - { - if(config.ui_page == PAGE_INTERNET) - client_serverbrowse_refresh(0); - else if(config.ui_page == PAGE_LAN) - client_serverbrowse_refresh(1); - first = false; - } - - if(client_state() == CLIENTSTATE_ONLINE) - { - color_tabbar_inactive = color_tabbar_inactive_ingame; - color_tabbar_active = color_tabbar_active_ingame; - } - else - { - menu2_render_background(); - color_tabbar_inactive = color_tabbar_inactive_outgame; - color_tabbar_active = color_tabbar_active_outgame; - } - - RECT tab_bar; - RECT main_view; - - // some margin around the screen - ui2_margin(&screen, 10.0f, &screen); - - if(popup == POPUP_NONE) - { - // do tab bar - ui2_hsplit_t(&screen, 26.0f, &tab_bar, &main_view); - ui2_vmargin(&tab_bar, 20.0f, &tab_bar); - menu2_render_menubar(tab_bar); - - // render current page - if(menu_game_active) - menu2_render_game(main_view); - else if(config.ui_page == PAGE_NEWS) - menu2_render_news(main_view); - else if(config.ui_page == PAGE_INTERNET) - menu2_render_serverbrowser(main_view); - else if(config.ui_page == PAGE_LAN) - menu2_render_serverbrowser(main_view); - else if(config.ui_page == PAGE_FAVORITES) - menu2_render_serverbrowser(main_view); - else if(config.ui_page == PAGE_SETTINGS) - menu2_render_settings(main_view); - } - else - { - // make sure that other windows doesn't do anything funnay! - //ui_set_hot_item(0); - //ui_set_active_item(0); - const char *title = ""; - const char *extra_text = ""; - const char *button_text = ""; - - if(popup == POPUP_CONNECTING) - { - title = "Connecting to"; - extra_text = config.ui_server_address; // TODO: query the client about the address - button_text = "Abort"; - } - else if(popup == POPUP_DISCONNECTED) - { - title = "Disconnected"; - extra_text = client_error_string(); - button_text = "Ok"; - } - else if(popup == POPUP_PASSWORD) - { - title = "Password Error"; - extra_text = client_error_string(); - button_text = "Try Again"; - } - else if(popup == POPUP_QUIT) - { - title = "Quit"; - extra_text = "Are you sure that you want to quit?"; - } - - - RECT box, part; - box = screen; - ui2_vmargin(&box, 150.0f, &box); - ui2_hmargin(&box, 150.0f, &box); - - // render the box - ui2_draw_rect(&box, vec4(0,0,0,0.5f), CORNER_ALL, 15.0f); - - ui2_hsplit_t(&box, 20.f, &part, &box); - ui2_hsplit_t(&box, 24.f, &part, &box); - ui2_do_label(&part, title, 24.f, 0); - ui2_hsplit_t(&box, 20.f, &part, &box); - ui2_hsplit_t(&box, 24.f, &part, &box); - ui2_do_label(&part, extra_text, 20.f, 0); - - if(popup == POPUP_QUIT) - { - RECT tryagain, abort; - ui2_hsplit_b(&box, 20.f, &box, &part); - ui2_hsplit_b(&box, 24.f, &box, &part); - ui2_vmargin(&part, 120.0f, &part); - - ui2_vsplit_l(&part, 100.0f, &abort, &part); - ui2_vsplit_r(&part, 100.0f, 0, &tryagain); - - static int button_abort = 0; - if(ui2_do_button(&button_abort, "No", 0, &abort, ui2_draw_menu_button, 0) || inp_key_down(KEY_ESC)) - popup = POPUP_NONE; - - static int button_tryagain = 0; - if(ui2_do_button(&button_tryagain, "Yes", 0, &tryagain, ui2_draw_menu_button, 0) || inp_key_down(KEY_ENTER)) - client_quit(); - } - else if(popup == POPUP_PASSWORD) - { - RECT label, textbox, tryagain, abort; - - ui2_hsplit_b(&box, 20.f, &box, &part); - ui2_hsplit_b(&box, 24.f, &box, &part); - ui2_vmargin(&part, 120.0f, &part); - - ui2_vsplit_l(&part, 100.0f, &abort, &part); - ui2_vsplit_r(&part, 100.0f, 0, &tryagain); - - static int button_abort = 0; - if(ui2_do_button(&button_abort, "Abort", 0, &abort, ui2_draw_menu_button, 0) || inp_key_down(KEY_ESC)) - popup = POPUP_NONE; - - static int button_tryagain = 0; - if(ui2_do_button(&button_tryagain, "Try again", 0, &tryagain, ui2_draw_menu_button, 0) || inp_key_down(KEY_ENTER)) - { - client_connect(config.ui_server_address); - } - - ui2_hsplit_b(&box, 60.f, &box, &part); - ui2_hsplit_b(&box, 24.f, &box, &part); - - ui2_vsplit_l(&part, 60.0f, 0, &label); - ui2_vsplit_l(&label, 100.0f, 0, &textbox); - ui2_vsplit_l(&textbox, 20.0f, 0, &textbox); - ui2_vsplit_r(&textbox, 60.0f, &textbox, 0); - ui2_do_label(&label, "Password:", 20, -1); - ui2_do_edit_box(&config.password, &textbox, config.password, sizeof(config.password), true); - } - else - { - ui2_hsplit_b(&box, 20.f, &box, &part); - ui2_hsplit_b(&box, 24.f, &box, &part); - ui2_vmargin(&part, 120.0f, &part); - - static int button = 0; - if(ui2_do_button(&button, button_text, 0, &part, ui2_draw_menu_button, 0) || inp_key_down(KEY_ESC) || inp_key_down(KEY_ENTER)) - { - if(popup == POPUP_CONNECTING) - client_disconnect(); - popup = POPUP_NONE; - } - } - } - - return 0; -} - -void modmenu_render() -{ - static int mouse_x = 0; - static int mouse_y = 0; - - // update colors - - vec3 rgb = hsl_to_rgb(vec3(config.ui_color_hue/255.0f, config.ui_color_sat/255.0f, config.ui_color_lht/255.0f)); - gui_color = vec4(rgb.r, rgb.g, rgb.b, config.ui_color_alpha/255.0f); - - color_tabbar_inactive_outgame = vec4(0,0,0,0.25f); - color_tabbar_active_outgame = vec4(0,0,0,0.5f); - - color_ingame_scale_i = 0.5f; - color_ingame_scale_a = 0.2f; - color_tabbar_inactive_ingame = vec4( - gui_color.r*color_ingame_scale_i, - gui_color.g*color_ingame_scale_i, - gui_color.b*color_ingame_scale_i, - gui_color.a*0.8f); - - color_tabbar_active_ingame = vec4( - gui_color.r*color_ingame_scale_a, - gui_color.g*color_ingame_scale_a, - gui_color.b*color_ingame_scale_a, - gui_color.a); - - - // handle mouse movement - float mx, my; - { - int rx, ry; - inp_mouse_relative(&rx, &ry); - mouse_x += rx; - mouse_y += ry; - if(mouse_x < 0) mouse_x = 0; - if(mouse_y < 0) mouse_y = 0; - if(mouse_x > gfx_screenwidth()) mouse_x = gfx_screenwidth(); - if(mouse_y > gfx_screenheight()) mouse_y = gfx_screenheight(); - - // update the ui - RECT *screen = ui2_screen(); - mx = (mouse_x/(float)gfx_screenwidth())*screen->w; - my = (mouse_y/(float)gfx_screenheight())*screen->h; - - int buttons = 0; - if(inp_key_pressed(KEY_MOUSE_1)) buttons |= 1; - if(inp_key_pressed(KEY_MOUSE_2)) buttons |= 2; - if(inp_key_pressed(KEY_MOUSE_3)) buttons |= 4; - - ui_update(mx,my,mx*3.0f,my*3.0f,buttons); - } - - menu2_render(); - - gfx_texture_set(data->images[IMAGE_CURSOR].id); - gfx_quads_begin(); - gfx_setcolor(1,1,1,1); - gfx_quads_drawTL(mx,my,24,24); - gfx_quads_end(); - - inp_clear(); -} diff --git a/src/game/g_game.cpp b/src/game/g_game.cpp new file mode 100644 index 00000000..e5b36ea0 --- /dev/null +++ b/src/game/g_game.cpp @@ -0,0 +1,411 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include "g_game.h" + +// TODO: OPT: rewrite this smarter! +void move_point(vec2 *inout_pos, vec2 *inout_vel, float elasticity, int *bounces) +{ + if(bounces) + *bounces = 0; + + vec2 pos = *inout_pos; + vec2 vel = *inout_vel; + if(col_check_point(pos + vel)) + { + int affected = 0; + if(col_check_point(pos.x + vel.x, pos.y)) + { + inout_vel->x *= -elasticity; + if(bounces) + (*bounces)++; + affected++; + } + + if(col_check_point(pos.x, pos.y + vel.y)) + { + inout_vel->y *= -elasticity; + if(bounces) + (*bounces)++; + affected++; + } + + if(affected == 0) + { + inout_vel->x *= -elasticity; + inout_vel->y *= -elasticity; + } + } + else + { + *inout_pos = pos + vel; + } +} + +bool test_box(vec2 pos, vec2 size) +{ + size *= 0.5f; + if(col_check_point(pos.x-size.x, pos.y-size.y)) + return true; + if(col_check_point(pos.x+size.x, pos.y-size.y)) + return true; + if(col_check_point(pos.x-size.x, pos.y+size.y)) + return true; + if(col_check_point(pos.x+size.x, pos.y+size.y)) + return true; + return false; +} + +void move_box(vec2 *inout_pos, vec2 *inout_vel, vec2 size, float elasticity) +{ + // do the move + vec2 pos = *inout_pos; + vec2 vel = *inout_vel; + + float distance = length(vel); + int max = (int)distance; + + if(distance > 0.00001f) + { + //vec2 old_pos = pos; + float fraction = 1.0f/(float)(max+1); + for(int i = 0; i <= max; i++) + { + //float amount = i/(float)max; + //if(max == 0) + //amount = 0; + + vec2 new_pos = pos + vel*fraction; // TODO: this row is not nice + + if(test_box(vec2(new_pos.x, new_pos.y), size)) + { + int hits = 0; + + if(test_box(vec2(pos.x, new_pos.y), size)) + { + new_pos.y = pos.y; + vel.y *= -elasticity; + hits++; + } + + if(test_box(vec2(new_pos.x, pos.y), size)) + { + new_pos.x = pos.x; + vel.x *= -elasticity; + hits++; + } + + // neither of the tests got a collision. + // this is a real _corner case_! + if(hits == 0) + { + new_pos.y = pos.y; + vel.y *= -elasticity; + new_pos.x = pos.x; + vel.x *= -elasticity; + } + } + + pos = new_pos; + } + } + + *inout_pos = pos; + *inout_vel = vel; +} + +void player_core::tick() +{ + float phys_size = 28.0f; + triggered_events = 0; + + #define MACRO_CHECK_VELOCITY { dbg_assert(length(vel) < 1000.0f, "velocity error"); } + + MACRO_CHECK_VELOCITY + + bool grounded = false; + if(col_check_point((int)(pos.x+phys_size/2), (int)(pos.y+phys_size/2+5))) + grounded = true; + if(col_check_point((int)(pos.x-phys_size/2), (int)(pos.y+phys_size/2+5))) + grounded = true; + + vec2 direction = normalize(vec2(input.target_x, input.target_y)); + + vel.y += gravity; + + MACRO_CHECK_VELOCITY + + float max_speed = grounded ? ground_control_speed : air_control_speed; + float accel = grounded ? ground_control_accel : air_control_accel; + float friction = grounded ? ground_friction : air_friction; + + // handle movement + if(input.left) + vel.x = saturated_add(-max_speed, max_speed, vel.x, -accel); + if(input.right) + vel.x = saturated_add(-max_speed, max_speed, vel.x, accel); + + MACRO_CHECK_VELOCITY + + if(!input.left && !input.right) + vel.x *= friction; + + MACRO_CHECK_VELOCITY + + // handle jumping + // 1 bit = to keep track if a jump has been made on this input + // 2 bit = to keep track if a air-jump has been made + if(grounded) + jumped &= ~2; + + if(input.jump) + { + if(!(jumped&1)) + { + if(grounded) + { + triggered_events |= COREEVENT_GROUND_JUMP; + vel.y = -ground_jump_speed; + jumped |= 1; + } + else if(!(jumped&2)) + { + triggered_events |= COREEVENT_AIR_JUMP; + vel.y = -ground_air_speed; + jumped |= 3; + } + } + } + else + jumped &= ~1; + + MACRO_CHECK_VELOCITY + + // do hook + if(input.hook) + { + if(hook_state == HOOK_IDLE) + { + hook_state = HOOK_FLYING; + hook_pos = pos; + hook_dir = direction; + hooked_player = -1; + hook_tick = 0; + triggered_events |= COREEVENT_HOOK_LAUNCH; + } + else if(hook_state == HOOK_FLYING) + { + vec2 new_pos = hook_pos+hook_dir*hook_fire_speed; + + // Check against other players first + for(int i = 0; i < MAX_CLIENTS; i++) + { + player_core *p = world->players[i]; + if(!p || p == this) + continue; + + //if(p != this && !p->dead && distance(p->pos, new_pos) < p->phys_size) + if(distance(p->pos, new_pos) < phys_size) + { + triggered_events |= COREEVENT_HOOK_ATTACH_PLAYER; + hook_state = HOOK_GRABBED; + hooked_player = i; + break; + } + } + /* + for(entity *ent = world.first_entity; ent; ent = ent->next_entity) + { + if(ent && ent->objtype == OBJTYPE_PLAYER) + { + player *p = (player*)ent; + if(p != this && !p->dead && distance(p->pos, new_pos) < p->phys_size) + { + hook_state = HOOK_GRABBED; + hooked_player = p; + break; + } + } + }*/ + + if(hook_state == HOOK_FLYING) + { + // check against ground + if(col_intersect_line(hook_pos, new_pos, &new_pos)) + { + triggered_events |= COREEVENT_HOOK_ATTACH_GROUND; + hook_state = HOOK_GRABBED; + hook_pos = new_pos; + } + else if(distance(pos, new_pos) > hook_length) + { + triggered_events |= COREEVENT_HOOK_RETRACT; + hook_state = HOOK_RETRACTED; + } + else + hook_pos = new_pos; + } + + if(hook_state == HOOK_GRABBED) + { + //create_sound(pos, SOUND_HOOK_ATTACH); + //hook_tick = server_tick(); + } + } + } + else + { + //release_hooked(); + hooked_player = -1; + hook_state = HOOK_IDLE; + hook_pos = pos; + } + + if(hook_state == HOOK_GRABBED) + { + if(hooked_player != -1) + { + player_core *p = world->players[hooked_player]; + if(p) + hook_pos = p->pos; + else + { + // release hook + hooked_player = -1; + hook_state = HOOK_RETRACTED; + hook_pos = pos; + } + + // keep players hooked for a max of 1.5sec + //if(server_tick() > hook_tick+(server_tickspeed()*3)/2) + //release_hooked(); + } + + // Old version feels much better (to me atleast) + if(distance(hook_pos, pos) > 46.0f) + { + vec2 hookvel = normalize(hook_pos-pos)*hook_drag_accel; + // the hook as more power to drag you up then down. + // this makes it easier to get on top of an platform + if(hookvel.y > 0) + hookvel.y *= 0.3f; + + // the hook will boost it's power if the player wants to move + // in that direction. otherwise it will dampen everything abit + if((hookvel.x < 0 && input.left) || (hookvel.x > 0 && input.right)) + hookvel.x *= 0.95f; + else + hookvel.x *= 0.75f; + + vec2 new_vel = vel+hookvel; + + // check if we are under the legal limit for the hook + if(length(new_vel) < hook_drag_speed || length(new_vel) < length(vel)) + vel = new_vel; // no problem. apply + + } + + // release hook + hook_tick++; + if(hooked_player != -1 && hook_tick > SERVER_TICK_SPEED*2) + { + hooked_player = -1; + hook_state = HOOK_RETRACTED; + hook_pos = pos; + } + } + + MACRO_CHECK_VELOCITY + + if(true) + { + for(int i = 0; i < MAX_CLIENTS; i++) + { + player_core *p = world->players[i]; + if(!p) + continue; + + //player *p = (player*)ent; + if(p == this) // || !(p->flags&FLAG_ALIVE) + continue; // make sure that we don't nudge our self + + // handle player <-> player collision + float d = distance(pos, p->pos); + vec2 dir = normalize(pos - p->pos); + if(d < phys_size*1.25f && d > 1.0f) + { + float a = phys_size*1.25f - d; + vel = vel + dir*a; + } + + MACRO_CHECK_VELOCITY + + // handle hook influence + if(hooked_player == i) + { + if(d > phys_size*1.50f) // TODO: fix tweakable variable + { + float accel = hook_drag_accel * (d/hook_length); + vel.x = saturated_add(-hook_drag_speed, hook_drag_speed, vel.x, -accel*dir.x); + vel.y = saturated_add(-hook_drag_speed, hook_drag_speed, vel.y, -accel*dir.y); + + MACRO_CHECK_VELOCITY + } + } + } + } +} + +void player_core::move() +{ + move_box(&pos, &vel, vec2(28.0f, 28.0f), 0); +} + +void player_core::write(obj_player_core *obj_core) +{ + obj_core->x = (int)pos.x; + obj_core->y = (int)pos.y; + obj_core->vx = (int)(vel.x*256.0f); + obj_core->vy = (int)(vel.y*256.0f); + obj_core->hook_state = hook_state; + obj_core->hook_tick = hook_tick; + obj_core->hook_x = (int)hook_pos.x; + obj_core->hook_y = (int)hook_pos.y; + obj_core->hook_dx = (int)(hook_dir.x*256.0f); + obj_core->hook_dy = (int)(hook_dir.y*256.0f); + obj_core->hooked_player = hooked_player; + obj_core->jumped = jumped; + + float a = 0; + if(input.target_x == 0) + a = atan((float)input.target_y); + else + a = atan((float)input.target_y/(float)input.target_x); + + if(input.target_x < 0) + a = a+pi; + + obj_core->angle = (int)(a*256.0f); +} + +void player_core::read(const obj_player_core *obj_core) +{ + pos.x = obj_core->x; + pos.y = obj_core->y; + vel.x = obj_core->vx/256.0f; + vel.y = obj_core->vy/256.0f; + hook_state = obj_core->hook_state; + hook_tick = obj_core->hook_tick; + hook_pos.x = obj_core->hook_x; + hook_pos.y = obj_core->hook_y; + hook_dir.x = obj_core->hook_dx/256.0f; + hook_dir.y = obj_core->hook_dy/256.0f; + hooked_player = obj_core->hooked_player; + jumped = obj_core->jumped; +} + +void player_core::quantize() +{ + obj_player_core c; + write(&c); + read(&c); +} + diff --git a/src/game/g_game.h b/src/game/g_game.h new file mode 100644 index 00000000..70a15a19 --- /dev/null +++ b/src/game/g_game.h @@ -0,0 +1,171 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include +#include +#include +#include "../engine/e_interface.h" +#include "g_mapres_col.h" + +#include "g_protocol.h" + +inline vec2 get_direction(int angle) +{ + float a = angle/256.0f; + return vec2(cosf(a), sinf(a)); +} + +inline vec2 get_dir(float a) +{ + return vec2(cosf(a), sinf(a)); +} + +inline float get_angle(vec2 dir) +{ + float a = atan(dir.y/dir.x); + if(dir.x < 0) + a = a+pi; + return a; +} + + +inline vec2 calc_pos(vec2 p, vec2 v, float gravity, float t) +{ + vec2 n; + n.x = p.x + v.x*t; + n.y = p.y + v.y*t - gravity*(t*t); + return n; +} + + +template +inline T saturated_add(T min, T max, T current, T modifier) +{ + if(modifier < 0) + { + if(current < min) + return current; + current += modifier; + if(current < min) + current = min; + return current; + } + else + { + if(current > max) + return current; + current += modifier; + if(current > max) + current = max; + return current; + } +} + +void move_point(vec2 *inout_pos, vec2 *inout_vel, float elasticity, int *bounces); +void move_box(vec2 *inout_pos, vec2 *inout_vel, vec2 size, float elasticity); +bool test_box(vec2 pos, vec2 size); + + +// hooking stuff +enum +{ + HOOK_RETRACTED=-1, + HOOK_IDLE=0, + HOOK_FLYING, + HOOK_GRABBED, + + COREEVENT_GROUND_JUMP=0x01, + COREEVENT_AIR_JUMP=0x02, + COREEVENT_HOOK_LAUNCH=0x04, + COREEVENT_HOOK_ATTACH_PLAYER=0x08, + COREEVENT_HOOK_ATTACH_GROUND=0x10, + COREEVENT_HOOK_RETRACT=0x20, +}; + +class world_core +{ +public: + world_core() + { + mem_zero(players, sizeof(players)); + } + + class player_core *players[MAX_CLIENTS]; +}; + +class player_core +{ +public: + world_core *world; + + vec2 pos; + vec2 vel; + + vec2 hook_pos; + vec2 hook_dir; + int hook_tick; + int hook_state; + int hooked_player; + + int jumped; + player_input input; + + int triggered_events; + + void tick(); + void move(); + + void read(const obj_player_core *obj_core); + void write(obj_player_core *obj_core); + void quantize(); +}; + + +#define LERP(a,b,t) (a + (b-a) * t) +#define min(a, b) ( a > b ? b : a) +#define max(a, b) ( a > b ? a : b) + +inline bool col_check_point(float x, float y) { return col_check_point((int)x, (int)y) != 0; } +inline bool col_check_point(vec2 p) { return col_check_point(p.x, p.y); } + +struct mapres_entity +{ + int x, y; + int data[1]; +}; + +struct mapres_spawnpoint +{ + int x, y; +}; + +struct mapres_item +{ + int x, y; + int type; +}; + +struct mapres_flagstand +{ + int x, y; +}; + +enum +{ + MAPRES_ENTS_START=1, + MAPRES_SPAWNPOINT=1, + MAPRES_ITEM=2, + MAPRES_SPAWNPOINT_RED=3, + MAPRES_SPAWNPOINT_BLUE=4, + MAPRES_FLAGSTAND_RED=5, + MAPRES_FLAGSTAND_BLUE=6, + MAPRES_ENTS_END, + + ITEM_NULL=0, + ITEM_WEAPON_GUN=0x00010001, + ITEM_WEAPON_SHOTGUN=0x00010002, + ITEM_WEAPON_ROCKET=0x00010003, + ITEM_WEAPON_SNIPER=0x00010004, + ITEM_WEAPON_HAMMER=0x00010005, + ITEM_HEALTH =0x00020001, + ITEM_ARMOR=0x00030001, + ITEM_NINJA=0x00040001, +}; diff --git a/src/game/g_mapres.h b/src/game/g_mapres.h new file mode 100644 index 00000000..77a3ccaa --- /dev/null +++ b/src/game/g_mapres.h @@ -0,0 +1,14 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +enum +{ + MAPRES_REGISTERED=0x8000, + MAPRES_IMAGE=0x8001, + MAPRES_TILEMAP=0x8002, + MAPRES_COLLISIONMAP=0x8003, + MAPRES_TEMP_THEME=0x8fff, +}; + +struct mapres_theme +{ + int id; +}; diff --git a/src/game/g_mapres_col.cpp b/src/game/g_mapres_col.cpp new file mode 100644 index 00000000..4b391204 --- /dev/null +++ b/src/game/g_mapres_col.cpp @@ -0,0 +1,69 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include +#include +#include +#include +#include "../engine/e_interface.h" +#include "g_mapres_col.h" +#include "g_mapres.h" + +/* + Simple collision rutines! +*/ +struct collision +{ + int w, h; + unsigned char *data; +}; + +static collision col; +static int global_dividor; + +int col_init(int dividor) +{ + mapres_collision *c = (mapres_collision*)map_find_item(MAPRES_COLLISIONMAP,0); + if(!c) + { + dbg_msg("mapres_col", "failed!"); + return 0; + } + col.w = c->width; + col.h = c->height; + global_dividor = dividor; + col.data = (unsigned char *)map_get_data(c->data_index); + return col.data ? 1 : 0; +} + +int col_check_point(int x, int y) +{ + int nx = x/global_dividor; + int ny = y/global_dividor; + if(nx < 0 || nx >= col.w || ny >= col.h) + return 1; + + if(y < 0) + return 0; // up == sky == free + + return col.data[ny*col.w+nx]; +} + +// TODO: rewrite this smarter! +bool col_intersect_line(vec2 pos0, vec2 pos1, vec2 *out) +{ + float d = distance(pos0, pos1); + + for(float f = 0; f < d; f++) + { + float a = f/d; + vec2 pos = mix(pos0, pos1, a); + if(col_check_point((int)pos.x, (int)pos.y)) + { + if(out) + *out = pos; + return true; + } + } + if(out) + *out = pos1; + return false; +} diff --git a/src/game/g_mapres_col.h b/src/game/g_mapres_col.h new file mode 100644 index 00000000..5d4427ab --- /dev/null +++ b/src/game/g_mapres_col.h @@ -0,0 +1,13 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include + +struct mapres_collision +{ + int width; + int height; + int data_index; +}; + +int col_init(int dividor); +int col_check_point(int x, int y); +bool col_intersect_line(vec2 pos0, vec2 pos1, vec2 *out); diff --git a/src/game/g_math.h b/src/game/g_math.h new file mode 100644 index 00000000..e2cf6bd8 --- /dev/null +++ b/src/game/g_math.h @@ -0,0 +1,30 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#ifndef BASE_MATH_H +#define BASE_MATH_H + +template +inline T clamp(T val, T min, T max) +{ + if(val < min) + return min; + if(val > max) + return max; + return val; +} + +inline float sign(float f) +{ + return f<0.0f?-1.0f:1.0f; +} + +template +inline T mix(const T a, const T b, TB amount) +{ + return a + (b-a)*amount; +} +const float pi = 3.1415926535897932384626433f; + +template inline T min(T a, T b) { return a inline T max(T a, T b) { return a>b?a:b; } + +#endif // BASE_MATH_H diff --git a/src/game/g_protocol.h b/src/game/g_protocol.h new file mode 100644 index 00000000..4fcc1e5f --- /dev/null +++ b/src/game/g_protocol.h @@ -0,0 +1,215 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +// NOTE: Be very careful when editing this file as it will change the network version + +// --------- PHYSICS TWEAK! -------- +const float ground_control_speed = 7.0f; +const float ground_control_accel = 2.0f; +const float ground_friction = 0.5f; +const float ground_jump_speed = 12.6f; +const float ground_air_speed = 11.5f; +const float air_control_speed = 5.0f; +const float air_control_accel = 1.5f; +const float air_friction = 0.95f; +const float hook_length = 34*10.0f; +const float hook_fire_speed = 45.0f; +const float hook_drag_accel = 3.0f; +const float hook_drag_speed = 15.0f; +const float gravity = 0.5f; +const float wall_friction = 0.80f; +const float wall_jump_speed_up = ground_jump_speed*0.8f; +const float wall_jump_speed_out = ground_jump_speed*0.8f; + +// Network stuff +enum +{ + OBJTYPE_NULL=0, + OBJTYPE_GAME, + OBJTYPE_PLAYER_INFO, + OBJTYPE_PLAYER_CHARACTER, // use this if you are searching for the player entity + OBJTYPE_PROJECTILE, + OBJTYPE_POWERUP, + OBJTYPE_FLAG, + EVENT_EXPLOSION, + EVENT_DAMAGEINDICATION, + EVENT_SOUND_WORLD, + EVENT_SMOKE, + EVENT_SPAWN, + EVENT_DEATH, + EVENT_AIR_JUMP, + + EVENT_DUMMY +}; + +enum +{ + MSG_NULL=0, + MSG_SAY, // client -> server + MSG_CHAT, // server -> client + MSG_SETINFO, // server -> client - contains name, skin and color info + MSG_KILLMSG, // server -> client + MSG_SETTEAM, + MSG_JOIN, + MSG_QUIT, + MSG_EMOTICON, + MSG_STARTINFO, // client -> server + MSG_CHANGEINFO, // client -> server + MSG_READY_TO_ENTER, // server -> client + MSG_WEAPON_PICKUP, + MSG_SOUND_GLOBAL, +}; + +enum +{ + EMOTE_NORMAL=0, + EMOTE_PAIN, + EMOTE_HAPPY, + EMOTE_SURPRISE, + EMOTE_ANGRY, + EMOTE_BLINK, +}; + +enum +{ + INPUT_STATE_MASK=0x1f, +}; + +enum +{ + STATE_UNKNOWN=0, + STATE_PLAYING, + STATE_IN_MENU, + STATE_CHATTING, + + GAMETYPE_DM=0, + GAMETYPE_TDM, + GAMETYPE_CTF, +}; + +struct player_input +{ + int left; + int right; + + int target_x; + int target_y; + + int jump; + int fire; + int hook; + int blink; + int state; + + int wanted_weapon; + int next_weapon; + int prev_weapon; +}; + +struct ev_common +{ + int x, y; +}; + +struct ev_explosion : public ev_common +{ +}; + +struct ev_spawn : public ev_common +{ +}; + +struct ev_death : public ev_common +{ +}; + +struct ev_sound : public ev_common +{ + int sound; +}; + +struct ev_damageind : public ev_common +{ + int angle; +}; + +struct obj_game +{ + int round_start_tick; + int game_over; + int sudden_death; + int paused; + + int score_limit; + int time_limit; + int gametype; + + int warmup; + + int teamscore[2]; +}; + +struct obj_projectile +{ + int type; + int tick; + int x, y; + int vx, vy; // should be an angle instead + int start_tick; +}; + +struct obj_powerup +{ + int x, y; + int type; // why do we need two types? + int subtype; +}; + +struct obj_flag +{ + int x, y; + int team; + int carried_by; // is set if the local player has the flag +}; + +// core object needed for physics +struct obj_player_core +{ + int x, y; + int vx, vy; + int angle; + int jumped; + + int hooked_player; + int hook_state; + int hook_tick; + int hook_x, hook_y; + int hook_dx, hook_dy; +}; + +// info about the player that is only needed when it's on screen +struct obj_player_character : public obj_player_core +{ + int state; + + int health; + int armor; + int ammocount; + int weaponstage; + + int weapon; // current active weapon + + int emote; + + int attacktick; // num attack ticks left of current attack +}; + +// information about the player that is always needed +struct obj_player_info +{ + int local; + int clientid; + + int team; + int score; + int latency; + int latency_flux; +}; diff --git a/src/game/g_variables.h b/src/game/g_variables.h new file mode 100644 index 00000000..44e52c6f --- /dev/null +++ b/src/game/g_variables.h @@ -0,0 +1,64 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +MACRO_CONFIG_INT(key_move_left, 'A', 32, 512) +MACRO_CONFIG_INT(key_move_right, 'D', 32, 512) +MACRO_CONFIG_INT(key_jump, 32, 32, 512) +MACRO_CONFIG_INT(key_fire, 384, 32, 512) +MACRO_CONFIG_INT(key_hook, 385, 32, 512) +MACRO_CONFIG_INT(key_weapon1, '1', 32, 512) +MACRO_CONFIG_INT(key_weapon2, '2', 32, 512) +MACRO_CONFIG_INT(key_weapon3, '3', 32, 512) +MACRO_CONFIG_INT(key_weapon4, '4', 32, 512) +MACRO_CONFIG_INT(key_weapon5, '5', 32, 512) +MACRO_CONFIG_INT(key_weapon6, '6', 32, 512) +MACRO_CONFIG_INT(key_weapon7, '7', 32, 512) + +MACRO_CONFIG_INT(key_next_weapon, 382, 32, 512) +MACRO_CONFIG_INT(key_prev_weapon, 383, 32, 512) + +MACRO_CONFIG_INT(key_emoticon, 'E', 32, 512) + +MACRO_CONFIG_INT(key_chat, 'T', 32, 512) +MACRO_CONFIG_INT(key_teamchat, 'Y', 32, 512) + +MACRO_CONFIG_INT(key_console, 256+2, 32, 512) +MACRO_CONFIG_INT(key_remoteconsole, 256+3, 32, 512) + + +MACRO_CONFIG_INT(dbg_bots, 0, 0, 7) + +MACRO_CONFIG_INT(cl_predict, 1, 0, 1) +MACRO_CONFIG_INT(cl_nameplates, 0, 0, 1) +MACRO_CONFIG_INT(cl_nameplates_always, 0, 0, 1) +MACRO_CONFIG_INT(cl_dynamic_camera, 1, 0, 1) +MACRO_CONFIG_INT(cl_team, -10, -1, 0) +MACRO_CONFIG_INT(cl_autoswitch_weapons, 0, 0, 1) + +MACRO_CONFIG_INT(player_use_custom_color, 0, 0, 1) +MACRO_CONFIG_INT(player_color_body, 65408, 0, 0) +MACRO_CONFIG_INT(player_color_feet, 65408, 0, 0) +MACRO_CONFIG_STR(player_skin, 64, "default") + +MACRO_CONFIG_INT(dbg_new_gui, 0, 0, 1) + +MACRO_CONFIG_INT(ui_page, 1, 0, 5) +MACRO_CONFIG_STR(ui_server_address, 128, "localhost:8303") +MACRO_CONFIG_INT(ui_scale, 100, 1, 100000) + +MACRO_CONFIG_INT(ui_color_hue, 160, 0, 255) +MACRO_CONFIG_INT(ui_color_sat, 70, 0, 255) +MACRO_CONFIG_INT(ui_color_lht, 175, 0, 255) +MACRO_CONFIG_INT(ui_color_alpha, 228, 0, 255) + + +MACRO_CONFIG_INT(sv_warmup, 0, 0, 0) +MACRO_CONFIG_STR(sv_msg, 512, "") +MACRO_CONFIG_INT(sv_teamdamage, 0, 0, 1) +MACRO_CONFIG_STR(sv_maprotation, 512, "") +MACRO_CONFIG_INT(sv_powerups, 1, 0, 1) +MACRO_CONFIG_INT(sv_scorelimit, 20, 0, 1000) +MACRO_CONFIG_INT(sv_timelimit, 0, 0, 1000) +MACRO_CONFIG_STR(sv_gametype, 32, "dm") +MACRO_CONFIG_INT(sv_restart, 0, 0, 120) + + + diff --git a/src/game/g_version.h b/src/game/g_version.h new file mode 100644 index 00000000..8f261c0f --- /dev/null +++ b/src/game/g_version.h @@ -0,0 +1,4 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include "generated/nethash.c" +#define TEEWARS_VERSION "0.3.0-dev" +#define TEEWARS_NETVERSION "0.3 " TEEWARS_NETVERSION_HASH diff --git a/src/game/g_vmath.h b/src/game/g_vmath.h new file mode 100644 index 00000000..65f94776 --- /dev/null +++ b/src/game/g_vmath.h @@ -0,0 +1,182 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#ifndef BASE_VMATH_H +#define BASE_VMATH_H + +//#include + +// ------------------------------------ + +template +class vector2_base +{ +public: + union { T x,u; }; + union { T y,v; }; + + vector2_base() {} + vector2_base(float nx, float ny) + { + x = nx; + y = ny; + } + + vector2_base operator -() const { return vector2_base(-x, -y); } + vector2_base operator -(const vector2_base &v) const { return vector2_base(x-v.x, y-v.y); } + vector2_base operator +(const vector2_base &v) const { return vector2_base(x+v.x, y+v.y); } + vector2_base operator *(const T v) const { return vector2_base(x*v, y*v); } + + const vector2_base &operator =(const vector2_base &v) { x = v.x; y = v.y; return *this; } + + const vector2_base &operator +=(const vector2_base &v) { x += v.x; y += v.y; return *this; } + const vector2_base &operator -=(const vector2_base &v) { x -= v.x; y -= v.y; return *this; } + const vector2_base &operator *=(const T v) { x *= v; y *= v; return *this; } + + bool operator ==(const vector2_base &v) const { return x == v.x && y == v.y; } //TODO: do this with an eps instead + + operator const T* () { return &x; } +}; + + +template +inline T length(const vector2_base &a) +{ + return sqrtf(a.x*a.x + a.y*a.y); +} + +template +inline T distance(const vector2_base a, const vector2_base &b) +{ + return length(a-b); +} + +template +inline T dot(const vector2_base a, const vector2_base &b) +{ + return a.x*b.x + a.y*b.y; +} + +template +inline vector2_base normalize(const vector2_base &v) +{ + T l = (T)(1.0f/sqrtf(v.x*v.x + v.y*v.y)); + return vector2_base(v.x*l, v.y*l); +} + +typedef vector2_base vec2; +typedef vector2_base bvec2; +typedef vector2_base ivec2; + +// ------------------------------------ +template +class vector3_base +{ +public: + union { T x,r,h; }; + union { T y,g,s; }; + union { T z,b,v,l; }; + + vector3_base() {} + vector3_base(float nx, float ny, float nz) + { + x = nx; + y = ny; + z = nz; + } + + const vector3_base &operator =(const vector3_base &v) { x = v.x; y = v.y; z = v.z; return *this; } + + vector3_base operator -(const vector3_base &v) const { return vector3_base(x-v.x, y-v.y, z-v.z); } + vector3_base operator -() const { return vector3_base(-x, -y, -z); } + vector3_base operator +(const vector3_base &v) const { return vector3_base(x+v.x, y+v.y, z+v.z); } + vector3_base operator *(const T v) const { return vector3_base(x*v, y*v, z*v); } + vector3_base operator *(const vector3_base &v) const { return vector3_base(x*v.x, y*v.y, z*v.z); } + vector3_base operator /(const T v) const { return vector3_base(x/v, y/v, z/v); } + + const vector3_base &operator +=(const vector3_base &v) { x += v.x; y += v.y; z += v.z; return *this; } + const vector3_base &operator -=(const vector3_base &v) { x -= v.x; y -= v.y; z -= v.z; return *this; } + const vector3_base &operator *=(const T v) { x *= v; y *= v; z *= v; return *this; } + + bool operator ==(const vector3_base &v) const { return x == v.x && y == v.y && z == v.z; } //TODO: do this with an eps instead + + operator const T* () { return &x; } +}; + +template +inline T length(const vector3_base &a) +{ + return sqrtf(a.x*a.x + a.y*a.y + a.z*a.z); +} + +template +inline T distance(const vector3_base &a, const vector3_base &b) +{ + return length(a-b); +} + +template +inline T dot(const vector3_base &a, const vector3_base &b) +{ + return a.x*b.x + a.y*b.y + a.z*b.z; +} + +template +inline vector3_base normalize(const vector3_base &v) +{ + T l = (T)(1.0f/sqrtf(v.x*v.x + v.y*v.y + v.z*v.z)); + return vector3_base(v.x*l, v.y*l, v.z*l); +} + +template +inline vector3_base cross(const vector3_base &a, const vector3_base &b) +{ + return vector3_base( + a.y*b.z - a.z*b.y, + a.z*b.x - a.x*b.z, + a.x*b.y - a.y*b.x); +} + +typedef vector3_base vec3; +typedef vector3_base bvec3; +typedef vector3_base ivec3; + +// ------------------------------------ + +template +class vector4_base +{ +public: + union { T x,r; }; + union { T y,g; }; + union { T z,b; }; + union { T w,a; }; + + vector4_base() {} + vector4_base(float nx, float ny, float nz, float nw) + { + x = nx; + y = ny; + z = nz; + w = nw; + } + + vector4_base operator +(const vector4_base &v) const { return vector4_base(x+v.x, y+v.y, z+v.z, w+v.w); } + vector4_base operator -(const vector4_base &v) const { return vector4_base(x-v.x, y-v.y, z-v.z, w-v.w); } + vector4_base operator -() const { return vector4_base(-x, -y, -z, -w); } + vector4_base operator *(const T v) const { return vector4_base(x*v, y*v, z*v, w*v); } + + const vector4_base &operator =(const vector4_base &v) { x = v.x; y = v.y; z = v.z; w = v.w; return *this; } + + const vector4_base &operator +=(const vector4_base &v) { x += v.x; y += v.y; z += v.z; w += v.w; return *this; } + const vector4_base &operator -=(const vector4_base &v) { x -= v.x; y -= v.y; z -= v.z; w -= v.w; return *this; } + const vector4_base &operator *=(const T v) { x *= v; y *= v; z *= v; w *= v; return *this; } + + bool operator ==(const vector4_base &v) const { return x == v.x && y == v.y && z == v.z && w == v.w; } //TODO: do this with an eps instead + + operator const T* () { return &x; } +}; + +typedef vector4_base vec4; +typedef vector4_base bvec4; +typedef vector4_base ivec4; + +#endif diff --git a/src/game/game.cpp b/src/game/game.cpp deleted file mode 100644 index 29e4da14..00000000 --- a/src/game/game.cpp +++ /dev/null @@ -1,411 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include "game.h" - -// TODO: OPT: rewrite this smarter! -void move_point(vec2 *inout_pos, vec2 *inout_vel, float elasticity, int *bounces) -{ - if(bounces) - *bounces = 0; - - vec2 pos = *inout_pos; - vec2 vel = *inout_vel; - if(col_check_point(pos + vel)) - { - int affected = 0; - if(col_check_point(pos.x + vel.x, pos.y)) - { - inout_vel->x *= -elasticity; - if(bounces) - (*bounces)++; - affected++; - } - - if(col_check_point(pos.x, pos.y + vel.y)) - { - inout_vel->y *= -elasticity; - if(bounces) - (*bounces)++; - affected++; - } - - if(affected == 0) - { - inout_vel->x *= -elasticity; - inout_vel->y *= -elasticity; - } - } - else - { - *inout_pos = pos + vel; - } -} - -bool test_box(vec2 pos, vec2 size) -{ - size *= 0.5f; - if(col_check_point(pos.x-size.x, pos.y-size.y)) - return true; - if(col_check_point(pos.x+size.x, pos.y-size.y)) - return true; - if(col_check_point(pos.x-size.x, pos.y+size.y)) - return true; - if(col_check_point(pos.x+size.x, pos.y+size.y)) - return true; - return false; -} - -void move_box(vec2 *inout_pos, vec2 *inout_vel, vec2 size, float elasticity) -{ - // do the move - vec2 pos = *inout_pos; - vec2 vel = *inout_vel; - - float distance = length(vel); - int max = (int)distance; - - if(distance > 0.00001f) - { - //vec2 old_pos = pos; - float fraction = 1.0f/(float)(max+1); - for(int i = 0; i <= max; i++) - { - //float amount = i/(float)max; - //if(max == 0) - //amount = 0; - - vec2 new_pos = pos + vel*fraction; // TODO: this row is not nice - - if(test_box(vec2(new_pos.x, new_pos.y), size)) - { - int hits = 0; - - if(test_box(vec2(pos.x, new_pos.y), size)) - { - new_pos.y = pos.y; - vel.y *= -elasticity; - hits++; - } - - if(test_box(vec2(new_pos.x, pos.y), size)) - { - new_pos.x = pos.x; - vel.x *= -elasticity; - hits++; - } - - // neither of the tests got a collision. - // this is a real _corner case_! - if(hits == 0) - { - new_pos.y = pos.y; - vel.y *= -elasticity; - new_pos.x = pos.x; - vel.x *= -elasticity; - } - } - - pos = new_pos; - } - } - - *inout_pos = pos; - *inout_vel = vel; -} - -void player_core::tick() -{ - float phys_size = 28.0f; - triggered_events = 0; - - #define MACRO_CHECK_VELOCITY { dbg_assert(length(vel) < 1000.0f, "velocity error"); } - - MACRO_CHECK_VELOCITY - - bool grounded = false; - if(col_check_point((int)(pos.x+phys_size/2), (int)(pos.y+phys_size/2+5))) - grounded = true; - if(col_check_point((int)(pos.x-phys_size/2), (int)(pos.y+phys_size/2+5))) - grounded = true; - - vec2 direction = normalize(vec2(input.target_x, input.target_y)); - - vel.y += gravity; - - MACRO_CHECK_VELOCITY - - float max_speed = grounded ? ground_control_speed : air_control_speed; - float accel = grounded ? ground_control_accel : air_control_accel; - float friction = grounded ? ground_friction : air_friction; - - // handle movement - if(input.left) - vel.x = saturated_add(-max_speed, max_speed, vel.x, -accel); - if(input.right) - vel.x = saturated_add(-max_speed, max_speed, vel.x, accel); - - MACRO_CHECK_VELOCITY - - if(!input.left && !input.right) - vel.x *= friction; - - MACRO_CHECK_VELOCITY - - // handle jumping - // 1 bit = to keep track if a jump has been made on this input - // 2 bit = to keep track if a air-jump has been made - if(grounded) - jumped &= ~2; - - if(input.jump) - { - if(!(jumped&1)) - { - if(grounded) - { - triggered_events |= COREEVENT_GROUND_JUMP; - vel.y = -ground_jump_speed; - jumped |= 1; - } - else if(!(jumped&2)) - { - triggered_events |= COREEVENT_AIR_JUMP; - vel.y = -ground_air_speed; - jumped |= 3; - } - } - } - else - jumped &= ~1; - - MACRO_CHECK_VELOCITY - - // do hook - if(input.hook) - { - if(hook_state == HOOK_IDLE) - { - hook_state = HOOK_FLYING; - hook_pos = pos; - hook_dir = direction; - hooked_player = -1; - hook_tick = 0; - triggered_events |= COREEVENT_HOOK_LAUNCH; - } - else if(hook_state == HOOK_FLYING) - { - vec2 new_pos = hook_pos+hook_dir*hook_fire_speed; - - // Check against other players first - for(int i = 0; i < MAX_CLIENTS; i++) - { - player_core *p = world->players[i]; - if(!p || p == this) - continue; - - //if(p != this && !p->dead && distance(p->pos, new_pos) < p->phys_size) - if(distance(p->pos, new_pos) < phys_size) - { - triggered_events |= COREEVENT_HOOK_ATTACH_PLAYER; - hook_state = HOOK_GRABBED; - hooked_player = i; - break; - } - } - /* - for(entity *ent = world.first_entity; ent; ent = ent->next_entity) - { - if(ent && ent->objtype == OBJTYPE_PLAYER) - { - player *p = (player*)ent; - if(p != this && !p->dead && distance(p->pos, new_pos) < p->phys_size) - { - hook_state = HOOK_GRABBED; - hooked_player = p; - break; - } - } - }*/ - - if(hook_state == HOOK_FLYING) - { - // check against ground - if(col_intersect_line(hook_pos, new_pos, &new_pos)) - { - triggered_events |= COREEVENT_HOOK_ATTACH_GROUND; - hook_state = HOOK_GRABBED; - hook_pos = new_pos; - } - else if(distance(pos, new_pos) > hook_length) - { - triggered_events |= COREEVENT_HOOK_RETRACT; - hook_state = HOOK_RETRACTED; - } - else - hook_pos = new_pos; - } - - if(hook_state == HOOK_GRABBED) - { - //create_sound(pos, SOUND_HOOK_ATTACH); - //hook_tick = server_tick(); - } - } - } - else - { - //release_hooked(); - hooked_player = -1; - hook_state = HOOK_IDLE; - hook_pos = pos; - } - - if(hook_state == HOOK_GRABBED) - { - if(hooked_player != -1) - { - player_core *p = world->players[hooked_player]; - if(p) - hook_pos = p->pos; - else - { - // release hook - hooked_player = -1; - hook_state = HOOK_RETRACTED; - hook_pos = pos; - } - - // keep players hooked for a max of 1.5sec - //if(server_tick() > hook_tick+(server_tickspeed()*3)/2) - //release_hooked(); - } - - // Old version feels much better (to me atleast) - if(distance(hook_pos, pos) > 46.0f) - { - vec2 hookvel = normalize(hook_pos-pos)*hook_drag_accel; - // the hook as more power to drag you up then down. - // this makes it easier to get on top of an platform - if(hookvel.y > 0) - hookvel.y *= 0.3f; - - // the hook will boost it's power if the player wants to move - // in that direction. otherwise it will dampen everything abit - if((hookvel.x < 0 && input.left) || (hookvel.x > 0 && input.right)) - hookvel.x *= 0.95f; - else - hookvel.x *= 0.75f; - - vec2 new_vel = vel+hookvel; - - // check if we are under the legal limit for the hook - if(length(new_vel) < hook_drag_speed || length(new_vel) < length(vel)) - vel = new_vel; // no problem. apply - - } - - // release hook - hook_tick++; - if(hooked_player != -1 && hook_tick > SERVER_TICK_SPEED*2) - { - hooked_player = -1; - hook_state = HOOK_RETRACTED; - hook_pos = pos; - } - } - - MACRO_CHECK_VELOCITY - - if(true) - { - for(int i = 0; i < MAX_CLIENTS; i++) - { - player_core *p = world->players[i]; - if(!p) - continue; - - //player *p = (player*)ent; - if(p == this) // || !(p->flags&FLAG_ALIVE) - continue; // make sure that we don't nudge our self - - // handle player <-> player collision - float d = distance(pos, p->pos); - vec2 dir = normalize(pos - p->pos); - if(d < phys_size*1.25f && d > 1.0f) - { - float a = phys_size*1.25f - d; - vel = vel + dir*a; - } - - MACRO_CHECK_VELOCITY - - // handle hook influence - if(hooked_player == i) - { - if(d > phys_size*1.50f) // TODO: fix tweakable variable - { - float accel = hook_drag_accel * (d/hook_length); - vel.x = saturated_add(-hook_drag_speed, hook_drag_speed, vel.x, -accel*dir.x); - vel.y = saturated_add(-hook_drag_speed, hook_drag_speed, vel.y, -accel*dir.y); - - MACRO_CHECK_VELOCITY - } - } - } - } -} - -void player_core::move() -{ - move_box(&pos, &vel, vec2(28.0f, 28.0f), 0); -} - -void player_core::write(obj_player_core *obj_core) -{ - obj_core->x = (int)pos.x; - obj_core->y = (int)pos.y; - obj_core->vx = (int)(vel.x*256.0f); - obj_core->vy = (int)(vel.y*256.0f); - obj_core->hook_state = hook_state; - obj_core->hook_tick = hook_tick; - obj_core->hook_x = (int)hook_pos.x; - obj_core->hook_y = (int)hook_pos.y; - obj_core->hook_dx = (int)(hook_dir.x*256.0f); - obj_core->hook_dy = (int)(hook_dir.y*256.0f); - obj_core->hooked_player = hooked_player; - obj_core->jumped = jumped; - - float a = 0; - if(input.target_x == 0) - a = atan((float)input.target_y); - else - a = atan((float)input.target_y/(float)input.target_x); - - if(input.target_x < 0) - a = a+pi; - - obj_core->angle = (int)(a*256.0f); -} - -void player_core::read(const obj_player_core *obj_core) -{ - pos.x = obj_core->x; - pos.y = obj_core->y; - vel.x = obj_core->vx/256.0f; - vel.y = obj_core->vy/256.0f; - hook_state = obj_core->hook_state; - hook_tick = obj_core->hook_tick; - hook_pos.x = obj_core->hook_x; - hook_pos.y = obj_core->hook_y; - hook_dir.x = obj_core->hook_dx/256.0f; - hook_dir.y = obj_core->hook_dy/256.0f; - hooked_player = obj_core->hooked_player; - jumped = obj_core->jumped; -} - -void player_core::quantize() -{ - obj_player_core c; - write(&c); - read(&c); -} - diff --git a/src/game/game.h b/src/game/game.h deleted file mode 100644 index c30543d9..00000000 --- a/src/game/game.h +++ /dev/null @@ -1,171 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include -#include -#include -#include "../engine/interface.h" -#include "mapres_col.h" - -#include "game_protocol.h" - -inline vec2 get_direction(int angle) -{ - float a = angle/256.0f; - return vec2(cosf(a), sinf(a)); -} - -inline vec2 get_dir(float a) -{ - return vec2(cosf(a), sinf(a)); -} - -inline float get_angle(vec2 dir) -{ - float a = atan(dir.y/dir.x); - if(dir.x < 0) - a = a+pi; - return a; -} - - -inline vec2 calc_pos(vec2 p, vec2 v, float gravity, float t) -{ - vec2 n; - n.x = p.x + v.x*t; - n.y = p.y + v.y*t - gravity*(t*t); - return n; -} - - -template -inline T saturated_add(T min, T max, T current, T modifier) -{ - if(modifier < 0) - { - if(current < min) - return current; - current += modifier; - if(current < min) - current = min; - return current; - } - else - { - if(current > max) - return current; - current += modifier; - if(current > max) - current = max; - return current; - } -} - -void move_point(vec2 *inout_pos, vec2 *inout_vel, float elasticity, int *bounces); -void move_box(vec2 *inout_pos, vec2 *inout_vel, vec2 size, float elasticity); -bool test_box(vec2 pos, vec2 size); - - -// hooking stuff -enum -{ - HOOK_RETRACTED=-1, - HOOK_IDLE=0, - HOOK_FLYING, - HOOK_GRABBED, - - COREEVENT_GROUND_JUMP=0x01, - COREEVENT_AIR_JUMP=0x02, - COREEVENT_HOOK_LAUNCH=0x04, - COREEVENT_HOOK_ATTACH_PLAYER=0x08, - COREEVENT_HOOK_ATTACH_GROUND=0x10, - COREEVENT_HOOK_RETRACT=0x20, -}; - -class world_core -{ -public: - world_core() - { - mem_zero(players, sizeof(players)); - } - - class player_core *players[MAX_CLIENTS]; -}; - -class player_core -{ -public: - world_core *world; - - vec2 pos; - vec2 vel; - - vec2 hook_pos; - vec2 hook_dir; - int hook_tick; - int hook_state; - int hooked_player; - - int jumped; - player_input input; - - int triggered_events; - - void tick(); - void move(); - - void read(const obj_player_core *obj_core); - void write(obj_player_core *obj_core); - void quantize(); -}; - - -#define LERP(a,b,t) (a + (b-a) * t) -#define min(a, b) ( a > b ? b : a) -#define max(a, b) ( a > b ? a : b) - -inline bool col_check_point(float x, float y) { return col_check_point((int)x, (int)y) != 0; } -inline bool col_check_point(vec2 p) { return col_check_point(p.x, p.y); } - -struct mapres_entity -{ - int x, y; - int data[1]; -}; - -struct mapres_spawnpoint -{ - int x, y; -}; - -struct mapres_item -{ - int x, y; - int type; -}; - -struct mapres_flagstand -{ - int x, y; -}; - -enum -{ - MAPRES_ENTS_START=1, - MAPRES_SPAWNPOINT=1, - MAPRES_ITEM=2, - MAPRES_SPAWNPOINT_RED=3, - MAPRES_SPAWNPOINT_BLUE=4, - MAPRES_FLAGSTAND_RED=5, - MAPRES_FLAGSTAND_BLUE=6, - MAPRES_ENTS_END, - - ITEM_NULL=0, - ITEM_WEAPON_GUN=0x00010001, - ITEM_WEAPON_SHOTGUN=0x00010002, - ITEM_WEAPON_ROCKET=0x00010003, - ITEM_WEAPON_SNIPER=0x00010004, - ITEM_WEAPON_HAMMER=0x00010005, - ITEM_HEALTH =0x00020001, - ITEM_ARMOR=0x00030001, - ITEM_NINJA=0x00040001, -}; diff --git a/src/game/game_protocol.h b/src/game/game_protocol.h deleted file mode 100644 index 4fcc1e5f..00000000 --- a/src/game/game_protocol.h +++ /dev/null @@ -1,215 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -// NOTE: Be very careful when editing this file as it will change the network version - -// --------- PHYSICS TWEAK! -------- -const float ground_control_speed = 7.0f; -const float ground_control_accel = 2.0f; -const float ground_friction = 0.5f; -const float ground_jump_speed = 12.6f; -const float ground_air_speed = 11.5f; -const float air_control_speed = 5.0f; -const float air_control_accel = 1.5f; -const float air_friction = 0.95f; -const float hook_length = 34*10.0f; -const float hook_fire_speed = 45.0f; -const float hook_drag_accel = 3.0f; -const float hook_drag_speed = 15.0f; -const float gravity = 0.5f; -const float wall_friction = 0.80f; -const float wall_jump_speed_up = ground_jump_speed*0.8f; -const float wall_jump_speed_out = ground_jump_speed*0.8f; - -// Network stuff -enum -{ - OBJTYPE_NULL=0, - OBJTYPE_GAME, - OBJTYPE_PLAYER_INFO, - OBJTYPE_PLAYER_CHARACTER, // use this if you are searching for the player entity - OBJTYPE_PROJECTILE, - OBJTYPE_POWERUP, - OBJTYPE_FLAG, - EVENT_EXPLOSION, - EVENT_DAMAGEINDICATION, - EVENT_SOUND_WORLD, - EVENT_SMOKE, - EVENT_SPAWN, - EVENT_DEATH, - EVENT_AIR_JUMP, - - EVENT_DUMMY -}; - -enum -{ - MSG_NULL=0, - MSG_SAY, // client -> server - MSG_CHAT, // server -> client - MSG_SETINFO, // server -> client - contains name, skin and color info - MSG_KILLMSG, // server -> client - MSG_SETTEAM, - MSG_JOIN, - MSG_QUIT, - MSG_EMOTICON, - MSG_STARTINFO, // client -> server - MSG_CHANGEINFO, // client -> server - MSG_READY_TO_ENTER, // server -> client - MSG_WEAPON_PICKUP, - MSG_SOUND_GLOBAL, -}; - -enum -{ - EMOTE_NORMAL=0, - EMOTE_PAIN, - EMOTE_HAPPY, - EMOTE_SURPRISE, - EMOTE_ANGRY, - EMOTE_BLINK, -}; - -enum -{ - INPUT_STATE_MASK=0x1f, -}; - -enum -{ - STATE_UNKNOWN=0, - STATE_PLAYING, - STATE_IN_MENU, - STATE_CHATTING, - - GAMETYPE_DM=0, - GAMETYPE_TDM, - GAMETYPE_CTF, -}; - -struct player_input -{ - int left; - int right; - - int target_x; - int target_y; - - int jump; - int fire; - int hook; - int blink; - int state; - - int wanted_weapon; - int next_weapon; - int prev_weapon; -}; - -struct ev_common -{ - int x, y; -}; - -struct ev_explosion : public ev_common -{ -}; - -struct ev_spawn : public ev_common -{ -}; - -struct ev_death : public ev_common -{ -}; - -struct ev_sound : public ev_common -{ - int sound; -}; - -struct ev_damageind : public ev_common -{ - int angle; -}; - -struct obj_game -{ - int round_start_tick; - int game_over; - int sudden_death; - int paused; - - int score_limit; - int time_limit; - int gametype; - - int warmup; - - int teamscore[2]; -}; - -struct obj_projectile -{ - int type; - int tick; - int x, y; - int vx, vy; // should be an angle instead - int start_tick; -}; - -struct obj_powerup -{ - int x, y; - int type; // why do we need two types? - int subtype; -}; - -struct obj_flag -{ - int x, y; - int team; - int carried_by; // is set if the local player has the flag -}; - -// core object needed for physics -struct obj_player_core -{ - int x, y; - int vx, vy; - int angle; - int jumped; - - int hooked_player; - int hook_state; - int hook_tick; - int hook_x, hook_y; - int hook_dx, hook_dy; -}; - -// info about the player that is only needed when it's on screen -struct obj_player_character : public obj_player_core -{ - int state; - - int health; - int armor; - int ammocount; - int weaponstage; - - int weapon; // current active weapon - - int emote; - - int attacktick; // num attack ticks left of current attack -}; - -// information about the player that is always needed -struct obj_player_info -{ - int local; - int clientid; - - int team; - int score; - int latency; - int latency_flux; -}; diff --git a/src/game/game_variables.h b/src/game/game_variables.h deleted file mode 100644 index 44e52c6f..00000000 --- a/src/game/game_variables.h +++ /dev/null @@ -1,64 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -MACRO_CONFIG_INT(key_move_left, 'A', 32, 512) -MACRO_CONFIG_INT(key_move_right, 'D', 32, 512) -MACRO_CONFIG_INT(key_jump, 32, 32, 512) -MACRO_CONFIG_INT(key_fire, 384, 32, 512) -MACRO_CONFIG_INT(key_hook, 385, 32, 512) -MACRO_CONFIG_INT(key_weapon1, '1', 32, 512) -MACRO_CONFIG_INT(key_weapon2, '2', 32, 512) -MACRO_CONFIG_INT(key_weapon3, '3', 32, 512) -MACRO_CONFIG_INT(key_weapon4, '4', 32, 512) -MACRO_CONFIG_INT(key_weapon5, '5', 32, 512) -MACRO_CONFIG_INT(key_weapon6, '6', 32, 512) -MACRO_CONFIG_INT(key_weapon7, '7', 32, 512) - -MACRO_CONFIG_INT(key_next_weapon, 382, 32, 512) -MACRO_CONFIG_INT(key_prev_weapon, 383, 32, 512) - -MACRO_CONFIG_INT(key_emoticon, 'E', 32, 512) - -MACRO_CONFIG_INT(key_chat, 'T', 32, 512) -MACRO_CONFIG_INT(key_teamchat, 'Y', 32, 512) - -MACRO_CONFIG_INT(key_console, 256+2, 32, 512) -MACRO_CONFIG_INT(key_remoteconsole, 256+3, 32, 512) - - -MACRO_CONFIG_INT(dbg_bots, 0, 0, 7) - -MACRO_CONFIG_INT(cl_predict, 1, 0, 1) -MACRO_CONFIG_INT(cl_nameplates, 0, 0, 1) -MACRO_CONFIG_INT(cl_nameplates_always, 0, 0, 1) -MACRO_CONFIG_INT(cl_dynamic_camera, 1, 0, 1) -MACRO_CONFIG_INT(cl_team, -10, -1, 0) -MACRO_CONFIG_INT(cl_autoswitch_weapons, 0, 0, 1) - -MACRO_CONFIG_INT(player_use_custom_color, 0, 0, 1) -MACRO_CONFIG_INT(player_color_body, 65408, 0, 0) -MACRO_CONFIG_INT(player_color_feet, 65408, 0, 0) -MACRO_CONFIG_STR(player_skin, 64, "default") - -MACRO_CONFIG_INT(dbg_new_gui, 0, 0, 1) - -MACRO_CONFIG_INT(ui_page, 1, 0, 5) -MACRO_CONFIG_STR(ui_server_address, 128, "localhost:8303") -MACRO_CONFIG_INT(ui_scale, 100, 1, 100000) - -MACRO_CONFIG_INT(ui_color_hue, 160, 0, 255) -MACRO_CONFIG_INT(ui_color_sat, 70, 0, 255) -MACRO_CONFIG_INT(ui_color_lht, 175, 0, 255) -MACRO_CONFIG_INT(ui_color_alpha, 228, 0, 255) - - -MACRO_CONFIG_INT(sv_warmup, 0, 0, 0) -MACRO_CONFIG_STR(sv_msg, 512, "") -MACRO_CONFIG_INT(sv_teamdamage, 0, 0, 1) -MACRO_CONFIG_STR(sv_maprotation, 512, "") -MACRO_CONFIG_INT(sv_powerups, 1, 0, 1) -MACRO_CONFIG_INT(sv_scorelimit, 20, 0, 1000) -MACRO_CONFIG_INT(sv_timelimit, 0, 0, 1000) -MACRO_CONFIG_STR(sv_gametype, 32, "dm") -MACRO_CONFIG_INT(sv_restart, 0, 0, 120) - - - diff --git a/src/game/generated/createdir.txt b/src/game/generated/createdir.txt new file mode 100644 index 00000000..e69de29b diff --git a/src/game/mapres.h b/src/game/mapres.h deleted file mode 100644 index 77a3ccaa..00000000 --- a/src/game/mapres.h +++ /dev/null @@ -1,14 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -enum -{ - MAPRES_REGISTERED=0x8000, - MAPRES_IMAGE=0x8001, - MAPRES_TILEMAP=0x8002, - MAPRES_COLLISIONMAP=0x8003, - MAPRES_TEMP_THEME=0x8fff, -}; - -struct mapres_theme -{ - int id; -}; diff --git a/src/game/mapres_col.cpp b/src/game/mapres_col.cpp deleted file mode 100644 index 046272ca..00000000 --- a/src/game/mapres_col.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include -#include -#include -#include -#include "../engine/interface.h" -#include "mapres_col.h" -#include "mapres.h" - -/* - Simple collision rutines! -*/ -struct collision -{ - int w, h; - unsigned char *data; -}; - -static collision col; -static int global_dividor; - -int col_init(int dividor) -{ - mapres_collision *c = (mapres_collision*)map_find_item(MAPRES_COLLISIONMAP,0); - if(!c) - { - dbg_msg("mapres_col", "failed!"); - return 0; - } - col.w = c->width; - col.h = c->height; - global_dividor = dividor; - col.data = (unsigned char *)map_get_data(c->data_index); - return col.data ? 1 : 0; -} - -int col_check_point(int x, int y) -{ - int nx = x/global_dividor; - int ny = y/global_dividor; - if(nx < 0 || nx >= col.w || ny >= col.h) - return 1; - - if(y < 0) - return 0; // up == sky == free - - return col.data[ny*col.w+nx]; -} - -// TODO: rewrite this smarter! -bool col_intersect_line(vec2 pos0, vec2 pos1, vec2 *out) -{ - float d = distance(pos0, pos1); - - for(float f = 0; f < d; f++) - { - float a = f/d; - vec2 pos = mix(pos0, pos1, a); - if(col_check_point((int)pos.x, (int)pos.y)) - { - if(out) - *out = pos; - return true; - } - } - if(out) - *out = pos1; - return false; -} diff --git a/src/game/mapres_col.h b/src/game/mapres_col.h deleted file mode 100644 index 9b7191c8..00000000 --- a/src/game/mapres_col.h +++ /dev/null @@ -1,13 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include - -struct mapres_collision -{ - int width; - int height; - int data_index; -}; - -int col_init(int dividor); -int col_check_point(int x, int y); -bool col_intersect_line(vec2 pos0, vec2 pos1, vec2 *out); diff --git a/src/game/math.h b/src/game/math.h deleted file mode 100644 index e2cf6bd8..00000000 --- a/src/game/math.h +++ /dev/null @@ -1,30 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#ifndef BASE_MATH_H -#define BASE_MATH_H - -template -inline T clamp(T val, T min, T max) -{ - if(val < min) - return min; - if(val > max) - return max; - return val; -} - -inline float sign(float f) -{ - return f<0.0f?-1.0f:1.0f; -} - -template -inline T mix(const T a, const T b, TB amount) -{ - return a + (b-a)*amount; -} -const float pi = 3.1415926535897932384626433f; - -template inline T min(T a, T b) { return a inline T max(T a, T b) { return a>b?a:b; } - -#endif // BASE_MATH_H diff --git a/src/game/server/data/createdir.txt b/src/game/server/data/createdir.txt deleted file mode 100644 index 258b42d5..00000000 --- a/src/game/server/data/createdir.txt +++ /dev/null @@ -1 +0,0 @@ -this file is here to make sure that this directory gets created diff --git a/src/game/server/game_server.cpp b/src/game/server/game_server.cpp deleted file mode 100644 index 658e72c9..00000000 --- a/src/game/server/game_server.cpp +++ /dev/null @@ -1,1860 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include -#include -#include -#include -#include "../version.h" -#include "game_server.h" -#include "srv_common.h" -#include "srv_ctf.h" -#include "srv_tdm.h" -#include "srv_dm.h" - -data_container *data = 0x0; - -class player* get_player(int index); -void create_damageind(vec2 p, float angle_mod, int amount); -void create_explosion(vec2 p, int owner, int weapon, bool bnodamage); -void create_smoke(vec2 p); -void create_spawn(vec2 p); -void create_death(vec2 p); -void create_sound(vec2 pos, int sound, int mask=-1); -class player *intersect_player(vec2 pos0, vec2 pos1, vec2 &new_pos, class entity *notthis = 0); - -game_world *world; - -////////////////////////////////////////////////// -// Event handler -////////////////////////////////////////////////// -event_handler::event_handler() -{ - clear(); -} - -void *event_handler::create(int type, int size, int mask) -{ - if(num_events == MAX_EVENTS) - return 0; - if(current_offset+size >= MAX_DATASIZE) - return 0; - - void *p = &data[current_offset]; - offsets[num_events] = current_offset; - types[num_events] = type; - sizes[num_events] = size; - client_masks[num_events] = mask; - current_offset += size; - num_events++; - return p; -} - -void event_handler::clear() -{ - num_events = 0; - current_offset = 0; -} - -void event_handler::snap(int snapping_client) -{ - for(int i = 0; i < num_events; i++) - { - if(cmask_is_set(client_masks[i], snapping_client)) - { - ev_common *ev = (ev_common *)&data[offsets[i]]; - if(distance(players[snapping_client].pos, vec2(ev->x, ev->y)) < 1500.0f) - { - void *d = snap_new_item(types[i], i, sizes[i]); - mem_copy(d, &data[offsets[i]], sizes[i]); - } - } - } -} - -event_handler events; - -////////////////////////////////////////////////// -// Entity -////////////////////////////////////////////////// -entity::entity(int objtype) -{ - this->objtype = objtype; - pos = vec2(0,0); - flags = FLAG_PHYSICS; - proximity_radius = 0; - - id = snap_new_id(); - - next_entity = 0; - prev_entity = 0; - prev_type_entity = 0; - next_type_entity = 0; -} - -entity::~entity() -{ - snap_free_id(id); -} - -////////////////////////////////////////////////// -// game world -////////////////////////////////////////////////// -game_world::game_world() -{ - paused = false; - reset_requested = false; - first_entity = 0x0; - for(int i = 0; i < NUM_ENT_TYPES; i++) - first_entity_types[i] = 0; -} - -int game_world::find_entities(vec2 pos, float radius, entity **ents, int max) -{ - int num = 0; - for(entity *ent = first_entity; ent; ent = ent->next_entity) - { - if(!(ent->flags&entity::FLAG_PHYSICS)) - continue; - - if(distance(ent->pos, pos) < radius+ent->proximity_radius) - { - ents[num] = ent; - num++; - if(num == max) - break; - } - } - - return num; -} - -int game_world::find_entities(vec2 pos, float radius, entity **ents, int max, const int* types, int maxtypes) -{ - int num = 0; - for(int t = 0; t < maxtypes; t++) - { - for(entity *ent = first_entity_types[types[t]]; ent; ent = ent->next_type_entity) - { - if(!(ent->flags&entity::FLAG_PHYSICS)) - continue; - - if(distance(ent->pos, pos) < radius+ent->proximity_radius) - { - ents[num] = ent; - num++; - if(num == max) - break; - } - } - } - - return num; -} - -void game_world::insert_entity(entity *ent) -{ - entity *cur = first_entity; - while(cur) - { - dbg_assert(cur != ent, "err"); - cur = cur->next_entity; - } - - // insert it - if(first_entity) - first_entity->prev_entity = ent; - ent->next_entity = first_entity; - ent->prev_entity = 0x0; - first_entity = ent; - - // into typelist aswell - if(first_entity_types[ent->objtype]) - first_entity_types[ent->objtype]->prev_type_entity = ent; - ent->next_type_entity = first_entity_types[ent->objtype]; - ent->prev_type_entity = 0x0; - first_entity_types[ent->objtype] = ent; -} - -void game_world::destroy_entity(entity *ent) -{ - ent->set_flag(entity::FLAG_DESTROY); -} - -void game_world::remove_entity(entity *ent) -{ - // not in the list - if(!ent->next_entity && !ent->prev_entity && first_entity != ent) - return; - - // remove - if(ent->prev_entity) - ent->prev_entity->next_entity = ent->next_entity; - else - first_entity = ent->next_entity; - if(ent->next_entity) - ent->next_entity->prev_entity = ent->prev_entity; - - if(ent->prev_type_entity) - ent->prev_type_entity->next_type_entity = ent->next_type_entity; - else - first_entity_types[ent->objtype] = ent->next_type_entity; - if(ent->next_type_entity) - ent->next_type_entity->prev_type_entity = ent->prev_type_entity; - - ent->next_entity = 0; - ent->prev_entity = 0; - ent->next_type_entity = 0; - ent->prev_type_entity = 0; -} - -// -void game_world::snap(int snapping_client) -{ - for(entity *ent = first_entity; ent; ent = ent->next_entity) - ent->snap(snapping_client); -} - -void game_world::reset() -{ - // reset all entities - for(entity *ent = first_entity; ent; ent = ent->next_entity) - ent->reset(); - remove_entities(); - - for(entity *ent = first_entity; ent; ent = ent->next_entity) - ent->post_reset(); - remove_entities(); - - reset_requested = false; -} - -void game_world::remove_entities() -{ - // destroy objects marked for destruction - entity *ent = first_entity; - while(ent) - { - entity *next = ent->next_entity; - if(ent->flags&entity::FLAG_DESTROY) - { - remove_entity(ent); - ent->destroy(); - } - ent = next; - } -} - -void game_world::tick() -{ - if(reset_requested) - reset(); - - if(!paused) - { - // update all objects - for(entity *ent = first_entity; ent; ent = ent->next_entity) - ent->tick(); - - for(entity *ent = first_entity; ent; ent = ent->next_entity) - ent->tick_defered(); - } - - remove_entities(); -} - -////////////////////////////////////////////////// -// projectile -////////////////////////////////////////////////// -projectile::projectile(int type, int owner, vec2 pos, vec2 vel, int span, entity* powner, - int damage, int flags, float force, int sound_impact, int weapon) -: entity(OBJTYPE_PROJECTILE) -{ - this->type = type; - this->pos = pos; - this->vel = vel * SERVER_TICK_SPEED; // TODO: remove this - this->lifespan = span; - this->owner = owner; - this->powner = powner; - this->flags = flags; - this->force = force; - this->damage = damage; - this->sound_impact = sound_impact; - this->weapon = weapon; - this->bounce = 0; - this->start_tick = server_tick(); - world->insert_entity(this); -} - -void projectile::reset() -{ - world->destroy_entity(this); -} - -void projectile::tick() -{ - float gravity = -400; - if(type != WEAPON_ROCKET) - gravity = -100; - - float pt = (server_tick()-start_tick-1)/(float)SERVER_TICK_SPEED; - float ct = (server_tick()-start_tick)/(float)SERVER_TICK_SPEED; - vec2 prevpos = calc_pos(pos, vel, gravity, pt); - vec2 curpos = calc_pos(pos, vel, gravity, ct); - - lifespan--; - - int collide = col_check_point((int)curpos.x, (int)curpos.y); - - vec2 new_pos; - entity *targetplayer = (entity*)intersect_player(prevpos, curpos, new_pos, powner); - - if(targetplayer || collide || lifespan < 0 ) - { - if (lifespan >= 0 || weapon == WEAPON_ROCKET) - create_sound(pos, sound_impact); - - if (flags & PROJECTILE_FLAGS_EXPLODE) - create_explosion(prevpos, owner, weapon, false); - else if (targetplayer) - { - targetplayer->take_damage(normalize(vel) * max(0.001f, force), damage, owner, weapon); - } - - world->destroy_entity(this); - } -} - -void projectile::snap(int snapping_client) -{ - float ct = (server_tick()-start_tick)/(float)SERVER_TICK_SPEED; - vec2 curpos = calc_pos(pos, vel, -7.5f*SERVER_TICK_SPEED, ct); - - if(distance(players[snapping_client].pos, curpos) > 1000.0f) - return; - - obj_projectile *proj = (obj_projectile *)snap_new_item(OBJTYPE_PROJECTILE, id, sizeof(obj_projectile)); - proj->x = (int)pos.x; - proj->y = (int)pos.y; - proj->vx = (int)vel.x; - proj->vy = (int)vel.y; - proj->start_tick = start_tick; - proj->type = type; -} - -////////////////////////////////////////////////// -// player -////////////////////////////////////////////////// -// TODO: move to separate file -player::player() -: entity(OBJTYPE_PLAYER_CHARACTER) -{ - init(); -} - -void player::init() -{ - proximity_radius = phys_size; - client_id = -1; - team = -1; // -1 == spectator - extrapowerflags = 0; - - latency_accum = 0; - latency_accum_min = 0; - latency_accum_max = 0; - latency_avg = 0; - latency_min = 0; - latency_max = 0; - - reset(); -} - -void player::reset() -{ - pos = vec2(0.0f, 0.0f); - core.vel = vec2(0.0f, 0.0f); - //direction = vec2(0.0f, 1.0f); - score = 0; - dead = true; - clear_flag(entity::FLAG_PHYSICS); - spawning = false; - die_tick = 0; - damage_taken = 0; - state = STATE_UNKNOWN; - - mem_zero(&input, sizeof(input)); - mem_zero(&previnput, sizeof(previnput)); - num_inputs = 0; - - last_action = -1; - - emote_stop = 0; - damage_taken_tick = 0; - attack_tick = 0; - numobjectshit = 0; - ninjaactivationtick = 0; - currentmovetime = 0; - - active_weapon = WEAPON_GUN; - last_weapon = WEAPON_HAMMER; - wanted_weapon = WEAPON_GUN; -} - -void player::destroy() { } - -void player::set_weapon(int w) -{ - last_weapon = active_weapon; - active_weapon = w; - if(active_weapon < 0 || active_weapon >= NUM_WEAPONS) - active_weapon = 0; -} - -void player::respawn() -{ - spawning = true; -} - - -void player::set_team(int new_team) -{ - team = new_team; - die(client_id, -1); - - dbg_msg("game", "cid=%d team=%d", client_id, team); - - if(team == -1) - clear_flag(FLAG_PHYSICS); - else - set_flag(FLAG_PHYSICS); -} - - -bool try_spawntype(int t, vec2 *outpos) -{ - // get spawn point - int start, num; - map_get_type(t, &start, &num); - if(!num) - return false; - - int id = rand()%num; - mapres_spawnpoint *sp = (mapres_spawnpoint*)map_get_item(start + id, NULL, NULL); - *outpos = vec2((float)sp->x, (float)sp->y); - return true; -} - -void player::try_respawn() -{ - vec2 spawnpos = vec2(100.0f, -60.0f); - - // get spawn point - if(gameobj->gametype == GAMETYPE_CTF) - { - // try first try own team spawn, then normal spawn and then enemy - if(!try_spawntype(MAPRES_SPAWNPOINT_RED+(team&1), &spawnpos)) - { - if(!try_spawntype(MAPRES_SPAWNPOINT, &spawnpos)) - try_spawntype(MAPRES_SPAWNPOINT_RED+((team+1)&1), &spawnpos); - } - } - else - { - if(!try_spawntype(MAPRES_SPAWNPOINT, &spawnpos)) - try_spawntype(MAPRES_SPAWNPOINT_RED+(rand()&1), &spawnpos); - } - - // check if the position is occupado - entity *ents[2] = {0}; - int types[] = {OBJTYPE_PLAYER_CHARACTER}; - int num_ents = world->find_entities(spawnpos, 64, ents, 2, types, 1); - for(int i = 0; i < num_ents; i++) - { - if(ents[i] != this) - return; - } - - spawning = false; - pos = spawnpos; - - core.pos = pos; - core.vel = vec2(0,0); - core.hooked_player = -1; - - health = 10; - armor = 0; - jumped = 0; - dead = false; - set_flag(entity::FLAG_PHYSICS); - state = STATE_PLAYING; - - core.hook_state = HOOK_IDLE; - - mem_zero(&input, sizeof(input)); - - // init weapons - mem_zero(&weapons, sizeof(weapons)); - weapons[WEAPON_HAMMER].got = true; - weapons[WEAPON_HAMMER].ammo = -1; - weapons[WEAPON_GUN].got = true; - weapons[WEAPON_GUN].ammo = data->weapons[WEAPON_GUN].maxammo; - - //weapons[WEAPON_SNIPER].got = true; - //weapons[WEAPON_SNIPER].ammo = data->weapons[WEAPON_SNIPER].maxammo; - active_weapon = WEAPON_GUN; - last_weapon = WEAPON_HAMMER; - wanted_weapon = WEAPON_GUN; - - reload_timer = 0; - - // Create sound and spawn effects - create_sound(pos, SOUND_PLAYER_SPAWN); - create_spawn(pos); - - gameobj->on_player_spawn(this); -} - -bool player::is_grounded() -{ - if(col_check_point((int)(pos.x+phys_size/2), (int)(pos.y+phys_size/2+5))) - return true; - if(col_check_point((int)(pos.x-phys_size/2), (int)(pos.y+phys_size/2+5))) - return true; - return false; -} - -struct input_count -{ - int presses; - int releases; -}; - -static input_count count_input(int prev, int cur) -{ - input_count c = {0,0}; - prev &= INPUT_STATE_MASK; - cur &= INPUT_STATE_MASK; - int i = prev; - while(i != cur) - { - i = (i+1)&INPUT_STATE_MASK; - if(i&1) - c.presses++; - else - c.releases++; - } - - return c; -} - - -int player::handle_ninja() -{ - vec2 direction = normalize(vec2(input.target_x, input.target_y)); - - if ((server_tick() - ninjaactivationtick) > (data->weapons[WEAPON_NINJA].duration * server_tickspeed() / 1000)) - { - // time's up, return - weapons[WEAPON_NINJA].got = false; - active_weapon = last_weapon; - set_weapon(active_weapon); - return 0; - } - - // Check if it should activate - if (count_input(previnput.fire, input.fire).presses && (server_tick() > currentcooldown)) - { - // ok then, activate ninja - attack_tick = server_tick(); - activationdir = direction; - currentmovetime = data->weapons[WEAPON_NINJA].movetime * server_tickspeed() / 1000; - currentcooldown = data->weapons[WEAPON_NINJA].firedelay * server_tickspeed() / 1000 + server_tick(); - - // reset hit objects - numobjectshit = 0; - - create_sound(pos, SOUND_NINJA_FIRE); - - // release all hooks when ninja is activated - //release_hooked(); - //release_hooks(); - } - - currentmovetime--; - - if (currentmovetime == 0) - { - // reset player velocity - core.vel *= 0.2f; - //return MODIFIER_RETURNFLAGS_OVERRIDEWEAPON; - } - - if (currentmovetime > 0) - { - // Set player velocity - core.vel = activationdir * data->weapons[WEAPON_NINJA].velocity; - vec2 oldpos = pos; - move_box(&core.pos, &core.vel, vec2(phys_size, phys_size), 0.0f); - // reset velocity so the client doesn't predict stuff - core.vel = vec2(0.0f,0.0f); - if ((currentmovetime % 2) == 0) - { - create_smoke(pos); - } - - // check if we hit anything along the way - { - int type = OBJTYPE_PLAYER_CHARACTER; - entity *ents[64]; - vec2 dir = pos - oldpos; - float radius = phys_size * 2.0f; //length(dir * 0.5f); - vec2 center = oldpos + dir * 0.5f; - int num = world->find_entities(center, radius, ents, 64, &type, 1); - - for (int i = 0; i < num; i++) - { - // Check if entity is a player - if (ents[i] == this) - continue; - // make sure we haven't hit this object before - bool balreadyhit = false; - for (int j = 0; j < numobjectshit; j++) - { - if (hitobjects[j] == ents[i]) - balreadyhit = true; - } - if (balreadyhit) - continue; - - // check so we are sufficiently close - if (distance(ents[i]->pos, pos) > (phys_size * 2.0f)) - continue; - - // hit a player, give him damage and stuffs... - create_sound(ents[i]->pos, SOUND_NINJA_HIT); - // set his velocity to fast upward (for now) - if(numobjectshit < 10) - hitobjects[numobjectshit++] = ents[i]; - ents[i]->take_damage(vec2(0,10.0f), data->weapons[WEAPON_NINJA].meleedamage, client_id,WEAPON_NINJA); - } - } - return MODIFIER_RETURNFLAGS_OVERRIDEVELOCITY | MODIFIER_RETURNFLAGS_OVERRIDEPOSITION | MODIFIER_RETURNFLAGS_OVERRIDEGRAVITY; - } - - return 0; -} - -int player::handle_sniper() -{ - return 0; - - struct input_count button = count_input(previnput.fire, input.fire); - if (button.releases) - { - vec2 direction = normalize(vec2(input.target_x, input.target_y)); - // Check if we were charging, if so fire - if (weapons[WEAPON_SNIPER].weaponstage >= WEAPONSTAGE_SNIPER_CHARGING) - { - new projectile(projectile::WEAPON_PROJECTILETYPE_SNIPER, - client_id, pos+vec2(0,0), direction*50.0f, - 100 + weapons[WEAPON_SNIPER].weaponstage * 20,this, weapons[WEAPON_SNIPER].weaponstage, 0, 0, -1, WEAPON_SNIPER); - create_sound(pos, SOUND_SNIPER_FIRE); - } - // Add blowback - core.vel = -direction * 10.0f * weapons[WEAPON_SNIPER].weaponstage; - - // update ammo and stuff - weapons[WEAPON_SNIPER].ammo = max(0,weapons[WEAPON_SNIPER].ammo - weapons[WEAPON_SNIPER].weaponstage); - weapons[WEAPON_SNIPER].weaponstage = WEAPONSTAGE_SNIPER_NEUTRAL; - weapons[WEAPON_SNIPER].chargetick = 0; - } - else if (input.fire & 1) - { - // Charge!! (if we are on the ground) - if (is_grounded() && weapons[WEAPON_SNIPER].ammo > 0) - { - if (!weapons[WEAPON_SNIPER].chargetick) - { - weapons[WEAPON_SNIPER].chargetick = server_tick(); - dbg_msg("game", "Chargetick='%d:'", server_tick()); - } - if ((server_tick() - weapons[WEAPON_SNIPER].chargetick) > server_tickspeed() * data->weapons[active_weapon].chargetime) - { - if (weapons[WEAPON_SNIPER].ammo > weapons[WEAPON_SNIPER].weaponstage) - { - weapons[WEAPON_SNIPER].weaponstage++; - weapons[WEAPON_SNIPER].chargetick = server_tick(); - } - else if ((server_tick() - weapons[WEAPON_SNIPER].chargetick) > server_tickspeed() * data->weapons[active_weapon].overchargetime) - { - // Ooopsie, weapon exploded - create_explosion(pos, client_id, WEAPON_SNIPER, false); - create_sound(pos, SOUND_ROCKET_EXPLODE); - // remove this weapon and change weapon to gun - weapons[WEAPON_SNIPER].got = false; - weapons[WEAPON_SNIPER].ammo = 0; - last_weapon = active_weapon; - active_weapon = WEAPON_GUN; - return 0; - } - } - - // While charging, don't move - return MODIFIER_RETURNFLAGS_OVERRIDEVELOCITY|MODIFIER_RETURNFLAGS_NOHOOK; - } - else if (weapons[WEAPON_SNIPER].weaponstage) - weapons[WEAPON_SNIPER].weaponstage = WEAPONSTAGE_SNIPER_NEUTRAL; - } - return 0; -} - -int player::handle_weapons() -{ - vec2 direction = normalize(vec2(input.target_x, input.target_y)); - - if(config.dbg_stress) - { - for(int i = 0; i < NUM_WEAPONS; i++) - { - weapons[i].got = true; - weapons[i].ammo = 10; - } - - if(reload_timer) // twice as fast reload - reload_timer--; - } - - // check reload timer - if(reload_timer) - { - reload_timer--; - return 0; - } - - if (active_weapon == WEAPON_NINJA) - { - // don't update other weapons while ninja is active - return handle_ninja(); - } - - // select weapon - int next = count_input(previnput.next_weapon, input.next_weapon).presses; - int prev = count_input(previnput.prev_weapon, input.prev_weapon).presses; - - if(next < 128) // make sure we only try sane stuff - { - while(next) // next weapon selection - { - wanted_weapon = (wanted_weapon+1)%NUM_WEAPONS; - if(weapons[wanted_weapon].got) - next--; - } - } - - if(prev < 128) // make sure we only try sane stuff - { - while(prev) // prev weapon selection - { - wanted_weapon = (wanted_weapon-1)<0?NUM_WEAPONS-1:wanted_weapon-1; - if(weapons[wanted_weapon].got) - prev--; - } - } - - if(input.wanted_weapon) // direct weapon selection - wanted_weapon = input.wanted_weapon-1; - - if(wanted_weapon < 0 || wanted_weapon >= NUM_WEAPONS) - wanted_weapon = 0; - - // switch weapon if wanted - if(data->weapons[active_weapon].duration <= 0) - { - if(wanted_weapon != -1 && wanted_weapon != active_weapon && wanted_weapon >= 0 && wanted_weapon < NUM_WEAPONS && weapons[wanted_weapon].got) - { - if(active_weapon != wanted_weapon) - create_sound(pos, SOUND_WEAPON_SWITCH); - - set_weapon(wanted_weapon); - } - } - - if (active_weapon == WEAPON_SNIPER) - { - // don't update other weapons while sniper is active - return handle_sniper(); - } - - if(count_input(previnput.fire, input.fire).presses) //previnput.fire != input.fire && (input.fire&1)) - { - if(reload_timer == 0) - { - // fire! - if(weapons[active_weapon].ammo) - { - switch(active_weapon) - { - case WEAPON_HAMMER: - // reset objects hit - numobjectshit = 0; - create_sound(pos, SOUND_HAMMER_FIRE); - break; - - case WEAPON_GUN: - new projectile(WEAPON_GUN, - client_id, - pos+vec2(0,0), - direction*30.0f, - 100, - this, - 1, 0, 0, -1, WEAPON_GUN); - create_sound(pos, SOUND_GUN_FIRE); - break; - case WEAPON_ROCKET: - { - new projectile(WEAPON_ROCKET, - client_id, - pos+vec2(0,0), - direction*15.0f, - 100, - this, - 1, projectile::PROJECTILE_FLAGS_EXPLODE, 0, SOUND_ROCKET_EXPLODE, WEAPON_ROCKET); - create_sound(pos, SOUND_ROCKET_FIRE); - break; - } - case WEAPON_SHOTGUN: - { - int shotspread = 2; - for(int i = -shotspread; i <= shotspread; i++) - { - float spreading[] = {-0.12f, -0.05f, 0, 0.05f, 0.12f}; - float a = get_angle(direction); - float v = 1.0f-fabs(i/(float)shotspread); - a += spreading[i+2]; - new projectile(WEAPON_SHOTGUN, - client_id, - pos+vec2(0,0), - vec2(cosf(a), sinf(a))*(30.0f + 15.0f*v), - //vec2(cosf(a), sinf(a))*20.0f, - (int)(server_tickspeed()*0.3f), - this, - 1, 0, 0, -1, WEAPON_SHOTGUN); - } - create_sound(pos, SOUND_SHOTGUN_FIRE); - break; - } - } - - weapons[active_weapon].ammo--; - attack_tick = server_tick(); - reload_timer = data->weapons[active_weapon].firedelay * server_tickspeed() / 1000; - } - else - { - create_sound(pos, SOUND_WEAPON_NOAMMO); - } - } - } - - // Update weapons - if (active_weapon == WEAPON_HAMMER && reload_timer > 0) - { - // Handle collisions - // only one that needs update (for now) - // do selection for the weapon and bash anything in it - // check if we hit anything along the way - int type = OBJTYPE_PLAYER_CHARACTER; - entity *ents[64]; - vec2 lookdir(direction.x > 0.0f ? 1.0f : -1.0f, 0.0f); - vec2 dir = lookdir * data->weapons[active_weapon].meleereach; - float radius = length(dir * 0.5f); - vec2 center = pos + dir * 0.5f; - int num = world->find_entities(center, radius, ents, 64, &type, 1); - - for (int i = 0; i < num; i++) - { - // Check if entity is a player - if (ents[i] == this) - continue; - // make sure we haven't hit this object before - bool balreadyhit = false; - for (int j = 0; j < numobjectshit; j++) - { - if (hitobjects[j] == ents[i]) - balreadyhit = true; - } - if (balreadyhit) - continue; - - // check so we are sufficiently close - if (distance(ents[i]->pos, pos) > (phys_size * 2.0f)) - continue; - - // hit a player, give him damage and stuffs... - // create sound for bash - //create_sound(ents[i]->pos, sound_impact); - vec2 fdir = normalize(ents[i]->pos- pos); - - // set his velocity to fast upward (for now) - create_smoke(ents[i]->pos); - create_sound(pos, SOUND_HAMMER_HIT); - if(numobjectshit < 10) - hitobjects[numobjectshit++] = ents[i]; - ents[i]->take_damage(vec2(0,-1.0f), data->weapons[active_weapon].meleedamage, client_id, active_weapon); - player* target = (player*)ents[i]; - vec2 dir; - if (length(target->pos - pos) > 0.0f) - dir = normalize(target->pos - pos); - else - dir = vec2(0,-1); - - target->core.vel += normalize(dir + vec2(0,-1.1f)) * 10.0f; - } - } - - if (data->weapons[active_weapon].ammoregentime) - { - // If equipped and not active, regen ammo? - if (reload_timer <= 0) - { - if (weapons[active_weapon].ammoregenstart < 0) - weapons[active_weapon].ammoregenstart = server_tick(); - - if ((server_tick() - weapons[active_weapon].ammoregenstart) >= data->weapons[active_weapon].ammoregentime * server_tickspeed() / 1000) - { - // Add some ammo - weapons[active_weapon].ammo = min(weapons[active_weapon].ammo + 1, data->weapons[active_weapon].maxammo); - weapons[active_weapon].ammoregenstart = -1; - } - } - else - { - weapons[active_weapon].ammoregenstart = -1; - } - } - - return 0; -} - -void player::tick() -{ - // check if we have enough input - // this is to prevent initial weird clicks - if(num_inputs < 2) - previnput = input; - - // do latency stuff - { - CLIENT_INFO info; - if(server_getclientinfo(client_id, &info)) - { - latency_accum += info.latency; - latency_accum_max = max(latency_accum_max, info.latency); - latency_accum_min = min(latency_accum_min, info.latency); - } - - if(server_tick()%server_tickspeed() == 0) - { - latency_avg = latency_accum/server_tickspeed(); - latency_max = latency_accum_max; - latency_min = latency_accum_min; - latency_accum = 0; - latency_accum_min = 1000; - latency_accum_max = 0; - } - } - - // enable / disable physics - if(team == -1 || dead) - world->core.players[client_id] = 0; - else - world->core.players[client_id] = &core; - - // spectator - if(team == -1) - return; - - if(spawning) - try_respawn(); - - // TODO: rework the input to be more robust - if(dead) - { - if(server_tick()-die_tick >= server_tickspeed()*5) // auto respawn after 3 sec - respawn(); - if((input.fire&1) && server_tick()-die_tick >= server_tickspeed()/2) // auto respawn after 0.5 sec - respawn(); - return; - } - - //player_core core; - //core.pos = pos; - //core.jumped = jumped; - core.input = input; - core.tick(); - - // handle weapons - handle_weapons(); - - state = input.state; - - // Previnput - previnput = input; - return; -} - -void player::tick_defered() -{ - if(!dead) - { - vec2 start_pos = core.pos; - vec2 start_vel = core.vel; - bool stuck_before = test_box(core.pos, vec2(28.0f, 28.0f)); - core.move(); - bool stuck_after_move = test_box(core.pos, vec2(28.0f, 28.0f)); - core.quantize(); - bool stuck_after_quant = test_box(core.pos, vec2(28.0f, 28.0f)); - pos = core.pos; - - if(!stuck_before && (stuck_after_move || stuck_after_quant)) - { - dbg_msg("player", "STUCK!!! %d %d %d %f %f %f %f %x %x %x %x", - stuck_before, - stuck_after_move, - stuck_after_quant, - start_pos.x, start_pos.y, - start_vel.x, start_vel.y, - *((unsigned *)&start_pos.x), *((unsigned *)&start_pos.y), - *((unsigned *)&start_vel.x), *((unsigned *)&start_vel.y)); - } - - - int events = core.triggered_events; - int mask = cmask_all_except_one(client_id); - - if(events&COREEVENT_GROUND_JUMP) create_sound(pos, SOUND_PLAYER_JUMP, mask); - if(events&COREEVENT_AIR_JUMP) - { - create_sound(pos, SOUND_PLAYER_JUMP, mask); - ev_common *c = (ev_common *)::events.create(EVENT_AIR_JUMP, sizeof(ev_common), mask); - if(c) - { - c->x = (int)pos.x; - c->y = (int)pos.y; - } - } - - //if(events&COREEVENT_HOOK_LAUNCH) snd_play_random(CHN_WORLD, SOUND_HOOK_LOOP, 1.0f, pos); - if(events&COREEVENT_HOOK_ATTACH_PLAYER) create_sound(pos, SOUND_HOOK_ATTACH_PLAYER, mask); - if(events&COREEVENT_HOOK_ATTACH_GROUND) create_sound(pos, SOUND_HOOK_ATTACH_GROUND, mask); - //if(events&COREEVENT_HOOK_RETRACT) snd_play_random(CHN_WORLD, SOUND_PLAYER_JUMP, 1.0f, pos); - - } - - if(team == -1) - { - pos.x = input.target_x; - pos.y = input.target_y; - } -} - -void player::die(int killer, int weapon) -{ - int mode_special = gameobj->on_player_death(this, get_player(killer), weapon); - - dbg_msg("game", "kill killer='%d:%s' victim='%d:%s' weapon=%d special=%d", - killer, server_clientname(killer), - client_id, server_clientname(client_id), weapon, mode_special); - - // send the kill message - msg_pack_start(MSG_KILLMSG, MSGFLAG_VITAL); - msg_pack_int(killer); - msg_pack_int(client_id); - msg_pack_int(weapon); - msg_pack_int(mode_special); - msg_pack_end(); - server_send_msg(-1); - - // a nice sound - create_sound(pos, SOUND_PLAYER_DIE); - - // set dead state - dead = true; - die_tick = server_tick(); - clear_flag(FLAG_PHYSICS); - create_death(pos); -} - -bool player::take_damage(vec2 force, int dmg, int from, int weapon) -{ - core.vel += force; - - if(gameobj->is_friendly_fire(client_id, from) && !config.sv_teamdamage) - return false; - - // player only inflicts half damage on self - if(from == client_id) - dmg = max(1, dmg/2); - - // CTF and TDM (TODO: check for FF) - //if (gameobj->gametype != GAMETYPE_DM && from >= 0 && players[from].team == team) - //return false; - - damage_taken++; - - // create healthmod indicator - if(server_tick() < damage_taken_tick+25) - { - // make sure that the damage indicators doesn't group together - create_damageind(pos, damage_taken*0.25f, dmg); - } - else - { - damage_taken = 0; - create_damageind(pos, 0, dmg); - } - - if(armor) - { - armor -= 1; - dmg--; - } - - if(dmg > armor) - { - dmg -= armor; - armor = 0; - health -= dmg; - } - else - armor -= dmg; - - damage_taken_tick = server_tick(); - - // do damage hit sound - if(from >= 0) - create_sound(get_player(from)->pos, SOUND_HIT, cmask_one(from)); - - // check for death - if(health <= 0) - { - die(from, weapon); - - // set attacker's face to happy (taunt!) - if (from >= 0 && from != client_id) - { - player *p = get_player(from); - - p->emote_type = EMOTE_HAPPY; - p->emote_stop = server_tick() + server_tickspeed(); - } - - return false; - } - - if (dmg > 2) - create_sound(pos, SOUND_PLAYER_PAIN_LONG); - else - create_sound(pos, SOUND_PLAYER_PAIN_SHORT); - - emote_type = EMOTE_PAIN; - emote_stop = server_tick() + 500 * server_tickspeed() / 1000; - - // spawn blood? - return true; -} - -void player::snap(int snaping_client) -{ - if(1) - { - obj_player_info *info = (obj_player_info *)snap_new_item(OBJTYPE_PLAYER_INFO, client_id, sizeof(obj_player_info)); - - info->latency = latency_avg; - info->latency_flux = latency_max-latency_min; - info->local = 0; - info->clientid = client_id; - info->score = score; - info->team = team; - - if(client_id == snaping_client) - info->local = 1; - } - - if(health > 0 && distance(players[snaping_client].pos, pos) < 1000.0f) - { - obj_player_character *character = (obj_player_character *)snap_new_item(OBJTYPE_PLAYER_CHARACTER, client_id, sizeof(obj_player_character)); - - core.write(character); - - // this is to make sure that players that are just standing still - // isn't sent. this is because the physics keep bouncing between - // 0-128 when just standing. - // TODO: fix the physics so this isn't needed - if(snaping_client != client_id && abs(character->vy) < 256.0f) - character->vy = 0; - - if (emote_stop < server_tick()) - { - emote_type = EMOTE_NORMAL; - emote_stop = -1; - } - - character->emote = emote_type; - - character->ammocount = weapons[active_weapon].ammo; - character->health = 0; - character->armor = 0; - character->weapon = active_weapon; - character->weaponstage = weapons[active_weapon].weaponstage; - character->attacktick = attack_tick; - - if(client_id == snaping_client) - { - character->health = health; - character->armor = armor; - } - - if(dead) - character->health = -1; - - //if(length(vel) > 15.0f) - // player->emote = EMOTE_HAPPY; - - //if(damage_taken_tick+50 > server_tick()) - // player->emote = EMOTE_PAIN; - - if (character->emote == EMOTE_NORMAL) - { - if(250 - ((server_tick() - last_action)%(250)) < 5) - character->emote = EMOTE_BLINK; - } - - character->state = state; - } -} - -player *players; - -////////////////////////////////////////////////// -// powerup -////////////////////////////////////////////////// -powerup::powerup(int _type, int _subtype) -: entity(OBJTYPE_POWERUP) -{ - type = _type; - subtype = _subtype; - proximity_radius = phys_size; - - reset(); - - // TODO: should this be done here? - world->insert_entity(this); -} - -void powerup::reset() -{ - if (data->powerupinfo[type].startspawntime > 0) - spawntick = server_tick() + server_tickspeed() * data->powerupinfo[type].startspawntime; - else - spawntick = -1; -} - - -void send_weapon_pickup(int cid, int weapon); - -void powerup::tick() -{ - // wait for respawn - if(spawntick > 0) - { - if(server_tick() > spawntick) - { - // respawn - spawntick = -1; - - if(type == POWERUP_WEAPON) - create_sound(pos, SOUND_WEAPON_SPAWN); - } - else - return; - } - // Check if a player intersected us - vec2 meh; - player* pplayer = intersect_player(pos, pos + vec2(0,16), meh, 0); - if (pplayer) - { - // player picked us up, is someone was hooking us, let them go - int respawntime = -1; - switch (type) - { - case POWERUP_HEALTH: - if(pplayer->health < 10) - { - create_sound(pos, SOUND_PICKUP_HEALTH); - pplayer->health = min(10, pplayer->health + data->powerupinfo[type].amount); - respawntime = data->powerupinfo[type].respawntime; - } - break; - case POWERUP_ARMOR: - if(pplayer->armor < 10) - { - create_sound(pos, SOUND_PICKUP_ARMOR); - pplayer->armor = min(10, pplayer->armor + data->powerupinfo[type].amount); - respawntime = data->powerupinfo[type].respawntime; - } - break; - - case POWERUP_WEAPON: - if(subtype >= 0 && subtype < NUM_WEAPONS) - { - if(pplayer->weapons[subtype].ammo < 10 || !pplayer->weapons[subtype].got) - { - pplayer->weapons[subtype].got = true; - pplayer->weapons[subtype].ammo = min(10, pplayer->weapons[subtype].ammo + data->powerupinfo[type].amount); - respawntime = data->powerupinfo[type].respawntime; - - // TODO: data compiler should take care of stuff like this - if(subtype == WEAPON_ROCKET) - create_sound(pos, SOUND_PICKUP_ROCKET); - else if(subtype == WEAPON_SHOTGUN) - create_sound(pos, SOUND_PICKUP_SHOTGUN); - - send_weapon_pickup(pplayer->client_id, subtype); - } - } - break; - case POWERUP_NINJA: - { - // activate ninja on target player - pplayer->ninjaactivationtick = server_tick(); - pplayer->weapons[WEAPON_NINJA].got = true; - pplayer->last_weapon = pplayer->active_weapon; - pplayer->active_weapon = WEAPON_NINJA; - respawntime = data->powerupinfo[type].respawntime; - create_sound(pos, SOUND_PICKUP_NINJA); - - // loop through all players, setting their emotes - entity *ents[64]; - const int types[] = {OBJTYPE_PLAYER_CHARACTER}; - int num = world->find_entities(vec2(0, 0), 1000000, ents, 64, types, 1); - for (int i = 0; i < num; i++) - { - player *p = (player *)ents[i]; - if (p != pplayer) - { - p->emote_type = EMOTE_SURPRISE; - p->emote_stop = server_tick() + server_tickspeed(); - } - } - - pplayer->emote_type = EMOTE_ANGRY; - pplayer->emote_stop = server_tick() + 1200 * server_tickspeed() / 1000; - - break; - } - default: - break; - }; - - if(respawntime >= 0) - { - dbg_msg("game", "pickup player='%d:%s' item=%d/%d", - pplayer->client_id, server_clientname(pplayer->client_id), type, subtype); - spawntick = server_tick() + server_tickspeed() * respawntime; - } - } -} - -void powerup::snap(int snapping_client) -{ - if(spawntick != -1) - return; - - obj_powerup *up = (obj_powerup *)snap_new_item(OBJTYPE_POWERUP, id, sizeof(obj_powerup)); - up->x = (int)pos.x; - up->y = (int)pos.y; - up->type = type; // TODO: two diffrent types? what gives? - up->subtype = subtype; -} - -// POWERUP END /////////////////////// - -player *get_player(int index) -{ - return &players[index]; -} - -void create_damageind(vec2 p, float angle, int amount) -{ - float a = 3 * 3.14159f / 2 + angle; - //float a = get_angle(dir); - float s = a-pi/3; - float e = a+pi/3; - for(int i = 0; i < amount; i++) - { - float f = mix(s, e, float(i+1)/float(amount+2)); - ev_damageind *ev = (ev_damageind *)events.create(EVENT_DAMAGEINDICATION, sizeof(ev_damageind)); - if(ev) - { - ev->x = (int)p.x; - ev->y = (int)p.y; - ev->angle = (int)(f*256.0f); - } - } -} - -void create_explosion(vec2 p, int owner, int weapon, bool bnodamage) -{ - // create the event - ev_explosion *ev = (ev_explosion *)events.create(EVENT_EXPLOSION, sizeof(ev_explosion)); - if(ev) - { - ev->x = (int)p.x; - ev->y = (int)p.y; - } - - if (!bnodamage) - { - // deal damage - entity *ents[64]; - const float radius = 128.0f; - const float innerradius = 42.0f; - int num = world->find_entities(p, radius, ents, 64); - for(int i = 0; i < num; i++) - { - vec2 diff = ents[i]->pos - p; - vec2 forcedir(0,1); - float l = length(diff); - if(l) - forcedir = normalize(diff); - l = 1-clamp((l-innerradius)/(radius-innerradius), 0.0f, 1.0f); - float dmg = 6 * l; - if((int)dmg) - ents[i]->take_damage(forcedir*dmg*2, (int)dmg, owner, weapon); - } - } -} - -void create_smoke(vec2 p) -{ - // create the event - ev_explosion *ev = (ev_explosion *)events.create(EVENT_SMOKE, sizeof(ev_explosion)); - if(ev) - { - ev->x = (int)p.x; - ev->y = (int)p.y; - } -} - -void create_spawn(vec2 p) -{ - // create the event - ev_spawn *ev = (ev_spawn *)events.create(EVENT_SPAWN, sizeof(ev_spawn)); - if(ev) - { - ev->x = (int)p.x; - ev->y = (int)p.y; - } -} - -void create_death(vec2 p) -{ - // create the event - ev_death *ev = (ev_death *)events.create(EVENT_DEATH, sizeof(ev_death)); - if(ev) - { - ev->x = (int)p.x; - ev->y = (int)p.y; - } -} - -void create_sound(vec2 pos, int sound, int mask) -{ - if (sound < 0) - return; - - // create a sound - ev_sound *ev = (ev_sound *)events.create(EVENT_SOUND_WORLD, sizeof(ev_sound), mask); - if(ev) - { - ev->x = (int)pos.x; - ev->y = (int)pos.y; - ev->sound = sound; - } -} - -void create_sound_global(int sound, int target) -{ - if (sound < 0) - return; - - msg_pack_start(MSG_SOUND_GLOBAL, MSGFLAG_VITAL); - msg_pack_int(sound); - server_send_msg(-1); -} - -// TODO: should be more general -player* intersect_player(vec2 pos0, vec2 pos1, vec2& new_pos, entity* notthis) -{ - // Find other players - entity *ents[64]; - vec2 dir = pos1 - pos0; - float radius = length(dir * 0.5f); - vec2 center = pos0 + dir * 0.5f; - const int types[] = {OBJTYPE_PLAYER_CHARACTER}; - int num = world->find_entities(center, radius, ents, 64, types, 1); - for (int i = 0; i < num; i++) - { - // Check if entity is a player - if (ents[i] != notthis) - { - new_pos = ents[i]->pos; - return (player*)ents[i]; - } - } - - return 0; -} - - -void send_chat(int cid, int team, const char *msg) -{ - if(cid >= 0 && cid < MAX_CLIENTS) - dbg_msg("chat", "%d:%d:%s: %s", cid, team, server_clientname(cid), msg); - else - dbg_msg("chat", "*** %s", msg); - - if(team == -1) - { - msg_pack_start(MSG_CHAT, MSGFLAG_VITAL); - msg_pack_int(cid); - msg_pack_int(0); - msg_pack_string(msg, 512); - msg_pack_end(); - server_send_msg(-1); - } - else - { - msg_pack_start(MSG_CHAT, MSGFLAG_VITAL); - msg_pack_int(cid); - msg_pack_int(1); - msg_pack_string(msg, 512); - msg_pack_end(); - - for(int i = 0; i < MAX_CLIENTS; i++) - { - if(players[i].client_id != -1 && players[i].team == team) - server_send_msg(i); - } - } -} - - -// Server hooks -void mods_tick() -{ - // clear all events - events.clear(); - world->tick(); - - if(world->paused) // make sure that the game object always updates - gameobj->tick(); - - if(config.sv_restart) - { - if(config.sv_restart > 1) - gameobj->do_warmup(config.sv_restart); - else - gameobj->startround(); - - config.sv_restart = 0; - } - - if(config.sv_msg[0] != 0) - { - send_chat(-1, 0, config.sv_msg); - config.sv_msg[0] = 0; - } -} - -void mods_snap(int client_id) -{ - world->snap(client_id); - events.snap(client_id); -} - -void mods_client_input(int client_id, void *input) -{ - if(!world->paused) - { - if (memcmp(&players[client_id].input, input, sizeof(player_input)) != 0) - players[client_id].last_action = server_tick(); - - //players[client_id].previnput = players[client_id].input; - players[client_id].input = *(player_input*)input; - players[client_id].num_inputs++; - } -} - -void send_info(int who, int to_who) -{ - msg_pack_start(MSG_SETINFO, MSGFLAG_VITAL); - msg_pack_int(who); - msg_pack_string(server_clientname(who), 64); - msg_pack_string(players[who].skin_name, 64); - msg_pack_int(players[who].use_custom_color); - msg_pack_int(players[who].color_body); - msg_pack_int(players[who].color_feet); - msg_pack_end(); - server_send_msg(to_who); -} - -void send_emoticon(int cid, int emoticon) -{ - msg_pack_start(MSG_EMOTICON, MSGFLAG_VITAL); - msg_pack_int(cid); - msg_pack_int(emoticon % 16); - msg_pack_end(); - server_send_msg(-1); -} - -void send_weapon_pickup(int cid, int weapon) -{ - msg_pack_start(MSG_WEAPON_PICKUP, MSGFLAG_VITAL); - msg_pack_int(weapon); - msg_pack_end(); - server_send_msg(cid); -} - -void mods_client_enter(int client_id) -{ - world->insert_entity(&players[client_id]); - players[client_id].respawn(); - dbg_msg("game", "join player='%d:%s'", client_id, server_clientname(client_id)); - - char buf[512]; - sprintf(buf, "%s has joined the game", server_clientname(client_id)); - send_chat(-1, -1, buf); -} - -void mods_connected(int client_id) -{ - players[client_id].init(); - players[client_id].client_id = client_id; - - //dbg_msg("game", "connected player='%d:%s'", client_id, server_clientname(client_id)); - - // Check which team the player should be on - if(gameobj->gametype == GAMETYPE_DM) - players[client_id].team = 0; - else - players[client_id].team = gameobj->getteam(client_id); -} - -void mods_client_drop(int client_id) -{ - char buf[512]; - sprintf(buf, "%s has left the game", server_clientname(client_id)); - send_chat(-1, -1, buf); - - dbg_msg("game", "leave player='%d:%s'", client_id, server_clientname(client_id)); - - gameobj->on_player_death(&players[client_id], 0, -1); - world->remove_entity(&players[client_id]); - world->core.players[client_id] = 0x0; - players[client_id].client_id = -1; -} - -void mods_message(int msg, int client_id) -{ - if(msg == MSG_SAY) - { - int team = msg_unpack_int(); - const char *text = msg_unpack_string(); - if(team) - team = players[client_id].team; - else - team = -1; - send_chat(client_id, team, text); - } - else if (msg == MSG_SETTEAM) - { - // Switch team on given client and kill/respawn him - players[client_id].set_team(msg_unpack_int()); - gameobj->on_player_info_change(&players[client_id]); - - // send all info to this client - for(int i = 0; i < MAX_CLIENTS; i++) - { - if(players[i].client_id != -1) - send_info(i, -1); - } - } - else if (msg == MSG_CHANGEINFO || msg == MSG_STARTINFO) - { - const char *name = msg_unpack_string(); - const char *skin_name = msg_unpack_string(); - players[client_id].use_custom_color = msg_unpack_int(); - players[client_id].color_body = msg_unpack_int(); - players[client_id].color_feet = msg_unpack_int(); - - // check for invalid chars - const char *p = name; - while (*p) - { - if(*p < 32) - return; - p++; - } - - // - if(msg == MSG_CHANGEINFO && strcmp(name, server_clientname(client_id)) != 0) - { - char msg[256]; - sprintf(msg, "*** %s changed name to %s", server_clientname(client_id), name); - send_chat(-1, -1, msg); - } - - //send_set_name(client_id, players[client_id].name, name); - strncpy(players[client_id].skin_name, skin_name, 64); - server_setclientname(client_id, name); - - gameobj->on_player_info_change(&players[client_id]); - - if(msg == MSG_STARTINFO) - { - // send all info to this client - for(int i = 0; i < MAX_CLIENTS; i++) - { - if(players[i].client_id != -1) - send_info(i, client_id); - } - - msg_pack_start(MSG_READY_TO_ENTER, MSGFLAG_VITAL); - msg_pack_end(); - server_send_msg(client_id); - } - - send_info(client_id, -1); - } - else if (msg == MSG_EMOTICON) - { - int emoteicon = msg_unpack_int(); - send_emoticon(client_id, emoteicon % 16); - } -} - -extern unsigned char internal_data[]; - -void mods_init() -{ - if(!data) /* only load once */ - data = load_data_from_memory(internal_data); - - col_init(32); - - world = new game_world; - players = new player[MAX_CLIENTS]; - - // select gametype - if(strcmp(config.sv_gametype, "ctf") == 0) - gameobj = new gameobject_ctf; - else if(strcmp(config.sv_gametype, "tdm") == 0) - gameobj = new gameobject_tdm; - else - gameobj = new gameobject_dm; - - // setup core world - for(int i = 0; i < MAX_CLIENTS; i++) - players[i].core.world = &world->core; - - // - int start, num; - map_get_type(MAPRES_ITEM, &start, &num); - - // TODO: this is way more complicated then it should be - for(int i = 0; i < num; i++) - { - mapres_item *it = (mapres_item *)map_get_item(start+i, 0, 0); - - int type = -1; - int subtype = 0; - - switch(it->type) - { - case ITEM_WEAPON_GUN: - type = POWERUP_WEAPON; - subtype = WEAPON_GUN; - break; - case ITEM_WEAPON_SHOTGUN: - type = POWERUP_WEAPON; - subtype = WEAPON_SHOTGUN; - break; - case ITEM_WEAPON_ROCKET: - type = POWERUP_WEAPON; - subtype = WEAPON_ROCKET; - break; - case ITEM_WEAPON_HAMMER: - type = POWERUP_WEAPON; - subtype = WEAPON_HAMMER; - break; - - case ITEM_HEALTH: - type = POWERUP_HEALTH; - break; - - case ITEM_ARMOR: - type = POWERUP_ARMOR; - break; - - case ITEM_NINJA: - if(config.sv_powerups) - { - type = POWERUP_NINJA; - subtype = WEAPON_NINJA; - } - break; - }; - - if(type != -1) - { - // LOL, the only new in the entire game code - // perhaps we can get rid of it. seems like a stupid thing to have - powerup *ppower = new powerup(type, subtype); - ppower->pos = vec2(it->x, it->y); - } - } - - if(gameobj->gametype == GAMETYPE_CTF) - { - } - - world->insert_entity(gameobj); - - - if(config.dbg_bots) - { - - for(int i = 0; i < config.dbg_bots ; i++) - { - mods_connected(MAX_CLIENTS-i-1); - mods_client_enter(MAX_CLIENTS-i-1); - if(gameobj->gametype != GAMETYPE_DM) - players[MAX_CLIENTS-i-1].team = i&1; - } - } -} - -void mods_shutdown() -{ - delete [] players; - delete gameobj; - delete world; - gameobj = 0; - players = 0; - world = 0; -} - -void mods_presnap() {} -void mods_postsnap() {} - -extern "C" const char *mods_net_version() { return TEEWARS_NETVERSION; } diff --git a/src/game/server/game_server.h b/src/game/server/game_server.h deleted file mode 100644 index 65ef58f4..00000000 --- a/src/game/server/game_server.h +++ /dev/null @@ -1,2 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - diff --git a/src/game/server/gs_common.cpp b/src/game/server/gs_common.cpp new file mode 100644 index 00000000..dc0d5c05 --- /dev/null +++ b/src/game/server/gs_common.cpp @@ -0,0 +1,264 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include +#include "gs_common.h" +#include + +gameobject::gameobject() +: entity(OBJTYPE_GAME) +{ + // select gametype + if(strcmp(config.sv_gametype, "ctf") == 0) + { + gametype = GAMETYPE_CTF; + dbg_msg("game", "-- Capture The Flag --"); + } + else if(strcmp(config.sv_gametype, "tdm") == 0) + { + gametype = GAMETYPE_TDM; + dbg_msg("game", "-- Team Death Match --"); + } + else + { + gametype = GAMETYPE_DM; + dbg_msg("game", "-- Death Match --"); + } + + // + do_warmup(config.sv_warmup); + game_over_tick = -1; + sudden_death = 0; + round_start_tick = server_tick(); + round_count = 0; + is_teamplay = false; + teamscore[0] = 0; + teamscore[1] = 0; +} + +void gameobject::endround() +{ + if(warmup) // game can't end when we are running warmup + return; + + world->paused = true; + game_over_tick = server_tick(); + sudden_death = 0; +} + +void gameobject::resetgame() +{ + world->reset_requested = true; +} + +static bool is_separator(char c) +{ + return c == ';' || c == ' ' || c == ',' || c == '\t'; +} + +void gameobject::startround() +{ + resetgame(); + + round_start_tick = server_tick(); + sudden_death = 0; + game_over_tick = -1; + world->paused = false; + teamscore[0] = 0; + teamscore[1] = 0; + round_count++; +} + +void gameobject::cyclemap() +{ + if(!strlen(config.sv_maprotation)) + return; + // handle maprotation + char buf[512]; + const char *s = strstr(config.sv_maprotation, config.sv_map); + if(s == 0) + s = config.sv_maprotation; // restart rotation + else + { + s += strlen(config.sv_map); // skip this map + while(is_separator(s[0])) + s++; + if(s[0] == 0) + s = config.sv_maprotation; // restart rotation + } + + int i = 0; + for(; i < 512; i++) + { + buf[i] = s[i]; + if(is_separator(s[i]) || s[i] == 0) + { + buf[i] = 0; + break; + } + } + + i = 0; // skip spaces + while(is_separator(buf[i])) + i++; + + dbg_msg("game", "rotating map to %s", &buf[i]); + strcpy(config.sv_map, &buf[i]); +} + +void gameobject::post_reset() +{ + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(players[i].client_id != -1) + players[i].respawn(); + } +} + +void gameobject::on_player_info_change(class player *p) +{ + const int team_colors[2] = {65387, 10223467}; + if(is_teamplay) + { + if(p->team >= 0 || p->team <= 1) + { + p->use_custom_color = 1; + p->color_body = team_colors[p->team]; + p->color_feet = team_colors[p->team]; + } + } +} + + +int gameobject::on_player_death(class player *victim, class player *killer, int weapon) +{ + // do scoreing + if(!killer) + return 0; + if(killer == victim) + victim->score--; // suicide + else + { + if(is_teamplay && victim->team == killer->team) + killer->score--; // teamkill + else + killer->score++; // normal kill + } + return 0; +} + +void gameobject::do_warmup(int seconds) +{ + warmup = seconds*SERVER_TICK_SPEED; +} + +bool gameobject::is_friendly_fire(int cid1, int cid2) +{ + if(cid1 == cid2) + return false; + + if(is_teamplay) + { + if(players[cid1].team == players[cid2].team) + return true; + } + + return false; +} + +void gameobject::tick() +{ + // do warmup + if(warmup) + { + warmup--; + if(!warmup) + startround(); + } + + if(game_over_tick != -1) + { + // game over.. wait for restart + if(server_tick() > game_over_tick+server_tickspeed()*10) + { + cyclemap(); + startround(); + } + } + + + // update browse info + int prog = -1; + if(config.sv_timelimit > 0) + prog = max(prog, (server_tick()-round_start_tick) * 100 / (config.sv_timelimit*server_tickspeed()*60)); + + if(config.sv_scorelimit) + { + if(is_teamplay) + { + prog = max(prog, (teamscore[0]*100)/config.sv_scorelimit); + prog = max(prog, (teamscore[1]*100)/config.sv_scorelimit); + } + else + { + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(players[i].client_id != -1) + prog = max(prog, (players[i].score*100)/config.sv_scorelimit); + } + } + } + + if(warmup) + prog = -1; + + server_setbrowseinfo(gametype, prog); +} + +void gameobject::snap(int snapping_client) +{ + obj_game *game = (obj_game *)snap_new_item(OBJTYPE_GAME, 0, sizeof(obj_game)); + game->paused = world->paused; + game->game_over = game_over_tick==-1?0:1; + game->sudden_death = sudden_death; + + game->score_limit = config.sv_scorelimit; + game->time_limit = config.sv_timelimit; + game->round_start_tick = round_start_tick; + game->gametype = gametype; + + game->warmup = warmup; + + game->teamscore[0] = teamscore[0]; + game->teamscore[1] = teamscore[1]; +} + +int gameobject::getteam(int notthisid) +{ + int numplayers[2] = {0,0}; + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(players[i].client_id != -1 && players[i].client_id != notthisid) + { + numplayers[players[i].team]++; + } + } + + return numplayers[0] > numplayers[1] ? 1 : 0; +} + +void gameobject::do_team_wincheck() +{ + if(game_over_tick == -1 && !warmup) + { + // check score win condition + if((config.sv_scorelimit > 0 && (teamscore[0] >= config.sv_scorelimit || teamscore[1] >= config.sv_scorelimit)) || + (config.sv_timelimit > 0 && (server_tick()-round_start_tick) >= config.sv_timelimit*server_tickspeed()*60)) + { + if(teamscore[0] != teamscore[1]) + endround(); + else + sudden_death = 1; + } + } +} + +gameobject *gameobj = 0; diff --git a/src/game/server/gs_common.h b/src/game/server/gs_common.h new file mode 100644 index 00000000..93264f9f --- /dev/null +++ b/src/game/server/gs_common.h @@ -0,0 +1,327 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include "../g_game.h" +#include "../generated/gs_data.h" + + +void create_sound_global(int sound, int target=-1); + +inline int cmask_all() { return -1; } +inline int cmask_one(int cid) { return 1<stand_pos = vec2(stand->x, stand->y); + f->pos = f->stand_pos; + flags[i] = f; + //dbg_msg("game", "flag at %f,%f", f->pos.x, f->pos.y); + } + else + { + // report massive failure + flags[i] = 0; + } + } + + is_teamplay = true; +} + +void gameobject_ctf::on_player_spawn(class player *p) +{ +} + +int gameobject_ctf::on_player_death(class player *victim, class player *killer, int weaponid) +{ + gameobject::on_player_death(victim, killer, weaponid); + int had_flag = 0; + + // drop flags + for(int fi = 0; fi < 2; fi++) + { + flag *f = flags[fi]; + if(f && f->carrying_player == killer) + had_flag |= 2; + if(f && f->carrying_player == victim) + { + create_sound_global(SOUND_CTF_DROP); + f->drop_tick = server_tick(); + f->carrying_player = 0; + f->vel = vec2(0,0); + + if(killer && killer->team != victim->team) + killer->score++; + + had_flag |= 1; + } + } + + return had_flag; +} + +void gameobject_ctf::tick() +{ + gameobject::tick(); + + do_team_wincheck(); + + // do flags + for(int fi = 0; fi < 2; fi++) + { + flag *f = flags[fi]; + + if(!f) + continue; + + // + if(f->carrying_player) + { + // update flag position + f->pos = f->carrying_player->pos; + + if(flags[fi^1]->at_stand) + { + if(distance(f->pos, flags[fi^1]->pos) < 24) + { + // CAPTURE! \o/ + teamscore[fi^1] += 100; + f->carrying_player->score += 5; + for(int i = 0; i < 2; i++) + flags[i]->reset(); + + dbg_msg("", "capture sound %d", SOUND_CTF_CAPTURE); + create_sound_global(SOUND_CTF_CAPTURE); + } + } + } + else + { + player *players[MAX_CLIENTS]; + int types[] = {OBJTYPE_PLAYER_CHARACTER}; + int num = world->find_entities(f->pos, 32.0f, (entity**)players, MAX_CLIENTS, types, 1); + for(int i = 0; i < num; i++) + { + if(players[i]->team == f->team) + { + // return the flag + if(!f->at_stand) + { + players[i]->score += 1; + create_sound_global(SOUND_CTF_RETURN); + f->reset(); + } + } + else + { + // take the flag + if(f->at_stand) + teamscore[fi^1]++; + f->at_stand = 0; + f->carrying_player = players[i]; + f->carrying_player->score += 1; + create_sound_global(SOUND_CTF_GRAB); + break; + } + } + + if(!f->carrying_player && !f->at_stand) + { + if(server_tick() > f->drop_tick + SERVER_TICK_SPEED*30) + { + create_sound_global(SOUND_CTF_RETURN); + f->reset(); + } + else + { + f->vel.y += gravity; + move_box(&f->pos, &f->vel, vec2(f->phys_size, f->phys_size), 0.5f); + } + } + } + } +} + +// Flag +flag::flag(int _team) +: entity(OBJTYPE_FLAG) +{ + team = _team; + proximity_radius = phys_size; + carrying_player = 0x0; + + reset(); + + // TODO: should this be done here? + world->insert_entity(this); +} + +void flag::reset() +{ + carrying_player = 0; + at_stand = 1; + pos = stand_pos; + vel = vec2(0,0); +} + +void flag::snap(int snapping_client) +{ + obj_flag *flag = (obj_flag *)snap_new_item(OBJTYPE_FLAG, team, sizeof(obj_flag)); + flag->x = (int)pos.x; + flag->y = (int)pos.y; + flag->team = team; + flag->carried_by = -1; + + if(at_stand) + flag->carried_by = -2; + else if(carrying_player) + flag->carried_by = carrying_player->client_id; +} diff --git a/src/game/server/gs_game_ctf.h b/src/game/server/gs_game_ctf.h new file mode 100644 index 00000000..02acef37 --- /dev/null +++ b/src/game/server/gs_game_ctf.h @@ -0,0 +1,33 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ + +// game object +class gameobject_ctf : public gameobject +{ +public: + class flag *flags[2]; + + gameobject_ctf(); + virtual void tick(); + + virtual void on_player_spawn(class player *p); + virtual int on_player_death(class player *victim, class player *killer, int weapon); +}; + +// TODO: move to seperate file +class flag : public entity +{ +public: + static const int phys_size = 14; + player *carrying_player; + vec2 vel; + vec2 stand_pos; + + int team; + int at_stand; + int drop_tick; + + flag(int _team); + + virtual void reset(); + virtual void snap(int snapping_client); +}; diff --git a/src/game/server/gs_game_dm.cpp b/src/game/server/gs_game_dm.cpp new file mode 100644 index 00000000..98317578 --- /dev/null +++ b/src/game/server/gs_game_dm.cpp @@ -0,0 +1,42 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include +#include "gs_common.h" +#include "gs_game_dm.h" + +void gameobject_dm::tick() +{ + if(game_over_tick == -1) + { + // game is running + + // gather some stats + int topscore = 0; + int topscore_count = 0; + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(players[i].client_id != -1) + { + if(players[i].score > topscore) + { + topscore = players[i].score; + topscore_count = 1; + } + else if(players[i].score == topscore) + topscore_count++; + } + } + + // check score win condition + if((config.sv_scorelimit > 0 && topscore >= config.sv_scorelimit) || + (config.sv_timelimit > 0 && (server_tick()-round_start_tick) >= config.sv_timelimit*server_tickspeed()*60)) + { + if(topscore_count == 1) + endround(); + else + sudden_death = 1; + } + } + + gameobject::tick(); +} + diff --git a/src/game/server/gs_game_dm.h b/src/game/server/gs_game_dm.h new file mode 100644 index 00000000..96bff3ae --- /dev/null +++ b/src/game/server/gs_game_dm.h @@ -0,0 +1,7 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +// game object +class gameobject_dm : public gameobject +{ +public: + virtual void tick(); +}; diff --git a/src/game/server/gs_game_tdm.cpp b/src/game/server/gs_game_tdm.cpp new file mode 100644 index 00000000..7aa12e2b --- /dev/null +++ b/src/game/server/gs_game_tdm.cpp @@ -0,0 +1,32 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include +#include "gs_common.h" +#include "gs_game_tdm.h" + +gameobject_tdm::gameobject_tdm() +{ + is_teamplay = true; +} + + +int gameobject_tdm::on_player_death(class player *victim, class player *killer, int weapon) +{ + gameobject::on_player_death(victim, killer, weapon); + + if(weapon >= 0) + { + // do team scoring + if(killer == victim) + teamscore[killer->team&1]--; // klant arschel + else + teamscore[killer->team&1]++; // good shit + } + return 0; +} + +void gameobject_tdm::tick() +{ + do_team_wincheck(); + + gameobject::tick(); +} diff --git a/src/game/server/gs_game_tdm.h b/src/game/server/gs_game_tdm.h new file mode 100644 index 00000000..70b4646e --- /dev/null +++ b/src/game/server/gs_game_tdm.h @@ -0,0 +1,10 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +// game object +class gameobject_tdm : public gameobject +{ +public: + gameobject_tdm(); + + int on_player_death(class player *victim, class player *killer, int weapon); + virtual void tick(); +}; diff --git a/src/game/server/gs_server.cpp b/src/game/server/gs_server.cpp new file mode 100644 index 00000000..65985371 --- /dev/null +++ b/src/game/server/gs_server.cpp @@ -0,0 +1,1859 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include +#include +#include +#include +#include "../g_version.h" +#include "gs_common.h" +#include "gs_game_ctf.h" +#include "gs_game_tdm.h" +#include "gs_game_dm.h" + +data_container *data = 0x0; + +class player* get_player(int index); +void create_damageind(vec2 p, float angle_mod, int amount); +void create_explosion(vec2 p, int owner, int weapon, bool bnodamage); +void create_smoke(vec2 p); +void create_spawn(vec2 p); +void create_death(vec2 p); +void create_sound(vec2 pos, int sound, int mask=-1); +class player *intersect_player(vec2 pos0, vec2 pos1, vec2 &new_pos, class entity *notthis = 0); + +game_world *world; + +////////////////////////////////////////////////// +// Event handler +////////////////////////////////////////////////// +event_handler::event_handler() +{ + clear(); +} + +void *event_handler::create(int type, int size, int mask) +{ + if(num_events == MAX_EVENTS) + return 0; + if(current_offset+size >= MAX_DATASIZE) + return 0; + + void *p = &data[current_offset]; + offsets[num_events] = current_offset; + types[num_events] = type; + sizes[num_events] = size; + client_masks[num_events] = mask; + current_offset += size; + num_events++; + return p; +} + +void event_handler::clear() +{ + num_events = 0; + current_offset = 0; +} + +void event_handler::snap(int snapping_client) +{ + for(int i = 0; i < num_events; i++) + { + if(cmask_is_set(client_masks[i], snapping_client)) + { + ev_common *ev = (ev_common *)&data[offsets[i]]; + if(distance(players[snapping_client].pos, vec2(ev->x, ev->y)) < 1500.0f) + { + void *d = snap_new_item(types[i], i, sizes[i]); + mem_copy(d, &data[offsets[i]], sizes[i]); + } + } + } +} + +event_handler events; + +////////////////////////////////////////////////// +// Entity +////////////////////////////////////////////////// +entity::entity(int objtype) +{ + this->objtype = objtype; + pos = vec2(0,0); + flags = FLAG_PHYSICS; + proximity_radius = 0; + + id = snap_new_id(); + + next_entity = 0; + prev_entity = 0; + prev_type_entity = 0; + next_type_entity = 0; +} + +entity::~entity() +{ + snap_free_id(id); +} + +////////////////////////////////////////////////// +// game world +////////////////////////////////////////////////// +game_world::game_world() +{ + paused = false; + reset_requested = false; + first_entity = 0x0; + for(int i = 0; i < NUM_ENT_TYPES; i++) + first_entity_types[i] = 0; +} + +int game_world::find_entities(vec2 pos, float radius, entity **ents, int max) +{ + int num = 0; + for(entity *ent = first_entity; ent; ent = ent->next_entity) + { + if(!(ent->flags&entity::FLAG_PHYSICS)) + continue; + + if(distance(ent->pos, pos) < radius+ent->proximity_radius) + { + ents[num] = ent; + num++; + if(num == max) + break; + } + } + + return num; +} + +int game_world::find_entities(vec2 pos, float radius, entity **ents, int max, const int* types, int maxtypes) +{ + int num = 0; + for(int t = 0; t < maxtypes; t++) + { + for(entity *ent = first_entity_types[types[t]]; ent; ent = ent->next_type_entity) + { + if(!(ent->flags&entity::FLAG_PHYSICS)) + continue; + + if(distance(ent->pos, pos) < radius+ent->proximity_radius) + { + ents[num] = ent; + num++; + if(num == max) + break; + } + } + } + + return num; +} + +void game_world::insert_entity(entity *ent) +{ + entity *cur = first_entity; + while(cur) + { + dbg_assert(cur != ent, "err"); + cur = cur->next_entity; + } + + // insert it + if(first_entity) + first_entity->prev_entity = ent; + ent->next_entity = first_entity; + ent->prev_entity = 0x0; + first_entity = ent; + + // into typelist aswell + if(first_entity_types[ent->objtype]) + first_entity_types[ent->objtype]->prev_type_entity = ent; + ent->next_type_entity = first_entity_types[ent->objtype]; + ent->prev_type_entity = 0x0; + first_entity_types[ent->objtype] = ent; +} + +void game_world::destroy_entity(entity *ent) +{ + ent->set_flag(entity::FLAG_DESTROY); +} + +void game_world::remove_entity(entity *ent) +{ + // not in the list + if(!ent->next_entity && !ent->prev_entity && first_entity != ent) + return; + + // remove + if(ent->prev_entity) + ent->prev_entity->next_entity = ent->next_entity; + else + first_entity = ent->next_entity; + if(ent->next_entity) + ent->next_entity->prev_entity = ent->prev_entity; + + if(ent->prev_type_entity) + ent->prev_type_entity->next_type_entity = ent->next_type_entity; + else + first_entity_types[ent->objtype] = ent->next_type_entity; + if(ent->next_type_entity) + ent->next_type_entity->prev_type_entity = ent->prev_type_entity; + + ent->next_entity = 0; + ent->prev_entity = 0; + ent->next_type_entity = 0; + ent->prev_type_entity = 0; +} + +// +void game_world::snap(int snapping_client) +{ + for(entity *ent = first_entity; ent; ent = ent->next_entity) + ent->snap(snapping_client); +} + +void game_world::reset() +{ + // reset all entities + for(entity *ent = first_entity; ent; ent = ent->next_entity) + ent->reset(); + remove_entities(); + + for(entity *ent = first_entity; ent; ent = ent->next_entity) + ent->post_reset(); + remove_entities(); + + reset_requested = false; +} + +void game_world::remove_entities() +{ + // destroy objects marked for destruction + entity *ent = first_entity; + while(ent) + { + entity *next = ent->next_entity; + if(ent->flags&entity::FLAG_DESTROY) + { + remove_entity(ent); + ent->destroy(); + } + ent = next; + } +} + +void game_world::tick() +{ + if(reset_requested) + reset(); + + if(!paused) + { + // update all objects + for(entity *ent = first_entity; ent; ent = ent->next_entity) + ent->tick(); + + for(entity *ent = first_entity; ent; ent = ent->next_entity) + ent->tick_defered(); + } + + remove_entities(); +} + +////////////////////////////////////////////////// +// projectile +////////////////////////////////////////////////// +projectile::projectile(int type, int owner, vec2 pos, vec2 vel, int span, entity* powner, + int damage, int flags, float force, int sound_impact, int weapon) +: entity(OBJTYPE_PROJECTILE) +{ + this->type = type; + this->pos = pos; + this->vel = vel * SERVER_TICK_SPEED; // TODO: remove this + this->lifespan = span; + this->owner = owner; + this->powner = powner; + this->flags = flags; + this->force = force; + this->damage = damage; + this->sound_impact = sound_impact; + this->weapon = weapon; + this->bounce = 0; + this->start_tick = server_tick(); + world->insert_entity(this); +} + +void projectile::reset() +{ + world->destroy_entity(this); +} + +void projectile::tick() +{ + float gravity = -400; + if(type != WEAPON_ROCKET) + gravity = -100; + + float pt = (server_tick()-start_tick-1)/(float)SERVER_TICK_SPEED; + float ct = (server_tick()-start_tick)/(float)SERVER_TICK_SPEED; + vec2 prevpos = calc_pos(pos, vel, gravity, pt); + vec2 curpos = calc_pos(pos, vel, gravity, ct); + + lifespan--; + + int collide = col_check_point((int)curpos.x, (int)curpos.y); + + vec2 new_pos; + entity *targetplayer = (entity*)intersect_player(prevpos, curpos, new_pos, powner); + + if(targetplayer || collide || lifespan < 0 ) + { + if (lifespan >= 0 || weapon == WEAPON_ROCKET) + create_sound(pos, sound_impact); + + if (flags & PROJECTILE_FLAGS_EXPLODE) + create_explosion(prevpos, owner, weapon, false); + else if (targetplayer) + { + targetplayer->take_damage(normalize(vel) * max(0.001f, force), damage, owner, weapon); + } + + world->destroy_entity(this); + } +} + +void projectile::snap(int snapping_client) +{ + float ct = (server_tick()-start_tick)/(float)SERVER_TICK_SPEED; + vec2 curpos = calc_pos(pos, vel, -7.5f*SERVER_TICK_SPEED, ct); + + if(distance(players[snapping_client].pos, curpos) > 1000.0f) + return; + + obj_projectile *proj = (obj_projectile *)snap_new_item(OBJTYPE_PROJECTILE, id, sizeof(obj_projectile)); + proj->x = (int)pos.x; + proj->y = (int)pos.y; + proj->vx = (int)vel.x; + proj->vy = (int)vel.y; + proj->start_tick = start_tick; + proj->type = type; +} + +////////////////////////////////////////////////// +// player +////////////////////////////////////////////////// +// TODO: move to separate file +player::player() +: entity(OBJTYPE_PLAYER_CHARACTER) +{ + init(); +} + +void player::init() +{ + proximity_radius = phys_size; + client_id = -1; + team = -1; // -1 == spectator + extrapowerflags = 0; + + latency_accum = 0; + latency_accum_min = 0; + latency_accum_max = 0; + latency_avg = 0; + latency_min = 0; + latency_max = 0; + + reset(); +} + +void player::reset() +{ + pos = vec2(0.0f, 0.0f); + core.vel = vec2(0.0f, 0.0f); + //direction = vec2(0.0f, 1.0f); + score = 0; + dead = true; + clear_flag(entity::FLAG_PHYSICS); + spawning = false; + die_tick = 0; + damage_taken = 0; + state = STATE_UNKNOWN; + + mem_zero(&input, sizeof(input)); + mem_zero(&previnput, sizeof(previnput)); + num_inputs = 0; + + last_action = -1; + + emote_stop = 0; + damage_taken_tick = 0; + attack_tick = 0; + numobjectshit = 0; + ninjaactivationtick = 0; + currentmovetime = 0; + + active_weapon = WEAPON_GUN; + last_weapon = WEAPON_HAMMER; + wanted_weapon = WEAPON_GUN; +} + +void player::destroy() { } + +void player::set_weapon(int w) +{ + last_weapon = active_weapon; + active_weapon = w; + if(active_weapon < 0 || active_weapon >= NUM_WEAPONS) + active_weapon = 0; +} + +void player::respawn() +{ + spawning = true; +} + + +void player::set_team(int new_team) +{ + team = new_team; + die(client_id, -1); + + dbg_msg("game", "cid=%d team=%d", client_id, team); + + if(team == -1) + clear_flag(FLAG_PHYSICS); + else + set_flag(FLAG_PHYSICS); +} + + +bool try_spawntype(int t, vec2 *outpos) +{ + // get spawn point + int start, num; + map_get_type(t, &start, &num); + if(!num) + return false; + + int id = rand()%num; + mapres_spawnpoint *sp = (mapres_spawnpoint*)map_get_item(start + id, NULL, NULL); + *outpos = vec2((float)sp->x, (float)sp->y); + return true; +} + +void player::try_respawn() +{ + vec2 spawnpos = vec2(100.0f, -60.0f); + + // get spawn point + if(gameobj->gametype == GAMETYPE_CTF) + { + // try first try own team spawn, then normal spawn and then enemy + if(!try_spawntype(MAPRES_SPAWNPOINT_RED+(team&1), &spawnpos)) + { + if(!try_spawntype(MAPRES_SPAWNPOINT, &spawnpos)) + try_spawntype(MAPRES_SPAWNPOINT_RED+((team+1)&1), &spawnpos); + } + } + else + { + if(!try_spawntype(MAPRES_SPAWNPOINT, &spawnpos)) + try_spawntype(MAPRES_SPAWNPOINT_RED+(rand()&1), &spawnpos); + } + + // check if the position is occupado + entity *ents[2] = {0}; + int types[] = {OBJTYPE_PLAYER_CHARACTER}; + int num_ents = world->find_entities(spawnpos, 64, ents, 2, types, 1); + for(int i = 0; i < num_ents; i++) + { + if(ents[i] != this) + return; + } + + spawning = false; + pos = spawnpos; + + core.pos = pos; + core.vel = vec2(0,0); + core.hooked_player = -1; + + health = 10; + armor = 0; + jumped = 0; + dead = false; + set_flag(entity::FLAG_PHYSICS); + state = STATE_PLAYING; + + core.hook_state = HOOK_IDLE; + + mem_zero(&input, sizeof(input)); + + // init weapons + mem_zero(&weapons, sizeof(weapons)); + weapons[WEAPON_HAMMER].got = true; + weapons[WEAPON_HAMMER].ammo = -1; + weapons[WEAPON_GUN].got = true; + weapons[WEAPON_GUN].ammo = data->weapons[WEAPON_GUN].maxammo; + + //weapons[WEAPON_SNIPER].got = true; + //weapons[WEAPON_SNIPER].ammo = data->weapons[WEAPON_SNIPER].maxammo; + active_weapon = WEAPON_GUN; + last_weapon = WEAPON_HAMMER; + wanted_weapon = WEAPON_GUN; + + reload_timer = 0; + + // Create sound and spawn effects + create_sound(pos, SOUND_PLAYER_SPAWN); + create_spawn(pos); + + gameobj->on_player_spawn(this); +} + +bool player::is_grounded() +{ + if(col_check_point((int)(pos.x+phys_size/2), (int)(pos.y+phys_size/2+5))) + return true; + if(col_check_point((int)(pos.x-phys_size/2), (int)(pos.y+phys_size/2+5))) + return true; + return false; +} + +struct input_count +{ + int presses; + int releases; +}; + +static input_count count_input(int prev, int cur) +{ + input_count c = {0,0}; + prev &= INPUT_STATE_MASK; + cur &= INPUT_STATE_MASK; + int i = prev; + while(i != cur) + { + i = (i+1)&INPUT_STATE_MASK; + if(i&1) + c.presses++; + else + c.releases++; + } + + return c; +} + + +int player::handle_ninja() +{ + vec2 direction = normalize(vec2(input.target_x, input.target_y)); + + if ((server_tick() - ninjaactivationtick) > (data->weapons[WEAPON_NINJA].duration * server_tickspeed() / 1000)) + { + // time's up, return + weapons[WEAPON_NINJA].got = false; + active_weapon = last_weapon; + set_weapon(active_weapon); + return 0; + } + + // Check if it should activate + if (count_input(previnput.fire, input.fire).presses && (server_tick() > currentcooldown)) + { + // ok then, activate ninja + attack_tick = server_tick(); + activationdir = direction; + currentmovetime = data->weapons[WEAPON_NINJA].movetime * server_tickspeed() / 1000; + currentcooldown = data->weapons[WEAPON_NINJA].firedelay * server_tickspeed() / 1000 + server_tick(); + + // reset hit objects + numobjectshit = 0; + + create_sound(pos, SOUND_NINJA_FIRE); + + // release all hooks when ninja is activated + //release_hooked(); + //release_hooks(); + } + + currentmovetime--; + + if (currentmovetime == 0) + { + // reset player velocity + core.vel *= 0.2f; + //return MODIFIER_RETURNFLAGS_OVERRIDEWEAPON; + } + + if (currentmovetime > 0) + { + // Set player velocity + core.vel = activationdir * data->weapons[WEAPON_NINJA].velocity; + vec2 oldpos = pos; + move_box(&core.pos, &core.vel, vec2(phys_size, phys_size), 0.0f); + // reset velocity so the client doesn't predict stuff + core.vel = vec2(0.0f,0.0f); + if ((currentmovetime % 2) == 0) + { + create_smoke(pos); + } + + // check if we hit anything along the way + { + int type = OBJTYPE_PLAYER_CHARACTER; + entity *ents[64]; + vec2 dir = pos - oldpos; + float radius = phys_size * 2.0f; //length(dir * 0.5f); + vec2 center = oldpos + dir * 0.5f; + int num = world->find_entities(center, radius, ents, 64, &type, 1); + + for (int i = 0; i < num; i++) + { + // Check if entity is a player + if (ents[i] == this) + continue; + // make sure we haven't hit this object before + bool balreadyhit = false; + for (int j = 0; j < numobjectshit; j++) + { + if (hitobjects[j] == ents[i]) + balreadyhit = true; + } + if (balreadyhit) + continue; + + // check so we are sufficiently close + if (distance(ents[i]->pos, pos) > (phys_size * 2.0f)) + continue; + + // hit a player, give him damage and stuffs... + create_sound(ents[i]->pos, SOUND_NINJA_HIT); + // set his velocity to fast upward (for now) + if(numobjectshit < 10) + hitobjects[numobjectshit++] = ents[i]; + ents[i]->take_damage(vec2(0,10.0f), data->weapons[WEAPON_NINJA].meleedamage, client_id,WEAPON_NINJA); + } + } + return MODIFIER_RETURNFLAGS_OVERRIDEVELOCITY | MODIFIER_RETURNFLAGS_OVERRIDEPOSITION | MODIFIER_RETURNFLAGS_OVERRIDEGRAVITY; + } + + return 0; +} + +int player::handle_sniper() +{ + return 0; + + struct input_count button = count_input(previnput.fire, input.fire); + if (button.releases) + { + vec2 direction = normalize(vec2(input.target_x, input.target_y)); + // Check if we were charging, if so fire + if (weapons[WEAPON_SNIPER].weaponstage >= WEAPONSTAGE_SNIPER_CHARGING) + { + new projectile(projectile::WEAPON_PROJECTILETYPE_SNIPER, + client_id, pos+vec2(0,0), direction*50.0f, + 100 + weapons[WEAPON_SNIPER].weaponstage * 20,this, weapons[WEAPON_SNIPER].weaponstage, 0, 0, -1, WEAPON_SNIPER); + create_sound(pos, SOUND_SNIPER_FIRE); + } + // Add blowback + core.vel = -direction * 10.0f * weapons[WEAPON_SNIPER].weaponstage; + + // update ammo and stuff + weapons[WEAPON_SNIPER].ammo = max(0,weapons[WEAPON_SNIPER].ammo - weapons[WEAPON_SNIPER].weaponstage); + weapons[WEAPON_SNIPER].weaponstage = WEAPONSTAGE_SNIPER_NEUTRAL; + weapons[WEAPON_SNIPER].chargetick = 0; + } + else if (input.fire & 1) + { + // Charge!! (if we are on the ground) + if (is_grounded() && weapons[WEAPON_SNIPER].ammo > 0) + { + if (!weapons[WEAPON_SNIPER].chargetick) + { + weapons[WEAPON_SNIPER].chargetick = server_tick(); + dbg_msg("game", "Chargetick='%d:'", server_tick()); + } + if ((server_tick() - weapons[WEAPON_SNIPER].chargetick) > server_tickspeed() * data->weapons[active_weapon].chargetime) + { + if (weapons[WEAPON_SNIPER].ammo > weapons[WEAPON_SNIPER].weaponstage) + { + weapons[WEAPON_SNIPER].weaponstage++; + weapons[WEAPON_SNIPER].chargetick = server_tick(); + } + else if ((server_tick() - weapons[WEAPON_SNIPER].chargetick) > server_tickspeed() * data->weapons[active_weapon].overchargetime) + { + // Ooopsie, weapon exploded + create_explosion(pos, client_id, WEAPON_SNIPER, false); + create_sound(pos, SOUND_ROCKET_EXPLODE); + // remove this weapon and change weapon to gun + weapons[WEAPON_SNIPER].got = false; + weapons[WEAPON_SNIPER].ammo = 0; + last_weapon = active_weapon; + active_weapon = WEAPON_GUN; + return 0; + } + } + + // While charging, don't move + return MODIFIER_RETURNFLAGS_OVERRIDEVELOCITY|MODIFIER_RETURNFLAGS_NOHOOK; + } + else if (weapons[WEAPON_SNIPER].weaponstage) + weapons[WEAPON_SNIPER].weaponstage = WEAPONSTAGE_SNIPER_NEUTRAL; + } + return 0; +} + +int player::handle_weapons() +{ + vec2 direction = normalize(vec2(input.target_x, input.target_y)); + + if(config.dbg_stress) + { + for(int i = 0; i < NUM_WEAPONS; i++) + { + weapons[i].got = true; + weapons[i].ammo = 10; + } + + if(reload_timer) // twice as fast reload + reload_timer--; + } + + // check reload timer + if(reload_timer) + { + reload_timer--; + return 0; + } + + if (active_weapon == WEAPON_NINJA) + { + // don't update other weapons while ninja is active + return handle_ninja(); + } + + // select weapon + int next = count_input(previnput.next_weapon, input.next_weapon).presses; + int prev = count_input(previnput.prev_weapon, input.prev_weapon).presses; + + if(next < 128) // make sure we only try sane stuff + { + while(next) // next weapon selection + { + wanted_weapon = (wanted_weapon+1)%NUM_WEAPONS; + if(weapons[wanted_weapon].got) + next--; + } + } + + if(prev < 128) // make sure we only try sane stuff + { + while(prev) // prev weapon selection + { + wanted_weapon = (wanted_weapon-1)<0?NUM_WEAPONS-1:wanted_weapon-1; + if(weapons[wanted_weapon].got) + prev--; + } + } + + if(input.wanted_weapon) // direct weapon selection + wanted_weapon = input.wanted_weapon-1; + + if(wanted_weapon < 0 || wanted_weapon >= NUM_WEAPONS) + wanted_weapon = 0; + + // switch weapon if wanted + if(data->weapons[active_weapon].duration <= 0) + { + if(wanted_weapon != -1 && wanted_weapon != active_weapon && wanted_weapon >= 0 && wanted_weapon < NUM_WEAPONS && weapons[wanted_weapon].got) + { + if(active_weapon != wanted_weapon) + create_sound(pos, SOUND_WEAPON_SWITCH); + + set_weapon(wanted_weapon); + } + } + + if (active_weapon == WEAPON_SNIPER) + { + // don't update other weapons while sniper is active + return handle_sniper(); + } + + if(count_input(previnput.fire, input.fire).presses) //previnput.fire != input.fire && (input.fire&1)) + { + if(reload_timer == 0) + { + // fire! + if(weapons[active_weapon].ammo) + { + switch(active_weapon) + { + case WEAPON_HAMMER: + // reset objects hit + numobjectshit = 0; + create_sound(pos, SOUND_HAMMER_FIRE); + break; + + case WEAPON_GUN: + new projectile(WEAPON_GUN, + client_id, + pos+vec2(0,0), + direction*30.0f, + 100, + this, + 1, 0, 0, -1, WEAPON_GUN); + create_sound(pos, SOUND_GUN_FIRE); + break; + case WEAPON_ROCKET: + { + new projectile(WEAPON_ROCKET, + client_id, + pos+vec2(0,0), + direction*15.0f, + 100, + this, + 1, projectile::PROJECTILE_FLAGS_EXPLODE, 0, SOUND_ROCKET_EXPLODE, WEAPON_ROCKET); + create_sound(pos, SOUND_ROCKET_FIRE); + break; + } + case WEAPON_SHOTGUN: + { + int shotspread = 2; + for(int i = -shotspread; i <= shotspread; i++) + { + float spreading[] = {-0.12f, -0.05f, 0, 0.05f, 0.12f}; + float a = get_angle(direction); + float v = 1.0f-fabs(i/(float)shotspread); + a += spreading[i+2]; + new projectile(WEAPON_SHOTGUN, + client_id, + pos+vec2(0,0), + vec2(cosf(a), sinf(a))*(30.0f + 15.0f*v), + //vec2(cosf(a), sinf(a))*20.0f, + (int)(server_tickspeed()*0.3f), + this, + 1, 0, 0, -1, WEAPON_SHOTGUN); + } + create_sound(pos, SOUND_SHOTGUN_FIRE); + break; + } + } + + weapons[active_weapon].ammo--; + attack_tick = server_tick(); + reload_timer = data->weapons[active_weapon].firedelay * server_tickspeed() / 1000; + } + else + { + create_sound(pos, SOUND_WEAPON_NOAMMO); + } + } + } + + // Update weapons + if (active_weapon == WEAPON_HAMMER && reload_timer > 0) + { + // Handle collisions + // only one that needs update (for now) + // do selection for the weapon and bash anything in it + // check if we hit anything along the way + int type = OBJTYPE_PLAYER_CHARACTER; + entity *ents[64]; + vec2 lookdir(direction.x > 0.0f ? 1.0f : -1.0f, 0.0f); + vec2 dir = lookdir * data->weapons[active_weapon].meleereach; + float radius = length(dir * 0.5f); + vec2 center = pos + dir * 0.5f; + int num = world->find_entities(center, radius, ents, 64, &type, 1); + + for (int i = 0; i < num; i++) + { + // Check if entity is a player + if (ents[i] == this) + continue; + // make sure we haven't hit this object before + bool balreadyhit = false; + for (int j = 0; j < numobjectshit; j++) + { + if (hitobjects[j] == ents[i]) + balreadyhit = true; + } + if (balreadyhit) + continue; + + // check so we are sufficiently close + if (distance(ents[i]->pos, pos) > (phys_size * 2.0f)) + continue; + + // hit a player, give him damage and stuffs... + // create sound for bash + //create_sound(ents[i]->pos, sound_impact); + vec2 fdir = normalize(ents[i]->pos- pos); + + // set his velocity to fast upward (for now) + create_smoke(ents[i]->pos); + create_sound(pos, SOUND_HAMMER_HIT); + if(numobjectshit < 10) + hitobjects[numobjectshit++] = ents[i]; + ents[i]->take_damage(vec2(0,-1.0f), data->weapons[active_weapon].meleedamage, client_id, active_weapon); + player* target = (player*)ents[i]; + vec2 dir; + if (length(target->pos - pos) > 0.0f) + dir = normalize(target->pos - pos); + else + dir = vec2(0,-1); + + target->core.vel += normalize(dir + vec2(0,-1.1f)) * 10.0f; + } + } + + if (data->weapons[active_weapon].ammoregentime) + { + // If equipped and not active, regen ammo? + if (reload_timer <= 0) + { + if (weapons[active_weapon].ammoregenstart < 0) + weapons[active_weapon].ammoregenstart = server_tick(); + + if ((server_tick() - weapons[active_weapon].ammoregenstart) >= data->weapons[active_weapon].ammoregentime * server_tickspeed() / 1000) + { + // Add some ammo + weapons[active_weapon].ammo = min(weapons[active_weapon].ammo + 1, data->weapons[active_weapon].maxammo); + weapons[active_weapon].ammoregenstart = -1; + } + } + else + { + weapons[active_weapon].ammoregenstart = -1; + } + } + + return 0; +} + +void player::tick() +{ + // check if we have enough input + // this is to prevent initial weird clicks + if(num_inputs < 2) + previnput = input; + + // do latency stuff + { + CLIENT_INFO info; + if(server_getclientinfo(client_id, &info)) + { + latency_accum += info.latency; + latency_accum_max = max(latency_accum_max, info.latency); + latency_accum_min = min(latency_accum_min, info.latency); + } + + if(server_tick()%server_tickspeed() == 0) + { + latency_avg = latency_accum/server_tickspeed(); + latency_max = latency_accum_max; + latency_min = latency_accum_min; + latency_accum = 0; + latency_accum_min = 1000; + latency_accum_max = 0; + } + } + + // enable / disable physics + if(team == -1 || dead) + world->core.players[client_id] = 0; + else + world->core.players[client_id] = &core; + + // spectator + if(team == -1) + return; + + if(spawning) + try_respawn(); + + // TODO: rework the input to be more robust + if(dead) + { + if(server_tick()-die_tick >= server_tickspeed()*5) // auto respawn after 3 sec + respawn(); + if((input.fire&1) && server_tick()-die_tick >= server_tickspeed()/2) // auto respawn after 0.5 sec + respawn(); + return; + } + + //player_core core; + //core.pos = pos; + //core.jumped = jumped; + core.input = input; + core.tick(); + + // handle weapons + handle_weapons(); + + state = input.state; + + // Previnput + previnput = input; + return; +} + +void player::tick_defered() +{ + if(!dead) + { + vec2 start_pos = core.pos; + vec2 start_vel = core.vel; + bool stuck_before = test_box(core.pos, vec2(28.0f, 28.0f)); + core.move(); + bool stuck_after_move = test_box(core.pos, vec2(28.0f, 28.0f)); + core.quantize(); + bool stuck_after_quant = test_box(core.pos, vec2(28.0f, 28.0f)); + pos = core.pos; + + if(!stuck_before && (stuck_after_move || stuck_after_quant)) + { + dbg_msg("player", "STUCK!!! %d %d %d %f %f %f %f %x %x %x %x", + stuck_before, + stuck_after_move, + stuck_after_quant, + start_pos.x, start_pos.y, + start_vel.x, start_vel.y, + *((unsigned *)&start_pos.x), *((unsigned *)&start_pos.y), + *((unsigned *)&start_vel.x), *((unsigned *)&start_vel.y)); + } + + + int events = core.triggered_events; + int mask = cmask_all_except_one(client_id); + + if(events&COREEVENT_GROUND_JUMP) create_sound(pos, SOUND_PLAYER_JUMP, mask); + if(events&COREEVENT_AIR_JUMP) + { + create_sound(pos, SOUND_PLAYER_JUMP, mask); + ev_common *c = (ev_common *)::events.create(EVENT_AIR_JUMP, sizeof(ev_common), mask); + if(c) + { + c->x = (int)pos.x; + c->y = (int)pos.y; + } + } + + //if(events&COREEVENT_HOOK_LAUNCH) snd_play_random(CHN_WORLD, SOUND_HOOK_LOOP, 1.0f, pos); + if(events&COREEVENT_HOOK_ATTACH_PLAYER) create_sound(pos, SOUND_HOOK_ATTACH_PLAYER, mask); + if(events&COREEVENT_HOOK_ATTACH_GROUND) create_sound(pos, SOUND_HOOK_ATTACH_GROUND, mask); + //if(events&COREEVENT_HOOK_RETRACT) snd_play_random(CHN_WORLD, SOUND_PLAYER_JUMP, 1.0f, pos); + + } + + if(team == -1) + { + pos.x = input.target_x; + pos.y = input.target_y; + } +} + +void player::die(int killer, int weapon) +{ + int mode_special = gameobj->on_player_death(this, get_player(killer), weapon); + + dbg_msg("game", "kill killer='%d:%s' victim='%d:%s' weapon=%d special=%d", + killer, server_clientname(killer), + client_id, server_clientname(client_id), weapon, mode_special); + + // send the kill message + msg_pack_start(MSG_KILLMSG, MSGFLAG_VITAL); + msg_pack_int(killer); + msg_pack_int(client_id); + msg_pack_int(weapon); + msg_pack_int(mode_special); + msg_pack_end(); + server_send_msg(-1); + + // a nice sound + create_sound(pos, SOUND_PLAYER_DIE); + + // set dead state + dead = true; + die_tick = server_tick(); + clear_flag(FLAG_PHYSICS); + create_death(pos); +} + +bool player::take_damage(vec2 force, int dmg, int from, int weapon) +{ + core.vel += force; + + if(gameobj->is_friendly_fire(client_id, from) && !config.sv_teamdamage) + return false; + + // player only inflicts half damage on self + if(from == client_id) + dmg = max(1, dmg/2); + + // CTF and TDM (TODO: check for FF) + //if (gameobj->gametype != GAMETYPE_DM && from >= 0 && players[from].team == team) + //return false; + + damage_taken++; + + // create healthmod indicator + if(server_tick() < damage_taken_tick+25) + { + // make sure that the damage indicators doesn't group together + create_damageind(pos, damage_taken*0.25f, dmg); + } + else + { + damage_taken = 0; + create_damageind(pos, 0, dmg); + } + + if(armor) + { + armor -= 1; + dmg--; + } + + if(dmg > armor) + { + dmg -= armor; + armor = 0; + health -= dmg; + } + else + armor -= dmg; + + damage_taken_tick = server_tick(); + + // do damage hit sound + if(from >= 0) + create_sound(get_player(from)->pos, SOUND_HIT, cmask_one(from)); + + // check for death + if(health <= 0) + { + die(from, weapon); + + // set attacker's face to happy (taunt!) + if (from >= 0 && from != client_id) + { + player *p = get_player(from); + + p->emote_type = EMOTE_HAPPY; + p->emote_stop = server_tick() + server_tickspeed(); + } + + return false; + } + + if (dmg > 2) + create_sound(pos, SOUND_PLAYER_PAIN_LONG); + else + create_sound(pos, SOUND_PLAYER_PAIN_SHORT); + + emote_type = EMOTE_PAIN; + emote_stop = server_tick() + 500 * server_tickspeed() / 1000; + + // spawn blood? + return true; +} + +void player::snap(int snaping_client) +{ + if(1) + { + obj_player_info *info = (obj_player_info *)snap_new_item(OBJTYPE_PLAYER_INFO, client_id, sizeof(obj_player_info)); + + info->latency = latency_avg; + info->latency_flux = latency_max-latency_min; + info->local = 0; + info->clientid = client_id; + info->score = score; + info->team = team; + + if(client_id == snaping_client) + info->local = 1; + } + + if(health > 0 && distance(players[snaping_client].pos, pos) < 1000.0f) + { + obj_player_character *character = (obj_player_character *)snap_new_item(OBJTYPE_PLAYER_CHARACTER, client_id, sizeof(obj_player_character)); + + core.write(character); + + // this is to make sure that players that are just standing still + // isn't sent. this is because the physics keep bouncing between + // 0-128 when just standing. + // TODO: fix the physics so this isn't needed + if(snaping_client != client_id && abs(character->vy) < 256.0f) + character->vy = 0; + + if (emote_stop < server_tick()) + { + emote_type = EMOTE_NORMAL; + emote_stop = -1; + } + + character->emote = emote_type; + + character->ammocount = weapons[active_weapon].ammo; + character->health = 0; + character->armor = 0; + character->weapon = active_weapon; + character->weaponstage = weapons[active_weapon].weaponstage; + character->attacktick = attack_tick; + + if(client_id == snaping_client) + { + character->health = health; + character->armor = armor; + } + + if(dead) + character->health = -1; + + //if(length(vel) > 15.0f) + // player->emote = EMOTE_HAPPY; + + //if(damage_taken_tick+50 > server_tick()) + // player->emote = EMOTE_PAIN; + + if (character->emote == EMOTE_NORMAL) + { + if(250 - ((server_tick() - last_action)%(250)) < 5) + character->emote = EMOTE_BLINK; + } + + character->state = state; + } +} + +player *players; + +////////////////////////////////////////////////// +// powerup +////////////////////////////////////////////////// +powerup::powerup(int _type, int _subtype) +: entity(OBJTYPE_POWERUP) +{ + type = _type; + subtype = _subtype; + proximity_radius = phys_size; + + reset(); + + // TODO: should this be done here? + world->insert_entity(this); +} + +void powerup::reset() +{ + if (data->powerupinfo[type].startspawntime > 0) + spawntick = server_tick() + server_tickspeed() * data->powerupinfo[type].startspawntime; + else + spawntick = -1; +} + + +void send_weapon_pickup(int cid, int weapon); + +void powerup::tick() +{ + // wait for respawn + if(spawntick > 0) + { + if(server_tick() > spawntick) + { + // respawn + spawntick = -1; + + if(type == POWERUP_WEAPON) + create_sound(pos, SOUND_WEAPON_SPAWN); + } + else + return; + } + // Check if a player intersected us + vec2 meh; + player* pplayer = intersect_player(pos, pos + vec2(0,16), meh, 0); + if (pplayer) + { + // player picked us up, is someone was hooking us, let them go + int respawntime = -1; + switch (type) + { + case POWERUP_HEALTH: + if(pplayer->health < 10) + { + create_sound(pos, SOUND_PICKUP_HEALTH); + pplayer->health = min(10, pplayer->health + data->powerupinfo[type].amount); + respawntime = data->powerupinfo[type].respawntime; + } + break; + case POWERUP_ARMOR: + if(pplayer->armor < 10) + { + create_sound(pos, SOUND_PICKUP_ARMOR); + pplayer->armor = min(10, pplayer->armor + data->powerupinfo[type].amount); + respawntime = data->powerupinfo[type].respawntime; + } + break; + + case POWERUP_WEAPON: + if(subtype >= 0 && subtype < NUM_WEAPONS) + { + if(pplayer->weapons[subtype].ammo < 10 || !pplayer->weapons[subtype].got) + { + pplayer->weapons[subtype].got = true; + pplayer->weapons[subtype].ammo = min(10, pplayer->weapons[subtype].ammo + data->powerupinfo[type].amount); + respawntime = data->powerupinfo[type].respawntime; + + // TODO: data compiler should take care of stuff like this + if(subtype == WEAPON_ROCKET) + create_sound(pos, SOUND_PICKUP_ROCKET); + else if(subtype == WEAPON_SHOTGUN) + create_sound(pos, SOUND_PICKUP_SHOTGUN); + + send_weapon_pickup(pplayer->client_id, subtype); + } + } + break; + case POWERUP_NINJA: + { + // activate ninja on target player + pplayer->ninjaactivationtick = server_tick(); + pplayer->weapons[WEAPON_NINJA].got = true; + pplayer->last_weapon = pplayer->active_weapon; + pplayer->active_weapon = WEAPON_NINJA; + respawntime = data->powerupinfo[type].respawntime; + create_sound(pos, SOUND_PICKUP_NINJA); + + // loop through all players, setting their emotes + entity *ents[64]; + const int types[] = {OBJTYPE_PLAYER_CHARACTER}; + int num = world->find_entities(vec2(0, 0), 1000000, ents, 64, types, 1); + for (int i = 0; i < num; i++) + { + player *p = (player *)ents[i]; + if (p != pplayer) + { + p->emote_type = EMOTE_SURPRISE; + p->emote_stop = server_tick() + server_tickspeed(); + } + } + + pplayer->emote_type = EMOTE_ANGRY; + pplayer->emote_stop = server_tick() + 1200 * server_tickspeed() / 1000; + + break; + } + default: + break; + }; + + if(respawntime >= 0) + { + dbg_msg("game", "pickup player='%d:%s' item=%d/%d", + pplayer->client_id, server_clientname(pplayer->client_id), type, subtype); + spawntick = server_tick() + server_tickspeed() * respawntime; + } + } +} + +void powerup::snap(int snapping_client) +{ + if(spawntick != -1) + return; + + obj_powerup *up = (obj_powerup *)snap_new_item(OBJTYPE_POWERUP, id, sizeof(obj_powerup)); + up->x = (int)pos.x; + up->y = (int)pos.y; + up->type = type; // TODO: two diffrent types? what gives? + up->subtype = subtype; +} + +// POWERUP END /////////////////////// + +player *get_player(int index) +{ + return &players[index]; +} + +void create_damageind(vec2 p, float angle, int amount) +{ + float a = 3 * 3.14159f / 2 + angle; + //float a = get_angle(dir); + float s = a-pi/3; + float e = a+pi/3; + for(int i = 0; i < amount; i++) + { + float f = mix(s, e, float(i+1)/float(amount+2)); + ev_damageind *ev = (ev_damageind *)events.create(EVENT_DAMAGEINDICATION, sizeof(ev_damageind)); + if(ev) + { + ev->x = (int)p.x; + ev->y = (int)p.y; + ev->angle = (int)(f*256.0f); + } + } +} + +void create_explosion(vec2 p, int owner, int weapon, bool bnodamage) +{ + // create the event + ev_explosion *ev = (ev_explosion *)events.create(EVENT_EXPLOSION, sizeof(ev_explosion)); + if(ev) + { + ev->x = (int)p.x; + ev->y = (int)p.y; + } + + if (!bnodamage) + { + // deal damage + entity *ents[64]; + const float radius = 128.0f; + const float innerradius = 42.0f; + int num = world->find_entities(p, radius, ents, 64); + for(int i = 0; i < num; i++) + { + vec2 diff = ents[i]->pos - p; + vec2 forcedir(0,1); + float l = length(diff); + if(l) + forcedir = normalize(diff); + l = 1-clamp((l-innerradius)/(radius-innerradius), 0.0f, 1.0f); + float dmg = 6 * l; + if((int)dmg) + ents[i]->take_damage(forcedir*dmg*2, (int)dmg, owner, weapon); + } + } +} + +void create_smoke(vec2 p) +{ + // create the event + ev_explosion *ev = (ev_explosion *)events.create(EVENT_SMOKE, sizeof(ev_explosion)); + if(ev) + { + ev->x = (int)p.x; + ev->y = (int)p.y; + } +} + +void create_spawn(vec2 p) +{ + // create the event + ev_spawn *ev = (ev_spawn *)events.create(EVENT_SPAWN, sizeof(ev_spawn)); + if(ev) + { + ev->x = (int)p.x; + ev->y = (int)p.y; + } +} + +void create_death(vec2 p) +{ + // create the event + ev_death *ev = (ev_death *)events.create(EVENT_DEATH, sizeof(ev_death)); + if(ev) + { + ev->x = (int)p.x; + ev->y = (int)p.y; + } +} + +void create_sound(vec2 pos, int sound, int mask) +{ + if (sound < 0) + return; + + // create a sound + ev_sound *ev = (ev_sound *)events.create(EVENT_SOUND_WORLD, sizeof(ev_sound), mask); + if(ev) + { + ev->x = (int)pos.x; + ev->y = (int)pos.y; + ev->sound = sound; + } +} + +void create_sound_global(int sound, int target) +{ + if (sound < 0) + return; + + msg_pack_start(MSG_SOUND_GLOBAL, MSGFLAG_VITAL); + msg_pack_int(sound); + server_send_msg(-1); +} + +// TODO: should be more general +player* intersect_player(vec2 pos0, vec2 pos1, vec2& new_pos, entity* notthis) +{ + // Find other players + entity *ents[64]; + vec2 dir = pos1 - pos0; + float radius = length(dir * 0.5f); + vec2 center = pos0 + dir * 0.5f; + const int types[] = {OBJTYPE_PLAYER_CHARACTER}; + int num = world->find_entities(center, radius, ents, 64, types, 1); + for (int i = 0; i < num; i++) + { + // Check if entity is a player + if (ents[i] != notthis) + { + new_pos = ents[i]->pos; + return (player*)ents[i]; + } + } + + return 0; +} + + +void send_chat(int cid, int team, const char *msg) +{ + if(cid >= 0 && cid < MAX_CLIENTS) + dbg_msg("chat", "%d:%d:%s: %s", cid, team, server_clientname(cid), msg); + else + dbg_msg("chat", "*** %s", msg); + + if(team == -1) + { + msg_pack_start(MSG_CHAT, MSGFLAG_VITAL); + msg_pack_int(cid); + msg_pack_int(0); + msg_pack_string(msg, 512); + msg_pack_end(); + server_send_msg(-1); + } + else + { + msg_pack_start(MSG_CHAT, MSGFLAG_VITAL); + msg_pack_int(cid); + msg_pack_int(1); + msg_pack_string(msg, 512); + msg_pack_end(); + + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(players[i].client_id != -1 && players[i].team == team) + server_send_msg(i); + } + } +} + + +// Server hooks +void mods_tick() +{ + // clear all events + events.clear(); + world->tick(); + + if(world->paused) // make sure that the game object always updates + gameobj->tick(); + + if(config.sv_restart) + { + if(config.sv_restart > 1) + gameobj->do_warmup(config.sv_restart); + else + gameobj->startround(); + + config.sv_restart = 0; + } + + if(config.sv_msg[0] != 0) + { + send_chat(-1, 0, config.sv_msg); + config.sv_msg[0] = 0; + } +} + +void mods_snap(int client_id) +{ + world->snap(client_id); + events.snap(client_id); +} + +void mods_client_input(int client_id, void *input) +{ + if(!world->paused) + { + if (memcmp(&players[client_id].input, input, sizeof(player_input)) != 0) + players[client_id].last_action = server_tick(); + + //players[client_id].previnput = players[client_id].input; + players[client_id].input = *(player_input*)input; + players[client_id].num_inputs++; + } +} + +void send_info(int who, int to_who) +{ + msg_pack_start(MSG_SETINFO, MSGFLAG_VITAL); + msg_pack_int(who); + msg_pack_string(server_clientname(who), 64); + msg_pack_string(players[who].skin_name, 64); + msg_pack_int(players[who].use_custom_color); + msg_pack_int(players[who].color_body); + msg_pack_int(players[who].color_feet); + msg_pack_end(); + server_send_msg(to_who); +} + +void send_emoticon(int cid, int emoticon) +{ + msg_pack_start(MSG_EMOTICON, MSGFLAG_VITAL); + msg_pack_int(cid); + msg_pack_int(emoticon % 16); + msg_pack_end(); + server_send_msg(-1); +} + +void send_weapon_pickup(int cid, int weapon) +{ + msg_pack_start(MSG_WEAPON_PICKUP, MSGFLAG_VITAL); + msg_pack_int(weapon); + msg_pack_end(); + server_send_msg(cid); +} + +void mods_client_enter(int client_id) +{ + world->insert_entity(&players[client_id]); + players[client_id].respawn(); + dbg_msg("game", "join player='%d:%s'", client_id, server_clientname(client_id)); + + char buf[512]; + sprintf(buf, "%s has joined the game", server_clientname(client_id)); + send_chat(-1, -1, buf); +} + +void mods_connected(int client_id) +{ + players[client_id].init(); + players[client_id].client_id = client_id; + + //dbg_msg("game", "connected player='%d:%s'", client_id, server_clientname(client_id)); + + // Check which team the player should be on + if(gameobj->gametype == GAMETYPE_DM) + players[client_id].team = 0; + else + players[client_id].team = gameobj->getteam(client_id); +} + +void mods_client_drop(int client_id) +{ + char buf[512]; + sprintf(buf, "%s has left the game", server_clientname(client_id)); + send_chat(-1, -1, buf); + + dbg_msg("game", "leave player='%d:%s'", client_id, server_clientname(client_id)); + + gameobj->on_player_death(&players[client_id], 0, -1); + world->remove_entity(&players[client_id]); + world->core.players[client_id] = 0x0; + players[client_id].client_id = -1; +} + +void mods_message(int msg, int client_id) +{ + if(msg == MSG_SAY) + { + int team = msg_unpack_int(); + const char *text = msg_unpack_string(); + if(team) + team = players[client_id].team; + else + team = -1; + send_chat(client_id, team, text); + } + else if (msg == MSG_SETTEAM) + { + // Switch team on given client and kill/respawn him + players[client_id].set_team(msg_unpack_int()); + gameobj->on_player_info_change(&players[client_id]); + + // send all info to this client + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(players[i].client_id != -1) + send_info(i, -1); + } + } + else if (msg == MSG_CHANGEINFO || msg == MSG_STARTINFO) + { + const char *name = msg_unpack_string(); + const char *skin_name = msg_unpack_string(); + players[client_id].use_custom_color = msg_unpack_int(); + players[client_id].color_body = msg_unpack_int(); + players[client_id].color_feet = msg_unpack_int(); + + // check for invalid chars + const char *p = name; + while (*p) + { + if(*p < 32) + return; + p++; + } + + // + if(msg == MSG_CHANGEINFO && strcmp(name, server_clientname(client_id)) != 0) + { + char msg[256]; + sprintf(msg, "*** %s changed name to %s", server_clientname(client_id), name); + send_chat(-1, -1, msg); + } + + //send_set_name(client_id, players[client_id].name, name); + strncpy(players[client_id].skin_name, skin_name, 64); + server_setclientname(client_id, name); + + gameobj->on_player_info_change(&players[client_id]); + + if(msg == MSG_STARTINFO) + { + // send all info to this client + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(players[i].client_id != -1) + send_info(i, client_id); + } + + msg_pack_start(MSG_READY_TO_ENTER, MSGFLAG_VITAL); + msg_pack_end(); + server_send_msg(client_id); + } + + send_info(client_id, -1); + } + else if (msg == MSG_EMOTICON) + { + int emoteicon = msg_unpack_int(); + send_emoticon(client_id, emoteicon % 16); + } +} + +extern unsigned char internal_data[]; + +void mods_init() +{ + if(!data) /* only load once */ + data = load_data_from_memory(internal_data); + + col_init(32); + + world = new game_world; + players = new player[MAX_CLIENTS]; + + // select gametype + if(strcmp(config.sv_gametype, "ctf") == 0) + gameobj = new gameobject_ctf; + else if(strcmp(config.sv_gametype, "tdm") == 0) + gameobj = new gameobject_tdm; + else + gameobj = new gameobject_dm; + + // setup core world + for(int i = 0; i < MAX_CLIENTS; i++) + players[i].core.world = &world->core; + + // + int start, num; + map_get_type(MAPRES_ITEM, &start, &num); + + // TODO: this is way more complicated then it should be + for(int i = 0; i < num; i++) + { + mapres_item *it = (mapres_item *)map_get_item(start+i, 0, 0); + + int type = -1; + int subtype = 0; + + switch(it->type) + { + case ITEM_WEAPON_GUN: + type = POWERUP_WEAPON; + subtype = WEAPON_GUN; + break; + case ITEM_WEAPON_SHOTGUN: + type = POWERUP_WEAPON; + subtype = WEAPON_SHOTGUN; + break; + case ITEM_WEAPON_ROCKET: + type = POWERUP_WEAPON; + subtype = WEAPON_ROCKET; + break; + case ITEM_WEAPON_HAMMER: + type = POWERUP_WEAPON; + subtype = WEAPON_HAMMER; + break; + + case ITEM_HEALTH: + type = POWERUP_HEALTH; + break; + + case ITEM_ARMOR: + type = POWERUP_ARMOR; + break; + + case ITEM_NINJA: + if(config.sv_powerups) + { + type = POWERUP_NINJA; + subtype = WEAPON_NINJA; + } + break; + }; + + if(type != -1) + { + // LOL, the only new in the entire game code + // perhaps we can get rid of it. seems like a stupid thing to have + powerup *ppower = new powerup(type, subtype); + ppower->pos = vec2(it->x, it->y); + } + } + + if(gameobj->gametype == GAMETYPE_CTF) + { + } + + world->insert_entity(gameobj); + + + if(config.dbg_bots) + { + + for(int i = 0; i < config.dbg_bots ; i++) + { + mods_connected(MAX_CLIENTS-i-1); + mods_client_enter(MAX_CLIENTS-i-1); + if(gameobj->gametype != GAMETYPE_DM) + players[MAX_CLIENTS-i-1].team = i&1; + } + } +} + +void mods_shutdown() +{ + delete [] players; + delete gameobj; + delete world; + gameobj = 0; + players = 0; + world = 0; +} + +void mods_presnap() {} +void mods_postsnap() {} + +extern "C" const char *mods_net_version() { return TEEWARS_NETVERSION; } diff --git a/src/game/server/srv_common.cpp b/src/game/server/srv_common.cpp deleted file mode 100644 index 83754780..00000000 --- a/src/game/server/srv_common.cpp +++ /dev/null @@ -1,264 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include -#include "srv_common.h" -#include - -gameobject::gameobject() -: entity(OBJTYPE_GAME) -{ - // select gametype - if(strcmp(config.sv_gametype, "ctf") == 0) - { - gametype = GAMETYPE_CTF; - dbg_msg("game", "-- Capture The Flag --"); - } - else if(strcmp(config.sv_gametype, "tdm") == 0) - { - gametype = GAMETYPE_TDM; - dbg_msg("game", "-- Team Death Match --"); - } - else - { - gametype = GAMETYPE_DM; - dbg_msg("game", "-- Death Match --"); - } - - // - do_warmup(config.sv_warmup); - game_over_tick = -1; - sudden_death = 0; - round_start_tick = server_tick(); - round_count = 0; - is_teamplay = false; - teamscore[0] = 0; - teamscore[1] = 0; -} - -void gameobject::endround() -{ - if(warmup) // game can't end when we are running warmup - return; - - world->paused = true; - game_over_tick = server_tick(); - sudden_death = 0; -} - -void gameobject::resetgame() -{ - world->reset_requested = true; -} - -static bool is_separator(char c) -{ - return c == ';' || c == ' ' || c == ',' || c == '\t'; -} - -void gameobject::startround() -{ - resetgame(); - - round_start_tick = server_tick(); - sudden_death = 0; - game_over_tick = -1; - world->paused = false; - teamscore[0] = 0; - teamscore[1] = 0; - round_count++; -} - -void gameobject::cyclemap() -{ - if(!strlen(config.sv_maprotation)) - return; - // handle maprotation - char buf[512]; - const char *s = strstr(config.sv_maprotation, config.sv_map); - if(s == 0) - s = config.sv_maprotation; // restart rotation - else - { - s += strlen(config.sv_map); // skip this map - while(is_separator(s[0])) - s++; - if(s[0] == 0) - s = config.sv_maprotation; // restart rotation - } - - int i = 0; - for(; i < 512; i++) - { - buf[i] = s[i]; - if(is_separator(s[i]) || s[i] == 0) - { - buf[i] = 0; - break; - } - } - - i = 0; // skip spaces - while(is_separator(buf[i])) - i++; - - dbg_msg("game", "rotating map to %s", &buf[i]); - strcpy(config.sv_map, &buf[i]); -} - -void gameobject::post_reset() -{ - for(int i = 0; i < MAX_CLIENTS; i++) - { - if(players[i].client_id != -1) - players[i].respawn(); - } -} - -void gameobject::on_player_info_change(class player *p) -{ - const int team_colors[2] = {65387, 10223467}; - if(is_teamplay) - { - if(p->team >= 0 || p->team <= 1) - { - p->use_custom_color = 1; - p->color_body = team_colors[p->team]; - p->color_feet = team_colors[p->team]; - } - } -} - - -int gameobject::on_player_death(class player *victim, class player *killer, int weapon) -{ - // do scoreing - if(!killer) - return 0; - if(killer == victim) - victim->score--; // suicide - else - { - if(is_teamplay && victim->team == killer->team) - killer->score--; // teamkill - else - killer->score++; // normal kill - } - return 0; -} - -void gameobject::do_warmup(int seconds) -{ - warmup = seconds*SERVER_TICK_SPEED; -} - -bool gameobject::is_friendly_fire(int cid1, int cid2) -{ - if(cid1 == cid2) - return false; - - if(is_teamplay) - { - if(players[cid1].team == players[cid2].team) - return true; - } - - return false; -} - -void gameobject::tick() -{ - // do warmup - if(warmup) - { - warmup--; - if(!warmup) - startround(); - } - - if(game_over_tick != -1) - { - // game over.. wait for restart - if(server_tick() > game_over_tick+server_tickspeed()*10) - { - cyclemap(); - startround(); - } - } - - - // update browse info - int prog = -1; - if(config.sv_timelimit > 0) - prog = max(prog, (server_tick()-round_start_tick) * 100 / (config.sv_timelimit*server_tickspeed()*60)); - - if(config.sv_scorelimit) - { - if(is_teamplay) - { - prog = max(prog, (teamscore[0]*100)/config.sv_scorelimit); - prog = max(prog, (teamscore[1]*100)/config.sv_scorelimit); - } - else - { - for(int i = 0; i < MAX_CLIENTS; i++) - { - if(players[i].client_id != -1) - prog = max(prog, (players[i].score*100)/config.sv_scorelimit); - } - } - } - - if(warmup) - prog = -1; - - server_setbrowseinfo(gametype, prog); -} - -void gameobject::snap(int snapping_client) -{ - obj_game *game = (obj_game *)snap_new_item(OBJTYPE_GAME, 0, sizeof(obj_game)); - game->paused = world->paused; - game->game_over = game_over_tick==-1?0:1; - game->sudden_death = sudden_death; - - game->score_limit = config.sv_scorelimit; - game->time_limit = config.sv_timelimit; - game->round_start_tick = round_start_tick; - game->gametype = gametype; - - game->warmup = warmup; - - game->teamscore[0] = teamscore[0]; - game->teamscore[1] = teamscore[1]; -} - -int gameobject::getteam(int notthisid) -{ - int numplayers[2] = {0,0}; - for(int i = 0; i < MAX_CLIENTS; i++) - { - if(players[i].client_id != -1 && players[i].client_id != notthisid) - { - numplayers[players[i].team]++; - } - } - - return numplayers[0] > numplayers[1] ? 1 : 0; -} - -void gameobject::do_team_wincheck() -{ - if(game_over_tick == -1 && !warmup) - { - // check score win condition - if((config.sv_scorelimit > 0 && (teamscore[0] >= config.sv_scorelimit || teamscore[1] >= config.sv_scorelimit)) || - (config.sv_timelimit > 0 && (server_tick()-round_start_tick) >= config.sv_timelimit*server_tickspeed()*60)) - { - if(teamscore[0] != teamscore[1]) - endround(); - else - sudden_death = 1; - } - } -} - -gameobject *gameobj = 0; diff --git a/src/game/server/srv_common.h b/src/game/server/srv_common.h deleted file mode 100644 index b28d3066..00000000 --- a/src/game/server/srv_common.h +++ /dev/null @@ -1,327 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include "../game.h" -#include "data.h" - - -void create_sound_global(int sound, int target=-1); - -inline int cmask_all() { return -1; } -inline int cmask_one(int cid) { return 1<stand_pos = vec2(stand->x, stand->y); - f->pos = f->stand_pos; - flags[i] = f; - //dbg_msg("game", "flag at %f,%f", f->pos.x, f->pos.y); - } - else - { - // report massive failure - flags[i] = 0; - } - } - - is_teamplay = true; -} - -void gameobject_ctf::on_player_spawn(class player *p) -{ -} - -int gameobject_ctf::on_player_death(class player *victim, class player *killer, int weaponid) -{ - gameobject::on_player_death(victim, killer, weaponid); - int had_flag = 0; - - // drop flags - for(int fi = 0; fi < 2; fi++) - { - flag *f = flags[fi]; - if(f && f->carrying_player == killer) - had_flag |= 2; - if(f && f->carrying_player == victim) - { - create_sound_global(SOUND_CTF_DROP); - f->drop_tick = server_tick(); - f->carrying_player = 0; - f->vel = vec2(0,0); - - if(killer && killer->team != victim->team) - killer->score++; - - had_flag |= 1; - } - } - - return had_flag; -} - -void gameobject_ctf::tick() -{ - gameobject::tick(); - - do_team_wincheck(); - - // do flags - for(int fi = 0; fi < 2; fi++) - { - flag *f = flags[fi]; - - if(!f) - continue; - - // - if(f->carrying_player) - { - // update flag position - f->pos = f->carrying_player->pos; - - if(flags[fi^1]->at_stand) - { - if(distance(f->pos, flags[fi^1]->pos) < 24) - { - // CAPTURE! \o/ - teamscore[fi^1] += 100; - f->carrying_player->score += 5; - for(int i = 0; i < 2; i++) - flags[i]->reset(); - - dbg_msg("", "capture sound %d", SOUND_CTF_CAPTURE); - create_sound_global(SOUND_CTF_CAPTURE); - } - } - } - else - { - player *players[MAX_CLIENTS]; - int types[] = {OBJTYPE_PLAYER_CHARACTER}; - int num = world->find_entities(f->pos, 32.0f, (entity**)players, MAX_CLIENTS, types, 1); - for(int i = 0; i < num; i++) - { - if(players[i]->team == f->team) - { - // return the flag - if(!f->at_stand) - { - players[i]->score += 1; - create_sound_global(SOUND_CTF_RETURN); - f->reset(); - } - } - else - { - // take the flag - if(f->at_stand) - teamscore[fi^1]++; - f->at_stand = 0; - f->carrying_player = players[i]; - f->carrying_player->score += 1; - create_sound_global(SOUND_CTF_GRAB); - break; - } - } - - if(!f->carrying_player && !f->at_stand) - { - if(server_tick() > f->drop_tick + SERVER_TICK_SPEED*30) - { - create_sound_global(SOUND_CTF_RETURN); - f->reset(); - } - else - { - f->vel.y += gravity; - move_box(&f->pos, &f->vel, vec2(f->phys_size, f->phys_size), 0.5f); - } - } - } - } -} - -// Flag -flag::flag(int _team) -: entity(OBJTYPE_FLAG) -{ - team = _team; - proximity_radius = phys_size; - carrying_player = 0x0; - - reset(); - - // TODO: should this be done here? - world->insert_entity(this); -} - -void flag::reset() -{ - carrying_player = 0; - at_stand = 1; - pos = stand_pos; - vel = vec2(0,0); -} - -void flag::snap(int snapping_client) -{ - obj_flag *flag = (obj_flag *)snap_new_item(OBJTYPE_FLAG, team, sizeof(obj_flag)); - flag->x = (int)pos.x; - flag->y = (int)pos.y; - flag->team = team; - flag->carried_by = -1; - - if(at_stand) - flag->carried_by = -2; - else if(carrying_player) - flag->carried_by = carrying_player->client_id; -} diff --git a/src/game/server/srv_ctf.h b/src/game/server/srv_ctf.h deleted file mode 100644 index 02acef37..00000000 --- a/src/game/server/srv_ctf.h +++ /dev/null @@ -1,33 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -// game object -class gameobject_ctf : public gameobject -{ -public: - class flag *flags[2]; - - gameobject_ctf(); - virtual void tick(); - - virtual void on_player_spawn(class player *p); - virtual int on_player_death(class player *victim, class player *killer, int weapon); -}; - -// TODO: move to seperate file -class flag : public entity -{ -public: - static const int phys_size = 14; - player *carrying_player; - vec2 vel; - vec2 stand_pos; - - int team; - int at_stand; - int drop_tick; - - flag(int _team); - - virtual void reset(); - virtual void snap(int snapping_client); -}; diff --git a/src/game/server/srv_dm.cpp b/src/game/server/srv_dm.cpp deleted file mode 100644 index d5720856..00000000 --- a/src/game/server/srv_dm.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include -#include "srv_common.h" -#include "srv_dm.h" - -void gameobject_dm::tick() -{ - if(game_over_tick == -1) - { - // game is running - - // gather some stats - int topscore = 0; - int topscore_count = 0; - for(int i = 0; i < MAX_CLIENTS; i++) - { - if(players[i].client_id != -1) - { - if(players[i].score > topscore) - { - topscore = players[i].score; - topscore_count = 1; - } - else if(players[i].score == topscore) - topscore_count++; - } - } - - // check score win condition - if((config.sv_scorelimit > 0 && topscore >= config.sv_scorelimit) || - (config.sv_timelimit > 0 && (server_tick()-round_start_tick) >= config.sv_timelimit*server_tickspeed()*60)) - { - if(topscore_count == 1) - endround(); - else - sudden_death = 1; - } - } - - gameobject::tick(); -} - diff --git a/src/game/server/srv_dm.h b/src/game/server/srv_dm.h deleted file mode 100644 index 96bff3ae..00000000 --- a/src/game/server/srv_dm.h +++ /dev/null @@ -1,7 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -// game object -class gameobject_dm : public gameobject -{ -public: - virtual void tick(); -}; diff --git a/src/game/server/srv_tdm.cpp b/src/game/server/srv_tdm.cpp deleted file mode 100644 index bcae397f..00000000 --- a/src/game/server/srv_tdm.cpp +++ /dev/null @@ -1,32 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include -#include "srv_common.h" -#include "srv_tdm.h" - -gameobject_tdm::gameobject_tdm() -{ - is_teamplay = true; -} - - -int gameobject_tdm::on_player_death(class player *victim, class player *killer, int weapon) -{ - gameobject::on_player_death(victim, killer, weapon); - - if(weapon >= 0) - { - // do team scoring - if(killer == victim) - teamscore[killer->team&1]--; // klant arschel - else - teamscore[killer->team&1]++; // good shit - } - return 0; -} - -void gameobject_tdm::tick() -{ - do_team_wincheck(); - - gameobject::tick(); -} diff --git a/src/game/server/srv_tdm.h b/src/game/server/srv_tdm.h deleted file mode 100644 index 70b4646e..00000000 --- a/src/game/server/srv_tdm.h +++ /dev/null @@ -1,10 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -// game object -class gameobject_tdm : public gameobject -{ -public: - gameobject_tdm(); - - int on_player_death(class player *victim, class player *killer, int weapon); - virtual void tick(); -}; diff --git a/src/game/version.h b/src/game/version.h deleted file mode 100644 index a2167ecd..00000000 --- a/src/game/version.h +++ /dev/null @@ -1,4 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include "nethash.c" -#define TEEWARS_VERSION "0.3.0-dev" -#define TEEWARS_NETVERSION "0.3 " TEEWARS_NETVERSION_HASH diff --git a/src/game/vmath.h b/src/game/vmath.h deleted file mode 100644 index 65f94776..00000000 --- a/src/game/vmath.h +++ /dev/null @@ -1,182 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#ifndef BASE_VMATH_H -#define BASE_VMATH_H - -//#include - -// ------------------------------------ - -template -class vector2_base -{ -public: - union { T x,u; }; - union { T y,v; }; - - vector2_base() {} - vector2_base(float nx, float ny) - { - x = nx; - y = ny; - } - - vector2_base operator -() const { return vector2_base(-x, -y); } - vector2_base operator -(const vector2_base &v) const { return vector2_base(x-v.x, y-v.y); } - vector2_base operator +(const vector2_base &v) const { return vector2_base(x+v.x, y+v.y); } - vector2_base operator *(const T v) const { return vector2_base(x*v, y*v); } - - const vector2_base &operator =(const vector2_base &v) { x = v.x; y = v.y; return *this; } - - const vector2_base &operator +=(const vector2_base &v) { x += v.x; y += v.y; return *this; } - const vector2_base &operator -=(const vector2_base &v) { x -= v.x; y -= v.y; return *this; } - const vector2_base &operator *=(const T v) { x *= v; y *= v; return *this; } - - bool operator ==(const vector2_base &v) const { return x == v.x && y == v.y; } //TODO: do this with an eps instead - - operator const T* () { return &x; } -}; - - -template -inline T length(const vector2_base &a) -{ - return sqrtf(a.x*a.x + a.y*a.y); -} - -template -inline T distance(const vector2_base a, const vector2_base &b) -{ - return length(a-b); -} - -template -inline T dot(const vector2_base a, const vector2_base &b) -{ - return a.x*b.x + a.y*b.y; -} - -template -inline vector2_base normalize(const vector2_base &v) -{ - T l = (T)(1.0f/sqrtf(v.x*v.x + v.y*v.y)); - return vector2_base(v.x*l, v.y*l); -} - -typedef vector2_base vec2; -typedef vector2_base bvec2; -typedef vector2_base ivec2; - -// ------------------------------------ -template -class vector3_base -{ -public: - union { T x,r,h; }; - union { T y,g,s; }; - union { T z,b,v,l; }; - - vector3_base() {} - vector3_base(float nx, float ny, float nz) - { - x = nx; - y = ny; - z = nz; - } - - const vector3_base &operator =(const vector3_base &v) { x = v.x; y = v.y; z = v.z; return *this; } - - vector3_base operator -(const vector3_base &v) const { return vector3_base(x-v.x, y-v.y, z-v.z); } - vector3_base operator -() const { return vector3_base(-x, -y, -z); } - vector3_base operator +(const vector3_base &v) const { return vector3_base(x+v.x, y+v.y, z+v.z); } - vector3_base operator *(const T v) const { return vector3_base(x*v, y*v, z*v); } - vector3_base operator *(const vector3_base &v) const { return vector3_base(x*v.x, y*v.y, z*v.z); } - vector3_base operator /(const T v) const { return vector3_base(x/v, y/v, z/v); } - - const vector3_base &operator +=(const vector3_base &v) { x += v.x; y += v.y; z += v.z; return *this; } - const vector3_base &operator -=(const vector3_base &v) { x -= v.x; y -= v.y; z -= v.z; return *this; } - const vector3_base &operator *=(const T v) { x *= v; y *= v; z *= v; return *this; } - - bool operator ==(const vector3_base &v) const { return x == v.x && y == v.y && z == v.z; } //TODO: do this with an eps instead - - operator const T* () { return &x; } -}; - -template -inline T length(const vector3_base &a) -{ - return sqrtf(a.x*a.x + a.y*a.y + a.z*a.z); -} - -template -inline T distance(const vector3_base &a, const vector3_base &b) -{ - return length(a-b); -} - -template -inline T dot(const vector3_base &a, const vector3_base &b) -{ - return a.x*b.x + a.y*b.y + a.z*b.z; -} - -template -inline vector3_base normalize(const vector3_base &v) -{ - T l = (T)(1.0f/sqrtf(v.x*v.x + v.y*v.y + v.z*v.z)); - return vector3_base(v.x*l, v.y*l, v.z*l); -} - -template -inline vector3_base cross(const vector3_base &a, const vector3_base &b) -{ - return vector3_base( - a.y*b.z - a.z*b.y, - a.z*b.x - a.x*b.z, - a.x*b.y - a.y*b.x); -} - -typedef vector3_base vec3; -typedef vector3_base bvec3; -typedef vector3_base ivec3; - -// ------------------------------------ - -template -class vector4_base -{ -public: - union { T x,r; }; - union { T y,g; }; - union { T z,b; }; - union { T w,a; }; - - vector4_base() {} - vector4_base(float nx, float ny, float nz, float nw) - { - x = nx; - y = ny; - z = nz; - w = nw; - } - - vector4_base operator +(const vector4_base &v) const { return vector4_base(x+v.x, y+v.y, z+v.z, w+v.w); } - vector4_base operator -(const vector4_base &v) const { return vector4_base(x-v.x, y-v.y, z-v.z, w-v.w); } - vector4_base operator -() const { return vector4_base(-x, -y, -z, -w); } - vector4_base operator *(const T v) const { return vector4_base(x*v, y*v, z*v, w*v); } - - const vector4_base &operator =(const vector4_base &v) { x = v.x; y = v.y; z = v.z; w = v.w; return *this; } - - const vector4_base &operator +=(const vector4_base &v) { x += v.x; y += v.y; z += v.z; w += v.w; return *this; } - const vector4_base &operator -=(const vector4_base &v) { x -= v.x; y -= v.y; z -= v.z; w -= v.w; return *this; } - const vector4_base &operator *=(const T v) { x *= v; y *= v; z *= v; w *= v; return *this; } - - bool operator ==(const vector4_base &v) const { return x == v.x && y == v.y && z == v.z && w == v.w; } //TODO: do this with an eps instead - - operator const T* () { return &x; } -}; - -typedef vector4_base vec4; -typedef vector4_base bvec4; -typedef vector4_base ivec4; - -#endif diff --git a/src/mastersrv/mastersrv.cpp b/src/mastersrv/mastersrv.cpp index cf32263f..aa799919 100644 --- a/src/mastersrv/mastersrv.cpp +++ b/src/mastersrv/mastersrv.cpp @@ -2,8 +2,8 @@ #include extern "C" { - #include - #include + #include + #include } #include "mastersrv.h" diff --git a/src/tools/crapnet.cpp b/src/tools/crapnet.cpp index 0513d484..1321c67f 100644 --- a/src/tools/crapnet.cpp +++ b/src/tools/crapnet.cpp @@ -1,5 +1,5 @@ /* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include +#include #include diff --git a/src/tools/fake_server.c b/src/tools/fake_server.c index 31e6006d..c81dfaaa 100644 --- a/src/tools/fake_server.c +++ b/src/tools/fake_server.c @@ -1,7 +1,7 @@ /* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include -#include -#include +#include +#include +#include #include #include #include diff --git a/src/tools/map_resave.c b/src/tools/map_resave.c index 69da79e8..37e00904 100644 --- a/src/tools/map_resave.c +++ b/src/tools/map_resave.c @@ -1,5 +1,5 @@ /* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include +#include int main(int argc, char **argv) { -- cgit 1.4.1