From a3c59ce1d725465f7c6360ec649dc8d954fe347d Mon Sep 17 00:00:00 2001 From: "C. J. Howard" Date: Wed, 15 Jun 2022 05:22:30 +0800 Subject: [PATCH] Add suport for mouse navigation in menus --- src/application.cpp | 28 +- src/application.hpp | 12 +- src/game/context.hpp | 28 +- src/game/menu.cpp | 348 ++++++++++++++++ src/game/menu.hpp | 52 +++ src/game/states/boot.cpp | 23 +- src/game/states/controller-config-menu.cpp | 43 ++ src/game/states/controller-config-menu.hpp | 39 ++ src/game/states/controls-menu.cpp | 123 ++++++ src/game/states/controls-menu.hpp | 39 ++ src/game/states/graphics-menu.cpp | 464 ++++++--------------- src/game/states/keyboard-config-menu.cpp | 55 +++ src/game/states/keyboard-config-menu.hpp | 39 ++ src/game/states/language-menu.cpp | 226 +++------- src/game/states/main-menu.cpp | 357 +++++----------- src/game/states/options-menu.cpp | 222 ++++------ src/game/states/sound-menu.cpp | 436 ++++++------------- src/ui/mouse-tracker.cpp | 75 ++++ src/ui/mouse-tracker.hpp | 58 +++ 19 files changed, 1425 insertions(+), 1242 deletions(-) create mode 100644 src/game/menu.cpp create mode 100644 src/game/menu.hpp create mode 100644 src/game/states/controller-config-menu.cpp create mode 100644 src/game/states/controller-config-menu.hpp create mode 100644 src/game/states/controls-menu.cpp create mode 100644 src/game/states/controls-menu.hpp create mode 100644 src/game/states/keyboard-config-menu.cpp create mode 100644 src/game/states/keyboard-config-menu.hpp create mode 100644 src/ui/mouse-tracker.cpp create mode 100644 src/ui/mouse-tracker.hpp diff --git a/src/application.cpp b/src/application.cpp index db1ac72..e619d4e 100644 --- a/src/application.cpp +++ b/src/application.cpp @@ -44,7 +44,7 @@ application::application(): update_callback(nullptr), render_callback(nullptr), fullscreen(true), - vsync(true), + v_sync(true), cursor_visible(true), display_dimensions({0, 0}), display_dpi(0.0f), @@ -82,7 +82,6 @@ application::application(): { logger->pop_task(EXIT_SUCCESS); } - // Load default OpenGL library logger->push_task("Loading OpenGL library"); if (SDL_GL_LoadLibrary(nullptr) != 0) @@ -102,7 +101,7 @@ application::application(): SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); - + // Get display dimensions SDL_DisplayMode sdl_desktop_display_mode; if (SDL_GetDesktopDisplayMode(0, &sdl_desktop_display_mode) != 0) @@ -130,7 +129,7 @@ application::application(): logger->push_task("Creating " + std::to_string(display_dimensions[0]) + "x" + std::to_string(display_dimensions[1]) + " window"); sdl_window = SDL_CreateWindow ( - "", + "Antkeeper", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, display_dimensions[0], display_dimensions[1], SDL_WINDOW_OPENGL | SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN @@ -159,10 +158,6 @@ application::application(): logger->pop_task(EXIT_SUCCESS); } - // Update window size and viewport size - SDL_GetWindowSize(sdl_window, &window_dimensions[0], &window_dimensions[1]); - SDL_GL_GetDrawableSize(sdl_window, &viewport_dimensions[0], &viewport_dimensions[1]); - // Make OpenGL context current logger->push_task("Making OpenGL context current"); if (SDL_GL_MakeCurrent(sdl_window, sdl_gl_context) != 0) @@ -186,9 +181,13 @@ application::application(): { logger->pop_task(EXIT_SUCCESS); } - + + // Update window size and viewport size + SDL_GetWindowSize(sdl_window, &window_dimensions[0], &window_dimensions[1]); + SDL_GL_GetDrawableSize(sdl_window, &viewport_dimensions[0], &viewport_dimensions[1]); + // Set v-sync mode - int swap_interval = (vsync) ? 1 : 0; + int swap_interval = (v_sync) ? 1 : 0; logger->push_task((swap_interval) ? "Enabling v-sync" : "Disabling v-sync"); if (SDL_GL_SetSwapInterval(swap_interval) != 0) { @@ -493,12 +492,12 @@ void application::set_fullscreen(bool fullscreen) } } -void application::set_vsync(bool vsync) +void application::set_v_sync(bool v_sync) { - if (this->vsync != vsync) + if (this->v_sync != v_sync) { - this->vsync = vsync; - SDL_GL_SetSwapInterval((vsync) ? 1 : 0); + this->v_sync = v_sync; + SDL_GL_SetSwapInterval((v_sync) ? 1 : 0); } } @@ -515,6 +514,7 @@ void application::swap_buffers() void application::show_window() { SDL_ShowWindow(sdl_window); + //SDL_GL_MakeCurrent(sdl_window, sdl_gl_context); } void application::hide_window() diff --git a/src/application.hpp b/src/application.hpp index ded3810..4c89e10 100644 --- a/src/application.hpp +++ b/src/application.hpp @@ -178,9 +178,9 @@ public: /** * Enables or disables v-sync mode. * - * @param vsync `true` if v-sync should be enabled, `false` otherwise. + * @param v_sync `true` if v-sync should be enabled, `false` otherwise. */ - void set_vsync(bool vsync); + void set_v_sync(bool v_sync); void set_window_opacity(float opacity); @@ -207,7 +207,7 @@ public: bool is_fullscreen() const; /// Returns `true` if the v-sync is enabled, `false` otherwise. - bool get_vsync() const; + bool get_v_sync() const; /// Returns the rasterizer for the window. gl::rasterizer* get_rasterizer(); @@ -241,7 +241,7 @@ private: update_callback_type update_callback; render_callback_type render_callback; bool fullscreen; - bool vsync; + bool v_sync; bool cursor_visible; std::array display_dimensions; float display_dpi; @@ -300,9 +300,9 @@ inline bool application::is_fullscreen() const return fullscreen; } -inline bool application::get_vsync() const +inline bool application::get_v_sync() const { - return vsync; + return v_sync; } inline gl::rasterizer* application::get_rasterizer() diff --git a/src/game/context.hpp b/src/game/context.hpp index d8ba33f..96bdb6a 100644 --- a/src/game/context.hpp +++ b/src/game/context.hpp @@ -49,6 +49,7 @@ #include "type/bitmap-font.hpp" #include "render/material.hpp" #include "render/material-property.hpp" +#include "ui/mouse-tracker.hpp" // Forward declarations class animator; @@ -122,7 +123,7 @@ struct context std::optional option_new_game; std::optional option_quick_start; std::optional option_reset; - std::optional option_vsync; + std::optional option_v_sync; std::optional option_windowed; // Paths @@ -208,6 +209,7 @@ struct context scene::text* main_menu_options_text; scene::text* main_menu_credits_text; scene::text* main_menu_quit_text; + std::vector main_menu_texts; int main_menu_index; scene::text* credits_text; scene::text* options_menu_controls_text; @@ -218,6 +220,15 @@ struct context std::vector options_menu_texts; std::vector> options_menu_callbacks; int options_menu_index; + scene::text* controls_menu_configure_keyboard_mouse_label_text; + scene::text* controls_menu_configure_game_controller_label_text; + scene::text* controls_menu_back_label_text; + std::vector controls_menu_label_texts; + std::vector controls_menu_value_texts; + int controls_menu_index; + std::vector> controls_menu_select_callbacks; + std::vector> controls_menu_left_callbacks; + std::vector> controls_menu_right_callbacks; scene::text* language_menu_language_text; scene::text* language_menu_back_text; std::vector language_menu_texts; @@ -259,8 +270,23 @@ struct context std::vector> graphics_menu_select_callbacks; std::vector> graphics_menu_left_callbacks; std::vector> graphics_menu_right_callbacks; + std::vector keyboard_config_menu_label_texts; + std::vector keyboard_config_menu_value_texts; + int keyboard_config_menu_index; + std::vector> keyboard_config_menu_select_callbacks; + std::vector> keyboard_config_menu_left_callbacks; + std::vector> keyboard_config_menu_right_callbacks; float font_size; bool dyslexia_font; + ui::mouse_tracker* menu_mouse_tracker; + + std::vector> menu_select_callbacks; + std::vector> menu_left_callbacks; + std::vector> menu_right_callbacks; + std::function menu_back_callback; + std::vector> menu_item_texts; + std::unordered_map menu_item_indices; + int* menu_item_index; // Surface scene scene::collection* surface_scene; diff --git a/src/game/menu.cpp b/src/game/menu.cpp new file mode 100644 index 0000000..d2e7b61 --- /dev/null +++ b/src/game/menu.cpp @@ -0,0 +1,348 @@ +/* + * Copyright (C) 2021 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include "game/menu.hpp" +#include "scene/text.hpp" +#include "application.hpp" +#include + +namespace game { +namespace menu { + +void init_menu_item_index(game::context* ctx, const std::string& menu_name) +{ + if (auto it = ctx->menu_item_indices.find(menu_name); it != ctx->menu_item_indices.end()) + { + ctx->menu_item_index = &it->second; + } + else + { + ctx->menu_item_index = &ctx->menu_item_indices[menu_name]; + *ctx->menu_item_index = 0; + } +} + +void update_text_font(game::context* ctx) +{ + for (auto [name, value]: ctx->menu_item_texts) + { + name->set_material(&ctx->menu_font_material); + name->set_font(&ctx->menu_font); + + if (value) + { + value->set_material(&ctx->menu_font_material); + value->set_font(&ctx->menu_font); + } + } +} + +void update_text_color(game::context* ctx) +{ + for (std::size_t i = 0; i < ctx->menu_item_texts.size(); ++i) + { + auto [name, value] = ctx->menu_item_texts[i]; + + const float4& color = (i == *ctx->menu_item_index) ? active_color : inactive_color; + + name->set_color(color); + if (value) + value->set_color(color); + } +} + +void update_text_tweens(game::context* ctx) +{ + for (auto [name, value]: ctx->menu_item_texts) + { + name->update_tweens(); + if (value) + value->update_tweens(); + } +} + +void align_text(game::context* ctx) +{ + // Calculate menu width + float menu_width = 0.0f; + float menu_spacing = ctx->menu_font.get_glyph_metrics(U'M').width; + + for (auto [name, value]: ctx->menu_item_texts) + { + float row_width = 0.0f; + + // Add name width to width + const auto& name_bounds = static_cast&>(name->get_local_bounds()); + row_width += name_bounds.max_point.x - name_bounds.min_point.x; + + if (value) + { + // Add value width to width + //const auto& value_bounds = static_cast&>(value->get_local_bounds()); + //row_width += value_bounds.max_point.x - value_bounds.min_point.x; + + // Add spacing to row width + row_width += menu_spacing * 8.0f; + } + + menu_width = std::max(menu_width, row_width); + } + + // Align texts + float menu_height = ctx->menu_item_texts.size() * ctx->menu_font.get_font_metrics().linespace; + float menu_x = -menu_width * 0.5f; + float menu_y = menu_height * 0.5f - ctx->menu_font.get_font_metrics().linespace; + + for (std::size_t i = 0; i < ctx->menu_item_texts.size(); ++i) + { + auto [name, value] = ctx->menu_item_texts[i]; + + float x = menu_x; + float y = menu_y - ctx->menu_font.get_font_metrics().linespace * i; + name->set_translation({std::round(x), std::round(y), 0.0f}); + + if (value) + { + const auto& value_bounds = static_cast&>(value->get_local_bounds()); + const float value_width = value_bounds.max_point.x - value_bounds.min_point.x; + x = menu_x + menu_width - value_width; + + value->set_translation({std::round(x), std::round(y), 0.0f}); + } + } +} + +void refresh_text(game::context* ctx) +{ + for (auto [name, value]: ctx->menu_item_texts) + { + name->refresh(); + if (value) + value->refresh(); + } +} + +void add_text_to_ui(game::context* ctx) +{ + for (auto [name, value]: ctx->menu_item_texts) + { + ctx->ui_scene->add_object(name); + if (value) + ctx->ui_scene->add_object(value); + } +} + +void remove_text_from_ui(game::context* ctx) +{ + for (auto [name, value]: ctx->menu_item_texts) + { + ctx->ui_scene->remove_object(name); + if (value) + ctx->ui_scene->remove_object(value); + } +} + +void delete_text(game::context* ctx) +{ + for (auto [name, value]: ctx->menu_item_texts) + { + delete name; + if (value) + delete value; + } + ctx->menu_item_texts.clear(); +} + +void clear_callbacks(game::context* ctx) +{ + // Clear menu item callbacks + ctx->menu_left_callbacks.clear(); + ctx->menu_right_callbacks.clear(); + ctx->menu_select_callbacks.clear(); + ctx->menu_back_callback = nullptr; +} + +void setup_controls(game::context* ctx) +{ + ctx->controls["menu_up"]->set_activated_callback + ( + [ctx]() + { + --(*ctx->menu_item_index); + if (*ctx->menu_item_index < 0) + *ctx->menu_item_index = ctx->menu_item_texts.size() - 1; + + update_text_color(ctx); + } + ); + ctx->controls["menu_down"]->set_activated_callback + ( + [ctx]() + { + ++(*ctx->menu_item_index); + if (*ctx->menu_item_index >= ctx->menu_item_texts.size()) + *ctx->menu_item_index = 0; + + update_text_color(ctx); + } + ); + ctx->controls["menu_left"]->set_activated_callback + ( + [ctx]() + { + auto callback = ctx->menu_left_callbacks[*ctx->menu_item_index]; + if (callback != nullptr) + callback(); + } + ); + ctx->controls["menu_right"]->set_activated_callback + ( + [ctx]() + { + auto callback = ctx->menu_right_callbacks[*ctx->menu_item_index]; + if (callback != nullptr) + callback(); + } + ); + ctx->controls["menu_select"]->set_activated_callback + ( + [ctx]() + { + auto callback = ctx->menu_select_callbacks[*ctx->menu_item_index]; + if (callback != nullptr) + callback(); + } + ); + ctx->controls["menu_back"]->set_activated_callback + ( + [ctx]() + { + if (ctx->menu_back_callback != nullptr) + ctx->menu_back_callback(); + } + ); + + ctx->menu_mouse_tracker->set_mouse_moved_callback + ( + [ctx](const mouse_moved_event& event) + { + for (std::size_t i = 0; i < ctx->menu_item_texts.size(); ++i) + { + auto [name, value] = ctx->menu_item_texts[i]; + + const auto& name_bounds = static_cast&>(name->get_world_bounds()); + float min_x = name_bounds.min_point.x; + float min_y = name_bounds.min_point.y; + float max_x = name_bounds.max_point.x; + float max_y = name_bounds.max_point.y; + if (value) + { + const auto& value_bounds = static_cast&>(value->get_world_bounds()); + min_x = std::min(min_x, value_bounds.min_point.x); + min_y = std::min(min_y, value_bounds.min_point.y); + max_x = std::max(max_x, value_bounds.max_point.x); + max_y = std::max(max_y, value_bounds.max_point.y); + } + + const auto& viewport = ctx->app->get_viewport_dimensions(); + const float x = static_cast(event.x - viewport[0] / 2); + const float y = static_cast((viewport[1] - event.y + 1) - viewport[1] / 2); + + if (x >= min_x && x <= max_x) + { + if (y >= min_y && y <= max_y) + { + *ctx->menu_item_index = i; + update_text_color(ctx); + break; + } + } + } + } + ); + + ctx->menu_mouse_tracker->set_mouse_button_pressed_callback + ( + [ctx](const mouse_button_pressed_event& event) + { + for (std::size_t i = 0; i < ctx->menu_item_texts.size(); ++i) + { + auto [name, value] = ctx->menu_item_texts[i]; + + const auto& name_bounds = static_cast&>(name->get_world_bounds()); + float min_x = name_bounds.min_point.x; + float min_y = name_bounds.min_point.y; + float max_x = name_bounds.max_point.x; + float max_y = name_bounds.max_point.y; + if (value) + { + const auto& value_bounds = static_cast&>(value->get_world_bounds()); + min_x = std::min(min_x, value_bounds.min_point.x); + min_y = std::min(min_y, value_bounds.min_point.y); + max_x = std::max(max_x, value_bounds.max_point.x); + max_y = std::max(max_y, value_bounds.max_point.y); + } + + const auto& viewport = ctx->app->get_viewport_dimensions(); + const float x = static_cast(event.x - viewport[0] / 2); + const float y = static_cast((viewport[1] - event.y + 1) - viewport[1] / 2); + + if (x >= min_x && x <= max_x) + { + if (y >= min_y && y <= max_y) + { + *ctx->menu_item_index = i; + update_text_color(ctx); + + if (event.button == 1) + { + auto callback = ctx->menu_select_callbacks[i]; + if (callback) + callback(); + } + else if (event.button == 3) + { + auto callback = ctx->menu_left_callbacks[i]; + if (callback) + callback(); + } + + return; + } + } + } + } + ); +} + +void clear_controls(game::context* ctx) +{ + ctx->controls["menu_up"]->set_activated_callback(nullptr); + ctx->controls["menu_down"]->set_activated_callback(nullptr); + ctx->controls["menu_left"]->set_activated_callback(nullptr); + ctx->controls["menu_right"]->set_activated_callback(nullptr); + ctx->controls["menu_select"]->set_activated_callback(nullptr); + ctx->controls["menu_back"]->set_activated_callback(nullptr); + + ctx->menu_mouse_tracker->set_mouse_moved_callback(nullptr); + ctx->menu_mouse_tracker->set_mouse_button_pressed_callback(nullptr); +} + +} // namespace menu +} // namespace game diff --git a/src/game/menu.hpp b/src/game/menu.hpp new file mode 100644 index 0000000..59e4382 --- /dev/null +++ b/src/game/menu.hpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2021 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_MENU_HPP +#define ANTKEEPER_GAME_MENU_HPP + +#include "game/context.hpp" + +namespace game { +namespace menu { + +/// RGBA color of active menu items. +static constexpr float4 active_color{1.0f, 1.0f, 1.0f, 1.0f}; + +/// RGBA color of inactive menu items. +static constexpr float4 inactive_color{1.0f, 1.0f, 1.0f, 0.5f}; + +void init_menu_item_index(game::context* ctx, const std::string& menu_name); +void setup_controls(game::context* ctx); + +void clear_controls(game::context* ctx); +void clear_callbacks(game::context* ctx); +void remove_text_from_ui(game::context* ctx); +void delete_text(game::context* ctx); + +void update_text_color(game::context* ctx); +void update_text_font(game::context* ctx); +void update_text_tweens(game::context* ctx); +void align_text(game::context* ctx); +void refresh_text(game::context* ctx); +void add_text_to_ui(game::context* ctx); + +} // namespace menu +} // namespace game + +#endif // ANTKEEPER_GAME_MENU_HPP diff --git a/src/game/states/boot.cpp b/src/game/states/boot.cpp index caa7eaa..a780d05 100644 --- a/src/game/states/boot.cpp +++ b/src/game/states/boot.cpp @@ -214,7 +214,7 @@ void parse_options(game::context* ctx, int argc, char** argv) // --vsync if (result.count("vsync")) - ctx->option_vsync = (result["vsync"].as()) ? true : false; + ctx->option_v_sync = (result["vsync"].as()) ? true : false; // --windowed if (result.count("windowed")) @@ -438,12 +438,12 @@ void setup_window(game::context* ctx) app->resize_window(resolution.x, resolution.y); // Set v-sync - bool vsync = true; - if (ctx->option_vsync.has_value()) - vsync = (ctx->option_vsync.value() != 0); - else if (config->contains("vsync")) - vsync = (*config)["vsync"].get(); - app->set_vsync(vsync); + bool v_sync = true; + if (ctx->option_v_sync.has_value()) + v_sync = (ctx->option_v_sync.value() != 0); + else if (config->contains("v_sync")) + v_sync = (*config)["v_sync"].get(); + app->set_v_sync(v_sync); // Set title app->set_title((*ctx->strings)["application_title"]); @@ -1006,9 +1006,18 @@ void setup_ui(game::context* ctx) ctx->main_menu_index = 0; ctx->options_menu_index = 0; + ctx->controls_menu_index = 0; ctx->graphics_menu_index = 0; ctx->sound_menu_index = 0; ctx->language_menu_index = 0; + ctx->keyboard_config_menu_index = 0; + + // Construct mouse tracker + ctx->menu_mouse_tracker = new ui::mouse_tracker(); + ctx->app->get_event_dispatcher()->subscribe(ctx->menu_mouse_tracker); + ctx->app->get_event_dispatcher()->subscribe(ctx->menu_mouse_tracker); + ctx->app->get_event_dispatcher()->subscribe(ctx->menu_mouse_tracker); + ctx->app->get_event_dispatcher()->subscribe(ctx->menu_mouse_tracker); } void setup_cli(game::context* ctx) diff --git a/src/game/states/controller-config-menu.cpp b/src/game/states/controller-config-menu.cpp new file mode 100644 index 0000000..08d2a6e --- /dev/null +++ b/src/game/states/controller-config-menu.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2021 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include "game/states/controller-config-menu.hpp" +#include "game/states/controls-menu.hpp" +#include "application.hpp" +#include "scene/text.hpp" +#include "render/passes/clear-pass.hpp" +#include "debug/logger.hpp" + +namespace game { +namespace state { +namespace controller_config_menu { + +void enter(game::context* ctx) +{ + +} + +void exit(game::context* ctx) +{ + +} + +} // namespace controller_config_menu +} // namespace state +} // namespace game diff --git a/src/game/states/controller-config-menu.hpp b/src/game/states/controller-config-menu.hpp new file mode 100644 index 0000000..453c89c --- /dev/null +++ b/src/game/states/controller-config-menu.hpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2021 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_STATE_CONTROLLER_CONFIG_MENU_HPP +#define ANTKEEPER_GAME_STATE_CONTROLLER_CONFIG_MENU_HPP + +#include "game/context.hpp" + +namespace game { +namespace state { + +/// Game controller config menu screen game state functions. +namespace controller_config_menu { + +void enter(game::context* ctx); +void exit(game::context* ctx); + +} // namespace controller_config_menu + +} // namespace state +} // namespace game + +#endif // ANTKEEPER_GAME_STATE_CONTROLLER_CONFIG_MENU_HPP diff --git a/src/game/states/controls-menu.cpp b/src/game/states/controls-menu.cpp new file mode 100644 index 0000000..9117d22 --- /dev/null +++ b/src/game/states/controls-menu.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2021 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include "game/states/controls-menu.hpp" +#include "game/states/keyboard-config-menu.hpp" +#include "game/states/controller-config-menu.hpp" +#include "game/states/options-menu.hpp" +#include "application.hpp" +#include "scene/text.hpp" +#include "render/passes/clear-pass.hpp" +#include "debug/logger.hpp" +#include "game/menu.hpp" + +namespace game { +namespace state { +namespace controls_menu { + +void enter(game::context* ctx) +{ + ctx->ui_clear_pass->set_cleared_buffers(true, true, false); + + // Construct menu item texts + scene::text* keyboard_text = new scene::text(); + scene::text* controller_text = new scene::text(); + scene::text* back_text = new scene::text(); + + // Build list of menu item texts + ctx->menu_item_texts.push_back({keyboard_text, nullptr}); + ctx->menu_item_texts.push_back({controller_text, nullptr}); + ctx->menu_item_texts.push_back({back_text, nullptr}); + + // Set content of menu item texts + keyboard_text->set_content((*ctx->strings)["controls_menu_keyboard"]); + controller_text->set_content((*ctx->strings)["controls_menu_controller"]); + back_text->set_content((*ctx->strings)["back"]); + + // Init menu item index + game::menu::init_menu_item_index(ctx, "controls"); + + game::menu::update_text_color(ctx); + game::menu::update_text_font(ctx); + game::menu::align_text(ctx); + game::menu::update_text_tweens(ctx); + game::menu::add_text_to_ui(ctx); + + // Construct menu item callbacks + auto select_keyboard_callback = [ctx]() + { + application::state next_state; + next_state.name = "keyboard_config_menu"; + next_state.enter = std::bind(game::state::keyboard_config_menu::enter, ctx); + next_state.exit = std::bind(game::state::keyboard_config_menu::exit, ctx); + ctx->app->change_state(next_state); + }; + auto select_controller_callback = [ctx]() + { + application::state next_state; + next_state.name = "controller_config_menu"; + next_state.enter = std::bind(game::state::controller_config_menu::enter, ctx); + next_state.exit = std::bind(game::state::controller_config_menu::exit, ctx); + ctx->app->change_state(next_state); + }; + auto select_back_callback = [ctx]() + { + application::state next_state; + next_state.name = "options_menu"; + next_state.enter = std::bind(game::state::options_menu::enter, ctx); + next_state.exit = std::bind(game::state::options_menu::exit, ctx); + ctx->app->change_state(next_state); + }; + + // Build list of menu select callbacks + ctx->menu_select_callbacks.push_back(select_keyboard_callback); + ctx->menu_select_callbacks.push_back(select_controller_callback); + ctx->menu_select_callbacks.push_back(select_back_callback); + + // Build list of menu left callbacks + ctx->menu_left_callbacks.push_back(nullptr); + ctx->menu_left_callbacks.push_back(nullptr); + ctx->menu_left_callbacks.push_back(nullptr); + + // Build list of menu right callbacks + ctx->menu_right_callbacks.push_back(nullptr); + ctx->menu_right_callbacks.push_back(nullptr); + ctx->menu_right_callbacks.push_back(nullptr); + + // Set menu back callback + ctx->menu_back_callback = select_back_callback; + + // Setup menu controls + game::menu::setup_controls(ctx); +} + +void exit(game::context* ctx) +{ + // Destruct menu + game::menu::clear_controls(ctx); + game::menu::clear_callbacks(ctx); + game::menu::remove_text_from_ui(ctx); + game::menu::delete_text(ctx); + + ctx->ui_clear_pass->set_cleared_buffers(false, true, false); +} + +} // namespace controls_menu +} // namespace state +} // namespace game diff --git a/src/game/states/controls-menu.hpp b/src/game/states/controls-menu.hpp new file mode 100644 index 0000000..4ddf975 --- /dev/null +++ b/src/game/states/controls-menu.hpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2021 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_STATE_CONTROLS_MENU_HPP +#define ANTKEEPER_GAME_STATE_CONTROLS_MENU_HPP + +#include "game/context.hpp" + +namespace game { +namespace state { + +/// Controls menu screen game state functions. +namespace controls_menu { + +void enter(game::context* ctx); +void exit(game::context* ctx); + +} // namespace controls_menu + +} // namespace state +} // namespace game + +#endif // ANTKEEPER_GAME_STATE_CONTROLS_MENU_HPP diff --git a/src/game/states/graphics-menu.cpp b/src/game/states/graphics-menu.cpp index a1a7c18..1bbad2e 100644 --- a/src/game/states/graphics-menu.cpp +++ b/src/game/states/graphics-menu.cpp @@ -24,249 +24,74 @@ #include "render/passes/clear-pass.hpp" #include "debug/logger.hpp" #include "game/fonts.hpp" +#include "game/menu.hpp" namespace game { namespace state { namespace graphics_menu { -static void update_text_color(game::context* ctx) +static void update_value_text_content(game::context* ctx) { - float4 inactive_color = {1.0f, 1.0f, 1.0f, 0.5f}; - float4 active_color = {1.0f, 1.0f, 1.0f, 1.0f}; - - for (std::size_t i = 0; i < ctx->graphics_menu_label_texts.size(); ++i) - { - scene::text* label_text = ctx->graphics_menu_label_texts[i]; - scene::text* value_text = ctx->graphics_menu_value_texts[i]; - - if (i == ctx->graphics_menu_index) - { - label_text->set_color(active_color); - if (value_text) - value_text->set_color(active_color); - } - else - { - label_text->set_color(inactive_color); - if (value_text) - value_text->set_color(inactive_color); - } - } -} - -static void align_texts(game::context* ctx) -{ - // Calculate menu width - float menu_width = 0.0f; - float menu_spacing = ctx->menu_font.get_glyph_metrics(U'M').width; - for (std::size_t i = 0; i < ctx->graphics_menu_label_texts.size(); ++i) - { - scene::text* label_text = ctx->graphics_menu_label_texts[i]; - scene::text* value_text = ctx->graphics_menu_value_texts[i]; - - float row_width = 0.0f; - - // Add label width to width - const auto& label_bounds = static_cast&>(label_text->get_local_bounds()); - row_width += label_bounds.max_point.x - label_bounds.min_point.x; - - if (value_text != nullptr) - { - // Add value width to width - //const auto& value_bounds = static_cast&>(value_text->get_local_bounds()); - //row_width += value_bounds.max_point.x - value_bounds.min_point.x; - - // Add spacing to row width - row_width += menu_spacing * 8.0f; - } - - menu_width = std::max(menu_width, row_width); - } - - // Align texts - float menu_height = ctx->graphics_menu_label_texts.size() * ctx->menu_font.get_font_metrics().linespace; - float menu_x = -menu_width * 0.5f; - float menu_y = menu_height * 0.5f - ctx->menu_font.get_font_metrics().linespace; - for (std::size_t i = 0; i < ctx->graphics_menu_label_texts.size(); ++i) - { - scene::text* label_text = ctx->graphics_menu_label_texts[i]; - - float x = menu_x; - float y = menu_y - ctx->menu_font.get_font_metrics().linespace * i; - label_text->set_translation({std::round(x), std::round(y), 0.0f}); - - scene::text* value_text = ctx->graphics_menu_value_texts[i]; - if (value_text) - { - const auto& value_bounds = static_cast&>(value_text->get_local_bounds()); - const float value_width = value_bounds.max_point.x - value_bounds.min_point.x; - x = menu_x + menu_width - value_width; - - value_text->set_translation({std::round(x), std::round(y), 0.0f}); - } - } -} - -static void update_text_tweens(game::context* ctx) -{ - for (std::size_t i = 0; i < ctx->graphics_menu_label_texts.size(); ++i) - { - scene::text* label_text = ctx->graphics_menu_label_texts[i]; - scene::text* value_text = ctx->graphics_menu_value_texts[i]; - - label_text->update_tweens(); - if (value_text) - value_text->update_tweens(); - } -} - -static void refresh_texts(game::context* ctx) -{ - for (std::size_t i = 0; i < ctx->graphics_menu_label_texts.size(); ++i) - { - scene::text* label_text = ctx->graphics_menu_label_texts[i]; - label_text->refresh(); - - scene::text* value_text = ctx->graphics_menu_value_texts[i]; - if (value_text) - value_text->refresh(); - } -} - -void enter(game::context* ctx) -{ - ctx->ui_clear_pass->set_cleared_buffers(true, true, false); - - // Construct graphics menu texts - ctx->graphics_menu_display_mode_label_text = new scene::text(); - ctx->graphics_menu_display_mode_value_text = new scene::text(); - ctx->graphics_menu_render_resolution_label_text = new scene::text(); - ctx->graphics_menu_render_resolution_value_text = new scene::text(); - ctx->graphics_menu_v_sync_label_text = new scene::text(); - ctx->graphics_menu_v_sync_value_text = new scene::text(); - ctx->graphics_menu_font_size_label_text = new scene::text(); - ctx->graphics_menu_font_size_value_text = new scene::text(); - ctx->graphics_menu_dyslexia_font_label_text = new scene::text(); - ctx->graphics_menu_dyslexia_font_value_text = new scene::text(); - ctx->graphics_menu_back_label_text = new scene::text(); - bool fullscreen = ctx->app->is_fullscreen(); - float render_resolution = ctx->render_resolution_scale; - bool vsync = ctx->app->get_vsync(); + float resolution = ctx->render_resolution_scale; + bool v_sync = ctx->app->get_v_sync(); float font_size = ctx->font_size; bool dyslexia_font = ctx->dyslexia_font; - const std::string string_fullscreen = (*ctx->strings)["graphics_menu_display_mode_fullscreen"]; - const std::string string_window = (*ctx->strings)["graphics_menu_display_mode_window"]; const std::string string_on = (*ctx->strings)["on"]; const std::string string_off = (*ctx->strings)["off"]; - // Set text content - ctx->graphics_menu_display_mode_label_text->set_content((*ctx->strings)["graphics_menu_display_mode"]); - ctx->graphics_menu_display_mode_value_text->set_content((fullscreen) ? string_fullscreen : string_window); - ctx->graphics_menu_render_resolution_label_text->set_content((*ctx->strings)["graphics_menu_render_resolution"]); - ctx->graphics_menu_render_resolution_value_text->set_content(std::to_string(static_cast(std::round(render_resolution * 100.0f))) + "%"); - ctx->graphics_menu_v_sync_label_text->set_content((*ctx->strings)["graphics_menu_v_sync"]); - ctx->graphics_menu_v_sync_value_text->set_content((vsync) ? string_on : string_off); - - ctx->graphics_menu_font_size_label_text->set_content((*ctx->strings)["graphics_menu_font_size"]); - ctx->graphics_menu_font_size_value_text->set_content(std::to_string(static_cast(std::round(font_size * 100.0f))) + "%"); + std::get<1>(ctx->menu_item_texts[0])->set_content((fullscreen) ? string_on : string_off); + std::get<1>(ctx->menu_item_texts[1])->set_content(std::to_string(static_cast(std::round(resolution * 100.0f))) + "%"); + std::get<1>(ctx->menu_item_texts[2])->set_content((v_sync) ? string_on : string_off); + std::get<1>(ctx->menu_item_texts[3])->set_content(std::to_string(static_cast(std::round(font_size * 100.0f))) + "%"); + std::get<1>(ctx->menu_item_texts[4])->set_content((dyslexia_font) ? string_on : string_off); +} + +void enter(game::context* ctx) +{ + ctx->ui_clear_pass->set_cleared_buffers(true, true, false); - ctx->graphics_menu_dyslexia_font_label_text->set_content((*ctx->strings)["graphics_menu_dyslexia_font"]); - ctx->graphics_menu_dyslexia_font_value_text->set_content((dyslexia_font) ? string_on : string_off); + // Construct menu item texts + scene::text* fullscreen_name_text = new scene::text(); + scene::text* fullscreen_value_text = new scene::text(); + scene::text* resolution_name_text = new scene::text(); + scene::text* resolution_value_text = new scene::text(); + scene::text* v_sync_name_text = new scene::text(); + scene::text* v_sync_value_text = new scene::text(); + scene::text* font_size_name_text = new scene::text(); + scene::text* font_size_value_text = new scene::text(); + scene::text* dyslexia_font_name_text = new scene::text(); + scene::text* dyslexia_font_value_text = new scene::text(); + scene::text* back_text = new scene::text(); - ctx->graphics_menu_back_label_text->set_content((*ctx->strings)["back"]); + // Build list of menu item texts + ctx->menu_item_texts.push_back({fullscreen_name_text, fullscreen_value_text}); + ctx->menu_item_texts.push_back({resolution_name_text, resolution_value_text}); + ctx->menu_item_texts.push_back({v_sync_name_text, v_sync_value_text}); + ctx->menu_item_texts.push_back({font_size_name_text, font_size_value_text}); + ctx->menu_item_texts.push_back({dyslexia_font_name_text, dyslexia_font_value_text}); + ctx->menu_item_texts.push_back({back_text, nullptr}); - // Build lists of graphics menu texts - ctx->graphics_menu_label_texts.push_back(ctx->graphics_menu_display_mode_label_text); - ctx->graphics_menu_label_texts.push_back(ctx->graphics_menu_render_resolution_label_text); - ctx->graphics_menu_label_texts.push_back(ctx->graphics_menu_v_sync_label_text); - ctx->graphics_menu_label_texts.push_back(ctx->graphics_menu_font_size_label_text); - ctx->graphics_menu_label_texts.push_back(ctx->graphics_menu_dyslexia_font_label_text); - ctx->graphics_menu_label_texts.push_back(ctx->graphics_menu_back_label_text); - ctx->graphics_menu_value_texts.push_back(ctx->graphics_menu_display_mode_value_text); - ctx->graphics_menu_value_texts.push_back(ctx->graphics_menu_render_resolution_value_text); - ctx->graphics_menu_value_texts.push_back(ctx->graphics_menu_v_sync_value_text); - ctx->graphics_menu_value_texts.push_back(ctx->graphics_menu_font_size_value_text); - ctx->graphics_menu_value_texts.push_back(ctx->graphics_menu_dyslexia_font_value_text); - ctx->graphics_menu_value_texts.push_back(nullptr); + // Set content of menu item texts + fullscreen_name_text->set_content((*ctx->strings)["graphics_menu_fullscreen"]); + resolution_name_text->set_content((*ctx->strings)["graphics_menu_resolution"]); + v_sync_name_text->set_content((*ctx->strings)["graphics_menu_v_sync"]); + font_size_name_text->set_content((*ctx->strings)["graphics_menu_font_size"]); + dyslexia_font_name_text->set_content((*ctx->strings)["graphics_menu_dyslexia_font"]); + back_text->set_content((*ctx->strings)["back"]); + update_value_text_content(ctx); - // Set text fonts - for (std::size_t i = 0; i < ctx->graphics_menu_label_texts.size(); ++i) - { - scene::text* label_text = ctx->graphics_menu_label_texts[i]; - label_text->set_material(&ctx->menu_font_material); - label_text->set_font(&ctx->menu_font); - - scene::text* value_text = ctx->graphics_menu_value_texts[i]; - if (value_text) - { - value_text->set_material(&ctx->menu_font_material); - value_text->set_font(&ctx->menu_font); - } - } + // Init menu item index + game::menu::init_menu_item_index(ctx, "graphics"); - // Align texts - align_texts(ctx); - - // Construct graphics menu callbacks - auto menu_back_callback = [ctx]() - { - application::state next_state; - next_state.name = "options_menu"; - next_state.enter = std::bind(game::state::options_menu::enter, ctx); - next_state.exit = std::bind(game::state::options_menu::exit, ctx); - ctx->app->change_state(next_state); - }; - - ctx->controls["menu_down"]->set_activated_callback - ( - [ctx]() - { - ++ctx->graphics_menu_index; - if (ctx->graphics_menu_index >= ctx->graphics_menu_label_texts.size()) - ctx->graphics_menu_index = 0; - - update_text_color(ctx); - } - ); - ctx->controls["menu_up"]->set_activated_callback - ( - [ctx]() - { - --ctx->graphics_menu_index; - if (ctx->graphics_menu_index < 0) - ctx->graphics_menu_index = ctx->graphics_menu_label_texts.size() - 1; - - update_text_color(ctx); - } - ); - ctx->controls["menu_left"]->set_activated_callback - ( - [ctx]() - { - if (ctx->graphics_menu_left_callbacks[ctx->graphics_menu_index]) - ctx->graphics_menu_left_callbacks[ctx->graphics_menu_index](); - } - ); - ctx->controls["menu_right"]->set_activated_callback - ( - [ctx]() - { - if (ctx->graphics_menu_right_callbacks[ctx->graphics_menu_index]) - ctx->graphics_menu_right_callbacks[ctx->graphics_menu_index](); - } - ); - ctx->controls["menu_select"]->set_activated_callback - ( - [ctx]() - { - if (ctx->graphics_menu_select_callbacks[ctx->graphics_menu_index]) - ctx->graphics_menu_select_callbacks[ctx->graphics_menu_index](); - } - ); - ctx->controls["menu_back"]->set_activated_callback(menu_back_callback); + game::menu::update_text_color(ctx); + game::menu::update_text_font(ctx); + game::menu::align_text(ctx); + game::menu::update_text_tweens(ctx); + game::menu::add_text_to_ui(ctx); + // Construct menu item callbacks auto toggle_fullscreen_callback = [ctx]() { bool fullscreen = !ctx->app->is_fullscreen(); @@ -282,15 +107,15 @@ void enter(game::context* ctx) ctx->app->resize_window(resolution.x, resolution.y); } - const std::string string_fullscreen = (*ctx->strings)["graphics_menu_display_mode_fullscreen"]; - const std::string string_window = (*ctx->strings)["graphics_menu_display_mode_window"];; - ctx->graphics_menu_display_mode_value_text->set_content((fullscreen) ? string_fullscreen : string_window); + update_value_text_content(ctx); + game::menu::align_text(ctx); + game::menu::update_text_tweens(ctx); // Save display mode config (*ctx->config)["fullscreen"] = fullscreen; }; - auto increase_render_resolution_callback = [ctx]() + auto increase_resolution_callback = [ctx]() { // Increase resolution if (ctx->controls["menu_modifier"]->is_active()) @@ -302,14 +127,15 @@ void enter(game::context* ctx) if (ctx->render_resolution_scale > 2.0f) ctx->render_resolution_scale = 2.0f; - // Update value text - ctx->graphics_menu_render_resolution_value_text->set_content(std::to_string(static_cast(std::round(ctx->render_resolution_scale * 100.0f))) + "%"); + update_value_text_content(ctx); + game::menu::align_text(ctx); + game::menu::update_text_tweens(ctx); // Update config (*ctx->config)["render_resolution"] = ctx->render_resolution_scale; }; - auto decrease_render_resolution_callback = [ctx]() + auto decrease_resolution_callback = [ctx]() { // Increase resolution if (ctx->controls["menu_modifier"]->is_active()) @@ -321,13 +147,28 @@ void enter(game::context* ctx) if (ctx->render_resolution_scale < 0.1f) ctx->render_resolution_scale = 0.1f; - // Update value text - ctx->graphics_menu_render_resolution_value_text->set_content(std::to_string(static_cast(std::round(ctx->render_resolution_scale * 100.0f))) + "%"); + update_value_text_content(ctx); + game::menu::align_text(ctx); + game::menu::update_text_tweens(ctx); // Update config (*ctx->config)["render_resolution"] = ctx->render_resolution_scale; }; + auto toggle_v_sync_callback = [ctx]() + { + bool v_sync = !ctx->app->get_v_sync(); + + ctx->app->set_v_sync(v_sync); + + update_value_text_content(ctx); + game::menu::align_text(ctx); + game::menu::update_text_tweens(ctx); + + // Save v-sync config + (*ctx->config)["v_sync"] = v_sync; + }; + auto increase_font_size_callback = [ctx]() { // Increase font size @@ -341,7 +182,7 @@ void enter(game::context* ctx) ctx->font_size = 2.0f; // Update value text - ctx->graphics_menu_font_size_value_text->set_content(std::to_string(static_cast(std::round(ctx->font_size * 100.0f))) + "%"); + update_value_text_content(ctx); // Update config (*ctx->config)["font_size"] = ctx->font_size; @@ -358,12 +199,10 @@ void enter(game::context* ctx) } ctx->logger->pop_task(EXIT_SUCCESS); - // Refresh text - refresh_texts(ctx); - - // Realign texts - align_texts(ctx); - update_text_tweens(ctx); + // Refresh and realign text + game::menu::refresh_text(ctx); + game::menu::align_text(ctx); + game::menu::update_text_tweens(ctx); }; auto decrease_font_size_callback = [ctx]() @@ -379,7 +218,7 @@ void enter(game::context* ctx) ctx->font_size = 0.1f; // Update value text - ctx->graphics_menu_font_size_value_text->set_content(std::to_string(static_cast(std::round(ctx->font_size * 100.0f))) + "%"); + update_value_text_content(ctx); // Update config (*ctx->config)["font_size"] = ctx->font_size; @@ -396,35 +235,18 @@ void enter(game::context* ctx) } ctx->logger->pop_task(EXIT_SUCCESS); - // Refresh text - refresh_texts(ctx); - - // Realign texts - align_texts(ctx); - update_text_tweens(ctx); - }; - - auto toggle_vsync_callback = [ctx]() - { - bool vsync = !ctx->app->get_vsync(); - - ctx->app->set_vsync(vsync); - - std::string string_on = (*ctx->strings)["on"]; - std::string string_off = (*ctx->strings)["off"]; - ctx->graphics_menu_v_sync_value_text->set_content((vsync) ? string_on : string_off); - - // Save v-sync config - (*ctx->config)["vsync"] = vsync; + // Refresh and realign text + game::menu::refresh_text(ctx); + game::menu::align_text(ctx); + game::menu::update_text_tweens(ctx); }; auto toggle_dyslexia_font_callback = [ctx]() { ctx->dyslexia_font = !ctx->dyslexia_font; - std::string string_on = (*ctx->strings)["on"]; - std::string string_off = (*ctx->strings)["off"]; - ctx->graphics_menu_dyslexia_font_value_text->set_content((ctx->dyslexia_font) ? string_on : string_off); + // Update value text + update_value_text_content(ctx); // Save dyslexia font config (*ctx->config)["dyslexia_font"] = ctx->dyslexia_font; @@ -441,86 +263,58 @@ void enter(game::context* ctx) } ctx->logger->pop_task(EXIT_SUCCESS); - // Refresh text - refresh_texts(ctx); - - // Realign texts - align_texts(ctx); - update_text_tweens(ctx); + // Refresh and realign text + game::menu::refresh_text(ctx); + game::menu::align_text(ctx); + game::menu::update_text_tweens(ctx); + }; + auto select_back_callback = [ctx]() + { + application::state next_state; + next_state.name = "options_menu"; + next_state.enter = std::bind(game::state::options_menu::enter, ctx); + next_state.exit = std::bind(game::state::options_menu::exit, ctx); + ctx->app->change_state(next_state); }; - // Build list of graphics menu callbacks - ctx->graphics_menu_select_callbacks.push_back(toggle_fullscreen_callback); - ctx->graphics_menu_select_callbacks.push_back(increase_render_resolution_callback); - ctx->graphics_menu_select_callbacks.push_back(toggle_vsync_callback); - ctx->graphics_menu_select_callbacks.push_back(increase_font_size_callback); - ctx->graphics_menu_select_callbacks.push_back(toggle_dyslexia_font_callback); - ctx->graphics_menu_select_callbacks.push_back(menu_back_callback); + // Build list of menu select callbacks + ctx->menu_select_callbacks.push_back(toggle_fullscreen_callback); + ctx->menu_select_callbacks.push_back(increase_resolution_callback); + ctx->menu_select_callbacks.push_back(toggle_v_sync_callback); + ctx->menu_select_callbacks.push_back(increase_font_size_callback); + ctx->menu_select_callbacks.push_back(toggle_dyslexia_font_callback); + ctx->menu_select_callbacks.push_back(select_back_callback); - ctx->graphics_menu_left_callbacks.push_back(toggle_fullscreen_callback); - ctx->graphics_menu_left_callbacks.push_back(decrease_render_resolution_callback); - ctx->graphics_menu_left_callbacks.push_back(toggle_vsync_callback); - ctx->graphics_menu_left_callbacks.push_back(decrease_font_size_callback); - ctx->graphics_menu_left_callbacks.push_back(toggle_dyslexia_font_callback); - ctx->graphics_menu_left_callbacks.push_back(nullptr); + // Build list of menu left callbacks + ctx->menu_left_callbacks.push_back(toggle_fullscreen_callback); + ctx->menu_left_callbacks.push_back(decrease_resolution_callback); + ctx->menu_left_callbacks.push_back(toggle_v_sync_callback); + ctx->menu_left_callbacks.push_back(decrease_font_size_callback); + ctx->menu_left_callbacks.push_back(toggle_dyslexia_font_callback); + ctx->menu_left_callbacks.push_back(nullptr); - ctx->graphics_menu_right_callbacks.push_back(toggle_fullscreen_callback); - ctx->graphics_menu_right_callbacks.push_back(increase_render_resolution_callback); - ctx->graphics_menu_right_callbacks.push_back(toggle_vsync_callback); - ctx->graphics_menu_right_callbacks.push_back(increase_font_size_callback); - ctx->graphics_menu_right_callbacks.push_back(toggle_dyslexia_font_callback); - ctx->graphics_menu_right_callbacks.push_back(nullptr); + // Build list of menu right callbacks + ctx->menu_right_callbacks.push_back(toggle_fullscreen_callback); + ctx->menu_right_callbacks.push_back(increase_resolution_callback); + ctx->menu_right_callbacks.push_back(toggle_v_sync_callback); + ctx->menu_right_callbacks.push_back(increase_font_size_callback); + ctx->menu_right_callbacks.push_back(toggle_dyslexia_font_callback); + ctx->menu_right_callbacks.push_back(nullptr); - // Add text objects to UI - for (std::size_t i = 0; i < ctx->graphics_menu_label_texts.size(); ++i) - { - scene::text* label_text = ctx->graphics_menu_label_texts[i]; - scene::text* value_text = ctx->graphics_menu_value_texts[i]; - - ctx->ui_scene->add_object(label_text); - if (value_text) - ctx->ui_scene->add_object(value_text); - } - update_text_color(ctx); - update_text_tweens(ctx); + // Set menu back callback + ctx->menu_back_callback = select_back_callback; + + // Setup menu controls + game::menu::setup_controls(ctx); } void exit(game::context* ctx) { - // Clear control callbacks - ctx->controls["menu_down"]->set_activated_callback(nullptr); - ctx->controls["menu_up"]->set_activated_callback(nullptr); - ctx->controls["menu_left"]->set_activated_callback(nullptr); - ctx->controls["menu_right"]->set_activated_callback(nullptr); - ctx->controls["menu_select"]->set_activated_callback(nullptr); - ctx->controls["menu_back"]->set_activated_callback(nullptr); - - // Clear menu callbacks - ctx->graphics_menu_select_callbacks.clear(); - ctx->graphics_menu_left_callbacks.clear(); - ctx->graphics_menu_right_callbacks.clear(); - - // Destruct graphics menu texts - for (std::size_t i = 0; i < ctx->graphics_menu_label_texts.size(); ++i) - { - scene::text* label_text = ctx->graphics_menu_label_texts[i]; - ctx->ui_scene->remove_object(label_text); - delete label_text; - - scene::text* value_text = ctx->graphics_menu_value_texts[i]; - if (value_text) - { - ctx->ui_scene->remove_object(value_text); - delete value_text; - } - } - ctx->graphics_menu_label_texts.clear(); - ctx->graphics_menu_value_texts.clear(); - - // Update volumes in config - //(*ctx->config)["master_volume"] = ctx->master_volume; - //(*ctx->config)["ambience_volume"] = ctx->ambience_volume; - //(*ctx->config)["effects_volume"] = ctx->effects_volume; + // Destruct menu + game::menu::clear_controls(ctx); + game::menu::clear_callbacks(ctx); + game::menu::remove_text_from_ui(ctx); + game::menu::delete_text(ctx); ctx->ui_clear_pass->set_cleared_buffers(false, true, false); } diff --git a/src/game/states/keyboard-config-menu.cpp b/src/game/states/keyboard-config-menu.cpp new file mode 100644 index 0000000..91fdb45 --- /dev/null +++ b/src/game/states/keyboard-config-menu.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2021 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include "game/states/keyboard-config-menu.hpp" +#include "game/states/controls-menu.hpp" +#include "application.hpp" +#include "scene/text.hpp" +#include "render/passes/clear-pass.hpp" +#include "debug/logger.hpp" +#include "resources/resource-manager.hpp" + +namespace game { +namespace state { +namespace keyboard_config_menu { + +void enter(game::context* ctx) +{ + // Load control profile + if (ctx->config->contains("control_profile")) + { + json* profile = ctx->resource_manager->load((*ctx->config)["control_profile"].get()); + + for (auto& control_element: (*profile)["controls"].items()) + { + ctx->logger->log(control_element.key()); + + + } + } +} + +void exit(game::context* ctx) +{ + +} + +} // namespace keyboard_config_menu +} // namespace state +} // namespace game diff --git a/src/game/states/keyboard-config-menu.hpp b/src/game/states/keyboard-config-menu.hpp new file mode 100644 index 0000000..b180e9e --- /dev/null +++ b/src/game/states/keyboard-config-menu.hpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2021 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_STATE_KEYBOARD_CONFIG_MENU_HPP +#define ANTKEEPER_GAME_STATE_KEYBOARD_CONFIG_MENU_HPP + +#include "game/context.hpp" + +namespace game { +namespace state { + +/// Keyboard/mouse config menu screen game state functions. +namespace keyboard_config_menu { + +void enter(game::context* ctx); +void exit(game::context* ctx); + +} // namespace keyboard_config_menu + +} // namespace state +} // namespace game + +#endif // ANTKEEPER_GAME_STATE_KEYBOARD_CONFIG_MENU_HPP diff --git a/src/game/states/language-menu.cpp b/src/game/states/language-menu.cpp index ca49375..4352b18 100644 --- a/src/game/states/language-menu.cpp +++ b/src/game/states/language-menu.cpp @@ -24,115 +24,50 @@ #include "render/passes/clear-pass.hpp" #include "debug/logger.hpp" #include "game/fonts.hpp" +#include "game/menu.hpp" namespace game { namespace state { namespace language_menu { -static void update_text_font(game::context* ctx) -{ - for (scene::text* text: ctx->language_menu_texts) - { - text->set_material(&ctx->menu_font_material); - text->set_font(&ctx->menu_font); - } -} - -static void update_text_color(game::context* ctx) -{ - float4 inactive_color = {1.0f, 1.0f, 1.0f, 0.5f}; - float4 active_color = {1.0f, 1.0f, 1.0f, 1.0f}; - - for (std::size_t i = 0; i < ctx->language_menu_texts.size(); ++i) - { - scene::text* text = ctx->language_menu_texts[i]; - - if (i == ctx->language_menu_index) - text->set_color(active_color); - else - text->set_color(inactive_color); - } -} - static void update_text_content(game::context* ctx) { - ctx->language_menu_language_text->set_content((*ctx->strings)["language_name"]); - ctx->language_menu_back_text->set_content((*ctx->strings)["back"]); -} - -static void refresh_texts(game::context* ctx) -{ - for (scene::text* text: ctx->language_menu_texts) - { - text->refresh(); - } -} - -static void align_texts(game::context* ctx) -{ - float menu_width = 0.0f; - for (std::size_t i = 0; i < ctx->language_menu_texts.size(); ++i) - { - scene::text* text = ctx->language_menu_texts[i]; - - // Update menu width - const auto& bounds = static_cast&>(text->get_local_bounds()); - float width = bounds.max_point.x - bounds.min_point.x; - menu_width = std::max(menu_width, width); - } + auto [language_name, language_value] = ctx->menu_item_texts[0]; + auto [back_name, back_value] = ctx->menu_item_texts[1]; - float menu_height = ctx->language_menu_texts.size() * ctx->menu_font.get_font_metrics().linespace; - float menu_x = -menu_width * 0.5f; - float menu_y = menu_height * 0.5f - ctx->menu_font.get_font_metrics().linespace; - for (std::size_t i = 0; i < ctx->language_menu_texts.size(); ++i) - { - scene::text* text = ctx->language_menu_texts[i]; - - // Align text - const auto& bounds = static_cast&>(text->get_local_bounds()); - float w = bounds.max_point.x - bounds.min_point.x; - float x = -w * 0.5f; - //float x = menu_x; - float y = menu_y - ctx->menu_font.get_font_metrics().linespace * i; - - text->set_translation({std::round(x), std::round(y), 0.0f}); - } -} - -static void update_text_tweens(game::context* ctx) -{ - for (scene::text* text: ctx->language_menu_texts) - { - text->update_tweens(); - } + language_name->set_content((*ctx->strings)["language_menu_language"]); + language_value->set_content((*ctx->strings)["language_name"]); + back_name->set_content((*ctx->strings)["back"]); } void enter(game::context* ctx) { ctx->ui_clear_pass->set_cleared_buffers(true, true, false); - // Construct language menu texts - ctx->language_menu_language_text = new scene::text(); - ctx->language_menu_back_text = new scene::text(); + // Construct menu item texts + scene::text* language_name_text = new scene::text(); + scene::text* language_value_text = new scene::text(); + scene::text* back_text = new scene::text(); - // Build list of language menu texts - ctx->language_menu_texts.push_back(ctx->language_menu_language_text); - ctx->language_menu_texts.push_back(ctx->language_menu_back_text); + // Build list of menu item texts + ctx->menu_item_texts.push_back({language_name_text, language_value_text}); + ctx->menu_item_texts.push_back({back_text, nullptr}); - // Construct language menu callbacks - auto menu_back_callback = [ctx]() - { - application::state next_state; - next_state.name = "options_menu"; - next_state.enter = std::bind(game::state::options_menu::enter, ctx); - next_state.exit = std::bind(game::state::options_menu::exit, ctx); - ctx->app->change_state(next_state); - }; + // Set content of menu item texts + update_text_content(ctx); + + // Init menu item index + game::menu::init_menu_item_index(ctx, "language"); + + game::menu::update_text_color(ctx); + game::menu::update_text_font(ctx); + game::menu::align_text(ctx); + game::menu::update_text_tweens(ctx); + game::menu::add_text_to_ui(ctx); + + // Construct menu item callbacks auto next_language_callback = [ctx]() { - if (ctx->language_menu_index != 0) - return; - // Increment language index ++ctx->language_index; if (ctx->language_index >= ctx->language_count) @@ -159,17 +94,14 @@ void enter(game::context* ctx) } ctx->logger->pop_task(EXIT_SUCCESS); - update_text_font(ctx); + game::menu::update_text_font(ctx); update_text_content(ctx); - refresh_texts(ctx); - align_texts(ctx); - update_text_tweens(ctx); + game::menu::refresh_text(ctx); + game::menu::align_text(ctx); + game::menu::update_text_tweens(ctx); }; auto previous_language_callback = [ctx]() { - if (ctx->language_menu_index != 0) - return; - // Increment language index --ctx->language_index; if (ctx->language_index < 0) @@ -196,81 +128,47 @@ void enter(game::context* ctx) } ctx->logger->pop_task(EXIT_SUCCESS); - update_text_font(ctx); + game::menu::update_text_font(ctx); update_text_content(ctx); - refresh_texts(ctx); - align_texts(ctx); - update_text_tweens(ctx); + game::menu::refresh_text(ctx); + game::menu::align_text(ctx); + game::menu::update_text_tweens(ctx); + }; + auto select_back_callback = [ctx]() + { + application::state next_state; + next_state.name = "options_menu"; + next_state.enter = std::bind(game::state::options_menu::enter, ctx); + next_state.exit = std::bind(game::state::options_menu::exit, ctx); + ctx->app->change_state(next_state); }; - // Build list of language menu callbacks - ctx->language_menu_callbacks.push_back(next_language_callback); - ctx->language_menu_callbacks.push_back(menu_back_callback); + // Build list of menu select callbacks + ctx->menu_select_callbacks.push_back(next_language_callback); + ctx->menu_select_callbacks.push_back(select_back_callback); - ctx->controls["menu_down"]->set_activated_callback - ( - [ctx]() - { - ++ctx->language_menu_index; - if (ctx->language_menu_index >= ctx->language_menu_texts.size()) - ctx->language_menu_index = 0; - - update_text_color(ctx); - } - ); - ctx->controls["menu_up"]->set_activated_callback - ( - [ctx]() - { - --ctx->language_menu_index; - if (ctx->language_menu_index < 0) - ctx->language_menu_index = ctx->language_menu_texts.size() - 1; - - update_text_color(ctx); - } - ); - ctx->controls["menu_left"]->set_activated_callback(previous_language_callback); - ctx->controls["menu_right"]->set_activated_callback(next_language_callback); - ctx->controls["menu_select"]->set_activated_callback - ( - [ctx]() - { - auto callback = ctx->language_menu_callbacks[ctx->language_menu_index]; - if (callback != nullptr) - callback(); - } - ); - ctx->controls["menu_back"]->set_activated_callback(menu_back_callback); + // Build list of menu left callbacks + ctx->menu_left_callbacks.push_back(previous_language_callback); + ctx->menu_left_callbacks.push_back(nullptr); - for (scene::text* text: ctx->language_menu_texts) - ctx->ui_scene->add_object(text); - update_text_font(ctx); - update_text_color(ctx); - update_text_content(ctx); - align_texts(ctx); - update_text_tweens(ctx); + // Build list of menu right callbacks + ctx->menu_right_callbacks.push_back(next_language_callback); + ctx->menu_right_callbacks.push_back(nullptr); + + // Set menu back callback + ctx->menu_back_callback = select_back_callback; + + // Setup menu controls + game::menu::setup_controls(ctx); } void exit(game::context* ctx) { - // Clear control callbacks - ctx->controls["menu_down"]->set_activated_callback(nullptr); - ctx->controls["menu_up"]->set_activated_callback(nullptr); - ctx->controls["menu_left"]->set_activated_callback(nullptr); - ctx->controls["menu_right"]->set_activated_callback(nullptr); - ctx->controls["menu_select"]->set_activated_callback(nullptr); - ctx->controls["menu_back"]->set_activated_callback(nullptr); - - // Clear language menu callbacks - ctx->language_menu_callbacks.clear(); - - // Destruct language menu texts - for (scene::text* text: ctx->language_menu_texts) - { - ctx->ui_scene->remove_object(text); - delete text; - } - ctx->language_menu_texts.clear(); + // Destruct menu + game::menu::clear_controls(ctx); + game::menu::clear_callbacks(ctx); + game::menu::remove_text_from_ui(ctx); + game::menu::delete_text(ctx); ctx->ui_clear_pass->set_cleared_buffers(false, true, false); } diff --git a/src/game/states/main-menu.cpp b/src/game/states/main-menu.cpp index ffd31fa..9270d7f 100644 --- a/src/game/states/main-menu.cpp +++ b/src/game/states/main-menu.cpp @@ -23,6 +23,7 @@ #include "game/states/forage.hpp" #include "game/states/nuptial-flight.hpp" #include "game/states/credits.hpp" +#include "game/menu.hpp" #include "render/passes/clear-pass.hpp" #include "resources/resource-manager.hpp" #include "render/model.hpp" @@ -42,264 +43,122 @@ void enter(game::context* ctx) { ctx->ui_clear_pass->set_cleared_buffers(true, true, false); - // Construct main menu texts - ctx->main_menu_start_text = new scene::text(); - ctx->main_menu_options_text = new scene::text(); - ctx->main_menu_credits_text = new scene::text(); - ctx->main_menu_quit_text = new scene::text(); - - // Set content of texts - ctx->main_menu_start_text->set_content((*ctx->strings)["main_menu_start"]); - ctx->main_menu_options_text->set_content((*ctx->strings)["main_menu_options"]); - ctx->main_menu_credits_text->set_content((*ctx->strings)["main_menu_credits"]); - ctx->main_menu_quit_text->set_content((*ctx->strings)["main_menu_quit"]); - - std::vector texts; - texts.push_back(ctx->main_menu_start_text); - texts.push_back(ctx->main_menu_options_text); - texts.push_back(ctx->main_menu_credits_text); - texts.push_back(ctx->main_menu_quit_text); - - float offset_y = 0.0f; - - float menu_height = texts.size() * ctx->menu_font.get_font_metrics().linespace; - float menu_y = menu_height * 0.5f - ctx->menu_font.get_font_metrics().linespace * 0.5f; - - for (std::size_t i = 0; i < texts.size(); ++i) + // Construct menu item texts + scene::text* start_text = new scene::text(); + scene::text* options_text = new scene::text(); + scene::text* credits_text = new scene::text(); + scene::text* quit_text = new scene::text(); + + // Build list of menu item texts + ctx->menu_item_texts.push_back({start_text, nullptr}); + ctx->menu_item_texts.push_back({options_text, nullptr}); + ctx->menu_item_texts.push_back({credits_text, nullptr}); + ctx->menu_item_texts.push_back({quit_text, nullptr}); + + // Set content of menu item texts + start_text->set_content((*ctx->strings)["main_menu_start"]); + options_text->set_content((*ctx->strings)["main_menu_options"]); + credits_text->set_content((*ctx->strings)["main_menu_credits"]); + quit_text->set_content((*ctx->strings)["main_menu_quit"]); + + // Init menu item index + game::menu::init_menu_item_index(ctx, "main"); + + game::menu::update_text_color(ctx); + game::menu::update_text_font(ctx); + game::menu::align_text(ctx); + game::menu::update_text_tweens(ctx); + game::menu::add_text_to_ui(ctx); + + auto select_start_callback = [ctx]() { - scene::text* text = texts[i]; - - text->set_material(&ctx->menu_font_material); - text->set_font(&ctx->menu_font); - text->set_color({1.0f, 1.0f, 1.0f, 0.5f}); - ctx->ui_scene->add_object(text); + // Disable controls and menu callbacks + game::menu::clear_controls(ctx); + game::menu::clear_callbacks(ctx); - // Align text - const auto& bounds = static_cast&>(text->get_local_bounds()); - float w = bounds.max_point.x - bounds.min_point.x; - float x = -w * 0.5f; - float y = menu_y - ctx->menu_font.get_font_metrics().linespace * i; - - text->set_translation({std::round(x), std::round(y), 0.0f}); - } - - float advance_x = ctx->menu_font.get_glyph_metrics(U' ').horizontal_advance * 2.0f; - - const auto& text_aabb = static_cast&>(ctx->main_menu_start_text->get_local_bounds()); - const auto& text_translation = ctx->main_menu_start_text->get_translation(); - - ctx->main_menu_start_text->set_color({1.0f, 1.0f, 1.0f, 1.0f}); - - ctx->controls["menu_down"]->set_activated_callback - ( - [ctx]() - { - ++ctx->main_menu_index; - if (ctx->main_menu_index > 3) - ctx->main_menu_index = 0; - - float4 active_color{1.0f, 1.0f, 1.0f, 1.0f}; - float4 inactive_color{1.0f, 1.0f, 1.0f, 0.5f}; - - ctx->main_menu_start_text->set_color((ctx->main_menu_index == 0) ? active_color : inactive_color); - ctx->main_menu_options_text->set_color((ctx->main_menu_index == 1) ? active_color : inactive_color); - ctx->main_menu_credits_text->set_color((ctx->main_menu_index == 2) ? active_color : inactive_color); - ctx->main_menu_quit_text->set_color((ctx->main_menu_index == 3) ? active_color : inactive_color); - } - ); - ctx->controls["menu_up"]->set_activated_callback - ( - [ctx]() - { - --ctx->main_menu_index; - if (ctx->main_menu_index < 0) - ctx->main_menu_index = 3; - - float4 active_color{1.0f, 1.0f, 1.0f, 1.0f}; - float4 inactive_color{1.0f, 1.0f, 1.0f, 0.5f}; - - ctx->main_menu_start_text->set_color((ctx->main_menu_index == 0) ? active_color : inactive_color); - ctx->main_menu_options_text->set_color((ctx->main_menu_index == 1) ? active_color : inactive_color); - ctx->main_menu_credits_text->set_color((ctx->main_menu_index == 2) ? active_color : inactive_color); - ctx->main_menu_quit_text->set_color((ctx->main_menu_index == 3) ? active_color : inactive_color); - } - ); - ctx->controls["menu_back"]->set_activated_callback - ( - [ctx]() + // Create change state function + auto change_state_nuptial_flight = [ctx]() { application::state next_state; - next_state.name = "title"; - next_state.enter = std::bind(game::state::title::enter, ctx); - next_state.exit = std::bind(game::state::title::exit, ctx); + next_state.name = "nuptial_flight"; + next_state.enter = std::bind(game::state::nuptial_flight::enter, ctx); + next_state.exit = std::bind(game::state::nuptial_flight::exit, ctx); ctx->app->change_state(next_state); - } - ); - ctx->controls["menu_select"]->set_activated_callback - ( - [ctx]() - { - if (ctx->main_menu_index == 0) - { - // Disable menu controls - ctx->controls["menu_down"]->set_activated_callback(nullptr); - ctx->controls["menu_up"]->set_activated_callback(nullptr); - ctx->controls["menu_select"]->set_activated_callback(nullptr); - - // Create change state function - auto change_state_nuptial_flight = [ctx]() - { - application::state next_state; - next_state.name = "nuptial_flight"; - next_state.enter = std::bind(game::state::nuptial_flight::enter, ctx); - next_state.exit = std::bind(game::state::nuptial_flight::exit, ctx); - ctx->app->change_state(next_state); - }; - - // Set up timing - const float fade_out_duration = 1.0f; - - // Schedule state change - timeline* timeline = ctx->timeline; - float t = timeline->get_position(); - timeline->add_sequence({{t + fade_out_duration, change_state_nuptial_flight}}); - - // Start fade out to white - ctx->fade_transition_color->set_value({1, 1, 1}); - ctx->fade_transition->transition(fade_out_duration, false, ease::out_quad, false); - } - else if (ctx->main_menu_index == 1) - { - // Disable menu controls - ctx->controls["menu_down"]->set_activated_callback(nullptr); - ctx->controls["menu_up"]->set_activated_callback(nullptr); - ctx->controls["menu_select"]->set_activated_callback(nullptr); - - // Create change state function - auto change_state_options = [ctx]() - { - application::state next_state; - next_state.name = "options_menu"; - next_state.enter = std::bind(game::state::options_menu::enter, ctx); - next_state.exit = std::bind(game::state::options_menu::exit, ctx); - ctx->app->change_state(next_state); - }; - - // Set up timing - const float fade_out_duration = 0.25f; - - // Schedule state change - timeline* timeline = ctx->timeline; - float t = timeline->get_position(); - timeline->add_sequence({{t + fade_out_duration, change_state_options}}); - - // Start fade out to black - ctx->fade_transition_color->set_value({0, 0, 0}); - ctx->fade_transition->transition(fade_out_duration, false, ease::out_quad); - } - else if (ctx->main_menu_index == 2) - { - // Disable menu controls - ctx->controls["menu_down"]->set_activated_callback(nullptr); - ctx->controls["menu_up"]->set_activated_callback(nullptr); - ctx->controls["menu_select"]->set_activated_callback(nullptr); - - // Create change state function - auto change_state_credits = [ctx]() - { - application::state next_state; - next_state.name = "credits"; - next_state.enter = std::bind(game::state::credits::enter, ctx); - next_state.exit = std::bind(game::state::credits::exit, ctx); - ctx->app->change_state(next_state); - }; - - // Set up timing - const float fade_out_duration = 0.5f; - - // Schedule state change - timeline* timeline = ctx->timeline; - float t = timeline->get_position(); - timeline->add_sequence({{t + fade_out_duration, change_state_credits}}); - - // Start fade out to black - ctx->fade_transition_color->set_value({0, 0, 0}); - ctx->fade_transition->transition(fade_out_duration, false, ease::out_quad); - } - else if (ctx->main_menu_index == 3) - { - ctx->app->close(EXIT_SUCCESS); - } - } - ); - - // Disable control callbacks - ctx->controls["menu_down"]->set_callbacks_enabled(false); - ctx->controls["menu_up"]->set_callbacks_enabled(false); - ctx->controls["menu_select"]->set_callbacks_enabled(false); - - // Build main menu fade in animation - ctx->main_menu_fade_animation = new animation(); - animation_channel* main_menu_opacity_channel = ctx->main_menu_fade_animation->add_channel(0); - ctx->main_menu_fade_animation->set_interpolator(ease::out_quad); - double main_menu_fade_in_duration = 0.0; - if (ctx->config->contains("main_menu_fade_in_duration")) - main_menu_fade_in_duration = (*ctx->config)["main_menu_fade_in_duration"].get(); - main_menu_opacity_channel->insert_keyframe({0.0, 0.0f}); - main_menu_opacity_channel->insert_keyframe({main_menu_fade_in_duration, 1.0f}); - - // Adjust main menu text opacity - ctx->main_menu_fade_animation->set_frame_callback - ( - [ctx](int channel, const float& opacity) - { - float4 active_color{1.0f, 1.0f, 1.0f, opacity}; - float4 inactive_color{1.0f, 1.0f, 1.0f, 0.5f * opacity}; - - ctx->main_menu_start_text->set_color((ctx->main_menu_index == 0) ? active_color : inactive_color); - ctx->main_menu_options_text->set_color((ctx->main_menu_index == 1) ? active_color : inactive_color); - ctx->main_menu_credits_text->set_color((ctx->main_menu_index == 2) ? active_color : inactive_color); - ctx->main_menu_quit_text->set_color((ctx->main_menu_index == 3) ? active_color : inactive_color); - - // Enable menu controls when visible - if (opacity > 0.0f) - { - ctx->controls["menu_down"]->set_callbacks_enabled(true); - ctx->controls["menu_up"]->set_callbacks_enabled(true); - ctx->controls["menu_select"]->set_callbacks_enabled(true); - } - } - ); - - ctx->animator->add_animation(ctx->main_menu_fade_animation); - ctx->main_menu_fade_animation->play(); + }; + + // Set up timing + const float fade_out_duration = 1.0f; + + // Schedule state change + timeline* timeline = ctx->timeline; + float t = timeline->get_position(); + timeline->add_sequence({{t + fade_out_duration, change_state_nuptial_flight}}); + + // Start fade out to white + ctx->fade_transition_color->set_value({1, 1, 1}); + ctx->fade_transition->transition(fade_out_duration, false, ease::out_quad, false); + }; + auto select_options_callback = [ctx]() + { + application::state next_state; + next_state.name = "options_menu"; + next_state.enter = std::bind(game::state::options_menu::enter, ctx); + next_state.exit = std::bind(game::state::options_menu::exit, ctx); + ctx->app->change_state(next_state); + }; + auto select_credits_callback = [ctx]() + { + application::state next_state; + next_state.name = "credits"; + next_state.enter = std::bind(game::state::credits::enter, ctx); + next_state.exit = std::bind(game::state::credits::exit, ctx); + ctx->app->change_state(next_state); + }; + auto select_quit_callback = [ctx]() + { + ctx->app->close(EXIT_SUCCESS); + }; + auto menu_back_callback = [ctx]() + { + application::state next_state; + next_state.name = "title"; + next_state.enter = std::bind(game::state::title::enter, ctx); + next_state.exit = std::bind(game::state::title::exit, ctx); + ctx->app->change_state(next_state); + }; + + // Build list of menu select callbacks + ctx->menu_select_callbacks.push_back(select_start_callback); + ctx->menu_select_callbacks.push_back(select_options_callback); + ctx->menu_select_callbacks.push_back(select_credits_callback); + ctx->menu_select_callbacks.push_back(select_quit_callback); + + // Build list of menu left callbacks + ctx->menu_left_callbacks.push_back(nullptr); + ctx->menu_left_callbacks.push_back(nullptr); + ctx->menu_left_callbacks.push_back(nullptr); + ctx->menu_left_callbacks.push_back(nullptr); + + // Build list of menu right callbacks + ctx->menu_right_callbacks.push_back(nullptr); + ctx->menu_right_callbacks.push_back(nullptr); + ctx->menu_right_callbacks.push_back(nullptr); + ctx->menu_right_callbacks.push_back(nullptr); + + // Set menu back callback + ctx->menu_back_callback = menu_back_callback; + + // Setup menu controls + game::menu::setup_controls(ctx); } void exit(game::context* ctx) { - // Disable menu control callbacks - ctx->controls["menu_down"]->set_activated_callback(nullptr); - ctx->controls["menu_up"]->set_activated_callback(nullptr); - ctx->controls["menu_back"]->set_activated_callback(nullptr); - ctx->controls["menu_select"]->set_activated_callback(nullptr); - - // Destruct main menu animation - ctx->animator->remove_animation(ctx->main_menu_fade_animation); - delete ctx->main_menu_fade_animation; - ctx->main_menu_fade_animation = nullptr; - - // Remove text objects from UI - std::vector texts; - texts.push_back(ctx->main_menu_start_text); - texts.push_back(ctx->main_menu_options_text); - texts.push_back(ctx->main_menu_credits_text); - texts.push_back(ctx->main_menu_quit_text); - for (scene::text* text: texts) - { - ctx->ui_scene->remove_object(text); - delete text; - text = nullptr; - } + // Destruct menu + game::menu::clear_controls(ctx); + game::menu::clear_callbacks(ctx); + game::menu::remove_text_from_ui(ctx); + game::menu::delete_text(ctx); ctx->ui_clear_pass->set_cleared_buffers(false, true, false); } diff --git a/src/game/states/options-menu.cpp b/src/game/states/options-menu.cpp index 0680d8a..a3a303e 100644 --- a/src/game/states/options-menu.cpp +++ b/src/game/states/options-menu.cpp @@ -19,11 +19,12 @@ #include "game/states/options-menu.hpp" #include "game/states/main-menu.hpp" -#include "game/states/graphics-menu.hpp" +#include "game/states/controls-menu.hpp" #include "game/states/graphics-menu.hpp" #include "game/states/sound-menu.hpp" #include "game/states/language-menu.hpp" #include "game/save.hpp" +#include "game/menu.hpp" #include "animation/ease.hpp" #include "animation/animation.hpp" #include "animation/animator.hpp" @@ -39,34 +40,46 @@ void enter(game::context* ctx) { ctx->ui_clear_pass->set_cleared_buffers(true, true, false); - // Construct options menu texts - ctx->options_menu_controls_text = new scene::text(); - ctx->options_menu_graphics_text = new scene::text(); - ctx->options_menu_sound_text = new scene::text(); - ctx->options_menu_language_text = new scene::text(); - ctx->options_menu_back_text = new scene::text(); + // Construct menu item texts + scene::text* controls_text = new scene::text(); + scene::text* graphics_text = new scene::text(); + scene::text* sound_text = new scene::text(); + scene::text* language_text = new scene::text(); + scene::text* back_text = new scene::text(); + + // Set content of menu item texts + controls_text->set_content((*ctx->strings)["options_menu_controls"]); + graphics_text->set_content((*ctx->strings)["options_menu_graphics"]); + sound_text->set_content((*ctx->strings)["options_menu_sound"]); + language_text->set_content((*ctx->strings)["options_menu_language"]); + back_text->set_content((*ctx->strings)["back"]); + + // Build list of menu item texts + ctx->menu_item_texts.push_back({controls_text, nullptr}); + ctx->menu_item_texts.push_back({graphics_text, nullptr}); + ctx->menu_item_texts.push_back({sound_text, nullptr}); + ctx->menu_item_texts.push_back({language_text, nullptr}); + ctx->menu_item_texts.push_back({back_text, nullptr}); + + // Init menu item index + game::menu::init_menu_item_index(ctx, "options"); - // Build list of options menu texts - ctx->options_menu_texts.push_back(ctx->options_menu_controls_text); - ctx->options_menu_texts.push_back(ctx->options_menu_graphics_text); - ctx->options_menu_texts.push_back(ctx->options_menu_sound_text); - ctx->options_menu_texts.push_back(ctx->options_menu_language_text); - ctx->options_menu_texts.push_back(ctx->options_menu_back_text); + game::menu::update_text_color(ctx); + game::menu::update_text_font(ctx); + game::menu::align_text(ctx); + game::menu::update_text_tweens(ctx); + game::menu::add_text_to_ui(ctx); - // Construct options menu callbacks - auto menu_back_callback = [ctx]() + // Construct menu item callbacks + auto select_controls_callback = [ctx]() { - // Save config - game::save_config(ctx); - - // Return to main menu application::state next_state; - next_state.name = "main_menu"; - next_state.enter = std::bind(game::state::main_menu::enter, ctx); - next_state.exit = std::bind(game::state::main_menu::exit, ctx); + next_state.name = "controls_menu"; + next_state.enter = std::bind(game::state::controls_menu::enter, ctx); + next_state.exit = std::bind(game::state::controls_menu::exit, ctx); ctx->app->change_state(next_state); }; - auto change_state_graphics_menu = [ctx]() + auto select_graphics_callback = [ctx]() { application::state next_state; next_state.name = "graphics_menu"; @@ -74,7 +87,7 @@ void enter(game::context* ctx) next_state.exit = std::bind(game::state::graphics_menu::exit, ctx); ctx->app->change_state(next_state); }; - auto change_state_sound_menu = [ctx]() + auto select_sound_callback = [ctx]() { application::state next_state; next_state.name = "sound_menu"; @@ -82,7 +95,7 @@ void enter(game::context* ctx) next_state.exit = std::bind(game::state::sound_menu::exit, ctx); ctx->app->change_state(next_state); }; - auto change_state_language_menu = [ctx]() + auto select_language_callback = [ctx]() { application::state next_state; next_state.name = "language_menu"; @@ -90,142 +103,49 @@ void enter(game::context* ctx) next_state.exit = std::bind(game::state::language_menu::exit, ctx); ctx->app->change_state(next_state); }; + auto select_back_callback = [ctx]() + { + // Save config + game::save_config(ctx); + + // Return to main menu + application::state next_state; + next_state.name = "main_menu"; + next_state.enter = std::bind(game::state::main_menu::enter, ctx); + next_state.exit = std::bind(game::state::main_menu::exit, ctx); + ctx->app->change_state(next_state); + }; - // Build list of options menu callbacks - ctx->options_menu_callbacks.push_back(nullptr); - ctx->options_menu_callbacks.push_back(change_state_graphics_menu); - ctx->options_menu_callbacks.push_back(change_state_sound_menu); - ctx->options_menu_callbacks.push_back(change_state_language_menu); - ctx->options_menu_callbacks.push_back(menu_back_callback); + // Build list of menu select callbacks + ctx->menu_select_callbacks.push_back(select_controls_callback); + ctx->menu_select_callbacks.push_back(select_graphics_callback); + ctx->menu_select_callbacks.push_back(select_sound_callback); + ctx->menu_select_callbacks.push_back(select_language_callback); + ctx->menu_select_callbacks.push_back(select_back_callback); - // Set content of texts - ctx->options_menu_controls_text->set_content((*ctx->strings)["options_menu_controls"]); - ctx->options_menu_graphics_text->set_content((*ctx->strings)["options_menu_graphics"]); - ctx->options_menu_sound_text->set_content((*ctx->strings)["options_menu_sound"]); - ctx->options_menu_language_text->set_content((*ctx->strings)["options_menu_language"]); - ctx->options_menu_back_text->set_content((*ctx->strings)["back"]); + // Build list of menu right callbacks + ctx->menu_right_callbacks.resize(5, nullptr); - float4 inactive_color = {1.0f, 1.0f, 1.0f, 0.5f}; - float4 active_color = {1.0f, 1.0f, 1.0f, 1.0f}; - float menu_width = 0.0f; - for (std::size_t i = 0; i < ctx->options_menu_texts.size(); ++i) - { - scene::text* text = ctx->options_menu_texts[i]; - - // Set text material and font - text->set_material(&ctx->menu_font_material); - text->set_font(&ctx->menu_font); - - // Set text color - if (i == ctx->options_menu_index) - text->set_color(active_color); - else - text->set_color(inactive_color); - - // Update menu width - const auto& bounds = static_cast&>(text->get_local_bounds()); - float width = bounds.max_point.x - bounds.min_point.x; - menu_width = std::max(menu_width, width); - - // Add text to UI - ctx->ui_scene->add_object(text); - } + // Build list of menu left callbacks + ctx->menu_left_callbacks.resize(5, nullptr); - // Align texts - float menu_height = ctx->options_menu_texts.size() * ctx->menu_font.get_font_metrics().linespace; - float menu_x = -menu_width * 0.5f; - float menu_y = menu_height * 0.5f - ctx->menu_font.get_font_metrics().linespace; - for (std::size_t i = 0; i < ctx->options_menu_texts.size(); ++i) - { - scene::text* text = ctx->options_menu_texts[i]; - - float x = menu_x; - float y = menu_y - ctx->menu_font.get_font_metrics().linespace * i; - - text->set_translation({std::round(x), std::round(y), 0.0f}); - text->update_tweens(); - } + // Set menu back callback + ctx->menu_back_callback = select_back_callback; - ctx->controls["menu_down"]->set_activated_callback - ( - [ctx]() - { - ++ctx->options_menu_index; - if (ctx->options_menu_index >= ctx->options_menu_texts.size()) - ctx->options_menu_index = 0; - - float4 active_color{1.0f, 1.0f, 1.0f, 1.0f}; - float4 inactive_color{1.0f, 1.0f, 1.0f, 0.5f}; - - for (std::size_t i = 0; i < ctx->options_menu_texts.size(); ++i) - { - scene::text* text = ctx->options_menu_texts[i]; - - if (i == ctx->options_menu_index) - text->set_color(active_color); - else - text->set_color(inactive_color); - } - } - ); - ctx->controls["menu_up"]->set_activated_callback - ( - [ctx]() - { - --ctx->options_menu_index; - if (ctx->options_menu_index < 0) - ctx->options_menu_index = ctx->options_menu_texts.size() - 1; - - float4 active_color{1.0f, 1.0f, 1.0f, 1.0f}; - float4 inactive_color{1.0f, 1.0f, 1.0f, 0.5f}; - - for (std::size_t i = 0; i < ctx->options_menu_texts.size(); ++i) - { - scene::text* text = ctx->options_menu_texts[i]; - - if (i == ctx->options_menu_index) - text->set_color(active_color); - else - text->set_color(inactive_color); - } - } - ); - ctx->controls["menu_select"]->set_activated_callback - ( - [ctx]() - { - auto callback = ctx->options_menu_callbacks[ctx->options_menu_index]; - if (callback != nullptr) - callback(); - } - ); - ctx->controls["menu_back"]->set_activated_callback(menu_back_callback); - /* - ctx->controls["menu_back"]->set_activated_callback - ( - std::bind(&application::close, ctx->app, 0) - ); - */ + // Setup menu controls + game::menu::setup_controls(ctx); } void exit(game::context* ctx) { - // Clear control callbacks - ctx->controls["menu_down"]->set_activated_callback(nullptr); - ctx->controls["menu_up"]->set_activated_callback(nullptr); - ctx->controls["menu_select"]->set_activated_callback(nullptr); - ctx->controls["menu_back"]->set_activated_callback(nullptr); + // Destruct menu + game::menu::clear_controls(ctx); + game::menu::clear_callbacks(ctx); + game::menu::remove_text_from_ui(ctx); + game::menu::delete_text(ctx); - // Clear options menu callbacks - ctx->options_menu_callbacks.clear(); - - // Destruct options menu texts - for (scene::text* text: ctx->options_menu_texts) - { - ctx->ui_scene->remove_object(text); - delete text; - } - ctx->options_menu_texts.clear(); + // Save config + game::save_config(ctx); ctx->ui_clear_pass->set_cleared_buffers(false, true, false); } diff --git a/src/game/states/sound-menu.cpp b/src/game/states/sound-menu.cpp index 502b433..f7bd8b4 100644 --- a/src/game/states/sound-menu.cpp +++ b/src/game/states/sound-menu.cpp @@ -23,182 +23,75 @@ #include "scene/text.hpp" #include "render/passes/clear-pass.hpp" #include "debug/logger.hpp" +#include "game/menu.hpp" namespace game { namespace state { namespace sound_menu { -static void update_text_color(game::context* ctx) +static void update_value_text_content(game::context* ctx) { - float4 inactive_color = {1.0f, 1.0f, 1.0f, 0.5f}; - float4 active_color = {1.0f, 1.0f, 1.0f, 1.0f}; + const std::string string_on = (*ctx->strings)["on"]; + const std::string string_off = (*ctx->strings)["off"]; - for (std::size_t i = 0; i < ctx->sound_menu_label_texts.size(); ++i) - { - scene::text* label_text = ctx->sound_menu_label_texts[i]; - scene::text* value_text = ctx->sound_menu_value_texts[i]; - - if (i == ctx->sound_menu_index) - { - label_text->set_color(active_color); - if (value_text) - value_text->set_color(active_color); - } - else - { - label_text->set_color(inactive_color); - if (value_text) - value_text->set_color(inactive_color); - } - } -} - -static void align_texts(game::context* ctx) -{ - // Calculate menu width - float menu_width = 0.0f; - float menu_spacing = ctx->menu_font.get_glyph_metrics(U'M').width; - for (std::size_t i = 0; i < ctx->sound_menu_label_texts.size(); ++i) - { - scene::text* label_text = ctx->sound_menu_label_texts[i]; - scene::text* value_text = ctx->sound_menu_value_texts[i]; - - float row_width = 0.0f; - - // Add label width to width - const auto& label_bounds = static_cast&>(label_text->get_local_bounds()); - row_width += label_bounds.max_point.x - label_bounds.min_point.x; - - if (value_text != nullptr) - { - // Add value width to width - //const auto& value_bounds = static_cast&>(value_text->get_local_bounds()); - //row_width += value_bounds.max_point.x - value_bounds.min_point.x; - - // Add spacing to row width - row_width += menu_spacing * 4.0f; - } - - menu_width = std::max(menu_width, row_width); - } - - // Align texts - float menu_height = ctx->sound_menu_label_texts.size() * ctx->menu_font.get_font_metrics().linespace; - float menu_x = -menu_width * 0.5f; - float menu_y = menu_height * 0.5f - ctx->menu_font.get_font_metrics().linespace; - for (std::size_t i = 0; i < ctx->sound_menu_label_texts.size(); ++i) - { - scene::text* label_text = ctx->sound_menu_label_texts[i]; - - float x = menu_x; - float y = menu_y - ctx->menu_font.get_font_metrics().linespace * i; - label_text->set_translation({std::round(x), std::round(y), 0.0f}); - - scene::text* value_text = ctx->sound_menu_value_texts[i]; - if (value_text) - { - const auto& value_bounds = static_cast&>(value_text->get_local_bounds()); - const float value_width = value_bounds.max_point.x - value_bounds.min_point.x; - x = menu_x + menu_width - value_width; - - value_text->set_translation({std::round(x), std::round(y), 0.0f}); - } - } -} - -static void update_text_tweens(game::context* ctx) -{ - for (std::size_t i = 0; i < ctx->sound_menu_label_texts.size(); ++i) - { - scene::text* label_text = ctx->sound_menu_label_texts[i]; - scene::text* value_text = ctx->sound_menu_value_texts[i]; - - label_text->update_tweens(); - if (value_text) - value_text->update_tweens(); - } + std::get<1>(ctx->menu_item_texts[0])->set_content(std::to_string(static_cast(std::round(ctx->master_volume * 100.0f))) + "%"); + std::get<1>(ctx->menu_item_texts[1])->set_content(std::to_string(static_cast(std::round(ctx->ambience_volume * 100.0f))) + "%"); + std::get<1>(ctx->menu_item_texts[2])->set_content(std::to_string(static_cast(std::round(ctx->effects_volume * 100.0f))) + "%"); + std::get<1>(ctx->menu_item_texts[3])->set_content((ctx->mono_audio) ? string_on : string_off); + std::get<1>(ctx->menu_item_texts[4])->set_content((ctx->captions) ? string_on : string_off); + std::get<1>(ctx->menu_item_texts[5])->set_content(std::to_string(static_cast(std::round(ctx->captions_size * 100.0f))) + "%"); } void enter(game::context* ctx) { ctx->ui_clear_pass->set_cleared_buffers(true, true, false); - // Construct sound menu texts - ctx->sound_menu_master_volume_label_text = new scene::text(); - ctx->sound_menu_master_volume_value_text = new scene::text(); - ctx->sound_menu_ambience_volume_label_text = new scene::text(); - ctx->sound_menu_ambience_volume_value_text = new scene::text(); - ctx->sound_menu_effects_volume_label_text = new scene::text(); - ctx->sound_menu_effects_volume_value_text = new scene::text(); - ctx->sound_menu_mono_audio_label_text = new scene::text(); - ctx->sound_menu_mono_audio_value_text = new scene::text(); - ctx->sound_menu_captions_label_text = new scene::text(); - ctx->sound_menu_captions_value_text = new scene::text(); - ctx->sound_menu_captions_size_label_text = new scene::text(); - ctx->sound_menu_captions_size_value_text = new scene::text(); - ctx->sound_menu_back_label_text = new scene::text(); - - // Set text content - const std::string string_on = (*ctx->strings)["on"]; - const std::string string_off = (*ctx->strings)["off"]; - ctx->sound_menu_master_volume_label_text->set_content((*ctx->strings)["sound_menu_master_volume"]); - ctx->sound_menu_master_volume_value_text->set_content(std::to_string(static_cast(ctx->master_volume * 100.0f + 0.5f)) + "%"); - ctx->sound_menu_ambience_volume_label_text->set_content((*ctx->strings)["sound_menu_ambience_volume"]); - ctx->sound_menu_ambience_volume_value_text->set_content(std::to_string(static_cast(ctx->ambience_volume * 100.0f + 0.5f)) + "%"); - ctx->sound_menu_effects_volume_label_text->set_content((*ctx->strings)["sound_menu_effects_volume"]); - ctx->sound_menu_effects_volume_value_text->set_content(std::to_string(static_cast(ctx->effects_volume * 100.0f + 0.5f)) + "%"); - ctx->sound_menu_mono_audio_label_text->set_content((*ctx->strings)["sound_menu_mono_audio"]); - ctx->sound_menu_mono_audio_value_text->set_content((ctx->mono_audio) ? string_on : string_off); - ctx->sound_menu_captions_label_text->set_content((*ctx->strings)["sound_menu_captions"]); - ctx->sound_menu_captions_value_text->set_content((ctx->captions) ? string_on : string_off); - ctx->sound_menu_captions_size_label_text->set_content((*ctx->strings)["sound_menu_captions_size"]); - ctx->sound_menu_captions_size_value_text->set_content(std::to_string(static_cast(ctx->captions_size * 100.0f + 0.5f)) + "%"); - ctx->sound_menu_back_label_text->set_content((*ctx->strings)["back"]); - - // Build lists of sound menu texts - ctx->sound_menu_label_texts.push_back(ctx->sound_menu_master_volume_label_text); - ctx->sound_menu_label_texts.push_back(ctx->sound_menu_ambience_volume_label_text); - ctx->sound_menu_label_texts.push_back(ctx->sound_menu_effects_volume_label_text); - ctx->sound_menu_label_texts.push_back(ctx->sound_menu_mono_audio_label_text); - ctx->sound_menu_label_texts.push_back(ctx->sound_menu_captions_label_text); - ctx->sound_menu_label_texts.push_back(ctx->sound_menu_captions_size_label_text); - ctx->sound_menu_label_texts.push_back(ctx->sound_menu_back_label_text); - - ctx->sound_menu_value_texts.push_back(ctx->sound_menu_master_volume_value_text); - ctx->sound_menu_value_texts.push_back(ctx->sound_menu_ambience_volume_value_text); - ctx->sound_menu_value_texts.push_back(ctx->sound_menu_effects_volume_value_text); - ctx->sound_menu_value_texts.push_back(ctx->sound_menu_mono_audio_value_text); - ctx->sound_menu_value_texts.push_back(ctx->sound_menu_captions_value_text); - ctx->sound_menu_value_texts.push_back(ctx->sound_menu_captions_size_value_text); - ctx->sound_menu_value_texts.push_back(nullptr); - - // Set text fonts - for (std::size_t i = 0; i < ctx->sound_menu_label_texts.size(); ++i) - { - scene::text* label_text = ctx->sound_menu_label_texts[i]; - label_text->set_material(&ctx->menu_font_material); - label_text->set_font(&ctx->menu_font); - - scene::text* value_text = ctx->sound_menu_value_texts[i]; - if (value_text) - { - value_text->set_material(&ctx->menu_font_material); - value_text->set_font(&ctx->menu_font); - } - } - - align_texts(ctx); - - // Construct sound menu callbacks - auto menu_back_callback = [ctx]() - { - application::state next_state; - next_state.name = "options_menu"; - next_state.enter = std::bind(game::state::options_menu::enter, ctx); - next_state.exit = std::bind(game::state::options_menu::exit, ctx); - ctx->app->change_state(next_state); - }; - auto increase_volume_callback = [ctx](float* volume, scene::text* value_text) + // Construct menu item texts + scene::text* master_volume_name_text = new scene::text(); + scene::text* master_volume_value_text = new scene::text(); + scene::text* ambience_volume_name_text = new scene::text(); + scene::text* ambience_volume_value_text = new scene::text(); + scene::text* effects_volume_name_text = new scene::text(); + scene::text* effects_volume_value_text = new scene::text(); + scene::text* mono_audio_name_text = new scene::text(); + scene::text* mono_audio_value_text = new scene::text(); + scene::text* captions_name_text = new scene::text(); + scene::text* captions_value_text = new scene::text(); + scene::text* captions_size_name_text = new scene::text(); + scene::text* captions_size_value_text = new scene::text(); + scene::text* back_text = new scene::text(); + + // Build list of menu item texts + ctx->menu_item_texts.push_back({master_volume_name_text, master_volume_value_text}); + ctx->menu_item_texts.push_back({ambience_volume_name_text, ambience_volume_value_text}); + ctx->menu_item_texts.push_back({effects_volume_name_text, effects_volume_value_text}); + ctx->menu_item_texts.push_back({mono_audio_name_text, mono_audio_value_text}); + ctx->menu_item_texts.push_back({captions_name_text, captions_value_text}); + ctx->menu_item_texts.push_back({captions_size_name_text, captions_size_value_text}); + ctx->menu_item_texts.push_back({back_text, nullptr}); + + // Set content of menu item texts + master_volume_name_text->set_content((*ctx->strings)["sound_menu_master_volume"]); + ambience_volume_name_text->set_content((*ctx->strings)["sound_menu_ambience_volume"]); + effects_volume_name_text->set_content((*ctx->strings)["sound_menu_effects_volume"]); + mono_audio_name_text->set_content((*ctx->strings)["sound_menu_mono_audio"]); + captions_name_text->set_content((*ctx->strings)["sound_menu_captions"]); + captions_size_name_text->set_content((*ctx->strings)["sound_menu_captions_size"]); + back_text->set_content((*ctx->strings)["back"]); + update_value_text_content(ctx); + + // Init menu item index + game::menu::init_menu_item_index(ctx, "sound"); + + game::menu::update_text_color(ctx); + game::menu::update_text_font(ctx); + game::menu::align_text(ctx); + game::menu::update_text_tweens(ctx); + game::menu::add_text_to_ui(ctx); + + + // Construct menu item callbacks + auto increase_volume_callback = [ctx](float* volume) { // Increase volume if (ctx->controls["menu_modifier"]->is_active()) @@ -210,14 +103,11 @@ void enter(game::context* ctx) if (*volume > 1.0f) *volume = 1.0f; - // Update volume value text - value_text->set_content(std::to_string(static_cast(std::round(*volume * 100.0f))) + "%"); - - // Realign text - align_texts(ctx); - value_text->update_tweens(); + update_value_text_content(ctx); + game::menu::align_text(ctx); + game::menu::update_text_tweens(ctx); }; - auto decrease_volume_callback = [ctx](float* volume, scene::text* value_text) + auto decrease_volume_callback = [ctx](float* volume) { // Decrease volume if (ctx->controls["menu_modifier"]->is_active()) @@ -229,36 +119,27 @@ void enter(game::context* ctx) if (*volume < 0.0f) *volume = 0.0f; - // Update volume value text - value_text->set_content(std::to_string(static_cast(std::round(*volume * 100.0f))) + "%"); - - // Realign text - align_texts(ctx); - value_text->update_tweens(); + update_value_text_content(ctx); + game::menu::align_text(ctx); + game::menu::update_text_tweens(ctx); }; auto toggle_mono_audio_callback = [ctx]() { ctx->mono_audio = !ctx->mono_audio; - const std::string string_on = (*ctx->strings)["on"]; - const std::string string_off = (*ctx->strings)["off"]; - ctx->sound_menu_mono_audio_value_text->set_content((ctx->mono_audio) ? string_on : string_off); - - align_texts(ctx); - ctx->sound_menu_mono_audio_value_text->update_tweens(); + update_value_text_content(ctx); + game::menu::align_text(ctx); + game::menu::update_text_tweens(ctx); }; auto toggle_captions_callback = [ctx]() { ctx->captions = !ctx->captions; - const std::string string_on = (*ctx->strings)["on"]; - const std::string string_off = (*ctx->strings)["off"]; - ctx->sound_menu_captions_value_text->set_content((ctx->captions) ? string_on : string_off); - - align_texts(ctx); - ctx->sound_menu_captions_value_text->update_tweens(); + update_value_text_content(ctx); + game::menu::align_text(ctx); + game::menu::update_text_tweens(ctx); }; auto increase_captions_size_callback = [ctx]() @@ -273,12 +154,9 @@ void enter(game::context* ctx) if (ctx->captions_size > 2.0f) ctx->captions_size = 2.0f; - // Update volume value text - ctx->sound_menu_captions_size_value_text->set_content(std::to_string(static_cast(std::round(ctx->captions_size * 100.0f))) + "%"); - - // Realign text - align_texts(ctx); - ctx->sound_menu_captions_size_value_text->update_tweens(); + update_value_text_content(ctx); + game::menu::align_text(ctx); + game::menu::update_text_tweens(ctx); }; auto decrease_captions_size_callback = [ctx]() @@ -293,134 +171,62 @@ void enter(game::context* ctx) if (ctx->captions_size < 0.1f) ctx->captions_size = 0.1f; - // Update volume value text - ctx->sound_menu_captions_size_value_text->set_content(std::to_string(static_cast(std::round(ctx->captions_size * 100.0f))) + "%"); - - // Realign text - align_texts(ctx); - ctx->sound_menu_captions_size_value_text->update_tweens(); + update_value_text_content(ctx); + game::menu::align_text(ctx); + game::menu::update_text_tweens(ctx); }; - - ctx->controls["menu_down"]->set_activated_callback - ( - [ctx]() - { - ++ctx->sound_menu_index; - if (ctx->sound_menu_index >= ctx->sound_menu_label_texts.size()) - ctx->sound_menu_index = 0; - - update_text_color(ctx); - } - ); - ctx->controls["menu_up"]->set_activated_callback - ( - [ctx]() - { - --ctx->sound_menu_index; - if (ctx->sound_menu_index < 0) - ctx->sound_menu_index = ctx->sound_menu_label_texts.size() - 1; - - update_text_color(ctx); - } - ); - ctx->controls["menu_left"]->set_activated_callback - ( - [ctx]() - { - if (ctx->sound_menu_left_callbacks[ctx->sound_menu_index]) - ctx->sound_menu_left_callbacks[ctx->sound_menu_index](); - } - ); - ctx->controls["menu_right"]->set_activated_callback - ( - [ctx]() - { - if (ctx->sound_menu_right_callbacks[ctx->sound_menu_index]) - ctx->sound_menu_right_callbacks[ctx->sound_menu_index](); - } - ); - ctx->controls["menu_select"]->set_activated_callback - ( - [ctx]() - { - if (ctx->sound_menu_select_callbacks[ctx->sound_menu_index]) - ctx->sound_menu_select_callbacks[ctx->sound_menu_index](); - } - ); - ctx->controls["menu_back"]->set_activated_callback(menu_back_callback); - - // Build list of sound menu callbacks - ctx->sound_menu_select_callbacks.push_back(std::bind(increase_volume_callback, &ctx->master_volume, ctx->sound_menu_master_volume_value_text)); - ctx->sound_menu_select_callbacks.push_back(std::bind(increase_volume_callback, &ctx->ambience_volume, ctx->sound_menu_ambience_volume_value_text)); - ctx->sound_menu_select_callbacks.push_back(std::bind(increase_volume_callback, &ctx->effects_volume, ctx->sound_menu_effects_volume_value_text)); - ctx->sound_menu_select_callbacks.push_back(toggle_mono_audio_callback); - ctx->sound_menu_select_callbacks.push_back(toggle_captions_callback); - ctx->sound_menu_select_callbacks.push_back(increase_captions_size_callback); - ctx->sound_menu_select_callbacks.push_back(menu_back_callback); - - ctx->sound_menu_right_callbacks.push_back(std::bind(increase_volume_callback, &ctx->master_volume, ctx->sound_menu_master_volume_value_text)); - ctx->sound_menu_right_callbacks.push_back(std::bind(increase_volume_callback, &ctx->ambience_volume, ctx->sound_menu_ambience_volume_value_text)); - ctx->sound_menu_right_callbacks.push_back(std::bind(increase_volume_callback, &ctx->effects_volume, ctx->sound_menu_effects_volume_value_text)); - ctx->sound_menu_right_callbacks.push_back(toggle_mono_audio_callback); - ctx->sound_menu_right_callbacks.push_back(toggle_captions_callback); - ctx->sound_menu_right_callbacks.push_back(increase_captions_size_callback); - ctx->sound_menu_right_callbacks.push_back(nullptr); - - ctx->sound_menu_left_callbacks.push_back(std::bind(decrease_volume_callback, &ctx->master_volume, ctx->sound_menu_master_volume_value_text)); - ctx->sound_menu_left_callbacks.push_back(std::bind(decrease_volume_callback, &ctx->ambience_volume, ctx->sound_menu_ambience_volume_value_text)); - ctx->sound_menu_left_callbacks.push_back(std::bind(decrease_volume_callback, &ctx->effects_volume, ctx->sound_menu_effects_volume_value_text)); - ctx->sound_menu_left_callbacks.push_back(toggle_mono_audio_callback); - ctx->sound_menu_left_callbacks.push_back(toggle_captions_callback); - ctx->sound_menu_left_callbacks.push_back(decrease_captions_size_callback); - ctx->sound_menu_left_callbacks.push_back(nullptr); - - // Add text objects to UI - for (std::size_t i = 0; i < ctx->sound_menu_label_texts.size(); ++i) + auto select_back_callback = [ctx]() { - scene::text* label_text = ctx->sound_menu_label_texts[i]; - scene::text* value_text = ctx->sound_menu_value_texts[i]; - - ctx->ui_scene->add_object(label_text); - if (value_text) - ctx->ui_scene->add_object(value_text); - } - update_text_color(ctx); - update_text_tweens(ctx); + application::state next_state; + next_state.name = "options_menu"; + next_state.enter = std::bind(game::state::options_menu::enter, ctx); + next_state.exit = std::bind(game::state::options_menu::exit, ctx); + ctx->app->change_state(next_state); + }; + + // Build list of menu select callbacks + ctx->menu_select_callbacks.push_back(std::bind(increase_volume_callback, &ctx->master_volume)); + ctx->menu_select_callbacks.push_back(std::bind(increase_volume_callback, &ctx->ambience_volume)); + ctx->menu_select_callbacks.push_back(std::bind(increase_volume_callback, &ctx->effects_volume)); + ctx->menu_select_callbacks.push_back(toggle_mono_audio_callback); + ctx->menu_select_callbacks.push_back(toggle_captions_callback); + ctx->menu_select_callbacks.push_back(increase_captions_size_callback); + ctx->menu_select_callbacks.push_back(select_back_callback); + + // Build list of menu left callbacks + ctx->menu_left_callbacks.push_back(std::bind(decrease_volume_callback, &ctx->master_volume)); + ctx->menu_left_callbacks.push_back(std::bind(decrease_volume_callback, &ctx->ambience_volume)); + ctx->menu_left_callbacks.push_back(std::bind(decrease_volume_callback, &ctx->effects_volume)); + ctx->menu_left_callbacks.push_back(toggle_mono_audio_callback); + ctx->menu_left_callbacks.push_back(toggle_captions_callback); + ctx->menu_left_callbacks.push_back(decrease_captions_size_callback); + ctx->menu_left_callbacks.push_back(nullptr); + + // Build list of menu right callbacks + ctx->menu_right_callbacks.push_back(std::bind(increase_volume_callback, &ctx->master_volume)); + ctx->menu_right_callbacks.push_back(std::bind(increase_volume_callback, &ctx->ambience_volume)); + ctx->menu_right_callbacks.push_back(std::bind(increase_volume_callback, &ctx->effects_volume)); + ctx->menu_right_callbacks.push_back(toggle_mono_audio_callback); + ctx->menu_right_callbacks.push_back(toggle_captions_callback); + ctx->menu_right_callbacks.push_back(increase_captions_size_callback); + ctx->menu_right_callbacks.push_back(nullptr); + + // Set menu back callback + ctx->menu_back_callback = select_back_callback; + + // Setup menu controls + game::menu::setup_controls(ctx); } void exit(game::context* ctx) { - // Clear control callbacks - ctx->controls["menu_down"]->set_activated_callback(nullptr); - ctx->controls["menu_up"]->set_activated_callback(nullptr); - ctx->controls["menu_left"]->set_activated_callback(nullptr); - ctx->controls["menu_right"]->set_activated_callback(nullptr); - ctx->controls["menu_select"]->set_activated_callback(nullptr); - ctx->controls["menu_back"]->set_activated_callback(nullptr); - - // Clear menu callbacks - ctx->sound_menu_select_callbacks.clear(); - ctx->sound_menu_left_callbacks.clear(); - ctx->sound_menu_right_callbacks.clear(); - - // Destruct sound menu texts - for (std::size_t i = 0; i < ctx->sound_menu_label_texts.size(); ++i) - { - scene::text* label_text = ctx->sound_menu_label_texts[i]; - ctx->ui_scene->remove_object(label_text); - delete label_text; - - scene::text* value_text = ctx->sound_menu_value_texts[i]; - if (value_text) - { - ctx->ui_scene->remove_object(value_text); - delete value_text; - } - } - ctx->sound_menu_label_texts.clear(); - ctx->sound_menu_value_texts.clear(); + // Destruct menu + game::menu::clear_controls(ctx); + game::menu::clear_callbacks(ctx); + game::menu::remove_text_from_ui(ctx); + game::menu::delete_text(ctx); - // Update volumes in config + // Update config (*ctx->config)["master_volume"] = ctx->master_volume; (*ctx->config)["ambience_volume"] = ctx->ambience_volume; (*ctx->config)["effects_volume"] = ctx->effects_volume; diff --git a/src/ui/mouse-tracker.cpp b/src/ui/mouse-tracker.cpp new file mode 100644 index 0000000..8b10cbe --- /dev/null +++ b/src/ui/mouse-tracker.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2021 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include "ui/mouse-tracker.hpp" + +namespace ui { + +mouse_tracker::mouse_tracker(): + mouse_moved_callback(nullptr), + mouse_button_pressed_callback(nullptr), + mouse_button_released_callback(nullptr), + mouse_wheel_scrolled_callback(nullptr) +{} + +void mouse_tracker::set_mouse_moved_callback(const std::function& callback) +{ + mouse_moved_callback = callback; +} + +void mouse_tracker::set_mouse_button_pressed_callback(const std::function& callback) +{ + mouse_button_pressed_callback = callback; +} + +void mouse_tracker::set_mouse_button_released_callback(const std::function& callback) +{ + mouse_button_released_callback = callback; +} + +void mouse_tracker::set_mouse_wheel_scrolled_callback(const std::function& callback) +{ + mouse_wheel_scrolled_callback = callback; +} + +void mouse_tracker::handle_event(const mouse_moved_event& event) +{ + if (mouse_moved_callback) + mouse_moved_callback(event); +} + +void mouse_tracker::handle_event(const mouse_button_pressed_event& event) +{ + if (mouse_button_pressed_callback) + mouse_button_pressed_callback(event); +} + +void mouse_tracker::handle_event(const mouse_button_released_event& event) +{ + if (mouse_button_released_callback) + mouse_button_released_callback(event); +} + +void mouse_tracker::handle_event(const mouse_wheel_scrolled_event& event) +{ + if (mouse_wheel_scrolled_callback) + mouse_wheel_scrolled_callback(event); +} + +} // namespace ui diff --git a/src/ui/mouse-tracker.hpp b/src/ui/mouse-tracker.hpp new file mode 100644 index 0000000..1e2a8bc --- /dev/null +++ b/src/ui/mouse-tracker.hpp @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2021 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_UI_MOUSE_TRACKER_HPP +#define ANTKEEPER_UI_MOUSE_TRACKER_HPP + +#include "event/input-events.hpp" +#include "event/event-handler.hpp" +#include + +namespace ui { + +class mouse_tracker: + public event_handler, + public event_handler, + public event_handler, + public event_handler +{ +public: + mouse_tracker(); + virtual ~mouse_tracker() = default; + + void set_mouse_moved_callback(const std::function& callback); + void set_mouse_button_pressed_callback(const std::function& callback); + void set_mouse_button_released_callback(const std::function& callback); + void set_mouse_wheel_scrolled_callback(const std::function& callback); + +private: + virtual void handle_event(const mouse_moved_event& event); + virtual void handle_event(const mouse_button_pressed_event& event); + virtual void handle_event(const mouse_button_released_event& event); + virtual void handle_event(const mouse_wheel_scrolled_event& event); + + std::function mouse_moved_callback; + std::function mouse_button_pressed_callback; + std::function mouse_button_released_callback; + std::function mouse_wheel_scrolled_callback; +}; + +} // namespace ui + +#endif // ANTKEEPER_UI_MOUSE_TRACKER_HPP