@ -0,0 +1,178 @@ | |||
/* | |||
* 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/fonts.hpp" | |||
#include "application.hpp" | |||
#include "type/type.hpp" | |||
#include "resources/resource-manager.hpp" | |||
#include "gl/texture-wrapping.hpp" | |||
#include "gl/texture-filter.hpp" | |||
#include "render/material.hpp" | |||
#include "render/material-flags.hpp" | |||
#include <codecvt> | |||
namespace game { | |||
static void build_bitmap_font(const type::typeface& typeface, float size, const std::unordered_set<char32_t>& charset, type::bitmap_font& font, render::material& material, gl::shader_program* shader_program) | |||
{ | |||
// Get font metrics for given size | |||
if (type::font_metrics metrics; typeface.get_metrics(size, metrics)) | |||
font.set_font_metrics(metrics); | |||
// Format font bitmap | |||
image& font_bitmap = font.get_bitmap(); | |||
font_bitmap.format(sizeof(std::byte), 1); | |||
// For each UTF-32 character code in the character set | |||
for (char32_t code: charset) | |||
{ | |||
// Skip missing glyphs | |||
if (!typeface.has_glyph(code)) | |||
continue; | |||
// Add glyph to font | |||
type::bitmap_glyph& glyph = font[code]; | |||
typeface.get_metrics(size, code, glyph.metrics); | |||
typeface.get_bitmap(size, code, glyph.bitmap); | |||
} | |||
// Pack glyph bitmaps into the font bitmap | |||
font.pack(); | |||
// Create font texture from bitmap | |||
gl::texture_2d* font_texture = new gl::texture_2d(font_bitmap.get_width(), font_bitmap.get_height(), gl::pixel_type::uint_8, gl::pixel_format::r, gl::color_space::linear, font_bitmap.get_pixels()); | |||
font_texture->set_wrapping(gl::texture_wrapping::extend, gl::texture_wrapping::extend); | |||
font_texture->set_filters(gl::texture_min_filter::linear, gl::texture_mag_filter::linear); | |||
// Create font material | |||
material.set_flags(MATERIAL_FLAG_TRANSLUCENT); | |||
material.add_property<const gl::texture_2d*>("font_bitmap")->set_value(font_texture); | |||
material.set_shader_program(shader_program); | |||
} | |||
void load_fonts(game::context* ctx) | |||
{ | |||
// Load dyslexia-friendly typeface (if enabled) | |||
bool dyslexia_font = false; | |||
if (ctx->config->contains("dyslexia_font")) | |||
{ | |||
dyslexia_font = (*ctx->config)["dyslexia_font"].get<bool>(); | |||
if (dyslexia_font) | |||
{ | |||
if (auto it = ctx->strings->find("font_dyslexia"); it != ctx->strings->end() && !it->second.empty() && it->second[0] != '#') | |||
{ | |||
ctx->logger->log(it->second); | |||
ctx->typefaces["dyslexia"] = ctx->resource_manager->load<type::typeface>(it->second); | |||
} | |||
else | |||
{ | |||
dyslexia_font = false; | |||
} | |||
} | |||
} | |||
// Load typefaces | |||
if (dyslexia_font) | |||
{ | |||
// Override standard typefaces with dyslexia-friendly typeface | |||
ctx->typefaces["serif"] = ctx->typefaces["dyslexia"]; | |||
ctx->typefaces["sans_serif"] = ctx->typefaces["dyslexia"]; | |||
ctx->typefaces["monospace"] = ctx->typefaces["dyslexia"]; | |||
} | |||
else | |||
{ | |||
// Load standard typefaces | |||
if (auto it = ctx->strings->find("font_serif"); it != ctx->strings->end()) | |||
ctx->typefaces["serif"] = ctx->resource_manager->load<type::typeface>(it->second); | |||
if (auto it = ctx->strings->find("font_sans_serif"); it != ctx->strings->end()) | |||
ctx->typefaces["sans_serif"] = ctx->resource_manager->load<type::typeface>(it->second); | |||
if (auto it = ctx->strings->find("font_monospace"); it != ctx->strings->end()) | |||
ctx->typefaces["monospace"] = ctx->resource_manager->load<type::typeface>(it->second); | |||
} | |||
// Build character set | |||
std::unordered_set<char32_t> charset; | |||
{ | |||
// Add all character codes from the basic Latin unicode block | |||
for (char32_t code = type::unicode::block::basic_latin.first; code <= type::unicode::block::basic_latin.last; ++code) | |||
charset.insert(code); | |||
// Add all character codes from game strings | |||
for (auto it = ctx->strings->begin(); it != ctx->strings->end(); ++it) | |||
{ | |||
// Convert UTF-8 string to UTF-32 | |||
std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> convert; | |||
std::u32string u32 = convert.from_bytes(it->second); | |||
/// Insert each character code from the UTF-32 string into the character set | |||
for (char32_t code: u32) | |||
charset.insert(code); | |||
} | |||
} | |||
// Load bitmap font shader | |||
gl::shader_program* bitmap_font_shader = ctx->resource_manager->load<gl::shader_program>("bitmap-font.glsl"); | |||
// Determine font point sizes | |||
float debug_font_size_pt = 12.0f; | |||
float menu_font_size_pt = 12.0f; | |||
float title_font_size_pt = 12.0f; | |||
if (ctx->config->contains("debug_font_size")) | |||
debug_font_size_pt = (*ctx->config)["debug_font_size"].get<float>(); | |||
if (ctx->config->contains("menu_font_size")) | |||
menu_font_size_pt = (*ctx->config)["menu_font_size"].get<float>(); | |||
if (ctx->config->contains("title_font_size")) | |||
title_font_size_pt = (*ctx->config)["title_font_size"].get<float>(); | |||
// Scale font point sizes | |||
if (ctx->config->contains("font_scale")) | |||
{ | |||
const float font_scale = (*ctx->config)["font_scale"].get<float>(); | |||
debug_font_size_pt *= font_scale; | |||
menu_font_size_pt *= font_scale; | |||
title_font_size_pt *= font_scale; | |||
} | |||
// Convert font point sizes to pixel sizes | |||
const float dpi = ctx->app->get_display_dpi(); | |||
const float debug_font_size_px = (debug_font_size_pt * dpi) / 72.0f; | |||
const float menu_font_size_px = (menu_font_size_pt * dpi) / 72.0f; | |||
const float title_font_size_px = (title_font_size_pt * dpi) / 72.0f; | |||
// Build debug font | |||
if (auto it = ctx->typefaces.find("monospace"); it != ctx->typefaces.end()) | |||
{ | |||
build_bitmap_font(*it->second, debug_font_size_px, charset, ctx->debug_font, ctx->debug_font_material, bitmap_font_shader); | |||
} | |||
// Build menu font | |||
if (auto it = ctx->typefaces.find("sans_serif"); it != ctx->typefaces.end()) | |||
{ | |||
build_bitmap_font(*it->second, menu_font_size_px, charset, ctx->menu_font, ctx->menu_font_material, bitmap_font_shader); | |||
} | |||
// Build title font | |||
if (auto it = ctx->typefaces.find("serif"); it != ctx->typefaces.end()) | |||
{ | |||
build_bitmap_font(*it->second, title_font_size_px, charset, ctx->title_font, ctx->title_font_material, bitmap_font_shader); | |||
} | |||
} | |||
} // namespace game |
@ -0,0 +1,31 @@ | |||
/* | |||
* 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_FONTS_HPP | |||
#define ANTKEEPER_GAME_FONTS_HPP | |||
#include "game/context.hpp" | |||
namespace game { | |||
void load_fonts(game::context* ctx); | |||
} // namespace game | |||
#endif // ANTKEEPER_GAME_FONTS_HPP |
@ -0,0 +1,142 @@ | |||
/* | |||
* 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/credits.hpp" | |||
#include "game/states/main-menu.hpp" | |||
#include "animation/ease.hpp" | |||
#include "animation/animation.hpp" | |||
#include "animation/animator.hpp" | |||
#include "application.hpp" | |||
#include "scene/text.hpp" | |||
#include "render/passes/clear-pass.hpp" | |||
namespace game { | |||
namespace state { | |||
namespace credits { | |||
void enter(game::context* ctx) | |||
{ | |||
ctx->ui_clear_pass->set_cleared_buffers(true, true, false); | |||
// Construct credits text | |||
ctx->credits_text = new scene::text(); | |||
ctx->credits_text->set_material(&ctx->menu_font_material); | |||
ctx->credits_text->set_font(&ctx->menu_font); | |||
ctx->credits_text->set_color({1.0f, 1.0f, 1.0f, 0.0f}); | |||
ctx->credits_text->set_content((*ctx->strings)["credits"]); | |||
// Align credits text | |||
const auto& credits_aabb = static_cast<const geom::aabb<float>&>(ctx->credits_text->get_local_bounds()); | |||
float credits_w = credits_aabb.max_point.x - credits_aabb.min_point.x; | |||
float credits_h = credits_aabb.max_point.y - credits_aabb.min_point.y; | |||
ctx->credits_text->set_translation({std::round(-credits_w * 0.5f), std::round(-credits_h * 0.5f), 0.0f}); | |||
// Load animation timing configuration | |||
double credits_fade_in_duration = 0.0; | |||
double credits_scroll_duration = 0.0; | |||
if (ctx->config->contains("credits_fade_in_duration")) | |||
credits_fade_in_duration = (*ctx->config)["credits_fade_in_duration"].get<double>(); | |||
if (ctx->config->contains("credits_scroll_duration")) | |||
credits_scroll_duration = (*ctx->config)["credits_scroll_duration"].get<double>(); | |||
auto set_credits_opacity = [ctx](int channel, const float& opacity) | |||
{ | |||
ctx->credits_text->set_color({1.0f, 1.0f, 1.0f, opacity}); | |||
}; | |||
// Build credits fade in animation | |||
ctx->credits_fade_in_animation = new animation<float>(); | |||
animation_channel<float>* credits_fade_in_opacity_channel = ctx->credits_fade_in_animation->add_channel(0); | |||
ctx->credits_fade_in_animation->set_interpolator(ease<float>::in_quad); | |||
credits_fade_in_opacity_channel->insert_keyframe({0.0, 0.0f}); | |||
credits_fade_in_opacity_channel->insert_keyframe({credits_fade_in_duration, 1.0f}); | |||
ctx->credits_fade_in_animation->set_frame_callback(set_credits_opacity); | |||
// Build credits scroll in animation | |||
ctx->credits_scroll_animation = new animation<float>(); | |||
// Trigger credits scroll animation after credits fade in animation ends | |||
ctx->credits_fade_in_animation->set_end_callback | |||
( | |||
[ctx]() | |||
{ | |||
ctx->credits_scroll_animation->play(); | |||
} | |||
); | |||
// Add credits animations to animator | |||
ctx->animator->add_animation(ctx->credits_fade_in_animation); | |||
ctx->animator->add_animation(ctx->credits_scroll_animation); | |||
// Start credits fade in animation | |||
ctx->credits_fade_in_animation->play(); | |||
// Set up credits skipper | |||
ctx->input_listener->set_callback | |||
( | |||
[ctx](const event_base& event) | |||
{ | |||
auto id = event.get_event_type_id(); | |||
if (id != mouse_moved_event::event_type_id && id != mouse_wheel_scrolled_event::event_type_id && id != gamepad_axis_moved_event::event_type_id) | |||
{ | |||
if (ctx->credits_text->get_color()[3] > 0.0f) | |||
{ | |||
ctx->input_listener->set_enabled(false); | |||
// Change state | |||
application::state next_state; | |||
next_state.name = "main_menu"; | |||
next_state.enter = std::bind(game::state::main_menu::enter, ctx, 2); | |||
next_state.exit = std::bind(game::state::main_menu::exit, ctx); | |||
ctx->app->change_state(next_state); | |||
} | |||
} | |||
} | |||
); | |||
ctx->input_listener->set_enabled(true); | |||
ctx->ui_scene->add_object(ctx->credits_text); | |||
ctx->credits_text->update_tweens(); | |||
} | |||
void exit(game::context* ctx) | |||
{ | |||
// Disable credits skipper | |||
ctx->input_listener->set_enabled(false); | |||
ctx->input_listener->set_callback(nullptr); | |||
// Destruct credits text | |||
ctx->ui_scene->remove_object(ctx->credits_text); | |||
delete ctx->credits_text; | |||
ctx->credits_text = nullptr; | |||
// Destruct credits animations | |||
ctx->animator->remove_animation(ctx->credits_fade_in_animation); | |||
ctx->animator->remove_animation(ctx->credits_scroll_animation); | |||
delete ctx->credits_fade_in_animation; | |||
delete ctx->credits_scroll_animation; | |||
ctx->credits_fade_in_animation = nullptr; | |||
ctx->credits_scroll_animation = nullptr; | |||
ctx->ui_clear_pass->set_cleared_buffers(false, true, false); | |||
} | |||
} // namespace credits | |||
} // namespace state | |||
} // namespace game |
@ -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_CREDITS_HPP | |||
#define ANTKEEPER_GAME_STATE_CREDITS_HPP | |||
#include "game/context.hpp" | |||
namespace game { | |||
namespace state { | |||
/// Credits screen game state functions. | |||
namespace credits { | |||
void enter(game::context* ctx); | |||
void exit(game::context* ctx); | |||
} // namespace credits | |||
} // namespace state | |||
} // namespace game | |||
#endif // ANTKEEPER_GAME_STATE_CREDITS_HPP |
@ -0,0 +1,279 @@ | |||
/* | |||
* 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/language-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/fonts.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); | |||
} | |||
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(); | |||
} | |||
} | |||
void enter(game::context* ctx) | |||
{ | |||
ctx->ui_clear_pass->set_cleared_buffers(true, true, false); | |||
ctx->language_menu_index = 0; | |||
// Construct language menu texts | |||
ctx->language_menu_language_text = new scene::text(); | |||
ctx->language_menu_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); | |||
// 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); | |||
}; | |||
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) | |||
ctx->language_index = 0; | |||
// Find corresponding language code and strings | |||
ctx->language_code = (*ctx->string_table)[0][ctx->language_index + 2]; | |||
ctx->strings = &ctx->string_table_map[ctx->language_code]; | |||
// Update language in config | |||
(*ctx->config)["language"] = ctx->language_code; | |||
ctx->logger->log("Language changed to \"" + ctx->language_code + "\""); | |||
// Reload fonts | |||
ctx->logger->push_task("Reloading fonts"); | |||
try | |||
{ | |||
game::load_fonts(ctx); | |||
} | |||
catch (...) | |||
{ | |||
ctx->logger->pop_task(EXIT_FAILURE); | |||
} | |||
ctx->logger->pop_task(EXIT_SUCCESS); | |||
update_text_font(ctx); | |||
update_text_content(ctx); | |||
refresh_texts(ctx); | |||
align_texts(ctx); | |||
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) | |||
ctx->language_index = ctx->language_count - 1; | |||
// Find corresponding language code and strings | |||
ctx->language_code = (*ctx->string_table)[0][ctx->language_index + 2]; | |||
ctx->strings = &ctx->string_table_map[ctx->language_code]; | |||
ctx->logger->log("Language changed to \"" + ctx->language_code + "\""); | |||
// Reload fonts | |||
ctx->logger->push_task("Reloading fonts"); | |||
try | |||
{ | |||
game::load_fonts(ctx); | |||
} | |||
catch (...) | |||
{ | |||
ctx->logger->pop_task(EXIT_FAILURE); | |||
} | |||
ctx->logger->pop_task(EXIT_SUCCESS); | |||
update_text_font(ctx); | |||
update_text_content(ctx); | |||
refresh_texts(ctx); | |||
align_texts(ctx); | |||
update_text_tweens(ctx); | |||
}; | |||
// Build list of language menu callbacks | |||
ctx->language_menu_callbacks.push_back(next_language_callback); | |||
ctx->language_menu_callbacks.push_back(menu_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); | |||
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); | |||
} | |||
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(); | |||
ctx->ui_clear_pass->set_cleared_buffers(false, true, false); | |||
} | |||
} // namespace language_menu | |||
} // namespace state | |||
} // namespace game |
@ -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_LANGUAGE_MENU_HPP | |||
#define ANTKEEPER_GAME_STATE_LANGUAGE_MENU_HPP | |||
#include "game/context.hpp" | |||
namespace game { | |||
namespace state { | |||
/// Language menu screen game state functions. | |||
namespace language_menu { | |||
void enter(game::context* ctx); | |||
void exit(game::context* ctx); | |||
} // namespace language_menu | |||
} // namespace state | |||
} // namespace game | |||
#endif // ANTKEEPER_GAME_STATE_LANGUAGE_MENU_HPP |
@ -0,0 +1,213 @@ | |||
/* | |||
* 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/options-menu.hpp" | |||
#include "game/states/main-menu.hpp" | |||
#include "game/states/language-menu.hpp" | |||
#include "animation/ease.hpp" | |||
#include "animation/animation.hpp" | |||
#include "animation/animator.hpp" | |||
#include "application.hpp" | |||
#include "scene/text.hpp" | |||
#include "render/passes/clear-pass.hpp" | |||
namespace game { | |||
namespace state { | |||
namespace options_menu { | |||
void enter(game::context* ctx) | |||
{ | |||
ctx->ui_clear_pass->set_cleared_buffers(true, true, false); | |||
ctx->options_menu_index = 0; | |||
// 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(); | |||
// 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); | |||
// Construct options menu callbacks | |||
auto menu_back_callback = [ctx]() | |||
{ | |||
application::state next_state; | |||
next_state.name = "main_menu"; | |||
next_state.enter = std::bind(game::state::main_menu::enter, ctx, 1); | |||
next_state.exit = std::bind(game::state::main_menu::exit, ctx); | |||
ctx->app->change_state(next_state); | |||
}; | |||
auto change_state_language_menu = [ctx]() | |||
{ | |||
application::state next_state; | |||
next_state.name = "language_menu"; | |||
next_state.enter = std::bind(game::state::language_menu::enter, ctx); | |||
next_state.exit = std::bind(game::state::language_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(nullptr); | |||
ctx->options_menu_callbacks.push_back(nullptr); | |||
ctx->options_menu_callbacks.push_back(change_state_language_menu); | |||
ctx->options_menu_callbacks.push_back(menu_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"]); | |||
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); | |||
} | |||
// 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(); | |||
} | |||
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) | |||
); | |||
*/ | |||
} | |||
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); | |||
// 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(); | |||
ctx->ui_clear_pass->set_cleared_buffers(false, true, false); | |||
} | |||
} // namespace options_menu | |||
} // namespace state | |||
} // namespace game |
@ -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_OPTIONS_MENU_HPP | |||
#define ANTKEEPER_GAME_STATE_OPTIONS_MENU_HPP | |||
#include "game/context.hpp" | |||
namespace game { | |||
namespace state { | |||
/// Options menu screen game state functions. | |||
namespace options_menu { | |||
void enter(game::context* ctx); | |||
void exit(game::context* ctx); | |||
} // namespace options_menu | |||
} // namespace state | |||
} // namespace game | |||
#endif // ANTKEEPER_GAME_STATE_OPTIONS_MENU_HPP |