mirror of
https://github.com/jkdansereau/goldeneye-pc-port.git
synced 2026-10-23 15:52:42 +02:00
feat(D214): keyboard rebinding via [Bind] config section
port/src/input.c: 12 gameplay actions (Forward/Back/StrafeLeft/StrafeRight/ TurnLeft/TurnRight/Fire/Aim/Action/Cancel/LeanLeft/Start), each bound to a comma-separated list of up to 4 SDL scancode names via Input.Bind.* keys. inputRebuildBinds() parses them in inputInit() after configLoad(); SDL_GetScancodeFromName resolves names, unknowns warn + skip. The hardcoded keyDown(ks, SDL_SCANCODE_*) block in inputComputePad is replaced with actHeld(ks, IA_*). Mouse buttons (fire/aim) stay hardwired. File-driven, no UI -- matches the PD port. A fresh ge007.ini gets the full [Bind] block with defaults visible. Defaults reproduce the previous FPS layout exactly: all 20 default key names round-trip to the identical SDL_Scancode the old code used (0 mismatches in a SDL_GetScancodeFromName test). Build clean; verify.sh dam/facility/bunker1 PASS crash-free. findings.md D214 + index; QOL-INVENTORY row marked done.
This commit is contained in:
1 parent
7bcfbf313c
commit
de31f2874b
4 files changed
+100
-17
No files matched your search
@@ -28,7 +28,7 @@ ScreenShakeIntensity. Debug: FrameDump, InputLog.
|
||||
|
||||
| Feature | PD location | Class | Effort | Notes / priority |
|
||||
|---|---|---|---|---|
|
||||
| **Key rebinding** (config-string binds per controller/action) | `input.c:69,627-675,1548` (`bindStrs`, `inputParseBindString`) | P | M | **High.** Explicit Phase 4 debt ("rebinding UI" — note PD has *no* UI either; config-file driven). Port-only in `port/src/input.c`. |
|
||||
| **Key rebinding** _(DONE M-83, D214)_ | `port/src/input.c` `[Bind]` section | P | M | Keyboard only, 12 actions, `Input.Bind.* = Key,Key` (SDL scancode names), file-driven like PD. Defaults = prior FPS layout (verified). Per-controller pad rebinding + a UI still open. |
|
||||
| **SkipIntro** | `main.c:139` (STAGE_TITLE → STAGE_CITRAINING) | P | S | **High.** GE analogue: stage-override path already exists (`boss.c:385-393`, `g_StageNum = LEVELID_TITLE` at :98); wire a flag + arg. 10-min check on what feeds `tokenFindLevel`. |
|
||||
| **DisplayFPS** _(DONE M-83, D213)_ | config `Video.DisplayFPS` | P | S | Top-right readout, config-only; ~0.5 s sample window. |
|
||||
| **Anisotropic filtering** _(DONE M-83, D212)_ | `Video.Anisotropy` (1–16, dflt 4) | P | S | fast3d already had the GL hook + a hardcoded 4×; M-83 exposed it as config. MipmapFilter / TextureFilter2D still not surfaced. |
|
||||
|
||||
@@ -56,6 +56,7 @@ D184,PARTIAL,F10 port-layer options overlay —,"LANDED, port-only, no #ifdef PO
|
||||
D211,PARTIAL,"PC FOV slider — Video.FovScale (M-83 QoL wave, port-only, WIDESCREEN-FOV-PLAN.md Phase 4).","LANDED, port-only, no #ifdef PORT."
|
||||
D212,PARTIAL,Anisotropic-filtering config wiring —,"LANDED, port-only. Build clean."
|
||||
D213,PARTIAL,On-screen FPS readout —,"LANDED, port-only. Build clean; bunker1 no-op confirmed."
|
||||
D214,PARTIAL,"Keyboard rebinding — [Bind] config section (M-83, port-only, PD parity, Phase 4 debt).","LANDED, port-only, no #ifdef PORT."
|
||||
D85,OPEN,room primary/secondary DL → texLoadFromGdl garbage,"OPEN (safety-netted; widen/pool fixes landed, geometry now renders)"
|
||||
D86 · D87,RESOLVED,modelInitRwData truncated ptr · attract-demo BE ramromfilestructure,resolved
|
||||
D88.4,RESOLVED,propDefs polymorphic record stream not byteswapped → setupDoor crash,resolved (d88_propdefs.py); layout audit M-20 → D132
|
||||
|
||||
|
@@ -430,6 +430,7 @@ covers D24–D69; the log continues in §H (D32 procedure, D70–D121).
|
||||
| D211 | **PC FOV slider — `Video.FovScale` (M-83 QoL wave, port-only, `WIDESCREEN-FOV-PLAN.md` Phase 4).** fast3d applies a multiplicative half-angle scale to every *perspective* projection matrix as it is loaded (`gfx_apply_fov_scale()` in `gfx_pc.cpp gfx_sp_matrix`, on the `G_MTX_PROJECTION | G_MTX_LOAD` path only — GE always reloads its view projection via `guPerspectiveF` at `fr.c:725`, so a relative `G_MTX_MUL` onto an already-scaled matrix cannot compound). Columns 0 and 1 are scaled by the same factor `tan(half_orig)/tan(half_adj)` → H and V FOV widen uniformly, aspect ratio exactly preserved (no stretch), and because the game rewrites fovy every frame for aim-zoom the scale composes with zoom for free. Ortho matrices (`P[3][3]==1`) are left untouched so HUD/menu 2D is unaffected. `Video.FovScale` = percent of the original vertical FOV, 50–150, **default 100 ⇒ `g_fov_scale == 1.0f` ⇒ guarded early-return, zero FP ops on the matrix ⇒ golden dumps byte-identical**. F10 overlay row "FOV scale %". Math checked offline (`s=1.25 → fovy 60°→75°`, aspect held at 1.3333). | **LANDED, port-only, no `#ifdef PORT`.** Build clean; `bunker1` framediff worst_cell 12.219 vs 12.221 baseline (= render noise, no-op confirmed). **Owed:** an interactive eyeball at FovScale ≠ 100 (world widens, HUD unmoved, aim-zoom still composes). |
|
||||
| D212 | **Anisotropic-filtering config wiring — `Video.Anisotropy` (M-83 QoL wave, port-only).** The fast3d GL backend already implemented `set_anisotropy_level` / `get_max_anisotropy_level` and applied `GL_TEXTURE_MAX_ANISOTROPY` on mipmapped tiles with a hardcoded `current_anisotropy_level = 4`; there was no way to configure it. Added the `gfx_set_anisotropy_level()` extern-C wrapper in `gfx_pc.cpp` (clamps to `[1, GL max]`, `reset_texture_state()` so live changes take), declared in `gfx_api.h`, and `Video.Anisotropy` (1–16) in `port/src/video.c` wired through `videoApplyImageOptions()` on init + live-config. **Default 4 = the value fast3d already applied ⇒ no visual delta at defaults.** F10 overlay row "Anisotropic". | **LANDED, port-only.** Build clean. **Owed:** eyeball at 16× on a grazing surface (Depot roof / Dam walkway) vs 1×. |
|
||||
| D213 | **On-screen FPS readout — `Video.DisplayFPS` (M-83 QoL wave, port-only, PD parity).** `port/src/optionsoverlay.c` samples frame time once per emitted frame (`fpsTick()` via `sysGetMicroseconds()`, ~0.5 s window) and, when enabled, emits a small top-right green "N FPS" DL even while the F10 panel is closed. Config-only knob (kept off the 13-row panel, which is at its documented layout limit — matches PD, whose `DisplayFPS` is also file-only). **Default 0 ⇒ `optionsOverlayEmit()` still returns NULL when the panel is closed ⇒ golden dumps byte-identical.** | **LANDED, port-only.** Build clean; `bunker1` no-op confirmed. **Owed:** eyeball that the counter reads a sane ~30 and doesn't overlap the HUD. |
|
||||
| D214 | **Keyboard rebinding — `[Bind]` config section (M-83, port-only, PD parity, Phase 4 debt).** `port/src/input.c` gains 12 gameplay actions (Forward/Back/StrafeLeft/StrafeRight/TurnLeft/TurnRight/Fire/Aim/Action/Cancel/LeanLeft/Start), each a comma-separated list of up to 4 SDL scancode names (`Input.Bind.Forward = W,Up` …). `inputRebuildBinds()` (called from `inputInit()`, after `configLoad()`) parses them via `SDL_GetScancodeFromName`; unknown names log a warning and are skipped. The old hardcoded `keyDown(ks, SDL_SCANCODE_*)` block in `inputComputePad` is replaced by `actHeld(ks, IA_*)`. Mouse buttons (fire = LMB, aim = RMB) stay hardwired; the mouse-wheel weapon-cycle A-pulse is untouched. **Defaults reproduce the previous FPS layout exactly — verified: all 20 default key names round-trip to the identical `SDL_Scancode` the old code used (`SDL_GetScancodeFromName` test, 0 mismatches).** Config-file only (no in-overlay text entry — matches PD, whose rebinding is also file-driven); a fresh `ge007.ini` gets the full `[Bind]` block with the defaults visible. | **LANDED, port-only, no `#ifdef PORT`.** Build clean. Default binds byte-equivalent to the prior mapping (scancode test). **Owed:** interactive check that a *non-default* bind (e.g. `Fire = Left Alt`) actually takes, and that ESC still cancels menus (it's in the default `Cancel` bind + a separate `video.c` path). |
|
||||
| D85 | room primary/secondary DL → `texLoadFromGdl` garbage | OPEN (safety-netted; widen/pool fixes landed, geometry now renders) |
|
||||
| D86 · D87 | modelInitRwData truncated ptr · attract-demo BE `ramromfilestructure` | resolved |
|
||||
| D88.1–D88.3 · D88.5–D88.6 | `Usetup*Z` header + sub-table width/endian conversion (`d88_emit.py`) | resolved |
|
||||
|
||||
+97
-16
@@ -399,6 +399,8 @@ static unsigned scriptApply(unsigned button)
|
||||
return m;
|
||||
}
|
||||
|
||||
static void inputRebuildBinds(void); /* D214; defined below with keyDown() */
|
||||
|
||||
int inputInit(void)
|
||||
{
|
||||
if (!SDL_WasInit(SDL_INIT_GAMECONTROLLER)) {
|
||||
@@ -413,6 +415,8 @@ int inputInit(void)
|
||||
}
|
||||
inputOpenPads();
|
||||
|
||||
inputRebuildBinds(); /* D214: parse [Bind] now that configLoad() has run */
|
||||
|
||||
/* Relative mouse mode for mouse-look. In click-to-lock mode we start
|
||||
* released and wait for a click in the window (video.c -> inputNotifyClick). */
|
||||
mouseGrabbed = mouseEnabled && !mouseCaptureMode;
|
||||
@@ -486,7 +490,79 @@ static int scaleAxis(int v)
|
||||
|
||||
static int keyDown(const Uint8 *ks, SDL_Scancode sc)
|
||||
{
|
||||
return ks && ks[sc];
|
||||
return ks && sc != SDL_SCANCODE_UNKNOWN && ks[sc];
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------
|
||||
* D214 — keyboard rebinding (PD parity: config-string driven, no UI).
|
||||
*
|
||||
* Each gameplay action maps to a comma-separated list of SDL scancode names
|
||||
* (as printed by SDL_GetScancodeName: "W", "Up", "Left Ctrl", "Space", ...).
|
||||
* The defaults reproduce the previously-hardcoded FPS layout exactly, so a
|
||||
* fresh or [Bind]-less ini changes nothing. Parsed once in inputInit(), after
|
||||
* configLoad(). Mouse buttons (fire = LMB, aim = RMB) stay hardwired.
|
||||
* ---------------------------------------------------------------------- */
|
||||
enum {
|
||||
IA_FORWARD, IA_BACK, IA_STRAFE_L, IA_STRAFE_R, IA_TURN_L, IA_TURN_R,
|
||||
IA_FIRE, IA_AIM, IA_ACTION, IA_CANCEL, IA_LEAN_L, IA_START, IA_COUNT
|
||||
};
|
||||
|
||||
static const struct { const char *key; const char *def; } kBindDefs[IA_COUNT] = {
|
||||
[IA_FORWARD] = { "Input.Bind.Forward", "W,Up" },
|
||||
[IA_BACK] = { "Input.Bind.Back", "S,Down" },
|
||||
[IA_STRAFE_L] = { "Input.Bind.StrafeLeft", "A" },
|
||||
[IA_STRAFE_R] = { "Input.Bind.StrafeRight", "D" },
|
||||
[IA_TURN_L] = { "Input.Bind.TurnLeft", "Left" },
|
||||
[IA_TURN_R] = { "Input.Bind.TurnRight", "Right" },
|
||||
[IA_FIRE] = { "Input.Bind.Fire", "Left Ctrl" },
|
||||
[IA_AIM] = { "Input.Bind.Aim", "Left Shift" },
|
||||
[IA_ACTION] = { "Input.Bind.Action", "Space,Z,E" },
|
||||
[IA_CANCEL] = { "Input.Bind.Cancel", "X,R,F,Escape" },
|
||||
[IA_LEAN_L] = { "Input.Bind.LeanLeft", "Q" },
|
||||
[IA_START] = { "Input.Bind.Start", "Return,Tab" },
|
||||
};
|
||||
|
||||
#define BIND_MAX_KEYS 4
|
||||
static char g_bindStr[IA_COUNT][64];
|
||||
static SDL_Scancode g_bind[IA_COUNT][BIND_MAX_KEYS];
|
||||
|
||||
static void inputRebuildBinds(void)
|
||||
{
|
||||
for (int a = 0; a < IA_COUNT; a++) {
|
||||
for (int k = 0; k < BIND_MAX_KEYS; k++) {
|
||||
g_bind[a][k] = SDL_SCANCODE_UNKNOWN;
|
||||
}
|
||||
char buf[64];
|
||||
strncpy(buf, g_bindStr[a][0] ? g_bindStr[a] : kBindDefs[a].def, sizeof(buf) - 1);
|
||||
buf[sizeof(buf) - 1] = 0;
|
||||
|
||||
int n = 0;
|
||||
for (char *tok = strtok(buf, ","); tok && n < BIND_MAX_KEYS; tok = strtok(NULL, ",")) {
|
||||
while (*tok == ' ' || *tok == '\t') tok++;
|
||||
char *end = tok + strlen(tok);
|
||||
while (end > tok && (end[-1] == ' ' || end[-1] == '\t')) *--end = 0;
|
||||
if (!*tok) continue;
|
||||
SDL_Scancode sc = SDL_GetScancodeFromName(tok);
|
||||
if (sc == SDL_SCANCODE_UNKNOWN) {
|
||||
sysLogPrintf(LOG_WARNING, "input: %s: unknown key name '%s'",
|
||||
kBindDefs[a].key, tok);
|
||||
continue;
|
||||
}
|
||||
g_bind[a][n++] = sc;
|
||||
}
|
||||
if (n == 0) {
|
||||
sysLogPrintf(LOG_WARNING, "input: %s has no valid keys; action unbound",
|
||||
kBindDefs[a].key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int actHeld(const Uint8 *ks, int act)
|
||||
{
|
||||
for (int k = 0; k < BIND_MAX_KEYS; k++) {
|
||||
if (keyDown(ks, g_bind[act][k])) return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Fill button mask + stick for controller idx. Returns the 16-bit mask. */
|
||||
@@ -538,32 +614,29 @@ unsigned inputComputePad(int idx, signed char *stick_x, signed char *stick_y)
|
||||
/* GE default control (1.1): stick Y = move fwd/back, stick X = turn,
|
||||
* C-left/right = sidestep, C-up/down = look. FPS layout: W/S move,
|
||||
* A/D strafe (C-buttons), mouse X turns (stick X), mouse Y looks. */
|
||||
if (keyDown(ks, SDL_SCANCODE_W) || keyDown(ks, SDL_SCANCODE_UP)) sy = STICK_MAX;
|
||||
if (keyDown(ks, SDL_SCANCODE_S) || keyDown(ks, SDL_SCANCODE_DOWN)) sy = -STICK_MAX;
|
||||
if (keyDown(ks, SDL_SCANCODE_A)) button |= GE_CONT_C; /* strafe left */
|
||||
if (keyDown(ks, SDL_SCANCODE_D)) button |= GE_CONT_F; /* strafe right */
|
||||
if (keyDown(ks, SDL_SCANCODE_LEFT)) sx = -STICK_MAX; /* keyboard turn */
|
||||
if (keyDown(ks, SDL_SCANCODE_RIGHT)) sx = STICK_MAX;
|
||||
if (actHeld(ks, IA_FORWARD)) sy = STICK_MAX;
|
||||
if (actHeld(ks, IA_BACK)) sy = -STICK_MAX;
|
||||
if (actHeld(ks, IA_STRAFE_L)) button |= GE_CONT_C; /* strafe left */
|
||||
if (actHeld(ks, IA_STRAFE_R)) button |= GE_CONT_F; /* strafe right */
|
||||
if (actHeld(ks, IA_TURN_L)) sx = -STICK_MAX; /* keyboard turn */
|
||||
if (actHeld(ks, IA_TURN_R)) sx = STICK_MAX;
|
||||
|
||||
if ((mb & SDL_BUTTON(SDL_BUTTON_LEFT)) || keyDown(ks, SDL_SCANCODE_LCTRL))
|
||||
if ((mb & SDL_BUTTON(SDL_BUTTON_LEFT)) || actHeld(ks, IA_FIRE))
|
||||
button |= GE_CONT_G;
|
||||
int aimHeld = (mb & SDL_BUTTON(SDL_BUTTON_RIGHT)) ||
|
||||
keyDown(ks, SDL_SCANCODE_LSHIFT);
|
||||
int aimHeld = (mb & SDL_BUTTON(SDL_BUTTON_RIGHT)) || actHeld(ks, IA_AIM);
|
||||
if (aimHeld)
|
||||
button |= GE_CONT_R;
|
||||
if (keyDown(ks, SDL_SCANCODE_SPACE) || keyDown(ks, SDL_SCANCODE_Z) ||
|
||||
keyDown(ks, SDL_SCANCODE_E))
|
||||
if (actHeld(ks, IA_ACTION))
|
||||
button |= GE_CONT_A;
|
||||
if (wheelPulse > 0) { /* mouse-wheel weapon cycle -> A pulse */
|
||||
button |= GE_CONT_A;
|
||||
wheelPulse--;
|
||||
}
|
||||
if (keyDown(ks, SDL_SCANCODE_X) || keyDown(ks, SDL_SCANCODE_R) ||
|
||||
keyDown(ks, SDL_SCANCODE_F) || keyDown(ks, SDL_SCANCODE_ESCAPE)) /* D145: ESC = B (back/cancel) */
|
||||
if (actHeld(ks, IA_CANCEL)) /* D145: Escape is in the default Cancel bind */
|
||||
button |= GE_CONT_B;
|
||||
if (keyDown(ks, SDL_SCANCODE_Q))
|
||||
if (actHeld(ks, IA_LEAN_L))
|
||||
button |= GE_CONT_L;
|
||||
if (keyDown(ks, SDL_SCANCODE_RETURN) || keyDown(ks, SDL_SCANCODE_TAB))
|
||||
if (actHeld(ks, IA_START))
|
||||
button |= GE_CONT_START;
|
||||
|
||||
/* Mouse-look. Mode-dependent (see the tuning-constants comment):
|
||||
@@ -952,4 +1025,12 @@ PD_CONSTRUCTOR static void inputConfigInit(void)
|
||||
configRegisterInt("Input.PadDeadzone", &padDeadzone, 0, 30000);
|
||||
configRegisterInt("Input.PadTriggerPct", &padTriggerPct, 1, 99);
|
||||
configRegisterInt("Input.PadLookInvertY", &padLookInvertY, 0, 1);
|
||||
|
||||
/* D214: keyboard rebinding. Seed each buffer with its default so the knob
|
||||
* is visible/editable in a fresh ge007.ini; configLoad() overwrites any the
|
||||
* user set, then inputInit() calls inputRebuildBinds(). */
|
||||
for (int a = 0; a < IA_COUNT; a++) {
|
||||
strncpy(g_bindStr[a], kBindDefs[a].def, sizeof(g_bindStr[a]) - 1);
|
||||
configRegisterString(kBindDefs[a].key, g_bindStr[a], sizeof(g_bindStr[a]));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user