Browse Source

Add suport for mouse navigation in menus

master
C. J. Howard 2 years ago
parent
commit
a3c59ce1d7
19 changed files with 1425 additions and 1242 deletions
  1. +14
    -14
      src/application.cpp
  2. +6
    -6
      src/application.hpp
  3. +27
    -1
      src/game/context.hpp
  4. +348
    -0
      src/game/menu.cpp
  5. +52
    -0
      src/game/menu.hpp
  6. +16
    -7
      src/game/states/boot.cpp
  7. +43
    -0
      src/game/states/controller-config-menu.cpp
  8. +39
    -0
      src/game/states/controller-config-menu.hpp
  9. +123
    -0
      src/game/states/controls-menu.cpp
  10. +39
    -0
      src/game/states/controls-menu.hpp
  11. +129
    -335
      src/game/states/graphics-menu.cpp
  12. +55
    -0
      src/game/states/keyboard-config-menu.cpp
  13. +39
    -0
      src/game/states/keyboard-config-menu.hpp
  14. +62
    -164
      src/game/states/language-menu.cpp
  15. +108
    -249
      src/game/states/main-menu.cpp
  16. +71
    -151
      src/game/states/options-menu.cpp
  17. +121
    -315
      src/game/states/sound-menu.cpp
  18. +75
    -0
      src/ui/mouse-tracker.cpp
  19. +58
    -0
      src/ui/mouse-tracker.hpp

+ 14
- 14
src/application.cpp View File

@ -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()

+ 6
- 6
src/application.hpp View File

@ -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<int, 2> 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()

+ 27
- 1
src/game/context.hpp View File

@ -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<bool> option_new_game;
std::optional<bool> option_quick_start;
std::optional<bool> option_reset;
std::optional<int> option_vsync;
std::optional<int> option_v_sync;
std::optional<bool> 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<scene::text*> 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<scene::text*> options_menu_texts;
std::vector<std::function<void()>> 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<scene::text*> controls_menu_label_texts;
std::vector<scene::text*> controls_menu_value_texts;
int controls_menu_index;
std::vector<std::function<void()>> controls_menu_select_callbacks;
std::vector<std::function<void()>> controls_menu_left_callbacks;
std::vector<std::function<void()>> controls_menu_right_callbacks;
scene::text* language_menu_language_text;
scene::text* language_menu_back_text;
std::vector<scene::text*> language_menu_texts;
@ -259,8 +270,23 @@ struct context
std::vector<std::function<void()>> graphics_menu_select_callbacks;
std::vector<std::function<void()>> graphics_menu_left_callbacks;
std::vector<std::function<void()>> graphics_menu_right_callbacks;
std::vector<scene::text*> keyboard_config_menu_label_texts;
std::vector<scene::text*> keyboard_config_menu_value_texts;
int keyboard_config_menu_index;
std::vector<std::function<void()>> keyboard_config_menu_select_callbacks;
std::vector<std::function<void()>> keyboard_config_menu_left_callbacks;
std::vector<std::function<void()>> keyboard_config_menu_right_callbacks;
float font_size;
bool dyslexia_font;
ui::mouse_tracker* menu_mouse_tracker;
std::vector<std::function<void()>> menu_select_callbacks;
std::vector<std::function<void()>> menu_left_callbacks;
std::vector<std::function<void()>> menu_right_callbacks;
std::function<void()> menu_back_callback;
std::vector<std::tuple<scene::text*, scene::text*>> menu_item_texts;
std::unordered_map<std::string, int> menu_item_indices;
int* menu_item_index;
// Surface scene
scene::collection* surface_scene;

+ 348
- 0
src/game/menu.cpp View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include "game/menu.hpp"
#include "scene/text.hpp"
#include "application.hpp"
#include <algorithm>
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<const geom::aabb<float>&>(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<const geom::aabb<float>&>(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<float>(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<const geom::aabb<float>&>(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<const geom::aabb<float>&>(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<const geom::aabb<float>&>(value->get_world_bounds());
min_x = std::min<float>(min_x, value_bounds.min_point.x);
min_y = std::min<float>(min_y, value_bounds.min_point.y);
max_x = std::max<float>(max_x, value_bounds.max_point.x);
max_y = std::max<float>(max_y, value_bounds.max_point.y);
}
const auto& viewport = ctx->app->get_viewport_dimensions();
const float x = static_cast<float>(event.x - viewport[0] / 2);
const float y = static_cast<float>((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<const geom::aabb<float>&>(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<const geom::aabb<float>&>(value->get_world_bounds());
min_x = std::min<float>(min_x, value_bounds.min_point.x);
min_y = std::min<float>(min_y, value_bounds.min_point.y);
max_x = std::max<float>(max_x, value_bounds.max_point.x);
max_y = std::max<float>(max_y, value_bounds.max_point.y);
}
const auto& viewport = ctx->app->get_viewport_dimensions();
const float x = static_cast<float>(event.x - viewport[0] / 2);
const float y = static_cast<float>((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

+ 52
- 0
src/game/menu.hpp View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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

+ 16
- 7
src/game/states/boot.cpp View File

@ -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<int>()) ? true : false;
ctx->option_v_sync = (result["vsync"].as<int>()) ? 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<bool>();
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<bool>();
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<mouse_moved_event>(ctx->menu_mouse_tracker);
ctx->app->get_event_dispatcher()->subscribe<mouse_button_pressed_event>(ctx->menu_mouse_tracker);
ctx->app->get_event_dispatcher()->subscribe<mouse_button_released_event>(ctx->menu_mouse_tracker);
ctx->app->get_event_dispatcher()->subscribe<mouse_wheel_scrolled_event>(ctx->menu_mouse_tracker);
}
void setup_cli(game::context* ctx)

+ 43
- 0
src/game/states/controller-config-menu.cpp View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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

+ 39
- 0
src/game/states/controller-config-menu.hpp View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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

+ 123
- 0
src/game/states/controls-menu.cpp View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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

+ 39
- 0
src/game/states/controls-menu.hpp View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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

+ 129
- 335
src/game/states/graphics-menu.cpp View File

@ -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<const geom::aabb<float>&>(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<const geom::aabb<float>&>(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<float>(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<const geom::aabb<float>&>(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<int>(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<int>(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<int>(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<int>(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<int>(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<int>(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<int>(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<int>(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);
}

+ 55
- 0
src/game/states/keyboard-config-menu.cpp View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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<json>((*ctx->config)["control_profile"].get<std::string>());
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

+ 39
- 0
src/game/states/keyboard-config-menu.hpp View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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

+ 62
- 164
src/game/states/language-menu.cpp View File

@ -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<const geom::aabb<float>&>(text->get_local_bounds());
float width = bounds.max_point.x - bounds.min_point.x;
menu_width = std::max<float>(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<const geom::aabb<float>&>(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);
}

+ 108
- 249
src/game/states/main-menu.cpp View File

@ -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<scene::text*> 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<const geom::aabb<float>&>(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<const geom::aabb<float>&>(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<float>::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<float>::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<float>::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<float>();
animation_channel<float>* main_menu_opacity_channel = ctx->main_menu_fade_animation->add_channel(0);
ctx->main_menu_fade_animation->set_interpolator(ease<float>::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<double>();
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<float>::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<scene::text*> 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);
}

+ 71
- 151
src/game/states/options-menu.cpp View File

@ -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<const geom::aabb<float>&>(text->get_local_bounds());
float width = bounds.max_point.x - bounds.min_point.x;
menu_width = std::max<float>(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);
}

+ 121
- 315
src/game/states/sound-menu.cpp View File

@ -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<const geom::aabb<float>&>(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<const geom::aabb<float>&>(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<float>(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<const geom::aabb<float>&>(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<int>(std::round(ctx->master_volume * 100.0f))) + "%");
std::get<1>(ctx->menu_item_texts[1])->set_content(std::to_string(static_cast<int>(std::round(ctx->ambience_volume * 100.0f))) + "%");
std::get<1>(ctx->menu_item_texts[2])->set_content(std::to_string(static_cast<int>(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<int>(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<int>(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<int>(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<int>(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<int>(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<int>(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<int>(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<int>(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<int>(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;

+ 75
- 0
src/ui/mouse-tracker.cpp View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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<void(const mouse_moved_event&)>& callback)
{
mouse_moved_callback = callback;
}
void mouse_tracker::set_mouse_button_pressed_callback(const std::function<void(const mouse_button_pressed_event&)>& callback)
{
mouse_button_pressed_callback = callback;
}
void mouse_tracker::set_mouse_button_released_callback(const std::function<void(const mouse_button_released_event&)>& callback)
{
mouse_button_released_callback = callback;
}
void mouse_tracker::set_mouse_wheel_scrolled_callback(const std::function<void(const mouse_wheel_scrolled_event&)>& 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

+ 58
- 0
src/ui/mouse-tracker.hpp View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_UI_MOUSE_TRACKER_HPP
#define ANTKEEPER_UI_MOUSE_TRACKER_HPP
#include "event/input-events.hpp"
#include "event/event-handler.hpp"
#include <functional>
namespace ui {
class mouse_tracker:
public event_handler<mouse_moved_event>,
public event_handler<mouse_button_pressed_event>,
public event_handler<mouse_button_released_event>,
public event_handler<mouse_wheel_scrolled_event>
{
public:
mouse_tracker();
virtual ~mouse_tracker() = default;
void set_mouse_moved_callback(const std::function<void(const mouse_moved_event&)>& callback);
void set_mouse_button_pressed_callback(const std::function<void(const mouse_button_pressed_event&)>& callback);
void set_mouse_button_released_callback(const std::function<void(const mouse_button_released_event&)>& callback);
void set_mouse_wheel_scrolled_callback(const std::function<void(const mouse_wheel_scrolled_event&)>& 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<void(const mouse_moved_event&)> mouse_moved_callback;
std::function<void(const mouse_button_pressed_event&)> mouse_button_pressed_callback;
std::function<void(const mouse_button_released_event&)> mouse_button_released_callback;
std::function<void(const mouse_wheel_scrolled_event&)> mouse_wheel_scrolled_callback;
};
} // namespace ui
#endif // ANTKEEPER_UI_MOUSE_TRACKER_HPP

Loading…
Cancel
Save