💿🐜 Antkeeper source code https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

227 lines
6.0 KiB

/*
* Copyright (C) 2023 Christopher J. Howard
*
* This file is part of Antkeeper source code.
*
* Antkeeper source code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#include "game/controls.hpp"
#include "game/menu.hpp"
#include <engine/config.hpp>
void setup_menu_controls(::game& ctx)
{
// Setup menu controls
ctx.menu_action_subscriptions.emplace_back
(
ctx.menu_up_action.get_activated_channel().subscribe
(
[&ctx](const auto& event)
{
--(*ctx.menu_item_index);
if (*ctx.menu_item_index < 0)
*ctx.menu_item_index = static_cast<int>(ctx.menu_item_texts.size()) - 1;
::menu::update_text_color(ctx);
}
)
);
ctx.menu_action_subscriptions.emplace_back
(
ctx.menu_down_action.get_activated_channel().subscribe
(
[&ctx](const auto& event)
{
++(*ctx.menu_item_index);
if (*ctx.menu_item_index >= ctx.menu_item_texts.size())
*ctx.menu_item_index = 0;
::menu::update_text_color(ctx);
}
)
);
ctx.menu_action_subscriptions.emplace_back
(
ctx.menu_left_action.get_activated_channel().subscribe
(
[&ctx](const auto& event)
{
auto callback = ctx.menu_left_callbacks[*ctx.menu_item_index];
if (callback != nullptr)
callback();
}
)
);
ctx.menu_action_subscriptions.emplace_back
(
ctx.menu_right_action.get_activated_channel().subscribe
(
[&ctx](const auto& event)
{
auto callback = ctx.menu_right_callbacks[*ctx.menu_item_index];
if (callback != nullptr)
callback();
}
)
);
ctx.menu_action_subscriptions.emplace_back
(
ctx.menu_select_action.get_activated_channel().subscribe
(
[&ctx](const auto& event)
{
const auto& callback = ctx.menu_select_callbacks[*ctx.menu_item_index];
if (callback != nullptr)
callback();
}
)
);
ctx.menu_action_subscriptions.emplace_back
(
ctx.menu_back_action.get_activated_channel().subscribe
(
[&ctx](const auto& event)
{
if (ctx.menu_back_callback != nullptr)
ctx.menu_back_callback();
}
)
);
// Set activation threshold for menu navigation controls to mitigate drifting gamepad axes
auto menu_action_threshold = [](float x) -> bool
{
return x > 0.5f;
};
ctx.menu_up_action.set_threshold_function(menu_action_threshold);
ctx.menu_down_action.set_threshold_function(menu_action_threshold);
ctx.menu_left_action.set_threshold_function(menu_action_threshold);
ctx.menu_right_action.set_threshold_function(menu_action_threshold);
}
void enable_menu_controls(::game& ctx)
{
ctx.menu_action_map.enable();
// Function to select menu item at mouse position
auto select_menu_item = [&ctx](const math::fvec2& mouse_position) -> bool
{
const float padding = config::menu_mouseover_padding * ctx.menu_font.get_font_metrics().size;
for (std::size_t i = 0; i < ctx.menu_item_texts.size(); ++i)
{
auto [name, value] = ctx.menu_item_texts[i];
const auto& name_bounds = name->get_bounds();
float min_x = name_bounds.min.x();
float min_y = name_bounds.min.y();
float max_x = name_bounds.max.x();
float max_y = name_bounds.max.y();
if (value)
{
const auto& value_bounds = value->get_bounds();
min_x = std::min<float>(min_x, value_bounds.min.x());
min_y = std::min<float>(min_y, value_bounds.min.y());
max_x = std::max<float>(max_x, value_bounds.max.x());
max_y = std::max<float>(max_y, value_bounds.max.y());
}
min_x -= padding;
min_y -= padding;
max_x += padding;
max_y += padding;
const auto& viewport = ctx.window->get_viewport_size();
const float x = mouse_position.x();
const float y = static_cast<float>((viewport[1] - mouse_position.y() + 1));
if (x >= min_x && x <= max_x)
{
if (y >= min_y && y <= max_y)
{
*ctx.menu_item_index = static_cast<int>(i);
::menu::update_text_color(ctx);
return true;
}
}
}
return false;
};
// Enable menu mouse tracking
ctx.menu_mouse_subscriptions.clear();
ctx.menu_mouse_subscriptions.emplace_back
(
ctx.input_manager->get_event_dispatcher().subscribe<input::mouse_moved_event>
(
[&ctx, select_menu_item](const auto& event)
{
// Select menu item at mouse position (if any)
select_menu_item(math::fvec2(event.position));
}
)
);
ctx.menu_mouse_subscriptions.emplace_back
(
ctx.input_manager->get_event_dispatcher().subscribe<input::mouse_button_pressed_event>
(
[&ctx, select_menu_item](const auto& event)
{
// Select menu item at mouse position (if any)
if (select_menu_item(math::fvec2(event.position)))
{
// Determine appropriate menu item callback
auto callback = ctx.menu_select_callbacks[*ctx.menu_item_index];
if (event.button == input::mouse_button::left)
{
if (ctx.menu_left_callbacks[*ctx.menu_item_index])
{
callback = ctx.menu_left_callbacks[*ctx.menu_item_index];
}
}
else if (event.button == input::mouse_button::right)
{
if (ctx.menu_right_callbacks[*ctx.menu_item_index])
{
callback = ctx.menu_right_callbacks[*ctx.menu_item_index];
}
}
// Invoke menu item callback
if (callback)
{
callback();
}
}
}
)
);
}
void disable_menu_controls(::game& ctx)
{
ctx.menu_action_map.disable();
ctx.menu_up_action.reset();
ctx.menu_down_action.reset();
ctx.menu_left_action.reset();
ctx.menu_right_action.reset();
ctx.menu_select_action.reset();
ctx.menu_back_action.reset();
ctx.menu_modifier_action.reset();
ctx.menu_mouse_subscriptions.clear();
}