-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathkeymap.h
More file actions
138 lines (119 loc) · 4.99 KB
/
Copy pathkeymap.h
File metadata and controls
138 lines (119 loc) · 4.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
// Universal rebindable keymap — ONE registry governing every game key, across
// BOTH the macroworld and the microworld. The control-scheme twin of
// ui_settings.h: a single table (kActionSpec) drives one Controls panel, one
// persisted prefs file, and the per-action binding everything else reads.
// There is deliberately no per-world input code to keep in lockstep — a
// binding IS a row here, and the worlds differ only by each row's scope.
//
// Bindings are SCANCODES, not keysyms: positional, so a layout change (or a
// non-QWERTY keyboard) never scatters the controls. Esc is the one fixed key —
// it opens the menu and cancels a rebind, so the player can never lock
// themselves out of the way back.
//
// To add an action: add an ActionId enumerator here and one row in kActionSpec
// (keymap.cpp), in the SAME order. It then appears in the Controls panel,
// persists, and can be read at its call-site — with no other code.
#pragma once
#include "ui/ui_settings.h" // UiScope — one scope vocabulary for panels AND keys
#include <SDL_scancode.h>
#include <cstddef>
#include <string>
namespace sm::ui {
// One enumerator per bindable action. Order == display order within a scope.
// `Count` must stay last; a static_assert on the row count guards drift.
enum class ActionId : std::uint8_t {
// Both worlds
Character,
ArmyTab,
SpellsTab,
Codex,
Map,
Quests,
Diplomacy,
Settlement,
EnterLeave,
Save,
Load,
DebugOverlay,
// Macroworld
Pause,
Rest,
EquipmentTab,
PanUp,
PanDown,
PanLeft,
PanRight,
// Microworld
MoveForward,
MoveBack,
MoveLeft,
MoveRight,
Attack,
CastSpell,
Jump,
Interact,
// Gathering is WORK, not a weapon arc (owner verdict 2026-09-12, CANON
// «Вердикты ТРУДА»): its own action, its own SP price.
Harvest,
// Пошаговый режим «как в Might & Magic» (owner verdict 2026-09-17,
// CANON S13): the P key, subworld only. Not a second combat system —
// one derived pause reason over the one recovery gate (kPauseTurnStop).
TurnBased,
// Possess died here 2026-09-06 (owner: вселение — не игроцкая кнопка;
// придёт заклинанием) — и ПРИШЛО им 2026-09-17: спелл possession,
// кастуется кнопкой CastSpell как любой другой; движковых дверей нет.
Count
};
inline constexpr std::size_t kActionCount =
static_cast<std::size_t>(ActionId::Count);
// Static descriptor for one action. `key` is the STABLE identifier written to
// the prefs file (order-independent, so reordering the enum never corrupts an
// existing file); `label` is user-facing. `scope` says which world listens —
// the same key may serve a Macro action and a Sub action at once (E is the
// equipment sheet above ground and the interact hand below), because the two
// worlds are never active together.
struct ActionSpec {
ActionId id;
const char* key;
const char* label;
UiScope scope;
SDL_Scancode def;
};
// THE registry (defined in keymap.cpp) — one row per action.
extern const ActionSpec kActionSpec[kActionCount];
// Spec row for an id (index into kActionSpec == enumerator value).
const ActionSpec& action_spec(ActionId id);
// true when `scope` listens in the current world.
bool scope_active(UiScope scope, bool subworldActive);
// The single source of truth for key bindings. The constructor seeds every
// entry from kActionSpec, so a fresh install (no prefs file yet) is already
// correct without any extra logic.
class Keymap {
public:
Keymap();
SDL_Scancode get(ActionId id) const { return binds_[idx(id)]; }
// Bind `sc` to `id`, and UNBIND it from any other action whose scope
// shares a world with `id` — one key, one meaning per world. The robbed
// action is left unbound (shown as "—" in the panel), never silently
// duplicated.
void set(ActionId id, SDL_Scancode sc);
void reset_defaults();
// The Controls panel's "press a key now" latch: the ActionId being
// rebound, or -1. Armed by the panel, consumed by the app's event loop
// (the SDL event is the only honest source of a scancode).
int pendingRebind = -1;
private:
static std::size_t idx(ActionId id) { return static_cast<std::size_t>(id); }
SDL_Scancode binds_[kActionCount];
};
// Persistence — the same tiny, forgiving text KV file idiom as ui_settings
// ("<key> <scancode int>" per line). A missing file or unknown/absent keys are
// non-fatal: defaults are kept. Adding or removing an action never invalidates
// an existing file.
bool load_keymap(Keymap& m, const std::string& path);
bool save_keymap(const Keymap& m, const std::string& path);
// The one Controls panel (menu → Controls), shared by both worlds. Returns
// true if the user changed anything this frame (so the caller can flush the
// prefs file).
bool draw_keymap_panel(Keymap& m, bool* open);
} // namespace sm::ui