Browse Source

Revise game states and application class. Remove dirent. Replace path strings with std::filesystem::path

master
C. J. Howard 1 year ago
parent
commit
a2ee4f2a56
66 changed files with 4520 additions and 4531 deletions
  1. +4
    -240
      src/application.cpp
  2. +11
    -95
      src/application.hpp
  3. +1
    -1
      src/debug/console-commands.cpp
  4. +40
    -40
      src/game/context.hpp
  5. +78
    -72
      src/game/controls.cpp
  6. +7
    -6
      src/game/controls.hpp
  7. +31
    -31
      src/game/fonts.cpp
  8. +1
    -1
      src/game/fonts.hpp
  9. +37
    -0
      src/game/graphics.cpp
  10. +1
    -0
      src/game/graphics.hpp
  11. +14
    -10
      src/game/loop.cpp
  12. +10
    -7
      src/game/loop.hpp
  13. +132
    -132
      src/game/menu.cpp
  14. +22
    -22
      src/game/menu.hpp
  15. +7
    -7
      src/game/save.cpp
  16. +1
    -1
      src/game/save.hpp
  17. +6
    -12
      src/game/state/base.cpp
  18. +54
    -0
      src/game/state/base.hpp
  19. +1202
    -0
      src/game/state/boot.cpp
  20. +68
    -0
      src/game/state/boot.hpp
  21. +3
    -3
      src/game/state/brood.cpp
  22. +0
    -0
      src/game/state/brood.hpp
  23. +65
    -46
      src/game/state/controls-menu.cpp
  24. +7
    -8
      src/game/state/controls-menu.hpp
  25. +143
    -0
      src/game/state/credits.cpp
  26. +7
    -8
      src/game/state/credits.hpp
  27. +48
    -33
      src/game/state/extras-menu.cpp
  28. +7
    -8
      src/game/state/extras-menu.hpp
  29. +6
    -6
      src/game/state/forage.cpp
  30. +0
    -0
      src/game/state/forage.hpp
  31. +149
    -137
      src/game/state/gamepad-config-menu.cpp
  32. +12
    -8
      src/game/state/gamepad-config-menu.hpp
  33. +357
    -0
      src/game/state/graphics-menu.cpp
  34. +10
    -8
      src/game/state/graphics-menu.hpp
  35. +134
    -123
      src/game/state/keyboard-config-menu.cpp
  36. +13
    -8
      src/game/state/keyboard-config-menu.hpp
  37. +68
    -57
      src/game/state/language-menu.cpp
  38. +10
    -7
      src/game/state/language-menu.hpp
  39. +284
    -0
      src/game/state/main-menu.cpp
  40. +11
    -8
      src/game/state/main-menu.hpp
  41. +544
    -0
      src/game/state/nuptial-flight.cpp
  42. +12
    -8
      src/game/state/nuptial-flight.hpp
  43. +237
    -0
      src/game/state/options-menu.cpp
  44. +7
    -8
      src/game/state/options-menu.hpp
  45. +75
    -51
      src/game/state/pause-menu.cpp
  46. +7
    -8
      src/game/state/pause-menu.hpp
  47. +262
    -0
      src/game/state/sound-menu.cpp
  48. +10
    -8
      src/game/state/sound-menu.hpp
  49. +170
    -0
      src/game/state/splash.cpp
  50. +7
    -8
      src/game/state/splash.hpp
  51. +0
    -1230
      src/game/states/boot.cpp
  52. +0
    -137
      src/game/states/credits.cpp
  53. +0
    -346
      src/game/states/graphics-menu.cpp
  54. +0
    -265
      src/game/states/main-menu.cpp
  55. +0
    -557
      src/game/states/nuptial-flight.cpp
  56. +0
    -214
      src/game/states/options-menu.cpp
  57. +0
    -254
      src/game/states/sound-menu.cpp
  58. +0
    -160
      src/game/states/splash.cpp
  59. +28
    -28
      src/game/tools.cpp
  60. +2
    -2
      src/game/tools.hpp
  61. +30
    -30
      src/game/world.cpp
  62. +6
    -6
      src/game/world.hpp
  63. +8
    -14
      src/main.cpp
  64. +39
    -0
      src/state-machine.hpp
  65. +11
    -43
      src/utility/paths.cpp
  66. +4
    -9
      src/utility/paths.hpp

+ 4
- 240
src/application.cpp View File

@ -17,10 +17,8 @@
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#include "animation/frame-scheduler.hpp"
#include "application.hpp"
#include "debug/logger.hpp"
#include "debug/performance-sampler.hpp"
#include "event/event-dispatcher.hpp"
#include "event/window-events.hpp"
#include "input/scancode.hpp"
@ -31,18 +29,11 @@
#include <glad/glad.h>
#include <stdexcept>
#include <utility>
#include <thread>
#include <stb/stb_image_write.h>
#include <iostream>
#include <iomanip>
application::application():
closed(false),
exit_status(EXIT_SUCCESS),
current_state{std::string(), nullptr, nullptr},
queued_state{std::string(), nullptr, nullptr},
update_callback(nullptr),
render_callback(nullptr),
fullscreen(true),
v_sync(false),
cursor_visible(true),
@ -51,7 +42,6 @@ application::application():
window_dimensions({0, 0}),
viewport_dimensions({0, 0}),
mouse_position({0, 0}),
update_rate(60.0),
logger(nullptr),
sdl_window(nullptr),
sdl_gl_context(nullptr)
@ -213,18 +203,7 @@ application::application():
mouse->set_event_dispatcher(event_dispatcher);
// Connect gamepads
translate_sdl_events();
// Setup frame scheduler
frame_scheduler = new ::frame_scheduler();
frame_scheduler->set_update_callback(std::bind(&application::update, this, std::placeholders::_1, std::placeholders::_2));
frame_scheduler->set_render_callback(std::bind(&application::render, this, std::placeholders::_1));
frame_scheduler->set_update_rate(update_rate);
frame_scheduler->set_max_frame_duration(0.25);
// Setup performance sampling
performance_sampler = new debug::performance_sampler();
performance_sampler->set_sample_size(15);
process_events();
}
application::~application()
@ -240,186 +219,9 @@ application::~application()
SDL_Quit();
}
void application::close(int status)
void application::close()
{
closed = true;
exit_status = status;
}
int application::execute(const application::state& initial_state)
{
try
{
// Enter initial application state
change_state(initial_state);
// Perform initial update
update(0.0, 0.0);
// Reset frame scheduler
frame_scheduler->reset();
// Schedule frames until closed
while (!closed)
{
translate_sdl_events();
// Enter queued state (if any)
if (queued_state.enter != nullptr || queued_state.exit != nullptr)
{
// Make a copy of the queued state
application::state queued_state_copy = queued_state;
// Clear the queued state
queued_state = {std::string(), nullptr, nullptr};
// Enter the queued state
change_state(queued_state_copy);
}
// Tick frame scheduler
frame_scheduler->tick();
// Sample frame duration
performance_sampler->sample(frame_scheduler->get_frame_duration());
}
// Exit current state
change_state({std::string(), nullptr, nullptr});
}
catch (const std::exception& e)
{
// Print exception to logger
logger->error(std::string("Unhandled exception: \"") + e.what() + std::string("\""));
// Show error message box with unhandled exception
SDL_ShowSimpleMessageBox
(
SDL_MESSAGEBOX_ERROR,
"Unhandled Exception",
e.what(),
sdl_window
);
// Set exit status to failure
exit_status = EXIT_FAILURE;
}
return exit_status;
}
void application::change_state(const application::state& next_state)
{
// Exit current state
if (current_state.exit)
{
logger->push_task("Exiting application state \"" + current_state.name + "\"");
try
{
current_state.exit();
}
catch (...)
{
logger->pop_task(EXIT_FAILURE);
throw;
}
logger->pop_task(EXIT_SUCCESS);
}
current_state = next_state;
// Enter next state
if (current_state.enter)
{
logger->push_task("Entering application state \"" + current_state.name + "\"");
try
{
current_state.enter();
}
catch (...)
{
logger->pop_task(EXIT_FAILURE);
throw;
}
logger->pop_task(EXIT_SUCCESS);
}
// Enter queued state (if any)
if (queued_state.enter != nullptr || queued_state.exit != nullptr)
{
// Make a copy of the queued state
application::state queued_state_copy = queued_state;
// Clear the queued state
queued_state = {std::string(), nullptr, nullptr};
// Enter the queued state
change_state(queued_state_copy);
}
}
void application::queue_state(const application::state& next_state)
{
queued_state = next_state;
logger->log("Queued application state \"" + queued_state.name + "\"");
}
std::shared_ptr<image> application::capture_frame() const
{
int w = viewport_dimensions[0];
int h = viewport_dimensions[1];
std::shared_ptr<image> frame = std::make_shared<image>();
frame->format(1, 3);
frame->resize(w, h);
logger->log("starting read");
// Read pixel data from framebuffer into image
glReadBuffer(GL_BACK);
logger->log("buffer read");
glReadPixels(0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, frame->get_pixels());
logger->log("ending read");
return std::move(frame);
}
void application::save_frame(const std::string& path) const
{
logger->push_task("Saving screenshot to \"" + path + "\"");
auto frame = capture_frame();
std::thread
(
[frame, path]
{
stbi_flip_vertically_on_write(1);
stbi_write_png(path.c_str(), frame->get_width(), frame->get_height(), frame->get_channel_count(), frame->get_pixels(), frame->get_width() * frame->get_channel_count());
}
).detach();
logger->pop_task(EXIT_SUCCESS);
}
void application::set_update_callback(const update_callback_type& callback)
{
update_callback = callback;
}
void application::set_render_callback(const render_callback_type& callback)
{
render_callback = callback;
}
void application::set_update_rate(double frequency)
{
update_rate = frequency;
frame_scheduler->set_update_rate(update_rate);
}
void application::set_title(const std::string& title)
@ -563,45 +365,7 @@ void application::add_game_controller_mappings(const void* mappings, std::size_t
}
}
void application::update(double t, double dt)
{
translate_sdl_events();
event_dispatcher->update(t);
if (update_callback)
{
update_callback(t, dt);
}
/*
static int frame = 0;
if (frame % 60 == 0)
{
std::cout << std::fixed;
std::cout << std::setprecision(2);
std::cout << performance_sampler->mean_frame_duration() * 1000.0 << "\n";
}
++frame;
*/
}
void application::render(double alpha)
{
/*
std::cout << std::fixed;
std::cout << std::setprecision(2);
std::cout << performance_sampler->mean_frame_duration() * 1000.0 << std::endl;
*/
if (render_callback)
{
render_callback(alpha);
}
SDL_GL_SwapWindow(sdl_window);
}
void application::translate_sdl_events()
void application::process_events()
{
// Mouse motion event accumulators
bool mouse_motion = false;
@ -787,7 +551,7 @@ void application::translate_sdl_events()
case SDL_QUIT:
{
close(EXIT_SUCCESS);
close();
break;
}
}

+ 11
- 95
src/application.hpp View File

@ -20,8 +20,6 @@
#ifndef ANTKEEPER_APPLICATION_HPP
#define ANTKEEPER_APPLICATION_HPP
#include <array>
#include <functional>
#include <list>
#include <memory>
#include <unordered_map>
@ -35,8 +33,6 @@
typedef struct SDL_Window SDL_Window;
typedef void* SDL_GLContext;
class event_dispatcher;
class frame_scheduler;
class image;
namespace debug
{
@ -50,23 +46,6 @@ namespace debug
class application
{
public:
/// Application state type.
struct state
{
/// Name of the state.
std::string name;
/// State enter function.
std::function<void()> enter;
/// State exit function.
std::function<void()> exit;
};
typedef std::function<int(application*)> bootloader_type;
typedef std::function<void(double, double)> update_callback_type;
typedef std::function<void(double)> render_callback_type;
/**
* Creates and initializes an application.
*/
@ -76,69 +55,11 @@ public:
* Destroys an application.
*/
~application();
/**
* Executes the application, causing it to run the bootloader then enter the execution loop until closed.
*
* @param initial_state Initial state of the application.
*
* @return Exit status code.
*/
int execute(const application::state& initial_state);
/**
* Requests the application's execution loop to cleanly terminate, and specifies its exit status code.
*
* @param status Status to be returned by application::execute() upon execution loop termination.
*/
void close(int status);
/**
* Changes the applications state, resulting in the execution of the current state's exit function (if any), followed by the new state's enter function (if any).
*
* @param next_state Next application state.
*/
void change_state(const application::state& next_state);
/**
* Queues the next applications state. This may be called from within a state's enter function.
*
* @param next_state Next application state.
*/
void queue_state(const application::state& next_state);
/**
* Captures a screenshot of the most recently rendered frame.
*
* @return Image containing the captured frame.
*/
std::shared_ptr<image> capture_frame() const;
/**
* Saves a PNG screenshot of the most recently rendered frame.
*
* @param path File path to the where the screenshot should be saved.
* Requests the application to close.
*/
void save_frame(const std::string& path) const;
/**
* Sets the update callback, which is executed at regular intervals until the application is closed. The update callback expects two parameters, the first being the total time in seconds since the application was executed (t), and the second being the time in seconds since the last update (dt). dt will always be a fixed value, and is determined by the user-specified update rate.
*
* @see application::set_update_rate()
*/
void set_update_callback(const update_callback_type& callback);
/**
* Sets the render callback, which is executed as many times as possible between update callbacks. The render callback expects one parameter, alpha, which is always between 0 and 1 and can be used to interpolate between update states.
*/
void set_render_callback(const render_callback_type& callback);
/**
* Sets the frequency with which the update callback should be called.
*
* @param frequency Number of times per second the update callback should be called.
*/
void set_update_rate(double frequency);
void close();
/**
* Sets the application window's title.
@ -227,20 +148,15 @@ public:
/// Returns the application's event dispatcher.
event_dispatcher* get_event_dispatcher();
void process_events();
bool was_closed() const;
private:
void update(double t, double dt);
void render(double alpha);
void translate_sdl_events();
void window_resized();
bool closed;
int exit_status;
application::state current_state;
application::state queued_state;
update_callback_type update_callback;
render_callback_type render_callback;
bool fullscreen;
bool v_sync;
bool cursor_visible;
@ -249,7 +165,6 @@ private:
int2 window_dimensions;
int2 viewport_dimensions;
int2 mouse_position;
double update_rate;
debug::logger* logger;
SDL_Window* sdl_window;
@ -257,10 +172,6 @@ private:
gl::rasterizer* rasterizer;
// Frame timing
frame_scheduler* frame_scheduler;
debug::performance_sampler* performance_sampler;
// Events
event_dispatcher* event_dispatcher;
@ -331,4 +242,9 @@ inline event_dispatcher* application::get_event_dispatcher()
return event_dispatcher;
}
inline bool application::was_closed() const
{
return closed;
}
#endif // ANTKEEPER_APPLICATION_HPP

+ 1
- 1
src/debug/console-commands.cpp View File

@ -32,7 +32,7 @@ std::string echo(std::string text)
std::string exit(game::context* ctx)
{
ctx->app->close(EXIT_SUCCESS);
ctx->app->close();
return std::string();
}

+ 40
- 40
src/game/context.hpp View File

@ -52,6 +52,11 @@
#include "render/material-property.hpp"
#include "ui/mouse-tracker.hpp"
#include "application.hpp"
#include "game/state/base.hpp"
#include "game/loop.hpp"
#include "state-machine.hpp"
#include "debug/performance-sampler.hpp"
#include <filesystem>
// Forward declarations
class animator;
@ -114,35 +119,50 @@ namespace game {
/// Container for data that is shared between game states.
struct context
{
application* app;
/// Hierarchichal state machine
hsm::state_machine<game::state::base> state_machine;
std::function<void()> resume_callback;
/// Debugging
debug::logger* logger;
std::ofstream log_filestream;
debug::performance_sampler performance_sampler;
debug::cli* cli;
// Command-line options
std::optional<bool> option_continue;
std::optional<std::string> option_data;
std::optional<bool> option_fullscreen;
std::optional<bool> option_new_game;
std::optional<bool> option_quick_start;
std::optional<bool> option_reset;
std::optional<int> option_v_sync;
std::optional<bool> option_windowed;
/// Queue for scheduling "next frame" function calls
std::queue<std::function<void()>> function_queue;
// Paths
std::string data_path;
std::string config_path;
std::string mods_path;
std::string saves_path;
std::string screenshots_path;
std::string controls_path;
std::string data_package_path;
// Parallel processes
std::unordered_map<std::string, std::function<void(double, double)>> processes;
// Configuration
json* config;
/// Interface for window management and input events
application* app;
// Controls
input::event_router* input_event_router;
input::mapper* input_mapper;
input::listener* input_listener;
std::unordered_map<std::string, input::control*> controls;
bool mouse_look;
/// Game loop
game::loop loop;
// Paths
std::filesystem::path data_path;
std::filesystem::path config_path;
std::filesystem::path mods_path;
std::filesystem::path saves_path;
std::filesystem::path screenshots_path;
std::filesystem::path controls_path;
std::filesystem::path data_package_path;
// Resources
resource_manager* resource_manager;
// Configuration
json* config;
// Localization
std::string language_code;
int language_count;
@ -252,14 +272,6 @@ struct context
animation<float>* credits_scroll_animation;
animation<float>* menu_bg_fade_in_animation;
animation<float>* menu_bg_fade_out_animation;
// Controls
input::event_router* input_event_router;
input::mapper* input_mapper;
input::listener* input_listener;
std::unordered_map<std::string, input::control*> controls;
bool mouse_look;
// Sound
float master_volume;
@ -268,9 +280,6 @@ struct context
bool mono_audio;
bool captions;
float captions_size;
// Parallel processes
std::unordered_map<std::string, std::function<void(double, double)>> processes;
// Entities
entity::registry* entity_registry;
@ -295,15 +304,6 @@ struct context
entity::system::astronomy* astronomy_system;
entity::system::orbit* orbit_system;
entity::system::proteome* proteome_system;
// State management
std::optional<application::state> paused_state;
// Misc
std::queue<std::function<void()>> function_queue;
// Debug
debug::cli* cli;
};
} // namespace game

+ 78
- 72
src/game/controls.cpp View File

@ -25,9 +25,9 @@
namespace game {
std::string gamepad_calibration_path(const game::context* ctx, const input::gamepad* gamepad)
std::filesystem::path gamepad_calibration_path(const game::context& ctx, const input::gamepad& gamepad)
{
return "gamepad-" + gamepad->get_guid() + ".json";
return std::filesystem::path("gamepad-" + gamepad.get_guid() + ".json");
}
json default_control_profile()
@ -65,25 +65,25 @@ json default_gamepad_calibration()
return calibration;
}
json* load_gamepad_calibration(game::context* ctx, input::gamepad* gamepad)
json* load_gamepad_calibration(game::context& ctx, const input::gamepad& gamepad)
{
// Determine path to gamepad calibration file
std::string filepath = gamepad_calibration_path(ctx, gamepad);
std::filesystem::path path = gamepad_calibration_path(ctx, gamepad);
// Load gamepad calibration file
json* calibration = ctx->resource_manager->load<json>(filepath);
json* calibration = ctx.resource_manager->load<json>(path.string());
return calibration;
}
bool save_gamepad_calibration(const game::context* ctx, const input::gamepad* gamepad, const json& calibration)
bool save_gamepad_calibration(const game::context& ctx, const input::gamepad& gamepad, const json& calibration)
{
// Determine path to gamepad calibration file
std::string filepath = ctx->controls_path + gamepad_calibration_path(ctx, gamepad);
// Determine absolute path to gamepad calibration file
std::filesystem::path path = ctx.controls_path / gamepad_calibration_path(ctx, gamepad);
// Open calibration file
std::ofstream stream;
stream.open(filepath);
stream.open(path);
if (!stream)
return false;
@ -101,7 +101,7 @@ bool save_gamepad_calibration(const game::context* ctx, const input::gamepad* ga
return true;
}
void apply_control_profile(game::context* ctx, const json& profile)
void apply_control_profile(game::context& ctx, const json& profile)
{
// Map gamepad buttons to strings
const std::unordered_map<std::string, input::gamepad_button> gamepad_button_map =
@ -135,14 +135,14 @@ void apply_control_profile(game::context* ctx, const json& profile)
};
// Remove all existing input mappings
for (auto control = ctx->controls.begin(); control != ctx->controls.end(); ++control)
for (auto control = ctx.controls.begin(); control != ctx.controls.end(); ++control)
{
ctx->input_event_router->remove_mappings(control->second);
ctx.input_event_router->remove_mappings(control->second);
}
// Get keyboard and mouse devices
input::keyboard* keyboard = ctx->app->get_keyboard();
input::mouse* mouse = ctx->app->get_mouse();
input::keyboard* keyboard = ctx.app->get_keyboard();
input::mouse* mouse = ctx.app->get_mouse();
// Find profile gamepad device
input::gamepad* gamepad = nullptr;
@ -153,7 +153,7 @@ void apply_control_profile(game::context* ctx, const json& profile)
const std::string gamepad_guid = gamepad_element->get<std::string>();
// Find gamepad with matching GUID
for (input::gamepad* device: ctx->app->get_gamepads())
for (input::gamepad* device: ctx.app->get_gamepads())
{
if (device->get_guid() == gamepad_guid)
{
@ -175,14 +175,14 @@ void apply_control_profile(game::context* ctx, const json& profile)
// Find or create control
input::control* control;
if (ctx->controls.count(control_name))
if (ctx.controls.count(control_name))
{
control = ctx->controls[control_name];
control = ctx.controls[control_name];
}
else
{
control = new input::control();
ctx->controls[control_name] = control;
ctx.controls[control_name] = control;
}
// For each mapping in the control
@ -190,7 +190,7 @@ void apply_control_profile(game::context* ctx, const json& profile)
{
if (!mapping_element->contains("device"))
{
ctx->logger->warning("Control \"" + control_name + "\" not mapped to a device");
ctx.logger->warning("Control \"" + control_name + "\" not mapped to a device");
continue;
}
@ -202,7 +202,7 @@ void apply_control_profile(game::context* ctx, const json& profile)
// Parse key name
if (!mapping_element->contains("key"))
{
ctx->logger->warning("Control \"" + control_name + "\" has invalid keyboard mapping");
ctx.logger->warning("Control \"" + control_name + "\" has invalid keyboard mapping");
continue;
}
std::string key = (*mapping_element)["key"].get<std::string>();
@ -211,14 +211,14 @@ void apply_control_profile(game::context* ctx, const json& profile)
input::scancode scancode = keyboard->get_scancode_from_name(key.c_str());
if (scancode == input::scancode::unknown)
{
ctx->logger->warning("Control \"" + control_name + "\" mapped to unknown keyboard key \"" + key + "\"");
ctx.logger->warning("Control \"" + control_name + "\" mapped to unknown keyboard key \"" + key + "\"");
continue;
}
// Map control to keyboard key
ctx->input_event_router->add_mapping(input::key_mapping(control, keyboard, scancode));
ctx.input_event_router->add_mapping(input::key_mapping(control, keyboard, scancode));
ctx->logger->log("Mapped control \"" + control_name + "\" to keyboard key \"" + key + "\"");
ctx.logger->log("Mapped control \"" + control_name + "\" to keyboard key \"" + key + "\"");
}
else if (device == "mouse")
{
@ -228,9 +228,9 @@ void apply_control_profile(game::context* ctx, const json& profile)
int button = (*mapping_element)["button"].get<int>();
// Map control to mouse button
ctx->input_event_router->add_mapping(input::mouse_button_mapping(control, mouse, button));
ctx.input_event_router->add_mapping(input::mouse_button_mapping(control, mouse, button));
ctx->logger->log("Mapped control \"" + control_name + "\" to mouse button " + std::to_string(button));
ctx.logger->log("Mapped control \"" + control_name + "\" to mouse button " + std::to_string(button));
}
else if (mapping_element->contains("wheel"))
{
@ -247,14 +247,14 @@ void apply_control_profile(game::context* ctx, const json& profile)
axis = input::mouse_wheel_axis::negative_y;
else
{
ctx->logger->warning("Control \"" + control_name + "\" is mapped to invalid mouse wheel axis \"" + wheel + "\"");
ctx.logger->warning("Control \"" + control_name + "\" is mapped to invalid mouse wheel axis \"" + wheel + "\"");
continue;
}
// Map control to mouse wheel axis
ctx->input_event_router->add_mapping(input::mouse_wheel_mapping(control, mouse, axis));
ctx.input_event_router->add_mapping(input::mouse_wheel_mapping(control, mouse, axis));
ctx->logger->log("Mapped control \"" + control_name + "\" to mouse wheel axis " + wheel);
ctx.logger->log("Mapped control \"" + control_name + "\" to mouse wheel axis " + wheel);
}
else if (mapping_element->contains("motion"))
{
@ -270,18 +270,18 @@ void apply_control_profile(game::context* ctx, const json& profile)
axis = input::mouse_motion_axis::negative_y;
else
{
ctx->logger->warning("Control \"" + control_name + "\" is mapped to invalid mouse motion axis \"" + motion + "\"");
ctx.logger->warning("Control \"" + control_name + "\" is mapped to invalid mouse motion axis \"" + motion + "\"");
continue;
}
// Map control to mouse motion axis
ctx->input_event_router->add_mapping(input::mouse_motion_mapping(control, mouse, axis));
ctx.input_event_router->add_mapping(input::mouse_motion_mapping(control, mouse, axis));
ctx->logger->log("Mapped control \"" + control_name + "\" to mouse motion axis " + motion);
ctx.logger->log("Mapped control \"" + control_name + "\" to mouse motion axis " + motion);
}
else
{
ctx->logger->warning("Control \"" + control_name + "\" has invalid mouse mapping");
ctx.logger->warning("Control \"" + control_name + "\" has invalid mouse mapping");
continue;
}
}
@ -295,14 +295,14 @@ void apply_control_profile(game::context* ctx, const json& profile)
auto button_it = gamepad_button_map.find(button);
if (button_it == gamepad_button_map.end())
{
ctx->logger->warning("Control \"" + control_name + "\" is mapped to invalid gamepad button \"" + button + "\"");
ctx.logger->warning("Control \"" + control_name + "\" is mapped to invalid gamepad button \"" + button + "\"");
continue;
}
// Map control to gamepad button
ctx->input_event_router->add_mapping(input::gamepad_button_mapping(control, gamepad, button_it->second));
ctx.input_event_router->add_mapping(input::gamepad_button_mapping(control, gamepad, button_it->second));
ctx->logger->log("Mapped control \"" + control_name + "\" to gamepad button " + button);
ctx.logger->log("Mapped control \"" + control_name + "\" to gamepad button " + button);
}
else if (mapping_element->contains("axis"))
{
@ -313,7 +313,7 @@ void apply_control_profile(game::context* ctx, const json& profile)
auto axis_it = gamepad_axis_map.find(axis_name);
if (axis_it == gamepad_axis_map.end())
{
ctx->logger->warning("Control \"" + control_name + "\" is mapped to invalid gamepad axis \"" + axis_name + "\"");
ctx.logger->warning("Control \"" + control_name + "\" is mapped to invalid gamepad axis \"" + axis_name + "\"");
continue;
}
@ -321,38 +321,38 @@ void apply_control_profile(game::context* ctx, const json& profile)
const char axis_sign = axis.back();
if (axis_sign != '-' && axis_sign != '+')
{
ctx->logger->warning("Control \"" + control_name + "\" is mapped to gamepad axis with invalid sign \"" + axis_sign + "\"");
ctx.logger->warning("Control \"" + control_name + "\" is mapped to gamepad axis with invalid sign \"" + axis_sign + "\"");
continue;
}
bool axis_negative = (axis_sign == '-');
// Map control to gamepad axis
ctx->input_event_router->add_mapping(input::gamepad_axis_mapping(control, gamepad, axis_it->second, axis_negative));
ctx.input_event_router->add_mapping(input::gamepad_axis_mapping(control, gamepad, axis_it->second, axis_negative));
ctx->logger->log("Mapped control \"" + control_name + "\" to gamepad axis " + axis);
ctx.logger->log("Mapped control \"" + control_name + "\" to gamepad axis " + axis);
}
else
{
ctx->logger->log("Control \"" + control_name + "\" has invalid gamepad mapping");
ctx.logger->log("Control \"" + control_name + "\" has invalid gamepad mapping");
continue;
}
}
else
{
ctx->logger->warning("Control \"" + control_name + "\" bound to unknown device \"" + device + "\"");
ctx.logger->warning("Control \"" + control_name + "\" bound to unknown device \"" + device + "\"");
}
}
}
}
}
void save_control_profile(game::context* ctx)
void save_control_profile(game::context& ctx)
{
std::string control_profile_path;
if (ctx->config->contains("control_profile"))
control_profile_path = ctx->config_path + "controls/" + (*ctx->config)["control_profile"].get<std::string>();
std::filesystem::path path;
if (ctx.config->contains("control_profile"))
path = ctx.config_path / "controls" / (*ctx.config)["control_profile"].get<std::string>();
ctx->logger->push_task("Saving control profile to \"" + control_profile_path + "\"");
ctx.logger->push_task("Saving control profile to \"" + path.string() + "\"");
try
{
json control_profile;
@ -361,8 +361,8 @@ void save_control_profile(game::context* ctx)
auto& controls_element = control_profile["controls"];
controls_element = json::object();
for (auto controls_it = ctx->controls.begin(); controls_it != ctx->controls.end(); ++controls_it)
{
for (auto controls_it = ctx.controls.begin(); controls_it != ctx.controls.end(); ++controls_it)
{
const std::string& control_name = controls_it->first;
input::control* control = controls_it->second;
@ -370,8 +370,14 @@ void save_control_profile(game::context* ctx)
auto& control_element = controls_element[control_name];
control_element = json::array();
// Add control mappings
auto mappings = ctx->input_event_router->get_mappings(control);
// Get control mappings
auto mappings = ctx.input_event_router->get_mappings(control);
// Skip unmapped controls
if (!mappings)
continue;
// Add control mapping elements
for (input::mapping* mapping: *mappings)
{
json mapping_element;
@ -454,7 +460,7 @@ void save_control_profile(game::context* ctx)
if (axis_mapping->negative)
mapping_element["axis"] = "leftx-";
else
mapping_element["axis+"] = "leftx+";
mapping_element["axis"] = "leftx+";
break;
case input::gamepad_axis::left_y:
if (axis_mapping->negative)
@ -466,7 +472,7 @@ void save_control_profile(game::context* ctx)
if (axis_mapping->negative)
mapping_element["axis"] = "rightx-";
else
mapping_element["axis+"] = "rightx+";
mapping_element["axis"] = "rightx+";
break;
case input::gamepad_axis::right_y:
if (axis_mapping->negative)
@ -552,65 +558,65 @@ void save_control_profile(game::context* ctx)
}
}
std::ofstream control_profile_file(control_profile_path);
std::ofstream control_profile_file(path);
control_profile_file << control_profile;
}
catch (...)
{
ctx->logger->pop_task(EXIT_FAILURE);
ctx.logger->pop_task(EXIT_FAILURE);
}
ctx->logger->pop_task(EXIT_SUCCESS);
ctx.logger->pop_task(EXIT_SUCCESS);
}
void apply_gamepad_calibration(input::gamepad* gamepad, const json& calibration)
void apply_gamepad_calibration(input::gamepad& gamepad, const json& calibration)
{
// Parse and apply activation thresholds
if (calibration.contains("leftx_activation"))
{
float min = calibration["leftx_activation"][0].get<float>();
float max = calibration["leftx_activation"][1].get<float>();
gamepad->set_activation_threshold(input::gamepad_axis::left_x, min, max);
gamepad.set_activation_threshold(input::gamepad_axis::left_x, min, max);
}
if (calibration.contains("lefty_activation"))
{
float min = calibration["lefty_activation"][0].get<float>();
float max = calibration["lefty_activation"][1].get<float>();
gamepad->set_activation_threshold(input::gamepad_axis::left_y, min, max);
gamepad.set_activation_threshold(input::gamepad_axis::left_y, min, max);
}
if (calibration.contains("rightx_activation"))
{
float min = calibration["rightx_activation"][0].get<float>();
float max = calibration["rightx_activation"][1].get<float>();
gamepad->set_activation_threshold(input::gamepad_axis::right_x, min, max);
gamepad.set_activation_threshold(input::gamepad_axis::right_x, min, max);
}
if (calibration.contains("righty_activation"))
{
float min = calibration["righty_activation"][0].get<float>();
float max = calibration["righty_activation"][1].get<float>();
gamepad->set_activation_threshold(input::gamepad_axis::right_y, min, max);
gamepad.set_activation_threshold(input::gamepad_axis::right_y, min, max);
}
if (calibration.contains("lefttrigger_activation"))
{
float min = calibration["lefttrigger_activation"][0].get<float>();
float max = calibration["lefttrigger_activation"][1].get<float>();
gamepad->set_activation_threshold(input::gamepad_axis::left_trigger, min, max);
gamepad.set_activation_threshold(input::gamepad_axis::left_trigger, min, max);
}
if (calibration.contains("righttrigger_activation"))
{
float min = calibration["righttrigger_activation"][0].get<float>();
float max = calibration["righttrigger_activation"][1].get<float>();
gamepad->set_activation_threshold(input::gamepad_axis::right_trigger, min, max);
gamepad.set_activation_threshold(input::gamepad_axis::right_trigger, min, max);
}
// Parse and apply deadzone shapes
if (calibration.contains("left_deadzone_cross"))
gamepad->set_left_deadzone_cross(calibration["left_deadzone_cross"].get<bool>());
gamepad.set_left_deadzone_cross(calibration["left_deadzone_cross"].get<bool>());
if (calibration.contains("right_deadzone_cross"))
gamepad->set_right_deadzone_cross(calibration["right_deadzone_cross"].get<bool>());
gamepad.set_right_deadzone_cross(calibration["right_deadzone_cross"].get<bool>());
if (calibration.contains("left_deadzone_roundness"))
gamepad->set_left_deadzone_roundness(calibration["left_deadzone_roundness"].get<float>());
gamepad.set_left_deadzone_roundness(calibration["left_deadzone_roundness"].get<float>());
if (calibration.contains("right_deadzone_roundness"))
gamepad->set_right_deadzone_roundness(calibration["right_deadzone_roundness"].get<float>());
gamepad.set_right_deadzone_roundness(calibration["right_deadzone_roundness"].get<float>());
auto parse_response_curve = [](const std::string& curve) -> input::gamepad_response_curve
{
@ -625,32 +631,32 @@ void apply_gamepad_calibration(input::gamepad* gamepad, const json& calibration)
if (calibration.contains("leftx_response_curve"))
{
auto curve = parse_response_curve(calibration["leftx_response_curve"].get<std::string>());
gamepad->set_response_curve(input::gamepad_axis::left_x, curve);
gamepad.set_response_curve(input::gamepad_axis::left_x, curve);
}
if (calibration.contains("lefty_response_curve"))
{
auto curve = parse_response_curve(calibration["lefty_response_curve"].get<std::string>());
gamepad->set_response_curve(input::gamepad_axis::left_y, curve);
gamepad.set_response_curve(input::gamepad_axis::left_y, curve);
}
if (calibration.contains("rightx_response_curve"))
{
auto curve = parse_response_curve(calibration["rightx_response_curve"].get<std::string>());
gamepad->set_response_curve(input::gamepad_axis::right_x, curve);
gamepad.set_response_curve(input::gamepad_axis::right_x, curve);
}
if (calibration.contains("righty_response_curve"))
{
auto curve = parse_response_curve(calibration["righty_response_curve"].get<std::string>());
gamepad->set_response_curve(input::gamepad_axis::right_y, curve);
gamepad.set_response_curve(input::gamepad_axis::right_y, curve);
}
if (calibration.contains("lefttrigger_response_curve"))
{
auto curve = parse_response_curve(calibration["lefttrigger_response_curve"].get<std::string>());
gamepad->set_response_curve(input::gamepad_axis::left_trigger, curve);
gamepad.set_response_curve(input::gamepad_axis::left_trigger, curve);
}
if (calibration.contains("righttrigger_response_curve"))
{
auto curve = parse_response_curve(calibration["righttrigger_response_curve"].get<std::string>());
gamepad->set_response_curve(input::gamepad_axis::right_trigger, curve);
gamepad.set_response_curve(input::gamepad_axis::right_trigger, curve);
}
}

+ 7
- 6
src/game/controls.hpp View File

@ -23,6 +23,7 @@
#include "game/context.hpp"
#include "resources/json.hpp"
#include "input/gamepad.hpp"
#include <filesystem>
namespace game {
@ -32,14 +33,14 @@ namespace game {
* @param ctx Game context.
* @param profile Control profile.
*/
void apply_control_profile(game::context* ctx, const json& profile);
void apply_control_profile(game::context& ctx, const json& profile);
/**
* Saves the current control profile.
*
* @param ctx Game context.
*/
void save_control_profile(game::context* ctx);
void save_control_profile(game::context& ctx);
/**
* Generates a default control profile.
@ -51,7 +52,7 @@ json default_control_profile();
/**
* Returns a string containing the path to the gamepad calibration file.
*/
std::string gamepad_calibration_path(const game::context* ctx, const input::gamepad* gamepad);
std::filesystem::path gamepad_calibration_path(const game::context& ctx, const input::gamepad& gamepad);
/**
* Generates default gamepad calibration settings.
@ -67,7 +68,7 @@ json default_gamepad_calibration();
* @param gamepad Gamepad for which to load calibration settings.
* @return Gamepad calibration settings, or `nullptr` if not loaded.
*/
json* load_gamepad_calibration(game::context* ctx, input::gamepad* gamepad);
json* load_gamepad_calibration(game::context& ctx, const input::gamepad& gamepad);
/**
* Saves gamepad calibration settings.
@ -76,7 +77,7 @@ json* load_gamepad_calibration(game::context* ctx, input::gamepad* gamepad);
* @param gamepad Gamepad for which to save calibration settings.
* @return `true` if calibration settings were successfully saved, `false` otherwise.
*/
bool save_gamepad_calibration(const game::context* ctx, const input::gamepad* gamepad, const json& calibration);
bool save_gamepad_calibration(const game::context& ctx, const input::gamepad& gamepad, const json& calibration);
/**
* Applies gamepad calibration settings.
@ -84,7 +85,7 @@ bool save_gamepad_calibration(const game::context* ctx, const input::gamepad* ga
* @param gamepad Gamepad to calibrate.
* @param calibration JSON element containing gamepad calibration settings.
*/
void apply_gamepad_calibration(input::gamepad* gamepad, const json& calibration);
void apply_gamepad_calibration(input::gamepad& gamepad, const json& calibration);
} // namespace game

+ 31
- 31
src/game/fonts.cpp View File

@ -66,16 +66,16 @@ static void build_bitmap_font(const type::typeface& typeface, float size, const
material.set_shader_program(shader_program);
}
void load_fonts(game::context* ctx)
void load_fonts(game::context& ctx)
{
// Load dyslexia-friendly typeface (if enabled)
bool dyslexia_font_loaded = false;
if (ctx->dyslexia_font)
if (ctx.dyslexia_font)
{
if (auto it = ctx->strings->find("font_dyslexia"); it != ctx->strings->end() && !it->second.empty() && it->second[0] != '#')
if (auto it = ctx.strings->find("font_dyslexia"); it != ctx.strings->end() && !it->second.empty() && it->second[0] != '#')
{
ctx->logger->log(it->second);
ctx->typefaces["dyslexia"] = ctx->resource_manager->load<type::typeface>(it->second);
ctx.logger->log(it->second);
ctx.typefaces["dyslexia"] = ctx.resource_manager->load<type::typeface>(it->second);
dyslexia_font_loaded = true;
}
}
@ -84,19 +84,19 @@ void load_fonts(game::context* ctx)
if (dyslexia_font_loaded)
{
// Override standard typefaces with dyslexia-friendly typeface
ctx->typefaces["serif"] = ctx->typefaces["dyslexia"];
ctx->typefaces["sans_serif"] = ctx->typefaces["dyslexia"];
ctx->typefaces["monospace"] = ctx->typefaces["dyslexia"];
ctx.typefaces["serif"] = ctx.typefaces["dyslexia"];
ctx.typefaces["sans_serif"] = ctx.typefaces["dyslexia"];
ctx.typefaces["monospace"] = ctx.typefaces["dyslexia"];
}
else
{
// Load standard typefaces
if (auto it = ctx->strings->find("font_serif"); it != ctx->strings->end())
ctx->typefaces["serif"] = ctx->resource_manager->load<type::typeface>(it->second);
if (auto it = ctx->strings->find("font_sans_serif"); it != ctx->strings->end())
ctx->typefaces["sans_serif"] = ctx->resource_manager->load<type::typeface>(it->second);
if (auto it = ctx->strings->find("font_monospace"); it != ctx->strings->end())
ctx->typefaces["monospace"] = ctx->resource_manager->load<type::typeface>(it->second);
if (auto it = ctx.strings->find("font_serif"); it != ctx.strings->end())
ctx.typefaces["serif"] = ctx.resource_manager->load<type::typeface>(it->second);
if (auto it = ctx.strings->find("font_sans_serif"); it != ctx.strings->end())
ctx.typefaces["sans_serif"] = ctx.resource_manager->load<type::typeface>(it->second);
if (auto it = ctx.strings->find("font_monospace"); it != ctx.strings->end())
ctx.typefaces["monospace"] = ctx.resource_manager->load<type::typeface>(it->second);
}
// Build character set
@ -107,7 +107,7 @@ void load_fonts(game::context* ctx)
charset.insert(code);
// Add all character codes from game strings
for (auto it = ctx->strings->begin(); it != ctx->strings->end(); ++it)
for (auto it = ctx.strings->begin(); it != ctx.strings->end(); ++it)
{
// Convert UTF-8 string to UTF-32
std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> convert;
@ -120,49 +120,49 @@ void load_fonts(game::context* ctx)
}
// Load bitmap font shader
gl::shader_program* bitmap_font_shader = ctx->resource_manager->load<gl::shader_program>("bitmap-font.glsl");
gl::shader_program* bitmap_font_shader = ctx.resource_manager->load<gl::shader_program>("bitmap-font.glsl");
// Determine font point sizes
float debug_font_size_pt = 12.0f;
float menu_font_size_pt = 12.0f;
float title_font_size_pt = 12.0f;
if (ctx->config->contains("debug_font_size"))
debug_font_size_pt = (*ctx->config)["debug_font_size"].get<float>();
if (ctx->config->contains("menu_font_size"))
menu_font_size_pt = (*ctx->config)["menu_font_size"].get<float>();
if (ctx->config->contains("title_font_size"))
title_font_size_pt = (*ctx->config)["title_font_size"].get<float>();
if (ctx.config->contains("debug_font_size"))
debug_font_size_pt = (*ctx.config)["debug_font_size"].get<float>();
if (ctx.config->contains("menu_font_size"))
menu_font_size_pt = (*ctx.config)["menu_font_size"].get<float>();
if (ctx.config->contains("title_font_size"))
title_font_size_pt = (*ctx.config)["title_font_size"].get<float>();
// Scale font point sizes
const float font_size = (*ctx->config)["font_size"].get<float>();
const float font_size = (*ctx.config)["font_size"].get<float>();
debug_font_size_pt *= font_size;
menu_font_size_pt *= font_size;
title_font_size_pt *= font_size;
// Convert font point sizes to pixel sizes
const float dpi = ctx->app->get_display_dpi();
const float dpi = ctx.app->get_display_dpi();
const float debug_font_size_px = (debug_font_size_pt * dpi) / 72.0f;
const float menu_font_size_px = (menu_font_size_pt * dpi) / 72.0f;
const float title_font_size_px = (title_font_size_pt * dpi) / 72.0f;
ctx->logger->log("font size: " + std::to_string(menu_font_size_px));
ctx.logger->log("font size: " + std::to_string(menu_font_size_px));
// Build debug font
if (auto it = ctx->typefaces.find("monospace"); it != ctx->typefaces.end())
if (auto it = ctx.typefaces.find("monospace"); it != ctx.typefaces.end())
{
build_bitmap_font(*it->second, debug_font_size_px, charset, ctx->debug_font, ctx->debug_font_material, bitmap_font_shader);
build_bitmap_font(*it->second, debug_font_size_px, charset, ctx.debug_font, ctx.debug_font_material, bitmap_font_shader);
}
// Build menu font
if (auto it = ctx->typefaces.find("sans_serif"); it != ctx->typefaces.end())
if (auto it = ctx.typefaces.find("sans_serif"); it != ctx.typefaces.end())
{
build_bitmap_font(*it->second, menu_font_size_px, charset, ctx->menu_font, ctx->menu_font_material, bitmap_font_shader);
build_bitmap_font(*it->second, menu_font_size_px, charset, ctx.menu_font, ctx.menu_font_material, bitmap_font_shader);
}
// Build title font
if (auto it = ctx->typefaces.find("serif"); it != ctx->typefaces.end())
if (auto it = ctx.typefaces.find("serif"); it != ctx.typefaces.end())
{
build_bitmap_font(*it->second, title_font_size_px, charset, ctx->title_font, ctx->title_font_material, bitmap_font_shader);
build_bitmap_font(*it->second, title_font_size_px, charset, ctx.title_font, ctx.title_font_material, bitmap_font_shader);
}
}

+ 1
- 1
src/game/fonts.hpp View File

@ -24,7 +24,7 @@
namespace game {
void load_fonts(game::context* ctx);
void load_fonts(game::context& ctx);
} // namespace game

+ 37
- 0
src/game/graphics.cpp View File

@ -23,6 +23,11 @@
#include "gl/texture-wrapping.hpp"
#include "gl/texture-filter.hpp"
#include "debug/logger.hpp"
#include "utility/timestamp.hpp"
#include <glad/glad.h>
#include <stb/stb_image_write.h>
#include <thread>
#include <filesystem>
namespace game {
namespace graphics {
@ -141,5 +146,37 @@ void resize_framebuffer_attachment(gl::texture_2d& texture, const int2& resoluti
texture.resize(resolution.x, resolution.y, texture.get_pixel_type(), texture.get_pixel_format(), texture.get_color_space(), nullptr);
}
void save_screenshot(game::context& ctx)
{
// Determine screenshot path
std::string filename = "antkeeper-" + timestamp() + ".png";
std::filesystem::path path = ctx.config_path / "gallery" / filename;
ctx.logger->push_task("Saving screenshot to \"" + path.string() + "\"");
const int2& viewport_dimensions = ctx.app->get_viewport_dimensions();
// Allocate image
std::shared_ptr<image> frame = std::make_shared<image>();
frame->format(1, 3);
frame->resize(viewport_dimensions.x, viewport_dimensions.y);
// Read pixel data from backbuffer into image
glReadBuffer(GL_BACK);
glReadPixels(0, 0, viewport_dimensions.x, viewport_dimensions.y, GL_RGB, GL_UNSIGNED_BYTE, frame->get_pixels());
// Write screenshot file in separate thread
std::thread
(
[frame, path]
{
stbi_flip_vertically_on_write(1);
stbi_write_png(path.string().c_str(), frame->get_width(), frame->get_height(), frame->get_channel_count(), frame->get_pixels(), frame->get_width() * frame->get_channel_count());
}
).detach();
ctx.logger->pop_task(EXIT_SUCCESS);
}
} // namespace graphics
} // namespace game

+ 1
- 0
src/game/graphics.hpp View File

@ -28,6 +28,7 @@ namespace graphics {
void create_framebuffers(game::context& ctx);
void destroy_framebuffers(game::context& ctx);
void change_render_resolution(game::context& ctx, float scale);
void save_screenshot(game::context& ctx);
} // namespace graphics
} // namespace game

src/animation/frame-scheduler.cpp → src/game/loop.cpp View File

@ -17,10 +17,12 @@
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#include "frame-scheduler.hpp"
#include "game/loop.hpp"
#include <algorithm>
frame_scheduler::frame_scheduler():
namespace game {
loop::loop():
update_callback([](double, double){}),
render_callback([](double){}),
update_rate(60.0),
@ -30,33 +32,33 @@ frame_scheduler::frame_scheduler():
reset();
}
void frame_scheduler::set_update_callback(std::function<void(double, double)> callback)
void loop::set_update_callback(std::function<void(double, double)> callback)
{
update_callback = callback;
}
void frame_scheduler::set_render_callback(std::function<void(double)> callback)
void loop::set_render_callback(std::function<void(double)> callback)
{
render_callback = callback;
}
void frame_scheduler::set_update_rate(double frequency)
void loop::set_update_rate(double frequency)
{
update_rate = frequency;
update_timestep = 1.0 / frequency;
}
void frame_scheduler::set_max_frame_duration(double duration)
void loop::set_max_frame_duration(double duration)
{
max_frame_duration = duration;
}
double frame_scheduler::get_frame_duration() const
double loop::get_frame_duration() const
{
return frame_duration;
}
void frame_scheduler::reset()
void loop::reset()
{
elapsed_time = 0.0;
accumulator = 0.0;
@ -65,10 +67,10 @@ void frame_scheduler::reset()
frame_duration = 0.0;
}
void frame_scheduler::tick()
void loop::tick()
{
frame_end = std::chrono::high_resolution_clock::now();
frame_duration = static_cast<double>(std::chrono::duration_cast<std::chrono::microseconds>(frame_end - frame_start).count()) / 1000000.0;
frame_duration = static_cast<double>(std::chrono::duration_cast<std::chrono::nanoseconds>(frame_end - frame_start).count()) / 1000000000.0;
frame_start = frame_end;
accumulator += std::min<double>(max_frame_duration, frame_duration);
@ -82,3 +84,5 @@ void frame_scheduler::tick()
render_callback(accumulator * update_rate);
}
} // namespace game

src/animation/frame-scheduler.hpp → src/game/loop.hpp View File

@ -17,26 +17,28 @@
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_FRAME_SCHEDULER_HPP
#define ANTKEEPER_FRAME_SCHEDULER_HPP
#ifndef ANTKEEPER_GAME_LOOP_HPP
#define ANTKEEPER_GAME_LOOP_HPP
#include <chrono>
#include <functional>
namespace game {
/**
* Schedules fixed-timestep update calls and variable timestep render calls.
* Game loop with fixed timestep update calls and variable timestep render calls.
*
* @see https://gafferongames.com/post/fix_your_timestep/
*/
class frame_scheduler
class loop
{
public:
frame_scheduler();
loop();
/**
* Sets the update callback.
*
* @param callback Function which takes two parameters: `t`, the total elapsed time, and `dt`, delta time (timestep) which is calculated as `1.0 / update_rate`. This function will be called at the frequency specified by `frame_scheduler::set_update_rate()`.
* @param callback Function which takes two parameters: `t`, the total elapsed time, and `dt`, delta time (timestep) which is calculated as `1.0 / update_rate`. This function will be called at the frequency specified by `loop::set_update_rate()`.
*/
void set_update_callback(std::function<void(double, double)> callback);
@ -89,5 +91,6 @@ private:
double frame_duration;
};
#endif // ANTKEEPER_FRAME_SCHEDULER_HPP
} // namespace game
#endif // ANTKEEPER_GAME_LOOP_HPP

+ 132
- 132
src/game/menu.cpp View File

@ -28,41 +28,41 @@
namespace game {
namespace menu {
void init_menu_item_index(game::context* ctx, const std::string& menu_name)
void init_menu_item_index(game::context& ctx, const std::string& menu_name)
{
if (auto it = ctx->menu_item_indices.find(menu_name); it != ctx->menu_item_indices.end())
if (auto it = ctx.menu_item_indices.find(menu_name); it != ctx.menu_item_indices.end())
{
ctx->menu_item_index = &it->second;
ctx.menu_item_index = &it->second;
}
else
{
ctx->menu_item_index = &ctx->menu_item_indices[menu_name];
*ctx->menu_item_index = 0;
ctx.menu_item_index = &ctx.menu_item_indices[menu_name];
*ctx.menu_item_index = 0;
}
}
void update_text_font(game::context* ctx)
void update_text_font(game::context& ctx)
{
for (auto [name, value]: ctx->menu_item_texts)
for (auto [name, value]: ctx.menu_item_texts)
{
name->set_material(&ctx->menu_font_material);
name->set_font(&ctx->menu_font);
name->set_material(&ctx.menu_font_material);
name->set_font(&ctx.menu_font);
if (value)
{
value->set_material(&ctx->menu_font_material);
value->set_font(&ctx->menu_font);
value->set_material(&ctx.menu_font_material);
value->set_font(&ctx.menu_font);
}
}
}
void update_text_color(game::context* ctx)
void update_text_color(game::context& ctx)
{
for (std::size_t i = 0; i < ctx->menu_item_texts.size(); ++i)
for (std::size_t i = 0; i < ctx.menu_item_texts.size(); ++i)
{
auto [name, value] = ctx->menu_item_texts[i];
auto [name, value] = ctx.menu_item_texts[i];
const float4& color = (i == *ctx->menu_item_index) ? active_color : inactive_color;
const float4& color = (i == *ctx.menu_item_index) ? active_color : inactive_color;
name->set_color(color);
if (value)
@ -70,9 +70,9 @@ void update_text_color(game::context* ctx)
}
}
void update_text_tweens(game::context* ctx)
void update_text_tweens(game::context& ctx)
{
for (auto [name, value]: ctx->menu_item_texts)
for (auto [name, value]: ctx.menu_item_texts)
{
name->update_tweens();
if (value)
@ -80,13 +80,13 @@ void update_text_tweens(game::context* ctx)
}
}
void align_text(game::context* ctx, bool center, bool has_back, float anchor_y)
void align_text(game::context& ctx, bool center, bool has_back, float anchor_y)
{
// Calculate menu width
float menu_width = 0.0f;
float menu_spacing = ctx->menu_font.get_glyph_metrics(U'M').width;
float menu_spacing = ctx.menu_font.get_glyph_metrics(U'M').width;
for (auto [name, value]: ctx->menu_item_texts)
for (auto [name, value]: ctx.menu_item_texts)
{
float row_width = 0.0f;
@ -110,23 +110,23 @@ void align_text(game::context* ctx, bool center, bool has_back, float anchor_y)
// Align texts
float menu_height;
if (has_back)
menu_height = (ctx->menu_item_texts.size() - 1) * ctx->menu_font.get_font_metrics().linespace - ctx->menu_font.get_font_metrics().linegap;
menu_height = (ctx.menu_item_texts.size() - 1) * ctx.menu_font.get_font_metrics().linespace - ctx.menu_font.get_font_metrics().linegap;
else
menu_height = ctx->menu_item_texts.size() * ctx->menu_font.get_font_metrics().linespace - ctx->menu_font.get_font_metrics().linegap;
menu_height = ctx.menu_item_texts.size() * ctx.menu_font.get_font_metrics().linespace - ctx.menu_font.get_font_metrics().linegap;
float menu_x = -menu_width * 0.5f;
float menu_y = anchor_y + menu_height * 0.5f - ctx->menu_font.get_font_metrics().size;
float menu_y = anchor_y + menu_height * 0.5f - ctx.menu_font.get_font_metrics().size;
for (std::size_t i = 0; i < ctx->menu_item_texts.size(); ++i)
for (std::size_t i = 0; i < ctx.menu_item_texts.size(); ++i)
{
auto [name, value] = ctx->menu_item_texts[i];
auto [name, value] = ctx.menu_item_texts[i];
float x = menu_x;
float y = menu_y - ctx->menu_font.get_font_metrics().linespace * i;
if (has_back && i == ctx->menu_item_texts.size() - 1)
y -= ctx->menu_font.get_font_metrics().linespace;
float y = menu_y - ctx.menu_font.get_font_metrics().linespace * i;
if (has_back && i == ctx.menu_item_texts.size() - 1)
y -= ctx.menu_font.get_font_metrics().linespace;
if (center || i == ctx->menu_item_texts.size() - 1)
if (center || i == ctx.menu_item_texts.size() - 1)
{
const auto& name_bounds = static_cast<const geom::aabb<float>&>(name->get_local_bounds());
const float name_width = name_bounds.max_point.x - name_bounds.min_point.x;
@ -140,7 +140,7 @@ void align_text(game::context* ctx, bool center, bool has_back, float anchor_y)
const auto& value_bounds = static_cast<const geom::aabb<float>&>(value->get_local_bounds());
const float value_width = value_bounds.max_point.x - value_bounds.min_point.x;
if (center || i == ctx->menu_item_texts.size() - 1)
if (center || i == ctx.menu_item_texts.size() - 1)
x = -value_width * 0.5f;
else
x = menu_x + menu_width - value_width;
@ -150,9 +150,9 @@ void align_text(game::context* ctx, bool center, bool has_back, float anchor_y)
}
}
void refresh_text(game::context* ctx)
void refresh_text(game::context& ctx)
{
for (auto [name, value]: ctx->menu_item_texts)
for (auto [name, value]: ctx.menu_item_texts)
{
name->refresh();
if (value)
@ -160,67 +160,67 @@ void refresh_text(game::context* ctx)
}
}
void add_text_to_ui(game::context* ctx)
void add_text_to_ui(game::context& ctx)
{
for (auto [name, value]: ctx->menu_item_texts)
for (auto [name, value]: ctx.menu_item_texts)
{
ctx->ui_scene->add_object(name);
ctx.ui_scene->add_object(name);
if (value)
ctx->ui_scene->add_object(value);
ctx.ui_scene->add_object(value);
}
}
void remove_text_from_ui(game::context* ctx)
void remove_text_from_ui(game::context& ctx)
{
for (auto [name, value]: ctx->menu_item_texts)
for (auto [name, value]: ctx.menu_item_texts)
{
ctx->ui_scene->remove_object(name);
ctx.ui_scene->remove_object(name);
if (value)
ctx->ui_scene->remove_object(value);
ctx.ui_scene->remove_object(value);
}
}
void delete_text(game::context* ctx)
void delete_text(game::context& ctx)
{
for (auto [name, value]: ctx->menu_item_texts)
for (auto [name, value]: ctx.menu_item_texts)
{
delete name;
if (value)
delete value;
}
ctx->menu_item_texts.clear();
ctx.menu_item_texts.clear();
}
void delete_animations(game::context* ctx)
void delete_animations(game::context& 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);
delete ctx.menu_fade_animation;
ctx.menu_fade_animation = nullptr;
}
void clear_callbacks(game::context* ctx)
void clear_callbacks(game::context& ctx)
{
// Clear menu item callbacks
ctx->menu_left_callbacks.clear();
ctx->menu_right_callbacks.clear();
ctx->menu_select_callbacks.clear();
ctx->menu_back_callback = nullptr;
ctx.menu_left_callbacks.clear();
ctx.menu_right_callbacks.clear();
ctx.menu_select_callbacks.clear();
ctx.menu_back_callback = nullptr;
}
void setup_animations(game::context* ctx)
void setup_animations(game::context& ctx)
{
ctx->menu_fade_animation = new animation<float>();
animation_channel<float>* opacity_channel = ctx->menu_fade_animation->add_channel(0);
ctx.menu_fade_animation = new animation<float>();
animation_channel<float>* opacity_channel = ctx.menu_fade_animation->add_channel(0);
ctx->menu_fade_animation->set_frame_callback
ctx.menu_fade_animation->set_frame_callback
(
[ctx](int channel, const float& opacity)
[&ctx](int channel, const float& opacity)
{
for (std::size_t i = 0; i < ctx->menu_item_texts.size(); ++i)
for (std::size_t i = 0; i < ctx.menu_item_texts.size(); ++i)
{
auto [name, value] = ctx->menu_item_texts[i];
auto [name, value] = ctx.menu_item_texts[i];
float4 color = (i == *ctx->menu_item_index) ? active_color : inactive_color;
float4 color = (i == *ctx.menu_item_index) ? active_color : inactive_color;
color[3] = color[3] * opacity;
if (name)
@ -231,23 +231,23 @@ void setup_animations(game::context* ctx)
}
);
ctx->animator->add_animation(ctx->menu_fade_animation);
ctx.animator->add_animation(ctx.menu_fade_animation);
}
void fade_in(game::context* ctx, const std::function<void()>& end_callback)
void fade_in(game::context& ctx, const std::function<void()>& end_callback)
{
ctx->menu_fade_animation->set_interpolator(ease<float>::out_cubic);
animation_channel<float>* opacity_channel = ctx->menu_fade_animation->get_channel(0);
ctx.menu_fade_animation->set_interpolator(ease<float>::out_cubic);
animation_channel<float>* opacity_channel = ctx.menu_fade_animation->get_channel(0);
opacity_channel->remove_keyframes();
opacity_channel->insert_keyframe({0.0, 0.0f});
opacity_channel->insert_keyframe({game::menu::fade_in_duration, 1.0f});
ctx->menu_fade_animation->set_end_callback(end_callback);
ctx.menu_fade_animation->set_end_callback(end_callback);
for (std::size_t i = 0; i < ctx->menu_item_texts.size(); ++i)
for (std::size_t i = 0; i < ctx.menu_item_texts.size(); ++i)
{
auto [name, value] = ctx->menu_item_texts[i];
auto [name, value] = ctx.menu_item_texts[i];
float4 color = (i == *ctx->menu_item_index) ? active_color : inactive_color;
float4 color = (i == *ctx.menu_item_index) ? active_color : inactive_color;
color[3] = 0.0f;
if (name)
@ -262,106 +262,106 @@ void fade_in(game::context* ctx, const std::function& end_callback)
}
}
ctx->menu_fade_animation->stop();
ctx->menu_fade_animation->play();
ctx.menu_fade_animation->stop();
ctx.menu_fade_animation->play();
}
void fade_out(game::context* ctx, const std::function<void()>& end_callback)
void fade_out(game::context& ctx, const std::function<void()>& end_callback)
{
ctx->menu_fade_animation->set_interpolator(ease<float>::out_cubic);
animation_channel<float>* opacity_channel = ctx->menu_fade_animation->get_channel(0);
ctx.menu_fade_animation->set_interpolator(ease<float>::out_cubic);
animation_channel<float>* opacity_channel = ctx.menu_fade_animation->get_channel(0);
opacity_channel->remove_keyframes();
opacity_channel->insert_keyframe({0.0, 1.0f});
opacity_channel->insert_keyframe({game::menu::fade_out_duration, 0.0f});
ctx->menu_fade_animation->set_end_callback(end_callback);
ctx.menu_fade_animation->set_end_callback(end_callback);
ctx->menu_fade_animation->stop();
ctx->menu_fade_animation->play();
ctx.menu_fade_animation->stop();
ctx.menu_fade_animation->play();
}
void fade_in_bg(game::context* ctx)
void fade_in_bg(game::context& ctx)
{
ctx->menu_bg_fade_out_animation->stop();
ctx->menu_bg_fade_in_animation->stop();
ctx->menu_bg_fade_in_animation->play();
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(game::context* ctx)
void fade_out_bg(game::context& ctx)
{
ctx->menu_bg_fade_in_animation->stop();
ctx->menu_bg_fade_out_animation->stop();
ctx->menu_bg_fade_out_animation->play();
ctx.menu_bg_fade_in_animation->stop();
ctx.menu_bg_fade_out_animation->stop();
ctx.menu_bg_fade_out_animation->play();
}
void setup_controls(game::context* ctx)
void setup_controls(game::context& ctx)
{
ctx->controls["menu_up"]->set_activated_callback
ctx.controls["menu_up"]->set_activated_callback
(
[ctx]()
[&ctx]()
{
--(*ctx->menu_item_index);
if (*ctx->menu_item_index < 0)
*ctx->menu_item_index = ctx->menu_item_texts.size() - 1;
--(*ctx.menu_item_index);
if (*ctx.menu_item_index < 0)
*ctx.menu_item_index = ctx.menu_item_texts.size() - 1;
update_text_color(ctx);
}
);
ctx->controls["menu_down"]->set_activated_callback
ctx.controls["menu_down"]->set_activated_callback
(
[ctx]()
[&ctx]()
{
++(*ctx->menu_item_index);
if (*ctx->menu_item_index >= ctx->menu_item_texts.size())
*ctx->menu_item_index = 0;
++(*ctx.menu_item_index);
if (*ctx.menu_item_index >= ctx.menu_item_texts.size())
*ctx.menu_item_index = 0;
update_text_color(ctx);
}
);
ctx->controls["menu_left"]->set_activated_callback
ctx.controls["menu_left"]->set_activated_callback
(
[ctx]()
[&ctx]()
{
auto callback = ctx->menu_left_callbacks[*ctx->menu_item_index];
auto callback = ctx.menu_left_callbacks[*ctx.menu_item_index];
if (callback != nullptr)
callback();
}
);
ctx->controls["menu_right"]->set_activated_callback
ctx.controls["menu_right"]->set_activated_callback
(
[ctx]()
[&ctx]()
{
auto callback = ctx->menu_right_callbacks[*ctx->menu_item_index];
auto callback = ctx.menu_right_callbacks[*ctx.menu_item_index];
if (callback != nullptr)
callback();
}
);
ctx->controls["menu_select"]->set_activated_callback
ctx.controls["menu_select"]->set_activated_callback
(
[ctx]()
[&ctx]()
{
auto callback = ctx->menu_select_callbacks[*ctx->menu_item_index];
auto callback = ctx.menu_select_callbacks[*ctx.menu_item_index];
if (callback != nullptr)
callback();
}
);
ctx->controls["menu_back"]->set_activated_callback
ctx.controls["menu_back"]->set_activated_callback
(
[ctx]()
[&ctx]()
{
if (ctx->menu_back_callback != nullptr)
ctx->menu_back_callback();
if (ctx.menu_back_callback != nullptr)
ctx.menu_back_callback();
}
);
ctx->menu_mouse_tracker->set_mouse_moved_callback
ctx.menu_mouse_tracker->set_mouse_moved_callback
(
[ctx](const mouse_moved_event& event)
[&ctx](const mouse_moved_event& event)
{
const float padding = game::menu::mouseover_padding * ctx->menu_font.get_font_metrics().size;
const float padding = game::menu::mouseover_padding * ctx.menu_font.get_font_metrics().size;
for (std::size_t i = 0; i < ctx->menu_item_texts.size(); ++i)
for (std::size_t i = 0; i < ctx.menu_item_texts.size(); ++i)
{
auto [name, value] = ctx->menu_item_texts[i];
auto [name, value] = ctx.menu_item_texts[i];
const auto& name_bounds = static_cast<const geom::aabb<float>&>(name->get_world_bounds());
float min_x = name_bounds.min_point.x;
@ -377,7 +377,7 @@ void setup_controls(game::context* ctx)
max_y = std::max<float>(max_y, value_bounds.max_point.y);
}
const auto& viewport = ctx->app->get_viewport_dimensions();
const auto& viewport = ctx.app->get_viewport_dimensions();
const float x = static_cast<float>(event.x - viewport[0] / 2);
const float y = static_cast<float>((viewport[1] - event.y + 1) - viewport[1] / 2);
@ -390,7 +390,7 @@ void setup_controls(game::context* ctx)
{
if (y >= min_y && y <= max_y)
{
*ctx->menu_item_index = i;
*ctx.menu_item_index = i;
update_text_color(ctx);
break;
}
@ -399,13 +399,13 @@ void setup_controls(game::context* ctx)
}
);
ctx->menu_mouse_tracker->set_mouse_button_pressed_callback
ctx.menu_mouse_tracker->set_mouse_button_pressed_callback
(
[ctx](const mouse_button_pressed_event& event)
[&ctx](const mouse_button_pressed_event& event)
{
for (std::size_t i = 0; i < ctx->menu_item_texts.size(); ++i)
for (std::size_t i = 0; i < ctx.menu_item_texts.size(); ++i)
{
auto [name, value] = ctx->menu_item_texts[i];
auto [name, value] = ctx.menu_item_texts[i];
const auto& name_bounds = static_cast<const geom::aabb<float>&>(name->get_world_bounds());
float min_x = name_bounds.min_point.x;
@ -421,7 +421,7 @@ void setup_controls(game::context* ctx)
max_y = std::max<float>(max_y, value_bounds.max_point.y);
}
const auto& viewport = ctx->app->get_viewport_dimensions();
const auto& viewport = ctx.app->get_viewport_dimensions();
const float x = static_cast<float>(event.x - viewport[0] / 2);
const float y = static_cast<float>((viewport[1] - event.y + 1) - viewport[1] / 2);
@ -429,18 +429,18 @@ void setup_controls(game::context* ctx)
{
if (y >= min_y && y <= max_y)
{
*ctx->menu_item_index = i;
*ctx.menu_item_index = i;
update_text_color(ctx);
if (event.button == 1)
{
auto callback = ctx->menu_select_callbacks[i];
auto callback = ctx.menu_select_callbacks[i];
if (callback)
callback();
}
else if (event.button == 3)
{
auto callback = ctx->menu_left_callbacks[i];
auto callback = ctx.menu_left_callbacks[i];
if (callback)
callback();
}
@ -453,17 +453,17 @@ void setup_controls(game::context* ctx)
);
}
void clear_controls(game::context* ctx)
void clear_controls(game::context& ctx)
{
ctx->controls["menu_up"]->set_activated_callback(nullptr);
ctx->controls["menu_down"]->set_activated_callback(nullptr);
ctx->controls["menu_left"]->set_activated_callback(nullptr);
ctx->controls["menu_right"]->set_activated_callback(nullptr);
ctx->controls["menu_select"]->set_activated_callback(nullptr);
ctx->controls["menu_back"]->set_activated_callback(nullptr);
ctx.controls["menu_up"]->set_activated_callback(nullptr);
ctx.controls["menu_down"]->set_activated_callback(nullptr);
ctx.controls["menu_left"]->set_activated_callback(nullptr);
ctx.controls["menu_right"]->set_activated_callback(nullptr);
ctx.controls["menu_select"]->set_activated_callback(nullptr);
ctx.controls["menu_back"]->set_activated_callback(nullptr);
ctx->menu_mouse_tracker->set_mouse_moved_callback(nullptr);
ctx->menu_mouse_tracker->set_mouse_button_pressed_callback(nullptr);
ctx.menu_mouse_tracker->set_mouse_moved_callback(nullptr);
ctx.menu_mouse_tracker->set_mouse_button_pressed_callback(nullptr);
}
} // namespace menu

+ 22
- 22
src/game/menu.hpp View File

@ -46,28 +46,28 @@ static constexpr float fade_in_duration = 0.25f;
/// Duration of the menu fade out animation
static constexpr float fade_out_duration = 0.125f;
void init_menu_item_index(game::context* ctx, const std::string& menu_name);
void setup_controls(game::context* ctx);
void setup_animations(game::context* ctx);
void clear_controls(game::context* ctx);
void clear_callbacks(game::context* ctx);
void remove_text_from_ui(game::context* ctx);
void delete_text(game::context* ctx);
void delete_animations(game::context* ctx);
void fade_in(game::context* ctx, const std::function<void()>& end_callback);
void fade_out(game::context* ctx, const std::function<void()>& end_callback);
void fade_in_bg(game::context* ctx);
void fade_out_bg(game::context* ctx);
void update_text_color(game::context* ctx);
void update_text_font(game::context* ctx);
void update_text_tweens(game::context* ctx);
void align_text(game::context* ctx, bool center = false, bool has_back = true, float anchor_y = 0.0f);
void refresh_text(game::context* ctx);
void add_text_to_ui(game::context* ctx);
void init_menu_item_index(game::context& ctx, const std::string& menu_name);
void setup_controls(game::context& ctx);
void setup_animations(game::context& ctx);
void clear_controls(game::context& ctx);
void clear_callbacks(game::context& ctx);
void remove_text_from_ui(game::context& ctx);
void delete_text(game::context& ctx);
void delete_animations(game::context& ctx);
void fade_in(game::context& ctx, const std::function<void()>& end_callback);
void fade_out(game::context& ctx, const std::function<void()>& end_callback);
void fade_in_bg(game::context& ctx);
void fade_out_bg(game::context& ctx);
void update_text_color(game::context& ctx);
void update_text_font(game::context& ctx);
void update_text_tweens(game::context& ctx);
void align_text(game::context& ctx, bool center = false, bool has_back = true, float anchor_y = 0.0f);
void refresh_text(game::context& ctx);
void add_text_to_ui(game::context& ctx);
} // namespace menu
} // namespace game

+ 7
- 7
src/game/save.cpp View File

@ -24,20 +24,20 @@
namespace game {
void save_config(game::context* ctx)
void save_config(game::context& ctx)
{
const std::string config_file_path = ctx->config_path + "config.json";
ctx->logger->push_task("Saving config to \"" + config_file_path + "\"");
std::filesystem::path path = ctx.config_path / "config.json";
ctx.logger->push_task("Saving config to \"" + path.string() + "\"");
try
{
std::ofstream config_file(config_file_path);
config_file << *(ctx->config);
std::ofstream config_file(path);
config_file << *(ctx.config);
}
catch (...)
{
ctx->logger->pop_task(EXIT_FAILURE);
ctx.logger->pop_task(EXIT_FAILURE);
}
ctx->logger->pop_task(EXIT_SUCCESS);
ctx.logger->pop_task(EXIT_SUCCESS);
}
} // namespace game

+ 1
- 1
src/game/save.hpp View File

@ -24,7 +24,7 @@
namespace game {
void save_config(game::context* ctx);
void save_config(game::context& ctx);
} // namespace game

src/game/states/boot.hpp → src/game/state/base.cpp View File

@ -17,23 +17,17 @@
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GAME_STATE_BOOT_HPP
#define ANTKEEPER_GAME_STATE_BOOT_HPP
#include "application.hpp"
#include "game/state/base.hpp"
namespace game {
namespace state {
/// Boot game state functions.
namespace boot {
void enter(application* app, int argc, char** argv);
void exit(application* app);
base::base(game::context& ctx):
ctx(ctx)
{}
} // namespace boot
base::~base()
{}
} // namespace state
} // namespace game
#endif // ANTKEEPER_GAME_STATE_BOOT_HPP

+ 54
- 0
src/game/state/base.hpp View File

@ -0,0 +1,54 @@
/*
* Copyright (C) 2021 Christopher J. Howard
*
* This file is part of Antkeeper source code.
*
* Antkeeper source code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GAME_STATE_BASE_HPP
#define ANTKEEPER_GAME_STATE_BASE_HPP
namespace game {
struct context;
namespace state {
/**
* Abstract base class for game states.
*/
class base
{
public:
/**
* Constructs a game state.
*
* @param ctx Reference to the game context on which this state will operate.
*/
base(game::context& ctx);
/**
* Destructs a game state.
*/
virtual ~base() = 0;
protected:
game::context& ctx;
};
} // namespace state
} // namespace game
#endif // ANTKEEPER_GAME_STATE_BASE_HPP

+ 1202
- 0
src/game/state/boot.cpp
File diff suppressed because it is too large
View File


+ 68
- 0
src/game/state/boot.hpp View File

@ -0,0 +1,68 @@
/*
* Copyright (C) 2021 Christopher J. Howard
*
* This file is part of Antkeeper source code.
*
* Antkeeper source code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GAME_STATE_BOOT_HPP
#define ANTKEEPER_GAME_STATE_BOOT_HPP
#include "game/state/base.hpp"
#include "game/context.hpp"
#include <optional>
namespace game {
namespace state {
class boot: public game::state::base
{
public:
boot(game::context& ctx, int argc, char** argv);
virtual ~boot();
private:
void parse_options(int argc, char** argv);
void setup_resources();
void load_config();
void load_strings();
void setup_window();
void setup_rendering();
void setup_sound();
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();
// Command line options
std::optional<bool> option_continue;
std::optional<std::string> option_data;
std::optional<bool> option_fullscreen;
std::optional<bool> option_new_game;
std::optional<bool> option_quick_start;
std::optional<bool> option_reset;
std::optional<int> option_v_sync;
std::optional<bool> option_windowed;
};
} // namespace state
} // namespace game
#endif // ANTKEEPER_GAME_STATE_BOOT_HPP

src/game/states/brood.cpp → src/game/state/brood.cpp View File

@ -17,7 +17,7 @@
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#include "game/states/brood.hpp"
#include "game/state/brood.hpp"
#include "entity/archetype.hpp"
#include "entity/commands.hpp"
#include "entity/components/observer.hpp"
@ -326,14 +326,14 @@ void setup_controls(game::context* ctx)
// Mouse rotate
ctx->controls["mouse_rotate"]->set_activated_callback
(
[ctx]()
[&ctx]()
{
ctx->app->set_relative_mouse_mode(true);
}
);
ctx->controls["mouse_rotate"]->set_deactivated_callback
(
[ctx]()
[&ctx]()
{
ctx->app->set_relative_mouse_mode(false);
}

src/game/states/brood.hpp → src/game/state/brood.hpp View File


src/game/states/controls-menu.cpp → src/game/state/controls-menu.cpp View File

@ -17,10 +17,10 @@
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#include "game/states/controls-menu.hpp"
#include "game/states/keyboard-config-menu.hpp"
#include "game/states/gamepad-config-menu.hpp"
#include "game/states/options-menu.hpp"
#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 "application.hpp"
#include "scene/text.hpp"
#include "debug/logger.hpp"
@ -28,24 +28,26 @@
namespace game {
namespace state {
namespace controls_menu {
void enter(game::context* ctx)
controls_menu::controls_menu(game::context& ctx):
game::state::base(ctx)
{
ctx.logger->push_task("Entering controls menu state");
// Construct menu item texts
scene::text* keyboard_text = new scene::text();
scene::text* gamepad_text = new scene::text();
scene::text* back_text = new scene::text();
// Build list of menu item texts
ctx->menu_item_texts.push_back({keyboard_text, nullptr});
ctx->menu_item_texts.push_back({gamepad_text, nullptr});
ctx->menu_item_texts.push_back({back_text, nullptr});
ctx.menu_item_texts.push_back({keyboard_text, nullptr});
ctx.menu_item_texts.push_back({gamepad_text, nullptr});
ctx.menu_item_texts.push_back({back_text, nullptr});
// Set content of menu item texts
keyboard_text->set_content((*ctx->strings)["controls_menu_keyboard"]);
gamepad_text->set_content((*ctx->strings)["controls_menu_gamepad"]);
back_text->set_content((*ctx->strings)["back"]);
keyboard_text->set_content((*ctx.strings)["controls_menu_keyboard"]);
gamepad_text->set_content((*ctx.strings)["controls_menu_gamepad"]);
back_text->set_content((*ctx.strings)["back"]);
// Init menu item index
game::menu::init_menu_item_index(ctx, "controls");
@ -58,7 +60,7 @@ void enter(game::context* ctx)
game::menu::setup_animations(ctx);
// Construct menu item callbacks
auto select_keyboard_callback = [ctx]()
auto select_keyboard_callback = [&ctx]()
{
// Disable controls
game::menu::clear_controls(ctx);
@ -66,17 +68,21 @@ void enter(game::context* ctx)
game::menu::fade_out
(
ctx,
[ctx]()
[&ctx]()
{
application::state next_state;
next_state.name = "keyboard_config_menu";
next_state.enter = std::bind(game::state::keyboard_config_menu::enter, ctx);
next_state.exit = std::bind(game::state::keyboard_config_menu::exit, ctx);
ctx->app->queue_state(next_state);
// Queue change to keyboard config menu state
ctx.function_queue.push
(
[&ctx]()
{
ctx.state_machine.pop();
ctx.state_machine.emplace(new game::state::keyboard_config_menu(ctx));
}
);
}
);
};
auto select_gamepad_callback = [ctx]()
auto select_gamepad_callback = [&ctx]()
{
// Disable controls
game::menu::clear_controls(ctx);
@ -84,17 +90,21 @@ void enter(game::context* ctx)
game::menu::fade_out
(
ctx,
[ctx]()
[&ctx]()
{
application::state next_state;
next_state.name = "gamepad_config_menu";
next_state.enter = std::bind(game::state::gamepad_config_menu::enter, ctx);
next_state.exit = std::bind(game::state::gamepad_config_menu::exit, ctx);
ctx->app->queue_state(next_state);
// Queue change to gamepad config menu state
ctx.function_queue.push
(
[&ctx]()
{
ctx.state_machine.pop();
ctx.state_machine.emplace(new game::state::gamepad_config_menu(ctx));
}
);
}
);
};
auto select_back_callback = [ctx]()
auto select_back_callback = [&ctx]()
{
// Disable controls
game::menu::clear_controls(ctx);
@ -102,52 +112,61 @@ void enter(game::context* ctx)
game::menu::fade_out
(
ctx,
[ctx]()
[&ctx]()
{
application::state next_state;
next_state.name = "options_menu";
next_state.enter = std::bind(game::state::options_menu::enter, ctx);
next_state.exit = std::bind(game::state::options_menu::exit, ctx);
ctx->app->queue_state(next_state);
// Queue change to options menu state
ctx.function_queue.push
(
[&ctx]()
{
ctx.state_machine.pop();
ctx.state_machine.emplace(new game::state::options_menu(ctx));
}
);
}
);
};
// Build list of menu select callbacks
ctx->menu_select_callbacks.push_back(select_keyboard_callback);
ctx->menu_select_callbacks.push_back(select_gamepad_callback);
ctx->menu_select_callbacks.push_back(select_back_callback);
ctx.menu_select_callbacks.push_back(select_keyboard_callback);
ctx.menu_select_callbacks.push_back(select_gamepad_callback);
ctx.menu_select_callbacks.push_back(select_back_callback);
// Build list of menu left callbacks
ctx->menu_left_callbacks.push_back(nullptr);
ctx->menu_left_callbacks.push_back(nullptr);
ctx->menu_left_callbacks.push_back(nullptr);
ctx.menu_left_callbacks.push_back(nullptr);
ctx.menu_left_callbacks.push_back(nullptr);
ctx.menu_left_callbacks.push_back(nullptr);
// Build list of menu right callbacks
ctx->menu_right_callbacks.push_back(nullptr);
ctx->menu_right_callbacks.push_back(nullptr);
ctx->menu_right_callbacks.push_back(nullptr);
ctx.menu_right_callbacks.push_back(nullptr);
ctx.menu_right_callbacks.push_back(nullptr);
ctx.menu_right_callbacks.push_back(nullptr);
// Set menu back callback
ctx->menu_back_callback = select_back_callback;
ctx.menu_back_callback = select_back_callback;
// Queue menu control setup
ctx->function_queue.push(std::bind(game::menu::setup_controls, ctx));
ctx.function_queue.push(std::bind(game::menu::setup_controls, std::ref(ctx)));
// Fade in menu
game::menu::fade_in(ctx, nullptr);
ctx.logger->pop_task(EXIT_SUCCESS);
}
void exit(game::context* ctx)
controls_menu::~controls_menu()
{
ctx.logger->push_task("Exiting options menu state");
// Destruct menu
game::menu::clear_controls(ctx);
game::menu::clear_callbacks(ctx);
game::menu::delete_animations(ctx);
game::menu::remove_text_from_ui(ctx);
game::menu::delete_text(ctx);
ctx.logger->pop_task(EXIT_SUCCESS);
}
} // namespace controls_menu
} // namespace state
} // namespace game

src/game/states/controls-menu.hpp → src/game/state/controls-menu.hpp View File

@ -20,18 +20,17 @@
#ifndef ANTKEEPER_GAME_STATE_CONTROLS_MENU_HPP
#define ANTKEEPER_GAME_STATE_CONTROLS_MENU_HPP
#include "game/context.hpp"
#include "game/state/base.hpp"
namespace game {
namespace state {
/// Controls menu screen game state functions.
namespace controls_menu {
void enter(game::context* ctx);
void exit(game::context* ctx);
} // namespace controls_menu
class controls_menu: public game::state::base
{
public:
controls_menu(game::context& ctx);
virtual ~controls_menu();
};
} // namespace state
} // namespace game

+ 143
- 0
src/game/state/credits.cpp View File

@ -0,0 +1,143 @@
/*
* Copyright (C) 2021 Christopher J. Howard
*
* This file is part of Antkeeper source code.
*
* Antkeeper source code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#include "game/state/credits.hpp"
#include "game/state/extras-menu.hpp"
#include "game/context.hpp"
#include "animation/ease.hpp"
#include "animation/animation.hpp"
#include "animation/animator.hpp"
#include "application.hpp"
#include "scene/text.hpp"
#include "debug/logger.hpp"
namespace game {
namespace state {
credits::credits(game::context& ctx):
game::state::base(ctx)
{
ctx.logger->push_task("Entering credits state");
// Construct credits text
ctx.credits_text = new scene::text();
ctx.credits_text->set_material(&ctx.menu_font_material);
ctx.credits_text->set_font(&ctx.menu_font);
ctx.credits_text->set_color({1.0f, 1.0f, 1.0f, 0.0f});
ctx.credits_text->set_content((*ctx.strings)["credits"]);
// Align credits text
const auto& credits_aabb = static_cast<const geom::aabb<float>&>(ctx.credits_text->get_local_bounds());
float credits_w = credits_aabb.max_point.x - credits_aabb.min_point.x;
float credits_h = credits_aabb.max_point.y - credits_aabb.min_point.y;
ctx.credits_text->set_translation({std::round(-credits_w * 0.5f), std::round(-credits_h * 0.5f), 0.0f});
// Load animation timing configuration
double credits_fade_in_duration = 0.0;
double credits_scroll_duration = 0.0;
if (ctx.config->contains("credits_fade_in_duration"))
credits_fade_in_duration = (*ctx.config)["credits_fade_in_duration"].get<double>();
if (ctx.config->contains("credits_scroll_duration"))
credits_scroll_duration = (*ctx.config)["credits_scroll_duration"].get<double>();
auto set_credits_opacity = [&ctx](int channel, const float& opacity)
{
ctx.credits_text->set_color({1.0f, 1.0f, 1.0f, opacity});
};
// Build credits fade in animation
ctx.credits_fade_in_animation = new animation<float>();
animation_channel<float>* credits_fade_in_opacity_channel = ctx.credits_fade_in_animation->add_channel(0);
ctx.credits_fade_in_animation->set_interpolator(ease<float>::in_quad);
credits_fade_in_opacity_channel->insert_keyframe({0.0, 0.0f});
credits_fade_in_opacity_channel->insert_keyframe({credits_fade_in_duration, 1.0f});
ctx.credits_fade_in_animation->set_frame_callback(set_credits_opacity);
// Build credits scroll in animation
ctx.credits_scroll_animation = new animation<float>();
// Trigger credits scroll animation after credits fade in animation ends
ctx.credits_fade_in_animation->set_end_callback
(
[&ctx]()
{
ctx.credits_scroll_animation->play();
}
);
// Add credits animations to animator
ctx.animator->add_animation(ctx.credits_fade_in_animation);
ctx.animator->add_animation(ctx.credits_scroll_animation);
// Start credits fade in animation
ctx.credits_fade_in_animation->play();
// Set up credits skipper
ctx.input_listener->set_callback
(
[&ctx](const event_base& event)
{
auto id = event.get_event_type_id();
if (id != mouse_moved_event::event_type_id && id != mouse_wheel_scrolled_event::event_type_id && id != gamepad_axis_moved_event::event_type_id)
{
if (ctx.credits_text->get_color()[3] > 0.0f)
{
ctx.input_listener->set_enabled(false);
// Change state
ctx.state_machine.pop();
ctx.state_machine.emplace(new game::state::extras_menu(ctx));
}
}
}
);
ctx.input_listener->set_enabled(true);
ctx.ui_scene->add_object(ctx.credits_text);
ctx.credits_text->update_tweens();
ctx.logger->pop_task(EXIT_SUCCESS);
}
credits::~credits()
{
ctx.logger->push_task("Exiting credits state");
// Disable credits skipper
ctx.input_listener->set_enabled(false);
ctx.input_listener->set_callback(nullptr);
// Destruct credits text
ctx.ui_scene->remove_object(ctx.credits_text);
delete ctx.credits_text;
ctx.credits_text = nullptr;
// Destruct credits animations
ctx.animator->remove_animation(ctx.credits_fade_in_animation);
ctx.animator->remove_animation(ctx.credits_scroll_animation);
delete ctx.credits_fade_in_animation;
delete ctx.credits_scroll_animation;
ctx.credits_fade_in_animation = nullptr;
ctx.credits_scroll_animation = nullptr;
ctx.logger->pop_task(EXIT_SUCCESS);
}
} // namespace state
} // namespace game

src/game/states/credits.hpp → src/game/state/credits.hpp View File

@ -20,18 +20,17 @@
#ifndef ANTKEEPER_GAME_STATE_CREDITS_HPP
#define ANTKEEPER_GAME_STATE_CREDITS_HPP
#include "game/context.hpp"
#include "game/state/base.hpp"
namespace game {
namespace state {
/// Credits screen game state functions.
namespace credits {
void enter(game::context* ctx);
void exit(game::context* ctx);
} // namespace credits
class credits: public game::state::base
{
public:
credits(game::context& ctx);
virtual ~credits();
};
} // namespace state
} // namespace game

src/game/states/extras-menu.cpp → src/game/state/extras-menu.cpp View File

@ -17,9 +17,9 @@
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#include "game/states/extras-menu.hpp"
#include "game/states/main-menu.hpp"
#include "game/states/credits.hpp"
#include "game/state/extras-menu.hpp"
#include "game/state/main-menu.hpp"
#include "game/state/credits.hpp"
#include "application.hpp"
#include "scene/text.hpp"
#include "debug/logger.hpp"
@ -28,21 +28,23 @@
namespace game {
namespace state {
namespace extras_menu {
void enter(game::context* ctx)
extras_menu::extras_menu(game::context& ctx):
game::state::base(ctx)
{
ctx.logger->push_task("Entering extras menu state");
// Construct menu item texts
scene::text* credits_text = new scene::text();
scene::text* back_text = new scene::text();
// Build list of menu item texts
ctx->menu_item_texts.push_back({credits_text, nullptr});
ctx->menu_item_texts.push_back({back_text, nullptr});
ctx.menu_item_texts.push_back({credits_text, nullptr});
ctx.menu_item_texts.push_back({back_text, nullptr});
// Set content of menu item texts
credits_text->set_content((*ctx->strings)["extras_menu_credits"]);
back_text->set_content((*ctx->strings)["back"]);
credits_text->set_content((*ctx.strings)["extras_menu_credits"]);
back_text->set_content((*ctx.strings)["back"]);
// Init menu item index
game::menu::init_menu_item_index(ctx, "extras");
@ -55,7 +57,7 @@ void enter(game::context* ctx)
game::menu::setup_animations(ctx);
// Construct menu item callbacks
auto select_credits_callback = [ctx]()
auto select_credits_callback = [&ctx]()
{
// Disable controls
game::menu::clear_controls(ctx);
@ -63,17 +65,21 @@ void enter(game::context* ctx)
game::menu::fade_out
(
ctx,
[ctx]()
[&ctx]()
{
application::state next_state;
next_state.name = "credits";
next_state.enter = std::bind(game::state::credits::enter, ctx);
next_state.exit = std::bind(game::state::credits::exit, ctx);
ctx->app->queue_state(next_state);
// Queue change to credits state
ctx.function_queue.push
(
[&ctx]()
{
ctx.state_machine.pop();
ctx.state_machine.emplace(new game::state::credits(ctx));
}
);
}
);
};
auto select_back_callback = [ctx]()
auto select_back_callback = [&ctx]()
{
// Disable controls
game::menu::clear_controls(ctx);
@ -81,49 +87,58 @@ void enter(game::context* ctx)
game::menu::fade_out
(
ctx,
[ctx]()
[&ctx]()
{
application::state next_state;
next_state.name = "main_menu";
next_state.enter = std::bind(game::state::main_menu::enter, ctx, false);
next_state.exit = std::bind(game::state::main_menu::exit, ctx);
ctx->app->queue_state(next_state);
// Queue change to main menu state
ctx.function_queue.push
(
[&ctx]()
{
ctx.state_machine.pop();
ctx.state_machine.emplace(new game::state::main_menu(ctx, false));
}
);
}
);
};
// Build list of menu select callbacks
ctx->menu_select_callbacks.push_back(select_credits_callback);
ctx->menu_select_callbacks.push_back(select_back_callback);
ctx.menu_select_callbacks.push_back(select_credits_callback);
ctx.menu_select_callbacks.push_back(select_back_callback);
// Build list of menu left callbacks
ctx->menu_left_callbacks.push_back(nullptr);
ctx->menu_left_callbacks.push_back(nullptr);
ctx.menu_left_callbacks.push_back(nullptr);
ctx.menu_left_callbacks.push_back(nullptr);
// Build list of menu right callbacks
ctx->menu_right_callbacks.push_back(nullptr);
ctx->menu_right_callbacks.push_back(nullptr);
ctx.menu_right_callbacks.push_back(nullptr);
ctx.menu_right_callbacks.push_back(nullptr);
// Set menu back callback
ctx->menu_back_callback = select_back_callback;
ctx.menu_back_callback = select_back_callback;
// Queue menu control setup
ctx->function_queue.push(std::bind(game::menu::setup_controls, ctx));
ctx.function_queue.push(std::bind(game::menu::setup_controls, std::ref(ctx)));
// Fade in menu
game::menu::fade_in(ctx, nullptr);
ctx.logger->pop_task(EXIT_SUCCESS);
}
void exit(game::context* ctx)
extras_menu::~extras_menu()
{
ctx.logger->push_task("Exiting extras menu state");
// Destruct menu
game::menu::clear_controls(ctx);
game::menu::clear_callbacks(ctx);
game::menu::delete_animations(ctx);
game::menu::remove_text_from_ui(ctx);
game::menu::delete_text(ctx);
ctx.logger->pop_task(EXIT_SUCCESS);
}
} // namespace extras_menu
} // namespace state
} // namespace game

src/game/states/extras-menu.hpp → src/game/state/extras-menu.hpp View File

@ -20,18 +20,17 @@
#ifndef ANTKEEPER_GAME_STATE_EXTRAS_MENU_HPP
#define ANTKEEPER_GAME_STATE_EXTRAS_MENU_HPP
#include "game/context.hpp"
#include "game/state/base.hpp"
namespace game {
namespace state {
/// Extras menu screen game state functions.
namespace extras_menu {
void enter(game::context* ctx);
void exit(game::context* ctx);
} // namespace extras_menu
class extras_menu: public game::state::base
{
public:
extras_menu(game::context& ctx);
virtual ~extras_menu();
};
} // namespace state
} // namespace game

src/game/states/forage.cpp → src/game/state/forage.cpp View File

@ -17,7 +17,7 @@
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#include "game/states/forage.hpp"
#include "game/state/forage.hpp"
#include "entity/archetype.hpp"
#include "entity/commands.hpp"
#include "entity/components/observer.hpp"
@ -203,8 +203,8 @@ void setup_camera(game::context* ctx)
void setup_tools(game::context* ctx)
{
ctx->entities["camera_tool"] = build_camera_tool(ctx);
ctx->entities["time_tool"] = build_time_tool(ctx);
ctx->entities["camera_tool"] = build_camera_tool(*ctx);
ctx->entities["time_tool"] = build_time_tool(*ctx);
// Set active tool
ctx->entities["active_tool"] = ctx->entities["time_tool"];
@ -477,7 +477,7 @@ void setup_controls(game::context* ctx)
// Use tool
ctx->controls["use_tool"]->set_activated_callback
(
[ctx]()
[&ctx]()
{
if (ctx->entities.count("active_tool"))
{
@ -490,7 +490,7 @@ void setup_controls(game::context* ctx)
);
ctx->controls["use_tool"]->set_deactivated_callback
(
[ctx]()
[&ctx]()
{
if (ctx->entities.count("active_tool"))
{
@ -503,7 +503,7 @@ void setup_controls(game::context* ctx)
);
ctx->controls["use_tool"]->set_active_callback
(
[ctx](float value)
[&ctx](float value)
{
if (ctx->entities.count("active_tool"))
{

src/game/states/forage.hpp → src/game/state/forage.hpp View File


src/game/states/gamepad-config-menu.cpp → src/game/state/gamepad-config-menu.cpp View File

@ -17,8 +17,9 @@
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#include "game/states/gamepad-config-menu.hpp"
#include "game/states/controls-menu.hpp"
#include "game/state/gamepad-config-menu.hpp"
#include "game/state/controls-menu.hpp"
#include "game/context.hpp"
#include "application.hpp"
#include "scene/text.hpp"
#include "debug/logger.hpp"
@ -28,13 +29,110 @@
namespace game {
namespace state {
namespace gamepad_config_menu {
static std::string get_binding_string(game::context* ctx, input::control* control)
gamepad_config_menu::gamepad_config_menu(game::context& ctx):
game::state::base(ctx)
{
ctx.logger->push_task("Entering gamepad config menu state");
// Add camera control menu items
add_control_item("move_forward");
add_control_item("move_back");
add_control_item("move_left");
add_control_item("move_right");
add_control_item("move_up");
add_control_item("move_down");
// Add application control menu items
add_control_item("toggle_fullscreen");
add_control_item("screenshot");
// Construct menu item texts
scene::text* back_text = new scene::text();
// Build list of menu item texts
ctx.menu_item_texts.push_back({back_text, nullptr});
// Set content of menu item texts
back_text->set_content((*ctx.strings)["back"]);
// Init menu item index
game::menu::init_menu_item_index(ctx, "gamepad_config");
game::menu::update_text_color(ctx);
game::menu::update_text_font(ctx);
game::menu::align_text(ctx);
game::menu::update_text_tweens(ctx);
game::menu::add_text_to_ui(ctx);
game::menu::setup_animations(ctx);
// Construct menu item callbacks
auto select_back_callback = [&ctx]()
{
// Disable controls
game::menu::clear_controls(ctx);
game::menu::fade_out
(
ctx,
[&ctx]()
{
// Queue change to controls menu state
ctx.function_queue.push
(
[&ctx]()
{
ctx.state_machine.pop();
ctx.state_machine.emplace(new game::state::controls_menu(ctx));
}
);
}
);
};
// Build list of menu select callbacks
ctx.menu_select_callbacks.push_back(select_back_callback);
// Build list of menu left callbacks
ctx.menu_left_callbacks.push_back(nullptr);
// Build list of menu right callbacks
ctx.menu_right_callbacks.push_back(nullptr);
// Set menu back callback
ctx.menu_back_callback = select_back_callback;
// Queue menu control setup
ctx.function_queue.push(std::bind(game::menu::setup_controls, std::ref(ctx)));
// Fade in menu
game::menu::fade_in(ctx, nullptr);
ctx.logger->pop_task(EXIT_SUCCESS);
}
gamepad_config_menu::~gamepad_config_menu()
{
ctx.logger->push_task("Exiting gamepad config menu state");
// Destruct menu
game::menu::clear_controls(ctx);
game::menu::clear_callbacks(ctx);
game::menu::delete_animations(ctx);
game::menu::remove_text_from_ui(ctx);
game::menu::delete_text(ctx);
// Save control profile
game::save_control_profile(ctx);
ctx.logger->pop_task(EXIT_SUCCESS);
}
std::string gamepad_config_menu::get_binding_string(input::control* control)
{
std::string binding_string;
auto mappings = ctx->input_event_router->get_mappings(control);
auto mappings = ctx.input_event_router->get_mappings(control);
for (input::mapping* mapping: *mappings)
{
std::string mapping_string;
@ -49,38 +147,38 @@ static std::string get_binding_string(game::context* ctx, input::control* contro
{
case input::gamepad_axis::left_x:
if (axis_mapping->negative)
mapping_string = (*ctx->strings)["gamepad_left_stick_left"];
mapping_string = (*ctx.strings)["gamepad_left_stick_left"];
else
mapping_string = (*ctx->strings)["gamepad_left_stick_right"];
mapping_string = (*ctx.strings)["gamepad_left_stick_right"];
break;
case input::gamepad_axis::left_y:
if (axis_mapping->negative)
mapping_string = (*ctx->strings)["gamepad_left_stick_up"];
mapping_string = (*ctx.strings)["gamepad_left_stick_up"];
else
mapping_string = (*ctx->strings)["gamepad_left_stick_down"];
mapping_string = (*ctx.strings)["gamepad_left_stick_down"];
break;
case input::gamepad_axis::right_x:
if (axis_mapping->negative)
mapping_string = (*ctx->strings)["gamepad_right_stick_left"];
mapping_string = (*ctx.strings)["gamepad_right_stick_left"];
else
mapping_string = (*ctx->strings)["gamepad_right_stick_right"];
mapping_string = (*ctx.strings)["gamepad_right_stick_right"];
break;
case input::gamepad_axis::right_y:
if (axis_mapping->negative)
mapping_string = (*ctx->strings)["gamepad_right_stick_up"];
mapping_string = (*ctx.strings)["gamepad_right_stick_up"];
else
mapping_string = (*ctx->strings)["gamepad_right_stick_down"];
mapping_string = (*ctx.strings)["gamepad_right_stick_down"];
break;
case input::gamepad_axis::left_trigger:
mapping_string = (*ctx->strings)["gamepad_left_trigger"];
mapping_string = (*ctx.strings)["gamepad_left_trigger"];
break;
case input::gamepad_axis::right_trigger:
mapping_string = (*ctx->strings)["gamepad_right_trigger"];
mapping_string = (*ctx.strings)["gamepad_right_trigger"];
break;
default:
@ -96,63 +194,63 @@ static std::string get_binding_string(game::context* ctx, input::control* contro
switch (button_mapping->button)
{
case input::gamepad_button::a:
mapping_string = (*ctx->strings)["gamepad_button_a"];
mapping_string = (*ctx.strings)["gamepad_button_a"];
break;
case input::gamepad_button::b:
mapping_string = (*ctx->strings)["gamepad_button_b"];
mapping_string = (*ctx.strings)["gamepad_button_b"];
break;
case input::gamepad_button::x:
mapping_string = (*ctx->strings)["gamepad_button_x"];
mapping_string = (*ctx.strings)["gamepad_button_x"];
break;
case input::gamepad_button::y:
mapping_string = (*ctx->strings)["gamepad_button_y"];
mapping_string = (*ctx.strings)["gamepad_button_y"];
break;
case input::gamepad_button::back:
mapping_string = (*ctx->strings)["gamepad_button_back"];
mapping_string = (*ctx.strings)["gamepad_button_back"];
break;
case input::gamepad_button::guide:
mapping_string = (*ctx->strings)["gamepad_button_guide"];
mapping_string = (*ctx.strings)["gamepad_button_guide"];
break;
case input::gamepad_button::start:
mapping_string = (*ctx->strings)["gamepad_button_start"];
mapping_string = (*ctx.strings)["gamepad_button_start"];
break;
case input::gamepad_button::left_stick:
mapping_string = (*ctx->strings)["gamepad_button_left_stick"];
mapping_string = (*ctx.strings)["gamepad_button_left_stick"];
break;
case input::gamepad_button::right_stick:
mapping_string = (*ctx->strings)["gamepad_button_right_stick"];
mapping_string = (*ctx.strings)["gamepad_button_right_stick"];
break;
case input::gamepad_button::left_shoulder:
mapping_string = (*ctx->strings)["gamepad_button_left_shoulder"];
mapping_string = (*ctx.strings)["gamepad_button_left_shoulder"];
break;
case input::gamepad_button::right_shoulder:
mapping_string = (*ctx->strings)["gamepad_button_right_shoulder"];
mapping_string = (*ctx.strings)["gamepad_button_right_shoulder"];
break;
case input::gamepad_button::dpad_up:
mapping_string = (*ctx->strings)["gamepad_button_dpad_up"];
mapping_string = (*ctx.strings)["gamepad_button_dpad_up"];
break;
case input::gamepad_button::dpad_down:
mapping_string = (*ctx->strings)["gamepad_button_dpad_down"];
mapping_string = (*ctx.strings)["gamepad_button_dpad_down"];
break;
case input::gamepad_button::dpad_left:
mapping_string = (*ctx->strings)["gamepad_button_dpad_left"];
mapping_string = (*ctx.strings)["gamepad_button_dpad_left"];
break;
case input::gamepad_button::dpad_right:
mapping_string = (*ctx->strings)["gamepad_button_dpad_right"];
mapping_string = (*ctx.strings)["gamepad_button_dpad_right"];
break;
default:
@ -181,32 +279,32 @@ static std::string get_binding_string(game::context* ctx, input::control* contro
return binding_string;
}
static void add_control_item(game::context* ctx, const std::string& control_name)
void gamepad_config_menu::add_control_item(const std::string& control_name)
{
// Get pointer to control
input::control* control = ctx->controls[control_name];
input::control* control = ctx.controls[control_name];
// Construct texts
scene::text* name_text = new scene::text();
scene::text* value_text = new scene::text();
// Add texts to list of menu item texts
ctx->menu_item_texts.push_back({name_text, value_text});
ctx.menu_item_texts.push_back({name_text, value_text});
// Set content of name text
std::string string_name = "control_" + control_name;
if (auto it = ctx->strings->find(string_name); it != ctx->strings->end())
if (auto it = ctx.strings->find(string_name); it != ctx.strings->end())
name_text->set_content(it->second);
else
name_text->set_content(control_name);
// Set content of value text
value_text->set_content(get_binding_string(ctx, control));
value_text->set_content(get_binding_string(control));
auto select_callback = [ctx, control, value_text]()
auto select_callback = [this, &ctx = this->ctx, control, value_text]()
{
// Clear binding string from value text
value_text->set_content((*ctx->strings)["ellipsis"]);
value_text->set_content((*ctx.strings)["ellipsis"]);
game::menu::align_text(ctx);
game::menu::update_text_tweens(ctx);
@ -214,13 +312,13 @@ static void add_control_item(game::context* ctx, const std::string& control_name
game::menu::clear_controls(ctx);
// Remove gamepad event mappings from control
ctx->input_event_router->remove_mappings(control, input::mapping_type::gamepad_axis);
ctx->input_event_router->remove_mappings(control, input::mapping_type::gamepad_button);
ctx.input_event_router->remove_mappings(control, input::mapping_type::gamepad_axis);
ctx.input_event_router->remove_mappings(control, input::mapping_type::gamepad_button);
// Setup input binding listener
ctx->input_listener->set_callback
ctx.input_listener->set_callback
(
[ctx, control, value_text](const event_base& event)
[this, &ctx, control, value_text](const event_base& event)
{
auto id = event.get_event_type_id();
if (id == gamepad_axis_moved_event::event_type_id)
@ -230,13 +328,13 @@ static void add_control_item(game::context* ctx, const std::string& control_name
if (std::abs(axis_event.value) < 0.5f)
return;
ctx->input_event_router->add_mapping(input::gamepad_axis_mapping(control, nullptr, axis_event.axis, (axis_event.value < 0)));
ctx.input_event_router->add_mapping(input::gamepad_axis_mapping(control, nullptr, axis_event.axis, (axis_event.value < 0)));
}
else if (id == gamepad_button_pressed_event::event_type_id)
{
// Map gamepad button event to control
const gamepad_button_pressed_event& button_event = static_cast<const gamepad_button_pressed_event&>(event);
ctx->input_event_router->add_mapping(input::gamepad_button_mapping(control, nullptr, button_event.button));
ctx.input_event_router->add_mapping(input::gamepad_button_mapping(control, nullptr, button_event.button));
}
else if (id == key_pressed_event::event_type_id)
{
@ -252,112 +350,26 @@ static void add_control_item(game::context* ctx, const std::string& control_name
}
// Update menu text
value_text->set_content(get_binding_string(ctx, control));
value_text->set_content(this->get_binding_string(control));
game::menu::align_text(ctx);
game::menu::update_text_tweens(ctx);
// Disable input listener
ctx->input_listener->set_enabled(false);
ctx->input_listener->set_callback(nullptr);
ctx.input_listener->set_enabled(false);
ctx.input_listener->set_callback(nullptr);
// Queue menu control setup
ctx->function_queue.push(std::bind(game::menu::setup_controls, ctx));
ctx.function_queue.push(std::bind(game::menu::setup_controls, std::ref(ctx)));
}
);
ctx->input_listener->set_enabled(true);
ctx.input_listener->set_enabled(true);
};
// Register menu item callbacks
ctx->menu_select_callbacks.push_back(select_callback);
ctx->menu_left_callbacks.push_back(nullptr);
ctx->menu_right_callbacks.push_back(nullptr);
}
void enter(game::context* ctx)
{
// Add camera control menu items
add_control_item(ctx, "move_forward");
add_control_item(ctx, "move_back");
add_control_item(ctx, "move_left");
add_control_item(ctx, "move_right");
add_control_item(ctx, "move_up");
add_control_item(ctx, "move_down");
// Add application control menu items
add_control_item(ctx, "toggle_fullscreen");
add_control_item(ctx, "screenshot");
// Construct menu item texts
scene::text* back_text = new scene::text();
// Build list of menu item texts
ctx->menu_item_texts.push_back({back_text, nullptr});
// Set content of menu item texts
back_text->set_content((*ctx->strings)["back"]);
// Init menu item index
game::menu::init_menu_item_index(ctx, "gamepad_config");
game::menu::update_text_color(ctx);
game::menu::update_text_font(ctx);
game::menu::align_text(ctx);
game::menu::update_text_tweens(ctx);
game::menu::add_text_to_ui(ctx);
game::menu::setup_animations(ctx);
// Construct menu item callbacks
auto select_back_callback = [ctx]()
{
// Disable controls
game::menu::clear_controls(ctx);
game::menu::fade_out
(
ctx,
[ctx]()
{
application::state next_state;
next_state.name = "controls_menu";
next_state.enter = std::bind(game::state::controls_menu::enter, ctx);
next_state.exit = std::bind(game::state::controls_menu::exit, ctx);
ctx->app->queue_state(next_state);
}
);
};
// Build list of menu select callbacks
ctx->menu_select_callbacks.push_back(select_back_callback);
// Build list of menu left callbacks
ctx->menu_left_callbacks.push_back(nullptr);
// Build list of menu right callbacks
ctx->menu_right_callbacks.push_back(nullptr);
// Set menu back callback
ctx->menu_back_callback = select_back_callback;
// Queue menu control setup
ctx->function_queue.push(std::bind(game::menu::setup_controls, ctx));
// Fade in menu
game::menu::fade_in(ctx, nullptr);
}
void exit(game::context* ctx)
{
// Destruct menu
game::menu::clear_controls(ctx);
game::menu::clear_callbacks(ctx);
game::menu::delete_animations(ctx);
game::menu::remove_text_from_ui(ctx);
game::menu::delete_text(ctx);
// Save control profile
game::save_control_profile(ctx);
ctx.menu_select_callbacks.push_back(select_callback);
ctx.menu_left_callbacks.push_back(nullptr);
ctx.menu_right_callbacks.push_back(nullptr);
}
} // namespace gamepad_config_menu
} // namespace state
} // namespace game

src/game/states/gamepad-config-menu.hpp → src/game/state/gamepad-config-menu.hpp View File

@ -20,18 +20,22 @@
#ifndef ANTKEEPER_GAME_STATE_GAMEPAD_CONFIG_MENU_HPP
#define ANTKEEPER_GAME_STATE_GAMEPAD_CONFIG_MENU_HPP
#include "game/context.hpp"
#include "game/state/base.hpp"
#include "input/control.hpp"
namespace game {
namespace state {
/// Gamepad config menu screen game state functions.
namespace gamepad_config_menu {
void enter(game::context* ctx);
void exit(game::context* ctx);
} // namespace gamepad_config_menu
class gamepad_config_menu: public game::state::base
{
public:
gamepad_config_menu(game::context& ctx);
virtual ~gamepad_config_menu();
private:
std::string get_binding_string(input::control* control);
void add_control_item(const std::string& control_name);
};
} // namespace state
} // namespace game

+ 357
- 0
src/game/state/graphics-menu.cpp View File

@ -0,0 +1,357 @@
/*
* Copyright (C) 2021 Christopher J. Howard
*
* This file is part of Antkeeper source code.
*
* Antkeeper source code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#include "game/state/graphics-menu.hpp"
#include "game/state/options-menu.hpp"
#include "application.hpp"
#include "scene/text.hpp"
#include "debug/logger.hpp"
#include "game/fonts.hpp"
#include "game/menu.hpp"
#include "game/graphics.hpp"
#include "animation/timeline.hpp"
namespace game {
namespace state {
static void update_value_text_content(game::context* ctx);
graphics_menu::graphics_menu(game::context& ctx):
game::state::base(ctx)
{
ctx.logger->push_task("Entering graphics menu state");
// Construct menu item texts
scene::text* fullscreen_name_text = new scene::text();
scene::text* fullscreen_value_text = new scene::text();
scene::text* resolution_name_text = new scene::text();
scene::text* resolution_value_text = new scene::text();
scene::text* v_sync_name_text = new scene::text();
scene::text* v_sync_value_text = new scene::text();
scene::text* font_size_name_text = new scene::text();
scene::text* font_size_value_text = new scene::text();
scene::text* dyslexia_font_name_text = new scene::text();
scene::text* dyslexia_font_value_text = new scene::text();
scene::text* back_text = new scene::text();
// Build list of menu item texts
ctx.menu_item_texts.push_back({fullscreen_name_text, fullscreen_value_text});
ctx.menu_item_texts.push_back({resolution_name_text, resolution_value_text});
ctx.menu_item_texts.push_back({v_sync_name_text, v_sync_value_text});
ctx.menu_item_texts.push_back({font_size_name_text, font_size_value_text});
ctx.menu_item_texts.push_back({dyslexia_font_name_text, dyslexia_font_value_text});
ctx.menu_item_texts.push_back({back_text, nullptr});
// Set content of menu item texts
fullscreen_name_text->set_content((*ctx.strings)["graphics_menu_fullscreen"]);
resolution_name_text->set_content((*ctx.strings)["graphics_menu_resolution"]);
v_sync_name_text->set_content((*ctx.strings)["graphics_menu_v_sync"]);
font_size_name_text->set_content((*ctx.strings)["graphics_menu_font_size"]);
dyslexia_font_name_text->set_content((*ctx.strings)["graphics_menu_dyslexia_font"]);
back_text->set_content((*ctx.strings)["back"]);
update_value_text_content();
// Init menu item index
game::menu::init_menu_item_index(ctx, "graphics");
game::menu::update_text_color(ctx);
game::menu::update_text_font(ctx);
game::menu::align_text(ctx);
game::menu::update_text_tweens(ctx);
game::menu::add_text_to_ui(ctx);
game::menu::setup_animations(ctx);
// Construct menu item callbacks
auto toggle_fullscreen_callback = [this, &ctx]()
{
bool fullscreen = !ctx.app->is_fullscreen();
ctx.app->set_fullscreen(fullscreen);
if (!fullscreen)
{
int2 resolution;
resolution.x = (*ctx.config)["windowed_resolution"][0].get<int>();
resolution.y = (*ctx.config)["windowed_resolution"][1].get<int>();
ctx.app->resize_window(resolution.x, resolution.y);
}
this->update_value_text_content();
game::menu::align_text(ctx);
game::menu::update_text_tweens(ctx);
// Save display mode config
(*ctx.config)["fullscreen"] = fullscreen;
};
auto increase_resolution_callback = [this, &ctx]()
{
// Increase resolution
if (ctx.controls["menu_modifier"]->is_active())
ctx.render_resolution_scale += 0.05f;
else
ctx.render_resolution_scale += 0.25f;
// Limit resolution
if (ctx.render_resolution_scale > 2.0f)
ctx.render_resolution_scale = 2.0f;
// Resize framebuffers
game::graphics::change_render_resolution(ctx, ctx.render_resolution_scale);
// Update text
this->update_value_text_content();
game::menu::align_text(ctx);
game::menu::update_text_tweens(ctx);
// Update config
(*ctx.config)["render_resolution"] = ctx.render_resolution_scale;
};
auto decrease_resolution_callback = [this, &ctx]()
{
// Increase resolution
if (ctx.controls["menu_modifier"]->is_active())
ctx.render_resolution_scale -= 0.05f;
else
ctx.render_resolution_scale -= 0.25f;
// Limit resolution
if (ctx.render_resolution_scale < 0.25f)
ctx.render_resolution_scale = 0.25f;
// Resize framebuffers
game::graphics::change_render_resolution(ctx, ctx.render_resolution_scale);
// Update text
this->update_value_text_content();
game::menu::align_text(ctx);
game::menu::update_text_tweens(ctx);
// Update config
(*ctx.config)["render_resolution"] = ctx.render_resolution_scale;
};
auto toggle_v_sync_callback = [this, &ctx]()
{
bool v_sync = !ctx.app->get_v_sync();
ctx.app->set_v_sync(v_sync);
this->update_value_text_content();
game::menu::align_text(ctx);
game::menu::update_text_tweens(ctx);
// Save v-sync config
(*ctx.config)["v_sync"] = v_sync;
};
auto increase_font_size_callback = [this, &ctx]()
{
// Increase font size
if (ctx.controls["menu_modifier"]->is_active())
ctx.font_size += 0.01f;
else
ctx.font_size += 0.1f;
// Limit font size
if (ctx.font_size > 2.0f)
ctx.font_size = 2.0f;
// Update value text
this->update_value_text_content();
// Update config
(*ctx.config)["font_size"] = ctx.font_size;
// Reload fonts
ctx.logger->push_task("Reloading fonts");
try
{
game::load_fonts(ctx);
}
catch (...)
{
ctx.logger->pop_task(EXIT_FAILURE);
}
ctx.logger->pop_task(EXIT_SUCCESS);
// Refresh and realign text
game::menu::refresh_text(ctx);
game::menu::align_text(ctx);
game::menu::update_text_tweens(ctx);
};
auto decrease_font_size_callback = [this, &ctx]()
{
// Decrease font size
if (ctx.controls["menu_modifier"]->is_active())
ctx.font_size -= 0.01f;
else
ctx.font_size -= 0.1f;
// Limit font size
if (ctx.font_size < 0.1f)
ctx.font_size = 0.1f;
// Update value text
this->update_value_text_content();
// Update config
(*ctx.config)["font_size"] = ctx.font_size;
// Reload fonts
ctx.logger->push_task("Reloading fonts");
try
{
game::load_fonts(ctx);
}
catch (...)
{
ctx.logger->pop_task(EXIT_FAILURE);
}
ctx.logger->pop_task(EXIT_SUCCESS);
// Refresh and realign text
game::menu::refresh_text(ctx);
game::menu::align_text(ctx);
game::menu::update_text_tweens(ctx);
};
auto toggle_dyslexia_font_callback = [this, &ctx]()
{
ctx.dyslexia_font = !ctx.dyslexia_font;
// Update value text
this->update_value_text_content();
// Save dyslexia font config
(*ctx.config)["dyslexia_font"] = ctx.dyslexia_font;
// Reload fonts
ctx.logger->push_task("Reloading fonts");
try
{
game::load_fonts(ctx);
}
catch (...)
{
ctx.logger->pop_task(EXIT_FAILURE);
}
ctx.logger->pop_task(EXIT_SUCCESS);
// Refresh and realign text
game::menu::refresh_text(ctx);
game::menu::align_text(ctx);
game::menu::update_text_tweens(ctx);
};
auto select_back_callback = [&ctx]()
{
// Disable controls
game::menu::clear_controls(ctx);
game::menu::fade_out
(
ctx,
[&ctx]()
{
// Queue change to options menu state
ctx.function_queue.push
(
[&ctx]()
{
ctx.state_machine.pop();
ctx.state_machine.emplace(new game::state::options_menu(ctx));
}
);
}
);
};
// Build list of menu select callbacks
ctx.menu_select_callbacks.push_back(toggle_fullscreen_callback);
ctx.menu_select_callbacks.push_back(increase_resolution_callback);
ctx.menu_select_callbacks.push_back(toggle_v_sync_callback);
ctx.menu_select_callbacks.push_back(increase_font_size_callback);
ctx.menu_select_callbacks.push_back(toggle_dyslexia_font_callback);
ctx.menu_select_callbacks.push_back(select_back_callback);
// Build list of menu left callbacks
ctx.menu_left_callbacks.push_back(toggle_fullscreen_callback);
ctx.menu_left_callbacks.push_back(decrease_resolution_callback);
ctx.menu_left_callbacks.push_back(toggle_v_sync_callback);
ctx.menu_left_callbacks.push_back(decrease_font_size_callback);
ctx.menu_left_callbacks.push_back(toggle_dyslexia_font_callback);
ctx.menu_left_callbacks.push_back(nullptr);
// Build list of menu right callbacks
ctx.menu_right_callbacks.push_back(toggle_fullscreen_callback);
ctx.menu_right_callbacks.push_back(increase_resolution_callback);
ctx.menu_right_callbacks.push_back(toggle_v_sync_callback);
ctx.menu_right_callbacks.push_back(increase_font_size_callback);
ctx.menu_right_callbacks.push_back(toggle_dyslexia_font_callback);
ctx.menu_right_callbacks.push_back(nullptr);
// Set menu back callback
ctx.menu_back_callback = select_back_callback;
// Queue menu control setup
ctx.function_queue.push(std::bind(game::menu::setup_controls, std::ref(ctx)));
// Fade in menu
game::menu::fade_in(ctx, nullptr);
ctx.logger->pop_task(EXIT_SUCCESS);
}
graphics_menu::~graphics_menu()
{
ctx.logger->push_task("Exiting graphics menu state");
// Destruct menu
game::menu::clear_controls(ctx);
game::menu::clear_callbacks(ctx);
game::menu::delete_animations(ctx);
game::menu::remove_text_from_ui(ctx);
game::menu::delete_text(ctx);
ctx.logger->pop_task(EXIT_SUCCESS);
}
void graphics_menu::update_value_text_content()
{
bool fullscreen = ctx.app->is_fullscreen();
float resolution = ctx.render_resolution_scale;
bool v_sync = ctx.app->get_v_sync();
float font_size = ctx.font_size;
bool dyslexia_font = ctx.dyslexia_font;
const std::string string_on = (*ctx.strings)["on"];
const std::string string_off = (*ctx.strings)["off"];
std::get<1>(ctx.menu_item_texts[0])->set_content((fullscreen) ? string_on : string_off);
std::get<1>(ctx.menu_item_texts[1])->set_content(std::to_string(static_cast<int>(std::round(resolution * 100.0f))) + "%");
std::get<1>(ctx.menu_item_texts[2])->set_content((v_sync) ? string_on : string_off);
std::get<1>(ctx.menu_item_texts[3])->set_content(std::to_string(static_cast<int>(std::round(font_size * 100.0f))) + "%");
std::get<1>(ctx.menu_item_texts[4])->set_content((dyslexia_font) ? string_on : string_off);
}
} // namespace state
} // namespace game

src/game/states/graphics-menu.hpp → src/game/state/graphics-menu.hpp View File

@ -20,18 +20,20 @@
#ifndef ANTKEEPER_GAME_STATE_GRAPHICS_MENU_HPP
#define ANTKEEPER_GAME_STATE_GRAPHICS_MENU_HPP
#include "game/context.hpp"
#include "game/state/base.hpp"
namespace game {
namespace state {
/// Sound menu screen game state functions.
namespace graphics_menu {
void enter(game::context* ctx);
void exit(game::context* ctx);
} // namespace graphics_menu
class graphics_menu: public game::state::base
{
public:
graphics_menu(game::context& ctx);
virtual ~graphics_menu();
private:
void update_value_text_content();
};
} // namespace state
} // namespace game

src/game/states/keyboard-config-menu.cpp → src/game/state/keyboard-config-menu.cpp View File

@ -17,8 +17,8 @@
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#include "game/states/keyboard-config-menu.hpp"
#include "game/states/controls-menu.hpp"
#include "game/state/keyboard-config-menu.hpp"
#include "game/state/controls-menu.hpp"
#include "application.hpp"
#include "scene/text.hpp"
#include "debug/logger.hpp"
@ -28,13 +28,110 @@
namespace game {
namespace state {
namespace keyboard_config_menu {
static std::string get_binding_string(game::context* ctx, input::control* control)
keyboard_config_menu::keyboard_config_menu(game::context& ctx):
game::state::base(ctx)
{
ctx.logger->push_task("Entering keyboard config menu state");
// Add camera control menu items
add_control_item("move_forward");
add_control_item("move_back");
add_control_item("move_left");
add_control_item("move_right");
add_control_item("move_up");
add_control_item("move_down");
// Add application control menu items
add_control_item("toggle_fullscreen");
add_control_item("screenshot");
// Construct menu item texts
scene::text* back_text = new scene::text();
// Build list of menu item texts
ctx.menu_item_texts.push_back({back_text, nullptr});
// Set content of menu item texts
back_text->set_content((*ctx.strings)["back"]);
// Init menu item index
game::menu::init_menu_item_index(ctx, "keyboard_config");
game::menu::update_text_color(ctx);
game::menu::update_text_font(ctx);
game::menu::align_text(ctx);
game::menu::update_text_tweens(ctx);
game::menu::add_text_to_ui(ctx);
game::menu::setup_animations(ctx);
// Construct menu item callbacks
auto select_back_callback = [&ctx]()
{
// Disable controls
game::menu::clear_controls(ctx);
game::menu::fade_out
(
ctx,
[&ctx]()
{
// Queue change to controls menu state
ctx.function_queue.push
(
[&ctx]()
{
ctx.state_machine.pop();
ctx.state_machine.emplace(new game::state::controls_menu(ctx));
}
);
}
);
};
// Build list of menu select callbacks
ctx.menu_select_callbacks.push_back(select_back_callback);
// Build list of menu left callbacks
ctx.menu_left_callbacks.push_back(nullptr);
// Build list of menu right callbacks
ctx.menu_right_callbacks.push_back(nullptr);
// Set menu back callback
ctx.menu_back_callback = select_back_callback;
// Queue menu control setup
ctx.function_queue.push(std::bind(game::menu::setup_controls, std::ref(ctx)));
// Fade in menu
game::menu::fade_in(ctx, nullptr);
ctx.logger->pop_task(EXIT_SUCCESS);
}
keyboard_config_menu::~keyboard_config_menu()
{
ctx.logger->push_task("Exiting keyboard config menu state");
// Destruct menu
game::menu::clear_controls(ctx);
game::menu::clear_callbacks(ctx);
game::menu::delete_animations(ctx);
game::menu::remove_text_from_ui(ctx);
game::menu::delete_text(ctx);
// Save control profile
game::save_control_profile(ctx);
ctx.logger->pop_task(EXIT_SUCCESS);
}
std::string keyboard_config_menu::get_binding_string(input::control* control)
{
std::string binding_string;
auto mappings = ctx->input_event_router->get_mappings(control);
auto mappings = ctx.input_event_router->get_mappings(control);
for (input::mapping* mapping: *mappings)
{
std::string mapping_string;
@ -57,19 +154,19 @@ static std::string get_binding_string(game::context* ctx, input::control* contro
switch (wheel_mapping->axis)
{
case input::mouse_wheel_axis::negative_x:
mapping_string = (*ctx->strings)["mouse_wheel_left"];
mapping_string = (*ctx.strings)["mouse_wheel_left"];
break;
case input::mouse_wheel_axis::positive_x:
mapping_string = (*ctx->strings)["mouse_wheel_right"];
mapping_string = (*ctx.strings)["mouse_wheel_right"];
break;
case input::mouse_wheel_axis::negative_y:
mapping_string = (*ctx->strings)["mouse_wheel_down"];
mapping_string = (*ctx.strings)["mouse_wheel_down"];
break;
case input::mouse_wheel_axis::positive_y:
mapping_string = (*ctx->strings)["mouse_wheel_up"];
mapping_string = (*ctx.strings)["mouse_wheel_up"];
break;
default:
@ -84,19 +181,19 @@ static std::string get_binding_string(game::context* ctx, input::control* contro
if (button_mapping->button == 1)
{
mapping_string = (*ctx->strings)["mouse_button_left"];
mapping_string = (*ctx.strings)["mouse_button_left"];
}
else if (button_mapping->button == 2)
{
mapping_string = (*ctx->strings)["mouse_button_middle"];
mapping_string = (*ctx.strings)["mouse_button_middle"];
}
else if (button_mapping->button == 3)
{
mapping_string = (*ctx->strings)["mouse_button_right"];
mapping_string = (*ctx.strings)["mouse_button_right"];
}
else
{
const std::string& format = (*ctx->strings)["mouse_button_n"];
const std::string& format = (*ctx.strings)["mouse_button_n"];
char buffer[64];
std::snprintf(buffer, 64, format.c_str(), button_mapping->button);
mapping_string = buffer;
@ -124,32 +221,32 @@ static std::string get_binding_string(game::context* ctx, input::control* contro
return binding_string;
}
static void add_control_item(game::context* ctx, const std::string& control_name)
void keyboard_config_menu::add_control_item(const std::string& control_name)
{
// Get pointer to control
input::control* control = ctx->controls[control_name];
input::control* control = ctx.controls[control_name];
// Construct texts
scene::text* name_text = new scene::text();
scene::text* value_text = new scene::text();
// Add texts to list of menu item texts
ctx->menu_item_texts.push_back({name_text, value_text});
ctx.menu_item_texts.push_back({name_text, value_text});
// Set content of name text
std::string string_name = "control_" + control_name;
if (auto it = ctx->strings->find(string_name); it != ctx->strings->end())
if (auto it = ctx.strings->find(string_name); it != ctx.strings->end())
name_text->set_content(it->second);
else
name_text->set_content(control_name);
// Set content of value text
value_text->set_content(get_binding_string(ctx, control));
value_text->set_content(get_binding_string( control));
auto select_callback = [ctx, control, value_text]()
auto select_callback = [this, &ctx = this->ctx, control, value_text]()
{
// Clear binding string from value text
value_text->set_content((*ctx->strings)["ellipsis"]);
value_text->set_content((*ctx.strings)["ellipsis"]);
game::menu::align_text(ctx);
game::menu::update_text_tweens(ctx);
@ -157,15 +254,15 @@ static void add_control_item(game::context* ctx, const std::string& control_name
game::menu::clear_controls(ctx);
// Remove keyboard and mouse event mappings from control
ctx->input_event_router->remove_mappings(control, input::mapping_type::key);
ctx->input_event_router->remove_mappings(control, input::mapping_type::mouse_motion);
ctx->input_event_router->remove_mappings(control, input::mapping_type::mouse_wheel);
ctx->input_event_router->remove_mappings(control, input::mapping_type::mouse_button);
ctx.input_event_router->remove_mappings(control, input::mapping_type::key);
ctx.input_event_router->remove_mappings(control, input::mapping_type::mouse_motion);
ctx.input_event_router->remove_mappings(control, input::mapping_type::mouse_wheel);
ctx.input_event_router->remove_mappings(control, input::mapping_type::mouse_button);
// Setup input binding listener
ctx->input_listener->set_callback
ctx.input_listener->set_callback
(
[ctx, control, value_text](const event_base& event)
[this, &ctx, control, value_text](const event_base& event)
{
auto id = event.get_event_type_id();
if (id == key_pressed_event::event_type_id)
@ -174,7 +271,7 @@ static void add_control_item(game::context* ctx, const std::string& control_name
const key_pressed_event& key_event = static_cast<const key_pressed_event&>(event);
if (key_event.scancode != input::scancode::escape && key_event.scancode != input::scancode::backspace)
ctx->input_event_router->add_mapping(input::key_mapping(control, key_event.keyboard, key_event.scancode));
ctx.input_event_router->add_mapping(input::key_mapping(control, key_event.keyboard, key_event.scancode));
}
else if (id == mouse_wheel_scrolled_event::event_type_id)
{
@ -193,13 +290,13 @@ static void add_control_item(game::context* ctx, const std::string& control_name
else
return;
ctx->input_event_router->add_mapping(input::mouse_wheel_mapping(control, wheel_event.mouse, axis));
ctx.input_event_router->add_mapping(input::mouse_wheel_mapping(control, wheel_event.mouse, axis));
}
else if (id == mouse_button_pressed_event::event_type_id)
{
// Map mouse button pressed event to control
const mouse_button_pressed_event& button_event = static_cast<const mouse_button_pressed_event&>(event);
ctx->input_event_router->add_mapping(input::mouse_button_mapping(control, button_event.mouse, button_event.button));
ctx.input_event_router->add_mapping(input::mouse_button_mapping(control, button_event.mouse, button_event.button));
}
else
{
@ -207,112 +304,26 @@ static void add_control_item(game::context* ctx, const std::string& control_name
}
// Update menu text
value_text->set_content(get_binding_string(ctx, control));
value_text->set_content(this->get_binding_string(control));
game::menu::align_text(ctx);
game::menu::update_text_tweens(ctx);
// Disable input listener
ctx->input_listener->set_enabled(false);
ctx->input_listener->set_callback(nullptr);
ctx.input_listener->set_enabled(false);
ctx.input_listener->set_callback(nullptr);
// Queue menu control setup
ctx->function_queue.push(std::bind(game::menu::setup_controls, ctx));
ctx.function_queue.push(std::bind(game::menu::setup_controls, std::ref(ctx)));
}
);
ctx->input_listener->set_enabled(true);
ctx.input_listener->set_enabled(true);
};
// Register menu item callbacks
ctx->menu_select_callbacks.push_back(select_callback);
ctx->menu_left_callbacks.push_back(nullptr);
ctx->menu_right_callbacks.push_back(nullptr);
}
void enter(game::context* ctx)
{
// Add camera control menu items
add_control_item(ctx, "move_forward");
add_control_item(ctx, "move_back");
add_control_item(ctx, "move_left");
add_control_item(ctx, "move_right");
add_control_item(ctx, "move_up");
add_control_item(ctx, "move_down");
// Add application control menu items
add_control_item(ctx, "toggle_fullscreen");
add_control_item(ctx, "screenshot");
// Construct menu item texts
scene::text* back_text = new scene::text();
// Build list of menu item texts
ctx->menu_item_texts.push_back({back_text, nullptr});
// Set content of menu item texts
back_text->set_content((*ctx->strings)["back"]);
// Init menu item index
game::menu::init_menu_item_index(ctx, "keyboard_config");
game::menu::update_text_color(ctx);
game::menu::update_text_font(ctx);
game::menu::align_text(ctx);
game::menu::update_text_tweens(ctx);
game::menu::add_text_to_ui(ctx);
game::menu::setup_animations(ctx);
// Construct menu item callbacks
auto select_back_callback = [ctx]()
{
// Disable controls
game::menu::clear_controls(ctx);
game::menu::fade_out
(
ctx,
[ctx]()
{
application::state next_state;
next_state.name = "controls_menu";
next_state.enter = std::bind(game::state::controls_menu::enter, ctx);
next_state.exit = std::bind(game::state::controls_menu::exit, ctx);
ctx->app->queue_state(next_state);
}
);
};
// Build list of menu select callbacks
ctx->menu_select_callbacks.push_back(select_back_callback);
// Build list of menu left callbacks
ctx->menu_left_callbacks.push_back(nullptr);
// Build list of menu right callbacks
ctx->menu_right_callbacks.push_back(nullptr);
// Set menu back callback
ctx->menu_back_callback = select_back_callback;
// Queue menu control setup
ctx->function_queue.push(std::bind(game::menu::setup_controls, ctx));
// Fade in menu
game::menu::fade_in(ctx, nullptr);
}
void exit(game::context* ctx)
{
// Destruct menu
game::menu::clear_controls(ctx);
game::menu::clear_callbacks(ctx);
game::menu::delete_animations(ctx);
game::menu::remove_text_from_ui(ctx);
game::menu::delete_text(ctx);
// Save control profile
game::save_control_profile(ctx);
ctx.menu_select_callbacks.push_back(select_callback);
ctx.menu_left_callbacks.push_back(nullptr);
ctx.menu_right_callbacks.push_back(nullptr);
}
} // namespace keyboard_config_menu
} // namespace state
} // namespace game

src/game/states/keyboard-config-menu.hpp → src/game/state/keyboard-config-menu.hpp View File

@ -20,18 +20,23 @@
#ifndef ANTKEEPER_GAME_STATE_KEYBOARD_CONFIG_MENU_HPP
#define ANTKEEPER_GAME_STATE_KEYBOARD_CONFIG_MENU_HPP
#include "game/context.hpp"
#include "game/state/base.hpp"
#include "input/control.hpp"
#include <string>
namespace game {
namespace state {
/// Keyboard/mouse config menu screen game state functions.
namespace keyboard_config_menu {
void enter(game::context* ctx);
void exit(game::context* ctx);
} // namespace keyboard_config_menu
class keyboard_config_menu: public game::state::base
{
public:
keyboard_config_menu(game::context& ctx);
virtual ~keyboard_config_menu();
private:
std::string get_binding_string(input::control* control);
void add_control_item(const std::string& control_name);
};
} // namespace state
} // namespace game

src/game/states/language-menu.cpp → src/game/state/language-menu.cpp View File

@ -17,8 +17,8 @@
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#include "game/states/language-menu.hpp"
#include "game/states/options-menu.hpp"
#include "game/state/language-menu.hpp"
#include "game/state/options-menu.hpp"
#include "application.hpp"
#include "scene/text.hpp"
#include "debug/logger.hpp"
@ -27,31 +27,23 @@
namespace game {
namespace state {
namespace language_menu {
static void update_text_content(game::context* ctx)
language_menu::language_menu(game::context& ctx):
game::state::base(ctx)
{
auto [language_name, language_value] = ctx->menu_item_texts[0];
auto [back_name, back_value] = ctx->menu_item_texts[1];
ctx.logger->push_task("Entering language menu state");
language_name->set_content((*ctx->strings)["language_menu_language"]);
language_value->set_content((*ctx->strings)["language_name"]);
back_name->set_content((*ctx->strings)["back"]);
}
void enter(game::context* ctx)
{
// Construct menu item texts
scene::text* language_name_text = new scene::text();
scene::text* language_value_text = new scene::text();
scene::text* back_text = new scene::text();
// Build list of menu item texts
ctx->menu_item_texts.push_back({language_name_text, language_value_text});
ctx->menu_item_texts.push_back({back_text, nullptr});
ctx.menu_item_texts.push_back({language_name_text, language_value_text});
ctx.menu_item_texts.push_back({back_text, nullptr});
// Set content of menu item texts
update_text_content(ctx);
update_text_content();
// Init menu item index
game::menu::init_menu_item_index(ctx, "language");
@ -64,75 +56,75 @@ void enter(game::context* ctx)
game::menu::setup_animations(ctx);
// Construct menu item callbacks
auto next_language_callback = [ctx]()
auto next_language_callback = [this, &ctx]()
{
// Increment language index
++ctx->language_index;
if (ctx->language_index >= ctx->language_count)
ctx->language_index = 0;
++ctx.language_index;
if (ctx.language_index >= ctx.language_count)
ctx.language_index = 0;
// Find corresponding language code and strings
ctx->language_code = (*ctx->string_table)[0][ctx->language_index + 2];
ctx->strings = &ctx->string_table_map[ctx->language_code];
ctx.language_code = (*ctx.string_table)[0][ctx.language_index + 2];
ctx.strings = &ctx.string_table_map[ctx.language_code];
// Update language in config
(*ctx->config)["language"] = ctx->language_code;
(*ctx.config)["language"] = ctx.language_code;
ctx->logger->log("Language changed to \"" + ctx->language_code + "\"");
ctx.logger->log("Language changed to \"" + ctx.language_code + "\"");
// Reload fonts
ctx->logger->push_task("Reloading fonts");
ctx.logger->push_task("Reloading fonts");
try
{
game::load_fonts(ctx);
}
catch (...)
{
ctx->logger->pop_task(EXIT_FAILURE);
ctx.logger->pop_task(EXIT_FAILURE);
}
ctx->logger->pop_task(EXIT_SUCCESS);
ctx.logger->pop_task(EXIT_SUCCESS);
game::menu::update_text_font(ctx);
update_text_content(ctx);
this->update_text_content();
game::menu::refresh_text(ctx);
game::menu::align_text(ctx);
game::menu::update_text_tweens(ctx);
};
auto previous_language_callback = [ctx]()
auto previous_language_callback = [this, &ctx]()
{
// Increment language index
--ctx->language_index;
if (ctx->language_index < 0)
ctx->language_index = ctx->language_count - 1;
--ctx.language_index;
if (ctx.language_index < 0)
ctx.language_index = ctx.language_count - 1;
// Find corresponding language code and strings
ctx->language_code = (*ctx->string_table)[0][ctx->language_index + 2];
ctx->strings = &ctx->string_table_map[ctx->language_code];
ctx.language_code = (*ctx.string_table)[0][ctx.language_index + 2];
ctx.strings = &ctx.string_table_map[ctx.language_code];
// Update language in config
(*ctx->config)["language"] = ctx->language_code;
(*ctx.config)["language"] = ctx.language_code;
ctx->logger->log("Language changed to \"" + ctx->language_code + "\"");
ctx.logger->log("Language changed to \"" + ctx.language_code + "\"");
// Reload fonts
ctx->logger->push_task("Reloading fonts");
ctx.logger->push_task("Reloading fonts");
try
{
game::load_fonts(ctx);
}
catch (...)
{
ctx->logger->pop_task(EXIT_FAILURE);
ctx.logger->pop_task(EXIT_FAILURE);
}
ctx->logger->pop_task(EXIT_SUCCESS);
ctx.logger->pop_task(EXIT_SUCCESS);
game::menu::update_text_font(ctx);
update_text_content(ctx);
this->update_text_content();
game::menu::refresh_text(ctx);
game::menu::align_text(ctx);
game::menu::update_text_tweens(ctx);
};
auto select_back_callback = [ctx]()
auto select_back_callback = [&ctx]()
{
// Disable controls
game::menu::clear_controls(ctx);
@ -140,49 +132,68 @@ void enter(game::context* ctx)
game::menu::fade_out
(
ctx,
[ctx]()
[&ctx]()
{
application::state next_state;
next_state.name = "options_menu";
next_state.enter = std::bind(game::state::options_menu::enter, ctx);
next_state.exit = std::bind(game::state::options_menu::exit, ctx);
ctx->app->queue_state(next_state);
// Queue change to options menu state
ctx.function_queue.push
(
[&ctx]()
{
ctx.state_machine.pop();
ctx.state_machine.emplace(new game::state::options_menu(ctx));
}
);
}
);
};
// Build list of menu select callbacks
ctx->menu_select_callbacks.push_back(next_language_callback);
ctx->menu_select_callbacks.push_back(select_back_callback);
ctx.menu_select_callbacks.push_back(next_language_callback);
ctx.menu_select_callbacks.push_back(select_back_callback);
// Build list of menu left callbacks
ctx->menu_left_callbacks.push_back(previous_language_callback);
ctx->menu_left_callbacks.push_back(nullptr);
ctx.menu_left_callbacks.push_back(previous_language_callback);
ctx.menu_left_callbacks.push_back(nullptr);
// Build list of menu right callbacks
ctx->menu_right_callbacks.push_back(next_language_callback);
ctx->menu_right_callbacks.push_back(nullptr);
ctx.menu_right_callbacks.push_back(next_language_callback);
ctx.menu_right_callbacks.push_back(nullptr);
// Set menu back callback
ctx->menu_back_callback = select_back_callback;
ctx.menu_back_callback = select_back_callback;
// Queue menu control setup
ctx->function_queue.push(std::bind(game::menu::setup_controls, ctx));
ctx.function_queue.push(std::bind(game::menu::setup_controls, std::ref(ctx)));
// Fade in menu
game::menu::fade_in(ctx, nullptr);
ctx.logger->pop_task(EXIT_SUCCESS);
}
void exit(game::context* ctx)
language_menu::~language_menu()
{
ctx.logger->push_task("Exiting language menu state");
// Destruct menu
game::menu::clear_controls(ctx);
game::menu::clear_callbacks(ctx);
game::menu::delete_animations(ctx);
game::menu::remove_text_from_ui(ctx);
game::menu::delete_text(ctx);
ctx.logger->pop_task(EXIT_SUCCESS);
}
void language_menu::update_text_content()
{
auto [language_name, language_value] = ctx.menu_item_texts[0];
auto [back_name, back_value] = ctx.menu_item_texts[1];
language_name->set_content((*ctx.strings)["language_menu_language"]);
language_value->set_content((*ctx.strings)["language_name"]);
back_name->set_content((*ctx.strings)["back"]);
}
} // namespace language_menu
} // namespace state
} // namespace game

src/game/states/language-menu.hpp → src/game/state/language-menu.hpp View File

@ -20,18 +20,21 @@
#ifndef ANTKEEPER_GAME_STATE_LANGUAGE_MENU_HPP
#define ANTKEEPER_GAME_STATE_LANGUAGE_MENU_HPP
#include "game/context.hpp"
#include "game/state/base.hpp"
namespace game {
namespace state {
/// Language menu screen game state functions.
namespace language_menu {
class language_menu: public game::state::base
{
public:
language_menu(game::context& ctx);
virtual ~language_menu();
private:
void update_text_content();
};
void enter(game::context* ctx);
void exit(game::context* ctx);
} // namespace language_menu
} // namespace state
} // namespace game

+ 284
- 0
src/game/state/main-menu.cpp View File

@ -0,0 +1,284 @@
/*
* Copyright (C) 2021 Christopher J. Howard
*
* This file is part of Antkeeper source code.
*
* Antkeeper source code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#include "game/state/main-menu.hpp"
#include "game/state/options-menu.hpp"
#include "game/state/extras-menu.hpp"
#include "game/state/forage.hpp"
#include "game/state/nuptial-flight.hpp"
#include "game/menu.hpp"
#include "render/passes/clear-pass.hpp"
#include "resources/resource-manager.hpp"
#include "render/model.hpp"
#include "animation/animation.hpp"
#include "animation/animator.hpp"
#include "animation/screen-transition.hpp"
#include "animation/ease.hpp"
#include "animation/timeline.hpp"
#include "application.hpp"
#include <limits>
namespace game {
namespace state {
main_menu::main_menu(game::context& ctx, bool fade_in):
game::state::base(ctx)
{
ctx.logger->push_task("Entering main menu state");
ctx.ui_clear_pass->set_cleared_buffers(true, true, false);
// Construct title text
ctx.title_text = new scene::text();
ctx.title_text->set_material(&ctx.title_font_material);
ctx.title_text->set_font(&ctx.title_font);
ctx.title_text->set_color({1.0f, 1.0f, 1.0f, 1.0f});
ctx.title_text->set_content((*ctx.strings)["title_antkeeper"]);
// Align title text
const auto& title_aabb = static_cast<const geom::aabb<float>&>(ctx.title_text->get_local_bounds());
float title_w = title_aabb.max_point.x - title_aabb.min_point.x;
float title_h = title_aabb.max_point.y - title_aabb.min_point.y;
ctx.title_text->set_translation({std::round(-title_w * 0.5f), std::round(-title_h * 0.5f + (ctx.app->get_viewport_dimensions().y / 3.0f) / 2.0f), 0.0f});
ctx.title_text->update_tweens();
// Add title text to UI
ctx.ui_scene->add_object(ctx.title_text);
// Construct title fade animation
ctx.title_fade_animation = new animation<float>();
animation_channel<float>* opacity_channel = ctx.title_fade_animation->add_channel(0);
ctx.title_fade_animation->set_frame_callback
(
[&ctx](int channel, const float& opacity)
{
float4 color = ctx.title_text->get_color();
color[3] = opacity;
ctx.title_text->set_color(color);
}
);
ctx.animator->add_animation(ctx.title_fade_animation);
// Construct menu item texts
scene::text* start_text = new scene::text();
scene::text* options_text = new scene::text();
scene::text* extras_text = new scene::text();
scene::text* quit_text = new scene::text();
// Build list of menu item texts
ctx.menu_item_texts.push_back({start_text, nullptr});
ctx.menu_item_texts.push_back({options_text, nullptr});
ctx.menu_item_texts.push_back({extras_text, nullptr});
ctx.menu_item_texts.push_back({quit_text, nullptr});
// Set content of menu item texts
start_text->set_content((*ctx.strings)["main_menu_start"]);
options_text->set_content((*ctx.strings)["main_menu_options"]);
extras_text->set_content((*ctx.strings)["main_menu_extras"]);
quit_text->set_content((*ctx.strings)["main_menu_quit"]);
// Init menu item index
game::menu::init_menu_item_index(ctx, "main");
game::menu::update_text_color(ctx);
game::menu::update_text_font(ctx);
game::menu::align_text(ctx, true, false, (-ctx.app->get_viewport_dimensions().y / 3.0f) / 2.0f);
game::menu::update_text_tweens(ctx);
game::menu::add_text_to_ui(ctx);
game::menu::setup_animations(ctx);
auto select_start_callback = [&ctx]()
{
// Disable controls and menu callbacks
game::menu::clear_controls(ctx);
game::menu::clear_callbacks(ctx);
// Create change state function
auto change_state_nuptial_flight = [&ctx]()
{
// Queue change to nuptial state
ctx.function_queue.push
(
[&ctx]()
{
ctx.state_machine.pop();
ctx.state_machine.emplace(new game::state::nuptial_flight(ctx));
}
);
};
// Set up timing
const float fade_out_duration = 1.0f;
// Schedule state change
timeline* timeline = ctx.timeline;
float t = timeline->get_position();
timeline->add_sequence({{t + fade_out_duration, change_state_nuptial_flight}});
// Start fade out to white
ctx.fade_transition_color->set_value({1, 1, 1});
ctx.fade_transition->transition(fade_out_duration, false, ease<float>::out_cubic, false);
};
auto select_options_callback = [this, &ctx]()
{
game::menu::clear_controls(ctx);
// Fade out title
this->fade_out_title();
// Fade out menu
game::menu::fade_out
(
ctx,
[&ctx]()
{
// Queue change to options menu state
ctx.function_queue.push
(
[&ctx]()
{
ctx.state_machine.pop();
ctx.state_machine.emplace(new game::state::options_menu(ctx));
}
);
}
);
};
auto select_extras_callback = [this, &ctx]()
{
// Disable controls
game::menu::clear_controls(ctx);
// Fade out title
this->fade_out_title();
// Fade out menu
game::menu::fade_out
(
ctx,
[&ctx]()
{
// Queue change to extras menu state
ctx.function_queue.push
(
[&ctx]()
{
ctx.state_machine.pop();
ctx.state_machine.emplace(new game::state::extras_menu(ctx));
}
);
}
);
};
auto select_quit_callback = [&ctx]()
{
ctx.app->close();
};
// Build list of menu select callbacks
ctx.menu_select_callbacks.push_back(select_start_callback);
ctx.menu_select_callbacks.push_back(select_options_callback);
ctx.menu_select_callbacks.push_back(select_extras_callback);
ctx.menu_select_callbacks.push_back(select_quit_callback);
// Build list of menu left callbacks
ctx.menu_left_callbacks.push_back(nullptr);
ctx.menu_left_callbacks.push_back(nullptr);
ctx.menu_left_callbacks.push_back(nullptr);
ctx.menu_left_callbacks.push_back(nullptr);
// Build list of menu right callbacks
ctx.menu_right_callbacks.push_back(nullptr);
ctx.menu_right_callbacks.push_back(nullptr);
ctx.menu_right_callbacks.push_back(nullptr);
ctx.menu_right_callbacks.push_back(nullptr);
// Set menu back callback
ctx.menu_back_callback = select_quit_callback;
// Queue menu control setup
ctx.function_queue.push(std::bind(game::menu::setup_controls, std::ref(ctx)));
if (fade_in)
{
ctx.fade_transition->transition(0.5f, true, ease<float>::out_cubic);
}
else
{
// Fade in title
ctx.title_text->set_color({1.0f, 1.0f, 1.0f, 0.0f});
ctx.title_text->update_tweens();
fade_in_title();
// Fade in menu
game::menu::fade_in(ctx, nullptr);
}
ctx.logger->pop_task(EXIT_SUCCESS);
}
main_menu::~main_menu()
{
ctx.logger->push_task("Exiting main menu state");
// Destruct menu
game::menu::clear_controls(ctx);
game::menu::clear_callbacks(ctx);
game::menu::delete_animations(ctx);
game::menu::remove_text_from_ui(ctx);
game::menu::delete_text(ctx);
// Destruct title animation
ctx.animator->remove_animation(ctx.title_fade_animation);
delete ctx.title_fade_animation;
ctx.title_fade_animation = nullptr;
// Destruct title text
ctx.ui_scene->remove_object(ctx.title_text);
delete ctx.title_text;
ctx.title_text = nullptr;
ctx.logger->pop_task(EXIT_SUCCESS);
}
void main_menu::fade_in_title()
{
ctx.title_fade_animation->set_interpolator(ease<float>::out_cubic);
animation_channel<float>* opacity_channel = ctx.title_fade_animation->get_channel(0);
opacity_channel->remove_keyframes();
opacity_channel->insert_keyframe({0.0, 0.0f});
opacity_channel->insert_keyframe({game::menu::fade_in_duration, 1.0f});
ctx.title_fade_animation->stop();
ctx.title_fade_animation->play();
}
void main_menu::fade_out_title()
{
ctx.title_fade_animation->set_interpolator(ease<float>::out_cubic);
animation_channel<float>* opacity_channel = ctx.title_fade_animation->get_channel(0);
opacity_channel->remove_keyframes();
opacity_channel->insert_keyframe({0.0, 1.0f});
opacity_channel->insert_keyframe({game::menu::fade_out_duration, 0.0f});
ctx.title_fade_animation->stop();
ctx.title_fade_animation->play();
}
} // namespace state
} // namespace game

src/game/states/main-menu.hpp → src/game/state/main-menu.hpp View File

@ -20,18 +20,21 @@
#ifndef ANTKEEPER_GAME_STATE_MAIN_MENU_HPP
#define ANTKEEPER_GAME_STATE_MAIN_MENU_HPP
#include "game/context.hpp"
#include "game/state/base.hpp"
namespace game {
namespace state {
/// Main menu screen game state functions.
namespace main_menu {
void enter(game::context* ctx, bool fade_in);
void exit(game::context* ctx);
} // namespace main_menu
class main_menu: public game::state::base
{
public:
main_menu(game::context& ctx, bool fade_in);
virtual ~main_menu();
private:
void fade_in_title();
void fade_out_title();
};
} // namespace state
} // namespace game

+ 544
- 0
src/game/state/nuptial-flight.cpp View File

@ -0,0 +1,544 @@
/*
* Copyright (C) 2021 Christopher J. Howard
*
* This file is part of Antkeeper source code.
*
* Antkeeper source code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#include "game/state/nuptial-flight.hpp"
#include "game/state/pause-menu.hpp"
#include "entity/archetype.hpp"
#include "entity/systems/astronomy.hpp"
#include "entity/systems/orbit.hpp"
#include "entity/systems/camera.hpp"
#include "entity/components/observer.hpp"
#include "entity/components/transform.hpp"
#include "entity/components/terrain.hpp"
#include "entity/components/camera.hpp"
#include "entity/components/constraints/spring-to.hpp"
#include "entity/components/constraints/three-dof.hpp"
#include "entity/components/constraint-stack.hpp"
#include "entity/commands.hpp"
#include "animation/screen-transition.hpp"
#include "animation/ease.hpp"
#include "resources/resource-manager.hpp"
#include "game/world.hpp"
#include "application.hpp"
#include "render/passes/clear-pass.hpp"
#include <memory>
#include <iostream>
#include "state-machine.hpp"
namespace game {
namespace state {
nuptial_flight::nuptial_flight(game::context& ctx):
game::state::base(ctx)
{
ctx.logger->push_task("Entering nuptial flight state");
// Disable UI color clear
ctx.ui_clear_pass->set_cleared_buffers(false, true, false);
// Create world
game::world::create_stars(ctx);
game::world::create_sun(ctx);
game::world::create_planet(ctx);
game::world::create_moon(ctx);
// Set time to solar noon
game::world::set_time(ctx, 0.0);
// Freeze time
game::world::set_time_scale(ctx, 0.0);
// Switch to surface camera
ctx.underground_camera->set_active(false);
ctx.surface_camera->set_active(true);
// Find planet EID by name
entity::id planet_eid = ctx.entities["planet"];
// Remove terrain component from planet (if any)
//if (ctx.entity_registry->has<entity::component::terrain>(planet_eid))
// ctx.entity_registry->remove<entity::component::terrain>(planet_eid);
// Enable clouds in sky pass
//ctx.surface_sky_pass->set_clouds_model(ctx.resource_manager->load<render::model>("cloud-plane.mdl"));
// Create observer
entity::id observer_eid = ctx.entity_registry->create();
{
entity::component::observer observer;
observer.reference_body_eid = planet_eid;
observer.elevation = 2000.0;
observer.latitude = 0.0;
observer.longitude = 0.0;
observer.camera = ctx.surface_camera;
ctx.entity_registry->assign<entity::component::observer>(observer_eid, observer);
// Set reference location of astronomy system
ctx.astronomy_system->set_reference_body(planet_eid);
ctx.astronomy_system->set_observer_location(double3{observer.elevation, observer.latitude, observer.longitude});
}
// Setup camera
setup_camera();
/*
ctx.surface_camera->look_at({0, 0, 1}, {0, 0, 0}, {0, 1, 0});
ctx.surface_camera->set_exposure(-14.5f);
ctx.surface_scene->update_tweens();
*/
// Queue fade in
ctx.fade_transition_color->set_value({1, 1, 1});
ctx.function_queue.push(std::bind(&screen_transition::transition, ctx.fade_transition, 5.0f, true, math::lerp<float, float>, true));
// Queue control setup
ctx.function_queue.push(std::bind(&nuptial_flight::enable_controls, this));
ctx.logger->pop_task(EXIT_SUCCESS);
}
nuptial_flight::~nuptial_flight()
{
ctx.logger->push_task("Exiting nuptial flight state");
// Resume time
//const double time_scale = (*ctx.config)["time_scale"].get<double>();
//game::world::set_time_scale(ctx, time_scale);
ctx.logger->pop_task(EXIT_SUCCESS);
}
void nuptial_flight::setup_camera()
{
// Switch to surface camera
ctx.underground_camera->set_active(false);
ctx.surface_camera->set_active(true);
// Create surface camera entity
if (!ctx.entities.count("surface_cam"))
{
// Create camera target entity
entity::id target_eid = ctx.entity_registry->create();
ctx.entities["surface_cam_target"] = target_eid;
{
// Transform
entity::component::transform target_transform;
target_transform.local = math::identity_transform<float>;
target_transform.world = target_transform.local;
target_transform.warp = true;
ctx.entity_registry->assign<entity::component::transform>(target_eid, target_transform);
}
// Create camera entity
entity::id camera_eid = ctx.entity_registry->create();
ctx.entities["surface_cam"] = camera_eid;
// Create camera transform component
entity::component::transform transform;
transform.local = math::identity_transform<float>;
transform.world = transform.local;
transform.warp = true;
ctx.entity_registry->assign<entity::component::transform>(camera_eid, transform);
// Create camera camera component
entity::component::camera camera;
camera.object = ctx.surface_camera;
ctx.entity_registry->assign<entity::component::camera>(camera_eid, camera);
// Create camera 3DOF constraint entity
entity::id three_dof_constraint_eid = ctx.entity_registry->create();
ctx.entities["surface_cam_3dof"] = three_dof_constraint_eid;
{
// Create 3DOF to constraint
entity::component::constraint::three_dof three_dof;
three_dof.yaw = 0.0f;
three_dof.pitch = 0.0f;
three_dof.roll = 0.0f;
ctx.entity_registry->assign<entity::component::constraint::three_dof>(three_dof_constraint_eid, three_dof);
// Create constraint stack node component
entity::component::constraint_stack_node node;
node.active = true;
node.weight = 1.0f;
node.next = entt::null;
ctx.entity_registry->assign<entity::component::constraint_stack_node>(three_dof_constraint_eid, node);
}
// Create camera spring to constraint entity
entity::id spring_constraint_eid = ctx.entity_registry->create();
{
// Create spring to constraint
entity::component::constraint::spring_to spring;
spring.target = target_eid;
spring.translation = {{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}, 1.0f, math::two_pi<float>};
spring.translation.w = hz_to_rads(8.0f);
spring.spring_translation = true;
spring.spring_rotation = false;
ctx.entity_registry->assign<entity::component::constraint::spring_to>(spring_constraint_eid, spring);
// Create constraint stack node component
entity::component::constraint_stack_node node;
node.active = true;
node.weight = 1.0f;
node.next = three_dof_constraint_eid;
ctx.entity_registry->assign<entity::component::constraint_stack_node>(spring_constraint_eid, node);
}
// Create camera constraint stack component
entity::component::constraint_stack constraint_stack;
constraint_stack.head = spring_constraint_eid;
ctx.entity_registry->assign<entity::component::constraint_stack>(camera_eid, constraint_stack);
}
ctx.surface_camera->set_exposure(-12.0f);
}
void nuptial_flight::enable_controls()
{
// Get camera entities
entity::id camera_eid = ctx.entities["surface_cam"];
entity::id target_eid = ctx.entities["surface_cam_target"];
entity::id three_dof_eid = ctx.entities["surface_cam_3dof"];
const float slow_modifier = 0.25f;
const float fast_modifier = 4.0f;
const float dolly_speed = 20.0f;
const float truck_speed = dolly_speed;
const float pedestal_speed = 30.0f;
float mouse_tilt_sensitivity = 1.0f;
float mouse_pan_sensitivity = 1.0f;
bool mouse_invert_tilt = false;
bool mouse_invert_pan = false;
float gamepad_tilt_sensitivity = 1.0f;
float gamepad_pan_sensitivity = 1.0f;
bool gamepad_invert_tilt = false;
bool gamepad_invert_pan = false;
bool mouse_look_toggle = false;
ctx.mouse_look = false;
if (ctx.config->contains("mouse_tilt_sensitivity"))
mouse_tilt_sensitivity = math::radians((*ctx.config)["mouse_tilt_sensitivity"].get<float>());
if (ctx.config->contains("mouse_pan_sensitivity"))
mouse_pan_sensitivity = math::radians((*ctx.config)["mouse_pan_sensitivity"].get<float>());
if (ctx.config->contains("mouse_invert_tilt"))
mouse_invert_tilt = math::radians((*ctx.config)["mouse_invert_tilt"].get<bool>());
if (ctx.config->contains("mouse_invert_pan"))
mouse_invert_pan = math::radians((*ctx.config)["mouse_invert_pan"].get<bool>());
if (ctx.config->contains("mouse_look_toggle"))
mouse_look_toggle = math::radians((*ctx.config)["mouse_look_toggle"].get<bool>());
if (ctx.config->contains("gamepad_tilt_sensitivity"))
gamepad_tilt_sensitivity = math::radians((*ctx.config)["gamepad_tilt_sensitivity"].get<float>());
if (ctx.config->contains("gamepad_pan_sensitivity"))
gamepad_pan_sensitivity = math::radians((*ctx.config)["gamepad_pan_sensitivity"].get<float>());
if (ctx.config->contains("gamepad_invert_tilt"))
gamepad_invert_tilt = math::radians((*ctx.config)["gamepad_invert_tilt"].get<bool>());
if (ctx.config->contains("gamepad_invert_pan"))
gamepad_invert_pan = math::radians((*ctx.config)["gamepad_invert_pan"].get<bool>());
const input::control* move_slow = ctx.controls["move_slow"];
const input::control* move_fast = ctx.controls["move_fast"];
const input::control* mouse_look = ctx.controls["mouse_look"];
float mouse_tilt_factor = mouse_tilt_sensitivity * (mouse_invert_tilt ? -1.0f : 1.0f);
float mouse_pan_factor = mouse_pan_sensitivity * (mouse_invert_pan ? -1.0f : 1.0f);
float gamepad_tilt_factor = gamepad_tilt_sensitivity * (gamepad_invert_tilt ? -1.0f : 1.0f);
float gamepad_pan_factor = gamepad_pan_sensitivity * (gamepad_invert_pan ? -1.0f : 1.0f);
ctx.controls["move_forward"]->set_active_callback
(
[&ctx = this->ctx, target_eid, three_dof_eid, truck_speed, move_slow, move_fast, slow_modifier, fast_modifier](float value)
{
if (move_slow->is_active())
value *= slow_modifier;
if (move_fast->is_active())
value *= fast_modifier;
auto& three_dof = ctx.entity_registry->get<entity::component::constraint::three_dof>(three_dof_eid);
const math::quaternion<float> yaw = math::angle_axis(three_dof.yaw, {0.0f, 1.0f, 0.0f});
const float3 movement = {0.0f, 0.0f, -truck_speed * value * (1.0f / 60.0f)};
entity::command::translate(*ctx.entity_registry, target_eid, yaw * movement);
}
);
// Dolly backward
ctx.controls["move_back"]->set_active_callback
(
[&ctx = this->ctx, target_eid, three_dof_eid, truck_speed, move_slow, move_fast, slow_modifier, fast_modifier](float value)
{
if (move_slow->is_active())
value *= slow_modifier;
if (move_fast->is_active())
value *= fast_modifier;
auto& three_dof = ctx.entity_registry->get<entity::component::constraint::three_dof>(three_dof_eid);
const math::quaternion<float> yaw = math::angle_axis(three_dof.yaw, {0.0f, 1.0f, 0.0f});
const float3 movement = {0.0f, 0.0f, truck_speed * value * (1.0f / 60.0f)};
entity::command::translate(*ctx.entity_registry, target_eid, yaw * movement);
}
);
// Truck right
ctx.controls["move_right"]->set_active_callback
(
[&ctx = this->ctx, target_eid, three_dof_eid, truck_speed, move_slow, move_fast, slow_modifier, fast_modifier](float value)
{
if (move_slow->is_active())
value *= slow_modifier;
if (move_fast->is_active())
value *= fast_modifier;
auto& three_dof = ctx.entity_registry->get<entity::component::constraint::three_dof>(three_dof_eid);
const math::quaternion<float> yaw = math::angle_axis(three_dof.yaw, {0.0f, 1.0f, 0.0f});
const float3 movement = {truck_speed * value * (1.0f / 60.0f), 0.0f, 0.0f};
entity::command::translate(*ctx.entity_registry, target_eid, yaw * movement);
}
);
// Truck left
ctx.controls["move_left"]->set_active_callback
(
[&ctx = this->ctx, target_eid, three_dof_eid, truck_speed, move_slow, move_fast, slow_modifier, fast_modifier](float value)
{
if (move_slow->is_active())
value *= slow_modifier;
if (move_fast->is_active())
value *= fast_modifier;
auto& three_dof = ctx.entity_registry->get<entity::component::constraint::three_dof>(three_dof_eid);
const math::quaternion<float> yaw = math::angle_axis(three_dof.yaw, {0.0f, 1.0f, 0.0f});
const float3 movement = {-truck_speed * value * (1.0f / 60.0f), 0.0f, 0.0f};
entity::command::translate(*ctx.entity_registry, target_eid, yaw * movement);
}
);
// Pedestal up
ctx.controls["move_up"]->set_active_callback
(
[&ctx = this->ctx, target_eid, pedestal_speed, move_slow, move_fast, slow_modifier, fast_modifier](float value)
{
if (move_slow->is_active())
value *= slow_modifier;
if (move_fast->is_active())
value *= fast_modifier;
const float3 movement = {0.0f, pedestal_speed * value * (1.0f / 60.0f), 0.0f};
entity::command::translate(*ctx.entity_registry, target_eid, movement);
}
);
// Pedestal down
ctx.controls["move_down"]->set_active_callback
(
[&ctx = this->ctx, target_eid, pedestal_speed, move_slow, move_fast, slow_modifier, fast_modifier](float value)
{
if (move_slow->is_active())
value *= slow_modifier;
if (move_fast->is_active())
value *= fast_modifier;
const float3 movement = {0.0f, -pedestal_speed * value * (1.0f / 60.0f), 0.0f};
entity::command::translate(*ctx.entity_registry, target_eid, movement);
}
);
// Mouse rotate
ctx.controls["mouse_look"]->set_activated_callback
(
[&ctx = this->ctx, mouse_look_toggle]()
{
if (mouse_look_toggle)
ctx.mouse_look = !ctx.mouse_look;
else
ctx.mouse_look = true;
ctx.app->set_relative_mouse_mode(ctx.mouse_look);
}
);
ctx.controls["mouse_look"]->set_deactivated_callback
(
[&ctx = this->ctx, mouse_look_toggle]()
{
if (!mouse_look_toggle)
{
ctx.mouse_look = false;
ctx.app->set_relative_mouse_mode(false);
}
}
);
// Pan left
ctx.controls["look_left_gamepad"]->set_active_callback
(
[&ctx = this->ctx, three_dof_eid, gamepad_pan_factor](float value)
{
auto& three_dof = ctx.entity_registry->get<entity::component::constraint::three_dof>(three_dof_eid);
three_dof.yaw += gamepad_pan_factor * value * (1.0f / 60.0f);
}
);
ctx.controls["look_left_mouse"]->set_active_callback
(
[&ctx = this->ctx, three_dof_eid, mouse_pan_factor](float value)
{
if (!ctx.mouse_look)
return;
auto& three_dof = ctx.entity_registry->get<entity::component::constraint::three_dof>(three_dof_eid);
three_dof.yaw += mouse_pan_factor * value * (1.0f / 60.0f);
}
);
// Pan right
ctx.controls["look_right_gamepad"]->set_active_callback
(
[&ctx = this->ctx, three_dof_eid, gamepad_pan_factor](float value)
{
auto& three_dof = ctx.entity_registry->get<entity::component::constraint::three_dof>(three_dof_eid);
three_dof.yaw -= gamepad_pan_factor * value * (1.0f / 60.0f);
}
);
ctx.controls["look_right_mouse"]->set_active_callback
(
[&ctx = this->ctx, three_dof_eid, mouse_pan_factor](float value)
{
if (!ctx.mouse_look)
return;
auto& three_dof = ctx.entity_registry->get<entity::component::constraint::three_dof>(three_dof_eid);
three_dof.yaw -= mouse_pan_factor * value * (1.0f / 60.0f);
}
);
// Tilt up
ctx.controls["look_up_gamepad"]->set_active_callback
(
[&ctx = this->ctx, three_dof_eid, gamepad_tilt_factor](float value)
{
auto& three_dof = ctx.entity_registry->get<entity::component::constraint::three_dof>(three_dof_eid);
three_dof.pitch -= gamepad_tilt_factor * value * (1.0f / 60.0f);
three_dof.pitch = std::max<float>(math::radians(-90.0f), three_dof.pitch);
}
);
ctx.controls["look_up_mouse"]->set_active_callback
(
[&ctx = this->ctx, three_dof_eid, mouse_tilt_factor](float value)
{
if (!ctx.mouse_look)
return;
auto& three_dof = ctx.entity_registry->get<entity::component::constraint::three_dof>(three_dof_eid);
three_dof.pitch -= mouse_tilt_factor * value * (1.0f / 60.0f);
three_dof.pitch = std::max<float>(math::radians(-90.0f), three_dof.pitch);
}
);
// Tilt down
ctx.controls["look_down_gamepad"]->set_active_callback
(
[&ctx = this->ctx, three_dof_eid, gamepad_tilt_factor](float value)
{
auto& three_dof = ctx.entity_registry->get<entity::component::constraint::three_dof>(three_dof_eid);
three_dof.pitch += gamepad_tilt_factor * value * (1.0f / 60.0f);
three_dof.pitch = std::min<float>(math::radians(90.0f), three_dof.pitch);
}
);
ctx.controls["look_down_mouse"]->set_active_callback
(
[&ctx = this->ctx, three_dof_eid, mouse_tilt_factor](float value)
{
if (!ctx.mouse_look)
return;
auto& three_dof = ctx.entity_registry->get<entity::component::constraint::three_dof>(three_dof_eid);
three_dof.pitch += mouse_tilt_factor * value * (1.0f / 60.0f);
three_dof.pitch = std::min<float>(math::radians(90.0f), three_dof.pitch);
}
);
/*
// Use tool
ctx.controls["use_tool"]->set_activated_callback
(
[&ctx]()
{
if (ctx.entities.count("active_tool"))
{
entity::id tool_eid = ctx.entities["active_tool"];
const auto& tool = ctx.entity_registry->get<entity::component::tool>(tool_eid);
if (tool.activated)
tool.activated();
}
}
);
ctx.controls["use_tool"]->set_deactivated_callback
(
[&ctx]()
{
if (ctx.entities.count("active_tool"))
{
entity::id tool_eid = ctx.entities["active_tool"];
const auto& tool = ctx.entity_registry->get<entity::component::tool>(tool_eid);
if (tool.deactivated)
tool.deactivated();
}
}
);
ctx.controls["use_tool"]->set_active_callback
(
[&ctx](float value)
{
if (ctx.entities.count("active_tool"))
{
entity::id tool_eid = ctx.entities["active_tool"];
const auto& tool = ctx.entity_registry->get<entity::component::tool>(tool_eid);
if (tool.active)
tool.active();
}
}
);
*/
// Setup pause control
ctx.controls["pause"]->set_activated_callback
(
[this, &ctx = this->ctx]()
{
// Disable controls
this->disable_controls();
// Set resume callback
ctx.resume_callback = [this, &ctx]()
{
this->enable_controls();
ctx.resume_callback = nullptr;
};
// Push pause menu state
ctx.state_machine.emplace(new game::state::pause_menu(ctx));
}
);
}
void nuptial_flight::disable_controls()
{
ctx.controls["pause"]->set_activated_callback(nullptr);
}
} // namespace state
} // namespace game

src/game/states/nuptial-flight.hpp → src/game/state/nuptial-flight.hpp View File

@ -20,18 +20,22 @@
#ifndef ANTKEEPER_GAME_STATE_NUPTIAL_FLIGHT_HPP
#define ANTKEEPER_GAME_STATE_NUPTIAL_FLIGHT_HPP
#include "game/context.hpp"
#include "game/state/base.hpp"
namespace game {
namespace state {
/// Nuptial flight game state functions.
namespace nuptial_flight {
void enter(game::context* ctx);
void exit(game::context* ctx);
} // namespace nuptial_flight
class nuptial_flight: public game::state::base
{
public:
nuptial_flight(game::context& ctx);
virtual ~nuptial_flight();
private:
void setup_camera();
void enable_controls();
void disable_controls();
};
} // namespace state
} // namespace game

+ 237
- 0
src/game/state/options-menu.cpp View File

@ -0,0 +1,237 @@
/*
* Copyright (C) 2021 Christopher J. Howard
*
* This file is part of Antkeeper source code.
*
* Antkeeper source code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#include "game/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/save.hpp"
#include "game/menu.hpp"
#include "animation/ease.hpp"
#include "animation/animation.hpp"
#include "animation/animator.hpp"
#include "application.hpp"
#include "scene/text.hpp"
#include "debug/logger.hpp"
namespace game {
namespace state {
options_menu::options_menu(game::context& ctx):
game::state::base(ctx)
{
ctx.logger->push_task("Entering options menu state");
// Construct menu item texts
scene::text* controls_text = new scene::text();
scene::text* graphics_text = new scene::text();
scene::text* sound_text = new scene::text();
scene::text* language_text = new scene::text();
scene::text* back_text = new scene::text();
// Set content of menu item texts
controls_text->set_content((*ctx.strings)["options_menu_controls"]);
graphics_text->set_content((*ctx.strings)["options_menu_graphics"]);
sound_text->set_content((*ctx.strings)["options_menu_sound"]);
language_text->set_content((*ctx.strings)["options_menu_language"]);
back_text->set_content((*ctx.strings)["back"]);
// Build list of menu item texts
ctx.menu_item_texts.push_back({controls_text, nullptr});
ctx.menu_item_texts.push_back({graphics_text, nullptr});
ctx.menu_item_texts.push_back({sound_text, nullptr});
ctx.menu_item_texts.push_back({language_text, nullptr});
ctx.menu_item_texts.push_back({back_text, nullptr});
// Init menu item index
game::menu::init_menu_item_index(ctx, "options");
game::menu::update_text_color(ctx);
game::menu::update_text_font(ctx);
game::menu::align_text(ctx, true);
game::menu::update_text_tweens(ctx);
game::menu::add_text_to_ui(ctx);
game::menu::setup_animations(ctx);
// Construct menu item callbacks
auto select_controls_callback = [&ctx]()
{
// Disable controls
game::menu::clear_controls(ctx);
// Return to main menu
game::menu::fade_out
(
ctx,
[&ctx]()
{
// Queue change to controls menu state
ctx.function_queue.push
(
[&ctx]()
{
ctx.state_machine.pop();
ctx.state_machine.emplace(new game::state::controls_menu(ctx));
}
);
}
);
};
auto select_graphics_callback = [&ctx]()
{
// Disable controls
game::menu::clear_controls(ctx);
// Return to main menu
game::menu::fade_out
(
ctx,
[&ctx]()
{
// Queue change to graphics menu state
ctx.function_queue.push
(
[&ctx]()
{
ctx.state_machine.pop();
ctx.state_machine.emplace(new game::state::graphics_menu(ctx));
}
);
}
);
};
auto select_sound_callback = [&ctx]()
{
// Disable controls
game::menu::clear_controls(ctx);
// Return to main menu
game::menu::fade_out
(
ctx,
[&ctx]()
{
// Queue change to sound menu state
ctx.function_queue.push
(
[&ctx]()
{
ctx.state_machine.pop();
ctx.state_machine.emplace(new game::state::sound_menu(ctx));
}
);
}
);
};
auto select_language_callback = [&ctx]()
{
// Disable controls
game::menu::clear_controls(ctx);
// Return to main menu
game::menu::fade_out
(
ctx,
[&ctx]()
{
// Queue change to language menu state
ctx.function_queue.push
(
[&ctx]()
{
ctx.state_machine.pop();
ctx.state_machine.emplace(new game::state::language_menu(ctx));
}
);
}
);
};
auto select_back_callback = [&ctx]()
{
// Disable controls
game::menu::clear_controls(ctx);
// Save config
game::save_config(ctx);
game::menu::fade_out
(
ctx,
[&ctx]()
{
// Queue change to main menu state
ctx.function_queue.push
(
[&ctx]()
{
ctx.state_machine.pop();
if (ctx.resume_callback)
ctx.state_machine.emplace(new game::state::pause_menu(ctx));
else
ctx.state_machine.emplace(new game::state::main_menu(ctx, false));
}
);
}
);
};
// Build list of menu select callbacks
ctx.menu_select_callbacks.push_back(select_controls_callback);
ctx.menu_select_callbacks.push_back(select_graphics_callback);
ctx.menu_select_callbacks.push_back(select_sound_callback);
ctx.menu_select_callbacks.push_back(select_language_callback);
ctx.menu_select_callbacks.push_back(select_back_callback);
// Build list of menu right callbacks
ctx.menu_right_callbacks.resize(5, nullptr);
// Build list of menu left callbacks
ctx.menu_left_callbacks.resize(5, nullptr);
// Set menu back callback
ctx.menu_back_callback = select_back_callback;
// Queue menu control setup
ctx.function_queue.push(std::bind(game::menu::setup_controls, std::ref(ctx)));
// Fade in menu
game::menu::fade_in(ctx, nullptr);
ctx.logger->pop_task(EXIT_SUCCESS);
}
options_menu::~options_menu()
{
ctx.logger->push_task("Exiting options menu state");
// Destruct menu
game::menu::clear_controls(ctx);
game::menu::clear_callbacks(ctx);
game::menu::delete_animations(ctx);
game::menu::remove_text_from_ui(ctx);
game::menu::delete_text(ctx);
ctx.logger->pop_task(EXIT_SUCCESS);
}
} // namespace state
} // namespace game

src/game/states/options-menu.hpp → src/game/state/options-menu.hpp View File

@ -20,18 +20,17 @@
#ifndef ANTKEEPER_GAME_STATE_OPTIONS_MENU_HPP
#define ANTKEEPER_GAME_STATE_OPTIONS_MENU_HPP
#include "game/context.hpp"
#include "game/state/base.hpp"
namespace game {
namespace state {
/// Options menu screen game state functions.
namespace options_menu {
void enter(game::context* ctx);
void exit(game::context* ctx);
} // namespace options_menu
class options_menu: public game::state::base
{
public:
options_menu(game::context& ctx);
virtual ~options_menu();
};
} // namespace state
} // namespace game

src/game/states/pause-menu.cpp → src/game/state/pause-menu.cpp View File

@ -17,22 +17,26 @@
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#include "game/states/pause-menu.hpp"
#include "game/states/main-menu.hpp"
#include "game/states/options-menu.hpp"
#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/menu.hpp"
#include "animation/ease.hpp"
#include "animation/animation.hpp"
#include "animation/animator.hpp"
#include "application.hpp"
#include "scene/text.hpp"
#include "debug/logger.hpp"
namespace game {
namespace state {
namespace pause_menu {
void enter(game::context* ctx)
pause_menu::pause_menu(game::context& ctx):
game::state::base(ctx)
{
ctx.logger->push_task("Entering pause menu state");
// Construct menu item texts
scene::text* resume_text = new scene::text();
scene::text* options_text = new scene::text();
@ -40,16 +44,16 @@ void enter(game::context* ctx)
scene::text* quit_text = new scene::text();
// Set content of menu item texts
resume_text->set_content((*ctx->strings)["pause_menu_resume"]);
options_text->set_content((*ctx->strings)["pause_menu_options"]);
main_menu_text->set_content((*ctx->strings)["pause_menu_main_menu"]);
quit_text->set_content((*ctx->strings)["pause_menu_quit"]);
resume_text->set_content((*ctx.strings)["pause_menu_resume"]);
options_text->set_content((*ctx.strings)["pause_menu_options"]);
main_menu_text->set_content((*ctx.strings)["pause_menu_main_menu"]);
quit_text->set_content((*ctx.strings)["pause_menu_quit"]);
// Build list of menu item texts
ctx->menu_item_texts.push_back({resume_text, nullptr});
ctx->menu_item_texts.push_back({options_text, nullptr});
ctx->menu_item_texts.push_back({main_menu_text, nullptr});
ctx->menu_item_texts.push_back({quit_text, nullptr});
ctx.menu_item_texts.push_back({resume_text, nullptr});
ctx.menu_item_texts.push_back({options_text, nullptr});
ctx.menu_item_texts.push_back({main_menu_text, nullptr});
ctx.menu_item_texts.push_back({quit_text, nullptr});
// Init menu item index
game::menu::init_menu_item_index(ctx, "pause");
@ -62,27 +66,34 @@ void enter(game::context* ctx)
game::menu::setup_animations(ctx);
// Construct menu item callbacks
auto select_resume_callback = [ctx]()
auto select_resume_callback = [&ctx]()
{
// Disable unpause control
ctx->controls["pause"]->set_activated_callback(nullptr);
ctx.controls["pause"]->set_activated_callback(nullptr);
// Disable menu controls
game::menu::clear_controls(ctx);
auto resume_paused_state = [ctx]()
auto resume_paused_state = [&ctx]()
{
ctx->app->queue_state(*ctx->paused_state);
ctx.function_queue.push
(
[&ctx]()
{
ctx.state_machine.pop();
ctx.resume_callback();
}
);
};
// Fade out pause menu then resume paused state
game::menu::fade_out(ctx, resume_paused_state);
game::menu::fade_out_bg(ctx);
};
auto select_options_callback = [ctx]()
auto select_options_callback = [&ctx]()
{
// Disable unpause control
ctx->controls["pause"]->set_activated_callback(nullptr);
ctx.controls["pause"]->set_activated_callback(nullptr);
// Disable menu controls
game::menu::clear_controls(ctx);
@ -91,78 +102,86 @@ void enter(game::context* ctx)
game::menu::fade_out
(
ctx,
[ctx]()
[&ctx]()
{
application::state next_state;
next_state.name = "options_menu";
next_state.enter = std::bind(game::state::options_menu::enter, ctx);
next_state.exit = std::bind(game::state::options_menu::exit, ctx);
ctx->app->queue_state(next_state);
// Queue change to options menu state
ctx.function_queue.push
(
[&ctx]()
{
ctx.state_machine.pop();
ctx.state_machine.emplace(new game::state::options_menu(ctx));
}
);
}
);
};
auto select_main_menu_callback = [ctx]()
auto select_main_menu_callback = [&ctx]()
{
// Disable unpause control
ctx->controls["pause"]->set_activated_callback(nullptr);
ctx.controls["pause"]->set_activated_callback(nullptr);
// Disable menu controls
game::menu::clear_controls(ctx);
// Clear paused state
ctx->paused_state.reset();
//ctx.paused_state.reset();
// Fade out pause menu then return to main menu
game::menu::fade_out
(
ctx,
[ctx]()
[&ctx]()
{
application::state next_state;
next_state.name = "main_menu";
next_state.enter = std::bind(game::state::main_menu::enter, ctx, true);
next_state.exit = std::bind(game::state::main_menu::exit, ctx);
ctx->app->queue_state(next_state);
// Queue change to main menu state
ctx.function_queue.push
(
[&ctx]()
{
ctx.state_machine.pop();
ctx.state_machine.emplace(new game::state::main_menu(ctx, true));
}
);
}
);
};
auto select_quit_callback = [ctx]()
auto select_quit_callback = [&ctx]()
{
// Disable unpause control
ctx->controls["pause"]->set_activated_callback(nullptr);
ctx.controls["pause"]->set_activated_callback(nullptr);
// Disable menu controls
game::menu::clear_controls(ctx);
// Clear paused state
ctx->paused_state.reset();
//ctx.paused_state.reset();
// Fade out then quit
game::menu::fade_out(ctx, std::bind(&application::close, ctx->app, EXIT_SUCCESS));
game::menu::fade_out(ctx, std::bind(&application::close, ctx.app));
};
// Build list of menu select callbacks
ctx->menu_select_callbacks.push_back(select_resume_callback);
ctx->menu_select_callbacks.push_back(select_options_callback);
ctx->menu_select_callbacks.push_back(select_main_menu_callback);
ctx->menu_select_callbacks.push_back(select_quit_callback);
ctx.menu_select_callbacks.push_back(select_resume_callback);
ctx.menu_select_callbacks.push_back(select_options_callback);
ctx.menu_select_callbacks.push_back(select_main_menu_callback);
ctx.menu_select_callbacks.push_back(select_quit_callback);
// Build list of menu right callbacks
ctx->menu_right_callbacks.resize(4, nullptr);
ctx.menu_right_callbacks.resize(4, nullptr);
// Build list of menu left callbacks
ctx->menu_left_callbacks.resize(4, nullptr);
ctx.menu_left_callbacks.resize(4, nullptr);
// Set menu back callback
ctx->menu_back_callback = select_resume_callback;
ctx.menu_back_callback = select_resume_callback;
// Queue control setup
ctx->function_queue.push
ctx.function_queue.push
(
[ctx, select_resume_callback]()
[&ctx, select_resume_callback]()
{
// Enable unpause control
ctx->controls["pause"]->set_activated_callback(select_resume_callback);
ctx.controls["pause"]->set_activated_callback(select_resume_callback);
// Enable menu controls
game::menu::setup_controls(ctx);
@ -171,20 +190,25 @@ void enter(game::context* ctx)
// Fade in menu and menu BG
game::menu::fade_in(ctx, nullptr);
if (!ctx->menu_bg_billboard->is_active())
if (!ctx.menu_bg_billboard->is_active())
game::menu::fade_in_bg(ctx);
ctx.logger->pop_task(EXIT_SUCCESS);
}
void exit(game::context* ctx)
pause_menu::~pause_menu()
{
ctx.logger->push_task("Exiting pause menu state");
// Destruct menu
game::menu::clear_controls(ctx);
game::menu::clear_callbacks(ctx);
game::menu::delete_animations(ctx);
game::menu::remove_text_from_ui(ctx);
game::menu::delete_text(ctx);
ctx.logger->pop_task(EXIT_SUCCESS);
}
} // namespace pause_menu
} // namespace state
} // namespace game

src/game/states/pause-menu.hpp → src/game/state/pause-menu.hpp View File

@ -20,18 +20,17 @@
#ifndef ANTKEEPER_GAME_STATE_PAUSE_MENU_HPP
#define ANTKEEPER_GAME_STATE_PAUSE_MENU_HPP
#include "game/context.hpp"
#include "game/state/base.hpp"
namespace game {
namespace state {
/// Pause menu screen game state functions.
namespace pause_menu {
void enter(game::context* ctx);
void exit(game::context* ctx);
} // namespace pause_menu
class pause_menu: public game::state::base
{
public:
pause_menu(game::context& ctx);
virtual ~pause_menu();
};
} // namespace state
} // namespace game

+ 262
- 0
src/game/state/sound-menu.cpp View File

@ -0,0 +1,262 @@
/*
* Copyright (C) 2021 Christopher J. Howard
*
* This file is part of Antkeeper source code.
*
* Antkeeper source code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#include "game/state/sound-menu.hpp"
#include "game/state/options-menu.hpp"
#include "application.hpp"
#include "scene/text.hpp"
#include "debug/logger.hpp"
#include "game/menu.hpp"
namespace game {
namespace state {
sound_menu::sound_menu(game::context& ctx):
game::state::base(ctx)
{
ctx.logger->push_task("Entering sound menu state");
// Construct menu item texts
scene::text* master_volume_name_text = new scene::text();
scene::text* master_volume_value_text = new scene::text();
scene::text* ambience_volume_name_text = new scene::text();
scene::text* ambience_volume_value_text = new scene::text();
scene::text* effects_volume_name_text = new scene::text();
scene::text* effects_volume_value_text = new scene::text();
scene::text* mono_audio_name_text = new scene::text();
scene::text* mono_audio_value_text = new scene::text();
scene::text* captions_name_text = new scene::text();
scene::text* captions_value_text = new scene::text();
scene::text* captions_size_name_text = new scene::text();
scene::text* captions_size_value_text = new scene::text();
scene::text* back_text = new scene::text();
// Build list of menu item texts
ctx.menu_item_texts.push_back({master_volume_name_text, master_volume_value_text});
ctx.menu_item_texts.push_back({ambience_volume_name_text, ambience_volume_value_text});
ctx.menu_item_texts.push_back({effects_volume_name_text, effects_volume_value_text});
ctx.menu_item_texts.push_back({mono_audio_name_text, mono_audio_value_text});
ctx.menu_item_texts.push_back({captions_name_text, captions_value_text});
ctx.menu_item_texts.push_back({captions_size_name_text, captions_size_value_text});
ctx.menu_item_texts.push_back({back_text, nullptr});
// Set content of menu item texts
master_volume_name_text->set_content((*ctx.strings)["sound_menu_master_volume"]);
ambience_volume_name_text->set_content((*ctx.strings)["sound_menu_ambience_volume"]);
effects_volume_name_text->set_content((*ctx.strings)["sound_menu_effects_volume"]);
mono_audio_name_text->set_content((*ctx.strings)["sound_menu_mono_audio"]);
captions_name_text->set_content((*ctx.strings)["sound_menu_captions"]);
captions_size_name_text->set_content((*ctx.strings)["sound_menu_captions_size"]);
back_text->set_content((*ctx.strings)["back"]);
update_value_text_content();
// Init menu item index
game::menu::init_menu_item_index(ctx, "sound");
game::menu::update_text_color(ctx);
game::menu::update_text_font(ctx);
game::menu::align_text(ctx);
game::menu::update_text_tweens(ctx);
game::menu::add_text_to_ui(ctx);
game::menu::setup_animations(ctx);
// Construct menu item callbacks
auto increase_volume_callback = [this, &ctx](float* volume)
{
// Increase volume
if (ctx.controls["menu_modifier"]->is_active())
*volume += 0.01f;
else
*volume += 0.1f;
// Limit volume
if (*volume > 1.0f)
*volume = 1.0f;
this->update_value_text_content();
game::menu::align_text(ctx);
game::menu::update_text_tweens(ctx);
};
auto decrease_volume_callback = [this, &ctx](float* volume)
{
// Decrease volume
if (ctx.controls["menu_modifier"]->is_active())
*volume -= 0.01f;
else
*volume -= 0.1f;
// Limit volume
if (*volume < 0.0f)
*volume = 0.0f;
this->update_value_text_content();
game::menu::align_text(ctx);
game::menu::update_text_tweens(ctx);
};
auto toggle_mono_audio_callback = [this, &ctx]()
{
ctx.mono_audio = !ctx.mono_audio;
this->update_value_text_content();
game::menu::align_text(ctx);
game::menu::update_text_tweens(ctx);
};
auto toggle_captions_callback = [this, &ctx]()
{
ctx.captions = !ctx.captions;
this->update_value_text_content();
game::menu::align_text(ctx);
game::menu::update_text_tweens(ctx);
};
auto increase_captions_size_callback = [this, &ctx]()
{
// Increase size
if (ctx.controls["menu_modifier"]->is_active())
ctx.captions_size += 0.01f;
else
ctx.captions_size += 0.1f;
// Limit size
if (ctx.captions_size > 2.0f)
ctx.captions_size = 2.0f;
this->update_value_text_content();
game::menu::align_text(ctx);
game::menu::update_text_tweens(ctx);
};
auto decrease_captions_size_callback = [this, &ctx]()
{
// Decrease size
if (ctx.controls["menu_modifier"]->is_active())
ctx.captions_size -= 0.01f;
else
ctx.captions_size -= 0.1f;
// Limit size
if (ctx.captions_size < 0.1f)
ctx.captions_size = 0.1f;
this->update_value_text_content();
game::menu::align_text(ctx);
game::menu::update_text_tweens(ctx);
};
auto select_back_callback = [&ctx]()
{
// Disable controls
game::menu::clear_controls(ctx);
game::menu::fade_out
(
ctx,
[&ctx]()
{
// Queue change to options menu state
ctx.function_queue.push
(
[&ctx]()
{
ctx.state_machine.pop();
ctx.state_machine.emplace(new game::state::options_menu(ctx));
}
);
}
);
};
// Build list of menu select callbacks
ctx.menu_select_callbacks.push_back(std::bind(increase_volume_callback, &ctx.master_volume));
ctx.menu_select_callbacks.push_back(std::bind(increase_volume_callback, &ctx.ambience_volume));
ctx.menu_select_callbacks.push_back(std::bind(increase_volume_callback, &ctx.effects_volume));
ctx.menu_select_callbacks.push_back(toggle_mono_audio_callback);
ctx.menu_select_callbacks.push_back(toggle_captions_callback);
ctx.menu_select_callbacks.push_back(increase_captions_size_callback);
ctx.menu_select_callbacks.push_back(select_back_callback);
// Build list of menu left callbacks
ctx.menu_left_callbacks.push_back(std::bind(decrease_volume_callback, &ctx.master_volume));
ctx.menu_left_callbacks.push_back(std::bind(decrease_volume_callback, &ctx.ambience_volume));
ctx.menu_left_callbacks.push_back(std::bind(decrease_volume_callback, &ctx.effects_volume));
ctx.menu_left_callbacks.push_back(toggle_mono_audio_callback);
ctx.menu_left_callbacks.push_back(toggle_captions_callback);
ctx.menu_left_callbacks.push_back(decrease_captions_size_callback);
ctx.menu_left_callbacks.push_back(nullptr);
// Build list of menu right callbacks
ctx.menu_right_callbacks.push_back(std::bind(increase_volume_callback, &ctx.master_volume));
ctx.menu_right_callbacks.push_back(std::bind(increase_volume_callback, &ctx.ambience_volume));
ctx.menu_right_callbacks.push_back(std::bind(increase_volume_callback, &ctx.effects_volume));
ctx.menu_right_callbacks.push_back(toggle_mono_audio_callback);
ctx.menu_right_callbacks.push_back(toggle_captions_callback);
ctx.menu_right_callbacks.push_back(increase_captions_size_callback);
ctx.menu_right_callbacks.push_back(nullptr);
// Set menu back callback
ctx.menu_back_callback = select_back_callback;
// Queue menu control setup
ctx.function_queue.push(std::bind(game::menu::setup_controls, std::ref(ctx)));
// Fade in menu
game::menu::fade_in(ctx, nullptr);
ctx.logger->pop_task(EXIT_SUCCESS);
}
sound_menu::~sound_menu()
{
ctx.logger->push_task("Exiting sound menu state");
// Destruct menu
game::menu::clear_controls(ctx);
game::menu::clear_callbacks(ctx);
game::menu::delete_animations(ctx);
game::menu::remove_text_from_ui(ctx);
game::menu::delete_text(ctx);
// Update config
(*ctx.config)["master_volume"] = ctx.master_volume;
(*ctx.config)["ambience_volume"] = ctx.ambience_volume;
(*ctx.config)["effects_volume"] = ctx.effects_volume;
(*ctx.config)["mono_audio"] = ctx.mono_audio;
(*ctx.config)["captions"] = ctx.captions;
(*ctx.config)["captions_size"] = ctx.captions_size;
ctx.logger->pop_task(EXIT_SUCCESS);
}
void sound_menu::update_value_text_content()
{
const std::string string_on = (*ctx.strings)["on"];
const std::string string_off = (*ctx.strings)["off"];
std::get<1>(ctx.menu_item_texts[0])->set_content(std::to_string(static_cast<int>(std::round(ctx.master_volume * 100.0f))) + "%");
std::get<1>(ctx.menu_item_texts[1])->set_content(std::to_string(static_cast<int>(std::round(ctx.ambience_volume * 100.0f))) + "%");
std::get<1>(ctx.menu_item_texts[2])->set_content(std::to_string(static_cast<int>(std::round(ctx.effects_volume * 100.0f))) + "%");
std::get<1>(ctx.menu_item_texts[3])->set_content((ctx.mono_audio) ? string_on : string_off);
std::get<1>(ctx.menu_item_texts[4])->set_content((ctx.captions) ? string_on : string_off);
std::get<1>(ctx.menu_item_texts[5])->set_content(std::to_string(static_cast<int>(std::round(ctx.captions_size * 100.0f))) + "%");
}
} // namespace state
} // namespace game

src/game/states/sound-menu.hpp → src/game/state/sound-menu.hpp View File

@ -20,18 +20,20 @@
#ifndef ANTKEEPER_GAME_STATE_SOUND_MENU_HPP
#define ANTKEEPER_GAME_STATE_SOUND_MENU_HPP
#include "game/context.hpp"
#include "game/state/base.hpp"
namespace game {
namespace state {
/// Sound menu screen game state functions.
namespace sound_menu {
void enter(game::context* ctx);
void exit(game::context* ctx);
} // namespace sound_menu
class sound_menu: public game::state::base
{
public:
sound_menu(game::context& ctx);
virtual ~sound_menu();
private:
void update_value_text_content();
};
} // namespace state
} // namespace game

+ 170
- 0
src/game/state/splash.cpp View File

@ -0,0 +1,170 @@
/*
* Copyright (C) 2021 Christopher J. Howard
*
* This file is part of Antkeeper source code.
*
* Antkeeper source code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#include "game/state/splash.hpp"
#include "game/state/main-menu.hpp"
#include "animation/screen-transition.hpp"
#include "animation/animation.hpp"
#include "animation/animator.hpp"
#include "animation/ease.hpp"
#include "application.hpp"
#include "render/passes/clear-pass.hpp"
#include "game/context.hpp"
#include "debug/logger.hpp"
namespace game {
namespace state {
splash::splash(game::context& ctx):
game::state::base(ctx)
{
ctx.logger->push_task("Entering splash state");
ctx.ui_clear_pass->set_cleared_buffers(true, true, false);
// Load animation timing configuration
double splash_fade_in_duration = 0.0;
double splash_duration = 0.0;
double splash_fade_out_duration = 0.0;
if (ctx.config->contains("splash_fade_in_duration"))
splash_fade_in_duration = (*ctx.config)["splash_fade_in_duration"].get<double>();
if (ctx.config->contains("splash_duration"))
splash_duration = (*ctx.config)["splash_duration"].get<double>();
if (ctx.config->contains("splash_fade_out_duration"))
splash_fade_out_duration = (*ctx.config)["splash_fade_out_duration"].get<double>();
// Build splash fade in animation
ctx.splash_fade_in_animation = new animation<float>();
animation_channel<float>* splash_fade_in_opacity_channel = ctx.splash_fade_in_animation->add_channel(0);
ctx.splash_fade_in_animation->set_interpolator(ease<float>::out_cubic);
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
ctx.splash_fade_out_animation = new animation<float>();
animation_channel<float>* splash_fade_out_opacity_channel = ctx.splash_fade_out_animation->add_channel(0);
ctx.splash_fade_out_animation->set_interpolator(ease<float>::out_cubic);
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 = [&ctx](int channel, const float& opacity)
{
static_cast<render::material_property<float4>*>(ctx.splash_billboard_material->get_property("tint"))->set_value(float4{1, 1, 1, opacity});
};
ctx.splash_fade_in_animation->set_frame_callback(set_splash_opacity);
ctx.splash_fade_out_animation->set_frame_callback(set_splash_opacity);
// Reset splash color when animation starts
ctx.splash_fade_in_animation->set_start_callback
(
[&ctx]()
{
static_cast<render::material_property<float4>*>(ctx.splash_billboard_material->get_property("tint"))->set_value(float4{1, 1, 1, 0});
ctx.splash_billboard_material->update_tweens();
}
);
// Trigger splash fade out animation when splash fade in animation ends
ctx.splash_fade_in_animation->set_end_callback
(
[&ctx]()
{
ctx.splash_fade_out_animation->play();
}
);
// Trigger a state change when the splash fade out animation ends
ctx.splash_fade_out_animation->set_end_callback
(
[&ctx]()
{
// Queue change to main menu state
ctx.function_queue.push
(
[&ctx]()
{
ctx.state_machine.pop();
ctx.state_machine.emplace(new game::state::main_menu(ctx, true));
}
);
}
);
// Add splash fade animations to animator
ctx.animator->add_animation(ctx.splash_fade_in_animation);
ctx.animator->add_animation(ctx.splash_fade_out_animation);
// Start splash fade in animation
ctx.splash_fade_in_animation->play();
// Set up splash skipper
ctx.input_listener->set_callback
(
[&ctx](const event_base& event)
{
auto id = event.get_event_type_id();
if (id != mouse_moved_event::event_type_id && id != mouse_wheel_scrolled_event::event_type_id && id != gamepad_axis_moved_event::event_type_id)
{
// Black out screen
ctx.rasterizer->set_clear_color(0.0f, 0.0f, 0.0f, 1.0f);
ctx.rasterizer->clear_framebuffer(true, false, false);
ctx.app->swap_buffers();
// Change to main menu state
ctx.state_machine.pop();
ctx.state_machine.emplace(new game::state::main_menu(ctx, true));
}
}
);
ctx.input_listener->set_enabled(true);
// Add splash billboard to UI scene
ctx.ui_scene->add_object(ctx.splash_billboard);
ctx.logger->pop_task(EXIT_SUCCESS);
}
splash::~splash()
{
ctx.logger->push_task("Exiting splash state");
// Remove splash billboard from UI scene
ctx.ui_scene->remove_object(ctx.splash_billboard);
// Disable splash skipper
ctx.input_listener->set_enabled(false);
ctx.input_listener->set_callback(nullptr);
// Destruct splash fade animations
ctx.animator->remove_animation(ctx.splash_fade_in_animation);
ctx.animator->remove_animation(ctx.splash_fade_out_animation);
delete ctx.splash_fade_in_animation;
delete ctx.splash_fade_out_animation;
ctx.splash_fade_in_animation = nullptr;
ctx.splash_fade_out_animation = nullptr;
ctx.ui_clear_pass->set_cleared_buffers(false, true, false);
ctx.logger->pop_task(EXIT_SUCCESS);
}
} // namespace state
} // namespace game

src/game/states/splash.hpp → src/game/state/splash.hpp View File

@ -20,18 +20,17 @@
#ifndef ANTKEEPER_GAME_STATE_SPLASH_HPP
#define ANTKEEPER_GAME_STATE_SPLASH_HPP
#include "game/context.hpp"
#include "game/state/base.hpp"
namespace game {
namespace state {
/// Splash screen game state functions.
namespace splash {
void enter(game::context* ctx);
void exit(game::context* ctx);
} // namespace splash
class splash: public game::state::base
{
public:
splash(game::context& ctx);
virtual ~splash();
};
} // namespace state
} // namespace game

+ 0
- 1230
src/game/states/boot.cpp
File diff suppressed because it is too large
View File


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

@ -1,137 +0,0 @@
/*
* Copyright (C) 2021 Christopher J. Howard
*
* This file is part of Antkeeper source code.
*
* Antkeeper source code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#include "game/states/credits.hpp"
#include "game/states/extras-menu.hpp"
#include "animation/ease.hpp"
#include "animation/animation.hpp"
#include "animation/animator.hpp"
#include "application.hpp"
#include "scene/text.hpp"
namespace game {
namespace state {
namespace credits {
void enter(game::context* ctx)
{
// Construct credits text
ctx->credits_text = new scene::text();
ctx->credits_text->set_material(&ctx->menu_font_material);
ctx->credits_text->set_font(&ctx->menu_font);
ctx->credits_text->set_color({1.0f, 1.0f, 1.0f, 0.0f});
ctx->credits_text->set_content((*ctx->strings)["credits"]);
// Align credits text
const auto& credits_aabb = static_cast<const geom::aabb<float>&>(ctx->credits_text->get_local_bounds());
float credits_w = credits_aabb.max_point.x - credits_aabb.min_point.x;
float credits_h = credits_aabb.max_point.y - credits_aabb.min_point.y;
ctx->credits_text->set_translation({std::round(-credits_w * 0.5f), std::round(-credits_h * 0.5f), 0.0f});
// Load animation timing configuration
double credits_fade_in_duration = 0.0;
double credits_scroll_duration = 0.0;
if (ctx->config->contains("credits_fade_in_duration"))
credits_fade_in_duration = (*ctx->config)["credits_fade_in_duration"].get<double>();
if (ctx->config->contains("credits_scroll_duration"))
credits_scroll_duration = (*ctx->config)["credits_scroll_duration"].get<double>();
auto set_credits_opacity = [ctx](int channel, const float& opacity)
{
ctx->credits_text->set_color({1.0f, 1.0f, 1.0f, opacity});
};
// Build credits fade in animation
ctx->credits_fade_in_animation = new animation<float>();
animation_channel<float>* credits_fade_in_opacity_channel = ctx->credits_fade_in_animation->add_channel(0);
ctx->credits_fade_in_animation->set_interpolator(ease<float>::in_quad);
credits_fade_in_opacity_channel->insert_keyframe({0.0, 0.0f});
credits_fade_in_opacity_channel->insert_keyframe({credits_fade_in_duration, 1.0f});
ctx->credits_fade_in_animation->set_frame_callback(set_credits_opacity);
// Build credits scroll in animation
ctx->credits_scroll_animation = new animation<float>();
// Trigger credits scroll animation after credits fade in animation ends
ctx->credits_fade_in_animation->set_end_callback
(
[ctx]()
{
ctx->credits_scroll_animation->play();
}
);
// Add credits animations to animator
ctx->animator->add_animation(ctx->credits_fade_in_animation);
ctx->animator->add_animation(ctx->credits_scroll_animation);
// Start credits fade in animation
ctx->credits_fade_in_animation->play();
// Set up credits skipper
ctx->input_listener->set_callback
(
[ctx](const event_base& event)
{
auto id = event.get_event_type_id();
if (id != mouse_moved_event::event_type_id && id != mouse_wheel_scrolled_event::event_type_id && id != gamepad_axis_moved_event::event_type_id)
{
if (ctx->credits_text->get_color()[3] > 0.0f)
{
ctx->input_listener->set_enabled(false);
// Change state
application::state next_state;
next_state.name = "extras_menu";
next_state.enter = std::bind(game::state::extras_menu::enter, ctx);
next_state.exit = std::bind(game::state::extras_menu::exit, ctx);
ctx->app->queue_state(next_state);
}
}
}
);
ctx->input_listener->set_enabled(true);
ctx->ui_scene->add_object(ctx->credits_text);
ctx->credits_text->update_tweens();
}
void exit(game::context* ctx)
{
// Disable credits skipper
ctx->input_listener->set_enabled(false);
ctx->input_listener->set_callback(nullptr);
// Destruct credits text
ctx->ui_scene->remove_object(ctx->credits_text);
delete ctx->credits_text;
ctx->credits_text = nullptr;
// Destruct credits animations
ctx->animator->remove_animation(ctx->credits_fade_in_animation);
ctx->animator->remove_animation(ctx->credits_scroll_animation);
delete ctx->credits_fade_in_animation;
delete ctx->credits_scroll_animation;
ctx->credits_fade_in_animation = nullptr;
ctx->credits_scroll_animation = nullptr;
}
} // namespace credits
} // namespace state
} // namespace game

+ 0
- 346
src/game/states/graphics-menu.cpp View File

@ -1,346 +0,0 @@
/*
* Copyright (C) 2021 Christopher J. Howard
*
* This file is part of Antkeeper source code.
*
* Antkeeper source code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#include "game/states/graphics-menu.hpp"
#include "game/states/options-menu.hpp"
#include "application.hpp"
#include "scene/text.hpp"
#include "debug/logger.hpp"
#include "game/fonts.hpp"
#include "game/menu.hpp"
#include "game/graphics.hpp"
#include "animation/timeline.hpp"
namespace game {
namespace state {
namespace graphics_menu {
static void update_value_text_content(game::context* ctx);
void enter(game::context* ctx)
{
// Construct menu item texts
scene::text* fullscreen_name_text = new scene::text();
scene::text* fullscreen_value_text = new scene::text();
scene::text* resolution_name_text = new scene::text();
scene::text* resolution_value_text = new scene::text();
scene::text* v_sync_name_text = new scene::text();
scene::text* v_sync_value_text = new scene::text();
scene::text* font_size_name_text = new scene::text();
scene::text* font_size_value_text = new scene::text();
scene::text* dyslexia_font_name_text = new scene::text();
scene::text* dyslexia_font_value_text = new scene::text();
scene::text* back_text = new scene::text();
// Build list of menu item texts
ctx->menu_item_texts.push_back({fullscreen_name_text, fullscreen_value_text});
ctx->menu_item_texts.push_back({resolution_name_text, resolution_value_text});
ctx->menu_item_texts.push_back({v_sync_name_text, v_sync_value_text});
ctx->menu_item_texts.push_back({font_size_name_text, font_size_value_text});
ctx->menu_item_texts.push_back({dyslexia_font_name_text, dyslexia_font_value_text});
ctx->menu_item_texts.push_back({back_text, nullptr});
// Set content of menu item texts
fullscreen_name_text->set_content((*ctx->strings)["graphics_menu_fullscreen"]);
resolution_name_text->set_content((*ctx->strings)["graphics_menu_resolution"]);
v_sync_name_text->set_content((*ctx->strings)["graphics_menu_v_sync"]);
font_size_name_text->set_content((*ctx->strings)["graphics_menu_font_size"]);
dyslexia_font_name_text->set_content((*ctx->strings)["graphics_menu_dyslexia_font"]);
back_text->set_content((*ctx->strings)["back"]);
update_value_text_content(ctx);
// Init menu item index
game::menu::init_menu_item_index(ctx, "graphics");
game::menu::update_text_color(ctx);
game::menu::update_text_font(ctx);
game::menu::align_text(ctx);
game::menu::update_text_tweens(ctx);
game::menu::add_text_to_ui(ctx);
game::menu::setup_animations(ctx);
// Construct menu item callbacks
auto toggle_fullscreen_callback = [ctx]()
{
bool fullscreen = !ctx->app->is_fullscreen();
ctx->app->set_fullscreen(fullscreen);
if (!fullscreen)
{
int2 resolution;
resolution.x = (*ctx->config)["windowed_resolution"][0].get<int>();
resolution.y = (*ctx->config)["windowed_resolution"][1].get<int>();
ctx->app->resize_window(resolution.x, resolution.y);
}
update_value_text_content(ctx);
game::menu::align_text(ctx);
game::menu::update_text_tweens(ctx);
// Save display mode config
(*ctx->config)["fullscreen"] = fullscreen;
};
auto increase_resolution_callback = [ctx]()
{
// Increase resolution
if (ctx->controls["menu_modifier"]->is_active())
ctx->render_resolution_scale += 0.05f;
else
ctx->render_resolution_scale += 0.25f;
// Limit resolution
if (ctx->render_resolution_scale > 2.0f)
ctx->render_resolution_scale = 2.0f;
// Resize framebuffers
game::graphics::change_render_resolution(*ctx, ctx->render_resolution_scale);
// Update text
update_value_text_content(ctx);
game::menu::align_text(ctx);
game::menu::update_text_tweens(ctx);
// Update config
(*ctx->config)["render_resolution"] = ctx->render_resolution_scale;
};
auto decrease_resolution_callback = [ctx]()
{
// Increase resolution
if (ctx->controls["menu_modifier"]->is_active())
ctx->render_resolution_scale -= 0.05f;
else
ctx->render_resolution_scale -= 0.25f;
// Limit resolution
if (ctx->render_resolution_scale < 0.25f)
ctx->render_resolution_scale = 0.25f;
// Resize framebuffers
game::graphics::change_render_resolution(*ctx, ctx->render_resolution_scale);
// Update text
update_value_text_content(ctx);
game::menu::align_text(ctx);
game::menu::update_text_tweens(ctx);
// Update config
(*ctx->config)["render_resolution"] = ctx->render_resolution_scale;
};
auto toggle_v_sync_callback = [ctx]()
{
bool v_sync = !ctx->app->get_v_sync();
ctx->app->set_v_sync(v_sync);
update_value_text_content(ctx);
game::menu::align_text(ctx);
game::menu::update_text_tweens(ctx);
// Save v-sync config
(*ctx->config)["v_sync"] = v_sync;
};
auto increase_font_size_callback = [ctx]()
{
// Increase font size
if (ctx->controls["menu_modifier"]->is_active())
ctx->font_size += 0.01f;
else
ctx->font_size += 0.1f;
// Limit font size
if (ctx->font_size > 2.0f)
ctx->font_size = 2.0f;
// Update value text
update_value_text_content(ctx);
// Update config
(*ctx->config)["font_size"] = ctx->font_size;
// Reload fonts
ctx->logger->push_task("Reloading fonts");
try
{
game::load_fonts(ctx);
}
catch (...)
{
ctx->logger->pop_task(EXIT_FAILURE);
}
ctx->logger->pop_task(EXIT_SUCCESS);
// Refresh and realign text
game::menu::refresh_text(ctx);
game::menu::align_text(ctx);
game::menu::update_text_tweens(ctx);
};
auto decrease_font_size_callback = [ctx]()
{
// Decrease font size
if (ctx->controls["menu_modifier"]->is_active())
ctx->font_size -= 0.01f;
else
ctx->font_size -= 0.1f;
// Limit font size
if (ctx->font_size < 0.1f)
ctx->font_size = 0.1f;
// Update value text
update_value_text_content(ctx);
// Update config
(*ctx->config)["font_size"] = ctx->font_size;
// Reload fonts
ctx->logger->push_task("Reloading fonts");
try
{
game::load_fonts(ctx);
}
catch (...)
{
ctx->logger->pop_task(EXIT_FAILURE);
}
ctx->logger->pop_task(EXIT_SUCCESS);
// Refresh and realign text
game::menu::refresh_text(ctx);
game::menu::align_text(ctx);
game::menu::update_text_tweens(ctx);
};
auto toggle_dyslexia_font_callback = [ctx]()
{
ctx->dyslexia_font = !ctx->dyslexia_font;
// Update value text
update_value_text_content(ctx);
// Save dyslexia font config
(*ctx->config)["dyslexia_font"] = ctx->dyslexia_font;
// Reload fonts
ctx->logger->push_task("Reloading fonts");
try
{
game::load_fonts(ctx);
}
catch (...)
{
ctx->logger->pop_task(EXIT_FAILURE);
}
ctx->logger->pop_task(EXIT_SUCCESS);
// Refresh and realign text
game::menu::refresh_text(ctx);
game::menu::align_text(ctx);
game::menu::update_text_tweens(ctx);
};
auto select_back_callback = [ctx]()
{
// Disable controls
game::menu::clear_controls(ctx);
game::menu::fade_out
(
ctx,
[ctx]()
{
application::state next_state;
next_state.name = "options_menu";
next_state.enter = std::bind(game::state::options_menu::enter, ctx);
next_state.exit = std::bind(game::state::options_menu::exit, ctx);
ctx->app->queue_state(next_state);
}
);
};
// Build list of menu select callbacks
ctx->menu_select_callbacks.push_back(toggle_fullscreen_callback);
ctx->menu_select_callbacks.push_back(increase_resolution_callback);
ctx->menu_select_callbacks.push_back(toggle_v_sync_callback);
ctx->menu_select_callbacks.push_back(increase_font_size_callback);
ctx->menu_select_callbacks.push_back(toggle_dyslexia_font_callback);
ctx->menu_select_callbacks.push_back(select_back_callback);
// Build list of menu left callbacks
ctx->menu_left_callbacks.push_back(toggle_fullscreen_callback);
ctx->menu_left_callbacks.push_back(decrease_resolution_callback);
ctx->menu_left_callbacks.push_back(toggle_v_sync_callback);
ctx->menu_left_callbacks.push_back(decrease_font_size_callback);
ctx->menu_left_callbacks.push_back(toggle_dyslexia_font_callback);
ctx->menu_left_callbacks.push_back(nullptr);
// Build list of menu right callbacks
ctx->menu_right_callbacks.push_back(toggle_fullscreen_callback);
ctx->menu_right_callbacks.push_back(increase_resolution_callback);
ctx->menu_right_callbacks.push_back(toggle_v_sync_callback);
ctx->menu_right_callbacks.push_back(increase_font_size_callback);
ctx->menu_right_callbacks.push_back(toggle_dyslexia_font_callback);
ctx->menu_right_callbacks.push_back(nullptr);
// Set menu back callback
ctx->menu_back_callback = select_back_callback;
// Queue menu control setup
ctx->function_queue.push(std::bind(game::menu::setup_controls, ctx));
// Fade in menu
game::menu::fade_in(ctx, nullptr);
}
void exit(game::context* ctx)
{
// Destruct menu
game::menu::clear_controls(ctx);
game::menu::clear_callbacks(ctx);
game::menu::delete_animations(ctx);
game::menu::remove_text_from_ui(ctx);
game::menu::delete_text(ctx);
}
static void update_value_text_content(game::context* ctx)
{
bool fullscreen = ctx->app->is_fullscreen();
float resolution = ctx->render_resolution_scale;
bool v_sync = ctx->app->get_v_sync();
float font_size = ctx->font_size;
bool dyslexia_font = ctx->dyslexia_font;
const std::string string_on = (*ctx->strings)["on"];
const std::string string_off = (*ctx->strings)["off"];
std::get<1>(ctx->menu_item_texts[0])->set_content((fullscreen) ? string_on : string_off);
std::get<1>(ctx->menu_item_texts[1])->set_content(std::to_string(static_cast<int>(std::round(resolution * 100.0f))) + "%");
std::get<1>(ctx->menu_item_texts[2])->set_content((v_sync) ? string_on : string_off);
std::get<1>(ctx->menu_item_texts[3])->set_content(std::to_string(static_cast<int>(std::round(font_size * 100.0f))) + "%");
std::get<1>(ctx->menu_item_texts[4])->set_content((dyslexia_font) ? string_on : string_off);
}
} // namespace graphics_menu
} // namespace state
} // namespace game

+ 0
- 265
src/game/states/main-menu.cpp View File

@ -1,265 +0,0 @@
/*
* Copyright (C) 2021 Christopher J. Howard
*
* This file is part of Antkeeper source code.
*
* Antkeeper source code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#include "game/states/main-menu.hpp"
#include "game/states/options-menu.hpp"
#include "game/states/extras-menu.hpp"
#include "game/states/forage.hpp"
#include "game/states/nuptial-flight.hpp"
#include "game/menu.hpp"
#include "render/passes/clear-pass.hpp"
#include "resources/resource-manager.hpp"
#include "render/model.hpp"
#include "animation/animation.hpp"
#include "animation/animator.hpp"
#include "animation/screen-transition.hpp"
#include "animation/ease.hpp"
#include "animation/timeline.hpp"
#include "application.hpp"
#include <limits>
namespace game {
namespace state {
namespace main_menu {
static void fade_in_title(game::context* ctx)
{
ctx->title_fade_animation->set_interpolator(ease<float>::out_cubic);
animation_channel<float>* opacity_channel = ctx->title_fade_animation->get_channel(0);
opacity_channel->remove_keyframes();
opacity_channel->insert_keyframe({0.0, 0.0f});
opacity_channel->insert_keyframe({game::menu::fade_in_duration, 1.0f});
ctx->title_fade_animation->stop();
ctx->title_fade_animation->play();
}
static void fade_out_title(game::context* ctx)
{
ctx->title_fade_animation->set_interpolator(ease<float>::out_cubic);
animation_channel<float>* opacity_channel = ctx->title_fade_animation->get_channel(0);
opacity_channel->remove_keyframes();
opacity_channel->insert_keyframe({0.0, 1.0f});
opacity_channel->insert_keyframe({game::menu::fade_out_duration, 0.0f});
ctx->title_fade_animation->stop();
ctx->title_fade_animation->play();
}
void enter(game::context* ctx, bool fade_in)
{
ctx->ui_clear_pass->set_cleared_buffers(true, true, false);
// Construct title text
ctx->title_text = new scene::text();
ctx->title_text->set_material(&ctx->title_font_material);
ctx->title_text->set_font(&ctx->title_font);
ctx->title_text->set_color({1.0f, 1.0f, 1.0f, 1.0f});
ctx->title_text->set_content((*ctx->strings)["title_antkeeper"]);
// Align title text
const auto& title_aabb = static_cast<const geom::aabb<float>&>(ctx->title_text->get_local_bounds());
float title_w = title_aabb.max_point.x - title_aabb.min_point.x;
float title_h = title_aabb.max_point.y - title_aabb.min_point.y;
ctx->title_text->set_translation({std::round(-title_w * 0.5f), std::round(-title_h * 0.5f + (ctx->app->get_viewport_dimensions().y / 3.0f) / 2.0f), 0.0f});
ctx->title_text->update_tweens();
// Add title text to UI
ctx->ui_scene->add_object(ctx->title_text);
// Construct title fade animation
ctx->title_fade_animation = new animation<float>();
animation_channel<float>* opacity_channel = ctx->title_fade_animation->add_channel(0);
ctx->title_fade_animation->set_frame_callback
(
[ctx](int channel, const float& opacity)
{
float4 color = ctx->title_text->get_color();
color[3] = opacity;
ctx->title_text->set_color(color);
}
);
ctx->animator->add_animation(ctx->title_fade_animation);
// Construct menu item texts
scene::text* start_text = new scene::text();
scene::text* options_text = new scene::text();
scene::text* extras_text = new scene::text();
scene::text* quit_text = new scene::text();
// Build list of menu item texts
ctx->menu_item_texts.push_back({start_text, nullptr});
ctx->menu_item_texts.push_back({options_text, nullptr});
ctx->menu_item_texts.push_back({extras_text, nullptr});
ctx->menu_item_texts.push_back({quit_text, nullptr});
// Set content of menu item texts
start_text->set_content((*ctx->strings)["main_menu_start"]);
options_text->set_content((*ctx->strings)["main_menu_options"]);
extras_text->set_content((*ctx->strings)["main_menu_extras"]);
quit_text->set_content((*ctx->strings)["main_menu_quit"]);
// Init menu item index
game::menu::init_menu_item_index(ctx, "main");
game::menu::update_text_color(ctx);
game::menu::update_text_font(ctx);
game::menu::align_text(ctx, true, false, (-ctx->app->get_viewport_dimensions().y / 3.0f) / 2.0f);
game::menu::update_text_tweens(ctx);
game::menu::add_text_to_ui(ctx);
game::menu::setup_animations(ctx);
auto select_start_callback = [ctx]()
{
// Disable controls and menu callbacks
game::menu::clear_controls(ctx);
game::menu::clear_callbacks(ctx);
// Create change state function
auto change_state_nuptial_flight = [ctx]()
{
application::state next_state;
next_state.name = "nuptial_flight";
next_state.enter = std::bind(game::state::nuptial_flight::enter, ctx);
next_state.exit = std::bind(game::state::nuptial_flight::exit, ctx);
ctx->app->change_state(next_state);
};
// Set up timing
const float fade_out_duration = 1.0f;
// Schedule state change
timeline* timeline = ctx->timeline;
float t = timeline->get_position();
timeline->add_sequence({{t + fade_out_duration, change_state_nuptial_flight}});
// Start fade out to white
ctx->fade_transition_color->set_value({1, 1, 1});
ctx->fade_transition->transition(fade_out_duration, false, ease<float>::out_cubic, false);
};
auto select_options_callback = [ctx]()
{
game::menu::clear_controls(ctx);
// Fade out title
fade_out_title(ctx);
// Fade out menu
game::menu::fade_out
(
ctx,
[ctx]()
{
application::state next_state;
next_state.name = "options_menu";
next_state.enter = std::bind(game::state::options_menu::enter, ctx);
next_state.exit = std::bind(game::state::options_menu::exit, ctx);
ctx->app->queue_state(next_state);
}
);
};
auto select_extras_callback = [ctx]()
{
// Disable controls
game::menu::clear_controls(ctx);
// Fade out title
fade_out_title(ctx);
// Fade out menu
game::menu::fade_out
(
ctx,
[ctx]()
{
application::state next_state;
next_state.name = "extras_menu";
next_state.enter = std::bind(game::state::extras_menu::enter, ctx);
next_state.exit = std::bind(game::state::extras_menu::exit, ctx);
ctx->app->queue_state(next_state);
}
);
};
auto select_quit_callback = [ctx]()
{
ctx->app->close(EXIT_SUCCESS);
};
// Build list of menu select callbacks
ctx->menu_select_callbacks.push_back(select_start_callback);
ctx->menu_select_callbacks.push_back(select_options_callback);
ctx->menu_select_callbacks.push_back(select_extras_callback);
ctx->menu_select_callbacks.push_back(select_quit_callback);
// Build list of menu left callbacks
ctx->menu_left_callbacks.push_back(nullptr);
ctx->menu_left_callbacks.push_back(nullptr);
ctx->menu_left_callbacks.push_back(nullptr);
ctx->menu_left_callbacks.push_back(nullptr);
// Build list of menu right callbacks
ctx->menu_right_callbacks.push_back(nullptr);
ctx->menu_right_callbacks.push_back(nullptr);
ctx->menu_right_callbacks.push_back(nullptr);
ctx->menu_right_callbacks.push_back(nullptr);
// Set menu back callback
ctx->menu_back_callback = select_quit_callback;
// Queue menu control setup
ctx->function_queue.push(std::bind(game::menu::setup_controls, ctx));
if (fade_in)
{
ctx->fade_transition->transition(0.5f, true, ease<float>::out_cubic);
}
else
{
// Fade in title
ctx->title_text->set_color({1.0f, 1.0f, 1.0f, 0.0f});
ctx->title_text->update_tweens();
fade_in_title(ctx);
// Fade in menu
game::menu::fade_in(ctx, nullptr);
}
}
void exit(game::context* ctx)
{
// Destruct menu
game::menu::clear_controls(ctx);
game::menu::clear_callbacks(ctx);
game::menu::delete_animations(ctx);
game::menu::remove_text_from_ui(ctx);
game::menu::delete_text(ctx);
// Destruct title animation
ctx->animator->remove_animation(ctx->title_fade_animation);
delete ctx->title_fade_animation;
ctx->title_fade_animation = nullptr;
// Destruct title text
ctx->ui_scene->remove_object(ctx->title_text);
delete ctx->title_text;
ctx->title_text = nullptr;
}
} // namespace main_menu
} // namespace state
} // namespace game

+ 0
- 557
src/game/states/nuptial-flight.cpp View File

@ -1,557 +0,0 @@
/*
* Copyright (C) 2021 Christopher J. Howard
*
* This file is part of Antkeeper source code.
*
* Antkeeper source code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#include "game/states/nuptial-flight.hpp"
#include "game/states/pause-menu.hpp"
#include "entity/archetype.hpp"
#include "entity/systems/astronomy.hpp"
#include "entity/systems/orbit.hpp"
#include "entity/systems/camera.hpp"
#include "entity/components/observer.hpp"
#include "entity/components/transform.hpp"
#include "entity/components/terrain.hpp"
#include "entity/components/camera.hpp"
#include "entity/components/constraints/spring-to.hpp"
#include "entity/components/constraints/three-dof.hpp"
#include "entity/components/constraint-stack.hpp"
#include "entity/commands.hpp"
#include "animation/screen-transition.hpp"
#include "animation/ease.hpp"
#include "resources/resource-manager.hpp"
#include "game/world.hpp"
#include "application.hpp"
#include "render/passes/clear-pass.hpp"
#include <memory>
#include <iostream>
#include "state-machine.hpp"
namespace game {
namespace state {
namespace nuptial_flight {
static void setup_camera(game::context* ctx);
static void enable_controls(game::context* ctx);
static void disable_controls(game::context* ctx);
void enter(game::context* ctx)
{
// Resume if paused
if (ctx->paused_state)
{
// Clear paused state
ctx->paused_state.reset();
// Enable controls
enable_controls(ctx);
return;
}
// Disable UI color clear
ctx->ui_clear_pass->set_cleared_buffers(false, true, false);
// Create world
game::world::create_stars(ctx);
game::world::create_sun(ctx);
game::world::create_planet(ctx);
game::world::create_moon(ctx);
// Set time to solar noon
game::world::set_time(ctx, 0.0);
// Freeze time
game::world::set_time_scale(ctx, 0.0);
// Switch to surface camera
ctx->underground_camera->set_active(false);
ctx->surface_camera->set_active(true);
// Find planet EID by name
entity::id planet_eid = ctx->entities["planet"];
// Remove terrain component from planet (if any)
//if (ctx->entity_registry->has<entity::component::terrain>(planet_eid))
// ctx->entity_registry->remove<entity::component::terrain>(planet_eid);
// Enable clouds in sky pass
//ctx->surface_sky_pass->set_clouds_model(ctx->resource_manager->load<render::model>("cloud-plane.mdl"));
// Create observer
entity::id observer_eid = ctx->entity_registry->create();
{
entity::component::observer observer;
observer.reference_body_eid = planet_eid;
observer.elevation = 2000.0;
observer.latitude = 0.0;
observer.longitude = 0.0;
observer.camera = ctx->surface_camera;
ctx->entity_registry->assign<entity::component::observer>(observer_eid, observer);
// Set reference location of astronomy system
ctx->astronomy_system->set_reference_body(planet_eid);
ctx->astronomy_system->set_observer_location(double3{observer.elevation, observer.latitude, observer.longitude});
}
// Setup camera
setup_camera(ctx);
/*
ctx->surface_camera->look_at({0, 0, 1}, {0, 0, 0}, {0, 1, 0});
ctx->surface_camera->set_exposure(-14.5f);
ctx->surface_scene->update_tweens();
*/
// Queue fade in
ctx->fade_transition_color->set_value({1, 1, 1});
ctx->function_queue.push(std::bind(&screen_transition::transition, ctx->fade_transition, 5.0f, true, math::lerp<float, float>, true));
// Queue control setup
ctx->function_queue.push(std::bind(enable_controls, ctx));
}
void exit(game::context* ctx)
{
// Resume time
//const double time_scale = (*ctx->config)["time_scale"].get<double>();
//game::world::set_time_scale(ctx, time_scale);
}
void setup_camera(game::context* ctx)
{
// Switch to surface camera
ctx->underground_camera->set_active(false);
ctx->surface_camera->set_active(true);
// Create surface camera entity
if (!ctx->entities.count("surface_cam"))
{
// Create camera target entity
entity::id target_eid = ctx->entity_registry->create();
ctx->entities["surface_cam_target"] = target_eid;
{
// Transform
entity::component::transform target_transform;
target_transform.local = math::identity_transform<float>;
target_transform.world = target_transform.local;
target_transform.warp = true;
ctx->entity_registry->assign<entity::component::transform>(target_eid, target_transform);
}
// Create camera entity
entity::id camera_eid = ctx->entity_registry->create();
ctx->entities["surface_cam"] = camera_eid;
// Create camera transform component
entity::component::transform transform;
transform.local = math::identity_transform<float>;
transform.world = transform.local;
transform.warp = true;
ctx->entity_registry->assign<entity::component::transform>(camera_eid, transform);
// Create camera camera component
entity::component::camera camera;
camera.object = ctx->surface_camera;
ctx->entity_registry->assign<entity::component::camera>(camera_eid, camera);
// Create camera 3DOF constraint entity
entity::id three_dof_constraint_eid = ctx->entity_registry->create();
ctx->entities["surface_cam_3dof"] = three_dof_constraint_eid;
{
// Create 3DOF to constraint
entity::component::constraint::three_dof three_dof;
three_dof.yaw = 0.0f;
three_dof.pitch = 0.0f;
three_dof.roll = 0.0f;
ctx->entity_registry->assign<entity::component::constraint::three_dof>(three_dof_constraint_eid, three_dof);
// Create constraint stack node component
entity::component::constraint_stack_node node;
node.active = true;
node.weight = 1.0f;
node.next = entt::null;
ctx->entity_registry->assign<entity::component::constraint_stack_node>(three_dof_constraint_eid, node);
}
// Create camera spring to constraint entity
entity::id spring_constraint_eid = ctx->entity_registry->create();
{
// Create spring to constraint
entity::component::constraint::spring_to spring;
spring.target = target_eid;
spring.translation = {{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}, 1.0f, math::two_pi<float>};
spring.translation.w = hz_to_rads(8.0f);
spring.spring_translation = true;
spring.spring_rotation = false;
ctx->entity_registry->assign<entity::component::constraint::spring_to>(spring_constraint_eid, spring);
// Create constraint stack node component
entity::component::constraint_stack_node node;
node.active = true;
node.weight = 1.0f;
node.next = three_dof_constraint_eid;
ctx->entity_registry->assign<entity::component::constraint_stack_node>(spring_constraint_eid, node);
}
// Create camera constraint stack component
entity::component::constraint_stack constraint_stack;
constraint_stack.head = spring_constraint_eid;
ctx->entity_registry->assign<entity::component::constraint_stack>(camera_eid, constraint_stack);
}
ctx->surface_camera->set_exposure(-12.0f);
}
void enable_controls(game::context* ctx)
{
// Get camera entities
entity::id camera_eid = ctx->entities["surface_cam"];
entity::id target_eid = ctx->entities["surface_cam_target"];
entity::id three_dof_eid = ctx->entities["surface_cam_3dof"];
const float slow_modifier = 0.25f;
const float fast_modifier = 4.0f;
const float dolly_speed = 20.0f;
const float truck_speed = dolly_speed;
const float pedestal_speed = 30.0f;
float mouse_tilt_sensitivity = 1.0f;
float mouse_pan_sensitivity = 1.0f;
bool mouse_invert_tilt = false;
bool mouse_invert_pan = false;
float gamepad_tilt_sensitivity = 1.0f;
float gamepad_pan_sensitivity = 1.0f;
bool gamepad_invert_tilt = false;
bool gamepad_invert_pan = false;
bool mouse_look_toggle = false;
ctx->mouse_look = false;
if (ctx->config->contains("mouse_tilt_sensitivity"))
mouse_tilt_sensitivity = math::radians((*ctx->config)["mouse_tilt_sensitivity"].get<float>());
if (ctx->config->contains("mouse_pan_sensitivity"))
mouse_pan_sensitivity = math::radians((*ctx->config)["mouse_pan_sensitivity"].get<float>());
if (ctx->config->contains("mouse_invert_tilt"))
mouse_invert_tilt = math::radians((*ctx->config)["mouse_invert_tilt"].get<bool>());
if (ctx->config->contains("mouse_invert_pan"))
mouse_invert_pan = math::radians((*ctx->config)["mouse_invert_pan"].get<bool>());
if (ctx->config->contains("mouse_look_toggle"))
mouse_look_toggle = math::radians((*ctx->config)["mouse_look_toggle"].get<bool>());
if (ctx->config->contains("gamepad_tilt_sensitivity"))
gamepad_tilt_sensitivity = math::radians((*ctx->config)["gamepad_tilt_sensitivity"].get<float>());
if (ctx->config->contains("gamepad_pan_sensitivity"))
gamepad_pan_sensitivity = math::radians((*ctx->config)["gamepad_pan_sensitivity"].get<float>());
if (ctx->config->contains("gamepad_invert_tilt"))
gamepad_invert_tilt = math::radians((*ctx->config)["gamepad_invert_tilt"].get<bool>());
if (ctx->config->contains("gamepad_invert_pan"))
gamepad_invert_pan = math::radians((*ctx->config)["gamepad_invert_pan"].get<bool>());
const input::control* move_slow = ctx->controls["move_slow"];
const input::control* move_fast = ctx->controls["move_fast"];
const input::control* mouse_look = ctx->controls["mouse_look"];
float mouse_tilt_factor = mouse_tilt_sensitivity * (mouse_invert_tilt ? -1.0f : 1.0f);
float mouse_pan_factor = mouse_pan_sensitivity * (mouse_invert_pan ? -1.0f : 1.0f);
float gamepad_tilt_factor = gamepad_tilt_sensitivity * (gamepad_invert_tilt ? -1.0f : 1.0f);
float gamepad_pan_factor = gamepad_pan_sensitivity * (gamepad_invert_pan ? -1.0f : 1.0f);
ctx->controls["move_forward"]->set_active_callback
(
[ctx, target_eid, three_dof_eid, truck_speed, move_slow, move_fast, slow_modifier, fast_modifier](float value)
{
if (move_slow->is_active())
value *= slow_modifier;
if (move_fast->is_active())
value *= fast_modifier;
auto& three_dof = ctx->entity_registry->get<entity::component::constraint::three_dof>(three_dof_eid);
const math::quaternion<float> yaw = math::angle_axis(three_dof.yaw, {0.0f, 1.0f, 0.0f});
const float3 movement = {0.0f, 0.0f, -truck_speed * value * (1.0f / 60.0f)};
entity::command::translate(*ctx->entity_registry, target_eid, yaw * movement);
}
);
// Dolly backward
ctx->controls["move_back"]->set_active_callback
(
[ctx, target_eid, three_dof_eid, truck_speed, move_slow, move_fast, slow_modifier, fast_modifier](float value)
{
if (move_slow->is_active())
value *= slow_modifier;
if (move_fast->is_active())
value *= fast_modifier;
auto& three_dof = ctx->entity_registry->get<entity::component::constraint::three_dof>(three_dof_eid);
const math::quaternion<float> yaw = math::angle_axis(three_dof.yaw, {0.0f, 1.0f, 0.0f});
const float3 movement = {0.0f, 0.0f, truck_speed * value * (1.0f / 60.0f)};
entity::command::translate(*ctx->entity_registry, target_eid, yaw * movement);
}
);
// Truck right
ctx->controls["move_right"]->set_active_callback
(
[ctx, target_eid, three_dof_eid, truck_speed, move_slow, move_fast, slow_modifier, fast_modifier](float value)
{
if (move_slow->is_active())
value *= slow_modifier;
if (move_fast->is_active())
value *= fast_modifier;
auto& three_dof = ctx->entity_registry->get<entity::component::constraint::three_dof>(three_dof_eid);
const math::quaternion<float> yaw = math::angle_axis(three_dof.yaw, {0.0f, 1.0f, 0.0f});
const float3 movement = {truck_speed * value * (1.0f / 60.0f), 0.0f, 0.0f};
entity::command::translate(*ctx->entity_registry, target_eid, yaw * movement);
}
);
// Truck left
ctx->controls["move_left"]->set_active_callback
(
[ctx, target_eid, three_dof_eid, truck_speed, move_slow, move_fast, slow_modifier, fast_modifier](float value)
{
if (move_slow->is_active())
value *= slow_modifier;
if (move_fast->is_active())
value *= fast_modifier;
auto& three_dof = ctx->entity_registry->get<entity::component::constraint::three_dof>(three_dof_eid);
const math::quaternion<float> yaw = math::angle_axis(three_dof.yaw, {0.0f, 1.0f, 0.0f});
const float3 movement = {-truck_speed * value * (1.0f / 60.0f), 0.0f, 0.0f};
entity::command::translate(*ctx->entity_registry, target_eid, yaw * movement);
}
);
// Pedestal up
ctx->controls["move_up"]->set_active_callback
(
[ctx, target_eid, pedestal_speed, move_slow, move_fast, slow_modifier, fast_modifier](float value)
{
if (move_slow->is_active())
value *= slow_modifier;
if (move_fast->is_active())
value *= fast_modifier;
const float3 movement = {0.0f, pedestal_speed * value * (1.0f / 60.0f), 0.0f};
entity::command::translate(*ctx->entity_registry, target_eid, movement);
}
);
// Pedestal down
ctx->controls["move_down"]->set_active_callback
(
[ctx, target_eid, pedestal_speed, move_slow, move_fast, slow_modifier, fast_modifier](float value)
{
if (move_slow->is_active())
value *= slow_modifier;
if (move_fast->is_active())
value *= fast_modifier;
const float3 movement = {0.0f, -pedestal_speed * value * (1.0f / 60.0f), 0.0f};
entity::command::translate(*ctx->entity_registry, target_eid, movement);
}
);
// Mouse rotate
ctx->controls["mouse_look"]->set_activated_callback
(
[ctx, mouse_look_toggle]()
{
if (mouse_look_toggle)
ctx->mouse_look = !ctx->mouse_look;
else
ctx->mouse_look = true;
ctx->app->set_relative_mouse_mode(ctx->mouse_look);
}
);
ctx->controls["mouse_look"]->set_deactivated_callback
(
[ctx, mouse_look_toggle]()
{
if (!mouse_look_toggle)
{
ctx->mouse_look = false;
ctx->app->set_relative_mouse_mode(false);
}
}
);
// Pan left
ctx->controls["look_left_gamepad"]->set_active_callback
(
[ctx, three_dof_eid, gamepad_pan_factor](float value)
{
auto& three_dof = ctx->entity_registry->get<entity::component::constraint::three_dof>(three_dof_eid);
three_dof.yaw += gamepad_pan_factor * value * (1.0f / 60.0f);
}
);
ctx->controls["look_left_mouse"]->set_active_callback
(
[ctx, three_dof_eid, mouse_pan_factor](float value)
{
if (!ctx->mouse_look)
return;
auto& three_dof = ctx->entity_registry->get<entity::component::constraint::three_dof>(three_dof_eid);
three_dof.yaw += mouse_pan_factor * value * (1.0f / 60.0f);
}
);
// Pan right
ctx->controls["look_right_gamepad"]->set_active_callback
(
[ctx, three_dof_eid, gamepad_pan_factor](float value)
{
auto& three_dof = ctx->entity_registry->get<entity::component::constraint::three_dof>(three_dof_eid);
three_dof.yaw -= gamepad_pan_factor * value * (1.0f / 60.0f);
}
);
ctx->controls["look_right_mouse"]->set_active_callback
(
[ctx, three_dof_eid, mouse_pan_factor](float value)
{
if (!ctx->mouse_look)
return;
auto& three_dof = ctx->entity_registry->get<entity::component::constraint::three_dof>(three_dof_eid);
three_dof.yaw -= mouse_pan_factor * value * (1.0f / 60.0f);
}
);
// Tilt up
ctx->controls["look_up_gamepad"]->set_active_callback
(
[ctx, three_dof_eid, gamepad_tilt_factor](float value)
{
auto& three_dof = ctx->entity_registry->get<entity::component::constraint::three_dof>(three_dof_eid);
three_dof.pitch -= gamepad_tilt_factor * value * (1.0f / 60.0f);
three_dof.pitch = std::max<float>(math::radians(-90.0f), three_dof.pitch);
}
);
ctx->controls["look_up_mouse"]->set_active_callback
(
[ctx, three_dof_eid, mouse_tilt_factor](float value)
{
if (!ctx->mouse_look)
return;
auto& three_dof = ctx->entity_registry->get<entity::component::constraint::three_dof>(three_dof_eid);
three_dof.pitch -= mouse_tilt_factor * value * (1.0f / 60.0f);
three_dof.pitch = std::max<float>(math::radians(-90.0f), three_dof.pitch);
}
);
// Tilt down
ctx->controls["look_down_gamepad"]->set_active_callback
(
[ctx, three_dof_eid, gamepad_tilt_factor](float value)
{
auto& three_dof = ctx->entity_registry->get<entity::component::constraint::three_dof>(three_dof_eid);
three_dof.pitch += gamepad_tilt_factor * value * (1.0f / 60.0f);
three_dof.pitch = std::min<float>(math::radians(90.0f), three_dof.pitch);
}
);
ctx->controls["look_down_mouse"]->set_active_callback
(
[ctx, three_dof_eid, mouse_tilt_factor](float value)
{
if (!ctx->mouse_look)
return;
auto& three_dof = ctx->entity_registry->get<entity::component::constraint::three_dof>(three_dof_eid);
three_dof.pitch += mouse_tilt_factor * value * (1.0f / 60.0f);
three_dof.pitch = std::min<float>(math::radians(90.0f), three_dof.pitch);
}
);
/*
// Use tool
ctx->controls["use_tool"]->set_activated_callback
(
[ctx]()
{
if (ctx->entities.count("active_tool"))
{
entity::id tool_eid = ctx->entities["active_tool"];
const auto& tool = ctx->entity_registry->get<entity::component::tool>(tool_eid);
if (tool.activated)
tool.activated();
}
}
);
ctx->controls["use_tool"]->set_deactivated_callback
(
[ctx]()
{
if (ctx->entities.count("active_tool"))
{
entity::id tool_eid = ctx->entities["active_tool"];
const auto& tool = ctx->entity_registry->get<entity::component::tool>(tool_eid);
if (tool.deactivated)
tool.deactivated();
}
}
);
ctx->controls["use_tool"]->set_active_callback
(
[ctx](float value)
{
if (ctx->entities.count("active_tool"))
{
entity::id tool_eid = ctx->entities["active_tool"];
const auto& tool = ctx->entity_registry->get<entity::component::tool>(tool_eid);
if (tool.active)
tool.active();
}
}
);
*/
// Setup pause control
ctx->controls["pause"]->set_activated_callback
(
[ctx]()
{
// Disable controls
disable_controls(ctx);
// Store paused state
ctx->paused_state =
{
"nuptial_flight",
std::bind(game::state::nuptial_flight::enter, ctx),
std::bind(game::state::nuptial_flight::exit, ctx)
};
// Change to pause menu state
application::state next_state;
next_state.name = "pause_menu";
next_state.enter = std::bind(game::state::pause_menu::enter, ctx);
next_state.exit = std::bind(game::state::pause_menu::exit, ctx);
ctx->app->change_state(next_state);
}
);
}
void disable_controls(game::context* ctx)
{
ctx->controls["pause"]->set_activated_callback(nullptr);
}
} // namespace nuptial_flight
} // namespace state
} // namespace game

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

@ -1,214 +0,0 @@
/*
* Copyright (C) 2021 Christopher J. Howard
*
* This file is part of Antkeeper source code.
*
* Antkeeper source code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#include "game/states/options-menu.hpp"
#include "game/states/main-menu.hpp"
#include "game/states/controls-menu.hpp"
#include "game/states/graphics-menu.hpp"
#include "game/states/sound-menu.hpp"
#include "game/states/language-menu.hpp"
#include "game/states/pause-menu.hpp"
#include "game/save.hpp"
#include "game/menu.hpp"
#include "animation/ease.hpp"
#include "animation/animation.hpp"
#include "animation/animator.hpp"
#include "application.hpp"
#include "scene/text.hpp"
namespace game {
namespace state {
namespace options_menu {
void enter(game::context* ctx)
{
// Construct menu item texts
scene::text* controls_text = new scene::text();
scene::text* graphics_text = new scene::text();
scene::text* sound_text = new scene::text();
scene::text* language_text = new scene::text();
scene::text* back_text = new scene::text();
// Set content of menu item texts
controls_text->set_content((*ctx->strings)["options_menu_controls"]);
graphics_text->set_content((*ctx->strings)["options_menu_graphics"]);
sound_text->set_content((*ctx->strings)["options_menu_sound"]);
language_text->set_content((*ctx->strings)["options_menu_language"]);
back_text->set_content((*ctx->strings)["back"]);
// Build list of menu item texts
ctx->menu_item_texts.push_back({controls_text, nullptr});
ctx->menu_item_texts.push_back({graphics_text, nullptr});
ctx->menu_item_texts.push_back({sound_text, nullptr});
ctx->menu_item_texts.push_back({language_text, nullptr});
ctx->menu_item_texts.push_back({back_text, nullptr});
// Init menu item index
game::menu::init_menu_item_index(ctx, "options");
game::menu::update_text_color(ctx);
game::menu::update_text_font(ctx);
game::menu::align_text(ctx, true);
game::menu::update_text_tweens(ctx);
game::menu::add_text_to_ui(ctx);
game::menu::setup_animations(ctx);
// Construct menu item callbacks
auto select_controls_callback = [ctx]()
{
// Disable controls
game::menu::clear_controls(ctx);
// Return to main menu
game::menu::fade_out
(
ctx,
[ctx]()
{
application::state next_state;
next_state.name = "controls_menu";
next_state.enter = std::bind(game::state::controls_menu::enter, ctx);
next_state.exit = std::bind(game::state::controls_menu::exit, ctx);
ctx->app->queue_state(next_state);
}
);
};
auto select_graphics_callback = [ctx]()
{
// Disable controls
game::menu::clear_controls(ctx);
// Return to main menu
game::menu::fade_out
(
ctx,
[ctx]()
{
application::state next_state;
next_state.name = "graphics_menu";
next_state.enter = std::bind(game::state::graphics_menu::enter, ctx);
next_state.exit = std::bind(game::state::graphics_menu::exit, ctx);
ctx->app->queue_state(next_state);
}
);
};
auto select_sound_callback = [ctx]()
{
// Disable controls
game::menu::clear_controls(ctx);
// Return to main menu
game::menu::fade_out
(
ctx,
[ctx]()
{
application::state next_state;
next_state.name = "sound_menu";
next_state.enter = std::bind(game::state::sound_menu::enter, ctx);
next_state.exit = std::bind(game::state::sound_menu::exit, ctx);
ctx->app->queue_state(next_state);
}
);
};
auto select_language_callback = [ctx]()
{
// Disable controls
game::menu::clear_controls(ctx);
// Return to main menu
game::menu::fade_out
(
ctx,
[ctx]()
{
application::state next_state;
next_state.name = "language_menu";
next_state.enter = std::bind(game::state::language_menu::enter, ctx);
next_state.exit = std::bind(game::state::language_menu::exit, ctx);
ctx->app->queue_state(next_state);
}
);
};
auto select_back_callback = [ctx]()
{
// Disable controls
game::menu::clear_controls(ctx);
// Save config
game::save_config(ctx);
application::state next_state;
if (ctx->paused_state)
{
// Return to pause menu
next_state.name = "pause_menu";
next_state.enter = std::bind(game::state::pause_menu::enter, ctx);
next_state.exit = std::bind(game::state::pause_menu::exit, ctx);
}
else
{
// Return to main menu
next_state.name = "main_menu";
next_state.enter = std::bind(game::state::main_menu::enter, ctx, false);
next_state.exit = std::bind(game::state::main_menu::exit, ctx);
}
game::menu::fade_out(ctx, std::bind(&application::queue_state, ctx->app, next_state));
};
// Build list of menu select callbacks
ctx->menu_select_callbacks.push_back(select_controls_callback);
ctx->menu_select_callbacks.push_back(select_graphics_callback);
ctx->menu_select_callbacks.push_back(select_sound_callback);
ctx->menu_select_callbacks.push_back(select_language_callback);
ctx->menu_select_callbacks.push_back(select_back_callback);
// Build list of menu right callbacks
ctx->menu_right_callbacks.resize(5, nullptr);
// Build list of menu left callbacks
ctx->menu_left_callbacks.resize(5, nullptr);
// Set menu back callback
ctx->menu_back_callback = select_back_callback;
// Queue menu control setup
ctx->function_queue.push(std::bind(game::menu::setup_controls, ctx));
// Fade in menu
game::menu::fade_in(ctx, nullptr);
}
void exit(game::context* ctx)
{
// Destruct menu
game::menu::clear_controls(ctx);
game::menu::clear_callbacks(ctx);
game::menu::delete_animations(ctx);
game::menu::remove_text_from_ui(ctx);
game::menu::delete_text(ctx);
// Save config
game::save_config(ctx);
}
} // namespace options_menu
} // namespace state
} // namespace game

+ 0
- 254
src/game/states/sound-menu.cpp View File

@ -1,254 +0,0 @@
/*
* Copyright (C) 2021 Christopher J. Howard
*
* This file is part of Antkeeper source code.
*
* Antkeeper source code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#include "game/states/sound-menu.hpp"
#include "game/states/options-menu.hpp"
#include "application.hpp"
#include "scene/text.hpp"
#include "debug/logger.hpp"
#include "game/menu.hpp"
#include "animation/timeline.hpp"
namespace game {
namespace state {
namespace sound_menu {
static void update_value_text_content(game::context* ctx)
{
const std::string string_on = (*ctx->strings)["on"];
const std::string string_off = (*ctx->strings)["off"];
std::get<1>(ctx->menu_item_texts[0])->set_content(std::to_string(static_cast<int>(std::round(ctx->master_volume * 100.0f))) + "%");
std::get<1>(ctx->menu_item_texts[1])->set_content(std::to_string(static_cast<int>(std::round(ctx->ambience_volume * 100.0f))) + "%");
std::get<1>(ctx->menu_item_texts[2])->set_content(std::to_string(static_cast<int>(std::round(ctx->effects_volume * 100.0f))) + "%");
std::get<1>(ctx->menu_item_texts[3])->set_content((ctx->mono_audio) ? string_on : string_off);
std::get<1>(ctx->menu_item_texts[4])->set_content((ctx->captions) ? string_on : string_off);
std::get<1>(ctx->menu_item_texts[5])->set_content(std::to_string(static_cast<int>(std::round(ctx->captions_size * 100.0f))) + "%");
}
void enter(game::context* ctx)
{
// Construct menu item texts
scene::text* master_volume_name_text = new scene::text();
scene::text* master_volume_value_text = new scene::text();
scene::text* ambience_volume_name_text = new scene::text();
scene::text* ambience_volume_value_text = new scene::text();
scene::text* effects_volume_name_text = new scene::text();
scene::text* effects_volume_value_text = new scene::text();
scene::text* mono_audio_name_text = new scene::text();
scene::text* mono_audio_value_text = new scene::text();
scene::text* captions_name_text = new scene::text();
scene::text* captions_value_text = new scene::text();
scene::text* captions_size_name_text = new scene::text();
scene::text* captions_size_value_text = new scene::text();
scene::text* back_text = new scene::text();
// Build list of menu item texts
ctx->menu_item_texts.push_back({master_volume_name_text, master_volume_value_text});
ctx->menu_item_texts.push_back({ambience_volume_name_text, ambience_volume_value_text});
ctx->menu_item_texts.push_back({effects_volume_name_text, effects_volume_value_text});
ctx->menu_item_texts.push_back({mono_audio_name_text, mono_audio_value_text});
ctx->menu_item_texts.push_back({captions_name_text, captions_value_text});
ctx->menu_item_texts.push_back({captions_size_name_text, captions_size_value_text});
ctx->menu_item_texts.push_back({back_text, nullptr});
// Set content of menu item texts
master_volume_name_text->set_content((*ctx->strings)["sound_menu_master_volume"]);
ambience_volume_name_text->set_content((*ctx->strings)["sound_menu_ambience_volume"]);
effects_volume_name_text->set_content((*ctx->strings)["sound_menu_effects_volume"]);
mono_audio_name_text->set_content((*ctx->strings)["sound_menu_mono_audio"]);
captions_name_text->set_content((*ctx->strings)["sound_menu_captions"]);
captions_size_name_text->set_content((*ctx->strings)["sound_menu_captions_size"]);
back_text->set_content((*ctx->strings)["back"]);
update_value_text_content(ctx);
// Init menu item index
game::menu::init_menu_item_index(ctx, "sound");
game::menu::update_text_color(ctx);
game::menu::update_text_font(ctx);
game::menu::align_text(ctx);
game::menu::update_text_tweens(ctx);
game::menu::add_text_to_ui(ctx);
game::menu::setup_animations(ctx);
// Construct menu item callbacks
auto increase_volume_callback = [ctx](float* volume)
{
// Increase volume
if (ctx->controls["menu_modifier"]->is_active())
*volume += 0.01f;
else
*volume += 0.1f;
// Limit volume
if (*volume > 1.0f)
*volume = 1.0f;
update_value_text_content(ctx);
game::menu::align_text(ctx);
game::menu::update_text_tweens(ctx);
};
auto decrease_volume_callback = [ctx](float* volume)
{
// Decrease volume
if (ctx->controls["menu_modifier"]->is_active())
*volume -= 0.01f;
else
*volume -= 0.1f;
// Limit volume
if (*volume < 0.0f)
*volume = 0.0f;
update_value_text_content(ctx);
game::menu::align_text(ctx);
game::menu::update_text_tweens(ctx);
};
auto toggle_mono_audio_callback = [ctx]()
{
ctx->mono_audio = !ctx->mono_audio;
update_value_text_content(ctx);
game::menu::align_text(ctx);
game::menu::update_text_tweens(ctx);
};
auto toggle_captions_callback = [ctx]()
{
ctx->captions = !ctx->captions;
update_value_text_content(ctx);
game::menu::align_text(ctx);
game::menu::update_text_tweens(ctx);
};
auto increase_captions_size_callback = [ctx]()
{
// Increase size
if (ctx->controls["menu_modifier"]->is_active())
ctx->captions_size += 0.01f;
else
ctx->captions_size += 0.1f;
// Limit size
if (ctx->captions_size > 2.0f)
ctx->captions_size = 2.0f;
update_value_text_content(ctx);
game::menu::align_text(ctx);
game::menu::update_text_tweens(ctx);
};
auto decrease_captions_size_callback = [ctx]()
{
// Decrease size
if (ctx->controls["menu_modifier"]->is_active())
ctx->captions_size -= 0.01f;
else
ctx->captions_size -= 0.1f;
// Limit size
if (ctx->captions_size < 0.1f)
ctx->captions_size = 0.1f;
update_value_text_content(ctx);
game::menu::align_text(ctx);
game::menu::update_text_tweens(ctx);
};
auto select_back_callback = [ctx]()
{
// Disable controls
game::menu::clear_controls(ctx);
game::menu::fade_out
(
ctx,
[ctx]()
{
application::state next_state;
next_state.name = "options_menu";
next_state.enter = std::bind(game::state::options_menu::enter, ctx);
next_state.exit = std::bind(game::state::options_menu::exit, ctx);
ctx->app->queue_state(next_state);
}
);
};
// Build list of menu select callbacks
ctx->menu_select_callbacks.push_back(std::bind(increase_volume_callback, &ctx->master_volume));
ctx->menu_select_callbacks.push_back(std::bind(increase_volume_callback, &ctx->ambience_volume));
ctx->menu_select_callbacks.push_back(std::bind(increase_volume_callback, &ctx->effects_volume));
ctx->menu_select_callbacks.push_back(toggle_mono_audio_callback);
ctx->menu_select_callbacks.push_back(toggle_captions_callback);
ctx->menu_select_callbacks.push_back(increase_captions_size_callback);
ctx->menu_select_callbacks.push_back(select_back_callback);
// Build list of menu left callbacks
ctx->menu_left_callbacks.push_back(std::bind(decrease_volume_callback, &ctx->master_volume));
ctx->menu_left_callbacks.push_back(std::bind(decrease_volume_callback, &ctx->ambience_volume));
ctx->menu_left_callbacks.push_back(std::bind(decrease_volume_callback, &ctx->effects_volume));
ctx->menu_left_callbacks.push_back(toggle_mono_audio_callback);
ctx->menu_left_callbacks.push_back(toggle_captions_callback);
ctx->menu_left_callbacks.push_back(decrease_captions_size_callback);
ctx->menu_left_callbacks.push_back(nullptr);
// Build list of menu right callbacks
ctx->menu_right_callbacks.push_back(std::bind(increase_volume_callback, &ctx->master_volume));
ctx->menu_right_callbacks.push_back(std::bind(increase_volume_callback, &ctx->ambience_volume));
ctx->menu_right_callbacks.push_back(std::bind(increase_volume_callback, &ctx->effects_volume));
ctx->menu_right_callbacks.push_back(toggle_mono_audio_callback);
ctx->menu_right_callbacks.push_back(toggle_captions_callback);
ctx->menu_right_callbacks.push_back(increase_captions_size_callback);
ctx->menu_right_callbacks.push_back(nullptr);
// Set menu back callback
ctx->menu_back_callback = select_back_callback;
// Schedule menu control setup
timeline* timeline = ctx->timeline;
float t = timeline->get_position();
timeline->add_sequence({{t + game::menu::input_delay, std::bind(game::menu::setup_controls, ctx)}});
// Fade in menu
game::menu::fade_in(ctx, nullptr);
}
void exit(game::context* ctx)
{
// Destruct menu
game::menu::clear_controls(ctx);
game::menu::clear_callbacks(ctx);
game::menu::delete_animations(ctx);
game::menu::remove_text_from_ui(ctx);
game::menu::delete_text(ctx);
// Update config
(*ctx->config)["master_volume"] = ctx->master_volume;
(*ctx->config)["ambience_volume"] = ctx->ambience_volume;
(*ctx->config)["effects_volume"] = ctx->effects_volume;
(*ctx->config)["mono_audio"] = ctx->mono_audio;
(*ctx->config)["captions"] = ctx->captions;
(*ctx->config)["captions_size"] = ctx->captions_size;
}
} // namespace sound_menu
} // namespace state
} // namespace game

+ 0
- 160
src/game/states/splash.cpp View File

@ -1,160 +0,0 @@
/*
* Copyright (C) 2021 Christopher J. Howard
*
* This file is part of Antkeeper source code.
*
* Antkeeper source code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#include "game/states/splash.hpp"
#include "game/states/main-menu.hpp"
#include "animation/screen-transition.hpp"
#include "animation/animation.hpp"
#include "animation/animator.hpp"
#include "animation/ease.hpp"
#include "application.hpp"
#include "render/passes/clear-pass.hpp"
namespace game {
namespace state {
namespace splash {
void enter(game::context* ctx)
{
ctx->ui_clear_pass->set_cleared_buffers(true, true, false);
// Load animation timing configuration
double splash_fade_in_duration = 0.0;
double splash_duration = 0.0;
double splash_fade_out_duration = 0.0;
if (ctx->config->contains("splash_fade_in_duration"))
splash_fade_in_duration = (*ctx->config)["splash_fade_in_duration"].get<double>();
if (ctx->config->contains("splash_duration"))
splash_duration = (*ctx->config)["splash_duration"].get<double>();
if (ctx->config->contains("splash_fade_out_duration"))
splash_fade_out_duration = (*ctx->config)["splash_fade_out_duration"].get<double>();
// Build splash fade in animation
ctx->splash_fade_in_animation = new animation<float>();
animation_channel<float>* splash_fade_in_opacity_channel = ctx->splash_fade_in_animation->add_channel(0);
ctx->splash_fade_in_animation->set_interpolator(ease<float>::out_cubic);
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
ctx->splash_fade_out_animation = new animation<float>();
animation_channel<float>* splash_fade_out_opacity_channel = ctx->splash_fade_out_animation->add_channel(0);
ctx->splash_fade_out_animation->set_interpolator(ease<float>::out_cubic);
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 = [ctx](int channel, const float& opacity)
{
static_cast<render::material_property<float4>*>(ctx->splash_billboard_material->get_property("tint"))->set_value(float4{1, 1, 1, opacity});
};
ctx->splash_fade_in_animation->set_frame_callback(set_splash_opacity);
ctx->splash_fade_out_animation->set_frame_callback(set_splash_opacity);
// Reset splash color when animation starts
ctx->splash_fade_in_animation->set_start_callback
(
[ctx]()
{
static_cast<render::material_property<float4>*>(ctx->splash_billboard_material->get_property("tint"))->set_value(float4{1, 1, 1, 0});
ctx->splash_billboard_material->update_tweens();
}
);
// Trigger splash fade out animation when splash fade in animation ends
ctx->splash_fade_in_animation->set_end_callback
(
[ctx]()
{
ctx->splash_fade_out_animation->play();
}
);
// Trigger a state change when the splash fade out animation ends
ctx->splash_fade_out_animation->set_end_callback
(
[ctx]()
{
application::state next_state;
next_state.name = "main_menu";
next_state.enter = std::bind(game::state::main_menu::enter, ctx, true);
next_state.exit = std::bind(game::state::main_menu::exit, ctx);
ctx->app->queue_state(next_state);
}
);
// Add splash fade animations to animator
ctx->animator->add_animation(ctx->splash_fade_in_animation);
ctx->animator->add_animation(ctx->splash_fade_out_animation);
// Start splash fade in animation
ctx->splash_fade_in_animation->play();
// Set up splash skipper
ctx->input_listener->set_callback
(
[ctx](const event_base& event)
{
auto id = event.get_event_type_id();
if (id != mouse_moved_event::event_type_id && id != mouse_wheel_scrolled_event::event_type_id && id != gamepad_axis_moved_event::event_type_id)
{
// Black out screen
ctx->rasterizer->set_clear_color(0.0f, 0.0f, 0.0f, 1.0f);
ctx->rasterizer->clear_framebuffer(true, false, false);
ctx->app->swap_buffers();
// Change state
application::state next_state;
next_state.name = "main_menu";
next_state.enter = std::bind(game::state::main_menu::enter, ctx, true);
next_state.exit = std::bind(game::state::main_menu::exit, ctx);
ctx->app->queue_state(next_state);
}
}
);
ctx->input_listener->set_enabled(true);
// Add splash billboard to UI scene
ctx->ui_scene->add_object(ctx->splash_billboard);
}
void exit(game::context* ctx)
{
// Remove splash billboard from UI scene
ctx->ui_scene->remove_object(ctx->splash_billboard);
// Disable splash skipper
ctx->input_listener->set_enabled(false);
ctx->input_listener->set_callback(nullptr);
// Destruct splash fade animations
ctx->animator->remove_animation(ctx->splash_fade_in_animation);
ctx->animator->remove_animation(ctx->splash_fade_out_animation);
delete ctx->splash_fade_in_animation;
delete ctx->splash_fade_out_animation;
ctx->splash_fade_in_animation = nullptr;
ctx->splash_fade_out_animation = nullptr;
ctx->ui_clear_pass->set_cleared_buffers(false, true, false);
}
} // namespace splash
} // namespace state
} // namespace game

+ 28
- 28
src/game/tools.cpp View File

@ -25,82 +25,82 @@
#include "entity/components/celestial-body.hpp"
#include "utility/timestamp.hpp"
#include "render/material.hpp"
#include "game/graphics.hpp"
namespace game {
entity::id build_camera_tool(game::context* ctx)
entity::id build_camera_tool(game::context& ctx)
{
// Create camera tool entity
entity::id tool_eid = ctx->entity_registry->create();
entity::id tool_eid = ctx.entity_registry->create();
// Create tool component
entity::component::tool tool;
// Setup tool activated callback
tool.activated = [ctx]()
tool.activated = [&ctx]()
{
if (!ctx->camera_flash_animation->is_stopped())
if (!ctx.camera_flash_animation->is_stopped())
return;
std::string path = ctx->screenshots_path + "antkeeper-" + timestamp() + ".png";
ctx->app->save_frame(path);
game::graphics::save_screenshot(ctx);
render::material_property<float4>* tint = static_cast<render::material_property<float4>*>(ctx->camera_flash_billboard->get_material()->get_property("tint"));
render::material_property<float4>* tint = static_cast<render::material_property<float4>*>(ctx.camera_flash_billboard->get_material()->get_property("tint"));
tint->set_value({1.0f, 1.0f, 1.0f, 1.0f});
ctx->camera_flash_billboard->get_material()->update_tweens();
ctx->ui_scene->add_object(ctx->camera_flash_billboard);
ctx.camera_flash_billboard->get_material()->update_tweens();
ctx.ui_scene->add_object(ctx.camera_flash_billboard);
ctx->camera_flash_animation->set_end_callback
ctx.camera_flash_animation->set_end_callback
(
[ctx]()
[&ctx]()
{
ctx->ui_scene->remove_object(ctx->camera_flash_billboard);
ctx.ui_scene->remove_object(ctx.camera_flash_billboard);
}
);
ctx->camera_flash_animation->set_frame_callback
ctx.camera_flash_animation->set_frame_callback
(
[ctx, tint](int channel, const float& opacity)
[&ctx, tint](int channel, const float& opacity)
{
tint->set_value({1.0f, 1.0f, 1.0f, opacity});
}
);
ctx->animator->remove_animation(ctx->camera_flash_animation);
ctx->animator->add_animation(ctx->camera_flash_animation);
ctx->camera_flash_animation->rewind();
ctx->camera_flash_animation->play();
ctx.animator->remove_animation(ctx.camera_flash_animation);
ctx.animator->add_animation(ctx.camera_flash_animation);
ctx.camera_flash_animation->rewind();
ctx.camera_flash_animation->play();
};
// Add tool component to camera tool entity
ctx->entity_registry->assign<entity::component::tool>(tool_eid, tool);
ctx.entity_registry->assign<entity::component::tool>(tool_eid, tool);
return tool_eid;
}
entity::id build_time_tool(game::context* ctx)
entity::id build_time_tool(game::context& ctx)
{
// Create time tool entity
entity::id tool_eid = ctx->entity_registry->create();
entity::id tool_eid = ctx.entity_registry->create();
// Create tool component
entity::component::tool tool;
// Setup tool active calback
tool.active = [ctx]()
tool.active = [&ctx]()
{
auto [mouse_x, mouse_y] = ctx->app->get_mouse()->get_current_position();
auto [window_w, window_h] = ctx->app->get_viewport_dimensions();
auto [mouse_x, mouse_y] = ctx.app->get_mouse()->get_current_position();
auto [window_w, window_h] = ctx.app->get_viewport_dimensions();
entity::id planet_eid = ctx->entities["planet"];
entity::component::celestial_body body = ctx->entity_registry->get<entity::component::celestial_body>(planet_eid);
entity::id planet_eid = ctx.entities["planet"];
entity::component::celestial_body body = ctx.entity_registry->get<entity::component::celestial_body>(planet_eid);
body.axial_rotation = math::radians(360.0f) * ((float)mouse_x / (float)window_w);
ctx->entity_registry->replace<entity::component::celestial_body>(planet_eid, body);
ctx.entity_registry->replace<entity::component::celestial_body>(planet_eid, body);
};
// Add tool component to time tool entity
ctx->entity_registry->assign<entity::component::tool>(tool_eid, tool);
ctx.entity_registry->assign<entity::component::tool>(tool_eid, tool);
return tool_eid;
}

+ 2
- 2
src/game/tools.hpp View File

@ -25,8 +25,8 @@
namespace game {
entity::id build_camera_tool(game::context* ctx);
entity::id build_time_tool(game::context* ctx);
entity::id build_camera_tool(game::context& ctx);
entity::id build_time_tool(game::context& ctx);
} // namespace game

+ 30
- 30
src/game/world.cpp View File

@ -53,10 +53,10 @@
namespace game {
namespace world {
void create_stars(game::context* ctx)
void create_stars(game::context& ctx)
{
// Load star catalog
string_table* star_catalog = ctx->resource_manager->load<string_table>("stars.csv");
string_table* star_catalog = ctx.resource_manager->load<string_table>("stars.csv");
// Allocate star catalog vertex data
std::size_t star_count = 0;
@ -130,7 +130,7 @@ void create_stars(game::context* ctx)
}
// Unload star catalog
ctx->resource_manager->unload("stars.csv");
ctx.resource_manager->unload("stars.csv");
// Allocate stars model
render::model* stars_model = new render::model();
@ -170,7 +170,7 @@ void create_stars(game::context* ctx)
vao->bind(render::vertex_attribute::color, color_attribute);
// Load star material
render::material* star_material = ctx->resource_manager->load<render::material>("fixed-star.mtl");
render::material* star_material = ctx.resource_manager->load<render::material>("fixed-star.mtl");
// Create model group
render::model_group* stars_model_group = stars_model->add_group("stars");
@ -180,15 +180,15 @@ void create_stars(game::context* ctx)
stars_model_group->set_index_count(star_count);
// Pass stars model to sky pass
ctx->surface_sky_pass->set_stars_model(stars_model);
ctx.surface_sky_pass->set_stars_model(stars_model);
}
void create_sun(game::context* ctx)
void create_sun(game::context& ctx)
{
// Create sun entity
entity::archetype* sun_archetype = ctx->resource_manager->load<entity::archetype>("sun.ent");
entity::id sun_eid = sun_archetype->create(*ctx->entity_registry);
ctx->entities["sun"] = sun_eid;
entity::archetype* sun_archetype = ctx.resource_manager->load<entity::archetype>("sun.ent");
entity::id sun_eid = sun_archetype->create(*ctx.entity_registry);
ctx.entities["sun"] = sun_eid;
// Create direct sun light scene object
scene::directional_light* sun_direct = new scene::directional_light();
@ -200,20 +200,20 @@ void create_sun(game::context* ctx)
sun_ambient->update_tweens();
// Add sun light scene objects to surface scene
ctx->surface_scene->add_object(sun_direct);
ctx->surface_scene->add_object(sun_ambient);
ctx.surface_scene->add_object(sun_direct);
ctx.surface_scene->add_object(sun_ambient);
// Pass direct sun light scene object to shadow map pass and astronomy system
ctx->surface_shadow_map_pass->set_light(sun_direct);
ctx->astronomy_system->set_sun_light(sun_direct);
ctx.surface_shadow_map_pass->set_light(sun_direct);
ctx.astronomy_system->set_sun_light(sun_direct);
}
void create_planet(game::context* ctx)
void create_planet(game::context& ctx)
{
// Create planet entity
entity::archetype* planet_archetype = ctx->resource_manager->load<entity::archetype>("planet.ent");
entity::id planet_eid = planet_archetype->create(*ctx->entity_registry);
ctx->entities["planet"] = planet_eid;
entity::archetype* planet_archetype = ctx.resource_manager->load<entity::archetype>("planet.ent");
entity::id planet_eid = planet_archetype->create(*ctx.entity_registry);
ctx.entities["planet"] = planet_eid;
// Assign planetary terrain component
entity::component::terrain terrain;
@ -224,38 +224,38 @@ void create_planet(game::context* ctx)
};
terrain.max_lod = 0;
terrain.patch_material = nullptr;
ctx->entity_registry->assign<entity::component::terrain>(planet_eid, terrain);
ctx.entity_registry->assign<entity::component::terrain>(planet_eid, terrain);
// Pass planet to astronomy system as reference body
ctx->astronomy_system->set_reference_body(planet_eid);
ctx.astronomy_system->set_reference_body(planet_eid);
// Load sky model
ctx->surface_sky_pass->set_sky_model(ctx->resource_manager->load<render::model>("sky-dome.mdl"));
ctx.surface_sky_pass->set_sky_model(ctx.resource_manager->load<render::model>("sky-dome.mdl"));
}
void create_moon(game::context* ctx)
void create_moon(game::context& ctx)
{
// Create lunar entity
entity::id moon_eid = ctx->entity_registry->create();
ctx->entities["moon"] = moon_eid;
entity::id moon_eid = ctx.entity_registry->create();
ctx.entities["moon"] = moon_eid;
// Pass moon model to sky pass
ctx->surface_sky_pass->set_moon_model(ctx->resource_manager->load<render::model>("moon.mdl"));
ctx.surface_sky_pass->set_moon_model(ctx.resource_manager->load<render::model>("moon.mdl"));
}
void set_time(game::context* ctx, double t)
void set_time(game::context& ctx, double t)
{
ctx->astronomy_system->set_universal_time(t);
ctx->orbit_system->set_universal_time(t);
ctx.astronomy_system->set_universal_time(t);
ctx.orbit_system->set_universal_time(t);
}
void set_time_scale(game::context* ctx, double scale)
void set_time_scale(game::context& ctx, double scale)
{
static constexpr double seconds_per_day = 24.0 * 60.0 * 60.0;
scale /= seconds_per_day;
ctx->orbit_system->set_time_scale(scale);
ctx->astronomy_system->set_time_scale(scale);
ctx.orbit_system->set_time_scale(scale);
ctx.astronomy_system->set_time_scale(scale);
}
} // namespace world

+ 6
- 6
src/game/world.hpp View File

@ -28,22 +28,22 @@ namespace game {
namespace world {
/// Creates the fixed stars.
void create_stars(game::context* ctx);
void create_stars(game::context& ctx);
/// Creates the sun.
void create_sun(game::context* ctx);
void create_sun(game::context& ctx);
/// Creates the planet.
void create_planet(game::context* ctx);
void create_planet(game::context& ctx);
/// Creates the moon.
void create_moon(game::context* ctx);
void create_moon(game::context& ctx);
/// Sets the current time.
void set_time(game::context* ctx, double t);
void set_time(game::context& ctx, double t);
/// Sets rate at which time passes.
void set_time_scale(game::context* ctx, double scale);
void set_time_scale(game::context& ctx, double scale);
} // namespace menu
} // namespace game

+ 8
- 14
src/main.cpp View File

@ -17,9 +17,8 @@
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#include "application.hpp"
#include "game/states/boot.hpp"
#include <functional>
#include "game/context.hpp"
#include "game/state/boot.hpp"
#include <iostream>
#include <stdexcept>
@ -27,22 +26,17 @@ int main(int argc, char* argv[])
{
try
{
// Construct application
application app;
// Allocate game context
game::context ctx;
// Setup initial application state
application::state boot_state;
boot_state.name = "boot";
boot_state.enter = std::bind(game::state::boot::enter, &app, argc, argv);
boot_state.exit = std::bind(game::state::boot::exit, &app);
// Execute application then return the exit status code
return app.execute(boot_state);
// Enter boot state
ctx.state_machine.emplace(new game::state::boot(ctx, argc, argv));
}
catch (const std::exception& e)
{
std::cerr << "Unhandled exception: \"" << e.what() << "\"" << std::endl;
return EXIT_FAILURE;
}
return EXIT_FAILURE;
return EXIT_SUCCESS;
}

+ 39
- 0
src/state-machine.hpp View File

@ -0,0 +1,39 @@
/*
* Copyright (C) 2021 Christopher J. Howard
*
* This file is part of Antkeeper source code.
*
* Antkeeper source code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_HSM_STATE_MACHINE_HPP
#define ANTKEEPER_HSM_STATE_MACHINE_HPP
#include <memory>
#include <stack>
/// Hierarchical State Machine (HSM)
namespace hsm {
/**
* Stack-based hierarchical state machine.
*
* @tparam T State type.
*/
template <typename T>
using state_machine = std::stack<std::unique_ptr<T>>;
} // namespace hsm
#endif // ANTKEEPER_HSM_STATE_MACHINE_HPP

+ 11
- 43
src/utility/paths.cpp View File

@ -48,9 +48,9 @@
}
#endif
std::string get_executable_path()
std::filesystem::path get_executable_path()
{
std::string executable_path;
std::filesystem::path executable_path;
#if defined(_WIN32)
// Get executable path on Windows
@ -73,38 +73,29 @@ std::string get_executable_path()
return executable_path;
}
std::string get_data_path(const std::string& application_name)
std::filesystem::path get_data_path(const std::string& application_name)
{
std::string data_path;
#if defined(_WIN32)
std::string executable_path = get_executable_path();
std::size_t delimeter = executable_path.find_last_of("\\/") + 1;
data_path = executable_path.substr(0, delimeter);
return get_executable_path().parent_path();
#else
std::string executable_path = get_executable_path();
std::size_t delimeter = executable_path.find_last_of("\\/") + 1;
data_path = executable_path.substr(0, delimeter) + std::string("../share/") + application_name + std::string("/");
return get_executable_path().parent_path().parent_path() / "share" / application_name;
#endif
return data_path;
}
std::string get_config_path(const std::string& application_name)
std::filesystem::path get_config_path(const std::string& application_name)
{
std::string config_path;
std::filesystem::path config_path;
#if defined(_WIN32)
std::wstring wpath(MAX_PATH, L'\0');
if (SHGetSpecialFolderPathW(nullptr, &wpath[0], CSIDL_LOCAL_APPDATA, FALSE))
{
wpath.erase(std::find(wpath.begin(), wpath.end(), L'\0'), wpath.end());
config_path = narrow(wpath);
config_path += std::string("\\") + application_name + std::string("\\");
config_path = std::filesystem::path(narrow(wpath)) / application_name;
}
#else
// Determine home path
std::string home_path = std::string(getpwuid(getuid())->pw_dir);
std::filesystem::path home_path = getpwuid(getuid())->pw_dir;
// Determine config path
char* xdgConfigHome = std::getenv("XDG_CONFIG_HOME");
@ -112,36 +103,13 @@ std::string get_config_path(const std::string& application_name)
{
// Default to $HOME/.config/ as per:
// https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html#variables
config_path = home_path + std::string("/.config/") + application_name + std::string("/");
config_path = home_path / ".config/" / application_name;
}
else
{
config_path = xdgConfigHome + std::string("/") + application_name + std::string("/");
config_path = std::filesystem::path(xdgConfigHome) / application_name;
}
#endif
return config_path;
}
bool path_exists(const std::string& path)
{
#if defined(_WIN32)
std::wstring wpath = widen(path);
DWORD attributes = GetFileAttributesW(wpath.c_str());
return (attributes != INVALID_FILE_ATTRIBUTES);
#else
struct stat info;
return (stat(path.c_str(), &info) == 0);
#endif
}
bool create_directory(const std::string& path)
{
#if defined(_WIN32)
std::wstring wpath = widen(path);
return (CreateDirectoryW(wpath.c_str(), nullptr) != 0);
#else
return (mkdir(path.c_str(), 0777) == 0);
#endif
}

+ 4
- 9
src/utility/paths.hpp View File

@ -20,6 +20,7 @@
#ifndef ANTKEEPER_PATHS_HPP
#define ANTKEEPER_PATHS_HPP
#include <filesystem>
#include <string>
/**
@ -27,7 +28,7 @@
*
* @return Path to the application's executable.
*/
std::string get_executable_path();
std::filesystem::path get_executable_path();
/**
* Returns the absolute path to the directory containing application data.
@ -38,7 +39,7 @@ std::string get_executable_path();
* @param application_name Name of the application.
* @return Path to the application's data directory.
*/
std::string get_data_path(const std::string& application_name);
std::filesystem::path get_data_path(const std::string& application_name);
/**
* Returns the absolute path to the directory containing user-specific application data.
@ -49,13 +50,7 @@ std::string get_data_path(const std::string& application_name);
* @param application_name Name of the application.
* @return Path to the application's config directory.
*/
std::string get_config_path(const std::string& application_name);
/// Checks if a file or directory exists
bool path_exists(const std::string& path);
/// Creates a directory
bool create_directory(const std::string& path);
std::filesystem::path get_config_path(const std::string& application_name);
#endif // ANTKEEPER_PATHS_HPP

Loading…
Cancel
Save