diff --git a/CMakeLists.txt b/CMakeLists.txt index 03b2f5a..c2fc691 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,5 @@ cmake_minimum_required(VERSION 3.25) - option(APPLICATION_NAME "Application name" "Antkeeper") option(APPLICATION_VERSION "Application version string" "0.0.0") option(APPLICATION_AUTHOR "Application author" "C. J. Howard") @@ -9,6 +8,7 @@ option(APPLICATION_AUTHOR "Application author" "C. J. Howard") string(TOLOWER ${APPLICATION_NAME} APPLICATION_SLUG) string(REPLACE " " "-" APPLICATION_SLUG ${APPLICATION_SLUG}) + project(${APPLICATION_SLUG} VERSION ${APPLICATION_VERSION} LANGUAGES CXX) set(APPLICATION_VERSION_MAJOR ${PROJECT_VERSION_MAJOR}) diff --git a/src/engine/app/sdl/sdl-window-manager.cpp b/src/engine/app/sdl/sdl-window-manager.cpp index 2ea89f6..02b1e86 100644 --- a/src/engine/app/sdl/sdl-window-manager.cpp +++ b/src/engine/app/sdl/sdl-window-manager.cpp @@ -92,7 +92,7 @@ sdl_window_manager::~sdl_window_manager() debug::log::trace("Quit SDL video subsystem"); } -window* sdl_window_manager::create_window +std::shared_ptr sdl_window_manager::create_window ( const std::string& title, const math::vector& windowed_position, @@ -103,7 +103,7 @@ window* sdl_window_manager::create_window ) { // Create new window - app::sdl_window* window = new app::sdl_window + std::shared_ptr window = std::make_shared ( title, windowed_position, @@ -114,7 +114,7 @@ window* sdl_window_manager::create_window ); // Map internal SDL window to window - window_map[window->internal_window] = window; + window_map[window->internal_window] = window.get(); return window; } @@ -394,16 +394,16 @@ void sdl_window_manager::update() sdl_window* sdl_window_manager::get_window(SDL_Window* internal_window) { - sdl_window* window = nullptr; if (auto i = window_map.find(internal_window); i != window_map.end()) { - window = i->second; + return i->second; } else { throw std::runtime_error("SDL window unrecognized by SDL window manager"); } - return window; + + return nullptr; } std::size_t sdl_window_manager::get_display_count() const diff --git a/src/engine/app/sdl/sdl-window-manager.hpp b/src/engine/app/sdl/sdl-window-manager.hpp index bea10f4..56b4fee 100644 --- a/src/engine/app/sdl/sdl-window-manager.hpp +++ b/src/engine/app/sdl/sdl-window-manager.hpp @@ -49,7 +49,7 @@ public: virtual void update(); /// @copydoc window::window() - [[nodiscard]] virtual window* create_window + [[nodiscard]] virtual std::shared_ptr create_window ( const std::string& title, const math::vector& windowed_position, diff --git a/src/engine/app/sdl/sdl-window.hpp b/src/engine/app/sdl/sdl-window.hpp index 4ff342a..9d69623 100644 --- a/src/engine/app/sdl/sdl-window.hpp +++ b/src/engine/app/sdl/sdl-window.hpp @@ -31,6 +31,21 @@ namespace app { class sdl_window: public window { public: + sdl_window + ( + const std::string& title, + const math::vector& windowed_position, + const math::vector& windowed_size, + bool maximized, + bool fullscreen, + bool v_sync + ); + + sdl_window(const sdl_window&) = delete; + sdl_window(sdl_window&&) = delete; + sdl_window& operator=(const sdl_window&) = delete; + sdl_window& operator=(sdl_window&&) = delete; + virtual ~sdl_window(); virtual void set_title(const std::string& title); virtual void set_position(const math::vector& position); @@ -51,21 +66,6 @@ public: private: friend class sdl_window_manager; - sdl_window - ( - const std::string& title, - const math::vector& windowed_position, - const math::vector& windowed_size, - bool maximized, - bool fullscreen, - bool v_sync - ); - - sdl_window(const sdl_window&) = delete; - sdl_window(sdl_window&&) = delete; - sdl_window& operator=(const sdl_window&) = delete; - sdl_window& operator=(sdl_window&&) = delete; - SDL_Window* internal_window; SDL_GLContext internal_context; gl::rasterizer* rasterizer; diff --git a/src/engine/app/window-manager.hpp b/src/engine/app/window-manager.hpp index c6bbb46..9e319a4 100644 --- a/src/engine/app/window-manager.hpp +++ b/src/engine/app/window-manager.hpp @@ -23,6 +23,7 @@ #include #include #include +#include #include namespace app { @@ -56,7 +57,7 @@ public: * @param fullscreen `true` if the window should start fullscreen, `false` otherwise. * @param v_sync `true` if v-sync should be enabled, `false` otherwise. */ - [[nodiscard]] virtual window* create_window + [[nodiscard]] virtual std::shared_ptr create_window ( const std::string& title, const math::vector& windowed_position, diff --git a/src/game/ant/swarm.cpp b/src/game/ant/swarm.cpp index 055ac0a..9640c03 100644 --- a/src/game/ant/swarm.cpp +++ b/src/game/ant/swarm.cpp @@ -48,7 +48,7 @@ static math::vector3 sphere_random(Generator& rng) return math::normalize(position) * std::cbrt(distribution(rng)); } -entity::id create_swarm(::context& ctx) +entity::id create_swarm(::game& ctx) { // Determine swarm properties const float3 swarm_center = {0, 100, 0}; @@ -166,7 +166,7 @@ entity::id create_swarm(::context& ctx) return swarm_eid; } -void destroy_swarm(::context& ctx, entity::id swarm_eid) +void destroy_swarm(::game& ctx, entity::id swarm_eid) { // Destroy alates auto view = ctx.entity_registry->view<::steering_component>(); diff --git a/src/game/ant/swarm.hpp b/src/game/ant/swarm.hpp index 544ed56..683491d 100644 --- a/src/game/ant/swarm.hpp +++ b/src/game/ant/swarm.hpp @@ -20,13 +20,13 @@ #ifndef ANTKEEPER_GAME_ANT_SWARM_HPP #define ANTKEEPER_GAME_ANT_SWARM_HPP -#include "game/context.hpp" +#include "game/game.hpp" #include namespace ant { -entity::id create_swarm(::context& ctx); -void destroy_swarm(::context& ctx, entity::id swarm_eid); +entity::id create_swarm(::game& ctx); +void destroy_swarm(::game& ctx, entity::id swarm_eid); } // namespace ant diff --git a/src/game/controls.cpp b/src/game/controls.cpp index 7f6ccce..f0b3b64 100644 --- a/src/game/controls.cpp +++ b/src/game/controls.cpp @@ -21,7 +21,7 @@ #include "game/graphics.hpp" #include "game/menu.hpp" #include "game/control-profile.hpp" -#include "game/state/pause-menu.hpp" +#include "game/states/pause-menu-state.hpp" #include #include #include @@ -29,7 +29,6 @@ using namespace hash::literals; - void reset_control_profile(::control_profile& profile) { auto& mappings = profile.mappings; @@ -133,7 +132,7 @@ void reset_control_profile(::control_profile& profile) mappings.emplace("pick_mate"_fnv1a32, new input::mouse_button_mapping(nullptr, input::mouse_button::right)); } -void apply_control_profile(::context& ctx, const ::control_profile& profile) +void apply_control_profile(::game& ctx, const ::control_profile& profile) { auto add_mappings = [&profile](input::action_map& map, input::action& action, std::uint32_t key) { @@ -173,7 +172,7 @@ void apply_control_profile(::context& ctx, const ::control_profile& profile) add_mappings(ctx.nuptial_flight_action_map, ctx.pick_mate_action, "pick_mate"_fnv1a32); } -void update_control_profile(::context& ctx, ::control_profile& profile) +void update_control_profile(::game& ctx, ::control_profile& profile) { auto add_mappings = [&profile](const input::action_map& map, const input::action& action, std::uint32_t key) { @@ -238,7 +237,7 @@ void update_control_profile(::context& ctx, ::control_profile& profile) add_mappings(ctx.nuptial_flight_action_map, ctx.pick_mate_action, "pick_mate"_fnv1a32); } -void setup_window_controls(::context& ctx) +void setup_window_controls(::game& ctx) { // Setup fullscreen control ctx.window_action_subscriptions.emplace_back @@ -265,7 +264,7 @@ void setup_window_controls(::context& ctx) ); } -void setup_menu_controls(::context& ctx) +void setup_menu_controls(::game& ctx) { // Setup menu controls ctx.menu_action_subscriptions.emplace_back @@ -355,7 +354,7 @@ void setup_menu_controls(::context& ctx) ctx.menu_right_action.set_threshold_function(menu_action_threshold); } -void setup_game_controls(::context& ctx) +void setup_game_controls(::game& ctx) { // Setup pause control ctx.movement_action_subscriptions.emplace_back @@ -375,7 +374,7 @@ void setup_game_controls(::context& ctx) ::disable_game_controls(ctx); // Push pause menu state - ctx.state_machine.emplace(new ::state::pause_menu(ctx)); + ctx.state_machine.emplace(std::make_unique(ctx)); } ); @@ -391,12 +390,12 @@ void setup_game_controls(::context& ctx) ); } -void enable_window_controls(::context& ctx) +void enable_window_controls(::game& ctx) { ctx.window_action_map.enable(); } -void enable_menu_controls(::context& ctx) +void enable_menu_controls(::game& ctx) { ctx.menu_action_map.enable(); @@ -497,22 +496,22 @@ void enable_menu_controls(::context& ctx) ); } -void enable_game_controls(::context& ctx) +void enable_game_controls(::game& ctx) { ctx.movement_action_map.enable(); } -void enable_nuptial_flight_controls(::context& ctx) +void enable_nuptial_flight_controls(::game& ctx) { ctx.nuptial_flight_action_map.enable(); } -void disable_window_controls(::context& ctx) +void disable_window_controls(::game& ctx) { ctx.window_action_map.disable(); } -void disable_menu_controls(::context& ctx) +void disable_menu_controls(::game& ctx) { ctx.menu_action_map.disable(); @@ -527,7 +526,7 @@ void disable_menu_controls(::context& ctx) ctx.menu_modifier_action.reset(); } -void disable_game_controls(::context& ctx) +void disable_game_controls(::game& ctx) { ctx.movement_action_map.disable(); @@ -540,7 +539,7 @@ void disable_game_controls(::context& ctx) ctx.pause_action.reset(); } -void disable_nuptial_flight_controls(::context& ctx) +void disable_nuptial_flight_controls(::game& ctx) { ctx.nuptial_flight_action_map.disable(); diff --git a/src/game/controls.hpp b/src/game/controls.hpp index f3e44a0..f4e9bee 100644 --- a/src/game/controls.hpp +++ b/src/game/controls.hpp @@ -20,7 +20,7 @@ #ifndef ANTKEEPER_GAME_CONTROLS_HPP #define ANTKEEPER_GAME_CONTROLS_HPP -#include "game/context.hpp" +#include "game/game.hpp" #include #include #include @@ -39,7 +39,7 @@ void reset_control_profile(::control_profile& profile); * @param ctx Game context. * @param profile Control profile to apply. */ -void apply_control_profile(::context& ctx, const ::control_profile& profile); +void apply_control_profile(::game& ctx, const ::control_profile& profile); /** * Updates a control profile after actions have been remapped. @@ -47,21 +47,21 @@ void apply_control_profile(::context& ctx, const ::control_profile& profile); * @param ctx Game context. * @param profile Control profile to update. */ -void update_control_profile(::context& ctx, ::control_profile& profile); +void update_control_profile(::game& ctx, ::control_profile& profile); -void setup_window_controls(::context& ctx); -void setup_menu_controls(::context& ctx); -void setup_game_controls(::context& ctx); +void setup_window_controls(::game& ctx); +void setup_menu_controls(::game& ctx); +void setup_game_controls(::game& ctx); -void enable_window_controls(::context& ctx); -void enable_menu_controls(::context& ctx); -void enable_game_controls(::context& ctx); -void enable_nuptial_flight_controls(::context& ctx); +void enable_window_controls(::game& ctx); +void enable_menu_controls(::game& ctx); +void enable_game_controls(::game& ctx); +void enable_nuptial_flight_controls(::game& ctx); -void disable_window_controls(::context& ctx); -void disable_menu_controls(::context& ctx); -void disable_game_controls(::context& ctx); -void disable_nuptial_flight_controls(::context& ctx); +void disable_window_controls(::game& ctx); +void disable_menu_controls(::game& ctx); +void disable_game_controls(::game& ctx); +void disable_nuptial_flight_controls(::game& ctx); #endif // ANTKEEPER_GAME_CONTROLS_HPP diff --git a/src/game/fonts.cpp b/src/game/fonts.cpp index c328556..d114810 100644 --- a/src/game/fonts.cpp +++ b/src/game/fonts.cpp @@ -68,7 +68,7 @@ static void build_bitmap_font(const type::typeface& typeface, float size, const material.set_shader_program(shader_program); } -void load_fonts(::context& ctx) +void load_fonts(::game& ctx) { // Load dyslexia-friendly typeface (if enabled) bool dyslexia_font_loaded = false; diff --git a/src/game/fonts.hpp b/src/game/fonts.hpp index 45a47c0..c8a71e6 100644 --- a/src/game/fonts.hpp +++ b/src/game/fonts.hpp @@ -20,10 +20,10 @@ #ifndef ANTKEEPER_GAME_FONTS_HPP #define ANTKEEPER_GAME_FONTS_HPP -#include "game/context.hpp" +#include "game/game.hpp" -void load_fonts(::context& ctx); +void load_fonts(::game& ctx); #endif // ANTKEEPER_GAME_FONTS_HPP diff --git a/src/game/game.cpp b/src/game/game.cpp new file mode 100644 index 0000000..a92e30c --- /dev/null +++ b/src/game/game.cpp @@ -0,0 +1,1340 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include "game/game.hpp" +#include "game/commands/commands.hpp" +#include "game/control-profile.hpp" +#include "game/controls.hpp" +#include "game/fonts.hpp" +#include "game/graphics.hpp" +#include "game/menu.hpp" +#include "game/settings.hpp" +#include "game/states/main-menu-state.hpp" +#include "game/states/splash-state.hpp" +#include "game/strings.hpp" +#include "game/systems/astronomy-system.hpp" +#include "game/systems/atmosphere-system.hpp" +#include "game/systems/behavior-system.hpp" +#include "game/systems/blackbody-system.hpp" +#include "game/systems/camera-system.hpp" +#include "game/systems/collision-system.hpp" +#include "game/systems/constraint-system.hpp" +#include "game/systems/locomotion-system.hpp" +#include "game/systems/orbit-system.hpp" +#include "game/systems/render-system.hpp" +#include "game/systems/spatial-system.hpp" +#include "game/systems/spring-system.hpp" +#include "game/systems/steering-system.hpp" +#include "game/systems/subterrain-system.hpp" +#include "game/systems/terrain-system.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Prevent cxxopts from using RTTI +#define CXXOPTS_NO_RTTI +#include + +using namespace hash::literals; + +game::game(int argc, const char* const* argv) +{ + // Boot process + debug::log::trace("Booting up..."); + + parse_options(argc, argv); + setup_resources(); + load_settings(); + setup_window(); + setup_audio(); + setup_input(); + load_strings(); + setup_rendering(); + setup_scenes(); + setup_animation(); + setup_ui(); + setup_entities(); + setup_systems(); + setup_controls(); + setup_debugging(); + setup_loop(); + + active_ecoregion = nullptr; + closed = false; + + debug::log::trace("Boot up complete"); +} + +game::~game() +{ + debug::log::trace("Booting down..."); + + // Update window settings + const auto& windowed_position = window->get_windowed_position(); + const auto& windowed_size = window->get_windowed_size(); + const bool maximized = window->is_maximized(); + const bool fullscreen = window->is_fullscreen(); + (*settings)["window_x"_fnv1a32] = windowed_position.x(); + (*settings)["window_y"_fnv1a32] = windowed_position.y(); + (*settings)["window_w"_fnv1a32] = windowed_size.x(); + (*settings)["window_h"_fnv1a32] = windowed_size.y(); + (*settings)["maximized"_fnv1a32] = maximized; + (*settings)["fullscreen"_fnv1a32] = fullscreen; + + // Destruct window + window.reset(); + + // Save settings + resource_manager->set_write_dir(shared_config_path); + resource_manager->save(settings.get(), "settings.cfg"); + + // Destruct input and window managers + input_manager.reset(); + window_manager.reset(); + + // Shut down audio + shutdown_audio(); + + debug::log::trace("Boot down complete"); +} + +void game::execute() +{ + // Change to initial state + state_machine.emplace(std::make_unique(*this, true)); + + debug::log::trace("Entered main loop"); + + while (!closed) + { + // Execute main loop + loop.tick(); + + // Sample frame duration + average_frame_time(static_cast(loop.get_frame_duration() * 1000.0)); + + frame_time_text->set_content(std::format("◷{:5.02f}", average_frame_time.average())); + } + + debug::log::trace("Exited main loop"); + + // Exit all active game states + while (!state_machine.empty()) + { + state_machine.pop(); + } +} + +void game::parse_options(int argc, const char* const* argv) +{ + if (argc <= 1) + { + // No command-line options specified + return; + } + + debug::log::trace("Parsing command-line options..."); + + // Parse command-line options with cxxopts + try + { + cxxopts::Options options(config::application_name, config::application_name); + options.add_options() + ("c,continue", "Continues from the last save") + ("d,data", "Sets the data package path", cxxopts::value()) + ("f,fullscreen", "Starts in fullscreen mode") + ("n,new-game", "Starts a new game") + ("q,quick-start", "Skips to the main menu") + ("r,reset", "Resets all settings to default") + ("v,v-sync", "Enables or disables v-sync", cxxopts::value()) + ("w,windowed", "Starts in windowed mode"); + auto result = options.parse(argc, argv); + + // --continue + if (result.count("continue")) + { + option_continue = true; + } + + // --data + if (result.count("data")) + { + option_data = result["data"].as(); + } + + // --fullscreen + if (result.count("fullscreen")) + { + option_fullscreen = true; + } + + // --new-game + if (result.count("new-game")) + { + option_new_game = true; + } + + // --quick-start + if (result.count("quick-start")) + { + option_quick_start = true; + } + + // --reset + if (result.count("reset")) + { + option_reset = true; + } + + // --v-sync + if (result.count("v-sync")) + { + option_v_sync = result["v-sync"].as(); + } + + // --windowed + if (result.count("windowed")) + { + option_windowed = true; + } + + debug::log::info("Parsed {} command-line options", argc); + } + catch (const std::exception& e) + { + debug::log::error("An error occurred while parsing command-line options: {}", e.what()); + } +} + +void game::setup_resources() +{ + // Allocate resource manager + resource_manager = std::make_unique<::resource_manager>(); + + // Get executable data path + const auto data_path = get_executable_data_path(); + + // Determine data package path + if (option_data) + { + // Handle command-line data path option + data_package_path = option_data.value(); + if (data_package_path.is_relative()) + { + data_package_path = data_path / data_package_path; + } + } + else + { + data_package_path = data_path / (config::application_slug + std::string("-data.zip")); + } + + // Determine mods path + mods_path = data_path / "mods"; + + // Determine config paths + local_config_path = get_local_config_path() / config::application_name; + shared_config_path = get_shared_config_path() / config::application_name; + saves_path = shared_config_path / "saves"; + screenshots_path = shared_config_path / "gallery"; + controls_path = shared_config_path / "controls"; + + // Log paths + debug::log::info("Data package path: \"{}\"", data_package_path.string()); + debug::log::info("Local config path: \"{}\"", local_config_path.string()); + debug::log::info("Shared config path: \"{}\"", shared_config_path.string()); + debug::log::info("Mods path: \"{}\"", mods_path.string()); + + // Create nonexistent config directories + std::vector config_paths; + config_paths.push_back(local_config_path); + config_paths.push_back(shared_config_path); + config_paths.push_back(saves_path); + config_paths.push_back(screenshots_path); + config_paths.push_back(controls_path); + for (const auto& path: config_paths) + { + try + { + if (std::filesystem::create_directories(path)) + { + debug::log::info("Created directory \"{}\"", path.string()); + } + } + catch (const std::filesystem::filesystem_error& e) + { + debug::log::error("Failed to create directory \"{}\": {}", path.string(), e.what()); + } + } + + // Scan for mods + std::vector mod_paths; + if (std::filesystem::is_directory(mods_path)) + { + for (const auto& entry: std::filesystem::directory_iterator{mods_path}) + { + if (entry.is_directory() || (entry.is_regular_file() && entry.path().extension() == ".zip")) + { + mod_paths.push_back(entry.path()); + debug::log::info("Found mod \"{}\"", entry.path().filename().string()); + } + } + } + + // Mount mod paths + for (const std::filesystem::path& mod_path: mod_paths) + { + resource_manager->mount(mods_path / mod_path); + } + + // Mount config path + resource_manager->mount(local_config_path); + resource_manager->mount(shared_config_path); + + // Mount data package path + resource_manager->mount(data_package_path); + + // Include resource search paths in order of priority + resource_manager->include("/controls/"); + resource_manager->include("/"); +} + +void game::load_settings() +{ + if (option_reset) + { + // Command-line reset option found, reset settings + settings = std::make_shared>(); + resource_manager->set_write_dir(shared_config_path); + resource_manager->save(settings.get(), "settings.cfg"); + debug::log::info("Settings reset"); + } + else + { + settings = std::make_shared>(); + + dict* loaded_settings = resource_manager->load>("settings.cfg"); + if (loaded_settings) + { + /// @TODO: don't copy loaded settings, rather return a shared_ptr from the resource manager + *settings = *loaded_settings; + } + else + { + debug::log::info("Settings not found"); + } + } +} + +void game::setup_window() +{ + // Construct window manager + window_manager.reset(app::window_manager::instance()); + + // Default window settings + std::string window_title = config::application_name; + int window_x = -1; + int window_y = -1; + int window_w = -1; + int window_h = -1; + bool maximized = true; + bool fullscreen = true; + bool v_sync = true; + + // Read window settings + bool resize = false; + read_or_write_setting(*this, "window_title"_fnv1a32, window_title); + read_or_write_setting(*this, "window_x"_fnv1a32, window_x); + read_or_write_setting(*this, "window_y"_fnv1a32, window_y); + if (!read_or_write_setting(*this, "window_w"_fnv1a32, window_w) || + !read_or_write_setting(*this, "window_h"_fnv1a32, window_h)) + { + resize = true; + } + read_or_write_setting(*this, "maximized"_fnv1a32, maximized); + read_or_write_setting(*this, "fullscreen"_fnv1a32, fullscreen); + read_or_write_setting(*this, "v_sync"_fnv1a32, v_sync); + + // If window size not set, resize and reposition relative to default display + if (resize) + { + const app::display& display = window_manager->get_display(0); + const auto& usable_bounds = display.get_usable_bounds(); + const auto usable_bounds_center = usable_bounds.center(); + + const float default_windowed_scale = 1.0f / 1.2f; + + window_w = static_cast((usable_bounds.max.x() - usable_bounds.min.x()) * default_windowed_scale); + window_h = static_cast((usable_bounds.max.y() - usable_bounds.min.y()) * default_windowed_scale); + window_x = usable_bounds_center.x() - window_w / 2; + window_y = usable_bounds_center.y() - window_h / 2; + } + + // Handle window-related command-line options + if (option_windowed) + { + // Start in windowed mode + maximized = false; + fullscreen = false; + } + if (option_fullscreen) + { + // Start in fullscreen mode + fullscreen = true; + } + if (option_v_sync) + { + v_sync = option_v_sync.value(); + } + + // Construct window + window = window_manager->create_window + ( + window_title, + {window_x, window_y}, + {window_w, window_h}, + maximized, + fullscreen, + v_sync + ); + + // Restrict window size + window->set_minimum_size({160, 144}); + + // Setup window closed callback + window_closed_subscription = window->get_closed_channel().subscribe + ( + [&](const auto& event) + { + closed = true; + } + ); +} + +void game::setup_audio() +{ + debug::log::trace("Setting up audio..."); + + // Default audio settings + master_volume = 1.0f; + ambience_volume = 1.0f; + effects_volume = 1.0f; + mono_audio = false; + captions = false; + captions_size = 1.0f; + + // Read audio settings + read_or_write_setting(*this, "master_volume"_fnv1a32, master_volume); + read_or_write_setting(*this, "ambience_volume"_fnv1a32, ambience_volume); + read_or_write_setting(*this, "effects_volume"_fnv1a32, effects_volume); + read_or_write_setting(*this, "mono_audio"_fnv1a32, mono_audio); + read_or_write_setting(*this, "captions"_fnv1a32, captions); + read_or_write_setting(*this, "captions_size"_fnv1a32, captions_size); + + // Open audio device + debug::log::trace("Opening audio device..."); + alc_device = alcOpenDevice(nullptr); + if (!alc_device) + { + debug::log::error("Failed to open audio device: AL error code {}", alGetError()); + return; + } + else + { + // Get audio device name + const ALCchar* alc_device_name = nullptr; + if (alcIsExtensionPresent(alc_device, "ALC_ENUMERATE_ALL_EXT")) + { + alc_device_name = alcGetString(alc_device, ALC_ALL_DEVICES_SPECIFIER); + } + if (alcGetError(alc_device) != AL_NO_ERROR || !alc_device_name) + { + alc_device_name = alcGetString(alc_device, ALC_DEVICE_SPECIFIER); + } + + // Log audio device name + debug::log::info("Opened audio device \"{}\"", alc_device_name); + } + + // Create audio context + debug::log::trace("Creating audio context..."); + alc_context = alcCreateContext(alc_device, nullptr); + if (!alc_context) + { + debug::log::error("Failed to create audio context: ALC error code {}", alcGetError(alc_device)); + alcCloseDevice(alc_device); + return; + } + else + { + debug::log::trace("Created audio context"); + } + + // Make audio context current + debug::log::trace("Making audio context current..."); + if (alcMakeContextCurrent(alc_context) == ALC_FALSE) + { + debug::log::error("Failed to make audio context current: ALC error code {}", alcGetError(alc_device)); + if (alc_context != nullptr) + { + alcDestroyContext(alc_context); + } + alcCloseDevice(alc_device); + return; + } + else + { + debug::log::trace("Made audio context current"); + } + + debug::log::trace("Set up audio"); +} + +void game::setup_input() +{ + // Construct input manager + input_manager.reset(app::input_manager::instance()); + + // Process initial input events, such as connecting gamepads + input_manager->update(); + + // Setup application quit callback + application_quit_subscription = input_manager->get_event_queue().subscribe + ( + [&](const auto& event) + { + closed = true; + } + ); + + // Gamepad deactivation function + auto deactivate_gamepad = [&](const auto& event) + { + if (gamepad_active) + { + gamepad_active = false; + input_manager->show_cursor(); + } + }; + + // Setup gamepad activation callbacks + gamepad_axis_moved_subscription = input_manager->get_event_queue().subscribe + ( + [&](const auto& event) + { + if (!gamepad_active && std::abs(event.position) > 0.5f) + { + gamepad_active = true; + input_manager->hide_cursor(); + } + } + ); + gamepad_button_pressed_subscription = input_manager->get_event_queue().subscribe + ( + [&](const auto& event) + { + if (!gamepad_active) + { + gamepad_active = true; + input_manager->hide_cursor(); + } + } + ); + + // Setup gamepad deactivation callbacks + mouse_button_pressed_subscription = input_manager->get_event_queue().subscribe + ( + deactivate_gamepad + ); + mouse_moved_subscription = input_manager->get_event_queue().subscribe + ( + deactivate_gamepad + ); + mouse_scrolled_subscription = input_manager->get_event_queue().subscribe + ( + deactivate_gamepad + ); + + // Activate gamepad if one is connected + if (!input_manager->get_gamepads().empty()) + { + gamepad_active = true; + input_manager->hide_cursor(); + } + else + { + gamepad_active = false; + } +} + +void game::load_strings() +{ + debug::log::trace("Loading strings..."); + + // Default strings settings + language_tag = "en"; + + // Read strings settings + read_or_write_setting(*this, "language_tag"_fnv1a32, language_tag); + + // Slugify language tag + std::string language_slug = language_tag; + std::transform + ( + language_slug.begin(), + language_slug.end(), + language_slug.begin(), + [](unsigned char c) + { + return std::tolower(c); + } + ); + + // Load string map + string_map = resource_manager->load(language_slug + ".str"); + + // Log language info + debug::log::info("Language tag: {}", language_tag); + + // Change window title + const std::string window_title = get_string(*this, "window_title"_fnv1a32); + window->set_title(window_title); + + // Update window title setting + (*settings)["window_title"_fnv1a32] = window_title; + + debug::log::trace("Loaded strings"); +} + +void game::setup_rendering() +{ + debug::log::trace("Setting up rendering..."); + + // Default rendering settings + render_scale = 1.0f; + anti_aliasing_method = render::anti_aliasing_method::fxaa; + shadow_map_resolution = 4096; + + // Read rendering settings + read_or_write_setting(*this, "render_scale"_fnv1a32, render_scale); + read_or_write_setting(*this, "anti_aliasing_method"_fnv1a32, *reinterpret_cast*>(&anti_aliasing_method)); + read_or_write_setting(*this, "shadow_map_resolution"_fnv1a32, shadow_map_resolution); + + // Create framebuffers + ::graphics::create_framebuffers(*this); + + // Load blue noise texture + gl::texture_2d* blue_noise_map = resource_manager->load("blue-noise.tex"); + + // Load fallback material + fallback_material = resource_manager->load("fallback.mtl"); + + // Setup common render passes + { + // Construct bloom pass + bloom_pass = std::make_unique(window->get_rasterizer(), resource_manager.get()); + bloom_pass->set_source_texture(hdr_color_texture); + bloom_pass->set_mip_chain_length(5); + bloom_pass->set_filter_radius(0.005f); + + common_final_pass = std::make_unique(window->get_rasterizer(), ldr_framebuffer_a, resource_manager.get()); + common_final_pass->set_color_texture(hdr_color_texture); + common_final_pass->set_bloom_texture(bloom_pass->get_bloom_texture()); + common_final_pass->set_bloom_weight(0.04f); + common_final_pass->set_blue_noise_texture(blue_noise_map); + + fxaa_pass = std::make_unique(window->get_rasterizer(), &window->get_rasterizer()->get_default_framebuffer(), resource_manager.get()); + fxaa_pass->set_source_texture(ldr_color_texture_a); + + resample_pass = std::make_unique(window->get_rasterizer(), &window->get_rasterizer()->get_default_framebuffer(), resource_manager.get()); + resample_pass->set_source_texture(ldr_color_texture_b); + resample_pass->set_enabled(false); + + // Configure anti-aliasing according to settings + graphics::select_anti_aliasing_method(*this, anti_aliasing_method); + + // Configure render scaling according to settings + graphics::change_render_resolution(*this, render_scale); + } + + // Setup UI compositor + { + ui_clear_pass = std::make_unique(window->get_rasterizer(), &window->get_rasterizer()->get_default_framebuffer()); + ui_clear_pass->set_cleared_buffers(false, true, false); + ui_clear_pass->set_clear_depth(-1.0f); + + ui_material_pass = std::make_unique(window->get_rasterizer(), &window->get_rasterizer()->get_default_framebuffer(), resource_manager.get()); + ui_material_pass->set_fallback_material(fallback_material); + + ui_compositor = std::make_unique(); + ui_compositor->add_pass(ui_clear_pass.get()); + ui_compositor->add_pass(ui_material_pass.get()); + } + + // Setup underground compositor + { + underground_clear_pass = std::make_unique(window->get_rasterizer(), hdr_framebuffer); + underground_clear_pass->set_cleared_buffers(true, true, false); + underground_clear_pass->set_clear_color({1, 0, 1, 0}); + underground_clear_pass->set_clear_depth(-1.0f); + + underground_material_pass = std::make_unique(window->get_rasterizer(), hdr_framebuffer, resource_manager.get()); + underground_material_pass->set_fallback_material(fallback_material); + + underground_compositor = std::make_unique(); + underground_compositor->add_pass(underground_clear_pass.get()); + underground_compositor->add_pass(underground_material_pass.get()); + underground_compositor->add_pass(bloom_pass.get()); + underground_compositor->add_pass(common_final_pass.get()); + underground_compositor->add_pass(fxaa_pass.get()); + underground_compositor->add_pass(resample_pass.get()); + } + + // Setup surface compositor + { + surface_shadow_map_clear_pass = std::make_unique(window->get_rasterizer(), shadow_map_framebuffer); + surface_shadow_map_clear_pass->set_cleared_buffers(false, true, false); + surface_shadow_map_clear_pass->set_clear_depth(1.0f); + + surface_shadow_map_pass = std::make_unique(window->get_rasterizer(), resource_manager.get()); + + surface_clear_pass = std::make_unique(window->get_rasterizer(), hdr_framebuffer); + surface_clear_pass->set_cleared_buffers(false, true, true); + surface_clear_pass->set_clear_depth(-1.0f); + + sky_pass = std::make_unique(window->get_rasterizer(), hdr_framebuffer, resource_manager.get()); + sky_pass->set_enabled(false); + sky_pass->set_magnification(3.0f); + + ground_pass = std::make_unique(window->get_rasterizer(), hdr_framebuffer, resource_manager.get()); + ground_pass->set_enabled(false); + + surface_material_pass = std::make_unique(window->get_rasterizer(), hdr_framebuffer, resource_manager.get()); + surface_material_pass->set_fallback_material(fallback_material); + + surface_outline_pass = std::make_unique(window->get_rasterizer(), hdr_framebuffer, resource_manager.get()); + surface_outline_pass->set_outline_width(0.25f); + surface_outline_pass->set_outline_color(float4{1.0f, 1.0f, 1.0f, 1.0f}); + + surface_compositor = std::make_unique(); + surface_compositor->add_pass(surface_shadow_map_clear_pass.get()); + surface_compositor->add_pass(surface_shadow_map_pass.get()); + surface_compositor->add_pass(surface_clear_pass.get()); + surface_compositor->add_pass(sky_pass.get()); + surface_compositor->add_pass(ground_pass.get()); + surface_compositor->add_pass(surface_material_pass.get()); + //surface_compositor->add_pass(surface_outline_pass.get()); + surface_compositor->add_pass(bloom_pass.get()); + surface_compositor->add_pass(common_final_pass.get()); + surface_compositor->add_pass(fxaa_pass.get()); + surface_compositor->add_pass(resample_pass.get()); + } + + // Create billboard VAO + { + const float billboard_vertex_data[] = + { + -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, + -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, + 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, + 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, + -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, + 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f + }; + + std::size_t billboard_vertex_size = 8; + std::size_t billboard_vertex_stride = sizeof(float) * billboard_vertex_size; + std::size_t billboard_vertex_count = 6; + + billboard_vbo = std::make_unique(sizeof(float) * billboard_vertex_size * billboard_vertex_count, billboard_vertex_data); + billboard_vao = std::make_unique(); + + std::size_t attribute_offset = 0; + + // Define position vertex attribute + gl::vertex_attribute position_attribute; + position_attribute.buffer = billboard_vbo.get(); + position_attribute.offset = attribute_offset; + position_attribute.stride = billboard_vertex_stride; + position_attribute.type = gl::vertex_attribute_type::float_32; + position_attribute.components = 3; + attribute_offset += position_attribute.components * sizeof(float); + + // Define UV vertex attribute + gl::vertex_attribute uv_attribute; + uv_attribute.buffer = billboard_vbo.get(); + uv_attribute.offset = attribute_offset; + uv_attribute.stride = billboard_vertex_stride; + uv_attribute.type = gl::vertex_attribute_type::float_32; + uv_attribute.components = 2; + attribute_offset += uv_attribute.components * sizeof(float); + + // Define barycentric vertex attribute + gl::vertex_attribute barycentric_attribute; + barycentric_attribute.buffer = billboard_vbo.get(); + barycentric_attribute.offset = attribute_offset; + barycentric_attribute.stride = billboard_vertex_stride; + barycentric_attribute.type = gl::vertex_attribute_type::float_32; + barycentric_attribute.components = 3; + attribute_offset += barycentric_attribute.components * sizeof(float); + + // Bind vertex attributes to VAO + billboard_vao->bind(render::vertex_attribute::position, position_attribute); + billboard_vao->bind(render::vertex_attribute::uv, uv_attribute); + billboard_vao->bind(render::vertex_attribute::barycentric, barycentric_attribute); + } + + // Create renderer + renderer = std::make_unique(); + renderer->set_billboard_vao(billboard_vao.get()); + + debug::log::trace("Set up rendering"); +} + +void game::setup_scenes() +{ + debug::log::trace("Setting up scenes..."); + + // Get default framebuffer + const auto& viewport_size = window->get_viewport_size(); + const float viewport_aspect_ratio = static_cast(viewport_size[0]) / static_cast(viewport_size[1]); + + // Setup surface scene + surface_scene = std::make_unique(); + + // Setup surface camera + surface_camera = std::make_unique(); + surface_camera->set_perspective(math::radians(45.0f), viewport_aspect_ratio, 0.1f, 5000.0f); + surface_camera->set_compositor(surface_compositor.get()); + surface_camera->set_composite_index(0); + surface_camera->set_active(false); + + // Add surface scene objects to surface scene + surface_scene->add_object(surface_camera.get()); + + // Setup underground scene + underground_scene = std::make_unique(); + + // Setup underground camera + underground_camera = std::make_unique(); + underground_camera->set_perspective(math::radians(45.0f), viewport_aspect_ratio, 0.1f, 1000.0f); + underground_camera->set_compositor(underground_compositor.get()); + underground_camera->set_composite_index(0); + underground_camera->set_active(false); + + // Add underground scene objects to underground scene + underground_scene->add_object(underground_camera.get()); + + // Clear active scene + active_scene = nullptr; + + debug::log::trace("Set up scenes"); +} + +void game::setup_animation() +{ + // Setup timeline system + timeline = std::make_unique<::timeline>(); + timeline->set_autoremove(true); + + // Setup animator + animator = std::make_unique<::animator>(); +} + +void game::setup_ui() +{ + // Default UI settings + font_scale = 1.0f; + debug_font_size_pt = 10.0f; + menu_font_size_pt = 22.0f; + title_font_size_pt = 80.0f; + dyslexia_font = false; + + // Read UI settings + read_or_write_setting(*this, "font_scale"_fnv1a32, font_scale); + read_or_write_setting(*this, "debug_font_size_pt"_fnv1a32, debug_font_size_pt); + read_or_write_setting(*this, "menu_font_size_pt"_fnv1a32, menu_font_size_pt); + read_or_write_setting(*this, "title_font_size_pt"_fnv1a32, title_font_size_pt); + read_or_write_setting(*this, "dyslexia_font"_fnv1a32, dyslexia_font); + + // Load fonts + debug::log::trace("Loading fonts..."); + try + { + ::load_fonts(*this); + debug::log::trace("Loaded fonts"); + } + catch (...) + { + debug::log::error("Failed to load fonts"); + } + + // Get default framebuffer + const auto& viewport_size = window->get_viewport_size(); + const float viewport_aspect_ratio = static_cast(viewport_size[0]) / static_cast(viewport_size[1]); + + // Setup UI scene + ui_scene = std::make_unique(); + + // Setup UI camera + ui_camera = std::make_unique(); + ui_camera->set_compositor(ui_compositor.get()); + const float clip_left = 0.0f; + const float clip_right = static_cast(viewport_size.x()); + const float clip_top = 0.0f; + const float clip_bottom = static_cast(viewport_size.y()); + const float clip_near = -100.0f; + const float clip_far = 100.0f; + ui_camera->set_orthographic(clip_left, clip_right, clip_top, clip_bottom, clip_near, clip_far); + ui_camera->look_at({0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, -1.0f}, {0.0f, 1.0f, 0.0f}); + ui_camera->update_tweens(); + + // Menu BG material + menu_bg_material.set_shader_program(resource_manager->load("ui-element-untextured.glsl")); + auto menu_bg_tint = menu_bg_material.add_property("tint"); + menu_bg_tint->set_value(float4{0.0f, 0.0f, 0.0f, 0.5f}); + menu_bg_material.set_blend_mode(render::blend_mode::translucent); + menu_bg_material.update_tweens(); + + // Menu BG billboard + menu_bg_billboard = std::make_unique(); + menu_bg_billboard->set_active(false); + menu_bg_billboard->set_material(&menu_bg_material); + menu_bg_billboard->set_scale({std::ceil(viewport_size.x() * 0.5f), std::ceil(viewport_size.y() * 0.5f), 1.0f}); + menu_bg_billboard->set_translation({std::floor(viewport_size.x() * 0.5f), std::floor(viewport_size.y() * 0.5f), -100.0f}); + menu_bg_billboard->update_tweens(); + + // Create fade transition + fade_transition = std::make_unique(); + fade_transition->get_material()->set_shader_program(resource_manager->load("fade-transition.glsl")); + fade_transition_color = fade_transition->get_material()->add_property("color"); + fade_transition_color->set_value({0, 0, 0}); + fade_transition->get_billboard()->set_translation({0, 0, 98}); + fade_transition->get_billboard()->update_tweens(); + + // Create inner radial transition + radial_transition_inner = std::make_unique(); + radial_transition_inner->get_material()->set_shader_program(resource_manager->load("radial-transition-inner.glsl")); + + // Create outer radial transition + radial_transition_outer = std::make_unique(); + radial_transition_outer->get_material()->set_shader_program(resource_manager->load("radial-transition-outer.glsl")); + + // Menu BG animations + { + auto menu_bg_frame_callback = [menu_bg_tint](int channel, const float& opacity) + { + menu_bg_tint->set_value(float4{0.0f, 0.0f, 0.0f, opacity}); + }; + + // Menu BG fade in animation + menu_bg_fade_in_animation = std::make_unique>(); + { + menu_bg_fade_in_animation->set_interpolator(ease::out_cubic); + animation_channel* channel = menu_bg_fade_in_animation->add_channel(0); + channel->insert_keyframe({0.0f, 0.0f}); + channel->insert_keyframe({config::menu_fade_in_duration, config::menu_bg_opacity}); + menu_bg_fade_in_animation->set_frame_callback(menu_bg_frame_callback); + menu_bg_fade_in_animation->set_start_callback + ( + [&, menu_bg_tint]() + { + ui_scene->add_object(menu_bg_billboard.get()); + + menu_bg_tint->set_value(float4{0.0f, 0.0f, 0.0f, 0.0f}); + menu_bg_tint->update_tweens(); + menu_bg_billboard->set_active(true); + } + ); + } + + // Menu BG fade out animation + menu_bg_fade_out_animation = std::make_unique>(); + { + menu_bg_fade_out_animation->set_interpolator(ease::out_cubic); + animation_channel* channel = menu_bg_fade_out_animation->add_channel(0); + channel->insert_keyframe({0.0f, config::menu_bg_opacity}); + channel->insert_keyframe({config::menu_fade_out_duration, 0.0f}); + menu_bg_fade_out_animation->set_frame_callback(menu_bg_frame_callback); + menu_bg_fade_out_animation->set_end_callback + ( + [&]() + { + ui_scene->remove_object(menu_bg_billboard.get()); + menu_bg_billboard->set_active(false); + } + ); + } + } + + // Add UI scene objects to UI scene + ui_scene->add_object(ui_camera.get()); + ui_scene->add_object(fade_transition->get_billboard()); + + // Add UI animations to animator + animator->add_animation(fade_transition->get_animation()); + animator->add_animation(menu_bg_fade_in_animation.get()); + animator->add_animation(menu_bg_fade_out_animation.get()); + + // Setup window resized callback + window_resized_subscription = window->get_resized_channel().subscribe + ( + [&](const auto& event) + { + const auto& viewport_size = event.window->get_viewport_size(); + const float viewport_aspect_ratio = static_cast(viewport_size.x()) / static_cast(viewport_size.y()); + + // Resize framebuffers + ::graphics::change_render_resolution(*this, render_scale); + + // Update camera projection matrix + surface_camera->set_perspective + ( + surface_camera->get_fov(), + viewport_aspect_ratio, + surface_camera->get_clip_near(), + surface_camera->get_clip_far() + ); + + // Update UI camera projection matrix + ui_camera->set_orthographic + ( + 0.0f, + static_cast(viewport_size.x()), + 0.0f, + static_cast(viewport_size.y()), + ui_camera->get_clip_near(), + ui_camera->get_clip_far() + ); + + // Resize menu BG billboard + menu_bg_billboard->set_scale({std::ceil(viewport_size.x() * 0.5f), std::ceil(viewport_size.y() * 0.5f), 1.0f}); + menu_bg_billboard->set_translation({std::floor(viewport_size.x() * 0.5f), std::floor(viewport_size.y() * 0.5f), -100.0f}); + + // Re-align debug text + frame_time_text->set_translation({std::round(0.0f), std::round(viewport_size.y() - debug_font.get_font_metrics().size), 99.0f}); + frame_time_text->update_tweens(); + + // Re-align menu text + ::menu::align_text(*this); + } + ); +} + +void game::setup_entities() +{ + // Create entity registry + entity_registry = std::make_unique(); +} + +void game::setup_systems() +{ + const auto& viewport_size = window->get_viewport_size(); + float4 viewport = {0.0f, 0.0f, static_cast(viewport_size[0]), static_cast(viewport_size[1])}; + + // Setup terrain system + terrain_system = std::make_unique<::terrain_system>(*entity_registry); + terrain_system->set_patch_side_length(31.0f); + terrain_system->set_patch_subdivisions(31); + terrain_system->set_scene_collection(surface_scene.get()); + + // Setup camera system + camera_system = std::make_unique<::camera_system>(*entity_registry); + camera_system->set_viewport(viewport); + + // Setup subterrain system + subterrain_system = std::make_unique<::subterrain_system>(*entity_registry, resource_manager.get()); + subterrain_system->set_scene(underground_scene.get()); + + // Setup collision system + collision_system = std::make_unique<::collision_system>(*entity_registry); + + // Setup behavior system + behavior_system = std::make_unique<::behavior_system>(*entity_registry); + + // Setup locomotion system + locomotion_system = std::make_unique<::locomotion_system>(*entity_registry); + + // Setup steering system + steering_system = std::make_unique<::steering_system>(*entity_registry); + + // Setup spring system + spring_system = std::make_unique<::spring_system>(*entity_registry); + + // Setup spatial system + spatial_system = std::make_unique<::spatial_system>(*entity_registry); + + // Setup constraint system + constraint_system = std::make_unique<::constraint_system>(*entity_registry); + + // Setup orbit system + orbit_system = std::make_unique<::orbit_system>(*entity_registry); + + // Setup blackbody system + blackbody_system = std::make_unique<::blackbody_system>(*entity_registry); + blackbody_system->set_illuminant(color::illuminant::deg2::d55); + + // RGB wavelengths for atmospheric scatteering + rgb_wavelengths = {680, 550, 440}; + + // Setup atmosphere system + atmosphere_system = std::make_unique<::atmosphere_system>(*entity_registry); + atmosphere_system->set_rgb_wavelengths(rgb_wavelengths * 1e-9); + atmosphere_system->set_sky_pass(sky_pass.get()); + + // Setup astronomy system + astronomy_system = std::make_unique<::astronomy_system>(*entity_registry); + astronomy_system->set_transmittance_samples(16); + astronomy_system->set_sky_pass(sky_pass.get()); + + // Setup render system + render_system = std::make_unique<::render_system>(*entity_registry); + render_system->set_renderer(renderer.get()); + //render_system->add_layer(underground_scene.get()); + render_system->add_layer(surface_scene.get()); + render_system->add_layer(ui_scene.get()); +} + +void game::setup_controls() +{ + debug::log::trace("Setting up controls..."); + + // Load SDL game controller mappings database + // debug::log::trace("Loading SDL game controller mappings..."); + // file_buffer* game_controller_db = resource_manager->load("gamecontrollerdb.txt"); + // if (!game_controller_db) + // { + // debug::log::error("Failed to load SDL game controller mappings"); + // } + // else + // { + // app->add_game_controller_mappings(game_controller_db->data(), game_controller_db->size()); + // debug::log::trace("Loaded SDL game controller mappings"); + + // resource_manager->unload("gamecontrollerdb.txt"); + // } + + // Pass input event queue to action maps + event::queue* input_event_queue = &input_manager->get_event_queue(); + window_action_map.set_event_queue(input_event_queue); + menu_action_map.set_event_queue(input_event_queue); + movement_action_map.set_event_queue(input_event_queue); + nuptial_flight_action_map.set_event_queue(input_event_queue); + + // Default control profile settings + control_profile_filename = "controls.cfg"; + control_profile = nullptr; + + // Read control profile settings + if (read_or_write_setting(*this, "control_profile"_fnv1a32, control_profile_filename)) + { + /// @TODO: return a shared_ptr from the resource manager + control_profile = resource_manager->load<::control_profile>(control_profile_filename); + } + + if (!control_profile) + { + // Allocate control profile + control_profile = new ::control_profile(); + + // Reset control profile to default settings. + ::reset_control_profile(*control_profile); + + // Save control profile + resource_manager->set_write_dir(controls_path); + resource_manager->save(control_profile, control_profile_filename); + } + + // Apply control profile + ::apply_control_profile(*this, *control_profile); + + // Setup action callbacks + setup_window_controls(*this); + setup_menu_controls(*this); + setup_game_controls(*this); + + // Enable window controls + enable_window_controls(*this); + + debug::log::trace("Set up controls"); +} + +void game::setup_debugging() +{ + cli = std::make_unique(); + + const auto& viewport_size = window->get_viewport_size(); + + frame_time_text = std::make_unique(); + frame_time_text->set_material(&debug_font_material); + frame_time_text->set_color({1.0f, 1.0f, 0.0f, 1.0f}); + frame_time_text->set_font(&debug_font); + frame_time_text->set_translation({std::round(0.0f), std::round(viewport_size.y() - debug_font.get_font_metrics().size), 99.0f}); + frame_time_text->update_tweens(); + + ui_scene->add_object(frame_time_text.get()); +} + +void game::setup_loop() +{ + // Default loop settings + double update_frequency = 60.0; + + // Read loop settings + read_or_write_setting(*this, "update_frequency"_fnv1a32, update_frequency); + + // Set update frequency + loop.set_update_frequency(update_frequency); + + // Set update callback + loop.set_update_callback + ( + [&](double t, double dt) + { + // Update tweens + sky_pass->update_tweens(); + surface_scene->update_tweens(); + underground_scene->update_tweens(); + ui_scene->update_tweens(); + + // Process events + window_manager->update(); + input_manager->update(); + + // Process function queue + while (!function_queue.empty()) + { + function_queue.front()(); + function_queue.pop(); + } + + // Update processes + std::for_each + ( + std::execution::par, + processes.begin(), + processes.end(), + [t, dt](const auto& process) + { + process.second(t, dt); + } + ); + + // Advance timeline + timeline->advance(dt); + + // Update entity systems + //terrain_system->update(t, dt); + //subterrain_system->update(t, dt); + collision_system->update(t, dt); + behavior_system->update(t, dt); + steering_system->update(t, dt); + locomotion_system->update(t, dt); + camera_system->update(t, dt); + orbit_system->update(t, dt); + blackbody_system->update(t, dt); + atmosphere_system->update(t, dt); + astronomy_system->update(t, dt); + spring_system->update(t, dt); + spatial_system->update(t, dt); + constraint_system->update(t, dt); + animator->animate(dt); + render_system->update(t, dt); + } + ); + + // Set render callback + loop.set_render_callback + ( + [&](double alpha) + { + render_system->draw(alpha); + window->swap_buffers(); + } + ); +} + +void game::shutdown_audio() +{ + debug::log::trace("Shutting down audio..."); + + if (alc_context) + { + alcMakeContextCurrent(nullptr); + alcDestroyContext(alc_context); + } + + if (alc_device) + { + alcCloseDevice(alc_device); + } + + debug::log::trace("Shut down audio"); +} diff --git a/src/game/context.hpp b/src/game/game.hpp similarity index 63% rename from src/game/context.hpp rename to src/game/game.hpp index cabf144..4dd72f6 100644 --- a/src/game/context.hpp +++ b/src/game/game.hpp @@ -17,18 +17,20 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_GAME_CONTEXT_HPP -#define ANTKEEPER_GAME_CONTEXT_HPP +#ifndef ANTKEEPER_GAME_HPP +#define ANTKEEPER_GAME_HPP +#include "game/ecoregion.hpp" +#include "game/loop.hpp" +#include "game/states/game-state.hpp" +#include +#include #include #include #include #include #include #include -#include "game/ecoregion.hpp" -#include "game/loop.hpp" -#include "game/state/base.hpp" #include #include #include @@ -50,8 +52,6 @@ #include #include #include -#include -#include #include #include #include @@ -92,31 +92,47 @@ namespace render } -// Forward declaration of system types. -class subterrain_system; -class terrain_system; -class spatial_system; +// Forward declarations of system types. class astronomy_system; -class blackbody_system; class atmosphere_system; -class orbit_system; class behavior_system; +class blackbody_system; +class camera_system; class collision_system; class constraint_system; class locomotion_system; -class camera_system; class nest_system; +class orbit_system; class render_system; -class steering_system; +class spatial_system; class spring_system; +class steering_system; +class subterrain_system; +class terrain_system; struct control_profile; -/// Container for data shared between game states. -struct context + +class game { - context(); - ~context(); +public: + /** + * Boots up the game. + * + * @param argc Command line argument count. + * @param argv Command line argument vector. + */ + game(int argc, const char* const* argv); + + /** + * Boots down the game. + */ + ~game(); + + /** + * Executes the game. + */ + void execute(); // Command-line options std::optional option_continue; @@ -129,7 +145,7 @@ struct context std::optional option_windowed; // Resource management and paths - resource_manager* resource_manager; + std::unique_ptr resource_manager; std::filesystem::path data_package_path; std::filesystem::path mods_path; std::filesystem::path local_config_path; @@ -139,17 +155,17 @@ struct context std::filesystem::path controls_path; // Persistent settings - dict* settings; + std::shared_ptr> settings; // Window management and window event handling - app::window_manager* window_manager; - app::window* window; + std::unique_ptr window_manager; + std::shared_ptr window; bool closed; std::shared_ptr<::event::subscription> window_closed_subscription; std::shared_ptr<::event::subscription> window_resized_subscription; // Input management and input event handling - app::input_manager* input_manager; + std::unique_ptr input_manager; std::shared_ptr<::event::subscription> application_quit_subscription; std::shared_ptr<::event::subscription> gamepad_axis_moved_subscription; std::shared_ptr<::event::subscription> gamepad_button_pressed_subscription; @@ -207,11 +223,12 @@ struct context // Debugging - scene::text* frame_time_text; - debug::cli* cli; + math::moving_average average_frame_time; + std::unique_ptr frame_time_text; + std::unique_ptr cli; // Hierarchichal state machine - hsm::state_machine<::state::base> state_machine; + hsm::state_machine state_machine; std::function resume_callback; // Queue for scheduling "next frame" function calls @@ -237,42 +254,45 @@ struct context gl::framebuffer* shadow_map_framebuffer; // Rendering - gl::rasterizer* rasterizer; - render::renderer* renderer; + //gl::rasterizer* rasterizer; int2 render_resolution; float render_scale; int shadow_map_resolution; - gl::vertex_buffer* billboard_vbo; - gl::vertex_array* billboard_vao; + std::unique_ptr billboard_vbo; + std::unique_ptr billboard_vao; render::material* fallback_material; - - // Compositing - render::clear_pass* ui_clear_pass; - render::material_pass* ui_material_pass; - render::compositor* ui_compositor; - render::bloom_pass* bloom_pass; - render::final_pass* common_final_pass; - render::fxaa_pass* fxaa_pass; - render::resample_pass* resample_pass; - render::clear_pass* underground_clear_pass; - render::material_pass* underground_material_pass; - render::compositor* underground_compositor; - render::clear_pass* surface_shadow_map_clear_pass; - render::shadow_map_pass* surface_shadow_map_pass; - render::clear_pass* surface_clear_pass; - render::sky_pass* sky_pass; - render::material_pass* surface_material_pass; - render::outline_pass* surface_outline_pass; - render::compositor* surface_compositor; - render::ground_pass* ground_pass; + std::unique_ptr ui_clear_pass; + std::unique_ptr ui_material_pass; + std::unique_ptr ui_compositor; + std::unique_ptr bloom_pass; + std::unique_ptr common_final_pass; + std::unique_ptr fxaa_pass; + std::unique_ptr resample_pass; + std::unique_ptr underground_clear_pass; + std::unique_ptr underground_material_pass; + std::unique_ptr underground_compositor; + std::unique_ptr surface_shadow_map_clear_pass; + std::unique_ptr surface_shadow_map_pass; + std::unique_ptr surface_clear_pass; + std::unique_ptr sky_pass; + std::unique_ptr surface_material_pass; + std::unique_ptr surface_outline_pass; + std::unique_ptr surface_compositor; + std::unique_ptr ground_pass; + std::unique_ptr renderer; // Scene utilities scene::collection* active_scene; // UI - scene::collection* ui_scene; - scene::camera* ui_camera; - scene::billboard* camera_flash_billboard; + std::unique_ptr ui_scene; + std::unique_ptr ui_camera; + std::unique_ptr menu_bg_billboard; + render::material menu_bg_material; + std::unique_ptr> menu_fade_animation; + std::unique_ptr> menu_bg_fade_in_animation; + std::unique_ptr> menu_bg_fade_out_animation; + float font_scale; bool dyslexia_font; float debug_font_size_pt; @@ -286,33 +306,26 @@ struct context std::vector> menu_item_texts; std::unordered_map menu_item_indices; int* menu_item_index; - scene::billboard* menu_bg_billboard; - animation* menu_fade_animation; - animation* menu_bg_fade_in_animation; - animation* menu_bg_fade_out_animation; - // Surface scene - scene::collection* surface_scene; - scene::camera* surface_camera; - - // Underground scene - scene::collection* underground_scene; - scene::camera* underground_camera; - scene::ambient_light* underground_ambient_light; - scene::spot_light* flashlight_spot_light; + // Scene + std::unique_ptr surface_scene; + std::unique_ptr surface_camera; + std::unique_ptr underground_scene; + std::unique_ptr underground_camera; + std::unique_ptr underground_ambient_light; + std::unique_ptr flashlight_spot_light; // Animation - timeline* timeline; - animator* animator; - animation* radial_transition_in; - animation* radial_transition_out; - screen_transition* fade_transition; + std::unique_ptr timeline; + std::unique_ptr animator; + std::unique_ptr> radial_transition_in; + std::unique_ptr> radial_transition_out; + std::unique_ptr fade_transition; render::material_property* fade_transition_color; - screen_transition* radial_transition_inner; - screen_transition* radial_transition_outer; - animation* equip_tool_animation; - animation* unequip_tool_animation; - animation* camera_flash_animation; + std::unique_ptr radial_transition_inner; + std::unique_ptr radial_transition_outer; + std::unique_ptr> equip_tool_animation; + std::unique_ptr> unequip_tool_animation; // Sound ALCdevice* alc_device; @@ -325,34 +338,51 @@ struct context float captions_size; // Entities - entity::registry* entity_registry; + std::unique_ptr entity_registry; std::unordered_map entities; // Systems - ::behavior_system* behavior_system; - ::camera_system* camera_system; - ::collision_system* collision_system; - ::constraint_system* constraint_system; - ::locomotion_system* locomotion_system; - ::steering_system* steering_system; - ::render_system* render_system; - ::subterrain_system* subterrain_system; - ::terrain_system* terrain_system; - ::spring_system* spring_system; - ::spatial_system* spatial_system; - ::blackbody_system* blackbody_system; - ::atmosphere_system* atmosphere_system; - ::astronomy_system* astronomy_system; - ::orbit_system* orbit_system; - - std::unique_ptr<::orbit_system> unique_orbit; + std::unique_ptr<::behavior_system> behavior_system; + std::unique_ptr<::camera_system> camera_system; + std::unique_ptr<::collision_system> collision_system; + std::unique_ptr<::constraint_system> constraint_system; + std::unique_ptr<::locomotion_system> locomotion_system; + std::unique_ptr<::steering_system> steering_system; + std::unique_ptr<::render_system> render_system; + std::unique_ptr<::subterrain_system> subterrain_system; + std::unique_ptr<::terrain_system> terrain_system; + std::unique_ptr<::spring_system> spring_system; + std::unique_ptr<::spatial_system> spatial_system; + std::unique_ptr<::blackbody_system> blackbody_system; + std::unique_ptr<::atmosphere_system> atmosphere_system; + std::unique_ptr<::astronomy_system> astronomy_system; + std::unique_ptr<::orbit_system> orbit_system; double3 rgb_wavelengths; const ecoregion* active_ecoregion; render::anti_aliasing_method anti_aliasing_method; + +private: + void parse_options(int argc, const char* const* argv); + void setup_resources(); + void load_settings(); + void setup_window(); + void setup_input(); + void load_strings(); + void setup_rendering(); + void setup_audio(); + void setup_scenes(); + void setup_animation(); + void setup_ui(); + void setup_entities(); + void setup_systems(); + void setup_controls(); + void setup_debugging(); + void setup_loop(); + + void shutdown_audio(); }; - -#endif // ANTKEEPER_GAME_CONTEXT_HPP +#endif // ANTKEEPER_GAME_HPP diff --git a/src/game/graphics.cpp b/src/game/graphics.cpp index 4f2a9e3..71f2b24 100644 --- a/src/game/graphics.cpp +++ b/src/game/graphics.cpp @@ -37,9 +37,9 @@ namespace graphics { -static void reroute_framebuffers(::context& ctx); +static void reroute_framebuffers(::game& ctx); -void create_framebuffers(::context& ctx) +void create_framebuffers(::game& ctx) { debug::log::trace("Creating framebuffers..."); @@ -87,7 +87,7 @@ void create_framebuffers(::context& ctx) debug::log::trace("Created framebuffers"); } -void destroy_framebuffers(::context& ctx) +void destroy_framebuffers(::game& ctx) { debug::log::trace("Destroying framebuffers..."); @@ -119,7 +119,7 @@ void destroy_framebuffers(::context& ctx) debug::log::trace("Destroyed framebuffers"); } -void change_render_resolution(::context& ctx, float scale) +void change_render_resolution(::game& ctx, float scale) { debug::log::trace("Changing render resolution to {}...", scale); @@ -160,7 +160,7 @@ void change_render_resolution(::context& ctx, float scale) debug::log::trace("Changed render resolution to {}", scale); } -void save_screenshot(::context& ctx) +void save_screenshot(::game& ctx) { // Determine timestamped screenshot filename const auto time = std::chrono::floor(std::chrono::system_clock::now()); @@ -198,7 +198,7 @@ void save_screenshot(::context& ctx) } -void select_anti_aliasing_method(::context& ctx, render::anti_aliasing_method method) +void select_anti_aliasing_method(::game& ctx, render::anti_aliasing_method method) { // Switch AA method switch (method) @@ -222,7 +222,7 @@ void select_anti_aliasing_method(::context& ctx, render::anti_aliasing_method me ctx.anti_aliasing_method = method; } -void reroute_framebuffers(::context& ctx) +void reroute_framebuffers(::game& ctx) { if (ctx.fxaa_pass->is_enabled()) { diff --git a/src/game/graphics.hpp b/src/game/graphics.hpp index a4b2a2a..54b20bf 100644 --- a/src/game/graphics.hpp +++ b/src/game/graphics.hpp @@ -20,17 +20,17 @@ #ifndef ANTKEEPER_GAME_GRAPHICS_HPP #define ANTKEEPER_GAME_GRAPHICS_HPP -#include "game/context.hpp" +#include "game/game.hpp" #include namespace graphics { -void create_framebuffers(::context& ctx); -void destroy_framebuffers(::context& ctx); -void change_render_resolution(::context& ctx, float scale); -void save_screenshot(::context& ctx); -void toggle_bloom(::context& ctx, bool enabled); -void select_anti_aliasing_method(::context& ctx, render::anti_aliasing_method method); +void create_framebuffers(::game& ctx); +void destroy_framebuffers(::game& ctx); +void change_render_resolution(::game& ctx, float scale); +void save_screenshot(::game& ctx); +void toggle_bloom(::game& ctx, bool enabled); +void select_anti_aliasing_method(::game& ctx, render::anti_aliasing_method method); } // namespace graphics diff --git a/src/game/load.cpp b/src/game/load.cpp index 36e51aa..db9ebc9 100644 --- a/src/game/load.cpp +++ b/src/game/load.cpp @@ -24,7 +24,7 @@ namespace load { -void colony(::context& ctx, const std::filesystem::path& path) +void colony(::game& ctx, const std::filesystem::path& path) { const std::string path_string = path.string(); diff --git a/src/game/load.hpp b/src/game/load.hpp index e221f3d..0d82159 100644 --- a/src/game/load.hpp +++ b/src/game/load.hpp @@ -20,14 +20,14 @@ #ifndef ANTKEEPER_GAME_LOAD_HPP #define ANTKEEPER_GAME_LOAD_HPP -#include "game/context.hpp" +#include "game/game.hpp" namespace load { /** * Loads a colony */ -void colony(::context& ctx, const std::filesystem::path& path); +void colony(::game& ctx, const std::filesystem::path& path); } // namespace load diff --git a/src/game/main.cpp b/src/game/main.cpp index 7188d4d..b8e50d9 100644 --- a/src/game/main.cpp +++ b/src/game/main.cpp @@ -17,17 +17,16 @@ * along with Antkeeper source code. If not, see . */ +#include "game/game.hpp" +#include +#include #include #include #include -#include "game/context.hpp" -#include "game/state/boot.hpp" #include #include -#include #include #include -#include #include #include #include @@ -201,11 +200,7 @@ int main(int argc, char* argv[]) try { - // Allocate game context - ::context ctx; - - // Enter boot state - ctx.state_machine.emplace(new ::state::boot(ctx, argc, argv)); + game(argc, argv).execute(); } catch (const std::exception& e) { diff --git a/src/game/menu.cpp b/src/game/menu.cpp index bdf49ce..5a32c33 100644 --- a/src/game/menu.cpp +++ b/src/game/menu.cpp @@ -30,7 +30,7 @@ using namespace math::glsl; namespace menu { -void init_menu_item_index(::context& ctx, const std::string& menu_name) +void init_menu_item_index(::game& ctx, const std::string& menu_name) { if (auto it = ctx.menu_item_indices.find(menu_name); it != ctx.menu_item_indices.end()) { @@ -43,7 +43,7 @@ void init_menu_item_index(::context& ctx, const std::string& menu_name) } } -void update_text_font(::context& ctx) +void update_text_font(::game& ctx) { for (auto [name, value]: ctx.menu_item_texts) { @@ -58,7 +58,7 @@ void update_text_font(::context& ctx) } } -void update_text_color(::context& ctx) +void update_text_color(::game& ctx) { for (std::size_t i = 0; i < ctx.menu_item_texts.size(); ++i) { @@ -72,7 +72,7 @@ void update_text_color(::context& ctx) } } -void update_text_tweens(::context& ctx) +void update_text_tweens(::game& ctx) { for (auto [name, value]: ctx.menu_item_texts) { @@ -82,7 +82,7 @@ void update_text_tweens(::context& ctx) } } -void align_text(::context& ctx, bool center, bool has_back, float anchor_y) +void align_text(::game& ctx, bool center, bool has_back, float anchor_y) { @@ -165,7 +165,7 @@ void align_text(::context& ctx, bool center, bool has_back, float anchor_y) } } -void refresh_text(::context& ctx) +void refresh_text(::game& ctx) { for (auto [name, value]: ctx.menu_item_texts) { @@ -175,7 +175,7 @@ void refresh_text(::context& ctx) } } -void add_text_to_ui(::context& ctx) +void add_text_to_ui(::game& ctx) { for (auto [name, value]: ctx.menu_item_texts) { @@ -185,7 +185,7 @@ void add_text_to_ui(::context& ctx) } } -void remove_text_from_ui(::context& ctx) +void remove_text_from_ui(::game& ctx) { for (auto [name, value]: ctx.menu_item_texts) { @@ -195,7 +195,7 @@ void remove_text_from_ui(::context& ctx) } } -void delete_text(::context& ctx) +void delete_text(::game& ctx) { for (auto [name, value]: ctx.menu_item_texts) { @@ -206,14 +206,13 @@ void delete_text(::context& ctx) ctx.menu_item_texts.clear(); } -void delete_animations(::context& ctx) +void delete_animations(::game& ctx) { - ctx.animator->remove_animation(ctx.menu_fade_animation); - delete ctx.menu_fade_animation; - ctx.menu_fade_animation = nullptr; + ctx.animator->remove_animation(ctx.menu_fade_animation.get()); + ctx.menu_fade_animation.reset(); } -void clear_callbacks(::context& ctx) +void clear_callbacks(::game& ctx) { // Clear menu item callbacks ctx.menu_left_callbacks.clear(); @@ -222,9 +221,9 @@ void clear_callbacks(::context& ctx) ctx.menu_back_callback = nullptr; } -void setup_animations(::context& ctx) +void setup_animations(::game& ctx) { - ctx.menu_fade_animation = new animation(); + ctx.menu_fade_animation = std::make_unique>(); animation_channel* opacity_channel = ctx.menu_fade_animation->add_channel(0); ctx.menu_fade_animation->set_frame_callback @@ -246,10 +245,10 @@ void setup_animations(::context& ctx) } ); - ctx.animator->add_animation(ctx.menu_fade_animation); + ctx.animator->add_animation(ctx.menu_fade_animation.get()); } -void fade_in(::context& ctx, const std::function& end_callback) +void fade_in(::game& ctx, const std::function& end_callback) { ctx.menu_fade_animation->set_interpolator(ease::out_cubic); animation_channel* opacity_channel = ctx.menu_fade_animation->get_channel(0); @@ -281,7 +280,7 @@ void fade_in(::context& ctx, const std::function& end_callback) ctx.menu_fade_animation->play(); } -void fade_out(::context& ctx, const std::function& end_callback) +void fade_out(::game& ctx, const std::function& end_callback) { ctx.menu_fade_animation->set_interpolator(ease::out_cubic); animation_channel* opacity_channel = ctx.menu_fade_animation->get_channel(0); @@ -294,14 +293,14 @@ void fade_out(::context& ctx, const std::function& end_callback) ctx.menu_fade_animation->play(); } -void fade_in_bg(::context& ctx) +void fade_in_bg(::game& ctx) { ctx.menu_bg_fade_out_animation->stop(); ctx.menu_bg_fade_in_animation->stop(); ctx.menu_bg_fade_in_animation->play(); } -void fade_out_bg(::context& ctx) +void fade_out_bg(::game& ctx) { ctx.menu_bg_fade_in_animation->stop(); ctx.menu_bg_fade_out_animation->stop(); diff --git a/src/game/menu.hpp b/src/game/menu.hpp index 09e58a7..490ad40 100644 --- a/src/game/menu.hpp +++ b/src/game/menu.hpp @@ -20,32 +20,32 @@ #ifndef ANTKEEPER_GAME_MENU_HPP #define ANTKEEPER_GAME_MENU_HPP -#include "game/context.hpp" +#include "game/game.hpp" namespace menu { -void init_menu_item_index(::context& ctx, const std::string& menu_name); -void setup_animations(::context& ctx); +void init_menu_item_index(::game& ctx, const std::string& menu_name); +void setup_animations(::game& ctx); -void clear_callbacks(::context& ctx); -void remove_text_from_ui(::context& ctx); -void delete_text(::context& ctx); -void delete_animations(::context& ctx); +void clear_callbacks(::game& ctx); +void remove_text_from_ui(::game& ctx); +void delete_text(::game& ctx); +void delete_animations(::game& ctx); -void fade_in(::context& ctx, const std::function& end_callback); -void fade_out(::context& ctx, const std::function& end_callback); +void fade_in(::game& ctx, const std::function& end_callback); +void fade_out(::game& ctx, const std::function& end_callback); -void fade_in_bg(::context& ctx); -void fade_out_bg(::context& ctx); +void fade_in_bg(::game& ctx); +void fade_out_bg(::game& ctx); -void update_text_color(::context& ctx); -void update_text_font(::context& ctx); -void update_text_tweens(::context& ctx); -void align_text(::context& ctx, bool center = false, bool has_back = true, float anchor_y = 0.0f); -void refresh_text(::context& ctx); -void add_text_to_ui(::context& ctx); +void update_text_color(::game& ctx); +void update_text_font(::game& ctx); +void update_text_tweens(::game& ctx); +void align_text(::game& ctx, bool center = false, bool has_back = true, float anchor_y = 0.0f); +void refresh_text(::game& ctx); +void add_text_to_ui(::game& ctx); } // namespace menu diff --git a/src/game/settings.hpp b/src/game/settings.hpp index e0da517..4b67b89 100644 --- a/src/game/settings.hpp +++ b/src/game/settings.hpp @@ -20,7 +20,7 @@ #ifndef ANTKEEPER_GAME_SETTINGS_HPP #define ANTKEEPER_GAME_SETTINGS_HPP -#include "game/context.hpp" +#include "game/game.hpp" #include @@ -36,7 +36,7 @@ * @return `true` if the setting was read, `false` if the setting was written. */ template -bool read_or_write_setting(::context& ctx, std::uint32_t key, T& value) +bool read_or_write_setting(::game& ctx, std::uint32_t key, T& value) { if (auto i = ctx.settings->find(key); i != ctx.settings->end()) { diff --git a/src/game/spawn.cpp b/src/game/spawn.cpp index b790987..bd5ec17 100644 --- a/src/game/spawn.cpp +++ b/src/game/spawn.cpp @@ -22,7 +22,7 @@ #include "game/components/model-component.hpp" -entity::id spawn_ant_egg(::context& ctx, const ant::genome& genome, bool fertilized, const float3& position) +entity::id spawn_ant_egg(::game& ctx, const ant::genome& genome, bool fertilized, const float3& position) { // Create entity entity::id egg_eid = ctx.entity_registry->create(); @@ -45,7 +45,7 @@ entity::id spawn_ant_egg(::context& ctx, const ant::genome& genome, bool fertili return egg_eid; } -entity::id spawn_ant_larva(::context& ctx, const ant::genome& genome, const float3& position) +entity::id spawn_ant_larva(::game& ctx, const ant::genome& genome, const float3& position) { // Create entity entity::id larva_eid = ctx.entity_registry->create(); @@ -68,7 +68,7 @@ entity::id spawn_ant_larva(::context& ctx, const ant::genome& genome, const floa return larva_eid; } -entity::id spawn_worker_ant(::context& ctx, const ant::genome& genome, const float3& position) +entity::id spawn_worker_ant(::game& ctx, const ant::genome& genome, const float3& position) { return entt::null; } diff --git a/src/game/spawn.hpp b/src/game/spawn.hpp index 8f7f9bd..6988154 100644 --- a/src/game/spawn.hpp +++ b/src/game/spawn.hpp @@ -20,7 +20,7 @@ #ifndef ANTKEEPER_GAME_SPAWN_HPP #define ANTKEEPER_GAME_SPAWN_HPP -#include "game/context.hpp" +#include "game/game.hpp" #include "game/ant/genome.hpp" #include @@ -35,7 +35,7 @@ * * @return Entity ID of the spawned ant egg. */ -entity::id spawn_ant_egg(::context& ctx, const ant::genome& genome, bool fertilized, const float3& position); +entity::id spawn_ant_egg(::game& ctx, const ant::genome& genome, bool fertilized, const float3& position); /** * Spawns an ant larva. @@ -46,7 +46,7 @@ entity::id spawn_ant_egg(::context& ctx, const ant::genome& genome, bool fertili * * @return Entity ID of the spawned ant larva. */ -entity::id spawn_ant_larva(::context& ctx, const ant::genome& genome, const float3& position); +entity::id spawn_ant_larva(::game& ctx, const ant::genome& genome, const float3& position); /** * Spawns a worker ant. @@ -57,7 +57,7 @@ entity::id spawn_ant_larva(::context& ctx, const ant::genome& genome, const floa * * @return Entity ID of the spawned worker ant. */ -entity::id spawn_worker_ant(::context& ctx, const ant::genome& genome, const float3& position); +entity::id spawn_worker_ant(::game& ctx, const ant::genome& genome, const float3& position); #endif // ANTKEEPER_GAME_SPAWN_HPP diff --git a/src/game/state/boot.cpp b/src/game/state/boot.cpp deleted file mode 100644 index 09c9a7c..0000000 --- a/src/game/state/boot.cpp +++ /dev/null @@ -1,1384 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "game/commands/commands.hpp" -#include "game/context.hpp" -#include "game/controls.hpp" -#include "game/control-profile.hpp" -#include "game/fonts.hpp" -#include "game/graphics.hpp" -#include "game/menu.hpp" -#include "game/strings.hpp" -#include "game/state/boot.hpp" -#include "game/state/splash.hpp" -#include "game/state/main-menu.hpp" -#include "game/systems/astronomy-system.hpp" -#include "game/systems/atmosphere-system.hpp" -#include "game/systems/behavior-system.hpp" -#include "game/systems/blackbody-system.hpp" -#include "game/systems/camera-system.hpp" -#include "game/systems/collision-system.hpp" -#include "game/systems/constraint-system.hpp" -#include "game/systems/locomotion-system.hpp" -#include "game/systems/orbit-system.hpp" -#include "game/systems/render-system.hpp" -#include "game/systems/spatial-system.hpp" -#include "game/systems/spring-system.hpp" -#include "game/systems/steering-system.hpp" -#include "game/systems/subterrain-system.hpp" -#include "game/systems/terrain-system.hpp" -#include "game/settings.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// Prevent cxxopts from using RTTI -#define CXXOPTS_NO_RTTI -#include - -using namespace hash::literals; - -namespace state { - -boot::boot(::context& ctx, int argc, const char* const* argv): - ::state::base(ctx) -{ - // Boot process - debug::log::trace("Booting up..."); - - parse_options(argc, argv); - setup_resources(); - load_settings(); - setup_window(); - setup_input(); - load_strings(); - setup_rendering(); - setup_audio(); - setup_scenes(); - setup_animation(); - setup_entities(); - setup_systems(); - setup_controls(); - setup_ui(); - setup_debugging(); - setup_loop(); - - ctx.active_ecoregion = nullptr; - - debug::log::trace("Boot up complete"); - - // Push next state - ctx.state_machine.emplace(new ::state::main_menu(ctx, true)); - - // Enter main loop - debug::log::trace("Entered main loop"); - loop(); -} - -boot::~boot() -{ - debug::log::trace("Booting down..."); - - // Update window settings - const auto& windowed_position = ctx.window->get_windowed_position(); - const auto& windowed_size = ctx.window->get_windowed_size(); - const bool maximized = ctx.window->is_maximized(); - const bool fullscreen = ctx.window->is_fullscreen(); - (*ctx.settings)["window_x"_fnv1a32] = windowed_position.x(); - (*ctx.settings)["window_y"_fnv1a32] = windowed_position.y(); - (*ctx.settings)["window_w"_fnv1a32] = windowed_size.x(); - (*ctx.settings)["window_h"_fnv1a32] = windowed_size.y(); - (*ctx.settings)["maximized"_fnv1a32] = maximized; - (*ctx.settings)["fullscreen"_fnv1a32] = fullscreen; - - // Destruct window - delete ctx.window; - - // Save settings - ctx.resource_manager->set_write_dir(ctx.shared_config_path); - ctx.resource_manager->save(ctx.settings, "settings.cfg"); - - // Destruct input and window managers - delete ctx.input_manager; - delete ctx.window_manager; - - // Shut down audio - shutdown_audio(); - - debug::log::trace("Boot down complete"); -} - -void boot::parse_options(int argc, const char* const* argv) -{ - if (argc <= 1) - { - // No command-line options specified - return; - } - - debug::log::trace("Parsing command-line options..."); - - // Parse command-line options with cxxopts - try - { - cxxopts::Options options(config::application_name, config::application_name); - options.add_options() - ("c,continue", "Continues from the last save") - ("d,data", "Sets the data package path", cxxopts::value()) - ("f,fullscreen", "Starts in fullscreen mode") - ("n,new-game", "Starts a new game") - ("q,quick-start", "Skips to the main menu") - ("r,reset", "Resets all settings to default") - ("v,v-sync", "Enables or disables v-sync", cxxopts::value()) - ("w,windowed", "Starts in windowed mode"); - auto result = options.parse(argc, argv); - - // --continue - if (result.count("continue")) - { - ctx.option_continue = true; - } - - // --data - if (result.count("data")) - { - ctx.option_data = result["data"].as(); - } - - // --fullscreen - if (result.count("fullscreen")) - { - ctx.option_fullscreen = true; - } - - // --new-game - if (result.count("new-game")) - { - ctx.option_new_game = true; - } - - // --quick-start - if (result.count("quick-start")) - { - ctx.option_quick_start = true; - } - - // --reset - if (result.count("reset")) - { - ctx.option_reset = true; - } - - // --v-sync - if (result.count("v-sync")) - { - ctx.option_v_sync = result["v-sync"].as(); - } - - // --windowed - if (result.count("windowed")) - { - ctx.option_windowed = true; - } - - debug::log::info("Parsed {} command-line options", argc); - } - catch (const std::exception& e) - { - debug::log::error("An error occurred while parsing command-line options: {}", e.what()); - } -} - -void boot::setup_resources() -{ - // Allocate resource manager - ctx.resource_manager = new resource_manager(); - - // Get executable data path - const auto data_path = get_executable_data_path(); - - // Determine data package path - if (ctx.option_data) - { - // Handle command-line data path option - ctx.data_package_path = ctx.option_data.value(); - if (ctx.data_package_path.is_relative()) - { - ctx.data_package_path = data_path / ctx.data_package_path; - } - } - else - { - ctx.data_package_path = data_path / (config::application_slug + std::string("-data.zip")); - } - - // Determine mods path - ctx.mods_path = data_path / "mods"; - - // Determine config paths - ctx.local_config_path = get_local_config_path() / config::application_name; - ctx.shared_config_path = get_shared_config_path() / config::application_name; - ctx.saves_path = ctx.shared_config_path / "saves"; - ctx.screenshots_path = ctx.shared_config_path / "gallery"; - ctx.controls_path = ctx.shared_config_path / "controls"; - - // Log paths - debug::log::info("Data package path: \"{}\"", ctx.data_package_path.string()); - debug::log::info("Local config path: \"{}\"", ctx.local_config_path.string()); - debug::log::info("Shared config path: \"{}\"", ctx.shared_config_path.string()); - debug::log::info("Mods path: \"{}\"", ctx.mods_path.string()); - - // Create nonexistent config directories - std::vector config_paths; - config_paths.push_back(ctx.local_config_path); - config_paths.push_back(ctx.shared_config_path); - config_paths.push_back(ctx.saves_path); - config_paths.push_back(ctx.screenshots_path); - config_paths.push_back(ctx.controls_path); - for (const auto& path: config_paths) - { - try - { - if (std::filesystem::create_directories(path)) - { - debug::log::info("Created directory \"{}\"", path.string()); - } - } - catch (const std::filesystem::filesystem_error& e) - { - debug::log::error("Failed to create directory \"{}\": {}", path.string(), e.what()); - } - } - - // Scan for mods - std::vector mod_paths; - if (std::filesystem::is_directory(ctx.mods_path)) - { - for (const auto& entry: std::filesystem::directory_iterator{ctx.mods_path}) - { - if (entry.is_directory() || (entry.is_regular_file() && entry.path().extension() == ".zip")) - { - mod_paths.push_back(entry.path()); - debug::log::info("Found mod \"{}\"", entry.path().filename().string()); - } - } - } - - // Mount mod paths - for (const std::filesystem::path& mod_path: mod_paths) - { - ctx.resource_manager->mount(ctx.mods_path / mod_path); - } - - // Mount config path - ctx.resource_manager->mount(ctx.local_config_path); - ctx.resource_manager->mount(ctx.shared_config_path); - - // Mount data package path - ctx.resource_manager->mount(ctx.data_package_path); - - // Include resource search paths in order of priority - ctx.resource_manager->include("/controls/"); - ctx.resource_manager->include("/"); -} - -void boot::load_settings() -{ - if (ctx.option_reset) - { - // Command-line reset option found, reset settings - ctx.settings = new dict(); - ctx.resource_manager->set_write_dir(ctx.shared_config_path); - ctx.resource_manager->save(ctx.settings, "settings.cfg"); - debug::log::info("Settings reset"); - } - else - { - ctx.settings = ctx.resource_manager->load>("settings.cfg"); - if (!ctx.settings) - { - debug::log::info("Settings not found"); - ctx.settings = new dict(); - } - } -} - -void boot::setup_window() -{ - // Construct window manager - ctx.window_manager = app::window_manager::instance(); - - // Default window settings - std::string window_title = config::application_name; - int window_x = -1; - int window_y = -1; - int window_w = -1; - int window_h = -1; - bool maximized = true; - bool fullscreen = true; - bool v_sync = true; - - // Read window settings - bool resize = false; - read_or_write_setting(ctx, "window_title"_fnv1a32, window_title); - read_or_write_setting(ctx, "window_x"_fnv1a32, window_x); - read_or_write_setting(ctx, "window_y"_fnv1a32, window_y); - if (!read_or_write_setting(ctx, "window_w"_fnv1a32, window_w) || - !read_or_write_setting(ctx, "window_h"_fnv1a32, window_h)) - { - resize = true; - } - read_or_write_setting(ctx, "maximized"_fnv1a32, maximized); - read_or_write_setting(ctx, "fullscreen"_fnv1a32, fullscreen); - read_or_write_setting(ctx, "v_sync"_fnv1a32, v_sync); - - // If window size not set, resize and reposition relative to default display - if (resize) - { - const app::display& display = ctx.window_manager->get_display(0); - const auto& usable_bounds = display.get_usable_bounds(); - const auto usable_bounds_center = usable_bounds.center(); - - const float default_windowed_scale = 1.0f / 1.2f; - - window_w = static_cast((usable_bounds.max.x() - usable_bounds.min.x()) * default_windowed_scale); - window_h = static_cast((usable_bounds.max.y() - usable_bounds.min.y()) * default_windowed_scale); - window_x = usable_bounds_center.x() - window_w / 2; - window_y = usable_bounds_center.y() - window_h / 2; - } - - // Handle window-related command-line options - if (ctx.option_windowed) - { - // Start in windowed mode - maximized = false; - fullscreen = false; - } - if (ctx.option_fullscreen) - { - // Start in fullscreen mode - fullscreen = true; - } - if (ctx.option_v_sync) - { - v_sync = ctx.option_v_sync.value(); - } - - // Construct window - ctx.window = ctx.window_manager->create_window - ( - window_title, - {window_x, window_y}, - {window_w, window_h}, - maximized, - fullscreen, - v_sync - ); - - // Restrict window size - ctx.window->set_minimum_size({160, 144}); - - // Setup window closed callback - ctx.window_closed_subscription = ctx.window->get_closed_channel().subscribe - ( - [&](const auto& event) - { - ctx.closed = true; - } - ); -} - -void boot::setup_input() -{ - // Construct input manager - ctx.input_manager = app::input_manager::instance(); - - // Process initial input events, such as connecting gamepads - ctx.input_manager->update(); - - // Setup application quit callback - ctx.application_quit_subscription = ctx.input_manager->get_event_queue().subscribe - ( - [&](const auto& event) - { - ctx.closed = true; - } - ); - - // Gamepad deactivation function - auto deactivate_gamepad = [&ctx = this->ctx](const auto& event) - { - if (ctx.gamepad_active) - { - ctx.gamepad_active = false; - ctx.input_manager->show_cursor(); - } - }; - - // Setup gamepad activation callbacks - ctx.gamepad_axis_moved_subscription = ctx.input_manager->get_event_queue().subscribe - ( - [&](const auto& event) - { - if (!ctx.gamepad_active && std::abs(event.position) > 0.5f) - { - ctx.gamepad_active = true; - ctx.input_manager->hide_cursor(); - } - } - ); - ctx.gamepad_button_pressed_subscription = ctx.input_manager->get_event_queue().subscribe - ( - [&ctx = this->ctx](const auto& event) - { - if (!ctx.gamepad_active) - { - ctx.gamepad_active = true; - ctx.input_manager->hide_cursor(); - } - } - ); - - // Setup gamepad deactivation callbacks - ctx.mouse_button_pressed_subscription = ctx.input_manager->get_event_queue().subscribe - ( - deactivate_gamepad - ); - ctx.mouse_moved_subscription = ctx.input_manager->get_event_queue().subscribe - ( - deactivate_gamepad - ); - ctx.mouse_scrolled_subscription = ctx.input_manager->get_event_queue().subscribe - ( - deactivate_gamepad - ); - - // Activate gamepad if one is connected - if (!ctx.input_manager->get_gamepads().empty()) - { - ctx.gamepad_active = true; - ctx.input_manager->hide_cursor(); - } - else - { - ctx.gamepad_active = false; - } -} - -void boot::load_strings() -{ - debug::log::trace("Loading strings..."); - - // Default strings settings - ctx.language_tag = "en"; - - // Read strings settings - read_or_write_setting(ctx, "language_tag"_fnv1a32, ctx.language_tag); - - // Slugify language tag - std::string language_slug = ctx.language_tag; - std::transform - ( - language_slug.begin(), - language_slug.end(), - language_slug.begin(), - [](unsigned char c) - { - return std::tolower(c); - } - ); - - // Load string map - ctx.string_map = ctx.resource_manager->load(language_slug + ".str"); - - // Log language info - debug::log::info("Language tag: {}", ctx.language_tag); - - // Change window title - const std::string window_title = get_string(ctx, "window_title"_fnv1a32); - ctx.window->set_title(window_title); - - // Update window title setting - (*ctx.settings)["window_title"_fnv1a32] = window_title; - - debug::log::trace("Loaded strings"); -} - -void boot::setup_rendering() -{ - debug::log::trace("Setting up rendering..."); - - // Default rendering settings - ctx.render_scale = 1.0f; - ctx.anti_aliasing_method = render::anti_aliasing_method::fxaa; - ctx.shadow_map_resolution = 4096; - - // Read rendering settings - read_or_write_setting(ctx, "render_scale"_fnv1a32, ctx.render_scale); - read_or_write_setting(ctx, "anti_aliasing_method"_fnv1a32, *reinterpret_cast*>(&ctx.anti_aliasing_method)); - read_or_write_setting(ctx, "shadow_map_resolution"_fnv1a32, ctx.shadow_map_resolution); - - // Create framebuffers - ::graphics::create_framebuffers(ctx); - - // Load blue noise texture - gl::texture_2d* blue_noise_map = ctx.resource_manager->load("blue-noise.tex"); - - // Load fallback material - ctx.fallback_material = ctx.resource_manager->load("fallback.mtl"); - - // Setup common render passes - { - // Construct bloom pass - ctx.bloom_pass = new render::bloom_pass(ctx.window->get_rasterizer(), ctx.resource_manager); - ctx.bloom_pass->set_source_texture(ctx.hdr_color_texture); - ctx.bloom_pass->set_mip_chain_length(5); - ctx.bloom_pass->set_filter_radius(0.005f); - - ctx.common_final_pass = new render::final_pass(ctx.window->get_rasterizer(), ctx.ldr_framebuffer_a, ctx.resource_manager); - ctx.common_final_pass->set_color_texture(ctx.hdr_color_texture); - ctx.common_final_pass->set_bloom_texture(ctx.bloom_pass->get_bloom_texture()); - ctx.common_final_pass->set_bloom_weight(0.04f); - ctx.common_final_pass->set_blue_noise_texture(blue_noise_map); - - ctx.fxaa_pass = new render::fxaa_pass(ctx.window->get_rasterizer(), &ctx.window->get_rasterizer()->get_default_framebuffer(), ctx.resource_manager); - ctx.fxaa_pass->set_source_texture(ctx.ldr_color_texture_a); - - ctx.resample_pass = new render::resample_pass(ctx.window->get_rasterizer(), &ctx.window->get_rasterizer()->get_default_framebuffer(), ctx.resource_manager); - ctx.resample_pass->set_source_texture(ctx.ldr_color_texture_b); - ctx.resample_pass->set_enabled(false); - - // Configure anti-aliasing according to settings - graphics::select_anti_aliasing_method(ctx, ctx.anti_aliasing_method); - - // Configure render scaling according to settings - graphics::change_render_resolution(ctx, ctx.render_scale); - } - - // Setup UI compositor - { - ctx.ui_clear_pass = new render::clear_pass(ctx.window->get_rasterizer(), &ctx.window->get_rasterizer()->get_default_framebuffer()); - ctx.ui_clear_pass->set_cleared_buffers(false, true, false); - ctx.ui_clear_pass->set_clear_depth(-1.0f); - - ctx.ui_material_pass = new render::material_pass(ctx.window->get_rasterizer(), &ctx.window->get_rasterizer()->get_default_framebuffer(), ctx.resource_manager); - ctx.ui_material_pass->set_fallback_material(ctx.fallback_material); - - ctx.ui_compositor = new render::compositor(); - ctx.ui_compositor->add_pass(ctx.ui_clear_pass); - ctx.ui_compositor->add_pass(ctx.ui_material_pass); - } - - // Setup underground compositor - { - ctx.underground_clear_pass = new render::clear_pass(ctx.window->get_rasterizer(), ctx.hdr_framebuffer); - ctx.underground_clear_pass->set_cleared_buffers(true, true, false); - ctx.underground_clear_pass->set_clear_color({1, 0, 1, 0}); - ctx.underground_clear_pass->set_clear_depth(-1.0f); - - ctx.underground_material_pass = new render::material_pass(ctx.window->get_rasterizer(), ctx.hdr_framebuffer, ctx.resource_manager); - ctx.underground_material_pass->set_fallback_material(ctx.fallback_material); - - ctx.underground_compositor = new render::compositor(); - ctx.underground_compositor->add_pass(ctx.underground_clear_pass); - ctx.underground_compositor->add_pass(ctx.underground_material_pass); - ctx.underground_compositor->add_pass(ctx.bloom_pass); - ctx.underground_compositor->add_pass(ctx.common_final_pass); - ctx.underground_compositor->add_pass(ctx.fxaa_pass); - ctx.underground_compositor->add_pass(ctx.resample_pass); - } - - // Setup surface compositor - { - ctx.surface_shadow_map_clear_pass = new render::clear_pass(ctx.window->get_rasterizer(), ctx.shadow_map_framebuffer); - ctx.surface_shadow_map_clear_pass->set_cleared_buffers(false, true, false); - ctx.surface_shadow_map_clear_pass->set_clear_depth(1.0f); - - ctx.surface_shadow_map_pass = new render::shadow_map_pass(ctx.window->get_rasterizer(), ctx.resource_manager); - - ctx.surface_clear_pass = new render::clear_pass(ctx.window->get_rasterizer(), ctx.hdr_framebuffer); - ctx.surface_clear_pass->set_cleared_buffers(false, true, true); - ctx.surface_clear_pass->set_clear_depth(-1.0f); - - ctx.sky_pass = new render::sky_pass(ctx.window->get_rasterizer(), ctx.hdr_framebuffer, ctx.resource_manager); - ctx.sky_pass->set_enabled(false); - ctx.sky_pass->set_magnification(3.0f); - - ctx.ground_pass = new render::ground_pass(ctx.window->get_rasterizer(), ctx.hdr_framebuffer, ctx.resource_manager); - ctx.ground_pass->set_enabled(false); - - ctx.surface_material_pass = new render::material_pass(ctx.window->get_rasterizer(), ctx.hdr_framebuffer, ctx.resource_manager); - ctx.surface_material_pass->set_fallback_material(ctx.fallback_material); - - ctx.surface_outline_pass = new render::outline_pass(ctx.window->get_rasterizer(), ctx.hdr_framebuffer, ctx.resource_manager); - ctx.surface_outline_pass->set_outline_width(0.25f); - ctx.surface_outline_pass->set_outline_color(float4{1.0f, 1.0f, 1.0f, 1.0f}); - - ctx.surface_compositor = new render::compositor(); - ctx.surface_compositor->add_pass(ctx.surface_shadow_map_clear_pass); - ctx.surface_compositor->add_pass(ctx.surface_shadow_map_pass); - ctx.surface_compositor->add_pass(ctx.surface_clear_pass); - ctx.surface_compositor->add_pass(ctx.sky_pass); - ctx.surface_compositor->add_pass(ctx.ground_pass); - ctx.surface_compositor->add_pass(ctx.surface_material_pass); - //ctx.surface_compositor->add_pass(ctx.surface_outline_pass); - ctx.surface_compositor->add_pass(ctx.bloom_pass); - ctx.surface_compositor->add_pass(ctx.common_final_pass); - ctx.surface_compositor->add_pass(ctx.fxaa_pass); - ctx.surface_compositor->add_pass(ctx.resample_pass); - } - - // Create billboard VAO - { - const float billboard_vertex_data[] = - { - -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, - -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, - 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, - 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, - -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, - 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f - }; - - std::size_t billboard_vertex_size = 8; - std::size_t billboard_vertex_stride = sizeof(float) * billboard_vertex_size; - std::size_t billboard_vertex_count = 6; - - ctx.billboard_vbo = new gl::vertex_buffer(sizeof(float) * billboard_vertex_size * billboard_vertex_count, billboard_vertex_data); - ctx.billboard_vao = new gl::vertex_array(); - - std::size_t attribute_offset = 0; - - // Define position vertex attribute - gl::vertex_attribute position_attribute; - position_attribute.buffer = ctx.billboard_vbo; - position_attribute.offset = attribute_offset; - position_attribute.stride = billboard_vertex_stride; - position_attribute.type = gl::vertex_attribute_type::float_32; - position_attribute.components = 3; - attribute_offset += position_attribute.components * sizeof(float); - - // Define UV vertex attribute - gl::vertex_attribute uv_attribute; - uv_attribute.buffer = ctx.billboard_vbo; - uv_attribute.offset = attribute_offset; - uv_attribute.stride = billboard_vertex_stride; - uv_attribute.type = gl::vertex_attribute_type::float_32; - uv_attribute.components = 2; - attribute_offset += uv_attribute.components * sizeof(float); - - // Define barycentric vertex attribute - gl::vertex_attribute barycentric_attribute; - barycentric_attribute.buffer = ctx.billboard_vbo; - barycentric_attribute.offset = attribute_offset; - barycentric_attribute.stride = billboard_vertex_stride; - barycentric_attribute.type = gl::vertex_attribute_type::float_32; - barycentric_attribute.components = 3; - attribute_offset += barycentric_attribute.components * sizeof(float); - - // Bind vertex attributes to VAO - ctx.billboard_vao->bind(render::vertex_attribute::position, position_attribute); - ctx.billboard_vao->bind(render::vertex_attribute::uv, uv_attribute); - ctx.billboard_vao->bind(render::vertex_attribute::barycentric, barycentric_attribute); - } - - // Create renderer - ctx.renderer = new render::renderer(); - ctx.renderer->set_billboard_vao(ctx.billboard_vao); - - debug::log::trace("Set up rendering"); -} - -void boot::setup_audio() -{ - debug::log::trace("Setting up audio..."); - - // Default audio settings - ctx.master_volume = 1.0f; - ctx.ambience_volume = 1.0f; - ctx.effects_volume = 1.0f; - ctx.mono_audio = false; - ctx.captions = false; - ctx.captions_size = 1.0f; - - // Read audio settings - read_or_write_setting(ctx, "master_volume"_fnv1a32, ctx.master_volume); - read_or_write_setting(ctx, "ambience_volume"_fnv1a32, ctx.ambience_volume); - read_or_write_setting(ctx, "effects_volume"_fnv1a32, ctx.effects_volume); - read_or_write_setting(ctx, "mono_audio"_fnv1a32, ctx.mono_audio); - read_or_write_setting(ctx, "captions"_fnv1a32, ctx.captions); - read_or_write_setting(ctx, "captions_size"_fnv1a32, ctx.captions_size); - - // Open audio device - debug::log::trace("Opening audio device..."); - ctx.alc_device = alcOpenDevice(nullptr); - if (!ctx.alc_device) - { - debug::log::error("Failed to open audio device: AL error code {}", alGetError()); - return; - } - else - { - // Get audio device name - const ALCchar* alc_device_name = nullptr; - if (alcIsExtensionPresent(ctx.alc_device, "ALC_ENUMERATE_ALL_EXT")) - { - alc_device_name = alcGetString(ctx.alc_device, ALC_ALL_DEVICES_SPECIFIER); - } - if (alcGetError(ctx.alc_device) != AL_NO_ERROR || !alc_device_name) - { - alc_device_name = alcGetString(ctx.alc_device, ALC_DEVICE_SPECIFIER); - } - - // Log audio device name - debug::log::info("Opened audio device \"{}\"", alc_device_name); - } - - // Create audio context - debug::log::trace("Creating audio context..."); - ctx.alc_context = alcCreateContext(ctx.alc_device, nullptr); - if (!ctx.alc_context) - { - debug::log::error("Failed to create audio context: ALC error code {}", alcGetError(ctx.alc_device)); - alcCloseDevice(ctx.alc_device); - return; - } - else - { - debug::log::trace("Created audio context"); - } - - // Make audio context current - debug::log::trace("Making audio context current..."); - if (alcMakeContextCurrent(ctx.alc_context) == ALC_FALSE) - { - debug::log::error("Failed to make audio context current: ALC error code {}", alcGetError(ctx.alc_device)); - if (ctx.alc_context != nullptr) - { - alcDestroyContext(ctx.alc_context); - } - alcCloseDevice(ctx.alc_device); - return; - } - else - { - debug::log::trace("Made audio context current"); - } - - debug::log::trace("Set up audio"); -} - -void boot::setup_scenes() -{ - debug::log::trace("Setting up scenes..."); - - // Get default framebuffer - const auto& viewport_size = ctx.window->get_viewport_size(); - const float viewport_aspect_ratio = static_cast(viewport_size[0]) / static_cast(viewport_size[1]); - - // Setup UI camera - ctx.ui_camera = new scene::camera(); - ctx.ui_camera->set_compositor(ctx.ui_compositor); - float clip_left = 0.0f; - float clip_right = viewport_size[0]; - float clip_top = 0.0f; - float clip_bottom = viewport_size[1]; - float clip_near = -100.0f; - float clip_far = 100.0f; - ctx.ui_camera->set_orthographic(clip_left, clip_right, clip_top, clip_bottom, clip_near, clip_far); - ctx.ui_camera->look_at({0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, -1.0f}, {0.0f, 1.0f, 0.0f}); - ctx.ui_camera->update_tweens(); - - // Setup underground camera - ctx.underground_camera = new scene::camera(); - ctx.underground_camera->set_perspective(math::radians(45.0f), viewport_aspect_ratio, 0.1f, 1000.0f); - ctx.underground_camera->set_compositor(ctx.underground_compositor); - ctx.underground_camera->set_composite_index(0); - ctx.underground_camera->set_active(false); - - // Setup surface camera - ctx.surface_camera = new scene::camera(); - ctx.surface_camera->set_perspective(math::radians(45.0f), viewport_aspect_ratio, 0.1f, 5000.0f); - ctx.surface_camera->set_compositor(ctx.surface_compositor); - ctx.surface_camera->set_composite_index(0); - ctx.surface_camera->set_active(false); - - // Setup UI scene - { - ctx.ui_scene = new scene::collection(); - - // Menu BG billboard - render::material* menu_bg_material = new render::material(); - menu_bg_material->set_shader_program(ctx.resource_manager->load("ui-element-untextured.glsl")); - auto menu_bg_tint = menu_bg_material->add_property("tint"); - menu_bg_tint->set_value(float4{0.0f, 0.0f, 0.0f, 0.5f}); - menu_bg_material->set_blend_mode(render::blend_mode::translucent); - menu_bg_material->update_tweens(); - ctx.menu_bg_billboard = new scene::billboard(); - ctx.menu_bg_billboard->set_active(false); - ctx.menu_bg_billboard->set_material(menu_bg_material); - ctx.menu_bg_billboard->set_scale({std::ceil(viewport_size.x() * 0.5f), std::ceil(viewport_size.y() * 0.5f), 1.0f}); - ctx.menu_bg_billboard->set_translation({std::floor(viewport_size.x() * 0.5f), std::floor(viewport_size.y() * 0.5f), -100.0f}); - ctx.menu_bg_billboard->update_tweens(); - - // Create camera flash billboard - render::material* flash_material = new render::material(); - flash_material->set_shader_program(ctx.resource_manager->load("ui-element-untextured.glsl")); - auto flash_tint = flash_material->add_property("tint"); - flash_tint->set_value(float4{1, 1, 1, 1}); - //flash_tint->set_tween_interpolator(ease::out_quad); - - flash_material->set_blend_mode(render::blend_mode::translucent); - flash_material->update_tweens(); - - ctx.camera_flash_billboard = new scene::billboard(); - ctx.camera_flash_billboard->set_material(flash_material); - ctx.camera_flash_billboard->set_scale({(float)viewport_size[0] * 0.5f, (float)viewport_size[1] * 0.5f, 1.0f}); - ctx.camera_flash_billboard->set_translation({0.0f, 0.0f, 0.0f}); - ctx.camera_flash_billboard->update_tweens(); - - // Create depth debug billboard - /* - material* depth_debug_material = new material(); - depth_debug_material->set_shader_program(ctx.resource_manager->load("ui-element-textured.glsl")); - depth_debug_material->add_property("background")->set_value(shadow_map_depth_texture); - depth_debug_material->add_property("tint")->set_value(float4{1, 1, 1, 1}); - billboard* depth_debug_billboard = new billboard(); - depth_debug_billboard->set_material(depth_debug_material); - depth_debug_billboard->set_scale({128, 128, 1}); - depth_debug_billboard->set_translation({-960 + 128, 1080 * 0.5f - 128, 0}); - depth_debug_billboard->update_tweens(); - ui_system->get_scene()->add_object(depth_debug_billboard); - */ - - ctx.ui_scene->add_object(ctx.ui_camera); - } - - // Setup underground scene - { - ctx.underground_scene = new scene::collection(); - ctx.underground_scene->add_object(ctx.underground_camera); - } - - // Setup surface scene - { - ctx.surface_scene = new scene::collection(); - ctx.surface_scene->add_object(ctx.surface_camera); - } - - // Clear active scene - ctx.active_scene = nullptr; - - debug::log::trace("Set up scenes"); -} - -void boot::setup_animation() -{ - // Setup timeline system - ctx.timeline = new timeline(); - ctx.timeline->set_autoremove(true); - - // Setup animator - ctx.animator = new animator(); - - // Create fade transition - ctx.fade_transition = new screen_transition(); - ctx.fade_transition->get_material()->set_shader_program(ctx.resource_manager->load("fade-transition.glsl")); - ctx.fade_transition_color = ctx.fade_transition->get_material()->add_property("color"); - ctx.fade_transition_color->set_value({0, 0, 0}); - ctx.fade_transition->get_billboard()->set_translation({0, 0, 98}); - ctx.fade_transition->get_billboard()->update_tweens(); - ctx.ui_scene->add_object(ctx.fade_transition->get_billboard()); - ctx.animator->add_animation(ctx.fade_transition->get_animation()); - - // Create inner radial transition - ctx.radial_transition_inner = new screen_transition(); - ctx.radial_transition_inner->get_material()->set_shader_program(ctx.resource_manager->load("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()); - - // Create outer radial transition - ctx.radial_transition_outer = new screen_transition(); - ctx.radial_transition_outer->get_material()->set_shader_program(ctx.resource_manager->load("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()); - - // Menu BG animations - { - render::material_property* menu_bg_tint = static_cast*>(ctx.menu_bg_billboard->get_material()->get_property("tint")); - auto menu_bg_frame_callback = [menu_bg_tint](int channel, const float& opacity) - { - menu_bg_tint->set_value(float4{0.0f, 0.0f, 0.0f, opacity}); - }; - - // Create menu BG fade in animation - ctx.menu_bg_fade_in_animation = new animation(); - { - ctx.menu_bg_fade_in_animation->set_interpolator(ease::out_cubic); - animation_channel* channel = ctx.menu_bg_fade_in_animation->add_channel(0); - channel->insert_keyframe({0.0f, 0.0f}); - channel->insert_keyframe({config::menu_fade_in_duration, config::menu_bg_opacity}); - ctx.menu_bg_fade_in_animation->set_frame_callback(menu_bg_frame_callback); - ctx.menu_bg_fade_in_animation->set_start_callback - ( - [&ctx = this->ctx, menu_bg_tint]() - { - ctx.ui_scene->add_object(ctx.menu_bg_billboard); - - menu_bg_tint->set_value(float4{0.0f, 0.0f, 0.0f, 0.0f}); - menu_bg_tint->update_tweens(); - ctx.menu_bg_billboard->set_active(true); - } - ); - } - - // Create menu BG fade out animation - ctx.menu_bg_fade_out_animation = new animation(); - { - ctx.menu_bg_fade_out_animation->set_interpolator(ease::out_cubic); - animation_channel* channel = ctx.menu_bg_fade_out_animation->add_channel(0); - channel->insert_keyframe({0.0f, config::menu_bg_opacity}); - channel->insert_keyframe({config::menu_fade_out_duration, 0.0f}); - ctx.menu_bg_fade_out_animation->set_frame_callback(menu_bg_frame_callback); - ctx.menu_bg_fade_out_animation->set_end_callback - ( - [&ctx = this->ctx]() - { - ctx.ui_scene->remove_object(ctx.menu_bg_billboard); - ctx.menu_bg_billboard->set_active(false); - } - ); - } - - ctx.animator->add_animation(ctx.menu_bg_fade_in_animation); - ctx.animator->add_animation(ctx.menu_bg_fade_out_animation); - } - - // Create camera flash animation - ctx.camera_flash_animation = new animation(); - { - ctx.camera_flash_animation->set_interpolator(ease::out_sine); - const float duration = 0.5f; - animation_channel* channel = ctx.camera_flash_animation->add_channel(0); - channel->insert_keyframe({0.0f, 1.0f}); - channel->insert_keyframe({duration, 0.0f}); - } -} - -void boot::setup_entities() -{ - // Create entity registry - ctx.entity_registry = new entt::registry(); -} - -void boot::setup_systems() -{ - const auto& viewport_size = ctx.window->get_viewport_size(); - float4 viewport = {0.0f, 0.0f, static_cast(viewport_size[0]), static_cast(viewport_size[1])}; - - // Setup terrain system - ctx.terrain_system = new ::terrain_system(*ctx.entity_registry); - ctx.terrain_system->set_patch_side_length(31.0f); - ctx.terrain_system->set_patch_subdivisions(31); - ctx.terrain_system->set_scene_collection(ctx.surface_scene); - - // Setup camera system - ctx.camera_system = new ::camera_system(*ctx.entity_registry); - ctx.camera_system->set_viewport(viewport); - - // Setup subterrain system - ctx.subterrain_system = new ::subterrain_system(*ctx.entity_registry, ctx.resource_manager); - ctx.subterrain_system->set_scene(ctx.underground_scene); - - // Setup collision system - ctx.collision_system = new ::collision_system(*ctx.entity_registry); - - // Setup behavior system - ctx.behavior_system = new ::behavior_system(*ctx.entity_registry); - - // Setup locomotion system - ctx.locomotion_system = new ::locomotion_system(*ctx.entity_registry); - - // Setup steering system - ctx.steering_system = new ::steering_system(*ctx.entity_registry); - - // Setup spring system - ctx.spring_system = new ::spring_system(*ctx.entity_registry); - - // Setup spatial system - ctx.spatial_system = new ::spatial_system(*ctx.entity_registry); - - // Setup constraint system - ctx.constraint_system = new ::constraint_system(*ctx.entity_registry); - - // Setup orbit system - ctx.orbit_system = new ::orbit_system(*ctx.entity_registry); - - // Setup blackbody system - ctx.blackbody_system = new ::blackbody_system(*ctx.entity_registry); - ctx.blackbody_system->set_illuminant(color::illuminant::deg2::d55); - - // RGB wavelengths for atmospheric scatteering - ctx.rgb_wavelengths = {680, 550, 440}; - - // Setup atmosphere system - ctx.atmosphere_system = new ::atmosphere_system(*ctx.entity_registry); - ctx.atmosphere_system->set_rgb_wavelengths(ctx.rgb_wavelengths * 1e-9); - ctx.atmosphere_system->set_sky_pass(ctx.sky_pass); - - // Setup astronomy system - ctx.astronomy_system = new ::astronomy_system(*ctx.entity_registry); - ctx.astronomy_system->set_transmittance_samples(16); - ctx.astronomy_system->set_sky_pass(ctx.sky_pass); - - // Setup render system - ctx.render_system = new ::render_system(*ctx.entity_registry); - //ctx.render_system->add_layer(ctx.underground_scene); - ctx.render_system->add_layer(ctx.surface_scene); - ctx.render_system->add_layer(ctx.ui_scene); - ctx.render_system->set_renderer(ctx.renderer); -} - -void boot::setup_controls() -{ - debug::log::trace("Setting up controls..."); - - // Load SDL game controller mappings database - // debug::log::trace("Loading SDL game controller mappings..."); - // file_buffer* game_controller_db = ctx.resource_manager->load("gamecontrollerdb.txt"); - // if (!game_controller_db) - // { - // debug::log::error("Failed to load SDL game controller mappings"); - // } - // else - // { - // ctx.app->add_game_controller_mappings(game_controller_db->data(), game_controller_db->size()); - // debug::log::trace("Loaded SDL game controller mappings"); - - // ctx.resource_manager->unload("gamecontrollerdb.txt"); - // } - - // Pass input event queue to action maps - event::queue* input_event_queue = &ctx.input_manager->get_event_queue(); - ctx.window_action_map.set_event_queue(input_event_queue); - ctx.menu_action_map.set_event_queue(input_event_queue); - ctx.movement_action_map.set_event_queue(input_event_queue); - ctx.nuptial_flight_action_map.set_event_queue(input_event_queue); - - // Default control profile settings - ctx.control_profile_filename = "controls.cfg"; - ctx.control_profile = nullptr; - - // Read control profile settings - if (read_or_write_setting(ctx, "control_profile"_fnv1a32, ctx.control_profile_filename)) - { - // Load control profile - //ctx.control_profile = ctx.resource_manager->load<::control_profile>(ctx.controls_path / ctx.control_profile_filename); - ctx.control_profile = ctx.resource_manager->load<::control_profile>(ctx.control_profile_filename); - } - - if (!ctx.control_profile) - { - // Allocate control profile - ctx.control_profile = new ::control_profile(); - - // Reset control profile to default settings. - ::reset_control_profile(*ctx.control_profile); - - // Save control profile - ctx.resource_manager->set_write_dir(ctx.controls_path); - ctx.resource_manager->save(ctx.control_profile, ctx.control_profile_filename); - } - - // Apply control profile - ::apply_control_profile(ctx, *ctx.control_profile); - - // Setup action callbacks - setup_window_controls(ctx); - setup_menu_controls(ctx); - setup_game_controls(ctx); - - // Enable window controls - enable_window_controls(ctx); - - debug::log::trace("Set up controls"); -} - -void boot::setup_ui() -{ - // Default UI settings - ctx.font_scale = 1.0f; - ctx.debug_font_size_pt = 10.0f; - ctx.menu_font_size_pt = 22.0f; - ctx.title_font_size_pt = 80.0f; - ctx.dyslexia_font = false; - - // Read UI settings - read_or_write_setting(ctx, "font_scale"_fnv1a32, ctx.font_scale); - read_or_write_setting(ctx, "debug_font_size_pt"_fnv1a32, ctx.debug_font_size_pt); - read_or_write_setting(ctx, "menu_font_size_pt"_fnv1a32, ctx.menu_font_size_pt); - read_or_write_setting(ctx, "title_font_size_pt"_fnv1a32, ctx.title_font_size_pt); - read_or_write_setting(ctx, "dyslexia_font"_fnv1a32, ctx.dyslexia_font); - - // Load fonts - debug::log::trace("Loading fonts..."); - try - { - ::load_fonts(ctx); - debug::log::trace("Loaded fonts"); - } - catch (...) - { - debug::log::error("Failed to load fonts"); - } - - // Setup window resized callback - ctx.window_resized_subscription = ctx.window->get_resized_channel().subscribe - ( - [&](const auto& event) - { - const auto& viewport_size = event.window->get_viewport_size(); - const float viewport_aspect_ratio = static_cast(viewport_size.x()) / static_cast(viewport_size.y()); - - // Resize framebuffers - ::graphics::change_render_resolution(ctx, ctx.render_scale); - - // Update camera projection matrix - ctx.surface_camera->set_perspective - ( - ctx.surface_camera->get_fov(), - viewport_aspect_ratio, - ctx.surface_camera->get_clip_near(), - ctx.surface_camera->get_clip_far() - ); - - // Update UI camera projection matrix - ctx.ui_camera->set_orthographic - ( - 0.0f, - viewport_size.x(), - 0.0f, - viewport_size.y(), - ctx.ui_camera->get_clip_near(), - ctx.ui_camera->get_clip_far() - ); - - // Resize menu BG billboard - ctx.menu_bg_billboard->set_scale({std::ceil(viewport_size.x() * 0.5f), std::ceil(viewport_size.y() * 0.5f), 1.0f}); - ctx.menu_bg_billboard->set_translation({std::floor(viewport_size.x() * 0.5f), std::floor(viewport_size.y() * 0.5f), -100.0f}); - - // Re-align debug text - ctx.frame_time_text->set_translation({std::round(0.0f), std::round(viewport_size.y() - ctx.debug_font.get_font_metrics().size), 99.0f}); - ctx.frame_time_text->update_tweens(); - - // Re-align menu text - ::menu::align_text(ctx); - } - ); -} - -void boot::setup_debugging() -{ - ctx.cli = new debug::cli(); - //debug::log::info(ctx.cli->interpret("echo hi 123")); - - const auto& viewport_size = ctx.window->get_viewport_size(); - - ctx.frame_time_text = new scene::text(); - ctx.frame_time_text->set_material(&ctx.debug_font_material); - ctx.frame_time_text->set_color({1.0f, 1.0f, 0.0f, 1.0f}); - ctx.frame_time_text->set_font(&ctx.debug_font); - ctx.frame_time_text->set_translation({std::round(0.0f), std::round(viewport_size.y() - ctx.debug_font.get_font_metrics().size), 99.0f}); - ctx.frame_time_text->update_tweens(); - - ctx.ui_scene->add_object(ctx.frame_time_text); -} - -void boot::setup_loop() -{ - // Default loop settings - double update_frequency = 60.0; - - // Read loop settings - read_or_write_setting(ctx, "update_frequency"_fnv1a32, update_frequency); - - // Set update frequency - ctx.loop.set_update_frequency(update_frequency); - - // Set update callback - ctx.loop.set_update_callback - ( - [&ctx = this->ctx](double t, double dt) - { - // Update tweens - ctx.sky_pass->update_tweens(); - ctx.surface_scene->update_tweens(); - ctx.underground_scene->update_tweens(); - ctx.ui_scene->update_tweens(); - - // Process events - ctx.window_manager->update(); - ctx.input_manager->update(); - - // Process function queue - while (!ctx.function_queue.empty()) - { - ctx.function_queue.front()(); - ctx.function_queue.pop(); - } - - // Update processes - std::for_each - ( - std::execution::par, - ctx.processes.begin(), - ctx.processes.end(), - [t, dt](const auto& process) - { - process.second(t, dt); - } - ); - - // Advance timeline - ctx.timeline->advance(dt); - - // Update entity systems - //ctx.terrain_system->update(t, dt); - //ctx.subterrain_system->update(t, dt); - ctx.collision_system->update(t, dt); - ctx.behavior_system->update(t, dt); - ctx.steering_system->update(t, dt); - ctx.locomotion_system->update(t, dt); - ctx.camera_system->update(t, dt); - ctx.orbit_system->update(t, dt); - ctx.blackbody_system->update(t, dt); - ctx.atmosphere_system->update(t, dt); - ctx.astronomy_system->update(t, dt); - ctx.spring_system->update(t, dt); - ctx.spatial_system->update(t, dt); - ctx.constraint_system->update(t, dt); - ctx.animator->animate(dt); - ctx.render_system->update(t, dt); - } - ); - - // Set render callback - ctx.loop.set_render_callback - ( - [&ctx = this->ctx](double alpha) - { - ctx.render_system->draw(alpha); - ctx.window->swap_buffers(); - } - ); -} - -void boot::loop() -{ - ctx.closed = false; - math::moving_average average_frame_time; - - while (!ctx.closed) - { - // Execute main loop - ctx.loop.tick(); - - // Sample frame duration - average_frame_time(static_cast(ctx.loop.get_frame_duration() * 1000.0)); - - ctx.frame_time_text->set_content(std::format("◷{:5.02f}", average_frame_time.average())); - } - - // Exit all active game states - while (!ctx.state_machine.empty()) - { - ctx.state_machine.pop(); - } -} - -void boot::shutdown_audio() -{ - debug::log::trace("Shutting down audio..."); - - if (ctx.alc_context) - { - alcMakeContextCurrent(nullptr); - alcDestroyContext(ctx.alc_context); - } - - if (ctx.alc_device) - { - alcCloseDevice(ctx.alc_device); - } - - debug::log::trace("Shut down audio"); -} - -} // namespace state diff --git a/src/game/state/boot.hpp b/src/game/state/boot.hpp deleted file mode 100644 index c4d0247..0000000 --- a/src/game/state/boot.hpp +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_STATE_BOOT_HPP -#define ANTKEEPER_GAME_STATE_BOOT_HPP - -#include "game/state/base.hpp" -#include "game/context.hpp" -#include - -namespace state { - -/** - * Boots the game up on construction, and down on destruction. - */ -class boot: public ::state::base -{ -public: - /** - * Boots up the game. - * - * @param ctx Game context. - * @param argc Command line argument count. - * @param argv Command line argument vector. - */ - boot(::context& ctx, int argc, const char* const* argv); - - /** - * Boots down the game. - */ - virtual ~boot(); - -private: - void parse_options(int argc, const char* const* argv); - void setup_resources(); - void load_settings(); - void setup_window(); - void setup_input(); - void load_strings(); - void setup_rendering(); - void setup_audio(); - void setup_scenes(); - void setup_animation(); - void setup_entities(); - void setup_systems(); - void setup_controls(); - void setup_ui(); - void setup_debugging(); - void setup_loop(); - - void loop(); - - void shutdown_audio(); -}; - -} // namespace state - -#endif // ANTKEEPER_GAME_STATE_BOOT_HPP diff --git a/src/game/state/controls-menu.hpp b/src/game/state/controls-menu.hpp deleted file mode 100644 index d3c2f50..0000000 --- a/src/game/state/controls-menu.hpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_STATE_CONTROLS_MENU_HPP -#define ANTKEEPER_GAME_STATE_CONTROLS_MENU_HPP - -#include "game/state/base.hpp" - -namespace state { - -class controls_menu: public ::state::base -{ -public: - controls_menu(::context& ctx); - virtual ~controls_menu(); -}; - -} // namespace state - -#endif // ANTKEEPER_GAME_STATE_CONTROLS_MENU_HPP diff --git a/src/game/state/options-menu.hpp b/src/game/state/options-menu.hpp deleted file mode 100644 index fa5d0ff..0000000 --- a/src/game/state/options-menu.hpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_STATE_OPTIONS_MENU_HPP -#define ANTKEEPER_GAME_STATE_OPTIONS_MENU_HPP - -#include "game/state/base.hpp" - -namespace state { - -class options_menu: public ::state::base -{ -public: - options_menu(::context& ctx); - virtual ~options_menu(); -}; - -} // namespace state - -#endif // ANTKEEPER_GAME_STATE_OPTIONS_MENU_HPP diff --git a/src/game/state/collection-menu.cpp b/src/game/states/collection-menu-state.cpp similarity index 95% rename from src/game/state/collection-menu.cpp rename to src/game/states/collection-menu-state.cpp index c674587..6d202fb 100644 --- a/src/game/state/collection-menu.cpp +++ b/src/game/states/collection-menu-state.cpp @@ -17,8 +17,8 @@ * along with Antkeeper source code. If not, see . */ -#include "game/state/collection-menu.hpp" -#include "game/state/main-menu.hpp" +#include "game/states/collection-menu-state.hpp" +#include "game/states/main-menu-state.hpp" #include "game/controls.hpp" #include #include @@ -32,10 +32,8 @@ using namespace hash::literals; -namespace state { - -collection_menu::collection_menu(::context& ctx): - ::state::base(ctx) +collection_menu_state::collection_menu_state(::game& ctx): + game_state(ctx) { debug::log::trace("Entering collection menu state..."); @@ -132,7 +130,7 @@ collection_menu::collection_menu(::context& ctx): debug::log::trace("Entered collection menu state"); } -collection_menu::~collection_menu() +collection_menu_state::~collection_menu_state() { debug::log::trace("Exiting collection menu state..."); @@ -142,7 +140,7 @@ collection_menu::~collection_menu() debug::log::trace("Exited collection menu state"); } -void collection_menu::resize_box() +void collection_menu_state::resize_box() { const float padding = 64.0f; const auto viewport_size = float2(ctx.window->get_viewport_size()); @@ -175,5 +173,3 @@ void collection_menu::resize_box() ); selection_billboard.update_tweens(); } - -} // namespace state diff --git a/src/game/state/collection-menu.hpp b/src/game/states/collection-menu-state.hpp similarity index 82% rename from src/game/state/collection-menu.hpp rename to src/game/states/collection-menu-state.hpp index 9eb3251..1248f67 100644 --- a/src/game/state/collection-menu.hpp +++ b/src/game/states/collection-menu-state.hpp @@ -17,10 +17,10 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_GAME_STATE_COLLECTION_MENU_HPP -#define ANTKEEPER_GAME_STATE_COLLECTION_MENU_HPP +#ifndef ANTKEEPER_COLLECTION_MENU_STATE_HPP +#define ANTKEEPER_COLLECTION_MENU_STATE_HPP -#include "game/state/base.hpp" +#include "game/states/game-state.hpp" #include #include #include @@ -28,13 +28,12 @@ #include #include -namespace state { -class collection_menu: public ::state::base +class collection_menu_state: public game_state { public: - collection_menu(::context& ctx); - virtual ~collection_menu(); + collection_menu_state(::game& ctx); + virtual ~collection_menu_state(); private: void resize_box(); @@ -58,6 +57,4 @@ private: float selection_size; }; -} // namespace state - -#endif // ANTKEEPER_GAME_STATE_COLLECTION_MENU_HPP +#endif // ANTKEEPER_COLLECTION_MENU_STATE_HPP diff --git a/src/game/state/controls-menu.cpp b/src/game/states/controls-menu-state.cpp similarity index 88% rename from src/game/state/controls-menu.cpp rename to src/game/states/controls-menu-state.cpp index 1aa429e..0d092d9 100644 --- a/src/game/state/controls-menu.cpp +++ b/src/game/states/controls-menu-state.cpp @@ -17,10 +17,10 @@ * along with Antkeeper source code. If not, see . */ -#include "game/state/controls-menu.hpp" -#include "game/state/keyboard-config-menu.hpp" -#include "game/state/gamepad-config-menu.hpp" -#include "game/state/options-menu.hpp" +#include "game/states/controls-menu-state.hpp" +#include "game/states/keyboard-config-menu-state.hpp" +#include "game/states/gamepad-config-menu-state.hpp" +#include "game/states/options-menu-state.hpp" #include "game/controls.hpp" #include #include @@ -30,10 +30,8 @@ using namespace hash::literals; -namespace state { - -controls_menu::controls_menu(::context& ctx): - ::state::base(ctx) +controls_menu_state::controls_menu_state(::game& ctx): + game_state(ctx) { debug::log::trace("Entering controls menu state..."); @@ -79,7 +77,7 @@ controls_menu::controls_menu(::context& ctx): [&ctx]() { ctx.state_machine.pop(); - ctx.state_machine.emplace(new ::state::keyboard_config_menu(ctx)); + ctx.state_machine.emplace(std::make_unique(ctx)); } ); } @@ -101,7 +99,7 @@ controls_menu::controls_menu(::context& ctx): [&ctx]() { ctx.state_machine.pop(); - ctx.state_machine.emplace(new ::state::gamepad_config_menu(ctx)); + ctx.state_machine.emplace(std::make_unique(ctx)); } ); } @@ -123,7 +121,7 @@ controls_menu::controls_menu(::context& ctx): [&ctx]() { ctx.state_machine.pop(); - ctx.state_machine.emplace(new ::state::options_menu(ctx)); + ctx.state_machine.emplace(std::make_unique(ctx)); } ); } @@ -157,7 +155,7 @@ controls_menu::controls_menu(::context& ctx): debug::log::trace("Entered controls menu state"); } -controls_menu::~controls_menu() +controls_menu_state::~controls_menu_state() { debug::log::trace("Exiting options menu state..."); @@ -170,5 +168,3 @@ controls_menu::~controls_menu() debug::log::trace("Exited controls menu state"); } - -} // namespace state diff --git a/src/game/state/pause-menu.hpp b/src/game/states/controls-menu-state.hpp similarity index 72% rename from src/game/state/pause-menu.hpp rename to src/game/states/controls-menu-state.hpp index 50ad923..5bfd87d 100644 --- a/src/game/state/pause-menu.hpp +++ b/src/game/states/controls-menu-state.hpp @@ -17,20 +17,17 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_GAME_STATE_PAUSE_MENU_HPP -#define ANTKEEPER_GAME_STATE_PAUSE_MENU_HPP +#ifndef ANTKEEPER_CONTROLS_MENU_STATE_HPP +#define ANTKEEPER_CONTROLS_MENU_STATE_HPP -#include "game/state/base.hpp" +#include "game/states/game-state.hpp" -namespace state { -class pause_menu: public ::state::base +class controls_menu_state: public game_state { public: - pause_menu(::context& ctx); - virtual ~pause_menu(); + controls_menu_state(::game& ctx); + virtual ~controls_menu_state(); }; -} // namespace state - -#endif // ANTKEEPER_GAME_STATE_PAUSE_MENU_HPP +#endif // ANTKEEPER_CONTROLS_MENU_STATE_HPP diff --git a/src/game/state/credits.cpp b/src/game/states/credits-state.cpp similarity index 94% rename from src/game/state/credits.cpp rename to src/game/states/credits-state.cpp index 74f862c..2a9a7c5 100644 --- a/src/game/state/credits.cpp +++ b/src/game/states/credits-state.cpp @@ -17,9 +17,9 @@ * along with Antkeeper source code. If not, see . */ -#include "game/state/credits.hpp" -#include "game/state/extras-menu.hpp" -#include "game/context.hpp" +#include "game/states/credits-state.hpp" +#include "game/states/extras-menu-state.hpp" +#include "game/game.hpp" #include #include #include @@ -32,10 +32,8 @@ using namespace hash::literals; using namespace math::glsl; -namespace state { - -credits::credits(::context& ctx): - ::state::base(ctx) +credits_state::credits_state(::game& ctx): + game_state(ctx) { debug::log::trace("Entering credits state..."); @@ -101,7 +99,7 @@ credits::credits(::context& ctx): { // Change to extras menu state ctx.state_machine.pop(); - ctx.state_machine.emplace(new ::state::extras_menu(ctx)); + ctx.state_machine.emplace(std::make_unique(ctx)); } ); }; @@ -143,7 +141,7 @@ credits::credits(::context& ctx): debug::log::trace("Entered credits state"); } -credits::~credits() +credits_state::~credits_state() { debug::log::trace("Exiting credits state..."); @@ -160,4 +158,3 @@ credits::~credits() debug::log::trace("Exited credits state"); } -} // namespace state diff --git a/src/game/state/credits.hpp b/src/game/states/credits-state.hpp similarity index 81% rename from src/game/state/credits.hpp rename to src/game/states/credits-state.hpp index b307114..f7dc177 100644 --- a/src/game/state/credits.hpp +++ b/src/game/states/credits-state.hpp @@ -17,22 +17,21 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_GAME_STATE_CREDITS_HPP -#define ANTKEEPER_GAME_STATE_CREDITS_HPP +#ifndef ANTKEEPER_CREDITS_STATE_HPP +#define ANTKEEPER_CREDITS_STATE_HPP -#include "game/state/base.hpp" +#include "game/states/game-state.hpp" #include #include #include #include -namespace state { -class credits: public ::state::base +class credits_state: public game_state { public: - credits(::context& ctx); - virtual ~credits(); + credits_state(::game& ctx); + virtual ~credits_state(); private: scene::text credits_text; @@ -42,6 +41,4 @@ private: std::shared_ptr window_resized_subscription; }; -} // namespace state - -#endif // ANTKEEPER_GAME_STATE_CREDITS_HPP +#endif // ANTKEEPER_CREDITS_STATE_HPP diff --git a/src/game/state/extras-menu.cpp b/src/game/states/extras-menu-state.cpp similarity index 90% rename from src/game/state/extras-menu.cpp rename to src/game/states/extras-menu-state.cpp index ca2402f..15be8ad 100644 --- a/src/game/state/extras-menu.cpp +++ b/src/game/states/extras-menu-state.cpp @@ -17,9 +17,9 @@ * along with Antkeeper source code. If not, see . */ -#include "game/state/extras-menu.hpp" -#include "game/state/main-menu.hpp" -#include "game/state/credits.hpp" +#include "game/states/extras-menu-state.hpp" +#include "game/states/main-menu-state.hpp" +#include "game/states/credits-state.hpp" #include "game/controls.hpp" #include #include @@ -30,10 +30,8 @@ using namespace hash::literals; -namespace state { - -extras_menu::extras_menu(::context& ctx): - ::state::base(ctx) +extras_menu_state::extras_menu_state(::game& ctx): + game_state(ctx) { debug::log::trace("Entering extras menu state..."); @@ -76,7 +74,7 @@ extras_menu::extras_menu(::context& ctx): [&ctx]() { ctx.state_machine.pop(); - ctx.state_machine.emplace(new ::state::credits(ctx)); + ctx.state_machine.emplace(std::make_unique(ctx)); } ); } @@ -98,7 +96,7 @@ extras_menu::extras_menu(::context& ctx): [&ctx]() { ctx.state_machine.pop(); - ctx.state_machine.emplace(new ::state::main_menu(ctx, false)); + ctx.state_machine.emplace(std::make_unique(ctx, false)); } ); } @@ -129,7 +127,7 @@ extras_menu::extras_menu(::context& ctx): debug::log::trace("Entered extras menu state"); } -extras_menu::~extras_menu() +extras_menu_state::~extras_menu_state() { debug::log::trace("Exiting extras menu state..."); @@ -143,4 +141,3 @@ extras_menu::~extras_menu() debug::log::trace("Exited extras menu state"); } -} // namespace state diff --git a/src/game/state/extras-menu.hpp b/src/game/states/extras-menu-state.hpp similarity index 72% rename from src/game/state/extras-menu.hpp rename to src/game/states/extras-menu-state.hpp index 1e66ca2..d1c93f4 100644 --- a/src/game/state/extras-menu.hpp +++ b/src/game/states/extras-menu-state.hpp @@ -17,20 +17,17 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_GAME_STATE_EXTRAS_MENU_HPP -#define ANTKEEPER_GAME_STATE_EXTRAS_MENU_HPP +#ifndef ANTKEEPER_EXTRAS_MENU_STATE_HPP +#define ANTKEEPER_EXTRAS_MENU_STATE_HPP -#include "game/state/base.hpp" +#include "game/states/game-state.hpp" -namespace state { -class extras_menu: public ::state::base +class extras_menu_state: public game_state { public: - extras_menu(::context& ctx); - virtual ~extras_menu(); + extras_menu_state(::game& ctx); + virtual ~extras_menu_state(); }; -} // namespace state - -#endif // ANTKEEPER_GAME_STATE_EXTRAS_MENU_HPP +#endif // ANTKEEPER_EXTRAS_MENU_STATE_HPP diff --git a/src/game/context.cpp b/src/game/states/game-state.cpp similarity index 87% rename from src/game/context.cpp rename to src/game/states/game-state.cpp index 8c00572..5b30a3d 100644 --- a/src/game/context.cpp +++ b/src/game/states/game-state.cpp @@ -17,13 +17,11 @@ * along with Antkeeper source code. If not, see . */ -#include "game/context.hpp" -#include "game/systems/orbit-system.hpp" +#include "game/states/game-state.hpp" - -context::context() +game_state::game_state(::game& ctx): + ctx(ctx) {} -context::~context() +game_state::~game_state() {} - diff --git a/src/game/state/base.hpp b/src/game/states/game-state.hpp similarity index 80% rename from src/game/state/base.hpp rename to src/game/states/game-state.hpp index eb3754c..7056915 100644 --- a/src/game/state/base.hpp +++ b/src/game/states/game-state.hpp @@ -17,18 +17,15 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_GAME_STATE_BASE_HPP -#define ANTKEEPER_GAME_STATE_BASE_HPP +#ifndef ANTKEEPER_GAME_STATE_HPP +#define ANTKEEPER_GAME_STATE_HPP - -struct context; - -namespace state { +class game; /** * Abstract base class for game states. */ -class base +class game_state { public: /** @@ -36,17 +33,15 @@ public: * * @param ctx Reference to the game context on which this state will operate. */ - base(::context& ctx); + game_state(::game& ctx); /** * Destructs a game state. */ - virtual ~base() = 0; + virtual ~game_state() = 0; protected: - ::context& ctx; + ::game& ctx; }; -} // namespace state - -#endif // ANTKEEPER_GAME_STATE_BASE_HPP +#endif // ANTKEEPER_GAME_STATE_HPP diff --git a/src/game/state/gamepad-config-menu.cpp b/src/game/states/gamepad-config-menu-state.cpp similarity index 94% rename from src/game/state/gamepad-config-menu.cpp rename to src/game/states/gamepad-config-menu-state.cpp index 1ae3eb1..5851ca8 100644 --- a/src/game/state/gamepad-config-menu.cpp +++ b/src/game/states/gamepad-config-menu-state.cpp @@ -17,10 +17,10 @@ * along with Antkeeper source code. If not, see . */ -#include "game/state/gamepad-config-menu.hpp" -#include "game/state/controls-menu.hpp" +#include "game/states/gamepad-config-menu-state.hpp" +#include "game/states/controls-menu-state.hpp" #include "game/controls.hpp" -#include "game/context.hpp" +#include "game/game.hpp" #include #include #include @@ -31,10 +31,9 @@ using namespace hash::literals; -namespace state { -gamepad_config_menu::gamepad_config_menu(::context& ctx): - ::state::base(ctx), +gamepad_config_menu_state::gamepad_config_menu_state(::game& ctx): + game_state(ctx), action_remapped(false) { debug::log::trace("Entering gamepad config menu state..."); @@ -84,7 +83,7 @@ gamepad_config_menu::gamepad_config_menu(::context& ctx): [&ctx]() { ctx.state_machine.pop(); - ctx.state_machine.emplace(new ::state::controls_menu(ctx)); + ctx.state_machine.emplace(std::make_unique(ctx)); } ); } @@ -112,7 +111,7 @@ gamepad_config_menu::gamepad_config_menu(::context& ctx): debug::log::trace("Entered gamepad config menu state"); } -gamepad_config_menu::~gamepad_config_menu() +gamepad_config_menu_state::~gamepad_config_menu_state() { debug::log::trace("Exiting gamepad config menu state..."); @@ -136,7 +135,7 @@ gamepad_config_menu::~gamepad_config_menu() debug::log::trace("Exited gamepad config menu state"); } -std::string gamepad_config_menu::get_mapping_string(const input::action_map& action_map, const input::action& control) +std::string gamepad_config_menu_state::get_mapping_string(const input::action_map& action_map, const input::action& control) { std::string mapping_string; @@ -288,7 +287,7 @@ std::string gamepad_config_menu::get_mapping_string(const input::action_map& act return mapping_string; } -void gamepad_config_menu::add_control_item(input::action_map& action_map, input::action& control, std::uint32_t control_name_hash) +void gamepad_config_menu_state::add_control_item(input::action_map& action_map, input::action& control, std::uint32_t control_name_hash) { // Construct texts scene::text* name_text = new scene::text(); @@ -369,5 +368,3 @@ void gamepad_config_menu::add_control_item(input::action_map& action_map, input: ctx.menu_left_callbacks.push_back(nullptr); ctx.menu_right_callbacks.push_back(nullptr); } - -} // namespace state diff --git a/src/game/state/gamepad-config-menu.hpp b/src/game/states/gamepad-config-menu-state.hpp similarity index 80% rename from src/game/state/gamepad-config-menu.hpp rename to src/game/states/gamepad-config-menu-state.hpp index 24c5001..32f880d 100644 --- a/src/game/state/gamepad-config-menu.hpp +++ b/src/game/states/gamepad-config-menu-state.hpp @@ -17,23 +17,22 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_GAME_STATE_GAMEPAD_CONFIG_MENU_HPP -#define ANTKEEPER_GAME_STATE_GAMEPAD_CONFIG_MENU_HPP +#ifndef ANTKEEPER_GAMEPAD_CONFIG_MENU_STATE_HPP +#define ANTKEEPER_GAMEPAD_CONFIG_MENU_STATE_HPP -#include "game/state/base.hpp" +#include "game/states/game-state.hpp" #include #include #include #include #include -namespace state { -class gamepad_config_menu: public ::state::base +class gamepad_config_menu_state: public game_state { public: - gamepad_config_menu(::context& ctx); - virtual ~gamepad_config_menu(); + gamepad_config_menu_state(::game& ctx); + virtual ~gamepad_config_menu_state(); private: std::string get_mapping_string(const input::action_map& action_map, const input::action& control); @@ -46,6 +45,5 @@ private: bool action_remapped; }; -} // namespace state -#endif // ANTKEEPER_GAME_STATE_GAMEPAD_CONFIG_MENU_HPP +#endif // ANTKEEPER_GAMEPAD_CONFIG_MENU_STATE_HPP diff --git a/src/game/state/graphics-menu.cpp b/src/game/states/graphics-menu-state.cpp similarity index 96% rename from src/game/state/graphics-menu.cpp rename to src/game/states/graphics-menu-state.cpp index 3ca20eb..2140a20 100644 --- a/src/game/state/graphics-menu.cpp +++ b/src/game/states/graphics-menu-state.cpp @@ -17,8 +17,8 @@ * along with Antkeeper source code. If not, see . */ -#include "game/state/graphics-menu.hpp" -#include "game/state/options-menu.hpp" +#include "game/states/graphics-menu-state.hpp" +#include "game/states/options-menu-state.hpp" #include "game/controls.hpp" #include #include @@ -31,12 +31,9 @@ using namespace hash::literals; -namespace state { -static void update_value_text_content(::context* ctx); - -graphics_menu::graphics_menu(::context& ctx): - ::state::base(ctx) +graphics_menu_state::graphics_menu_state(::game& ctx): + game_state(ctx) { debug::log::trace("Entering graphics menu state..."); @@ -309,7 +306,7 @@ graphics_menu::graphics_menu(::context& ctx): [&ctx]() { ctx.state_machine.pop(); - ctx.state_machine.emplace(new ::state::options_menu(ctx)); + ctx.state_machine.emplace(std::make_unique(ctx)); } ); } @@ -355,7 +352,7 @@ graphics_menu::graphics_menu(::context& ctx): debug::log::trace("Entered graphics menu state"); } -graphics_menu::~graphics_menu() +graphics_menu_state::~graphics_menu_state() { debug::log::trace("Exiting graphics menu state..."); @@ -369,7 +366,7 @@ graphics_menu::~graphics_menu() debug::log::trace("Exited graphics menu state"); } -void graphics_menu::update_value_text_content() +void graphics_menu_state::update_value_text_content() { const bool fullscreen = ctx.window->is_fullscreen(); const float render_scale = ctx.render_scale; @@ -404,5 +401,3 @@ void graphics_menu::update_value_text_content() std::get<1>(ctx.menu_item_texts[4])->set_content(std::to_string(static_cast(std::round(font_scale * 100.0f))) + "%"); std::get<1>(ctx.menu_item_texts[5])->set_content((dyslexia_font) ? string_on : string_off); } - -} // namespace state diff --git a/src/game/state/sound-menu.hpp b/src/game/states/graphics-menu-state.hpp similarity index 73% rename from src/game/state/sound-menu.hpp rename to src/game/states/graphics-menu-state.hpp index 010e677..16c35c8 100644 --- a/src/game/state/sound-menu.hpp +++ b/src/game/states/graphics-menu-state.hpp @@ -17,23 +17,20 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_GAME_STATE_SOUND_MENU_HPP -#define ANTKEEPER_GAME_STATE_SOUND_MENU_HPP +#ifndef ANTKEEPER_GRAPHICS_MENU_STATE_HPP +#define ANTKEEPER_GRAPHICS_MENU_STATE_HPP -#include "game/state/base.hpp" +#include "game/states/game-state.hpp" -namespace state { -class sound_menu: public ::state::base +class graphics_menu_state: public game_state { public: - sound_menu(::context& ctx); - virtual ~sound_menu(); + graphics_menu_state(::game& ctx); + virtual ~graphics_menu_state(); private: void update_value_text_content(); }; -} // namespace state - -#endif // ANTKEEPER_GAME_STATE_SOUND_MENU_HPP +#endif // ANTKEEPER_GRAPHICS_MENU_STATE_HPP diff --git a/src/game/state/keyboard-config-menu.cpp b/src/game/states/keyboard-config-menu-state.cpp similarity index 93% rename from src/game/state/keyboard-config-menu.cpp rename to src/game/states/keyboard-config-menu-state.cpp index 1d28c7d..0d4a89f 100644 --- a/src/game/state/keyboard-config-menu.cpp +++ b/src/game/states/keyboard-config-menu-state.cpp @@ -17,8 +17,8 @@ * along with Antkeeper source code. If not, see . */ -#include "game/state/keyboard-config-menu.hpp" -#include "game/state/controls-menu.hpp" +#include "game/states/keyboard-config-menu-state.hpp" +#include "game/states/controls-menu-state.hpp" #include "game/controls.hpp" #include #include @@ -32,10 +32,8 @@ using namespace hash::literals; -namespace state { - -keyboard_config_menu::keyboard_config_menu(::context& ctx): - ::state::base(ctx), +keyboard_config_menu_state::keyboard_config_menu_state(::game& ctx): + game_state(ctx), action_remapped(false) { debug::log::trace("Entering keyboard config menu state..."); @@ -85,7 +83,7 @@ keyboard_config_menu::keyboard_config_menu(::context& ctx): [&ctx]() { ctx.state_machine.pop(); - ctx.state_machine.emplace(new ::state::controls_menu(ctx)); + ctx.state_machine.emplace(std::make_unique(ctx)); } ); } @@ -113,7 +111,7 @@ keyboard_config_menu::keyboard_config_menu(::context& ctx): debug::log::trace("Entered keyboard config menu state"); } -keyboard_config_menu::~keyboard_config_menu() +keyboard_config_menu_state::~keyboard_config_menu_state() { debug::log::trace("Exiting keyboard config menu state..."); @@ -137,7 +135,7 @@ keyboard_config_menu::~keyboard_config_menu() debug::log::trace("Exited keyboard config menu state..."); } -std::string keyboard_config_menu::get_mapping_string(const input::action_map& action_map, const input::action& control) +std::string keyboard_config_menu_state::get_mapping_string(const input::action_map& action_map, const input::action& control) { std::string mapping_string; @@ -211,7 +209,7 @@ std::string keyboard_config_menu::get_mapping_string(const input::action_map& ac return mapping_string; } -void keyboard_config_menu::add_control_item(input::action_map& action_map, input::action& control, std::uint32_t control_name_hash) +void keyboard_config_menu_state::add_control_item(input::action_map& action_map, input::action& control, std::uint32_t control_name_hash) { // Construct texts scene::text* name_text = new scene::text(); @@ -294,5 +292,3 @@ void keyboard_config_menu::add_control_item(input::action_map& action_map, input ctx.menu_left_callbacks.push_back(nullptr); ctx.menu_right_callbacks.push_back(nullptr); } - -} // namespace state diff --git a/src/game/state/keyboard-config-menu.hpp b/src/game/states/keyboard-config-menu-state.hpp similarity index 79% rename from src/game/state/keyboard-config-menu.hpp rename to src/game/states/keyboard-config-menu-state.hpp index 4cda283..3cd2f3d 100644 --- a/src/game/state/keyboard-config-menu.hpp +++ b/src/game/states/keyboard-config-menu-state.hpp @@ -17,23 +17,22 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_GAME_STATE_KEYBOARD_CONFIG_MENU_HPP -#define ANTKEEPER_GAME_STATE_KEYBOARD_CONFIG_MENU_HPP +#ifndef ANTKEEPER_KEYBOARD_CONFIG_MENU_STATE_HPP +#define ANTKEEPER_KEYBOARD_CONFIG_MENU_STATE_HPP -#include "game/state/base.hpp" +#include "game/states/game-state.hpp" #include #include #include #include #include -namespace state { -class keyboard_config_menu: public ::state::base +class keyboard_config_menu_state: public game_state { public: - keyboard_config_menu(::context& ctx); - virtual ~keyboard_config_menu(); + keyboard_config_menu_state(::game& ctx); + virtual ~keyboard_config_menu_state(); private: std::string get_mapping_string(const input::action_map& action_map, const input::action& control); @@ -46,6 +45,4 @@ private: bool action_remapped; }; -} // namespace state - -#endif // ANTKEEPER_GAME_STATE_KEYBOARD_CONFIG_MENU_HPP +#endif // ANTKEEPER_KEYBOARD_CONFIG_MENU_STATE_HPP diff --git a/src/game/state/language-menu.cpp b/src/game/states/language-menu-state.cpp similarity index 94% rename from src/game/state/language-menu.cpp rename to src/game/states/language-menu-state.cpp index e768863..fbb028c 100644 --- a/src/game/state/language-menu.cpp +++ b/src/game/states/language-menu-state.cpp @@ -17,8 +17,8 @@ * along with Antkeeper source code. If not, see . */ -#include "game/state/language-menu.hpp" -#include "game/state/options-menu.hpp" +#include "game/states/language-menu-state.hpp" +#include "game/states/options-menu-state.hpp" #include "game/controls.hpp" #include #include @@ -32,10 +32,8 @@ using namespace hash::literals; -namespace state { - -language_menu::language_menu(::context& ctx): - ::state::base(ctx) +language_menu_state::language_menu_state(::game& ctx): + game_state(ctx) { debug::log::trace("Entering language menu state..."); @@ -155,7 +153,7 @@ language_menu::language_menu(::context& ctx): [&ctx]() { ctx.state_machine.pop(); - ctx.state_machine.emplace(new ::state::options_menu(ctx)); + ctx.state_machine.emplace(std::make_unique(ctx)); } ); } @@ -186,7 +184,7 @@ language_menu::language_menu(::context& ctx): debug::log::trace("Entered language menu state"); } -language_menu::~language_menu() +language_menu_state::~language_menu_state() { debug::log::trace("Exiting language menu state..."); @@ -200,7 +198,7 @@ language_menu::~language_menu() debug::log::trace("Exited language menu state"); } -void language_menu::update_text_content() +void language_menu_state::update_text_content() { auto [language_name, language_value] = ctx.menu_item_texts[0]; auto [back_name, back_value] = ctx.menu_item_texts[1]; @@ -209,5 +207,3 @@ void language_menu::update_text_content() language_value->set_content(get_string(ctx, "language_name_native"_fnv1a32)); back_name->set_content(get_string(ctx, "back"_fnv1a32)); } - -} // namespace state diff --git a/src/game/state/language-menu.hpp b/src/game/states/language-menu-state.hpp similarity index 74% rename from src/game/state/language-menu.hpp rename to src/game/states/language-menu-state.hpp index 66a76ea..c994efc 100644 --- a/src/game/state/language-menu.hpp +++ b/src/game/states/language-menu-state.hpp @@ -17,20 +17,19 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_GAME_STATE_LANGUAGE_MENU_HPP -#define ANTKEEPER_GAME_STATE_LANGUAGE_MENU_HPP +#ifndef ANTKEEPER_LANGUAGE_MENU_STATE_HPP +#define ANTKEEPER_LANGUAGE_MENU_STATE_HPP -#include "game/state/base.hpp" +#include "game/states/game-state.hpp" #include #include -namespace state { -class language_menu: public ::state::base +class language_menu_state: public game_state { public: - language_menu(::context& ctx); - virtual ~language_menu(); + language_menu_state(::game& ctx); + virtual ~language_menu_state(); private: void update_text_content(); @@ -39,7 +38,4 @@ private: std::size_t language_index; }; - -} // namespace state - -#endif // ANTKEEPER_GAME_STATE_LANGUAGE_MENU_HPP +#endif // ANTKEEPER_LANGUAGE_MENU_STATE_HPP diff --git a/src/game/state/main-menu.cpp b/src/game/states/main-menu-state.cpp similarity index 92% rename from src/game/state/main-menu.cpp rename to src/game/states/main-menu-state.cpp index 41b7d8d..19ea46a 100644 --- a/src/game/state/main-menu.cpp +++ b/src/game/states/main-menu-state.cpp @@ -17,7 +17,7 @@ * along with Antkeeper source code. If not, see . */ -#include "game/state/main-menu.hpp" +#include "game/states/main-menu-state.hpp" #include #include #include @@ -29,11 +29,11 @@ #include "game/controls.hpp" #include "game/ecoregion.hpp" #include "game/menu.hpp" -#include "game/state/collection-menu.hpp" -#include "game/state/extras-menu.hpp" -#include "game/state/nuptial-flight.hpp" -#include "game/state/nest-selection.hpp" -#include "game/state/options-menu.hpp" +#include "game/states/collection-menu-state.hpp" +#include "game/states/extras-menu-state.hpp" +#include "game/states/nuptial-flight-state.hpp" +#include "game/states/nest-selection-state.hpp" +#include "game/states/options-menu-state.hpp" #include "game/strings.hpp" #include "game/world.hpp" #include @@ -51,10 +51,8 @@ using namespace hash::literals; using namespace math::glsl; -namespace state { - -main_menu::main_menu(::context& ctx, bool fade_in): - ::state::base(ctx) +main_menu_state::main_menu_state(::game& ctx, bool fade_in): + game_state(ctx) { debug::log::trace("Entering main menu state..."); @@ -133,9 +131,9 @@ main_menu::main_menu(::context& ctx, bool fade_in): [&ctx]() { ctx.state_machine.pop(); - ctx.state_machine.emplace(new ::state::nuptial_flight(ctx)); - //ctx.state_machine.emplace(new ::state::collection_menu(ctx)); - //ctx.state_machine.emplace(new ::state::nest_selection(ctx)); + ctx.state_machine.emplace(std::make_unique(ctx)); + //ctx.state_machine.emplace(std::make_unique(ctx)); + //ctx.state_machine.emplace(std::make_unique(ctx)); } ); }; @@ -171,7 +169,7 @@ main_menu::main_menu(::context& ctx, bool fade_in): [&ctx]() { ctx.state_machine.pop(); - ctx.state_machine.emplace(new ::state::options_menu(ctx)); + ctx.state_machine.emplace(std::make_unique(ctx)); } ); } @@ -197,7 +195,7 @@ main_menu::main_menu(::context& ctx, bool fade_in): [&ctx]() { ctx.state_machine.pop(); - ctx.state_machine.emplace(new ::state::extras_menu(ctx)); + ctx.state_machine.emplace(std::make_unique(ctx)); } ); } @@ -310,7 +308,7 @@ main_menu::main_menu(::context& ctx, bool fade_in): debug::log::trace("Entered main menu state"); } -main_menu::~main_menu() +main_menu_state::~main_menu_state() { debug::log::trace("Exiting main menu state..."); @@ -333,7 +331,7 @@ main_menu::~main_menu() debug::log::trace("Exited main menu state"); } -void main_menu::fade_in_title() +void main_menu_state::fade_in_title() { animation_channel* opacity_channel = title_fade_animation.get_channel(0); opacity_channel->remove_keyframes(); @@ -343,7 +341,7 @@ void main_menu::fade_in_title() title_fade_animation.play(); } -void main_menu::fade_out_title() +void main_menu_state::fade_out_title() { animation_channel* opacity_channel = title_fade_animation.get_channel(0); opacity_channel->remove_keyframes(); @@ -352,5 +350,3 @@ void main_menu::fade_out_title() title_fade_animation.stop(); title_fade_animation.play(); } - -} // namespace state diff --git a/src/game/state/main-menu.hpp b/src/game/states/main-menu-state.hpp similarity index 79% rename from src/game/state/main-menu.hpp rename to src/game/states/main-menu-state.hpp index adc58b1..14a3236 100644 --- a/src/game/state/main-menu.hpp +++ b/src/game/states/main-menu-state.hpp @@ -17,23 +17,22 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_GAME_STATE_MAIN_MENU_HPP -#define ANTKEEPER_GAME_STATE_MAIN_MENU_HPP +#ifndef ANTKEEPER_MAIN_MENU_STATE_HPP +#define ANTKEEPER_MAIN_MENU_STATE_HPP -#include "game/state/base.hpp" +#include "game/states/game-state.hpp" #include #include #include #include #include -namespace state { -class main_menu: public ::state::base +class main_menu_state: public game_state { public: - main_menu(::context& ctx, bool fade_in); - virtual ~main_menu(); + main_menu_state(::game& ctx, bool fade_in); + virtual ~main_menu_state(); private: void fade_in_title(); @@ -45,6 +44,4 @@ private: std::shared_ptr window_resized_subscription; }; -} // namespace state - -#endif // ANTKEEPER_GAME_STATE_MAIN_MENU_HPP +#endif // ANTKEEPER_MAIN_MENU_STATE_HPP diff --git a/src/game/state/nest-selection.cpp b/src/game/states/nest-selection-state.cpp similarity index 96% rename from src/game/state/nest-selection.cpp rename to src/game/states/nest-selection-state.cpp index 54f1652..7f7ea3a 100644 --- a/src/game/state/nest-selection.cpp +++ b/src/game/states/nest-selection-state.cpp @@ -45,8 +45,8 @@ #include "game/constraints/track-to-constraint.hpp" #include "game/controls.hpp" #include "game/spawn.hpp" -#include "game/state/nest-selection.hpp" -#include "game/state/pause-menu.hpp" +#include "game/states/nest-selection-state.hpp" +#include "game/states/pause-menu-state.hpp" #include "game/systems/astronomy-system.hpp" #include "game/systems/atmosphere-system.hpp" #include "game/systems/camera-system.hpp" @@ -67,10 +67,9 @@ using namespace ::ant; -namespace state { -nest_selection::nest_selection(::context& ctx): - ::state::base(ctx) +nest_selection_state::nest_selection_state(::game& ctx): + game_state(ctx) { debug::log::trace("Entering nest selection state..."); @@ -202,12 +201,12 @@ nest_selection::nest_selection(::context& ctx): // Queue fade in ctx.fade_transition_color->set_value({0, 0, 0}); - ctx.function_queue.push(std::bind(&screen_transition::transition, ctx.fade_transition, 1.0f, true, ease::out_sine, true, nullptr)); + ctx.function_queue.push(std::bind(&screen_transition::transition, ctx.fade_transition.get(), 1.0f, true, ease::out_sine, true, nullptr)); debug::log::trace("Entered nest selection state"); } -nest_selection::~nest_selection() +nest_selection_state::~nest_selection_state() { debug::log::trace("Exiting nest selection state..."); @@ -219,7 +218,7 @@ nest_selection::~nest_selection() debug::log::trace("Exited nest selection state"); } -void nest_selection::create_first_person_camera_rig() +void nest_selection_state::create_first_person_camera_rig() { // Construct first person camera rig spring rotation constraint spring_rotation_constraint first_person_camera_rig_spring_rotation; @@ -270,7 +269,7 @@ void nest_selection::create_first_person_camera_rig() // Construct first person camera rig camera component camera_component first_person_camera_rig_camera; - first_person_camera_rig_camera.object = ctx.surface_camera; + first_person_camera_rig_camera.object = ctx.surface_camera.get(); // Construct first person camera rig entity first_person_camera_rig_eid = ctx.entity_registry->create(); @@ -300,7 +299,7 @@ void nest_selection::create_first_person_camera_rig() set_first_person_camera_rig_pedestal(first_person_camera_rig_pedestal); } -void nest_selection::destroy_first_person_camera_rig() +void nest_selection_state::destroy_first_person_camera_rig() { ctx.entity_registry->destroy(first_person_camera_rig_eid); ctx.entity_registry->destroy(first_person_camera_rig_spring_translation_eid); @@ -308,7 +307,7 @@ void nest_selection::destroy_first_person_camera_rig() ctx.entity_registry->destroy(first_person_camera_rig_fov_spring_eid); } -void nest_selection::set_first_person_camera_rig_pedestal(float pedestal) +void nest_selection_state::set_first_person_camera_rig_pedestal(float pedestal) { first_person_camera_rig_pedestal = pedestal; const float elevation = math::log_lerp(first_person_camera_rig_min_elevation, first_person_camera_rig_max_elevation, first_person_camera_rig_pedestal); @@ -333,7 +332,7 @@ void nest_selection::set_first_person_camera_rig_pedestal(float pedestal) ); } -void nest_selection::move_first_person_camera_rig(const float2& direction, float factor) +void nest_selection_state::move_first_person_camera_rig(const float2& direction, float factor) { const float speed = math::log_lerp(first_person_camera_near_speed, first_person_camera_far_speed, first_person_camera_rig_pedestal) * factor; @@ -353,7 +352,7 @@ void nest_selection::move_first_person_camera_rig(const float2& direction, float ); } -void nest_selection::satisfy_first_person_camera_rig_constraints() +void nest_selection_state::satisfy_first_person_camera_rig_constraints() { // Satisfy first person camera rig spring translation constraint ctx.entity_registry->patch @@ -389,7 +388,7 @@ void nest_selection::satisfy_first_person_camera_rig_constraints() ); } -void nest_selection::enable_controls() +void nest_selection_state::enable_controls() { /* // Reset mouse look @@ -714,7 +713,7 @@ void nest_selection::enable_controls() }; // Push pause menu state - ctx.state_machine.emplace(new ::state::pause_menu(ctx)); + ctx.state_machine.emplace(std::make_unique(ctx)); } ); @@ -740,7 +739,7 @@ void nest_selection::enable_controls() */ } -void nest_selection::disable_controls() +void nest_selection_state::disable_controls() { /* if (mouse_look) @@ -777,4 +776,3 @@ void nest_selection::disable_controls() */ } -} // namespace state diff --git a/src/game/state/nest-selection.hpp b/src/game/states/nest-selection-state.hpp similarity index 85% rename from src/game/state/nest-selection.hpp rename to src/game/states/nest-selection-state.hpp index 5387d86..40f72d3 100644 --- a/src/game/state/nest-selection.hpp +++ b/src/game/states/nest-selection-state.hpp @@ -17,20 +17,19 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_GAME_STATE_NEST_SELECTION_HPP -#define ANTKEEPER_GAME_STATE_NEST_SELECTION_HPP +#ifndef ANTKEEPER_NEST_SELECTION_STATE_HPP +#define ANTKEEPER_NEST_SELECTION_STATE_HPP -#include "game/state/base.hpp" +#include "game/states/game-state.hpp" #include #include -namespace state { -class nest_selection: public ::state::base +class nest_selection_state: public game_state { public: - nest_selection(::context& ctx); - virtual ~nest_selection(); + nest_selection_state(::game& ctx); + virtual ~nest_selection_state(); private: void create_first_person_camera_rig(); @@ -59,6 +58,4 @@ private: bool mouse_look; }; -} // namespace state - -#endif // ANTKEEPER_GAME_STATE_NEST_SELECTION_HPP +#endif // ANTKEEPER_NEST_SELECTION_STATE_HPP diff --git a/src/game/state/nuptial-flight.cpp b/src/game/states/nuptial-flight-state.cpp similarity index 96% rename from src/game/state/nuptial-flight.cpp rename to src/game/states/nuptial-flight-state.cpp index 2a6cceb..1c40d49 100644 --- a/src/game/state/nuptial-flight.cpp +++ b/src/game/states/nuptial-flight-state.cpp @@ -17,9 +17,9 @@ * along with Antkeeper source code. If not, see . */ -#include "game/state/nuptial-flight.hpp" -#include "game/state/pause-menu.hpp" -#include "game/state/nest-selection.hpp" +#include "game/states/nuptial-flight-state.hpp" +#include "game/states/pause-menu-state.hpp" +#include "game/states/nest-selection-state.hpp" #include "game/ant/swarm.hpp" #include #include "game/systems/camera-system.hpp" @@ -68,10 +68,8 @@ using namespace hash::literals; -namespace state { - -nuptial_flight::nuptial_flight(::context& ctx): - ::state::base(ctx) +nuptial_flight_state::nuptial_flight_state(::game& ctx): + game_state(ctx) { debug::log::trace("Entering nuptial flight state..."); @@ -174,12 +172,12 @@ nuptial_flight::nuptial_flight(::context& ctx): // Queue fade in ctx.fade_transition_color->set_value({0, 0, 0}); - ctx.function_queue.push(std::bind(&screen_transition::transition, ctx.fade_transition, 1.0f, true, ease::out_sine, true, nullptr)); + ctx.function_queue.push(std::bind(&screen_transition::transition, ctx.fade_transition.get(), 1.0f, true, ease::out_sine, true, nullptr)); debug::log::trace("Entered nuptial flight state"); } -nuptial_flight::~nuptial_flight() +nuptial_flight_state::~nuptial_flight_state() { debug::log::trace("Exiting nuptial flight state..."); @@ -200,7 +198,7 @@ nuptial_flight::~nuptial_flight() debug::log::trace("Exited nuptial flight state"); } -void nuptial_flight::create_camera_rig() +void nuptial_flight_state::create_camera_rig() { // Construct camera rig focus ease to constraint ease_to_constraint camera_rig_focus_ease_to; @@ -312,7 +310,7 @@ void nuptial_flight::create_camera_rig() // Construct camera rig camera component camera_component camera_rig_camera; - camera_rig_camera.object = ctx.surface_camera; + camera_rig_camera.object = ctx.surface_camera.get(); // Construct camera rig entity camera_rig_eid = ctx.entity_registry->create(); @@ -342,7 +340,7 @@ void nuptial_flight::create_camera_rig() set_camera_rig_zoom(0.25f); } -void nuptial_flight::destroy_camera_rig() +void nuptial_flight_state::destroy_camera_rig() { ctx.entity_registry->destroy(camera_rig_eid); ctx.entity_registry->destroy(camera_rig_spring_translation_eid); @@ -356,7 +354,7 @@ void nuptial_flight::destroy_camera_rig() ctx.entity_registry->destroy(camera_rig_fov_spring_eid); } -void nuptial_flight::set_camera_rig_zoom(float zoom) +void nuptial_flight_state::set_camera_rig_zoom(float zoom) { camera_rig_zoom = zoom; @@ -381,7 +379,7 @@ void nuptial_flight::set_camera_rig_zoom(float zoom) ); } -void nuptial_flight::satisfy_camera_rig_constraints() +void nuptial_flight_state::satisfy_camera_rig_constraints() { // Satisfy camera rig focus ease to constraint ctx.entity_registry->patch @@ -427,7 +425,7 @@ void nuptial_flight::satisfy_camera_rig_constraints() ); } -void nuptial_flight::setup_controls() +void nuptial_flight_state::setup_controls() { action_subscriptions.emplace_back ( @@ -462,7 +460,7 @@ void nuptial_flight::setup_controls() ); } -void nuptial_flight::enable_controls() +void nuptial_flight_state::enable_controls() { /* // Reset mouse look @@ -763,7 +761,7 @@ void nuptial_flight::enable_controls() // Change to nest selection state ctx.state_machine.pop(); - ctx.state_machine.emplace(new ::state::nest_selection(ctx)); + ctx.state_machine.emplace(std::make_unique(ctx)); } ); @@ -813,7 +811,7 @@ void nuptial_flight::enable_controls() }; // Push pause menu state - ctx.state_machine.emplace(new ::state::pause_menu(ctx)); + ctx.state_machine.emplace(std::make_unique(ctx)); } ); @@ -839,7 +837,7 @@ void nuptial_flight::enable_controls() */ } -void nuptial_flight::disable_controls() +void nuptial_flight_state::disable_controls() { /* if (mouse_look) @@ -876,7 +874,7 @@ void nuptial_flight::disable_controls() */ } -void nuptial_flight::select_entity(entity::id entity_id) +void nuptial_flight_state::select_entity(entity::id entity_id) { if (entity_id != selected_eid) { @@ -962,7 +960,7 @@ void nuptial_flight::select_entity(entity::id entity_id) } } -void nuptial_flight::select_nearest_entity(const float3& direction) +void nuptial_flight_state::select_nearest_entity(const float3& direction) { if (!ctx.entity_registry->valid(selected_eid)) return; @@ -982,5 +980,3 @@ void nuptial_flight::select_nearest_entity(const float3& direction) select_entity(picked_eid); } } - -} // namespace state diff --git a/src/game/state/nuptial-flight.hpp b/src/game/states/nuptial-flight-state.hpp similarity index 87% rename from src/game/state/nuptial-flight.hpp rename to src/game/states/nuptial-flight-state.hpp index 4a08507..f9d9d7f 100644 --- a/src/game/state/nuptial-flight.hpp +++ b/src/game/states/nuptial-flight-state.hpp @@ -17,23 +17,22 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_GAME_STATE_NUPTIAL_FLIGHT_HPP -#define ANTKEEPER_GAME_STATE_NUPTIAL_FLIGHT_HPP +#ifndef ANTKEEPER_NUPTIAL_FLIGHT_STATE_HPP +#define ANTKEEPER_NUPTIAL_FLIGHT_STATE_HPP -#include "game/state/base.hpp" +#include "game/states/game-state.hpp" #include #include #include #include #include -namespace state { -class nuptial_flight: public ::state::base +class nuptial_flight_state: public game_state { public: - nuptial_flight(::context& ctx); - virtual ~nuptial_flight(); + nuptial_flight_state(::game& ctx); + virtual ~nuptial_flight_state(); private: void create_camera_rig(); @@ -84,6 +83,4 @@ private: std::vector> action_subscriptions; }; -} // namespace state - -#endif // ANTKEEPER_GAME_STATE_NUPTIAL_FLIGHT_HPP +#endif // ANTKEEPER_NUPTIAL_FLIGHT_STATE_HPP diff --git a/src/game/state/options-menu.cpp b/src/game/states/options-menu-state.cpp similarity index 86% rename from src/game/state/options-menu.cpp rename to src/game/states/options-menu-state.cpp index d39a347..7f25a67 100644 --- a/src/game/state/options-menu.cpp +++ b/src/game/states/options-menu-state.cpp @@ -17,13 +17,13 @@ * along with Antkeeper source code. If not, see . */ -#include "game/state/options-menu.hpp" -#include "game/state/main-menu.hpp" -#include "game/state/controls-menu.hpp" -#include "game/state/graphics-menu.hpp" -#include "game/state/sound-menu.hpp" -#include "game/state/language-menu.hpp" -#include "game/state/pause-menu.hpp" +#include "game/states/options-menu-state.hpp" +#include "game/states/main-menu-state.hpp" +#include "game/states/controls-menu-state.hpp" +#include "game/states/graphics-menu-state.hpp" +#include "game/states/sound-menu-state.hpp" +#include "game/states/language-menu-state.hpp" +#include "game/states/pause-menu-state.hpp" #include "game/menu.hpp" #include "game/controls.hpp" #include @@ -36,10 +36,8 @@ using namespace hash::literals; -namespace state { - -options_menu::options_menu(::context& ctx): - ::state::base(ctx) +options_menu_state::options_menu_state(::game& ctx): + game_state(ctx) { debug::log::trace("Entering options menu state..."); @@ -92,7 +90,7 @@ options_menu::options_menu(::context& ctx): [&ctx]() { ctx.state_machine.pop(); - ctx.state_machine.emplace(new ::state::controls_menu(ctx)); + ctx.state_machine.emplace(std::make_unique(ctx)); } ); } @@ -115,7 +113,7 @@ options_menu::options_menu(::context& ctx): [&ctx]() { ctx.state_machine.pop(); - ctx.state_machine.emplace(new ::state::graphics_menu(ctx)); + ctx.state_machine.emplace(std::make_unique(ctx)); } ); } @@ -138,7 +136,7 @@ options_menu::options_menu(::context& ctx): [&ctx]() { ctx.state_machine.pop(); - ctx.state_machine.emplace(new ::state::sound_menu(ctx)); + ctx.state_machine.emplace(std::make_unique(ctx)); } ); } @@ -161,7 +159,7 @@ options_menu::options_menu(::context& ctx): [&ctx]() { ctx.state_machine.pop(); - ctx.state_machine.emplace(new ::state::language_menu(ctx)); + ctx.state_machine.emplace(std::make_unique(ctx)); } ); } @@ -187,9 +185,9 @@ options_menu::options_menu(::context& ctx): { ctx.state_machine.pop(); if (ctx.resume_callback) - ctx.state_machine.emplace(new ::state::pause_menu(ctx)); + ctx.state_machine.emplace(std::make_unique(ctx)); else - ctx.state_machine.emplace(new ::state::main_menu(ctx, false)); + ctx.state_machine.emplace(std::make_unique(ctx, false)); } ); } @@ -221,7 +219,7 @@ options_menu::options_menu(::context& ctx): debug::log::trace("Entered options menu state"); } -options_menu::~options_menu() +options_menu_state::~options_menu_state() { debug::log::trace("Exiting options menu state..."); @@ -234,5 +232,3 @@ options_menu::~options_menu() debug::log::trace("Exited options menu state"); } - -} // namespace state diff --git a/src/game/states/options-menu-state.hpp b/src/game/states/options-menu-state.hpp new file mode 100644 index 0000000..10c831d --- /dev/null +++ b/src/game/states/options-menu-state.hpp @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_OPTIONS_MENU_STATE_HPP +#define ANTKEEPER_OPTIONS_MENU_STATE_HPP + +#include "game/states/game-state.hpp" + + +class options_menu_state: public game_state +{ +public: + options_menu_state(::game& ctx); + virtual ~options_menu_state(); +}; + +#endif // ANTKEEPER_OPTIONS_MENU_STATE_HPP diff --git a/src/game/state/pause-menu.cpp b/src/game/states/pause-menu-state.cpp similarity index 93% rename from src/game/state/pause-menu.cpp rename to src/game/states/pause-menu-state.cpp index d55306e..cc33751 100644 --- a/src/game/state/pause-menu.cpp +++ b/src/game/states/pause-menu-state.cpp @@ -17,10 +17,10 @@ * along with Antkeeper source code. If not, see . */ -#include "game/state/pause-menu.hpp" -#include "game/state/main-menu.hpp" -#include "game/state/options-menu.hpp" -#include "game/state/nuptial-flight.hpp" +#include "game/states/pause-menu-state.hpp" +#include "game/states/main-menu-state.hpp" +#include "game/states/options-menu-state.hpp" +#include "game/states/nuptial-flight-state.hpp" #include "game/menu.hpp" #include "game/controls.hpp" #include @@ -35,10 +35,8 @@ using namespace hash::literals; -namespace state { - -pause_menu::pause_menu(::context& ctx): - ::state::base(ctx) +pause_menu_state::pause_menu_state(::game& ctx): + game_state(ctx) { debug::log::trace("Entering pause menu state..."); @@ -115,7 +113,7 @@ pause_menu::pause_menu(::context& ctx): [&ctx]() { ctx.state_machine.pop(); - ctx.state_machine.emplace(new ::state::options_menu(ctx)); + ctx.state_machine.emplace(std::make_unique(ctx)); } ); } @@ -142,7 +140,7 @@ pause_menu::pause_menu(::context& ctx): ctx.menu_bg_billboard->set_active(false); ctx.state_machine.pop(); ctx.state_machine.pop(); - ctx.state_machine.emplace(new ::state::main_menu(ctx, true)); + ctx.state_machine.emplace(std::make_unique(ctx, true)); } ); }; @@ -212,7 +210,7 @@ pause_menu::pause_menu(::context& ctx): debug::log::trace("Entered pause menu state"); } -pause_menu::~pause_menu() +pause_menu_state::~pause_menu_state() { debug::log::trace("Exiting pause menu state..."); @@ -225,5 +223,3 @@ pause_menu::~pause_menu() debug::log::trace("Exited pause menu state"); } - -} // namespace state diff --git a/src/game/state/base.cpp b/src/game/states/pause-menu-state.hpp similarity index 74% rename from src/game/state/base.cpp rename to src/game/states/pause-menu-state.hpp index 56da769..53be936 100644 --- a/src/game/state/base.cpp +++ b/src/game/states/pause-menu-state.hpp @@ -17,15 +17,17 @@ * along with Antkeeper source code. If not, see . */ -#include "game/state/base.hpp" +#ifndef ANTKEEPER_PAUSE_MENU_STATE_HPP +#define ANTKEEPER_PAUSE_MENU_STATE_HPP -namespace state { +#include "game/states/game-state.hpp" -base::base(::context& ctx): - ctx(ctx) -{} -base::~base() -{} +class pause_menu_state: public game_state +{ +public: + pause_menu_state(::game& ctx); + virtual ~pause_menu_state(); +}; -} // namespace state +#endif // ANTKEEPER_PAUSE_MENU_STATE_HPP diff --git a/src/game/state/sound-menu.cpp b/src/game/states/sound-menu-state.cpp similarity index 96% rename from src/game/state/sound-menu.cpp rename to src/game/states/sound-menu-state.cpp index 3b17869..1ff4366 100644 --- a/src/game/state/sound-menu.cpp +++ b/src/game/states/sound-menu-state.cpp @@ -17,8 +17,8 @@ * along with Antkeeper source code. If not, see . */ -#include "game/state/sound-menu.hpp" -#include "game/state/options-menu.hpp" +#include "game/states/sound-menu-state.hpp" +#include "game/states/options-menu-state.hpp" #include "game/controls.hpp" #include #include @@ -28,10 +28,9 @@ using namespace hash::literals; -namespace state { -sound_menu::sound_menu(::context& ctx): - ::state::base(ctx) +sound_menu_state::sound_menu_state(::game& ctx): + game_state(ctx) { debug::log::trace("Entering sound menu state..."); @@ -180,7 +179,7 @@ sound_menu::sound_menu(::context& ctx): [&ctx]() { ctx.state_machine.pop(); - ctx.state_machine.emplace(new ::state::options_menu(ctx)); + ctx.state_machine.emplace(std::make_unique(ctx)); } ); } @@ -226,7 +225,7 @@ sound_menu::sound_menu(::context& ctx): debug::log::trace("Entered sound menu state"); } -sound_menu::~sound_menu() +sound_menu_state::~sound_menu_state() { debug::log::trace("Exiting sound menu state..."); @@ -240,7 +239,7 @@ sound_menu::~sound_menu() debug::log::trace("Exited sound menu state"); } -void sound_menu::update_value_text_content() +void sound_menu_state::update_value_text_content() { const std::string string_on = get_string(ctx, "on"_fnv1a32); const std::string string_off = get_string(ctx, "off"_fnv1a32); @@ -252,5 +251,3 @@ void sound_menu::update_value_text_content() std::get<1>(ctx.menu_item_texts[4])->set_content((ctx.captions) ? string_on : string_off); std::get<1>(ctx.menu_item_texts[5])->set_content(std::to_string(static_cast(std::round(ctx.captions_size * 100.0f))) + "%"); } - -} // namespace state diff --git a/src/game/state/graphics-menu.hpp b/src/game/states/sound-menu-state.hpp similarity index 72% rename from src/game/state/graphics-menu.hpp rename to src/game/states/sound-menu-state.hpp index dfb8915..15d841f 100644 --- a/src/game/state/graphics-menu.hpp +++ b/src/game/states/sound-menu-state.hpp @@ -17,23 +17,20 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_GAME_STATE_GRAPHICS_MENU_HPP -#define ANTKEEPER_GAME_STATE_GRAPHICS_MENU_HPP +#ifndef ANTKEEPER_SOUND_MENU_STATE_HPP +#define ANTKEEPER_SOUND_MENU_STATE_HPP -#include "game/state/base.hpp" +#include "game/states/game-state.hpp" -namespace state { -class graphics_menu: public ::state::base +class sound_menu_state: public game_state { public: - graphics_menu(::context& ctx); - virtual ~graphics_menu(); + sound_menu_state(::game& ctx); + virtual ~sound_menu_state(); private: void update_value_text_content(); }; -} // namespace state - -#endif // ANTKEEPER_GAME_STATE_GRAPHICS_MENU_HPP +#endif // ANTKEEPER_SOUND_MENU_STATE_HPP diff --git a/src/game/state/splash.cpp b/src/game/states/splash-state.cpp similarity index 92% rename from src/game/state/splash.cpp rename to src/game/states/splash-state.cpp index 9717d9b..db7cf6e 100644 --- a/src/game/state/splash.cpp +++ b/src/game/states/splash-state.cpp @@ -1,221 +1,217 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "game/state/splash.hpp" -#include -#include -#include -#include -#include -#include "game/context.hpp" -#include "game/state/main-menu.hpp" -#include -#include -#include -#include -#include - -using namespace math::glsl; - -namespace state { - -splash::splash(::context& ctx): - ::state::base(ctx) -{ - debug::log::trace("Entering splash state..."); - - const vec2 viewport_size = vec2(ctx.window->get_viewport_size()); - const vec2 viewport_center = viewport_size * 0.5f; - - // Enable color buffer clearing in UI pass - ctx.ui_clear_pass->set_cleared_buffers(true, true, false); - - // Load splash texture - const gl::texture_2d* splash_texture = ctx.resource_manager->load("splash.tex"); - - // Get splash texture dimensions - auto splash_dimensions = splash_texture->get_dimensions(); - - // Construct splash billboard material - splash_billboard_material.set_blend_mode(render::blend_mode::translucent); - splash_billboard_material.set_shader_program(ctx.resource_manager->load("ui-element-textured.glsl")); - splash_billboard_material.add_property("background")->set_value(splash_texture); - render::material_property* splash_tint = splash_billboard_material.add_property("tint"); - splash_tint->set_value(float4{1, 1, 1, 0}); - splash_billboard_material.update_tweens(); - - // Construct splash billboard - splash_billboard.set_material(&splash_billboard_material); - splash_billboard.set_scale({(float)std::get<0>(splash_dimensions) * 0.5f, (float)std::get<1>(splash_dimensions) * 0.5f, 1.0f}); - splash_billboard.set_translation({std::round(viewport_center.x()), std::round(viewport_center.y()), 0.0f}); - splash_billboard.update_tweens(); - - // Add splash billboard to UI scene - ctx.ui_scene->add_object(&splash_billboard); - - // Load animation timing configuration - const double splash_fade_in_duration = 0.5; - const double splash_duration = 2.0; - const double splash_fade_out_duration = 0.5; - - // Construct splash fade in animation - splash_fade_in_animation.set_interpolator(ease::out_cubic); - animation_channel* splash_fade_in_opacity_channel = splash_fade_in_animation.add_channel(0); - 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}); - - // Build splash fade out animation - splash_fade_out_animation.set_interpolator(ease::out_cubic); - animation_channel* splash_fade_out_opacity_channel = splash_fade_out_animation.add_channel(0); - splash_fade_out_opacity_channel->insert_keyframe({0.0, 1.0f}); - splash_fade_out_opacity_channel->insert_keyframe({splash_fade_out_duration, 0.0f}); - - // Setup animation frame callbacks - auto set_splash_opacity = [splash_tint](int channel, const float& opacity) - { - splash_tint->set_value(float4{1, 1, 1, opacity}); - }; - splash_fade_in_animation.set_frame_callback(set_splash_opacity); - splash_fade_out_animation.set_frame_callback(set_splash_opacity); - - // Trigger splash fade out animation when splash fade in animation ends - splash_fade_in_animation.set_end_callback - ( - [this]() - { - this->splash_fade_out_animation.play(); - } - ); - - // Trigger a state change when the splash fade out animation ends - splash_fade_out_animation.set_end_callback - ( - [&ctx]() - { - // Queue change to main menu state - ctx.function_queue.push - ( - [&ctx]() - { - ctx.state_machine.pop(); - ctx.state_machine.emplace(new ::state::main_menu(ctx, true)); - } - ); - } - ); - - // Add splash fade animations to animator - ctx.animator->add_animation(&splash_fade_in_animation); - ctx.animator->add_animation(&splash_fade_out_animation); - - // Start splash fade in animation - splash_fade_in_animation.play(); - - // Setup window resized callback - window_resized_subscription = ctx.window->get_resized_channel().subscribe - ( - [&](const auto& event) - { - const vec2 viewport_size = vec2(event.window->get_viewport_size()); - const vec2 viewport_center = viewport_size * 0.5f; - splash_billboard.set_translation({std::round(viewport_center.x()), std::round(viewport_center.y()), 0.0f}); - splash_billboard.update_tweens(); - } - ); - - // Construct splash skip function - auto skip = [&](const auto& event) - { - ctx.function_queue.emplace - ( - [&]() - { - // Black out screen - ctx.window->get_rasterizer()->set_clear_color(0.0f, 0.0f, 0.0f, 1.0f); - ctx.window->get_rasterizer()->clear_framebuffer(true, false, false); - ctx.window->swap_buffers(); - - // Change to main menu state - ctx.state_machine.pop(); - ctx.state_machine.emplace(new ::state::main_menu(ctx, true)); - } - ); - }; - - // Set up splash skippers - input_mapped_subscriptions.emplace_back - ( - ctx.input_mapper.get_gamepad_button_mapped_channel().subscribe - ( - skip - ) - ); - input_mapped_subscriptions.emplace_back - ( - ctx.input_mapper.get_key_mapped_channel().subscribe - ( - skip - ) - ); - input_mapped_subscriptions.emplace_back - ( - ctx.input_mapper.get_mouse_button_mapped_channel().subscribe - ( - skip - ) - ); - - // Enable splash skippers next frame - ctx.function_queue.push - ( - [&]() - { - ctx.input_mapper.connect(ctx.input_manager->get_event_queue()); - } - ); - - debug::log::trace("Entered splash state"); -} - -splash::~splash() -{ - debug::log::trace("Exiting splash state..."); - - // Disable splash skippers - ctx.input_mapper.disconnect(); - input_mapped_subscriptions.clear(); - - // Remove splash fade animations from animator - ctx.animator->remove_animation(&splash_fade_in_animation); - ctx.animator->remove_animation(&splash_fade_out_animation); - - // Remove splash billboard from UI scene - ctx.ui_scene->remove_object(&splash_billboard); - - // Unload splash texture - ctx.resource_manager->unload("splash.tex"); - - // Disable color buffer clearing in UI pass - ctx.ui_clear_pass->set_cleared_buffers(false, true, false); - - debug::log::trace("Exited splash state"); -} - -} // namespace state +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include "game/states/splash-state.hpp" +#include "game/game.hpp" +#include "game/states/main-menu-state.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace math::glsl; + +splash_state::splash_state(::game& ctx): + game_state(ctx) +{ + debug::log::trace("Entering splash state..."); + + const vec2 viewport_size = vec2(ctx.window->get_viewport_size()); + const vec2 viewport_center = viewport_size * 0.5f; + + // Enable color buffer clearing in UI pass + ctx.ui_clear_pass->set_cleared_buffers(true, true, false); + + // Load splash texture + const gl::texture_2d* splash_texture = ctx.resource_manager->load("splash.tex"); + + // Get splash texture dimensions + auto splash_dimensions = splash_texture->get_dimensions(); + + // Construct splash billboard material + splash_billboard_material.set_blend_mode(render::blend_mode::translucent); + splash_billboard_material.set_shader_program(ctx.resource_manager->load("ui-element-textured.glsl")); + splash_billboard_material.add_property("background")->set_value(splash_texture); + render::material_property* splash_tint = splash_billboard_material.add_property("tint"); + splash_tint->set_value(float4{1, 1, 1, 0}); + splash_billboard_material.update_tweens(); + + // Construct splash billboard + splash_billboard.set_material(&splash_billboard_material); + splash_billboard.set_scale({(float)std::get<0>(splash_dimensions) * 0.5f, (float)std::get<1>(splash_dimensions) * 0.5f, 1.0f}); + splash_billboard.set_translation({std::round(viewport_center.x()), std::round(viewport_center.y()), 0.0f}); + splash_billboard.update_tweens(); + + // Add splash billboard to UI scene + ctx.ui_scene->add_object(&splash_billboard); + + // Load animation timing configuration + const double splash_fade_in_duration = 0.5; + const double splash_duration = 2.0; + const double splash_fade_out_duration = 0.5; + + // Construct splash fade in animation + splash_fade_in_animation.set_interpolator(ease::out_cubic); + animation_channel* splash_fade_in_opacity_channel = splash_fade_in_animation.add_channel(0); + 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}); + + // Build splash fade out animation + splash_fade_out_animation.set_interpolator(ease::out_cubic); + animation_channel* splash_fade_out_opacity_channel = splash_fade_out_animation.add_channel(0); + splash_fade_out_opacity_channel->insert_keyframe({0.0, 1.0f}); + splash_fade_out_opacity_channel->insert_keyframe({splash_fade_out_duration, 0.0f}); + + // Setup animation frame callbacks + auto set_splash_opacity = [splash_tint](int channel, const float& opacity) + { + splash_tint->set_value(float4{1, 1, 1, opacity}); + }; + splash_fade_in_animation.set_frame_callback(set_splash_opacity); + splash_fade_out_animation.set_frame_callback(set_splash_opacity); + + // Trigger splash fade out animation when splash fade in animation ends + splash_fade_in_animation.set_end_callback + ( + [this]() + { + this->splash_fade_out_animation.play(); + } + ); + + // Trigger a state change when the splash fade out animation ends + splash_fade_out_animation.set_end_callback + ( + [&ctx]() + { + // Queue change to main menu state + ctx.function_queue.push + ( + [&ctx]() + { + ctx.state_machine.pop(); + ctx.state_machine.emplace(std::make_unique(ctx, true)); + } + ); + } + ); + + // Add splash fade animations to animator + ctx.animator->add_animation(&splash_fade_in_animation); + ctx.animator->add_animation(&splash_fade_out_animation); + + // Start splash fade in animation + splash_fade_in_animation.play(); + + // Setup window resized callback + window_resized_subscription = ctx.window->get_resized_channel().subscribe + ( + [&](const auto& event) + { + const vec2 viewport_size = vec2(event.window->get_viewport_size()); + const vec2 viewport_center = viewport_size * 0.5f; + splash_billboard.set_translation({std::round(viewport_center.x()), std::round(viewport_center.y()), 0.0f}); + splash_billboard.update_tweens(); + } + ); + + // Construct splash skip function + auto skip = [&](const auto& event) + { + ctx.function_queue.emplace + ( + [&]() + { + // Black out screen + ctx.window->get_rasterizer()->set_clear_color(0.0f, 0.0f, 0.0f, 1.0f); + ctx.window->get_rasterizer()->clear_framebuffer(true, false, false); + ctx.window->swap_buffers(); + + // Change to main menu state + ctx.state_machine.pop(); + ctx.state_machine.emplace(std::make_unique(ctx, true)); + } + ); + }; + + // Set up splash skippers + input_mapped_subscriptions.emplace_back + ( + ctx.input_mapper.get_gamepad_button_mapped_channel().subscribe + ( + skip + ) + ); + input_mapped_subscriptions.emplace_back + ( + ctx.input_mapper.get_key_mapped_channel().subscribe + ( + skip + ) + ); + input_mapped_subscriptions.emplace_back + ( + ctx.input_mapper.get_mouse_button_mapped_channel().subscribe + ( + skip + ) + ); + + // Enable splash skippers next frame + ctx.function_queue.push + ( + [&]() + { + ctx.input_mapper.connect(ctx.input_manager->get_event_queue()); + } + ); + + debug::log::trace("Entered splash state"); +} + +splash_state::~splash_state() +{ + debug::log::trace("Exiting splash state..."); + + // Disable splash skippers + ctx.input_mapper.disconnect(); + input_mapped_subscriptions.clear(); + + // Remove splash fade animations from animator + ctx.animator->remove_animation(&splash_fade_in_animation); + ctx.animator->remove_animation(&splash_fade_out_animation); + + // Remove splash billboard from UI scene + ctx.ui_scene->remove_object(&splash_billboard); + + // Unload splash texture + ctx.resource_manager->unload("splash.tex"); + + // Disable color buffer clearing in UI pass + ctx.ui_clear_pass->set_cleared_buffers(false, true, false); + + debug::log::trace("Exited splash state"); +} diff --git a/src/game/state/splash.hpp b/src/game/states/splash-state.hpp similarity index 82% rename from src/game/state/splash.hpp rename to src/game/states/splash-state.hpp index d497000..c148f5c 100644 --- a/src/game/state/splash.hpp +++ b/src/game/states/splash-state.hpp @@ -17,23 +17,22 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_GAME_STATE_SPLASH_HPP -#define ANTKEEPER_GAME_STATE_SPLASH_HPP +#ifndef ANTKEEPER_SPLASH_STATE_HPP +#define ANTKEEPER_SPLASH_STATE_HPP -#include "game/state/base.hpp" +#include "game/states/game-state.hpp" #include #include #include #include #include -namespace state { -class splash: public ::state::base +class splash_state: public game_state { public: - splash(::context& ctx); - virtual ~splash(); + splash_state(::game& ctx); + virtual ~splash_state(); private: render::material splash_billboard_material; @@ -44,6 +43,4 @@ private: std::shared_ptr window_resized_subscription; }; -} // namespace state - -#endif // ANTKEEPER_GAME_STATE_SPLASH_HPP +#endif // ANTKEEPER_SPLASH_STATE_HPP diff --git a/src/game/strings.cpp b/src/game/strings.cpp index c75a1e3..bd1efe7 100644 --- a/src/game/strings.cpp +++ b/src/game/strings.cpp @@ -21,7 +21,7 @@ #include -std::string get_string(const ::context& ctx, std::uint32_t key) +std::string get_string(const ::game& ctx, std::uint32_t key) { if (auto i = ctx.string_map->find(key); i != ctx.string_map->end()) { diff --git a/src/game/strings.hpp b/src/game/strings.hpp index 5238556..3af8574 100644 --- a/src/game/strings.hpp +++ b/src/game/strings.hpp @@ -20,7 +20,7 @@ #ifndef ANTKEEPER_GAME_STRINGS_HPP #define ANTKEEPER_GAME_STRINGS_HPP -#include "game/context.hpp" +#include "game/game.hpp" #include #include @@ -34,7 +34,7 @@ * * @return `true` if the string was found, `false` otherwise. */ -[[nodiscard]] std::string get_string(const ::context& ctx, std::uint32_t key); +[[nodiscard]] std::string get_string(const ::game& ctx, std::uint32_t key); #endif // ANTKEEPER_GAME_STRINGS_HPP diff --git a/src/game/systems/render-system.cpp b/src/game/systems/render-system.cpp index cba828d..bb4fd26 100644 --- a/src/game/systems/render-system.cpp +++ b/src/game/systems/render-system.cpp @@ -289,4 +289,3 @@ void render_system::on_light_destroy(entity::registry& registry, entity::id enti delete light; } } - diff --git a/src/game/world.cpp b/src/game/world.cpp index 3ae3706..896d3d9 100644 --- a/src/game/world.cpp +++ b/src/game/world.cpp @@ -73,24 +73,24 @@ namespace world { /// Loads an ephemeris. -static void load_ephemeris(::context& ctx); +static void load_ephemeris(::game& ctx); /// Creates the fixed stars. -static void create_stars(::context& ctx); +static void create_stars(::game& ctx); /// Creates the Sun. -static void create_sun(::context& ctx); +static void create_sun(::game& ctx); /// Creates the Earth-Moon system. -static void create_earth_moon_system(::context& ctx); +static void create_earth_moon_system(::game& ctx); /// Creates the Earth. -static void create_earth(::context& ctx); +static void create_earth(::game& ctx); /// Creates the Moon. -static void create_moon(::context& ctx); +static void create_moon(::game& ctx); -void cosmogenesis(::context& ctx) +void cosmogenesis(::game& ctx) { debug::log::trace("Generating cosmos..."); @@ -102,7 +102,7 @@ void cosmogenesis(::context& ctx) debug::log::trace("Generated cosmos"); } -void create_observer(::context& ctx) +void create_observer(::game& ctx) { debug::log::trace("Creating observer..."); @@ -138,7 +138,7 @@ void create_observer(::context& ctx) debug::log::trace("Created observer"); } -void set_location(::context& ctx, double elevation, double latitude, double longitude) +void set_location(::game& ctx, double elevation, double latitude, double longitude) { if (auto it = ctx.entities.find("observer"); it != ctx.entities.end()) { @@ -161,7 +161,7 @@ void set_location(::context& ctx, double elevation, double latitude, double long } } -void set_time(::context& ctx, double t) +void set_time(::game& ctx, double t) { try { @@ -176,7 +176,7 @@ void set_time(::context& ctx, double t) } } -void set_time(::context& ctx, int year, int month, int day, int hour, int minute, double second) +void set_time(::game& ctx, int year, int month, int day, int hour, int minute, double second) { double longitude = 0.0; @@ -201,7 +201,7 @@ void set_time(::context& ctx, int year, int month, int day, int hour, int minute set_time(ctx, t); } -void set_time_scale(::context& ctx, double scale) +void set_time_scale(::game& ctx, double scale) { // Convert time scale from seconds to days const double astronomical_scale = scale / physics::time::seconds_per_day; @@ -210,12 +210,12 @@ void set_time_scale(::context& ctx, double scale) ctx.astronomy_system->set_time_scale(astronomical_scale); } -void load_ephemeris(::context& ctx) +void load_ephemeris(::game& ctx) { ctx.orbit_system->set_ephemeris(ctx.resource_manager->load>("de421.eph")); } -void create_stars(::context& ctx) +void create_stars(::game& ctx) { debug::log::trace("Generating fixed stars..."); @@ -352,7 +352,7 @@ void create_stars(::context& ctx) debug::log::trace("Generated fixed stars"); } -void create_sun(::context& ctx) +void create_sun(::game& ctx) { debug::log::trace("Generating Sun..."); @@ -398,7 +398,7 @@ void create_sun(::context& ctx) debug::log::trace("Generated Sun"); } -void create_earth_moon_system(::context& ctx) +void create_earth_moon_system(::game& ctx) { debug::log::trace("Generating Earth-Moon system..."); @@ -418,7 +418,7 @@ void create_earth_moon_system(::context& ctx) debug::log::trace("Generated Earth-Moon system"); } -void create_earth(::context& ctx) +void create_earth(::game& ctx) { debug::log::trace("Generating Earth..."); @@ -435,7 +435,7 @@ void create_earth(::context& ctx) debug::log::trace("Generated Earth"); } -void create_moon(::context& ctx) +void create_moon(::game& ctx) { debug::log::trace("Generating Moon..."); @@ -466,7 +466,7 @@ void create_moon(::context& ctx) debug::log::trace("Generated Moon"); } -void enter_ecoregion(::context& ctx, const ecoregion& ecoregion) +void enter_ecoregion(::game& ctx, const ecoregion& ecoregion) { /* image img; diff --git a/src/game/world.hpp b/src/game/world.hpp index cc569fc..afae12d 100644 --- a/src/game/world.hpp +++ b/src/game/world.hpp @@ -20,7 +20,7 @@ #ifndef ANTKEEPER_GAME_WORLD_HPP #define ANTKEEPER_GAME_WORLD_HPP -#include "game/context.hpp" +#include "game/game.hpp" #include "game/ecoregion.hpp" @@ -28,10 +28,10 @@ namespace world { /// Creates the cosmos. -void cosmogenesis(::context& ctx); +void cosmogenesis(::game& ctx); /// Creates the observer. -void create_observer(::context& ctx); +void create_observer(::game& ctx); /** * Sets the location of the observer. @@ -41,7 +41,7 @@ void create_observer(::context& ctx); * @param latitude Latitude, in radians. * @param longitude Longitude, in radians. */ -void set_location(::context& ctx, double elevation, double latitude, double longitude); +void set_location(::game& ctx, double elevation, double latitude, double longitude); /** * Sets the current time. @@ -49,7 +49,7 @@ void set_location(::context& ctx, double elevation, double latitude, double long * @param ctx Game context. * @param t UT1 time, in days. */ -void set_time(::context& ctx, double t); +void set_time(::game& ctx, double t); /** * Sets the current time. @@ -62,7 +62,7 @@ void set_time(::context& ctx, double t); * @param minute Minute number on `[0, 59]`. * @param second Fractional second on `[0.0, 60.0)`. */ -void set_time(::context& ctx, int year, int month, int day, int hour, int minute, double second); +void set_time(::game& ctx, int year, int month, int day, int hour, int minute, double second); /** * Sets rate at which time passes. @@ -70,7 +70,7 @@ void set_time(::context& ctx, int year, int month, int day, int hour, int minute * @param ctx Game context. * @param scale Time scale. */ -void set_time_scale(::context& ctx, double scale); +void set_time_scale(::game& ctx, double scale); /** * Enters a ecoregion. @@ -78,7 +78,7 @@ void set_time_scale(::context& ctx, double scale); * @param ctx Game context. * @param ecoregion Ecoregion to enter. */ -void enter_ecoregion(::context& ctx, const ecoregion& ecoregion); +void enter_ecoregion(::game& ctx, const ecoregion& ecoregion); } // namespace menu