migrada configuracio a yaml
This commit is contained in:
@@ -103,7 +103,7 @@ void Director::init() {
|
||||
Input::init(Asset::get()->getPath("gamecontrollerdb.txt"), Asset::get()->getPath("controllers.json")); // Carga configuración de controles
|
||||
|
||||
Logger::section("INIT CONFIG");
|
||||
Options::setConfigFile(Asset::get()->getPath("config_v2.txt")); // Establece el fichero de configuración
|
||||
Options::setConfigFile(Asset::get()->getPath("config.yaml")); // Establece el fichero de configuración
|
||||
Options::setControllersFile(Asset::get()->getPath("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
|
||||
|
||||
14726
source/external/fkyaml_node.hpp
vendored
Normal file
14726
source/external/fkyaml_node.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -2,22 +2,18 @@
|
||||
|
||||
#include <SDL3/SDL.h> // Para SDL_ScaleMode, SDL_LogCategory, SDL_LogError, SDL_LogInfo, SDL_LogWarn
|
||||
|
||||
#include <algorithm> // Para clamp, __any_of_fn, any_of
|
||||
#include <cstddef> // Para size_t
|
||||
#include <fstream> // Para basic_ostream, operator<<, basic_ofstream, basic_ostream::operator<<, basic_istream, basic_ifstream, ifstream, istringstream, ofstream
|
||||
#include <functional> // Para function
|
||||
#include <map> // Para map, operator==, _Rb_tree_const_iterator
|
||||
#include <sstream> // Para basic_istringstream
|
||||
#include <stdexcept> // Para invalid_argument, out_of_range
|
||||
#include <string> // Para char_traits, basic_string, stoi, string, operator<<, operator+, operator==, operator>>, getline, operator<=>, to_string
|
||||
#include <utility> // Para pair
|
||||
#include <vector> // Para vector
|
||||
#include <algorithm> // Para clamp
|
||||
#include <cstddef> // Para size_t
|
||||
#include <fstream> // Para ifstream, ofstream
|
||||
#include <string> // Para string
|
||||
#include <vector> // Para vector
|
||||
|
||||
#include "difficulty.hpp" // Para Code, init
|
||||
#include "input.hpp" // Para Input
|
||||
#include "lang.hpp" // Para getText, Code
|
||||
#include "ui/logger.hpp" // Para info
|
||||
#include "utils.hpp" // Para stringToBool, boolToString, getFileName
|
||||
#include "difficulty.hpp" // Para Code, init
|
||||
#include "external/fkyaml_node.hpp" // Para fkyaml::node
|
||||
#include "input.hpp" // Para Input
|
||||
#include "lang.hpp" // Para getText, Code
|
||||
#include "ui/logger.hpp" // Para info
|
||||
#include "utils.hpp" // Para boolToString, getFileName
|
||||
|
||||
namespace Options {
|
||||
// --- Variables globales ---
|
||||
@@ -29,14 +25,11 @@ namespace Options {
|
||||
Keyboard keyboard; // Opciones para el teclado
|
||||
PendingChanges pending_changes; // Opciones que se aplican al cerrar
|
||||
|
||||
// Declaraciones
|
||||
auto set(const std::string& var, const std::string& value) -> bool;
|
||||
|
||||
// Establece el fichero de configuración
|
||||
void setConfigFile(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; };
|
||||
void setControllersFile(const std::string& file_path) { settings.controllers_file = file_path; }
|
||||
|
||||
// Inicializa las opciones del programa
|
||||
void init() {
|
||||
@@ -53,56 +46,170 @@ namespace Options {
|
||||
pending_changes.has_pending_changes = false;
|
||||
}
|
||||
|
||||
// --- Funciones helper de carga desde YAML ---
|
||||
|
||||
void loadWindowFromYaml(const fkyaml::node& yaml) {
|
||||
if (!yaml.contains("window")) { return; }
|
||||
const auto& win = yaml["window"];
|
||||
if (win.contains("zoom")) {
|
||||
try {
|
||||
int val = win["zoom"].get_value<int>();
|
||||
window.zoom = (val > 0) ? val : window.zoom;
|
||||
} catch (...) {}
|
||||
}
|
||||
}
|
||||
|
||||
void loadVideoFromYaml(const fkyaml::node& yaml) {
|
||||
if (!yaml.contains("video")) { return; }
|
||||
const auto& vid = yaml["video"];
|
||||
|
||||
if (vid.contains("fullscreen")) {
|
||||
try { video.fullscreen = vid["fullscreen"].get_value<bool>(); } catch (...) {}
|
||||
}
|
||||
if (vid.contains("scale_mode")) {
|
||||
try { video.scale_mode = static_cast<SDL_ScaleMode>(vid["scale_mode"].get_value<int>()); } catch (...) {}
|
||||
}
|
||||
if (vid.contains("vsync")) {
|
||||
try { video.vsync = vid["vsync"].get_value<bool>(); } catch (...) {}
|
||||
}
|
||||
if (vid.contains("integer_scale")) {
|
||||
try { video.integer_scale = vid["integer_scale"].get_value<bool>(); } catch (...) {}
|
||||
}
|
||||
if (vid.contains("shaders")) {
|
||||
try { video.shaders = vid["shaders"].get_value<bool>(); } catch (...) {}
|
||||
}
|
||||
}
|
||||
|
||||
void loadAudioFromYaml(const fkyaml::node& yaml) {
|
||||
if (!yaml.contains("audio")) { return; }
|
||||
const auto& aud = yaml["audio"];
|
||||
|
||||
if (aud.contains("enabled")) {
|
||||
try { audio.enabled = aud["enabled"].get_value<bool>(); } catch (...) {}
|
||||
}
|
||||
if (aud.contains("volume")) {
|
||||
try { audio.volume = std::clamp(aud["volume"].get_value<int>(), 0, 100); } catch (...) {}
|
||||
}
|
||||
if (aud.contains("music")) {
|
||||
const auto& mus = aud["music"];
|
||||
if (mus.contains("enabled")) {
|
||||
try { audio.music.enabled = mus["enabled"].get_value<bool>(); } catch (...) {}
|
||||
}
|
||||
if (mus.contains("volume")) {
|
||||
try { audio.music.volume = std::clamp(mus["volume"].get_value<int>(), 0, 100); } catch (...) {}
|
||||
}
|
||||
}
|
||||
if (aud.contains("sound")) {
|
||||
const auto& snd = aud["sound"];
|
||||
if (snd.contains("enabled")) {
|
||||
try { audio.sound.enabled = snd["enabled"].get_value<bool>(); } catch (...) {}
|
||||
}
|
||||
if (snd.contains("volume")) {
|
||||
try { audio.sound.volume = std::clamp(snd["volume"].get_value<int>(), 0, 100); } catch (...) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void loadGameFromYaml(const fkyaml::node& yaml) {
|
||||
if (!yaml.contains("game")) { return; }
|
||||
const auto& game = yaml["game"];
|
||||
|
||||
if (game.contains("language")) {
|
||||
try {
|
||||
auto lang = static_cast<Lang::Code>(game["language"].get_value<int>());
|
||||
if (lang == Lang::Code::ENGLISH || lang == Lang::Code::VALENCIAN || lang == Lang::Code::SPANISH) {
|
||||
settings.language = lang;
|
||||
} else {
|
||||
settings.language = Lang::Code::ENGLISH;
|
||||
}
|
||||
pending_changes.new_language = settings.language;
|
||||
} catch (...) {}
|
||||
}
|
||||
if (game.contains("difficulty")) {
|
||||
try {
|
||||
settings.difficulty = static_cast<Difficulty::Code>(game["difficulty"].get_value<int>());
|
||||
pending_changes.new_difficulty = settings.difficulty;
|
||||
} catch (...) {}
|
||||
}
|
||||
if (game.contains("autofire")) {
|
||||
try { settings.autofire = game["autofire"].get_value<bool>(); } catch (...) {}
|
||||
}
|
||||
if (game.contains("shutdown_enabled")) {
|
||||
try { settings.shutdown_enabled = game["shutdown_enabled"].get_value<bool>(); } catch (...) {}
|
||||
}
|
||||
if (game.contains("params_file")) {
|
||||
try { settings.params_file = game["params_file"].get_value<std::string>(); } catch (...) {}
|
||||
}
|
||||
}
|
||||
|
||||
void loadControllersFromYaml(const fkyaml::node& yaml) {
|
||||
if (!yaml.contains("controllers")) { return; }
|
||||
const auto& controllers = yaml["controllers"];
|
||||
|
||||
size_t i = 0;
|
||||
for (const auto& ctrl : controllers) {
|
||||
if (i >= GamepadManager::size()) { break; }
|
||||
if (ctrl.contains("name")) {
|
||||
try { gamepad_manager[i].name = ctrl["name"].get_value<std::string>(); } catch (...) {}
|
||||
}
|
||||
if (ctrl.contains("path")) {
|
||||
try { gamepad_manager[i].path = ctrl["path"].get_value<std::string>(); } catch (...) {}
|
||||
}
|
||||
if (ctrl.contains("player")) {
|
||||
try {
|
||||
int player_int = ctrl["player"].get_value<int>();
|
||||
if (player_int == 1) {
|
||||
gamepad_manager[i].player_id = Player::Id::PLAYER1;
|
||||
} else if (player_int == 2) {
|
||||
gamepad_manager[i].player_id = Player::Id::PLAYER2;
|
||||
}
|
||||
} catch (...) {}
|
||||
}
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
void loadKeyboardFromYaml(const fkyaml::node& yaml) {
|
||||
if (!yaml.contains("keyboard")) { return; }
|
||||
const auto& kb = yaml["keyboard"];
|
||||
if (kb.contains("player")) {
|
||||
try { keyboard.player_id = static_cast<Player::Id>(kb["player"].get_value<int>()); } catch (...) {}
|
||||
}
|
||||
}
|
||||
|
||||
// Carga el fichero de configuración
|
||||
auto loadFromFile() -> bool {
|
||||
// 1. Inicializa las opciones con valores por defecto.
|
||||
init();
|
||||
|
||||
std::ifstream file(settings.config_file);
|
||||
bool file_exists = file.is_open(); // Guardamos si el fichero existía al abrirlo
|
||||
|
||||
// 2. Si el fichero existe, lo leemos para obtener los nombres de los mandos.
|
||||
if (file_exists) {
|
||||
// --- CASO: EL FICHERO EXISTE ---
|
||||
// SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\nReading file: %s", getFileName(settings.config_file).c_str());
|
||||
Logger::info("Reading file: " + getFileName(settings.config_file));
|
||||
std::string line;
|
||||
std::string param_name;
|
||||
std::string param_value;
|
||||
|
||||
while (std::getline(file, line)) {
|
||||
// Elimina comentarios
|
||||
auto comment_pos = line.find('#');
|
||||
if (comment_pos != std::string::npos) {
|
||||
line.resize(comment_pos);
|
||||
}
|
||||
|
||||
// Si la línea contiene '=', lo reemplazamos por un espacio para compatibilidad
|
||||
auto equals_pos = line.find('=');
|
||||
if (equals_pos != std::string::npos) {
|
||||
line[equals_pos] = ' ';
|
||||
}
|
||||
|
||||
// Usa un stream para separar palabras (elimina automáticamente espacios extra)
|
||||
std::istringstream iss(line);
|
||||
if (iss >> param_name >> param_value) {
|
||||
if (!set(param_name, param_value)) {
|
||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Unknown parameter: %s", param_name.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
// 3. Llamamos al asignador inteligente.
|
||||
gamepad_manager.assignAndLinkGamepads();
|
||||
|
||||
// 4. Si el fichero no existía, lo creamos ahora con la configuración por defecto.
|
||||
if (!file_exists) {
|
||||
if (!file.is_open()) {
|
||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Config file not found. Creating default settings.");
|
||||
saveToFile();
|
||||
return true;
|
||||
}
|
||||
|
||||
Logger::info("Reading file: " + getFileName(settings.config_file));
|
||||
std::string content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
||||
file.close();
|
||||
|
||||
try {
|
||||
auto yaml = fkyaml::node::deserialize(content);
|
||||
|
||||
loadWindowFromYaml(yaml);
|
||||
loadVideoFromYaml(yaml);
|
||||
loadAudioFromYaml(yaml);
|
||||
loadGameFromYaml(yaml);
|
||||
loadControllersFromYaml(yaml);
|
||||
loadKeyboardFromYaml(yaml);
|
||||
|
||||
} catch (const fkyaml::exception& e) {
|
||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Error parsing YAML config: %s. Using defaults.", e.what());
|
||||
init();
|
||||
saveToFile();
|
||||
return true;
|
||||
}
|
||||
|
||||
gamepad_manager.assignAndLinkGamepads();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -115,158 +222,71 @@ namespace Options {
|
||||
return false;
|
||||
}
|
||||
|
||||
// SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Writing file: %s", getFileName(settings.config_file).c_str());
|
||||
Logger::info("Writing file: " + getFileName(settings.config_file));
|
||||
|
||||
applyPendingChanges();
|
||||
|
||||
// Versión del archivo
|
||||
file << "# Coffee Crisis Arcade Edition - Configuration File\n";
|
||||
file << "# Format: key value\n";
|
||||
file << "config.version " << settings.config_version << "\n";
|
||||
file << "# This file is automatically generated and managed by the game.\n";
|
||||
file << "\n";
|
||||
|
||||
// Opciones de ventana
|
||||
file << "\n# WINDOW\n";
|
||||
file << "window.zoom " << window.zoom << "\n";
|
||||
file << "version: " << settings.config_version << "\n";
|
||||
file << "\n";
|
||||
|
||||
// Opciones de video
|
||||
file << "\n# VIDEO\n";
|
||||
file << "# video.scale_mode [" << static_cast<int>(SDL_ScaleMode::SDL_SCALEMODE_NEAREST) << ": nearest, " << static_cast<int>(SDL_ScaleMode::SDL_SCALEMODE_LINEAR) << ": linear]\n";
|
||||
file << "video.fullscreen " << boolToString(video.fullscreen) << "\n";
|
||||
file << "video.scale_mode " << static_cast<int>(video.scale_mode) << "\n";
|
||||
file << "video.vsync " << boolToString(video.vsync) << "\n";
|
||||
file << "video.integer_scale " << boolToString(video.integer_scale) << "\n";
|
||||
file << "video.shaders " << boolToString(video.shaders) << "\n";
|
||||
// WINDOW
|
||||
file << "# WINDOW\n";
|
||||
file << "window:\n";
|
||||
file << " zoom: " << window.zoom << "\n";
|
||||
file << "\n";
|
||||
|
||||
// Opciones de audio
|
||||
file << "\n# AUDIO\n";
|
||||
file << "# volume range: [0 .. 100]\n";
|
||||
file << "audio.enabled " << boolToString(audio.enabled) << "\n";
|
||||
file << "audio.volume " << audio.volume << "\n";
|
||||
file << "audio.music.enabled " << boolToString(audio.music.enabled) << "\n";
|
||||
file << "audio.music.volume " << audio.music.volume << "\n";
|
||||
file << "audio.sound.enabled " << boolToString(audio.sound.enabled) << "\n";
|
||||
file << "audio.sound.volume " << audio.sound.volume << "\n";
|
||||
// VIDEO
|
||||
file << "# VIDEO\n";
|
||||
file << "video:\n";
|
||||
file << " fullscreen: " << boolToString(video.fullscreen) << "\n";
|
||||
file << " scale_mode: " << static_cast<int>(video.scale_mode) << " # " << static_cast<int>(SDL_ScaleMode::SDL_SCALEMODE_NEAREST) << ": nearest, " << static_cast<int>(SDL_ScaleMode::SDL_SCALEMODE_LINEAR) << ": linear\n";
|
||||
file << " vsync: " << boolToString(video.vsync) << "\n";
|
||||
file << " integer_scale: " << boolToString(video.integer_scale) << "\n";
|
||||
file << " shaders: " << boolToString(video.shaders) << "\n";
|
||||
file << "\n";
|
||||
|
||||
// Opciones del juego
|
||||
file << "\n# GAME\n";
|
||||
file << "# game.language [0: spanish, 1: valencian, 2: english]\n";
|
||||
file << "# game.difficulty [" << static_cast<int>(Difficulty::Code::EASY) << ": easy, " << static_cast<int>(Difficulty::Code::NORMAL) << ": normal, " << static_cast<int>(Difficulty::Code::HARD) << ": hard]\n";
|
||||
file << "game.language " << static_cast<int>(settings.language) << "\n";
|
||||
file << "game.difficulty " << static_cast<int>(settings.difficulty) << "\n";
|
||||
file << "game.autofire " << boolToString(settings.autofire) << "\n";
|
||||
file << "game.shutdown_enabled " << boolToString(settings.shutdown_enabled) << "\n";
|
||||
file << "game.params_file " << settings.params_file << "\n";
|
||||
// AUDIO
|
||||
file << "# AUDIO (volume range: 0..100)\n";
|
||||
file << "audio:\n";
|
||||
file << " enabled: " << boolToString(audio.enabled) << "\n";
|
||||
file << " volume: " << audio.volume << "\n";
|
||||
file << " music:\n";
|
||||
file << " enabled: " << boolToString(audio.music.enabled) << "\n";
|
||||
file << " volume: " << audio.music.volume << "\n";
|
||||
file << " sound:\n";
|
||||
file << " enabled: " << boolToString(audio.sound.enabled) << "\n";
|
||||
file << " volume: " << audio.sound.volume << "\n";
|
||||
file << "\n";
|
||||
|
||||
// Opciones de mandos
|
||||
file << "\n# CONTROLLERS\n";
|
||||
// GAME
|
||||
file << "# GAME\n";
|
||||
file << "game:\n";
|
||||
file << " language: " << static_cast<int>(settings.language) << " # 0: spanish, 1: valencian, 2: english\n";
|
||||
file << " difficulty: " << static_cast<int>(settings.difficulty) << " # " << static_cast<int>(Difficulty::Code::EASY) << ": easy, " << static_cast<int>(Difficulty::Code::NORMAL) << ": normal, " << static_cast<int>(Difficulty::Code::HARD) << ": hard\n";
|
||||
file << " autofire: " << boolToString(settings.autofire) << "\n";
|
||||
file << " shutdown_enabled: " << boolToString(settings.shutdown_enabled) << "\n";
|
||||
file << " params_file: " << settings.params_file << "\n";
|
||||
file << "\n";
|
||||
|
||||
// CONTROLLERS
|
||||
file << "# CONTROLLERS\n";
|
||||
file << "controllers:\n";
|
||||
gamepad_manager.saveToFile(file);
|
||||
file << "\n";
|
||||
|
||||
// Opciones de teclado
|
||||
file << "\n# KEYBOARD\n";
|
||||
file << "keyboard.player " << static_cast<int>(keyboard.player_id) << "\n";
|
||||
// KEYBOARD
|
||||
file << "# KEYBOARD\n";
|
||||
file << "keyboard:\n";
|
||||
file << " player: " << static_cast<int>(keyboard.player_id) << "\n";
|
||||
|
||||
// Cierra el fichero
|
||||
file.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Función auxiliar para analizar la configuración del mando y reducir duplicación
|
||||
void parseAndSetController(const std::string& var, const std::string& value) {
|
||||
size_t first_dot = var.find('.');
|
||||
size_t second_dot = var.find('.', first_dot + 1);
|
||||
|
||||
if (first_dot == std::string::npos || second_dot == std::string::npos) {
|
||||
return; // Formato inválido
|
||||
}
|
||||
|
||||
try {
|
||||
int controller_index = std::stoi(var.substr(first_dot + 1, second_dot - first_dot - 1));
|
||||
std::string setting_key = var.substr(second_dot + 1);
|
||||
|
||||
gamepad_manager.setControllerProperty(controller_index, setting_key, value);
|
||||
} catch (const std::exception&) {
|
||||
// Error en parsing
|
||||
}
|
||||
}
|
||||
|
||||
auto set(const std::string& var, const std::string& value) -> bool {
|
||||
// Clausula de protección: ignora líneas vacías o comentarios
|
||||
if (var.empty() || var.starts_with("#")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Un mapa estático asegura que se inicializa solo una vez
|
||||
static const std::map<std::string, std::function<void(const std::string&)>> SETTINGS_MAP = {
|
||||
// Configuración
|
||||
{"config.version", [](const auto& val) -> auto { settings.config_version = std::stoi(val); }},
|
||||
// Ventana
|
||||
{"window.zoom", [](const auto& val) -> auto { window.zoom = std::stoi(val); }},
|
||||
// Vídeo
|
||||
{"video.fullscreen", [](const auto& val) -> auto { video.fullscreen = stringToBool(val); }},
|
||||
{"video.scale_mode", [](const auto& val) -> auto { video.scale_mode = static_cast<SDL_ScaleMode>(std::stoi(val)); }},
|
||||
{"video.shaders", [](const auto& val) -> auto { video.shaders = stringToBool(val); }},
|
||||
{"video.integer_scale", [](const auto& val) -> auto { video.integer_scale = stringToBool(val); }},
|
||||
{"video.vsync", [](const auto& val) -> auto { video.vsync = stringToBool(val); }},
|
||||
// Audio
|
||||
{"audio.enabled", [](const auto& val) -> auto { audio.enabled = stringToBool(val); }},
|
||||
{"audio.volume", [](const auto& val) -> auto { audio.volume = std::clamp(std::stoi(val), 0, 100); }},
|
||||
{"audio.music.enabled", [](const auto& val) -> auto { audio.music.enabled = stringToBool(val); }},
|
||||
{"audio.music.volume", [](const auto& val) -> auto { audio.music.volume = std::clamp(std::stoi(val), 0, 100); }},
|
||||
{"audio.sound.enabled", [](const auto& val) -> auto { audio.sound.enabled = stringToBool(val); }},
|
||||
{"audio.sound.volume", [](const auto& val) -> auto { audio.sound.volume = std::clamp(std::stoi(val), 0, 100); }},
|
||||
// Juego
|
||||
{"game.language", [](const auto& val) -> auto {
|
||||
settings.language = static_cast<Lang::Code>(std::stoi(val));
|
||||
|
||||
if (settings.language != Lang::Code::ENGLISH &&
|
||||
settings.language != Lang::Code::VALENCIAN &&
|
||||
settings.language != Lang::Code::SPANISH) {
|
||||
settings.language = Lang::Code::ENGLISH;
|
||||
}
|
||||
pending_changes.new_language = settings.language;
|
||||
}},
|
||||
{"game.difficulty", [](const auto& val) -> auto {
|
||||
settings.difficulty = static_cast<Difficulty::Code>(std::stoi(val));
|
||||
pending_changes.new_difficulty = settings.difficulty;
|
||||
}},
|
||||
{"game.autofire", [](const auto& val) -> auto { settings.autofire = stringToBool(val); }},
|
||||
{"game.shutdown_enabled", [](const auto& val) -> auto { settings.shutdown_enabled = stringToBool(val); }},
|
||||
{"game.params_file", [](const auto& val) -> auto { settings.params_file = val; }},
|
||||
// Teclado
|
||||
{"keyboard.player", [](const auto& val) -> auto { keyboard.player_id = static_cast<Player::Id>(stoi(val)); }}};
|
||||
|
||||
// Maneja por separado la configuración general de los mandos
|
||||
if (var.starts_with("controller.")) {
|
||||
try {
|
||||
parseAndSetController(var, value);
|
||||
return true;
|
||||
} catch (const std::out_of_range& e) {
|
||||
// Error: por ejemplo, índice de mando fuera de rango
|
||||
return false;
|
||||
} catch (const std::invalid_argument& e) {
|
||||
// Error: por ejemplo, fallo en std::stoi
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Busca el nombre de la variable en el mapa
|
||||
if (auto it = SETTINGS_MAP.find(var); it != SETTINGS_MAP.end()) {
|
||||
try {
|
||||
// Ejecuta la función lambda asociada
|
||||
it->second(value);
|
||||
return true;
|
||||
} catch (const std::invalid_argument& e) {
|
||||
// Maneja casos donde std::stoi falla por entrada inválida
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Si la clave no se encontró en el mapa ni en la lógica de mandos
|
||||
return false;
|
||||
}
|
||||
|
||||
// Asigna el teclado al jugador
|
||||
void setKeyboardToPlayer(Player::Id player_id) {
|
||||
keyboard.player_id = player_id;
|
||||
@@ -324,27 +344,18 @@ namespace Options {
|
||||
|
||||
// Asigna los mandos físicos basándose en la configuración actual.
|
||||
void GamepadManager::assignAndLinkGamepads() {
|
||||
// 1. Obtenemos los mandos físicos conectados.
|
||||
auto physical_gamepads = Input::get()->getGamepads();
|
||||
|
||||
// 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 ya asignados.
|
||||
std::vector<std::shared_ptr<Input::Gamepad>> assigned_instances;
|
||||
|
||||
// --- 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();
|
||||
}
|
||||
|
||||
@@ -356,19 +367,16 @@ namespace Options {
|
||||
for (size_t i = 0; i < MAX_PLAYERS; ++i) {
|
||||
const std::string& desired_path = desired_paths[i];
|
||||
if (desired_path.empty()) {
|
||||
continue; // No hay ruta guardada para este slot.
|
||||
continue;
|
||||
}
|
||||
|
||||
// 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.
|
||||
gamepads_[i].name = physical_gamepad->name;
|
||||
|
||||
assigned_instances.push_back(physical_gamepad);
|
||||
break; // Mando encontrado para este jugador, pasamos al siguiente.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -380,20 +388,17 @@ namespace Options {
|
||||
std::vector<std::shared_ptr<Input::Gamepad>>& assigned_instances) {
|
||||
for (size_t i = 0; i < MAX_PLAYERS; ++i) {
|
||||
if (gamepads_[i].instance != nullptr) {
|
||||
continue; // Este jugador ya tiene un mando.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Buscamos un mando físico que todavía esté libre.
|
||||
for (const auto& physical_gamepad : physical_gamepads) {
|
||||
if (!isGamepadAssigned(physical_gamepad, assigned_instances)) {
|
||||
gamepads_[i].instance = physical_gamepad;
|
||||
|
||||
// MUY IMPORTANTE: Actualizamos la configuración para reflejar la realidad.
|
||||
gamepads_[i].name = physical_gamepad->name;
|
||||
gamepads_[i].path = physical_gamepad->path;
|
||||
|
||||
assigned_instances.push_back(physical_gamepad);
|
||||
break; // Mando encontrado, pasamos al siguiente jugador.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -401,21 +406,14 @@ namespace Options {
|
||||
|
||||
// --- 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 = Lang::getText("[SERVICE_MENU] NO_CONTROLLER");
|
||||
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(
|
||||
const std::shared_ptr<Input::Gamepad>& physical_gamepad,
|
||||
const std::vector<std::shared_ptr<Input::Gamepad>>& assigned_instances) -> bool { // NOLINT(readability-named-parameter)
|
||||
@@ -447,4 +445,4 @@ namespace Options {
|
||||
}
|
||||
return Player::Id::NO_PLAYER;
|
||||
}
|
||||
} // namespace Options
|
||||
} // namespace Options
|
||||
|
||||
@@ -156,14 +156,9 @@ namespace Options {
|
||||
void saveToFile(std::ofstream& file) const {
|
||||
for (size_t i = 0; i < MAX_PLAYERS; ++i) {
|
||||
const auto& gamepad = gamepads_[i];
|
||||
// Guardar el nombre solo si hay path (mando real asignado)
|
||||
if (!gamepad.path.empty()) {
|
||||
file << "controller." << i << ".name " << gamepad.name << "\n";
|
||||
} else {
|
||||
file << "controller." << i << ".name \n"; // vacío
|
||||
}
|
||||
file << "controller." << i << ".path " << gamepad.path << "\n";
|
||||
file << "controller." << i << ".player " << static_cast<int>(gamepad.player_id) << "\n";
|
||||
file << " - name: \"" << (gamepad.path.empty() ? "" : gamepad.name) << "\"\n";
|
||||
file << " path: \"" << gamepad.path << "\"\n";
|
||||
file << " player: " << static_cast<int>(gamepad.player_id) << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user