varies coses i detallets

This commit is contained in:
2026-04-16 18:46:58 +02:00
parent fe41919e1e
commit 6394e9afab
17 changed files with 513 additions and 156 deletions

59
.gitignore vendored
View File

@@ -1,8 +1,57 @@
# --- Build outputs ---
build/
dist/
aee aee
aee.exe aee.exe
.DS_Store *.o
trick.ini *.obj
.vscode/ *.exe
*.app
# --- Generated assets ---
resource.pack
data.jrf data.jrf
build/
dist/ # --- Runtime / debug junk ---
trick.ini
*.log
*.dmp
# --- Editor / IDE ---
.vscode/
.idea/
*.swp
*.swo
*~
.cache/
compile_commands.json
# --- macOS ---
.DS_Store
.AppleDouble
.LSOverride
._*
.Spotlight-V100
.Trashes
.fseventsd
.DocumentRevisions-V100
.TemporaryItems
.VolumeIcon.icns
Icon?
# --- Windows ---
Thumbs.db
Thumbs.db:encryptable
ehthumbs.db
ehthumbs_vista.db
Desktop.ini
desktop.ini
$RECYCLE.BIN/
*.lnk
# --- Linux ---
*~
.fuse_hidden*
.directory
.Trash-*
.nfs*

View File

@@ -113,9 +113,10 @@ Flat C-style APIs (no classes), prefixed by subsystem. Being progressively conve
### Input Layer (`source/core/input/`) ### Input Layer (`source/core/input/`)
- **GlobalInputs** (`global_inputs.hpp/cpp`) — Maps configurable function keys to presentation actions. Uses debounce. Returns whether a key was consumed (to suppress from game layer) - **KeyConfig** (`key_config.hpp/cpp`) — **Font única de veritat per a les tecles d'UI/sistema**. Carrega `data/input/keys.yaml` al boot (12 entrades: F1-F10 GlobalInputs + F11 pausa + F12 menú de servei) i opcionalment aplica overrides des de `~/.config/jailgames/aee/keys.yaml`. Exposa `KeyConfig::scancode("id")`, `scancodePtr("id")` (per a Menu KeyBind), `setScancode(...)`, `isGuiKey(sc)` (filtre del Director per a no propagar tecles d'UI a `JI_AnyKey`). `saveOverrides()` només persistix les entrades que difereixen del default. Les tecles de moviment del jugador NO viuen ací — es queden a `Options::keys_game`
- **GlobalInputs** (`global_inputs.hpp/cpp`) — Maps function keys (via `KeyConfig::scancode("dec_zoom")`, etc.) to presentation actions. Uses debounce. Returns whether a key was consumed (to suppress from game layer)
- **Mouse** (`mouse.hpp/cpp`) — Auto-hides cursor after 3 seconds of inactivity - **Mouse** (`mouse.hpp/cpp`) — Auto-hides cursor after 3 seconds of inactivity
- **Gamepad** (`gamepad.hpp/cpp`) — First-gamepad support with hot-plug. Poll-based each frame: D-pad/left stick (deadzone 12000) → virtual arrow keys for game movement; A/B buttons, Start, Back translate to synthetic SDL key events (F12/ESC/Enter/Backspace) when menu is open, so Director handles them exactly like keyboard. Loads extra mappings from `gamecontrollerdb.txt` (next to the executable) at init via `SDL_AddGamepadMappingsFromFile`, extending SDL's built-in controller database - **Gamepad** (`gamepad.hpp/cpp`) — First-gamepad support with hot-plug + overlay notification with controller name. Poll-based each frame: D-pad/left stick (deadzone 12000) → virtual arrow keys for game movement. Mapeig: SOUTH/EAST/WEST/NORTH (4 botons frontals) → Enter sintètic per avançar escenes; al menú EAST=accept, SOUTH=cancel/back. SELECT → menu_toggle (servei), START → pause_toggle (via `KeyConfig::scancode(...)`). Loads extra mappings from `gamecontrollerdb.txt` at init via `SDL_AddGamepadMappingsFromFile`
- **KeyRemap** (`key_remap.hpp/cpp`) — Each frame, reads `Options::keys_game.*` and mirrors physical keyboard state to virtual standard scancodes (`SDL_SCANCODE_UP`/DOWN/LEFT/RIGHT). Allows full movement key remapping without touching hardcoded game code in `prota.cpp`/`mapa.cpp` - **KeyRemap** (`key_remap.hpp/cpp`) — Each frame, reads `Options::keys_game.*` and mirrors physical keyboard state to virtual standard scancodes (`SDL_SCANCODE_UP`/DOWN/LEFT/RIGHT). Allows full movement key remapping without touching hardcoded game code in `prota.cpp`/`mapa.cpp`
### Locale Layer (`source/core/locale/`) ### Locale Layer (`source/core/locale/`)
@@ -127,8 +128,8 @@ Flat C-style APIs (no classes), prefixed by subsystem. Being progressively conve
Follows the pattern from `jaildoctors_dilemma`, persists to YAML: Follows the pattern from `jaildoctors_dilemma`, persists to YAML:
- **defines.hpp** — Game constants: `Texts::WINDOW_TITLE`, `Texts::VERSION`, `GameScreen::WIDTH/HEIGHT` - **defines.hpp** — Game constants: `Texts::WINDOW_TITLE`, `Texts::VERSION`, `GameScreen::WIDTH/HEIGHT`
- **defaults.hpp** — Default values: `Defaults::KeysGUI`, `Defaults::KeysGame`, `Defaults::Video`, `Defaults::Audio`, `Defaults::Window`, `Defaults::Game` - **defaults.hpp** — Default values: `Defaults::KeysGame`, `Defaults::Video`, `Defaults::Audio`, `Defaults::Window`, `Defaults::Game`. (Les tecles d'UI viuen a `data/input/keys.yaml` via `KeyConfig`)
- **options.hpp/cpp** — `Options` namespace with inline globals and YAML load/save. Structs: `KeysGUI`, `KeysGame`, `Video`, `RenderInfo`, `Audio`, `Window`, `Game`, `PostFXPreset`, `CrtPiPreset` - **options.hpp/cpp** — `Options` namespace with inline globals and YAML load/save. Structs: `KeysGame`, `Video`, `RenderInfo`, `Audio`, `Window`, `Game`, `PostFXPreset`, `CrtPiPreset`
### Utilities (`source/utils/`) ### Utilities (`source/utils/`)
@@ -155,7 +156,7 @@ Follows the pattern from `jaildoctors_dilemma`, persists to YAML:
| Backspace | Go up one menu level / close menu if at root | | Backspace | Go up one menu level / close menu if at root |
| ↑↓←→ / Enter | Menu navigation | | ↑↓←→ / Enter | Menu navigation |
All key bindings are configurable via `Options::keys_gui` and stored in `config.yaml` (section `controls:` with SDL scancode names). Game movement keys (`Options::keys_game.up/down/left/right`) can be remapped via the CONTROLS submenu — the `KeyRemap` module mirrors custom physical keys to virtual standard scancodes so hardcoded game code keeps working. UI/system key bindings are loaded from [data/input/keys.yaml](data/input/keys.yaml) via `KeyConfig`. Overrides fets des del menú es persistixen a `~/.config/jailgames/aee/keys.yaml` (només les que difereixen del default). Game movement keys (`Options::keys_game.up/down/left/right`) viuen separadament a `config.yaml` (secció `controls:`) i es remapejen via la CONTROLS submenu — el `KeyRemap` module mirrors custom physical keys to virtual standard scancodes so hardcoded game code keeps working.
### Execution Model (Single-threaded, Scene-based) ### Execution Model (Single-threaded, Scene-based)
@@ -224,7 +225,8 @@ JD8_Flip produces ABGR byte order: `0xFF000000 + R + (G<<8) + (B<<16)`. SDL text
| File | Content | | File | Content |
|------|---------| |------|---------|
| `~/.config/jailgames/aee/config.yaml` | Main config: video (incl. `vsync`, `integer_scale`), audio (incl. `enabled` master + `music_*` + `sound_*`), window, render_info (incl. `show_time`), game, shader selection, controls (movement keys + menu_toggle + pause_toggle) | | `~/.config/jailgames/aee/config.yaml` | Main config: video (incl. `vsync`, `integer_scale`), audio (incl. `enabled` master + `music_*` + `sound_*`), window, render_info (incl. `show_time`), game, shader selection, controls (només moviment del jugador) |
| `~/.config/jailgames/aee/keys.yaml` | UI key overrides (només entrades que difereixen del default de [data/input/keys.yaml](data/input/keys.yaml)). Generat per `KeyConfig::saveOverrides()` |
| `~/.config/jailgames/aee/postfx.yaml` | PostFX shader presets (6 defaults: CRT, NTSC, CURVED, SCANLINES, SUBTLE, CRT LIVE) | | `~/.config/jailgames/aee/postfx.yaml` | PostFX shader presets (6 defaults: CRT, NTSC, CURVED, SCANLINES, SUBTLE, CRT LIVE) |
| `~/.config/jailgames/aee/crtpi.yaml` | CRT-Pi shader presets (4 defaults: DEFAULT, CURVED, SHARP, MINIMAL) | | `~/.config/jailgames/aee/crtpi.yaml` | CRT-Pi shader presets (4 defaults: DEFAULT, CURVED, SHARP, MINIMAL) |

View File

@@ -38,6 +38,7 @@ set(APP_SOURCES
# Core - Input (nova capa) # Core - Input (nova capa)
source/core/input/gamepad.cpp source/core/input/gamepad.cpp
source/core/input/global_inputs.cpp source/core/input/global_inputs.cpp
source/core/input/key_config.cpp
source/core/input/key_remap.cpp source/core/input/key_remap.cpp
source/core/input/mouse.cpp source/core/input/mouse.cpp
@@ -241,6 +242,27 @@ if(NOT EMSCRIPTEN)
) )
target_include_directories(pack_resources PRIVATE "${CMAKE_SOURCE_DIR}/source") target_include_directories(pack_resources PRIVATE "${CMAKE_SOURCE_DIR}/source")
target_compile_options(pack_resources PRIVATE -Wall) target_compile_options(pack_resources PRIVATE -Wall)
# --- Regeneració automàtica de resource.pack ---
# Cada `cmake --build build` torna a empaquetar `data/` si algun fitxer ha
# canviat. Evita debugar amb un pack obsolet. CONFIGURE_DEPENDS força CMake
# a re-globbar a la pròxima invocació (recull fitxers nous afegits a data/).
file(GLOB_RECURSE DATA_FILES CONFIGURE_DEPENDS "${CMAKE_SOURCE_DIR}/data/*")
set(RESOURCE_PACK "${CMAKE_SOURCE_DIR}/resource.pack")
add_custom_command(
OUTPUT ${RESOURCE_PACK}
COMMAND $<TARGET_FILE:pack_resources>
"${CMAKE_SOURCE_DIR}/data"
"${RESOURCE_PACK}"
DEPENDS pack_resources ${DATA_FILES}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
COMMENT "Empaquetant data/ → resource.pack"
VERBATIM
)
add_custom_target(resource_pack ALL DEPENDS ${RESOURCE_PACK})
add_dependencies(${PROJECT_NAME} resource_pack)
endif() endif()
# --- CLANG-FORMAT TARGETS --- # --- CLANG-FORMAT TARGETS ---

50
data/input/keys.yaml Normal file
View File

@@ -0,0 +1,50 @@
# Aventures En Egipte — Configuració de tecles d'UI
#
# Font única de veritat per a les tecles de funció / sistema.
# Les tecles de moviment del jugador viuen separades a config.yaml (secció `controls:`).
#
# Si l'usuari remapeja alguna tecla des del menú de servei, la diferència respecte
# aquests valors per defecte es persistix a ~/.config/jailgames/aee/keys.yaml.
#
# Camps:
# id - Identificador usat des del codi via KeyConfig::scancode("id")
# code - Nom SDL del scancode (per SDL_GetScancodeFromName), p.ex. "F1", "Escape"
# desc - Descripció curta (per a HELP / overlays futurs)
keys:
- id: dec_zoom
code: "F1"
desc: "Redueix el zoom de la finestra"
- id: inc_zoom
code: "F2"
desc: "Augmenta el zoom de la finestra"
- id: fullscreen
code: "F3"
desc: "Pantalla completa"
- id: toggle_shader
code: "F4"
desc: "Activa/desactiva shaders"
- id: toggle_aspect_ratio
code: "F5"
desc: "Aspecte 4:3 / pixels quadrats"
- id: toggle_supersampling
code: "F6"
desc: "Activa/desactiva supersampling"
- id: next_shader
code: "F7"
desc: "Tipus de shader (PostFX / CRT-Pi)"
- id: next_shader_preset
code: "F8"
desc: "Pròxim preset del shader"
- id: toggle_stretch_filter
code: "F9"
desc: "Filtre 4:3 (nearest / linear)"
- id: toggle_render_info
code: "F10"
desc: "Mostra info de renderitzat"
- id: pause_toggle
code: "F11"
desc: "Pausa el joc"
- id: menu_toggle
code: "F12"
desc: "Menú de servei"

View File

@@ -4,77 +4,78 @@
menu: menu:
titles: titles:
root: "OPCIONS" root: "Opcions"
video: "VIDEO" video: "Vídeo"
audio: "AUDIO" audio: "Àudio"
controls: "CONTROLS" controls: "Controls"
game: "JOC" game: "Joc"
items: items:
video: "VIDEO" video: "Vídeo"
audio: "AUDIO" audio: "Àudio"
controls: "CONTROLS" controls: "Controls"
game: "JOC" game: "Joc"
use_new_logo: "LOGO NOU" use_new_logo: "Logo nou"
show_title_credits: "CREDITS DEL PORT" show_title_credits: "Crèdits del port"
zoom: "ZOOM" zoom: "Zoom"
screen: "PANTALLA" screen: "Pantalla"
shader: "SHADER" shader: "Shader"
aspect_4_3: "ASPECTE 4:3" aspect_4_3: "Aspecte 4:3"
supersampling: "SUPERSAMPLING" supersampling: "Supersampling"
vsync: "VSYNC" vsync: "Vsync"
integer_scale: "ESCALA ENTERA" integer_scale: "Escala entera"
shader_type: "TIPUS SHADER" shader_type: "Tipus shader"
preset: "PRESET" preset: "Preset"
stretch_filter: "FILTRE 4:3" stretch_filter: "Filtre 4:3"
render_info: "RENDER INFO" render_info: "Render info"
uptime: "TEMPS DE JOC" uptime: "Temps de joc"
master_enable: "AUDIO" master_enable: "Àudio"
master_volume: "MASTER" master_volume: "Màster"
music: "MUSICA" music: "Música"
music_volume: "VOL MUSICA" music_volume: "Vol música"
sounds: "SONS" sounds: "Sons"
sounds_volume: "VOL SONS" sounds_volume: "Vol sons"
move_up: "MOU AMUNT" move_up: "Mou amunt"
move_down: "MOU AVALL" move_down: "Mou avall"
move_left: "MOU ESQUERRA" move_left: "Mou esquerra"
move_right: "MOU DRETA" move_right: "Mou dreta"
menu_key: "TECLA MENU" menu_key: "Tecla menú"
values: values:
"yes": "SI" "yes": "Sí"
"no": "NO" "no": "No"
"on": "ON" "on": "On"
"off": "OFF" "off": "Off"
fullscreen: "COMPLETA" fullscreen: "Completa"
windowed: "FINESTRA" windowed: "Finestra"
linear: "LINEAR" linear: "Linear"
nearest: "NEAREST" nearest: "Nearest"
top: "TOP" top: "Top"
bottom: "BOTTOM" bottom: "Bottom"
press_key: "<PREM TECLA>" press_key: "<Prem tecla>"
empty: "(BUIT)" empty: "(Buit)"
unknown: "---" unknown: "---"
window: window:
title: "© 2000 Aventures en Egipte — JailDesigner" title: "© 2000 Aventures en Egipte — JailDesigner"
notifications: notifications:
exit_double_esc: "TORNA A PULSAR ESC PER EIXIR" exit_double_esc: "Torna a pulsar ESC per a eixir"
zoom_fmt: "ZOOM %dX" zoom_fmt: "Zoom %dX"
fullscreen: "PANTALLA COMPLETA" fullscreen: "Pantalla completa"
windowed: "FINESTRA" windowed: "Finestra"
shader_on: "SHADER ON" shader_on: "Shader on"
shader_off: "SHADER OFF" shader_off: "Shader off"
aspect_43: "4:3 CRT" aspect_43: "4:3 CRT"
aspect_square: "PIXELS QUADRATS" aspect_square: "Píxels quadrats"
ss_on: "SUPERSAMPLING ON" ss_on: "Supersampling on"
ss_off: "SUPERSAMPLING OFF" ss_off: "Supersampling off"
preset_fmt: "PRESET: %s" preset_fmt: "Preset: %s"
filter_linear: "FILTRE: LINEAR" filter_linear: "Filtre: linear"
filter_nearest: "FILTRE: NEAREST" filter_nearest: "Filtre: nearest"
pause: "PAUSA" pause: "Pausa"
resume: "REPRES" gamepad_connected: "connectat"
gamepad_disconnected: "desconnectat"
credits: credits:
port_role: "Conversio a C++ i SDL3" port_role: "Conversio a C++ i SDL3"

Binary file not shown.

View File

@@ -1,10 +1,13 @@
#include "core/input/gamepad.hpp" #include "core/input/gamepad.hpp"
#include <cstdio> #include <cstdio>
#include <string>
#include "core/input/key_config.hpp"
#include "core/jail/jinput.hpp" #include "core/jail/jinput.hpp"
#include "core/locale/locale.hpp"
#include "core/rendering/menu.hpp" #include "core/rendering/menu.hpp"
#include "game/options.hpp" #include "core/rendering/overlay.hpp"
namespace Gamepad { namespace Gamepad {
@@ -19,11 +22,23 @@ namespace Gamepad {
static bool prev_down_ = false; static bool prev_down_ = false;
static bool prev_left_ = false; static bool prev_left_ = false;
static bool prev_right_ = false; static bool prev_right_ = false;
static bool prev_a_ = false; static bool prev_south_ = false;
static bool prev_b_ = false; static bool prev_east_ = false;
static bool prev_west_ = false;
static bool prev_north_ = false;
static bool prev_start_ = false; static bool prev_start_ = false;
static bool prev_back_ = false; static bool prev_back_ = false;
static void notify(const char* name, const char* status_key) {
std::string msg = (name && *name) ? name : "Gamepad";
msg += ' ';
msg += Locale::get(status_key);
Overlay::showNotification(msg.c_str(), 2.5F);
}
static void notifyConnected(const char* name) { notify(name, "notifications.gamepad_connected"); }
static void notifyDisconnected(const char* name) { notify(name, "notifications.gamepad_disconnected"); }
static void openFirstGamepad() { static void openFirstGamepad() {
int count = 0; int count = 0;
SDL_JoystickID* ids = SDL_GetGamepads(&count); SDL_JoystickID* ids = SDL_GetGamepads(&count);
@@ -70,12 +85,16 @@ namespace Gamepad {
pad_ = SDL_OpenGamepad(event.gdevice.which); pad_ = SDL_OpenGamepad(event.gdevice.which);
if (pad_) { if (pad_) {
pad_id_ = event.gdevice.which; pad_id_ = event.gdevice.which;
SDL_Log("Gamepad connectat: %s", SDL_GetGamepadName(pad_)); const char* name = SDL_GetGamepadName(pad_);
SDL_Log("Gamepad connectat: %s", name ? name : "");
notifyConnected(name);
} }
} }
} else if (event.type == SDL_EVENT_GAMEPAD_REMOVED) { } else if (event.type == SDL_EVENT_GAMEPAD_REMOVED) {
if (pad_ && event.gdevice.which == pad_id_) { if (pad_ && event.gdevice.which == pad_id_) {
SDL_Log("Gamepad desconnectat"); const char* name = SDL_GetGamepadName(pad_);
std::string saved_name = name ? name : "";
SDL_Log("Gamepad desconnectat: %s", saved_name.c_str());
SDL_CloseGamepad(pad_); SDL_CloseGamepad(pad_);
pad_ = nullptr; pad_ = nullptr;
pad_id_ = 0; pad_id_ = 0;
@@ -84,6 +103,7 @@ namespace Gamepad {
JI_SetVirtualKey(SDL_SCANCODE_DOWN, JI_VSRC_GAMEPAD, false); JI_SetVirtualKey(SDL_SCANCODE_DOWN, JI_VSRC_GAMEPAD, false);
JI_SetVirtualKey(SDL_SCANCODE_LEFT, JI_VSRC_GAMEPAD, false); JI_SetVirtualKey(SDL_SCANCODE_LEFT, JI_VSRC_GAMEPAD, false);
JI_SetVirtualKey(SDL_SCANCODE_RIGHT, JI_VSRC_GAMEPAD, false); JI_SetVirtualKey(SDL_SCANCODE_RIGHT, JI_VSRC_GAMEPAD, false);
notifyDisconnected(saved_name.c_str());
} }
} }
} }
@@ -125,16 +145,18 @@ namespace Gamepad {
bool lt = dlt || slt; bool lt = dlt || slt;
bool rt = drt || srt; bool rt = drt || srt;
// Botons // Botons frontals (layout SDL: SOUTH=A/Cross, EAST=B/Circle, WEST=X/Square, NORTH=Y/Triangle)
bool a = SDL_GetGamepadButton(pad_, SDL_GAMEPAD_BUTTON_SOUTH); // A/Cross bool south = SDL_GetGamepadButton(pad_, SDL_GAMEPAD_BUTTON_SOUTH);
bool b = SDL_GetGamepadButton(pad_, SDL_GAMEPAD_BUTTON_EAST); // B/Circle bool east = SDL_GetGamepadButton(pad_, SDL_GAMEPAD_BUTTON_EAST);
bool west = SDL_GetGamepadButton(pad_, SDL_GAMEPAD_BUTTON_WEST);
bool north = SDL_GetGamepadButton(pad_, SDL_GAMEPAD_BUTTON_NORTH);
bool start = SDL_GetGamepadButton(pad_, SDL_GAMEPAD_BUTTON_START); bool start = SDL_GetGamepadButton(pad_, SDL_GAMEPAD_BUTTON_START);
bool back = SDL_GetGamepadButton(pad_, SDL_GAMEPAD_BUTTON_BACK); bool back = SDL_GetGamepadButton(pad_, SDL_GAMEPAD_BUTTON_BACK);
// Start → obre/tanca menú (flanc) // Select (Back) → obre/tanca menú de servei (flanc)
if (start && !prev_start_) pushKey(Options::keys_gui.menu_toggle); if (back && !prev_back_) pushKey(KeyConfig::scancode("menu_toggle"));
// Back → ESC (flanc) // Start → pausa (flanc)
if (back && !prev_back_) pushKey(SDL_SCANCODE_ESCAPE); if (start && !prev_start_) pushKey(KeyConfig::scancode("pause_toggle"));
if (Menu::isOpen()) { if (Menu::isOpen()) {
// Navegació del menú per flanc // Navegació del menú per flanc
@@ -142,8 +164,9 @@ namespace Gamepad {
if (dn && !prev_down_) pushKey(SDL_SCANCODE_DOWN); if (dn && !prev_down_) pushKey(SDL_SCANCODE_DOWN);
if (lt && !prev_left_) pushKey(SDL_SCANCODE_LEFT); if (lt && !prev_left_) pushKey(SDL_SCANCODE_LEFT);
if (rt && !prev_right_) pushKey(SDL_SCANCODE_RIGHT); if (rt && !prev_right_) pushKey(SDL_SCANCODE_RIGHT);
if (a && !prev_a_) pushKey(SDL_SCANCODE_RETURN); // EAST accepta, SOUTH cancela / endarrere
if (b && !prev_b_) pushKey(SDL_SCANCODE_BACKSPACE); if (east && !prev_east_) pushKey(SDL_SCANCODE_RETURN);
if (south && !prev_south_) pushKey(SDL_SCANCODE_BACKSPACE);
// Assegura que el joc no rep tecles de moviment mentre el menú està obert // Assegura que el joc no rep tecles de moviment mentre el menú està obert
JI_SetVirtualKey(SDL_SCANCODE_UP, JI_VSRC_GAMEPAD, false); JI_SetVirtualKey(SDL_SCANCODE_UP, JI_VSRC_GAMEPAD, false);
@@ -156,16 +179,21 @@ namespace Gamepad {
JI_SetVirtualKey(SDL_SCANCODE_DOWN, JI_VSRC_GAMEPAD, dn); JI_SetVirtualKey(SDL_SCANCODE_DOWN, JI_VSRC_GAMEPAD, dn);
JI_SetVirtualKey(SDL_SCANCODE_LEFT, JI_VSRC_GAMEPAD, lt); JI_SetVirtualKey(SDL_SCANCODE_LEFT, JI_VSRC_GAMEPAD, lt);
JI_SetVirtualKey(SDL_SCANCODE_RIGHT, JI_VSRC_GAMEPAD, rt); JI_SetVirtualKey(SDL_SCANCODE_RIGHT, JI_VSRC_GAMEPAD, rt);
// Botó A al joc: emet Enter per avançar seqüències (JI_AnyKey) // Qualsevol dels 4 botons frontals avança escenes (JI_AnyKey via Enter sintètic)
if (a && !prev_a_) pushKey(SDL_SCANCODE_RETURN); if ((south && !prev_south_) || (east && !prev_east_) ||
(west && !prev_west_) || (north && !prev_north_)) {
pushKey(SDL_SCANCODE_RETURN);
}
} }
prev_up_ = up; prev_up_ = up;
prev_down_ = dn; prev_down_ = dn;
prev_left_ = lt; prev_left_ = lt;
prev_right_ = rt; prev_right_ = rt;
prev_a_ = a; prev_south_ = south;
prev_b_ = b; prev_east_ = east;
prev_west_ = west;
prev_north_ = north;
prev_start_ = start; prev_start_ = start;
prev_back_ = back; prev_back_ = back;
} }

View File

@@ -3,6 +3,7 @@
#include <cstdio> #include <cstdio>
#include <string> #include <string>
#include "core/input/key_config.hpp"
#include "core/jail/jinput.hpp" #include "core/jail/jinput.hpp"
#include "core/locale/locale.hpp" #include "core/locale/locale.hpp"
#include "core/rendering/overlay.hpp" #include "core/rendering/overlay.hpp"
@@ -26,7 +27,7 @@ namespace GlobalInputs {
bool consumed = false; bool consumed = false;
// F1 — Reduir zoom // F1 — Reduir zoom
bool dec_zoom = JI_KeyPressed(Options::keys_gui.dec_zoom); bool dec_zoom = JI_KeyPressed(KeyConfig::scancode("dec_zoom"));
if (dec_zoom && !dec_zoom_prev) { if (dec_zoom && !dec_zoom_prev) {
Screen::get()->decZoom(); Screen::get()->decZoom();
char msg[32]; char msg[32];
@@ -37,7 +38,7 @@ namespace GlobalInputs {
dec_zoom_prev = dec_zoom; dec_zoom_prev = dec_zoom;
// F2 — Augmentar zoom // F2 — Augmentar zoom
bool inc_zoom = JI_KeyPressed(Options::keys_gui.inc_zoom); bool inc_zoom = JI_KeyPressed(KeyConfig::scancode("inc_zoom"));
if (inc_zoom && !inc_zoom_prev) { if (inc_zoom && !inc_zoom_prev) {
Screen::get()->incZoom(); Screen::get()->incZoom();
char msg[32]; char msg[32];
@@ -48,7 +49,7 @@ namespace GlobalInputs {
inc_zoom_prev = inc_zoom; inc_zoom_prev = inc_zoom;
// F3 — Toggle pantalla completa // F3 — Toggle pantalla completa
bool fullscreen = JI_KeyPressed(Options::keys_gui.fullscreen); bool fullscreen = JI_KeyPressed(KeyConfig::scancode("fullscreen"));
if (fullscreen && !fullscreen_prev) { if (fullscreen && !fullscreen_prev) {
Screen::get()->toggleFullscreen(); Screen::get()->toggleFullscreen();
Overlay::showNotification(Screen::get()->isFullscreen() ? Locale::get("notifications.fullscreen") : Locale::get("notifications.windowed")); Overlay::showNotification(Screen::get()->isFullscreen() ? Locale::get("notifications.fullscreen") : Locale::get("notifications.windowed"));
@@ -57,7 +58,7 @@ namespace GlobalInputs {
fullscreen_prev = fullscreen; fullscreen_prev = fullscreen;
// F4 — Toggle shaders // F4 — Toggle shaders
bool shader = JI_KeyPressed(Options::keys_gui.toggle_shader); bool shader = JI_KeyPressed(KeyConfig::scancode("toggle_shader"));
if (shader && !shader_prev) { if (shader && !shader_prev) {
Screen::get()->toggleShaders(); Screen::get()->toggleShaders();
Overlay::showNotification(Options::video.shader_enabled ? Locale::get("notifications.shader_on") : Locale::get("notifications.shader_off")); Overlay::showNotification(Options::video.shader_enabled ? Locale::get("notifications.shader_on") : Locale::get("notifications.shader_off"));
@@ -66,7 +67,7 @@ namespace GlobalInputs {
shader_prev = shader; shader_prev = shader;
// F5 — Toggle aspect ratio 4:3 // F5 — Toggle aspect ratio 4:3
bool aspect = JI_KeyPressed(Options::keys_gui.toggle_aspect_ratio); bool aspect = JI_KeyPressed(KeyConfig::scancode("toggle_aspect_ratio"));
if (aspect && !aspect_prev) { if (aspect && !aspect_prev) {
Screen::get()->toggleAspectRatio(); Screen::get()->toggleAspectRatio();
Overlay::showNotification(Options::video.aspect_ratio_4_3 ? Locale::get("notifications.aspect_43") : Locale::get("notifications.aspect_square")); Overlay::showNotification(Options::video.aspect_ratio_4_3 ? Locale::get("notifications.aspect_43") : Locale::get("notifications.aspect_square"));
@@ -75,7 +76,7 @@ namespace GlobalInputs {
aspect_prev = aspect; aspect_prev = aspect;
// F6 — Toggle supersampling // F6 — Toggle supersampling
bool ss = JI_KeyPressed(Options::keys_gui.toggle_supersampling); bool ss = JI_KeyPressed(KeyConfig::scancode("toggle_supersampling"));
if (ss && !ss_prev) { if (ss && !ss_prev) {
Screen::get()->toggleSupersampling(); Screen::get()->toggleSupersampling();
Overlay::showNotification(Options::video.supersampling ? Locale::get("notifications.ss_on") : Locale::get("notifications.ss_off")); Overlay::showNotification(Options::video.supersampling ? Locale::get("notifications.ss_on") : Locale::get("notifications.ss_off"));
@@ -84,7 +85,7 @@ namespace GlobalInputs {
ss_prev = ss; ss_prev = ss;
// F7 — Canviar tipus de shader (PostFX ↔ CrtPi) // F7 — Canviar tipus de shader (PostFX ↔ CrtPi)
bool next_shader = JI_KeyPressed(Options::keys_gui.next_shader); bool next_shader = JI_KeyPressed(KeyConfig::scancode("next_shader"));
if (next_shader && !next_shader_prev) { if (next_shader && !next_shader_prev) {
Screen::get()->nextShaderType(); Screen::get()->nextShaderType();
char msg[64]; char msg[64];
@@ -95,7 +96,7 @@ namespace GlobalInputs {
next_shader_prev = next_shader; next_shader_prev = next_shader;
// F8 — Pròxim preset del shader actiu // F8 — Pròxim preset del shader actiu
bool next_preset = JI_KeyPressed(Options::keys_gui.next_shader_preset); bool next_preset = JI_KeyPressed(KeyConfig::scancode("next_shader_preset"));
if (next_preset && !next_preset_prev) { if (next_preset && !next_preset_prev) {
Screen::get()->nextPreset(); Screen::get()->nextPreset();
char msg[64]; char msg[64];
@@ -106,7 +107,7 @@ namespace GlobalInputs {
next_preset_prev = next_preset; next_preset_prev = next_preset;
// F9 — Toggle filtre d'estirament 4:3 (NEAREST ↔ LINEAR) // F9 — Toggle filtre d'estirament 4:3 (NEAREST ↔ LINEAR)
bool stretch_filter = JI_KeyPressed(Options::keys_gui.toggle_stretch_filter); bool stretch_filter = JI_KeyPressed(KeyConfig::scancode("toggle_stretch_filter"));
if (stretch_filter && !stretch_filter_prev) { if (stretch_filter && !stretch_filter_prev) {
Screen::get()->toggleStretchFilter(); Screen::get()->toggleStretchFilter();
Overlay::showNotification(Options::video.stretch_filter_linear ? Locale::get("notifications.filter_linear") : Locale::get("notifications.filter_nearest")); Overlay::showNotification(Options::video.stretch_filter_linear ? Locale::get("notifications.filter_linear") : Locale::get("notifications.filter_nearest"));
@@ -115,7 +116,7 @@ namespace GlobalInputs {
stretch_filter_prev = stretch_filter; stretch_filter_prev = stretch_filter;
// F10 — Toggle render info (FPS, driver, shader) // F10 — Toggle render info (FPS, driver, shader)
bool render_info = JI_KeyPressed(Options::keys_gui.toggle_render_info); bool render_info = JI_KeyPressed(KeyConfig::scancode("toggle_render_info"));
if (render_info && !render_info_prev) { if (render_info && !render_info_prev) {
Overlay::toggleRenderInfo(); Overlay::toggleRenderInfo();
} }

View File

@@ -0,0 +1,182 @@
#include "core/input/key_config.hpp"
#include <fstream>
#include <iostream>
#include <utility>
#include "core/resources/resource_helper.hpp"
#include "external/fkyaml_node.hpp"
namespace KeyConfig {
namespace {
std::vector<KeyEntry> entries_;
std::unordered_map<std::string, size_t> index_;
std::string overrides_path_;
auto findIndex(const std::string& id) -> size_t {
auto it = index_.find(id);
if (it == index_.end()) return SIZE_MAX;
return it->second;
}
void loadDefaults(const std::string& defaults_resource_path) {
auto buf = ResourceHelper::loadFile(defaults_resource_path);
if (buf.empty()) {
std::cerr << "KeyConfig: no s'ha pogut llegir " << defaults_resource_path << '\n';
return;
}
std::string content(buf.begin(), buf.end());
try {
auto yaml = fkyaml::node::deserialize(content);
if (!yaml.contains("keys")) return;
for (const auto& node : yaml["keys"]) {
KeyEntry entry;
entry.id = node["id"].get_value<std::string>();
entry.code = node["code"].get_value<std::string>();
if (node.contains("desc")) {
entry.desc = node["desc"].get_value<std::string>();
}
SDL_Scancode sc = SDL_GetScancodeFromName(entry.code.c_str());
if (sc == SDL_SCANCODE_UNKNOWN) {
std::cerr << "KeyConfig: scancode desconegut '" << entry.code
<< "' per '" << entry.id << "'\n";
}
entry.scancode = sc;
entry.default_scancode = sc;
index_[entry.id] = entries_.size();
entries_.push_back(std::move(entry));
}
std::cout << "KeyConfig: " << entries_.size() << " tecles carregades de "
<< defaults_resource_path << '\n';
} catch (const fkyaml::exception& e) {
std::cerr << "KeyConfig: error parsejant YAML: " << e.what() << '\n';
}
}
void applyOverrides(const std::string& disk_path) {
std::ifstream file(disk_path);
if (!file.good()) return;
std::string content((std::istreambuf_iterator<char>(file)),
std::istreambuf_iterator<char>());
file.close();
try {
auto yaml = fkyaml::node::deserialize(content);
if (!yaml.contains("overrides")) return;
int applied = 0;
for (const auto& kv : yaml["overrides"].as_map()) {
auto id = kv.first.get_value<std::string>();
auto code = kv.second.get_value<std::string>();
auto idx = findIndex(id);
if (idx == SIZE_MAX) {
std::cerr << "KeyConfig: override per id desconegut '" << id << "'\n";
continue;
}
SDL_Scancode sc = SDL_GetScancodeFromName(code.c_str());
if (sc == SDL_SCANCODE_UNKNOWN) {
std::cerr << "KeyConfig: override amb scancode invàlid '" << code
<< "' per '" << id << "'\n";
continue;
}
entries_[idx].scancode = sc;
entries_[idx].code = code;
applied++;
}
if (applied > 0) {
std::cout << "KeyConfig: aplicats " << applied
<< " overrides de " << disk_path << '\n';
}
} catch (const fkyaml::exception& e) {
std::cerr << "KeyConfig: error parsejant overrides: " << e.what() << '\n';
}
}
} // namespace
void init(const std::string& defaults_resource_path,
const std::string& user_overrides_disk_path) {
entries_.clear();
index_.clear();
overrides_path_ = user_overrides_disk_path;
loadDefaults(defaults_resource_path);
if (!overrides_path_.empty()) {
applyOverrides(overrides_path_);
}
}
void destroy() {
entries_.clear();
index_.clear();
overrides_path_.clear();
}
auto scancode(const std::string& id) -> SDL_Scancode {
auto idx = findIndex(id);
if (idx == SIZE_MAX) return SDL_SCANCODE_UNKNOWN;
return entries_[idx].scancode;
}
auto scancodePtr(const std::string& id) -> SDL_Scancode* {
auto idx = findIndex(id);
if (idx == SIZE_MAX) return nullptr;
return &entries_[idx].scancode;
}
void setScancode(const std::string& id, SDL_Scancode sc) {
auto idx = findIndex(id);
if (idx == SIZE_MAX) return;
entries_[idx].scancode = sc;
const char* name = SDL_GetScancodeName(sc);
entries_[idx].code = (name != nullptr) ? name : "";
}
auto isGuiKey(SDL_Scancode sc) -> bool {
if (sc == SDL_SCANCODE_UNKNOWN) return false;
for (const auto& e : entries_) {
if (e.scancode == sc) return true;
}
return false;
}
auto entries() -> const std::vector<KeyEntry>& {
return entries_;
}
auto saveOverrides() -> bool {
if (overrides_path_.empty()) return false;
// Recull només les entrades remapeades.
std::vector<const KeyEntry*> changed;
for (const auto& e : entries_) {
if (e.scancode != e.default_scancode) changed.push_back(&e);
}
std::ofstream file(overrides_path_);
if (!file.is_open()) {
std::cerr << "KeyConfig: no es pot escriure " << overrides_path_ << '\n';
return false;
}
file << "# AEE - Overrides de tecles d'UI\n";
file << "# Auto-generat. Només llista les tecles modificades respecte\n";
file << "# els valors per defecte de data/input/keys.yaml.\n";
file << "\n";
if (changed.empty()) {
file << "overrides: {}\n";
} else {
file << "overrides:\n";
for (const auto* e : changed) {
file << " " << e->id << ": \"" << e->code << "\"\n";
}
}
return true;
}
} // namespace KeyConfig

View File

@@ -0,0 +1,52 @@
#pragma once
#include <SDL3/SDL.h>
#include <string>
#include <unordered_map>
#include <vector>
// KeyConfig: font única de veritat per a les tecles d'UI/sistema.
//
// Llegeix els valors per defecte des de `data/input/keys.yaml` (recurs read-only)
// i opcionalment aplica overrides des d'un fitxer de l'usuari (per a remapejos
// fets des del menú de servei). Els callers consulten per `id` (ex. "menu_toggle").
//
// Les tecles de moviment del jugador NO viuen ací — es queden a Options::keys_game.
struct KeyEntry {
std::string id;
std::string code; // nom SDL del scancode tal com apareix al YAML
std::string desc;
SDL_Scancode scancode{SDL_SCANCODE_UNKNOWN};
SDL_Scancode default_scancode{SDL_SCANCODE_UNKNOWN};
};
namespace KeyConfig {
// Inicialitza KeyConfig llegint defaults des d'un recurs (via ResourceHelper)
// i opcionalment sobreposant overrides des d'un fitxer de disc.
void init(const std::string& defaults_resource_path,
const std::string& user_overrides_disk_path);
void destroy();
// Consulta el scancode actual associat a un id. Torna SDL_SCANCODE_UNKNOWN si no existix.
[[nodiscard]] auto scancode(const std::string& id) -> SDL_Scancode;
// Punter estable al scancode d'un id — útil per a Menu::ItemKind::KeyBind.
// Torna nullptr si l'id no existix.
[[nodiscard]] auto scancodePtr(const std::string& id) -> SDL_Scancode*;
// Estableix el scancode d'un id. No persistix per si sol — cal cridar saveOverrides().
void setScancode(const std::string& id, SDL_Scancode sc);
// True si el scancode coincidix amb alguna tecla d'UI registrada.
// Usat pel Director per a evitar que tecles d'UI activen `key_pressed_` al joc.
[[nodiscard]] auto isGuiKey(SDL_Scancode sc) -> bool;
// Llistat complet de les entrades (per a HELP / debug / iteració).
[[nodiscard]] auto entries() -> const std::vector<KeyEntry>&;
// Persistix al fitxer d'overrides les entrades que difereixen del default.
// Si no s'ha proporcionat user_overrides_disk_path al init, és no-op.
auto saveOverrides() -> bool;
} // namespace KeyConfig

View File

@@ -6,6 +6,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "core/input/key_config.hpp"
#include "core/locale/locale.hpp" #include "core/locale/locale.hpp"
#include "core/rendering/overlay.hpp" #include "core/rendering/overlay.hpp"
#include "core/rendering/screen.hpp" #include "core/rendering/screen.hpp"
@@ -181,7 +182,7 @@ namespace Menu {
p.items.push_back({Locale::get("menu.items.move_down"), ItemKind::KeyBind, nullptr, nullptr, nullptr, &Options::keys_game.down}); p.items.push_back({Locale::get("menu.items.move_down"), ItemKind::KeyBind, nullptr, nullptr, nullptr, &Options::keys_game.down});
p.items.push_back({Locale::get("menu.items.move_left"), ItemKind::KeyBind, nullptr, nullptr, nullptr, &Options::keys_game.left}); p.items.push_back({Locale::get("menu.items.move_left"), ItemKind::KeyBind, nullptr, nullptr, nullptr, &Options::keys_game.left});
p.items.push_back({Locale::get("menu.items.move_right"), ItemKind::KeyBind, nullptr, nullptr, nullptr, &Options::keys_game.right}); p.items.push_back({Locale::get("menu.items.move_right"), ItemKind::KeyBind, nullptr, nullptr, nullptr, &Options::keys_game.right});
p.items.push_back({Locale::get("menu.items.menu_key"), ItemKind::KeyBind, nullptr, nullptr, nullptr, &Options::keys_gui.menu_toggle}); p.items.push_back({Locale::get("menu.items.menu_key"), ItemKind::KeyBind, nullptr, nullptr, nullptr, KeyConfig::scancodePtr("menu_toggle")});
return p; return p;
} }

View File

@@ -5,6 +5,7 @@
#include "core/input/gamepad.hpp" #include "core/input/gamepad.hpp"
#include "core/input/global_inputs.hpp" #include "core/input/global_inputs.hpp"
#include "core/input/key_config.hpp"
#include "core/input/key_remap.hpp" #include "core/input/key_remap.hpp"
#include "core/input/mouse.hpp" #include "core/input/mouse.hpp"
#include "core/jail/jail_audio.hpp" #include "core/jail/jail_audio.hpp"
@@ -281,17 +282,18 @@ void Director::handleEvent(const SDL_Event& event) {
menu_keys_held_[event.key.scancode] = true; menu_keys_held_[event.key.scancode] = true;
return; return;
} }
// Pausa: F11 (o tecla configurada) pausa/reprén la simulació // Pausa: F11 (o tecla configurada) pausa/reprén la simulació.
// No mostrem notificació — l'indicador persistent "Pausa" a la cantonada
// superior dreta (pintat per Overlay) ja comunica l'estat.
if (event.type == SDL_EVENT_KEY_DOWN && !event.key.repeat && if (event.type == SDL_EVENT_KEY_DOWN && !event.key.repeat &&
event.key.scancode == Options::keys_gui.pause_toggle) { event.key.scancode == KeyConfig::scancode("pause_toggle")) {
togglePause(); togglePause();
Overlay::showNotification(paused_ ? Locale::get("notifications.pause") : Locale::get("notifications.resume"));
menu_keys_held_[event.key.scancode] = true; menu_keys_held_[event.key.scancode] = true;
return; return;
} }
// Menú: F12 (o tecla configurada) obre/tanca el menú flotant // Menú: F12 (o tecla configurada) obre/tanca el menú flotant
if (event.type == SDL_EVENT_KEY_DOWN && !event.key.repeat && if (event.type == SDL_EVENT_KEY_DOWN && !event.key.repeat &&
event.key.scancode == Options::keys_gui.menu_toggle) { event.key.scancode == KeyConfig::scancode("menu_toggle")) {
Menu::toggle(); Menu::toggle();
JI_SetInputBlocked(Menu::isOpen()); JI_SetInputBlocked(Menu::isOpen());
menu_keys_held_[event.key.scancode] = true; menu_keys_held_[event.key.scancode] = true;
@@ -346,19 +348,12 @@ void Director::handleEvent(const SDL_Event& event) {
// quan l'overlay faça timeout // quan l'overlay faça timeout
return; return;
} else { } else {
// Comprova si és una tecla GUI (no passa al joc) // Comprova si és una tecla d'UI registrada (no passa al joc).
// KeyConfig::isGuiKey cobreix totes les tecles GUI a la vegada,
// incloent pause_toggle i menu_toggle (defensa en profunditat:
// aquestes ja s'haurien hagut de menjar al swallow d'amunt).
const auto sc = event.key.scancode; const auto sc = event.key.scancode;
const bool is_gui_key = (sc == Options::keys_gui.dec_zoom || if (!KeyConfig::isGuiKey(sc)) {
sc == Options::keys_gui.inc_zoom ||
sc == Options::keys_gui.fullscreen ||
sc == Options::keys_gui.toggle_shader ||
sc == Options::keys_gui.toggle_aspect_ratio ||
sc == Options::keys_gui.toggle_supersampling ||
sc == Options::keys_gui.next_shader ||
sc == Options::keys_gui.next_shader_preset ||
sc == Options::keys_gui.toggle_stretch_filter ||
sc == Options::keys_gui.toggle_render_info);
if (!is_gui_key) {
key_pressed_ = true; key_pressed_ = true;
JI_moveCheats(sc); JI_moveCheats(sc);
} }

View File

@@ -2,21 +2,7 @@
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
// Tecles GUI (capa de presentació — finestra, zoom, shaders, etc.) // Tecles GUI: viuen a data/input/keys.yaml (font única — KeyConfig).
namespace Defaults::KeysGUI {
constexpr SDL_Scancode DEC_ZOOM = SDL_SCANCODE_F1;
constexpr SDL_Scancode INC_ZOOM = SDL_SCANCODE_F2;
constexpr SDL_Scancode FULLSCREEN = SDL_SCANCODE_F3;
constexpr SDL_Scancode TOGGLE_SHADER = SDL_SCANCODE_F4;
constexpr SDL_Scancode TOGGLE_ASPECT_RATIO = SDL_SCANCODE_F5;
constexpr SDL_Scancode TOGGLE_SUPERSAMPLING = SDL_SCANCODE_F6;
constexpr SDL_Scancode NEXT_SHADER = SDL_SCANCODE_F7;
constexpr SDL_Scancode NEXT_SHADER_PRESET = SDL_SCANCODE_F8;
constexpr SDL_Scancode TOGGLE_STRETCH_FILTER = SDL_SCANCODE_F9;
constexpr SDL_Scancode TOGGLE_RENDER_INFO = SDL_SCANCODE_F10;
constexpr SDL_Scancode PAUSE_TOGGLE = SDL_SCANCODE_F11;
constexpr SDL_Scancode MENU_TOGGLE = SDL_SCANCODE_F12;
} // namespace Defaults::KeysGUI
// Tecles de joc (moviment del personatge, accions) // Tecles de joc (moviment del personatge, accions)
namespace Defaults::KeysGame { namespace Defaults::KeysGame {

View File

@@ -58,7 +58,7 @@ void ModuleGame::onEnter() {
// Arranca el fade-in tick-based. El `PaletteFade` avança un pas (de // Arranca el fade-in tick-based. El `PaletteFade` avança un pas (de
// 32) per cada tick; durant aquesta fase el gameplay no corre, // 32) per cada tick; durant aquesta fase el gameplay no corre,
// només Draw+fade. Substituïx la crida bloquejant `JD8_FadeToPal`. // només Draw+fade. Substituïx la crida bloquejant `JD8_FadeToPal`.
fade_.startFadeTo(JD8_LoadPalette(info::ctx.pepe_activat ? "frames2.gif" : "frames.gif")); fade_.startFadeTo(JD8_LoadPalette(info::ctx.pepe_activat ? "gfx/frames2.gif" : "gfx/frames.gif"));
phase_ = Phase::FadingIn; phase_ = Phase::FadingIn;
} }

View File

@@ -189,8 +189,6 @@ namespace Options {
loadScancodeField(node, "down", keys_game.down); loadScancodeField(node, "down", keys_game.down);
loadScancodeField(node, "left", keys_game.left); loadScancodeField(node, "left", keys_game.left);
loadScancodeField(node, "right", keys_game.right); loadScancodeField(node, "right", keys_game.right);
loadScancodeField(node, "menu_toggle", keys_gui.menu_toggle);
loadScancodeField(node, "pause_toggle", keys_gui.pause_toggle);
} }
} }
@@ -333,15 +331,14 @@ namespace Options {
file << " show_title_credits: " << (game.show_title_credits ? "true" : "false") << "\n"; file << " show_title_credits: " << (game.show_title_credits ? "true" : "false") << "\n";
file << "\n"; file << "\n";
// CONTROLS // CONTROLS — només moviment del jugador. Les tecles d'UI viuen a
file << "# CONTROLS (noms SDL: \"Up\", \"Down\", \"W\", \"Space\", \"F12\", etc.)\n"; // data/input/keys.yaml (defaults) + ~/.config/jailgames/aee/keys.yaml (overrides).
file << "# CONTROLS (noms SDL: \"Up\", \"Down\", \"W\", \"Space\", etc.)\n";
file << "controls:\n"; file << "controls:\n";
file << " up: \"" << SDL_GetScancodeName(keys_game.up) << "\"\n"; file << " up: \"" << SDL_GetScancodeName(keys_game.up) << "\"\n";
file << " down: \"" << SDL_GetScancodeName(keys_game.down) << "\"\n"; file << " down: \"" << SDL_GetScancodeName(keys_game.down) << "\"\n";
file << " left: \"" << SDL_GetScancodeName(keys_game.left) << "\"\n"; file << " left: \"" << SDL_GetScancodeName(keys_game.left) << "\"\n";
file << " right: \"" << SDL_GetScancodeName(keys_game.right) << "\"\n"; file << " right: \"" << SDL_GetScancodeName(keys_game.right) << "\"\n";
file << " menu_toggle: \"" << SDL_GetScancodeName(keys_gui.menu_toggle) << "\"\n";
file << " pause_toggle: \"" << SDL_GetScancodeName(keys_gui.pause_toggle) << "\"\n";
file.close(); file.close();

View File

@@ -8,23 +8,8 @@
namespace Options { namespace Options {
// Tecles GUI (finestra, zoom, shaders) // Tecles de joc (moviment, accions). Les tecles d'UI viuen ara a KeyConfig
struct KeysGUI { // (carregades de data/input/keys.yaml).
SDL_Scancode dec_zoom{Defaults::KeysGUI::DEC_ZOOM};
SDL_Scancode inc_zoom{Defaults::KeysGUI::INC_ZOOM};
SDL_Scancode fullscreen{Defaults::KeysGUI::FULLSCREEN};
SDL_Scancode toggle_shader{Defaults::KeysGUI::TOGGLE_SHADER};
SDL_Scancode toggle_aspect_ratio{Defaults::KeysGUI::TOGGLE_ASPECT_RATIO};
SDL_Scancode toggle_supersampling{Defaults::KeysGUI::TOGGLE_SUPERSAMPLING};
SDL_Scancode next_shader{Defaults::KeysGUI::NEXT_SHADER};
SDL_Scancode next_shader_preset{Defaults::KeysGUI::NEXT_SHADER_PRESET};
SDL_Scancode toggle_stretch_filter{Defaults::KeysGUI::TOGGLE_STRETCH_FILTER};
SDL_Scancode toggle_render_info{Defaults::KeysGUI::TOGGLE_RENDER_INFO};
SDL_Scancode pause_toggle{Defaults::KeysGUI::PAUSE_TOGGLE};
SDL_Scancode menu_toggle{Defaults::KeysGUI::MENU_TOGGLE};
};
// Tecles de joc (moviment, accions)
struct KeysGame { struct KeysGame {
SDL_Scancode up{Defaults::KeysGame::UP}; SDL_Scancode up{Defaults::KeysGame::UP};
SDL_Scancode down{Defaults::KeysGame::DOWN}; SDL_Scancode down{Defaults::KeysGame::DOWN};
@@ -123,7 +108,6 @@ namespace Options {
// --- Variables globals --- // --- Variables globals ---
inline std::string version{}; inline std::string version{};
inline KeysGUI keys_gui{};
inline KeysGame keys_game{}; inline KeysGame keys_game{};
inline Video video{}; inline Video video{};
inline RenderInfo render_info{}; inline RenderInfo render_info{};

View File

@@ -11,6 +11,7 @@
#include <ctime> #include <ctime>
#include <string> #include <string>
#include "core/input/key_config.hpp"
#include "core/jail/jail_audio.hpp" #include "core/jail/jail_audio.hpp"
#include "core/jail/jdraw8.hpp" #include "core/jail/jdraw8.hpp"
#include "core/jail/jfile.hpp" #include "core/jail/jfile.hpp"
@@ -57,6 +58,10 @@ SDL_AppResult SDL_AppInit(void** /*appstate*/, int /*argc*/, char* /*argv*/[]) {
Options::setConfigFile(std::string(file_getconfigfolder()) + "config.yaml"); Options::setConfigFile(std::string(file_getconfigfolder()) + "config.yaml");
Options::loadFromFile(); Options::loadFromFile();
// KeyConfig: defaults des de data/input/keys.yaml + overrides de l'usuari
KeyConfig::init("input/keys.yaml",
std::string(file_getconfigfolder()) + "keys.yaml");
#ifndef NDEBUG #ifndef NDEBUG
// debug.yaml: estat inicial de gameplay per a tests ràpids, // debug.yaml: estat inicial de gameplay per a tests ràpids,
// només en builds de debug. // només en builds de debug.
@@ -119,11 +124,13 @@ void SDL_AppQuit(void* /*appstate*/, SDL_AppResult /*result*/) {
Director::get()->teardown(); Director::get()->teardown();
Options::saveToFile(); Options::saveToFile();
KeyConfig::saveOverrides();
#ifndef NDEBUG #ifndef NDEBUG
Options::saveDebugToFile(); Options::saveDebugToFile();
#endif #endif
Director::destroy(); Director::destroy();
KeyConfig::destroy();
Menu::destroy(); Menu::destroy();
Overlay::destroy(); Overlay::destroy();
JA_Quit(); JA_Quit();