From 2d9a6e744e69bce591b2b7887f1d6cddec4f729f Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Thu, 31 Jul 2025 07:17:14 +0200 Subject: [PATCH] =?UTF-8?q?commit=20ma=C3=B1anero=20de=20canvi=20de=20pc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/director.cpp | 18 +-- source/gamepad_config_manager.h | 191 ++++++++++++++++++++++++++++++++ source/input.cpp | 18 +-- source/input.h | 12 +- source/options.cpp | 18 +-- source/options.h | 89 +++++---------- 6 files changed, 257 insertions(+), 89 deletions(-) create mode 100644 source/gamepad_config_manager.h diff --git a/source/director.cpp b/source/director.cpp index 41eed0e..53f0b14 100644 --- a/source/director.cpp +++ b/source/director.cpp @@ -81,13 +81,14 @@ Director::~Director() { // Inicializa todo void Director::init() { - // Configuración inicial de recursos - Asset::init(executable_path_); // Inicializa el sistema de gestión de archivos - setFileList(); // Crea el índice de archivos - Options::setFile(Asset::get()->get("config.txt")); // Establece el fichero de configuración - Options::loadFromFile(); // Carga el archivo de configuración - loadParams(); // Carga los parámetros del programa - loadScoreFile(); // Carga el archivo de puntuaciones + // Configuración inicial de parametros + Asset::init(executable_path_); // Inicializa el sistema de gestión de archivos + setFileList(); // Crea el índice de archivos + Options::setConfigFile(Asset::get()->get("config.txt")); // Establece el fichero de configuración + Options::setControllersFile(Asset::get()->get("controllers.json")); // Establece el fichero de configuración de mandos + Options::loadFromFile(); // Carga el archivo de configuración + loadParams(); // Carga los parámetros del programa + loadScoreFile(); // Carga el archivo de puntuaciones // Inicialización de subsistemas principales Lang::setLanguage(Options::settings.language); // Carga el archivo de idioma @@ -255,6 +256,7 @@ void Director::setFileList() { // Ficheros de configuración Asset::get()->add(system_folder_ + "/config.txt", AssetType::DATA, false, true); + Asset::get()->add(system_folder_ + "/controllers.json", AssetType::DATA, false, true); Asset::get()->add(system_folder_ + "/score.bin", AssetType::DATA, false, true); Asset::get()->add(PREFIX + "/data/config/param_320x240.txt", AssetType::DATA); Asset::get()->add(PREFIX + "/data/config/param_320x256.txt", AssetType::DATA); @@ -599,7 +601,7 @@ void Director::reset() { { Resource::get()->reload(); } - //Input::get()->discoverGameControllers(); + // Input::get()->discoverGameControllers(); bindInputs(); ServiceMenu::get()->reset(); Section::name = Section::Name::LOGO; diff --git a/source/gamepad_config_manager.h b/source/gamepad_config_manager.h new file mode 100644 index 0000000..d5b3159 --- /dev/null +++ b/source/gamepad_config_manager.h @@ -0,0 +1,191 @@ +#include +#pragma once + +#include +#include +#include +#include + +#include "input.h" // Para Input + +// Mapas para convertir entre enums y strings +std::unordered_map actionToString = { + {Input::Action::FIRE_LEFT, "FIRE_LEFT"}, + {Input::Action::FIRE_CENTER, "FIRE_CENTER"}, + {Input::Action::FIRE_RIGHT, "FIRE_RIGHT"}, + {Input::Action::START, "START"}, + {Input::Action::SERVICE, "SERVICE"}}; + +std::unordered_map stringToAction = { + {"FIRE_LEFT", Input::Action::FIRE_LEFT}, + {"FIRE_CENTER", Input::Action::FIRE_CENTER}, + {"FIRE_RIGHT", Input::Action::FIRE_RIGHT}, + {"START", Input::Action::START}, + {"SERVICE", Input::Action::SERVICE}}; + +std::unordered_map buttonToString = { + {SDL_GAMEPAD_BUTTON_WEST, "WEST"}, + {SDL_GAMEPAD_BUTTON_NORTH, "NORTH"}, + {SDL_GAMEPAD_BUTTON_EAST, "EAST"}, + {SDL_GAMEPAD_BUTTON_SOUTH, "SOUTH"}, + {SDL_GAMEPAD_BUTTON_START, "START"}, + {SDL_GAMEPAD_BUTTON_BACK, "BACK"}, + {SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, "LEFT_SHOULDER"}, + {SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, "RIGHT_SHOULDER"} + // Añade todos los botones que necesites +}; + +std::unordered_map stringToButton = { + {"WEST", SDL_GAMEPAD_BUTTON_WEST}, + {"NORTH", SDL_GAMEPAD_BUTTON_NORTH}, + {"EAST", SDL_GAMEPAD_BUTTON_EAST}, + {"SOUTH", SDL_GAMEPAD_BUTTON_SOUTH}, + {"START", SDL_GAMEPAD_BUTTON_START}, + {"BACK", SDL_GAMEPAD_BUTTON_BACK}, + {"LEFT_SHOULDER", SDL_GAMEPAD_BUTTON_LEFT_SHOULDER}, + {"RIGHT_SHOULDER", SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER} + // Añade todos los botones que necesites +}; + +struct GamepadConfig { + std::string name; // Nombre del dispositivo + std::unordered_map bindings; // Asociación acción-botón + + GamepadConfig(std::string name = "") + : name(name), + bindings{ + {Input::Action::FIRE_LEFT, SDL_GAMEPAD_BUTTON_WEST}, + {Input::Action::FIRE_CENTER, SDL_GAMEPAD_BUTTON_NORTH}, + {Input::Action::FIRE_RIGHT, SDL_GAMEPAD_BUTTON_EAST}, + {Input::Action::START, SDL_GAMEPAD_BUTTON_START}, + {Input::Action::SERVICE, SDL_GAMEPAD_BUTTON_BACK}} {} + + // Reasigna un botón a una acción + void rebindAction(Input::Action action, SDL_GamepadButton new_button) { + bindings[action] = new_button; + } +}; + +class GamepadConfigManager { + public: + // Escribir vector de GamepadConfig a archivo JSON + static bool writeToJson(const std::vector& configs, const std::string& filename) { + try { + nlohmann::json j; + j["gamepads"] = nlohmann::json::array(); + + for (const auto& config : configs) { + nlohmann::json gamepadJson; + gamepadJson["name"] = config.name; + gamepadJson["bindings"] = nlohmann::json::object(); + + // Convertir bindings a JSON + for (const auto& [action, button] : config.bindings) { + std::string actionStr = actionToString[action]; + std::string buttonStr = buttonToString[button]; + gamepadJson["bindings"][actionStr] = buttonStr; + } + + j["gamepads"].push_back(gamepadJson); + } + + // Escribir al archivo + std::ofstream file(filename); + if (!file.is_open()) { + return false; + } + + file << j.dump(4); // Formato con indentación de 4 espacios + file.close(); + return true; + + } catch (const std::exception& e) { + // Log del error si tienes sistema de logging + return false; + } + } + + // Leer vector de GamepadConfig desde archivo JSON + static bool readFromJson(std::vector& configs, const std::string& filename) { + try { + std::ifstream file(filename); + if (!file.is_open()) { + return false; + } + + nlohmann::json j; + file >> j; + file.close(); + + configs.clear(); + + if (!j.contains("gamepads") || !j["gamepads"].is_array()) { + return false; + } + + for (const auto& gamepadJson : j["gamepads"]) { + if (!gamepadJson.contains("name") || !gamepadJson.contains("bindings")) { + continue; // Saltar configuraciones malformadas + } + + GamepadConfig config(gamepadJson["name"]); + + // Limpiar bindings por defecto para cargar los del archivo + config.bindings.clear(); + + // Cargar bindings desde JSON + for (const auto& [actionStr, buttonStr] : gamepadJson["bindings"].items()) { + auto actionIt = stringToAction.find(actionStr); + auto buttonIt = stringToButton.find(buttonStr); + + if (actionIt != stringToAction.end() && buttonIt != stringToButton.end()) { + config.bindings[actionIt->second] = buttonIt->second; + } + } + + configs.push_back(config); + } + + return true; + + } catch (const std::exception& e) { + // Log del error si tienes sistema de logging + return false; + } + } + + // Método auxiliar para verificar si un archivo existe + static bool fileExists(const std::string& filename) { + std::ifstream file(filename); + return file.good(); + } +}; + +/* +// Ejemplo de uso +void ejemploUso() { + std::vector configs; + + // Crear algunas configuraciones de ejemplo + GamepadConfig config1("Xbox Controller"); + config1.rebindAction(Input::Action::FIRE_LEFT, SDL_GAMEPAD_BUTTON_SOUTH); + + GamepadConfig config2("PS4 Controller"); + config2.rebindAction(Input::Action::START, SDL_GAMEPAD_BUTTON_OPTIONS); + + configs.push_back(config1); + configs.push_back(config2); + + // Escribir a archivo + if (GamepadConfigManager::writeToJson(configs, "gamepad_config.json")) { + std::cout << "Configuración guardada exitosamente" << std::endl; + } + + // Leer desde archivo + std::vector loadedConfigs; + if (GamepadConfigManager::readFromJson(loadedConfigs, "gamepad_config.json")) { + std::cout << "Configuración cargada exitosamente" << std::endl; + std::cout << "Número de configuraciones: " << loadedConfigs.size() << std::endl; + } +} +*/ \ No newline at end of file diff --git a/source/input.cpp b/source/input.cpp index 62a0f7a..d92065c 100644 --- a/source/input.cpp +++ b/source/input.cpp @@ -43,14 +43,14 @@ void Input::bindKey(Action input, SDL_Scancode code) { // Asigna inputs a botones del mando void Input::bindGameControllerButton(std::shared_ptr gamepad, Action input, SDL_GamepadButton button) { if (gamepad != nullptr) { - gamepad->bindings.at(static_cast(input)).button = button; + gamepad->button_states.at(static_cast(input)).button = button; } } // Asigna inputs a botones del mando void Input::bindGameControllerButton(std::shared_ptr gamepad, Action input_target, Action input_source) { if (gamepad != nullptr) { - gamepad->bindings.at(static_cast(input_target)).button = gamepad->bindings.at(static_cast(input_source)).button; + gamepad->button_states.at(static_cast(input_target)).button = gamepad->button_states.at(static_cast(input_source)).button; } } @@ -74,9 +74,9 @@ auto Input::checkAction(Action input, bool repeat, Device device, std::shared_pt if (!success_controller) { if (repeat) { // El usuario quiere saber si está pulsada (estado mantenido) - success_controller = gamepad->bindings.at(INPUT_INDEX).is_held; + success_controller = gamepad->button_states.at(INPUT_INDEX).is_held; } else { // El usuario quiere saber si ACABA de ser pulsada (evento de un solo fotograma) - success_controller = gamepad->bindings.at(INPUT_INDEX).just_pressed; + success_controller = gamepad->button_states.at(INPUT_INDEX).just_pressed; } } } @@ -108,7 +108,7 @@ auto Input::checkAnyInput(Device device, std::shared_ptr gamepad) -> bo // Iteramos sobre todas las acciones, no sobre el número de mandos. for (int i = 0; i < NUM_ACTIONS; ++i) { // Leemos el estado pre-calculado para el mando y la acción específicos. - if (gamepad->bindings.at(i).just_pressed) { + if (gamepad->button_states.at(i).just_pressed) { return true; // Se encontró una acción recién pulsada en el mando. } } @@ -160,7 +160,7 @@ auto Input::getJoyIndex(SDL_JoystickID id) const -> int { // Obtiene el SDL_GamepadButton asignado a un input auto Input::getControllerBinding(std::shared_ptr gamepad, Action input) const -> SDL_GamepadButton { - return gamepad->bindings.at(static_cast(input)).button; + return gamepad->button_states.at(static_cast(input)).button; } // Convierte un InputAction a std::string @@ -217,7 +217,7 @@ auto Input::checkAxisInput(Action input, std::shared_ptr gamepad, bool } // Referencia al binding correspondiente - auto &binding = gamepad->bindings.at(static_cast(input)); + auto &binding = gamepad->button_states.at(static_cast(input)); if (repeat) { // Si se permite repetir, simplemente devolvemos el estado actual @@ -258,7 +258,7 @@ void Input::resetInputStates() { } // Resetear todos los ControllerBindings.active a false for (auto &gamepad : gamepads_) { - for (auto &binding : gamepad->bindings) { + for (auto &binding : gamepad->button_states) { binding.is_held = false; binding.just_pressed = false; } @@ -279,7 +279,7 @@ void Input::update() { // --- MANDOS --- for (auto gamepad : gamepads_) { - for (auto &binding : gamepad->bindings) { + for (auto &binding : gamepad->button_states) { bool button_is_down_now = static_cast(SDL_GetGamepadButton(gamepad->pad, binding.button)) != 0; // El estado .is_held del fotograma anterior nos sirve para saber si es un pulso nuevo diff --git a/source/input.h b/source/input.h index feac8cf..120c49a 100644 --- a/source/input.h +++ b/source/input.h @@ -73,22 +73,22 @@ class Input { }; // --- Estructuras --- - struct KeyBindings { + struct KeyState { Uint8 scancode; // Scancode asociado bool is_held; // Está pulsada ahora mismo bool just_pressed; // Se acaba de pulsar en este fotograma - KeyBindings(Uint8 scancode = 0, bool is_held = false, bool just_pressed = false) + KeyState(Uint8 scancode = 0, bool is_held = false, bool just_pressed = false) : scancode(scancode), is_held(is_held), just_pressed(just_pressed) {} }; - struct ControllerBindings { + struct ButtonState { SDL_GamepadButton button; // GameControllerButton asociado bool is_held; // Está pulsada ahora mismo bool just_pressed; // Se acaba de pulsar en este fotograma bool axis_active; // Estado del eje - ControllerBindings(SDL_GamepadButton btn = SDL_GAMEPAD_BUTTON_INVALID, bool is_held = false, bool just_pressed = false, bool axis_act = false) + ButtonState(SDL_GamepadButton btn = SDL_GAMEPAD_BUTTON_INVALID, bool is_held = false, bool just_pressed = false, bool axis_act = false) : button(btn), is_held(is_held), just_pressed(just_pressed), axis_active(axis_act) {} }; @@ -96,7 +96,7 @@ class Input { SDL_Gamepad *pad; SDL_JoystickID instance_id; std::string name; - std::vector bindings; + std::vector button_states; Gamepad(SDL_Gamepad *gamepad) : pad(gamepad), @@ -154,7 +154,7 @@ class Input { // --- Variables internas --- std::vector> gamepads_; // Mandos conectados - std::vector key_bindings_; // Vector con las teclas asociadas a los inputs predefinidos + std::vector key_bindings_; // Vector con las teclas asociadas a los inputs predefinidos std::vector button_inputs_; // Inputs asignados al jugador y a botones, excluyendo direcciones std::string game_controller_db_path_; // Ruta al archivo gamecontrollerdb.txt diff --git a/source/options.cpp b/source/options.cpp index ea1d497..d67bf72 100644 --- a/source/options.cpp +++ b/source/options.cpp @@ -19,18 +19,22 @@ namespace Options { // --- Variables globales --- -WindowOptions window; // Opciones de la ventana -SettingsOptions settings; // Opciones del juego -VideoOptions video; // Opciones de vídeo -AudioOptions audio; // Opciones de audio -std::vector controllers; // Opciones de mando para cada jugador -PendingChanges pending_changes; // Opciones que se aplican al cerrar +WindowOptions window; // Opciones de la ventana +SettingsOptions settings; // Opciones del juego +VideoOptions video; // Opciones de vídeo +AudioOptions audio; // Opciones de audio +std::vector controllers; // Opciones de mando para cada jugador +PendingChanges pending_changes; // Opciones que se aplican al cerrar +std::vector gamepad_configs; // Lista con las configuraciones registradas para cada mando // Declaraciones auto set(const std::string& var, const std::string& value) -> bool; // Establece el fichero de configuración -void setFile(const std::string& file_path) { settings.config_file = file_path; }; +void setConfigFile(const std::string& file_path) { settings.config_file = file_path; }; + +// Establece el fichero de configuración de mandos +void setControllersFile(const std::string& file_path) { settings.controllers_file = file_path; }; // Inicializa las opciones del programa void init() { diff --git a/source/options.h b/source/options.h index 7c3e31c..fe06bff 100644 --- a/source/options.h +++ b/source/options.h @@ -5,9 +5,11 @@ #include // Para copy #include // Para array #include // Para allocator, string -#include // Para vector +#include +#include // Para vector -#include "difficulty.h" // Para Code +#include "difficulty.h" // Para Code +#include "gamepad_config_manager.h" #include "input.h" // Para InputAction, InputDevice #include "lang.h" // Para Code #include "manage_hiscore_table.h" // Para HiScoreEntry @@ -77,6 +79,7 @@ struct SettingsOptions { std::vector hi_score_table; // Tabla de mejores puntuaciones std::vector last_hi_score_entry; // Últimas posiciones de entrada en la tabla std::string config_file; // Ruta al fichero donde guardar la configuración y las opciones del juego + std::string controllers_file; // Ruta al fichero con las configuraciones de los mandos // Constructor por defecto con valores iniciales SettingsOptions() @@ -89,49 +92,14 @@ struct SettingsOptions { } }; -// --- Opciones de mando --- struct GamepadOptions { - Input::Gamepad gamepad; // Mando - int player_id; // Jugador asociado al mando - Input::Device type{Input::Device::CONTROLLER}; // Indica si se usará teclado, mando o ambos - std::string name; // Nombre del dispositivo - bool plugged{false}; // Indica si el mando está conectado - std::vector inputs; // Listado de acciones asignadas - std::vector buttons; // Listado de botones asignados a cada acción + Input::Gamepad gamepad = nullptr; // Mando + int player_id; // Jugador asociado al mando + Input::Device type = Input::Device::CONTROLLER; // Tipo de dispositivo + GamepadConfig config; - // Constructor por defecto - GamepadOptions() - : gamepad(nullptr), - player_id(INVALID_INDEX), - inputs{ - Input::Action::FIRE_LEFT, - Input::Action::FIRE_CENTER, - Input::Action::FIRE_RIGHT, - Input::Action::START, - Input::Action::SERVICE}, - buttons{ - SDL_GAMEPAD_BUTTON_WEST, - SDL_GAMEPAD_BUTTON_NORTH, - SDL_GAMEPAD_BUTTON_EAST, - SDL_GAMEPAD_BUTTON_START, - SDL_GAMEPAD_BUTTON_BACK} {} - - // Constructor con player_id - GamepadOptions(int custom_player_id) - : gamepad(nullptr), - player_id(custom_player_id), - inputs{ - Input::Action::FIRE_LEFT, - Input::Action::FIRE_CENTER, - Input::Action::FIRE_RIGHT, - Input::Action::START, - Input::Action::SERVICE}, - buttons{ - SDL_GAMEPAD_BUTTON_WEST, - SDL_GAMEPAD_BUTTON_NORTH, - SDL_GAMEPAD_BUTTON_EAST, - SDL_GAMEPAD_BUTTON_START, - SDL_GAMEPAD_BUTTON_BACK} {} + GamepadOptions(int custom_player_id = INVALID_INDEX) + : player_id(custom_player_id) {} }; // --- Opciones pendientes de aplicar --- @@ -145,22 +113,25 @@ struct PendingChanges { }; // --- Variables globales --- -extern WindowOptions window; // Opciones de la ventana -extern SettingsOptions settings; // Opciones del juego -extern VideoOptions video; // Opciones de vídeo -extern AudioOptions audio; // Opciones de audio -extern std::vector controllers; // Opciones de mando para cada jugador -extern PendingChanges pending_changes; // Opciones que se aplican al cerrar +extern WindowOptions window; // Opciones de la ventana +extern SettingsOptions settings; // Opciones del juego +extern VideoOptions video; // Opciones de vídeo +extern AudioOptions audio; // Opciones de audio +extern std::vector controllers; // Opciones de mando para cada jugador +extern PendingChanges pending_changes; // Opciones que se aplican al cerrar +extern std::vector gamepad_configs; // Lista con las configuraciones registradas para cada mando // --- Funciones de configuración --- -void init(); // Inicializa las opciones del programa -void setFile(const std::string &file_path); // Establece el fichero de configuración -auto loadFromFile() -> bool; // Carga el fichero de configuración -auto saveToFile() -> bool; // Guarda el fichero de configuración -void setKeyboardToPlayer(int player_id); // Asigna el teclado al jugador -void swapKeyboard(); // Intercambia el teclado de jugador -void swapControllers(); // Intercambia los jugadores asignados a los dos primeros mandos -auto getPlayerWhoUsesKeyboard() -> int; // Averigua quién está usando el teclado -void applyPendingChanges(); // Aplica los cambios pendientes copiando los valores a sus variables -void checkPendingChanges(); // Verifica si hay cambios pendientes +void init(); // Inicializa las opciones del programa +void setConfigFile(const std::string &file_path); // Establece el fichero de configuración +void setControllersFile(const std::string &file_path); // Establece el fichero de configuración de mandos +auto loadFromFile() -> bool; // Carga el fichero de configuración +auto saveToFile() -> bool; // Guarda el fichero de configuración +void setKeyboardToPlayer(int player_id); // Asigna el teclado al jugador +void swapKeyboard(); // Intercambia el teclado de jugador +void swapControllers(); // Intercambia los jugadores asignados a los dos primeros mandos +auto getPlayerWhoUsesKeyboard() -> int; // Averigua quién está usando el teclado +void applyPendingChanges(); // Aplica los cambios pendientes copiando los valores a sus variables +void checkPendingChanges(); // Verifica si hay cambios pendientes + } // namespace Options \ No newline at end of file