mirror of
https://github.com/jkdansereau/goldeneye-pc-port.git
synced 2026-10-23 15:52:42 +02:00
- 5 sections (INPUT/GAMEPLAY/GRAPHICS/AUDIO/VIDEO, all below MAX_PROWS): DISPLAY+GRAPHICS merged into GRAPHICS; AUDIO carries the watch music/FX sliders out of the Bond-file page; VIDEO keeps the pure video rows; __HdrMouse/__HdrPad merged into INPUT; __HdrBond/__BondFile retired for the front-only top save-file row (frontoptions.c) - save-file scoping: per-file sections (GAMEPLAY, AUDIO) annotated '(File N)'/'(no file)' via the new Row.saveScoped flag + watchSettingsActiveFolder(); per-file rows in mixed sections carry a dim '(save)' tag; the D353 optionsRowIsBondChooser special-case is generalized (both UIs) - N64-watch rows Bond.Look / Bond.AimControl commented out of the menu surface (D181/D216 style: keys + watch paths stay live) - per-section 'Reset to defaults' replaces the Turok rows (plan Gate E): edge-triggered two-step arm->confirm within 3 s, one commit, disarm on timeout / navigate-away / close (optionsResetMaintain/Clear); rowResetSection walks the section's declared range: watch rows -> BLANKSAVEDATA through the D352 commit path (front direct, stage queue), ini rows -> kResetDefaults[] (the port's C initializers); __Resolution is a documented exclusion (action-backed) - GE_WSPROBE_RESET probe: front tick-120 prepare/dispatch/verify, stage tick-120 dispatch / tick-121 verify; a direct -level_XX boot starts its timer from phase 0 (front prepare is front-only) - docs: findings.md D356 + index + CSV, WATCH-SETTINGS-PLAN status, OPTIONS-MENU-PLAN pointer, plan doc status -> LANDED Verified headless: GE_WSPROBE_RESET front (all 5 sections failures=0, __Resolution untouched, file-2 scope isolation OK) and -level_33 (6 commands queued, all sections verified post-drain, saved music/sfx = 0xFF); GE_WSPROBE / GE_WSPROBE_FRONT regressions clean; 248/248 build + link.
119 lines
5.7 KiB
Markdown
119 lines
5.7 KiB
Markdown
# In-game PC options menu — design + resume point
|
||
|
||
Status: **config-system foundation landed (M-35); menu surface not built.**
|
||
(Updated 2026-09-27: the menu surface *has* since been built and iterated
|
||
as D346 -> D356; the current layout and per-section reset live in
|
||
`docs/dev/D356-SETTINGS-REGROUP-PLAN.md` + `docs/dev/WATCH-SETTINGS-PLAN.md`
|
||
and `docs/dev/findings.md` D346–D356. This doc remains the historical
|
||
M3-scope record.)
|
||
Pattern follows `docs/dev/AUDIO-PLAN.md` — a plan doc that a later session
|
||
executes.
|
||
|
||
Findings context: D180 (input), D181 (first route-(b) hook). Config API:
|
||
`configRegister{Int,UInt,Float,String}` with clamps (`port/include/config.h`).
|
||
|
||
---
|
||
|
||
## 1. What PD does, and why it doesn't port directly
|
||
|
||
PD's `pd_port/port/src/optionsmenu.c` (~2000 lines) hooks port-side handlers
|
||
into **PD's own decomp menu tables** (`menudata`/`menuitem` arrays with
|
||
`MENUITEMTYPE_SLIDER` etc.). It works because PD's front end is a generic
|
||
data-driven list menu.
|
||
|
||
GE is different. GE's settings UI is the **in-game watch** (`src/game/options.c`):
|
||
`draw_watch_game_options_page` / `draw_watch_control_options_page` +
|
||
`watch_screenN_navigation` — hand-drawn pages with bespoke nav functions,
|
||
not a table. The front-end menus (`front.c`, `initmenus.c`, `mpmenu.c`) are
|
||
also bespoke draw/nav pairs, not a reusable list widget.
|
||
|
||
So there is **no table to inject rows into**. A PC options surface is either
|
||
(A) a new hand-built watch page, (B) a new hand-built front-end screen, or
|
||
(C) a port-layer overlay drawn outside the game's menu system.
|
||
|
||
## 2. Recommended approach: (C) port-layer overlay
|
||
|
||
A self-contained immediate-mode overlay in `port/`, toggled by a hotkey
|
||
(e.g. `F10`), drawn as fast3d 2D quads + the game's own `textRender` over
|
||
the top of whatever is on screen, with keyboard/mouse nav handled entirely
|
||
in the port layer. Reasons:
|
||
|
||
- **Zero `src/` menu-code edits.** No new bespoke nav function to get wrong
|
||
(the watch-nav code is exactly the D118d / over-scroll family).
|
||
- Works in-level *and* in the front end (it's above the game).
|
||
- The values it edits are already port-owned `config.c` variables — the
|
||
overlay is just a view over the registered option list.
|
||
- Precedent: the port already draws port-owned UI (FPS in the window title,
|
||
F12 screenshot). This is the render-side equivalent.
|
||
|
||
Cost: an immediate-mode widget layer (label, slider, toggle, dropdown) in
|
||
fast3d 2D. ~300–500 lines. The `config.c` registry already gives us
|
||
key/min/max/value + a type tag (after M-35) — enough to auto-generate rows.
|
||
|
||
### Sketch
|
||
|
||
```
|
||
port/src/optionsoverlay.c
|
||
optionsOverlayToggle() // F10 in video.c event pump
|
||
optionsOverlayHandleInput() // called from inputComputePad idx 0 when open:
|
||
// swallow kbd/mouse from the game, drive the cursor
|
||
optionsOverlayRender(Gfx**) // called from a fast3d 2D hook after the game DL
|
||
```
|
||
|
||
Registry additions needed in `config.c`:
|
||
- `configForEachOption(cb)` — iterate {key, type, ptr, min, max}.
|
||
- optional per-option metadata: display label, step, enum-value names,
|
||
"apply live" vs "needs restart". Add a `configRegisterIntEx(...)` variant
|
||
or a side table keyed by dotted key.
|
||
|
||
Live-apply: most keys already re-read their variable every frame
|
||
(`mouseAimSpeed`, `portScreenShakeScale`, `cfgVSync` via `videoSetVSync`).
|
||
Ones that need a hook (MSAA, fullscreen) get an `configOnChange` callback
|
||
or are flagged "restart".
|
||
|
||
## 3. Minimum option set for v1
|
||
|
||
| Row | Key | Type | Notes |
|
||
|---|---|---|---|
|
||
| VSync | `Video.VSync` | toggle | live (`SDL_GL_SetSwapInterval`) |
|
||
| Frame cap | `Video.FpsCap` | int slider 0–360 | live |
|
||
| MSAA | `Video.MSAA` | dropdown 1/2/4/8 | needs FBO rebuild — flag "restart" for v1 |
|
||
| Texture filter | `Video.TextureFilter` | dropdown nearest/bilinear/3-point | live-ish |
|
||
| Mouse aim speed | `Input.MouseAimSpeed` | slider 1–100 | live |
|
||
| Mouse turn speed | `Input.MouseTurnSpeed` | slider 1–100 | live |
|
||
| Mouse invert Y | `Input.MouseInvertY` | toggle | live |
|
||
| Capture mode | `Input.MouseCaptureMode` | toggle | live |
|
||
| Screen shake | `Game.ScreenShakeIntensity` | slider 0–3 (×) | live (D181) |
|
||
| Screenshot key | — | (F12, documented; rebind = later) | — |
|
||
|
||
`configSave()` on overlay close.
|
||
|
||
## 4. Alternative if an in-fiction surface is required later
|
||
|
||
> **2026-09-27: route B chosen (v0.4.0 intake).** The entry point is on the
|
||
> file select screen, not a main options menu (GE's front end has none). M1
|
||
> landed as D343: an "Options" label that opens the F10 overlay, via one
|
||
> Rule-2 `#ifdef PORT` draw hook. M2 (a GE-styled screen that reuses the
|
||
> overlay's rows) follows.
|
||
|
||
Route (B): a new front-end screen `constructor_menuXX_pcoptions` +
|
||
`menuXX_pcoptions_navigation` in `front.c` under `#ifdef PORT`, reached from
|
||
a new "PC OPTIONS" row on the main options menu. Each row calls a
|
||
`configGet/Set` shim. This is a genuine route-(b) `src/` edit (menu content
|
||
change) → its own Dxx, N64 verbatim under `#else`, opt-in. More faithful,
|
||
more surface area, more nav bugs. Defer unless the owner wants it in-fiction.
|
||
|
||
## 5. Resume checklist
|
||
|
||
1. Add `configForEachOption` + a label/step/enum side-table to `config.c`.
|
||
2. Build the fast3d 2D immediate-mode widget helpers (reuse `gDPFillRectangle`
|
||
+ `textRender`; see how `options.c draw_watch_game_options_page` emits
|
||
text for the font/`Gfx**` conventions).
|
||
3. `port/src/optionsoverlay.c` state machine + F10 toggle in `video.c`.
|
||
4. Route input: when open, `inputComputePad` idx 0 returns a neutral pad and
|
||
forwards nav to the overlay (same "swallow" pattern WI-1 uses for the
|
||
free-cursor click).
|
||
5. `configOnChange` hooks for MSAA/fullscreen; everything else is live.
|
||
6. Verify: overlay open/close in-level and in the front end; golden dumps
|
||
unaffected with the overlay closed (it draws nothing).
|