diff options
| author | Magnus Auvinen <magnus.auvinen@gmail.com> | 2008-08-27 15:48:50 +0000 |
|---|---|---|
| committer | Magnus Auvinen <magnus.auvinen@gmail.com> | 2008-08-27 15:48:50 +0000 |
| commit | dfe499248f1b1236487156b28e4a535d7963fe35 (patch) | |
| tree | a750b0f28cfd3f3e252602681412ac1adc6d29c7 /src/game/client | |
| parent | d711dd190cac809a9bd278fba03ed974812bb863 (diff) | |
| download | zcatch-dfe499248f1b1236487156b28e4a535d7963fe35.tar.gz zcatch-dfe499248f1b1236487156b28e4a535d7963fe35.zip | |
major commit. game client restructure. not complete, loads of stuff not working, but the structure is there
Diffstat (limited to 'src/game/client')
62 files changed, 6513 insertions, 5492 deletions
diff --git a/src/game/client/animstate.cpp b/src/game/client/animstate.cpp new file mode 100644 index 00000000..d8c20dec --- /dev/null +++ b/src/game/client/animstate.cpp @@ -0,0 +1,95 @@ + +#include <base/math.hpp> +#include <engine/e_client_interface.h> +#include <game/generated/g_protocol.hpp> +#include <game/generated/gc_data.hpp> + +#include "animstate.hpp" + +static void anim_seq_eval(ANIM_SEQUENCE *seq, float time, ANIM_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 + ANIM_KEYFRAME *frame1 = 0; + ANIM_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); + } + } +} + +static void anim_add_keyframe(ANIM_KEYFRAME *seq, ANIM_KEYFRAME *added, float amount) +{ + seq->x += added->x*amount; + seq->y += added->y*amount; + seq->angle += added->angle*amount; +} + +static 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 ANIMSTATE::set(ANIMATION *anim, float time) +{ + anim_seq_eval(&anim->body, time, &body); + anim_seq_eval(&anim->back_foot, time, &back_foot); + anim_seq_eval(&anim->front_foot, time, &front_foot); + anim_seq_eval(&anim->attach, time, &attach); +} + +void ANIMSTATE::add(ANIMATION *anim, float time, float amount) +{ + ANIMSTATE add; + add.set(anim, time); + anim_add(this, &add, amount); +} + +ANIMSTATE *ANIMSTATE::get_idle() +{ + static ANIMSTATE state; + static bool init = true; + + if(init) + { + state.set(&data->animations[ANIM_BASE], 0); + state.add(&data->animations[ANIM_IDLE], 0, 1.0f); + init = false; + } + + return &state; +} diff --git a/src/game/client/animstate.hpp b/src/game/client/animstate.hpp new file mode 100644 index 00000000..4b84dd66 --- /dev/null +++ b/src/game/client/animstate.hpp @@ -0,0 +1,24 @@ +#ifndef GAME_CLIENT_ANIMATION_H +#define GAME_CLIENT_ANIMATION_H + +class ANIMSTATE +{ +public: + ANIM_KEYFRAME body; + ANIM_KEYFRAME back_foot; + ANIM_KEYFRAME front_foot; + ANIM_KEYFRAME attach; + + void set(ANIMATION *anim, float time); + void add(ANIMATION *added, float time, float amount); + + static ANIMSTATE *get_idle(); +}; + +//void anim_seq_eval(ANIM_SEQUENCE *seq, float time, ANIM_KEYFRAME *frame); +//void anim_eval(ANIMATION *anim, float time, ANIM_STATE *state); +//void anim_add_keyframe(ANIM_KEYFRAME *seq, ANIM_KEYFRAME *added, float amount); +//void anim_add(ANIM_STATE *state, ANIM_STATE *added, float amount); +//void anim_eval_add(ANIM_STATE *state, ANIMATION *anim, float time, float amount); + +#endif diff --git a/src/game/client/component.hpp b/src/game/client/component.hpp new file mode 100644 index 00000000..5bc7830d --- /dev/null +++ b/src/game/client/component.hpp @@ -0,0 +1,22 @@ +#ifndef GAME_CLIENT_GAMESYSTEM_H +#define GAME_CLIENT_GAMESYSTEM_H + +#include <engine/e_client_interface.h> + +class GAMECLIENT; + +class COMPONENT +{ +public: + GAMECLIENT *client; + + virtual void on_statechange(int new_state, int old_state) {}; + virtual void on_init() {}; + virtual void on_reset() {}; + virtual void on_render() {}; + 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; } +}; + +#endif diff --git a/src/game/client/components/binds.cpp b/src/game/client/components/binds.cpp new file mode 100644 index 00000000..5d033ad4 --- /dev/null +++ b/src/game/client/components/binds.cpp @@ -0,0 +1,181 @@ +extern "C" { + #include <engine/e_console.h> +} + +#include "binds.hpp" + +BINDS::BINDS() +{ + mem_zero(keybindings, sizeof(keybindings)); +} + +void BINDS::bind(int keyid, const char *str) +{ + if(keyid < 0 && keyid >= KEY_LAST) + return; + + str_copy(keybindings[keyid], str, sizeof(keybindings[keyid])); + if(!keybindings[keyid][0]) + dbg_msg("binds", "unbound %s (%d)", inp_key_name(keyid), keyid); + else + dbg_msg("binds", "bound %s (%d) = %s", inp_key_name(keyid), keyid, keybindings[keyid]); +} + + +bool BINDS::on_input(INPUT_EVENT e) +{ + // don't handle invalid events and keys that arn't set to anything + if(e.key <= 0 || e.key >= KEY_LAST || keybindings[e.key][0] == 0) + return false; + + int stroke = 0; + if(e.flags&INPFLAG_PRESS) + stroke = 1; + console_execute_line_stroked(stroke, keybindings[e.key]); + return true; +} + +void BINDS::unbindall() +{ + for(int i = 0; i < KEY_LAST; i++) + keybindings[i][0] = 0; +} + +const char *BINDS::get(int keyid) +{ + if(keyid > 0 && keyid < KEY_LAST) + return keybindings[keyid]; + return ""; +} + +void BINDS::set_defaults() +{ + unbindall(); + + // set default key bindings + bind(KEY_F1, "toggle_local_console"); + bind(KEY_F2, "toggle_remote_console"); + bind(KEY_TAB, "+scoreboard"); + bind(KEY_F10, "screenshot"); + + bind('A', "+left"); + bind('D', "+right"); + bind(KEY_SPACE, "+jump"); + bind(KEY_MOUSE_1, "+fire"); + bind(KEY_MOUSE_2, "+hook"); + bind(KEY_LSHIFT, "+emote"); + + bind('1', "+weapon1"); + bind('2', "+weapon2"); + bind('3', "+weapon3"); + bind('4', "+weapon4"); + bind('5', "+weapon5"); + + bind(KEY_MOUSE_WHEEL_UP, "+prevweapon"); + bind(KEY_MOUSE_WHEEL_DOWN, "+nextweapon"); + + bind('T', "chat all"); + bind('Y', "chat team"); +} + +void BINDS::on_init() +{ + set_defaults(); +} + +/* +static int get_key_id(const char *key_name) +{ + // check for numeric + if(key_name[0] == '#') + { + int i = atoi(key_name+1); + if(i > 0 && i < KEY_LAST) + return i; // numeric + } + + // search for key + for(int i = 0; i < KEY_LAST; i++) + { + if(strcmp(key_name, inp_key_name(i)) == 0) + return i; + } + + return 0; +} + +static void con_bind(void *result, void *user_data) +{ + const char *key_name = console_arg_string(result, 0); + int id = get_key_id(key_name); + + if(!id) + { + dbg_msg("binds", "key %s not found", key_name); + return; + } + + binds_set(id, console_arg_string(result, 1)); +} + + +static void con_unbind(void *result, void *user_data) +{ + const char *key_name = console_arg_string(result, 0); + int id = get_key_id(key_name); + + if(!id) + { + dbg_msg("binds", "key %s not found", key_name); + return; + } + + binds_set(id, ""); +} + + +static void con_unbindall(void *result, void *user_data) +{ + binds_unbindall(); +} + + +static void con_dump_binds(void *result, void *user_data) +{ + for(int i = 0; i < KEY_LAST; i++) + { + if(keybindings[i][0] == 0) + continue; + dbg_msg("binds", "%s (%d) = %s", inp_key_name(i), i, keybindings[i]); + } +} + +void binds_save() +{ + char buffer[256]; + char *end = buffer+sizeof(buffer)-8; + client_save_line("unbindall"); + for(int i = 0; i < KEY_LAST; i++) + { + if(keybindings[i][0] == 0) + continue; + str_format(buffer, sizeof(buffer), "bind %s ", inp_key_name(i)); + + // process the string. we need to escape some characters + const char *src = keybindings[i]; + char *dst = buffer + strlen(buffer); + *dst++ = '"'; + while(*src && dst < end) + { + if(*src == '"' || *src == '\\') // escape \ and " + *dst++ = '\\'; + *dst++ = *src++; + } + *dst++ = '"'; + *dst++ = 0; + + client_save_line(buffer); + } +} + +*/ diff --git a/src/game/client/components/binds.hpp b/src/game/client/components/binds.hpp new file mode 100644 index 00000000..304b4a8d --- /dev/null +++ b/src/game/client/components/binds.hpp @@ -0,0 +1,19 @@ +#include <game/client/component.hpp> + +class BINDS : public COMPONENT +{ + char keybindings[KEY_LAST][128]; +public: + BINDS(); + + void bind(int keyid, const char *str); + void set_defaults(); + void unbindall(); + const char *get(int keyid); + + /*virtual void on_reset(); + virtual void on_render(); + virtual void on_message(int msgtype, void *rawmsg);*/ + virtual void on_init(); + virtual bool on_input(INPUT_EVENT e); +}; diff --git a/src/game/client/components/broadcast.cpp b/src/game/client/components/broadcast.cpp new file mode 100644 index 00000000..31ff6a71 --- /dev/null +++ b/src/game/client/components/broadcast.cpp @@ -0,0 +1,37 @@ +#include <engine/e_client_interface.h> +#include <engine/e_config.h> +#include <game/generated/g_protocol.hpp> +#include <game/generated/gc_data.hpp> + +#include <game/client/gameclient.hpp> +//#include <game/client/gc_anim.hpp> +#include <game/client/gc_client.hpp> + +#include "broadcast.hpp" + +void BROADCAST::on_reset() +{ + broadcast_time = 0; +} + +void BROADCAST::on_render() +{ + gfx_mapscreen(0, 0, 300*gfx_screenaspect(), 300); + + if(time_get() < broadcast_time) + { + float w = gfx_text_width(0, 14, broadcast_text, -1); + gfx_text(0, 150*gfx_screenaspect()-w/2, 35, 14, broadcast_text, -1); + } +} + +void BROADCAST::on_message(int msgtype, void *rawmsg) +{ + if(msgtype == NETMSGTYPE_SV_BROADCAST) + { + NETMSG_SV_BROADCAST *msg = (NETMSG_SV_BROADCAST *)rawmsg; + str_copy(broadcast_text, msg->message, sizeof(broadcast_text)); + broadcast_time = time_get()+time_freq()*10; + } +} + diff --git a/src/game/client/components/broadcast.hpp b/src/game/client/components/broadcast.hpp new file mode 100644 index 00000000..102201cc --- /dev/null +++ b/src/game/client/components/broadcast.hpp @@ -0,0 +1,14 @@ +#include <game/client/component.hpp> + +class BROADCAST : public COMPONENT +{ +public: + // broadcasts + char broadcast_text[1024]; + int64 broadcast_time; + + virtual void on_reset(); + virtual void on_render(); + virtual void on_message(int msgtype, void *rawmsg); +}; + diff --git a/src/game/client/components/camera.cpp b/src/game/client/components/camera.cpp new file mode 100644 index 00000000..32442031 --- /dev/null +++ b/src/game/client/components/camera.cpp @@ -0,0 +1,45 @@ +extern "C" { + #include <engine/e_config.h> + #include <engine/e_client_interface.h> +} + +#include <base/math.hpp> +#include <game/collision.hpp> +#include <game/client/gameclient.hpp> +#include <game/client/component.hpp> + +#include "camera.hpp" +#include "controls.hpp" + +CAMERA::CAMERA() +{ +} + +void CAMERA::on_render() +{ + //vec2 center; + zoom = 1.0f; + + bool spectate = false; + + if(spectate) + center = gameclient.controls->mouse_pos; + else + { + + float l = length(gameclient.controls->mouse_pos); + float deadzone = config.cl_mouse_deadzone; + float follow_factor = config.cl_mouse_followfactor/100.0f; + vec2 camera_offset(0, 0); + + float offset_amount = max(l-deadzone, 0.0f) * follow_factor; + if(l > 0.0001f) // make sure that this isn't 0 + camera_offset = normalize(gameclient.controls->mouse_pos)*offset_amount; + + center = gameclient.local_character_pos + camera_offset; + } + + // set listner pos + snd_set_listener_pos(center.x, center.y); +} + diff --git a/src/game/client/components/camera.hpp b/src/game/client/components/camera.hpp new file mode 100644 index 00000000..1cb05f5b --- /dev/null +++ b/src/game/client/components/camera.hpp @@ -0,0 +1,13 @@ +#include <base/vmath.hpp> +#include <game/client/component.hpp> + +class CAMERA : public COMPONENT +{ +public: + vec2 center; + float zoom; + + CAMERA(); + virtual void on_render(); +}; + diff --git a/src/game/client/components/chat.cpp b/src/game/client/components/chat.cpp new file mode 100644 index 00000000..ecd29ab8 --- /dev/null +++ b/src/game/client/components/chat.cpp @@ -0,0 +1,201 @@ +#include <engine/e_client_interface.h> +#include <game/generated/g_protocol.hpp> +#include <game/generated/gc_data.hpp> + +#include <game/client/gameclient.hpp> +//#include <game/client/gc_anim.hpp> +#include <game/client/gc_client.hpp> + +#include "chat.hpp" + +void CHAT::on_reset() +{ + mode = MODE_NONE; + for(int i = 0; i < MAX_LINES; i++) + lines[i].tick = -1000000; + current_line = 0; +} + + +bool CHAT::on_input(INPUT_EVENT e) +{ + if(mode == MODE_NONE) + return false; + + if(e.flags&INPFLAG_PRESS && (e.key == KEY_ENTER || e.key == KEY_KP_ENTER)) + { + if(input.get_string()[0]) + gameclient.chat->say(mode == MODE_ALL ? 0 : 1, input.get_string()); + mode = MODE_NONE; + } + else + input.process_input(e); + + return true; +} + + +void CHAT::enable_mode(int team) +{ + if(mode == MODE_NONE) + { + if(team) + mode = MODE_TEAM; + else + mode = MODE_ALL; + + input.clear(); + inp_clear_events(); + } +} + +void CHAT::on_message(int msgtype, void *rawmsg) +{ + if(msgtype == NETMSGTYPE_SV_CHAT) + { + NETMSG_SV_CHAT *msg = (NETMSG_SV_CHAT *)rawmsg; + add_line(msg->cid, msg->team, msg->message); + + if(msg->cid >= 0) // TODO: repair me + 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); + } +} + + + +void CHAT::add_line(int client_id, int team, const char *line) +{ + current_line = (current_line+1)%MAX_LINES; + lines[current_line].tick = client_tick(); + lines[current_line].client_id = client_id; + lines[current_line].team = team; + lines[current_line].name_color = -2; + + if(client_id == -1) // server message + { + str_copy(lines[current_line].name, "*** ", sizeof(lines[current_line].name)); + str_format(lines[current_line].text, sizeof(lines[current_line].text), "%s", line); + } + else + { + if(gameclient.clients[client_id].team == -1) + lines[current_line].name_color = -1; + + if(gameclient.snap.gameobj && gameclient.snap.gameobj->flags&GAMEFLAG_TEAMS) + { + if(gameclient.clients[client_id].team == 0) + lines[current_line].name_color = 0; + else if(gameclient.clients[client_id].team == 1) + lines[current_line].name_color = 1; + } + + str_copy(lines[current_line].name, gameclient.clients[client_id].name, sizeof(lines[current_line].name)); + str_format(lines[current_line].text, sizeof(lines[current_line].text), ": %s", line); + } + + dbg_msg("chat", "%s%s", lines[current_line].name, lines[current_line].text); +} + +void CHAT::on_render() +{ + gfx_mapscreen(0,0,300*gfx_screenaspect(),300); + float x = 10.0f; + float y = 300.0f-30.0f; + if(mode != MODE_NONE) + { + // render chat input + TEXT_CURSOR cursor; + gfx_text_set_cursor(&cursor, x, y, 8.0f, TEXTFLAG_RENDER); + cursor.line_width = 300.0f; + + if(mode == MODE_ALL) + gfx_text_ex(&cursor, "All: ", -1); + else if(mode == MODE_TEAM) + gfx_text_ex(&cursor, "Team: ", -1); + else + gfx_text_ex(&cursor, "Chat: ", -1); + + gfx_text_ex(&cursor, input.get_string(), input.cursor_offset()); + TEXT_CURSOR marker = cursor; + gfx_text_ex(&marker, "|", -1); + gfx_text_ex(&cursor, input.get_string()+input.cursor_offset(), -1); + } + + y -= 8; + + int i; + for(i = 0; i < MAX_LINES; i++) + { + int r = ((current_line-i)+MAX_LINES)%MAX_LINES; + if(client_tick() > lines[r].tick+50*15) + break; + + float begin = x; + float fontsize = 8.0f; + + // get the y offset + TEXT_CURSOR cursor; + gfx_text_set_cursor(&cursor, begin, 0, fontsize, 0); + cursor.line_width = 300.0f; + gfx_text_ex(&cursor, lines[r].name, -1); + gfx_text_ex(&cursor, lines[r].text, -1); + y -= cursor.y + cursor.font_size; + + // reset the cursor + gfx_text_set_cursor(&cursor, begin, y, fontsize, TEXTFLAG_RENDER); + cursor.line_width = 300.0f; + + // render name + gfx_text_color(0.8f,0.8f,0.8f,1); + if(lines[r].client_id == -1) + gfx_text_color(1,1,0.5f,1); // system + else if(lines[r].team) + gfx_text_color(0.45f,0.9f,0.45f,1); // team message + else if(lines[r].name_color == 0) + gfx_text_color(1.0f,0.5f,0.5f,1); // red + else if(lines[r].name_color == 1) + gfx_text_color(0.7f,0.7f,1.0f,1); // blue + else if(lines[r].name_color == -1) + gfx_text_color(0.75f,0.5f,0.75f, 1); // spectator + + // render name + gfx_text_ex(&cursor, lines[r].name, -1); + + // render line + gfx_text_color(1,1,1,1); + if(lines[r].client_id == -1) + gfx_text_color(1,1,0.5f,1); // system + else if(lines[r].team) + gfx_text_color(0.65f,1,0.65f,1); // team message + + gfx_text_ex(&cursor, lines[r].text, -1); + } + + gfx_text_color(1,1,1,1); +} + +void con_chat(void *result, void *user_data) +{ + /* + const char *mode = console_arg_string(result, 0); + if(strcmp(mode, "all") == 0) + chat_enable_mode(0); + else if(strcmp(mode, "team") == 0) + chat_enable_mode(1); + else + dbg_msg("console", "expected all or team as mode"); + */ +} + + +void CHAT::say(int team, const char *line) +{ + // send chat message + NETMSG_CL_SAY msg; + msg.team = team; + msg.message = line; + msg.pack(MSGFLAG_VITAL); + client_send_msg(); +} diff --git a/src/game/client/components/chat.hpp b/src/game/client/components/chat.hpp new file mode 100644 index 00000000..caec18b2 --- /dev/null +++ b/src/game/client/components/chat.hpp @@ -0,0 +1,49 @@ +#include <game/client/component.hpp> +#include <game/client/lineinput.hpp> + +class CHAT : public COMPONENT +{ +public: + LINEINPUT input; + + enum + { + MAX_LINES = 10, + }; + + struct LINE + { + int tick; + int client_id; + int team; + int name_color; + char name[64]; + char text[512]; + }; + + LINE lines[MAX_LINES]; + int current_line; + + // chat + enum + { + MODE_NONE=0, + MODE_ALL, + MODE_TEAM, + }; + + int mode; + + void add_line(int client_id, int team, const char *line); + //void chat_reset(); + //bool chat_input_handle(INPUT_EVENT e, void *user_data); + + void enable_mode(int team); + + void say(int team, const char *line); + + virtual void on_reset(); + virtual void on_render(); + virtual void on_message(int msgtype, void *rawmsg); + virtual bool on_input(INPUT_EVENT e); +}; diff --git a/src/game/client/gc_console.cpp b/src/game/client/components/console.cpp index eb73ebc6..6437b718 100644 --- a/src/game/client/gc_console.cpp +++ b/src/game/client/components/console.cpp @@ -1,5 +1,5 @@ -#include "gc_console.hpp" -#include "../generated/gc_data.hpp" +//#include "gc_console.hpp" +#include <game/generated/gc_data.hpp> #include <base/system.h> @@ -14,10 +14,14 @@ extern "C" { #include <cstring> #include <cstdio> -#include "gc_ui.hpp" -#include "gc_client.hpp" +#include <game/client/gc_ui.hpp> +#include <game/client/gc_client.hpp> -#include "../version.hpp" +#include <game/version.hpp> + +#include <game/client/lineinput.hpp> + +#include "console.hpp" enum { @@ -27,335 +31,307 @@ enum CONSOLE_CLOSING, }; -class CONSOLE +CONSOLE::INSTANCE::INSTANCE(int t) { -public: - char history_data[65536]; - RINGBUFFER *history; - char *history_entry; - - char backlog_data[65536]; - RINGBUFFER *backlog; - - - line_input input; - - int type; + // init ringbuffers + history = ringbuf_init(history_data, sizeof(history_data)); + backlog = ringbuf_init(backlog_data, sizeof(backlog_data)); -public: - CONSOLE(int t) - { - // init ringbuffers - history = ringbuf_init(history_data, sizeof(history_data)); - backlog = ringbuf_init(backlog_data, sizeof(backlog_data)); - - history_entry = 0x0; - - type = t; - } + history_entry = 0x0; - void execute_line(const char *line) + type = t; +} + +void CONSOLE::INSTANCE::execute_line(const char *line) +{ + if(type == 0) + console_execute_line(line); + else { - if(type == 0) - console_execute_line(line); + if(client_rcon_authed()) + client_rcon(line); else - { - if(client_rcon_authed()) - client_rcon(line); - else - client_rcon_auth("", line); - } + client_rcon_auth("", line); } +} + +void CONSOLE::INSTANCE::on_input(INPUT_EVENT e) +{ + bool handled = false; - void handle_event(INPUT_EVENT e) + if(e.flags&INPFLAG_PRESS) { - bool handled = false; - - if(e.flags&INPFLAG_PRESS) + if(e.key == KEY_ENTER || e.key == KEY_KP_ENTER) { - if(e.key == KEY_ENTER || e.key == KEY_KP_ENTER) + if(input.get_string()[0]) { - if(input.get_string()[0]) - { - char *entry = (char *)ringbuf_allocate(history, input.get_length()+1); - mem_copy(entry, input.get_string(), input.get_length()+1); - - execute_line(input.get_string()); - input.clear(); - history_entry = 0x0; - } + char *entry = (char *)ringbuf_allocate(history, input.get_length()+1); + mem_copy(entry, input.get_string(), input.get_length()+1); - handled = true; + execute_line(input.get_string()); + input.clear(); + history_entry = 0x0; } - else if (e.key == KEY_UP) + + handled = true; + } + else if (e.key == KEY_UP) + { + if (history_entry) { - if (history_entry) - { - char *test = (char *)ringbuf_prev(history, history_entry); + char *test = (char *)ringbuf_prev(history, history_entry); - if (test) - history_entry = test; - } - else - history_entry = (char *)ringbuf_last(history); - - if (history_entry) - { - unsigned int len = strlen(history_entry); - if (len < sizeof(input) - 1) - input.set(history_entry); - } - handled = true; + if (test) + history_entry = test; } - else if (e.key == KEY_DOWN) + else + history_entry = (char *)ringbuf_last(history); + + if (history_entry) { - if (history_entry) - history_entry = (char *)ringbuf_next(history, history_entry); - - if (history_entry) - { - unsigned int len = strlen(history_entry); - if (len < sizeof(input) - 1) - input.set(history_entry); - } - else - input.clear(); - handled = true; + unsigned int len = strlen(history_entry); + if (len < sizeof(input) - 1) + input.set(history_entry); } + handled = true; + } + else if (e.key == KEY_DOWN) + { + if (history_entry) + history_entry = (char *)ringbuf_next(history, history_entry); + + if (history_entry) + { + unsigned int len = strlen(history_entry); + if (len < sizeof(input) - 1) + input.set(history_entry); + } + else + input.clear(); + handled = true; } - - if(!handled) - input.process_input(e); } - void print_line(const char *line) - { - int len = strlen(line); + if(!handled) + input.process_input(e); +} - if (len > 255) - len = 255; +void CONSOLE::INSTANCE::print_line(const char *line) +{ + int len = strlen(line); - char *entry = (char *)ringbuf_allocate(backlog, len+1); - mem_copy(entry, line, len+1); - } -}; + if (len > 255) + len = 255; -static CONSOLE local_console(0); -static CONSOLE remote_console(1); + char *entry = (char *)ringbuf_allocate(backlog, len+1); + mem_copy(entry, line, len+1); +} -static int console_type = 0; -static int console_state = CONSOLE_CLOSED; -static float state_change_end = 0.0f; -static const float state_change_duration = 0.1f; +CONSOLE::CONSOLE() +: local_console(0), remote_console(1) +{ + console_type = 0; + console_state = CONSOLE_CLOSED; + state_change_end = 0.0f; + state_change_duration = 0.1f; +} -static float time_now() +float CONSOLE::time_now() { static long long time_start = time_get(); return float(time_get()-time_start)/float(time_freq()); } -static CONSOLE *current_console() +CONSOLE::INSTANCE *CONSOLE::current_console() { if(console_type != 0) return &remote_console; return &local_console; } -static void client_console_print(const char *str) +void CONSOLE::on_reset() { - local_console.print_line(str); } -void console_rcon_print(const char *line) +// only defined for 0<=t<=1 +static float console_scale_func(float t) { - remote_console.print_line(line); + //return t; + return sinf(acosf(1.0f-t)); } -static void con_team(void *result, void *user_data) +void CONSOLE::on_render() { - send_switch_team(console_arg_int(result, 0)); -} -static void con_say(void *result, void *user_data) -{ - chat_say(0, console_arg_string(result, 0)); -} + RECT screen = *ui_screen(); + float console_max_height = screen.h*3/5.0f; + float console_height; -static void con_sayteam(void *result, void *user_data) -{ - chat_say(1, console_arg_string(result, 0)); -} + float progress = (time_now()-(state_change_end-state_change_duration))/float(state_change_duration); + if (progress >= 1.0f) + { + if (console_state == CONSOLE_CLOSING) + console_state = CONSOLE_CLOSED; + else if (console_state == CONSOLE_OPENING) + console_state = CONSOLE_OPEN; + progress = 1.0f; + } + + if (console_state == CONSOLE_CLOSED) + return; -static void con_chat(void *result, void *user_data) -{ - const char *mode = console_arg_string(result, 0); - if(strcmp(mode, "all") == 0) - chat_enable_mode(0); - else if(strcmp(mode, "team") == 0) - chat_enable_mode(1); - else - dbg_msg("console", "expected all or team as mode"); -} + float console_height_scale; + if (console_state == CONSOLE_OPENING) + console_height_scale = console_scale_func(progress); + else if (console_state == CONSOLE_CLOSING) + console_height_scale = console_scale_func(1.0f-progress); + else //if (console_state == CONSOLE_OPEN) + console_height_scale = console_scale_func(1.0f); -void send_kill(int client_id); + console_height = console_height_scale*console_max_height; -static void con_kill(void *result, void *user_data) -{ - send_kill(-1); -} + gfx_mapscreen(screen.x, screen.y, screen.w, screen.h); -static char keybindings[KEY_LAST][128] = {{0}}; + // do console shadow + gfx_texture_set(-1); + gfx_quads_begin(); + gfx_setcolorvertex(0, 0,0,0, 0.5f); + gfx_setcolorvertex(1, 0,0,0, 0.5f); + gfx_setcolorvertex(2, 0,0,0, 0.0f); + gfx_setcolorvertex(3, 0,0,0, 0.0f); + gfx_quads_drawTL(0,console_height,screen.w,10.0f); + gfx_quads_end(); -const char *binds_get(int keyid) -{ - if(keyid > 0 && keyid < KEY_LAST) - return keybindings[keyid]; - return ""; -} + // do background + gfx_texture_set(data->images[IMAGE_CONSOLE_BG].id); + gfx_quads_begin(); + gfx_setcolor(0.2f, 0.2f, 0.2f,0.9f); + if(console_type != 0) + gfx_setcolor(0.4f, 0.2f, 0.2f,0.9f); + gfx_quads_setsubset(0,-console_height*0.075f,screen.w*0.075f*0.5f,0); + gfx_quads_drawTL(0,0,screen.w,console_height); + gfx_quads_end(); -void binds_set(int keyid, const char *str) -{ - if(keyid < 0 && keyid >= KEY_LAST) - return; - - str_copy(keybindings[keyid], str, sizeof(keybindings[keyid])); - if(!keybindings[keyid][0]) - dbg_msg("binds", "unbound %s (%d)", inp_key_name(keyid), keyid); - else - dbg_msg("binds", "bound %s (%d) = %s", inp_key_name(keyid), keyid, keybindings[keyid]); -} + // do small bar shadow + gfx_texture_set(-1); + gfx_quads_begin(); + gfx_setcolorvertex(0, 0,0,0, 0.0f); + gfx_setcolorvertex(1, 0,0,0, 0.0f); + gfx_setcolorvertex(2, 0,0,0, 0.25f); + gfx_setcolorvertex(3, 0,0,0, 0.25f); + gfx_quads_drawTL(0,console_height-20,screen.w,10); + gfx_quads_end(); -void binds_unbindall() -{ - for(int i = 0; i < KEY_LAST; i++) - keybindings[i][0] = 0; -} + // do the lower bar + gfx_texture_set(data->images[IMAGE_CONSOLE_BAR].id); + gfx_quads_begin(); + gfx_setcolor(1.0f, 1.0f, 1.0f, 0.9f); + gfx_quads_setsubset(0,0.1f,screen.w*0.015f,1-0.1f); + gfx_quads_drawTL(0,console_height-10.0f,screen.w,10.0f); + gfx_quads_end(); + + console_height -= 10.0f; + + INSTANCE *console = current_console(); -void binds_default() -{ - binds_unbindall(); + { + float font_size = 10.0f; + float row_height = font_size*1.25f; + float x = 3; + float y = console_height - row_height - 2; - // set default key bindings - binds_set(KEY_F1, "toggle_local_console"); - binds_set(KEY_F2, "toggle_remote_console"); - binds_set(KEY_TAB, "+scoreboard"); - binds_set(KEY_F10, "screenshot"); - - binds_set('A', "+left"); - binds_set('D', "+right"); - binds_set(KEY_SPACE, "+jump"); - binds_set(KEY_MOUSE_1, "+fire"); - binds_set(KEY_MOUSE_2, "+hook"); - binds_set(KEY_LSHIFT, "+emote"); - - binds_set('1', "+weapon1"); - binds_set('2', "+weapon2"); - binds_set('3', "+weapon3"); - binds_set('4', "+weapon4"); - binds_set('5', "+weapon5"); - - binds_set(KEY_MOUSE_WHEEL_UP, "+prevweapon"); - binds_set(KEY_MOUSE_WHEEL_DOWN, "+nextweapon"); - - binds_set('T', "chat all"); - binds_set('Y', "chat team"); + // render prompt + TEXT_CURSOR cursor; + gfx_text_set_cursor(&cursor, x, y, font_size, TEXTFLAG_RENDER); + + const char *prompt = "> "; + if(console_type) + { + if(client_state() == CLIENTSTATE_ONLINE) + { + if(client_rcon_authed()) + prompt = "rcon> "; + else + prompt = "ENTER PASSWORD> "; + } + else + prompt = "NOT CONNECTED> "; + } + + gfx_text_ex(&cursor, prompt, -1); + + // render console input + gfx_text_ex(&cursor, console->input.get_string(), console->input.cursor_offset()); + TEXT_CURSOR marker = cursor; + gfx_text_ex(&marker, "|", -1); + gfx_text_ex(&cursor, console->input.get_string()+console->input.cursor_offset(), -1); + + // render version + char buf[128]; + str_format(buf, sizeof(buf), "v%s", GAME_VERSION); + float version_width = gfx_text_width(0, font_size, buf, -1); + gfx_text(0, screen.w-version_width-5, y, font_size, buf, -1); + + // render log + y -= row_height; + char *entry = (char *)ringbuf_last(console->backlog); + while (y > 0.0f && entry) + { + gfx_text(0, x, y, font_size, entry, -1); + y -= row_height; + + entry = (char *)ringbuf_prev(console->backlog, entry); + } + } } -static int get_key_id(const char *key_name) +void CONSOLE::on_message(int msgtype, void *rawmsg) { - // check for numeric - if(key_name[0] == '#') - { - int i = atoi(key_name+1); - if(i > 0 && i < KEY_LAST) - return i; // numeric - } - - // search for key - for(int i = 0; i < KEY_LAST; i++) - { - if(strcmp(key_name, inp_key_name(i)) == 0) - return i; - } - - return 0; } -static void con_bind(void *result, void *user_data) +bool CONSOLE::on_input(INPUT_EVENT e) { - const char *key_name = console_arg_string(result, 0); - int id = get_key_id(key_name); - - if(!id) - { - dbg_msg("binds", "key %s not found", key_name); - return; - } - - binds_set(id, console_arg_string(result, 1)); + return false; } -static void con_unbind(void *result, void *user_data) +/* +static void client_console_print(const char *str) { - const char *key_name = console_arg_string(result, 0); - int id = get_key_id(key_name); - - if(!id) - { - dbg_msg("binds", "key %s not found", key_name); - return; - } - - binds_set(id, ""); + // TODO: repair me + //local_console.print_line(str); } +void console_rcon_print(const char *line) +{ + // TODO: repair me + //remote_console.print_line(line); +}*/ -static void con_unbindall(void *result, void *user_data) +/* +static void con_team(void *result, void *user_data) { - binds_unbindall(); + send_switch_team(console_arg_int(result, 0)); } +static void con_say(void *result, void *user_data) +{ + chat_say(0, console_arg_string(result, 0)); +} -static void con_dump_binds(void *result, void *user_data) +static void con_sayteam(void *result, void *user_data) { - for(int i = 0; i < KEY_LAST; i++) - { - if(keybindings[i][0] == 0) - continue; - dbg_msg("binds", "%s (%d) = %s", inp_key_name(i), i, keybindings[i]); - } + chat_say(1, console_arg_string(result, 0)); } -void binds_save() +void send_kill(int client_id); + +static void con_kill(void *result, void *user_data) { - char buffer[256]; - char *end = buffer+sizeof(buffer)-8; - client_save_line("unbindall"); - for(int i = 0; i < KEY_LAST; i++) - { - if(keybindings[i][0] == 0) - continue; - str_format(buffer, sizeof(buffer), "bind %s ", inp_key_name(i)); - - // process the string. we need to escape some characters - const char *src = keybindings[i]; - char *dst = buffer + strlen(buffer); - *dst++ = '"'; - while(*src && dst < end) - { - if(*src == '"' || *src == '\\') // escape \ and " - *dst++ = '\\'; - *dst++ = *src++; - } - *dst++ = '"'; - *dst++ = 0; - - client_save_line(buffer); - } + send_kill(-1); } static void con_key_input_state(void *result, void *user_data) @@ -399,6 +375,8 @@ static void con_emote(void *result, void *user_data) send_emoticon(console_arg_int(result, 0)); } +extern void con_chat(void *result, void *user_data); + void client_console_init() { console_register_print_callback(client_console_print); @@ -515,142 +493,11 @@ void console_toggle(int type) console_type = type; } -// only defined for 0<=t<=1 -static float console_scale_func(float t) -{ - //return t; - return sinf(acosf(1.0f-t)); -} -void console_render() -{ - RECT screen = *ui_screen(); - float console_max_height = screen.h*3/5.0f; - float console_height; - - float progress = (time_now()-(state_change_end-state_change_duration))/float(state_change_duration); - - if (progress >= 1.0f) - { - if (console_state == CONSOLE_CLOSING) - console_state = CONSOLE_CLOSED; - else if (console_state == CONSOLE_OPENING) - console_state = CONSOLE_OPEN; - - progress = 1.0f; - } - - if (console_state == CONSOLE_CLOSED) - return; - - float console_height_scale; - - if (console_state == CONSOLE_OPENING) - console_height_scale = console_scale_func(progress); - else if (console_state == CONSOLE_CLOSING) - console_height_scale = console_scale_func(1.0f-progress); - else //if (console_state == CONSOLE_OPEN) - console_height_scale = console_scale_func(1.0f); - - console_height = console_height_scale*console_max_height; - - gfx_mapscreen(screen.x, screen.y, screen.w, screen.h); - - // do console shadow - gfx_texture_set(-1); - gfx_quads_begin(); - gfx_setcolorvertex(0, 0,0,0, 0.5f); - gfx_setcolorvertex(1, 0,0,0, 0.5f); - gfx_setcolorvertex(2, 0,0,0, 0.0f); - gfx_setcolorvertex(3, 0,0,0, 0.0f); - gfx_quads_drawTL(0,console_height,screen.w,10.0f); - gfx_quads_end(); - - // do background - gfx_texture_set(data->images[IMAGE_CONSOLE_BG].id); - gfx_quads_begin(); - gfx_setcolor(0.2f, 0.2f, 0.2f,0.9f); - if(console_type != 0) - gfx_setcolor(0.4f, 0.2f, 0.2f,0.9f); - gfx_quads_setsubset(0,-console_height*0.075f,screen.w*0.075f*0.5f,0); - gfx_quads_drawTL(0,0,screen.w,console_height); - gfx_quads_end(); - - // do small bar shadow - gfx_texture_set(-1); - gfx_quads_begin(); - gfx_setcolorvertex(0, 0,0,0, 0.0f); - gfx_setcolorvertex(1, 0,0,0, 0.0f); - gfx_setcolorvertex(2, 0,0,0, 0.25f); - gfx_setcolorvertex(3, 0,0,0, 0.25f); - gfx_quads_drawTL(0,console_height-20,screen.w,10); - gfx_quads_end(); - - // do the lower bar - gfx_texture_set(data->images[IMAGE_CONSOLE_BAR].id); - gfx_quads_begin(); - gfx_setcolor(1.0f, 1.0f, 1.0f, 0.9f); - gfx_quads_setsubset(0,0.1f,screen.w*0.015f,1-0.1f); - gfx_quads_drawTL(0,console_height-10.0f,screen.w,10.0f); - gfx_quads_end(); - - console_height -= 10.0f; - - CONSOLE *console = current_console(); - - { - float font_size = 10.0f; - float row_height = font_size*1.25f; - float x = 3; - float y = console_height - row_height - 2; - - // render prompt - TEXT_CURSOR cursor; - gfx_text_set_cursor(&cursor, x, y, font_size, TEXTFLAG_RENDER); - - const char *prompt = "> "; - if(console_type) - { - if(client_state() == CLIENTSTATE_ONLINE) - { - if(client_rcon_authed()) - prompt = "rcon> "; - else - prompt = "ENTER PASSWORD> "; - } - else - prompt = "NOT CONNECTED> "; - } - - gfx_text_ex(&cursor, prompt, -1); - - // render console input - gfx_text_ex(&cursor, console->input.get_string(), console->input.cursor_offset()); - TEXT_CURSOR marker = cursor; - gfx_text_ex(&marker, "|", -1); - gfx_text_ex(&cursor, console->input.get_string()+console->input.cursor_offset(), -1); - - // render version - char buf[128]; - str_format(buf, sizeof(buf), "v%s", GAME_VERSION); - float version_width = gfx_text_width(0, font_size, buf, -1); - gfx_text(0, screen.w-version_width-5, y, font_size, buf, -1); - - // render log - y -= row_height; - char *entry = (char *)ringbuf_last(console->backlog); - while (y > 0.0f && entry) - { - gfx_text(0, x, y, font_size, entry, -1); - y -= row_height; - - entry = (char *)ringbuf_prev(console->backlog, entry); - } - } -} int console_active() { return console_state != CONSOLE_CLOSED; } +*/ diff --git a/src/game/client/components/console.hpp b/src/game/client/components/console.hpp new file mode 100644 index 00000000..988e4ea3 --- /dev/null +++ b/src/game/client/components/console.hpp @@ -0,0 +1,54 @@ +extern "C" { + #include <engine/e_client_interface.h> + #include <engine/e_config.h> + #include <engine/e_console.h> + #include <engine/e_ringbuffer.h> + #include <engine/client/ec_font.h> +} + +#include <game/client/component.hpp> + +class CONSOLE : public COMPONENT +{ + class INSTANCE + { + public: + char history_data[65536]; + RINGBUFFER *history; + char *history_entry; + + char backlog_data[65536]; + RINGBUFFER *backlog; + + LINEINPUT input; + + int type; + + public: + INSTANCE(int t); + + void execute_line(const char *line); + + void on_input(INPUT_EVENT e); + void print_line(const char *line); + }; + + INSTANCE local_console; + INSTANCE remote_console; + + INSTANCE *current_console(); + float time_now(); + + int console_type; + int console_state; + float state_change_end; + float state_change_duration; + +public: + CONSOLE(); + + virtual void on_reset(); + virtual void on_render(); + virtual void on_message(int msgtype, void *rawmsg); + virtual bool on_input(INPUT_EVENT e); +}; diff --git a/src/game/client/components/controls.cpp b/src/game/client/components/controls.cpp new file mode 100644 index 00000000..9f0ba30a --- /dev/null +++ b/src/game/client/components/controls.cpp @@ -0,0 +1,184 @@ +extern "C" { + #include <engine/e_config.h> + #include <engine/e_console.h> + #include <engine/e_client_interface.h> +} + +#include <base/math.hpp> +#include <game/collision.hpp> +#include <game/client/gameclient.hpp> +#include <game/client/component.hpp> + +#include "controls.hpp" + +CONTROLS::CONTROLS() +{ +} + +static void con_key_input_state(void *result, void *user_data) +{ + ((int *)user_data)[0] = console_arg_int(result, 0); +} + +static void con_key_input_counter(void *result, void *user_data) +{ + int *v = (int *)user_data; + if(((*v)&1) != console_arg_int(result, 0)) + (*v)++; + *v &= INPUT_STATE_MASK; +} +/* +static void con_key_input_weapon(void *result, void *user_data) +{ + int w = (char *)user_data - (char *)0; + if(console_arg_int(result, 0)) + input_data.wanted_weapon = w; +} + +static void con_key_input_nextprev_weapon(void *result, void *user_data) +{ + con_key_input_counter(result, user_data); + input_data.wanted_weapon = 0; +}*/ + +void CONTROLS::on_init() +{ + // game commands + MACRO_REGISTER_COMMAND("+left", "", con_key_input_state, &input_direction_left); + MACRO_REGISTER_COMMAND("+right", "", con_key_input_state, &input_direction_right); + MACRO_REGISTER_COMMAND("+jump", "", con_key_input_state, &input_data.jump); + MACRO_REGISTER_COMMAND("+hook", "", con_key_input_state, &input_data.hook); + MACRO_REGISTER_COMMAND("+fire", "", con_key_input_counter, &input_data.fire); + /* + MACRO_REGISTER_COMMAND("+weapon1", "", con_key_input_weapon, (void *)1); + MACRO_REGISTER_COMMAND("+weapon2", "", con_key_input_weapon, (void *)2); + MACRO_REGISTER_COMMAND("+weapon3", "", con_key_input_weapon, (void *)3); + MACRO_REGISTER_COMMAND("+weapon4", "", con_key_input_weapon, (void *)4); + MACRO_REGISTER_COMMAND("+weapon5", "", con_key_input_weapon, (void *)5); + + MACRO_REGISTER_COMMAND("+nextweapon", "", con_key_input_nextprev_weapon, &input_data.next_weapon); + MACRO_REGISTER_COMMAND("+prevweapon", "", con_key_input_nextprev_weapon, &input_data.prev_weapon); + */ +} + +int CONTROLS::snapinput(int *data) +{ + static NETOBJ_PLAYER_INPUT last_data = {0}; + static int64 last_send_time = 0; + + // update player state + /*if(chat_mode != CHATMODE_NONE) // TODO: repair me + input_data.player_state = PLAYERSTATE_CHATTING; + else if(menu_active) + input_data.player_state = PLAYERSTATE_IN_MENU; + else + input_data.player_state = PLAYERSTATE_PLAYING;*/ + last_data.player_state = input_data.player_state; + + // we freeze the input if chat or menu is activated + /* repair me + if(menu_active || chat_mode != CHATMODE_NONE || console_active()) + { + last_data.direction = 0; + last_data.hook = 0; + last_data.jump = 0; + + input_data = last_data; + + mem_copy(data, &input_data, sizeof(input_data)); + return sizeof(input_data); + }*/ + + input_data.target_x = (int)mouse_pos.x; + input_data.target_y = (int)mouse_pos.y; + if(!input_data.target_x && !input_data.target_y) + input_data.target_y = 1; + + // set direction + input_data.direction = 0; + if(input_direction_left && !input_direction_right) + input_data.direction = -1; + if(!input_direction_left && input_direction_right) + input_data.direction = 1; + + // stress testing + if(config.dbg_stress) + { + float t = client_localtime(); + mem_zero(&input_data, sizeof(input_data)); + + input_data.direction = ((int)t/2)&1; + input_data.jump = ((int)t); + input_data.fire = ((int)(t*10)); + input_data.hook = ((int)(t*2))&1; + input_data.wanted_weapon = ((int)t)%NUM_WEAPONS; + input_data.target_x = (int)(sinf(t*3)*100.0f); + input_data.target_y = (int)(cosf(t*3)*100.0f); + } + + // check if we need to send input + bool send = false; + if(input_data.direction != last_data.direction) send = true; + else if(input_data.jump != last_data.jump) send = true; + else if(input_data.fire != last_data.fire) send = true; + else if(input_data.hook != last_data.hook) send = true; + else if(input_data.player_state != last_data.player_state) send = true; + else if(input_data.wanted_weapon != last_data.wanted_weapon) send = true; + else if(input_data.next_weapon != last_data.next_weapon) send = true; + else if(input_data.prev_weapon != last_data.prev_weapon) send = true; + + if(time_get() > last_send_time + time_freq()/5) + send = true; + + last_data = input_data; + if(!send) + return 0; + + // copy and return size + last_send_time = time_get(); + mem_copy(data, &input_data, sizeof(input_data)); + return sizeof(input_data); +} + +bool CONTROLS::on_mousemove(float x, float y) +{ + mouse_pos += vec2(x, y); // TODO: ugly + + bool spectate = false; + + // + float camera_max_distance = 200.0f; + float follow_factor = config.cl_mouse_followfactor/100.0f; + float deadzone = config.cl_mouse_deadzone; + float mouse_max = min(camera_max_distance/follow_factor + deadzone, (float)config.cl_mouse_max_distance); + + //vec2 camera_offset(0, 0); + + if(spectate) + { + if(mouse_pos.x < 200.0f) mouse_pos.x = 200.0f; + if(mouse_pos.y < 200.0f) mouse_pos.y = 200.0f; + if(mouse_pos.x > col_width()*32-200.0f) mouse_pos.x = col_width()*32-200.0f; + if(mouse_pos.y > col_height()*32-200.0f) mouse_pos.y = col_height()*32-200.0f; + + target_pos = mouse_pos; + } + else + { + float l = length(mouse_pos); + + if(l > mouse_max) + { + mouse_pos = normalize(mouse_pos)*mouse_max; + l = mouse_max; + } + + target_pos = gameclient.local_character_pos + mouse_pos; + + //float offset_amount = max(l-deadzone, 0.0f) * follow_factor; + //if(l > 0.0001f) // make sure that this isn't 0 + //camera_offset = normalize(mouse_pos)*offset_amount; + } + + return true; +} diff --git a/src/game/client/components/controls.hpp b/src/game/client/components/controls.hpp new file mode 100644 index 00000000..d875522a --- /dev/null +++ b/src/game/client/components/controls.hpp @@ -0,0 +1,19 @@ +#include <base/vmath.hpp> +#include <game/client/component.hpp> + +class CONTROLS : public COMPONENT +{ +public: + vec2 mouse_pos; + vec2 target_pos; + + NETOBJ_PLAYER_INPUT input_data; + int input_direction_left; + int input_direction_right; + + CONTROLS(); + virtual bool on_mousemove(float x, float y); + virtual void on_init(); + + int snapinput(int *data); +}; diff --git a/src/game/client/components/damageind.cpp b/src/game/client/components/damageind.cpp new file mode 100644 index 00000000..15ca4b18 --- /dev/null +++ b/src/game/client/components/damageind.cpp @@ -0,0 +1,66 @@ +#include <engine/e_client_interface.h> +#include <game/generated/g_protocol.hpp> +#include <game/generated/gc_data.hpp> + +#include <game/gamecore.hpp> // get_angle +#include <game/client/gc_ui.hpp> +#include <game/client/gc_render.hpp> +#include "damageind.hpp" + +DAMAGEIND::DAMAGEIND() +{ + lastupdate = 0; + num_items = 0; +} + +DAMAGEIND::ITEM *DAMAGEIND::create_i() +{ + if (num_items < MAX_ITEMS) + { + ITEM *p = &items[num_items]; + num_items++; + return p; + } + return 0; +} + +void DAMAGEIND::destroy_i(DAMAGEIND::ITEM *i) +{ + num_items--; + *i = items[num_items]; +} + +void DAMAGEIND::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 DAMAGEIND::on_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(); +} diff --git a/src/game/client/components/damageind.hpp b/src/game/client/components/damageind.hpp new file mode 100644 index 00000000..c74af9ca --- /dev/null +++ b/src/game/client/components/damageind.hpp @@ -0,0 +1,31 @@ +#include <base/vmath.hpp> +#include <game/client/component.hpp> + +class DAMAGEIND : public COMPONENT +{ + int64 lastupdate; + struct ITEM + { + vec2 pos; + vec2 dir; + float life; + float startangle; + }; + + enum + { + MAX_ITEMS=64, + }; + + ITEM items[MAX_ITEMS]; + int num_items; + + ITEM *create_i(); + void destroy_i(ITEM *i); + +public: + DAMAGEIND(); + + void create(vec2 pos, vec2 dir); + virtual void on_render(); +}; diff --git a/src/game/client/components/debughud.cpp b/src/game/client/components/debughud.cpp new file mode 100644 index 00000000..7ec7b88f --- /dev/null +++ b/src/game/client/components/debughud.cpp @@ -0,0 +1,113 @@ +#include <memory.h> // memcmp + +extern "C" { + #include <engine/e_config.h> +} + +#include <engine/e_client_interface.h> +#include <game/generated/g_protocol.hpp> +#include <game/generated/gc_data.hpp> + +#include <game/layers.hpp> + +#include <game/client/gameclient.hpp> +#include <game/client/animstate.hpp> +#include <game/client/gc_client.hpp> +#include <game/client/gc_render.hpp> + +//#include "controls.hpp" +//#include "camera.hpp" +#include "debughud.hpp" + +void DEBUGHUD::render_netcorrections() +{ + if(!config.debug || !gameclient.snap.local_character || !gameclient.snap.local_prev_character) + return; + + gfx_mapscreen(0, 0, 300*gfx_screenaspect(), 300); + + /*float speed = distance(vec2(netobjects.local_prev_character->x, netobjects.local_prev_character->y), + vec2(netobjects.local_character->x, netobjects.local_character->y));*/ + + float velspeed = length(vec2(gameclient.snap.local_character->vx/256.0f, gameclient.snap.local_character->vy/256.0f))*50; + + float ramp = velocity_ramp(velspeed, tuning.velramp_start, tuning.velramp_range, tuning.velramp_curvature); + + char buf[512]; + str_format(buf, sizeof(buf), "%.0f\n%.0f\n%.2f\n%d %s\n%d %d", + velspeed, velspeed*ramp, ramp, + netobj_num_corrections(), netobj_corrected_on(), + gameclient.snap.local_character->x, + gameclient.snap.local_character->y + ); + gfx_text(0, 150, 50, 12, buf, -1); +} + +void DEBUGHUD::render_tuning() +{ + // render tuning debugging + if(!config.dbg_tuning) + return; + + TUNING_PARAMS standard_tuning; + + gfx_mapscreen(0, 0, 300*gfx_screenaspect(), 300); + + float y = 50.0f; + int count = 0; + for(int i = 0; i < tuning.num(); i++) + { + char buf[128]; + float current, standard; + tuning.get(i, ¤t); + standard_tuning.get(i, &standard); + + if(standard == current) + gfx_text_color(1,1,1,1.0f); + else + gfx_text_color(1,0.25f,0.25f,1.0f); + + float w; + float x = 5.0f; + + str_format(buf, sizeof(buf), "%.2f", standard); + x += 20.0f; + w = gfx_text_width(0, 5, buf, -1); + gfx_text(0x0, x-w, y+count*6, 5, buf, -1); + + str_format(buf, sizeof(buf), "%.2f", current); + x += 20.0f; + w = gfx_text_width(0, 5, buf, -1); + gfx_text(0x0, x-w, y+count*6, 5, buf, -1); + + x += 5.0f; + gfx_text(0x0, x, y+count*6, 5, tuning.names[i], -1); + + count++; + } + + y = y+count*6; + + gfx_texture_set(-1); + gfx_blend_normal(); + gfx_lines_begin(); + float height = 50.0f; + float pv = 1; + for(int i = 0; i < 100; i++) + { + float speed = i/100.0f * 3000; + float ramp = velocity_ramp(speed, tuning.velramp_start, tuning.velramp_range, tuning.velramp_curvature); + float rampedspeed = (speed * ramp)/1000.0f; + gfx_lines_draw((i-1)*2, y+height-pv*height, i*2, y+height-rampedspeed*height); + //gfx_lines_draw((i-1)*2, 200, i*2, 200); + pv = rampedspeed; + } + gfx_lines_end(); + gfx_text_color(1,1,1,1); +} + +void DEBUGHUD::on_render() +{ + render_tuning(); + render_netcorrections(); +} diff --git a/src/game/client/components/debughud.hpp b/src/game/client/components/debughud.hpp new file mode 100644 index 00000000..473b2ce2 --- /dev/null +++ b/src/game/client/components/debughud.hpp @@ -0,0 +1,10 @@ +#include <game/client/component.hpp> + +class DEBUGHUD : public COMPONENT +{ + void render_netcorrections(); + void render_tuning(); +public: + virtual void on_render(); +}; + diff --git a/src/game/client/components/effects.cpp b/src/game/client/components/effects.cpp new file mode 100644 index 00000000..000d0f3a --- /dev/null +++ b/src/game/client/components/effects.cpp @@ -0,0 +1,240 @@ +#include <engine/e_client_interface.h> +//#include <gc_client.hpp> +#include <game/generated/gc_data.hpp> + +#include <game/client/components/particles.hpp> +#include <game/client/components/skins.hpp> +#include <game/client/components/flow.hpp> +#include <game/client/components/damageind.hpp> +#include <game/client/gameclient.hpp> +#include <game/client/gc_client.hpp> + +#include "effects.hpp" + +EFFECTS::EFFECTS() +{ + add_50hz = false; + add_100hz = false; +} + +void EFFECTS::air_jump(vec2 pos) +{ + PARTICLE p; + p.set_default(); + p.spr = SPRITE_PART_AIRJUMP; + p.pos = pos + vec2(-6.0f, 16.0f); + p.vel = vec2(0, -200); + p.life_span = 0.5f; + p.start_size = 48.0f; + p.end_size = 0; + p.rot = frandom()*pi*2; + p.rotspeed = pi*2; + p.gravity = 500; + p.friction = 0.7f; + p.flow_affected = 0.0f; + gameclient.particles->add(PARTICLES::GROUP_GENERAL, &p); + + p.pos = pos + vec2(6.0f, 16.0f); + gameclient.particles->add(PARTICLES::GROUP_GENERAL, &p); +} + +void EFFECTS::damage_indicator(vec2 pos, vec2 dir) +{ + gameclient.damageind->create(pos, dir); +} + +void EFFECTS::powerupshine(vec2 pos, vec2 size) +{ + if(!add_50hz) + return; + + PARTICLE p; + p.set_default(); + p.spr = SPRITE_PART_SLICE; + p.pos = pos + vec2((frandom()-0.5f)*size.x, (frandom()-0.5f)*size.y); + p.vel = vec2(0, 0); + p.life_span = 0.5f; + p.start_size = 16.0f; + p.end_size = 0; + p.rot = frandom()*pi*2; + p.rotspeed = pi*2; + p.gravity = 500; + p.friction = 0.9f; + p.flow_affected = 0.0f; + gameclient.particles->add(PARTICLES::GROUP_GENERAL, &p); +} + +void EFFECTS::smoketrail(vec2 pos, vec2 vel) +{ + if(!add_50hz) + return; + + PARTICLE p; + p.set_default(); + p.spr = SPRITE_PART_SMOKE; + p.pos = pos; + p.vel = vel + random_dir()*50.0f; + p.life_span = 0.5f + frandom()*0.5f; + p.start_size = 12.0f + frandom()*8; + p.end_size = 0; + p.friction = 0.7; + p.gravity = frandom()*-500.0f; + gameclient.particles->add(PARTICLES::GROUP_PROJECTILE_TRAIL, &p); +} + + +void EFFECTS::skidtrail(vec2 pos, vec2 vel) +{ + if(!add_100hz) + return; + + PARTICLE p; + p.set_default(); + p.spr = SPRITE_PART_SMOKE; + p.pos = pos; + p.vel = vel + random_dir()*50.0f; + p.life_span = 0.5f + frandom()*0.5f; + p.start_size = 24.0f + frandom()*12; + p.end_size = 0; + p.friction = 0.7f; + p.gravity = frandom()*-500.0f; + p.color = vec4(0.75f,0.75f,0.75f,1.0f); + gameclient.particles->add(PARTICLES::GROUP_GENERAL, &p); +} + +void EFFECTS::bullettrail(vec2 pos) +{ + if(!add_100hz) + return; + + PARTICLE p; + p.set_default(); + p.spr = SPRITE_PART_BALL; + p.pos = pos; + p.life_span = 0.25f + frandom()*0.25f; + p.start_size = 8.0f; + p.end_size = 0; + p.friction = 0.7f; + gameclient.particles->add(PARTICLES::GROUP_PROJECTILE_TRAIL, &p); +} + +void EFFECTS::playerspawn(vec2 pos) +{ + for(int i = 0; i < 32; i++) + { + PARTICLE p; + p.set_default(); + p.spr = SPRITE_PART_SHELL; + p.pos = pos; + p.vel = random_dir() * (pow(frandom(), 3)*600.0f); + p.life_span = 0.3f + frandom()*0.3f; + p.start_size = 64.0f + frandom()*32; + p.end_size = 0; + p.rot = frandom()*pi*2; + p.rotspeed = frandom(); + p.gravity = frandom()*-400.0f; + p.friction = 0.7f; + p.color = vec4(0xb5/255.0f, 0x50/255.0f, 0xcb/255.0f, 1.0f); + gameclient.particles->add(PARTICLES::GROUP_GENERAL, &p); + + } +} + +void EFFECTS::playerdeath(vec2 pos, int cid) +{ + vec3 blood_color(1.0f,1.0f,1.0f); + + if(cid >= 0) + { + const SKINS::SKIN *s = gameclient.skins->get(gameclient.clients[cid].skin_id); + if(s) + blood_color = s->blood_color; + } + + for(int i = 0; i < 64; i++) + { + PARTICLE p; + p.set_default(); + p.spr = SPRITE_PART_SPLAT01 + (rand()%3); + p.pos = pos; + p.vel = random_dir() * ((frandom()+0.1f)*900.0f); + p.life_span = 0.3f + frandom()*0.3f; + p.start_size = 24.0f + frandom()*16; + p.end_size = 0; + p.rot = frandom()*pi*2; + p.rotspeed = (frandom()-0.5f) * pi; + p.gravity = 800.0f; + p.friction = 0.8f; + vec3 c = blood_color * (0.75f + frandom()*0.25f); + p.color = vec4(c.r, c.g, c.b, 0.75f); + gameclient.particles->add(PARTICLES::GROUP_GENERAL, &p); + } +} + + +void EFFECTS::explosion(vec2 pos) +{ + // add to flow + for(int y = -8; y <= 8; y++) + for(int x = -8; x <= 8; x++) + { + if(x == 0 && y == 0) + continue; + + float a = 1 - (length(vec2(x,y)) / length(vec2(8,8))); + gameclient.flow->add(pos+vec2(x,y)*16, normalize(vec2(x,y))*5000.0f*a, 10.0f); + } + + // add the explosion + PARTICLE p; + p.set_default(); + p.spr = SPRITE_PART_EXPL01; + p.pos = pos; + p.life_span = 0.4f; + p.start_size = 150.0f; + p.end_size = 0; + p.rot = frandom()*pi*2; + gameclient.particles->add(PARTICLES::GROUP_EXPLOSIONS, &p); + + // add the smoke + for(int i = 0; i < 24; i++) + { + PARTICLE p; + p.set_default(); + p.spr = SPRITE_PART_SMOKE; + p.pos = pos; + p.vel = random_dir() * ((1.0f + frandom()*0.2f) * 1000.0f); + p.life_span = 0.5f + frandom()*0.4f; + p.start_size = 32.0f + frandom()*8; + p.end_size = 0; + p.gravity = frandom()*-800.0f; + p.friction = 0.4f; + p.color = mix(vec4(0.75f,0.75f,0.75f,1.0f), vec4(0.5f,0.5f,0.5f,1.0f), frandom()); + gameclient.particles->add(PARTICLES::GROUP_GENERAL, &p); + } +} + +void EFFECTS::on_render() +{ + static int64 last_update_100hz = 0; + static int64 last_update_50hz = 0; + + if(time_get()-last_update_100hz > time_freq()/100) + { + add_100hz = true; + last_update_100hz = time_get(); + } + else + add_100hz = false; + + if(time_get()-last_update_50hz > time_freq()/100) + { + add_50hz = true; + last_update_50hz = time_get(); + } + else + add_50hz = false; + + if(add_50hz) + gameclient.flow->update(); +} diff --git a/src/game/client/components/effects.hpp b/src/game/client/components/effects.hpp new file mode 100644 index 00000000..13af8947 --- /dev/null +++ b/src/game/client/components/effects.hpp @@ -0,0 +1,23 @@ +#include <game/client/component.hpp> + +class EFFECTS : public COMPONENT +{ + bool add_50hz; + bool add_100hz; +public: + EFFECTS(); + + virtual void on_render(); + + void bullettrail(vec2 pos); + void smoketrail(vec2 pos, vec2 vel); + void skidtrail(vec2 pos, vec2 vel); + void explosion(vec2 pos); + void air_jump(vec2 pos); + void damage_indicator(vec2 pos, vec2 dir); + void playerspawn(vec2 pos); + void playerdeath(vec2 pos, int cid); + void powerupshine(vec2 pos, vec2 size); + + void update(); +}; diff --git a/src/game/client/components/emoticon.cpp b/src/game/client/components/emoticon.cpp new file mode 100644 index 00000000..f359530a --- /dev/null +++ b/src/game/client/components/emoticon.cpp @@ -0,0 +1,121 @@ +#include <engine/e_client_interface.h> +#include <game/generated/g_protocol.hpp> +#include <game/generated/gc_data.hpp> + +#include <game/gamecore.hpp> // get_angle +#include <game/client/gc_ui.hpp> +#include <game/client/gc_render.hpp> +#include "emoticon.hpp" + +EMOTICON::EMOTICON() +{ + on_reset(); +} + +void EMOTICON::on_reset() +{ + selector_active = 0; + selected_emote = -1; +} + +void EMOTICON::on_message(int msgtype, void *rawmsg) +{ +} + +bool EMOTICON::on_input(INPUT_EVENT e) +{ + return false; +} + + +void EMOTICON::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 EMOTICON::on_render() +{ + int x, y; + inp_mouse_relative(&x, &y); + + selector_mouse.x += x; + selector_mouse.y += y; + + if (length(selector_mouse) > 140) + selector_mouse = normalize(selector_mouse) * 140; + + float selected_angle = get_angle(selector_mouse) + 2*pi/24; + if (selected_angle < 0) + selected_angle += 2*pi; + + if (length(selector_mouse) > 100) + selected_emote = (int)(selected_angle / (2*pi) * 12.0f); + + RECT screen = *ui_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_emote == 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(selector_mouse.x+screen.w/2,selector_mouse.y+screen.h/2,24,24); + gfx_quads_end(); +} + + + +void EMOTICON::emote(int emoticon) +{ + NETMSG_CL_EMOTICON msg; + msg.emoticon = emoticon; + msg.pack(MSGFLAG_VITAL); + client_send_msg(); +} diff --git a/src/game/client/components/emoticon.hpp b/src/game/client/components/emoticon.hpp new file mode 100644 index 00000000..17e977ab --- /dev/null +++ b/src/game/client/components/emoticon.hpp @@ -0,0 +1,22 @@ +#include <base/vmath.hpp> +#include <game/client/component.hpp> + +class EMOTICON : public COMPONENT +{ + void draw_circle(float x, float y, float r, int segments); + + vec2 selector_mouse; + int selector_active; + int selected_emote; + +public: + EMOTICON(); + + virtual void on_reset(); + virtual void on_render(); + virtual void on_message(int msgtype, void *rawmsg); + virtual bool on_input(INPUT_EVENT e); + + void emote(int emoticon); +}; + diff --git a/src/game/client/gc_flow.cpp b/src/game/client/components/flow.cpp index b18a41e6..b2f983e6 100644 --- a/src/game/client/gc_flow.cpp +++ b/src/game/client/components/flow.cpp @@ -1,19 +1,34 @@ -#include <engine/e_client_interface.h> -#include <engine/e_config.h> -#include "gc_client.hpp" -#include "../layers.hpp" +#include <game/mapitems.hpp> +#include <game/layers.hpp> +#include "flow.hpp" -struct FLOWCELL +FLOW::FLOW() { - vec2 vel; -}; + cells = 0; + height = 0; + width = 0; + spacing = 16; +} + +void FLOW::dbg_render() +{ + if(!cells) + return; -static FLOWCELL *cells = 0; -static int height = 0; -static int width = 0; -static int spacing = 16; + gfx_texture_set(-1); + gfx_lines_begin(); + for(int y = 0; y < height; y++) + for(int x = 0; x < width; x++) + { + vec2 pos(x*spacing, y*spacing); + vec2 vel = cells[y*width+x].vel * 0.01f; + gfx_lines_draw(pos.x, pos.y, pos.x+vel.x, pos.y+vel.y); + } + + gfx_lines_end(); +} -void flow_init() +void FLOW::init() { if(cells) { @@ -26,15 +41,15 @@ void flow_init() height = tilemap->height*32/spacing; // allocate and clear - cells = (FLOWCELL *)mem_alloc(sizeof(FLOWCELL)*width*height, 1); + cells = (CELL *)mem_alloc(sizeof(CELL)*width*height, 1); for(int y = 0; y < height; y++) for(int x = 0; x < width; x++) cells[y*width+x].vel = vec2(0.0f, 0.0f); } -void flow_update() +void FLOW::update() { - if(!config.cl_flow) + if(!cells) return; for(int y = 0; y < height; y++) @@ -42,46 +57,28 @@ void flow_update() cells[y*width+x].vel *= 0.85f; } -void flow_dbg_render() -{ - if(!config.cl_flow) - return; - - gfx_texture_set(-1); - gfx_lines_begin(); - for(int y = 0; y < height; y++) - for(int x = 0; x < width; x++) - { - vec2 pos(x*spacing, y*spacing); - vec2 vel = cells[y*width+x].vel * 0.01f; - gfx_lines_draw(pos.x, pos.y, pos.x+vel.x, pos.y+vel.y); - } - - gfx_lines_end(); -} - -void flow_add(vec2 pos, vec2 vel, float size) +vec2 FLOW::get(vec2 pos) { - if(!config.cl_flow) - return; - + if(!cells) + return vec2(0,0); + int x = (int)(pos.x / spacing); int y = (int)(pos.y / spacing); if(x < 0 || y < 0 || x >= width || y >= height) - return; + return vec2(0,0); - cells[y*width+x].vel += vel; + return cells[y*width+x].vel; } -vec2 flow_get(vec2 pos) +void FLOW::add(vec2 pos, vec2 vel, float size) { - if(!config.cl_flow) - return vec2(0,0); - + if(!cells) + return; + int x = (int)(pos.x / spacing); int y = (int)(pos.y / spacing); if(x < 0 || y < 0 || x >= width || y >= height) - return vec2(0,0); + return; - return cells[y*width+x].vel; + cells[y*width+x].vel += vel; } diff --git a/src/game/client/components/flow.hpp b/src/game/client/components/flow.hpp new file mode 100644 index 00000000..351b1f69 --- /dev/null +++ b/src/game/client/components/flow.hpp @@ -0,0 +1,25 @@ +#include <base/vmath.hpp> +#include <game/client/component.hpp> + +class FLOW : public COMPONENT +{ + struct CELL + { + vec2 vel; + }; + + CELL *cells; + int height; + int width; + int spacing; + + void dbg_render(); + void init(); +public: + FLOW(); + + vec2 get(vec2 pos); + void add(vec2 pos, vec2 vel, float size); + void update(); +}; + diff --git a/src/game/client/components/hud.cpp b/src/game/client/components/hud.cpp new file mode 100644 index 00000000..0e8d371c --- /dev/null +++ b/src/game/client/components/hud.cpp @@ -0,0 +1,273 @@ +#include <memory.h> // memcmp + +extern "C" { + #include <engine/e_config.h> +} + +#include <engine/e_client_interface.h> +#include <game/generated/g_protocol.hpp> +#include <game/generated/gc_data.hpp> + +#include <game/layers.hpp> + +#include <game/client/gameclient.hpp> +#include <game/client/animstate.hpp> +#include <game/client/gc_client.hpp> +#include <game/client/gc_render.hpp> + +#include "controls.hpp" +#include "camera.hpp" +#include "hud.hpp" + +HUD::HUD() +{ + +} + +void HUD::on_reset() +{ +} + +void HUD::render_goals() +{ + // TODO: split this up into these: + // render_gametimer + // render_suddendeath + // render_scorehud + // render_warmuptimer + + int gametype = gameclient.snap.gameobj->gametype; + int gameflags = gameclient.snap.gameobj->flags; + + float whole = 300*gfx_screenaspect(); + float half = whole/2.0f; + + + gfx_mapscreen(0,0,300*gfx_screenaspect(),300); + if(!gameclient.snap.gameobj->sudden_death) + { + char buf[32]; + int time = 0; + if(gameclient.snap.gameobj->time_limit) + { + time = gameclient.snap.gameobj->time_limit*60 - ((client_tick()-gameclient.snap.gameobj->round_start_tick)/client_tickspeed()); + + if(gameclient.snap.gameobj->game_over) + time = 0; + } + else + time = (client_tick()-gameclient.snap.gameobj->round_start_tick)/client_tickspeed(); + + str_format(buf, sizeof(buf), "%d:%02d", time /60, time %60); + float w = gfx_text_width(0, 16, buf, -1); + gfx_text(0, half-w/2, 2, 16, buf, -1); + } + + if(gameclient.snap.gameobj->sudden_death) + { + const char *text = "Sudden Death"; + float w = gfx_text_width(0, 16, text, -1); + gfx_text(0, half-w/2, 2, 16, text, -1); + } + + // render small score hud + if(!(gameclient.snap.gameobj && gameclient.snap.gameobj->game_over) && (gameflags&GAMEFLAG_TEAMS)) + { + 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(whole-40, 300-40-15+t*20, 50, 18, 5.0f); + gfx_quads_end(); + + char buf[32]; + str_format(buf, sizeof(buf), "%d", t?gameclient.snap.gameobj->teamscore_blue:gameclient.snap.gameobj->teamscore_red); + float w = gfx_text_width(0, 14, buf, -1); + + if(gametype == GAMETYPE_CTF) + { + gfx_text(0, whole-20-w/2+5, 300-40-15+t*20, 14, buf, -1); + if(gameclient.snap.flags[t]) + { + if(gameclient.snap.flags[t]->carried_by == -2 || (gameclient.snap.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(whole-40+5, 300-40-15+t*20+1, size/2, size); + gfx_quads_end(); + } + else if(gameclient.snap.flags[t]->carried_by >= 0) + { + int id = gameclient.snap.flags[t]->carried_by%MAX_CLIENTS; + const char *name = gameclient.clients[id].name; + float w = gfx_text_width(0, 10, name, -1); + gfx_text(0, whole-40-5-w, 300-40-15+t*20+2, 10, name, -1); + TEE_RENDER_INFO info = gameclient.clients[id].render_info; + info.size = 18.0f; + + render_tee(ANIMSTATE::get_idle(), &info, EMOTE_NORMAL, vec2(1,0), + vec2(whole-40+10, 300-40-15+9+t*20+1)); + } + } + } + else + gfx_text(0, whole-20-w/2, 300-40-15+t*20, 14, buf, -1); + } + } + + // render warmup timer + if(gameclient.snap.gameobj->warmup) + { + char buf[256]; + float w = gfx_text_width(0, 24, "Warmup", -1); + gfx_text(0, 150*gfx_screenaspect()+-w/2, 50, 24, "Warmup", -1); + + int seconds = gameclient.snap.gameobj->warmup/SERVER_TICK_SPEED; + if(seconds < 5) + str_format(buf, sizeof(buf), "%d.%d", seconds, (gameclient.snap.gameobj->warmup*10/SERVER_TICK_SPEED)%10); + else + str_format(buf, sizeof(buf), "%d", seconds); + w = gfx_text_width(0, 24, buf, -1); + gfx_text(0, 150*gfx_screenaspect()+-w/2, 75, 24, buf, -1); + } +} + +static void mapscreen_to_group(float center_x, float center_y, MAPITEM_GROUP *group) +{ + float points[4]; + mapscreen_to_world(center_x, center_y, group->parallax_x/100.0f, group->parallax_y/100.0f, + group->offset_x, group->offset_y, gfx_screenaspect(), 1.0f, points); + gfx_mapscreen(points[0], points[1], points[2], points[3]); +} + +void HUD::render_fps() +{ + if(config.cl_showfps) + { + char buf[512]; + str_format(buf, sizeof(buf), "%d", (int)(1.0f/client_frametime())); + gfx_text(0, width-10-gfx_text_width(0,12,buf,-1), 10, 12, buf, -1); + } +} + +void HUD::render_connectionwarning() +{ + if(client_connection_problems()) + { + const char *text = "Connection Problems..."; + float w = gfx_text_width(0, 24, text, -1); + gfx_text(0, 150*gfx_screenaspect()-w/2, 50, 24, text, -1); + } +} + +void HUD::render_tunewarning() +{ + TUNING_PARAMS standard_tuning; + + // render warning about non standard tuning + bool flash = time_get()/(time_freq()/2)%2 == 0; + if(config.cl_warning_tuning && memcmp(&standard_tuning, &tuning, sizeof(TUNING_PARAMS)) != 0) + { + const char *text = "Warning! Server is running non-standard tuning."; + if(flash) + gfx_text_color(1,0.4f,0.4f,1.0f); + else + gfx_text_color(0.75f,0.2f,0.2f,1.0f); + gfx_text(0x0, 5, 40, 6, text, -1); + gfx_text_color(1,1,1,1); + } +} + +void HUD::render_cursor() +{ + mapscreen_to_group(gameclient.camera->center.x, gameclient.camera->center.y, layers_game_group()); + gfx_texture_set(data->images[IMAGE_GAME].id); + gfx_quads_begin(); + + // render cursor + // TODO: repair me + //if (!menu_active) + { + dbg_msg("", "%f %f", gameclient.controls->target_pos.x, gameclient.controls->target_pos.y); + select_sprite(data->weapons.id[gameclient.snap.local_character->weapon%NUM_WEAPONS].sprite_cursor); + float cursorsize = 64; + draw_sprite(gameclient.controls->target_pos.x, gameclient.controls->target_pos.y, cursorsize); + } + gfx_quads_end(); +} + +void HUD::render_healthandammo() +{ + //mapscreen_to_group(gacenter_x, center_y, layers_game_group()); + + float x = 5; + float y = 5; + + // render ammo count + // render gui stuff + gfx_quads_begin(); + gfx_mapscreen(0,0,width,300); + + // if weaponstage is active, put a "glow" around the stage ammo + select_sprite(data->weapons.id[gameclient.snap.local_character->weapon%NUM_WEAPONS].sprite_proj); + for (int i = 0; i < min(gameclient.snap.local_character->ammocount, 10); 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 < gameclient.snap.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 < gameclient.snap.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(); +} + +void HUD::on_render() +{ + if(!gameclient.snap.gameobj) + return; + + width = 300*gfx_screenaspect(); + + bool spectate = false; + if(gameclient.snap.local_info && gameclient.snap.local_info->team == -1) + spectate = true; + + if(gameclient.snap.local_character && !spectate && !(gameclient.snap.gameobj && gameclient.snap.gameobj->game_over)) + render_healthandammo(); + + render_goals(); + render_fps(); + render_connectionwarning(); + render_tunewarning(); + render_cursor(); +} diff --git a/src/game/client/components/hud.hpp b/src/game/client/components/hud.hpp new file mode 100644 index 00000000..f8eb5e8f --- /dev/null +++ b/src/game/client/components/hud.hpp @@ -0,0 +1,21 @@ +#include <game/client/component.hpp> + +class HUD : public COMPONENT +{ + float width; + + void render_cursor(); + + void render_fps(); + void render_connectionwarning(); + void render_tunewarning(); + void render_healthandammo(); + void render_goals(); + +public: + HUD(); + + virtual void on_reset(); + virtual void on_render(); +}; + diff --git a/src/game/client/components/items.cpp b/src/game/client/components/items.cpp new file mode 100644 index 00000000..15cc7524 --- /dev/null +++ b/src/game/client/components/items.cpp @@ -0,0 +1,254 @@ +#include <engine/e_client_interface.h> +#include <game/generated/g_protocol.hpp> +#include <game/generated/gc_data.hpp> + +#include <game/gamecore.hpp> // get_angle +#include <game/client/gameclient.hpp> +#include <game/client/gc_client.hpp> +#include <game/client/gc_ui.hpp> +#include <game/client/gc_render.hpp> + +#include <game/client/components/flow.hpp> +#include <game/client/components/effects.hpp> + +#include "items.hpp" + +void ITEMS::render_projectile(const NETOBJ_PROJECTILE *current, int itemid) +{ + gfx_texture_set(data->images[IMAGE_GAME].id); + gfx_quads_begin(); + + // get positions + float curvature = 0; + float speed = 0; + if(current->type == WEAPON_GRENADE) + { + curvature = tuning.grenade_curvature; + speed = tuning.grenade_speed; + } + else if(current->type == WEAPON_SHOTGUN) + { + curvature = tuning.shotgun_curvature; + speed = tuning.shotgun_speed; + } + else if(current->type == WEAPON_GUN) + { + curvature = tuning.gun_curvature; + speed = tuning.gun_speed; + } + + float ct = (client_tick()-current->start_tick)/(float)SERVER_TICK_SPEED + client_ticktime()*1/(float)SERVER_TICK_SPEED; + vec2 startpos(current->x, current->y); + vec2 startvel(current->vx/100.0f, current->vy/100.0f); + vec2 pos = calc_pos(startpos, startvel, curvature, speed, ct); + vec2 prevpos = calc_pos(startpos, startvel, curvature, speed, ct-0.001f); + + select_sprite(data->weapons.id[clamp(current->type, 0, NUM_WEAPONS-1)].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 + if(current->type == WEAPON_GRENADE) + { + gameclient.effects->smoketrail(pos, vel*-1); + gameclient.flow->add(pos, vel*1000*client_frametime(), 10.0f); + gfx_quads_setrotation(client_localtime()*pi*2*2 + itemid); + } + else + { + gameclient.effects->bullettrail(pos); + gameclient.flow->add(pos, vel*1000*client_frametime(), 10.0f); + + if(length(vel) > 0.00001f) + gfx_quads_setrotation(get_angle(vel)); + else + gfx_quads_setrotation(0); + + } + + gfx_quads_draw(pos.x, pos.y, 32, 32); + gfx_quads_setrotation(0); + gfx_quads_end(); +} + +void ITEMS::render_pickup(const NETOBJ_PICKUP *prev, const NETOBJ_PICKUP *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.id[clamp(current->subtype, 0, NUM_WEAPONS-1)].sprite_body); + size = data->weapons.id[clamp(current->subtype, 0, NUM_WEAPONS-1)].visual_size; + } + else + { + const int c[] = { + SPRITE_PICKUP_HEALTH, + SPRITE_PICKUP_ARMOR, + SPRITE_PICKUP_WEAPON, + SPRITE_PICKUP_NINJA + }; + select_sprite(c[current->type]); + + if(c[current->type] == SPRITE_PICKUP_NINJA) + { + gameclient.effects->powerupshine(pos, vec2(96,18)); + 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(); +} + +void ITEMS::render_flag(const NETOBJ_FLAG *prev, const NETOBJ_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()); + + // make sure that the flag isn't interpolated between capture and return + if(prev->carried_by != current->carried_by) + pos = vec2(current->x, current->y); + + // make sure to use predicted position if we are the carrier + if(gameclient.snap.local_info && current->carried_by == gameclient.snap.local_info->cid) + pos = gameclient.local_character_pos; + + gfx_quads_draw(pos.x, pos.y-size*0.75f, size, size*2); + gfx_quads_end(); +} + + +void ITEMS::render_laser(const struct NETOBJ_LASER *current) +{ + vec2 pos = vec2(current->x, current->y); + vec2 from = vec2(current->from_x, current->from_y); + vec2 dir = normalize(pos-from); + + float ticks = client_tick() + client_intratick() - current->start_tick; + float ms = (ticks/50.0f) * 1000.0f; + float a = ms / tuning.laser_bounce_delay; + a = clamp(a, 0.0f, 1.0f); + float ia = 1-a; + + vec2 out, border; + + gfx_blend_normal(); + gfx_texture_set(-1); + gfx_quads_begin(); + + //vec4 inner_color(0.15f,0.35f,0.75f,1.0f); + //vec4 outer_color(0.65f,0.85f,1.0f,1.0f); + + // do outline + vec4 outer_color(0.075f,0.075f,0.25f,1.0f); + gfx_setcolor(outer_color.r,outer_color.g,outer_color.b,1.0f); + out = vec2(dir.y, -dir.x) * (7.0f*ia); + + gfx_quads_draw_freeform( + from.x-out.x, from.y-out.y, + from.x+out.x, from.y+out.y, + pos.x-out.x, pos.y-out.y, + pos.x+out.x, pos.y+out.y + ); + + // do inner + vec4 inner_color(0.5f,0.5f,1.0f,1.0f); + out = vec2(dir.y, -dir.x) * (5.0f*ia); + gfx_setcolor(inner_color.r, inner_color.g, inner_color.b, 1.0f); // center + + gfx_quads_draw_freeform( + from.x-out.x, from.y-out.y, + from.x+out.x, from.y+out.y, + pos.x-out.x, pos.y-out.y, + pos.x+out.x, pos.y+out.y + ); + + gfx_quads_end(); + + // render head + { + gfx_blend_normal(); + gfx_texture_set(data->images[IMAGE_PARTICLES].id); + gfx_quads_begin(); + + int sprites[] = {SPRITE_PART_SPLAT01, SPRITE_PART_SPLAT02, SPRITE_PART_SPLAT03}; + select_sprite(sprites[client_tick()%3]); + gfx_quads_setrotation(client_tick()); + gfx_setcolor(outer_color.r,outer_color.g,outer_color.b,1.0f); + gfx_quads_draw(pos.x, pos.y, 24,24); + gfx_setcolor(inner_color.r, inner_color.g, inner_color.b, 1.0f); + gfx_quads_draw(pos.x, pos.y, 20,20); + gfx_quads_end(); + } + + gfx_blend_normal(); +} + +void ITEMS::on_render() +{ + 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 == NETOBJTYPE_PROJECTILE) + { + render_projectile((const NETOBJ_PROJECTILE *)data, item.id); + } + else if(item.type == NETOBJTYPE_PICKUP) + { + const void *prev = snap_find_item(SNAP_PREV, item.type, item.id); + if(prev) + render_pickup((const NETOBJ_PICKUP *)prev, (const NETOBJ_PICKUP *)data); + } + else if(item.type == NETOBJTYPE_LASER) + { + render_laser((const NETOBJ_LASER *)data); + } + else if(item.type == NETOBJTYPE_FLAG) + { + const void *prev = snap_find_item(SNAP_PREV, item.type, item.id); + if (prev) + render_flag((const NETOBJ_FLAG *)prev, (const NETOBJ_FLAG *)data); + } + } + + // render extra projectiles + /* + for(int i = 0; i < extraproj_num; i++) + { + if(extraproj_projectiles[i].start_tick < client_tick()) + { + extraproj_projectiles[i] = extraproj_projectiles[extraproj_num-1]; + extraproj_num--; + } + else + render_projectile(&extraproj_projectiles[i], 0); + }*/ +} diff --git a/src/game/client/components/items.hpp b/src/game/client/components/items.hpp new file mode 100644 index 00000000..2f33c8c4 --- /dev/null +++ b/src/game/client/components/items.hpp @@ -0,0 +1,13 @@ +#include <game/client/component.hpp> + +class ITEMS : public COMPONENT +{ + void render_projectile(const NETOBJ_PROJECTILE *current, int itemid); + void render_pickup(const NETOBJ_PICKUP *prev, const NETOBJ_PICKUP *current); + void render_flag(const NETOBJ_FLAG *prev, const NETOBJ_FLAG *current); + void render_laser(const struct NETOBJ_LASER *current); + +public: + virtual void on_render(); +}; + diff --git a/src/game/client/components/killmessages.cpp b/src/game/client/components/killmessages.cpp new file mode 100644 index 00000000..41ba2e48 --- /dev/null +++ b/src/game/client/components/killmessages.cpp @@ -0,0 +1,130 @@ +#include <engine/e_client_interface.h> +#include <game/generated/g_protocol.hpp> +#include <game/generated/gc_data.hpp> + +#include <game/client/gameclient.hpp> +#include <game/client/animstate.hpp> +#include <game/client/gc_client.hpp> + +#include "killmessages.hpp" + +void KILLMESSAGES::on_reset() +{ + killmsg_current = 0; + for(int i = 0; i < killmsg_max; i++) + killmsgs[i].tick = -100000; +} + +void KILLMESSAGES::on_message(int msgtype, void *rawmsg) +{ + if(msgtype == NETMSGTYPE_SV_KILLMSG) + { + NETMSG_SV_KILLMSG *msg = (NETMSG_SV_KILLMSG *)rawmsg; + + // unpack messages + KILLMSG kill; + kill.killer = msg->killer; + kill.victim = msg->victim; + kill.weapon = msg->weapon; + kill.mode_special = msg->mode_special; + kill.tick = client_tick(); + + // add the message + killmsg_current = (killmsg_current+1)%killmsg_max; + killmsgs[killmsg_current] = kill; + } +} + +void KILLMESSAGES::on_render() +{ + float width = 400*3.0f*gfx_screenaspect(); + float height = 400*3.0f; + + 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 = 36.0f; + float killername_w = gfx_text_width(0, font_size, gameclient.clients[killmsgs[r].killer].name, -1); + float victimname_w = gfx_text_width(0, font_size, gameclient.clients[killmsgs[r].victim].name, -1); + + float x = startx; + + // render victim name + x -= victimname_w; + gfx_text(0, x, y, font_size, gameclient.clients[killmsgs[r].victim].name, -1); + + // render victim tee + x -= 24.0f; + + if(gameclient.snap.gameobj && gameclient.snap.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(gameclient.clients[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(ANIMSTATE::get_idle(), &gameclient.clients[killmsgs[r].victim].render_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.id[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(gameclient.snap.gameobj && gameclient.snap.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(gameclient.clients[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(ANIMSTATE::get_idle(), &gameclient.clients[killmsgs[r].killer].render_info, EMOTE_ANGRY, vec2(1,0), vec2(x, y+28)); + x -= 32.0f; + + // render killer name + x -= killername_w; + gfx_text(0, x, y, font_size, gameclient.clients[killmsgs[r].killer].name, -1); + } + + y += 44; + } +} diff --git a/src/game/client/components/killmessages.hpp b/src/game/client/components/killmessages.hpp new file mode 100644 index 00000000..f29e0bdf --- /dev/null +++ b/src/game/client/components/killmessages.hpp @@ -0,0 +1,24 @@ +#include <game/client/component.hpp> + +class KILLMESSAGES : public COMPONENT +{ +public: + // kill messages + 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]; + int killmsg_current; + + virtual void on_reset(); + virtual void on_render(); + virtual void on_message(int msgtype, void *rawmsg); +}; + diff --git a/src/game/client/components/maplayers.cpp b/src/game/client/components/maplayers.cpp new file mode 100644 index 00000000..8ed6264b --- /dev/null +++ b/src/game/client/components/maplayers.cpp @@ -0,0 +1,159 @@ + +extern "C" { + #include <engine/e_config.h> +} + +#include <game/layers.hpp> +#include <game/client/gameclient.hpp> +#include <game/client/component.hpp> +#include <game/client/gc_render.hpp> +#include <game/client/gc_map_image.hpp> + +#include <game/client/components/camera.hpp> + +#include "maplayers.hpp" + +MAPLAYERS::MAPLAYERS(int t) +{ + type = t; +} + + +static void mapscreen_to_group(float center_x, float center_y, MAPITEM_GROUP *group) +{ + float points[4]; + mapscreen_to_world(center_x, center_y, group->parallax_x/100.0f, group->parallax_y/100.0f, + group->offset_x, group->offset_y, gfx_screenaspect(), 1.0f, points); + gfx_mapscreen(points[0], points[1], points[2], points[3]); +} + + +static void envelope_eval(float time_offset, int env, float *channels) +{ + channels[0] = 0; + channels[1] = 0; + channels[2] = 0; + channels[3] = 0; + + ENVPOINT *points; + + { + int start, num; + map_get_type(MAPITEMTYPE_ENVPOINTS, &start, &num); + if(num) + points = (ENVPOINT *)map_get_item(start, 0, 0); + } + + int start, num; + map_get_type(MAPITEMTYPE_ENVELOPE, &start, &num); + + if(env >= num) + return; + + MAPITEM_ENVELOPE *item = (MAPITEM_ENVELOPE *)map_get_item(start+env, 0, 0); + render_eval_envelope(points+item->start_point, item->num_points, 4, client_localtime()+time_offset, channels); +} + +void MAPLAYERS::on_render() +{ + vec2 center = gameclient.camera->center; + //float center_x = gameclient.camera->center.x; + //float center_y = gameclient.camera->center.y; + + bool passed_gamelayer = false; + + for(int g = 0; g < layers_num_groups(); g++) + { + MAPITEM_GROUP *group = layers_get_group(g); + + if(group->version >= 2 && group->use_clipping) + { + // set clipping + float points[4]; + mapscreen_to_group(center.x, center.y, layers_game_group()); + gfx_getscreen(&points[0], &points[1], &points[2], &points[3]); + float x0 = (group->clip_x - points[0]) / (points[2]-points[0]); + float y0 = (group->clip_y - points[1]) / (points[3]-points[1]); + float x1 = ((group->clip_x+group->clip_w) - points[0]) / (points[2]-points[0]); + float y1 = ((group->clip_y+group->clip_h) - points[1]) / (points[3]-points[1]); + + gfx_clip_enable((int)(x0*gfx_screenwidth()), (int)(y0*gfx_screenheight()), + (int)((x1-x0)*gfx_screenwidth()), (int)((y1-y0)*gfx_screenheight())); + } + + mapscreen_to_group(center.x, center.y, group); + + for(int l = 0; l < group->num_layers; l++) + { + MAPITEM_LAYER *layer = layers_get_layer(group->start_layer+l); + bool render = false; + bool is_game_layer = false; + + // skip rendering if detail layers if not wanted + if(layer->flags&LAYERFLAG_DETAIL && !config.gfx_high_detail) + continue; + + if(layer == (MAPITEM_LAYER*)layers_game_layer()) + { + is_game_layer = true; + passed_gamelayer = 1; + } + + if(type == -1) + render = true; + else if(type == 0) + { + if(passed_gamelayer) + return; + render = true; + } + else + { + if(passed_gamelayer && !is_game_layer) + render = true; + } + + if(render && !is_game_layer) + { + //layershot_begin(); + + if(layer->type == LAYERTYPE_TILES) + { + MAPITEM_LAYER_TILEMAP *tmap = (MAPITEM_LAYER_TILEMAP *)layer; + if(tmap->image == -1) + gfx_texture_set(-1); + else + gfx_texture_set(img_get(tmap->image)); + + TILE *tiles = (TILE *)map_get_data(tmap->data); + gfx_blend_none(); + render_tilemap(tiles, tmap->width, tmap->height, 32.0f, vec4(1,1,1,1), TILERENDERFLAG_EXTEND|LAYERRENDERFLAG_OPAQUE); + gfx_blend_normal(); + render_tilemap(tiles, tmap->width, tmap->height, 32.0f, vec4(1,1,1,1), TILERENDERFLAG_EXTEND|LAYERRENDERFLAG_TRANSPARENT); + } + else if(layer->type == LAYERTYPE_QUADS) + { + MAPITEM_LAYER_QUADS *qlayer = (MAPITEM_LAYER_QUADS *)layer; + if(qlayer->image == -1) + gfx_texture_set(-1); + else + gfx_texture_set(img_get(qlayer->image)); + + QUAD *quads = (QUAD *)map_get_data_swapped(qlayer->data); + + gfx_blend_none(); + render_quads(quads, qlayer->num_quads, envelope_eval, LAYERRENDERFLAG_OPAQUE); + gfx_blend_normal(); + render_quads(quads, qlayer->num_quads, envelope_eval, LAYERRENDERFLAG_TRANSPARENT); + } + + //layershot_end(); + } + } + + gfx_clip_disable(); + } + + gfx_clip_disable(); +} + diff --git a/src/game/client/components/maplayers.hpp b/src/game/client/components/maplayers.hpp new file mode 100644 index 00000000..c2919f08 --- /dev/null +++ b/src/game/client/components/maplayers.hpp @@ -0,0 +1,16 @@ +#include <game/client/component.hpp> + +class MAPLAYERS : public COMPONENT +{ + int type; +public: + enum + { + TYPE_BACKGROUND=0, + TYPE_FOREGROUND, + }; + + MAPLAYERS(int type); + virtual void on_render(); +}; + diff --git a/src/game/client/components/menus.cpp b/src/game/client/components/menus.cpp new file mode 100644 index 00000000..fc409821 --- /dev/null +++ b/src/game/client/components/menus.cpp @@ -0,0 +1,1051 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include <stdio.h> +#include <math.h> +#include <string.h> +#include <stdlib.h> + +#include <base/system.h> +#include <base/math.hpp> +#include <base/vmath.hpp> + +#include "menus.hpp" +#include "skins.hpp" + +extern "C" { + #include <engine/e_client_interface.h> + #include <engine/e_config.h> + #include <engine/client/ec_font.h> +} + +#include <game/version.hpp> +#include <game/generated/g_protocol.hpp> + +#include <game/generated/gc_data.hpp> +#include <game/client/components/binds.hpp> +#include <game/client/gameclient.hpp> +#include <game/client/animstate.hpp> +#include <game/client/gc_render.hpp> +#include <game/client/gc_ui.hpp> +#include <game/client/gc_client.hpp> +#include <mastersrv/mastersrv.h> + +vec4 MENUS::gui_color; +vec4 MENUS::color_tabbar_inactive_outgame; +vec4 MENUS::color_tabbar_active_outgame; +vec4 MENUS::color_tabbar_inactive; +vec4 MENUS::color_tabbar_active; +vec4 MENUS::color_tabbar_inactive_ingame; +vec4 MENUS::color_tabbar_active_ingame; + + +MENUS::MENUS() +{ + popup = POPUP_NONE; + active_page = PAGE_INTERNET; + game_page = PAGE_GAME; + + need_restart = false; + menu_active = true; +} + +vec4 MENUS::button_color_mul(const void *id) +{ + if(ui_active_item() == id) + return vec4(1,1,1,0.5f); + else if(ui_hot_item() == id) + return vec4(1,1,1,1.5f); + return vec4(1,1,1,1); +} + +void MENUS::ui_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 == -1) + { + } + else 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(); +} + + +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); +} + +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); +} + +void MENUS::ui_draw_menu_tab_button(const void *id, const char *text, int checked, const RECT *r, const void *extra) +{ + if(checked) + ui_draw_rect(r, color_tabbar_active, CORNER_T, 10.0f); + else + ui_draw_rect(r, color_tabbar_inactive, CORNER_T, 10.0f); + ui_do_label(r, text, 22.0f, 0); +} + + +void MENUS::ui_draw_settings_tab_button(const void *id, const char *text, int checked, const RECT *r, const void *extra) +{ + if(checked) + 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); +} + +void MENUS::ui_draw_grid_header(const void *id, const char *text, int checked, const RECT *r, const void *extra) +{ + if(checked) + 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); +} + +void MENUS::ui_draw_list_row(const void *id, const char *text, int checked, const RECT *r, const void *extra) +{ + if(checked) + { + RECT sr = *r; + 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); +} + +void MENUS::ui_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; + ui_vsplit_l(&t, 5.0f, 0, &t); + + 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); +} + +void MENUS::ui_draw_checkbox(const void *id, const char *text, int checked, const RECT *r, const void *extra) +{ + ui_draw_checkbox_common(id, text, checked?"X":"", r); +} + + +void MENUS::ui_draw_checkbox_number(const void *id, const char *text, int checked, const RECT *r, const void *extra) +{ + char buf[16]; + str_format(buf, sizeof(buf), "%d", checked); + ui_draw_checkbox_common(id, text, buf, r); +} + +int MENUS::ui_do_edit_box(void *id, const RECT *rect, char *str, int str_size, float font_size, bool hidden) +{ + int inside = ui_mouse_inside(rect); + int r = 0; + static int at_index = 0; + + if(ui_last_active_item() == id) + { + 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_text_width(0, font_size, str, i) + 10 > mx_rel) + { + at_index = i - 1; + break; + } + + if (i == len) + at_index = len; + } + } + + for(int i = 0; i < inp_num_events(); i++) + { + INPUT_EVENT e = inp_get_event(i); + char c = e.ch; + int k = e.key; + + 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(e.flags&INPFLAG_PRESS) + { + 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; + } + + 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; + ui_draw_rect(&textbox, vec4(1,1,1,0.5f), CORNER_ALL, 5.0f); + ui_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; + } + + ui_do_label(&textbox, display_str, font_size, -1); + + if (ui_last_active_item() == id && !just_got_active) + { + float w = gfx_text_width(0, font_size, display_str, at_index); + textbox.x += w*ui_scale(); + ui_do_label(&textbox, "_", font_size, -1); + } + + return r; +} + +float MENUS::ui_do_scrollbar_v(const void *id, const RECT *rect, float current) +{ + RECT handle; + static float offset_y; + ui_hsplit_t(rect, 33, &handle, 0); + + handle.y += (rect->h-handle.h)*current; + + /* logic */ + float ret = current; + int inside = ui_mouse_inside(&handle); + + 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; + ui_vmargin(rect, 5.0f, &rail); + ui_draw_rect(&rail, vec4(1,1,1,0.25f), 0, 0.0f); + + RECT slider = handle; + slider.w = rail.x-slider.x; + ui_draw_rect(&slider, vec4(1,1,1,0.25f), CORNER_L, 2.5f); + slider.x = rail.x+rail.w; + ui_draw_rect(&slider, vec4(1,1,1,0.25f), CORNER_R, 2.5f); + + slider = handle; + ui_margin(&slider, 5.0f, &slider); + ui_draw_rect(&slider, vec4(1,1,1,0.25f)*button_color_mul(id), CORNER_ALL, 2.5f); + + return ret; +} + + + +float MENUS::ui_do_scrollbar_h(const void *id, const RECT *rect, float current) +{ + RECT handle; + static float offset_x; + ui_vsplit_l(rect, 33, &handle, 0); + + handle.x += (rect->w-handle.w)*current; + + /* logic */ + float ret = current; + int inside = ui_mouse_inside(&handle); + + 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; + ui_hmargin(rect, 5.0f, &rail); + ui_draw_rect(&rail, vec4(1,1,1,0.25f), 0, 0.0f); + + RECT slider = handle; + slider.h = rail.y-slider.y; + ui_draw_rect(&slider, vec4(1,1,1,0.25f), CORNER_T, 2.5f); + slider.y = rail.y+rail.h; + ui_draw_rect(&slider, vec4(1,1,1,0.25f), CORNER_B, 2.5f); + + slider = handle; + ui_margin(&slider, 5.0f, &slider); + ui_draw_rect(&slider, vec4(1,1,1,0.25f)*button_color_mul(id), CORNER_ALL, 2.5f); + + return ret; +} + +int MENUS::ui_do_key_reader(void *id, const RECT *rect, int key) +{ + // process + static bool mouse_released = true; + int inside = ui_mouse_inside(rect); + int new_key = key; + + if(!ui_mouse_button(0)) + mouse_released = true; + + if(ui_active_item() == id) + { + for(int i = 0; i < inp_num_events(); i++) + { + INPUT_EVENT e = inp_get_event(i); + if(e.flags&INPFLAG_PRESS && e.key && e.key != KEY_ESC) + { + new_key = e.key; + ui_set_active_item(0); + mouse_released = false; + inp_clear_events(); + break; + } + } + } + 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) + ui_draw_keyselect_button(id, "???", 0, rect, 0); + else + { + if(key == 0) + ui_draw_keyselect_button(id, "", 0, rect, 0); + else + ui_draw_keyselect_button(id, inp_key_name(key), 0, rect, 0); + } + return new_key; +} + + +int MENUS::render_menubar(RECT r) +{ + RECT box = r; + RECT button; + + int active_page = config.ui_page; + int new_page = -1; + + if(client_state() != CLIENTSTATE_OFFLINE) + active_page = game_page; + + if(client_state() == CLIENTSTATE_OFFLINE) + { + /* offline menus */ + if(0) // this is not done yet + { + ui_vsplit_l(&box, 90.0f, &button, &box); + static int news_button=0; + if (ui_do_button(&news_button, "News", active_page==PAGE_NEWS, &button, ui_draw_menu_tab_button, 0)) + new_page = PAGE_NEWS; + ui_vsplit_l(&box, 30.0f, 0, &box); + } + + ui_vsplit_l(&box, 110.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)) + { + client_serverbrowse_refresh(0); + new_page = PAGE_INTERNET; + } + + ui_vsplit_l(&box, 4.0f, 0, &box); + ui_vsplit_l(&box, 90.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)) + { + client_serverbrowse_refresh(1); + new_page = PAGE_LAN; + } + + if(0) // this one is not done yet + { + ui_vsplit_l(&box, 4.0f, 0, &box); + ui_vsplit_l(&box, 120.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)) + new_page = PAGE_FAVORITES; + } + + + } + else + { + /* online menus */ + ui_vsplit_l(&box, 90.0f, &button, &box); + static int game_button=0; + if (ui_do_button(&game_button, "Game", active_page==PAGE_GAME, &button, ui_draw_menu_tab_button, 0)) + new_page = PAGE_GAME; + + ui_vsplit_l(&box, 4.0f, 0, &box); + ui_vsplit_l(&box, 140.0f, &button, &box); + static int server_info_button=0; + if (ui_do_button(&server_info_button, "Server Info", active_page==PAGE_SERVER_INFO, &button, ui_draw_menu_tab_button, 0)) + new_page = PAGE_SERVER_INFO; + + ui_vsplit_l(&box, 30.0f, 0, &box); + } + + /* + ui_vsplit_r(&box, 110.0f, &box, &button); + static int system_button=0; + if (ui_do_button(&system_button, "System", config.ui_page==PAGE_SYSTEM, &button, ui_draw_menu_tab_button, 0)) + config.ui_page = PAGE_SYSTEM; + + ui_vsplit_r(&box, 30.0f, &box, 0); + */ + + ui_vsplit_r(&box, 110.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); + 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; + + if(new_page != -1) + { + if(client_state() == CLIENTSTATE_OFFLINE) + config.ui_page = new_page; + else + game_page = new_page; + } + + return 0; +} + +void MENUS::render_background() +{ + RECT s = *ui_screen(); + + gfx_texture_set(-1); + gfx_quads_begin(); + vec4 bottom(gui_color.r*0.6f, gui_color.g*0.6f, gui_color.b*0.6f, 1.0f); + vec4 top(gui_color.r, gui_color.g, gui_color.b, 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, s.w, s.h); + gfx_quads_end(); + + if(data->images[IMAGE_BANNER].id != 0) + { + 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(); + } +} + +void MENUS::render_loading(float percent) +{ + // need up date this here to get correct + 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); + + RECT screen = *ui_screen(); + gfx_mapscreen(screen.x, screen.y, screen.w, screen.h); + + render_background(); + + 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_text_width(0, 48.0f, caption, -1); + RECT r; + r.x = x; + r.y = y+20; + r.w = w; + r.h = h; + ui_do_label(&r, caption, 48.0f, 0, -1); + + gfx_texture_set(-1); + gfx_quads_begin(); + gfx_setcolor(1,1,1,0.75f); + draw_round_rect(x+40, y+h-75, (w-80)*percent, 25, 5.0f); + gfx_quads_end(); + + gfx_swap(); +} + +void MENUS::render_news(RECT main_view) +{ + ui_draw_rect(&main_view, color_tabbar_active, CORNER_ALL, 10.0f); +} + +void MENUS::render_game(RECT main_view) +{ + RECT button; + ui_hsplit_t(&main_view, 45.0f, &main_view, 0); + ui_draw_rect(&main_view, color_tabbar_active, CORNER_ALL, 10.0f); + + ui_hsplit_t(&main_view, 10.0f, 0, &main_view); + ui_hsplit_t(&main_view, 25.0f, &main_view, 0); + ui_vmargin(&main_view, 10.0f, &main_view); + + ui_vsplit_r(&main_view, 120.0f, &main_view, &button); + static int disconnect_button = 0; + if(ui_do_button(&disconnect_button, "Disconnect", 0, &button, ui_draw_menu_button, 0)) + client_disconnect(); + + if(gameclient.snap.local_info && gameclient.snap.gameobj) + { + if(gameclient.snap.local_info->team != -1) + { + ui_vsplit_l(&main_view, 10.0f, &button, &main_view); + ui_vsplit_l(&main_view, 120.0f, &button, &main_view); + static int spectate_button = 0; + if(ui_do_button(&spectate_button, "Spectate", 0, &button, ui_draw_menu_button, 0)) + { + gameclient.send_switch_team(-1); + menu_active = false; + } + } + + if(gameclient.snap.gameobj->flags & GAMEFLAG_TEAMS) + { + if(gameclient.snap.local_info->team != 0) + { + ui_vsplit_l(&main_view, 10.0f, &button, &main_view); + ui_vsplit_l(&main_view, 120.0f, &button, &main_view); + static int spectate_button = 0; + if(ui_do_button(&spectate_button, "Join Red", 0, &button, ui_draw_menu_button, 0)) + { + gameclient.send_switch_team(0); + menu_active = false; + } + } + + if(gameclient.snap.local_info->team != 1) + { + ui_vsplit_l(&main_view, 10.0f, &button, &main_view); + ui_vsplit_l(&main_view, 120.0f, &button, &main_view); + static int spectate_button = 0; + if(ui_do_button(&spectate_button, "Join Blue", 0, &button, ui_draw_menu_button, 0)) + { + gameclient.send_switch_team(1); + menu_active = false; + } + } + } + else + { + if(gameclient.snap.local_info->team != 0) + { + ui_vsplit_l(&main_view, 10.0f, &button, &main_view); + ui_vsplit_l(&main_view, 120.0f, &button, &main_view); + static int spectate_button = 0; + if(ui_do_button(&spectate_button, "Join Game", 0, &button, ui_draw_menu_button, 0)) + { + gameclient.send_switch_team(0); + menu_active = false; + } + } + } + } +} + +void MENUS::render_serverinfo(RECT main_view) +{ + // render background + ui_draw_rect(&main_view, color_tabbar_active, CORNER_ALL, 10.0f); + + // render motd + RECT view; + ui_margin(&main_view, 10.0f, &view); + //void gfx_text(void *font, float x, float y, float size, const char *text, int max_width); + // TODO: repair me + //gfx_text(0, view.x, view.y, 16, server_motd, -1); +} + +void MENUS::init() +{ + if(config.cl_show_welcome) + popup = POPUP_FIRST_LAUNCH; + config.cl_show_welcome = 0; +} + +int MENUS::render() +{ + RECT screen = *ui_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 + { + 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 + ui_margin(&screen, 10.0f, &screen); + + if(popup == POPUP_NONE) + { + // do tab bar + ui_hsplit_t(&screen, 26.0f, &tab_bar, &main_view); + ui_vmargin(&tab_bar, 20.0f, &tab_bar); + render_menubar(tab_bar); + + // render current page + if(client_state() != CLIENTSTATE_OFFLINE) + { + if(game_page == PAGE_GAME) + render_game(main_view); + else if(game_page == PAGE_SERVER_INFO) + render_serverinfo(main_view); + else if(game_page == PAGE_SETTINGS) + render_settings(main_view); + } + else if(config.ui_page == PAGE_NEWS) + render_news(main_view); + else if(config.ui_page == PAGE_INTERNET) + render_serverbrowser(main_view); + else if(config.ui_page == PAGE_LAN) + render_serverbrowser(main_view); + else if(config.ui_page == PAGE_FAVORITES) + render_serverbrowser(main_view); + else if(config.ui_page == PAGE_SETTINGS) + 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); + char buf[128]; + const char *title = ""; + const char *extra_text = ""; + const char *button_text = ""; + int extra_align = 0; + + if(popup == POPUP_CONNECTING) + { + title = "Connecting to"; + extra_text = config.ui_server_address; // TODO: query the client about the address + button_text = "Abort"; + if(client_mapdownload_totalsize() > 0) + { + title = "Downloading map"; + str_format(buf, sizeof(buf), "%d/%d KiB", client_mapdownload_amount()/1024, client_mapdownload_totalsize()/1024); + extra_text = buf; + } + } + else if(popup == POPUP_DISCONNECTED) + { + title = "Disconnected"; + extra_text = client_error_string(); + button_text = "Ok"; + extra_align = -1; + } + 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?"; + } + else if(popup == POPUP_FIRST_LAUNCH) + { + title = "Welcome to Teeworlds"; + extra_text = + "As this is the first time you launch the game, please enter your nick name below. " + "It's recommended that you check the settings to adjust them to your liking " + "before joining a server."; + button_text = "Ok"; + extra_align = -1; + } + + RECT box, part; + box = screen; + ui_vmargin(&box, 150.0f, &box); + ui_hmargin(&box, 150.0f, &box); + + // render the box + ui_draw_rect(&box, vec4(0,0,0,0.5f), CORNER_ALL, 15.0f); + + ui_hsplit_t(&box, 20.f, &part, &box); + ui_hsplit_t(&box, 24.f, &part, &box); + ui_do_label(&part, title, 24.f, 0); + ui_hsplit_t(&box, 20.f, &part, &box); + ui_hsplit_t(&box, 24.f, &part, &box); + ui_vmargin(&part, 20.f, &part); + + if(extra_align == -1) + ui_do_label(&part, extra_text, 20.f, -1, (int)part.w); + else + ui_do_label(&part, extra_text, 20.f, 0, -1); + + if(popup == POPUP_QUIT) + { + RECT yes, no; + ui_hsplit_b(&box, 20.f, &box, &part); + ui_hsplit_b(&box, 24.f, &box, &part); + ui_vmargin(&part, 80.0f, &part); + + ui_vsplit_mid(&part, &no, &yes); + + ui_vmargin(&yes, 20.0f, &yes); + ui_vmargin(&no, 20.0f, &no); + + static int button_abort = 0; + if(ui_do_button(&button_abort, "No", 0, &no, ui_draw_menu_button, 0) || inp_key_down(KEY_ESC)) + popup = POPUP_NONE; + + static int button_tryagain = 0; + if(ui_do_button(&button_tryagain, "Yes", 0, &yes, ui_draw_menu_button, 0) || inp_key_down(KEY_ENTER)) + client_quit(); + } + else if(popup == POPUP_PASSWORD) + { + RECT label, textbox, tryagain, abort; + + ui_hsplit_b(&box, 20.f, &box, &part); + ui_hsplit_b(&box, 24.f, &box, &part); + ui_vmargin(&part, 80.0f, &part); + + ui_vsplit_mid(&part, &abort, &tryagain); + + ui_vmargin(&tryagain, 20.0f, &tryagain); + ui_vmargin(&abort, 20.0f, &abort); + + static int button_abort = 0; + if(ui_do_button(&button_abort, "Abort", 0, &abort, ui_draw_menu_button, 0) || inp_key_down(KEY_ESC)) + popup = POPUP_NONE; + + static int button_tryagain = 0; + if(ui_do_button(&button_tryagain, "Try again", 0, &tryagain, ui_draw_menu_button, 0) || inp_key_down(KEY_ENTER)) + { + client_connect(config.ui_server_address); + } + + ui_hsplit_b(&box, 60.f, &box, &part); + ui_hsplit_b(&box, 24.f, &box, &part); + + ui_vsplit_l(&part, 60.0f, 0, &label); + ui_vsplit_l(&label, 100.0f, 0, &textbox); + ui_vsplit_l(&textbox, 20.0f, 0, &textbox); + ui_vsplit_r(&textbox, 60.0f, &textbox, 0); + ui_do_label(&label, "Password:", 20, -1); + ui_do_edit_box(&config.password, &textbox, config.password, sizeof(config.password), 14.0f, true); + } + else if(popup == POPUP_FIRST_LAUNCH) + { + RECT label, textbox; + + ui_hsplit_b(&box, 20.f, &box, &part); + ui_hsplit_b(&box, 24.f, &box, &part); + ui_vmargin(&part, 80.0f, &part); + + static int enter_button = 0; + if(ui_do_button(&enter_button, "Enter", 0, &part, ui_draw_menu_button, 0) || inp_key_down(KEY_ENTER)) + popup = POPUP_NONE; + + ui_hsplit_b(&box, 40.f, &box, &part); + ui_hsplit_b(&box, 24.f, &box, &part); + + ui_vsplit_l(&part, 60.0f, 0, &label); + ui_vsplit_l(&label, 100.0f, 0, &textbox); + ui_vsplit_l(&textbox, 20.0f, 0, &textbox); + ui_vsplit_r(&textbox, 60.0f, &textbox, 0); + ui_do_label(&label, "Nickname:", 20, -1); + ui_do_edit_box(&config.player_name, &textbox, config.player_name, sizeof(config.player_name), 14.0f); + } + else + { + ui_hsplit_b(&box, 20.f, &box, &part); + ui_hsplit_b(&box, 24.f, &box, &part); + ui_vmargin(&part, 120.0f, &part); + + static int button = 0; + if(ui_do_button(&button, button_text, 0, &part, ui_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 MENUS::on_reset() +{ +} + +bool MENUS::on_mousemove(float x, float y) +{ + if(!menu_active) + return false; + + mouse_pos.x += x; + mouse_pos.y += y; + if(mouse_pos.x < 0) mouse_pos.x = 0; + if(mouse_pos.y < 0) mouse_pos.y = 0; + if(mouse_pos.x > gfx_screenwidth()) mouse_pos.x = gfx_screenwidth(); + if(mouse_pos.y > gfx_screenheight()) mouse_pos.y = gfx_screenheight(); + + return true; +} + +bool MENUS::on_input(INPUT_EVENT e) +{ + return false; +} + +void MENUS::on_statechange(int new_state, int old_state) +{ + if(new_state == CLIENTSTATE_OFFLINE) + { + 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; + } } + else if(new_state == CLIENTSTATE_LOADING) + popup = POPUP_CONNECTING; + else if(new_state == CLIENTSTATE_CONNECTING) + popup = POPUP_CONNECTING; + else if (new_state == CLIENTSTATE_ONLINE) + { + popup = POPUP_NONE; + menu_active = false; + //menu_game_active = true; + //snapshot_count = 0; + } +} + +void MENUS::on_render() +{ + if(!menu_active) + return; + + // 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); + + float color_ingame_scale_i = 0.5f; + float 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); + + // update the ui + RECT *screen = ui_screen(); + float mx = (mouse_pos.x/(float)gfx_screenwidth())*screen->w; + float my = (mouse_pos.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); + + // render + render(); + + // render cursor + 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(); + + // render debug information + if(config.debug) + { + RECT screen = *ui_screen(); + gfx_mapscreen(screen.x, screen.y, screen.w, screen.h); + + char buf[512]; + str_format(buf, sizeof(buf), "%p %p %p", ui_hot_item(), ui_active_item(), ui_last_active_item()); + TEXT_CURSOR cursor; + gfx_text_set_cursor(&cursor, 10, 10, 10, TEXTFLAG_RENDER); + gfx_text_ex(&cursor, buf, -1); + } + +} diff --git a/src/game/client/components/menus.hpp b/src/game/client/components/menus.hpp new file mode 100644 index 00000000..4d45c65a --- /dev/null +++ b/src/game/client/components/menus.hpp @@ -0,0 +1,96 @@ +#include <base/vmath.hpp> + +#include <game/client/component.hpp> +#include <game/client/gc_ui.hpp> + +class MENUS : public COMPONENT +{ + static vec4 gui_color; + static vec4 color_tabbar_inactive_outgame; + static vec4 color_tabbar_active_outgame; + static vec4 color_tabbar_inactive_ingame; + static vec4 color_tabbar_active_ingame; + static vec4 color_tabbar_inactive; + static vec4 color_tabbar_active; + + static vec4 button_color_mul(const void *id); + + 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); + static void ui_draw_menu_tab_button(const void *id, const char *text, int checked, const RECT *r, const void *extra); + static void ui_draw_settings_tab_button(const void *id, const char *text, int checked, const RECT *r, const void *extra); + static void ui_draw_grid_header(const void *id, const char *text, int checked, const RECT *r, const void *extra); + static void ui_draw_list_row(const void *id, const char *text, int checked, const RECT *r, const void *extra); + static void ui_draw_checkbox_common(const void *id, const char *text, const char *boxtext, const RECT *r); + static void ui_draw_checkbox(const void *id, const char *text, int checked, const RECT *r, const void *extra); + static void ui_draw_checkbox_number(const void *id, const char *text, int checked, const RECT *r, const void *extra); + static int ui_do_edit_box(void *id, const RECT *rect, char *str, int str_size, float font_size, bool hidden=false); + + static float ui_do_scrollbar_v(const void *id, const RECT *rect, float current); + static float ui_do_scrollbar_h(const void *id, const RECT *rect, float current); + + static int ui_do_key_reader(void *id, const RECT *rect, int key); + + + enum + { + POPUP_NONE=0, + POPUP_FIRST_LAUNCH, + POPUP_CONNECTING, + POPUP_DISCONNECTED, + POPUP_PASSWORD, + POPUP_QUIT, + }; + + enum + { + PAGE_NEWS=0, + PAGE_GAME, + PAGE_SERVER_INFO, + PAGE_INTERNET, + PAGE_LAN, + PAGE_FAVORITES, + PAGE_SETTINGS, + PAGE_SYSTEM, + }; + + int game_page; + int popup; + int active_page; + bool menu_active; + vec2 mouse_pos; + + // for graphic settings + bool need_restart; + + // found in menus.cpp + int render(); + void render_background(); + void render_loading(float percent); + int render_menubar(RECT r); + void render_news(RECT main_view); + void render_game(RECT main_view); + void render_serverinfo(RECT main_view); + + // found in menus_browser.cpp + void render_serverbrowser(RECT main_view); + + // found in menus_settings.cpp + void render_settings_player(RECT main_view); + void render_settings_controls(RECT main_view); + void render_settings_graphics(RECT main_view); + void render_settings_sound(RECT main_view); + void render_settings(RECT main_view); + +public: + MENUS(); + + void init(); + + virtual void on_statechange(int new_state, int old_state); + virtual void on_reset(); + virtual void on_render(); + virtual bool on_input(INPUT_EVENT e); + virtual bool on_mousemove(float x, float y); +}; diff --git a/src/game/client/components/menus_browser.cpp b/src/game/client/components/menus_browser.cpp new file mode 100644 index 00000000..05ca620b --- /dev/null +++ b/src/game/client/components/menus_browser.cpp @@ -0,0 +1,531 @@ + +#include <string.h> // strcmp, strlen, strncpy +#include <stdlib.h> // atoi + +extern "C" { + #include <engine/e_client_interface.h> + #include <engine/e_config.h> + #include <engine/client/ec_font.h> +} + +#include <game/generated/g_protocol.hpp> +#include <game/generated/gc_data.hpp> + +#include <game/client/gc_ui.hpp> +#include <game/client/gc_render.hpp> +#include "menus.hpp" + +void MENUS::render_serverbrowser(RECT main_view) +{ + ui_draw_rect(&main_view, color_tabbar_active, CORNER_ALL, 10.0f); + + RECT view; + ui_margin(&main_view, 10.0f, &view); + + RECT headers; + RECT filters; + RECT status; + RECT toolbox; + RECT server_details; + RECT server_scoreboard; + + //ui_hsplit_t(&view, 20.0f, &status, &view); + ui_hsplit_b(&view, 110.0f, &view, &filters); + + // split off a piece for details and scoreboard + ui_vsplit_r(&view, 200.0f, &view, &server_details); + + // server list + ui_hsplit_t(&view, 16.0f, &headers, &view); + //ui_hsplit_b(&view, 110.0f, &view, &filters); + ui_hsplit_b(&view, 5.0f, &view, 0); + ui_hsplit_b(&view, 20.0f, &view, &status); + + //ui_vsplit_r(&filters, 300.0f, &filters, &toolbox); + //ui_vsplit_r(&filters, 150.0f, &filters, 0); + + ui_vsplit_mid(&filters, &filters, &toolbox); + ui_vsplit_r(&filters, 50.0f, &filters, 0); + + // split of the scrollbar + ui_draw_rect(&headers, vec4(1,1,1,0.25f), CORNER_T, 5.0f); + ui_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, + COL_VERSION, + }; + + 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_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) + { + ui_vsplit_l(&headers, cols[i].width, &cols[i].rect, &headers); + + if(i+1 < num_cols) + { + //cols[i].flags |= SPACER; + ui_vsplit_l(&headers, 2, &cols[i].spacer, &headers); + } + } + } + + for(int i = num_cols-1; i >= 0; i--) + { + if(cols[i].direction == 1) + { + ui_vsplit_r(&headers, cols[i].width, &headers, &cols[i].rect); + ui_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(ui_do_button(cols[i].caption, cols[i].caption, config.b_sort == cols[i].sort, &cols[i].rect, ui_draw_grid_header, 0)) + { + if(cols[i].sort != -1) + { + if(config.b_sort == cols[i].sort) + config.b_sort_order ^= 1; + else + config.b_sort_order = 0; + config.b_sort = cols[i].sort; + } + } + } + + ui_draw_rect(&view, vec4(0,0,0,0.15f), 0, 0); + + RECT scroll; + ui_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; + ui_hmargin(&scroll, 5.0f, &scroll); + scrollvalue = ui_do_scrollbar_v(&scrollbar, &scroll, scrollvalue); + + int scrollnum = num_servers-num+10; + if(scrollnum > 0) + { + if(inp_key_presses(KEY_MOUSE_WHEEL_UP)) + scrollvalue -= 1.0f/scrollnum; + if(inp_key_presses(KEY_MOUSE_WHEEL_DOWN)) + scrollvalue += 1.0f/scrollnum; + + if(scrollvalue < 0) scrollvalue = 0; + if(scrollvalue > 1) scrollvalue = 1; + } + else + scrollnum = 0; + + // set clipping + ui_clip_enable(&view); + + int start = (int)(scrollnum*scrollvalue); + if(start < 0) + start = 0; + + RECT original_view = view; + view.y -= scrollvalue*scrollnum*cols[0].rect.h; + + int new_selected = -1; + int selected_index = -1; + int num_players = 0; + + for (int i = 0; i < num_servers; i++) + { + SERVER_INFO *item = client_serverbrowse_sorted_get(i); + num_players += item->num_players; + } + + for (int i = 0; i < num_servers; i++) + { + int item_index = i; + SERVER_INFO *item = client_serverbrowse_sorted_get(item_index); + RECT row; + RECT select_hit_box; + + int selected = strcmp(item->address, config.ui_server_address) == 0; //selected_index==item_index; + + ui_hsplit_t(&view, 17.0f, &row, &view); + select_hit_box = row; + + if(selected) + { + 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); + } + + + // make sure that only those in view can be selected + if(row.y+row.h > original_view.y) + { + if(select_hit_box.y < original_view.y) // clip the selection + { + select_hit_box.h -= original_view.y-select_hit_box.y; + select_hit_box.y = original_view.y; + } + + if(ui_do_button(item, "", selected, &select_hit_box, 0, 0)) + { + new_selected = item_index; + } + } + + // check if we need to do more + if(row.y > original_view.y+original_view.h) + break; + + 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 = ui_do_button(item, "L", l, &button, ui_draw_browse_icon, 0); + + if(id == COL_FLAGS) + { + if(item->flags&1) + ui_draw_browse_icon(0x100, &button); + } + else if(id == COL_NAME) + { + TEXT_CURSOR cursor; + gfx_text_set_cursor(&cursor, button.x, button.y, 12.0f, TEXTFLAG_RENDER); + + if(config.b_filter_string[0] && (item->quicksearch_hit&BROWSEQUICK_SERVERNAME)) + { + // highlight the parts that matches + const char *s = str_find_nocase(item->name, config.b_filter_string); + if(s) + { + gfx_text_ex(&cursor, item->name, (int)(s-item->name)); + gfx_text_color(0.4f,0.4f,1.0f,1); + gfx_text_ex(&cursor, s, strlen(config.b_filter_string)); + gfx_text_color(1,1,1,1); + gfx_text_ex(&cursor, s+strlen(config.b_filter_string), -1); + } + else + gfx_text_ex(&cursor, item->name, -1); + } + else + gfx_text_ex(&cursor, item->name, -1); + } + else if(id == COL_MAP) + ui_do_label(&button, item->map, 12.0f, -1); + else if(id == COL_PLAYERS) + { + str_format(temp, sizeof(temp), "%i/%i", item->num_players, item->max_players); + if(config.b_filter_string[0] && (item->quicksearch_hit&BROWSEQUICK_PLAYERNAME)) + gfx_text_color(0.4f,0.4f,1.0f,1); + ui_do_label(&button, temp, 12.0f, 1); + gfx_text_color(1,1,1,1); + } + else if(id == COL_PING) + { + str_format(temp, sizeof(temp), "%i", item->latency); + ui_do_label(&button, temp, 12.0f, 1); + } + else if(id == COL_PROGRESS) + { + if(item->progression > 100) + item->progression = 100; + ui_draw_browse_icon(item->progression, &button); + } + else if(id == COL_VERSION) + { + const char *version = item->version; + if(strcmp(version, "0.3 e2d7973c6647a13c") == 0) // TODO: remove me later on + version = "0.3.0"; + ui_do_label(&button, version, 12.0f, 1); + } + else if(id == COL_GAMETYPE) + ui_do_label(&button, item->gametype, 12.0f, 0); + } + } + + ui_clip_disable(); + + if(new_selected != -1) + { + // select the new server + SERVER_INFO *item = client_serverbrowse_sorted_get(new_selected); + strncpy(config.ui_server_address, item->address, sizeof(config.ui_server_address)); + if(inp_mouse_doubleclick()) + client_connect(config.ui_server_address); + } + + SERVER_INFO *selected_server = client_serverbrowse_sorted_get(selected_index); + RECT server_header; + + ui_vsplit_l(&server_details, 10.0f, 0x0, &server_details); + + // split off a piece to use for scoreboard + ui_hsplit_t(&server_details, 140.0f, &server_details, &server_scoreboard); + ui_hsplit_b(&server_details, 10.0f, &server_details, 0x0); + + // server details + const float font_size = 12.0f; + ui_hsplit_t(&server_details, 20.0f, &server_header, &server_details); + ui_draw_rect(&server_header, vec4(1,1,1,0.25f), CORNER_T, 4.0f); + ui_draw_rect(&server_details, vec4(0,0,0,0.15f), CORNER_B, 4.0f); + ui_vsplit_l(&server_header, 8.0f, 0x0, &server_header); + ui_do_label(&server_header, "Server Details: ", font_size+2.0f, -1); + + ui_vsplit_l(&server_details, 5.0f, 0x0, &server_details); + + ui_margin(&server_details, 3.0f, &server_details); + + if (selected_server) + { + RECT row; + static const char *labels[] = { "Version:", "Game Type:", "Progression:", "Ping:" }; + + RECT left_column; + RECT right_column; + + ui_vsplit_l(&server_details, 5.0f, 0x0, &server_details); + ui_vsplit_l(&server_details, 80.0f, &left_column, &right_column); + + for (int i = 0; i < 4; i++) + { + ui_hsplit_t(&left_column, 15.0f, &row, &left_column); + ui_do_label(&row, labels[i], font_size, -1); + } + + ui_hsplit_t(&right_column, 15.0f, &row, &right_column); + ui_do_label(&row, selected_server->version, font_size, -1); + + ui_hsplit_t(&right_column, 15.0f, &row, &right_column); + ui_do_label(&row, selected_server->gametype, font_size, -1); + + char temp[16]; + + if(selected_server->progression < 0) + str_format(temp, sizeof(temp), "N/A"); + else + str_format(temp, sizeof(temp), "%d%%", selected_server->progression); + ui_hsplit_t(&right_column, 15.0f, &row, &right_column); + ui_do_label(&row, temp, font_size, -1); + + str_format(temp, sizeof(temp), "%d", selected_server->latency); + ui_hsplit_t(&right_column, 15.0f, &row, &right_column); + ui_do_label(&row, temp, font_size, -1); + } + + // server scoreboard + ui_hsplit_b(&server_scoreboard, 10.0f, &server_scoreboard, 0x0); + ui_hsplit_t(&server_scoreboard, 20.0f, &server_header, &server_scoreboard); + ui_draw_rect(&server_header, vec4(1,1,1,0.25f), CORNER_T, 4.0f); + ui_draw_rect(&server_scoreboard, vec4(0,0,0,0.15f), CORNER_B, 4.0f); + ui_vsplit_l(&server_header, 8.0f, 0x0, &server_header); + ui_do_label(&server_header, "Scoreboard: ", font_size+2.0f, -1); + + ui_vsplit_l(&server_scoreboard, 5.0f, 0x0, &server_scoreboard); + + ui_margin(&server_scoreboard, 3.0f, &server_scoreboard); + + if (selected_server) + { + for (int i = 0; i < selected_server->num_players; i++) + { + RECT row; + char temp[16]; + ui_hsplit_t(&server_scoreboard, 16.0f, &row, &server_scoreboard); + + str_format(temp, sizeof(temp), "%d", selected_server->players[i].score); + ui_do_label(&row, temp, font_size, -1); + + ui_vsplit_l(&row, 25.0f, 0x0, &row); + + TEXT_CURSOR cursor; + gfx_text_set_cursor(&cursor, row.x, row.y, 12.0f, TEXTFLAG_RENDER); + + const char *name = selected_server->players[i].name; + if(config.b_filter_string[0]) + { + // highlight the parts that matches + const char *s = str_find_nocase(name, config.b_filter_string); + if(s) + { + gfx_text_ex(&cursor, name, (int)(s-name)); + gfx_text_color(0.4f,0.4f,1,1); + gfx_text_ex(&cursor, s, strlen(config.b_filter_string)); + gfx_text_color(1,1,1,1); + gfx_text_ex(&cursor, s+strlen(config.b_filter_string), -1); + } + else + gfx_text_ex(&cursor, name, -1); + } + else + gfx_text_ex(&cursor, name, -1); + + /*ui_do_label(&row, selected_server->player_names[i], font_size, -1);*/ + } + } + + RECT button; + RECT types; + ui_hsplit_t(&filters, 20.0f, &button, &filters); + ui_do_label(&button, "Quick search: ", 14.0f, -1); + ui_vsplit_l(&button, 95.0f, 0, &button); + ui_do_edit_box(&config.b_filter_string, &button, config.b_filter_string, sizeof(config.b_filter_string), 14.0f); + + ui_vsplit_l(&filters, 180.0f, &filters, &types); + + // render filters + ui_hsplit_t(&filters, 20.0f, &button, &filters); + if (ui_do_button(&config.b_filter_empty, "Has people playing", config.b_filter_empty, &button, ui_draw_checkbox, 0)) + config.b_filter_empty ^= 1; + + ui_hsplit_t(&filters, 20.0f, &button, &filters); + if (ui_do_button(&config.b_filter_full, "Server not full", config.b_filter_full, &button, ui_draw_checkbox, 0)) + config.b_filter_full ^= 1; + + ui_hsplit_t(&filters, 20.0f, &button, &filters); + if (ui_do_button(&config.b_filter_pw, "No password", config.b_filter_pw, &button, ui_draw_checkbox, 0)) + config.b_filter_pw ^= 1; + + ui_hsplit_t(&filters, 20.0f, &button, &filters); + if (ui_do_button((char *)&config.b_filter_compatversion, "Compatible Version", config.b_filter_compatversion, &button, ui_draw_checkbox, 0)) + config.b_filter_compatversion ^= 1; + + // game types + /* + ui_hsplit_t(&types, 20.0f, &button, &types); + if (ui_do_button(&config.b_filter_gametype, "DM", config.b_filter_gametype&(1<<GAME_TYPE_DM), &button, ui_draw_checkbox, 0)) + config.b_filter_gametype ^= (1<<GAME_TYPE_DM); + + ui_hsplit_t(&types, 20.0f, &button, &types); + if (ui_do_button((char *)&config.b_filter_gametype + 1, "TDM", config.b_filter_gametype&(1<<GAME_TYPE_TDM), &button, ui_draw_checkbox, 0)) + config.b_filter_gametype ^= (1<<GAME_TYPE_TDM); + + ui_hsplit_t(&types, 20.0f, &button, &types); + if (ui_do_button((char *)&config.b_filter_gametype + 2, "CTF", config.b_filter_gametype&(1<<GAME_TYPE_CTF), &button, ui_draw_checkbox, 0)) + config.b_filter_gametype ^= (1<<GAME_TYPE_CTF); + */ + + // ping + ui_hsplit_t(&types, 2.0f, &button, &types); + ui_hsplit_t(&types, 20.0f, &button, &types); + { + RECT editbox; + ui_vsplit_l(&button, 40.0f, &editbox, &button); + ui_vsplit_l(&button, 5.0f, &button, &button); + + char buf[8]; + str_format(buf, sizeof(buf), "%d", config.b_filter_ping); + ui_do_edit_box(&config.b_filter_ping, &editbox, buf, sizeof(buf), 14.0f); + config.b_filter_ping = atoi(buf); + + ui_do_label(&button, "Maximum ping", 14.0f, -1); + } + + + // render status + ui_draw_rect(&status, vec4(1,1,1,0.25f), CORNER_B, 5.0f); + ui_vmargin(&status, 50.0f, &status); + char buf[128]; + str_format(buf, sizeof(buf), "%d of %d servers, %d players", client_serverbrowse_sorted_num(), client_serverbrowse_num(), num_players); + ui_do_label(&status, buf, 14.0f, -1); + + // render toolbox + { + RECT buttons, button; + ui_hsplit_b(&toolbox, 25.0f, &toolbox, &buttons); + + ui_vsplit_r(&buttons, 100.0f, &buttons, &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)) + client_connect(config.ui_server_address); + + ui_vsplit_r(&buttons, 20.0f, &buttons, &button); + ui_vsplit_r(&buttons, 100.0f, &buttons, &button); + ui_vmargin(&button, 2.0f, &button); + static int refresh_button = 0; + if(ui_do_button(&refresh_button, "Refresh", 0, &button, ui_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); + } + + //ui_vsplit_r(&buttons, 30.0f, &buttons, &button); + ui_vsplit_l(&buttons, 120.0f, &button, &buttons); + static int clear_button = 0; + if(ui_do_button(&clear_button, "Reset Filter", 0, &button, ui_draw_menu_button, 0)) + { + config.b_filter_full = 0; + config.b_filter_empty = 0; + config.b_filter_pw = 0; + config.b_filter_ping = 999; + config.b_filter_gametype = 0xf; + config.b_filter_compatversion = 1; + config.b_filter_string[0] = 0; + } + + + ui_hsplit_t(&toolbox, 20.0f, &button, &toolbox); + ui_do_label(&button, "Host address:", 14.0f, -1); + ui_vsplit_l(&button, 100.0f, 0, &button); + ui_do_edit_box(&config.ui_server_address, &button, config.ui_server_address, sizeof(config.ui_server_address), 14.0f); + } +} diff --git a/src/game/client/components/menus_settings.cpp b/src/game/client/components/menus_settings.cpp new file mode 100644 index 00000000..f031c20f --- /dev/null +++ b/src/game/client/components/menus_settings.cpp @@ -0,0 +1,567 @@ + +#include <base/math.hpp> + +#include <string.h> // strcmp, strlen, strncpy +#include <stdlib.h> // atoi + +extern "C" { + #include <engine/e_client_interface.h> + #include <engine/e_config.h> + #include <engine/client/ec_font.h> +} + +#include <game/generated/g_protocol.hpp> +#include <game/generated/gc_data.hpp> + +#include <game/client/gc_ui.hpp> +#include <game/client/gc_render.hpp> +#include <game/client/gameclient.hpp> +#include <game/client/animstate.hpp> + +#include "binds.hpp" +#include "menus.hpp" +#include "skins.hpp" + +void MENUS::render_settings_player(RECT main_view) +{ + RECT button; + RECT skinselection; + ui_vsplit_l(&main_view, 300.0f, &main_view, &skinselection); + + + ui_hsplit_t(&main_view, 20.0f, &button, &main_view); + + // render settings + { + ui_hsplit_t(&main_view, 20.0f, &button, &main_view); + ui_do_label(&button, "Name:", 14.0, -1); + ui_vsplit_l(&button, 80.0f, 0, &button); + ui_vsplit_l(&button, 180.0f, &button, 0); + ui_do_edit_box(config.player_name, &button, config.player_name, sizeof(config.player_name), 14.0f); + + static int dynamic_camera_button = 0; + ui_hsplit_t(&main_view, 20.0f, &button, &main_view); + if(ui_do_button(&dynamic_camera_button, "Dynamic Camera", config.cl_mouse_deadzone != 0, &button, ui_draw_checkbox, 0)) + { + + if(config.cl_mouse_deadzone) + { + config.cl_mouse_followfactor = 0; + config.cl_mouse_max_distance = 400; + config.cl_mouse_deadzone = 0; + } + else + { + config.cl_mouse_followfactor = 60; + config.cl_mouse_max_distance = 1000; + config.cl_mouse_deadzone = 300; + } + } + + ui_hsplit_t(&main_view, 20.0f, &button, &main_view); + if (ui_do_button(&config.cl_autoswitch_weapons, "Switch weapon on pickup", config.cl_autoswitch_weapons, &button, ui_draw_checkbox, 0)) + config.cl_autoswitch_weapons ^= 1; + + ui_hsplit_t(&main_view, 20.0f, &button, &main_view); + if (ui_do_button(&config.cl_nameplates, "Show name plates", config.cl_nameplates, &button, ui_draw_checkbox, 0)) + config.cl_nameplates ^= 1; + + //if(config.cl_nameplates) + { + ui_hsplit_t(&main_view, 20.0f, &button, &main_view); + ui_vsplit_l(&button, 15.0f, 0, &button); + if (ui_do_button(&config.cl_nameplates_always, "Always show name plates", config.cl_nameplates_always, &button, ui_draw_checkbox, 0)) + config.cl_nameplates_always ^= 1; + } + + ui_hsplit_t(&main_view, 20.0f, &button, &main_view); + + ui_hsplit_t(&main_view, 20.0f, &button, &main_view); + if (ui_do_button(&config.player_color_body, "Custom colors", config.player_use_custom_color, &button, ui_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; + ui_hsplit_t(&main_view, 20.0f, &text, &main_view); + ui_vsplit_l(&text, 15.0f, 0, &text); + ui_do_label(&text, parts[i], 14.0f, -1); + + int prevcolor = *colors[i]; + int color = 0; + for(int s = 0; s < 3; s++) + { + RECT text; + ui_hsplit_t(&main_view, 19.0f, &button, &main_view); + ui_vsplit_l(&button, 30.0f, 0, &button); + ui_vsplit_l(&button, 30.0f, &text, &button); + ui_vsplit_r(&button, 5.0f, &button, 0); + ui_hsplit_t(&button, 4.0f, 0, &button); + + float k = ((prevcolor>>((2-s)*8))&0xff) / 255.0f; + k = ui_do_scrollbar_h(&color_slider[i][s], &button, k); + color <<= 8; + color += clamp((int)(k*255), 0, 255); + ui_do_label(&text, labels[s], 15.0f, -1); + + } + + *colors[i] = color; + ui_hsplit_t(&main_view, 5.0f, 0, &main_view); + } + } + } + + // draw header + RECT header, footer; + ui_hsplit_t(&skinselection, 20, &header, &skinselection); + ui_draw_rect(&header, vec4(1,1,1,0.25f), CORNER_T, 5.0f); + ui_do_label(&header, "Skins", 18.0f, 0); + + // draw footers + ui_hsplit_b(&skinselection, 20, &skinselection, &footer); + ui_draw_rect(&footer, vec4(1,1,1,0.25f), CORNER_B, 5.0f); + ui_vsplit_l(&footer, 10.0f, 0, &footer); + + // modes + ui_draw_rect(&skinselection, vec4(0,0,0,0.15f), 0, 0); + + RECT scroll; + ui_vsplit_r(&skinselection, 15, &skinselection, &scroll); + + RECT list = skinselection; + ui_hsplit_t(&list, 50, &button, &list); + + int num = (int)(skinselection.h/button.h); + static float scrollvalue = 0; + static int scrollbar = 0; + ui_hmargin(&scroll, 5.0f, &scroll); + scrollvalue = ui_do_scrollbar_v(&scrollbar, &scroll, scrollvalue); + + int start = (int)((gameclient.skins->num()-num)*scrollvalue); + if(start < 0) + start = 0; + + for(int i = start; i < start+num && i < gameclient.skins->num(); i++) + { + const SKINS::SKIN *s = gameclient.skins->get(i); + + // no special skins + if(s->name[0] == 'x' && s->name[1] == '_') + { + num++; + continue; + } + + char buf[128]; + str_format(buf, sizeof(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 = gameclient.skins->get_color(config.player_color_body); + info.color_feet = gameclient.skins->get_color(config.player_color_feet); + info.texture = s->color_texture; + } + + info.size = ui_scale()*50.0f; + + RECT icon; + RECT text; + ui_vsplit_l(&button, 50.0f, &icon, &text); + + if(ui_do_button(s, "", selected, &button, ui_draw_list_row, 0)) + config_set_player_skin(&config, s->name); + + ui_hsplit_t(&text, 12.0f, 0, &text); // some margin from the top + ui_do_label(&text, buf, 18.0f, 0); + + ui_hsplit_t(&icon, 5.0f, 0, &icon); // some margin from the top + render_tee(ANIMSTATE::get_idle(), &info, 0, vec2(1, 0), vec2(icon.x+icon.w/2, icon.y+icon.h/2)); + + if(config.debug) + { + gfx_texture_set(-1); + gfx_quads_begin(); + gfx_setcolor(s->blood_color.r, s->blood_color.g, s->blood_color.b, 1.0f); + gfx_quads_drawTL(icon.x, icon.y, 12, 12); + gfx_quads_end(); + } + + ui_hsplit_t(&list, 50, &button, &list); + } +} + +typedef void (*assign_func_callback)(CONFIGURATION *config, int value); + +void MENUS::render_settings_controls(RECT main_view) +{ + RECT right_part; + ui_vsplit_l(&main_view, 300.0f, &main_view, &right_part); + + { + RECT button, label; + ui_hsplit_t(&main_view, 20.0f, &button, &main_view); + ui_vsplit_l(&button, 110.0f, &label, &button); + ui_do_label(&label, "Mouse sens.", 14.0f, -1); + ui_hmargin(&button, 2.0f, &button); + config.inp_mousesens = (int)(ui_do_scrollbar_h(&config.inp_mousesens, &button, (config.inp_mousesens-5)/500.0f)*500.0f)+5; + //*key.key = ui_do_key_reader(key.key, &button, *key.key); + ui_hsplit_t(&main_view, 20.0f, 0, &main_view); + } + + typedef struct + { + const char *name; + const char *command; + int keyid; + } KEYINFO; + + KEYINFO keys[] = + { + { "Move Left:", "+left", 0}, + { "Move Right:", "+right", 0 }, + { "Jump:", "+jump", 0 }, + { "Fire:", "+fire", 0 }, + { "Hook:", "+hook", 0 }, + { "Hammer:", "+weapon1", 0 }, + { "Pistol:", "+weapon2", 0 }, + { "Shotgun:", "+weapon3", 0 }, + { "Grenade:", "+weapon4", 0 }, + { "Rifle:", "+weapon5", 0 }, + { "Next Weapon:", "+nextweapon", 0 }, + { "Prev. Weapon:", "+prevweapon", 0 }, + { "Emoticon:", "+emote", 0 }, + { "Chat:", "chat all", 0 }, + { "Team Chat:", "chat team", 0 }, + { "Console:", "toggle_local_console", 0 }, + { "Remote Console:", "toggle_remote_console", 0 }, + { "Screenshot:", "screenshot", 0 }, + }; + + const int key_count = sizeof(keys) / sizeof(KEYINFO); + + // this is kinda slow, but whatever + for(int keyid = 0; keyid < KEY_LAST; keyid++) + { + const char *bind = gameclient.binds->get(keyid); + if(!bind[0]) + continue; + + for(int i = 0; i < key_count; i++) + if(strcmp(bind, keys[i].command) == 0) + { + keys[i].keyid = keyid; + break; + } + } + + for (int i = 0; i < key_count; i++) + { + KEYINFO key = keys[i]; + RECT button, label; + ui_hsplit_t(&main_view, 20.0f, &button, &main_view); + ui_vsplit_l(&button, 110.0f, &label, &button); + + ui_do_label(&label, key.name, 14.0f, -1); + int oldid = key.keyid; + int newid = ui_do_key_reader((void *)keys[i].name, &button, oldid); + if(newid != oldid) + { + gameclient.binds->bind(oldid, ""); + gameclient.binds->bind(newid, keys[i].command); + } + ui_hsplit_t(&main_view, 5.0f, 0, &main_view); + } + + // defaults + RECT button; + ui_hsplit_b(&right_part, 25.0f, &right_part, &button); + ui_vsplit_l(&button, 50.0f, 0, &button); + static int default_button = 0; + if (ui_do_button((void*)&default_button, "Reset to defaults", 0, &button, ui_draw_menu_button, 0)) + gameclient.binds->set_defaults(); +} + +void MENUS::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; + ui_vsplit_l(&main_view, 300.0f, &main_view, &modelist); + + // draw allmodes switch + RECT header, footer; + ui_hsplit_t(&modelist, 20, &button, &modelist); + if (ui_do_button(&config.gfx_display_all_modes, "Show only supported", config.gfx_display_all_modes^1, &button, ui_draw_checkbox, 0)) + { + config.gfx_display_all_modes ^= 1; + num_modes = gfx_get_video_modes(modes, MAX_RESOLUTIONS); + } + + // draw header + ui_hsplit_t(&modelist, 20, &header, &modelist); + ui_draw_rect(&header, vec4(1,1,1,0.25f), CORNER_T, 5.0f); + ui_do_label(&header, "Display Modes", 14.0f, 0); + + // draw footers + ui_hsplit_b(&modelist, 20, &modelist, &footer); + str_format(buf, sizeof(buf), "Current: %dx%d %d bit", config.gfx_screen_width, config.gfx_screen_height, config.gfx_color_depth); + ui_draw_rect(&footer, vec4(1,1,1,0.25f), CORNER_B, 5.0f); + ui_vsplit_l(&footer, 10.0f, 0, &footer); + ui_do_label(&footer, buf, 14.0f, -1); + + // modes + ui_draw_rect(&modelist, vec4(0,0,0,0.15f), 0, 0); + + RECT scroll; + ui_vsplit_r(&modelist, 15, &modelist, &scroll); + + RECT list = modelist; + ui_hsplit_t(&list, 20, &button, &list); + + int num = (int)(modelist.h/button.h); + static float scrollvalue = 0; + static int scrollbar = 0; + ui_hmargin(&scroll, 5.0f, &scroll); + scrollvalue = ui_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; + } + + str_format(buf, sizeof(buf), " %dx%d %d bit", modes[i].width, modes[i].height, depth); + if(ui_do_button(&modes[i], buf, selected, &button, ui_draw_list_row, 0)) + { + config.gfx_color_depth = depth; + config.gfx_screen_width = modes[i].width; + config.gfx_screen_height = modes[i].height; + if(!selected) + need_restart = true; + } + + ui_hsplit_t(&list, 20, &button, &list); + } + + + // switches + ui_hsplit_t(&main_view, 20.0f, &button, &main_view); + if (ui_do_button(&config.gfx_fullscreen, "Fullscreen", config.gfx_fullscreen, &button, ui_draw_checkbox, 0)) + { + config.gfx_fullscreen ^= 1; + need_restart = true; + } + + ui_hsplit_t(&main_view, 20.0f, &button, &main_view); + if (ui_do_button(&config.gfx_vsync, "V-Sync", config.gfx_vsync, &button, ui_draw_checkbox, 0)) + config.gfx_vsync ^= 1; + + ui_hsplit_t(&main_view, 20.0f, &button, &main_view); + if (ui_do_button(&config.gfx_fsaa_samples, "FSAA samples", config.gfx_fsaa_samples, &button, ui_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; + need_restart = true; + } + + ui_hsplit_t(&main_view, 40.0f, &button, &main_view); + ui_hsplit_t(&main_view, 20.0f, &button, &main_view); + if (ui_do_button(&config.gfx_texture_quality, "Quality Textures", config.gfx_texture_quality, &button, ui_draw_checkbox, 0)) + { + config.gfx_texture_quality ^= 1; + need_restart = true; + } + + ui_hsplit_t(&main_view, 20.0f, &button, &main_view); + if (ui_do_button(&config.gfx_texture_compression, "Texture Compression", config.gfx_texture_compression, &button, ui_draw_checkbox, 0)) + { + config.gfx_texture_compression ^= 1; + need_restart = true; + } + + ui_hsplit_t(&main_view, 20.0f, &button, &main_view); + if (ui_do_button(&config.gfx_high_detail, "High Detail", config.gfx_high_detail, &button, ui_draw_checkbox, 0)) + config.gfx_high_detail ^= 1; + + // + + RECT text; + ui_hsplit_t(&main_view, 20.0f, 0, &main_view); + ui_hsplit_t(&main_view, 20.0f, &text, &main_view); + //ui_vsplit_l(&text, 15.0f, 0, &text); + ui_do_label(&text, "UI Color", 14.0f, -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; + ui_hsplit_t(&main_view, 19.0f, &button, &main_view); + ui_vmargin(&button, 15.0f, &button); + ui_vsplit_l(&button, 30.0f, &text, &button); + ui_vsplit_r(&button, 5.0f, &button, 0); + ui_hsplit_t(&button, 4.0f, 0, &button); + + float k = (*color_slider[s]) / 255.0f; + k = ui_do_scrollbar_h(color_slider[s], &button, k); + *color_slider[s] = (int)(k*255.0f); + ui_do_label(&text, labels[s], 15.0f, -1); + } +} + +void MENUS::render_settings_sound(RECT main_view) +{ + RECT button; + ui_vsplit_l(&main_view, 300.0f, &main_view, 0); + + ui_hsplit_t(&main_view, 20.0f, &button, &main_view); + if (ui_do_button(&config.snd_enable, "Use Sounds", config.snd_enable, &button, ui_draw_checkbox, 0)) + { + config.snd_enable ^= 1; + need_restart = true; + } + + if(!config.snd_enable) + return; + + ui_hsplit_t(&main_view, 20.0f, &button, &main_view); + if (ui_do_button(&config.snd_nonactive_mute, "Mute when not active", config.snd_nonactive_mute, &button, ui_draw_checkbox, 0)) + config.snd_nonactive_mute ^= 1; + + // sample rate box + { + char buf[64]; + str_format(buf, sizeof(buf), "%d", config.snd_rate); + ui_hsplit_t(&main_view, 20.0f, &button, &main_view); + ui_do_label(&button, "Sample Rate", 14.0f, -1); + ui_vsplit_l(&button, 110.0f, 0, &button); + ui_vsplit_l(&button, 180.0f, &button, 0); + ui_do_edit_box(&config.snd_rate, &button, buf, sizeof(buf), 14.0f); + int before = config.snd_rate; + config.snd_rate = atoi(buf); + + if(config.snd_rate != before) + need_restart = true; + + if(config.snd_rate < 1) + config.snd_rate = 1; + } + + // volume slider + { + RECT button, label; + ui_hsplit_t(&main_view, 5.0f, &button, &main_view); + ui_hsplit_t(&main_view, 20.0f, &button, &main_view); + ui_vsplit_l(&button, 110.0f, &label, &button); + ui_hmargin(&button, 2.0f, &button); + ui_do_label(&label, "Sound Volume", 14.0f, -1); + config.snd_volume = (int)(ui_do_scrollbar_h(&config.snd_volume, &button, config.snd_volume/100.0f)*100.0f); + ui_hsplit_t(&main_view, 20.0f, 0, &main_view); + } +} + + + /* +static void menu2_render_settings_network(RECT main_view) +{ + RECT button; + ui_vsplit_l(&main_view, 300.0f, &main_view, 0); + + { + ui_hsplit_t(&main_view, 20.0f, &button, &main_view); + ui_do_label(&button, "Rcon Password", 14.0, -1); + ui_vsplit_l(&button, 110.0f, 0, &button); + ui_vsplit_l(&button, 180.0f, &button, 0); + ui_do_edit_box(&config.rcon_password, &button, config.rcon_password, sizeof(config.rcon_password), true); + } +}*/ + +void MENUS::render_settings(RECT main_view) +{ + static int settings_page = 0; + + // render background + RECT temp, tabbar; + ui_vsplit_r(&main_view, 120.0f, &main_view, &tabbar); + ui_draw_rect(&main_view, color_tabbar_active, CORNER_B|CORNER_TL, 10.0f); + ui_hsplit_t(&tabbar, 50.0f, &temp, &tabbar); + ui_draw_rect(&temp, color_tabbar_active, CORNER_R, 10.0f); + + ui_hsplit_t(&main_view, 10.0f, 0, &main_view); + + RECT button; + + const char *tabs[] = {"Player", "Controls", "Graphics", "Sound"}; + int num_tabs = (int)(sizeof(tabs)/sizeof(*tabs)); + + for(int i = 0; i < num_tabs; i++) + { + ui_hsplit_t(&tabbar, 10, &button, &tabbar); + ui_hsplit_t(&tabbar, 26, &button, &tabbar); + if(ui_do_button(tabs[i], tabs[i], settings_page == i, &button, ui_draw_settings_tab_button, 0)) + settings_page = i; + } + + ui_margin(&main_view, 10.0f, &main_view); + + if(settings_page == 0) + render_settings_player(main_view); + else if(settings_page == 1) + render_settings_controls(main_view); + else if(settings_page == 2) + render_settings_graphics(main_view); + else if(settings_page == 3) + render_settings_sound(main_view); + + if(need_restart) + { + RECT restart_warning; + ui_hsplit_b(&main_view, 40, &main_view, &restart_warning); + ui_do_label(&restart_warning, "You must restart the game for all settings to take effect.", 15.0f, -1, 220); + } +} diff --git a/src/game/client/components/motd.cpp b/src/game/client/components/motd.cpp new file mode 100644 index 00000000..bd04d089 --- /dev/null +++ b/src/game/client/components/motd.cpp @@ -0,0 +1,75 @@ +#include <engine/e_client_interface.h> +#include <engine/e_config.h> +#include <game/generated/g_protocol.hpp> +#include <game/generated/gc_data.hpp> + +#include <game/client/gameclient.hpp> +//#include <game/client/gc_anim.hpp> +#include <game/client/gc_client.hpp> + +#include "motd.hpp" + +void MOTD::on_reset() +{ + server_motd_time = 0; +} + +void MOTD::on_render() +{ + float width = 400*3.0f*gfx_screenaspect(); + float height = 400*3.0f; + + // TODO: repair me + if(/* !do_scoreboard && */ time_get() < server_motd_time) + { + gfx_mapscreen(0, 0, width, height); + + float h = 800.0f; + float w = 650.0f; + float x = width/2 - w/2; + float y = 150.0f; + + gfx_blend_normal(); + gfx_texture_set(-1); + gfx_quads_begin(); + gfx_setcolor(0,0,0,0.5f); + draw_round_rect(x, y, w, h, 40.0f); + gfx_quads_end(); + + gfx_text(0, x+40.0f, y+40.0f, 32.0f, server_motd, (int)(w-80.0f)); + } +} + +void MOTD::on_message(int msgtype, void *rawmsg) +{ + if(msgtype == NETMSGTYPE_SV_MOTD) + { + NETMSG_SV_MOTD *msg = (NETMSG_SV_MOTD *)rawmsg; + + // process escaping + str_copy(server_motd, msg->message, sizeof(server_motd)); + for(int i = 0; server_motd[i]; i++) + { + if(server_motd[i] == '\\') + { + if(server_motd[i+1] == 'n') + { + server_motd[i] = ' '; + server_motd[i+1] = '\n'; + i++; + } + } + } + + if(server_motd[0] && config.cl_motd_time) + server_motd_time = time_get()+time_freq()*config.cl_motd_time; + else + server_motd_time = 0; + } +} + +bool MOTD::on_input(INPUT_EVENT e) +{ + return false; +} + diff --git a/src/game/client/components/motd.hpp b/src/game/client/components/motd.hpp new file mode 100644 index 00000000..e26dff9b --- /dev/null +++ b/src/game/client/components/motd.hpp @@ -0,0 +1,15 @@ +#include <game/client/component.hpp> + +class MOTD : public COMPONENT +{ +public: + // motd + int64 server_motd_time; + char server_motd[900]; // FUGLY + + virtual void on_reset(); + virtual void on_render(); + virtual void on_message(int msgtype, void *rawmsg); + virtual bool on_input(INPUT_EVENT e); +}; + diff --git a/src/game/client/gc_particles.cpp b/src/game/client/components/particles.cpp index 504ebbab..0c449263 100644 --- a/src/game/client/gc_particles.cpp +++ b/src/game/client/components/particles.cpp @@ -1,19 +1,18 @@ -#include <engine/e_client_interface.h> -#include "gc_client.hpp" -#include "../generated/gc_data.hpp" +#include <base/math.hpp> +#include <game/generated/gc_data.hpp> +#include <game/client/gc_render.hpp> +#include "particles.hpp" -// NOTE: the way the particle system works isn't very cache friendly - -enum +PARTICLES::PARTICLES() { - MAX_PARTICLES=1024*8, -}; + on_reset(); + render_trail.parts = this; + render_explosions.parts = this; + render_general.parts = this; +} -static PARTICLE particles[MAX_PARTICLES]; -static int first_free = -1; -static int first_part[NUM_PARTGROUPS] = {-1}; -void particle_reset() +void PARTICLES::on_reset() { // reset particles for(int i = 0; i < MAX_PARTICLES; i++) @@ -26,12 +25,11 @@ void particle_reset() particles[MAX_PARTICLES-1].next_part = -1; first_free = 0; - for(int i = 0; i < NUM_PARTGROUPS; i++) + for(int i = 0; i < NUM_GROUPS; i++) first_part[i] = -1; } - -void particle_add(int group, PARTICLE *part) +void PARTICLES::add(int group, PARTICLE *part) { if (first_free == -1) return; @@ -55,7 +53,7 @@ void particle_add(int group, PARTICLE *part) particles[id].life = 0; } -void particle_update(float time_passed) +void PARTICLES::update(float time_passed) { static float friction_fraction = 0; friction_fraction += time_passed; @@ -70,13 +68,13 @@ void particle_update(float time_passed) friction_fraction -= 0.05f; } - for(int g = 0; g < NUM_PARTGROUPS; g++) + for(int g = 0; g < NUM_GROUPS; g++) { int i = first_part[g]; while(i != -1) { int next = particles[i].next_part; - particles[i].vel += flow_get(particles[i].pos)*time_passed * particles[i].flow_affected; + //particles[i].vel += flow_get(particles[i].pos)*time_passed * particles[i].flow_affected; particles[i].vel.y += particles[i].gravity*time_passed; for(int f = 0; f < friction_count; f++) // apply friction @@ -84,7 +82,9 @@ void particle_update(float time_passed) // move the point vec2 vel = particles[i].vel*time_passed; - move_point(&particles[i].pos, &vel, 0.1f+0.9f*frandom(), NULL); + + // TODO: repair me + //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; @@ -115,7 +115,15 @@ void particle_update(float time_passed) } } -void particle_render(int group) +void PARTICLES::on_render() +{ + static int64 lasttime = 0; + int64 t = time_get(); + update((float)((t-lasttime)/(double)time_freq())); + lasttime = t; +} + +void PARTICLES::render_group(int group) { gfx_blend_normal(); //gfx_blend_additive(); diff --git a/src/game/client/components/particles.hpp b/src/game/client/components/particles.hpp new file mode 100644 index 00000000..6c466d94 --- /dev/null +++ b/src/game/client/components/particles.hpp @@ -0,0 +1,91 @@ +#include <base/vmath.hpp> +#include <game/client/component.hpp> + +// particles +struct PARTICLE +{ + void set_default() + { + vel = vec2(0,0); + life_span = 0; + start_size = 32; + end_size = 32; + rot = 0; + rotspeed = 0; + gravity = 0; + friction = 0; + flow_affected = 1.0f; + color = vec4(1,1,1,1); + } + + vec2 pos; + vec2 vel; + + int spr; + + float flow_affected; + + float life_span; + + float start_size; + float end_size; + + float rot; + float rotspeed; + + float gravity; + float friction; + + vec4 color; + + // set by the particle system + float life; + int prev_part; + int next_part; +}; + +class PARTICLES : public COMPONENT +{ + friend class GAMECLIENT; +public: + enum + { + GROUP_PROJECTILE_TRAIL=0, + GROUP_EXPLOSIONS, + GROUP_GENERAL, + NUM_GROUPS + }; + + PARTICLES(); + + void add(int group, PARTICLE *part); + + virtual void on_reset(); + virtual void on_render(); + +private: + + enum + { + MAX_PARTICLES=1024*8, + }; + + PARTICLE particles[MAX_PARTICLES]; + int first_free; + int first_part[NUM_GROUPS]; + + void render_group(int group); + void update(float time_passed); + + template<int TGROUP> + class RENDER_GROUP : public COMPONENT + { + public: + PARTICLES *parts; + virtual void on_render() { parts->render_group(TGROUP); } + }; + + RENDER_GROUP<GROUP_PROJECTILE_TRAIL> render_trail; + RENDER_GROUP<GROUP_EXPLOSIONS> render_explosions; + RENDER_GROUP<GROUP_GENERAL> render_general; +}; diff --git a/src/game/client/gc_render_obj.cpp b/src/game/client/components/players.cpp index aa64a127..3178b82f 100644 --- a/src/game/client/gc_render_obj.cpp +++ b/src/game/client/components/players.cpp @@ -1,229 +1,26 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include <math.h> -#include <stdio.h> -#include <base/math.hpp> +extern "C" { + #include <engine/e_config.h> +} #include <engine/e_client_interface.h> -#include <engine/e_config.h> -#include <game/generated/gc_data.hpp> #include <game/generated/g_protocol.hpp> -#include "gc_render.hpp" -#include "gc_anim.hpp" -#include "gc_client.hpp" -#include "gc_skin.hpp" - - -void render_projectile(const NETOBJ_PROJECTILE *current, int itemid) -{ - if(debug_firedelay) - { - int64 delay = time_get()-debug_firedelay; - dbg_msg("game", "firedelay=%.2f ms", delay/(float)time_freq()*1000.0f); - debug_firedelay = 0; - } - - gfx_texture_set(data->images[IMAGE_GAME].id); - gfx_quads_begin(); - - // get positions - float curvature = 0; - float speed = 0; - if(current->type == WEAPON_GRENADE) - { - curvature = tuning.grenade_curvature; - speed = tuning.grenade_speed; - } - else if(current->type == WEAPON_SHOTGUN) - { - curvature = tuning.shotgun_curvature; - speed = tuning.shotgun_speed; - } - else if(current->type == WEAPON_GUN) - { - curvature = tuning.gun_curvature; - speed = tuning.gun_speed; - } - - float ct = (client_tick()-current->start_tick)/(float)SERVER_TICK_SPEED + client_ticktime()*1/(float)SERVER_TICK_SPEED; - vec2 startpos(current->x, current->y); - vec2 startvel(current->vx/100.0f, current->vy/100.0f); - vec2 pos = calc_pos(startpos, startvel, curvature, speed, ct); - vec2 prevpos = calc_pos(startpos, startvel, curvature, speed, ct-0.001f); - - select_sprite(data->weapons.id[clamp(current->type, 0, NUM_WEAPONS-1)].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 - if(current->type == WEAPON_GRENADE) - { - effect_smoketrail(pos, vel*-1); - flow_add(pos, vel*1000*client_frametime(), 10.0f); - gfx_quads_setrotation(client_localtime()*pi*2*2 + itemid); - } - else - { - effect_bullettrail(pos); - flow_add(pos, vel*1000*client_frametime(), 10.0f); - - if(length(vel) > 0.00001f) - gfx_quads_setrotation(get_angle(vel)); - else - gfx_quads_setrotation(0); - - } - - gfx_quads_draw(pos.x, pos.y, 32, 32); - gfx_quads_setrotation(0); - gfx_quads_end(); -} - -void render_pickup(const NETOBJ_PICKUP *prev, const NETOBJ_PICKUP *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.id[clamp(current->subtype, 0, NUM_WEAPONS-1)].sprite_body); - size = data->weapons.id[clamp(current->subtype, 0, NUM_WEAPONS-1)].visual_size; - } - else - { - const int c[] = { - SPRITE_PICKUP_HEALTH, - SPRITE_PICKUP_ARMOR, - SPRITE_PICKUP_WEAPON, - SPRITE_PICKUP_NINJA - }; - select_sprite(c[current->type]); - - if(c[current->type] == SPRITE_PICKUP_NINJA) - { - effect_powerupshine(pos, vec2(96,18)); - 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(); -} - -void render_flag(const NETOBJ_FLAG *prev, const NETOBJ_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()); - - // make sure that the flag isn't interpolated between capture and return - if(prev->carried_by != current->carried_by) - pos = vec2(current->x, current->y); - - // make sure to use predicted position if we are the carrier - if(netobjects.local_info && current->carried_by == netobjects.local_info->cid) - pos = local_character_pos; - - gfx_quads_draw(pos.x, pos.y-size*0.75f, size, size*2); - gfx_quads_end(); -} - - -void render_laser(const struct NETOBJ_LASER *current) -{ - - vec2 pos = vec2(current->x, current->y); - vec2 from = vec2(current->from_x, current->from_y); - vec2 dir = normalize(pos-from); - - float ticks = client_tick() + client_intratick() - current->start_tick; - float ms = (ticks/50.0f) * 1000.0f; - float a = ms / tuning.laser_bounce_delay; - a = clamp(a, 0.0f, 1.0f); - float ia = 1-a; - - - vec2 out, border; - - gfx_blend_normal(); - gfx_texture_set(-1); - gfx_quads_begin(); - - //vec4 inner_color(0.15f,0.35f,0.75f,1.0f); - //vec4 outer_color(0.65f,0.85f,1.0f,1.0f); - - // do outline - vec4 outer_color(0.075f,0.075f,0.25f,1.0f); - gfx_setcolor(outer_color.r,outer_color.g,outer_color.b,1.0f); - out = vec2(dir.y, -dir.x) * (7.0f*ia); - - gfx_quads_draw_freeform( - from.x-out.x, from.y-out.y, - from.x+out.x, from.y+out.y, - pos.x-out.x, pos.y-out.y, - pos.x+out.x, pos.y+out.y - ); - - // do inner - vec4 inner_color(0.5f,0.5f,1.0f,1.0f); - out = vec2(dir.y, -dir.x) * (5.0f*ia); - gfx_setcolor(inner_color.r, inner_color.g, inner_color.b, 1.0f); // center - - gfx_quads_draw_freeform( - from.x-out.x, from.y-out.y, - from.x+out.x, from.y+out.y, - pos.x-out.x, pos.y-out.y, - pos.x+out.x, pos.y+out.y - ); - - gfx_quads_end(); - - // render head - { - gfx_blend_normal(); - gfx_texture_set(data->images[IMAGE_PARTICLES].id); - gfx_quads_begin(); - - int sprites[] = {SPRITE_PART_SPLAT01, SPRITE_PART_SPLAT02, SPRITE_PART_SPLAT03}; - select_sprite(sprites[client_tick()%3]); - gfx_quads_setrotation(client_tick()); - gfx_setcolor(outer_color.r,outer_color.g,outer_color.b,1.0f); - gfx_quads_draw(pos.x, pos.y, 24,24); - gfx_setcolor(inner_color.r, inner_color.g, inner_color.b, 1.0f); - gfx_quads_draw(pos.x, pos.y, 20,20); - gfx_quads_end(); - } - - gfx_blend_normal(); -} - +#include <game/generated/gc_data.hpp> +#include <game/gamecore.hpp> // get_angle +#include <game/client/animstate.hpp> +#include <game/client/gameclient.hpp> +#include <game/client/gc_client.hpp> +#include <game/client/gc_ui.hpp> +#include <game/client/gc_render.hpp> +#include <game/client/components/flow.hpp> +#include <game/client/components/skins.hpp> +#include <game/client/components/effects.hpp> +#include "players.hpp" -static void render_hand(TEE_RENDER_INFO *info, vec2 center_pos, vec2 dir, float angle_offset, vec2 post_rot_offset) +void PLAYERS::render_hand(TEE_RENDER_INFO *info, vec2 center_pos, vec2 dir, float angle_offset, vec2 post_rot_offset) { // for drawing hand //const skin *s = skin_get(skin_id); @@ -266,7 +63,7 @@ static void render_hand(TEE_RENDER_INFO *info, vec2 center_pos, vec2 dir, float gfx_quads_end(); } -void render_player( +void PLAYERS::render_player( const NETOBJ_CHARACTER *prev_char, const NETOBJ_CHARACTER *player_char, const NETOBJ_PLAYER_INFO *prev_info, @@ -279,25 +76,25 @@ void render_player( player = *player_char; NETOBJ_PLAYER_INFO info = *player_info; - TEE_RENDER_INFO render_info = client_datas[info.cid].render_info; + TEE_RENDER_INFO render_info = gameclient.clients[info.cid].render_info; // check for teamplay modes bool is_teamplay = false; - if(netobjects.gameobj && netobjects.gameobj->gametype != GAMETYPE_DM) - is_teamplay = true; + if(gameclient.snap.gameobj) + is_teamplay = gameclient.snap.gameobj->flags&GAMEFLAG_TEAMS != 0; // check for ninja if (player.weapon == WEAPON_NINJA) { // change the skin for the player to the ninja - int skin = skin_find("x_ninja"); + int skin = gameclient.skins->find("x_ninja"); if(skin != -1) { if(is_teamplay) - render_info.texture = skin_get(skin)->color_texture; + render_info.texture = gameclient.skins->get(skin)->color_texture; else { - render_info.texture = skin_get(skin)->org_texture; + render_info.texture = gameclient.skins->get(skin)->org_texture; render_info.color_body = vec4(1,1,1,1); render_info.color_feet = vec4(1,1,1,1); } @@ -320,13 +117,13 @@ void render_player( if(player.attacktick != prev.attacktick) mixspeed = 0.1f; - float angle = mix(client_datas[info.cid].angle, player.angle/256.0f, mixspeed); - client_datas[info.cid].angle = angle; + float angle = mix(gameclient.clients[info.cid].angle, player.angle/256.0f, mixspeed); + gameclient.clients[info.cid].angle = angle; vec2 direction = get_direction((int)(angle*256.0f)); if(info.local && config.cl_predict) { - if(!netobjects.local_character || (netobjects.local_character->health < 0) || (netobjects.gameobj && netobjects.gameobj->game_over)) + if(!gameclient.snap.local_character || (gameclient.snap.local_character->health < 0) || (gameclient.snap.gameobj && gameclient.snap.gameobj->game_over)) { } else @@ -341,7 +138,7 @@ void 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); - flow_add(position, vel*100.0f, 10.0f); + gameclient.flow->add(position, vel*100.0f, 10.0f); render_info.got_airjump = player.jumped&2?0:1; @@ -354,25 +151,25 @@ void render_player( // evaluate animation float walk_time = fmod(position.x, 100.0f)/100.0f; - ANIM_STATE state; - anim_eval(&data->animations[ANIM_BASE], 0, &state); + ANIMSTATE state; + state.set(&data->animations[ANIM_BASE], 0); if(inair) - anim_eval_add(&state, &data->animations[ANIM_INAIR], 0, 1.0f); // TODO: some sort of time here + state.add(&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 + state.add(&data->animations[ANIM_IDLE], 0, 1.0f); // TODO: some sort of time here else if(!want_other_dir) - anim_eval_add(&state, &data->animations[ANIM_WALK], walk_time, 1.0f); + state.add(&data->animations[ANIM_WALK], walk_time, 1.0f); if (player.weapon == WEAPON_HAMMER) { float a = clamp((client_tick()-player.attacktick+ticktime)/10.0f, 0.0f, 1.0f); - anim_eval_add(&state, &data->animations[ANIM_HAMMER_SWING], a, 1.0f); + state.add(&data->animations[ANIM_HAMMER_SWING], a, 1.0f); } if (player.weapon == WEAPON_NINJA) { float a = clamp((client_tick()-player.attacktick+ticktime)/40.0f, 0.0f, 1.0f); - anim_eval_add(&state, &data->animations[ANIM_NINJA_SWING], a, 1.0f); + state.add(&data->animations[ANIM_NINJA_SWING], a, 1.0f); } // do skidding @@ -385,7 +182,7 @@ void render_player( skid_sound_time = time_get(); } - effect_skidtrail( + gameclient.effects->skidtrail( position+vec2(-player.wanted_direction*6,12), vec2(-player.wanted_direction*100*length(vel),-50) ); @@ -403,7 +200,7 @@ void render_player( if(player_char->hooked_player != -1) { - if(netobjects.local_info && player_char->hooked_player == netobjects.local_info->cid) + if(gameclient.snap.local_info && player_char->hooked_player == gameclient.snap.local_info->cid) { hook_pos = mix(vec2(predicted_prev_char.pos.x, predicted_prev_char.pos.y), vec2(predicted_char.pos.x, predicted_char.pos.y), client_predintratick()); @@ -477,12 +274,12 @@ void render_player( { gfx_quads_setrotation(-pi/2-state.attach.angle*pi*2); p.x -= data->weapons.id[iw].offsetx; - effect_powerupshine(p+vec2(32,0), vec2(32,12)); + gameclient.effects->powerupshine(p+vec2(32,0), vec2(32,12)); } else { gfx_quads_setrotation(-pi/2+state.attach.angle*pi*2); - effect_powerupshine(p-vec2(32,0), vec2(32,12)); + gameclient.effects->powerupshine(p-vec2(32,0), vec2(32,12)); } draw_sprite(p.x, p.y, data->weapons.id[iw].visual_size); @@ -582,13 +379,13 @@ void render_player( gfx_quads_end(); } - if (client_datas[info.cid].emoticon_start != -1 && client_datas[info.cid].emoticon_start + 2 * client_tickspeed() > client_tick()) + if (gameclient.clients[info.cid].emoticon_start != -1 && gameclient.clients[info.cid].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.cid].emoticon_start; - int from_end = client_datas[info.cid].emoticon_start + 2 * client_tickspeed() - client_tick(); + int since_start = client_tick() - gameclient.clients[info.cid].emoticon_start; + int from_end = gameclient.clients[info.cid].emoticon_start + 2 * client_tickspeed() - client_tick(); float a = 1; @@ -609,7 +406,7 @@ void render_player( 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.cid].emoticon); + select_sprite(SPRITE_OOP + gameclient.clients[info.cid].emoticon); gfx_quads_draw(position.x, position.y - 23 - 32*h, 64, 64*h); gfx_quads_end(); } @@ -620,9 +417,9 @@ void render_player( //gfx_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); + a = clamp(1-powf(distance(gameclient.local_target_pos, position)/200.0f,16.0f), 0.0f, 1.0f); - const char *name = client_datas[info.cid].name; + const char *name = gameclient.clients[info.cid].name; float tw = gfx_text_width(0, 28.0f, name, -1); gfx_text_color(1,1,1,a); gfx_text(0, position.x-tw/2.0f, position.y-60, 28.0f, name, -1); @@ -637,3 +434,30 @@ void render_player( gfx_text_color(1,1,1,1); } } + +void PLAYERS::on_render() +{ + 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 == NETOBJTYPE_CHARACTER) + { + const void *prev = snap_find_item(SNAP_PREV, item.type, item.id); + const void *prev_info = snap_find_item(SNAP_PREV, NETOBJTYPE_PLAYER_INFO, item.id); + const void *info = snap_find_item(SNAP_CURRENT, NETOBJTYPE_PLAYER_INFO, item.id); + + if(prev && prev_info && info) + { + render_player( + (const NETOBJ_CHARACTER *)prev, + (const NETOBJ_CHARACTER *)data, + (const NETOBJ_PLAYER_INFO *)prev_info, + (const NETOBJ_PLAYER_INFO *)info + ); + } + } + } +} diff --git a/src/game/client/components/players.hpp b/src/game/client/components/players.hpp new file mode 100644 index 00000000..bdce91de --- /dev/null +++ b/src/game/client/components/players.hpp @@ -0,0 +1,16 @@ +#include <game/client/component.hpp> + +class PLAYERS : public COMPONENT +{ + void render_hand(class TEE_RENDER_INFO *info, vec2 center_pos, vec2 dir, float angle_offset, vec2 post_rot_offset); + void render_player( + const class NETOBJ_CHARACTER *prev_char, + const class NETOBJ_CHARACTER *player_char, + const class NETOBJ_PLAYER_INFO *prev_info, + const class NETOBJ_PLAYER_INFO *player_info + ); + +public: + virtual void on_render(); +}; + diff --git a/src/game/client/components/scoreboard.cpp b/src/game/client/components/scoreboard.cpp new file mode 100644 index 00000000..075f249e --- /dev/null +++ b/src/game/client/components/scoreboard.cpp @@ -0,0 +1,254 @@ +#include <string.h> + +#include <engine/e_client_interface.h> +#include <game/generated/g_protocol.hpp> +#include <game/generated/gc_data.hpp> +#include <game/client/gameclient.hpp> +#include <game/client/animstate.hpp> +#include <game/client/gc_render.hpp> +#include "scoreboard.hpp" + +void SCOREBOARD::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(gameclient.snap.gameobj && gameclient.snap.gameobj->time_limit) + { + char buf[64]; + str_format(buf, sizeof(buf), "Time Limit: %d min", gameclient.snap.gameobj->time_limit); + gfx_text(0, x+w/2, y, 24.0f, buf, -1); + } + if(gameclient.snap.gameobj && gameclient.snap.gameobj->score_limit) + { + char buf[64]; + str_format(buf, sizeof(buf), "Score Limit: %d", gameclient.snap.gameobj->score_limit); + gfx_text(0, x+40, y, 24.0f, buf, -1); + } +} + +void SCOREBOARD::render_spectators(float x, float y, float w) +{ + char buffer[1024*4]; + int count = 0; + float h = 120.0f; + + str_copy(buffer, "Spectators: ", sizeof(buffer)); + + 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(); + + 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 == NETOBJTYPE_PLAYER_INFO) + { + const NETOBJ_PLAYER_INFO *info = (const NETOBJ_PLAYER_INFO *)data; + if(info->team == -1) + { + if(count) + strcat(buffer, ", "); + strcat(buffer, gameclient.clients[info->cid].name); + count++; + } + } + } + + gfx_text(0, x+10, y, 32, buffer, (int)w-20); +} + +void SCOREBOARD::render_scoreboard(float x, float y, float w, int team, const char *title) +{ + //float ystart = y; + float h = 750.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(gameclient.snap.gameobj->game_over) + title = "Game Over"; + else + title = "Score Board"; + } + + float tw = gfx_text_width(0, 48, title, -1); + + if(team == -1) + { + gfx_text(0, x+w/2-tw/2, y, 48, title, -1); + } + else + { + gfx_text(0, x+10, y, 48, title, -1); + + if(gameclient.snap.gameobj) + { + char buf[128]; + int score = team ? gameclient.snap.gameobj->teamscore_blue : gameclient.snap.gameobj->teamscore_red; + str_format(buf, sizeof(buf), "%d", score); + tw = gfx_text_width(0, 48, buf, -1); + gfx_text(0, x+w-tw-30, y, 48, buf, -1); + } + } + + y += 54.0f; + + // find players + const NETOBJ_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 == NETOBJTYPE_PLAYER_INFO) + { + players[num_players] = (const NETOBJ_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 NETOBJ_PLAYER_INFO *tmp = players[i]; + players[i] = players[i+1]; + players[i+1] = tmp; + } + } + } + + // render headlines + gfx_text(0, x+10, y, 24.0f, "Score", -1); + gfx_text(0, x+125, y, 24.0f, "Name", -1); + gfx_text(0, x+w-70, y, 24.0f, "Ping", -1); + y += 29.0f; + + // render player scores + for(int i = 0; i < num_players; i++) + { + const NETOBJ_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 = 35.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(); + } + + str_format(buf, sizeof(buf), "%4d", info->score); + gfx_text(0, x+60-gfx_text_width(0, font_size,buf,-1), y, font_size, buf, -1); + + gfx_text(0, x+128, y, font_size, gameclient.clients[info->cid].name, -1); + + str_format(buf, sizeof(buf), "%4d", info->latency); + float tw = gfx_text_width(0, font_size, buf, -1); + gfx_text(0, x+w-tw-35, y, font_size, buf, -1); + + // render avatar + if((gameclient.snap.flags[0] && gameclient.snap.flags[0]->carried_by == info->cid) || + (gameclient.snap.flags[1] && gameclient.snap.flags[1]->carried_by == info->cid)) + { + gfx_blend_normal(); + gfx_texture_set(data->images[IMAGE_GAME].id); + gfx_quads_begin(); + + if(info->team == 0) select_sprite(SPRITE_FLAG_BLUE, SPRITE_FLAG_FLIP_X); + else select_sprite(SPRITE_FLAG_RED, SPRITE_FLAG_FLIP_X); + + float size = 64.0f; + gfx_quads_drawTL(x+55, y-15, size/2, size); + gfx_quads_end(); + } + + render_tee(ANIMSTATE::get_idle(), &gameclient.clients[info->cid].render_info, EMOTE_NORMAL, vec2(1,0), vec2(x+90, y+28)); + + + y += 50.0f; + } +} + +void SCOREBOARD::on_render() +{ + + // TODO: repair me + /* + bool do_scoreboard = false; + + // if we are dead + if(!spectate && (!gameclient.snap.local_character || gameclient.snap.local_character->health < 0)) + do_scoreboard = true; + + // if we the game is over + if(gameclient.snap.gameobj && gameclient.snap.gameobj->game_over) + do_scoreboard = true;*/ + + + float width = 400*3.0f*gfx_screenaspect(); + float height = 400*3.0f; + + gfx_mapscreen(0, 0, width, height); + + float w = 650.0f; + + if(gameclient.snap.gameobj && !(gameclient.snap.gameobj->flags&GAMEFLAG_TEAMS)) + { + render_scoreboard(width/2-w/2, 150.0f, w, 0, 0); + //render_scoreboard(gameobj, 0, 0, -1, 0); + } + else + { + + if(gameclient.snap.gameobj && gameclient.snap.gameobj->game_over) + { + const char *text = "DRAW!"; + if(gameclient.snap.gameobj->teamscore_red > gameclient.snap.gameobj->teamscore_blue) + text = "Red Team Wins!"; + else if(gameclient.snap.gameobj->teamscore_blue > gameclient.snap.gameobj->teamscore_red) + text = "Blue Team Wins!"; + + float w = gfx_text_width(0, 92.0f, text, -1); + gfx_text(0, 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+750+25, w); + render_spectators(width/2-w/2, 150+750+25+50+25, w); +} diff --git a/src/game/client/components/scoreboard.hpp b/src/game/client/components/scoreboard.hpp new file mode 100644 index 00000000..b1913cec --- /dev/null +++ b/src/game/client/components/scoreboard.hpp @@ -0,0 +1,11 @@ +#include <game/client/component.hpp> + +class SCOREBOARD : public COMPONENT +{ + void render_goals(float x, float y, float w); + void render_spectators(float x, float y, float w); + void render_scoreboard(float x, float y, float w, int team, const char *title); +public: + virtual void on_render(); +}; + diff --git a/src/game/client/gc_skin.cpp b/src/game/client/components/skins.cpp index e0aa2c84..eae45c4d 100644 --- a/src/game/client/gc_skin.cpp +++ b/src/game/client/components/skins.cpp @@ -7,20 +7,18 @@ #include <base/math.hpp> #include <engine/e_client_interface.h> -#include "gc_skin.hpp" +#include "skins.hpp" -enum +SKINS::SKINS() { - MAX_SKINS=256, -}; - -static skin skins[MAX_SKINS] = {{0}}; -static int num_skins = 0; + num_skins = 0; +} -static void skinscan(const char *name, int is_dir, void *user) +void SKINS::skinscan(const char *name, int is_dir, void *user) { + SKINS *self = (SKINS *)user; int l = strlen(name); - if(l < 4 || is_dir || num_skins == MAX_SKINS) + if(l < 4 || is_dir || self->num_skins == MAX_SKINS) return; if(strcmp(name+l-4, ".png") != 0) return; @@ -34,7 +32,7 @@ static void skinscan(const char *name, int is_dir, void *user) return; } - skins[num_skins].org_texture = gfx_load_texture_raw(info.width, info.height, info.format, info.data, info.format, 0); + self->skins[self->num_skins].org_texture = gfx_load_texture_raw(info.width, info.height, info.format, info.data, info.format, 0); int body_size = 96; // body size unsigned char *d = (unsigned char *)info.data; @@ -54,7 +52,7 @@ static void skinscan(const char *name, int is_dir, void *user) } } - skins[num_skins].blood_color = normalize(vec3(colors[0], colors[1], colors[2])); + self->skins[self->num_skins].blood_color = normalize(vec3(colors[0], colors[1], colors[2])); } // create colorless version @@ -107,33 +105,34 @@ static void skinscan(const char *name, int is_dir, void *user) } } - skins[num_skins].color_texture = gfx_load_texture_raw(info.width, info.height, info.format, info.data, info.format, 0); + self->skins[self->num_skins].color_texture = gfx_load_texture_raw(info.width, info.height, info.format, info.data, info.format, 0); 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++; + strncpy(self->skins[self->num_skins].name, name, min((int)sizeof(self->skins[self->num_skins].name),l-4)); + dbg_msg("game", "load skin %s", self->skins[self->num_skins].name); + self->num_skins++; } -void skin_init() +void SKINS::init() { // load skins - fs_listdir("data/skins", skinscan, 0); + num_skins = 0; + fs_listdir("data/skins", skinscan, this); } -int skin_num() +int SKINS::num() { return num_skins; } -const skin *skin_get(int index) +const SKINS::SKIN *SKINS::get(int index) { return &skins[index%num_skins]; } -int skin_find(const char *name) +int SKINS::find(const char *name) { for(int i = 0; i < num_skins; i++) { @@ -143,9 +142,6 @@ int skin_find(const char *name) return -1; } - - - // these converter functions were nicked from some random internet pages static float hue_to_rgb(float v1, float v2, float h) { @@ -157,7 +153,7 @@ static float hue_to_rgb(float v1, float v2, float h) return v1; } -vec3 hsl_to_rgb(vec3 in) +static vec3 hsl_to_rgb(vec3 in) { float v1, v2; vec3 out; @@ -185,7 +181,7 @@ vec3 hsl_to_rgb(vec3 in) return out; } -vec4 skin_get_color(int v) +vec4 SKINS::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/components/skins.hpp b/src/game/client/components/skins.hpp new file mode 100644 index 00000000..078fd71d --- /dev/null +++ b/src/game/client/components/skins.hpp @@ -0,0 +1,36 @@ +#include <base/vmath.hpp> +#include <game/client/component.hpp> + +class SKINS : public COMPONENT +{ +public: + // do this better and nicer + typedef struct + { + int org_texture; + int color_texture; + char name[31]; + char term[1]; + vec3 blood_color; + } SKIN; + + SKINS(); + + void init(); + + vec4 get_color(int v); + int num(); + const SKIN *get(int index); + int find(const char *name); + +private: + enum + { + MAX_SKINS=256, + }; + + SKIN skins[MAX_SKINS]; + int num_skins; + + static void skinscan(const char *name, int is_dir, void *user); +}; diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp new file mode 100644 index 00000000..0fb6c61e --- /dev/null +++ b/src/game/client/gameclient.cpp @@ -0,0 +1,548 @@ +#include <engine/e_client_interface.h> +#include <game/generated/g_protocol.hpp> + +#include "gameclient.hpp" +#include "gc_client.hpp" +#include "components/killmessages.hpp" +#include "components/chat.hpp" +#include "components/motd.hpp" +#include "components/broadcast.hpp" +#include "components/console.hpp" +#include "components/binds.hpp" +#include "components/particles.hpp" +#include "components/menus.hpp" +#include "components/skins.hpp" +#include "components/flow.hpp" +#include "components/players.hpp" +#include "components/items.hpp" +#include "components/maplayers.hpp" +#include "components/camera.hpp" +#include "components/hud.hpp" +#include "components/debughud.hpp" +#include "components/controls.hpp" +#include "components/effects.hpp" + +GAMECLIENT gameclient; + +// instanciate all systems +static KILLMESSAGES killmessages; +static CAMERA camera; +static CHAT chat; +static MOTD motd; +static BROADCAST broadcast; +static CONSOLE console; +static BINDS binds; +static PARTICLES particles; +static MENUS menus; +static SKINS skins; +static FLOW flow; +static HUD hud; +static DEBUGHUD debughud; +static CONTROLS controls; +static EFFECTS effects; + +static PLAYERS players; +static ITEMS items; + +static MAPLAYERS maplayers_background(MAPLAYERS::TYPE_BACKGROUND); +static MAPLAYERS maplayers_foreground(MAPLAYERS::TYPE_FOREGROUND); + + +GAMECLIENT::STACK::STACK() { num = 0; } +void GAMECLIENT::STACK::add(class COMPONENT *component) { components[num++] = component; } + +void GAMECLIENT::on_init() +{ + // setup pointers + binds = &::binds; + console = &::console; + particles = &::particles; + menus = &::menus; + skins = &::skins; + chat = &::chat; + flow = &::flow; + camera = &::camera; + controls = &::controls; + effects = &::effects; + + // make a list of all the systems, make sure to add them in the corrent render order + all.add(skins); + all.add(effects); // doesn't render anything, just updates effects + all.add(particles); + all.add(binds); + all.add(controls); + all.add(camera); + all.add(particles); // doesn't render anything, just updates all the particles + + all.add(&maplayers_background); // first to render + all.add(&particles->render_trail); + all.add(&particles->render_explosions); + all.add(&items); + all.add(&players); + all.add(&maplayers_foreground); + all.add(&particles->render_general); + all.add(&hud); + all.add(&killmessages); + all.add(chat); + all.add(&broadcast); + all.add(&debughud); + all.add(&motd); + all.add(menus); + all.add(console); + + // build the input stack + input.add(console); + input.add(menus); + input.add(chat); + input.add(controls); + input.add(binds); + + // init all components + for(int i = 0; i < all.num; i++) + all.components[i]->on_init(); + + /* + input_stack.add_handler(console_input_special_binds, 0); // F1-Fx binds + input_stack.add_handler(console_input_cli, 0); // console + input_stack.add_handler(chat_input_handle, 0); // chat + //input_stack.add_handler() // ui + input_stack.add_handler(console_input_normal_binds, 0); // binds + */ +} + +void GAMECLIENT::dispatch_input() +{ + // handle mouse movement + int x=0, y=0; + inp_mouse_relative(&x, &y); + for(int h = 0; h < input.num; h++) + { + if(input.components[h]->on_mousemove(x, y)) + break; + } + + // handle key presses + for(int i = 0; i < inp_num_events(); i++) + { + INPUT_EVENT e = inp_get_event(i); + + for(int h = 0; h < input.num; h++) + { + if(input.components[h]->on_input(e)) + { + //dbg_msg("", "%d char=%d key=%d flags=%d", h, e.ch, e.key, e.flags); + break; + } + } + } + + // clear all events for this frame + inp_clear_events(); +} + + +int GAMECLIENT::on_snapinput(int *data) +{ + return controls->snapinput(data); +} + + +void GAMECLIENT::on_connected() +{ + on_reset(); + + // send the inital info + send_info(true); +} + +void GAMECLIENT::on_reset() +{ + // clear out the invalid pointers + last_new_predicted_tick = -1; + mem_zero(&gameclient.snap, sizeof(gameclient.snap)); + + for(int i = 0; i < MAX_CLIENTS; i++) + { + clients[i].name[0] = 0; + clients[i].skin_id = 0; + clients[i].team = 0; + clients[i].angle = 0; + clients[i].emoticon = 0; + clients[i].emoticon_start = -1; + clients[i].skin_info.texture = gameclient.skins->get(0)->color_texture; + clients[i].skin_info.color_body = vec4(1,1,1,1); + clients[i].skin_info.color_feet = vec4(1,1,1,1); + clients[i].update_render_info(); + } + + for(int i = 0; i < all.num; i++) + all.components[i]->on_reset(); +} + + +void GAMECLIENT::update_local_character_pos() +{ + if(config.cl_predict) + { + if(!snap.local_character || (snap.local_character->health < 0) || (snap.gameobj && snap.gameobj->game_over)) + { + // don't use predicted + } + else + local_character_pos = mix(predicted_prev_char.pos, predicted_char.pos, client_predintratick()); + } + else if(snap.local_character && snap.local_prev_character) + { + local_character_pos = mix( + vec2(snap.local_prev_character->x, snap.local_prev_character->y), + vec2(snap.local_character->x, snap.local_character->y), client_intratick()); + } +} + +void GAMECLIENT::on_render() +{ + // update the local character position + update_local_character_pos(); + + // dispatch all input to systems + dispatch_input(); + + // render all systems + for(int i = 0; i < all.num; i++) + all.components[i]->on_render(); +} + +void GAMECLIENT::on_message(int msgtype) +{ + void *rawmsg = netmsg_secure_unpack(msgtype); + if(!rawmsg) + { + dbg_msg("client", "dropped weird message '%s' (%d), failed on '%s'", netmsg_get_name(msgtype), msgtype, netmsg_failed_on()); + return; + } + + // TODO: this should be done smarter + for(int i = 0; i < all.num; i++) + all.components[i]->on_message(msgtype, rawmsg); +} + +void GAMECLIENT::on_statechange(int new_state, int old_state) +{ + // clear out the invalid pointers + mem_zero(&gameclient.snap, sizeof(gameclient.snap)); + + for(int i = 0; i < all.num; i++) + all.components[i]->on_statechange(new_state, old_state); +} + + + +void GAMECLIENT::process_events() +{ + int snaptype = SNAP_CURRENT; + int num = snap_num_items(snaptype); + for(int index = 0; index < num; index++) + { + SNAP_ITEM item; + const void *data = snap_get_item(snaptype, index, &item); + + if(item.type == NETEVENTTYPE_DAMAGEIND) + { + NETEVENT_DAMAGEIND *ev = (NETEVENT_DAMAGEIND *)data; + gameclient.effects->damage_indicator(vec2(ev->x, ev->y), get_direction(ev->angle)); + } + else if(item.type == NETEVENTTYPE_AIRJUMP) + { + NETEVENT_COMMON *ev = (NETEVENT_COMMON *)data; + gameclient.effects->air_jump(vec2(ev->x, ev->y)); + } + else if(item.type == NETEVENTTYPE_EXPLOSION) + { + NETEVENT_EXPLOSION *ev = (NETEVENT_EXPLOSION *)data; + gameclient.effects->explosion(vec2(ev->x, ev->y)); + } + else if(item.type == NETEVENTTYPE_SPAWN) + { + NETEVENT_SPAWN *ev = (NETEVENT_SPAWN *)data; + gameclient.effects->playerspawn(vec2(ev->x, ev->y)); + } + else if(item.type == NETEVENTTYPE_DEATH) + { + NETEVENT_DEATH *ev = (NETEVENT_DEATH *)data; + gameclient.effects->playerdeath(vec2(ev->x, ev->y), ev->cid); + } + else if(item.type == NETEVENTTYPE_SOUNDWORLD) + { + NETEVENT_SOUNDWORLD *ev = (NETEVENT_SOUNDWORLD *)data; + snd_play_random(CHN_WORLD, ev->soundid, 1.0f, vec2(ev->x, ev->y)); + } + } +} + +void GAMECLIENT::on_snapshot() +{ + // clear out the invalid pointers + mem_zero(&gameclient.snap, sizeof(gameclient.snap)); + + static int snapshot_count = 0; + snapshot_count++; + + // secure snapshot + { + int num = snap_num_items(SNAP_CURRENT); + for(int index = 0; index < num; index++) + { + SNAP_ITEM item; + void *data = snap_get_item(SNAP_CURRENT, index, &item); + if(netobj_validate(item.type, data, item.datasize) != 0) + { + if(config.debug) + dbg_msg("game", "invalidated index=%d type=%d (%s) size=%d id=%d", index, item.type, netobj_get_name(item.type), item.datasize, item.id); + snap_invalidate_item(SNAP_CURRENT, index); + } + } + } + + process_events(); + + if(config.dbg_stress) + { + if((client_tick()%250) == 0) + { + NETMSG_CL_SAY msg; + msg.team = -1; + msg.message = "galenskap!!!!"; + msg.pack(MSGFLAG_VITAL); + client_send_msg(); + } + } + + // 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 == NETOBJTYPE_PLAYER_INFO) + { + const NETOBJ_PLAYER_INFO *info = (const NETOBJ_PLAYER_INFO *)data; + + gameclient.clients[info->cid].team = info->team; + + if(info->local) + { + gameclient.snap.local_info = info; + const void *data = snap_find_item(SNAP_CURRENT, NETOBJTYPE_CHARACTER, item.id); + if(data) + { + gameclient.snap.local_character = (const NETOBJ_CHARACTER *)data; + gameclient.local_character_pos = vec2(gameclient.snap.local_character->x, gameclient.snap.local_character->y); + + const void *p = snap_find_item(SNAP_PREV, NETOBJTYPE_CHARACTER, item.id); + if(p) + gameclient.snap.local_prev_character = (NETOBJ_CHARACTER *)p; + } + } + } + else if(item.type == NETOBJTYPE_GAME) + gameclient.snap.gameobj = (NETOBJ_GAME *)data; + else if(item.type == NETOBJTYPE_FLAG) + { + gameclient.snap.flags[item.id%2] = (const NETOBJ_FLAG *)data; + } + } + } + + for(int i = 0; i < MAX_CLIENTS; i++) + gameclient.clients[i].update_render_info(); + +} + +void GAMECLIENT::on_predict() +{ + CHARACTER_CORE before_prev_char = predicted_prev_char; + CHARACTER_CORE before_char = predicted_char; + + // repredict character + WORLD_CORE world; + world.tuning = tuning; + 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 == NETOBJTYPE_CHARACTER) + { + const NETOBJ_CHARACTER *character = (const NETOBJ_CHARACTER *)data; + gameclient.clients[client_id].predicted.world = &world; + world.characters[client_id] = &gameclient.clients[client_id].predicted; + + gameclient.clients[client_id].predicted.read(character); + } + else if(item.type == NETOBJTYPE_PLAYER_INFO) + { + const NETOBJ_PLAYER_INFO *info = (const NETOBJ_PLAYER_INFO *)data; + if(info->local) + local_cid = client_id; + } + } + + // we can't predict without our own id + if(local_cid == -1) + return; + + // predict + for(int tick = client_tick()+1; tick <= client_predtick(); tick++) + { + // fetch the local + if(tick == client_predtick() && world.characters[local_cid]) + predicted_prev_char = *world.characters[local_cid]; + + // first calculate where everyone should move + for(int c = 0; c < MAX_CLIENTS; c++) + { + if(!world.characters[c]) + continue; + + mem_zero(&world.characters[c]->input, sizeof(world.characters[c]->input)); + if(local_cid == c) + { + // apply player input + int *input = client_get_input(tick); + if(input) + world.characters[c]->input = *((NETOBJ_PLAYER_INPUT*)input); + } + + world.characters[c]->tick(); + } + + // move all players and quantize their data + for(int c = 0; c < MAX_CLIENTS; c++) + { + if(!world.characters[c]) + continue; + + world.characters[c]->move(); + world.characters[c]->quantize(); + } + + if(tick > last_new_predicted_tick) + { + last_new_predicted_tick = tick; + + if(local_cid != -1 && world.characters[local_cid]) + { + vec2 pos = world.characters[local_cid]->pos; + int events = world.characters[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) + { + gameclient.effects->air_jump(pos); + snd_play_random(CHN_WORLD, SOUND_PLAYER_AIRJUMP, 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.characters[local_cid]) + predicted_char = *world.characters[local_cid]; + } + + if(config.debug && predicted_tick == client_predtick()) + { + if(predicted_char.pos.x != before_char.pos.x || + predicted_char.pos.y != before_char.pos.y) + { + dbg_msg("client", "prediction error, (%d %d) (%d %d)", + (int)before_char.pos.x, (int)before_char.pos.y, + (int)predicted_char.pos.x, (int)predicted_char.pos.y); + } + + if(predicted_prev_char.pos.x != before_prev_char.pos.x || + predicted_prev_char.pos.y != before_prev_char.pos.y) + { + dbg_msg("client", "prediction error, prev (%d %d) (%d %d)", + (int)before_prev_char.pos.x, (int)before_prev_char.pos.y, + (int)predicted_prev_char.pos.x, (int)predicted_prev_char.pos.y); + } + } + + predicted_tick = client_predtick(); +} + +void GAMECLIENT::CLIENT_DATA::update_render_info() +{ + render_info = skin_info; + + // force team colors + if(gameclient.snap.gameobj && gameclient.snap.gameobj->flags&GAMEFLAG_TEAMS) + { + const int team_colors[2] = {65387, 10223467}; + if(team >= 0 || team <= 1) + { + render_info.texture = gameclient.skins->get(skin_id)->color_texture; + render_info.color_body = gameclient.skins->get_color(team_colors[team]); + render_info.color_feet = gameclient.skins->get_color(team_colors[team]); + } + } +} + + + + +void GAMECLIENT::send_switch_team(int team) +{ + NETMSG_CL_SETTEAM msg; + msg.team = team; + msg.pack(MSGFLAG_VITAL); + client_send_msg(); +} + +void GAMECLIENT::send_info(bool start) +{ + if(start) + { + NETMSG_CL_STARTINFO msg; + msg.name = config.player_name; + msg.skin = config.player_skin; + msg.use_custom_color = config.player_use_custom_color; + msg.color_body = config.player_color_body; + msg.color_feet = config.player_color_feet; + msg.pack(MSGFLAG_VITAL|MSGFLAG_FLUSH); + } + else + { + NETMSG_CL_CHANGEINFO msg; + msg.name = config.player_name; + msg.skin = config.player_skin; + msg.use_custom_color = config.player_use_custom_color; + msg.color_body = config.player_color_body; + msg.color_feet = config.player_color_feet; + msg.pack(MSGFLAG_VITAL); + } + client_send_msg(); +} + +void GAMECLIENT::send_kill(int client_id) +{ + NETMSG_CL_KILL msg; + msg.pack(MSGFLAG_VITAL); + client_send_msg(); +} diff --git a/src/game/client/gameclient.hpp b/src/game/client/gameclient.hpp new file mode 100644 index 00000000..335c3695 --- /dev/null +++ b/src/game/client/gameclient.hpp @@ -0,0 +1,109 @@ + +#include <base/vmath.hpp> +#include <game/gamecore.hpp> +#include "gc_render.hpp" + +class GAMECLIENT +{ + class STACK + { + public: + enum + { + MAX_COMPONENTS = 64, + }; + + STACK(); + void add(class COMPONENT *component); + + class COMPONENT *components[MAX_COMPONENTS]; + int num; + }; + + STACK all; + STACK input; + + void dispatch_input(); + void process_events(); + void update_local_character_pos(); + + int predicted_tick; + int last_new_predicted_tick; + +public: + + vec2 local_character_pos; + vec2 local_target_pos; + + // snap pointers + struct SNAPSTATE + { + const NETOBJ_CHARACTER *local_character; + const NETOBJ_CHARACTER *local_prev_character; + const NETOBJ_PLAYER_INFO *local_info; + const NETOBJ_FLAG *flags[2]; + const NETOBJ_GAME *gameobj; + + const NETOBJ_PLAYER_INFO *player_infos[MAX_CLIENTS]; + const NETOBJ_PLAYER_INFO *info_by_score[MAX_CLIENTS]; + int num_players; + }; + + SNAPSTATE snap; + + // client data + struct CLIENT_DATA + { + char name[64]; + char skin_name[64]; + int skin_id; + int skin_color; + int team; + int emoticon; + int emoticon_start; + CHARACTER_CORE predicted; + + TEE_RENDER_INFO skin_info; // this is what the server reports + TEE_RENDER_INFO render_info; // this is what we use + + float angle; + + void update_render_info(); + }; + + CLIENT_DATA clients[MAX_CLIENTS]; + + void on_reset(); + + // hooks + void on_connected(); + void on_render(); + void on_init(); + void on_statechange(int new_state, int old_state); + void on_message(int msgtype); + void on_snapshot(); + void on_predict(); + int on_snapinput(int *data); + + // actions + // TODO: move these + void send_switch_team(int team); + void send_info(bool start); + void send_kill(int client_id); + + // pointers to all systems + class CONSOLE *console; + class BINDS *binds; + class PARTICLES *particles; + class MENUS *menus; + class SKINS *skins; + class FLOW *flow; + class CHAT *chat; + class DAMAGEIND *damageind; + class CAMERA *camera; + class CONTROLS *controls; + class EFFECTS *effects; +}; + +extern GAMECLIENT gameclient; + diff --git a/src/game/client/gc_anim.hpp b/src/game/client/gc_anim.hpp deleted file mode 100644 index f6e9aac3..00000000 --- a/src/game/client/gc_anim.hpp +++ /dev/null @@ -1,14 +0,0 @@ - -struct ANIM_STATE -{ - ANIM_KEYFRAME body; - ANIM_KEYFRAME back_foot; - ANIM_KEYFRAME front_foot; - ANIM_KEYFRAME attach; -}; - -void anim_seq_eval(ANIM_SEQUENCE *seq, float time, ANIM_KEYFRAME *frame); -void anim_eval(ANIMATION *anim, float time, ANIM_STATE *state); -void anim_add_keyframe(ANIM_KEYFRAME *seq, ANIM_KEYFRAME *added, float amount); -void anim_add(ANIM_STATE *state, ANIM_STATE *added, float amount); -void anim_eval_add(ANIM_STATE *state, ANIMATION *anim, float time, float amount); diff --git a/src/game/client/gc_client.cpp b/src/game/client/gc_client.cpp index f9900ab3..2418b6ea 100644 --- a/src/game/client/gc_client.cpp +++ b/src/game/client/gc_client.cpp @@ -6,8 +6,6 @@ #include <string.h> extern "C" { - #include <engine/e_config.h> // TODO: this shouldn't be here - #include <engine/client/ec_font.h> // TODO: this shouldn't be here #include <engine/e_client_interface.h> }; @@ -16,70 +14,16 @@ extern "C" { #include "../layers.hpp" #include "gc_map_image.hpp" #include "../generated/gc_data.hpp" -#include "gc_menu.hpp" -#include "gc_skin.hpp" #include "gc_ui.hpp" #include "gc_client.hpp" #include "gc_render.hpp" -#include "gc_anim.hpp" -#include "gc_console.hpp" -//struct data_container *data = 0; -int64 debug_firedelay = 0; - -NETOBJ_PLAYER_INPUT input_data = {0}; -int input_target_lock = 0; -int input_direction_left = 0; -int input_direction_right = 0; - -int chat_mode = CHATMODE_NONE; -bool menu_active = false; -bool menu_game_active = false; -int emoticon_selector_active = 0; -int scoreboard_active = 0; -static int emoticon_selected_emote = -1; +#include "components/skins.hpp" +#include "components/damageind.hpp" +#include "gameclient.hpp" TUNING_PARAMS tuning; -vec2 mouse_pos; -vec2 local_character_pos; -vec2 local_target_pos; - -/* -const NETOBJ_PLAYER_CHARACTER *local_character = 0; -const NETOBJ_PLAYER_CHARACTER *local_prev_character = 0; -const NETOBJ_PLAYER_INFO *local_info = 0; -const NETOBJ_FLAG *flags[2] = {0,0}; -const NETOBJ_GAME *gameobj = 0; -*/ - -SNAPSTATE netobjects; - -int picked_up_weapon = -1; - -CLIENT_DATA client_datas[MAX_CLIENTS]; -void CLIENT_DATA::update_render_info() -{ - render_info = skin_info; - - // force team colors - if(netobjects.gameobj && netobjects.gameobj->gametype != GAMETYPE_DM) - { - const int team_colors[2] = {65387, 10223467}; - if(team >= 0 || team <= 1) - { - render_info.texture = skin_get(skin_id)->color_texture; - render_info.color_body = skin_get_color(team_colors[team]); - render_info.color_feet = skin_get_color(team_colors[team]); - } - } -} - - -// broadcasts -char broadcast_text[1024] = {0}; -int64 broadcast_time = 0; - void snd_play_random(int chn, int setid, float vol, vec2 pos) { SOUNDSET *set = &data->sounds[setid]; @@ -101,1570 +45,3 @@ void snd_play_random(int chn, int setid, float vol, vec2 pos) snd_play_at(chn, set->sounds[id].id, 0, pos.x, pos.y); set->last = id; } - - -void send_switch_team(int team) -{ - NETMSG_CL_SETTEAM msg; - msg.team = team; - msg.pack(MSGFLAG_VITAL); - client_send_msg(); -} - -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 dmgind; - -void effect_damage_indicator(vec2 pos, vec2 dir) -{ - dmgind.create(pos, dir); -} - -void render_damage_indicators() -{ - dmgind.render(); -} - -static line_input chat_input; -static const int chat_max_lines = 10; - -bool chat_input_handle(INPUT_EVENT e, void *user_data) -{ - if(chat_mode == CHATMODE_NONE) - return false; - - if(e.flags&INPFLAG_PRESS && (e.key == KEY_ENTER || e.key == KEY_KP_ENTER)) - { - if(chat_input.get_string()[0]) - chat_say(chat_mode == CHATMODE_ALL ? 0 : 1, chat_input.get_string()); - chat_mode = CHATMODE_NONE; - } - else - chat_input.process_input(e); - - return true; -} - -struct chatline -{ - int tick; - int client_id; - int team; - int name_color; - char name[64]; - char text[512]; -}; - -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; - chat_lines[chat_current_line].name_color = -2; - - if(client_id == -1) // server message - { - str_copy(chat_lines[chat_current_line].name, "*** ", sizeof(chat_lines[chat_current_line].name)); - str_format(chat_lines[chat_current_line].text, sizeof(chat_lines[chat_current_line].text), "%s", line); - } - else - { - if(client_datas[client_id].team == -1) - chat_lines[chat_current_line].name_color = -1; - - if(netobjects.gameobj && netobjects.gameobj->gametype != GAMETYPE_DM) - { - if(client_datas[client_id].team == 0) - chat_lines[chat_current_line].name_color = 0; - else if(client_datas[client_id].team == 1) - chat_lines[chat_current_line].name_color = 1; - } - - str_copy(chat_lines[chat_current_line].name, client_datas[client_id].name, sizeof(chat_lines[chat_current_line].name)); - str_format(chat_lines[chat_current_line].text, sizeof(chat_lines[chat_current_line].text), ": %s", line); - } - - if(config.debug) - dbg_msg("message", "chat cid=%d team=%d line='%s'", client_id, team, line); - dbg_msg("chat", "%s%s", chat_lines[chat_current_line].name, chat_lines[chat_current_line].text); - -} - - -KILLMSG killmsgs[killmsg_max]; -int killmsg_current = 0; - -//bool add_trail = false; - -line_input::line_input() -{ - clear(); -} - -void line_input::clear() -{ - mem_zero(str, sizeof(str)); - len = 0; - cursor_pos = 0; -} - -void line_input::set(const char *string) -{ - str_copy(str, string, sizeof(str)); - len = strlen(str); - cursor_pos = len; -} - -void line_input::process_input(INPUT_EVENT e) -{ - if(cursor_pos > len) - cursor_pos = len; - - char c = e.ch; - int k = e.key; - - if (!(c >= 0 && c < 32)) - { - if (len < sizeof(str) - 1 && cursor_pos < sizeof(str) - 1) - { - memmove(str + cursor_pos + 1, str + cursor_pos, len - cursor_pos + 1); - str[cursor_pos] = c; - cursor_pos++; - len++; - } - } - - if(e.flags&INPFLAG_PRESS) - { - if (k == KEY_BACKSPACE && cursor_pos > 0) - { - memmove(str + cursor_pos - 1, str + cursor_pos, len - cursor_pos + 1); - cursor_pos--; - len--; - } - else if (k == KEY_DEL && cursor_pos < len) - { - memmove(str + cursor_pos, str + cursor_pos + 1, len - cursor_pos); - len--; - } - else if (k == KEY_LEFT && cursor_pos > 0) - cursor_pos--; - else if (k == KEY_RIGHT && cursor_pos < len) - cursor_pos++; - else if (k == KEY_HOME) - cursor_pos = 0; - else if (k == KEY_END) - cursor_pos = len; - } -} - -INPUT_STACK_HANDLER::INPUT_STACK_HANDLER() -{ - num_handlers = 0; -} - -void INPUT_STACK_HANDLER::add_handler(CALLBACK cb, void *user) -{ - user_data[num_handlers] = user; - handlers[num_handlers++] = cb; -} - -void INPUT_STACK_HANDLER::dispatch_input() -{ - for(int i = 0; i < inp_num_events(); i++) - { - INPUT_EVENT e = inp_get_event(i); - - for(int h = 0; h < num_handlers; h++) - { - if(handlers[h](e, user_data[h])) - { - //dbg_msg("", "%d char=%d key=%d flags=%d", h, e.ch, e.key, e.flags); - break; - } - } - } - - inp_clear_events(); -} - - -INPUT_STACK_HANDLER input_stack; - -extern int render_popup(const char *caption, const char *text, const char *button_text); - -void process_events(int snaptype) -{ - int num = snap_num_items(snaptype); - for(int index = 0; index < num; index++) - { - SNAP_ITEM item; - const void *data = snap_get_item(snaptype, index, &item); - - if(item.type == NETEVENTTYPE_DAMAGEIND) - { - NETEVENT_DAMAGEIND *ev = (NETEVENT_DAMAGEIND *)data; - effect_damage_indicator(vec2(ev->x, ev->y), get_direction(ev->angle)); - } - else if(item.type == NETEVENTTYPE_AIRJUMP) - { - NETEVENT_COMMON *ev = (NETEVENT_COMMON *)data; - effect_air_jump(vec2(ev->x, ev->y)); - } - else if(item.type == NETEVENTTYPE_EXPLOSION) - { - NETEVENT_EXPLOSION *ev = (NETEVENT_EXPLOSION *)data; - effect_explosion(vec2(ev->x, ev->y)); - } - /*else if(item.type == EVENT_SMOKE) - { - EV_EXPLOSION *ev = (EV_EXPLOSION *)data; - vec2 p(ev->x, ev->y); - }*/ - else if(item.type == NETEVENTTYPE_SPAWN) - { - NETEVENT_SPAWN *ev = (NETEVENT_SPAWN *)data; - effect_playerspawn(vec2(ev->x, ev->y)); - } - else if(item.type == NETEVENTTYPE_DEATH) - { - NETEVENT_DEATH *ev = (NETEVENT_DEATH *)data; - effect_playerdeath(vec2(ev->x, ev->y), ev->cid); - } - else if(item.type == NETEVENTTYPE_SOUNDWORLD) - { - NETEVENT_SOUNDWORLD *ev = (NETEVENT_SOUNDWORLD *)data; - snd_play_random(CHN_WORLD, ev->soundid, 1.0f, vec2(ev->x, ev->y)); - } - } -} - -void clear_object_pointers() -{ - // clear out the invalid pointers - mem_zero(&netobjects, sizeof(netobjects)); -} - -void send_info(bool start) -{ - if(start) - { - NETMSG_CL_STARTINFO msg; - msg.name = config.player_name; - msg.skin = config.player_skin; - msg.use_custom_color = config.player_use_custom_color; - msg.color_body = config.player_color_body; - msg.color_feet = config.player_color_feet; - msg.pack(MSGFLAG_VITAL|MSGFLAG_FLUSH); - } - else - { - NETMSG_CL_CHANGEINFO msg; - msg.name = config.player_name; - msg.skin = config.player_skin; - msg.use_custom_color = config.player_use_custom_color; - msg.color_body = config.player_color_body; - msg.color_feet = config.player_color_feet; - msg.pack(MSGFLAG_VITAL); - } - client_send_msg(); -} - -void send_emoticon(int emoticon) -{ - NETMSG_CL_EMOTICON msg; - msg.emoticon = emoticon; - msg.pack(MSGFLAG_VITAL); - client_send_msg(); -} - -void send_kill(int client_id) -{ - NETMSG_CL_KILL msg; - msg.pack(MSGFLAG_VITAL); - client_send_msg(); -} - -void anim_seq_eval(ANIM_SEQUENCE *seq, float time, ANIM_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 - ANIM_KEYFRAME *frame1 = 0; - ANIM_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, ANIM_STATE *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(ANIM_KEYFRAME *seq, ANIM_KEYFRAME *added, float amount) -{ - seq->x += added->x*amount; - seq->y += added->y*amount; - seq->angle += added->angle*amount; -} - -void anim_add(ANIM_STATE *state, ANIM_STATE *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(ANIM_STATE *state, ANIMATION *anim, float time, float amount) -{ - ANIM_STATE add; - anim_eval(anim, time, &add); - anim_add(state, &add, amount); -} - -static 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); - } -} - -static vec2 emoticon_selector_mouse; - -void 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; - - if (length(emoticon_selector_mouse) > 100) - emoticon_selected_emote = (int)(selected_angle / (2*pi) * 12.0f); - - RECT screen = *ui_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 = emoticon_selected_emote == 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(); -} - -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(netobjects.gameobj && netobjects.gameobj->time_limit) - { - char buf[64]; - str_format(buf, sizeof(buf), "Time Limit: %d min", netobjects.gameobj->time_limit); - gfx_text(0, x+w/2, y, 24.0f, buf, -1); - } - if(netobjects.gameobj && netobjects.gameobj->score_limit) - { - char buf[64]; - str_format(buf, sizeof(buf), "Score Limit: %d", netobjects.gameobj->score_limit); - gfx_text(0, x+40, y, 24.0f, buf, -1); - } -} - -void render_spectators(float x, float y, float w) -{ - char buffer[1024*4]; - int count = 0; - float h = 120.0f; - - str_copy(buffer, "Spectators: ", sizeof(buffer)); - - 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(); - - 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 == NETOBJTYPE_PLAYER_INFO) - { - const NETOBJ_PLAYER_INFO *info = (const NETOBJ_PLAYER_INFO *)data; - if(info->team == -1) - { - if(count) - strcat(buffer, ", "); - strcat(buffer, client_datas[info->cid].name); - count++; - } - } - } - - gfx_text(0, x+10, y, 32, buffer, (int)w-20); -} - -void render_scoreboard(float x, float y, float w, int team, const char *title) -{ - ANIM_STATE 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 = 750.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(netobjects.gameobj->game_over) - title = "Game Over"; - else - title = "Score Board"; - } - - float tw = gfx_text_width(0, 48, title, -1); - - if(team == -1) - { - gfx_text(0, x+w/2-tw/2, y, 48, title, -1); - } - else - { - gfx_text(0, x+10, y, 48, title, -1); - - if(netobjects.gameobj) - { - char buf[128]; - int score = team ? netobjects.gameobj->teamscore_blue : netobjects.gameobj->teamscore_red; - str_format(buf, sizeof(buf), "%d", score); - tw = gfx_text_width(0, 48, buf, -1); - gfx_text(0, x+w-tw-30, y, 48, buf, -1); - } - } - - y += 54.0f; - - // find players - const NETOBJ_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 == NETOBJTYPE_PLAYER_INFO) - { - players[num_players] = (const NETOBJ_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 NETOBJ_PLAYER_INFO *tmp = players[i]; - players[i] = players[i+1]; - players[i+1] = tmp; - } - } - } - - // render headlines - gfx_text(0, x+10, y, 24.0f, "Score", -1); - gfx_text(0, x+125, y, 24.0f, "Name", -1); - gfx_text(0, x+w-70, y, 24.0f, "Ping", -1); - y += 29.0f; - - // render player scores - for(int i = 0; i < num_players; i++) - { - const NETOBJ_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 = 35.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(); - } - - str_format(buf, sizeof(buf), "%4d", info->score); - gfx_text(0, x+60-gfx_text_width(0, font_size,buf,-1), y, font_size, buf, -1); - - gfx_text(0, x+128, y, font_size, client_datas[info->cid].name, -1); - - str_format(buf, sizeof(buf), "%4d", info->latency); - float tw = gfx_text_width(0, font_size, buf, -1); - gfx_text(0, x+w-tw-35, y, font_size, buf, -1); - - // render avatar - if((netobjects.flags[0] && netobjects.flags[0]->carried_by == info->cid) || - (netobjects.flags[1] && netobjects.flags[1]->carried_by == info->cid)) - { - gfx_blend_normal(); - gfx_texture_set(data->images[IMAGE_GAME].id); - gfx_quads_begin(); - - if(info->team == 0) select_sprite(SPRITE_FLAG_BLUE, SPRITE_FLAG_FLIP_X); - else select_sprite(SPRITE_FLAG_RED, SPRITE_FLAG_FLIP_X); - - float size = 64.0f; - gfx_quads_drawTL(x+55, y-15, size/2, size); - gfx_quads_end(); - } - - render_tee(&idlestate, &client_datas[info->cid].render_info, EMOTE_NORMAL, vec2(1,0), vec2(x+90, y+28)); - - - y += 50.0f; - } -} -/* -static int 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; - - return (*v&1); -}*/ - -void chat_say(int team, const char *line) -{ - // send chat message - NETMSG_CL_SAY msg; - msg.team = team; - msg.message = line; - msg.pack(MSGFLAG_VITAL); - client_send_msg(); -} - -void chat_enable_mode(int team) -{ - if(chat_mode == CHATMODE_NONE) - { - if(team) - chat_mode = CHATMODE_TEAM; - else - chat_mode = CHATMODE_ALL; - - chat_input.clear(); - inp_clear_events(); - } -} - -void render_game() -{ - // update the effects - effects_update(); - particle_update(client_frametime()); - - - float width = 400*3.0f*gfx_screenaspect(); - float height = 400*3.0f; - - bool spectate = false; - - if(config.cl_predict) - { - if(!netobjects.local_character || (netobjects.local_character->health < 0) || (netobjects.gameobj && netobjects.gameobj->game_over)) - { - // don't use predicted - } - else - local_character_pos = mix(predicted_prev_char.pos, predicted_char.pos, client_predintratick()); - } - else if(netobjects.local_character && netobjects.local_prev_character) - { - local_character_pos = mix( - vec2(netobjects.local_prev_character->x, netobjects.local_prev_character->y), - vec2(netobjects.local_character->x, netobjects.local_character->y), client_intratick()); - } - - if(netobjects.local_info && netobjects.local_info->team == -1) - spectate = true; - - ANIM_STATE 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(server_motd_time) - server_motd_time = 0; - else if (chat_mode) - chat_mode = CHATMODE_NONE; - else if(!console_active()) - { - 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; - } - - // - float camera_max_distance = 200.0f; - float deadzone = config.cl_mouse_deadzone; - float follow_factor = config.cl_mouse_followfactor/100.0f; - float mouse_max = min(camera_max_distance/follow_factor + deadzone, (float)config.cl_mouse_max_distance); - vec2 camera_offset(0, 0); - - // fetch new input - if(!menu_active) - { - int x, y; - if(!emoticon_selector_active) - { - inp_mouse_relative(&x, &y); - mouse_pos += vec2(x, y); - } - - if(spectate) - { - if(mouse_pos.x < 200.0f) mouse_pos.x = 200.0f; - if(mouse_pos.y < 200.0f) mouse_pos.y = 200.0f; - if(mouse_pos.x > col_width()*32-200.0f) mouse_pos.x = col_width()*32-200.0f; - if(mouse_pos.y > col_height()*32-200.0f) mouse_pos.y = col_height()*32-200.0f; - } - else - { - float l = length(mouse_pos); - - if(l > mouse_max) - { - mouse_pos = normalize(mouse_pos)*mouse_max; - l = mouse_max; - } - - float offset_amount = max(l-deadzone, 0) * follow_factor; - if(l > 0.0001f) // make sure that this isn't 0 - camera_offset = normalize(mouse_pos)*offset_amount; - } - } - - // 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); - } - - // center at char but can be moved when mouse is far away - /* - float offx = 0, offy = 0; - if (config.cl_dynamic_camera) - { - 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 - float zoom = 1.0f; - - if(spectate) - render_world(mouse_pos.x, mouse_pos.y, zoom); - else - { - render_world(local_character_pos.x+camera_offset.x, local_character_pos.y+camera_offset.y, zoom); - - // draw screen box - if(0) - { - gfx_texture_set(-1); - gfx_blend_normal(); - gfx_lines_begin(); - float cx = local_character_pos.x+camera_offset.x; - float cy = local_character_pos.y+camera_offset.y; - 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; - - // 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(netobjects.local_character && !spectate && !(netobjects.gameobj && netobjects.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.id[netobjects.local_character->weapon%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,300*gfx_screenaspect(),300); - - // if weaponstage is active, put a "glow" around the stage ammo - select_sprite(data->weapons.id[netobjects.local_character->weapon%NUM_WEAPONS].sprite_proj); - for (int i = 0; i < min(netobjects.local_character->ammocount, 10); 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 < netobjects.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 < netobjects.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 = 36.0f; - float killername_w = gfx_text_width(0, font_size, client_datas[killmsgs[r].killer].name, -1); - float victimname_w = gfx_text_width(0, font_size, client_datas[killmsgs[r].victim].name, -1); - - float x = startx; - - // render victim name - x -= victimname_w; - gfx_text(0, x, y, font_size, client_datas[killmsgs[r].victim].name, -1); - - // render victim tee - x -= 24.0f; - - if(netobjects.gameobj && netobjects.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].render_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.id[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(netobjects.gameobj && netobjects.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].render_info, EMOTE_ANGRY, vec2(1,0), vec2(x, y+28)); - x -= 32.0f; - - // render killer name - x -= killername_w; - gfx_text(0, x, y, font_size, client_datas[killmsgs[r].killer].name, -1); - } - - y += 44; - } - } - - // render chat - { - gfx_mapscreen(0,0,300*gfx_screenaspect(),300); - float x = 10.0f; - float y = 300.0f-30.0f; - if(chat_mode != CHATMODE_NONE) - { - // render chat input - TEXT_CURSOR cursor; - gfx_text_set_cursor(&cursor, x, y, 8.0f, TEXTFLAG_RENDER); - cursor.line_width = 300.0f; - - if(chat_mode == CHATMODE_ALL) - gfx_text_ex(&cursor, "All: ", -1); - else if(chat_mode == CHATMODE_TEAM) - gfx_text_ex(&cursor, "Team: ", -1); - else - gfx_text_ex(&cursor, "Chat: ", -1); - - gfx_text_ex(&cursor, chat_input.get_string(), chat_input.cursor_offset()); - TEXT_CURSOR marker = cursor; - gfx_text_ex(&marker, "|", -1); - gfx_text_ex(&cursor, chat_input.get_string()+chat_input.cursor_offset(), -1); - } - - y -= 8; - - 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; - - float begin = x; - float fontsize = 8.0f; - - // get the y offset - TEXT_CURSOR cursor; - gfx_text_set_cursor(&cursor, begin, 0, fontsize, 0); - cursor.line_width = 300.0f; - gfx_text_ex(&cursor, chat_lines[r].name, -1); - gfx_text_ex(&cursor, chat_lines[r].text, -1); - y -= cursor.y + cursor.font_size; - - // reset the cursor - gfx_text_set_cursor(&cursor, begin, y, fontsize, TEXTFLAG_RENDER); - cursor.line_width = 300.0f; - - // render name - gfx_text_color(0.8f,0.8f,0.8f,1); - if(chat_lines[r].client_id == -1) - gfx_text_color(1,1,0.5f,1); // system - else if(chat_lines[r].team) - gfx_text_color(0.45f,0.9f,0.45f,1); // team message - else if(chat_lines[r].name_color == 0) - gfx_text_color(1.0f,0.5f,0.5f,1); // red - else if(chat_lines[r].name_color == 1) - gfx_text_color(0.7f,0.7f,1.0f,1); // blue - else if(chat_lines[r].name_color == -1) - gfx_text_color(0.75f,0.5f,0.75f, 1); // spectator - - // render name - gfx_text_ex(&cursor, chat_lines[r].name, -1); - - // render line - gfx_text_color(1,1,1,1); - if(chat_lines[r].client_id == -1) - gfx_text_color(1,1,0.5f,1); // system - else if(chat_lines[r].team) - gfx_text_color(0.65f,1,0.65f,1); // team message - - gfx_text_ex(&cursor, chat_lines[r].text, -1); - } - - gfx_text_color(1,1,1,1); - } - - // render goals - if(netobjects.gameobj) - { - int gametype = netobjects.gameobj->gametype; - - float whole = 300*gfx_screenaspect(); - float half = whole/2.0f; - - gfx_mapscreen(0,0,300*gfx_screenaspect(),300); - if(!netobjects.gameobj->sudden_death) - { - char buf[32]; - int time = 0; - if(netobjects.gameobj->time_limit) - { - time = netobjects.gameobj->time_limit*60 - ((client_tick()-netobjects.gameobj->round_start_tick)/client_tickspeed()); - - if(netobjects.gameobj->game_over) - time = 0; - } - else - time = (client_tick()-netobjects.gameobj->round_start_tick)/client_tickspeed(); - - str_format(buf, sizeof(buf), "%d:%02d", time /60, time %60); - float w = gfx_text_width(0, 16, buf, -1); - gfx_text(0, half-w/2, 2, 16, buf, -1); - } - - if(netobjects.gameobj->sudden_death) - { - const char *text = "Sudden Death"; - float w = gfx_text_width(0, 16, text, -1); - gfx_text(0, half-w/2, 2, 16, text, -1); - } - - // render small score hud - if(!(netobjects.gameobj && netobjects.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(whole-40, 300-40-15+t*20, 50, 18, 5.0f); - gfx_quads_end(); - - char buf[32]; - str_format(buf, sizeof(buf), "%d", t?netobjects.gameobj->teamscore_blue:netobjects.gameobj->teamscore_red); - float w = gfx_text_width(0, 14, buf, -1); - - if(gametype == GAMETYPE_CTF) - { - gfx_text(0, whole-20-w/2+5, 300-40-15+t*20, 14, buf, -1); - if(netobjects.flags[t]) - { - if(netobjects.flags[t]->carried_by == -2 || (netobjects.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(whole-40+5, 300-40-15+t*20+1, size/2, size); - gfx_quads_end(); - } - else if(netobjects.flags[t]->carried_by >= 0) - { - int id = netobjects.flags[t]->carried_by%MAX_CLIENTS; - const char *name = client_datas[id].name; - float w = gfx_text_width(0, 10, name, -1); - gfx_text(0, whole-40-5-w, 300-40-15+t*20+2, 10, name, -1); - TEE_RENDER_INFO info = client_datas[id].render_info; - info.size = 18.0f; - - render_tee(&idlestate, &info, EMOTE_NORMAL, vec2(1,0), - vec2(whole-40+10, 300-40-15+9+t*20+1)); - } - } - } - else - gfx_text(0, whole-20-w/2, 300-40-15+t*20, 14, buf, -1); - } - } - - // render warmup timer - if(netobjects.gameobj->warmup) - { - char buf[256]; - float w = gfx_text_width(0, 24, "Warmup", -1); - gfx_text(0, 150*gfx_screenaspect()+-w/2, 50, 24, "Warmup", -1); - - int seconds = netobjects.gameobj->warmup/SERVER_TICK_SPEED; - if(seconds < 5) - str_format(buf, sizeof(buf), "%d.%d", seconds, (netobjects.gameobj->warmup*10/SERVER_TICK_SPEED)%10); - else - str_format(buf, sizeof(buf), "%d", seconds); - w = gfx_text_width(0, 24, buf, -1); - gfx_text(0, 150*gfx_screenaspect()+-w/2, 75, 24, buf, -1); - } - } - - if (menu_active) - { - menu_render(); - return; - } - - // do emoticon - if(emoticon_selector_active) - emoticon_selector_render(); - else - { - emoticon_selector_mouse = vec2(0,0); - - if(emoticon_selected_emote != -1) - { - send_emoticon(emoticon_selected_emote); - emoticon_selected_emote = -1; - } - } - - // render debug stuff - - { - float w = 300*gfx_screenaspect(); - gfx_mapscreen(0, 0, w, 300); - - char buf[512]; - if(config.cl_showfps) - { - str_format(buf, sizeof(buf), "%d", (int)(1.0f/client_frametime())); - gfx_text(0, w-10-gfx_text_width(0,12,buf,-1), 10, 12, buf, -1); - } - } - - if(config.debug && netobjects.local_character && netobjects.local_prev_character) - { - gfx_mapscreen(0, 0, 300*gfx_screenaspect(), 300); - - /*float speed = distance(vec2(netobjects.local_prev_character->x, netobjects.local_prev_character->y), - vec2(netobjects.local_character->x, netobjects.local_character->y));*/ - - float velspeed = length(vec2(netobjects.local_character->vx/256.0f, netobjects.local_character->vy/256.0f))*50; - - float ramp = velocity_ramp(velspeed, tuning.velramp_start, tuning.velramp_range, tuning.velramp_curvature); - - char buf[512]; - str_format(buf, sizeof(buf), "%.0f\n%.0f\n%.2f\n%d %s\n%d %d", - velspeed, velspeed*ramp, ramp, - netobj_num_corrections(), netobj_corrected_on(), - netobjects.local_character->x, - netobjects.local_character->y - ); - gfx_text(0, 150, 50, 12, buf, -1); - } - - - bool do_scoreboard = false; - - // if we are dead - if(!spectate && (!netobjects.local_character || netobjects.local_character->health < 0)) - do_scoreboard = true; - - // if we the game is over - if(netobjects.gameobj && netobjects.gameobj->game_over) - do_scoreboard = true; - - // showing motd, skip it - if(time_get() < server_motd_time) - do_scoreboard = false; - - // always show if we really want - if(scoreboard_active) - { - server_motd_time = 0; // disables the motd - do_scoreboard = true; - } - - // render motd - if(!do_scoreboard && time_get() < server_motd_time) - { - gfx_mapscreen(0, 0, width, height); - - float h = 800.0f; - float w = 650.0f; - float x = width/2 - w/2; - float y = 150.0f; - - gfx_blend_normal(); - gfx_texture_set(-1); - gfx_quads_begin(); - gfx_setcolor(0,0,0,0.5f); - draw_round_rect(x, y, w, h, 40.0f); - gfx_quads_end(); - - gfx_text(0, x+40.0f, y+40.0f, 32.0f, server_motd, (int)(w-80.0f)); - } - - // render scoreboard - if(do_scoreboard) - { - gfx_mapscreen(0, 0, width, height); - - float w = 650.0f; - - if(netobjects.gameobj && netobjects.gameobj->gametype == GAMETYPE_DM) - { - render_scoreboard(width/2-w/2, 150.0f, w, 0, 0); - //render_scoreboard(gameobj, 0, 0, -1, 0); - } - else - { - - if(netobjects.gameobj && netobjects.gameobj->game_over) - { - const char *text = "DRAW!"; - if(netobjects.gameobj->teamscore_red > netobjects.gameobj->teamscore_blue) - text = "Red Team Wins!"; - else if(netobjects.gameobj->teamscore_blue > netobjects.gameobj->teamscore_red) - text = "Blue Team Wins!"; - - float w = gfx_text_width(0, 92.0f, text, -1); - gfx_text(0, 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+750+25, w); - render_spectators(width/2-w/2, 150+750+25+50+25, w); - } - - { - gfx_mapscreen(0, 0, 300*gfx_screenaspect(), 300); - - if(client_connection_problems()) - { - const char *text = "Connection Problems..."; - float w = gfx_text_width(0, 24, text, -1); - gfx_text(0, 150*gfx_screenaspect()-w/2, 50, 24, text, -1); - } - - if(time_get() < broadcast_time) - { - float w = gfx_text_width(0, 14, broadcast_text, -1); - gfx_text(0, 150*gfx_screenaspect()-w/2, 35, 14, broadcast_text, -1); - } - - TUNING_PARAMS standard_tuning; - - // render warning about non standard tuning - bool flash = time_get()/(time_freq()/2)%2 == 0; - if(config.cl_warning_tuning && memcmp(&standard_tuning, &tuning, sizeof(TUNING_PARAMS)) != 0) - { - const char *text = "Warning! Server is running non-standard tuning."; - if(flash) - gfx_text_color(1,0.4f,0.4f,1.0f); - else - gfx_text_color(0.75f,0.2f,0.2f,1.0f); - gfx_text(0x0, 5, 40, 6, text, -1); - gfx_text_color(1,1,1,1); - } - - // render tuning debugging - if(config.dbg_tuning) - { - float y = 50.0f; - int count = 0; - for(int i = 0; i < tuning.num(); i++) - { - char buf[128]; - float current, standard; - tuning.get(i, ¤t); - standard_tuning.get(i, &standard); - - if(standard == current) - gfx_text_color(1,1,1,1.0f); - else - gfx_text_color(1,0.25f,0.25f,1.0f); - - float w; - float x = 5.0f; - - str_format(buf, sizeof(buf), "%.2f", standard); - x += 20.0f; - w = gfx_text_width(0, 5, buf, -1); - gfx_text(0x0, x-w, y+count*6, 5, buf, -1); - - str_format(buf, sizeof(buf), "%.2f", current); - x += 20.0f; - w = gfx_text_width(0, 5, buf, -1); - gfx_text(0x0, x-w, y+count*6, 5, buf, -1); - - x += 5.0f; - gfx_text(0x0, x, y+count*6, 5, tuning.names[i], -1); - - count++; - } - - y = y+count*6; - - gfx_texture_set(-1); - gfx_blend_normal(); - gfx_lines_begin(); - float height = 50.0f; - float pv = 1; - for(int i = 0; i < 100; i++) - { - float speed = i/100.0f * 3000; - float ramp = velocity_ramp(speed, tuning.velramp_start, tuning.velramp_range, tuning.velramp_curvature); - float rampedspeed = (speed * ramp)/1000.0f; - gfx_lines_draw((i-1)*2, y+height-pv*height, i*2, y+height-rampedspeed*height); - //gfx_lines_draw((i-1)*2, 200, i*2, 200); - pv = rampedspeed; - } - gfx_lines_end(); - } - - gfx_text_color(1,1,1,1); - } - -} - - -extern "C" const char *modc_getitemname(int type) -{ - return netobj_get_name(type); -} diff --git a/src/game/client/gc_client.hpp b/src/game/client/gc_client.hpp index 2b6ccd46..371338ff 100644 --- a/src/game/client/gc_client.hpp +++ b/src/game/client/gc_client.hpp @@ -14,50 +14,12 @@ enum CHN_GLOBAL, }; -//extern struct data_container *data; - -extern vec2 mouse_pos; -extern vec2 local_character_pos; -extern vec2 local_target_pos; - -// snap pointers -struct SNAPSTATE -{ - const NETOBJ_CHARACTER *local_character; - const NETOBJ_CHARACTER *local_prev_character; - const NETOBJ_PLAYER_INFO *local_info; - const NETOBJ_FLAG *flags[2]; - const NETOBJ_GAME *gameobj; - - const NETOBJ_PLAYER_INFO *player_infos[MAX_CLIENTS]; - const NETOBJ_PLAYER_INFO *info_by_score[MAX_CLIENTS]; - int num_players; -}; - -extern SNAPSTATE netobjects; - -/* -extern const NETOBJ_PLAYER_CHARACTER *local_character; -extern const NETOBJ_PLAYER_CHARACTER *local_prev_character; -extern const NETOBJ_PLAYER_INFO *local_info; -extern const NETOBJ_FLAG *flags[2]; -extern const NETOBJ_GAME *gameobj; -* */ - extern TUNING_PARAMS tuning; // predicted players extern CHARACTER_CORE predicted_prev_char; extern CHARACTER_CORE predicted_char; -// input -extern NETOBJ_PLAYER_INPUT input_data; -extern int input_direction_left; -extern int input_direction_right; - -// debug -extern int64 debug_firedelay; - // extra projs enum { @@ -66,211 +28,49 @@ enum extern NETOBJ_PROJECTILE extraproj_projectiles[MAX_EXTRA_PROJECTILES]; extern int extraproj_num; - void extraproj_reset(); -// chat -enum -{ - CHATMODE_NONE=0, - CHATMODE_ALL, - CHATMODE_TEAM, -}; - -extern int chat_mode; -void chat_add_line(int client_id, int team, const char *line); -void chat_reset(); -bool chat_input_handle(INPUT_EVENT e, void *user_data); - -// broadcasts -extern char broadcast_text[1024]; -extern int64 broadcast_time; - -// motd -extern int64 server_motd_time; -extern char server_motd[900]; // FUGLY - -// line input helter -class line_input -{ - char str[256]; - unsigned len; - unsigned cursor_pos; -public: - class callback - { - public: - virtual ~callback() {} - virtual bool event(INPUT_EVENT e) = 0; - }; - - line_input(); - void clear(); - void process_input(INPUT_EVENT e); - void set(const char *string); - const char *get_string() const { return str; } - int get_length() const { return len; } - unsigned cursor_offset() const { return cursor_pos; } -}; - -class INPUT_STACK_HANDLER -{ -public: - typedef bool (*CALLBACK)(INPUT_EVENT e, void *user); - - INPUT_STACK_HANDLER(); - void add_handler(CALLBACK cb, void *user_data); - void dispatch_input(); - -private: - enum - { - MAX_HANDLERS=16 - }; - - CALLBACK handlers[MAX_HANDLERS]; - void *user_data[MAX_HANDLERS]; - int num_handlers; -}; - -extern INPUT_STACK_HANDLER input_stack; - - -extern int emoticon_selector_active; // TODO: ugly -extern int scoreboard_active; // TODO: ugly - -// client data -struct CLIENT_DATA -{ - char name[64]; - char skin_name[64]; - int skin_id; - int skin_color; - int team; - int emoticon; - int emoticon_start; - CHARACTER_CORE predicted; - - TEE_RENDER_INFO skin_info; // this is what the server reports - TEE_RENDER_INFO render_info; // this is what we use - - float angle; - - void update_render_info(); -}; - -extern CLIENT_DATA client_datas[MAX_CLIENTS]; - -// kill messages -struct KILLMSG -{ - int weapon; - int victim; - int killer; - int mode_special; // for CTF, if the guy is carrying a flag for example - int tick; -}; - -const int killmsg_max = 5; -extern KILLMSG killmsgs[killmsg_max]; -extern int killmsg_current; - -// -void send_switch_team(int team); - // various helpers void snd_play_random(int chn, int setid, float vol, vec2 pos); -void process_events(int snaptype); -void clear_object_pointers(); -void reset_projectile_particles(); -void send_info(bool start); -void send_emoticon(int emoticon); -void chat_say(int team, const char *line); void chat_enable_mode(int team); inline vec2 random_dir() { return normalize(vec2(frandom()-0.5f, frandom()-0.5f)); } - -// effects -void effects_update(); - -void effect_bullettrail(vec2 pos); -void effect_smoketrail(vec2 pos, vec2 vel); -void effect_skidtrail(vec2 pos, vec2 vel); -void effect_explosion(vec2 pos); -void effect_air_jump(vec2 pos); -void effect_damage_indicator(vec2 pos, vec2 dir); -void effect_playerspawn(vec2 pos); -void effect_playerdeath(vec2 pos, int cid); -void effect_powerupshine(vec2 pos, vec2 size); - -// particles -struct PARTICLE +inline float hue_to_rgb(float v1, float v2, float h) { - void set_default() - { - vel = vec2(0,0); - life_span = 0; - start_size = 32; - end_size = 32; - rot = 0; - rotspeed = 0; - gravity = 0; - friction = 0; - flow_affected = 1.0f; - color = vec4(1,1,1,1); - } - - vec2 pos; - vec2 vel; - - int spr; - - float flow_affected; - - float life_span; - - float start_size; - float end_size; - - float rot; - float rotspeed; - - float gravity; - float friction; - - vec4 color; - - // set by the particle system - float life; - int prev_part; - int next_part; -}; - -enum + 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; +} + +inline vec3 hsl_to_rgb(vec3 in) { - PARTGROUP_PROJECTILE_TRAIL=0, - PARTGROUP_EXPLOSIONS, - PARTGROUP_GENERAL, - NUM_PARTGROUPS -}; + float v1, v2; + vec3 out; -void particle_add(int group, PARTICLE *part); -void particle_render(int group); -void particle_update(float time_passed); -void particle_reset(); + 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); -// flow grid -vec2 flow_get(vec2 pos); -void flow_add(vec2 pos, vec2 vel, float size); -void flow_dbg_render(); -void flow_init(); -void flow_update(); + v1 = 2 * in.l - v2; -// -void binds_default(); -void binds_save(); -void binds_set(int keyid, const char *str); -const char *binds_get(int keyid); + 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; +} diff --git a/src/game/client/gc_console.hpp b/src/game/client/gc_console.hpp deleted file mode 100644 index 0f5e7b9f..00000000 --- a/src/game/client/gc_console.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef _GC_CONSOLE_H -#define _GC_CONSOLE_H - -#include <engine/e_client_interface.h> - -bool console_input_cli(INPUT_EVENT e, void *user_data); -bool console_input_special_binds(INPUT_EVENT e, void *user_data); -bool console_input_normal_binds(INPUT_EVENT e, void *user_data); - -//void console_handle_input(); - -void console_clear(int type); -void console_toggle(int tpye); -void console_render(); -int console_active(); -void client_console_init(); -void console_rcon_print(const char *line); - -#endif diff --git a/src/game/client/gc_effects.cpp b/src/game/client/gc_effects.cpp index 387e1471..f8c4e2c8 100644 --- a/src/game/client/gc_effects.cpp +++ b/src/game/client/gc_effects.cpp @@ -1,8 +1,13 @@ #include <engine/e_client_interface.h> #include "gc_client.hpp" -#include "gc_skin.hpp" #include "../generated/gc_data.hpp" +#include "components/particles.hpp" +#include "components/skins.hpp" +#include "components/flow.hpp" +#include "components/damageind.hpp" +#include "gameclient.hpp" + static bool add_50hz = false; static bool add_100hz = false; @@ -21,10 +26,15 @@ void effect_air_jump(vec2 pos) p.gravity = 500; p.friction = 0.7f; p.flow_affected = 0.0f; - particle_add(PARTGROUP_GENERAL, &p); + gameclient.particles->add(PARTICLES::GROUP_GENERAL, &p); p.pos = pos + vec2(6.0f, 16.0f); - particle_add(PARTGROUP_GENERAL, &p); + gameclient.particles->add(PARTICLES::GROUP_GENERAL, &p); +} + +void effect_damage_indicator(vec2 pos, vec2 dir) +{ + gameclient.damageind->create(pos, dir); } void effect_powerupshine(vec2 pos, vec2 size) @@ -45,7 +55,7 @@ void effect_powerupshine(vec2 pos, vec2 size) p.gravity = 500; p.friction = 0.9f; p.flow_affected = 0.0f; - particle_add(PARTGROUP_GENERAL, &p); + gameclient.particles->add(PARTICLES::GROUP_GENERAL, &p); } void effect_smoketrail(vec2 pos, vec2 vel) @@ -63,7 +73,7 @@ void effect_smoketrail(vec2 pos, vec2 vel) p.end_size = 0; p.friction = 0.7; p.gravity = frandom()*-500.0f; - particle_add(PARTGROUP_PROJECTILE_TRAIL, &p); + gameclient.particles->add(PARTICLES::GROUP_PROJECTILE_TRAIL, &p); } @@ -83,7 +93,7 @@ void effect_skidtrail(vec2 pos, vec2 vel) p.friction = 0.7f; p.gravity = frandom()*-500.0f; p.color = vec4(0.75f,0.75f,0.75f,1.0f); - particle_add(PARTGROUP_GENERAL, &p); + gameclient.particles->add(PARTICLES::GROUP_GENERAL, &p); } void effect_bullettrail(vec2 pos) @@ -99,7 +109,7 @@ void effect_bullettrail(vec2 pos) p.start_size = 8.0f; p.end_size = 0; p.friction = 0.7f; - particle_add(PARTGROUP_PROJECTILE_TRAIL, &p); + gameclient.particles->add(PARTICLES::GROUP_PROJECTILE_TRAIL, &p); } void effect_playerspawn(vec2 pos) @@ -119,7 +129,7 @@ void effect_playerspawn(vec2 pos) p.gravity = frandom()*-400.0f; p.friction = 0.7f; p.color = vec4(0xb5/255.0f, 0x50/255.0f, 0xcb/255.0f, 1.0f); - particle_add(PARTGROUP_GENERAL, &p); + gameclient.particles->add(PARTICLES::GROUP_GENERAL, &p); } } @@ -130,7 +140,7 @@ void effect_playerdeath(vec2 pos, int cid) if(cid >= 0) { - const skin *s = skin_get(client_datas[cid].skin_id); + const SKINS::SKIN *s = gameclient.skins->get(gameclient.clients[cid].skin_id); if(s) blood_color = s->blood_color; } @@ -151,7 +161,7 @@ void effect_playerdeath(vec2 pos, int cid) p.friction = 0.8f; vec3 c = blood_color * (0.75f + frandom()*0.25f); p.color = vec4(c.r, c.g, c.b, 0.75f); - particle_add(PARTGROUP_GENERAL, &p); + gameclient.particles->add(PARTICLES::GROUP_GENERAL, &p); } } @@ -166,7 +176,7 @@ void effect_explosion(vec2 pos) continue; float a = 1 - (length(vec2(x,y)) / length(vec2(8,8))); - flow_add(pos+vec2(x,y)*16, normalize(vec2(x,y))*5000.0f*a, 10.0f); + gameclient.flow->add(pos+vec2(x,y)*16, normalize(vec2(x,y))*5000.0f*a, 10.0f); } // add the explosion @@ -178,7 +188,7 @@ void effect_explosion(vec2 pos) p.start_size = 150.0f; p.end_size = 0; p.rot = frandom()*pi*2; - particle_add(PARTGROUP_EXPLOSIONS, &p); + gameclient.particles->add(PARTICLES::GROUP_EXPLOSIONS, &p); // add the smoke for(int i = 0; i < 24; i++) @@ -194,7 +204,7 @@ void effect_explosion(vec2 pos) p.gravity = frandom()*-800.0f; p.friction = 0.4f; p.color = mix(vec4(0.75f,0.75f,0.75f,1.0f), vec4(0.5f,0.5f,0.5f,1.0f), frandom()); - particle_add(PARTGROUP_GENERAL, &p); + gameclient.particles->add(PARTICLES::GROUP_GENERAL, &p); } } @@ -220,6 +230,5 @@ void effects_update() add_50hz = false; if(add_50hz) - flow_update(); - + gameclient.flow->update(); } diff --git a/src/game/client/gc_hooks.cpp b/src/game/client/gc_hooks.cpp index 8db0995f..bef6f508 100644 --- a/src/game/client/gc_hooks.cpp +++ b/src/game/client/gc_hooks.cpp @@ -14,11 +14,13 @@ extern "C" { #include <game/layers.hpp> + +#include "gameclient.hpp" +#include "components/skins.hpp" + #include "gc_client.hpp" -#include "gc_skin.hpp" #include "gc_render.hpp" #include "gc_map_image.hpp" -#include "gc_console.hpp" extern unsigned char internal_data[]; @@ -31,7 +33,7 @@ static float load_current; extern "C" void modc_console_init() { - client_console_init(); + //client_console_init(); } //binds_save() @@ -41,8 +43,8 @@ static void load_sounds_thread(void *do_render) // load sounds for(int s = 0; s < data->num_sounds; s++) { - if(do_render) - render_loading(load_current/load_total); + //if(do_render) // TODO: repair me + //render_loading(load_current/load_total); for(int i = 0; i < data->sounds[s].num_sounds; i++) { int id = snd_load_wv(data->sounds[s].sounds[i].filename); @@ -58,26 +60,20 @@ extern "C" void modc_init() { for(int i = 0; i < NUM_NETOBJTYPES; i++) snap_set_staticsize(i, netobj_get_size(i)); + + gameclient.on_init(); static FONT_SET default_font; int64 start = time_get(); - // setup input stack - input_stack.add_handler(console_input_special_binds, 0); // F1-Fx binds - input_stack.add_handler(console_input_cli, 0); // console - input_stack.add_handler(chat_input_handle, 0); // chat - //input_stack.add_handler() // ui - input_stack.add_handler(console_input_normal_binds, 0); // binds - - int before = gfx_memory_usage(); font_set_load(&default_font, "data/fonts/default_font%d.tfnt", "data/fonts/default_font%d.png", "data/fonts/default_font%d_b.png", 14, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 36); dbg_msg("font", "gfx memory used for font textures: %d", gfx_memory_usage()-before); gfx_text_set_default_font(&default_font); - particle_reset(); - menu_init(); + //particle_reset(); + //menu_init(); // setup sound channels snd_set_channel(CHN_GUI, 1.0f, 0.0f); @@ -100,12 +96,14 @@ extern "C" void modc_init() // load textures for(int i = 0; i < data->num_images; i++) { - render_loading(load_current/load_total); + // TODO: repair me + //render_loading(load_current/load_total); data->images[i].id = gfx_load_texture(data->images[i].filename, IMG_AUTO, 0); load_current++; } - skin_init(); + gameclient.skins->init(); + //skin_init(); if(config.cl_threadsoundloading) thread_create(load_sounds_thread, 0); @@ -118,370 +116,41 @@ extern "C" void modc_init() extern "C" void modc_save_config() { - binds_save(); -} - -extern "C" void modc_entergame() -{ -} - -extern "C" void modc_shutdown() -{ - // shutdown the menu + //binds_save(); } CHARACTER_CORE predicted_prev_char; CHARACTER_CORE predicted_char; -static int predicted_tick = 0; -static int last_new_predicted_tick = -1; - -extern "C" void modc_predict() -{ - CHARACTER_CORE before_prev_char = predicted_prev_char; - CHARACTER_CORE before_char = predicted_char; - - // repredict character - WORLD_CORE world; - world.tuning = tuning; - 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 == NETOBJTYPE_CHARACTER) - { - const NETOBJ_CHARACTER *character = (const NETOBJ_CHARACTER *)data; - client_datas[client_id].predicted.world = &world; - world.characters[client_id] = &client_datas[client_id].predicted; - - client_datas[client_id].predicted.read(character); - } - else if(item.type == NETOBJTYPE_PLAYER_INFO) - { - const NETOBJ_PLAYER_INFO *info = (const NETOBJ_PLAYER_INFO *)data; - if(info->local) - local_cid = client_id; - } - } - - // we can't predict without our own id - if(local_cid == -1) - return; - - // predict - for(int tick = client_tick()+1; tick <= client_predtick(); tick++) - { - // fetch the local - if(tick == client_predtick() && world.characters[local_cid]) - predicted_prev_char = *world.characters[local_cid]; - - // first calculate where everyone should move - for(int c = 0; c < MAX_CLIENTS; c++) - { - if(!world.characters[c]) - continue; - - mem_zero(&world.characters[c]->input, sizeof(world.characters[c]->input)); - if(local_cid == c) - { - // apply player input - int *input = client_get_input(tick); - if(input) - world.characters[c]->input = *((NETOBJ_PLAYER_INPUT*)input); - } - - world.characters[c]->tick(); - } - - // move all players and quantize their data - for(int c = 0; c < MAX_CLIENTS; c++) - { - if(!world.characters[c]) - continue; - - world.characters[c]->move(); - world.characters[c]->quantize(); - } - - if(tick > last_new_predicted_tick) - { - last_new_predicted_tick = tick; - - if(local_cid != -1 && world.characters[local_cid]) - { - vec2 pos = world.characters[local_cid]->pos; - int events = world.characters[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) - { - effect_air_jump(pos); - snd_play_random(CHN_WORLD, SOUND_PLAYER_AIRJUMP, 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.characters[local_cid]) - predicted_char = *world.characters[local_cid]; - } - - if(config.debug && predicted_tick == client_predtick()) - { - if(predicted_char.pos.x != before_char.pos.x || - predicted_char.pos.y != before_char.pos.y) - { - dbg_msg("client", "prediction error, (%d %d) (%d %d)", - (int)before_char.pos.x, (int)before_char.pos.y, - (int)predicted_char.pos.x, (int)predicted_char.pos.y); - } - - if(predicted_prev_char.pos.x != before_prev_char.pos.x || - predicted_prev_char.pos.y != before_prev_char.pos.y) - { - dbg_msg("client", "prediction error, prev (%d %d) (%d %d)", - (int)before_prev_char.pos.x, (int)before_prev_char.pos.y, - (int)predicted_prev_char.pos.x, (int)predicted_prev_char.pos.y); - } - } - - predicted_tick = client_predtick(); -} - - -extern "C" void modc_newsnapshot() -{ - static int snapshot_count = 0; - snapshot_count++; - - // secure snapshot - { - int num = snap_num_items(SNAP_CURRENT); - for(int index = 0; index < num; index++) - { - SNAP_ITEM item; - void *data = snap_get_item(SNAP_CURRENT, index, &item); - if(netobj_validate(item.type, data, item.datasize) != 0) - { - if(config.debug) - dbg_msg("game", "invalidated index=%d type=%d (%s) size=%d id=%d", index, item.type, netobj_get_name(item.type), item.datasize, item.id); - snap_invalidate_item(SNAP_CURRENT, index); - } - } - } - - - process_events(SNAP_CURRENT); - - if(config.dbg_stress) - { - if((client_tick()%250) == 0) - { - NETMSG_CL_SAY msg; - msg.team = -1; - msg.message = "galenskap!!!!"; - msg.pack(MSGFLAG_VITAL); - 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 == NETOBJTYPE_PLAYER_INFO) - { - const NETOBJ_PLAYER_INFO *info = (const NETOBJ_PLAYER_INFO *)data; - - client_datas[info->cid].team = info->team; - - if(info->local) - { - netobjects.local_info = info; - const void *data = snap_find_item(SNAP_CURRENT, NETOBJTYPE_CHARACTER, item.id); - if(data) - { - netobjects.local_character = (const NETOBJ_CHARACTER *)data; - local_character_pos = vec2(netobjects.local_character->x, netobjects.local_character->y); - - const void *p = snap_find_item(SNAP_PREV, NETOBJTYPE_CHARACTER, item.id); - if(p) - netobjects.local_prev_character = (NETOBJ_CHARACTER *)p; - } - } - } - else if(item.type == NETOBJTYPE_GAME) - netobjects.gameobj = (NETOBJ_GAME *)data; - else if(item.type == NETOBJTYPE_FLAG) - { - netobjects.flags[item.id%2] = (const NETOBJ_FLAG *)data; - } - } - } - - for(int i = 0; i < MAX_CLIENTS; i++) - client_datas[i].update_render_info(); -} - -extern "C" void modc_render() -{ - // this should be moved around abit - if(client_state() == CLIENTSTATE_ONLINE) - render_game(); - else - menu_render(); - - input_stack.dispatch_input(); - console_render(); -} +extern "C" void modc_entergame() {} +extern "C" void modc_shutdown() {} +extern "C" void modc_predict() { gameclient.on_predict(); } +extern "C" void modc_newsnapshot() { gameclient.on_snapshot(); } +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(); } extern "C" void modc_rcon_line(const char *line) { - console_rcon_print(line); -} - -extern "C" int modc_snap_input(int *data) -{ - static NETOBJ_PLAYER_INPUT last_data = {0}; - static int64 last_send_time = 0; - - // update player state - if(chat_mode != CHATMODE_NONE) - input_data.player_state = PLAYERSTATE_CHATTING; - else if(menu_active) - input_data.player_state = PLAYERSTATE_IN_MENU; - else - input_data.player_state = PLAYERSTATE_PLAYING; - last_data.player_state = input_data.player_state; - - // we freeze the input if chat or menu is activated - if(menu_active || chat_mode != CHATMODE_NONE || console_active()) - { - last_data.direction = 0; - last_data.hook = 0; - last_data.jump = 0; - - input_data = last_data; - - mem_copy(data, &input_data, sizeof(input_data)); - return sizeof(input_data); - } - - input_data.target_x = (int)mouse_pos.x; - input_data.target_y = (int)mouse_pos.y; - if(!input_data.target_x && !input_data.target_y) - input_data.target_y = 1; - - // set direction - input_data.direction = 0; - if(input_direction_left && !input_direction_right) - input_data.direction = -1; - if(!input_direction_left && input_direction_right) - input_data.direction = 1; - - // stress testing - if(config.dbg_stress) - { - float t = client_localtime(); - mem_zero(&input_data, sizeof(input_data)); - - input_data.direction = ((int)t/2)&1; - input_data.jump = ((int)t); - input_data.fire = ((int)(t*10)); - input_data.hook = ((int)(t*2))&1; - input_data.wanted_weapon = ((int)t)%NUM_WEAPONS; - input_data.target_x = (int)(sinf(t*3)*100.0f); - input_data.target_y = (int)(cosf(t*3)*100.0f); - } - - // check if we need to send input - bool send = false; - if(input_data.direction != last_data.direction) send = true; - else if(input_data.jump != last_data.jump) send = true; - else if(input_data.fire != last_data.fire) send = true; - else if(input_data.hook != last_data.hook) send = true; - else if(input_data.player_state != last_data.player_state) send = true; - else if(input_data.wanted_weapon != last_data.wanted_weapon) send = true; - else if(input_data.next_weapon != last_data.next_weapon) send = true; - else if(input_data.prev_weapon != last_data.prev_weapon) send = true; - - if(time_get() > last_send_time + time_freq()/5) - send = true; - - last_data = input_data; - if(!send) - return 0; - - // copy and return size - last_send_time = time_get(); - mem_copy(data, &input_data, sizeof(input_data)); - return sizeof(input_data); -} - -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_LOADING) - menu_do_connecting(); - else if(state == CLIENTSTATE_CONNECTING) - menu_do_connecting(); - else if (state == CLIENTSTATE_ONLINE) - { - menu_active = false; - menu_game_active = true; - //snapshot_count = 0; - - menu_do_connected(); - } + //console_rcon_print(line); } +/* NETOBJ_PROJECTILE extraproj_projectiles[MAX_EXTRA_PROJECTILES]; int extraproj_num; void extraproj_reset() { extraproj_num = 0; -} - -char server_motd[900] = {0}; -int64 server_motd_time = 0; +}*/ extern "C" void modc_message(int msgtype) { // special messages if(msgtype == NETMSGTYPE_SV_EXTRAPROJECTILE) { + /* int num = msg_unpack_int(); for(int k = 0; k < num; k++) @@ -500,7 +169,7 @@ extern "C" void modc_message(int msgtype) } } - return; + return;*/ } else if(msgtype == NETMSGTYPE_SV_TUNEPARAMS) { @@ -518,7 +187,9 @@ extern "C" void modc_message(int msgtype) tuning = new_tuning; return; } - + + gameclient.on_message(msgtype); + // normal void *rawmsg = netmsg_secure_unpack(msgtype); if(!rawmsg) @@ -526,25 +197,23 @@ extern "C" void modc_message(int msgtype) dbg_msg("client", "dropped weird message '%s' (%d), failed on '%s'", netmsg_get_name(msgtype), msgtype, netmsg_failed_on()); return; } - + + if(msgtype == NETMSGTYPE_SV_CHAT) { - NETMSG_SV_CHAT *msg = (NETMSG_SV_CHAT *)rawmsg; - chat_add_line(msg->cid, msg->team, msg->message); - if(msg->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(msgtype == NETMSGTYPE_SV_BROADCAST) { + /* NETMSG_SV_BROADCAST *msg = (NETMSG_SV_BROADCAST *)rawmsg; str_copy(broadcast_text, msg->message, sizeof(broadcast_text)); broadcast_time = time_get()+time_freq()*10; + */ } else if(msgtype == NETMSGTYPE_SV_MOTD) { + /* NETMSG_SV_MOTD *msg = (NETMSG_SV_MOTD *)rawmsg; // process escaping @@ -566,43 +235,45 @@ extern "C" void modc_message(int msgtype) server_motd_time = time_get()+time_freq()*config.cl_motd_time; else server_motd_time = 0; + */ } else if(msgtype == NETMSGTYPE_SV_SETINFO) { NETMSG_SV_SETINFO *msg = (NETMSG_SV_SETINFO *)rawmsg; - str_copy(client_datas[msg->cid].name, msg->name, 64); - str_copy(client_datas[msg->cid].skin_name, msg->skin, 64); + str_copy(gameclient.clients[msg->cid].name, msg->name, 64); + str_copy(gameclient.clients[msg->cid].skin_name, msg->skin, 64); // make sure that we don't set a special skin on the client - if(client_datas[msg->cid].skin_name[0] == 'x' || client_datas[msg->cid].skin_name[1] == '_') - str_copy(client_datas[msg->cid].skin_name, "default", 64); + if(gameclient.clients[msg->cid].skin_name[0] == 'x' || gameclient.clients[msg->cid].skin_name[1] == '_') + str_copy(gameclient.clients[msg->cid].skin_name, "default", 64); - client_datas[msg->cid].skin_info.color_body = skin_get_color(msg->color_body); - client_datas[msg->cid].skin_info.color_feet = skin_get_color(msg->color_feet); - client_datas[msg->cid].skin_info.size = 64; + gameclient.clients[msg->cid].skin_info.color_body = gameclient.skins->get_color(msg->color_body); + gameclient.clients[msg->cid].skin_info.color_feet = gameclient.skins->get_color(msg->color_feet); + gameclient.clients[msg->cid].skin_info.size = 64; // find new skin - client_datas[msg->cid].skin_id = skin_find(client_datas[msg->cid].skin_name); - if(client_datas[msg->cid].skin_id < 0) - client_datas[msg->cid].skin_id = 0; + gameclient.clients[msg->cid].skin_id = gameclient.skins->find(gameclient.clients[msg->cid].skin_name); + if(gameclient.clients[msg->cid].skin_id < 0) + gameclient.clients[msg->cid].skin_id = 0; if(msg->use_custom_color) - client_datas[msg->cid].skin_info.texture = skin_get(client_datas[msg->cid].skin_id)->color_texture; + gameclient.clients[msg->cid].skin_info.texture = gameclient.skins->get(gameclient.clients[msg->cid].skin_id)->color_texture; else { - client_datas[msg->cid].skin_info.texture = skin_get(client_datas[msg->cid].skin_id)->org_texture; - client_datas[msg->cid].skin_info.color_body = vec4(1,1,1,1); - client_datas[msg->cid].skin_info.color_feet = vec4(1,1,1,1); + gameclient.clients[msg->cid].skin_info.texture = gameclient.skins->get(gameclient.clients[msg->cid].skin_id)->org_texture; + gameclient.clients[msg->cid].skin_info.color_body = vec4(1,1,1,1); + gameclient.clients[msg->cid].skin_info.color_feet = vec4(1,1,1,1); } - client_datas[msg->cid].update_render_info(); + gameclient.clients[msg->cid].update_render_info(); } else if(msgtype == NETMSGTYPE_SV_WEAPONPICKUP) { - NETMSG_SV_WEAPONPICKUP *msg = (NETMSG_SV_WEAPONPICKUP *)rawmsg; + // TODO: repair me + /*NETMSG_SV_WEAPONPICKUP *msg = (NETMSG_SV_WEAPONPICKUP *)rawmsg; if(config.cl_autoswitch_weapons) - input_data.wanted_weapon = msg->weapon+1; + input_data.wanted_weapon = msg->weapon+1;*/ } else if(msgtype == NETMSGTYPE_SV_READYTOENTER) { @@ -610,8 +281,11 @@ extern "C" void modc_message(int msgtype) } else if(msgtype == NETMSGTYPE_SV_KILLMSG) { + /* NETMSG_SV_KILLMSG *msg = (NETMSG_SV_KILLMSG *)rawmsg; + gameclient.killmsgs.handle_message((NETMSG_SV_KILLMSG *)rawmsg); + // unpack messages KILLMSG kill; kill.killer = msg->killer; @@ -622,15 +296,15 @@ extern "C" void modc_message(int msgtype) // add the message killmsg_current = (killmsg_current+1)%killmsg_max; - killmsgs[killmsg_current] = kill; + killmsgs[killmsg_current] = kill;*/ } else if (msgtype == NETMSGTYPE_SV_EMOTICON) { NETMSG_SV_EMOTICON *msg = (NETMSG_SV_EMOTICON *)rawmsg; // apply - client_datas[msg->cid].emoticon = msg->emoticon; - client_datas[msg->cid].emoticon_start = client_tick(); + gameclient.clients[msg->cid].emoticon = msg->emoticon; + gameclient.clients[msg->cid].emoticon_start = client_tick(); } else if(msgtype == NETMSGTYPE_SV_SOUNDGLOBAL) { @@ -645,35 +319,17 @@ extern "C" void modc_connected() layers_init(); col_init(); img_init(); - flow_init(); + //flow_init(); render_tilemap_generate_skip(); + gameclient.on_connected(); //tilemap_init(); - chat_reset(); - particle_reset(); - extraproj_reset(); + //particle_reset(); + //extraproj_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].skin_id = 0; - client_datas[i].team = 0; - client_datas[i].angle = 0; - client_datas[i].emoticon = 0; - client_datas[i].emoticon_start = -1; - client_datas[i].skin_info.texture = skin_get(0)->color_texture; - client_datas[i].skin_info.color_body = vec4(1,1,1,1); - client_datas[i].skin_info.color_feet = vec4(1,1,1,1); - client_datas[i].update_render_info(); - } - - for(int i = 0; i < killmsg_max; i++) - killmsgs[i].tick = -100000; - send_info(true); + //last_new_predicted_tick = -1; } extern "C" const char *modc_net_version() { return GAME_NETVERSION; } +extern "C" const char *modc_getitemname(int type) { return netobj_get_name(type); } diff --git a/src/game/client/gc_menu.cpp b/src/game/client/gc_menu.cpp deleted file mode 100644 index fb7c5437..00000000 --- a/src/game/client/gc_menu.cpp +++ /dev/null @@ -1,2161 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include <stdio.h> -#include <math.h> -#include <string.h> -#include <stdlib.h> - -#include <base/system.h> -#include <base/math.hpp> -#include <base/vmath.hpp> - - -extern "C" { - #include <engine/e_client_interface.h> - #include <engine/e_config.h> - #include <engine/client/ec_font.h> -} - -#include "../version.hpp" -#include <game/generated/g_protocol.hpp> - -#include "../generated/gc_data.hpp" -#include "gc_render.hpp" -#include "gc_anim.hpp" -#include "gc_skin.hpp" -#include "gc_ui.hpp" -#include "gc_client.hpp" -#include <mastersrv/mastersrv.h> - -//extern data_container *data; - -extern bool menu_active; -//extern bool menu_game_active; - -static bool need_restart = false; - -enum -{ - POPUP_NONE=0, - POPUP_FIRST_LAUNCH, - POPUP_CONNECTING, - POPUP_DISCONNECTED, - POPUP_PASSWORD, - POPUP_QUIT, -}; - -static int popup = POPUP_NONE; - -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 = color_tabbar_inactive_outgame; -static vec4 color_tabbar_active = color_tabbar_active_outgame; - -enum -{ - PAGE_NEWS=0, - PAGE_GAME, - PAGE_SERVER_INFO, - PAGE_INTERNET, - PAGE_LAN, - PAGE_FAVORITES, - PAGE_SETTINGS, - PAGE_SYSTEM, -}; - -static int menu_game_page = PAGE_GAME; - -static void ui_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 == -1) - { - } - else 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 vec4 button_color_mul(const void *id) -{ - if(ui_active_item() == id) - return vec4(1,1,1,0.5f); - else if(ui_hot_item() == id) - return vec4(1,1,1,1.5f); - return vec4(1,1,1,1); -} - -static void 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); -} - -static void 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); -} - -static void ui_draw_menu_tab_button(const void *id, const char *text, int checked, const RECT *r, const void *extra) -{ - if(checked) - ui_draw_rect(r, color_tabbar_active, CORNER_T, 10.0f); - else - ui_draw_rect(r, color_tabbar_inactive, CORNER_T, 10.0f); - ui_do_label(r, text, 22.0f, 0); -} - - -static void ui_draw_settings_tab_button(const void *id, const char *text, int checked, const RECT *r, const void *extra) -{ - if(checked) - 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); -} - -static void ui_draw_grid_header(const void *id, const char *text, int checked, const RECT *r, const void *extra) -{ - if(checked) - 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); -} - -static void ui_draw_list_row(const void *id, const char *text, int checked, const RECT *r, const void *extra) -{ - if(checked) - { - RECT sr = *r; - 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); -} - -static void ui_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; - ui_vsplit_l(&t, 5.0f, 0, &t); - - 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); -} - -static void ui_draw_checkbox(const void *id, const char *text, int checked, const RECT *r, const void *extra) -{ - ui_draw_checkbox_common(id, text, checked?"X":"", r); -} - - -static void ui_draw_checkbox_number(const void *id, const char *text, int checked, const RECT *r, const void *extra) -{ - char buf[16]; - str_format(buf, sizeof(buf), "%d", checked); - ui_draw_checkbox_common(id, text, buf, r); -} - -int ui_do_edit_box(void *id, const RECT *rect, char *str, int str_size, float font_size, bool hidden=false) -{ - int inside = ui_mouse_inside(rect); - int r = 0; - static int at_index = 0; - - if(ui_last_active_item() == id) - { - 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_text_width(0, font_size, str, i) + 10 > mx_rel) - { - at_index = i - 1; - break; - } - - if (i == len) - at_index = len; - } - } - - for(int i = 0; i < inp_num_events(); i++) - { - INPUT_EVENT e = inp_get_event(i); - char c = e.ch; - int k = e.key; - - 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(e.flags&INPFLAG_PRESS) - { - 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; - } - - 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; - ui_draw_rect(&textbox, vec4(1,1,1,0.5f), CORNER_ALL, 5.0f); - ui_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; - } - - ui_do_label(&textbox, display_str, font_size, -1); - - if (ui_last_active_item() == id && !just_got_active) - { - float w = gfx_text_width(0, font_size, display_str, at_index); - textbox.x += w*ui_scale(); - ui_do_label(&textbox, "_", font_size, -1); - } - - return r; -} - -float ui_do_scrollbar_v(const void *id, const RECT *rect, float current) -{ - RECT handle; - static float offset_y; - ui_hsplit_t(rect, 33, &handle, 0); - - handle.y += (rect->h-handle.h)*current; - - /* logic */ - float ret = current; - int inside = ui_mouse_inside(&handle); - - 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; - ui_vmargin(rect, 5.0f, &rail); - ui_draw_rect(&rail, vec4(1,1,1,0.25f), 0, 0.0f); - - RECT slider = handle; - slider.w = rail.x-slider.x; - ui_draw_rect(&slider, vec4(1,1,1,0.25f), CORNER_L, 2.5f); - slider.x = rail.x+rail.w; - ui_draw_rect(&slider, vec4(1,1,1,0.25f), CORNER_R, 2.5f); - - slider = handle; - ui_margin(&slider, 5.0f, &slider); - ui_draw_rect(&slider, vec4(1,1,1,0.25f)*button_color_mul(id), CORNER_ALL, 2.5f); - - return ret; -} - - - -float ui_do_scrollbar_h(const void *id, const RECT *rect, float current) -{ - RECT handle; - static float offset_x; - ui_vsplit_l(rect, 33, &handle, 0); - - handle.x += (rect->w-handle.w)*current; - - /* logic */ - float ret = current; - int inside = ui_mouse_inside(&handle); - - 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; - ui_hmargin(rect, 5.0f, &rail); - ui_draw_rect(&rail, vec4(1,1,1,0.25f), 0, 0.0f); - - RECT slider = handle; - slider.h = rail.y-slider.y; - ui_draw_rect(&slider, vec4(1,1,1,0.25f), CORNER_T, 2.5f); - slider.y = rail.y+rail.h; - ui_draw_rect(&slider, vec4(1,1,1,0.25f), CORNER_B, 2.5f); - - slider = handle; - ui_margin(&slider, 5.0f, &slider); - ui_draw_rect(&slider, vec4(1,1,1,0.25f)*button_color_mul(id), CORNER_ALL, 2.5f); - - return ret; -} - -int ui_do_key_reader(void *id, const RECT *rect, int key) -{ - // process - static bool mouse_released = true; - int inside = ui_mouse_inside(rect); - int new_key = key; - - if(!ui_mouse_button(0)) - mouse_released = true; - - if(ui_active_item() == id) - { - for(int i = 0; i < inp_num_events(); i++) - { - INPUT_EVENT e = inp_get_event(i); - if(e.flags&INPFLAG_PRESS && e.key && e.key != KEY_ESC) - { - new_key = e.key; - ui_set_active_item(0); - mouse_released = false; - inp_clear_events(); - break; - } - } - } - 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) - ui_draw_keyselect_button(id, "???", 0, rect, 0); - else - { - if(key == 0) - ui_draw_keyselect_button(id, "", 0, rect, 0); - else - ui_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(client_state() != CLIENTSTATE_OFFLINE) - active_page = menu_game_page; - - if(client_state() == CLIENTSTATE_OFFLINE) - { - /* offline menus */ - if(0) // this is not done yet - { - ui_vsplit_l(&box, 90.0f, &button, &box); - static int news_button=0; - if (ui_do_button(&news_button, "News", active_page==PAGE_NEWS, &button, ui_draw_menu_tab_button, 0)) - new_page = PAGE_NEWS; - ui_vsplit_l(&box, 30.0f, 0, &box); - } - - ui_vsplit_l(&box, 110.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)) - { - client_serverbrowse_refresh(0); - new_page = PAGE_INTERNET; - } - - ui_vsplit_l(&box, 4.0f, 0, &box); - ui_vsplit_l(&box, 90.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)) - { - client_serverbrowse_refresh(1); - new_page = PAGE_LAN; - } - - if(0) // this one is not done yet - { - ui_vsplit_l(&box, 4.0f, 0, &box); - ui_vsplit_l(&box, 120.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)) - new_page = PAGE_FAVORITES; - } - - - } - else - { - /* online menus */ - ui_vsplit_l(&box, 90.0f, &button, &box); - static int game_button=0; - if (ui_do_button(&game_button, "Game", active_page==PAGE_GAME, &button, ui_draw_menu_tab_button, 0)) - new_page = PAGE_GAME; - - ui_vsplit_l(&box, 4.0f, 0, &box); - ui_vsplit_l(&box, 140.0f, &button, &box); - static int server_info_button=0; - if (ui_do_button(&server_info_button, "Server Info", active_page==PAGE_SERVER_INFO, &button, ui_draw_menu_tab_button, 0)) - new_page = PAGE_SERVER_INFO; - - ui_vsplit_l(&box, 30.0f, 0, &box); - } - - /* - ui_vsplit_r(&box, 110.0f, &box, &button); - static int system_button=0; - if (ui_do_button(&system_button, "System", config.ui_page==PAGE_SYSTEM, &button, ui_draw_menu_tab_button, 0)) - config.ui_page = PAGE_SYSTEM; - - ui_vsplit_r(&box, 30.0f, &box, 0); - */ - - ui_vsplit_r(&box, 110.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); - 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; - - if(new_page != -1) - { - if(client_state() == CLIENTSTATE_OFFLINE) - config.ui_page = new_page; - else - menu_game_page = new_page; - } - - return 0; -} - -static void menu2_render_background() -{ - RECT s = *ui_screen(); - - gfx_texture_set(-1); - gfx_quads_begin(); - vec4 bottom(gui_color.r*0.6f, gui_color.g*0.6f, gui_color.b*0.6f, 1.0f); - vec4 top(gui_color.r, gui_color.g, gui_color.b, 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, s.w, s.h); - gfx_quads_end(); - - if(data->images[IMAGE_BANNER].id != 0) - { - 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(); - } -} - -void render_loading(float percent) -{ - // need up date this here to get correct - 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); - - RECT screen = *ui_screen(); - gfx_mapscreen(screen.x, screen.y, screen.w, screen.h); - - menu2_render_background(); - - 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_text_width(0, 48.0f, caption, -1); - RECT r; - r.x = x; - r.y = y+20; - r.w = w; - r.h = h; - ui_do_label(&r, caption, 48.0f, 0, -1); - - gfx_texture_set(-1); - gfx_quads_begin(); - gfx_setcolor(1,1,1,0.75f); - draw_round_rect(x+40, y+h-75, (w-80)*percent, 25, 5.0f); - gfx_quads_end(); - - gfx_swap(); -} - -static void menu2_render_serverbrowser(RECT main_view) -{ - ui_draw_rect(&main_view, color_tabbar_active, CORNER_ALL, 10.0f); - - RECT view; - ui_margin(&main_view, 10.0f, &view); - - RECT headers; - RECT filters; - RECT status; - RECT toolbox; - RECT server_details; - RECT server_scoreboard; - - //ui_hsplit_t(&view, 20.0f, &status, &view); - ui_hsplit_b(&view, 110.0f, &view, &filters); - - // split off a piece for details and scoreboard - ui_vsplit_r(&view, 200.0f, &view, &server_details); - - // server list - ui_hsplit_t(&view, 16.0f, &headers, &view); - //ui_hsplit_b(&view, 110.0f, &view, &filters); - ui_hsplit_b(&view, 5.0f, &view, 0); - ui_hsplit_b(&view, 20.0f, &view, &status); - - //ui_vsplit_r(&filters, 300.0f, &filters, &toolbox); - //ui_vsplit_r(&filters, 150.0f, &filters, 0); - - ui_vsplit_mid(&filters, &filters, &toolbox); - ui_vsplit_r(&filters, 50.0f, &filters, 0); - - // split of the scrollbar - ui_draw_rect(&headers, vec4(1,1,1,0.25f), CORNER_T, 5.0f); - ui_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, - COL_VERSION, - }; - - 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_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) - { - ui_vsplit_l(&headers, cols[i].width, &cols[i].rect, &headers); - - if(i+1 < num_cols) - { - //cols[i].flags |= SPACER; - ui_vsplit_l(&headers, 2, &cols[i].spacer, &headers); - } - } - } - - for(int i = num_cols-1; i >= 0; i--) - { - if(cols[i].direction == 1) - { - ui_vsplit_r(&headers, cols[i].width, &headers, &cols[i].rect); - ui_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(ui_do_button(cols[i].caption, cols[i].caption, config.b_sort == cols[i].sort, &cols[i].rect, ui_draw_grid_header, 0)) - { - if(cols[i].sort != -1) - { - if(config.b_sort == cols[i].sort) - config.b_sort_order ^= 1; - else - config.b_sort_order = 0; - config.b_sort = cols[i].sort; - } - } - } - - ui_draw_rect(&view, vec4(0,0,0,0.15f), 0, 0); - - RECT scroll; - ui_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; - ui_hmargin(&scroll, 5.0f, &scroll); - scrollvalue = ui_do_scrollbar_v(&scrollbar, &scroll, scrollvalue); - - int scrollnum = num_servers-num+10; - if(scrollnum > 0) - { - if(inp_key_presses(KEY_MOUSE_WHEEL_UP)) - scrollvalue -= 1.0f/scrollnum; - if(inp_key_presses(KEY_MOUSE_WHEEL_DOWN)) - scrollvalue += 1.0f/scrollnum; - - if(scrollvalue < 0) scrollvalue = 0; - if(scrollvalue > 1) scrollvalue = 1; - } - else - scrollnum = 0; - - // set clipping - ui_clip_enable(&view); - - int start = (int)(scrollnum*scrollvalue); - if(start < 0) - start = 0; - - RECT original_view = view; - view.y -= scrollvalue*scrollnum*cols[0].rect.h; - - int new_selected = -1; - int selected_index = -1; - int num_players = 0; - - for (int i = 0; i < num_servers; i++) - { - SERVER_INFO *item = client_serverbrowse_sorted_get(i); - num_players += item->num_players; - } - - for (int i = 0; i < num_servers; i++) - { - int item_index = i; - SERVER_INFO *item = client_serverbrowse_sorted_get(item_index); - RECT row; - RECT select_hit_box; - - int selected = strcmp(item->address, config.ui_server_address) == 0; //selected_index==item_index; - - - ui_hsplit_t(&view, 17.0f, &row, &view); - select_hit_box = row; - - if(selected) - { - 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); - } - - - // make sure that only those in view can be selected - if(row.y+row.h > original_view.y) - { - if(select_hit_box.y < original_view.y) // clip the selection - { - select_hit_box.h -= original_view.y-select_hit_box.y; - select_hit_box.y = original_view.y; - } - - if(ui_do_button(item, "", selected, &select_hit_box, 0, 0)) - { - new_selected = item_index; - } - } - - // check if we need to do more - if(row.y > original_view.y+original_view.h) - break; - - 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 = ui_do_button(item, "L", l, &button, ui_draw_browse_icon, 0); - - if(id == COL_FLAGS) - { - if(item->flags&1) - ui_draw_browse_icon(0x100, &button); - } - else if(id == COL_NAME) - { - TEXT_CURSOR cursor; - gfx_text_set_cursor(&cursor, button.x, button.y, 12.0f, TEXTFLAG_RENDER); - - if(config.b_filter_string[0] && (item->quicksearch_hit&BROWSEQUICK_SERVERNAME)) - { - // highlight the parts that matches - const char *s = str_find_nocase(item->name, config.b_filter_string); - if(s) - { - gfx_text_ex(&cursor, item->name, (int)(s-item->name)); - gfx_text_color(0.4f,0.4f,1.0f,1); - gfx_text_ex(&cursor, s, strlen(config.b_filter_string)); - gfx_text_color(1,1,1,1); - gfx_text_ex(&cursor, s+strlen(config.b_filter_string), -1); - } - else - gfx_text_ex(&cursor, item->name, -1); - } - else - gfx_text_ex(&cursor, item->name, -1); - } - else if(id == COL_MAP) - ui_do_label(&button, item->map, 12.0f, -1); - else if(id == COL_PLAYERS) - { - str_format(temp, sizeof(temp), "%i/%i", item->num_players, item->max_players); - if(config.b_filter_string[0] && (item->quicksearch_hit&BROWSEQUICK_PLAYERNAME)) - gfx_text_color(0.4f,0.4f,1.0f,1); - ui_do_label(&button, temp, 12.0f, 1); - gfx_text_color(1,1,1,1); - } - else if(id == COL_PING) - { - str_format(temp, sizeof(temp), "%i", item->latency); - ui_do_label(&button, temp, 12.0f, 1); - } - else if(id == COL_PROGRESS) - { - if(item->progression > 100) - item->progression = 100; - ui_draw_browse_icon(item->progression, &button); - } - else if(id == COL_VERSION) - { - const char *version = item->version; - if(strcmp(version, "0.3 e2d7973c6647a13c") == 0) // TODO: remove me later on - version = "0.3.0"; - ui_do_label(&button, version, 12.0f, 1); - } - 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"; - ui_do_label(&button, type, 12.0f, 0); - } - } - } - - ui_clip_disable(); - - if(new_selected != -1) - { - // select the new server - SERVER_INFO *item = client_serverbrowse_sorted_get(new_selected); - strncpy(config.ui_server_address, item->address, sizeof(config.ui_server_address)); - if(inp_mouse_doubleclick()) - client_connect(config.ui_server_address); - } - - SERVER_INFO *selected_server = client_serverbrowse_sorted_get(selected_index); - RECT server_header; - - ui_vsplit_l(&server_details, 10.0f, 0x0, &server_details); - - // split off a piece to use for scoreboard - ui_hsplit_t(&server_details, 140.0f, &server_details, &server_scoreboard); - ui_hsplit_b(&server_details, 10.0f, &server_details, 0x0); - - // server details - const float font_size = 12.0f; - ui_hsplit_t(&server_details, 20.0f, &server_header, &server_details); - ui_draw_rect(&server_header, vec4(1,1,1,0.25f), CORNER_T, 4.0f); - ui_draw_rect(&server_details, vec4(0,0,0,0.15f), CORNER_B, 4.0f); - ui_vsplit_l(&server_header, 8.0f, 0x0, &server_header); - ui_do_label(&server_header, "Server Details: ", font_size+2.0f, -1); - - ui_vsplit_l(&server_details, 5.0f, 0x0, &server_details); - - ui_margin(&server_details, 3.0f, &server_details); - - if (selected_server) - { - RECT row; - static const char *labels[] = { "Version:", "Game Type:", "Progression:", "Ping:" }; - - RECT left_column; - RECT right_column; - - ui_vsplit_l(&server_details, 5.0f, 0x0, &server_details); - ui_vsplit_l(&server_details, 80.0f, &left_column, &right_column); - - for (int i = 0; i < 4; i++) - { - ui_hsplit_t(&left_column, 15.0f, &row, &left_column); - ui_do_label(&row, labels[i], font_size, -1); - } - - ui_hsplit_t(&right_column, 15.0f, &row, &right_column); - ui_do_label(&row, selected_server->version, font_size, -1); - - ui_hsplit_t(&right_column, 15.0f, &row, &right_column); - static const char *game_types[] = { "DM", "TDM", "CTF" }; - if (selected_server->game_type >= 0 && selected_server->game_type < (int)(sizeof(game_types)/sizeof(*game_types))) - ui_do_label(&row, game_types[selected_server->game_type], font_size, -1); - - char temp[16]; - - if(selected_server->progression < 0) - str_format(temp, sizeof(temp), "N/A"); - else - str_format(temp, sizeof(temp), "%d%%", selected_server->progression); - ui_hsplit_t(&right_column, 15.0f, &row, &right_column); - ui_do_label(&row, temp, font_size, -1); - - str_format(temp, sizeof(temp), "%d", selected_server->latency); - ui_hsplit_t(&right_column, 15.0f, &row, &right_column); - ui_do_label(&row, temp, font_size, -1); - } - - // server scoreboard - ui_hsplit_b(&server_scoreboard, 10.0f, &server_scoreboard, 0x0); - ui_hsplit_t(&server_scoreboard, 20.0f, &server_header, &server_scoreboard); - ui_draw_rect(&server_header, vec4(1,1,1,0.25f), CORNER_T, 4.0f); - ui_draw_rect(&server_scoreboard, vec4(0,0,0,0.15f), CORNER_B, 4.0f); - ui_vsplit_l(&server_header, 8.0f, 0x0, &server_header); - ui_do_label(&server_header, "Scoreboard: ", font_size+2.0f, -1); - - ui_vsplit_l(&server_scoreboard, 5.0f, 0x0, &server_scoreboard); - - ui_margin(&server_scoreboard, 3.0f, &server_scoreboard); - - if (selected_server) - { - for (int i = 0; i < selected_server->num_players; i++) - { - RECT row; - char temp[16]; - ui_hsplit_t(&server_scoreboard, 16.0f, &row, &server_scoreboard); - - str_format(temp, sizeof(temp), "%d", selected_server->players[i].score); - ui_do_label(&row, temp, font_size, -1); - - ui_vsplit_l(&row, 25.0f, 0x0, &row); - - TEXT_CURSOR cursor; - gfx_text_set_cursor(&cursor, row.x, row.y, 12.0f, TEXTFLAG_RENDER); - - const char *name = selected_server->players[i].name; - if(config.b_filter_string[0]) - { - // highlight the parts that matches - const char *s = str_find_nocase(name, config.b_filter_string); - if(s) - { - gfx_text_ex(&cursor, name, (int)(s-name)); - gfx_text_color(0.4f,0.4f,1,1); - gfx_text_ex(&cursor, s, strlen(config.b_filter_string)); - gfx_text_color(1,1,1,1); - gfx_text_ex(&cursor, s+strlen(config.b_filter_string), -1); - } - else - gfx_text_ex(&cursor, name, -1); - } - else - gfx_text_ex(&cursor, name, -1); - - /*ui_do_label(&row, selected_server->player_names[i], font_size, -1);*/ - } - } - - RECT button; - RECT types; - ui_hsplit_t(&filters, 20.0f, &button, &filters); - ui_do_label(&button, "Quick search: ", 14.0f, -1); - ui_vsplit_l(&button, 95.0f, 0, &button); - ui_do_edit_box(&config.b_filter_string, &button, config.b_filter_string, sizeof(config.b_filter_string), 14.0f); - - ui_vsplit_l(&filters, 180.0f, &filters, &types); - - // render filters - ui_hsplit_t(&filters, 20.0f, &button, &filters); - if (ui_do_button(&config.b_filter_empty, "Has people playing", config.b_filter_empty, &button, ui_draw_checkbox, 0)) - config.b_filter_empty ^= 1; - - ui_hsplit_t(&filters, 20.0f, &button, &filters); - if (ui_do_button(&config.b_filter_full, "Server not full", config.b_filter_full, &button, ui_draw_checkbox, 0)) - config.b_filter_full ^= 1; - - ui_hsplit_t(&filters, 20.0f, &button, &filters); - if (ui_do_button(&config.b_filter_pw, "No password", config.b_filter_pw, &button, ui_draw_checkbox, 0)) - config.b_filter_pw ^= 1; - - ui_hsplit_t(&filters, 20.0f, &button, &filters); - if (ui_do_button((char *)&config.b_filter_compatversion, "Compatible Version", config.b_filter_compatversion, &button, ui_draw_checkbox, 0)) - config.b_filter_compatversion ^= 1; - - // game types - ui_hsplit_t(&types, 20.0f, &button, &types); - if (ui_do_button(&config.b_filter_gametype, "DM", config.b_filter_gametype&(1<<GAMETYPE_DM), &button, ui_draw_checkbox, 0)) - config.b_filter_gametype ^= (1<<GAMETYPE_DM); - - ui_hsplit_t(&types, 20.0f, &button, &types); - if (ui_do_button((char *)&config.b_filter_gametype + 1, "TDM", config.b_filter_gametype&(1<<GAMETYPE_TDM), &button, ui_draw_checkbox, 0)) - config.b_filter_gametype ^= (1<<GAMETYPE_TDM); - - ui_hsplit_t(&types, 20.0f, &button, &types); - if (ui_do_button((char *)&config.b_filter_gametype + 2, "CTF", config.b_filter_gametype&(1<<GAMETYPE_CTF), &button, ui_draw_checkbox, 0)) - config.b_filter_gametype ^= (1<<GAMETYPE_CTF); - - // ping - ui_hsplit_t(&types, 2.0f, &button, &types); - ui_hsplit_t(&types, 20.0f, &button, &types); - { - RECT editbox; - ui_vsplit_l(&button, 40.0f, &editbox, &button); - ui_vsplit_l(&button, 5.0f, &button, &button); - - char buf[8]; - str_format(buf, sizeof(buf), "%d", config.b_filter_ping); - ui_do_edit_box(&config.b_filter_ping, &editbox, buf, sizeof(buf), 14.0f); - config.b_filter_ping = atoi(buf); - - ui_do_label(&button, "Maximum ping", 14.0f, -1); - } - - - // render status - ui_draw_rect(&status, vec4(1,1,1,0.25f), CORNER_B, 5.0f); - ui_vmargin(&status, 50.0f, &status); - char buf[128]; - str_format(buf, sizeof(buf), "%d of %d servers, %d players", client_serverbrowse_sorted_num(), client_serverbrowse_num(), num_players); - ui_do_label(&status, buf, 14.0f, -1); - - // render toolbox - { - RECT buttons, button; - ui_hsplit_b(&toolbox, 25.0f, &toolbox, &buttons); - - ui_vsplit_r(&buttons, 100.0f, &buttons, &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)) - client_connect(config.ui_server_address); - - ui_vsplit_r(&buttons, 20.0f, &buttons, &button); - ui_vsplit_r(&buttons, 100.0f, &buttons, &button); - ui_vmargin(&button, 2.0f, &button); - static int refresh_button = 0; - if(ui_do_button(&refresh_button, "Refresh", 0, &button, ui_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); - } - - //ui_vsplit_r(&buttons, 30.0f, &buttons, &button); - ui_vsplit_l(&buttons, 120.0f, &button, &buttons); - static int clear_button = 0; - if(ui_do_button(&clear_button, "Reset Filter", 0, &button, ui_draw_menu_button, 0)) - { - config.b_filter_full = 0; - config.b_filter_empty = 0; - config.b_filter_pw = 0; - config.b_filter_ping = 999; - config.b_filter_gametype = 0xf; - config.b_filter_compatversion = 1; - config.b_filter_string[0] = 0; - } - - - ui_hsplit_t(&toolbox, 20.0f, &button, &toolbox); - ui_do_label(&button, "Host address:", 14.0f, -1); - ui_vsplit_l(&button, 100.0f, 0, &button); - ui_do_edit_box(&config.ui_server_address, &button, config.ui_server_address, sizeof(config.ui_server_address), 14.0f); - } -} - -static void menu2_render_settings_player(RECT main_view) -{ - RECT button; - RECT skinselection; - ui_vsplit_l(&main_view, 300.0f, &main_view, &skinselection); - - - ui_hsplit_t(&main_view, 20.0f, &button, &main_view); - - // render settings - { - ui_hsplit_t(&main_view, 20.0f, &button, &main_view); - ui_do_label(&button, "Name:", 14.0, -1); - ui_vsplit_l(&button, 80.0f, 0, &button); - ui_vsplit_l(&button, 180.0f, &button, 0); - ui_do_edit_box(config.player_name, &button, config.player_name, sizeof(config.player_name), 14.0f); - - static int dynamic_camera_button = 0; - ui_hsplit_t(&main_view, 20.0f, &button, &main_view); - if(ui_do_button(&dynamic_camera_button, "Dynamic Camera", config.cl_mouse_deadzone != 0, &button, ui_draw_checkbox, 0)) - { - - if(config.cl_mouse_deadzone) - { - config.cl_mouse_followfactor = 0; - config.cl_mouse_max_distance = 400; - config.cl_mouse_deadzone = 0; - } - else - { - config.cl_mouse_followfactor = 60; - config.cl_mouse_max_distance = 1000; - config.cl_mouse_deadzone = 300; - } - } - - ui_hsplit_t(&main_view, 20.0f, &button, &main_view); - if (ui_do_button(&config.cl_autoswitch_weapons, "Switch weapon on pickup", config.cl_autoswitch_weapons, &button, ui_draw_checkbox, 0)) - config.cl_autoswitch_weapons ^= 1; - - ui_hsplit_t(&main_view, 20.0f, &button, &main_view); - if (ui_do_button(&config.cl_nameplates, "Show name plates", config.cl_nameplates, &button, ui_draw_checkbox, 0)) - config.cl_nameplates ^= 1; - - //if(config.cl_nameplates) - { - ui_hsplit_t(&main_view, 20.0f, &button, &main_view); - ui_vsplit_l(&button, 15.0f, 0, &button); - if (ui_do_button(&config.cl_nameplates_always, "Always show name plates", config.cl_nameplates_always, &button, ui_draw_checkbox, 0)) - config.cl_nameplates_always ^= 1; - } - - ui_hsplit_t(&main_view, 20.0f, &button, &main_view); - - ui_hsplit_t(&main_view, 20.0f, &button, &main_view); - if (ui_do_button(&config.player_color_body, "Custom colors", config.player_use_custom_color, &button, ui_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; - ui_hsplit_t(&main_view, 20.0f, &text, &main_view); - ui_vsplit_l(&text, 15.0f, 0, &text); - ui_do_label(&text, parts[i], 14.0f, -1); - - int prevcolor = *colors[i]; - int color = 0; - for(int s = 0; s < 3; s++) - { - RECT text; - ui_hsplit_t(&main_view, 19.0f, &button, &main_view); - ui_vsplit_l(&button, 30.0f, 0, &button); - ui_vsplit_l(&button, 30.0f, &text, &button); - ui_vsplit_r(&button, 5.0f, &button, 0); - ui_hsplit_t(&button, 4.0f, 0, &button); - - float k = ((prevcolor>>((2-s)*8))&0xff) / 255.0f; - k = ui_do_scrollbar_h(&color_slider[i][s], &button, k); - color <<= 8; - color += clamp((int)(k*255), 0, 255); - ui_do_label(&text, labels[s], 15.0f, -1); - - } - - *colors[i] = color; - ui_hsplit_t(&main_view, 5.0f, 0, &main_view); - } - } - } - - // draw header - RECT header, footer; - ui_hsplit_t(&skinselection, 20, &header, &skinselection); - ui_draw_rect(&header, vec4(1,1,1,0.25f), CORNER_T, 5.0f); - ui_do_label(&header, "Skins", 18.0f, 0); - - // draw footers - ui_hsplit_b(&skinselection, 20, &skinselection, &footer); - ui_draw_rect(&footer, vec4(1,1,1,0.25f), CORNER_B, 5.0f); - ui_vsplit_l(&footer, 10.0f, 0, &footer); - - // modes - ui_draw_rect(&skinselection, vec4(0,0,0,0.15f), 0, 0); - - RECT scroll; - ui_vsplit_r(&skinselection, 15, &skinselection, &scroll); - - RECT list = skinselection; - ui_hsplit_t(&list, 50, &button, &list); - - int num = (int)(skinselection.h/button.h); - static float scrollvalue = 0; - static int scrollbar = 0; - ui_hmargin(&scroll, 5.0f, &scroll); - scrollvalue = ui_do_scrollbar_v(&scrollbar, &scroll, scrollvalue); - - int start = (int)((skin_num()-num)*scrollvalue); - if(start < 0) - start = 0; - - ANIM_STATE 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); - - // no special skins - if(s->name[0] == 'x' && s->name[1] == '_') - { - num++; - continue; - } - - char buf[128]; - str_format(buf, sizeof(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 = ui_scale()*50.0f; - - RECT icon; - RECT text; - ui_vsplit_l(&button, 50.0f, &icon, &text); - - if(ui_do_button(s, "", selected, &button, ui_draw_list_row, 0)) - config_set_player_skin(&config, s->name); - - ui_hsplit_t(&text, 12.0f, 0, &text); // some margin from the top - ui_do_label(&text, buf, 18.0f, 0); - - ui_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)); - - if(config.debug) - { - gfx_texture_set(-1); - gfx_quads_begin(); - gfx_setcolor(s->blood_color.r, s->blood_color.g, s->blood_color.b, 1.0f); - gfx_quads_drawTL(icon.x, icon.y, 12, 12); - gfx_quads_end(); - } - - ui_hsplit_t(&list, 50, &button, &list); - } -} - -typedef void (*assign_func_callback)(CONFIGURATION *config, int value); - -static void menu2_render_settings_controls(RECT main_view) -{ - RECT right_part; - ui_vsplit_l(&main_view, 300.0f, &main_view, &right_part); - - { - RECT button, label; - ui_hsplit_t(&main_view, 20.0f, &button, &main_view); - ui_vsplit_l(&button, 110.0f, &label, &button); - ui_do_label(&label, "Mouse sens.", 14.0f, -1); - ui_hmargin(&button, 2.0f, &button); - config.inp_mousesens = (int)(ui_do_scrollbar_h(&config.inp_mousesens, &button, (config.inp_mousesens-5)/500.0f)*500.0f)+5; - //*key.key = ui_do_key_reader(key.key, &button, *key.key); - ui_hsplit_t(&main_view, 20.0f, 0, &main_view); - } - - typedef struct - { - const char *name; - const char *command; - int keyid; - } KEYINFO; - - KEYINFO keys[] = - { - { "Move Left:", "+left", 0}, - { "Move Right:", "+right", 0 }, - { "Jump:", "+jump", 0 }, - { "Fire:", "+fire", 0 }, - { "Hook:", "+hook", 0 }, - { "Hammer:", "+weapon1", 0 }, - { "Pistol:", "+weapon2", 0 }, - { "Shotgun:", "+weapon3", 0 }, - { "Grenade:", "+weapon4", 0 }, - { "Rifle:", "+weapon5", 0 }, - { "Next Weapon:", "+nextweapon", 0 }, - { "Prev. Weapon:", "+prevweapon", 0 }, - { "Emoticon:", "+emote", 0 }, - { "Chat:", "chat all", 0 }, - { "Team Chat:", "chat team", 0 }, - { "Console:", "toggle_local_console", 0 }, - { "Remote Console:", "toggle_remote_console", 0 }, - { "Screenshot:", "screenshot", 0 }, - }; - - const int key_count = sizeof(keys) / sizeof(KEYINFO); - - // this is kinda slow, but whatever - for(int keyid = 0; keyid < KEY_LAST; keyid++) - { - const char *bind = binds_get(keyid); - if(!bind[0]) - continue; - - for(int i = 0; i < key_count; i++) - if(strcmp(bind, keys[i].command) == 0) - { - keys[i].keyid = keyid; - break; - } - } - - for (int i = 0; i < key_count; i++) - { - KEYINFO key = keys[i]; - RECT button, label; - ui_hsplit_t(&main_view, 20.0f, &button, &main_view); - ui_vsplit_l(&button, 110.0f, &label, &button); - - ui_do_label(&label, key.name, 14.0f, -1); - int oldid = key.keyid; - int newid = ui_do_key_reader((void *)keys[i].name, &button, oldid); - if(newid != oldid) - { - binds_set(oldid, ""); - binds_set(newid, keys[i].command); - } - ui_hsplit_t(&main_view, 5.0f, 0, &main_view); - } - - // defaults - RECT button; - ui_hsplit_b(&right_part, 25.0f, &right_part, &button); - ui_vsplit_l(&button, 50.0f, 0, &button); - if (ui_do_button((void*)binds_default, "Reset to defaults", 0, &button, ui_draw_menu_button, 0)) - binds_default(); -} - -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; - ui_vsplit_l(&main_view, 300.0f, &main_view, &modelist); - - // draw allmodes switch - RECT header, footer; - ui_hsplit_t(&modelist, 20, &button, &modelist); - if (ui_do_button(&config.gfx_display_all_modes, "Show only supported", config.gfx_display_all_modes^1, &button, ui_draw_checkbox, 0)) - { - config.gfx_display_all_modes ^= 1; - num_modes = gfx_get_video_modes(modes, MAX_RESOLUTIONS); - } - - // draw header - ui_hsplit_t(&modelist, 20, &header, &modelist); - ui_draw_rect(&header, vec4(1,1,1,0.25f), CORNER_T, 5.0f); - ui_do_label(&header, "Display Modes", 14.0f, 0); - - // draw footers - ui_hsplit_b(&modelist, 20, &modelist, &footer); - str_format(buf, sizeof(buf), "Current: %dx%d %d bit", config.gfx_screen_width, config.gfx_screen_height, config.gfx_color_depth); - ui_draw_rect(&footer, vec4(1,1,1,0.25f), CORNER_B, 5.0f); - ui_vsplit_l(&footer, 10.0f, 0, &footer); - ui_do_label(&footer, buf, 14.0f, -1); - - // modes - ui_draw_rect(&modelist, vec4(0,0,0,0.15f), 0, 0); - - RECT scroll; - ui_vsplit_r(&modelist, 15, &modelist, &scroll); - - RECT list = modelist; - ui_hsplit_t(&list, 20, &button, &list); - - int num = (int)(modelist.h/button.h); - static float scrollvalue = 0; - static int scrollbar = 0; - ui_hmargin(&scroll, 5.0f, &scroll); - scrollvalue = ui_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; - } - - str_format(buf, sizeof(buf), " %dx%d %d bit", modes[i].width, modes[i].height, depth); - if(ui_do_button(&modes[i], buf, selected, &button, ui_draw_list_row, 0)) - { - config.gfx_color_depth = depth; - config.gfx_screen_width = modes[i].width; - config.gfx_screen_height = modes[i].height; - if(!selected) - need_restart = true; - } - - ui_hsplit_t(&list, 20, &button, &list); - } - - - // switches - ui_hsplit_t(&main_view, 20.0f, &button, &main_view); - if (ui_do_button(&config.gfx_fullscreen, "Fullscreen", config.gfx_fullscreen, &button, ui_draw_checkbox, 0)) - { - config.gfx_fullscreen ^= 1; - need_restart = true; - } - - ui_hsplit_t(&main_view, 20.0f, &button, &main_view); - if (ui_do_button(&config.gfx_vsync, "V-Sync", config.gfx_vsync, &button, ui_draw_checkbox, 0)) - config.gfx_vsync ^= 1; - - ui_hsplit_t(&main_view, 20.0f, &button, &main_view); - if (ui_do_button(&config.gfx_fsaa_samples, "FSAA samples", config.gfx_fsaa_samples, &button, ui_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; - need_restart = true; - } - - ui_hsplit_t(&main_view, 40.0f, &button, &main_view); - ui_hsplit_t(&main_view, 20.0f, &button, &main_view); - if (ui_do_button(&config.gfx_texture_quality, "Quality Textures", config.gfx_texture_quality, &button, ui_draw_checkbox, 0)) - { - config.gfx_texture_quality ^= 1; - need_restart = true; - } - - ui_hsplit_t(&main_view, 20.0f, &button, &main_view); - if (ui_do_button(&config.gfx_texture_compression, "Texture Compression", config.gfx_texture_compression, &button, ui_draw_checkbox, 0)) - { - config.gfx_texture_compression ^= 1; - need_restart = true; - } - - ui_hsplit_t(&main_view, 20.0f, &button, &main_view); - if (ui_do_button(&config.gfx_high_detail, "High Detail", config.gfx_high_detail, &button, ui_draw_checkbox, 0)) - config.gfx_high_detail ^= 1; - - // - - RECT text; - ui_hsplit_t(&main_view, 20.0f, 0, &main_view); - ui_hsplit_t(&main_view, 20.0f, &text, &main_view); - //ui_vsplit_l(&text, 15.0f, 0, &text); - ui_do_label(&text, "UI Color", 14.0f, -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; - ui_hsplit_t(&main_view, 19.0f, &button, &main_view); - ui_vmargin(&button, 15.0f, &button); - ui_vsplit_l(&button, 30.0f, &text, &button); - ui_vsplit_r(&button, 5.0f, &button, 0); - ui_hsplit_t(&button, 4.0f, 0, &button); - - float k = (*color_slider[s]) / 255.0f; - k = ui_do_scrollbar_h(color_slider[s], &button, k); - *color_slider[s] = (int)(k*255.0f); - ui_do_label(&text, labels[s], 15.0f, -1); - } -} - -static void menu2_render_settings_sound(RECT main_view) -{ - RECT button; - ui_vsplit_l(&main_view, 300.0f, &main_view, 0); - - ui_hsplit_t(&main_view, 20.0f, &button, &main_view); - if (ui_do_button(&config.snd_enable, "Use Sounds", config.snd_enable, &button, ui_draw_checkbox, 0)) - { - config.snd_enable ^= 1; - need_restart = true; - } - - if(!config.snd_enable) - return; - - ui_hsplit_t(&main_view, 20.0f, &button, &main_view); - if (ui_do_button(&config.snd_nonactive_mute, "Mute when not active", config.snd_nonactive_mute, &button, ui_draw_checkbox, 0)) - config.snd_nonactive_mute ^= 1; - - // sample rate box - { - char buf[64]; - str_format(buf, sizeof(buf), "%d", config.snd_rate); - ui_hsplit_t(&main_view, 20.0f, &button, &main_view); - ui_do_label(&button, "Sample Rate", 14.0f, -1); - ui_vsplit_l(&button, 110.0f, 0, &button); - ui_vsplit_l(&button, 180.0f, &button, 0); - ui_do_edit_box(&config.snd_rate, &button, buf, sizeof(buf), 14.0f); - int before = config.snd_rate; - config.snd_rate = atoi(buf); - - if(config.snd_rate != before) - need_restart = true; - - if(config.snd_rate < 1) - config.snd_rate = 1; - } - - // volume slider - { - RECT button, label; - ui_hsplit_t(&main_view, 5.0f, &button, &main_view); - ui_hsplit_t(&main_view, 20.0f, &button, &main_view); - ui_vsplit_l(&button, 110.0f, &label, &button); - ui_hmargin(&button, 2.0f, &button); - ui_do_label(&label, "Sound Volume", 14.0f, -1); - config.snd_volume = (int)(ui_do_scrollbar_h(&config.snd_volume, &button, config.snd_volume/100.0f)*100.0f); - ui_hsplit_t(&main_view, 20.0f, 0, &main_view); - } -} - - - /* -static void menu2_render_settings_network(RECT main_view) -{ - RECT button; - ui_vsplit_l(&main_view, 300.0f, &main_view, 0); - - { - ui_hsplit_t(&main_view, 20.0f, &button, &main_view); - ui_do_label(&button, "Rcon Password", 14.0, -1); - ui_vsplit_l(&button, 110.0f, 0, &button); - ui_vsplit_l(&button, 180.0f, &button, 0); - ui_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; - ui_vsplit_r(&main_view, 120.0f, &main_view, &tabbar); - ui_draw_rect(&main_view, color_tabbar_active, CORNER_B|CORNER_TL, 10.0f); - ui_hsplit_t(&tabbar, 50.0f, &temp, &tabbar); - ui_draw_rect(&temp, color_tabbar_active, CORNER_R, 10.0f); - - ui_hsplit_t(&main_view, 10.0f, 0, &main_view); - - RECT button; - - const char *tabs[] = {"Player", "Controls", "Graphics", "Sound"}; - int num_tabs = (int)(sizeof(tabs)/sizeof(*tabs)); - - for(int i = 0; i < num_tabs; i++) - { - ui_hsplit_t(&tabbar, 10, &button, &tabbar); - ui_hsplit_t(&tabbar, 26, &button, &tabbar); - if(ui_do_button(tabs[i], tabs[i], settings_page == i, &button, ui_draw_settings_tab_button, 0)) - settings_page = i; - } - - ui_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_graphics(main_view); - else if(settings_page == 3) - menu2_render_settings_sound(main_view); - - if(need_restart) - { - RECT restart_warning; - ui_hsplit_b(&main_view, 40, &main_view, &restart_warning); - ui_do_label(&restart_warning, "You must restart the game for all settings to take effect.", 15.0f, -1, 220); - } -} - -static void menu2_render_news(RECT main_view) -{ - ui_draw_rect(&main_view, color_tabbar_active, CORNER_ALL, 10.0f); -} - -static void menu2_render_game(RECT main_view) -{ - RECT button; - ui_hsplit_t(&main_view, 45.0f, &main_view, 0); - ui_draw_rect(&main_view, color_tabbar_active, CORNER_ALL, 10.0f); - - ui_hsplit_t(&main_view, 10.0f, 0, &main_view); - ui_hsplit_t(&main_view, 25.0f, &main_view, 0); - ui_vmargin(&main_view, 10.0f, &main_view); - - ui_vsplit_r(&main_view, 120.0f, &main_view, &button); - static int disconnect_button = 0; - if(ui_do_button(&disconnect_button, "Disconnect", 0, &button, ui_draw_menu_button, 0)) - client_disconnect(); - - if(netobjects.local_info && netobjects.gameobj) - { - if(netobjects.local_info->team != -1) - { - ui_vsplit_l(&main_view, 10.0f, &button, &main_view); - ui_vsplit_l(&main_view, 120.0f, &button, &main_view); - static int spectate_button = 0; - if(ui_do_button(&spectate_button, "Spectate", 0, &button, ui_draw_menu_button, 0)) - { - send_switch_team(-1); - menu_active = false; - } - } - - if(netobjects.gameobj->gametype == GAMETYPE_DM) - { - if(netobjects.local_info->team != 0) - { - ui_vsplit_l(&main_view, 10.0f, &button, &main_view); - ui_vsplit_l(&main_view, 120.0f, &button, &main_view); - static int spectate_button = 0; - if(ui_do_button(&spectate_button, "Join Game", 0, &button, ui_draw_menu_button, 0)) - { - send_switch_team(0); - menu_active = false; - } - } - } - else - { - if(netobjects.local_info->team != 0) - { - ui_vsplit_l(&main_view, 10.0f, &button, &main_view); - ui_vsplit_l(&main_view, 120.0f, &button, &main_view); - static int spectate_button = 0; - if(ui_do_button(&spectate_button, "Join Red", 0, &button, ui_draw_menu_button, 0)) - { - send_switch_team(0); - menu_active = false; - } - } - - if(netobjects.local_info->team != 1) - { - ui_vsplit_l(&main_view, 10.0f, &button, &main_view); - ui_vsplit_l(&main_view, 120.0f, &button, &main_view); - static int spectate_button = 0; - if(ui_do_button(&spectate_button, "Join Blue", 0, &button, ui_draw_menu_button, 0)) - { - send_switch_team(1); - menu_active = false; - } - } - } - } -} - -void menu2_render_serverinfo(RECT main_view) -{ - // render background - ui_draw_rect(&main_view, color_tabbar_active, CORNER_ALL, 10.0f); - - // render motd - RECT view; - ui_margin(&main_view, 10.0f, &view); - //void gfx_text(void *font, float x, float y, float size, const char *text, int max_width); - gfx_text(0, view.x, view.y, 16, server_motd, -1); -} - -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; -} - -void menu_init() -{ - if(config.cl_show_welcome) - popup = POPUP_FIRST_LAUNCH; - config.cl_show_welcome = 0; -} - -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); - - ANIM_STATE 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; //ui_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 = *ui_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 - ui_margin(&screen, 10.0f, &screen); - - if(popup == POPUP_NONE) - { - // do tab bar - ui_hsplit_t(&screen, 26.0f, &tab_bar, &main_view); - ui_vmargin(&tab_bar, 20.0f, &tab_bar); - menu2_render_menubar(tab_bar); - - // render current page - if(client_state() != CLIENTSTATE_OFFLINE) - { - if(menu_game_page == PAGE_GAME) - menu2_render_game(main_view); - else if(menu_game_page == PAGE_SERVER_INFO) - menu2_render_serverinfo(main_view); - else if(menu_game_page == PAGE_SETTINGS) - menu2_render_settings(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); - char buf[128]; - const char *title = ""; - const char *extra_text = ""; - const char *button_text = ""; - int extra_align = 0; - - if(popup == POPUP_CONNECTING) - { - title = "Connecting to"; - extra_text = config.ui_server_address; // TODO: query the client about the address - button_text = "Abort"; - if(client_mapdownload_totalsize() > 0) - { - title = "Downloading map"; - str_format(buf, sizeof(buf), "%d/%d KiB", client_mapdownload_amount()/1024, client_mapdownload_totalsize()/1024); - extra_text = buf; - } - } - else if(popup == POPUP_DISCONNECTED) - { - title = "Disconnected"; - extra_text = client_error_string(); - button_text = "Ok"; - extra_align = -1; - } - 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?"; - } - else if(popup == POPUP_FIRST_LAUNCH) - { - title = "Welcome to Teeworlds"; - extra_text = - "As this is the first time you launch the game, please enter your nick name below. " - "It's recommended that you check the settings to adjust them to your liking " - "before joining a server."; - button_text = "Ok"; - extra_align = -1; - } - - RECT box, part; - box = screen; - ui_vmargin(&box, 150.0f, &box); - ui_hmargin(&box, 150.0f, &box); - - // render the box - ui_draw_rect(&box, vec4(0,0,0,0.5f), CORNER_ALL, 15.0f); - - ui_hsplit_t(&box, 20.f, &part, &box); - ui_hsplit_t(&box, 24.f, &part, &box); - ui_do_label(&part, title, 24.f, 0); - ui_hsplit_t(&box, 20.f, &part, &box); - ui_hsplit_t(&box, 24.f, &part, &box); - ui_vmargin(&part, 20.f, &part); - - if(extra_align == -1) - ui_do_label(&part, extra_text, 20.f, -1, (int)part.w); - else - ui_do_label(&part, extra_text, 20.f, 0, -1); - - if(popup == POPUP_QUIT) - { - RECT yes, no; - ui_hsplit_b(&box, 20.f, &box, &part); - ui_hsplit_b(&box, 24.f, &box, &part); - ui_vmargin(&part, 80.0f, &part); - - ui_vsplit_mid(&part, &no, &yes); - - ui_vmargin(&yes, 20.0f, &yes); - ui_vmargin(&no, 20.0f, &no); - - static int button_abort = 0; - if(ui_do_button(&button_abort, "No", 0, &no, ui_draw_menu_button, 0) || inp_key_down(KEY_ESC)) - popup = POPUP_NONE; - - static int button_tryagain = 0; - if(ui_do_button(&button_tryagain, "Yes", 0, &yes, ui_draw_menu_button, 0) || inp_key_down(KEY_ENTER)) - client_quit(); - } - else if(popup == POPUP_PASSWORD) - { - RECT label, textbox, tryagain, abort; - - ui_hsplit_b(&box, 20.f, &box, &part); - ui_hsplit_b(&box, 24.f, &box, &part); - ui_vmargin(&part, 80.0f, &part); - - ui_vsplit_mid(&part, &abort, &tryagain); - - ui_vmargin(&tryagain, 20.0f, &tryagain); - ui_vmargin(&abort, 20.0f, &abort); - - static int button_abort = 0; - if(ui_do_button(&button_abort, "Abort", 0, &abort, ui_draw_menu_button, 0) || inp_key_down(KEY_ESC)) - popup = POPUP_NONE; - - static int button_tryagain = 0; - if(ui_do_button(&button_tryagain, "Try again", 0, &tryagain, ui_draw_menu_button, 0) || inp_key_down(KEY_ENTER)) - { - client_connect(config.ui_server_address); - } - - ui_hsplit_b(&box, 60.f, &box, &part); - ui_hsplit_b(&box, 24.f, &box, &part); - - ui_vsplit_l(&part, 60.0f, 0, &label); - ui_vsplit_l(&label, 100.0f, 0, &textbox); - ui_vsplit_l(&textbox, 20.0f, 0, &textbox); - ui_vsplit_r(&textbox, 60.0f, &textbox, 0); - ui_do_label(&label, "Password:", 20, -1); - ui_do_edit_box(&config.password, &textbox, config.password, sizeof(config.password), 14.0f, true); - } - else if(popup == POPUP_FIRST_LAUNCH) - { - RECT label, textbox; - - ui_hsplit_b(&box, 20.f, &box, &part); - ui_hsplit_b(&box, 24.f, &box, &part); - ui_vmargin(&part, 80.0f, &part); - - static int enter_button = 0; - if(ui_do_button(&enter_button, "Enter", 0, &part, ui_draw_menu_button, 0) || inp_key_down(KEY_ENTER)) - popup = POPUP_NONE; - - ui_hsplit_b(&box, 40.f, &box, &part); - ui_hsplit_b(&box, 24.f, &box, &part); - - ui_vsplit_l(&part, 60.0f, 0, &label); - ui_vsplit_l(&label, 100.0f, 0, &textbox); - ui_vsplit_l(&textbox, 20.0f, 0, &textbox); - ui_vsplit_r(&textbox, 60.0f, &textbox, 0); - ui_do_label(&label, "Nickname:", 20, -1); - ui_do_edit_box(&config.player_name, &textbox, config.player_name, sizeof(config.player_name), 14.0f); - } - else - { - ui_hsplit_b(&box, 20.f, &box, &part); - ui_hsplit_b(&box, 24.f, &box, &part); - ui_vmargin(&part, 120.0f, &part); - - static int button = 0; - if(ui_do_button(&button, button_text, 0, &part, ui_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 menu_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 = ui_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(); - - if(config.debug) - { - RECT screen = *ui_screen(); - gfx_mapscreen(screen.x, screen.y, screen.w, screen.h); - - char buf[512]; - str_format(buf, sizeof(buf), "%p %p %p", ui_hot_item(), ui_active_item(), ui_last_active_item()); - TEXT_CURSOR cursor; - gfx_text_set_cursor(&cursor, 10, 10, 10, TEXTFLAG_RENDER); - gfx_text_ex(&cursor, buf, -1); - } - -} diff --git a/src/game/client/gc_menu.hpp b/src/game/client/gc_menu.hpp deleted file mode 100644 index ba12894c..00000000 --- a/src/game/client/gc_menu.hpp +++ /dev/null @@ -1,5 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#ifndef __MENU_H -#define __MENU_H - -#endif diff --git a/src/game/client/gc_render.cpp b/src/game/client/gc_render.cpp index 56e82c69..6dacc354 100644 --- a/src/game/client/gc_render.cpp +++ b/src/game/client/gc_render.cpp @@ -8,14 +8,14 @@ #include <game/generated/gc_data.hpp> #include <game/generated/g_protocol.hpp> #include <game/layers.hpp> +#include "animstate.hpp" #include "gc_render.hpp" -#include "gc_anim.hpp" #include "gc_client.hpp" #include "gc_map_image.hpp" static float sprite_w_scale; static float sprite_h_scale; - +/* static void layershot_begin() { if(!config.cl_layershot) @@ -33,7 +33,7 @@ static void layershot_end() str_format(buf, sizeof(buf), "screenshots/layers_%04d.png", config.cl_layershot); gfx_screenshot_direct(buf); config.cl_layershot++; -} +}*/ void select_sprite(SPRITE *spr, int flags, int sx, int sy) { @@ -83,8 +83,6 @@ void draw_sprite(float x, float y, float size) gfx_quads_draw(x, y, size*sprite_w_scale, size*sprite_h_scale); } - - void draw_round_rect_ext(float x, float y, float w, float h, float r, int corners) { int num = 8; @@ -155,7 +153,7 @@ void ui_draw_rect(const RECT *r, vec4 color, int corners, float rounding) gfx_quads_end(); } -void render_tee(ANIM_STATE *anim, TEE_RENDER_INFO *info, int emote, vec2 dir, vec2 pos) +void render_tee(ANIMSTATE *anim, TEE_RENDER_INFO *info, int emote, vec2 dir, vec2 pos) { vec2 direction = dir; vec2 position = pos; @@ -281,137 +279,6 @@ void mapscreen_to_world(float center_x, float center_y, float parallax_x, float points[3] = offset_y+center_y+height/2; } -static void mapscreen_to_group(float center_x, float center_y, MAPITEM_GROUP *group) -{ - float points[4]; - mapscreen_to_world(center_x, center_y, group->parallax_x/100.0f, group->parallax_y/100.0f, - group->offset_x, group->offset_y, gfx_screenaspect(), 1.0f, points); - gfx_mapscreen(points[0], points[1], points[2], points[3]); -} - -static void envelope_eval(float time_offset, int env, float *channels) -{ - channels[0] = 0; - channels[1] = 0; - channels[2] = 0; - channels[3] = 0; - - ENVPOINT *points; - - { - int start, num; - map_get_type(MAPITEMTYPE_ENVPOINTS, &start, &num); - if(num) - points = (ENVPOINT *)map_get_item(start, 0, 0); - } - - int start, num; - map_get_type(MAPITEMTYPE_ENVELOPE, &start, &num); - - if(env >= num) - return; - - MAPITEM_ENVELOPE *item = (MAPITEM_ENVELOPE *)map_get_item(start+env, 0, 0); - render_eval_envelope(points+item->start_point, item->num_points, 4, client_localtime()+time_offset, channels); -} - -void render_layers(float center_x, float center_y, int pass) -{ - bool passed_gamelayer = false; - - for(int g = 0; g < layers_num_groups(); g++) - { - MAPITEM_GROUP *group = layers_get_group(g); - - if(group->version >= 2 && group->use_clipping) - { - // set clipping - float points[4]; - mapscreen_to_group(center_x, center_y, layers_game_group()); - gfx_getscreen(&points[0], &points[1], &points[2], &points[3]); - float x0 = (group->clip_x - points[0]) / (points[2]-points[0]); - float y0 = (group->clip_y - points[1]) / (points[3]-points[1]); - float x1 = ((group->clip_x+group->clip_w) - points[0]) / (points[2]-points[0]); - float y1 = ((group->clip_y+group->clip_h) - points[1]) / (points[3]-points[1]); - - gfx_clip_enable((int)(x0*gfx_screenwidth()), (int)(y0*gfx_screenheight()), - (int)((x1-x0)*gfx_screenwidth()), (int)((y1-y0)*gfx_screenheight())); - } - - mapscreen_to_group(center_x, center_y, group); - - for(int l = 0; l < group->num_layers; l++) - { - MAPITEM_LAYER *layer = layers_get_layer(group->start_layer+l); - bool render = false; - bool is_game_layer = false; - - // skip rendering if detail layers if not wanted - if(layer->flags&LAYERFLAG_DETAIL && !config.gfx_high_detail) - continue; - - if(layer == (MAPITEM_LAYER*)layers_game_layer()) - { - is_game_layer = true; - passed_gamelayer = 1; - } - - if(pass == -1) - render = true; - else if(pass == 0) - { - if(passed_gamelayer) - return; - render = true; - } - else - { - if(passed_gamelayer && !is_game_layer) - render = true; - } - - if(render && !is_game_layer) - { - layershot_begin(); - - if(layer->type == LAYERTYPE_TILES) - { - MAPITEM_LAYER_TILEMAP *tmap = (MAPITEM_LAYER_TILEMAP *)layer; - if(tmap->image == -1) - gfx_texture_set(-1); - else - gfx_texture_set(img_get(tmap->image)); - - TILE *tiles = (TILE *)map_get_data(tmap->data); - gfx_blend_none(); - render_tilemap(tiles, tmap->width, tmap->height, 32.0f, vec4(1,1,1,1), TILERENDERFLAG_EXTEND|LAYERRENDERFLAG_OPAQUE); - gfx_blend_normal(); - render_tilemap(tiles, tmap->width, tmap->height, 32.0f, vec4(1,1,1,1), TILERENDERFLAG_EXTEND|LAYERRENDERFLAG_TRANSPARENT); - } - else if(layer->type == LAYERTYPE_QUADS) - { - MAPITEM_LAYER_QUADS *qlayer = (MAPITEM_LAYER_QUADS *)layer; - if(qlayer->image == -1) - gfx_texture_set(-1); - else - gfx_texture_set(img_get(qlayer->image)); - - QUAD *quads = (QUAD *)map_get_data_swapped(qlayer->data); - - gfx_blend_none(); - render_quads(quads, qlayer->num_quads, envelope_eval, LAYERRENDERFLAG_OPAQUE); - gfx_blend_normal(); - render_quads(quads, qlayer->num_quads, envelope_eval, LAYERRENDERFLAG_TRANSPARENT); - } - - layershot_end(); - } - } - - gfx_clip_disable(); - } -} - void render_tilemap_generate_skip() { for(int g = 0; g < layers_num_groups(); g++) @@ -444,119 +311,3 @@ void render_tilemap_generate_skip() } } } - -static void 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 == NETOBJTYPE_PROJECTILE) - { - render_projectile((const NETOBJ_PROJECTILE *)data, item.id); - } - else if(item.type == NETOBJTYPE_PICKUP) - { - const void *prev = snap_find_item(SNAP_PREV, item.type, item.id); - if(prev) - render_pickup((const NETOBJ_PICKUP *)prev, (const NETOBJ_PICKUP *)data); - } - else if(item.type == NETOBJTYPE_LASER) - { - render_laser((const NETOBJ_LASER *)data); - } - else if(item.type == NETOBJTYPE_FLAG) - { - const void *prev = snap_find_item(SNAP_PREV, item.type, item.id); - if (prev) - render_flag((const NETOBJ_FLAG *)prev, (const NETOBJ_FLAG *)data); - } - } - - // render extra projectiles - for(int i = 0; i < extraproj_num; i++) - { - if(extraproj_projectiles[i].start_tick < client_tick()) - { - extraproj_projectiles[i] = extraproj_projectiles[extraproj_num-1]; - extraproj_num--; - } - else - render_projectile(&extraproj_projectiles[i], 0); - } -} - - -static void render_players() -{ - 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 == NETOBJTYPE_CHARACTER) - { - const void *prev = snap_find_item(SNAP_PREV, item.type, item.id); - const void *prev_info = snap_find_item(SNAP_PREV, NETOBJTYPE_PLAYER_INFO, item.id); - const void *info = snap_find_item(SNAP_CURRENT, NETOBJTYPE_PLAYER_INFO, item.id); - - if(prev && prev_info && info) - { - render_player( - (const NETOBJ_CHARACTER *)prev, - (const NETOBJ_CHARACTER *)data, - (const NETOBJ_PLAYER_INFO *)prev_info, - (const NETOBJ_PLAYER_INFO *)info - ); - } - } - } -} - -// renders the complete game world -void render_world(float center_x, float center_y, float zoom) -{ - // render background layers - render_layers(center_x, center_y, 0); - gfx_clip_disable(); - - // render trails - layershot_begin(); - particle_render(PARTGROUP_PROJECTILE_TRAIL); - layershot_end(); - - // render items - layershot_begin(); - render_items(); - layershot_end(); - - // render players above all - layershot_begin(); - render_players(); - layershot_end(); - - // render particles - layershot_begin(); - particle_render(PARTGROUP_EXPLOSIONS); - particle_render(PARTGROUP_GENERAL); - layershot_end(); - - if(config.dbg_flow) - flow_dbg_render(); - - // render foreground layers - layershot_begin(); - render_layers(center_x, center_y, 1); - layershot_end(); - gfx_clip_disable(); - - // render damage indications - layershot_begin(); - render_damage_indicators(); - layershot_end(); - - config.cl_layershot = 0; -} diff --git a/src/game/client/gc_render.hpp b/src/game/client/gc_render.hpp index e48d0fcd..088ff78a 100644 --- a/src/game/client/gc_render.hpp +++ b/src/game/client/gc_render.hpp @@ -50,9 +50,6 @@ void draw_round_rect_ext(float x, float y, float w, float h, float r, int corner void ui_draw_rect(const RECT *r, vec4 color, int corners, float rounding); // larger rendering methods -void menu_render(); -void render_game(); -void render_world(float center_x, float center_y, float zoom); void render_loading(float percent); void render_damage_indicators(); @@ -61,15 +58,8 @@ void render_particles(); void render_tilemap_generate_skip(); // object render methods (gc_render_obj.cpp) -void render_tee(class ANIM_STATE *anim, TEE_RENDER_INFO *info, int emote, vec2 dir, vec2 pos); -void render_flag(const struct NETOBJ_FLAG *prev, const struct NETOBJ_FLAG *current); -void render_pickup(const struct NETOBJ_PICKUP *prev, const struct NETOBJ_PICKUP *current); -void render_projectile(const struct NETOBJ_PROJECTILE *current, int itemid); -void render_laser(const struct NETOBJ_LASER *current); -void render_player( - const struct NETOBJ_CHARACTER *prev_char, const struct NETOBJ_CHARACTER *player_char, - const struct NETOBJ_PLAYER_INFO *prev_info, const struct NETOBJ_PLAYER_INFO *player_info); - +void render_tee(class ANIMSTATE *anim, TEE_RENDER_INFO *info, int emote, vec2 dir, vec2 pos); + // map render methods (gc_render_map.cpp) void render_eval_envelope(ENVPOINT *points, int num_points, int channels, float time, float *result); void render_quads(QUAD *quads, int num_quads, void (*eval)(float time_offset, int env, float *channels), int flags); diff --git a/src/game/client/gc_skin.hpp b/src/game/client/gc_skin.hpp deleted file mode 100644 index 786f6768..00000000 --- a/src/game/client/gc_skin.hpp +++ /dev/null @@ -1,21 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include <base/vmath.hpp> - -// do this better and nicer -typedef struct -{ - int org_texture; - int color_texture; - char name[31]; - char term[1]; - vec3 blood_color; -} 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/lineinput.cpp b/src/game/client/lineinput.cpp new file mode 100644 index 00000000..7a02d1f9 --- /dev/null +++ b/src/game/client/lineinput.cpp @@ -0,0 +1,65 @@ +#include <engine/e_client_interface.h> +#include <string.h> // strlen +#include "lineinput.hpp" + +LINEINPUT::LINEINPUT() +{ + clear(); +} + +void LINEINPUT::clear() +{ + mem_zero(str, sizeof(str)); + len = 0; + cursor_pos = 0; +} + +void LINEINPUT::set(const char *string) +{ + str_copy(str, string, sizeof(str)); + len = strlen(str); + cursor_pos = len; +} + +void LINEINPUT::process_input(INPUT_EVENT e) +{ + if(cursor_pos > len) + cursor_pos = len; + + char c = e.ch; + int k = e.key; + + if (!(c >= 0 && c < 32)) + { + if (len < sizeof(str) - 1 && cursor_pos < sizeof(str) - 1) + { + memmove(str + cursor_pos + 1, str + cursor_pos, len - cursor_pos + 1); + str[cursor_pos] = c; + cursor_pos++; + len++; + } + } + + if(e.flags&INPFLAG_PRESS) + { + if (k == KEY_BACKSPACE && cursor_pos > 0) + { + memmove(str + cursor_pos - 1, str + cursor_pos, len - cursor_pos + 1); + cursor_pos--; + len--; + } + else if (k == KEY_DEL && cursor_pos < len) + { + memmove(str + cursor_pos, str + cursor_pos + 1, len - cursor_pos); + len--; + } + else if (k == KEY_LEFT && cursor_pos > 0) + cursor_pos--; + else if (k == KEY_RIGHT && cursor_pos < len) + cursor_pos++; + else if (k == KEY_HOME) + cursor_pos = 0; + else if (k == KEY_END) + cursor_pos = len; + } +} diff --git a/src/game/client/lineinput.hpp b/src/game/client/lineinput.hpp new file mode 100644 index 00000000..cf42a7a5 --- /dev/null +++ b/src/game/client/lineinput.hpp @@ -0,0 +1,23 @@ + +// line input helter +class LINEINPUT +{ + char str[256]; + unsigned len; + unsigned cursor_pos; +public: + class CALLBACK + { + public: + virtual ~CALLBACK() {} + virtual bool event(INPUT_EVENT e) = 0; + }; + + LINEINPUT(); + void clear(); + void process_input(INPUT_EVENT e); + void set(const char *string); + const char *get_string() const { return str; } + int get_length() const { return len; } + unsigned cursor_offset() const { return cursor_pos; } +}; |