varies coses i detallets
This commit is contained in:
59
.gitignore
vendored
59
.gitignore
vendored
@@ -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*
|
||||||
|
|||||||
14
CLAUDE.md
14
CLAUDE.md
@@ -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) |
|
||||||
|
|
||||||
|
|||||||
@@ -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
50
data/input/keys.yaml
Normal 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"
|
||||||
@@ -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"
|
||||||
|
|||||||
BIN
resource.pack
BIN
resource.pack
Binary file not shown.
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
182
source/core/input/key_config.cpp
Normal file
182
source/core/input/key_config.cpp
Normal 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
|
||||||
52
source/core/input/key_config.hpp
Normal file
52
source/core/input/key_config.hpp
Normal 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
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|
||||||
|
|||||||
@@ -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{};
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
Reference in New Issue
Block a user