key_config.cpp i keys.yaml per a centralitzar i no hardcodejar tecles
This commit is contained in:
@@ -36,6 +36,7 @@ set(APP_SOURCES
|
|||||||
source/core/input/global_inputs.cpp
|
source/core/input/global_inputs.cpp
|
||||||
source/core/input/input_types.cpp
|
source/core/input/input_types.cpp
|
||||||
source/core/input/input.cpp
|
source/core/input/input.cpp
|
||||||
|
source/core/input/key_config.cpp
|
||||||
source/core/input/mouse.cpp
|
source/core/input/mouse.cpp
|
||||||
|
|
||||||
# Core - Rendering
|
# Core - Rendering
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ assets:
|
|||||||
input:
|
input:
|
||||||
DATA:
|
DATA:
|
||||||
- ${PREFIX}/gamecontrollerdb.txt
|
- ${PREFIX}/gamecontrollerdb.txt
|
||||||
|
- ${PREFIX}/data/input/keys.yaml
|
||||||
|
|
||||||
# SYSTEM
|
# SYSTEM
|
||||||
system:
|
system:
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ categories:
|
|||||||
- keyword: HELP
|
- keyword: HELP
|
||||||
handler: cmd_help
|
handler: cmd_help
|
||||||
description: "Show this help"
|
description: "Show this help"
|
||||||
usage: "HELP [<command>]"
|
usage: "HELP [KEYS [scope]|<command>]"
|
||||||
dynamic_completions: true
|
dynamic_completions: true
|
||||||
|
|
||||||
- keyword: "?"
|
- keyword: "?"
|
||||||
|
|||||||
203
data/input/keys.yaml
Normal file
203
data/input/keys.yaml
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
# Projecte 2026 - Keybinding Configuration
|
||||||
|
# Single source of truth for all key assignments.
|
||||||
|
# Code reads this at startup; HELP KEYS displays it in the console.
|
||||||
|
#
|
||||||
|
# Fields:
|
||||||
|
# id - Identifier used in C++ code (KeyConfig::key("SCOPE", "id"))
|
||||||
|
# key - Display text for HELP KEYS (human-readable)
|
||||||
|
# code - SDL key name for SDL_GetKeyFromName() — omit for mouse/composite entries
|
||||||
|
# desc - Short description (console is 256px wide)
|
||||||
|
# action - (Optional, GLOBAL only) InputAction name to bind via Input::bindKey()
|
||||||
|
|
||||||
|
scopes:
|
||||||
|
- name: GLOBAL
|
||||||
|
keys:
|
||||||
|
- id: zoom_down
|
||||||
|
key: "F1"
|
||||||
|
code: "F1"
|
||||||
|
desc: "zoom down"
|
||||||
|
action: WINDOW_DEC_ZOOM
|
||||||
|
- id: zoom_up
|
||||||
|
key: "F2"
|
||||||
|
code: "F2"
|
||||||
|
desc: "zoom up"
|
||||||
|
action: WINDOW_INC_ZOOM
|
||||||
|
- id: fullscreen
|
||||||
|
key: "F3"
|
||||||
|
code: "F3"
|
||||||
|
desc: "fullscreen"
|
||||||
|
action: TOGGLE_FULLSCREEN
|
||||||
|
- id: shader
|
||||||
|
key: "F4"
|
||||||
|
code: "F4"
|
||||||
|
desc: "shader on/off"
|
||||||
|
action: TOGGLE_POSTFX
|
||||||
|
- id: shader_preset
|
||||||
|
key: "Shift+F4"
|
||||||
|
desc: "next shader preset"
|
||||||
|
- id: shader_type
|
||||||
|
key: "Ctrl+F4"
|
||||||
|
desc: "next shader type"
|
||||||
|
- id: next_palette
|
||||||
|
key: "F5"
|
||||||
|
code: "F5"
|
||||||
|
desc: "next palette"
|
||||||
|
action: NEXT_PALETTE
|
||||||
|
- id: prev_palette
|
||||||
|
key: "Ctrl+F5"
|
||||||
|
desc: "prev palette"
|
||||||
|
- id: palette_sort
|
||||||
|
key: "F6"
|
||||||
|
code: "F6"
|
||||||
|
desc: "palette sort mode"
|
||||||
|
action: NEXT_PALETTE_SORT
|
||||||
|
- id: integer_scale
|
||||||
|
key: "F7"
|
||||||
|
code: "F7"
|
||||||
|
desc: "integer scale"
|
||||||
|
action: TOGGLE_INTEGER_SCALE
|
||||||
|
- id: music
|
||||||
|
key: "F8"
|
||||||
|
code: "F8"
|
||||||
|
desc: "music on/off"
|
||||||
|
action: TOGGLE_MUSIC
|
||||||
|
- id: border
|
||||||
|
key: "F9"
|
||||||
|
code: "F9"
|
||||||
|
desc: "border"
|
||||||
|
action: TOGGLE_BORDER
|
||||||
|
- id: vsync
|
||||||
|
key: "F10"
|
||||||
|
code: "F10"
|
||||||
|
desc: "vsync"
|
||||||
|
action: TOGGLE_VSYNC
|
||||||
|
- id: pause
|
||||||
|
key: "F11"
|
||||||
|
code: "F11"
|
||||||
|
desc: "pause"
|
||||||
|
action: PAUSE
|
||||||
|
- id: info
|
||||||
|
key: "F12"
|
||||||
|
code: "F12"
|
||||||
|
desc: "show info"
|
||||||
|
action: TOGGLE_DEBUG
|
||||||
|
- id: screenshot
|
||||||
|
key: "Ctrl+S"
|
||||||
|
code: "S"
|
||||||
|
desc: "screenshot"
|
||||||
|
action: SCREENSHOT
|
||||||
|
- id: console
|
||||||
|
key: "`"
|
||||||
|
code: "`"
|
||||||
|
desc: "console"
|
||||||
|
action: TOGGLE_CONSOLE
|
||||||
|
- id: quit
|
||||||
|
key: "Esc"
|
||||||
|
code: "Escape"
|
||||||
|
desc: "quit/back"
|
||||||
|
action: EXIT
|
||||||
|
|
||||||
|
- name: EDITOR
|
||||||
|
keys:
|
||||||
|
- id: toggle
|
||||||
|
key: "9"
|
||||||
|
code: "9"
|
||||||
|
desc: "toggle editor"
|
||||||
|
- id: grid
|
||||||
|
key: "G"
|
||||||
|
code: "G"
|
||||||
|
desc: "toggle grid"
|
||||||
|
- id: collision
|
||||||
|
key: "8"
|
||||||
|
code: "8"
|
||||||
|
desc: "draw/collision mode"
|
||||||
|
- id: tile_picker
|
||||||
|
key: "T"
|
||||||
|
code: "T"
|
||||||
|
desc: "tile picker"
|
||||||
|
- id: eraser
|
||||||
|
key: "E"
|
||||||
|
code: "E"
|
||||||
|
desc: "eraser"
|
||||||
|
- id: minimap
|
||||||
|
key: "M"
|
||||||
|
code: "M"
|
||||||
|
desc: "minimap"
|
||||||
|
- id: nav_up
|
||||||
|
key: "Up"
|
||||||
|
code: "Up"
|
||||||
|
desc: "room up"
|
||||||
|
- id: nav_down
|
||||||
|
key: "Down"
|
||||||
|
code: "Down"
|
||||||
|
desc: "room down"
|
||||||
|
- id: nav_left
|
||||||
|
key: "Left"
|
||||||
|
code: "Left"
|
||||||
|
desc: "room left"
|
||||||
|
- id: nav_right
|
||||||
|
key: "Right"
|
||||||
|
code: "Right"
|
||||||
|
desc: "room right"
|
||||||
|
- id: cancel
|
||||||
|
key: "Esc"
|
||||||
|
code: "Escape"
|
||||||
|
desc: "cancel/clear brush"
|
||||||
|
|
||||||
|
- name: MINIMAP
|
||||||
|
keys:
|
||||||
|
- id: numbers
|
||||||
|
key: "N"
|
||||||
|
code: "N"
|
||||||
|
desc: "room numbers"
|
||||||
|
- id: capture
|
||||||
|
key: "S"
|
||||||
|
code: "S"
|
||||||
|
desc: "capture minimap"
|
||||||
|
- id: close_m
|
||||||
|
key: "M"
|
||||||
|
code: "M"
|
||||||
|
desc: "close"
|
||||||
|
- id: close_esc
|
||||||
|
key: "Esc"
|
||||||
|
code: "Escape"
|
||||||
|
desc: "close"
|
||||||
|
|
||||||
|
- name: DEBUG
|
||||||
|
keys:
|
||||||
|
- id: debug_mode
|
||||||
|
key: "0"
|
||||||
|
code: "0"
|
||||||
|
desc: "debug mode"
|
||||||
|
- id: infinite_lives
|
||||||
|
key: "1"
|
||||||
|
code: "1"
|
||||||
|
desc: "infinite lives"
|
||||||
|
- id: invincibility
|
||||||
|
key: "2"
|
||||||
|
code: "2"
|
||||||
|
desc: "invincibility"
|
||||||
|
- id: nav_up
|
||||||
|
key: "W"
|
||||||
|
code: "W"
|
||||||
|
desc: "room up"
|
||||||
|
- id: nav_left
|
||||||
|
key: "A"
|
||||||
|
code: "A"
|
||||||
|
desc: "room left"
|
||||||
|
- id: nav_down
|
||||||
|
key: "S"
|
||||||
|
code: "S"
|
||||||
|
desc: "room down"
|
||||||
|
- id: nav_right
|
||||||
|
key: "D"
|
||||||
|
code: "D"
|
||||||
|
desc: "room right"
|
||||||
|
- id: reload
|
||||||
|
key: "R"
|
||||||
|
code: "R"
|
||||||
|
desc: "reload resources"
|
||||||
|
- id: test_cheevo
|
||||||
|
key: "3"
|
||||||
|
code: "3"
|
||||||
|
desc: "test achievement"
|
||||||
@@ -7,9 +7,9 @@
|
|||||||
|
|
||||||
#include "core/input/input.hpp" // Para Input, InputAction, Input::DO_NOT_ALLOW_REPEAT
|
#include "core/input/input.hpp" // Para Input, InputAction, Input::DO_NOT_ALLOW_REPEAT
|
||||||
#include "core/locale/locale.hpp" // Para Locale
|
#include "core/locale/locale.hpp" // Para Locale
|
||||||
#include "core/rendering/render_info.hpp" // Para RenderInfo
|
#include "core/rendering/render_info.hpp" // Para RenderInfo
|
||||||
#include "core/rendering/screen.hpp" // Para Screen
|
#include "core/rendering/screen.hpp" // Para Screen
|
||||||
#include "core/rendering/screenshot.hpp" // Para Screenshot
|
#include "core/rendering/screenshot.hpp" // Para Screenshot
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
#include "core/system/debug.hpp" // Para Debug (persistencia de render_info en debug.yaml)
|
#include "core/system/debug.hpp" // Para Debug (persistencia de render_info en debug.yaml)
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -27,34 +27,17 @@ auto Input::get() -> Input* { return Input::instance; }
|
|||||||
// Constructor
|
// Constructor
|
||||||
Input::Input(std::string game_controller_db_path)
|
Input::Input(std::string game_controller_db_path)
|
||||||
: gamepad_mappings_file_(std::move(game_controller_db_path)) {
|
: gamepad_mappings_file_(std::move(game_controller_db_path)) {
|
||||||
// Inicializar bindings del teclado
|
// Bindings de gameplay (rebindeables por el jugador vía Options)
|
||||||
|
// Las teclas de sistema (F1-F12, B, S, `, Esc...) se cargan desde keys.yaml
|
||||||
|
// vía KeyConfig::applyGlobalBindings() — NO duplicar aquí.
|
||||||
keyboard_.bindings = {
|
keyboard_.bindings = {
|
||||||
// Movimiento del jugador
|
|
||||||
{Action::LEFT, KeyState{.scancode = SDL_SCANCODE_LEFT}},
|
{Action::LEFT, KeyState{.scancode = SDL_SCANCODE_LEFT}},
|
||||||
{Action::RIGHT, KeyState{.scancode = SDL_SCANCODE_RIGHT}},
|
{Action::RIGHT, KeyState{.scancode = SDL_SCANCODE_RIGHT}},
|
||||||
{Action::JUMP, KeyState{.scancode = SDL_SCANCODE_UP}},
|
{Action::JUMP, KeyState{.scancode = SDL_SCANCODE_UP}},
|
||||||
{Action::DOWN, KeyState{.scancode = SDL_SCANCODE_DOWN}},
|
{Action::DOWN, KeyState{.scancode = SDL_SCANCODE_DOWN}},
|
||||||
|
|
||||||
// Inputs de control
|
|
||||||
{Action::ACCEPT, KeyState{.scancode = SDL_SCANCODE_RETURN}},
|
{Action::ACCEPT, KeyState{.scancode = SDL_SCANCODE_RETURN}},
|
||||||
{Action::CANCEL, KeyState{.scancode = SDL_SCANCODE_ESCAPE}},
|
{Action::CANCEL, KeyState{.scancode = SDL_SCANCODE_ESCAPE}},
|
||||||
{Action::EXIT, KeyState{.scancode = SDL_SCANCODE_ESCAPE}},
|
};
|
||||||
|
|
||||||
// Inputs de sistema
|
|
||||||
{Action::WINDOW_DEC_ZOOM, KeyState{.scancode = SDL_SCANCODE_F1}},
|
|
||||||
{Action::WINDOW_INC_ZOOM, KeyState{.scancode = SDL_SCANCODE_F2}},
|
|
||||||
{Action::TOGGLE_FULLSCREEN, KeyState{.scancode = SDL_SCANCODE_F3}},
|
|
||||||
{Action::TOGGLE_SHADER, KeyState{.scancode = SDL_SCANCODE_F4}},
|
|
||||||
{Action::NEXT_PALETTE, KeyState{.scancode = SDL_SCANCODE_F5}},
|
|
||||||
{Action::NEXT_PALETTE_SORT, KeyState{.scancode = SDL_SCANCODE_F6}},
|
|
||||||
{Action::TOGGLE_INTEGER_SCALE, KeyState{.scancode = SDL_SCANCODE_F7}},
|
|
||||||
{Action::TOGGLE_IN_GAME_MUSIC, KeyState{.scancode = SDL_SCANCODE_F8}},
|
|
||||||
{Action::TOGGLE_BORDER, KeyState{.scancode = SDL_SCANCODE_F9}},
|
|
||||||
{Action::TOGGLE_VSYNC, KeyState{.scancode = SDL_SCANCODE_F10}},
|
|
||||||
{Action::PAUSE, KeyState{.scancode = SDL_SCANCODE_F11}},
|
|
||||||
{Action::TOGGLE_INFO, KeyState{.scancode = SDL_SCANCODE_F12}},
|
|
||||||
{Action::TOGGLE_CONSOLE, KeyState{.scancode = SDL_SCANCODE_GRAVE}},
|
|
||||||
{Action::SCREENSHOT, KeyState{.scancode = SDL_SCANCODE_S}}};
|
|
||||||
|
|
||||||
initSDLGamePad(); // Inicializa el subsistema SDL_INIT_GAMEPAD
|
initSDLGamePad(); // Inicializa el subsistema SDL_INIT_GAMEPAD
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,9 @@ const std::unordered_map<InputAction, std::string> ACTION_TO_STRING = {
|
|||||||
{InputAction::NEXT_PALETTE_SORT, "NEXT_PALETTE_SORT"},
|
{InputAction::NEXT_PALETTE_SORT, "NEXT_PALETTE_SORT"},
|
||||||
{InputAction::TOGGLE_SHADER, "TOGGLE_POSTFX"},
|
{InputAction::TOGGLE_SHADER, "TOGGLE_POSTFX"},
|
||||||
{InputAction::NEXT_SHADER_PRESET, "NEXT_POSTFX_PRESET"},
|
{InputAction::NEXT_SHADER_PRESET, "NEXT_POSTFX_PRESET"},
|
||||||
|
{InputAction::TOGGLE_SUPERSAMPLING, "TOGGLE_SUPERSAMPLING"},
|
||||||
{InputAction::TOGGLE_INFO, "TOGGLE_DEBUG"},
|
{InputAction::TOGGLE_INFO, "TOGGLE_DEBUG"},
|
||||||
|
{InputAction::TOGGLE_CONSOLE, "TOGGLE_CONSOLE"},
|
||||||
{InputAction::SCREENSHOT, "SCREENSHOT"},
|
{InputAction::SCREENSHOT, "SCREENSHOT"},
|
||||||
{InputAction::NONE, "NONE"}};
|
{InputAction::NONE, "NONE"}};
|
||||||
|
|
||||||
@@ -49,7 +51,9 @@ const std::unordered_map<std::string, InputAction> STRING_TO_ACTION = {
|
|||||||
{"NEXT_PALETTE_SORT", InputAction::NEXT_PALETTE_SORT},
|
{"NEXT_PALETTE_SORT", InputAction::NEXT_PALETTE_SORT},
|
||||||
{"TOGGLE_POSTFX", InputAction::TOGGLE_SHADER},
|
{"TOGGLE_POSTFX", InputAction::TOGGLE_SHADER},
|
||||||
{"NEXT_POSTFX_PRESET", InputAction::NEXT_SHADER_PRESET},
|
{"NEXT_POSTFX_PRESET", InputAction::NEXT_SHADER_PRESET},
|
||||||
|
{"TOGGLE_SUPERSAMPLING", InputAction::TOGGLE_SUPERSAMPLING},
|
||||||
{"TOGGLE_DEBUG", InputAction::TOGGLE_INFO},
|
{"TOGGLE_DEBUG", InputAction::TOGGLE_INFO},
|
||||||
|
{"TOGGLE_CONSOLE", InputAction::TOGGLE_CONSOLE},
|
||||||
{"SCREENSHOT", InputAction::SCREENSHOT},
|
{"SCREENSHOT", InputAction::SCREENSHOT},
|
||||||
{"NONE", InputAction::NONE}};
|
{"NONE", InputAction::NONE}};
|
||||||
|
|
||||||
|
|||||||
126
source/core/input/key_config.cpp
Normal file
126
source/core/input/key_config.cpp
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
#include "core/input/key_config.hpp"
|
||||||
|
|
||||||
|
#include <iostream> // Para cerr
|
||||||
|
#include <string> // Para string
|
||||||
|
#include <utility> // Para move
|
||||||
|
#include <vector> // Para vector
|
||||||
|
|
||||||
|
#include "core/input/input.hpp" // Para Input
|
||||||
|
#include "core/input/input_types.hpp" // Para STRING_TO_ACTION
|
||||||
|
#include "core/resources/resource_helper.hpp" // Para Resource::Helper
|
||||||
|
#include "external/fkyaml_node.hpp" // Para fkyaml::node
|
||||||
|
|
||||||
|
// ── Singleton ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
KeyConfig* KeyConfig::instance_ = nullptr;
|
||||||
|
|
||||||
|
void KeyConfig::init(const std::string& yaml_path) {
|
||||||
|
instance_ = new KeyConfig();
|
||||||
|
instance_->load(yaml_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeyConfig::destroy() {
|
||||||
|
delete instance_;
|
||||||
|
instance_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto KeyConfig::get() -> KeyConfig* { return instance_; }
|
||||||
|
|
||||||
|
// ── Carga del YAML ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
void KeyConfig::load(const std::string& yaml_path) {
|
||||||
|
auto file_data = Resource::Helper::loadFile(yaml_path);
|
||||||
|
if (file_data.empty()) {
|
||||||
|
std::cerr << "KeyConfig: Unable to load " << yaml_path << '\n';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string yaml_content(file_data.begin(), file_data.end());
|
||||||
|
fkyaml::node yaml;
|
||||||
|
try {
|
||||||
|
yaml = fkyaml::node::deserialize(yaml_content);
|
||||||
|
} catch (const fkyaml::exception& e) {
|
||||||
|
std::cerr << "KeyConfig: YAML parse error: " << e.what() << '\n';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!yaml.contains("scopes")) { return; }
|
||||||
|
|
||||||
|
for (const auto& scope_node : yaml["scopes"]) {
|
||||||
|
KeyConfigScope scope;
|
||||||
|
scope.name = scope_node["name"].get_value<std::string>();
|
||||||
|
|
||||||
|
if (scope_node.contains("keys")) {
|
||||||
|
for (const auto& key_node : scope_node["keys"]) {
|
||||||
|
KeyEntry entry;
|
||||||
|
entry.id = key_node["id"].get_value<std::string>();
|
||||||
|
entry.display_key = key_node["key"].get_value<std::string>();
|
||||||
|
entry.desc = key_node["desc"].get_value<std::string>();
|
||||||
|
|
||||||
|
// Convertir el nombre SDL a keycode
|
||||||
|
if (key_node.contains("code")) {
|
||||||
|
auto code = key_node["code"].get_value<std::string>();
|
||||||
|
entry.keycode = SDL_GetKeyFromName(code.c_str());
|
||||||
|
if (entry.keycode == SDLK_UNKNOWN) {
|
||||||
|
std::cerr << "KeyConfig: Unknown key name '" << code
|
||||||
|
<< "' for " << scope.name << "." << entry.id << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// InputAction opcional (para scope GLOBAL)
|
||||||
|
if (key_node.contains("action")) {
|
||||||
|
entry.action = key_node["action"].get_value<std::string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
scope.index[entry.id] = scope.entries.size();
|
||||||
|
scope.entries.push_back(std::move(entry));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scope_index_[scope.name] = scopes_.size();
|
||||||
|
scopes_.push_back(std::move(scope));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "KeyConfig: Loaded " << scopes_.size() << " scopes from " << yaml_path << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Consultas ───────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
auto KeyConfig::key(const std::string& scope, const std::string& id) const -> SDL_Keycode {
|
||||||
|
const auto* s = getScope(scope);
|
||||||
|
if (s == nullptr) { return SDLK_UNKNOWN; }
|
||||||
|
auto it = s->index.find(id);
|
||||||
|
if (it == s->index.end()) { return SDLK_UNKNOWN; }
|
||||||
|
return s->entries[it->second].keycode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeyConfig::applyGlobalBindings() const {
|
||||||
|
const auto* global = getScope("GLOBAL");
|
||||||
|
if (global == nullptr) { return; }
|
||||||
|
|
||||||
|
for (const auto& entry : global->entries) {
|
||||||
|
if (entry.action.empty() || entry.keycode == SDLK_UNKNOWN) { continue; }
|
||||||
|
|
||||||
|
auto it = STRING_TO_ACTION.find(entry.action);
|
||||||
|
if (it == STRING_TO_ACTION.end()) {
|
||||||
|
std::cerr << "KeyConfig: Unknown action '" << entry.action << "' for " << entry.id << '\n';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convertir keycode a scancode para el sistema de Input
|
||||||
|
SDL_Scancode scancode = SDL_GetScancodeFromKey(entry.keycode, nullptr);
|
||||||
|
if (scancode != SDL_SCANCODE_UNKNOWN) {
|
||||||
|
Input::get()->bindKey(it->second, scancode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto KeyConfig::getScopes() const -> const std::vector<KeyConfigScope>& {
|
||||||
|
return scopes_;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto KeyConfig::getScope(const std::string& name) const -> const KeyConfigScope* {
|
||||||
|
auto it = scope_index_.find(name);
|
||||||
|
if (it == scope_index_.end()) { return nullptr; }
|
||||||
|
return &scopes_[it->second];
|
||||||
|
}
|
||||||
53
source/core/input/key_config.hpp
Normal file
53
source/core/input/key_config.hpp
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
|
#include <string> // Para string
|
||||||
|
#include <unordered_map> // Para unordered_map
|
||||||
|
#include <vector> // Para vector
|
||||||
|
|
||||||
|
// Entrada de tecla cargada desde keys.yaml
|
||||||
|
struct KeyEntry {
|
||||||
|
std::string id; // Identificador usado en código (ej. "grid", "tile_picker")
|
||||||
|
std::string display_key; // Texto para mostrar en HELP KEYS (ej. "G", "T")
|
||||||
|
std::string desc; // Descripción corta
|
||||||
|
SDL_Keycode keycode{SDLK_UNKNOWN}; // Tecla SDL asignada
|
||||||
|
std::string action; // (Opcional) Nombre de InputAction para scope GLOBAL
|
||||||
|
};
|
||||||
|
|
||||||
|
// Grupo de teclas por ámbito
|
||||||
|
struct KeyConfigScope {
|
||||||
|
std::string name; // "GLOBAL", "EDITOR", "MINIMAP", "DEBUG"
|
||||||
|
std::vector<KeyEntry> entries;
|
||||||
|
std::unordered_map<std::string, size_t> index; // id → posición en entries
|
||||||
|
};
|
||||||
|
|
||||||
|
// Registro centralizado de teclas — fuente única de verdad (keys.yaml)
|
||||||
|
class KeyConfig {
|
||||||
|
public:
|
||||||
|
// Singleton
|
||||||
|
static void init(const std::string& yaml_path);
|
||||||
|
static void destroy();
|
||||||
|
static auto get() -> KeyConfig*;
|
||||||
|
|
||||||
|
// Consulta la SDL_Keycode asignada a un id dentro de un scope
|
||||||
|
[[nodiscard]] auto key(const std::string& scope, const std::string& id) const -> SDL_Keycode;
|
||||||
|
|
||||||
|
// Aplica las teclas del scope GLOBAL al sistema de Input (Input::bindKey)
|
||||||
|
void applyGlobalBindings() const;
|
||||||
|
|
||||||
|
// Acceso a scopes para HELP KEYS y para aplicar bindings globales
|
||||||
|
[[nodiscard]] auto getScopes() const -> const std::vector<KeyConfigScope>&;
|
||||||
|
[[nodiscard]] auto getScope(const std::string& name) const -> const KeyConfigScope*;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static KeyConfig* instance_;
|
||||||
|
|
||||||
|
KeyConfig() = default;
|
||||||
|
~KeyConfig() = default;
|
||||||
|
|
||||||
|
void load(const std::string& yaml_path);
|
||||||
|
|
||||||
|
std::vector<KeyConfigScope> scopes_;
|
||||||
|
std::unordered_map<std::string, size_t> scope_index_; // nombre → posición en scopes_
|
||||||
|
};
|
||||||
@@ -9,9 +9,8 @@
|
|||||||
#include <vector> // Para vector
|
#include <vector> // Para vector
|
||||||
|
|
||||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||||
#include "external/stb_image_write.h" // Para stbi_write_png
|
|
||||||
|
|
||||||
#include "core/rendering/surface.hpp" // Para Surface
|
#include "core/rendering/surface.hpp" // Para Surface
|
||||||
|
#include "external/stb_image_write.h" // Para stbi_write_png
|
||||||
|
|
||||||
namespace Screenshot {
|
namespace Screenshot {
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
|
|
||||||
#include "core/audio/audio.hpp" // Para Audio
|
#include "core/audio/audio.hpp" // Para Audio
|
||||||
#include "core/input/input.hpp" // Para Input, InputAction
|
#include "core/input/input.hpp" // Para Input, InputAction
|
||||||
|
#include "core/input/key_config.hpp" // Para KeyConfig
|
||||||
#include "core/locale/locale.hpp" // Para Locale
|
#include "core/locale/locale.hpp" // Para Locale
|
||||||
#include "core/rendering/render_info.hpp" // Para RenderInfo
|
#include "core/rendering/render_info.hpp" // Para RenderInfo
|
||||||
#include "core/rendering/screen.hpp" // Para Screen
|
#include "core/rendering/screen.hpp" // Para Screen
|
||||||
@@ -186,6 +187,9 @@ Director::Director() {
|
|||||||
Console::init("8bithud");
|
Console::init("8bithud");
|
||||||
Screen::get()->setNotificationsEnabled(true);
|
Screen::get()->setNotificationsEnabled(true);
|
||||||
|
|
||||||
|
// Cargar configuración de teclas desde YAML (fuente única de verdad)
|
||||||
|
KeyConfig::init("data/input/keys.yaml");
|
||||||
|
|
||||||
// Special handling for gamecontrollerdb.txt - SDL needs filesystem path
|
// Special handling for gamecontrollerdb.txt - SDL needs filesystem path
|
||||||
#ifdef RELEASE_BUILD
|
#ifdef RELEASE_BUILD
|
||||||
// In release, construct the path manually (not from Asset which has empty executable_path)
|
// In release, construct the path manually (not from Asset which has empty executable_path)
|
||||||
@@ -196,6 +200,9 @@ Director::Director() {
|
|||||||
Input::init(Resource::List::get()->get("gamecontrollerdb.txt")); // Carga configuración de controles
|
Input::init(Resource::List::get()->get("gamecontrollerdb.txt")); // Carga configuración de controles
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Aplicar teclas globales desde KeyConfig al sistema de Input
|
||||||
|
KeyConfig::get()->applyGlobalBindings();
|
||||||
|
|
||||||
// Aplica las teclas y botones del gamepad configurados desde Options
|
// Aplica las teclas y botones del gamepad configurados desde Options
|
||||||
Input::get()->applyKeyboardBindingsFromOptions();
|
Input::get()->applyKeyboardBindingsFromOptions();
|
||||||
Input::get()->applyGamepadBindingsFromOptions();
|
Input::get()->applyGamepadBindingsFromOptions();
|
||||||
@@ -246,6 +253,7 @@ Director::~Director() {
|
|||||||
Debug::destroy();
|
Debug::destroy();
|
||||||
#endif
|
#endif
|
||||||
Input::destroy();
|
Input::destroy();
|
||||||
|
KeyConfig::destroy();
|
||||||
Console::destroy();
|
Console::destroy();
|
||||||
RenderInfo::destroy();
|
RenderInfo::destroy();
|
||||||
Notifier::destroy();
|
Notifier::destroy();
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
#include <set> // Para set
|
#include <set> // Para set
|
||||||
#include <system_error> // Para std::error_code
|
#include <system_error> // Para std::error_code
|
||||||
|
|
||||||
|
#include "core/input/key_config.hpp" // Para KeyConfig
|
||||||
#include "core/input/mouse.hpp" // Para Mouse
|
#include "core/input/mouse.hpp" // Para Mouse
|
||||||
#include "core/rendering/render_info.hpp" // Para RenderInfo
|
#include "core/rendering/render_info.hpp" // Para RenderInfo
|
||||||
#include "core/rendering/screen.hpp" // Para Screen
|
#include "core/rendering/screen.hpp" // Para Screen
|
||||||
@@ -434,8 +435,10 @@ void MapEditor::render() {
|
|||||||
void MapEditor::handleEvent(const SDL_Event& event) { // NOLINT(readability-function-cognitive-complexity)
|
void MapEditor::handleEvent(const SDL_Event& event) { // NOLINT(readability-function-cognitive-complexity)
|
||||||
// Si el tile picker está abierto, los eventos van a él.
|
// Si el tile picker está abierto, los eventos van a él.
|
||||||
// Excepción: la T lo cierra como toggle (sin tocar el brush).
|
// Excepción: la T lo cierra como toggle (sin tocar el brush).
|
||||||
|
const auto* kc = KeyConfig::get();
|
||||||
|
|
||||||
if (tile_picker_.isOpen()) {
|
if (tile_picker_.isOpen()) {
|
||||||
if (event.type == SDL_EVENT_KEY_DOWN && event.key.key == SDLK_T && static_cast<int>(event.key.repeat) == 0) {
|
if (event.type == SDL_EVENT_KEY_DOWN && event.key.key == kc->key("EDITOR", "tile_picker") && static_cast<int>(event.key.repeat) == 0) {
|
||||||
tile_picker_.close();
|
tile_picker_.close();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -446,7 +449,7 @@ void MapEditor::handleEvent(const SDL_Event& event) { // NOLINT(readability-fun
|
|||||||
// Si el mini mapa está visible, delegar eventos (ESC o M para cerrar)
|
// Si el mini mapa está visible, delegar eventos (ESC o M para cerrar)
|
||||||
if (mini_map_visible_ && mini_map_) {
|
if (mini_map_visible_ && mini_map_) {
|
||||||
if (event.type == SDL_EVENT_KEY_DOWN &&
|
if (event.type == SDL_EVENT_KEY_DOWN &&
|
||||||
(event.key.key == SDLK_ESCAPE || event.key.key == SDLK_M) &&
|
(event.key.key == kc->key("EDITOR", "cancel") || event.key.key == kc->key("EDITOR", "minimap")) &&
|
||||||
static_cast<int>(event.key.repeat) == 0) {
|
static_cast<int>(event.key.repeat) == 0) {
|
||||||
mini_map_visible_ = false;
|
mini_map_visible_ = false;
|
||||||
return;
|
return;
|
||||||
@@ -456,19 +459,19 @@ void MapEditor::handleEvent(const SDL_Event& event) { // NOLINT(readability-fun
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ESC: cancelar eyedropper en progreso (sin tocar el brush previo)
|
// ESC: cancelar eyedropper en progreso (sin tocar el brush previo)
|
||||||
if (event.type == SDL_EVENT_KEY_DOWN && event.key.key == SDLK_ESCAPE && eyedropper_.active) {
|
if (event.type == SDL_EVENT_KEY_DOWN && event.key.key == kc->key("EDITOR", "cancel") && eyedropper_.active) {
|
||||||
eyedropper_.active = false;
|
eyedropper_.active = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ESC: desactivar brush
|
// ESC: desactivar brush
|
||||||
if (event.type == SDL_EVENT_KEY_DOWN && event.key.key == SDLK_ESCAPE && !brush_.isEmpty()) {
|
if (event.type == SDL_EVENT_KEY_DOWN && event.key.key == kc->key("EDITOR", "cancel") && !brush_.isEmpty()) {
|
||||||
brush_.clear();
|
brush_.clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// E: toggle borrador (alterna entre brush vacío y brush 1x1 ERASE)
|
// E: toggle borrador (alterna entre brush vacío y brush 1x1 ERASE)
|
||||||
if (event.type == SDL_EVENT_KEY_DOWN && event.key.key == SDLK_E && static_cast<int>(event.key.repeat) == 0) {
|
if (event.type == SDL_EVENT_KEY_DOWN && event.key.key == kc->key("EDITOR", "eraser") && static_cast<int>(event.key.repeat) == 0) {
|
||||||
if (brush_.width == 1 && brush_.height == 1 && !brush_.tiles.empty() && brush_.tiles[0] == BrushPattern::ERASE) {
|
if (brush_.width == 1 && brush_.height == 1 && !brush_.tiles.empty() && brush_.tiles[0] == BrushPattern::ERASE) {
|
||||||
brush_.clear();
|
brush_.clear();
|
||||||
} else {
|
} else {
|
||||||
@@ -478,13 +481,13 @@ void MapEditor::handleEvent(const SDL_Event& event) { // NOLINT(readability-fun
|
|||||||
}
|
}
|
||||||
|
|
||||||
// M: toggle mini mapa
|
// M: toggle mini mapa
|
||||||
if (event.type == SDL_EVENT_KEY_DOWN && event.key.key == SDLK_M && static_cast<int>(event.key.repeat) == 0) {
|
if (event.type == SDL_EVENT_KEY_DOWN && event.key.key == kc->key("EDITOR", "minimap") && static_cast<int>(event.key.repeat) == 0) {
|
||||||
toggleMiniMap();
|
toggleMiniMap();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 8: alternar entre draw y collision
|
// 8: alternar entre draw y collision
|
||||||
if (event.type == SDL_EVENT_KEY_DOWN && event.key.key == SDLK_8 && static_cast<int>(event.key.repeat) == 0) {
|
if (event.type == SDL_EVENT_KEY_DOWN && event.key.key == kc->key("EDITOR", "collision") && static_cast<int>(event.key.repeat) == 0) {
|
||||||
setEditingCollision(!editing_collision_);
|
setEditingCollision(!editing_collision_);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -492,21 +495,15 @@ void MapEditor::handleEvent(const SDL_Event& event) { // NOLINT(readability-fun
|
|||||||
// Cursores: navegar a habitación adyacente
|
// Cursores: navegar a habitación adyacente
|
||||||
if (event.type == SDL_EVENT_KEY_DOWN && static_cast<int>(event.key.repeat) == 0) {
|
if (event.type == SDL_EVENT_KEY_DOWN && static_cast<int>(event.key.repeat) == 0) {
|
||||||
std::string direction;
|
std::string direction;
|
||||||
switch (event.key.key) {
|
const auto NAV_KEY = event.key.key;
|
||||||
case SDLK_UP:
|
if (NAV_KEY == kc->key("EDITOR", "nav_up")) {
|
||||||
direction = "UP";
|
direction = "UP";
|
||||||
break;
|
} else if (NAV_KEY == kc->key("EDITOR", "nav_down")) {
|
||||||
case SDLK_DOWN:
|
direction = "DOWN";
|
||||||
direction = "DOWN";
|
} else if (NAV_KEY == kc->key("EDITOR", "nav_left")) {
|
||||||
break;
|
direction = "LEFT";
|
||||||
case SDLK_LEFT:
|
} else if (NAV_KEY == kc->key("EDITOR", "nav_right")) {
|
||||||
direction = "LEFT";
|
direction = "RIGHT";
|
||||||
break;
|
|
||||||
case SDLK_RIGHT:
|
|
||||||
direction = "RIGHT";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
if (!direction.empty() && GameControl::get_adjacent_room) {
|
if (!direction.empty() && GameControl::get_adjacent_room) {
|
||||||
std::string adjacent = GameControl::get_adjacent_room(direction);
|
std::string adjacent = GameControl::get_adjacent_room(direction);
|
||||||
@@ -523,7 +520,7 @@ void MapEditor::handleEvent(const SDL_Event& event) { // NOLINT(readability-fun
|
|||||||
}
|
}
|
||||||
|
|
||||||
// T: abrir TilePicker (el cierre con T también se gestiona arriba, antes de delegar al picker)
|
// T: abrir TilePicker (el cierre con T también se gestiona arriba, antes de delegar al picker)
|
||||||
if (event.type == SDL_EVENT_KEY_DOWN && event.key.key == SDLK_T && static_cast<int>(event.key.repeat) == 0) {
|
if (event.type == SDL_EVENT_KEY_DOWN && event.key.key == kc->key("EDITOR", "tile_picker") && static_cast<int>(event.key.repeat) == 0) {
|
||||||
// Deseleccionar entidades
|
// Deseleccionar entidades
|
||||||
selection_.clear();
|
selection_.clear();
|
||||||
|
|
||||||
|
|||||||
@@ -10,9 +10,10 @@
|
|||||||
#include <queue> // Para queue (BFS)
|
#include <queue> // Para queue (BFS)
|
||||||
#include <set> // Para set
|
#include <set> // Para set
|
||||||
|
|
||||||
|
#include "core/input/key_config.hpp" // Para KeyConfig
|
||||||
#include "core/rendering/screen.hpp" // Para Screen
|
#include "core/rendering/screen.hpp" // Para Screen
|
||||||
#include "core/rendering/surface.hpp" // Para Surface
|
|
||||||
#include "core/rendering/screenshot.hpp" // Para Screenshot::save
|
#include "core/rendering/screenshot.hpp" // Para Screenshot::save
|
||||||
|
#include "core/rendering/surface.hpp" // Para Surface
|
||||||
#include "core/rendering/text.hpp" // Para Text (números de room)
|
#include "core/rendering/text.hpp" // Para Text (números de room)
|
||||||
#include "core/resources/resource_cache.hpp" // Para Resource::Cache
|
#include "core/resources/resource_cache.hpp" // Para Resource::Cache
|
||||||
#include "game/gameplay/room.hpp" // Para Room::Data
|
#include "game/gameplay/room.hpp" // Para Room::Data
|
||||||
@@ -365,13 +366,15 @@ void MiniMap::render(const std::string& current_room) {
|
|||||||
// Maneja eventos del minimapa (drag para explorar, click para navegar)
|
// Maneja eventos del minimapa (drag para explorar, click para navegar)
|
||||||
void MiniMap::handleEvent(const SDL_Event& event, const std::string& current_room) {
|
void MiniMap::handleEvent(const SDL_Event& event, const std::string& current_room) {
|
||||||
// Toggle de números de room
|
// Toggle de números de room
|
||||||
if (event.type == SDL_EVENT_KEY_DOWN && event.key.key == SDLK_N && static_cast<int>(event.key.repeat) == 0) {
|
const auto* kc = KeyConfig::get();
|
||||||
|
|
||||||
|
if (event.type == SDL_EVENT_KEY_DOWN && event.key.key == kc->key("MINIMAP", "numbers") && static_cast<int>(event.key.repeat) == 0) {
|
||||||
show_numbers_ = !show_numbers_;
|
show_numbers_ = !show_numbers_;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Captura del minimapa
|
// Captura del minimapa
|
||||||
if (event.type == SDL_EVENT_KEY_DOWN && event.key.key == SDLK_S && static_cast<int>(event.key.repeat) == 0) {
|
if (event.type == SDL_EVENT_KEY_DOWN && event.key.key == kc->key("MINIMAP", "capture") && static_cast<int>(event.key.repeat) == 0) {
|
||||||
if (map_surface_) {
|
if (map_surface_) {
|
||||||
// Renderizar números sobre map_surface_ si están activos
|
// Renderizar números sobre map_surface_ si están activos
|
||||||
if (show_numbers_) {
|
if (show_numbers_) {
|
||||||
|
|||||||
@@ -103,10 +103,10 @@ class MiniMap {
|
|||||||
static constexpr int PADDING = 4; // Padding alrededor del minimapa
|
static constexpr int PADDING = 4; // Padding alrededor del minimapa
|
||||||
|
|
||||||
// Colores del minimapa (índices de paleta)
|
// Colores del minimapa (índices de paleta)
|
||||||
Uint8 bg_color_{2}; // Fondo general (configurable)
|
Uint8 bg_color_{2}; // Fondo general (configurable)
|
||||||
Uint8 conn_color_{14}; // Líneas de conexión (configurable)
|
Uint8 conn_color_{14}; // Líneas de conexión (configurable)
|
||||||
static constexpr Uint8 COLOR_ROOM_BORDER = 0; // Borde de cada miniroom
|
static constexpr Uint8 COLOR_ROOM_BORDER = 0; // Borde de cada miniroom
|
||||||
static constexpr Uint8 COLOR_SHADOW = 1; // Sombra de cada miniroom
|
static constexpr Uint8 COLOR_SHADOW = 1; // Sombra de cada miniroom
|
||||||
static constexpr Uint8 COLOR_NUMBER_TEXT = 15; // Texto de números (blanco)
|
static constexpr Uint8 COLOR_NUMBER_TEXT = 15; // Texto de números (blanco)
|
||||||
static constexpr Uint8 COLOR_NUMBER_SHADOW = 1; // Sombra de números (oscuro)
|
static constexpr Uint8 COLOR_NUMBER_SHADOW = 1; // Sombra de números (oscuro)
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include "core/audio/audio.hpp" // Para Audio
|
#include "core/audio/audio.hpp" // Para Audio
|
||||||
#include "core/input/global_inputs.hpp" // Para check
|
#include "core/input/global_inputs.hpp" // Para check
|
||||||
#include "core/input/input.hpp" // Para Input, InputAction, Input::DO_NOT_ALLOW_REPEAT
|
#include "core/input/input.hpp" // Para Input, InputAction, Input::DO_NOT_ALLOW_REPEAT
|
||||||
|
#include "core/input/key_config.hpp" // Para KeyConfig
|
||||||
#include "core/locale/locale.hpp" // Para Locale
|
#include "core/locale/locale.hpp" // Para Locale
|
||||||
#include "core/rendering/screen.hpp" // Para Screen
|
#include "core/rendering/screen.hpp" // Para Screen
|
||||||
#include "core/rendering/surface.hpp" // Para Surface
|
#include "core/rendering/surface.hpp" // Para Surface
|
||||||
@@ -207,7 +208,7 @@ void Game::handleEvents() {
|
|||||||
|
|
||||||
if (!Console::get()->isActive()) {
|
if (!Console::get()->isActive()) {
|
||||||
// Tecla 9: toggle editor (funciona tanto dentro como fuera del editor)
|
// Tecla 9: toggle editor (funciona tanto dentro como fuera del editor)
|
||||||
if (event.type == SDL_EVENT_KEY_DOWN && event.key.key == SDLK_9 && static_cast<int>(event.key.repeat) == 0) {
|
if (event.type == SDL_EVENT_KEY_DOWN && event.key.key == KeyConfig::get()->key("EDITOR", "toggle") && static_cast<int>(event.key.repeat) == 0) {
|
||||||
if (MapEditor::get()->isActive()) {
|
if (MapEditor::get()->isActive()) {
|
||||||
GameControl::exit_editor();
|
GameControl::exit_editor();
|
||||||
Notifier::get()->show({Locale::get()->get("game.editor_disabled")});
|
Notifier::get()->show({Locale::get()->get("game.editor_disabled")});
|
||||||
@@ -215,7 +216,7 @@ void Game::handleEvents() {
|
|||||||
GameControl::enter_editor();
|
GameControl::enter_editor();
|
||||||
Notifier::get()->show({Locale::get()->get("game.editor_enabled")});
|
Notifier::get()->show({Locale::get()->get("game.editor_enabled")});
|
||||||
}
|
}
|
||||||
} else if (event.type == SDL_EVENT_KEY_DOWN && event.key.key == SDLK_G && static_cast<int>(event.key.repeat) == 0 && MapEditor::get()->isActive()) {
|
} else if (event.type == SDL_EVENT_KEY_DOWN && event.key.key == KeyConfig::get()->key("EDITOR", "grid") && static_cast<int>(event.key.repeat) == 0 && MapEditor::get()->isActive()) {
|
||||||
MapEditor::get()->showGrid(!MapEditor::get()->isGridEnabled());
|
MapEditor::get()->showGrid(!MapEditor::get()->isGridEnabled());
|
||||||
} else if (MapEditor::get()->isActive()) {
|
} else if (MapEditor::get()->isActive()) {
|
||||||
MapEditor::get()->handleEvent(event);
|
MapEditor::get()->handleEvent(event);
|
||||||
@@ -640,59 +641,40 @@ void Game::renderDebugInfo() {
|
|||||||
// Comprueba los eventos
|
// Comprueba los eventos
|
||||||
void Game::handleDebugEvents(const SDL_Event& event) {
|
void Game::handleDebugEvents(const SDL_Event& event) {
|
||||||
if (event.type == SDL_EVENT_KEY_DOWN && static_cast<int>(event.key.repeat) == 0) {
|
if (event.type == SDL_EVENT_KEY_DOWN && static_cast<int>(event.key.repeat) == 0) {
|
||||||
switch (event.key.key) {
|
const auto KEY = event.key.key;
|
||||||
case SDLK_R:
|
const auto* kc = KeyConfig::get();
|
||||||
Resource::Cache::get()->reload();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SDLK_W:
|
if (KEY == kc->key("DEBUG", "reload")) {
|
||||||
changeRoom(room_->getRoom(Room::Border::TOP));
|
Resource::Cache::get()->reload();
|
||||||
break;
|
} else if (KEY == kc->key("DEBUG", "nav_up")) {
|
||||||
|
changeRoom(room_->getRoom(Room::Border::TOP));
|
||||||
case SDLK_A:
|
} else if (KEY == kc->key("DEBUG", "nav_left")) {
|
||||||
changeRoom(room_->getRoom(Room::Border::LEFT));
|
changeRoom(room_->getRoom(Room::Border::LEFT));
|
||||||
break;
|
} else if (KEY == kc->key("DEBUG", "nav_down")) {
|
||||||
|
changeRoom(room_->getRoom(Room::Border::BOTTOM));
|
||||||
case SDLK_S:
|
} else if (KEY == kc->key("DEBUG", "nav_right")) {
|
||||||
changeRoom(room_->getRoom(Room::Border::BOTTOM));
|
changeRoom(room_->getRoom(Room::Border::RIGHT));
|
||||||
break;
|
} else if (KEY == kc->key("DEBUG", "infinite_lives")) {
|
||||||
|
toggleCheat(Options::cheats.infinite_lives, Locale::get()->get("game.cheat_infinite_lives"));
|
||||||
case SDLK_D:
|
} else if (KEY == kc->key("DEBUG", "invincibility")) {
|
||||||
changeRoom(room_->getRoom(Room::Border::RIGHT));
|
toggleCheat(Options::cheats.invincible, Locale::get()->get("game.cheat_invincible"));
|
||||||
break;
|
} else if (KEY == kc->key("DEBUG", "test_cheevo")) {
|
||||||
|
Notifier::get()->show({Locale::get()->get("achievements.header"), Locale::get()->get("achievements.c11")}, Notifier::Style::CHEEVO, -1, false, "F7");
|
||||||
case SDLK_1:
|
} else if (KEY == kc->key("DEBUG", "debug_mode")) {
|
||||||
toggleCheat(Options::cheats.infinite_lives, Locale::get()->get("game.cheat_infinite_lives"));
|
const bool ENTERING_DEBUG = !Debug::get()->isEnabled();
|
||||||
break;
|
if (ENTERING_DEBUG) {
|
||||||
|
invincible_before_debug_ = (Options::cheats.invincible == Options::Cheat::State::ENABLED);
|
||||||
case SDLK_2:
|
|
||||||
toggleCheat(Options::cheats.invincible, Locale::get()->get("game.cheat_invincible"));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SDLK_7:
|
|
||||||
Notifier::get()->show({Locale::get()->get("achievements.header"), Locale::get()->get("achievements.c11")}, Notifier::Style::CHEEVO, -1, false, "F7");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SDLK_0: {
|
|
||||||
const bool ENTERING_DEBUG = !Debug::get()->isEnabled();
|
|
||||||
if (ENTERING_DEBUG) {
|
|
||||||
invincible_before_debug_ = (Options::cheats.invincible == Options::Cheat::State::ENABLED);
|
|
||||||
}
|
|
||||||
Debug::get()->toggleEnabled();
|
|
||||||
Notifier::get()->show({Debug::get()->isEnabled() ? Locale::get()->get("game.debug_enabled") : Locale::get()->get("game.debug_disabled")});
|
|
||||||
room_->redrawMap();
|
|
||||||
if (Debug::get()->isEnabled()) {
|
|
||||||
Options::cheats.invincible = Options::Cheat::State::ENABLED;
|
|
||||||
} else {
|
|
||||||
Options::cheats.invincible = invincible_before_debug_ ? Options::Cheat::State::ENABLED : Options::Cheat::State::DISABLED;
|
|
||||||
}
|
|
||||||
scoreboard_data_->music = !Debug::get()->isEnabled();
|
|
||||||
scoreboard_data_->music ? Audio::get()->resumeMusic() : Audio::get()->pauseMusic();
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
Debug::get()->toggleEnabled();
|
||||||
default:
|
Notifier::get()->show({Debug::get()->isEnabled() ? Locale::get()->get("game.debug_enabled") : Locale::get()->get("game.debug_disabled")});
|
||||||
break;
|
room_->redrawMap();
|
||||||
|
if (Debug::get()->isEnabled()) {
|
||||||
|
Options::cheats.invincible = Options::Cheat::State::ENABLED;
|
||||||
|
} else {
|
||||||
|
Options::cheats.invincible = invincible_before_debug_ ? Options::Cheat::State::ENABLED : Options::Cheat::State::DISABLED;
|
||||||
|
}
|
||||||
|
scoreboard_data_->music = !Debug::get()->isEnabled();
|
||||||
|
scoreboard_data_->music ? Audio::get()->resumeMusic() : Audio::get()->pauseMusic();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ static auto parseTokens(const std::string& input) -> std::vector<std::string> {
|
|||||||
// Calcula la altura total de la consola para N líneas de mensaje (+ 1 línea de input)
|
// Calcula la altura total de la consola para N líneas de mensaje (+ 1 línea de input)
|
||||||
static auto calcTargetHeight(int num_msg_lines) -> float {
|
static auto calcTargetHeight(int num_msg_lines) -> float {
|
||||||
constexpr int PADDING_IN_V = Console::TEXT_SIZE / 2;
|
constexpr int PADDING_IN_V = Console::TEXT_SIZE / 2;
|
||||||
return static_cast<float>((Console::TEXT_SIZE * (num_msg_lines + 1)) + (PADDING_IN_V * 2));
|
return static_cast<float>((Console::LINE_HEIGHT * (num_msg_lines + 1)) + (PADDING_IN_V * 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Divide text en líneas respetando los \n existentes y haciendo word-wrap por ancho en píxeles
|
// Divide text en líneas respetando los \n existentes y haciendo word-wrap por ancho en píxeles
|
||||||
@@ -173,7 +173,7 @@ void Console::redrawText() {
|
|||||||
const int VISIBLE = std::min(remaining, static_cast<int>(line.size()));
|
const int VISIBLE = std::min(remaining, static_cast<int>(line.size()));
|
||||||
text_->writeColored(PADDING_IN_H, y_pos, line.substr(0, VISIBLE), MSG_COLOR);
|
text_->writeColored(PADDING_IN_H, y_pos, line.substr(0, VISIBLE), MSG_COLOR);
|
||||||
remaining -= VISIBLE;
|
remaining -= VISIBLE;
|
||||||
y_pos += TEXT_SIZE;
|
y_pos += LINE_HEIGHT;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Línea de input (siempre la última): prompt en PROMPT_COLOR, comando + cursor en COMMAND_COLOR
|
// Línea de input (siempre la última): prompt en PROMPT_COLOR, comando + cursor en COMMAND_COLOR
|
||||||
|
|||||||
@@ -29,7 +29,8 @@ class Console {
|
|||||||
void handleEvent(const SDL_Event& event);
|
void handleEvent(const SDL_Event& event);
|
||||||
|
|
||||||
// Constantes públicas
|
// Constantes públicas
|
||||||
static constexpr int TEXT_SIZE = 6; // Tamaño de carácter del font de la consola
|
static constexpr int TEXT_SIZE = 6; // Tamaño de carácter del font de la consola
|
||||||
|
static constexpr int LINE_HEIGHT = 7; // TEXT_SIZE + 1px de interlineado
|
||||||
|
|
||||||
// Consultas
|
// Consultas
|
||||||
auto isActive() -> bool; // true si RISING, ACTIVE o VANISHING
|
auto isActive() -> bool; // true si RISING, ACTIVE o VANISHING
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include <vector> // Para vector
|
#include <vector> // Para vector
|
||||||
|
|
||||||
#include "core/audio/audio.hpp" // Para Audio
|
#include "core/audio/audio.hpp" // Para Audio
|
||||||
|
#include "core/input/key_config.hpp" // Para KeyConfig
|
||||||
#include "core/locale/locale.hpp" // Para Locale
|
#include "core/locale/locale.hpp" // Para Locale
|
||||||
#include "core/rendering/render_info.hpp" // Para RenderInfo
|
#include "core/rendering/render_info.hpp" // Para RenderInfo
|
||||||
#include "core/rendering/screen.hpp" // Para Screen
|
#include "core/rendering/screen.hpp" // Para Screen
|
||||||
@@ -1092,7 +1093,9 @@ void CommandRegistry::registerHandlers() { // NOLINT(readability-function-cogni
|
|||||||
|
|
||||||
// HELP: lista de comandos visibles en el scope activo
|
// HELP: lista de comandos visibles en el scope activo
|
||||||
dynamic_providers_["HELP"] = [this]() -> std::vector<std::string> {
|
dynamic_providers_["HELP"] = [this]() -> std::vector<std::string> {
|
||||||
return getVisibleKeywords();
|
auto kws = getVisibleKeywords();
|
||||||
|
kws.insert(kws.begin(), "KEYS");
|
||||||
|
return kws;
|
||||||
};
|
};
|
||||||
dynamic_providers_["SET ITEMCOLOR1"] = color_provider;
|
dynamic_providers_["SET ITEMCOLOR1"] = color_provider;
|
||||||
dynamic_providers_["SET ITEMCOLOR2"] = color_provider;
|
dynamic_providers_["SET ITEMCOLOR2"] = color_provider;
|
||||||
@@ -1237,6 +1240,10 @@ void CommandRegistry::load(const std::string& yaml_path) { // NOLINT(readabilit
|
|||||||
// Registrar el handler de HELP (captura this)
|
// Registrar el handler de HELP (captura this)
|
||||||
handlers_["cmd_help"] = [this](const std::vector<std::string>& args) -> std::string {
|
handlers_["cmd_help"] = [this](const std::vector<std::string>& args) -> std::string {
|
||||||
if (!args.empty()) {
|
if (!args.empty()) {
|
||||||
|
// HELP KEYS [scope]: referencia de atajos de teclado
|
||||||
|
if (args[0] == "KEYS") {
|
||||||
|
return generateKeysHelp(args.size() > 1 ? args[1] : "");
|
||||||
|
}
|
||||||
// HELP <command>: mostrar ayuda detallada de un comando
|
// HELP <command>: mostrar ayuda detallada de un comando
|
||||||
const auto* cmd = findCommand(args[0]);
|
const auto* cmd = findCommand(args[0]);
|
||||||
if (cmd != nullptr) {
|
if (cmd != nullptr) {
|
||||||
@@ -1268,6 +1275,44 @@ void CommandRegistry::load(const std::string& yaml_path) { // NOLINT(readabilit
|
|||||||
completions_map_[path] = opts;
|
completions_map_[path] = opts;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Proveedor dinámico para HELP KEYS <scope> (usa KeyConfig)
|
||||||
|
dynamic_providers_["HELP KEYS"] = []() -> std::vector<std::string> {
|
||||||
|
std::vector<std::string> names;
|
||||||
|
if (KeyConfig::get() != nullptr) {
|
||||||
|
for (const auto& scope : KeyConfig::get()->getScopes()) {
|
||||||
|
names.push_back(scope.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return names;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto CommandRegistry::generateKeysHelp(const std::string& scope_filter) -> std::string {
|
||||||
|
if (KeyConfig::get() == nullptr) { return "KeyConfig not loaded"; }
|
||||||
|
|
||||||
|
// Sin argumento: mostrar solo GLOBAL
|
||||||
|
const std::string FILTER = scope_filter.empty() ? "GLOBAL" : scope_filter;
|
||||||
|
const auto* scope = KeyConfig::get()->getScope(FILTER);
|
||||||
|
|
||||||
|
if (scope == nullptr) {
|
||||||
|
std::string filter_lower = FILTER;
|
||||||
|
std::ranges::transform(filter_lower, filter_lower.begin(), ::tolower);
|
||||||
|
return "Unknown scope: " + filter_lower;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cabecera del scope
|
||||||
|
std::string name_lower = scope->name;
|
||||||
|
std::ranges::transform(name_lower, name_lower.begin(), ::tolower);
|
||||||
|
std::string result = '[' + name_lower + "]\n";
|
||||||
|
|
||||||
|
// Una tecla por línea: key = desc
|
||||||
|
for (size_t i = 0; i < scope->entries.size(); ++i) {
|
||||||
|
const auto& entry = scope->entries[i];
|
||||||
|
result += entry.display_key + " = " + entry.desc;
|
||||||
|
if (i + 1 < scope->entries.size()) { result += '\n'; }
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CommandRegistry::findCommand(const std::string& keyword) const -> const CommandDef* {
|
auto CommandRegistry::findCommand(const std::string& keyword) const -> const CommandDef* {
|
||||||
@@ -1346,7 +1391,6 @@ auto CommandRegistry::generateConsoleHelp() const -> std::string { // NOLINT(re
|
|||||||
|
|
||||||
if (active_scope_ == "editor" && !editor_cmds.empty()) {
|
if (active_scope_ == "editor" && !editor_cmds.empty()) {
|
||||||
result += "Editor:\n" + editor_cmds + "\n";
|
result += "Editor:\n" + editor_cmds + "\n";
|
||||||
result += "keys: 9=editor g=grid 8=collision e=eraser m=map\n";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!debug_cmds.empty()) {
|
if (!debug_cmds.empty()) {
|
||||||
|
|||||||
@@ -58,4 +58,5 @@ class CommandRegistry {
|
|||||||
|
|
||||||
void registerHandlers();
|
void registerHandlers();
|
||||||
[[nodiscard]] auto isCommandVisible(const CommandDef& cmd) const -> bool;
|
[[nodiscard]] auto isCommandVisible(const CommandDef& cmd) const -> bool;
|
||||||
|
[[nodiscard]] static auto generateKeysHelp(const std::string& scope_filter) -> std::string;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user