mirror of
https://github.com/jkdansereau/goldeneye-pc-port.git
synced 2026-10-23 15:52:42 +02:00
docs: add CONTROLLER-INPUT-PLAN (v0.5.0 controller/input options wave)
Low-hanging-fruit controller/input options ported from the Turok PC port's menu, scoped as port-layer-only (zero src/ edits): right-stick look X/Y sensitivity, southpaw trigger swap, per-stick deadzone (with the legacy single-deadzone migration), look smoothing, and a mouse-vertical-sensitivity row. All off-by-default (byte-identical at defaults, golden gate preserved). Documents what the GE port already has, the per-item code shape + insertion points in input.c, and the Tier B/C items explicitly out of scope (head bob, bloom/shadows, death-cam, uncapped-FPS -> UNLOCKED-FPS-PLAN, controller rebind -> PR #109).
This commit is contained in:
1 parent
d934a93253
commit
83ece6c370
1 file changed
+188
@@ -0,0 +1,188 @@
|
||||
# Controller & input options wave (port plan)
|
||||
|
||||
Status: **PROPOSED (v0.5.0 candidate)** — the "low-hanging fruit" from the
|
||||
Nightdive *Turok* PC-port options menu that is cheap, well-scoped, and
|
||||
**port-layer only** (zero `src/` / `src/game` edits → rule 2 and the ABI
|
||||
exception do not apply). Sequenced after v0.4.0 ships. This is the *input*
|
||||
wave; the sibling *video/audio/graphics* Tier-A items (borderless window,
|
||||
primary display, brightness, master volume, button glyphs, uncapped-FPS) are a
|
||||
separate follow-on wave — see §Out of scope.
|
||||
|
||||
Reference: `docs/dev/RUMBLE-PLAN.md` (D401) is the closest precedent — a
|
||||
controller option that lives entirely in `port/src/input.c` +
|
||||
`port/src/optionsoverlay.c` + a `configRegister*` key + a `kResetDefaults`
|
||||
entry, with the game side (`src/joy.c`, `src/` stick consumers) untouched.
|
||||
`docs/dev/GEPD-INPUT-PLAN.md` documents the pad poll path this wave extends.
|
||||
|
||||
---
|
||||
|
||||
## Ground truth (current state, verified 2026-09-27)
|
||||
|
||||
**Pad poll = `inputComputePad()`** (`port/src/input.c:1375`), runs on the game
|
||||
thread (`osContStartReadData`). It reads the SDL axes/buttons for pad `idx`,
|
||||
writes out a 16-bit button mask plus `*stick_x` / `*stick_y` (the N64
|
||||
`signed char` stick, clamped to `STICK_MAX`). All the knobs in this wave are
|
||||
applied **inside** `inputComputePad`, before the two `*stick_x`/`*stick_y`
|
||||
writes at `:2063-2064` — i.e. the port re-shapes the *input the game already
|
||||
consumes*, never the game's stick→turn math.
|
||||
|
||||
**Two pitch modes drive the look stick** (selected by `Input.NaturalPitch`,
|
||||
the default-on "SOLITARE" mode, D194/D238):
|
||||
- **Continuous (natural-pitch, the default):** the **right stick** is the
|
||||
continuous look axis — `input.c:1927-1933`:
|
||||
`rxs = scaleAxis(rx);` … `rys = -scaleAxis(ry); if (padLookInvertY)
|
||||
rys = -rys; sx = rxs; sy = rys;`. The left stick becomes digital-step
|
||||
movement. **This is the path the X/Y look-sensitivity + look-smoothing +
|
||||
right-stick deadzone knobs target.**
|
||||
- **Digital (native, natural-pitch off):** the left stick is analog
|
||||
movement/turn and the right stick is digitized into the C-button look
|
||||
(`:1935-1947`, thresholds at `RSTICK_THRESHOLD`). Look here is the faithful
|
||||
N64 *digital* C-button turn — **not sens-scalable** (there is no analog look
|
||||
to scale), so the new look-sens/smoothing knobs are inert in this mode,
|
||||
exactly as `Input.AimMode`/`Input.AimRange` already are per mode.
|
||||
|
||||
**Existing pad knobs** (all `configRegister*` in `input.c`, rows in
|
||||
`optionsoverlay.c` INPUT section): `Input.PadDeadzone` (single,
|
||||
`padDeadzone = STICK_DEADZONE`, `:524`), `Input.PadTriggerPct` (`:525`),
|
||||
`Input.PadLookInvertY` (`:526`), `Input.RumbleScale` (D401). `scaleAxis()`
|
||||
applies the shared deadzone + `STICK_MAX` scale to a raw `int16` axis.
|
||||
|
||||
**Already covered elsewhere** (do not re-plan): mouse sens/invert/smoothing
|
||||
(`Input.MouseSensitivity`/`MouseInvertY`/`MouseSmoothing`), **uncapped FPS**
|
||||
(`docs/dev/UNLOCKED-FPS-PLAN.md` — parked; has a real perf prerequisite, D250),
|
||||
**controller rebinding** (PR #109 / D383 — the hard, open gap, out of scope).
|
||||
|
||||
---
|
||||
|
||||
## Items (all port-layer)
|
||||
|
||||
### 1. `Input.PadLookSensX` — right-stick look **horizontal** sensitivity *(S)*
|
||||
New int key, default **100** (= 1×, current behaviour), range ~**25–200 %**.
|
||||
Applied in the continuous-look branch only: after `rxs = scaleAxis(rx)`,
|
||||
`rxs = clamp(rxs * padLookSensX / 100, ±STICK_MAX)` before `sx = rxs`
|
||||
(`input.c:1928/1932`). Pure gain on the emitted look X. In digital mode: inert.
|
||||
- Row: INPUT, "Look sensitivity (left/right)", slider, after "Rumble strength".
|
||||
- Risk: none (monotonic gain; 100% = byte-identical to today). Deck-testable.
|
||||
|
||||
### 2. `Input.PadLookSensY` — right-stick look **vertical (pitch)** sensitivity *(S)*
|
||||
Same shape, applied to `rys` (`input.c:1930/1933`): `rys = clamp(rys *
|
||||
padLookSensY / 100, ±STICK_MAX)`. Default **100**, range ~**25–200 %**.
|
||||
Kept separate from X (Turok splits them; pitch and yaw feel different on a
|
||||
stick). Independent of the existing `Input.HipfirePitchSpeed` (that one is the
|
||||
hipfire/mouse pitch rate, a different path). In digital mode: inert.
|
||||
- Row: INPUT, "Look sensitivity (up/down)", slider.
|
||||
- Risk: none (same as item 1).
|
||||
|
||||
### 3. `Input.PadSouthpaw` — swap L/R triggers *(S)*
|
||||
New toggle, default **off**. Today (`input.c:1954-1958`): `TRIGGERRIGHT >
|
||||
trigPt → GE_CONT_G`; `TRIGGERLEFT > trigPt → GE_CONT_R`. Under southpaw, swap
|
||||
the two `GE_CONT_G`/`GE_CONT_R` assignments (left trigger becomes the
|
||||
right-shoulder action and vice-versa). Two lines. Lets left-handed /
|
||||
southpaw players put their index finger on the primary fire trigger.
|
||||
- Row: INPUT, "Southpaw", toggle.
|
||||
- Risk: none at default off; trivial to verify by re-aiming.
|
||||
|
||||
### 4. `Input.PadDeadzoneL` / `Input.PadDeadzoneR` — per-stick deadzone *(S–M)*
|
||||
Split the single `Input.PadDeadzone` into a **left (movement)** and a
|
||||
**right (look)** deadzone. Thread the deadzone through `scaleAxis()` (it is
|
||||
currently the global `padDeadzone`) as a per-call argument: left-stick calls
|
||||
use `padDeadzoneL`, right-stick calls use `padDeadzoneR`.
|
||||
- **Back-compat/migration:** on load, if `Input.PadDeadzoneL`/`R` are absent
|
||||
but legacy `Input.PadDeadzone` is set, seed both from it (one-time, like the
|
||||
`AimStyle`→`AimMode` migration at `input.c:449-454`). Keep `Input.PadDeadzone`
|
||||
registered so old inis keep loading; it becomes hidden/superseded (D337/`D333`
|
||||
precedent for a superseded key).
|
||||
- Rows: INPUT, "Deadzone (left stick)" + "Deadzone (right stick)", sliders.
|
||||
- Risk: low — a deadzone is a per-axis floor; splitting it cannot change
|
||||
behaviour when both equal the current single value. Watch the menu-navigation
|
||||
path (`:1918-1922`, left stick in menus) to use the **left** deadzone there.
|
||||
|
||||
### 5. `Input.PadLookSmoothing` — right-stick look low-pass *(M)*
|
||||
New key, default **0 (off)**, range 0–10 (0 = off = today). When >0, low-pass
|
||||
filter the *emitted* right-stick look deflection so the look axis eases rather
|
||||
than tracking the stick 1:1 — the pad analogue of `Input.MouseSmoothing`
|
||||
(which smooths the *relative* mouse delta). Because the continuous stick is
|
||||
**absolute** (not a delta), implement as a per-pad exponential moving average
|
||||
on the emitted `rxs`/`rys` (state kept per `idx`, reset on pad hot-unplug and
|
||||
on aim-mode exit so releasing the stick recentres cleanly). The higher the
|
||||
value, the heavier the easing.
|
||||
- Row: INPUT, "Look smoothing", slider (0 = off).
|
||||
- Risk: **medium** — an EMA on an absolute stick can feel laggy or fail to
|
||||
recentre if the filter isn't driven back to 0 when the stick is centred;
|
||||
needs hands-on tuning on the Deck (the natural test device). Keep default off
|
||||
so it ships inert until tuned.
|
||||
|
||||
### 6. Mouse **vertical** sensitivity row *(trivial — folds in)*
|
||||
`Input.MouseYScale` already exists as a registered key; it just has no F10
|
||||
row. Add one slider row (INPUT) so the asymmetric X/Y mouse sensitivity is
|
||||
user-adjustable without editing the ini. No `input.c` change — row only.
|
||||
|
||||
---
|
||||
|
||||
## Shared plumbing (do once, not per item)
|
||||
- **`configRegisterInt`/`Float`** for each new key in `input.c` next to the
|
||||
existing pad-key registrations (~`:2536-2538`).
|
||||
- **Static state** next to `padDeadzone`/`padLookInvertY` (`:524-526`):
|
||||
`padLookSensX/Y`, `padSouthpaw`, `padDeadzoneL/R`, `padLookSmoothing` + the
|
||||
per-pad EMA state for item 5.
|
||||
- **F10 rows** in `optionsoverlay.c` `rows[]` INPUT section (after
|
||||
"Rumble strength", with the other controller rows) + matching **front options**
|
||||
rows via the shared `optionsRow*` accessors (both UIs read `rows[]` — D401
|
||||
precedent, so one definition covers F10 *and* the front options screen).
|
||||
- **`kResetDefaults`** entries for each new key (neutral: sens 100, smoothing
|
||||
0, southpaw 0, deadzones = `STICK_DEADZONE`) so INPUT "Reset to defaults"
|
||||
covers them (D401 item-3 precedent).
|
||||
|
||||
## Ground rules
|
||||
1. **Port-layer only** — every edit is in `port/src/input.c`,
|
||||
`port/src/optionsoverlay.c`, `port/src/frontoptions.c`, and `CMakeLists.txt`
|
||||
(nothing new to compile — no new files). Zero `src/` / `src/game` edits →
|
||||
rule 2 and the pointer-width ABI exception are not engaged.
|
||||
2. **Off-by-default = today's behaviour.** Every new key defaults to a neutral
|
||||
value that leaves the emitted stick/buttons byte-identical to the current
|
||||
build, so the golden gate (D117/`golden_gate.sh`) is unaffected at defaults
|
||||
and the v0.4.0 playtest baseline is preserved.
|
||||
3. **Per-mode honesty.** The continuous-look knobs (1, 2, 5) apply only in
|
||||
natural-pitch mode; in digital mode they are inert — document this on the
|
||||
rows (mirrors how `Input.AimRange` is hidden in Centred mode, D338), rather
|
||||
than pretending they affect the digital C-button look.
|
||||
4. **Deck is the reference device** for the pad wave (native Steam Controller,
|
||||
the real-world target); Windows + a plugged gamepad as the second check.
|
||||
|
||||
## Verification
|
||||
1. `./build-pc.sh ntsc-final` clean (no new symbols/dupes — the `configRegister*`
|
||||
+ `kResetDefaults` additions are the only new references).
|
||||
2. `/linkcheck` + the golden gate (`tools_pc/golden_gate.sh`) — must pass
|
||||
**unchanged at defaults** (proves the neutral-default invariant).
|
||||
3. Hands-on, on a pad / the Steam Deck: each knob at a non-neutral value
|
||||
changes feel as expected and at default leaves it identical; southpaw
|
||||
re-maps the triggers; per-stick deadzones floor the correct stick; look
|
||||
smoothing eases and **recentres on stick release** (item 5's known risk).
|
||||
4. New finding (D4xx, next free label) recording the wave + per-item
|
||||
Deck-test result, cross-ref'd to D401 (precedent), D337/D338 (per-mode
|
||||
gating pattern), D333 (`AimStyle` migration pattern), PR #109 (out-of-scope
|
||||
rebind).
|
||||
|
||||
## Out of scope for this wave (see the assessment above)
|
||||
- **Sibling Tier-A, other sections (separate follow-on wave):** borderless
|
||||
windowed, primary-display selection, brightness slider, master audio volume,
|
||||
button-prompt glyphs.
|
||||
- **Tier B (medium, contained):** radial fog toggle, HUD opacity, flash-
|
||||
intensity slider (promote `Game.NoHitFlash`), show-HUD master toggle, audio
|
||||
output device, low-pass filter, SMAA/FXAA.
|
||||
- **Uncapped FPS** — `docs/dev/UNLOCKED-FPS-PLAN.md` (parked; D250 perf
|
||||
prerequisite means it is not quick).
|
||||
- **Tier C (rule-2 / big-feature / out-of-scope):** head-bobbing sliders,
|
||||
death-cinematic / switch-on-pickup / walk toggles (all `src/game` flow),
|
||||
bloom / light-scattering / water refraction-reflection / simple shadows,
|
||||
violence / blood colour, multiple graphics APIs, and **full controller
|
||||
rebinding** (PR #109 — the genuinely hard input item).
|
||||
|
||||
## Suggested sequencing (v0.5.0)
|
||||
1. **Wave A (this doc):** items 1–4 + 6 (sens X/Y, southpaw, per-stick
|
||||
deadzone, mouse-Y row) — all low-risk, shippable behind neutral defaults.
|
||||
2. **Item 5 (look smoothing)** lands in the same wave but may ship default-off
|
||||
pending Deck feel-tuning.
|
||||
3. **Wave B:** the sibling video/audio/graphics Tier-A items.
|
||||
4. Revisit Tier C only with explicit sign-off (rule-2 items need the
|
||||
`docs/dev-process.md` procedure).
|
||||
Reference in new issue
Block a user