From b164c11ba755fb6e58f454855d8efc03c5984f54 Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Mon, 30 Mar 2026 23:27:38 +0200 Subject: [PATCH] console crea la tabla tab_completions automaticament --- source/game/ui/console.cpp | 143 +++++++++++++++++++++++-------------- source/game/ui/console.hpp | 12 ++-- 2 files changed, 95 insertions(+), 60 deletions(-) diff --git a/source/game/ui/console.cpp b/source/game/ui/console.cpp index 7351ba8..1240e18 100644 --- a/source/game/ui/console.cpp +++ b/source/game/ui/console.cpp @@ -28,11 +28,16 @@ // ── Sistema de comandos ──────────────────────────────────────────────────────── +// Mapa de completions: {ruta_completa_en_mayúsculas, {opciones}} +// Ej: {"CHEAT OPEN THE", {"JAIL"}} +using CompletionMap = std::vector>>; + struct ConsoleCommand { std::string_view keyword; std::function& args)> execute; - bool instant{false}; // Si true, muestra la respuesta sin efecto typewriter - bool hidden{false}; // Si true, no aparece en el autocompletado (TAB) + bool instant{false}; // Si true, muestra la respuesta sin efecto typewriter + bool hidden{false}; // Si true, no aparece en el autocompletado (TAB) + CompletionMap completions{}; // Árbol de sub-argumentos para TAB; cargado en el constructor de Console }; // Convierte la entrada a uppercase y la divide en tokens por espacios @@ -196,6 +201,11 @@ static const std::vector COMMANDS = { return "PostFX Supersampling OFF"; } return "usage: ss [on|off|size|upscale [nearest|linear]|downscale [bilinear|lanczos2|lanczos3]]"; + }, + .completions = { + {"SS", {"ON", "OFF", "SIZE", "UPSCALE", "DOWNSCALE"}}, + {"SS UPSCALE", {"NEAREST", "LINEAR"}}, + {"SS DOWNSCALE", {"BILINEAR", "LANCZOS2", "LANCZOS3"}}, }}, // SHADER [ON|OFF|NEXT [PRESET]|POSTFX|CRTPI] — Toggle/cicla/selecciona shader (F4 / Shift+F4) @@ -248,10 +258,15 @@ static const std::vector COMMANDS = { (Options::current_shader == Rendering::ShaderType::CRTPI ? "CRTPI" : "POSTFX"); } return "usage: shader [on|off|next [preset]|postfx|crtpi]"; + }, + .completions = { + {"SHADER", {"ON", "OFF", "NEXT", "POSTFX", "CRTPI"}}, + {"SHADER NEXT", {"PRESET"}}, }}, // BORDER [ON|OFF] — Borde decorativo (B) - {.keyword = "BORDER", .execute = BOOL_TOGGLE_CMD("Border", Options::video.border.enabled, Screen::get()->toggleBorder())}, + {.keyword = "BORDER", .execute = BOOL_TOGGLE_CMD("Border", Options::video.border.enabled, Screen::get()->toggleBorder()), + .completions = {{"BORDER", {"ON", "OFF"}}}}, // FULLSCREEN [ON|OFF [PLEASE]] — Pantalla completa (F3); OFF bloqueado en kiosk sin PLEASE {.keyword = "FULLSCREEN", .execute = [](const std::vector& args) -> std::string { @@ -279,7 +294,8 @@ static const std::vector COMMANDS = { return std::string("Fullscreen ") + (Options::video.fullscreen ? "ON" : "OFF"); } return "usage: fullscreen [on|off]"; - }}, + }, + .completions = {{"FULLSCREEN", {"ON", "OFF"}}}}, // ZOOM UP/DOWN — Zoom de ventana (F1/F2) {.keyword = "ZOOM", .execute = [](const std::vector& args) -> std::string { @@ -293,7 +309,8 @@ static const std::vector COMMANDS = { return "Zoom " + std::to_string(Options::window.zoom); } return "usage: zoom [up|down]"; - }}, + }, + .completions = {{"ZOOM", {"UP", "DOWN"}}}}, // INTSCALE [ON|OFF] — Escalado entero (F7) {.keyword = "INTSCALE", .execute = [](const std::vector& args) -> std::string { @@ -308,10 +325,12 @@ static const std::vector COMMANDS = { Screen::get()->toggleIntegerScale(); Screen::get()->setVideoMode(Options::video.fullscreen); return std::string("IntScale ") + (Options::video.integer_scale ? "ON" : "OFF"); - }}, + }, + .completions = {{"INTSCALE", {"ON", "OFF"}}}}, // VSYNC [ON|OFF] — Sincronización vertical - {.keyword = "VSYNC", .execute = BOOL_TOGGLE_CMD("VSync", Options::video.vertical_sync, Screen::get()->toggleVSync())}, + {.keyword = "VSYNC", .execute = BOOL_TOGGLE_CMD("VSync", Options::video.vertical_sync, Screen::get()->toggleVSync()), + .completions = {{"VSYNC", {"ON", "OFF"}}}}, // DRIVER [LIST|AUTO|] — Driver GPU (aplica en el próximo arranque) {.keyword = "DRIVER", .execute = [](const std::vector& args) -> std::string { @@ -367,7 +386,8 @@ static const std::vector COMMANDS = { Options::video.gpu_preferred_driver = driver_lower; Options::saveToFile(); return "Driver: " + driver_lower + " (restart)"; - }}, + }, + .completions = {{"DRIVER", {"LIST", "AUTO", "NONE"}}}}, // PALETTE NEXT/PREV/ — Paleta de colores (F5/F6 o por nombre) {.keyword = "PALETTE", .execute = [](const std::vector& args) -> std::string { @@ -405,7 +425,8 @@ static const std::vector COMMANDS = { if (!args.empty()) { return "usage: debug [on|off]"; } GameControl::toggle_debug_mode(); return std::string("Debug mode ") + (Debug::get()->isEnabled() ? "ON" : "OFF"); - }}, + }, + .completions = {{"DEBUG", {"ON", "OFF"}}}}, // ROOM |NEXT|PREV — Cambia a la habitación indicada (1-60); solo en escena GAME {.keyword = "ROOM", .execute = [](const std::vector& args) -> std::string { @@ -431,7 +452,8 @@ static const std::vector COMMANDS = { return std::string("Room: ") + buf; } return std::string("Room not found: ") + buf; - }}, + }, + .completions = {{"ROOM", {"NEXT", "PREV"}}}}, #endif @@ -453,6 +475,13 @@ static const std::vector COMMANDS = { if (RenderInfo::get()->isActive()) { return "Info overlay already ON"; } RenderInfo::get()->toggle(); return "Info overlay ON"; + }, + .completions = { +#ifdef _DEBUG + {"SHOW", {"INFO", "NOTIFICATION", "CHEEVO"}}, +#else + {"SHOW", {"INFO"}}, +#endif }}, // HIDE INFO — disponible en Release @@ -461,7 +490,8 @@ static const std::vector COMMANDS = { if (!RenderInfo::get()->isActive()) { return "Info overlay already OFF"; } RenderInfo::get()->toggle(); return "Info overlay OFF"; - }}, + }, + .completions = {{"HIDE", {"INFO"}}}}, // CHEAT — Trucos de juego; solo en escena GAME; no aparece en ayuda en builds Release {.keyword = "CHEAT", .execute = [](const std::vector& args) -> std::string { @@ -526,7 +556,17 @@ static const std::vector COMMANDS = { return "usage: cheat [infinite lives|invincibility|open the jail|close the jail]"; }, - .hidden = CHEAT_HIDDEN}, + .hidden = CHEAT_HIDDEN, + .completions = { + {"CHEAT", {"INFINITE", "INVINCIBILITY", "OPEN", "CLOSE"}}, + {"CHEAT INFINITE", {"LIVES"}}, + {"CHEAT INFINITE LIVES", {"ON", "OFF"}}, + {"CHEAT INVINCIBILITY", {"ON", "OFF"}}, + {"CHEAT OPEN", {"THE"}}, + {"CHEAT OPEN THE", {"JAIL"}}, + {"CHEAT CLOSE", {"THE"}}, + {"CHEAT CLOSE THE", {"JAIL"}}, + }}, // SET PLAYER SKIN <1|2> — Cambia la skin del jugador (disponible en todos los builds, GAME) // SET INITIAL [ROOM|POS] — Guarda habitación/posición actual como inicio (solo _DEBUG, GAME) @@ -612,6 +652,17 @@ static const std::vector COMMANDS = { return result; #else return "usage: set player skin <1|2>"; +#endif + }, + .completions = { +#ifdef _DEBUG + {"SET", {"PLAYER", "INITIAL", "ITEMS"}}, + {"SET PLAYER", {"SKIN"}}, + {"SET INITIAL", {"ROOM", "POS", "SCENE"}}, + {"SET INITIAL SCENE", {"LOGO", "LOADING", "TITLE", "CREDITS", "GAME", "ENDING", "ENDING2"}}, +#else + {"SET", {"PLAYER"}}, + {"SET PLAYER", {"SKIN"}}, #endif }}, @@ -647,7 +698,8 @@ static const std::vector COMMANDS = { if (args[0] == "ENDING") { return GO_TO(SceneManager::Scene::ENDING, "Ending"); } if (args[0] == "ENDING2") { return GO_TO(SceneManager::Scene::ENDING2, "Ending 2"); } return "Unknown scene: " + args[0]; - }}, + }, + .completions = {{"SCENE", {"LOGO", "LOADING", "TITLE", "CREDITS", "GAME", "ENDING", "ENDING2", "RESTART"}}}}, #endif // RESTART — Reiniciar desde el principio (equivale a SCENE LOGO) @@ -676,7 +728,8 @@ static const std::vector COMMANDS = { return "Kiosk mode ON"; } return "usage: kiosk [on]"; - }}, + }, + .completions = {{"KIOSK", {"ON"}}}}, // AUDIO [ON|OFF|VOL <0-100>] — Audio maestro (estado + volumen) {.keyword = "AUDIO", .execute = [](const std::vector& args) -> std::string { @@ -707,7 +760,8 @@ static const std::vector COMMANDS = { } catch (...) { return "usage: audio vol <0-100>"; } } return "usage: audio [on|off|vol n]"; - }}, + }, + .completions = {{"AUDIO", {"ON", "OFF", "VOL"}}}}, // MUSIC [ON|OFF|VOL <0-100>] — Volumen e interruptor de música {.keyword = "MUSIC", .execute = [](const std::vector& args) -> std::string { @@ -742,7 +796,8 @@ static const std::vector COMMANDS = { } catch (...) { return "usage: music vol <0-100>"; } } return "usage: music [on|off|vol n]"; - }}, + }, + .completions = {{"MUSIC", {"ON", "OFF", "VOL"}}}}, // SOUND [ON|OFF|VOL <0-100>] — Volumen e interruptor de efectos de sonido {.keyword = "SOUND", .execute = [](const std::vector& args) -> std::string { @@ -777,7 +832,8 @@ static const std::vector COMMANDS = { } catch (...) { return "usage: sound vol <0-100>"; } } return "usage: sound [on|off|vol n]"; - }}, + }, + .completions = {{"SOUND", {"ON", "OFF", "VOL"}}}}, // EXIT / QUIT — Cerrar la aplicacion (bloqueado en kiosk) {.keyword = "EXIT", .execute = [](const std::vector& args) -> std::string { @@ -905,6 +961,14 @@ Console::Console(const std::string& font_name) target_height_ = height_; y_ = -height_; + // Construir mapa de autocompletado a partir de COMMANDS + for (const auto& cmd : COMMANDS) { + for (const auto& [path, opts] : cmd.completions) { + auto& vec = tab_completions_[std::string(path)]; + for (const auto& opt : opts) { vec.emplace_back(opt); } + } + } + buildSurface(); } @@ -1151,40 +1215,11 @@ void Console::handleEvent(const SDL_Event& event) { } } } else { - // Modo sub-argumento: completar primer arg del comando base - using SubArgs = std::vector; - using Entry = std::pair; - static const std::vector SUB_ARGS = { - {"SS", {"ON", "OFF", "SIZE", "UPSCALE", "DOWNSCALE"}}, - {"SHADER", {"ON", "OFF", "NEXT", "POSTFX", "CRTPI"}}, - {"BORDER", {"ON", "OFF"}}, - {"FULLSCREEN", {"ON", "OFF"}}, - {"ZOOM", {"UP", "DOWN"}}, - {"INTSCALE", {"ON", "OFF"}}, - {"VSYNC", {"ON", "OFF"}}, - {"DRIVER", {"LIST", "AUTO", "NONE"}}, - {"AUDIO", {"ON", "OFF", "VOL"}}, - {"MUSIC", {"ON", "OFF", "VOL"}}, - {"SOUND", {"ON", "OFF", "VOL"}}, -#ifdef _DEBUG - {"SHOW", {"INFO", "NOTIFICATION", "CHEEVO"}}, - {"SET", {"PLAYER", "INITIAL", "ITEMS"}}, - {"DEBUG", {"ON", "OFF"}}, - {"ROOM", {"NEXT", "PREV"}}, - {"SCENE", {"LOGO", "LOADING", "TITLE", "CREDITS", "GAME", "ENDING", "ENDING2", "RESTART"}}, -#else - {"SHOW", {"INFO"}}, - {"SET", {"PLAYER"}}, -#endif - {"HIDE", {"INFO"}}, - {"KIOSK", {"ON"}}, - {"CHEAT", {"INFINITE", "INVINCIBILITY", "OPEN", "CLOSE"}}, - }; - const std::string base_cmd = upper.substr(0, space_pos); + const std::string base_cmd = upper.substr(0, space_pos); const std::string sub_prefix = upper.substr(space_pos + 1); if (base_cmd == "PALETTE" && Screen::get() != nullptr) { // NEXT/PREV primero, luego todos los nombres de paleta disponibles - for (const auto sv : {"NEXT", "PREV"}) { + 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)); } @@ -1195,14 +1230,12 @@ void Console::handleEvent(const SDL_Event& event) { } } } else { - for (const auto& [keyword, args] : SUB_ARGS) { - if (keyword == base_cmd) { - for (const auto& arg : args) { - if (sub_prefix.empty() || std::string_view{arg}.starts_with(sub_prefix)) { - tab_matches_.emplace_back(std::string(base_cmd) + " " + std::string(arg)); - } + const auto it = tab_completions_.find(base_cmd); + if (it != tab_completions_.end()) { + for (const auto& arg : it->second) { + if (sub_prefix.empty() || std::string_view{arg}.starts_with(sub_prefix)) { + tab_matches_.emplace_back(base_cmd + " " + arg); } - break; } } } diff --git a/source/game/ui/console.hpp b/source/game/ui/console.hpp index e362322..8a88c29 100644 --- a/source/game/ui/console.hpp +++ b/source/game/ui/console.hpp @@ -2,11 +2,12 @@ #include -#include // Para deque (historial) -#include // Para function -#include // Para shared_ptr -#include // Para string -#include // Para vector +#include // Para deque (historial) +#include // Para function +#include // Para shared_ptr +#include // Para string +#include // Para unordered_map (tab_completions_) +#include // Para vector class Surface; class Sprite; @@ -101,4 +102,5 @@ class Console { // Estado de autocompletado (TAB) std::vector tab_matches_; // Comandos que coinciden con el prefijo actual int tab_index_{-1}; // Índice actual en tab_matches_ + std::unordered_map> tab_completions_; // Mapa pre-calculado en constructor };