diff --git a/data/lang/ba_BA.json b/data/lang/ba_BA.json index bcbbb09..ddbf31f 100644 --- a/data/lang/ba_BA.json +++ b/data/lang/ba_BA.json @@ -74,6 +74,10 @@ "[SERVICE_MENU] MUSIC_VOLUME": "Volumen de la musica", "[SERVICE_MENU] SFX_VOLUME": "Volumen dels sons", "[SERVICE_MENU] AUTOFIRE": "Dispar automatic", + "[SERVICE_MENU] LANGUAGE": "Idioma", + "[SERVICE_MENU] LANG ES": "Castella", + "[SERVICE_MENU] LANG EN": "Angles", + "[SERVICE_MENU] LANG BA": "Balooncia", "[SERVICE_MENU] VIDEO": "Video", "[SERVICE_MENU] AUDIO": "Audio", "[SERVICE_MENU] GAME": "Joc", diff --git a/data/lang/en_UK.json b/data/lang/en_UK.json index 5e84c6a..2133a6a 100644 --- a/data/lang/en_UK.json +++ b/data/lang/en_UK.json @@ -74,6 +74,10 @@ "[SERVICE_MENU] MUSIC_VOLUME": "Music Volume", "[SERVICE_MENU] SFX_VOLUME": "Sound Volume", "[SERVICE_MENU] AUTOFIRE": "Autofire", + "[SERVICE_MENU] LANGUAGE": "Language", + "[SERVICE_MENU] LANG ES": "Spanish", + "[SERVICE_MENU] LANG EN": "English", + "[SERVICE_MENU] LANG BA": "Balooncia", "[SERVICE_MENU] VIDEO": "Video", "[SERVICE_MENU] AUDIO": "Audio", "[SERVICE_MENU] GAME": "Game", diff --git a/data/lang/es_ES.json b/data/lang/es_ES.json index 2b89737..b653b06 100644 --- a/data/lang/es_ES.json +++ b/data/lang/es_ES.json @@ -74,6 +74,10 @@ "[SERVICE_MENU] MUSIC_VOLUME": "Volumen de la musica", "[SERVICE_MENU] SFX_VOLUME": "Volumen de los efectos", "[SERVICE_MENU] AUTOFIRE": "Disparo automatico", + "[SERVICE_MENU] LANGUAGE": "Idioma", + "[SERVICE_MENU] LANG ES": "Castellano", + "[SERVICE_MENU] LANG EN": "Ingles", + "[SERVICE_MENU] LANG BA": "Balooncia", "[SERVICE_MENU] VIDEO": "Video", "[SERVICE_MENU] AUDIO": "Audio", "[SERVICE_MENU] GAME": "Juego", diff --git a/source/director.cpp b/source/director.cpp index 60df752..c5e506e 100644 --- a/source/director.cpp +++ b/source/director.cpp @@ -93,15 +93,15 @@ void Director::init() loadScoreFile(); // Carga el archivo de puntuaciones // Inicialización de subsistemas principales - lang::loadFromFile(getLangFile(static_cast(options.game.language))); // Carga el archivo de idioma - Screen::init(); // Inicializa la pantalla y el sistema de renderizado - Audio::init(); // Activa el sistema de audio - Resource::init(); // Inicializa el sistema de gestión de recursos - Input::init(Asset::get()->get("gamecontrollerdb.txt")); // Carga configuración de controles - bindInputs(); // Asigna los controles a la entrada del sistema - ServiceMenu::init(); // Inicializa el menú de servicio - Notifier::init(std::string(), Resource::get()->getText("8bithud")); // Inicialización del sistema de notificaciones - Screen::get()->getSingletons(); // Obtiene los punteros al resto de singletones + lang::setLanguage(options.game.language); // Carga el archivo de idioma + Screen::init(); // Inicializa la pantalla y el sistema de renderizado + Audio::init(); // Activa el sistema de audio + Resource::init(); // Inicializa el sistema de gestión de recursos + Input::init(Asset::get()->get("gamecontrollerdb.txt")); // Carga configuración de controles + bindInputs(); // Asigna los controles a la entrada del sistema + ServiceMenu::init(); // Inicializa el menú de servicio + Notifier::init(std::string(), Resource::get()->getText("8bithud")); // Inicialización del sistema de notificaciones + Screen::get()->getSingletons(); // Obtiene los punteros al resto de singletones } // Cierra todo y libera recursos del sistema y de los singletons @@ -616,9 +616,12 @@ void Director::runDemoGame() game->run(); } -// Ejecuta la sección init -void Director::runInit() +// Reinicia objetos y vuelve a la sección inicial +void Director::reset() { + saveOptionsFile(Asset::get()->get("config.txt")); + loadOptionsFile(Asset::get()->get("config.txt")); + lang::setLanguage(options.game.language); Audio::get()->stopMusic(); Audio::get()->stopAllSounds(); if (section::options == section::Options::RELOAD || true) @@ -636,8 +639,8 @@ int Director::run() { switch (section::name) { - case section::Name::INIT: - runInit(); + case section::Name::RESET: + reset(); break; case section::Name::LOGO: runLogo(); @@ -687,27 +690,6 @@ int Director::run() return (section::options == section::Options::QUIT_WITH_CONTROLLER) ? 1 : 0; } -// Obtiene una fichero a partir de un lang::Code -std::string Director::getLangFile(lang::Code code) -{ - switch (code) - { - case lang::Code::VALENCIAN: - return Asset::get()->get("ba_BA.json"); - break; - case lang::Code::SPANISH: - return Asset::get()->get("es_ES.json"); - break; - case lang::Code::ENGLISH: - return Asset::get()->get("en_UK.json"); - break; - default: - break; - } - - return Asset::get()->get("en_UK.json"); -} - #ifdef ARCADE // Apaga el sistema void Director::shutdownSystem(bool should_shutdown) diff --git a/source/director.h b/source/director.h index 2eb0639..d3f268e 100644 --- a/source/director.h +++ b/source/director.h @@ -46,7 +46,7 @@ private: void runCredits(); // Muestra los créditos del juego void runHiScoreTable(); // Muestra la tabla de puntuaciones void runDemoGame(); // Ejecuta el modo demo - void runInit(); // Ejecuta la fase de inicialización + void reset(); // Reinicia objetos y vuelve a la sección inicial // Gestión de archivos de idioma std::string getLangFile(lang::Code code); // Obtiene un fichero de idioma según el código diff --git a/source/game.cpp b/source/game.cpp index 2a9733b..b7f6593 100644 --- a/source/game.cpp +++ b/source/game.cpp @@ -1724,13 +1724,13 @@ void Game::initDifficultyVars() void Game::initPlayers(int player_id) { // Crea los dos jugadores - const int y = param.game.play_area.rect.h - 30; - players_.emplace_back(std::make_unique(1, param.game.play_area.first_quarter_x - 15, y, demo_.enabled, param.game.play_area.rect, player_textures_[0], player_animations_)); + const int Y = param.game.play_area.rect.h - 30; + players_.emplace_back(std::make_unique(1, param.game.play_area.first_quarter_x - 15, Y, demo_.enabled, param.game.play_area.rect, player_textures_[0], player_animations_)); players_.back()->setScoreBoardPanel(SCOREBOARD_LEFT_PANEL); players_.back()->setName(lang::getText("[SCOREBOARD] 1")); players_.back()->setController(getController(players_.back()->getId())); - players_.emplace_back(std::make_unique(2, param.game.play_area.third_quarter_x - 15, y, demo_.enabled, param.game.play_area.rect, player_textures_[1], player_animations_)); + players_.emplace_back(std::make_unique(2, param.game.play_area.third_quarter_x - 15, Y, demo_.enabled, param.game.play_area.rect, player_textures_[1], player_animations_)); players_.back()->setScoreBoardPanel(SCOREBOARD_RIGHT_PANEL); players_.back()->setName(lang::getText("[SCOREBOARD] 2")); players_.back()->setController(getController(players_.back()->getId())); diff --git a/source/global_inputs.cpp b/source/global_inputs.cpp index 0c46c23..7e52fbd 100644 --- a/source/global_inputs.cpp +++ b/source/global_inputs.cpp @@ -44,7 +44,7 @@ namespace globalInputs const std::string CODE = "RESET"; if (Notifier::get()->checkCode(CODE)) { - section::name = section::Name::INIT; + section::name = section::Name::RESET; Notifier::get()->show({lang::getText("[NOTIFICATIONS] 15")}); } else @@ -124,7 +124,7 @@ namespace globalInputs { options.game.language = lang::getNextLangCode(options.game.language); lang::loadFromFile(getLangFile(static_cast(options.game.language))); - section::name = section::Name::INIT; + section::name = section::Name::RESET; section::options = section::Options::RELOAD; Notifier::get()->show({lang::getText("[NOTIFICATIONS] 05") + getLangName(options.game.language)}); } diff --git a/source/lang.cpp b/source/lang.cpp index b6cd6bd..22289a3 100644 --- a/source/lang.cpp +++ b/source/lang.cpp @@ -2,6 +2,8 @@ #include #include #include "json.hpp" +#include "options.h" +#include "asset.h" using json = nlohmann::json; @@ -9,6 +11,12 @@ namespace lang { std::unordered_map texts; + // Vector con los idiomas soportados + std::vector languages = { + {Code::SPANISH, "Castellano", "es_ES.json"}, + {Code::VALENCIAN, "Balooncia", "ba_BA.json"}, + {Code::ENGLISH, "Ingles", "en_UK.json"}}; + // Inicializa los textos del juego en el idioma seleccionado bool loadFromFile(const std::string &file_path) { @@ -50,8 +58,81 @@ namespace lang // Obtiene el código del siguiente idioma disponible Code getNextLangCode(Code lang) { - auto index = static_cast(lang); - index = (index + 1) % 3; - return static_cast(index); + for (size_t i = 0; i < languages.size(); ++i) + { + if (languages[i].code == lang) + { + return languages[(i + 1) % languages.size()].code; + } + } + // Si no se encuentra, devuelve el primero por defecto + return languages[0].code; + } + + // Obtiene un idioma del vector de idiomas a partir de un código + Language getLanguage(Code code) + { + for (const auto &lang : languages) + { + if (lang.code == code) + return lang; + } + // Si no se encuentra, devuelve el primero por defecto + return languages[0]; + } + + // Devuelve el código de un idioma a partir de un nombre + Code getCodeFromName(const std::string& name) + { + for (const auto& lang : languages) + { + if (lang.name == name) + return lang.code; + } + // Si no se encuentra, devuelve el primero por defecto + return languages[0].code; + } + + // Actualiza los nombres de los idiomas + void updateLanguageNames() + { + for (auto &lang : languages) + { + switch (lang.code) + { + case Code::SPANISH: + lang.name = lang::getText("[SERVICE_MENU] LANG ES"); + break; + case Code::VALENCIAN: + lang.name = lang::getText("[SERVICE_MENU] LANG BA"); + break; + case Code::ENGLISH: + lang.name = lang::getText("[SERVICE_MENU] LANG EN"); + break; + default: + lang.name = "Unknown"; + break; + } + } + } + + // Obtiene una fichero a partir de un lang::Code + std::string getLanguageFileName(lang::Code code) + { + for (const auto &lang : languages) + { + if (lang.code == code) + return Asset::get()->get(lang.file_name); + } + // Si no se encuentra, devuelve el fichero del primer idioma por defecto + return Asset::get()->get(languages[0].file_name); + } + + // Establece el idioma + void setLanguage(Code lang) + { + options.game.language = lang; + loadFromFile(Asset::get()->get(getLanguage(lang).file_name)); + updateLanguageNames(); } } diff --git a/source/lang.h b/source/lang.h index 90c421d..5b2b358 100644 --- a/source/lang.h +++ b/source/lang.h @@ -1,6 +1,7 @@ #pragma once #include +#include namespace lang { @@ -12,12 +13,38 @@ namespace lang ENGLISH = 2 }; + // Estructura que representa un idioma + struct Language + { + Code code; // Código que identifica al idioma + std::string name; // Nombre que identifica el idioma + std::string file_name; // Nombre del fichero con los textos + + Language(Code c, const std::string &n, const std::string &fn) + : code(c), name(n), file_name(fn) {} + }; + // Carga los textos desde el fichero JSON especificado bool loadFromFile(const std::string &file_path); - // Obtiene un texto a partir de una clave + // Obtiene el texto por clave std::string getText(const std::string &key); // Obtiene el código del siguiente idioma (circular) - Code getNextLangCode(Code lang); + Code getNextLangCode(Code current_lang); + + // Obtiene el idioma correspondiente al código proporcionado + Language getLanguage(Code code); + + // Devuelve el código de un idioma a partir de un nombre + Code getCodeFromName(const std::string& name); + + // Actualiza los nombres de los idiomas + void updateLanguageNames(); + + // Obtiene el nombre del fichero de textos asociado a un código de idioma + std::string getLanguageFileName(Code code); + + // Establece el idioma actual + void setLanguage(Code lang); } diff --git a/source/options.cpp b/source/options.cpp index 147b735..500938e 100644 --- a/source/options.cpp +++ b/source/options.cpp @@ -47,6 +47,11 @@ void initOptions() options.controllers.at(0).player_id = 1; options.controllers.at(1).player_id = 2; setKeyboardToPlayer(1); + + // Opciones pendientes + options.pending_changes.new_language = options.game.language; + options.pending_changes.new_difficulty = options.game.difficulty; + options.pending_changes.has_pending_changes = false; } // Carga el fichero de configuración @@ -115,6 +120,8 @@ bool saveOptionsFile(std::string file_path) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Writing file: %s", getFileName(file_path).c_str()); + applyPendingChanges(); + // Opciones de video file << "## VIDEO\n"; file << "## video.scale_mode [" << static_cast(SDL_ScaleMode::SDL_SCALEMODE_NEAREST) << ": nearest, " << static_cast(SDL_ScaleMode::SDL_SCALEMODE_LINEAR) << ": lineal]\n"; @@ -237,10 +244,12 @@ bool setOptions(const std::string &var, const std::string &value) else if (var == "game.language") { options.game.language = static_cast(std::stoi(value)); + options.pending_changes.new_language = options.game.language; } else if (var == "game.difficulty") { options.game.difficulty = static_cast(std::stoi(value)); + options.pending_changes.new_difficulty = options.game.difficulty; } else if (var == "game.autofire") { @@ -365,4 +374,15 @@ int getPlayerWhoUsesKeyboard() } } return 0; -} \ No newline at end of file +} + +// Aplica los cambios pendientes copiando los valores a sus variables +void applyPendingChanges() +{ + if (options.pending_changes.has_pending_changes) + { + options.game.language = options.pending_changes.new_language; + options.game.difficulty = options.pending_changes.new_difficulty; + options.pending_changes.has_pending_changes = false; + } +} diff --git a/source/options.h b/source/options.h index 229f5ff..6b76d9a 100644 --- a/source/options.h +++ b/source/options.h @@ -97,6 +97,14 @@ struct GamepadOptions buttons{SDL_GAMEPAD_BUTTON_WEST, SDL_GAMEPAD_BUTTON_NORTH, SDL_GAMEPAD_BUTTON_EAST, SDL_GAMEPAD_BUTTON_START, SDL_GAMEPAD_BUTTON_BACK} {} }; +// --- Opciones pendientes de aplicar --- +struct PendingChanges +{ + lang::Code new_language; // Idioma en espera de aplicar + GameDifficulty new_difficulty; // Dificultad en espera de aplicar + bool has_pending_changes = false; // Indica si hay cambios pendientes +}; + // --- Opciones generales del programa --- struct Options { @@ -105,6 +113,7 @@ struct Options 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 }; // --- Variables globales --- @@ -117,4 +126,5 @@ bool saveOptionsFile(std::string file_path); // Guarda el fichero de configuraci void setKeyboardToPlayer(int player_id); // Asigna el teclado al jugador void swapOptionsKeyboard(); // Intercambia el teclado de jugador void swapOptionsControllers(); // Intercambia los jugadores asignados a los dos primeros mandos -int getPlayerWhoUsesKeyboard(); // Averigua quién está usando el teclado \ No newline at end of file +int getPlayerWhoUsesKeyboard(); // Averigua quién está usando el teclado +void applyPendingChanges(); // Aplica los cambios pendientes copiando los valores a sus variables \ No newline at end of file diff --git a/source/section.h b/source/section.h index c71545b..ccf65df 100644 --- a/source/section.h +++ b/source/section.h @@ -11,7 +11,7 @@ namespace section // --- Enumeraciones de secciones del programa --- enum class Name { - INIT, // Inicialización + RESET, // Inicialización LOGO, // Pantalla de logo INTRO, // Introducción TITLE, // Pantalla de título/menú principal diff --git a/source/service_menu.cpp b/source/service_menu.cpp index d6d65e3..2b9f774 100644 --- a/source/service_menu.cpp +++ b/source/service_menu.cpp @@ -38,7 +38,9 @@ void ServiceMenu::toggle() { enabled_ = !enabled_; if (!enabled_) + { reset(); + } } // Dibuja el menú de servicio en pantalla @@ -135,14 +137,8 @@ void ServiceMenu::setAnchors() // Establce la posición donde empezar a escribir las opciones del menu void ServiceMenu::setOptionsPosition() { - /* - const size_t MAX_ENTRIES = findLargestGroupSize(); - const size_t CURRENT_ENTRIES = display_options_.size(); - const size_t ADDED_SPACE = ((MAX_ENTRIES - CURRENT_ENTRIES) * (options_height_ + options_padding_)) / 2; - options_y_ = rect_.y + upper_height_ + lower_padding_ + ADDED_SPACE; - */ - resize(); - options_y_ = rect_.y + upper_height_ + lower_padding_; + resize(); + options_y_ = rect_.y + upper_height_ + lower_padding_; } // Cambia el tamaño de la ventana de menu @@ -222,11 +218,13 @@ void ServiceMenu::setSelectorDown() void ServiceMenu::adjustOption(bool adjust_up) { if (display_options_.empty() || selected_ >= display_options_.size()) - return; - - if (display_options_.at(selected_).behavior == OptionBehavior::ADJUST) { - display_options_.at(selected_).adjustValue(adjust_up); + return; + } + + if (display_options_.at(selected_)->behavior == OptionBehavior::ADJUST) + { + display_options_.at(selected_)->adjustValue(adjust_up); option_pairs_ = getOptionPairs(current_settings_group_); applySettings(current_settings_group_); playMenuSound(); @@ -237,13 +235,15 @@ void ServiceMenu::adjustOption(bool adjust_up) void ServiceMenu::selectOption() { if (display_options_.empty() || selected_ >= display_options_.size()) + { return; + } // Carpeta - if (display_options_.at(selected_).type == ValueType::FOLDER) + if (display_options_.at(selected_)->type == ValueType::FOLDER) { previous_settings_group_ = current_settings_group_; - current_settings_group_ = display_options_.at(selected_).target_group; + current_settings_group_ = display_options_.at(selected_)->target_group; updateMenu(current_settings_group_); selected_ = 0; setOptionsPosition(); @@ -252,21 +252,21 @@ void ServiceMenu::selectOption() } // Opción - if (display_options_.at(selected_).behavior == OptionBehavior::SELECT) + if (display_options_.at(selected_)->behavior == OptionBehavior::SELECT) { - if (display_options_.at(selected_).caption == lang::getText("[SERVICE_MENU] RESET")) + if (display_options_.at(selected_)->caption == lang::getText("[SERVICE_MENU] RESET")) { - section::name = section::Name::INIT; + section::name = section::Name::RESET; toggle(); return; } - else if (display_options_.at(selected_).caption == lang::getText("[SERVICE_MENU] QUIT")) + else if (display_options_.at(selected_)->caption == lang::getText("[SERVICE_MENU] QUIT")) { section::name = section::Name::QUIT; section::options = section::Options::QUIT_WITH_KEYBOARD; return; } - else if (display_options_.at(selected_).caption == lang::getText("[SERVICE_MENU] SHUTDOWN")) + else if (display_options_.at(selected_)->caption == lang::getText("[SERVICE_MENU] SHUTDOWN")) { section::name = section::Name::QUIT; section::options = section::Options::QUIT_WITH_CONTROLLER; @@ -314,6 +314,15 @@ void ServiceMenu::initializeOptions() // Game options_.emplace_back(lang::getText("[SERVICE_MENU] AUTOFIRE"), SettingsGroup::GAME, OptionBehavior::ADJUST, &options.game.autofire, ValueType::BOOL); + options_.emplace_back( + lang::getText("[SERVICE_MENU] LANGUAGE"), + SettingsGroup::GAME, + OptionBehavior::ADJUST, + &options.pending_changes.new_language, + std::vector{ + lang::getText("[SERVICE_MENU] LANG ES"), + lang::getText("[SERVICE_MENU] LANG BA"), + lang::getText("[SERVICE_MENU] LANG EN")}); // System options_.emplace_back(lang::getText("[SERVICE_MENU] RESET"), SettingsGroup::SYSTEM, OptionBehavior::SELECT, nullptr, ValueType::NONE); @@ -330,33 +339,31 @@ void ServiceMenu::initializeOptions() // Devuelve las opciones del grupo como pares (nombre, valor) ServiceMenu::OptionPairs ServiceMenu::getOptionPairs(ServiceMenu::SettingsGroup group) const { - OptionPairs optionPairs; + OptionPairs option_pairs; for (const auto &option : options_) { if (option.group == group) { - optionPairs.emplace_back(option.caption, option.getValueAsString()); + option_pairs.emplace_back(option.caption, option.getValueAsString()); } } - return optionPairs; + return option_pairs; } // Devuelve las opciones del grupo como un vector de OptionEntry -std::vector ServiceMenu::getOptionsByGroup(SettingsGroup group) const +std::vector ServiceMenu::getOptionsByGroup(SettingsGroup group) { - std::vector filteredOptions; - - for (const auto &option : options_) + std::vector filtered_options; + for (auto &option : options_) { if (option.group == group) { - filteredOptions.push_back(option); + filtered_options.push_back(&option); } } - - return filteredOptions; + return filtered_options; } // Aplica la configuración correspondiente al grupo seleccionado @@ -367,11 +374,11 @@ void ServiceMenu::applySettings(ServiceMenu::SettingsGroup group) case SettingsGroup::VIDEO: Screen::get()->applySettings(); break; - case SettingsGroup::AUDIO: Audio::get()->applySettings(); break; - + case SettingsGroup::GAME: + break; default: break; } @@ -380,6 +387,7 @@ void ServiceMenu::applySettings(ServiceMenu::SettingsGroup group) // Actualiza las opciones mostradas según el grupo seleccionado void ServiceMenu::updateMenu(SettingsGroup group) { + AdjustListValues(); option_pairs_ = getOptionPairs(group); display_options_ = getOptionsByGroup(group); } @@ -423,8 +431,34 @@ ServiceMenu::GroupAlignment ServiceMenu::getGroupAlignment(SettingsGroup group) case SettingsGroup::AUDIO: case SettingsGroup::GAME: return GroupAlignment::LEFT; - default: return GroupAlignment::CENTERED; } +} + +// Devuelve un puntero a OptionEntry a partir del caption, o nullptr si no se encuentra +ServiceMenu::OptionEntry *ServiceMenu::getOptionEntryByCaption(const std::string &caption) +{ + for (auto &option : options_) + { + if (option.caption == caption) + { + return &option; + } + } + return nullptr; +} + +// Ajusta los valores de las opciones tipo lista +void ServiceMenu::AdjustListValues() +{ + // Idioma + auto option = getOptionEntryByCaption(lang::getText("[SERVICE_MENU] LANGUAGE")); + for (size_t i = 0; i < option->value_list.size(); ++i) + { + if (lang::getCodeFromName(option->value_list[i]) == options.game.language) + { + option->list_index = i; + } + } } \ No newline at end of file diff --git a/source/service_menu.h b/source/service_menu.h index 6c8832d..9906387 100644 --- a/source/service_menu.h +++ b/source/service_menu.h @@ -7,6 +7,7 @@ #include #include "utils.h" #include "lang.h" +#include "options.h" class Text; @@ -68,6 +69,7 @@ private: { BOOL, // Valor booleano INT, // Valor entero + LIST, // Lista de valores FOLDER, // Referencia a otro grupo NONE // Sin valor asociado }; @@ -85,28 +87,35 @@ private: SettingsGroup group; // Categoría de la opción OptionBehavior behavior; // Cómo se interactúa con la opción void *linked_variable; // Puntero a la variable que controla la opción - ValueType type; // Tipo de la variable (bool, int, folder, none) + ValueType type; // Tipo de la variable - int min_value; // Valor mínimo (solo aplicable si type == INT) - int max_value; // Valor máximo (solo aplicable si type == INT) - int step_value; // Incremento al modificar la opción (solo aplicable si type == INT) + int min_value; // Valor mínimo (solo aplicable si type == INT) + int max_value; // Valor máximo (solo aplicable si type == INT) + int step_value; // Incremento al modificar la opción (solo aplicable si type == INT) + std::vector value_list; // Lista de valores posibles (solo aplicable si type == LIST) + size_t list_index; // Índice del valor seleccionado dentro de value_list SettingsGroup target_group; // Grupo al que hace referencia la opción si es de tipo FOLDER - // Constructor para opciones de tipo BOOL, NONE o FOLDER + // Constructor para opciones de tipo BOOL, NONE, FOLDER OptionEntry(std::string cap, SettingsGroup grp, OptionBehavior beh, void *var, ValueType t) : caption(cap), group(grp), behavior(beh), linked_variable(var), type(t), - min_value(0), max_value(0), step_value(0), target_group(SettingsGroup::SYSTEM) {} + min_value(0), max_value(0), step_value(0), list_index(0), target_group(SettingsGroup::SYSTEM) {} - // Constructor para opciones de tipo INT con valores mínimos, máximos e incremento + // Constructor para opciones de tipo INT OptionEntry(std::string cap, SettingsGroup grp, OptionBehavior beh, void *var, ValueType t, int min, int max, int step) : caption(cap), group(grp), behavior(beh), linked_variable(var), type(t), - min_value(min), max_value(max), step_value(step), target_group(SettingsGroup::SYSTEM) {} + min_value(min), max_value(max), step_value(step), list_index(0), target_group(SettingsGroup::SYSTEM) {} - // Constructor para opciones de tipo FOLDER que referencian otro grupo + // Constructor para opciones de tipo LIST + OptionEntry(std::string cap, SettingsGroup grp, OptionBehavior beh, void *var, std::vector values) + : caption(cap), group(grp), behavior(beh), linked_variable(var), type(ValueType::LIST), + min_value(0), max_value(0), step_value(0), value_list(values), list_index(0), target_group(SettingsGroup::SYSTEM) {} + + // Constructor para opciones de tipo FOLDER OptionEntry(std::string cap, SettingsGroup grp, OptionBehavior beh, SettingsGroup tgtGrp) : caption(cap), group(grp), behavior(beh), linked_variable(nullptr), type(ValueType::FOLDER), - min_value(0), max_value(0), step_value(0), target_group(tgtGrp) {} + min_value(0), max_value(0), step_value(0), list_index(0), target_group(tgtGrp) {} // Método para modificar el valor de la opción void adjustValue(bool adjust_up) @@ -124,6 +133,25 @@ private: bool &value = *(static_cast(linked_variable)); value = !value; } + else if (type == ValueType::LIST && !value_list.empty()) + { + list_index = adjust_up ? (list_index + 1) % value_list.size() + : (list_index - 1 + value_list.size()) % value_list.size(); + + // Idioma + if (linked_variable == &options.pending_changes.new_language) + { + options.pending_changes.new_language = lang::getCodeFromName(value_list[list_index]); + options.pending_changes.has_pending_changes = true; + } + + // Dificultad + if (linked_variable == &options.pending_changes.new_difficulty) + { + // options.pending_changes.new_difficulty = + options.pending_changes.has_pending_changes = true; + } + } } } @@ -136,6 +164,8 @@ private: return (*(static_cast(linked_variable))) ? lang::getText("[SERVICE_MENU] ON") : lang::getText("[SERVICE_MENU] OFF"); case ValueType::INT: return std::to_string(*(static_cast(linked_variable))); + case ValueType::LIST: + return value_list.empty() ? "" : value_list[list_index]; default: return ""; } @@ -143,18 +173,18 @@ private: }; // --- Variables internas --- - bool enabled_ = false; // Indica si el menú de servicio está activo - SDL_FRect rect_; // Rectángulo que define el área del menú de servicio - std::shared_ptr element_text_; // Objeto para escribir el texto de los elementos - std::shared_ptr title_text_; // Objeto para escribir el texto del título - size_t selected_ = 0; // Índice del elemento del menú seleccionado - Uint32 counter_ = 0; // Contador interno - std::vector options_; // Listado con todas las opciones del menú de servicio - std::vector display_options_; // Opciones actualmente mostradas en pantalla - OptionPairs option_pairs_; // Opciones actuales del menú (filtradas por grupo) - SettingsGroup current_settings_group_; // Grupo de opciones actualmente activo - SettingsGroup previous_settings_group_; // Grupo de opciones anterior - Aspect aspect_ = Aspect::ASPECT1; // Estilo visual del menú + bool enabled_ = false; // Indica si el menú de servicio está activo + SDL_FRect rect_; // Rectángulo que define el área del menú de servicio + std::shared_ptr element_text_; // Objeto para escribir el texto de los elementos + std::shared_ptr title_text_; // Objeto para escribir el texto del título + size_t selected_ = 0; // Índice del elemento del menú seleccionado + Uint32 counter_ = 0; // Contador interno + std::vector options_; // Listado con todas las opciones del menú de servicio + std::vector display_options_; // Opciones actualmente mostradas en pantalla (punteros) + OptionPairs option_pairs_; // Opciones actuales del menú (filtradas por grupo) + SettingsGroup current_settings_group_; // Grupo de opciones actualmente activo + SettingsGroup previous_settings_group_; // Grupo de opciones anterior + Aspect aspect_ = Aspect::ASPECT1; // Estilo visual del menú // --- Variables de aspecto --- Color bg_color_ = SERV_MENU_BG_COLOR; // Color de fondo @@ -179,19 +209,21 @@ private: void resize(); // Cambia el tamaño de la ventana de menu // --- Métodos internos: Gestión de opciones --- - void initializeOptions(); // Crea todas las opciones del menú de servicio - OptionPairs getOptionPairs(SettingsGroup group) const; // Devuelve las opciones como pares de strings para un grupo - std::vector getOptionsByGroup(SettingsGroup group) const; // Devuelve las opciones de un grupo + void initializeOptions(); // Crea todas las opciones del menú de servicio + OptionPairs getOptionPairs(SettingsGroup group) const; // Devuelve las opciones como pares de strings para un grupo + std::vector getOptionsByGroup(SettingsGroup group); // Devuelve punteros a las opciones de un grupo // --- Métodos internos: Lógica de menú --- void applySettings(SettingsGroup group); // Aplica la configuración de un grupo void updateMenu(SettingsGroup group); // Actualiza las opciones mostradas según el grupo + void AdjustListValues(); // Ajusta los valores de las opciones tipo lista // --- Métodos internos: Utilidades --- - void updateCounter(); // Actualiza el contador interno - int calculateMenuHeight() const; // Calcula la altura del menú - int findLargestGroupSize() const; // Devuelve el tamaño del grupo más grande - GroupAlignment getGroupAlignment(SettingsGroup group) const; // Devuelve la alineación del grupo + void updateCounter(); // Actualiza el contador interno + int calculateMenuHeight() const; // Calcula la altura del menú + int findLargestGroupSize() const; // Devuelve el tamaño del grupo más grande + GroupAlignment getGroupAlignment(SettingsGroup group) const; // Devuelve la alineación del grupo + OptionEntry *getOptionEntryByCaption(const std::string &caption); // Devuelve un puntero a OptionEntry a partir del caption // --- Patrón Singleton --- ServiceMenu(); // Constructor privado