Varios arreglos a tot lo de definir botons dins del service menu i lo de triar mando

This commit is contained in:
2025-08-07 21:11:05 +02:00
parent d8a37c555f
commit fdfc976749
11 changed files with 223 additions and 233 deletions

View File

@@ -6,7 +6,7 @@
"[DEFINE_BUTTONS] TITLE": "define buttons title",
"[DEFINE_BUTTONS] FIRE_LEFT": "Disparar cap a l'esquerra",
"[DEFINE_BUTTONS] FIRE_UP": "Disparar cap amunt amunt amunt amunt",
"[DEFINE_BUTTONS] FIRE_UP": "Disparar cap amunt",
"[DEFINE_BUTTONS] FIRE_RIGHT": "Disparar cap a la dreta",
"[DEFINE_BUTTONS] START": "Start",
"[DEFINE_BUTTONS] SERVICE_MENU": "Menu de servei",

View File

@@ -24,7 +24,7 @@ DefineButtons::DefineButtons()
// Crear la ventana de mensaje
WindowMessage::Config config;
config.bg_color = Color{20, 30, 50, 224}; // Fondo azul oscuro semi-transparente
config.bg_color = Color{20, 30, 50, 240}; // Fondo azul oscuro semi-transparente
config.border_color = Color{100, 150, 200, 255}; // Borde azul claro
config.title_color = Color{100, 150, 200, 255}; // Titulo azul claro
config.text_color = Color{220, 220, 220, 255}; // Texto gris claro
@@ -157,8 +157,8 @@ void DefineButtons::checkEnd() {
if (window_message_) {
window_message_->clearTexts();
window_message_->addText(Lang::getText("[DEFINE_BUTTONS] CONFIGURATION_COMPLETE"));
window_message_->autoSize();
window_message_->centerOnScreen();
//window_message_->autoSize();
//window_message_->centerOnScreen();
}
// Se deshabilitará desde el ServiceMenu después de un breve delay
@@ -178,26 +178,9 @@ void DefineButtons::updateWindowMessage() {
window_message_->clearTexts();
if (index_button_ < buttons_.size()) {
// Mostrar progreso
/*std::string progress = "(" + std::to_string(index_button_ + 1) + "/" +
std::to_string(buttons_.size()) + ")";
window_message_->addText(progress);
window_message_->addText("");*/
// 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);
// Botones ya configurados
/*if (index_button_ > 0) {
window_message_->addText("");
window_message_->addText(Lang::getText("[DEFINE_BUTTONS] CONFIGURED") + ":");
for (size_t i = 0; i < index_button_; ++i) {
std::string configured = "✓ " + buttons_[i].label;
window_message_->addText(configured);
}
}*/
}
}

View File

@@ -30,16 +30,7 @@ void check(const SDL_Event &event) {
break;
}
if (ServiceMenu::get()->isEnabled()) {
ServiceMenu::get()->handleEvent(event); // Método que vamos a crear
// Si DefineButtons está activo, no procesar más eventos
if (ServiceMenu::get()->isDefiningButtons()) {
return;
}
}
ServiceMenu::get()->handleEvent(event);
Mouse::handleEvent(event);
static auto *input_ = Input::get();

View File

@@ -183,95 +183,6 @@ auto checkServiceButton() -> bool {
return false;
}
// Comprueba las entradas del menú de servicio
auto checkServiceInputs() -> bool {
if (!ServiceMenu::get()->isEnabled()) {
return false;
}
// Teclado
{
// Arriba
if (Input::get()->checkAction(Input::Action::UP, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
ServiceMenu::get()->setSelectorUp();
return true;
}
// Abajo
if (Input::get()->checkAction(Input::Action::DOWN, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
ServiceMenu::get()->setSelectorDown();
return true;
}
// Derecha
if (Input::get()->checkAction(Input::Action::RIGHT, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
ServiceMenu::get()->adjustOption(true);
return true;
}
// Izquierda
if (Input::get()->checkAction(Input::Action::LEFT, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
ServiceMenu::get()->adjustOption(false);
return true;
}
// Aceptar
if (Input::get()->checkAction(Input::Action::SM_SELECT, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
ServiceMenu::get()->selectOption();
return true;
}
// Atras
if (Input::get()->checkAction(Input::Action::SM_BACK, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
ServiceMenu::get()->moveBack();
return true;
}
}
// Mandos
{
auto gamepads = Input::get()->getGamepads();
for (auto gamepad : gamepads) {
// Arriba
if (Input::get()->checkAction(Input::Action::UP, Input::DO_NOT_ALLOW_REPEAT, Input::DO_NOT_CHECK_KEYBOARD, gamepad)) {
ServiceMenu::get()->setSelectorUp();
return true;
}
// Abajo
if (Input::get()->checkAction(Input::Action::DOWN, Input::DO_NOT_ALLOW_REPEAT, Input::DO_NOT_CHECK_KEYBOARD, gamepad)) {
ServiceMenu::get()->setSelectorDown();
return true;
}
// Derecha
if (Input::get()->checkAction(Input::Action::RIGHT, Input::DO_NOT_ALLOW_REPEAT, Input::DO_NOT_CHECK_KEYBOARD, gamepad)) {
ServiceMenu::get()->adjustOption(true);
return true;
}
// Izquierda
if (Input::get()->checkAction(Input::Action::LEFT, Input::DO_NOT_ALLOW_REPEAT, Input::DO_NOT_CHECK_KEYBOARD, gamepad)) {
ServiceMenu::get()->adjustOption(false);
return true;
}
// Aceptar
if (Input::get()->checkAction(Input::Action::SM_SELECT, Input::DO_NOT_ALLOW_REPEAT, Input::DO_NOT_CHECK_KEYBOARD, gamepad)) {
ServiceMenu::get()->selectOption();
return true;
}
// Atras
if (Input::get()->checkAction(Input::Action::SM_BACK, Input::DO_NOT_ALLOW_REPEAT, Input::DO_NOT_CHECK_KEYBOARD, gamepad)) {
ServiceMenu::get()->moveBack();
return true;
}
}
}
return false;
}
// Comprueba las entradas fuera del menú de servicio
auto checkInputs() -> bool {
// Teclado
@@ -370,7 +281,7 @@ auto check() -> bool {
if (checkServiceButton()) {
return true;
}
if (checkServiceInputs()) {
if (ServiceMenu::get()->checkInput()) {
return true;
}
if (checkInputs()) {

View File

@@ -296,22 +296,28 @@ auto getGamepadInfo(Player::Id player_id) -> std::string {
// Asigna los mandos físicos basándose en la configuración actual.
void GamepadManager::assignAndLinkGamepads() {
// 1. Obtenemos todos los mandos físicos que están conectados ahora mismo.
// 1. Obtenemos los mandos físicos conectados.
auto physical_gamepads = Input::get()->getGamepads();
// 2. Reiniciamos las asignaciones actuales y guardamos los datos deseados de la config.
// 2. Reiniciamos las asignaciones actuales.
std::array<std::string, MAX_PLAYERS> desired_paths;
for (size_t i = 0; i < MAX_PLAYERS; ++i) {
desired_paths[i] = gamepads_[i].path;
gamepads_[i].instance = nullptr;
}
// 3. Vector para rastrear los mandos físicos que ya hemos asignado.
// 3. Vector para rastrear los mandos ya asignados.
std::vector<std::shared_ptr<Input::Gamepad>> assigned_instances;
// --- Ejecutamos las pasadas de asignación ---
// --- Ejecutamos las pasadas de asignación y limpieza ---
// Pasada 1: Intenta asignar por la ruta guardada.
assignGamepadsByPath(desired_paths, physical_gamepads, assigned_instances);
// Pasada 2: Asigna los mandos restantes a los jugadores libres.
assignRemainingGamepads(physical_gamepads, assigned_instances);
// Pasada 3: Limpia los datos de los slots que se quedaron sin mando.
clearUnassignedGamepadSlots();
}
// --- PRIMERA PASADA: Intenta asignar mandos basándose en la ruta guardada ---
@@ -328,7 +334,12 @@ void GamepadManager::assignGamepadsByPath(
// Buscamos un mando físico que coincida con la ruta y no esté ya asignado.
for (const auto& physical_gamepad : physical_gamepads) {
if (physical_gamepad->path == desired_path && !isGamepadAssigned(physical_gamepad, assigned_instances)) {
// Asignamos y actualizamos TODOS los datos.
gamepads_[i].instance = physical_gamepad;
gamepads_[i].name = physical_gamepad->name; // <--- LA LÍNEA QUE FALTABA
// No es necesario actualizar la path aquí porque ya coincide.
assigned_instances.push_back(physical_gamepad);
break; // Mando encontrado para este jugador, pasamos al siguiente.
}
@@ -361,6 +372,21 @@ void GamepadManager::assignRemainingGamepads(
}
}
// --- TERCERA PASADA: Limpia la información "fantasma" de los slots no asignados ---
void GamepadManager::clearUnassignedGamepadSlots() {
// Recorremos los slots de jugador una última vez.
for (auto& gamepad_config : gamepads_) {
// Si un slot no tiene una instancia física enlazada (instance == nullptr),
// significa que no hay un mando para él.
if (gamepad_config.instance == nullptr) {
// Limpiamos sus datos de configuración para no mostrar información
// de un mando que ya no está conectado.
gamepad_config.name = "";
gamepad_config.path = "";
}
}
}
// Función auxiliar para comprobar si un mando físico ya está en la lista de asignados.
// Devuelve 'true' si ya ha sido asignado, 'false' en caso contrario.
auto GamepadManager::isGamepadAssigned(

View File

@@ -238,6 +238,7 @@ class GamepadManager {
void assignRemainingGamepads(
const std::vector<std::shared_ptr<Input::Gamepad>>& physical_gamepads,
std::vector<std::shared_ptr<Input::Gamepad>>& assigned_instances);
void clearUnassignedGamepadSlots();
[[nodiscard]] static auto isGamepadAssigned(
const std::shared_ptr<Input::Gamepad>& physical_gamepad,
const std::vector<std::shared_ptr<Input::Gamepad>>& assigned_instances) -> bool;

View File

@@ -59,11 +59,6 @@ void MenuRenderer::render(const ServiceMenu *menu_state) {
std::string truncated_value = getTruncatedValue(option_pairs.at(i).second, available_width);
// Si la opción tiene Behavior::BOTH, añadir indicador visual
if (i < display_options.size() && display_options[i]->getBehavior() == MenuOption::Behavior::BOTH) {
truncated_value = "-" + truncated_value + "-";
}
element_text_->writeColored(rect_.x + ServiceMenu::OPTIONS_HORIZONTAL_PADDING, y, option_pairs.at(i).first, current_color, -2);
const int X = rect_.x + rect_.w - ServiceMenu::OPTIONS_HORIZONTAL_PADDING - element_text_->length(truncated_value, -2);
element_text_->writeColored(X, y, truncated_value, current_color, -2);
@@ -72,11 +67,6 @@ void MenuRenderer::render(const ServiceMenu *menu_state) {
const int available_width = rect_.w - (ServiceMenu::OPTIONS_HORIZONTAL_PADDING * 2);
std::string truncated_caption = getTruncatedValue(option_pairs.at(i).first, available_width);
// Si la opción tiene Behavior::BOTH, añadir indicador visual
if (i < display_options.size() && display_options[i]->getBehavior() == MenuOption::Behavior::BOTH) {
truncated_caption = "-" + truncated_caption + "-";
}
element_text_->writeDX(Text::CENTER | Text::COLOR, rect_.x + rect_.w / 2, y, truncated_caption, -2, current_color);
}
y += options_height_ + options_padding_;

View File

@@ -3,6 +3,7 @@
#include <algorithm> // Para max
#include "audio.h" // Para Audio
#include "define_buttons.h" // Para DefineButtons
#include "difficulty.h" // Para getCodeFromName, getNameFromCode
#include "input.h" // Para Input
#include "lang.h" // Para getText, getCodeFromName, getNameFromCode
@@ -16,7 +17,6 @@
#include "section.hpp" // Para Name, name, Options, options
#include "ui/ui_message.h" // Para UIMessage
#include "utils.h" // Para Zone
#include "define_buttons.h" // Para DefineButtons
// Singleton
ServiceMenu *ServiceMenu::instance = nullptr;
@@ -39,6 +39,10 @@ ServiceMenu::ServiceMenu()
}
void ServiceMenu::toggle() {
if (define_buttons_ && define_buttons_->isEnabled()) {
return; // No se puede mostrar u ocultar el menu de servicio si se estan definiendo los botones
}
playBackSound();
enabled_ = !enabled_;
if (!enabled_) {
@@ -85,9 +89,9 @@ void ServiceMenu::update() {
// Si DefineButtons ha terminado, deshabilitarlo
if (define_buttons_->isEnabled() && define_buttons_->isFinished()) {
// Pequeño delay antes de cerrar
static int finish_delay = 0;
static size_t finish_delay = 0;
finish_delay++;
if (finish_delay > 60) { // 1 segundo a 60 FPS
if (finish_delay > DEFINE_BUTTONS_FINISH_DELAY_FRAMES) {
define_buttons_->disable();
finish_delay = 0;
}
@@ -283,7 +287,7 @@ void ServiceMenu::initializeOptions() {
},
[this]() {
// Acción: configurar botones del mando del jugador 1
auto* gamepad = &Options::gamepad_manager.getGamepad(Player::Id::PLAYER1);
auto *gamepad = &Options::gamepad_manager.getGamepad(Player::Id::PLAYER1);
if (gamepad && gamepad->instance) {
define_buttons_->enable(gamepad);
}
@@ -301,7 +305,7 @@ void ServiceMenu::initializeOptions() {
},
[this]() {
// Acción: configurar botones del mando del jugador 2
auto* gamepad = &Options::gamepad_manager.getGamepad(Player::Id::PLAYER2);
auto *gamepad = &Options::gamepad_manager.getGamepad(Player::Id::PLAYER2);
if (gamepad && gamepad->instance) {
define_buttons_->enable(gamepad);
}
@@ -552,64 +556,93 @@ void ServiceMenu::handleEvent(const SDL_Event &event) {
define_buttons_->checkEvents(event);
return; // No procesar otros eventos mientras DefineButtons está activo
}
// Procesar eventos normales del ServiceMenu
/*switch (event.type) {
case SDL_EVENT_KEY_DOWN:
switch (event.key.key) {
case SDLK_ESCAPE:
case SDLK_BACKSPACE:
moveBack();
break;
case SDLK_RETURN:
case SDLK_KP_ENTER:
selectOption();
break;
case SDLK_UP:
setSelectorUp();
break;
case SDLK_DOWN:
setSelectorDown();
break;
case SDLK_LEFT:
adjustOption(false);
break;
case SDLK_RIGHT:
adjustOption(true);
break;
default:
break;
}
break;
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
switch (event.gbutton.button) {
case SDL_GAMEPAD_BUTTON_SOUTH:
case SDL_GAMEPAD_BUTTON_BACK:
moveBack();
break;
case SDL_GAMEPAD_BUTTON_EAST:
case SDL_GAMEPAD_BUTTON_START:
selectOption();
break;
case SDL_GAMEPAD_BUTTON_DPAD_UP:
setSelectorUp();
break;
case SDL_GAMEPAD_BUTTON_DPAD_DOWN:
setSelectorDown();
break;
case SDL_GAMEPAD_BUTTON_DPAD_LEFT:
adjustOption(false);
break;
case SDL_GAMEPAD_BUTTON_DPAD_RIGHT:
adjustOption(true);
break;
default:
break;
}
break;
default:
break;
}*/
}
bool ServiceMenu::checkInput() {
if (!enabled_) {
return false;
}
if (define_buttons_ && define_buttons_->isEnabled()) {
return true;
}
static auto input = Input::get();
// --- Teclado ---
// Arriba
if (input->checkAction(Input::Action::UP, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
setSelectorUp();
return true;
}
// Abajo
if (input->checkAction(Input::Action::DOWN, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
setSelectorDown();
return true;
}
// Derecha
if (input->checkAction(Input::Action::RIGHT, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
adjustOption(true);
return true;
}
// Izquierda
if (input->checkAction(Input::Action::LEFT, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
adjustOption(false);
return true;
}
// Aceptar
if (input->checkAction(Input::Action::SM_SELECT, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
selectOption();
return true;
}
// Atras
if (input->checkAction(Input::Action::SM_BACK, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
moveBack();
return true;
}
// --- Mandos ---
auto gamepads = input->getGamepads();
for (auto gamepad : gamepads) {
// Arriba
if (input->checkAction(Input::Action::UP, Input::DO_NOT_ALLOW_REPEAT, Input::DO_NOT_CHECK_KEYBOARD, gamepad)) {
setSelectorUp();
return true;
}
// Abajo
if (input->checkAction(Input::Action::DOWN, Input::DO_NOT_ALLOW_REPEAT, Input::DO_NOT_CHECK_KEYBOARD, gamepad)) {
setSelectorDown();
return true;
}
// Derecha
if (input->checkAction(Input::Action::RIGHT, Input::DO_NOT_ALLOW_REPEAT, Input::DO_NOT_CHECK_KEYBOARD, gamepad)) {
adjustOption(true);
return true;
}
// Izquierda
if (input->checkAction(Input::Action::LEFT, Input::DO_NOT_ALLOW_REPEAT, Input::DO_NOT_CHECK_KEYBOARD, gamepad)) {
adjustOption(false);
return true;
}
// Aceptar
if (input->checkAction(Input::Action::SM_SELECT, Input::DO_NOT_ALLOW_REPEAT, Input::DO_NOT_CHECK_KEYBOARD, gamepad)) {
selectOption();
return true;
}
// Atras
if (input->checkAction(Input::Action::SM_BACK, Input::DO_NOT_ALLOW_REPEAT, Input::DO_NOT_CHECK_KEYBOARD, gamepad)) {
moveBack();
return true;
}
}
return false;
}

View File

@@ -34,6 +34,7 @@ class ServiceMenu {
static constexpr size_t MIN_WIDTH = 240;
static constexpr size_t MIN_GAP_OPTION_VALUE = 30;
static constexpr size_t SETTINGS_GROUP_SIZE = 6;
static constexpr size_t DEFINE_BUTTONS_FINISH_DELAY_FRAMES = 60 * 3; // 3 segundo a 60 FPS
// --- Métodos de singleton ---
static void init();
@@ -56,8 +57,9 @@ class ServiceMenu {
void moveBack();
void checkEvents(const SDL_Event &event); // Nuevo método para eventos
// NUEVO: Método para manejar eventos (llamado desde GlobalEvents)
// --- Método para manejar eventos (llamado desde GlobalEvents) ---
void handleEvent(const SDL_Event &event);
bool checkInput();
// NUEVO: Getter para saber si DefineButtons está activo
[[nodiscard]] auto isDefiningButtons() const -> bool {

View File

@@ -36,17 +36,20 @@ void WindowMessage::render() {
SDL_RenderRect(renderer, &rect_);
float current_y = rect_.y + config_.padding;
float available_width = getAvailableTextWidth();
// Dibujar título si existe
if (!title_.empty()) {
std::string visible_title = getTruncatedText(title_, available_width);
text_renderer_->writeStyle(
rect_.x + rect_.w / 2.0f,
current_y,
title_,
visible_title,
title_style_);
current_y += text_renderer_->getCharacterSize() + config_.title_separator_spacing;
// Línea separadora debajo del título
// Línea separadora debajo del título (solo si hay título visible)
if (!visible_title.empty()) {
SDL_SetRenderDrawColor(renderer, config_.border_color.r, config_.border_color.g,
config_.border_color.b, config_.border_color.a);
SDL_RenderLine(renderer,
@@ -55,14 +58,18 @@ void WindowMessage::render() {
rect_.x + rect_.w - config_.padding,
current_y - config_.title_separator_spacing / 2.0f);
}
}
// Dibujar textos
for (const auto& text : texts_) {
std::string visible_text = getTruncatedText(text, available_width);
if (!visible_text.empty()) {
text_renderer_->writeStyle(
rect_.x + rect_.w / 2.0f,
current_y,
text,
visible_text,
text_style_);
}
current_y += text_renderer_->getCharacterSize() + config_.line_spacing;
}
}
@@ -285,3 +292,45 @@ auto WindowMessage::easeOut(float t) const -> float {
// Función de suavizado ease-out cuadrática
return 1.0f - (1.0f - t) * (1.0f - t);
}
auto WindowMessage::getAvailableTextWidth() const -> float {
// Ancho disponible = ancho total - padding en ambos lados
return rect_.w - (config_.padding * 2.0f);
}
auto WindowMessage::getTruncatedText(const std::string& text, float available_width) const -> std::string {
if (text.empty()) {
return text;
}
// Si el texto completo cabe, devolverlo tal como está
int text_width = text_renderer_->length(text, -2);
if (text_width <= available_width) {
return text;
}
// Si no hay espacio suficiente, devolver string vacío
if (available_width < 10.0f) { // Mínimo espacio para al menos un carácter
return "";
}
// Buscar cuántos caracteres caben usando búsqueda binaria
int left = 0;
int right = text.length();
int best_length = 0;
while (left <= right) {
int mid = (left + right) / 2;
std::string partial = text.substr(0, mid);
int partial_width = text_renderer_->length(partial, -2);
if (partial_width <= available_width) {
best_length = mid;
left = mid + 1;
} else {
right = mid - 1;
}
}
return text.substr(0, best_length);
}

View File

@@ -169,6 +169,10 @@ class WindowMessage {
// Función de suavizado (ease-out)
[[nodiscard]] auto easeOut(float t) const -> float;
// Métodos para manejo de texto durante animación
[[nodiscard]] auto getTruncatedText(const std::string& text, float available_width) const -> std::string;
[[nodiscard]] auto getAvailableTextWidth() const -> float;
[[nodiscard]] auto calculateContentHeight() const -> float;
[[nodiscard]] auto calculateContentWidth() const -> float;
[[nodiscard]] auto getScreenWidth() const -> float;