From d0fbd68055e3f4a796330cc8acc6c0954b5327ff Mon Sep 17 00:00:00 2001 From: untodesu Date: Thu, 11 Sep 2025 15:48:53 +0500 Subject: Run clang-format across the project --- game/client/gui/background.cc | 78 +- game/client/gui/background.hh | 16 +- game/client/gui/bother.cc | 326 +++--- game/client/gui/bother.hh | 44 +- game/client/gui/chat.cc | 546 ++++----- game/client/gui/chat.hh | 34 +- game/client/gui/crosshair.cc | 86 +- game/client/gui/crosshair.hh | 16 +- game/client/gui/direct_connection.cc | 290 ++--- game/client/gui/direct_connection.hh | 14 +- game/client/gui/gui_screen.hh | 20 +- game/client/gui/hotbar.cc | 366 +++--- game/client/gui/hotbar.hh | 54 +- game/client/gui/imdraw_ext.cc | 68 +- game/client/gui/imdraw_ext.hh | 34 +- game/client/gui/language.cc | 402 +++---- game/client/gui/language.hh | 84 +- game/client/gui/main_menu.cc | 344 +++--- game/client/gui/main_menu.hh | 16 +- game/client/gui/message_box.cc | 190 +-- game/client/gui/message_box.hh | 40 +- game/client/gui/metrics.cc | 206 ++-- game/client/gui/metrics.hh | 14 +- game/client/gui/play_menu.cc | 1128 +++++++++--------- game/client/gui/play_menu.hh | 18 +- game/client/gui/progress_bar.cc | 222 ++-- game/client/gui/progress_bar.hh | 38 +- game/client/gui/scoreboard.cc | 206 ++-- game/client/gui/scoreboard.hh | 14 +- game/client/gui/settings.cc | 2138 +++++++++++++++++----------------- game/client/gui/settings.hh | 180 +-- game/client/gui/splash.cc | 356 +++--- game/client/gui/splash.hh | 16 +- game/client/gui/status_lines.cc | 168 +-- game/client/gui/status_lines.hh | 42 +- game/client/gui/window_title.cc | 46 +- game/client/gui/window_title.hh | 12 +- 37 files changed, 3936 insertions(+), 3936 deletions(-) (limited to 'game/client/gui') diff --git a/game/client/gui/background.cc b/game/client/gui/background.cc index 50fef01..d1500bb 100644 --- a/game/client/gui/background.cc +++ b/game/client/gui/background.cc @@ -1,39 +1,39 @@ -#include "client/pch.hh" - -#include "client/gui/background.hh" - -#include "core/math/constexpr.hh" - -#include "core/resource/resource.hh" - -#include "client/resource/texture_gui.hh" - -#include "client/globals.hh" - -static resource_ptr texture; - -void gui::background::init(void) -{ - texture = resource::load("textures/gui/background.png", TEXTURE_GUI_LOAD_VFLIP); - - if(texture == nullptr) { - spdlog::critical("background: texture load failed"); - std::terminate(); - } -} - -void gui::background::shutdown(void) -{ - texture = nullptr; -} - -void gui::background::layout(void) -{ - auto viewport = ImGui::GetMainViewport(); - auto draw_list = ImGui::GetBackgroundDrawList(); - - auto scaled_width = 0.75f * static_cast(globals::width / globals::gui_scale); - auto scaled_height = 0.75f * static_cast(globals::height / globals::gui_scale); - auto scale_uv = ImVec2(scaled_width / static_cast(texture->size.x), scaled_height / static_cast(texture->size.y)); - draw_list->AddImage(texture->handle, ImVec2(0.0f, 0.0f), viewport->Size, ImVec2(0.0f, 0.0f), scale_uv); -} +#include "client/pch.hh" + +#include "client/gui/background.hh" + +#include "core/math/constexpr.hh" + +#include "core/resource/resource.hh" + +#include "client/resource/texture_gui.hh" + +#include "client/globals.hh" + +static resource_ptr texture; + +void gui::background::init(void) +{ + texture = resource::load("textures/gui/background.png", TEXTURE_GUI_LOAD_VFLIP); + + if(texture == nullptr) { + spdlog::critical("background: texture load failed"); + std::terminate(); + } +} + +void gui::background::shutdown(void) +{ + texture = nullptr; +} + +void gui::background::layout(void) +{ + auto viewport = ImGui::GetMainViewport(); + auto draw_list = ImGui::GetBackgroundDrawList(); + + auto scaled_width = 0.75f * static_cast(globals::width / globals::gui_scale); + auto scaled_height = 0.75f * static_cast(globals::height / globals::gui_scale); + auto scale_uv = ImVec2(scaled_width / static_cast(texture->size.x), scaled_height / static_cast(texture->size.y)); + draw_list->AddImage(texture->handle, ImVec2(0.0f, 0.0f), viewport->Size, ImVec2(0.0f, 0.0f), scale_uv); +} diff --git a/game/client/gui/background.hh b/game/client/gui/background.hh index 5c72a3f..6145484 100644 --- a/game/client/gui/background.hh +++ b/game/client/gui/background.hh @@ -1,8 +1,8 @@ -#pragma once - -namespace gui::background -{ -void init(void); -void shutdown(void); -void layout(void); -} // namespace gui::background +#pragma once + +namespace gui::background +{ +void init(void); +void shutdown(void); +void layout(void); +} // namespace gui::background diff --git a/game/client/gui/bother.cc b/game/client/gui/bother.cc index d87f220..1bb7097 100644 --- a/game/client/gui/bother.cc +++ b/game/client/gui/bother.cc @@ -1,163 +1,163 @@ -#include "client/pch.hh" - -#include "client/gui/bother.hh" - -#include "shared/protocol.hh" - -#include "client/globals.hh" - -// Maximum amount of peers used for bothering -constexpr static std::size_t BOTHER_PEERS = 4; - -struct BotherQueueItem final { - unsigned int identity; - std::string hostname; - std::uint16_t port; -}; - -static ENetHost* bother_host; -static entt::dispatcher bother_dispatcher; -static std::unordered_set bother_set; -static std::deque bother_queue; - -static void on_status_response_packet(const protocol::StatusResponse& packet) -{ - auto identity = static_cast(reinterpret_cast(packet.peer->data)); - - bother_set.erase(identity); - - gui::BotherResponseEvent event; - event.identity = identity; - event.is_server_unreachable = false; - event.protocol_version = packet.version; - event.num_players = packet.num_players; - event.max_players = packet.max_players; - event.motd = packet.motd; - globals::dispatcher.trigger(event); - - enet_peer_disconnect(packet.peer, protocol::CHANNEL); -} - -void gui::bother::init(void) -{ - bother_host = enet_host_create(nullptr, BOTHER_PEERS, 1, 0, 0); - bother_dispatcher.clear(); - bother_set.clear(); - - bother_dispatcher.sink().connect<&on_status_response_packet>(); -} - -void gui::bother::shutdown(void) -{ - enet_host_destroy(bother_host); - bother_dispatcher.clear(); - bother_set.clear(); -} - -void gui::bother::update_late(void) -{ - unsigned int free_peers = 0U; - - // Figure out how much times we can call - // enet_host_connect and reallistically succeed - for(unsigned int i = 0U; i < bother_host->peerCount; ++i) { - if(bother_host->peers[i].state != ENET_PEER_STATE_DISCONNECTED) { - continue; - } - - free_peers += 1U; - } - - for(unsigned int i = 0U; (i < free_peers) && bother_queue.size(); ++i) { - const auto& item = bother_queue.front(); - - ENetAddress address; - enet_address_set_host(&address, item.hostname.c_str()); - address.port = enet_uint16(item.port); - - if(auto peer = enet_host_connect(bother_host, &address, 1, 0)) { - peer->data = reinterpret_cast(static_cast(item.identity)); - bother_set.insert(item.identity); - enet_host_flush(bother_host); - } - - bother_queue.pop_front(); - } - - ENetEvent enet_event; - - if(0 < enet_host_service(bother_host, &enet_event, 0)) { - if(enet_event.type == ENET_EVENT_TYPE_CONNECT) { - protocol::StatusRequest packet; - packet.version = protocol::VERSION; - protocol::send(enet_event.peer, protocol::encode(packet)); - return; - } - - if(enet_event.type == ENET_EVENT_TYPE_RECEIVE) { - protocol::decode(bother_dispatcher, enet_event.packet, enet_event.peer); - enet_packet_destroy(enet_event.packet); - return; - } - - if(enet_event.type == ENET_EVENT_TYPE_DISCONNECT) { - auto identity = static_cast(reinterpret_cast(enet_event.peer->data)); - - if(bother_set.count(identity)) { - BotherResponseEvent event; - event.identity = identity; - event.is_server_unreachable = true; - globals::dispatcher.trigger(event); - } - - bother_set.erase(identity); - - return; - } - } -} - -void gui::bother::ping(unsigned int identity, std::string_view host, std::uint16_t port) -{ - if(bother_set.count(identity)) { - // Already in the process - return; - } - - for(const auto& item : bother_queue) { - if(item.identity == identity) { - // Already in the queue - return; - } - } - - BotherQueueItem item; - item.identity = identity; - item.hostname = host; - item.port = port; - - bother_queue.push_back(item); -} - -void gui::bother::cancel(unsigned int identity) -{ - bother_set.erase(identity); - - auto item = bother_queue.cbegin(); - - while(item != bother_queue.cend()) { - if(item->identity == identity) { - item = bother_queue.erase(item); - continue; - } - - item = std::next(item); - } - - for(unsigned int i = 0U; i < bother_host->peerCount; ++i) { - if(bother_host->peers[i].data == reinterpret_cast(static_cast(identity))) { - enet_peer_reset(&bother_host->peers[i]); - break; - } - } -} +#include "client/pch.hh" + +#include "client/gui/bother.hh" + +#include "shared/protocol.hh" + +#include "client/globals.hh" + +// Maximum amount of peers used for bothering +constexpr static std::size_t BOTHER_PEERS = 4; + +struct BotherQueueItem final { + unsigned int identity; + std::string hostname; + std::uint16_t port; +}; + +static ENetHost* bother_host; +static entt::dispatcher bother_dispatcher; +static std::unordered_set bother_set; +static std::deque bother_queue; + +static void on_status_response_packet(const protocol::StatusResponse& packet) +{ + auto identity = static_cast(reinterpret_cast(packet.peer->data)); + + bother_set.erase(identity); + + gui::BotherResponseEvent event; + event.identity = identity; + event.is_server_unreachable = false; + event.protocol_version = packet.version; + event.num_players = packet.num_players; + event.max_players = packet.max_players; + event.motd = packet.motd; + globals::dispatcher.trigger(event); + + enet_peer_disconnect(packet.peer, protocol::CHANNEL); +} + +void gui::bother::init(void) +{ + bother_host = enet_host_create(nullptr, BOTHER_PEERS, 1, 0, 0); + bother_dispatcher.clear(); + bother_set.clear(); + + bother_dispatcher.sink().connect<&on_status_response_packet>(); +} + +void gui::bother::shutdown(void) +{ + enet_host_destroy(bother_host); + bother_dispatcher.clear(); + bother_set.clear(); +} + +void gui::bother::update_late(void) +{ + unsigned int free_peers = 0U; + + // Figure out how much times we can call + // enet_host_connect and reallistically succeed + for(unsigned int i = 0U; i < bother_host->peerCount; ++i) { + if(bother_host->peers[i].state != ENET_PEER_STATE_DISCONNECTED) { + continue; + } + + free_peers += 1U; + } + + for(unsigned int i = 0U; (i < free_peers) && bother_queue.size(); ++i) { + const auto& item = bother_queue.front(); + + ENetAddress address; + enet_address_set_host(&address, item.hostname.c_str()); + address.port = enet_uint16(item.port); + + if(auto peer = enet_host_connect(bother_host, &address, 1, 0)) { + peer->data = reinterpret_cast(static_cast(item.identity)); + bother_set.insert(item.identity); + enet_host_flush(bother_host); + } + + bother_queue.pop_front(); + } + + ENetEvent enet_event; + + if(0 < enet_host_service(bother_host, &enet_event, 0)) { + if(enet_event.type == ENET_EVENT_TYPE_CONNECT) { + protocol::StatusRequest packet; + packet.version = protocol::VERSION; + protocol::send(enet_event.peer, protocol::encode(packet)); + return; + } + + if(enet_event.type == ENET_EVENT_TYPE_RECEIVE) { + protocol::decode(bother_dispatcher, enet_event.packet, enet_event.peer); + enet_packet_destroy(enet_event.packet); + return; + } + + if(enet_event.type == ENET_EVENT_TYPE_DISCONNECT) { + auto identity = static_cast(reinterpret_cast(enet_event.peer->data)); + + if(bother_set.count(identity)) { + BotherResponseEvent event; + event.identity = identity; + event.is_server_unreachable = true; + globals::dispatcher.trigger(event); + } + + bother_set.erase(identity); + + return; + } + } +} + +void gui::bother::ping(unsigned int identity, std::string_view host, std::uint16_t port) +{ + if(bother_set.count(identity)) { + // Already in the process + return; + } + + for(const auto& item : bother_queue) { + if(item.identity == identity) { + // Already in the queue + return; + } + } + + BotherQueueItem item; + item.identity = identity; + item.hostname = host; + item.port = port; + + bother_queue.push_back(item); +} + +void gui::bother::cancel(unsigned int identity) +{ + bother_set.erase(identity); + + auto item = bother_queue.cbegin(); + + while(item != bother_queue.cend()) { + if(item->identity == identity) { + item = bother_queue.erase(item); + continue; + } + + item = std::next(item); + } + + for(unsigned int i = 0U; i < bother_host->peerCount; ++i) { + if(bother_host->peers[i].data == reinterpret_cast(static_cast(identity))) { + enet_peer_reset(&bother_host->peers[i]); + break; + } + } +} diff --git a/game/client/gui/bother.hh b/game/client/gui/bother.hh index c83294c..fc5bab4 100644 --- a/game/client/gui/bother.hh +++ b/game/client/gui/bother.hh @@ -1,22 +1,22 @@ -#pragma once - -namespace gui -{ -struct BotherResponseEvent final { - unsigned int identity; - bool is_server_unreachable; - std::uint32_t protocol_version; - std::uint16_t num_players; - std::uint16_t max_players; - std::string motd; -}; -} // namespace gui - -namespace gui::bother -{ -void init(void); -void shutdown(void); -void update_late(void); -void ping(unsigned int identity, std::string_view host, std::uint16_t port); -void cancel(unsigned int identity); -} // namespace gui::bother +#pragma once + +namespace gui +{ +struct BotherResponseEvent final { + unsigned int identity; + bool is_server_unreachable; + std::uint32_t protocol_version; + std::uint16_t num_players; + std::uint16_t max_players; + std::string motd; +}; +} // namespace gui + +namespace gui::bother +{ +void init(void); +void shutdown(void); +void update_late(void); +void ping(unsigned int identity, std::string_view host, std::uint16_t port); +void cancel(unsigned int identity); +} // namespace gui::bother diff --git a/game/client/gui/chat.cc b/game/client/gui/chat.cc index 6e4498b..aaee8c6 100644 --- a/game/client/gui/chat.cc +++ b/game/client/gui/chat.cc @@ -1,273 +1,273 @@ -#include "client/pch.hh" - -#include "client/gui/chat.hh" - -#include "core/config/number.hh" -#include "core/config/string.hh" - -#include "core/io/config_map.hh" - -#include "core/resource/resource.hh" - -#include "core/utils/string.hh" - -#include "shared/protocol.hh" - -#include "client/config/keybind.hh" - -#include "client/gui/gui_screen.hh" -#include "client/gui/imdraw_ext.hh" -#include "client/gui/language.hh" -#include "client/gui/settings.hh" - -#include "client/io/glfw.hh" - -#include "client/resource/sound_effect.hh" - -#include "client/sound/sound.hh" - -#include "client/game.hh" -#include "client/globals.hh" -#include "client/session.hh" - -constexpr static ImGuiWindowFlags WINDOW_FLAGS = ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoDecoration; -constexpr static unsigned int MAX_HISTORY_SIZE = 128U; - -struct GuiChatMessage final { - std::uint64_t spawn; - std::string text; - ImVec4 color; -}; - -static config::KeyBind key_chat(GLFW_KEY_ENTER); -static config::Unsigned history_size(32U, 0U, MAX_HISTORY_SIZE); - -static std::deque history; -static std::string chat_input; -static bool needs_focus; - -static resource_ptr sfx_chat_message; - -static void append_text_message(const std::string& sender, const std::string& text) -{ - GuiChatMessage message; - message.spawn = globals::curtime; - message.text = std::format("<{}> {}", sender, text); - message.color = ImGui::GetStyleColorVec4(ImGuiCol_Text); - history.push_back(message); - - if(sfx_chat_message && session::is_ingame()) { - sound::play_ui(sfx_chat_message, false, 1.0f); - } -} - -static void append_player_join(const std::string& sender) -{ - GuiChatMessage message; - message.spawn = globals::curtime; - message.text = std::format("{} {}", sender, gui::language::resolve("chat.client_join")); - message.color = ImGui::GetStyleColorVec4(ImGuiCol_DragDropTarget); - history.push_back(message); - - if(sfx_chat_message && session::is_ingame()) { - sound::play_ui(sfx_chat_message, false, 1.0f); - } -} - -static void append_player_leave(const std::string& sender, const std::string& reason) -{ - GuiChatMessage message; - message.spawn = globals::curtime; - message.text = std::format("{} {} ({})", sender, gui::language::resolve("chat.client_left"), gui::language::resolve(reason.c_str())); - message.color = ImGui::GetStyleColorVec4(ImGuiCol_DragDropTarget); - history.push_back(message); - - if(sfx_chat_message && session::is_ingame()) { - sound::play_ui(sfx_chat_message, false, 1.0f); - } -} - -static void on_chat_message_packet(const protocol::ChatMessage& packet) -{ - if(packet.type == protocol::ChatMessage::TEXT_MESSAGE) { - append_text_message(packet.sender, packet.message); - return; - } - - if(packet.type == protocol::ChatMessage::PLAYER_JOIN) { - append_player_join(packet.sender); - return; - } - - if(packet.type == protocol::ChatMessage::PLAYER_LEAVE) { - append_player_leave(packet.sender, packet.message); - return; - } -} - -static void on_glfw_key(const io::GlfwKeyEvent& event) -{ - if(event.action == GLFW_PRESS) { - if((event.key == GLFW_KEY_ENTER) && (globals::gui_screen == GUI_CHAT)) { - if(!utils::is_whitespace(chat_input)) { - protocol::ChatMessage packet; - packet.type = protocol::ChatMessage::TEXT_MESSAGE; - packet.sender = client_game::username.get(); - packet.message = chat_input; - - protocol::send(session::peer, protocol::encode(packet)); - } - - globals::gui_screen = GUI_SCREEN_NONE; - - chat_input.clear(); - - return; - } - - if((event.key == GLFW_KEY_ESCAPE) && (globals::gui_screen == GUI_CHAT)) { - globals::gui_screen = GUI_SCREEN_NONE; - return; - } - - if(key_chat.equals(event.key) && !globals::gui_screen) { - globals::gui_screen = GUI_CHAT; - needs_focus = true; - return; - } - } -} - -void gui::client_chat::init(void) -{ - globals::client_config.add_value("chat.key", key_chat); - globals::client_config.add_value("chat.history_size", history_size); - - settings::add_keybind(2, key_chat, settings_location::KEYBOARD_MISC, "key.chat"); - settings::add_slider(1, history_size, settings_location::VIDEO_GUI, "chat.history_size", false); - - globals::dispatcher.sink().connect<&on_chat_message_packet>(); - globals::dispatcher.sink().connect<&on_glfw_key>(); - - sfx_chat_message = resource::load("sounds/ui/chat_message.wav"); -} - -void gui::client_chat::init_late(void) -{ -} - -void gui::client_chat::shutdown(void) -{ - sfx_chat_message = nullptr; -} - -void gui::client_chat::update(void) -{ - while(history.size() > history_size.get_value()) { - history.pop_front(); - } -} - -void gui::client_chat::layout(void) -{ - auto viewport = ImGui::GetMainViewport(); - auto window_start = ImVec2(0.0f, 0.0f); - auto window_size = ImVec2(0.75f * viewport->Size.x, viewport->Size.y); - - ImGui::SetNextWindowPos(window_start); - ImGui::SetNextWindowSize(window_size); - - ImGui::PushFont(globals::font_unscii16, 8.0f); - - if(!ImGui::Begin("###chat", nullptr, WINDOW_FLAGS)) { - ImGui::End(); - return; - } - - auto& padding = ImGui::GetStyle().FramePadding; - auto& spacing = ImGui::GetStyle().ItemSpacing; - auto font = ImGui::GetFont(); - - auto draw_list = ImGui::GetWindowDrawList(); - - // The text input widget occupies the bottom part - // of the chat window, we need to reserve some space for it - auto ypos = window_size.y - 2.5f * ImGui::GetFontSize() - 2.0f * padding.y - 2.0f * spacing.y; - - if(globals::gui_screen == GUI_CHAT) { - if(needs_focus) { - ImGui::SetKeyboardFocusHere(); - needs_focus = false; - } - - ImGui::SetNextItemWidth(window_size.x + 32.0f * padding.x); - ImGui::SetCursorScreenPos(ImVec2(padding.x, ypos)); - ImGui::InputText("###chat.input", &chat_input); - } - - if(!client_game::hide_hud && ((globals::gui_screen == GUI_SCREEN_NONE) || (globals::gui_screen == GUI_CHAT))) { - for(auto it = history.crbegin(); it < history.crend(); ++it) { - auto text_size = ImGui::CalcTextSize(it->text.c_str(), it->text.c_str() + it->text.size(), false, window_size.x); - auto rect_size = ImVec2(window_size.x, text_size.y + 2.0f * padding.y); - - auto rect_pos = ImVec2(padding.x, ypos - text_size.y - 2.0f * padding.y); - auto rect_end = ImVec2(rect_pos.x + rect_size.x, rect_pos.y + rect_size.y); - auto text_pos = ImVec2(rect_pos.x + padding.x, rect_pos.y + padding.y); - - auto fadeout_seconds = 10.0f; - auto fadeout = std::exp(-1.0f * std::pow(1.0e-6 * static_cast(globals::curtime - it->spawn) / fadeout_seconds, 10.0f)); - - float rect_alpha; - float text_alpha; - - if(globals::gui_screen == GUI_CHAT) { - rect_alpha = 0.75f; - text_alpha = 1.00f; - } - else { - rect_alpha = 0.50f * fadeout; - text_alpha = 1.00f * fadeout; - } - - auto rect_col = ImGui::GetColorU32(ImGuiCol_FrameBg, rect_alpha); - auto text_col = ImGui::GetColorU32(ImVec4(it->color.x, it->color.y, it->color.z, it->color.w * text_alpha)); - auto shadow_col = ImGui::GetColorU32(ImVec4(0.0f, 0.0f, 0.0f, text_alpha)); - - draw_list->AddRectFilled(rect_pos, rect_end, rect_col); - - imdraw_ext::text_shadow_w(it->text, text_pos, text_col, shadow_col, font, draw_list, 8.0f, window_size.x); - - ypos -= rect_size.y; - } - } - - ImGui::End(); - ImGui::PopFont(); -} - -void gui::client_chat::clear(void) -{ - history.clear(); -} - -void gui::client_chat::refresh_timings(void) -{ - for(auto it = history.begin(); it < history.end(); ++it) { - // Reset the spawn time so the fadeout timer - // is reset; SpawnPlayer handler might call this - it->spawn = globals::curtime; - } -} - -void gui::client_chat::print(const std::string& text) -{ - GuiChatMessage message = {}; - message.spawn = globals::curtime; - message.text = text; - message.color = ImGui::GetStyleColorVec4(ImGuiCol_Text); - history.push_back(message); - - if(sfx_chat_message && session::is_ingame()) { - sound::play_ui(sfx_chat_message, false, 1.0f); - } -} +#include "client/pch.hh" + +#include "client/gui/chat.hh" + +#include "core/config/number.hh" +#include "core/config/string.hh" + +#include "core/io/config_map.hh" + +#include "core/resource/resource.hh" + +#include "core/utils/string.hh" + +#include "shared/protocol.hh" + +#include "client/config/keybind.hh" + +#include "client/gui/gui_screen.hh" +#include "client/gui/imdraw_ext.hh" +#include "client/gui/language.hh" +#include "client/gui/settings.hh" + +#include "client/io/glfw.hh" + +#include "client/resource/sound_effect.hh" + +#include "client/sound/sound.hh" + +#include "client/game.hh" +#include "client/globals.hh" +#include "client/session.hh" + +constexpr static ImGuiWindowFlags WINDOW_FLAGS = ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoDecoration; +constexpr static unsigned int MAX_HISTORY_SIZE = 128U; + +struct GuiChatMessage final { + std::uint64_t spawn; + std::string text; + ImVec4 color; +}; + +static config::KeyBind key_chat(GLFW_KEY_ENTER); +static config::Unsigned history_size(32U, 0U, MAX_HISTORY_SIZE); + +static std::deque history; +static std::string chat_input; +static bool needs_focus; + +static resource_ptr sfx_chat_message; + +static void append_text_message(const std::string& sender, const std::string& text) +{ + GuiChatMessage message; + message.spawn = globals::curtime; + message.text = std::format("<{}> {}", sender, text); + message.color = ImGui::GetStyleColorVec4(ImGuiCol_Text); + history.push_back(message); + + if(sfx_chat_message && session::is_ingame()) { + sound::play_ui(sfx_chat_message, false, 1.0f); + } +} + +static void append_player_join(const std::string& sender) +{ + GuiChatMessage message; + message.spawn = globals::curtime; + message.text = std::format("{} {}", sender, gui::language::resolve("chat.client_join")); + message.color = ImGui::GetStyleColorVec4(ImGuiCol_DragDropTarget); + history.push_back(message); + + if(sfx_chat_message && session::is_ingame()) { + sound::play_ui(sfx_chat_message, false, 1.0f); + } +} + +static void append_player_leave(const std::string& sender, const std::string& reason) +{ + GuiChatMessage message; + message.spawn = globals::curtime; + message.text = std::format("{} {} ({})", sender, gui::language::resolve("chat.client_left"), gui::language::resolve(reason.c_str())); + message.color = ImGui::GetStyleColorVec4(ImGuiCol_DragDropTarget); + history.push_back(message); + + if(sfx_chat_message && session::is_ingame()) { + sound::play_ui(sfx_chat_message, false, 1.0f); + } +} + +static void on_chat_message_packet(const protocol::ChatMessage& packet) +{ + if(packet.type == protocol::ChatMessage::TEXT_MESSAGE) { + append_text_message(packet.sender, packet.message); + return; + } + + if(packet.type == protocol::ChatMessage::PLAYER_JOIN) { + append_player_join(packet.sender); + return; + } + + if(packet.type == protocol::ChatMessage::PLAYER_LEAVE) { + append_player_leave(packet.sender, packet.message); + return; + } +} + +static void on_glfw_key(const io::GlfwKeyEvent& event) +{ + if(event.action == GLFW_PRESS) { + if((event.key == GLFW_KEY_ENTER) && (globals::gui_screen == GUI_CHAT)) { + if(!utils::is_whitespace(chat_input)) { + protocol::ChatMessage packet; + packet.type = protocol::ChatMessage::TEXT_MESSAGE; + packet.sender = client_game::username.get(); + packet.message = chat_input; + + protocol::send(session::peer, protocol::encode(packet)); + } + + globals::gui_screen = GUI_SCREEN_NONE; + + chat_input.clear(); + + return; + } + + if((event.key == GLFW_KEY_ESCAPE) && (globals::gui_screen == GUI_CHAT)) { + globals::gui_screen = GUI_SCREEN_NONE; + return; + } + + if(key_chat.equals(event.key) && !globals::gui_screen) { + globals::gui_screen = GUI_CHAT; + needs_focus = true; + return; + } + } +} + +void gui::client_chat::init(void) +{ + globals::client_config.add_value("chat.key", key_chat); + globals::client_config.add_value("chat.history_size", history_size); + + settings::add_keybind(2, key_chat, settings_location::KEYBOARD_MISC, "key.chat"); + settings::add_slider(1, history_size, settings_location::VIDEO_GUI, "chat.history_size", false); + + globals::dispatcher.sink().connect<&on_chat_message_packet>(); + globals::dispatcher.sink().connect<&on_glfw_key>(); + + sfx_chat_message = resource::load("sounds/ui/chat_message.wav"); +} + +void gui::client_chat::init_late(void) +{ +} + +void gui::client_chat::shutdown(void) +{ + sfx_chat_message = nullptr; +} + +void gui::client_chat::update(void) +{ + while(history.size() > history_size.get_value()) { + history.pop_front(); + } +} + +void gui::client_chat::layout(void) +{ + auto viewport = ImGui::GetMainViewport(); + auto window_start = ImVec2(0.0f, 0.0f); + auto window_size = ImVec2(0.75f * viewport->Size.x, viewport->Size.y); + + ImGui::SetNextWindowPos(window_start); + ImGui::SetNextWindowSize(window_size); + + ImGui::PushFont(globals::font_unscii16, 8.0f); + + if(!ImGui::Begin("###chat", nullptr, WINDOW_FLAGS)) { + ImGui::End(); + return; + } + + auto& padding = ImGui::GetStyle().FramePadding; + auto& spacing = ImGui::GetStyle().ItemSpacing; + auto font = ImGui::GetFont(); + + auto draw_list = ImGui::GetWindowDrawList(); + + // The text input widget occupies the bottom part + // of the chat window, we need to reserve some space for it + auto ypos = window_size.y - 2.5f * ImGui::GetFontSize() - 2.0f * padding.y - 2.0f * spacing.y; + + if(globals::gui_screen == GUI_CHAT) { + if(needs_focus) { + ImGui::SetKeyboardFocusHere(); + needs_focus = false; + } + + ImGui::SetNextItemWidth(window_size.x + 32.0f * padding.x); + ImGui::SetCursorScreenPos(ImVec2(padding.x, ypos)); + ImGui::InputText("###chat.input", &chat_input); + } + + if(!client_game::hide_hud && ((globals::gui_screen == GUI_SCREEN_NONE) || (globals::gui_screen == GUI_CHAT))) { + for(auto it = history.crbegin(); it < history.crend(); ++it) { + auto text_size = ImGui::CalcTextSize(it->text.c_str(), it->text.c_str() + it->text.size(), false, window_size.x); + auto rect_size = ImVec2(window_size.x, text_size.y + 2.0f * padding.y); + + auto rect_pos = ImVec2(padding.x, ypos - text_size.y - 2.0f * padding.y); + auto rect_end = ImVec2(rect_pos.x + rect_size.x, rect_pos.y + rect_size.y); + auto text_pos = ImVec2(rect_pos.x + padding.x, rect_pos.y + padding.y); + + auto fadeout_seconds = 10.0f; + auto fadeout = std::exp(-1.0f * std::pow(1.0e-6 * static_cast(globals::curtime - it->spawn) / fadeout_seconds, 10.0f)); + + float rect_alpha; + float text_alpha; + + if(globals::gui_screen == GUI_CHAT) { + rect_alpha = 0.75f; + text_alpha = 1.00f; + } + else { + rect_alpha = 0.50f * fadeout; + text_alpha = 1.00f * fadeout; + } + + auto rect_col = ImGui::GetColorU32(ImGuiCol_FrameBg, rect_alpha); + auto text_col = ImGui::GetColorU32(ImVec4(it->color.x, it->color.y, it->color.z, it->color.w * text_alpha)); + auto shadow_col = ImGui::GetColorU32(ImVec4(0.0f, 0.0f, 0.0f, text_alpha)); + + draw_list->AddRectFilled(rect_pos, rect_end, rect_col); + + imdraw_ext::text_shadow_w(it->text, text_pos, text_col, shadow_col, font, draw_list, 8.0f, window_size.x); + + ypos -= rect_size.y; + } + } + + ImGui::End(); + ImGui::PopFont(); +} + +void gui::client_chat::clear(void) +{ + history.clear(); +} + +void gui::client_chat::refresh_timings(void) +{ + for(auto it = history.begin(); it < history.end(); ++it) { + // Reset the spawn time so the fadeout timer + // is reset; SpawnPlayer handler might call this + it->spawn = globals::curtime; + } +} + +void gui::client_chat::print(const std::string& text) +{ + GuiChatMessage message = {}; + message.spawn = globals::curtime; + message.text = text; + message.color = ImGui::GetStyleColorVec4(ImGuiCol_Text); + history.push_back(message); + + if(sfx_chat_message && session::is_ingame()) { + sound::play_ui(sfx_chat_message, false, 1.0f); + } +} diff --git a/game/client/gui/chat.hh b/game/client/gui/chat.hh index 6a3ea33..2fc6fb3 100644 --- a/game/client/gui/chat.hh +++ b/game/client/gui/chat.hh @@ -1,17 +1,17 @@ -#pragma once - -namespace gui::client_chat -{ -void init(void); -void init_late(void); -void shutdown(void); -void update(void); -void layout(void); -} // namespace gui::client_chat - -namespace gui::client_chat -{ -void clear(void); -void refresh_timings(void); -void print(const std::string& string); -} // namespace gui::client_chat +#pragma once + +namespace gui::client_chat +{ +void init(void); +void init_late(void); +void shutdown(void); +void update(void); +void layout(void); +} // namespace gui::client_chat + +namespace gui::client_chat +{ +void clear(void); +void refresh_timings(void); +void print(const std::string& string); +} // namespace gui::client_chat diff --git a/game/client/gui/crosshair.cc b/game/client/gui/crosshair.cc index 29b5fe5..b5650a9 100644 --- a/game/client/gui/crosshair.cc +++ b/game/client/gui/crosshair.cc @@ -1,43 +1,43 @@ -#include "client/pch.hh" - -#include "client/gui/crosshair.hh" - -#include "core/math/constexpr.hh" - -#include "core/resource/resource.hh" - -#include "client/resource/texture_gui.hh" - -#include "client/globals.hh" -#include "client/session.hh" - -static resource_ptr texture; - -void gui::crosshair::init(void) -{ - texture = resource::load( - "textures/gui/hud_crosshair.png", TEXTURE_GUI_LOAD_CLAMP_S | TEXTURE_GUI_LOAD_CLAMP_T | TEXTURE_GUI_LOAD_VFLIP); - - if(texture == nullptr) { - spdlog::critical("crosshair: texture load failed"); - std::terminate(); - } -} - -void gui::crosshair::shutdown(void) -{ - texture = nullptr; -} - -void gui::crosshair::layout(void) -{ - auto viewport = ImGui::GetMainViewport(); - auto draw_list = ImGui::GetForegroundDrawList(); - - auto scaled_width = math::max(texture->size.x, globals::gui_scale * texture->size.x / 2); - auto scaled_height = math::max(texture->size.y, globals::gui_scale * texture->size.y / 2); - auto start = ImVec2( - static_cast(0.5f * viewport->Size.x) - (scaled_width / 2), static_cast(0.5f * viewport->Size.y) - (scaled_height / 2)); - auto end = ImVec2(start.x + scaled_width, start.y + scaled_height); - draw_list->AddImage(texture->handle, start, end); -} +#include "client/pch.hh" + +#include "client/gui/crosshair.hh" + +#include "core/math/constexpr.hh" + +#include "core/resource/resource.hh" + +#include "client/resource/texture_gui.hh" + +#include "client/globals.hh" +#include "client/session.hh" + +static resource_ptr texture; + +void gui::crosshair::init(void) +{ + texture = resource::load("textures/gui/hud_crosshair.png", + TEXTURE_GUI_LOAD_CLAMP_S | TEXTURE_GUI_LOAD_CLAMP_T | TEXTURE_GUI_LOAD_VFLIP); + + if(texture == nullptr) { + spdlog::critical("crosshair: texture load failed"); + std::terminate(); + } +} + +void gui::crosshair::shutdown(void) +{ + texture = nullptr; +} + +void gui::crosshair::layout(void) +{ + auto viewport = ImGui::GetMainViewport(); + auto draw_list = ImGui::GetForegroundDrawList(); + + auto scaled_width = math::max(texture->size.x, globals::gui_scale * texture->size.x / 2); + auto scaled_height = math::max(texture->size.y, globals::gui_scale * texture->size.y / 2); + auto start = ImVec2(static_cast(0.5f * viewport->Size.x) - (scaled_width / 2), + static_cast(0.5f * viewport->Size.y) - (scaled_height / 2)); + auto end = ImVec2(start.x + scaled_width, start.y + scaled_height); + draw_list->AddImage(texture->handle, start, end); +} diff --git a/game/client/gui/crosshair.hh b/game/client/gui/crosshair.hh index 589727e..dca5725 100644 --- a/game/client/gui/crosshair.hh +++ b/game/client/gui/crosshair.hh @@ -1,8 +1,8 @@ -#pragma once - -namespace gui::crosshair -{ -void init(void); -void shutdown(void); -void layout(void); -} // namespace gui::crosshair +#pragma once + +namespace gui::crosshair +{ +void init(void); +void shutdown(void); +void layout(void); +} // namespace gui::crosshair diff --git a/game/client/gui/direct_connection.cc b/game/client/gui/direct_connection.cc index 37372e2..dfbd05b 100644 --- a/game/client/gui/direct_connection.cc +++ b/game/client/gui/direct_connection.cc @@ -1,145 +1,145 @@ -#include "client/pch.hh" - -#include "client/gui/direct_connection.hh" - -#include "core/config/boolean.hh" - -#include "core/utils/string.hh" - -#include "shared/protocol.hh" - -#include "client/gui/gui_screen.hh" -#include "client/gui/language.hh" - -#include "client/io/glfw.hh" - -#include "client/game.hh" -#include "client/globals.hh" -#include "client/session.hh" - -constexpr static ImGuiWindowFlags WINDOW_FLAGS = ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoDecoration; - -static std::string str_title; -static std::string str_connect; -static std::string str_cancel; - -static std::string str_hostname; -static std::string str_password; - -static std::string direct_hostname; -static std::string direct_password; - -static void on_glfw_key(const io::GlfwKeyEvent& event) -{ - if((event.key == GLFW_KEY_ESCAPE) && (event.action == GLFW_PRESS)) { - if(globals::gui_screen == GUI_DIRECT_CONNECTION) { - globals::gui_screen = GUI_PLAY_MENU; - return; - } - } -} - -static void on_language_set(const gui::LanguageSetEvent& event) -{ - str_title = gui::language::resolve("direct_connection.title"); - str_connect = gui::language::resolve_gui("direct_connection.connect"); - str_cancel = gui::language::resolve_gui("direct_connection.cancel"); - - str_hostname = gui::language::resolve("direct_connection.hostname"); - str_password = gui::language::resolve("direct_connection.password"); -} - -static void connect_to_server(void) -{ - auto parts = utils::split(direct_hostname, ":"); - std::string parsed_hostname; - std::uint16_t parsed_port; - - if(!parts[0].empty()) { - parsed_hostname = parts[0]; - } - else { - parsed_hostname = std::string("localhost"); - } - - if(parts.size() >= 2) { - parsed_port = math::clamp(strtoul(parts[1].c_str(), nullptr, 10), 1024, UINT16_MAX); - } - else { - parsed_port = protocol::PORT; - } - - session::connect(parsed_hostname.c_str(), parsed_port, direct_password.c_str()); -} - -void gui::direct_connection::init(void) -{ - globals::dispatcher.sink().connect<&on_glfw_key>(); - globals::dispatcher.sink().connect<&on_language_set>(); -} - -void gui::direct_connection::layout(void) -{ - auto viewport = ImGui::GetMainViewport(); - auto window_start = ImVec2(0.25f * viewport->Size.x, 0.20f * viewport->Size.y); - auto window_size = ImVec2(0.50f * viewport->Size.x, 0.80f * viewport->Size.y); - - ImGui::SetNextWindowPos(window_start); - ImGui::SetNextWindowSize(window_size); - - if(ImGui::Begin("###UIDirectConnect", nullptr, WINDOW_FLAGS)) { - const float title_width = ImGui::CalcTextSize(str_title.c_str()).x; - ImGui::SetCursorPosX(0.5f * (window_size.x - title_width)); - ImGui::TextUnformatted(str_title.c_str()); - - ImGui::Dummy(ImVec2(0.0f, 16.0f * globals::gui_scale)); - - ImGuiInputTextFlags hostname_flags = ImGuiInputTextFlags_CharsNoBlank; - - if(client_game::streamer_mode.get_value()) { - // Hide server hostname to avoid things like - // followers flooding the server that is streamed online - hostname_flags |= ImGuiInputTextFlags_Password; - } - - auto avail_width = ImGui::GetContentRegionAvail().x; - - ImGui::PushItemWidth(avail_width); - - ImGui::InputText("###UIDirectConnect_hostname", &direct_hostname, hostname_flags); - - if(ImGui::BeginItemTooltip()) { - ImGui::PushTextWrapPos(ImGui::GetFontSize() * 16.0f); - ImGui::TextUnformatted(str_hostname.c_str()); - ImGui::PopTextWrapPos(); - ImGui::EndTooltip(); - } - - ImGui::InputText("###UIDirectConnect_password", &direct_password, ImGuiInputTextFlags_Password); - - if(ImGui::BeginItemTooltip()) { - ImGui::PushTextWrapPos(ImGui::GetFontSize() * 16.0f); - ImGui::TextUnformatted(str_password.c_str()); - ImGui::PopTextWrapPos(); - ImGui::EndTooltip(); - } - - ImGui::PopItemWidth(); - - ImGui::Dummy(ImVec2(0.0f, 4.0f * globals::gui_scale)); - - ImGui::BeginDisabled(utils::is_whitespace(direct_hostname)); - - if(ImGui::Button(str_connect.c_str(), ImVec2(avail_width, 0.0f))) { - connect_to_server(); - } - - ImGui::EndDisabled(); - - if(ImGui::Button(str_cancel.c_str(), ImVec2(avail_width, 0.0f))) { - globals::gui_screen = GUI_PLAY_MENU; - } - } - - ImGui::End(); -} +#include "client/pch.hh" + +#include "client/gui/direct_connection.hh" + +#include "core/config/boolean.hh" + +#include "core/utils/string.hh" + +#include "shared/protocol.hh" + +#include "client/gui/gui_screen.hh" +#include "client/gui/language.hh" + +#include "client/io/glfw.hh" + +#include "client/game.hh" +#include "client/globals.hh" +#include "client/session.hh" + +constexpr static ImGuiWindowFlags WINDOW_FLAGS = ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoDecoration; + +static std::string str_title; +static std::string str_connect; +static std::string str_cancel; + +static std::string str_hostname; +static std::string str_password; + +static std::string direct_hostname; +static std::string direct_password; + +static void on_glfw_key(const io::GlfwKeyEvent& event) +{ + if((event.key == GLFW_KEY_ESCAPE) && (event.action == GLFW_PRESS)) { + if(globals::gui_screen == GUI_DIRECT_CONNECTION) { + globals::gui_screen = GUI_PLAY_MENU; + return; + } + } +} + +static void on_language_set(const gui::LanguageSetEvent& event) +{ + str_title = gui::language::resolve("direct_connection.title"); + str_connect = gui::language::resolve_gui("direct_connection.connect"); + str_cancel = gui::language::resolve_gui("direct_connection.cancel"); + + str_hostname = gui::language::resolve("direct_connection.hostname"); + str_password = gui::language::resolve("direct_connection.password"); +} + +static void connect_to_server(void) +{ + auto parts = utils::split(direct_hostname, ":"); + std::string parsed_hostname; + std::uint16_t parsed_port; + + if(!parts[0].empty()) { + parsed_hostname = parts[0]; + } + else { + parsed_hostname = std::string("localhost"); + } + + if(parts.size() >= 2) { + parsed_port = math::clamp(strtoul(parts[1].c_str(), nullptr, 10), 1024, UINT16_MAX); + } + else { + parsed_port = protocol::PORT; + } + + session::connect(parsed_hostname.c_str(), parsed_port, direct_password.c_str()); +} + +void gui::direct_connection::init(void) +{ + globals::dispatcher.sink().connect<&on_glfw_key>(); + globals::dispatcher.sink().connect<&on_language_set>(); +} + +void gui::direct_connection::layout(void) +{ + auto viewport = ImGui::GetMainViewport(); + auto window_start = ImVec2(0.25f * viewport->Size.x, 0.20f * viewport->Size.y); + auto window_size = ImVec2(0.50f * viewport->Size.x, 0.80f * viewport->Size.y); + + ImGui::SetNextWindowPos(window_start); + ImGui::SetNextWindowSize(window_size); + + if(ImGui::Begin("###UIDirectConnect", nullptr, WINDOW_FLAGS)) { + const float title_width = ImGui::CalcTextSize(str_title.c_str()).x; + ImGui::SetCursorPosX(0.5f * (window_size.x - title_width)); + ImGui::TextUnformatted(str_title.c_str()); + + ImGui::Dummy(ImVec2(0.0f, 16.0f * globals::gui_scale)); + + ImGuiInputTextFlags hostname_flags = ImGuiInputTextFlags_CharsNoBlank; + + if(client_game::streamer_mode.get_value()) { + // Hide server hostname to avoid things like + // followers flooding the server that is streamed online + hostname_flags |= ImGuiInputTextFlags_Password; + } + + auto avail_width = ImGui::GetContentRegionAvail().x; + + ImGui::PushItemWidth(avail_width); + + ImGui::InputText("###UIDirectConnect_hostname", &direct_hostname, hostname_flags); + + if(ImGui::BeginItemTooltip()) { + ImGui::PushTextWrapPos(ImGui::GetFontSize() * 16.0f); + ImGui::TextUnformatted(str_hostname.c_str()); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); + } + + ImGui::InputText("###UIDirectConnect_password", &direct_password, ImGuiInputTextFlags_Password); + + if(ImGui::BeginItemTooltip()) { + ImGui::PushTextWrapPos(ImGui::GetFontSize() * 16.0f); + ImGui::TextUnformatted(str_password.c_str()); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); + } + + ImGui::PopItemWidth(); + + ImGui::Dummy(ImVec2(0.0f, 4.0f * globals::gui_scale)); + + ImGui::BeginDisabled(utils::is_whitespace(direct_hostname)); + + if(ImGui::Button(str_connect.c_str(), ImVec2(avail_width, 0.0f))) { + connect_to_server(); + } + + ImGui::EndDisabled(); + + if(ImGui::Button(str_cancel.c_str(), ImVec2(avail_width, 0.0f))) { + globals::gui_screen = GUI_PLAY_MENU; + } + } + + ImGui::End(); +} diff --git a/game/client/gui/direct_connection.hh b/game/client/gui/direct_connection.hh index aa02d7c..c63156b 100644 --- a/game/client/gui/direct_connection.hh +++ b/game/client/gui/direct_connection.hh @@ -1,7 +1,7 @@ -#pragma once - -namespace gui::direct_connection -{ -void init(void); -void layout(void); -} // namespace gui::direct_connection +#pragma once + +namespace gui::direct_connection +{ +void init(void); +void layout(void); +} // namespace gui::direct_connection diff --git a/game/client/gui/gui_screen.hh b/game/client/gui/gui_screen.hh index 2eae310..c29f701 100644 --- a/game/client/gui/gui_screen.hh +++ b/game/client/gui/gui_screen.hh @@ -1,10 +1,10 @@ -#pragma once - -constexpr static unsigned int GUI_SCREEN_NONE = 0x0000U; -constexpr static unsigned int GUI_MAIN_MENU = 0x0001U; -constexpr static unsigned int GUI_PLAY_MENU = 0x0002U; -constexpr static unsigned int GUI_SETTINGS = 0x0003U; -constexpr static unsigned int GUI_PROGRESS_BAR = 0x0004U; -constexpr static unsigned int GUI_MESSAGE_BOX = 0x0005U; -constexpr static unsigned int GUI_CHAT = 0x0006U; -constexpr static unsigned int GUI_DIRECT_CONNECTION = 0x0007U; +#pragma once + +constexpr static unsigned int GUI_SCREEN_NONE = 0x0000U; +constexpr static unsigned int GUI_MAIN_MENU = 0x0001U; +constexpr static unsigned int GUI_PLAY_MENU = 0x0002U; +constexpr static unsigned int GUI_SETTINGS = 0x0003U; +constexpr static unsigned int GUI_PROGRESS_BAR = 0x0004U; +constexpr static unsigned int GUI_MESSAGE_BOX = 0x0005U; +constexpr static unsigned int GUI_CHAT = 0x0006U; +constexpr static unsigned int GUI_DIRECT_CONNECTION = 0x0007U; diff --git a/game/client/gui/hotbar.cc b/game/client/gui/hotbar.cc index ca058a3..806d82b 100644 --- a/game/client/gui/hotbar.cc +++ b/game/client/gui/hotbar.cc @@ -1,183 +1,183 @@ -#include "client/pch.hh" - -#include "client/gui/hotbar.hh" - -#include "core/io/config_map.hh" - -#include "core/resource/resource.hh" - -#include "shared/world/item_registry.hh" - -#include "client/config/keybind.hh" - -#include "client/gui/settings.hh" -#include "client/gui/status_lines.hh" - -#include "client/io/glfw.hh" - -#include "client/resource/texture_gui.hh" - -#include "client/globals.hh" - -constexpr static float ITEM_SIZE = 20.0f; -constexpr static float ITEM_PADDING = 2.0f; -constexpr static float SELECTOR_PADDING = 1.0f; -constexpr static float HOTBAR_PADDING = 2.0f; - -unsigned int gui::hotbar::active_slot = 0U; -item_id gui::hotbar::slots[HOTBAR_SIZE]; - -static config::KeyBind hotbar_keys[HOTBAR_SIZE]; - -static resource_ptr hotbar_background; -static resource_ptr hotbar_selector; - -static ImU32 get_color_alpha(ImGuiCol style_color, float alpha) -{ - const auto& color = ImGui::GetStyleColorVec4(style_color); - return ImGui::GetColorU32(ImVec4(color.x, color.y, color.z, alpha)); -} - -static void update_hotbar_item(void) -{ - if(gui::hotbar::slots[gui::hotbar::active_slot] == NULL_ITEM_ID) { - gui::status_lines::unset(gui::STATUS_HOTBAR); - return; - } - - if(auto info = world::item_registry::find(gui::hotbar::slots[gui::hotbar::active_slot])) { - gui::status_lines::set(gui::STATUS_HOTBAR, info->name, ImVec4(1.0f, 1.0f, 1.0f, 1.0f), 5.0f); - return; - } -} - -static void on_glfw_key(const io::GlfwKeyEvent& event) -{ - if((event.action == GLFW_PRESS) && !globals::gui_screen) { - for(unsigned int i = 0U; i < HOTBAR_SIZE; ++i) { - if(hotbar_keys[i].equals(event.key)) { - gui::hotbar::active_slot = i; - update_hotbar_item(); - break; - } - } - } -} - -static void on_glfw_scroll(const io::GlfwScrollEvent& event) -{ - if(!globals::gui_screen) { - if(event.dy < 0.0) { - gui::hotbar::next_slot(); - return; - } - - if(event.dy > 0.0) { - gui::hotbar::prev_slot(); - return; - } - } -} - -void gui::hotbar::init(void) -{ - hotbar_keys[0].set_key(GLFW_KEY_1); - hotbar_keys[1].set_key(GLFW_KEY_2); - hotbar_keys[2].set_key(GLFW_KEY_3); - hotbar_keys[3].set_key(GLFW_KEY_4); - hotbar_keys[4].set_key(GLFW_KEY_5); - hotbar_keys[5].set_key(GLFW_KEY_6); - hotbar_keys[6].set_key(GLFW_KEY_7); - hotbar_keys[7].set_key(GLFW_KEY_8); - hotbar_keys[8].set_key(GLFW_KEY_9); - - globals::client_config.add_value("hotbar.key.0", hotbar_keys[0]); - globals::client_config.add_value("hotbar.key.1", hotbar_keys[1]); - globals::client_config.add_value("hotbar.key.3", hotbar_keys[2]); - globals::client_config.add_value("hotbar.key.4", hotbar_keys[3]); - globals::client_config.add_value("hotbar.key.5", hotbar_keys[4]); - globals::client_config.add_value("hotbar.key.6", hotbar_keys[5]); - globals::client_config.add_value("hotbar.key.7", hotbar_keys[6]); - globals::client_config.add_value("hotbar.key.8", hotbar_keys[7]); - globals::client_config.add_value("hotbar.key.9", hotbar_keys[8]); - - settings::add_keybind(10, hotbar_keys[0], settings_location::KEYBOARD_GAMEPLAY, "hotbar.slot.0"); - settings::add_keybind(11, hotbar_keys[1], settings_location::KEYBOARD_GAMEPLAY, "hotbar.slot.1"); - settings::add_keybind(12, hotbar_keys[2], settings_location::KEYBOARD_GAMEPLAY, "hotbar.slot.2"); - settings::add_keybind(13, hotbar_keys[3], settings_location::KEYBOARD_GAMEPLAY, "hotbar.slot.3"); - settings::add_keybind(14, hotbar_keys[4], settings_location::KEYBOARD_GAMEPLAY, "hotbar.slot.4"); - settings::add_keybind(15, hotbar_keys[5], settings_location::KEYBOARD_GAMEPLAY, "hotbar.slot.5"); - settings::add_keybind(16, hotbar_keys[6], settings_location::KEYBOARD_GAMEPLAY, "hotbar.slot.6"); - settings::add_keybind(17, hotbar_keys[7], settings_location::KEYBOARD_GAMEPLAY, "hotbar.slot.7"); - settings::add_keybind(18, hotbar_keys[8], settings_location::KEYBOARD_GAMEPLAY, "hotbar.slot.8"); - - hotbar_background = resource::load("textures/gui/hud_hotbar.png", TEXTURE_GUI_LOAD_CLAMP_S | TEXTURE_GUI_LOAD_CLAMP_T); - hotbar_selector = resource::load("textures/gui/hud_selector.png", TEXTURE_GUI_LOAD_CLAMP_S | TEXTURE_GUI_LOAD_CLAMP_T); - - globals::dispatcher.sink().connect<&on_glfw_key>(); - globals::dispatcher.sink().connect<&on_glfw_scroll>(); -} - -void gui::hotbar::shutdown(void) -{ - hotbar_background = nullptr; - hotbar_selector = nullptr; -} - -void gui::hotbar::layout(void) -{ - auto& style = ImGui::GetStyle(); - - auto item_size = ITEM_SIZE * globals::gui_scale; - auto hotbar_width = HOTBAR_SIZE * item_size; - auto hotbar_padding = HOTBAR_PADDING * globals::gui_scale; - - auto viewport = ImGui::GetMainViewport(); - auto draw_list = ImGui::GetForegroundDrawList(); - - // Draw the hotbar background image - auto background_start = ImVec2(0.5f * viewport->Size.x - 0.5f * hotbar_width, viewport->Size.y - item_size - hotbar_padding); - auto background_end = ImVec2(background_start.x + hotbar_width, background_start.y + item_size); - draw_list->AddImage(hotbar_background->handle, background_start, background_end); - - // Draw the hotbar selector image - auto selector_padding_a = SELECTOR_PADDING * globals::gui_scale; - auto selector_padding_b = SELECTOR_PADDING * globals::gui_scale * 2.0f; - auto selector_start = ImVec2( - background_start.x + gui::hotbar::active_slot * item_size - selector_padding_a, background_start.y - selector_padding_a); - auto selector_end = ImVec2(selector_start.x + item_size + selector_padding_b, selector_start.y + item_size + selector_padding_b); - draw_list->AddImage(hotbar_selector->handle, selector_start, selector_end); - - // Figure out item texture padding values - auto item_padding_a = ITEM_PADDING * globals::gui_scale; - auto item_padding_b = ITEM_PADDING * globals::gui_scale * 2.0f; - - // Draw individual item textures in the hotbar - for(std::size_t i = 0; i < HOTBAR_SIZE; ++i) { - const auto info = world::item_registry::find(gui::hotbar::slots[i]); - - if((info == nullptr) || (info->cached_texture == nullptr)) { - // There's either no item in the slot - // or the item doesn't have a texture - continue; - } - - const auto item_start = ImVec2(background_start.x + i * item_size + item_padding_a, background_start.y + item_padding_a); - const auto item_end = ImVec2(item_start.x + item_size - item_padding_b, item_start.y + item_size - item_padding_b); - draw_list->AddImage(info->cached_texture->handle, item_start, item_end); - } -} - -void gui::hotbar::next_slot(void) -{ - gui::hotbar::active_slot += 1U; - gui::hotbar::active_slot %= HOTBAR_SIZE; - update_hotbar_item(); -} - -void gui::hotbar::prev_slot(void) -{ - gui::hotbar::active_slot += HOTBAR_SIZE - 1U; - gui::hotbar::active_slot %= HOTBAR_SIZE; - update_hotbar_item(); -} +#include "client/pch.hh" + +#include "client/gui/hotbar.hh" + +#include "core/io/config_map.hh" + +#include "core/resource/resource.hh" + +#include "shared/world/item_registry.hh" + +#include "client/config/keybind.hh" + +#include "client/gui/settings.hh" +#include "client/gui/status_lines.hh" + +#include "client/io/glfw.hh" + +#include "client/resource/texture_gui.hh" + +#include "client/globals.hh" + +constexpr static float ITEM_SIZE = 20.0f; +constexpr static float ITEM_PADDING = 2.0f; +constexpr static float SELECTOR_PADDING = 1.0f; +constexpr static float HOTBAR_PADDING = 2.0f; + +unsigned int gui::hotbar::active_slot = 0U; +item_id gui::hotbar::slots[HOTBAR_SIZE]; + +static config::KeyBind hotbar_keys[HOTBAR_SIZE]; + +static resource_ptr hotbar_background; +static resource_ptr hotbar_selector; + +static ImU32 get_color_alpha(ImGuiCol style_color, float alpha) +{ + const auto& color = ImGui::GetStyleColorVec4(style_color); + return ImGui::GetColorU32(ImVec4(color.x, color.y, color.z, alpha)); +} + +static void update_hotbar_item(void) +{ + if(gui::hotbar::slots[gui::hotbar::active_slot] == NULL_ITEM_ID) { + gui::status_lines::unset(gui::STATUS_HOTBAR); + return; + } + + if(auto info = world::item_registry::find(gui::hotbar::slots[gui::hotbar::active_slot])) { + gui::status_lines::set(gui::STATUS_HOTBAR, info->name, ImVec4(1.0f, 1.0f, 1.0f, 1.0f), 5.0f); + return; + } +} + +static void on_glfw_key(const io::GlfwKeyEvent& event) +{ + if((event.action == GLFW_PRESS) && !globals::gui_screen) { + for(unsigned int i = 0U; i < HOTBAR_SIZE; ++i) { + if(hotbar_keys[i].equals(event.key)) { + gui::hotbar::active_slot = i; + update_hotbar_item(); + break; + } + } + } +} + +static void on_glfw_scroll(const io::GlfwScrollEvent& event) +{ + if(!globals::gui_screen) { + if(event.dy < 0.0) { + gui::hotbar::next_slot(); + return; + } + + if(event.dy > 0.0) { + gui::hotbar::prev_slot(); + return; + } + } +} + +void gui::hotbar::init(void) +{ + hotbar_keys[0].set_key(GLFW_KEY_1); + hotbar_keys[1].set_key(GLFW_KEY_2); + hotbar_keys[2].set_key(GLFW_KEY_3); + hotbar_keys[3].set_key(GLFW_KEY_4); + hotbar_keys[4].set_key(GLFW_KEY_5); + hotbar_keys[5].set_key(GLFW_KEY_6); + hotbar_keys[6].set_key(GLFW_KEY_7); + hotbar_keys[7].set_key(GLFW_KEY_8); + hotbar_keys[8].set_key(GLFW_KEY_9); + + globals::client_config.add_value("hotbar.key.0", hotbar_keys[0]); + globals::client_config.add_value("hotbar.key.1", hotbar_keys[1]); + globals::client_config.add_value("hotbar.key.3", hotbar_keys[2]); + globals::client_config.add_value("hotbar.key.4", hotbar_keys[3]); + globals::client_config.add_value("hotbar.key.5", hotbar_keys[4]); + globals::client_config.add_value("hotbar.key.6", hotbar_keys[5]); + globals::client_config.add_value("hotbar.key.7", hotbar_keys[6]); + globals::client_config.add_value("hotbar.key.8", hotbar_keys[7]); + globals::client_config.add_value("hotbar.key.9", hotbar_keys[8]); + + settings::add_keybind(10, hotbar_keys[0], settings_location::KEYBOARD_GAMEPLAY, "hotbar.slot.0"); + settings::add_keybind(11, hotbar_keys[1], settings_location::KEYBOARD_GAMEPLAY, "hotbar.slot.1"); + settings::add_keybind(12, hotbar_keys[2], settings_location::KEYBOARD_GAMEPLAY, "hotbar.slot.2"); + settings::add_keybind(13, hotbar_keys[3], settings_location::KEYBOARD_GAMEPLAY, "hotbar.slot.3"); + settings::add_keybind(14, hotbar_keys[4], settings_location::KEYBOARD_GAMEPLAY, "hotbar.slot.4"); + settings::add_keybind(15, hotbar_keys[5], settings_location::KEYBOARD_GAMEPLAY, "hotbar.slot.5"); + settings::add_keybind(16, hotbar_keys[6], settings_location::KEYBOARD_GAMEPLAY, "hotbar.slot.6"); + settings::add_keybind(17, hotbar_keys[7], settings_location::KEYBOARD_GAMEPLAY, "hotbar.slot.7"); + settings::add_keybind(18, hotbar_keys[8], settings_location::KEYBOARD_GAMEPLAY, "hotbar.slot.8"); + + hotbar_background = resource::load("textures/gui/hud_hotbar.png", TEXTURE_GUI_LOAD_CLAMP_S | TEXTURE_GUI_LOAD_CLAMP_T); + hotbar_selector = resource::load("textures/gui/hud_selector.png", TEXTURE_GUI_LOAD_CLAMP_S | TEXTURE_GUI_LOAD_CLAMP_T); + + globals::dispatcher.sink().connect<&on_glfw_key>(); + globals::dispatcher.sink().connect<&on_glfw_scroll>(); +} + +void gui::hotbar::shutdown(void) +{ + hotbar_background = nullptr; + hotbar_selector = nullptr; +} + +void gui::hotbar::layout(void) +{ + auto& style = ImGui::GetStyle(); + + auto item_size = ITEM_SIZE * globals::gui_scale; + auto hotbar_width = HOTBAR_SIZE * item_size; + auto hotbar_padding = HOTBAR_PADDING * globals::gui_scale; + + auto viewport = ImGui::GetMainViewport(); + auto draw_list = ImGui::GetForegroundDrawList(); + + // Draw the hotbar background image + auto background_start = ImVec2(0.5f * viewport->Size.x - 0.5f * hotbar_width, viewport->Size.y - item_size - hotbar_padding); + auto background_end = ImVec2(background_start.x + hotbar_width, background_start.y + item_size); + draw_list->AddImage(hotbar_background->handle, background_start, background_end); + + // Draw the hotbar selector image + auto selector_padding_a = SELECTOR_PADDING * globals::gui_scale; + auto selector_padding_b = SELECTOR_PADDING * globals::gui_scale * 2.0f; + auto selector_start = ImVec2(background_start.x + gui::hotbar::active_slot * item_size - selector_padding_a, + background_start.y - selector_padding_a); + auto selector_end = ImVec2(selector_start.x + item_size + selector_padding_b, selector_start.y + item_size + selector_padding_b); + draw_list->AddImage(hotbar_selector->handle, selector_start, selector_end); + + // Figure out item texture padding values + auto item_padding_a = ITEM_PADDING * globals::gui_scale; + auto item_padding_b = ITEM_PADDING * globals::gui_scale * 2.0f; + + // Draw individual item textures in the hotbar + for(std::size_t i = 0; i < HOTBAR_SIZE; ++i) { + const auto info = world::item_registry::find(gui::hotbar::slots[i]); + + if((info == nullptr) || (info->cached_texture == nullptr)) { + // There's either no item in the slot + // or the item doesn't have a texture + continue; + } + + const auto item_start = ImVec2(background_start.x + i * item_size + item_padding_a, background_start.y + item_padding_a); + const auto item_end = ImVec2(item_start.x + item_size - item_padding_b, item_start.y + item_size - item_padding_b); + draw_list->AddImage(info->cached_texture->handle, item_start, item_end); + } +} + +void gui::hotbar::next_slot(void) +{ + gui::hotbar::active_slot += 1U; + gui::hotbar::active_slot %= HOTBAR_SIZE; + update_hotbar_item(); +} + +void gui::hotbar::prev_slot(void) +{ + gui::hotbar::active_slot += HOTBAR_SIZE - 1U; + gui::hotbar::active_slot %= HOTBAR_SIZE; + update_hotbar_item(); +} diff --git a/game/client/gui/hotbar.hh b/game/client/gui/hotbar.hh index 85d75aa..88ce791 100644 --- a/game/client/gui/hotbar.hh +++ b/game/client/gui/hotbar.hh @@ -1,27 +1,27 @@ -#pragma once - -#include "shared/types.hh" - -// TODO: design an inventory system and an item -// registry and integrate the hotbar into that system - -constexpr static unsigned int HOTBAR_SIZE = 9U; - -namespace gui::hotbar -{ -extern unsigned int active_slot; -extern item_id slots[HOTBAR_SIZE]; -} // namespace gui::hotbar - -namespace gui::hotbar -{ -void init(void); -void shutdown(void); -void layout(void); -} // namespace gui::hotbar - -namespace gui::hotbar -{ -void next_slot(void); -void prev_slot(void); -} // namespace gui::hotbar +#pragma once + +#include "shared/types.hh" + +// TODO: design an inventory system and an item +// registry and integrate the hotbar into that system + +constexpr static unsigned int HOTBAR_SIZE = 9U; + +namespace gui::hotbar +{ +extern unsigned int active_slot; +extern item_id slots[HOTBAR_SIZE]; +} // namespace gui::hotbar + +namespace gui::hotbar +{ +void init(void); +void shutdown(void); +void layout(void); +} // namespace gui::hotbar + +namespace gui::hotbar +{ +void next_slot(void); +void prev_slot(void); +} // namespace gui::hotbar diff --git a/game/client/gui/imdraw_ext.cc b/game/client/gui/imdraw_ext.cc index 832f284..c3d40c9 100644 --- a/game/client/gui/imdraw_ext.cc +++ b/game/client/gui/imdraw_ext.cc @@ -1,34 +1,34 @@ -#include "client/pch.hh" - -#include "client/gui/imdraw_ext.hh" - -#include "client/globals.hh" - -void gui::imdraw_ext::text_shadow( - const std::string& text, const ImVec2& position, ImU32 text_color, ImU32 shadow_color, ImFont* font, ImDrawList* draw_list) -{ - imdraw_ext::text_shadow(text, position, text_color, shadow_color, font, draw_list, font->LegacySize); -} - -void gui::imdraw_ext::text_shadow(const std::string& text, const ImVec2& position, ImU32 text_color, ImU32 shadow_color, ImFont* font, - ImDrawList* draw_list, float font_size) -{ - const auto shadow_position = ImVec2(position.x + 0.5f * globals::gui_scale, position.y + 0.5f * globals::gui_scale); - draw_list->AddText(font, globals::gui_scale * font_size, shadow_position, shadow_color, text.c_str(), text.c_str() + text.size()); - draw_list->AddText(font, globals::gui_scale * font_size, position, text_color, text.c_str(), text.c_str() + text.size()); -} - -void gui::imdraw_ext::text_shadow_w(const std::string& text, const ImVec2& position, ImU32 text_color, ImU32 shadow_color, ImFont* font, - ImDrawList* draw_list, float wrap_width) -{ - imdraw_ext::text_shadow_w(text, position, text_color, shadow_color, font, draw_list, font->LegacySize, wrap_width); -} - -void gui::imdraw_ext::text_shadow_w(const std::string& text, const ImVec2& position, ImU32 text_color, ImU32 shadow_color, ImFont* font, - ImDrawList* draw_list, float font_size, float wrap_width) -{ - const auto shadow_position = ImVec2(position.x + 0.5f * globals::gui_scale, position.y + 0.5f * globals::gui_scale); - draw_list->AddText( - font, globals::gui_scale * font_size, shadow_position, shadow_color, text.c_str(), text.c_str() + text.size(), wrap_width); - draw_list->AddText(font, globals::gui_scale * font_size, position, text_color, text.c_str(), text.c_str() + text.size(), wrap_width); -} +#include "client/pch.hh" + +#include "client/gui/imdraw_ext.hh" + +#include "client/globals.hh" + +void gui::imdraw_ext::text_shadow(const std::string& text, const ImVec2& position, ImU32 text_color, ImU32 shadow_color, ImFont* font, + ImDrawList* draw_list) +{ + imdraw_ext::text_shadow(text, position, text_color, shadow_color, font, draw_list, font->LegacySize); +} + +void gui::imdraw_ext::text_shadow(const std::string& text, const ImVec2& position, ImU32 text_color, ImU32 shadow_color, ImFont* font, + ImDrawList* draw_list, float font_size) +{ + const auto shadow_position = ImVec2(position.x + 0.5f * globals::gui_scale, position.y + 0.5f * globals::gui_scale); + draw_list->AddText(font, globals::gui_scale * font_size, shadow_position, shadow_color, text.c_str(), text.c_str() + text.size()); + draw_list->AddText(font, globals::gui_scale * font_size, position, text_color, text.c_str(), text.c_str() + text.size()); +} + +void gui::imdraw_ext::text_shadow_w(const std::string& text, const ImVec2& position, ImU32 text_color, ImU32 shadow_color, ImFont* font, + ImDrawList* draw_list, float wrap_width) +{ + imdraw_ext::text_shadow_w(text, position, text_color, shadow_color, font, draw_list, font->LegacySize, wrap_width); +} + +void gui::imdraw_ext::text_shadow_w(const std::string& text, const ImVec2& position, ImU32 text_color, ImU32 shadow_color, ImFont* font, + ImDrawList* draw_list, float font_size, float wrap_width) +{ + const auto shadow_position = ImVec2(position.x + 0.5f * globals::gui_scale, position.y + 0.5f * globals::gui_scale); + draw_list->AddText(font, globals::gui_scale * font_size, shadow_position, shadow_color, text.c_str(), text.c_str() + text.size(), + wrap_width); + draw_list->AddText(font, globals::gui_scale * font_size, position, text_color, text.c_str(), text.c_str() + text.size(), wrap_width); +} diff --git a/game/client/gui/imdraw_ext.hh b/game/client/gui/imdraw_ext.hh index a7e1503..e1475b2 100644 --- a/game/client/gui/imdraw_ext.hh +++ b/game/client/gui/imdraw_ext.hh @@ -1,17 +1,17 @@ -#pragma once - -namespace gui::imdraw_ext -{ -void text_shadow(const std::string& text, const ImVec2& position, ImU32 text_color, ImU32 shadow_color, ImFont* font, - ImDrawList* draw_list); -void text_shadow(const std::string& text, const ImVec2& position, ImU32 text_color, ImU32 shadow_color, ImFont* font, ImDrawList* draw_list, - float font_size); -} // namespace gui::imdraw_ext - -namespace gui::imdraw_ext -{ -void text_shadow_w(const std::string& text, const ImVec2& position, ImU32 text_color, ImU32 shadow_color, ImFont* font, - ImDrawList* draw_list, float wrap_width); -void text_shadow_w(const std::string& text, const ImVec2& position, ImU32 text_color, ImU32 shadow_color, ImFont* font, - ImDrawList* draw_list, float font_size, float wrap_width); -} // namespace gui::imdraw_ext +#pragma once + +namespace gui::imdraw_ext +{ +void text_shadow(const std::string& text, const ImVec2& position, ImU32 text_color, ImU32 shadow_color, ImFont* font, + ImDrawList* draw_list); +void text_shadow(const std::string& text, const ImVec2& position, ImU32 text_color, ImU32 shadow_color, ImFont* font, ImDrawList* draw_list, + float font_size); +} // namespace gui::imdraw_ext + +namespace gui::imdraw_ext +{ +void text_shadow_w(const std::string& text, const ImVec2& position, ImU32 text_color, ImU32 shadow_color, ImFont* font, + ImDrawList* draw_list, float wrap_width); +void text_shadow_w(const std::string& text, const ImVec2& position, ImU32 text_color, ImU32 shadow_color, ImFont* font, + ImDrawList* draw_list, float font_size, float wrap_width); +} // namespace gui::imdraw_ext diff --git a/game/client/gui/language.cc b/game/client/gui/language.cc index f63dd99..57a43fe 100644 --- a/game/client/gui/language.cc +++ b/game/client/gui/language.cc @@ -1,201 +1,201 @@ -#include "client/pch.hh" - -#include "client/gui/language.hh" - -#include "core/config/string.hh" - -#include "core/io/config_map.hh" - -#include "client/gui/settings.hh" - -#include "client/globals.hh" - -constexpr static std::string_view DEFAULT_LANGUAGE = "en_US"; - -// Available languages are kept in a special manifest file which -// is essentially a key-value map of semi-IETF-compliant language tags -// and the language's endonym; after reading the manifest, the translation -// system knows what language it can load and will act accordingly -constexpr static std::string_view MANIFEST_PATH = "lang/manifest.json"; - -static gui::LanguageManifest manifest; -static gui::LanguageIterator current_language; -static std::unordered_map language_map; -static std::unordered_map ietf_map; -static config::String config_language(DEFAULT_LANGUAGE); - -static void send_language_event(gui::LanguageIterator new_language) -{ - gui::LanguageSetEvent event; - event.new_language = new_language; - globals::dispatcher.trigger(event); -} - -void gui::language::init(void) -{ - globals::client_config.add_value("language", config_language); - - settings::add_language_select(0, settings_location::GENERAL, "language"); - - auto file = PHYSFS_openRead(std::string(MANIFEST_PATH).c_str()); - - if(file == nullptr) { - spdlog::critical("language: {}: {}", MANIFEST_PATH, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); - std::terminate(); - } - - auto source = std::string(PHYSFS_fileLength(file), char(0x00)); - PHYSFS_readBytes(file, source.data(), source.size()); - PHYSFS_close(file); - - auto jsonv = json_parse_string(source.c_str()); - const auto json = json_value_get_object(jsonv); - const auto count = json_object_get_count(json); - - if((jsonv == nullptr) || (json == nullptr) || (count == 0)) { - spdlog::critical("language: {}: parse error", MANIFEST_PATH); - json_value_free(jsonv); - std::terminate(); - } - - for(std::size_t i = 0; i < count; ++i) { - const auto ietf = json_object_get_name(json, i); - const auto value = json_object_get_value_at(json, i); - const auto endonym = json_value_get_string(value); - - if(ietf && endonym) { - LanguageInfo info; - info.ietf = std::string(ietf); - info.endonym = std::string(endonym); - info.display = std::format("{} ({})", endonym, ietf); - manifest.push_back(info); - } - } - - for(auto it = manifest.cbegin(); it != manifest.cend(); ++it) { - ietf_map.emplace(it->ietf, it); - } - - json_value_free(jsonv); - - // This is temporary! This value will - // be overriden in init_late after the - // config system updates config_language - current_language = manifest.cend(); -} - -void gui::language::init_late(void) -{ - auto user_language = ietf_map.find(config_language.get_value()); - - if(user_language != ietf_map.cend()) { - gui::language::set(user_language->second); - return; - } - - auto fallback = ietf_map.find(std::string(DEFAULT_LANGUAGE)); - - if(fallback != ietf_map.cend()) { - gui::language::set(fallback->second); - return; - } - - spdlog::critical("language: we're doomed!"); - spdlog::critical("language: {} doesn't exist!", DEFAULT_LANGUAGE); - std::terminate(); -} - -void gui::language::set(LanguageIterator new_language) -{ - if(new_language != manifest.cend()) { - auto path = std::format("lang/lang.{}.json", new_language->ietf); - - auto file = PHYSFS_openRead(path.c_str()); - - if(file == nullptr) { - spdlog::warn("language: {}: {}", path, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); - send_language_event(new_language); - return; - } - - auto source = std::string(PHYSFS_fileLength(file), char(0x00)); - PHYSFS_readBytes(file, source.data(), source.size()); - PHYSFS_close(file); - - auto jsonv = json_parse_string(source.c_str()); - const auto json = json_value_get_object(jsonv); - const auto count = json_object_get_count(json); - - if((jsonv == nullptr) || (json == nullptr) || (count == 0)) { - spdlog::warn("language: {}: parse error", path); - send_language_event(new_language); - json_value_free(jsonv); - return; - } - - language_map.clear(); - - for(size_t i = 0; i < count; ++i) { - const auto key = json_object_get_name(json, i); - const auto value = json_object_get_value_at(json, i); - const auto value_str = json_value_get_string(value); - - if(key && value_str) { - language_map.emplace(key, value_str); - continue; - } - } - - json_value_free(jsonv); - - current_language = new_language; - config_language.set(new_language->ietf.c_str()); - } - - send_language_event(new_language); -} - -gui::LanguageIterator gui::language::get_current(void) -{ - return current_language; -} - -gui::LanguageIterator gui::language::find(std::string_view ietf) -{ - const auto it = ietf_map.find(std::string(ietf)); - if(it != ietf_map.cend()) { - return it->second; - } - else { - return manifest.cend(); - } -} - -gui::LanguageIterator gui::language::cbegin(void) -{ - return manifest.cbegin(); -} - -gui::LanguageIterator gui::language::cend(void) -{ - return manifest.cend(); -} - -std::string_view gui::language::resolve(std::string_view key) -{ - const auto it = language_map.find(std::string(key)); - - if(it != language_map.cend()) { - return it->second; - } - - return key; -} - -std::string gui::language::resolve_gui(std::string_view key) -{ - // We need window tags to retain their hierarchy when a language - // dynamically changes; ImGui allows to provide hidden unique identifiers - // to GUI primitives that have their name change dynamically, so we're using this - return std::format("{}###{}", gui::language::resolve(key), key); -} +#include "client/pch.hh" + +#include "client/gui/language.hh" + +#include "core/config/string.hh" + +#include "core/io/config_map.hh" + +#include "client/gui/settings.hh" + +#include "client/globals.hh" + +constexpr static std::string_view DEFAULT_LANGUAGE = "en_US"; + +// Available languages are kept in a special manifest file which +// is essentially a key-value map of semi-IETF-compliant language tags +// and the language's endonym; after reading the manifest, the translation +// system knows what language it can load and will act accordingly +constexpr static std::string_view MANIFEST_PATH = "lang/manifest.json"; + +static gui::LanguageManifest manifest; +static gui::LanguageIterator current_language; +static std::unordered_map language_map; +static std::unordered_map ietf_map; +static config::String config_language(DEFAULT_LANGUAGE); + +static void send_language_event(gui::LanguageIterator new_language) +{ + gui::LanguageSetEvent event; + event.new_language = new_language; + globals::dispatcher.trigger(event); +} + +void gui::language::init(void) +{ + globals::client_config.add_value("language", config_language); + + settings::add_language_select(0, settings_location::GENERAL, "language"); + + auto file = PHYSFS_openRead(std::string(MANIFEST_PATH).c_str()); + + if(file == nullptr) { + spdlog::critical("language: {}: {}", MANIFEST_PATH, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + std::terminate(); + } + + auto source = std::string(PHYSFS_fileLength(file), char(0x00)); + PHYSFS_readBytes(file, source.data(), source.size()); + PHYSFS_close(file); + + auto jsonv = json_parse_string(source.c_str()); + const auto json = json_value_get_object(jsonv); + const auto count = json_object_get_count(json); + + if((jsonv == nullptr) || (json == nullptr) || (count == 0)) { + spdlog::critical("language: {}: parse error", MANIFEST_PATH); + json_value_free(jsonv); + std::terminate(); + } + + for(std::size_t i = 0; i < count; ++i) { + const auto ietf = json_object_get_name(json, i); + const auto value = json_object_get_value_at(json, i); + const auto endonym = json_value_get_string(value); + + if(ietf && endonym) { + LanguageInfo info; + info.ietf = std::string(ietf); + info.endonym = std::string(endonym); + info.display = std::format("{} ({})", endonym, ietf); + manifest.push_back(info); + } + } + + for(auto it = manifest.cbegin(); it != manifest.cend(); ++it) { + ietf_map.emplace(it->ietf, it); + } + + json_value_free(jsonv); + + // This is temporary! This value will + // be overriden in init_late after the + // config system updates config_language + current_language = manifest.cend(); +} + +void gui::language::init_late(void) +{ + auto user_language = ietf_map.find(config_language.get_value()); + + if(user_language != ietf_map.cend()) { + gui::language::set(user_language->second); + return; + } + + auto fallback = ietf_map.find(std::string(DEFAULT_LANGUAGE)); + + if(fallback != ietf_map.cend()) { + gui::language::set(fallback->second); + return; + } + + spdlog::critical("language: we're doomed!"); + spdlog::critical("language: {} doesn't exist!", DEFAULT_LANGUAGE); + std::terminate(); +} + +void gui::language::set(LanguageIterator new_language) +{ + if(new_language != manifest.cend()) { + auto path = std::format("lang/lang.{}.json", new_language->ietf); + + auto file = PHYSFS_openRead(path.c_str()); + + if(file == nullptr) { + spdlog::warn("language: {}: {}", path, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + send_language_event(new_language); + return; + } + + auto source = std::string(PHYSFS_fileLength(file), char(0x00)); + PHYSFS_readBytes(file, source.data(), source.size()); + PHYSFS_close(file); + + auto jsonv = json_parse_string(source.c_str()); + const auto json = json_value_get_object(jsonv); + const auto count = json_object_get_count(json); + + if((jsonv == nullptr) || (json == nullptr) || (count == 0)) { + spdlog::warn("language: {}: parse error", path); + send_language_event(new_language); + json_value_free(jsonv); + return; + } + + language_map.clear(); + + for(size_t i = 0; i < count; ++i) { + const auto key = json_object_get_name(json, i); + const auto value = json_object_get_value_at(json, i); + const auto value_str = json_value_get_string(value); + + if(key && value_str) { + language_map.emplace(key, value_str); + continue; + } + } + + json_value_free(jsonv); + + current_language = new_language; + config_language.set(new_language->ietf.c_str()); + } + + send_language_event(new_language); +} + +gui::LanguageIterator gui::language::get_current(void) +{ + return current_language; +} + +gui::LanguageIterator gui::language::find(std::string_view ietf) +{ + const auto it = ietf_map.find(std::string(ietf)); + if(it != ietf_map.cend()) { + return it->second; + } + else { + return manifest.cend(); + } +} + +gui::LanguageIterator gui::language::cbegin(void) +{ + return manifest.cbegin(); +} + +gui::LanguageIterator gui::language::cend(void) +{ + return manifest.cend(); +} + +std::string_view gui::language::resolve(std::string_view key) +{ + const auto it = language_map.find(std::string(key)); + + if(it != language_map.cend()) { + return it->second; + } + + return key; +} + +std::string gui::language::resolve_gui(std::string_view key) +{ + // We need window tags to retain their hierarchy when a language + // dynamically changes; ImGui allows to provide hidden unique identifiers + // to GUI primitives that have their name change dynamically, so we're using this + return std::format("{}###{}", gui::language::resolve(key), key); +} diff --git a/game/client/gui/language.hh b/game/client/gui/language.hh index 90132d7..0628941 100644 --- a/game/client/gui/language.hh +++ b/game/client/gui/language.hh @@ -1,42 +1,42 @@ -#pragma once - -namespace gui -{ -struct LanguageInfo final { - std::string endonym; // Language's self-name - std::string display; // Display for the settings GUI - std::string ietf; // Semi-compliant language abbreviation -}; - -using LanguageManifest = std::vector; -using LanguageIterator = LanguageManifest::const_iterator; - -struct LanguageSetEvent final { - LanguageIterator new_language; -}; -} // namespace gui - -namespace gui::language -{ -void init(void); -void init_late(void); -} // namespace gui::language - -namespace gui::language -{ -void set(LanguageIterator new_language); -} // namespace gui::language - -namespace gui::language -{ -LanguageIterator get_current(void); -LanguageIterator find(std::string_view ietf); -LanguageIterator cbegin(void); -LanguageIterator cend(void); -} // namespace gui::language - -namespace gui::language -{ -std::string_view resolve(std::string_view key); -std::string resolve_gui(std::string_view key); -} // namespace gui::language +#pragma once + +namespace gui +{ +struct LanguageInfo final { + std::string endonym; // Language's self-name + std::string display; // Display for the settings GUI + std::string ietf; // Semi-compliant language abbreviation +}; + +using LanguageManifest = std::vector; +using LanguageIterator = LanguageManifest::const_iterator; + +struct LanguageSetEvent final { + LanguageIterator new_language; +}; +} // namespace gui + +namespace gui::language +{ +void init(void); +void init_late(void); +} // namespace gui::language + +namespace gui::language +{ +void set(LanguageIterator new_language); +} // namespace gui::language + +namespace gui::language +{ +LanguageIterator get_current(void); +LanguageIterator find(std::string_view ietf); +LanguageIterator cbegin(void); +LanguageIterator cend(void); +} // namespace gui::language + +namespace gui::language +{ +std::string_view resolve(std::string_view key); +std::string resolve_gui(std::string_view key); +} // namespace gui::language diff --git a/game/client/gui/main_menu.cc b/game/client/gui/main_menu.cc index 3c68612..aa506d3 100644 --- a/game/client/gui/main_menu.cc +++ b/game/client/gui/main_menu.cc @@ -1,172 +1,172 @@ -#include "client/pch.hh" - -#include "client/gui/main_menu.hh" - -#include "core/math/constexpr.hh" - -#include "core/resource/resource.hh" - -#include "core/version.hh" - -#include "client/gui/gui_screen.hh" -#include "client/gui/language.hh" -#include "client/gui/window_title.hh" - -#include "client/io/glfw.hh" - -#include "client/resource/texture_gui.hh" - -#include "client/globals.hh" -#include "client/session.hh" - -constexpr static ImGuiWindowFlags WINDOW_FLAGS = ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoDecoration; - -static std::string str_play; -static std::string str_resume; -static std::string str_settings; -static std::string str_leave; -static std::string str_quit; - -static resource_ptr title; -static float title_aspect; - -static void on_glfw_key(const io::GlfwKeyEvent& event) -{ - if(session::is_ingame() && (event.key == GLFW_KEY_ESCAPE) && (event.action == GLFW_PRESS)) { - if(globals::gui_screen == GUI_SCREEN_NONE) { - globals::gui_screen = GUI_MAIN_MENU; - return; - } - - if(globals::gui_screen == GUI_MAIN_MENU) { - globals::gui_screen = GUI_SCREEN_NONE; - return; - } - } -} - -static void on_language_set(const gui::LanguageSetEvent& event) -{ - str_play = gui::language::resolve_gui("main_menu.play"); - str_resume = gui::language::resolve_gui("main_menu.resume"); - str_settings = gui::language::resolve("main_menu.settings"); - str_leave = gui::language::resolve("main_menu.leave"); - str_quit = gui::language::resolve("main_menu.quit"); -} - -void gui::main_menu::init(void) -{ - title = resource::load("textures/gui/menu_title.png", TEXTURE_GUI_LOAD_CLAMP_S | TEXTURE_GUI_LOAD_CLAMP_T); - - if(title == nullptr) { - spdlog::critical("main_menu: texture load failed"); - std::terminate(); - } - - if(title->size.x > title->size.y) { - title_aspect = static_cast(title->size.x) / static_cast(title->size.y); - } - else { - title_aspect = static_cast(title->size.y) / static_cast(title->size.x); - } - - globals::dispatcher.sink().connect<&on_glfw_key>(); - globals::dispatcher.sink().connect<&on_language_set>(); -} - -void gui::main_menu::shutdown(void) -{ - title = nullptr; -} - -void gui::main_menu::layout(void) -{ - const auto viewport = ImGui::GetMainViewport(); - const auto window_start = ImVec2(0.0f, viewport->Size.y * 0.15f); - const auto window_size = ImVec2(viewport->Size.x, viewport->Size.y); - - ImGui::SetNextWindowPos(window_start); - ImGui::SetNextWindowSize(window_size); - - if(ImGui::Begin("###main_menu", nullptr, WINDOW_FLAGS)) { - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 2.0f * globals::gui_scale)); - - if(session::is_ingame()) { - ImGui::Dummy(ImVec2(0.0f, 32.0f * globals::gui_scale)); - } - else { - auto reference_height = 0.225f * window_size.y; - auto image_width = math::min(window_size.x, reference_height * title_aspect); - auto image_height = image_width / title_aspect; - ImGui::SetCursorPosX(0.5f * (window_size.x - image_width)); - ImGui::Image(title->handle, ImVec2(image_width, image_height)); - } - - ImGui::Dummy(ImVec2(0.0f, 24.0f * globals::gui_scale)); - - const float button_width = 240.0f * globals::gui_scale; - const float button_xpos = 0.5f * (window_size.x - button_width); - - if(session::is_ingame()) { - ImGui::SetCursorPosX(button_xpos); - - if(ImGui::Button(str_resume.c_str(), ImVec2(button_width, 0.0f))) { - globals::gui_screen = GUI_SCREEN_NONE; - } - - ImGui::Spacing(); - } - else { - ImGui::SetCursorPosX(button_xpos); - - if(ImGui::Button(str_play.c_str(), ImVec2(button_width, 0.0f))) { - globals::gui_screen = GUI_PLAY_MENU; - } - - ImGui::Spacing(); - } - - ImGui::SetCursorPosX(button_xpos); - - if(ImGui::Button(str_settings.c_str(), ImVec2(button_width, 0.0f))) { - globals::gui_screen = GUI_SETTINGS; - } - - ImGui::Spacing(); - - if(session::is_ingame()) { - ImGui::SetCursorPosX(button_xpos); - - if(ImGui::Button(str_leave.c_str(), ImVec2(button_width, 0.0f))) { - session::disconnect("protocol.client_disconnect"); - globals::gui_screen = GUI_PLAY_MENU; - gui::window_title::update(); - } - - ImGui::Spacing(); - } - else { - ImGui::SetCursorPosX(button_xpos); - - if(ImGui::Button(str_quit.c_str(), ImVec2(button_width, 0.0f))) { - glfwSetWindowShouldClose(globals::window, true); - } - - ImGui::Spacing(); - } - - if(!session::is_ingame()) { - const auto& padding = ImGui::GetStyle().FramePadding; - const auto& spacing = ImGui::GetStyle().ItemSpacing; - - ImGui::PushFont(globals::font_unscii8, 4.0f); - ImGui::SetCursorScreenPos(ImVec2(padding.x + spacing.x, window_size.y - ImGui::GetFontSize() - padding.y - spacing.y)); - ImGui::Text("Voxelius %*s", version::semver.size(), version::semver.data()); // string_view is not always null-terminated - ImGui::PopFont(); - } - - ImGui::PopStyleVar(); - } - - ImGui::End(); -} +#include "client/pch.hh" + +#include "client/gui/main_menu.hh" + +#include "core/math/constexpr.hh" + +#include "core/resource/resource.hh" + +#include "core/version.hh" + +#include "client/gui/gui_screen.hh" +#include "client/gui/language.hh" +#include "client/gui/window_title.hh" + +#include "client/io/glfw.hh" + +#include "client/resource/texture_gui.hh" + +#include "client/globals.hh" +#include "client/session.hh" + +constexpr static ImGuiWindowFlags WINDOW_FLAGS = ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoDecoration; + +static std::string str_play; +static std::string str_resume; +static std::string str_settings; +static std::string str_leave; +static std::string str_quit; + +static resource_ptr title; +static float title_aspect; + +static void on_glfw_key(const io::GlfwKeyEvent& event) +{ + if(session::is_ingame() && (event.key == GLFW_KEY_ESCAPE) && (event.action == GLFW_PRESS)) { + if(globals::gui_screen == GUI_SCREEN_NONE) { + globals::gui_screen = GUI_MAIN_MENU; + return; + } + + if(globals::gui_screen == GUI_MAIN_MENU) { + globals::gui_screen = GUI_SCREEN_NONE; + return; + } + } +} + +static void on_language_set(const gui::LanguageSetEvent& event) +{ + str_play = gui::language::resolve_gui("main_menu.play"); + str_resume = gui::language::resolve_gui("main_menu.resume"); + str_settings = gui::language::resolve("main_menu.settings"); + str_leave = gui::language::resolve("main_menu.leave"); + str_quit = gui::language::resolve("main_menu.quit"); +} + +void gui::main_menu::init(void) +{ + title = resource::load("textures/gui/menu_title.png", TEXTURE_GUI_LOAD_CLAMP_S | TEXTURE_GUI_LOAD_CLAMP_T); + + if(title == nullptr) { + spdlog::critical("main_menu: texture load failed"); + std::terminate(); + } + + if(title->size.x > title->size.y) { + title_aspect = static_cast(title->size.x) / static_cast(title->size.y); + } + else { + title_aspect = static_cast(title->size.y) / static_cast(title->size.x); + } + + globals::dispatcher.sink().connect<&on_glfw_key>(); + globals::dispatcher.sink().connect<&on_language_set>(); +} + +void gui::main_menu::shutdown(void) +{ + title = nullptr; +} + +void gui::main_menu::layout(void) +{ + const auto viewport = ImGui::GetMainViewport(); + const auto window_start = ImVec2(0.0f, viewport->Size.y * 0.15f); + const auto window_size = ImVec2(viewport->Size.x, viewport->Size.y); + + ImGui::SetNextWindowPos(window_start); + ImGui::SetNextWindowSize(window_size); + + if(ImGui::Begin("###main_menu", nullptr, WINDOW_FLAGS)) { + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 2.0f * globals::gui_scale)); + + if(session::is_ingame()) { + ImGui::Dummy(ImVec2(0.0f, 32.0f * globals::gui_scale)); + } + else { + auto reference_height = 0.225f * window_size.y; + auto image_width = math::min(window_size.x, reference_height * title_aspect); + auto image_height = image_width / title_aspect; + ImGui::SetCursorPosX(0.5f * (window_size.x - image_width)); + ImGui::Image(title->handle, ImVec2(image_width, image_height)); + } + + ImGui::Dummy(ImVec2(0.0f, 24.0f * globals::gui_scale)); + + const float button_width = 240.0f * globals::gui_scale; + const float button_xpos = 0.5f * (window_size.x - button_width); + + if(session::is_ingame()) { + ImGui::SetCursorPosX(button_xpos); + + if(ImGui::Button(str_resume.c_str(), ImVec2(button_width, 0.0f))) { + globals::gui_screen = GUI_SCREEN_NONE; + } + + ImGui::Spacing(); + } + else { + ImGui::SetCursorPosX(button_xpos); + + if(ImGui::Button(str_play.c_str(), ImVec2(button_width, 0.0f))) { + globals::gui_screen = GUI_PLAY_MENU; + } + + ImGui::Spacing(); + } + + ImGui::SetCursorPosX(button_xpos); + + if(ImGui::Button(str_settings.c_str(), ImVec2(button_width, 0.0f))) { + globals::gui_screen = GUI_SETTINGS; + } + + ImGui::Spacing(); + + if(session::is_ingame()) { + ImGui::SetCursorPosX(button_xpos); + + if(ImGui::Button(str_leave.c_str(), ImVec2(button_width, 0.0f))) { + session::disconnect("protocol.client_disconnect"); + globals::gui_screen = GUI_PLAY_MENU; + gui::window_title::update(); + } + + ImGui::Spacing(); + } + else { + ImGui::SetCursorPosX(button_xpos); + + if(ImGui::Button(str_quit.c_str(), ImVec2(button_width, 0.0f))) { + glfwSetWindowShouldClose(globals::window, true); + } + + ImGui::Spacing(); + } + + if(!session::is_ingame()) { + const auto& padding = ImGui::GetStyle().FramePadding; + const auto& spacing = ImGui::GetStyle().ItemSpacing; + + ImGui::PushFont(globals::font_unscii8, 4.0f); + ImGui::SetCursorScreenPos(ImVec2(padding.x + spacing.x, window_size.y - ImGui::GetFontSize() - padding.y - spacing.y)); + ImGui::Text("Voxelius %*s", version::semver.size(), version::semver.data()); // string_view is not always null-terminated + ImGui::PopFont(); + } + + ImGui::PopStyleVar(); + } + + ImGui::End(); +} diff --git a/game/client/gui/main_menu.hh b/game/client/gui/main_menu.hh index 205f078..8e30e38 100644 --- a/game/client/gui/main_menu.hh +++ b/game/client/gui/main_menu.hh @@ -1,8 +1,8 @@ -#pragma once - -namespace gui::main_menu -{ -void init(void); -void shutdown(void); -void layout(void); -} // namespace gui::main_menu +#pragma once + +namespace gui::main_menu +{ +void init(void); +void shutdown(void); +void layout(void); +} // namespace gui::main_menu diff --git a/game/client/gui/message_box.cc b/game/client/gui/message_box.cc index 59e2d33..b7f109a 100644 --- a/game/client/gui/message_box.cc +++ b/game/client/gui/message_box.cc @@ -1,95 +1,95 @@ -#include "client/pch.hh" - -#include "client/gui/message_box.hh" - -#include "client/gui/gui_screen.hh" -#include "client/gui/language.hh" - -#include "client/globals.hh" - -constexpr static ImGuiWindowFlags WINDOW_FLAGS = ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoDecoration; - -struct Button final { - gui::message_box_action action; - std::string str_title; -}; - -static std::string str_title; -static std::string str_subtitle; -static std::vector