Browse Source

Add credits state and language menu state

master
C. J. Howard 1 year ago
parent
commit
aef9db4c96
20 changed files with 1486 additions and 289 deletions
  1. +1
    -0
      CMakeLists.txt
  2. +17
    -0
      src/animation/animator.cpp
  3. +7
    -0
      src/animation/animator.hpp
  4. +37
    -3
      src/application.cpp
  5. +9
    -0
      src/application.hpp
  6. +26
    -2
      src/game/context.hpp
  7. +178
    -0
      src/game/fonts.cpp
  8. +31
    -0
      src/game/fonts.hpp
  9. +11
    -7
      src/game/states/boot.cpp
  10. +142
    -0
      src/game/states/credits.cpp
  11. +39
    -0
      src/game/states/credits.hpp
  12. +279
    -0
      src/game/states/language-menu.cpp
  13. +39
    -0
      src/game/states/language-menu.hpp
  14. +5
    -104
      src/game/states/loading.cpp
  15. +182
    -73
      src/game/states/main-menu.cpp
  16. +1
    -1
      src/game/states/main-menu.hpp
  17. +213
    -0
      src/game/states/options-menu.cpp
  18. +39
    -0
      src/game/states/options-menu.hpp
  19. +82
    -35
      src/game/states/splash.cpp
  20. +148
    -64
      src/game/states/title.cpp

+ 1
- 0
CMakeLists.txt View File

@ -1,5 +1,6 @@
cmake_minimum_required(VERSION 3.7)
option(VERSION_STRING "Project version string" "0.0.0")
project(antkeeper VERSION ${VERSION_STRING} LANGUAGES CXX)

+ 17
- 0
src/animation/animator.cpp View File

@ -19,22 +19,36 @@
#include "animator.hpp"
#include "animation/animation.hpp"
#include <stdexcept>
animator::animator():
animating(0)
{}
void animator::animate(double dt)
{
// Advance animations
++animating;
for (animation_base* animation: animations)
{
animation->advance(dt);
}
--animating;
}
void animator::add_animation(animation_base* animation)
{
if (animating)
throw std::runtime_error("Attempting to add animation to animator while animating");
animations.emplace(animation);
}
void animator::remove_animation(animation_base* animation)
{
if (animating)
throw std::runtime_error("Attempting to remove animation from animator while animating");
auto it = animations.find(animation);
if (it != animations.end())
{
@ -44,5 +58,8 @@ void animator::remove_animation(animation_base* animation)
void animator::remove_animations()
{
if (animating)
throw std::runtime_error("Attempting to remove animations from animator while animating");
animations.clear();
}

+ 7
- 0
src/animation/animator.hpp View File

@ -20,6 +20,7 @@
#ifndef ANTKEEPER_ANIMATOR_HPP
#define ANTKEEPER_ANIMATOR_HPP
#include <list>
#include <unordered_set>
class animation_base;
@ -30,6 +31,9 @@ class animation_base;
class animator
{
public:
/// Constructs an animator.
animator();
/**
* Progresses all active animations by @p dt.
*
@ -56,6 +60,9 @@ public:
private:
std::unordered_set<animation_base*> animations;
std::list<animation_base*> animations_to_add;
std::list<animation_base*> animations_to_remove;
int animating;
};
#endif // ANTKEEPER_ANIMATOR_HPP

+ 37
- 3
src/application.cpp View File

@ -47,6 +47,7 @@ application::application():
vsync(true),
cursor_visible(true),
display_dimensions({0, 0}),
display_dpi(0.0f),
window_dimensions({0, 0}),
viewport_dimensions({0, 0}),
mouse_position({0, 0}),
@ -97,6 +98,8 @@ application::application():
SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG);
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);
@ -104,15 +107,25 @@ application::application():
SDL_DisplayMode sdl_desktop_display_mode;
if (SDL_GetDesktopDisplayMode(0, &sdl_desktop_display_mode) != 0)
{
logger->error("Failed to get desktop display mode: \"" + std::string(SDL_GetError()) + "\"");
logger->error("Failed to detect desktop display mode: \"" + std::string(SDL_GetError()) + "\"");
throw std::runtime_error("Failed to detect desktop display mode");
}
else
{
logger->log("Detected " + std::to_string(sdl_desktop_display_mode.w) + "x" + std::to_string(sdl_desktop_display_mode.h) + " display");
display_dimensions = {sdl_desktop_display_mode.w, sdl_desktop_display_mode.h};
}
// Get display DPI
if (SDL_GetDisplayDPI(0, &display_dpi, nullptr, nullptr) != 0)
{
logger->error("Failed to detect display DPI: \"" + std::string(SDL_GetError()) + "\"");
throw std::runtime_error("Failed to detect display DPI");
}
else
{
logger->log("Detected " + std::to_string(sdl_desktop_display_mode.w) + "x" + std::to_string(sdl_desktop_display_mode.h) + " display with " + std::to_string(display_dpi) + " DPI");
}
// Create a hidden fullscreen window
logger->push_task("Creating " + std::to_string(display_dimensions[0]) + "x" + std::to_string(display_dimensions[1]) + " window");
sdl_window = SDL_CreateWindow
@ -259,6 +272,21 @@ int application::execute(const application::state& initial_state)
// Schedule frames until closed
while (!closed)
{
translate_sdl_events();
// Enter queued state (if any)
if (queued_state.enter != nullptr || queued_state.exit != nullptr)
{
// Make a copy of the queued state
application::state queued_state_copy = queued_state;
// Clear the queued state
queued_state = {std::string(), nullptr, nullptr};
// Enter the queued state
change_state(queued_state_copy);
}
// Tick frame scheduler
frame_scheduler->tick();
@ -354,13 +382,19 @@ std::shared_ptr application::capture_frame() const
int h = viewport_dimensions[1];
std::shared_ptr<image> frame = std::make_shared<image>();
frame->format(3, false);
frame->format(1, 3);
frame->resize(w, h);
logger->log("starting read");
// Read pixel data from framebuffer into image
glReadBuffer(GL_BACK);
logger->log("buffer read");
glReadPixels(0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, frame->get_pixels());
logger->log("ending read");
return std::move(frame);
}

+ 9
- 0
src/application.hpp View File

@ -194,6 +194,9 @@ public:
/// Returns the dimensions of the current display.
const std::array<int, 2>& get_display_dimensions() const;
/// Returns the DPI of the display.
float get_display_dpi() const;
/// Returns the dimensions of the window.
const std::array<int, 2>& get_window_dimensions() const;
@ -238,6 +241,7 @@ private:
bool vsync;
bool cursor_visible;
std::array<int, 2> display_dimensions;
float display_dpi;
std::array<int, 2> window_dimensions;
std::array<int, 2> viewport_dimensions;
std::array<int, 2> mouse_position;
@ -273,6 +277,11 @@ inline const std::array& application::get_display_dimensions() const
return display_dimensions;
}
inline float application::get_display_dpi() const
{
return display_dpi;
}
inline const std::array<int, 2>& application::get_window_dimensions() const
{
return window_dimensions;

+ 26
- 2
src/game/context.hpp View File

@ -43,6 +43,7 @@
#include <queue>
#include <string>
#include <unordered_map>
#include <vector>
#include "resources/json.hpp"
#include "type/typeface.hpp"
#include "type/bitmap-font.hpp"
@ -141,6 +142,7 @@ struct context
// Localization
std::string language_code;
int language_count;
int language_index;
string_table* string_table;
string_table_map string_table_map;
@ -200,12 +202,26 @@ struct context
scene::billboard* splash_billboard;
scene::billboard* camera_flash_billboard;
scene::text* title_text;
scene::text* title_version_text;
scene::text* title_press_any_key_text;
scene::text* main_menu_start_text;
scene::text* main_menu_options_text;
scene::text* main_menu_credits_text;
scene::text* main_menu_quit_text;
scene::model_instance* ui_pointer;
int main_menu_index;
scene::text* credits_text;
scene::text* options_menu_controls_text;
scene::text* options_menu_graphics_text;
scene::text* options_menu_sound_text;
scene::text* options_menu_language_text;
scene::text* options_menu_back_text;
std::vector<scene::text*> options_menu_texts;
std::vector<std::function<void()>> options_menu_callbacks;
int options_menu_index;
scene::text* language_menu_language_text;
scene::text* language_menu_back_text;
std::vector<scene::text*> language_menu_texts;
std::vector<std::function<void()>> language_menu_callbacks;
int language_menu_index;
// Surface scene
scene::collection* surface_scene;
@ -229,6 +245,14 @@ struct context
animation<float>* equip_tool_animation;
animation<float>* unequip_tool_animation;
animation<float>* camera_flash_animation;
animation<float>* splash_fade_in_animation;
animation<float>* splash_fade_out_animation;
animation<float>* title_fade_in_animation;
animation<float>* title_fade_out_animation;
animation<float>* title_press_any_key_animation;
animation<float>* main_menu_fade_animation;
animation<float>* credits_fade_in_animation;
animation<float>* credits_scroll_animation;
// Controls
input::event_router* input_event_router;

+ 178
- 0
src/game/fonts.cpp View File

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

+ 31
- 0
src/game/fonts.hpp View File

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

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

@ -381,10 +381,13 @@ void load_strings(game::context* ctx)
for (int i = 2; i < (*ctx->string_table)[0].size(); ++i)
{
if ((*ctx->string_table)[0][i] == ctx->language_code)
ctx->language_index = i;
ctx->language_index = i - 2;
}
logger->log("lang index: " + std::to_string(ctx->language_index));
ctx->language_count = (*ctx->string_table)[0].size() - 2;
logger->log("language count: " + std::to_string(ctx->language_count));
logger->log("language index: " + std::to_string(ctx->language_index));
logger->log("language code: " + ctx->language_code);
ctx->strings = &ctx->string_table_map[ctx->language_code];
@ -439,7 +442,7 @@ void setup_window(game::context* ctx)
app->set_vsync(vsync);
// Set title
app->set_title((*ctx->strings)["title"]);
app->set_title((*ctx->strings)["application_title"]);
// Show window
ctx->app->get_rasterizer()->set_clear_color(0.0f, 0.0f, 0.0f, 1.0f);
@ -694,6 +697,7 @@ void setup_scenes(game::context* ctx)
const gl::texture_2d* splash_texture = ctx->resource_manager->load<gl::texture_2d>("splash.tex");
auto splash_dimensions = splash_texture->get_dimensions();
ctx->splash_billboard_material = new render::material();
ctx->splash_billboard_material->set_flags(MATERIAL_FLAG_TRANSLUCENT);
ctx->splash_billboard_material->set_shader_program(ctx->resource_manager->load<gl::shader_program>("ui-element-textured.glsl"));
ctx->splash_billboard_material->add_property<const gl::texture_2d*>("background")->set_value(splash_texture);
ctx->splash_billboard_material->add_property<float4>("tint")->set_value(float4{1, 1, 1, 1});
@ -790,14 +794,14 @@ void setup_animation(game::context* ctx)
// Create inner radial transition
ctx->radial_transition_inner = new screen_transition();
ctx->radial_transition_inner->get_material()->set_shader_program(ctx->resource_manager->load<gl::shader_program>("radial-transition-inner.glsl"));
ctx->ui_scene->add_object(ctx->radial_transition_inner->get_billboard());
ctx->animator->add_animation(ctx->radial_transition_inner->get_animation());
//ctx->ui_scene->add_object(ctx->radial_transition_inner->get_billboard());
//ctx->animator->add_animation(ctx->radial_transition_inner->get_animation());
// Create outer radial transition
ctx->radial_transition_outer = new screen_transition();
ctx->radial_transition_outer->get_material()->set_shader_program(ctx->resource_manager->load<gl::shader_program>("radial-transition-outer.glsl"));
ctx->ui_scene->add_object(ctx->radial_transition_outer->get_billboard());
ctx->animator->add_animation(ctx->radial_transition_outer->get_animation());
//ctx->ui_scene->add_object(ctx->radial_transition_outer->get_billboard());
//ctx->animator->add_animation(ctx->radial_transition_outer->get_animation());
// Create camera flash animation
ctx->camera_flash_animation = new animation<float>();

+ 142
- 0
src/game/states/credits.cpp View File

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

+ 39
- 0
src/game/states/credits.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_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

+ 279
- 0
src/game/states/language-menu.cpp View File

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

+ 39
- 0
src/game/states/language-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_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

+ 5
- 104
src/game/states/loading.cpp View File

@ -50,16 +50,13 @@
#include "scene/ambient-light.hpp"
#include "scene/directional-light.hpp"
#include "utility/timestamp.hpp"
#include "type/type.hpp"
#include "configuration.hpp"
#include <stb/stb_image_write.h>
#include <unordered_set>
#include <codecvt>
#include "gl/texture-wrapping.hpp"
#include "gl/texture-filter.hpp"
#include "scene/text.hpp"
#include "render/material-flags.hpp"
#include "game/fonts.hpp"
namespace game {
namespace state {
@ -68,9 +65,6 @@ namespace loading {
/// Loads control profile and calibrates gamepads
static void load_controls(game::context* ctx);
/// Loads typefaces and builds fonts
static void load_fonts(game::context* ctx);
/// Creates the universe and solar system.
static void cosmogenesis(game::context* ctx);
@ -107,7 +101,7 @@ void enter(game::context* ctx)
ctx->logger->push_task("Loading fonts");
try
{
load_fonts(ctx);
game::load_fonts(ctx);
}
catch (...)
{
@ -115,6 +109,7 @@ void enter(game::context* ctx)
}
ctx->logger->pop_task(EXIT_SUCCESS);
/*
// Create universe
ctx->logger->push_task("Creating the universe");
try
@ -127,13 +122,14 @@ void enter(game::context* ctx)
throw;
}
ctx->logger->pop_task(EXIT_SUCCESS);
*/
// Determine next game state
application::state next_state;
if (ctx->option_quick_start.has_value())
{
next_state.name = "main_menu";
next_state.enter = std::bind(game::state::main_menu::enter, ctx);
next_state.enter = std::bind(game::state::main_menu::enter, ctx, 0);
next_state.exit = std::bind(game::state::main_menu::exit, ctx);
}
else
@ -222,12 +218,6 @@ void load_controls(game::context* ctx)
}
);
// Menu back
ctx->controls["menu_back"]->set_activated_callback
(
std::bind(&application::close, ctx->app, 0)
);
// Set activation threshold for menu navigation controls to mitigate drifting gamepad axes
const float menu_activation_threshold = 0.1f;
ctx->controls["menu_up"]->set_activation_threshold(menu_activation_threshold);
@ -236,95 +226,6 @@ void load_controls(game::context* ctx)
ctx->controls["menu_right"]->set_activation_threshold(menu_activation_threshold);
}
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 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");
// Build debug font
if (auto it = ctx->typefaces.find("monospace"); it != ctx->typefaces.end())
{
build_bitmap_font(*it->second, 22.0f, 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, 28.0f, 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, 96.0f, charset, ctx->title_font, ctx->title_font_material, bitmap_font_shader);
}
}
void cosmogenesis(game::context* ctx)
{
// Init time

+ 182
- 73
src/game/states/main-menu.cpp View File

@ -18,11 +18,16 @@
*/
#include "game/states/main-menu.hpp"
#include "game/states/title.hpp"
#include "game/states/options-menu.hpp"
#include "game/states/forage.hpp"
#include "game/states/nuptial-flight.hpp"
#include "game/states/credits.hpp"
#include "render/passes/clear-pass.hpp"
#include "resources/resource-manager.hpp"
#include "render/model.hpp"
#include "animation/animation.hpp"
#include "animation/animator.hpp"
#include "animation/screen-transition.hpp"
#include "animation/ease.hpp"
#include "animation/timeline.hpp"
@ -33,8 +38,10 @@ namespace game {
namespace state {
namespace main_menu {
void enter(game::context* ctx)
void enter(game::context* ctx, int main_menu_index)
{
ctx->main_menu_index = main_menu_index;
ctx->ui_clear_pass->set_cleared_buffers(true, true, false);
// Construct main menu texts
@ -57,89 +64,174 @@ void enter(game::context* ctx)
float offset_y = 0.0f;
for (scene::text* text: texts)
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)
{
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);
// Align text
const auto& text_aabb = static_cast<const geom::aabb<float>&>(text->get_local_bounds());
float title_w = text_aabb.max_point.x - text_aabb.min_point.x;
float title_h = text_aabb.max_point.y - text_aabb.min_point.y;
text->set_translation({std::round(-title_w * 0.5f), std::round(-title_h * 0.5f) + offset_y, 0.0f});
offset_y -= ctx->menu_font.get_font_metrics().linespace * 1.5f;
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;
// Update menu AABB
text->set_translation({std::round(x), std::round(y), 0.0f});
}
// Load pointer
ctx->ui_pointer = new scene::model_instance();
ctx->ui_pointer->set_model(ctx->resource_manager->load<render::model>("pointer.mdl"));
ctx->ui_scene->add_object(ctx->ui_pointer);
// Scale and position pointer
float pointer_scale = (ctx->menu_font.get_font_metrics().ascent - ctx->menu_font.get_font_metrics().descent) * (1.0f/3.0f);
ctx->ui_pointer->set_scale({pointer_scale, pointer_scale, pointer_scale});
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->ui_pointer->set_translation(text_translation + float3{-advance_x, (text_aabb.max_point.y - text_aabb.min_point.y) * 0.5f, 0.0f});
ctx->main_menu_start_text->set_color({1.0f, 1.0f, 1.0f, 1.0f});
ctx->controls["menu_down"]->set_activated_callback
(
[ctx]()
{
ctx->ui_pointer->set_translation(ctx->ui_pointer->get_translation() - float3{0.0f, ctx->menu_font.get_font_metrics().linespace * 1.5f, 0.0f});
++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->ui_pointer->set_translation(ctx->ui_pointer->get_translation() + float3{0.0f, ctx->menu_font.get_font_metrics().linespace * 1.5f, 0.0f});
--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]()
{
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);
}
);
ctx->controls["menu_select"]->set_activated_callback
(
[ctx]()
{
// 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 functions
auto change_state_nuptial_flight = [ctx]()
if (ctx->main_menu_index == 0)
{
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::sequence sequence =
// 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)
{
{t + fade_out_duration, change_state_nuptial_flight}
};
timeline->add_sequence(sequence);
// 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);
// 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);
}
}
);
@ -148,34 +240,56 @@ void enter(game::context* ctx)
ctx->controls["menu_up"]->set_callbacks_enabled(false);
ctx->controls["menu_select"]->set_callbacks_enabled(false);
// Start fade in from black
const float fade_in_duration = 0.5f;
ctx->fade_transition_color->set_value({0, 0, 0});
ctx->fade_transition->transition(fade_in_duration, true, ease<float>::in_quad);
// 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});
// Schedule enabling of control callbacks
auto enable_control_callbacks = [ctx]()
{
ctx->controls["menu_down"]->set_callbacks_enabled(true);
ctx->controls["menu_up"]->set_callbacks_enabled(true);
ctx->controls["menu_select"]->set_callbacks_enabled(true);
};
timeline* timeline = ctx->timeline;
float t = timeline->get_position();
timeline::sequence sequence =
{
{t + fade_in_duration, enable_control_callbacks}
};
timeline->add_sequence(sequence);
// 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();
}
void exit(game::context* ctx)
{
// Remove control callbacks
// 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);
@ -189,11 +303,6 @@ void exit(game::context* ctx)
text = nullptr;
}
// Remove pointer from UI
ctx->ui_scene->remove_object(ctx->ui_pointer);
delete ctx->ui_pointer;
ctx->ui_pointer = nullptr;
ctx->ui_clear_pass->set_cleared_buffers(false, true, false);
}

+ 1
- 1
src/game/states/main-menu.hpp View File

@ -28,7 +28,7 @@ namespace state {
/// Main menu screen game state functions.
namespace main_menu {
void enter(game::context* ctx);
void enter(game::context* ctx, int main_menu_index);
void exit(game::context* ctx);
} // namespace main_menu

+ 213
- 0
src/game/states/options-menu.cpp View File

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

+ 39
- 0
src/game/states/options-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_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

+ 82
- 35
src/game/states/splash.cpp View File

@ -20,6 +20,8 @@
#include "game/states/splash.hpp"
#include "game/states/title.hpp"
#include "animation/screen-transition.hpp"
#include "animation/animation.hpp"
#include "animation/animator.hpp"
#include "animation/ease.hpp"
#include "animation/timeline.hpp"
#include "application.hpp"
@ -33,43 +35,78 @@ void enter(game::context* ctx)
{
ctx->ui_clear_pass->set_cleared_buffers(true, true, false);
// Add splash billboard to UI scene
ctx->ui_scene->add_object(ctx->splash_billboard);
// Load animation timing configuration
double splash_fade_in_duration = 0.0;
double splash_duration = 0.0;
double splash_fade_out_duration = 0.0;
if (ctx->config->contains("splash_fade_in_duration"))
splash_fade_in_duration = (*ctx->config)["splash_fade_in_duration"].get<double>();
if (ctx->config->contains("splash_duration"))
splash_duration = (*ctx->config)["splash_duration"].get<double>();
if (ctx->config->contains("splash_fade_out_duration"))
splash_fade_out_duration = (*ctx->config)["splash_fade_out_duration"].get<double>();
// Setup timing
const float splash_fade_in_duration = 0.5f;
const float splash_hang_duration = 2.0f;
const float splash_fade_out_duration = 0.5f;
// Build splash fade in animation
ctx->splash_fade_in_animation = new animation<float>();
animation_channel<float>* splash_fade_in_opacity_channel = ctx->splash_fade_in_animation->add_channel(0);
ctx->splash_fade_in_animation->set_interpolator(ease<float>::in_quad);
splash_fade_in_opacity_channel->insert_keyframe({0.0, 0.0f});
splash_fade_in_opacity_channel->insert_keyframe({splash_fade_in_duration, 1.0f});
splash_fade_in_opacity_channel->insert_keyframe({splash_fade_in_duration + splash_duration, 1.0f});
// Start fade in
ctx->fade_transition->transition(splash_fade_in_duration, true, ease<float>::in_quad);
// Build splash fade out animation
ctx->splash_fade_out_animation = new animation<float>();
animation_channel<float>* splash_fade_out_opacity_channel = ctx->splash_fade_out_animation->add_channel(0);
ctx->splash_fade_out_animation->set_interpolator(ease<float>::out_quad);
splash_fade_out_opacity_channel->insert_keyframe({0.0, 1.0f});
splash_fade_out_opacity_channel->insert_keyframe({splash_fade_out_duration, 0.0f});
// Crate fade out function
auto fade_out = [ctx, splash_fade_out_duration]()
// Setup animation frame callbacks
auto set_splash_opacity = [ctx](int channel, const float& opacity)
{
ctx->fade_transition->transition(splash_fade_out_duration, false, ease<float>::out_quad);
static_cast<render::material_property<float4>*>(ctx->splash_billboard_material->get_property("tint"))->set_value(float4{1, 1, 1, opacity});
};
ctx->splash_fade_in_animation->set_frame_callback(set_splash_opacity);
ctx->splash_fade_out_animation->set_frame_callback(set_splash_opacity);
// Create change state function
auto change_state = [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);
};
// Reset splash color when animation starts
ctx->splash_fade_in_animation->set_start_callback
(
[ctx]()
{
static_cast<render::material_property<float4>*>(ctx->splash_billboard_material->get_property("tint"))->set_value(float4{1, 1, 1, 0});
ctx->splash_billboard_material->update_tweens();
}
);
// Schedule fade out and change state events
timeline* timeline = ctx->timeline;
float t = timeline->get_position();
timeline::sequence splash_sequence =
{
{t + splash_fade_in_duration + splash_hang_duration, fade_out},
{t + splash_fade_in_duration + splash_hang_duration + splash_fade_out_duration, change_state}
};
timeline->add_sequence(splash_sequence);
// Trigger splash fade out animation when splash fade in animation ends
ctx->splash_fade_in_animation->set_end_callback
(
[ctx]()
{
ctx->splash_fade_out_animation->play();
}
);
// Trigger a state change when the splash fade out animation ends
ctx->splash_fade_out_animation->set_end_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->queue_state(next_state);
}
);
// Add splash fade animations to animator
ctx->animator->add_animation(ctx->splash_fade_in_animation);
ctx->animator->add_animation(ctx->splash_fade_out_animation);
// Start splash fade in animation
ctx->splash_fade_in_animation->play();
// Set up splash skipper
ctx->input_listener->set_callback
@ -79,32 +116,42 @@ void enter(game::context* ctx)
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)
{
ctx->timeline->clear();
ctx->fade_transition->get_animation()->stop();
// Black out screen
ctx->rasterizer->set_clear_color(0.0f, 0.0f, 0.0f, 1.0f);
ctx->rasterizer->clear_framebuffer(true, false, false);
ctx->app->swap_buffers();
// Change state
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);
}
}
);
ctx->input_listener->set_enabled(true);
// Add splash billboard to UI scene
ctx->ui_scene->add_object(ctx->splash_billboard);
}
void exit(game::context* ctx)
{
// Remove splash billboard from UI scene
ctx->ui_scene->remove_object(ctx->splash_billboard);
// Disable splash skipper
ctx->input_listener->set_enabled(false);
ctx->input_listener->set_callback(nullptr);
// Remove splash billboard from UI scene
ctx->ui_scene->remove_object(ctx->splash_billboard);
// Destruct splash fade animations
ctx->animator->remove_animation(ctx->splash_fade_in_animation);
ctx->animator->remove_animation(ctx->splash_fade_out_animation);
delete ctx->splash_fade_in_animation;
delete ctx->splash_fade_out_animation;
ctx->splash_fade_in_animation = nullptr;
ctx->splash_fade_out_animation = nullptr;
ctx->ui_clear_pass->set_cleared_buffers(false, true, false);
}

+ 148
- 64
src/game/states/title.cpp View File

@ -21,7 +21,8 @@
#include "game/states/main-menu.hpp"
#include "animation/screen-transition.hpp"
#include "animation/ease.hpp"
#include "animation/timeline.hpp"
#include "animation/animation.hpp"
#include "animation/animator.hpp"
#include "application.hpp"
#include "scene/text.hpp"
#include "configuration.hpp"
@ -35,30 +36,116 @@ void enter(game::context* ctx)
{
ctx->ui_clear_pass->set_cleared_buffers(true, true, false);
// Setup timing
const float title_fade_in_duration = 0.5f;
const float title_fade_out_duration = 0.5f;
// Construct title text
ctx->title_text = new scene::text();
ctx->title_text->set_material(&ctx->title_font_material);
ctx->title_text->set_font(&ctx->title_font);
ctx->title_text->set_color({1.0f, 1.0f, 1.0f, 0.0f});
ctx->title_text->set_content((*ctx->strings)["title_antkeeper"]);
// Start fade in
ctx->fade_transition->transition(title_fade_in_duration, true, ease<float>::in_quad);
// Construct "Press any key" text
ctx->title_press_any_key_text = new scene::text();
ctx->title_press_any_key_text->set_material(&ctx->menu_font_material);
ctx->title_press_any_key_text->set_font(&ctx->menu_font);
ctx->title_press_any_key_text->set_color({1.0f, 1.0f, 1.0f, 0.0f});
ctx->title_press_any_key_text->set_content((*ctx->strings)["title_press_any_key"]);
// Crate fade out function
auto fade_out = [ctx, title_fade_out_duration]()
{
ctx->fade_transition->transition(title_fade_out_duration, false, ease<float>::out_quad);
};
int window_height = std::get<1>(ctx->app->get_viewport_dimensions());
// Align title text
const auto& title_aabb = static_cast<const geom::aabb<float>&>(ctx->title_text->get_local_bounds());
float title_w = title_aabb.max_point.x - title_aabb.min_point.x;
float title_h = title_aabb.max_point.y - title_aabb.min_point.y;
ctx->title_text->set_translation({std::round(-title_w * 0.5f), std::round(-title_h * 0.5f + (window_height / 3) / 2), 0.0f});
// Align "Press any key" text
const auto& any_key_aabb = static_cast<const geom::aabb<float>&>(ctx->title_press_any_key_text->get_local_bounds());
float any_key_w = any_key_aabb.max_point.x - any_key_aabb.min_point.x;
float any_key_h = any_key_aabb.max_point.y - any_key_aabb.min_point.y;
ctx->title_press_any_key_text->set_translation({std::round(-any_key_w * 0.5f), std::round(-any_key_h * 0.5f - (window_height / 3) / 2), 0.0f});
// Create change state function
auto change_state = [ctx]()
// Load animation timing configuration
double title_fade_in_duration = 0.0;
double title_fade_out_duration = 0.0;
double title_press_any_key_duration = 0.0;
double title_press_any_key_delay = 0.0;
if (ctx->config->contains("title_fade_in_duration"))
title_fade_in_duration = (*ctx->config)["title_fade_in_duration"].get<double>();
if (ctx->config->contains("title_fade_out_duration"))
title_fade_out_duration = (*ctx->config)["title_fade_out_duration"].get<double>();
if (ctx->config->contains("title_press_any_key_duration"))
title_press_any_key_duration = (*ctx->config)["title_press_any_key_duration"].get<double>();
if (ctx->config->contains("title_press_any_key_delay"))
title_press_any_key_delay = (*ctx->config)["title_press_any_key_delay"].get<double>();
auto set_title_opacity = [ctx](int channel, const float& opacity)
{
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);
ctx->title_text->set_color({1.0f, 1.0f, 1.0f, opacity});
};
// Build title fade in animation
ctx->title_fade_in_animation = new animation<float>();
animation_channel<float>* title_fade_in_opacity_channel = ctx->title_fade_in_animation->add_channel(0);
ctx->title_fade_in_animation->set_interpolator(ease<float>::in_quad);
title_fade_in_opacity_channel->insert_keyframe({0.0, 0.0f});
title_fade_in_opacity_channel->insert_keyframe({title_fade_in_duration, 1.0f});
title_fade_in_opacity_channel->insert_keyframe({title_fade_in_duration + title_press_any_key_delay, 1.0f});
ctx->title_fade_in_animation->set_frame_callback(set_title_opacity);
// Trigger "Press any key" animation after title fade in animation ends
ctx->title_fade_in_animation->set_end_callback
(
[ctx]()
{
ctx->title_press_any_key_animation->play();
}
);
// Build title fade out animation
ctx->title_fade_out_animation = new animation<float>();
animation_channel<float>* title_fade_out_opacity_channel = ctx->title_fade_out_animation->add_channel(0);
ctx->title_fade_out_animation->set_interpolator(ease<float>::out_quad);
title_fade_out_opacity_channel->insert_keyframe({0.0, 1.0f});
title_fade_out_opacity_channel->insert_keyframe({title_fade_out_duration, 0.0f});
ctx->title_fade_out_animation->set_frame_callback(set_title_opacity);
// Trigger a state change when the title fade out animation ends
ctx->title_fade_out_animation->set_end_callback
(
[ctx]()
{
application::state next_state;
next_state.name = "main_menu";
next_state.enter = std::bind(game::state::main_menu::enter, ctx, 0);
next_state.exit = std::bind(game::state::main_menu::exit, ctx);
ctx->app->queue_state(next_state);
}
);
// Build "Press any key" animation
ctx->title_press_any_key_animation = new animation<float>();
ctx->title_press_any_key_animation->loop(true);
animation_channel<float>* title_press_any_key_opacity_channel = ctx->title_press_any_key_animation->add_channel(0);
ctx->title_press_any_key_animation->set_interpolator(math::lerp<float, double>);
title_press_any_key_opacity_channel->insert_keyframe({0.0, 0.0f});
title_press_any_key_opacity_channel->insert_keyframe({title_press_any_key_duration * 0.5, 1.0f});
title_press_any_key_opacity_channel->insert_keyframe({title_press_any_key_duration, 0.0f});
ctx->title_press_any_key_animation->set_frame_callback
(
[ctx](int channel, const float& opacity)
{
ctx->title_press_any_key_text->set_color({1.0f, 1.0f, 1.0f, 0.5f * ease<float>::out_cubic(0.0f, 1.0f, opacity)});
}
);
// Add title fade animations to animator
ctx->animator->add_animation(ctx->title_fade_in_animation);
ctx->animator->add_animation(ctx->title_fade_out_animation);
ctx->animator->add_animation(ctx->title_press_any_key_animation);
// Start title fade in animation
ctx->title_fade_in_animation->play();
// Set up title skipper
ctx->input_listener->set_callback
(
@ -67,69 +154,66 @@ void enter(game::context* ctx)
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)
{
ctx->timeline->clear();
ctx->fade_transition->get_animation()->stop();
ctx->fade_transition->get_billboard()->set_active(false);
ctx->rasterizer->set_clear_color(0.0f, 0.0f, 0.0f, 1.0f);
ctx->rasterizer->clear_framebuffer(true, false, false);
ctx->app->swap_buffers();
/*
if (ctx->title_fade_in_animation->is_stopped())
{
ctx->title_fade_out_animation->play();
ctx->input_listener->set_enabled(false);
}
*/
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);
if (ctx->title_text->get_color()[3] > 0.0f)
{
ctx->input_listener->set_enabled(false);
// Black out screen
ctx->rasterizer->set_clear_color(0.0f, 0.0f, 0.0f, 1.0f);
ctx->rasterizer->clear_framebuffer(true, false, false);
ctx->app->swap_buffers();
// Change state
application::state next_state;
next_state.name = "main_menu";
next_state.enter = std::bind(game::state::main_menu::enter, ctx, 0);
next_state.exit = std::bind(game::state::main_menu::exit, ctx);
ctx->app->change_state(next_state);
}
}
}
);
ctx->input_listener->set_enabled(true);
// Construct title text
ctx->title_text = new scene::text();
ctx->title_text->set_material(&ctx->title_font_material);
ctx->title_text->set_font(&ctx->title_font);
ctx->title_text->set_color({1.0f, 1.0f, 1.0f, 1.0f});
ctx->title_text->set_content((*ctx->strings)["title"]);
ctx->ui_scene->add_object(ctx->title_text);
ctx->title_text->update_tweens();
// Construct version string text
ctx->title_version_text = new scene::text();
ctx->title_version_text->set_material(&ctx->debug_font_material);
ctx->title_version_text->set_font(&ctx->debug_font);
ctx->title_version_text->set_color({1.0f, 1.0f, 1.0f, 1.0f});
ctx->title_version_text->set_content(ANTKEEPER_VERSION_STRING);
ctx->ui_scene->add_object(ctx->title_version_text);
// Align title text
const auto& title_aabb = static_cast<const geom::aabb<float>&>(ctx->title_text->get_local_bounds());
float title_w = title_aabb.max_point.x - title_aabb.min_point.x;
float title_h = title_aabb.max_point.y - title_aabb.min_point.y;
ctx->title_text->set_translation({std::round(-title_w * 0.5f), std::round(-title_h * 0.5f), 0.0f});
// Align version string
const auto& version_aabb = static_cast<const geom::aabb<float>&>(ctx->title_version_text->get_local_bounds());
float version_w = version_aabb.max_point.x - version_aabb.min_point.x;
float version_h = version_aabb.max_point.y - version_aabb.min_point.y;
const float version_padding = 12.0f;
auto viewport = ctx->app->get_viewport_dimensions();
ctx->title_version_text->set_translation({viewport[0] * 0.5f - version_w - version_padding, -viewport[1] * 0.5f + version_padding, 0.0f});
ctx->ui_scene->add_object(ctx->title_press_any_key_text);
ctx->title_press_any_key_text->update_tweens();
}
void exit(game::context* ctx)
{
// Remove and destruct title and version text
// Remove title text
ctx->ui_scene->remove_object(ctx->title_text);
ctx->ui_scene->remove_object(ctx->title_version_text);
delete ctx->title_text;
delete ctx->title_version_text;
ctx->title_text = nullptr;
ctx->title_version_text = nullptr;
ctx->ui_scene->remove_object(ctx->title_press_any_key_text);
// Disable title skipper
ctx->input_listener->set_enabled(false);
ctx->input_listener->set_callback(nullptr);
// Destruct title animations
ctx->animator->remove_animation(ctx->title_fade_in_animation);
ctx->animator->remove_animation(ctx->title_fade_out_animation);
ctx->animator->remove_animation(ctx->title_press_any_key_animation);
delete ctx->title_fade_in_animation;
delete ctx->title_fade_out_animation;
delete ctx->title_press_any_key_animation;
ctx->title_fade_in_animation = nullptr;
ctx->title_fade_out_animation = nullptr;
ctx->title_press_any_key_animation = nullptr;
ctx->ui_clear_pass->set_cleared_buffers(false, true, false);
}

Loading…
Cancel
Save