#include "define_buttons.h" #include // Para __all_of_fn, all_of #include // Para identity #include // Para allocator, unique_ptr, shared_ptr, make_unique, operator== #include "color.h" // Para Color #include "input.h" // Para Input #include "input_types.h" // Para InputAction #include "lang.h" // Para getText #include "options.h" // Para Gamepad #include "param.h" // Para Param, ParamGame, param #include "resource.h" // Para Resource #include "ui/window_message.h" // Para WindowMessage #include "utils.h" // 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( 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() { if (!enabled_) { return; } // Actualizar la ventana siempre if (window_message_) { window_message_->update(); } // Manejar la secuencia de cierre si ya terminamos if (finished_ && message_shown_) { message_timer_++; // Después del delay, iniciar animación de cierre (solo una vez) if (message_timer_ > MESSAGE_DISPLAY_FRAMES && !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(event.button); if (checkButtonNotInUse(BUTTON)) { buttons_.at(index_button_).button = static_cast(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(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(SDL_GAMEPAD_BUTTON_INVALID)); buttons_.emplace_back(Lang::getText("[DEFINE_BUTTONS] FIRE_UP"), Input::Action::FIRE_CENTER, static_cast(SDL_GAMEPAD_BUTTON_INVALID)); buttons_.emplace_back(Lang::getText("[DEFINE_BUTTONS] FIRE_RIGHT"), Input::Action::FIRE_RIGHT, static_cast(SDL_GAMEPAD_BUTTON_INVALID)); buttons_.emplace_back(Lang::getText("[DEFINE_BUTTONS] START"), Input::Action::START, static_cast(SDL_GAMEPAD_BUTTON_INVALID)); buttons_.emplace_back(Lang::getText("[DEFINE_BUTTONS] SERVICE_MENU"), Input::Action::SERVICE, static_cast(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; } } 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); } }