From fe520dd341a8082dd3150e9a87d8d4242314d8f4 Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Wed, 1 Apr 2026 20:08:55 +0200 Subject: [PATCH] - Es pot posar shader preset directament per nom desde la consola - shader preset i palette ja autocompleten amb la llista de noms --- data/console/commands.yaml | 6 +- source/core/input/global_inputs.cpp | 4 +- source/core/rendering/render_info.cpp | 5 +- source/game/ui/console.cpp | 24 +----- source/game/ui/console_commands.cpp | 120 ++++++++++++++++++++------ source/game/ui/console_commands.hpp | 9 +- source/utils/utils.cpp | 7 ++ source/utils/utils.hpp | 1 + 8 files changed, 119 insertions(+), 57 deletions(-) diff --git a/data/console/commands.yaml b/data/console/commands.yaml index 93bfb53..d00f710 100644 --- a/data/console/commands.yaml +++ b/data/console/commands.yaml @@ -30,10 +30,10 @@ categories: - keyword: SHADER handler: cmd_shader description: "Toggle/select shader (F4)" - usage: "SHADER [ON|OFF|NEXT [PRESET]|POSTFX|CRTPI]" + usage: "SHADER [ON|OFF|NEXT|POSTFX|CRTPI|PRESET [NEXT|PREV|]]" completions: - SHADER: [ON, OFF, NEXT, POSTFX, CRTPI] - SHADER NEXT: [PRESET] + SHADER: [ON, OFF, NEXT, POSTFX, CRTPI, PRESET] + dynamic_completions: true - keyword: BORDER handler: cmd_border diff --git a/source/core/input/global_inputs.cpp b/source/core/input/global_inputs.cpp index 190bbe6..f187392 100644 --- a/source/core/input/global_inputs.cpp +++ b/source/core/input/global_inputs.cpp @@ -100,13 +100,13 @@ namespace GlobalInputs { if (!Options::crtpi_presets.empty()) { Options::video.shader.current_crtpi_preset = (Options::video.shader.current_crtpi_preset + 1) % static_cast(Options::crtpi_presets.size()); Screen::get()->reloadCrtPi(); - Notifier::get()->show({Locale::get()->get("ui.crtpi") + " " + Options::crtpi_presets[static_cast(Options::video.shader.current_crtpi_preset)].name}); // NOLINT(readability-static-accessed-through-instance) + Notifier::get()->show({Locale::get()->get("ui.crtpi") + " " + prettyName(Options::crtpi_presets[static_cast(Options::video.shader.current_crtpi_preset)].name)}); // NOLINT(readability-static-accessed-through-instance) } } else { if (!Options::postfx_presets.empty()) { Options::video.shader.current_postfx_preset = (Options::video.shader.current_postfx_preset + 1) % static_cast(Options::postfx_presets.size()); Screen::get()->reloadPostFX(); - Notifier::get()->show({Locale::get()->get("ui.postfx") + " " + Options::postfx_presets[static_cast(Options::video.shader.current_postfx_preset)].name}); // NOLINT(readability-static-accessed-through-instance) + Notifier::get()->show({Locale::get()->get("ui.postfx") + " " + prettyName(Options::postfx_presets[static_cast(Options::video.shader.current_postfx_preset)].name)}); // NOLINT(readability-static-accessed-through-instance) } } } diff --git a/source/core/rendering/render_info.cpp b/source/core/rendering/render_info.cpp index 7763d3c..bcc61a9 100644 --- a/source/core/rendering/render_info.cpp +++ b/source/core/rendering/render_info.cpp @@ -12,6 +12,7 @@ #include "core/rendering/surface.hpp" // Para Surface #include "core/rendering/text.hpp" // Para Text #include "game/options.hpp" // Para Options +#include "utils/utils.hpp" // Para prettyName #include "game/ui/console.hpp" // Para Console #include "game/ui/notifier.hpp" // Para Notifier @@ -95,11 +96,11 @@ void RenderInfo::render() const { std::string preset_name = "-"; if (IS_CRTPI) { if (!Options::crtpi_presets.empty()) { - preset_name = Options::crtpi_presets[static_cast(Options::video.shader.current_crtpi_preset)].name; + preset_name = prettyName(Options::crtpi_presets[static_cast(Options::video.shader.current_crtpi_preset)].name); } } else { if (!Options::postfx_presets.empty()) { - preset_name = Options::postfx_presets[static_cast(Options::video.shader.current_postfx_preset)].name; + preset_name = prettyName(Options::postfx_presets[static_cast(Options::video.shader.current_postfx_preset)].name); } } const bool SHOW_SS = Options::video.supersampling.enabled && !IS_CRTPI; diff --git a/source/game/ui/console.cpp b/source/game/ui/console.cpp index d00199f..091620e 100644 --- a/source/game/ui/console.cpp +++ b/source/game/ui/console.cpp @@ -359,26 +359,10 @@ void Console::handleEvent(const SDL_Event& event) { } else { const std::string base_cmd = upper.substr(0, space_pos); const std::string sub_prefix = upper.substr(space_pos + 1); - if (registry_.hasDynamicCompletions(base_cmd) && base_cmd == "PALETTE" && Screen::get() != nullptr) { - // NEXT/PREV primero, luego todos los nombres de paleta disponibles - for (const auto* sv : {"NEXT", "PREV"}) { - if (sub_prefix.empty() || std::string_view{sv}.starts_with(sub_prefix)) { - tab_matches_.emplace_back("PALETTE " + std::string(sv)); - } - } - for (const auto& name : Screen::get()->getPaletteNames()) { - if (sub_prefix.empty() || std::string_view{name}.starts_with(sub_prefix)) { - tab_matches_.emplace_back("PALETTE " + name); - } - } - } else { - const auto* opts = registry_.getCompletions(base_cmd); - if (opts != nullptr) { - for (const auto& arg : *opts) { - if (sub_prefix.empty() || std::string_view{arg}.starts_with(sub_prefix)) { - tab_matches_.emplace_back(base_cmd + " " + arg); - } - } + const auto opts = registry_.getCompletions(base_cmd); + for (const auto& arg : opts) { + if (sub_prefix.empty() || std::string_view{arg}.starts_with(sub_prefix)) { + tab_matches_.emplace_back(base_cmd + " " + arg); } } } diff --git a/source/game/ui/console_commands.cpp b/source/game/ui/console_commands.cpp index 6b1e68e..19cbc7c 100644 --- a/source/game/ui/console_commands.cpp +++ b/source/game/ui/console_commands.cpp @@ -19,6 +19,7 @@ #include "game/options.hpp" // Para Options #include "game/scene_manager.hpp" // Para SceneManager #include "game/ui/notifier.hpp" // Para Notifier +#include "utils/utils.hpp" // Para toUpper, prettyName #ifdef _DEBUG #include "core/system/debug.hpp" // Para Debug @@ -110,7 +111,59 @@ static auto cmd_ss(const std::vector& args) -> std::string { return "usage: ss [on|off|size|upscale [nearest|linear]|downscale [bilinear|lanczos2|lanczos3]]"; } -// SHADER [ON|OFF|NEXT [PRESET]|POSTFX|CRTPI] +// Helper: aplica un preset por dirección (NEXT/PREV) o nombre; devuelve mensaje +static auto applyPreset(const std::vector& args) -> std::string { + const bool IS_CRTPI = Options::video.shader.current_shader == Rendering::ShaderType::CRTPI; + const auto& presets_postfx = Options::postfx_presets; + const auto& presets_crtpi = Options::crtpi_presets; + const std::string shader_label = IS_CRTPI ? "CrtPi" : "PostFX"; + + auto& current_idx = IS_CRTPI ? Options::video.shader.current_crtpi_preset + : Options::video.shader.current_postfx_preset; + const int count = static_cast(IS_CRTPI ? presets_crtpi.size() : presets_postfx.size()); + + if (count == 0) { return "No " + shader_label + " presets available"; } + + const auto presetName = [&]() -> std::string { + const auto& name = IS_CRTPI ? presets_crtpi[static_cast(current_idx)].name + : presets_postfx[static_cast(current_idx)].name; + return prettyName(name); + }; + + if (args.empty()) { + return shader_label + " preset: " + presetName(); + } + + if (args[0] == "NEXT") { + current_idx = (current_idx + 1) % count; + } else if (args[0] == "PREV") { + current_idx = (current_idx - 1 + count) % count; + } else { + // Buscar por nombre (case-insensitive, con guiones) + std::string search = args[0]; + std::ranges::transform(search, search.begin(), ::toupper); + bool found = false; + for (int i = 0; i < count; ++i) { + const auto& name = IS_CRTPI ? presets_crtpi[static_cast(i)].name + : presets_postfx[static_cast(i)].name; + if (toUpper(name) == search) { + current_idx = i; + found = true; + break; + } + } + if (!found) { + std::string name_lower = args[0]; + std::ranges::transform(name_lower, name_lower.begin(), ::tolower); + return "Unknown preset: " + name_lower; + } + } + + if (IS_CRTPI) { Screen::get()->reloadCrtPi(); } else { Screen::get()->reloadPostFX(); } + return shader_label + " preset: " + presetName(); +} + +// SHADER [ON|OFF|NEXT|POSTFX|CRTPI|PRESET [NEXT|PREV|]] static auto cmd_shader(const std::vector& args) -> std::string { if (!Screen::get()->isHardwareAccelerated()) { return "No GPU acceleration"; } if (args.empty()) { @@ -136,29 +189,15 @@ static auto cmd_shader(const std::vector& args) -> std::string { return "Shader: CrtPi"; } if (args[0] == "NEXT") { - if (args.size() >= 2 && args[1] == "PRESET") { - if (Options::video.shader.current_shader == Rendering::ShaderType::CRTPI) { - if (Options::crtpi_presets.empty()) { return "No CrtPi presets available"; } - Options::video.shader.current_crtpi_preset = - (Options::video.shader.current_crtpi_preset + 1) % - static_cast(Options::crtpi_presets.size()); - Screen::get()->reloadCrtPi(); - return "CrtPi preset: " + - Options::crtpi_presets[static_cast(Options::video.shader.current_crtpi_preset)].name; - } - if (Options::postfx_presets.empty()) { return "No PostFX presets available"; } - Options::video.shader.current_postfx_preset = - (Options::video.shader.current_postfx_preset + 1) % - static_cast(Options::postfx_presets.size()); - Screen::get()->reloadPostFX(); - return "PostFX preset: " + - Options::postfx_presets[static_cast(Options::video.shader.current_postfx_preset)].name; - } Screen::get()->nextShader(); return std::string("Shader: ") + (Options::video.shader.current_shader == Rendering::ShaderType::CRTPI ? "CrtPi" : "PostFX"); } - return "usage: shader [on|off|next [preset]|postfx|crtpi]"; + if (args[0] == "PRESET") { + const std::vector rest(args.begin() + 1, args.end()); + return applyPreset(rest); + } + return "usage: shader [on|off|next|postfx|crtpi|preset [next|prev|]]"; } // BORDER [ON|OFF] @@ -744,6 +783,30 @@ void CommandRegistry::registerHandlers() { handlers_["cmd_scene"] = cmd_scene; #endif // HELP se registra en load() como lambda que captura this + + // Proveedores de completions dinámicas + // PALETTE: NEXT, PREV + nombres de paletas disponibles (UPPERCASE) + dynamic_providers_["PALETTE"] = []() -> std::vector { + std::vector result = {"NEXT", "PREV"}; + if (Screen::get() != nullptr) { + for (const auto& name : Screen::get()->getPaletteNames()) { + result.push_back(toUpper(name)); + } + } + return result; + }; + + // SHADER PRESET: NEXT, PREV + nombres de presets del shader activo (UPPERCASE) + dynamic_providers_["SHADER PRESET"] = []() -> std::vector { + std::vector result = {"NEXT", "PREV"}; + const bool IS_CRTPI = Options::video.shader.current_shader == Rendering::ShaderType::CRTPI; + if (IS_CRTPI) { + for (const auto& p : Options::crtpi_presets) { result.push_back(toUpper(p.name)); } + } else { + for (const auto& p : Options::postfx_presets) { result.push_back(toUpper(p.name)); } + } + return result; + }; } void CommandRegistry::load(const std::string& yaml_path) { @@ -914,10 +977,16 @@ auto CommandRegistry::generateConsoleHelp() const -> std::string { return result; } -auto CommandRegistry::getCompletions(const std::string& path) const -> const std::vector* { +auto CommandRegistry::getCompletions(const std::string& path) const -> std::vector { + // Primero: buscar proveedor dinámico (tiene prioridad si existe) + const auto dyn_it = dynamic_providers_.find(path); + if (dyn_it != dynamic_providers_.end()) { + return dyn_it->second(); + } + // Fallback: completions estáticas del YAML const auto it = completions_map_.find(path); - if (it != completions_map_.end()) { return &it->second; } - return nullptr; + if (it != completions_map_.end()) { return it->second; } + return {}; } auto CommandRegistry::getVisibleKeywords() const -> std::vector { @@ -929,8 +998,3 @@ auto CommandRegistry::getVisibleKeywords() const -> std::vector { } return result; } - -auto CommandRegistry::hasDynamicCompletions(const std::string& keyword) const -> bool { - const auto* def = findCommand(keyword); - return def != nullptr && def->dynamic_completions; -} diff --git a/source/game/ui/console_commands.hpp b/source/game/ui/console_commands.hpp index c77ef45..6b23276 100644 --- a/source/game/ui/console_commands.hpp +++ b/source/game/ui/console_commands.hpp @@ -23,6 +23,9 @@ struct CommandDef { // Tipo de función handler para comandos using CommandHandler = std::function& args)>; +// Proveedor de completions dinámicas: devuelve las opciones para TAB en UPPERCASE +using DynamicCompletionProvider = std::function()>; + // Registro de comandos: une metadatos YAML con handlers C++ class CommandRegistry { public: @@ -38,14 +41,16 @@ class CommandRegistry { [[nodiscard]] auto generateConsoleHelp() const -> std::string; // TAB completion - [[nodiscard]] auto getCompletions(const std::string& path) const -> const std::vector*; + // Devuelve las opciones de completado para un path dado (ej: "SHADER", "SHADER PRESET") + // Combina completions estáticas del YAML con dinámicas registradas en C++ + [[nodiscard]] auto getCompletions(const std::string& path) const -> std::vector; [[nodiscard]] auto getVisibleKeywords() const -> std::vector; - [[nodiscard]] auto hasDynamicCompletions(const std::string& keyword) const -> bool; private: std::vector commands_; std::unordered_map handlers_; std::unordered_map> completions_map_; + std::unordered_map dynamic_providers_; void registerHandlers(); }; diff --git a/source/utils/utils.cpp b/source/utils/utils.cpp index 325f548..bbaebab 100644 --- a/source/utils/utils.cpp +++ b/source/utils/utils.cpp @@ -414,6 +414,13 @@ auto toUpper(const std::string& str) -> std::string { return upper_str; } +// Convierte guiones a espacios ("crt-live" → "crt live") +auto prettyName(const std::string& str) -> std::string { + std::string result = str; + std::ranges::replace(result, '-', ' '); + return result; +} + // Obtiene el nombre de un fichero a partir de una ruta completa auto getFileName(const std::string& path) -> std::string { return std::filesystem::path(path).filename().string(); diff --git a/source/utils/utils.hpp b/source/utils/utils.hpp index aa63afb..e526867 100644 --- a/source/utils/utils.hpp +++ b/source/utils/utils.hpp @@ -98,6 +98,7 @@ auto stringToBool(const std::string& str) -> bool; // Strin auto boolToString(bool value) -> std::string; // Bool a string (1/0) auto toLower(const std::string& str) -> std::string; // String a minúsculas auto toUpper(const std::string& str) -> std::string; // String a mayúsculas +auto prettyName(const std::string& str) -> std::string; // Guiones a espacios ("crt-live" → "crt live") // OPERACIONES CON STRINGS auto stringInVector(const std::vector& vec, const std::string& str) -> bool; // Busca string en vector