Files
coffee_crisis_arcade_edition/source/define_buttons.cpp

269 lines
8.9 KiB
C++

#include "define_buttons.hpp"
#include <algorithm> // Para __all_of_fn, all_of
#include <functional> // Para identity
#include <memory> // Para allocator, unique_ptr, shared_ptr, make_unique, operator==
#include "color.hpp" // Para Color
#include "input.hpp" // Para Input
#include "input_types.hpp" // Para InputAction
#include "lang.hpp" // Para getText
#include "options.hpp" // Para Gamepad
#include "param.hpp" // Para Param, ParamGame, param
#include "resource.hpp" // Para Resource
#include "ui/window_message.hpp" // Para WindowMessage
#include "utils.hpp" // Para Zone
DefineButtons::DefineButtons()
: input_(Input::get()) {
clearButtons();
auto gamepads = input_->getGamepads();
for (const auto& gamepad : gamepads) {
controller_names_.emplace_back(Input::getControllerName(gamepad));
}
// Crear la ventana de mensaje
WindowMessage::Config config(param.service_menu.window_message);
auto text_renderer = Resource::get()->getText("04b_25_flat");
window_message_ = std::make_unique<WindowMessage>(
text_renderer,
Lang::getText("[DEFINE_BUTTONS] TITLE"),
config);
window_message_->setPosition(param.game.game_area.center_x, param.game.game_area.center_y, WindowMessage::PositionMode::CENTERED);
}
void DefineButtons::render() {
if (enabled_ && window_message_) {
window_message_->render();
}
}
void DefineButtons::update(float delta_time) {
if (!enabled_) {
return;
}
// Actualizar la ventana siempre
if (window_message_) {
window_message_->update(delta_time);
}
// Manejar la secuencia de cierre si ya terminamos
if (finished_ && message_shown_) {
message_timer_ += delta_time;
// Después del delay, iniciar animación de cierre (solo una vez)
if (message_timer_ >= MESSAGE_DISPLAY_DURATION_S && !closing_) {
if (window_message_) {
window_message_->hide(); // Iniciar animación de cierre
}
closing_ = true;
}
}
}
void DefineButtons::handleEvents(const SDL_Event& event) {
if (enabled_) {
switch (event.type) {
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
doControllerButtonDown(event.gbutton);
break;
case SDL_EVENT_GAMEPAD_BUTTON_UP:
checkEnd();
break;
case SDL_EVENT_GAMEPAD_AXIS_MOTION:
doControllerAxisMotion(event.gaxis);
break;
default:
break;
}
}
}
auto DefineButtons::enable(Options::Gamepad* options_gamepad) -> bool {
if (options_gamepad != nullptr) {
options_gamepad_ = options_gamepad;
enabled_ = true;
finished_ = false;
index_button_ = 0;
message_shown_ = false;
closing_ = false;
l2_was_pressed_ = false;
r2_was_pressed_ = false;
clearButtons();
updateWindowMessage();
if (window_message_) {
window_message_->autoSize();
window_message_->show();
}
return true;
}
return false;
}
void DefineButtons::disable() {
enabled_ = false;
finished_ = false;
message_shown_ = false;
closing_ = false;
l2_was_pressed_ = false;
r2_was_pressed_ = false;
if (window_message_) {
window_message_->hide();
}
}
void DefineButtons::doControllerButtonDown(const SDL_GamepadButtonEvent& event) {
auto gamepad = input_->getGamepad(event.which);
if (!gamepad || gamepad != options_gamepad_->instance) {
return;
}
const auto BUTTON = static_cast<SDL_GamepadButton>(event.button);
if (checkButtonNotInUse(BUTTON)) {
buttons_.at(index_button_).button = static_cast<int>(BUTTON);
incIndexButton();
updateWindowMessage();
}
}
void DefineButtons::doControllerAxisMotion(const SDL_GamepadAxisEvent& event) {
auto gamepad = input_->getGamepad(event.which);
if (!gamepad || gamepad != options_gamepad_->instance) {
return;
}
// Solo manejamos L2 y R2 como botones con lógica de transición
if (event.axis == SDL_GAMEPAD_AXIS_LEFT_TRIGGER) {
bool l2_is_pressed_now = event.value > 16384;
// Solo actuar en la transición de no presionado a presionado
if (l2_is_pressed_now && !l2_was_pressed_) {
const auto TRIGGER_BUTTON = Input::TRIGGER_L2_AS_BUTTON;
if (checkTriggerNotInUse(TRIGGER_BUTTON)) {
buttons_.at(index_button_).button = TRIGGER_BUTTON;
incIndexButton();
updateWindowMessage();
}
}
// Detectar liberación del trigger para llamar checkEnd()
if (!l2_is_pressed_now && l2_was_pressed_) {
checkEnd();
}
l2_was_pressed_ = l2_is_pressed_now;
} else if (event.axis == SDL_GAMEPAD_AXIS_RIGHT_TRIGGER) {
bool r2_is_pressed_now = event.value > 16384;
// Solo actuar en la transición de no presionado a presionado
if (r2_is_pressed_now && !r2_was_pressed_) {
const auto TRIGGER_BUTTON = Input::TRIGGER_R2_AS_BUTTON;
if (checkTriggerNotInUse(TRIGGER_BUTTON)) {
buttons_.at(index_button_).button = TRIGGER_BUTTON;
incIndexButton();
updateWindowMessage();
}
}
// Detectar liberación del trigger para llamar checkEnd()
if (!r2_is_pressed_now && r2_was_pressed_) {
checkEnd();
}
r2_was_pressed_ = r2_is_pressed_now;
}
}
void DefineButtons::bindButtons(Options::Gamepad* options_gamepad) {
for (const auto& button : buttons_) {
Input::bindGameControllerButton(options_gamepad->instance, button.action, static_cast<SDL_GamepadButton>(button.button));
}
Input::bindGameControllerButton(options_gamepad->instance, Input::Action::SM_SELECT, Input::Action::FIRE_LEFT);
Input::bindGameControllerButton(options_gamepad->instance, Input::Action::SM_BACK, Input::Action::FIRE_CENTER);
}
void DefineButtons::incIndexButton() {
if (index_button_ < buttons_.size() - 1) {
++index_button_;
} else {
finished_ = true;
}
}
auto DefineButtons::checkButtonNotInUse(SDL_GamepadButton button) -> bool {
return std::ranges::all_of(buttons_, [button](const auto& b) {
return b.button != button;
});
}
auto DefineButtons::checkTriggerNotInUse(int trigger_button) -> bool {
return std::ranges::all_of(buttons_, [trigger_button](const auto& b) {
return b.button != trigger_button;
});
}
void DefineButtons::clearButtons() {
buttons_.clear();
buttons_.emplace_back(Lang::getText("[DEFINE_BUTTONS] FIRE_LEFT"), Input::Action::FIRE_LEFT, static_cast<int>(SDL_GAMEPAD_BUTTON_INVALID));
buttons_.emplace_back(Lang::getText("[DEFINE_BUTTONS] FIRE_UP"), Input::Action::FIRE_CENTER, static_cast<int>(SDL_GAMEPAD_BUTTON_INVALID));
buttons_.emplace_back(Lang::getText("[DEFINE_BUTTONS] FIRE_RIGHT"), Input::Action::FIRE_RIGHT, static_cast<int>(SDL_GAMEPAD_BUTTON_INVALID));
buttons_.emplace_back(Lang::getText("[DEFINE_BUTTONS] START"), Input::Action::START, static_cast<int>(SDL_GAMEPAD_BUTTON_INVALID));
buttons_.emplace_back(Lang::getText("[DEFINE_BUTTONS] SERVICE_MENU"), Input::Action::SERVICE, static_cast<int>(SDL_GAMEPAD_BUTTON_INVALID));
}
void DefineButtons::checkEnd() {
if (finished_ && !message_shown_) {
bindButtons(options_gamepad_);
input_->saveGamepadConfigFromGamepad(options_gamepad_->instance);
input_->resetInputStates();
// Mostrar mensaje de finalización
if (window_message_) {
window_message_->clearTexts();
window_message_->addText(Lang::getText("[DEFINE_BUTTONS] CONFIGURATION_COMPLETE"));
}
// Solo marcar que ya mostramos el mensaje
message_shown_ = true;
message_timer_ = 0.0f;
}
}
auto DefineButtons::isReadyToClose() const -> bool {
// Solo está listo para cerrar si:
// 1. Terminó
// 2. Ya mostró el mensaje
// 3. Está cerrando
// 4. La ventana ya no está visible (animación terminada)
return finished_ && message_shown_ && closing_ &&
(!window_message_ || !window_message_->isVisible());
}
void DefineButtons::updateWindowMessage() {
if (!window_message_ || (options_gamepad_ == nullptr)) {
return;
}
// Configurar título
std::string title = Lang::getText("[DEFINE_BUTTONS] CONFIGURING") + ": " + options_gamepad_->name;
window_message_->setTitle(title);
// Limpiar textos anteriores
window_message_->clearTexts();
if (index_button_ < buttons_.size()) {
// Instrucción actual
std::string instruction = Lang::getText("[DEFINE_BUTTONS] PRESS_BUTTON_FOR") + ":";
window_message_->addText(instruction);
window_message_->addText(buttons_.at(index_button_).label);
}
}