diff options
| author | Magnus Auvinen <magnus.auvinen@gmail.com> | 2008-10-06 18:05:01 +0000 |
|---|---|---|
| committer | Magnus Auvinen <magnus.auvinen@gmail.com> | 2008-10-06 18:05:01 +0000 |
| commit | 12472ef7f405f5e8eb620059cbf95926a458538a (patch) | |
| tree | 712cc453e491ff46c96b48785a94093b1d17cb1f /src/game | |
| parent | d1b55351ccc2252917ad494b74bb6ad562df34ce (diff) | |
| download | zcatch-12472ef7f405f5e8eb620059cbf95926a458538a.tar.gz zcatch-12472ef7f405f5e8eb620059cbf95926a458538a.zip | |
major update. continued on ban support. added demo recording (client and server side). added demo player. added demo menu. demos have some quirks and file size optimizations havn't been done yet. some interface tweaks
Diffstat (limited to 'src/game')
23 files changed, 657 insertions, 89 deletions
diff --git a/src/game/client/clienthooks.cpp b/src/game/client/clienthooks.cpp index 88a7722a..1611b00d 100644 --- a/src/game/client/clienthooks.cpp +++ b/src/game/client/clienthooks.cpp @@ -17,6 +17,7 @@ extern "C" void modc_init() { gameclient.on_init(); } extern "C" void modc_connected() { gameclient.on_connected(); } extern "C" void modc_predict() { gameclient.on_predict(); } extern "C" void modc_newsnapshot() { gameclient.on_snapshot(); } +extern "C" void modc_recordkeyframe() { gameclient.on_recordkeyframe(); } extern "C" int modc_snap_input(int *data) { return gameclient.on_snapinput(data); } extern "C" void modc_statechange(int state, int old) { gameclient.on_statechange(state, old); } extern "C" void modc_render() { gameclient.on_render(); } diff --git a/src/game/client/component.hpp b/src/game/client/component.hpp index f03165d2..3d5edb86 100644 --- a/src/game/client/component.hpp +++ b/src/game/client/component.hpp @@ -18,6 +18,7 @@ public: virtual void on_save() {}; virtual void on_reset() {}; virtual void on_render() {}; + virtual void on_mapload() {}; virtual void on_message(int msg, void *rawmsg) {} virtual bool on_mousemove(float x, float y) { return false; } virtual bool on_input(INPUT_EVENT e) { return false; } diff --git a/src/game/client/components/hud.cpp b/src/game/client/components/hud.cpp index 86ce6257..2b1c1663 100644 --- a/src/game/client/components/hud.cpp +++ b/src/game/client/components/hud.cpp @@ -305,7 +305,8 @@ void HUD::on_render() render_goals(); render_fps(); - render_connectionwarning(); + if(client_state() != CLIENTSTATE_DEMOPLAYBACK) + render_connectionwarning(); render_tunewarning(); render_teambalancewarning(); render_voting(); diff --git a/src/game/client/components/mapimages.cpp b/src/game/client/components/mapimages.cpp index 9dc2447a..7f839da0 100644 --- a/src/game/client/components/mapimages.cpp +++ b/src/game/client/components/mapimages.cpp @@ -8,7 +8,7 @@ MAPIMAGES::MAPIMAGES() count = 0; } -void MAPIMAGES::on_reset() +void MAPIMAGES::on_mapload() { // unload all textures for(int i = 0; i < count; i++) diff --git a/src/game/client/components/mapimages.hpp b/src/game/client/components/mapimages.hpp index e1e0063d..cba46033 100644 --- a/src/game/client/components/mapimages.hpp +++ b/src/game/client/components/mapimages.hpp @@ -9,7 +9,7 @@ public: int get(int index) const { return textures[index]; } int num() const { return count; } - - virtual void on_reset(); + + virtual void on_mapload(); }; diff --git a/src/game/client/components/maplayers.cpp b/src/game/client/components/maplayers.cpp index e1518036..c6fea413 100644 --- a/src/game/client/components/maplayers.cpp +++ b/src/game/client/components/maplayers.cpp @@ -50,7 +50,7 @@ static void envelope_eval(float time_offset, int env, float *channels) void MAPLAYERS::on_render() { - if(client_state() != CLIENTSTATE_ONLINE) + if(client_state() != CLIENTSTATE_ONLINE && client_state() != CLIENTSTATE_DEMOPLAYBACK) return; vec2 center = gameclient.camera->center; diff --git a/src/game/client/components/menus.cpp b/src/game/client/components/menus.cpp index b1043870..f71f0d96 100644 --- a/src/game/client/components/menus.cpp +++ b/src/game/client/components/menus.cpp @@ -32,6 +32,11 @@ vec4 MENUS::color_tabbar_active; vec4 MENUS::color_tabbar_inactive_ingame; vec4 MENUS::color_tabbar_active_ingame; + +float MENUS::button_height = 25.0f; +float MENUS::listheader_height = 16.0f; +float MENUS::fontmod_height = 0.8f; + INPUT_EVENT MENUS::inputevents[MAX_INPUTEVENTS]; int MENUS::num_inputevents; @@ -84,9 +89,10 @@ MENUS::MENUS() menu_active = true; num_inputevents = 0; - last_input = time_get(); + demos = 0; + num_demos = 0; - button_height = 25.0f; + last_input = time_get(); } vec4 MENUS::button_color_mul(const void *id) @@ -111,22 +117,27 @@ void MENUS::ui_draw_browse_icon(int what, const RECT *r) void MENUS::ui_draw_menu_button(const void *id, const char *text, int checked, const RECT *r, const void *extra) { ui_draw_rect(r, vec4(1,1,1,0.5f)*button_color_mul(id), CORNER_ALL, 5.0f); - ui_do_label(r, text, 18.0f, 0); + ui_do_label(r, text, r->h*fontmod_height, 0); } void MENUS::ui_draw_keyselect_button(const void *id, const char *text, int checked, const RECT *r, const void *extra) { ui_draw_rect(r, vec4(1,1,1,0.5f)*button_color_mul(id), CORNER_ALL, 5.0f); - ui_do_label(r, text, 14.0f, 0); + ui_do_label(r, text, r->h*fontmod_height, 0); } void MENUS::ui_draw_menu_tab_button(const void *id, const char *text, int checked, const RECT *r, const void *extra) { + int corners = CORNER_T; + vec4 colormod(1,1,1,1); + if(extra) + corners = *(int *)extra; + if(checked) - ui_draw_rect(r, color_tabbar_active, CORNER_T, 10.0f); + ui_draw_rect(r, color_tabbar_active, corners, 10.0f); else - ui_draw_rect(r, color_tabbar_inactive, CORNER_T, 10.0f); - ui_do_label(r, text, 22.0f, 0); + ui_draw_rect(r, color_tabbar_inactive, corners, 10.0f); + ui_do_label(r, text, r->h*fontmod_height, 0); } @@ -136,7 +147,7 @@ void MENUS::ui_draw_settings_tab_button(const void *id, const char *text, int ch ui_draw_rect(r, color_tabbar_active, CORNER_R, 10.0f); else ui_draw_rect(r, color_tabbar_inactive, CORNER_R, 10.0f); - ui_do_label(r, text, 20.0f, 0); + ui_do_label(r, text, r->h*fontmod_height, 0); } void MENUS::ui_draw_grid_header(const void *id, const char *text, int checked, const RECT *r, const void *extra) @@ -145,7 +156,7 @@ void MENUS::ui_draw_grid_header(const void *id, const char *text, int checked, c ui_draw_rect(r, vec4(1,1,1,0.5f), CORNER_T, 5.0f); RECT t; ui_vsplit_l(r, 5.0f, 0, &t); - ui_do_label(&t, text, 14.0f, -1); + ui_do_label(&t, text, r->h*fontmod_height, -1); } void MENUS::ui_draw_list_row(const void *id, const char *text, int checked, const RECT *r, const void *extra) @@ -156,7 +167,7 @@ void MENUS::ui_draw_list_row(const void *id, const char *text, int checked, cons ui_margin(&sr, 1.5f, &sr); ui_draw_rect(&sr, vec4(1,1,1,0.5f), CORNER_ALL, 4.0f); } - ui_do_label(r, text, 14.0f, -1); + ui_do_label(r, text, r->h*fontmod_height, -1); } void MENUS::ui_draw_checkbox_common(const void *id, const char *text, const char *boxtext, const RECT *r) @@ -171,8 +182,8 @@ void MENUS::ui_draw_checkbox_common(const void *id, const char *text, const char ui_margin(&c, 2.0f, &c); ui_draw_rect(&c, vec4(1,1,1,0.25f)*button_color_mul(id), CORNER_ALL, 3.0f); c.y += 2; - ui_do_label(&c, boxtext, 12.0f, 0); - ui_do_label(&t, text, 14.0f, -1); + ui_do_label(&c, boxtext, r->h*fontmod_height*0.75f, 0); + ui_do_label(&t, text, r->h*fontmod_height*0.8f, -1); } void MENUS::ui_draw_checkbox(const void *id, const char *text, int checked, const RECT *r, const void *extra) @@ -489,31 +500,43 @@ int MENUS::render_menubar(RECT r) ui_vsplit_l(&box, 30.0f, 0, &box); } - ui_vsplit_l(&box, 110.0f, &button, &box); + ui_vsplit_l(&box, 100.0f, &button, &box); static int internet_button=0; - if (ui_do_button(&internet_button, "Internet", active_page==PAGE_INTERNET, &button, ui_draw_menu_tab_button, 0)) + int corners = CORNER_TL; + if (ui_do_button(&internet_button, "Internet", active_page==PAGE_INTERNET, &button, ui_draw_menu_tab_button, &corners)) { client_serverbrowse_refresh(BROWSETYPE_INTERNET); new_page = PAGE_INTERNET; } - ui_vsplit_l(&box, 4.0f, 0, &box); - ui_vsplit_l(&box, 90.0f, &button, &box); + //ui_vsplit_l(&box, 4.0f, 0, &box); + ui_vsplit_l(&box, 80.0f, &button, &box); static int lan_button=0; - if (ui_do_button(&lan_button, "LAN", active_page==PAGE_LAN, &button, ui_draw_menu_tab_button, 0)) + corners = 0; + if (ui_do_button(&lan_button, "LAN", active_page==PAGE_LAN, &button, ui_draw_menu_tab_button, &corners)) { client_serverbrowse_refresh(BROWSETYPE_LAN); new_page = PAGE_LAN; } - ui_vsplit_l(&box, 4.0f, 0, &box); - ui_vsplit_l(&box, 120.0f, &button, &box); + //ui_vsplit_l(&box, 4.0f, 0, &box); + ui_vsplit_l(&box, 110.0f, &button, &box); static int favorites_button=0; - if (ui_do_button(&favorites_button, "Favorites", active_page==PAGE_FAVORITES, &button, ui_draw_menu_tab_button, 0)) + corners = CORNER_TR; + if (ui_do_button(&favorites_button, "Favorites", active_page==PAGE_FAVORITES, &button, ui_draw_menu_tab_button, &corners)) { client_serverbrowse_refresh(BROWSETYPE_FAVORITES); new_page = PAGE_FAVORITES; } + + ui_vsplit_l(&box, 4.0f*5, 0, &box); + ui_vsplit_l(&box, 100.0f, &button, &box); + static int demos_button=0; + if (ui_do_button(&demos_button, "Demos", active_page==PAGE_DEMOS, &button, ui_draw_menu_tab_button, 0)) + { + //client_serverbrowse_refresh(BROWSETYPE_FAVORITES); + new_page = PAGE_DEMOS; + } } else { @@ -547,13 +570,13 @@ int MENUS::render_menubar(RECT r) ui_vsplit_r(&box, 30.0f, &box, 0); */ - ui_vsplit_r(&box, 110.0f, &box, &button); + ui_vsplit_r(&box, 90.0f, &box, &button); static int quit_button=0; if (ui_do_button(&quit_button, "Quit", 0, &button, ui_draw_menu_tab_button, 0)) popup = POPUP_QUIT; ui_vsplit_r(&box, 10.0f, &box, &button); - ui_vsplit_r(&box, 110.0f, &box, &button); + ui_vsplit_r(&box, 120.0f, &box, &button); static int settings_button=0; if (ui_do_button(&settings_button, "Settings", active_page==PAGE_SETTINGS, &button, ui_draw_menu_tab_button, 0)) new_page = PAGE_SETTINGS; @@ -663,7 +686,7 @@ int MENUS::render() if(popup == POPUP_NONE) { // do tab bar - ui_hsplit_t(&screen, 26.0f, &tab_bar, &main_view); + ui_hsplit_t(&screen, 24.0f, &tab_bar, &main_view); ui_vmargin(&tab_bar, 20.0f, &tab_bar); render_menubar(tab_bar); @@ -691,6 +714,8 @@ int MENUS::render() render_serverbrowser(main_view); else if(config.ui_page == PAGE_LAN) render_serverbrowser(main_view); + else if(config.ui_page == PAGE_DEMOS) + render_demolist(main_view); else if(config.ui_page == PAGE_FAVORITES) render_serverbrowser(main_view); else if(config.ui_page == PAGE_SETTINGS) @@ -922,7 +947,7 @@ void MENUS::on_statechange(int new_state, int old_state) popup = POPUP_CONNECTING; else if(new_state == CLIENTSTATE_CONNECTING) popup = POPUP_CONNECTING; - else if (new_state == CLIENTSTATE_ONLINE) + else if (new_state == CLIENTSTATE_ONLINE || new_state == CLIENTSTATE_DEMOPLAYBACK) { popup = POPUP_NONE; menu_active = false; @@ -931,17 +956,19 @@ void MENUS::on_statechange(int new_state, int old_state) void MENUS::on_render() { - if(client_state() != CLIENTSTATE_ONLINE) + if(client_state() != CLIENTSTATE_ONLINE && client_state() != CLIENTSTATE_DEMOPLAYBACK) menu_active = true; + + if(client_state() == CLIENTSTATE_DEMOPLAYBACK) + { + RECT screen = *ui_screen(); + gfx_mapscreen(screen.x, screen.y, screen.w, screen.h); + render_demoplayer(screen); + } if(!menu_active) return; - - - if(inp_key_down('M')) button_height += 1.0f; - if(inp_key_down('N')) button_height -= 1.0f; - // 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); @@ -1002,8 +1029,8 @@ void MENUS::on_render() } else { - //render_background(); - render(); + if(client_state() != CLIENTSTATE_DEMOPLAYBACK) + render(); // render cursor gfx_texture_set(data->images[IMAGE_CURSOR].id); @@ -1024,6 +1051,7 @@ void MENUS::on_render() gfx_text_set_cursor(&cursor, 10, 10, 10, TEXTFLAG_RENDER); gfx_text_ex(&cursor, buf, -1); } + } num_inputevents = 0; diff --git a/src/game/client/components/menus.hpp b/src/game/client/components/menus.hpp index 8aae925f..51090b93 100644 --- a/src/game/client/components/menus.hpp +++ b/src/game/client/components/menus.hpp @@ -15,6 +15,9 @@ class MENUS : public COMPONENT static vec4 button_color_mul(const void *id); + + static void ui_draw_demoplayer_button(const void *id, const char *text, int checked, const RECT *r, const void *extra); + static void ui_draw_browse_icon(int what, const RECT *r); static void ui_draw_menu_button(const void *id, const char *text, int checked, const RECT *r, const void *extra); static void ui_draw_keyselect_button(const void *id, const char *text, int checked, const RECT *r, const void *extra); @@ -33,6 +36,19 @@ class MENUS : public COMPONENT static int ui_do_key_reader(void *id, const RECT *rect, int key); static void ui_do_getbuttons(int start, int stop, RECT view); + struct LISTBOXITEM + { + int visible; + int selected; + RECT rect; + }; + + static void ui_do_listbox_start(void *id, const RECT *rect, float row_height, const char *title, int num_items, int selected_index); + static LISTBOXITEM ui_do_listbox_nextitem(void *id); + static int ui_do_listbox_end(); + + //static void demolist_listdir_callback(const char *name, int is_dir, void *user); + //static void demolist_list_callback(const RECT *rect, int index, void *user); enum { @@ -53,6 +69,7 @@ class MENUS : public COMPONENT PAGE_INTERNET, PAGE_LAN, PAGE_FAVORITES, + PAGE_DEMOS, PAGE_SETTINGS, PAGE_SYSTEM, }; @@ -71,7 +88,9 @@ class MENUS : public COMPONENT static int num_inputevents; // some settings - float button_height; + static float button_height; + static float listheader_height; + static float fontmod_height; // for graphic settings bool need_restart; @@ -79,6 +98,20 @@ class MENUS : public COMPONENT // for call vote int callvote_selectedplayer; int callvote_selectedmap; + + // demo + struct DEMOITEM + { + char filename[512]; + char name[256]; + }; + + DEMOITEM *demos; + int num_demos; + + void demolist_populate(); + static void demolist_count_callback(const char *name, int is_dir, void *user); + static void demolist_fetch_callback(const char *name, int is_dir, void *user); // found in menus.cpp int render(); @@ -87,6 +120,10 @@ class MENUS : public COMPONENT int render_menubar(RECT r); void render_news(RECT main_view); + // found in menus_demo.cpp + void render_demoplayer(RECT main_view); + void render_demolist(RECT main_view); + // found in menus_ingame.cpp void render_game(RECT main_view); void render_serverinfo(RECT main_view); diff --git a/src/game/client/components/menus_browser.cpp b/src/game/client/components/menus_browser.cpp index a4e730e6..3cba5311 100644 --- a/src/game/client/components/menus_browser.cpp +++ b/src/game/client/components/menus_browser.cpp @@ -21,7 +21,7 @@ void MENUS::render_serverbrowser_serverlist(RECT view) RECT headers; RECT status; - ui_hsplit_t(&view, 16.0f, &headers, &view); + ui_hsplit_t(&view, listheader_height, &headers, &view); ui_hsplit_b(&view, 20.0f, &view, &status); // split of the scrollbar @@ -634,7 +634,7 @@ void MENUS::render_serverbrowser(RECT main_view) ui_vmargin(&button, 2.0f, &button); //ui_vmargin(&button, 2.0f, &button); static int join_button = 0; - if(ui_do_button(&join_button, "Connect", 0, &button, ui_draw_menu_button, 0) || inp_key_down(KEY_ENTER)) + if(ui_do_button(&join_button, "Connect", 0, &button, ui_draw_menu_button, 0)) // || inp_key_down(KEY_ENTER)) client_connect(config.ui_server_address); ui_hsplit_b(&button_box, 5.0f, &button_box, &button); diff --git a/src/game/client/components/menus_demo.cpp b/src/game/client/components/menus_demo.cpp new file mode 100644 index 00000000..81717bf7 --- /dev/null +++ b/src/game/client/components/menus_demo.cpp @@ -0,0 +1,390 @@ + +#include <base/math.hpp> + +//#include <string.h> // strcmp, strlen, strncpy +//#include <stdlib.h> // atoi + +#include <engine/e_client_interface.h> +#include <game/client/gc_render.hpp> +#include <game/client/gameclient.hpp> + +//#include <game/generated/g_protocol.hpp> +//#include <game/generated/gc_data.hpp> + +#include <game/client/ui.hpp> +//#include <game/client/gameclient.hpp> +//#include <game/client/animstate.hpp> + +#include "menus.hpp" + +void MENUS::ui_draw_demoplayer_button(const void *id, const char *text, int checked, const RECT *r, const void *extra) +{ + ui_draw_rect(r, vec4(1,1,1, checked ? 0.10f : 0.5f)*button_color_mul(id), CORNER_ALL, 5.0f); + ui_do_label(r, text, 14.0f, 0); +} + +void MENUS::render_demoplayer(RECT main_view) +{ + const DEMOPLAYBACK_INFO *info = client_demoplayer_getinfo(); + + const float seekbar_height = 15.0f; + const float buttonbar_height = 20.0f; + const float margins = 5.0f; + float total_height; + + if(menu_active) + total_height = seekbar_height+buttonbar_height+margins*3; + else + total_height = seekbar_height+margins*2; + + ui_hsplit_b(&main_view, total_height, 0, &main_view); + ui_vsplit_l(&main_view, 250.0f, 0, &main_view); + ui_vsplit_r(&main_view, 250.0f, &main_view, 0); + + ui_draw_rect(&main_view, color_tabbar_active, CORNER_T, 10.0f); + + ui_margin(&main_view, 5.0f, &main_view); + + RECT seekbar, buttonbar; + + if(menu_active) + { + ui_hsplit_t(&main_view, seekbar_height, &seekbar, &buttonbar); + ui_hsplit_t(&buttonbar, margins, 0, &buttonbar); + } + else + seekbar = main_view; + + // do seekbar + { + static int seekbar_id = 0; + void *id = &seekbar_id; + char buffer[128]; + + ui_draw_rect(&seekbar, vec4(0,0,0,0.5f), CORNER_ALL, 5.0f); + + int current_tick = info->current_tick - info->first_tick; + int total_ticks = info->last_tick - info->first_tick; + + float amount = current_tick/(float)total_ticks; + + RECT filledbar = seekbar; + filledbar.w = 10.0f + (filledbar.w-10.0f)*amount; + + ui_draw_rect(&filledbar, vec4(1,1,1,0.5f), CORNER_ALL, 5.0f); + + str_format(buffer, sizeof(buffer), "%d:%02d / %d:%02d", + current_tick/SERVER_TICK_SPEED/60, (current_tick/SERVER_TICK_SPEED)%60, + total_ticks/SERVER_TICK_SPEED/60, (total_ticks/SERVER_TICK_SPEED)%60); + ui_do_label(&seekbar, buffer, seekbar.h*0.70f, 0); + + // do the logic + int inside = ui_mouse_inside(&seekbar); + + if(ui_active_item() == id) + { + if(!ui_mouse_button(0)) + ui_set_active_item(0); + else + { + float amount = (ui_mouse_x()-seekbar.x)/(float)seekbar.w; + if(amount > 0 && amount < 1.0f) + { + gameclient.on_reset(); + client_demoplayer_setpos(amount); + } + } + } + else if(ui_hot_item() == id) + { + if(ui_mouse_button(0)) + ui_set_active_item(id); + } + + if(inside) + ui_set_hot_item(id); + } + + + if(menu_active) + { + // do buttons + RECT button; + + // pause button + ui_vsplit_l(&buttonbar, buttonbar_height, &button, &buttonbar); + static int pause_button = 0; + if(ui_do_button(&pause_button, "| |", info->paused, &button, ui_draw_demoplayer_button, 0)) + client_demoplayer_setpause(!info->paused); + + // play button + ui_vsplit_l(&buttonbar, margins, 0, &buttonbar); + ui_vsplit_l(&buttonbar, buttonbar_height, &button, &buttonbar); + static int play_button = 0; + if(ui_do_button(&play_button, ">", !info->paused, &button, ui_draw_demoplayer_button, 0)) + { + client_demoplayer_setpause(0); + client_demoplayer_setspeed(1.0f); + } + + // slowdown + ui_vsplit_l(&buttonbar, margins, 0, &buttonbar); + ui_vsplit_l(&buttonbar, buttonbar_height, &button, &buttonbar); + static int slowdown_button = 0; + if(ui_do_button(&slowdown_button, "<<", 0, &button, ui_draw_demoplayer_button, 0)) + { + if(info->speed > 4.0f) client_demoplayer_setspeed(4.0f); + else if(info->speed > 2.0f) client_demoplayer_setspeed(2.0f); + else if(info->speed > 1.0f) client_demoplayer_setspeed(1.0f); + else if(info->speed > 0.5f) client_demoplayer_setspeed(0.5f); + else client_demoplayer_setspeed(0.05f); + } + + // fastforward + ui_vsplit_l(&buttonbar, margins, 0, &buttonbar); + ui_vsplit_l(&buttonbar, buttonbar_height, &button, &buttonbar); + static int fastforward_button = 0; + if(ui_do_button(&fastforward_button, ">>", 0, &button, ui_draw_demoplayer_button, 0)) + { + if(info->speed < 0.5f) client_demoplayer_setspeed(0.5f); + else if(info->speed < 1.0f) client_demoplayer_setspeed(1.0f); + else if(info->speed < 2.0f) client_demoplayer_setspeed(2.0f); + else if(info->speed < 4.0f) client_demoplayer_setspeed(4.0f); + else client_demoplayer_setspeed(8.0f); + } + + // speed meter + ui_vsplit_l(&buttonbar, margins*3, 0, &buttonbar); + char buffer[64]; + if(info->speed >= 1.0f) + str_format(buffer, sizeof(buffer), "x%.0f", info->speed); + else + str_format(buffer, sizeof(buffer), "x%.1f", info->speed); + ui_do_label(&buttonbar, buffer, button.h*0.7f, -1); + + // exit button + ui_vsplit_r(&buttonbar, buttonbar_height*3, &buttonbar, &button); + static int exit_button = 0; + if(ui_do_button(&exit_button, "Exit", 0, &button, ui_draw_demoplayer_button, 0)) + client_disconnect(); + } +} + +static RECT listbox_originalview; +static RECT listbox_view; +static float listbox_rowheight; +static int listbox_itemindex; +static int listbox_selected_index; +static int listbox_new_selected; + +void MENUS::ui_do_listbox_start(void *id, const RECT *rect, float row_height, const char *title, int num_items, int selected_index) +{ + RECT scroll, row; + RECT view = *rect; + RECT header, footer; + + // draw header + ui_hsplit_t(&view, listheader_height, &header, &view); + ui_draw_rect(&header, vec4(1,1,1,0.25f), CORNER_T, 5.0f); + ui_do_label(&header, title, header.h*fontmod_height, 0); + + // draw footers + ui_hsplit_b(&view, listheader_height, &view, &footer); + ui_draw_rect(&footer, vec4(1,1,1,0.25f), CORNER_B, 5.0f); + ui_vsplit_l(&footer, 10.0f, 0, &footer); + + // background + ui_draw_rect(&view, vec4(0,0,0,0.15f), 0, 0); + + // prepare the scroll + ui_vsplit_r(&view, 15, &view, &scroll); + + // setup the variables + listbox_originalview = view; + listbox_selected_index = selected_index; + listbox_new_selected = selected_index; + listbox_itemindex = 0; + listbox_rowheight = row_height; + //int num_servers = client_serverbrowse_sorted_num(); + + + // do the scrollbar + ui_hsplit_t(&view, listbox_rowheight, &row, 0); + + int num = (int)(listbox_originalview.h/row.h); + static float scrollvalue = 0; + ui_hmargin(&scroll, 5.0f, &scroll); + scrollvalue = ui_do_scrollbar_v(id, &scroll, scrollvalue); + + int start = (int)(num*scrollvalue); + if(start < 0) + start = 0; + + // the list + listbox_view = listbox_originalview; + ui_vmargin(&listbox_view, 5.0f, &listbox_view); + ui_clip_enable(&listbox_view); + listbox_view.y -= scrollvalue*num*row.h; +} + +MENUS::LISTBOXITEM MENUS::ui_do_listbox_nextitem(void *id) +{ + RECT row; + LISTBOXITEM item = {0}; + ui_hsplit_t(&listbox_view, listbox_rowheight-2.0f, &row, &listbox_view); + ui_hsplit_t(&listbox_view, 2.0f, 0, &listbox_view); + + RECT select_hit_box = row; + + item.visible = 1; + if(listbox_selected_index == listbox_itemindex) + item.selected = 1; + + // make sure that only those in view can be selected + if(row.y+row.h > listbox_originalview.y) + { + + if(select_hit_box.y < listbox_originalview.y) // clip the selection + { + select_hit_box.h -= listbox_originalview.y-select_hit_box.y; + select_hit_box.y = listbox_originalview.y; + } + + if(ui_do_button(id, "", listbox_selected_index==listbox_itemindex, &select_hit_box, 0, 0)) + listbox_new_selected = listbox_itemindex; + } + else + item.visible = 0; + + item.rect = row; + + // check if we need to do more + if(row.y > listbox_originalview.y+listbox_originalview.h) + item.visible = 0; + + if(listbox_selected_index==listbox_itemindex) + { + //selected_index = i; + RECT r = row; + ui_margin(&r, 1.5f, &r); + ui_draw_rect(&r, vec4(1,1,1,0.5f), CORNER_ALL, 4.0f); + } + + listbox_itemindex++; + + ui_vmargin(&item.rect, 5.0f, &item.rect); + + return item; +} + +int MENUS::ui_do_listbox_end() +{ + ui_clip_disable(); + return listbox_new_selected; +} + +/* +void MENUS::demolist_listdir_callback(const char *name, int is_dir, void *user) +{ + + (*count)++; + LISTBOXITEM item = ui_do_listbox_nextitem((void*)(10+*count)); + if(item.visible) + ui_do_label(&item.rect, name, item.rect.h*fontmod_height, -1); +} + + + DEMOITEM *demos; + int num_demos; + */ + +void MENUS::demolist_count_callback(const char *name, int is_dir, void *user) +{ + if(is_dir || name[0] == '.') + return; + (*(int *)user)++; +} + +struct FETCH_CALLBACKINFO +{ + MENUS *self; + const char *prefix; + int count; +}; + +void MENUS::demolist_fetch_callback(const char *name, int is_dir, void *user) +{ + if(is_dir || name[0] == '.') + return; + + FETCH_CALLBACKINFO *info = (FETCH_CALLBACKINFO *)user; + + if(info->count == info->self->num_demos) + return; + + str_format(info->self->demos[info->count].filename, sizeof(info->self->demos[info->count].filename), "%s/%s", info->prefix, name); + str_copy(info->self->demos[info->count].name, name, sizeof(info->self->demos[info->count].name)); + info->count++; +} + + +void MENUS::demolist_populate() +{ + if(demos) + mem_free(demos); + demos = 0; + num_demos = 0; + + char buf[512]; + str_format(buf, sizeof(buf), "%s/demos", client_user_directory()); + fs_listdir(buf, demolist_count_callback, &num_demos); + fs_listdir("demos", demolist_count_callback, &num_demos); + + demos = (DEMOITEM *)mem_alloc(sizeof(DEMOITEM)*num_demos, 1); + mem_zero(demos, sizeof(DEMOITEM)*num_demos); + + FETCH_CALLBACKINFO info = {this, buf, 0}; + fs_listdir(buf, demolist_fetch_callback, &info); + info.prefix = "demos"; + fs_listdir("demos", demolist_fetch_callback, &info); +} + + +void MENUS::render_demolist(RECT main_view) +{ + static int inited = 0; + if(!inited) + demolist_populate(); + inited = 1; + + // render background + ui_draw_rect(&main_view, color_tabbar_active, CORNER_ALL, 10.0f); + ui_margin(&main_view, 10.0f, &main_view); + + RECT buttonbar; + ui_hsplit_b(&main_view, button_height+5.0f, &main_view, &buttonbar); + ui_hsplit_t(&buttonbar, 5.0f, 0, &buttonbar); + + static int selected_item = -1; + static int num_items = 0; + static int demolist_id = 0; + + ui_do_listbox_start(&demolist_id, &main_view, 17.0f, "Demos", num_items, selected_item); + for(int i = 0; i < num_demos; i++) + { + LISTBOXITEM item = ui_do_listbox_nextitem((void*)(10+i)); + if(item.visible) + ui_do_label(&item.rect, demos[i].name, item.rect.h*fontmod_height, -1); + } + selected_item = ui_do_listbox_end(); + + RECT button; + ui_vsplit_r(&buttonbar, 120.0f, &buttonbar, &button); + static int play_button = 0; + if(ui_do_button(&play_button, "Play", 0, &button, ui_draw_menu_button, 0)) + { + if(selected_item >= 0 && selected_item < num_demos) + client_demoplayer_play(demos[selected_item].filename); + } +} + diff --git a/src/game/client/components/menus_ingame.cpp b/src/game/client/components/menus_ingame.cpp index 33b4ffca..cfd92daa 100644 --- a/src/game/client/components/menus_ingame.cpp +++ b/src/game/client/components/menus_ingame.cpp @@ -283,7 +283,7 @@ void MENUS::render_servercontrol_map(RECT main_view) ui_vmargin(&button, 5.0f, &button); ui_do_label(&button, gameclient.maplist->name(i), 18.0f, -1); - } + } } void MENUS::render_servercontrol_kick(RECT main_view) diff --git a/src/game/client/components/players.cpp b/src/game/client/components/players.cpp index 6bf4221e..02977632 100644 --- a/src/game/client/components/players.cpp +++ b/src/game/client/components/players.cpp @@ -132,7 +132,7 @@ void PLAYERS::render_player( gameclient.clients[info.cid].angle = angle; vec2 direction = get_direction((int)(angle*256.0f)); - if(info.local && config.cl_predict) + if(info.local && config.cl_predict && client_state() != CLIENTSTATE_DEMOPLAYBACK) { if(!gameclient.snap.local_character || (gameclient.snap.local_character->health < 0) || (gameclient.snap.gameobj && gameclient.snap.gameobj->game_over)) { @@ -149,6 +149,8 @@ void PLAYERS::render_player( vec2 position = mix(vec2(prev.x, prev.y), vec2(player.x, player.y), intratick); vec2 vel = mix(vec2(prev.vx/256.0f, prev.vy/256.0f), vec2(player.vx/256.0f, player.vy/256.0f), intratick); + //dbg_msg("", "%d %d %d %d %f", prev.x, prev.y, player.x, player.y, intratick); + gameclient.flow->add(position, vel*100.0f, 10.0f); render_info.got_airjump = player.jumped&2?0:1; diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp index 3aed1cd1..fee12eb0 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -262,8 +262,9 @@ void GAMECLIENT::on_connected() layers_init(); col_init(); render_tilemap_generate_skip(); - - on_reset(); + + for(int i = 0; i < all.num; i++) + all.components[i]->on_mapload(); // send the inital info send_info(true); @@ -313,8 +314,44 @@ void GAMECLIENT::update_local_character_pos() } } + +static void evolve(NETOBJ_CHARACTER *character, int tick) +{ + WORLD_CORE tempworld; + CHARACTER_CORE tempcore; + mem_zero(&tempcore, sizeof(tempcore)); + tempcore.world = &tempworld; + tempcore.read(character); + //tempcore.input.direction = character->wanted_direction; + while(character->tick < tick) + { + character->tick++; + tempcore.tick(false); + tempcore.move(); + tempcore.quantize(); + } + + tempcore.write(character); +} + + void GAMECLIENT::on_render() { + // perform dead reckoning + // TODO: move this to a betterlocation + /* + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(!snap.characters[i].active) + continue; + + // perform dead reckoning + if(snap.characters[i].prev.tick) + evolve(&snap.characters[i].prev, client_prevtick()); + if(snap.characters[i].cur.tick) + evolve(&snap.characters[i].cur, client_tick()); + }*/ + // update the local character position update_local_character_pos(); @@ -393,6 +430,9 @@ void GAMECLIENT::on_message(int msgtype) if(clients[msg->cid].skin_name[0] == 'x' || clients[msg->cid].skin_name[1] == '_') str_copy(clients[msg->cid].skin_name, "default", 64); + clients[msg->cid].color_body = msg->color_body; + clients[msg->cid].color_feet = msg->color_feet; + clients[msg->cid].skin_info.color_body = skins->get_color(msg->color_body); clients[msg->cid].skin_info.color_feet = skins->get_color(msg->color_feet); clients[msg->cid].skin_info.size = 64; @@ -402,6 +442,8 @@ void GAMECLIENT::on_message(int msgtype) if(clients[msg->cid].skin_id < 0) clients[msg->cid].skin_id = 0; + clients[msg->cid].use_custom_color = msg->use_custom_color; + if(msg->use_custom_color) clients[msg->cid].skin_info.texture = gameclient.skins->get(clients[msg->cid].skin_id)->color_texture; else @@ -485,27 +527,6 @@ void GAMECLIENT::process_events() } } -static void evolve(NETOBJ_CHARACTER *character, int tick) -{ - WORLD_CORE tempworld; - CHARACTER_CORE tempcore; - mem_zero(&tempcore, sizeof(tempcore)); - tempcore.world = &tempworld; - tempcore.read(character); - //tempcore.input.direction = character->wanted_direction; - if(tick-character->tick > 50*3) - dbg_msg("", "%d -> %d = %d", character->tick, tick, tick-character->tick); - while(character->tick < tick) - { - character->tick++; - tempcore.tick(false); - tempcore.move(); - tempcore.quantize(); - } - - tempcore.write(character); -} - void GAMECLIENT::on_snapshot() { // clear out the invalid pointers @@ -582,7 +603,6 @@ void GAMECLIENT::on_snapshot() snap.characters[item.id].prev = *((const NETOBJ_CHARACTER *)old); snap.characters[item.id].cur = *((const NETOBJ_CHARACTER *)data); - // perform dead reckoning if(snap.characters[item.id].prev.tick) evolve(&snap.characters[item.id].prev, client_prevtick()); if(snap.characters[item.id].cur.tick) @@ -596,6 +616,9 @@ void GAMECLIENT::on_snapshot() } } + if(client_state() == CLIENTSTATE_DEMOPLAYBACK) + gameclient.snap.spectate = true; + // setup local pointers if(snap.local_cid >= 0) { @@ -779,6 +802,25 @@ void GAMECLIENT::send_kill(int client_id) client_send_msg(); } +void GAMECLIENT::on_recordkeyframe() +{ + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(!snap.player_infos[i]) + continue; + + NETMSG_SV_SETINFO msg; + msg.cid = i; + msg.name = clients[i].name; + msg.skin = clients[i].skin_name; + msg.use_custom_color = clients[i].use_custom_color; + msg.color_body = clients[i].color_body; + msg.color_feet = clients[i].color_feet; + msg.pack(MSGFLAG_NOSEND|MSGFLAG_RECORD); + client_send_msg(); + } +} + void GAMECLIENT::con_team(void *result, void *user_data) { ((GAMECLIENT*)user_data)->send_switch_team(console_arg_int(result, 0)); diff --git a/src/game/client/gameclient.hpp b/src/game/client/gameclient.hpp index 3ced19ce..b9ee325e 100644 --- a/src/game/client/gameclient.hpp +++ b/src/game/client/gameclient.hpp @@ -83,6 +83,10 @@ public: // client data struct CLIENT_DATA { + int use_custom_color; + int color_body; + int color_feet; + char name[64]; char skin_name[64]; int skin_id; @@ -115,6 +119,7 @@ public: void on_snapshot(); void on_predict(); int on_snapinput(int *data); + void on_recordkeyframe(); // actions // TODO: move these diff --git a/src/game/server/entities/character.cpp b/src/game/server/entities/character.cpp index 071407b1..0516ddc5 100644 --- a/src/game/server/entities/character.cpp +++ b/src/game/server/entities/character.cpp @@ -828,7 +828,7 @@ bool CHARACTER::take_damage(vec2 force, int dmg, int from, int weapon) void CHARACTER::snap(int snaping_client) { - if(distance(game.players[snaping_client]->view_pos, pos) > 1000.0f) + if(networkclipped(snaping_client)) return; NETOBJ_CHARACTER *character = (NETOBJ_CHARACTER *)snap_new_item(NETOBJTYPE_CHARACTER, player->client_id, sizeof(NETOBJ_CHARACTER)); diff --git a/src/game/server/entities/laser.cpp b/src/game/server/entities/laser.cpp index 4e9909ba..8b512d82 100644 --- a/src/game/server/entities/laser.cpp +++ b/src/game/server/entities/laser.cpp @@ -100,7 +100,7 @@ void LASER::tick() void LASER::snap(int snapping_client) { - if(distance(game.players[snapping_client]->view_pos, pos) > 1000.0f) + if(networkclipped(snapping_client)) return; NETOBJ_LASER *obj = (NETOBJ_LASER *)snap_new_item(NETOBJTYPE_LASER, id, sizeof(NETOBJ_LASER)); diff --git a/src/game/server/entities/projectile.cpp b/src/game/server/entities/projectile.cpp index a2e10437..cd15ba10 100644 --- a/src/game/server/entities/projectile.cpp +++ b/src/game/server/entities/projectile.cpp @@ -78,9 +78,7 @@ void PROJECTILE::tick() if(flags & PROJECTILE_FLAGS_EXPLODE) game.create_explosion(curpos, owner, weapon, false); else if(targetchr) - { targetchr->take_damage(direction * max(0.001f, force), damage, owner, weapon); - } game.world.destroy_entity(this); } @@ -100,7 +98,7 @@ void PROJECTILE::snap(int snapping_client) { float ct = (server_tick()-start_tick)/(float)server_tickspeed(); - if(distance(game.players[snapping_client]->view_pos, get_pos(ct)) > 1000.0f) + if(networkclipped(snapping_client, get_pos(ct))) return; NETOBJ_PROJECTILE *proj = (NETOBJ_PROJECTILE *)snap_new_item(NETOBJTYPE_PROJECTILE, id, sizeof(NETOBJ_PROJECTILE)); diff --git a/src/game/server/entity.cpp b/src/game/server/entity.cpp index 2cc7c8f7..1af5f60a 100644 --- a/src/game/server/entity.cpp +++ b/src/game/server/entity.cpp @@ -26,3 +26,17 @@ ENTITY::~ENTITY() game.world.remove_entity(this); snap_free_id(id); } + +int ENTITY::networkclipped(int snapping_client) +{ + return networkclipped(snapping_client, pos); +} + +int ENTITY::networkclipped(int snapping_client, vec2 check_pos) +{ + if(snapping_client == -1) + return 0; + if(distance(game.players[snapping_client]->view_pos, check_pos) > 1000.0f) + return 1; + return 0; +} diff --git a/src/game/server/entity.hpp b/src/game/server/entity.hpp index 8ccb2d9a..debe57b6 100644 --- a/src/game/server/entity.hpp +++ b/src/game/server/entity.hpp @@ -74,34 +74,34 @@ public: /* Function: destroy - Destorys the entity. + Destorys the entity. */ virtual void destroy() { delete this; } /* Function: reset - Called when the game resets the map. Puts the entity - back to it's starting state or perhaps destroys it. + Called when the game resets the map. Puts the entity + back to it's starting state or perhaps destroys it. */ virtual void reset() {} /* Function: tick - Called progress the entity to the next tick. Updates - and moves the entity to it's new state and position. + Called progress the entity to the next tick. Updates + and moves the entity to it's new state and position. */ virtual void tick() {} /* Function: tick_defered - Called after all entities tick() function has been called. + Called after all entities tick() function has been called. */ virtual void tick_defered() {} /* Function: snap - Called when a new snapshot is being generated for a specific - client. + Called when a new snapshot is being generated for a specific + client. Arguments: snapping_client - ID of the client which snapshot is @@ -110,16 +110,34 @@ public: recording. */ virtual void snap(int snapping_client) {} + + /* + Function: networkclipped(int snapping_client) + Performs a series of test to see if a client can see the + entity. + + Arguments: + snapping_client - ID of the client which snapshot is + being generated. Could be -1 to create a complete + snapshot of everything in the game for demo + recording. + + Returns: + Non-zero if the entity doesn't have to be in the snapshot. + */ + int networkclipped(int snapping_client); + int networkclipped(int snapping_client, vec2 check_pos); + /* Variable: proximity_radius - Contains the physical size of the entity. + Contains the physical size of the entity. */ float proximity_radius; /* Variable: pos - Contains the current posititon of the entity. + Contains the current posititon of the entity. */ vec2 pos; }; diff --git a/src/game/server/eventhandler.cpp b/src/game/server/eventhandler.cpp index ce6a3b71..761eaf2c 100644 --- a/src/game/server/eventhandler.cpp +++ b/src/game/server/eventhandler.cpp @@ -36,10 +36,10 @@ void EVENTHANDLER::snap(int snapping_client) { for(int i = 0; i < num_events; i++) { - if(cmask_is_set(client_masks[i], snapping_client)) + if(snapping_client == -1 || cmask_is_set(client_masks[i], snapping_client)) { NETEVENT_COMMON *ev = (NETEVENT_COMMON *)&data[offsets[i]]; - if(distance(game.players[snapping_client]->view_pos, vec2(ev->x, ev->y)) < 1500.0f) + if(snapping_client == -1 || distance(game.players[snapping_client]->view_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]); diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index fb426ad6..0cdd4227 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -171,7 +171,7 @@ void GAMECONTEXT::send_chat(int chatter_cid, int team, const char *text) } -void GAMECONTEXT::send_info(int who, int to_who) +void GAMECONTEXT::send_info(int who, int to_who, bool recordonly) { NETMSG_SV_SETINFO msg; msg.cid = who; @@ -180,9 +180,17 @@ void GAMECONTEXT::send_info(int who, int to_who) msg.use_custom_color = players[who]->use_custom_color; msg.color_body = players[who]->color_body; msg.color_feet = players[who]->color_feet; - msg.pack(MSGFLAG_VITAL); - server_send_msg(to_who); + if(recordonly) + { + msg.pack(MSGFLAG_NOSEND); + server_send_msg(to_who); + } + else + { + msg.pack(MSGFLAG_VITAL); + server_send_msg(to_who); + } } void GAMECONTEXT::send_emoticon(int cid, int emoticon) @@ -333,6 +341,18 @@ void GAMECONTEXT::tick() void GAMECONTEXT::snap(int client_id) { + // check if we are recording a demo + if(client_id == -1) + { + // we are recording, make sure that we set + // the info for all players all the time + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(game.players[i]) + send_info(i, -1, true); + } + } + world.snap(client_id); controller->snap(client_id); events.snap(client_id); diff --git a/src/game/server/gamecontext.hpp b/src/game/server/gamecontext.hpp index 124df645..8a1ff918 100644 --- a/src/game/server/gamecontext.hpp +++ b/src/game/server/gamecontext.hpp @@ -77,7 +77,7 @@ public: void send_emoticon(int cid, int emoticon); void send_weapon_pickup(int cid, int weapon); void send_broadcast(const char *text, int cid); - void send_info(int who, int to_who); + void send_info(int who, int to_who, bool recordonly = false); }; diff --git a/src/game/server/gamecontroller.cpp b/src/game/server/gamecontroller.cpp index 7329240b..aa8c09b7 100644 --- a/src/game/server/gamecontroller.cpp +++ b/src/game/server/gamecontroller.cpp @@ -457,8 +457,19 @@ void GAMECONTROLLER::snap(int snapping_client) gameobj->round_num = (strlen(config.sv_maprotation) || config.sv_rounds_per_map > 1) ? config.sv_rounds_per_map : 0; gameobj->round_current = round_count+1; - gameobj->teamscore_red = is_teamplay() ? teamscore[0] : game.players[snapping_client]->score; - gameobj->teamscore_blue = teamscore[1]; + + if(snapping_client == -1) + { + // we are recording a demo, just set the scores + gameobj->teamscore_red = teamscore[0]; + gameobj->teamscore_blue = teamscore[1]; + } + else + { + // TODO: this little hack should be removed + gameobj->teamscore_red = is_teamplay() ? teamscore[0] : game.players[snapping_client]->score; + gameobj->teamscore_blue = teamscore[1]; + } } int GAMECONTROLLER::get_auto_team(int notthisid) |