diff --git a/docs/dev/findings-index.csv b/docs/dev/findings-index.csv index 2ad8cc33..00c1fe6e 100644 --- a/docs/dev/findings-index.csv +++ b/docs/dev/findings-index.csv @@ -207,3 +207,4 @@ D326,CLOSED,Random Eye randomizer repacks the ROM too —,CLOSED as a question D327,RESOLVED,"Mod support, resolved plan:",RESOLVED — superseded by D328; approach (a) is NOT ruled out (these scans missed the 1172-compressed data segment). D328,CLOSED,"Mod support is viable and D327's ""ruled out"" was wrong:",CLOSED as a question — D329,FIXED,The D318/D320 guard anim pin is a tick-granularity divergence:,"FIXED — Rule-2 high-FPS fix in model.c; clk=1 no longer pins (WSL + native Windows), 0 triggers at clk=2; bunker1 golden failure predates it (on/off A/B); user playtest confirms N64-matching Ourumov…" +D330,FIXED,"#103 (v0.3.0, Win 11):","FIXED (port/src/input.c), build clean, bunker1 golden 3/3; mouse feel-check owed (a human must run it:" diff --git a/docs/dev/findings.md b/docs/dev/findings.md index 9ebbf6a0..8206502c 100644 --- a/docs/dev/findings.md +++ b/docs/dev/findings.md @@ -615,6 +615,7 @@ covers D24–D69; the log continues in §H (D32 procedure, D70–D121). | D327 | **Mod support, resolved plan: reading the file table out of the ROM is ruled out by measurement; content-addressed offset remapping (gap inference over contiguously-packed files) is the viable route (1964GEPD fork review, 2026-09-22).** — full `## D327` entry at file tail | RESOLVED — superseded by D328; approach (a) is NOT ruled out (these scans missed the 1172-compressed data segment). `hw_address` absent from the ROM in all 4 encodings tested (0 hits for the segment-relative form); `file_resource_table.inc.c` is a decomp reconstruction. Viable route: locate unchanged files by content, bound changed ones by neighbour gaps (`obInit` differencing proves contiguity). Verified primitive: GE's `0x7F000000` segment maps uncompressed from ROM `0x34b30`, uniquely signature-findable. 1964 fork is GPL-2.0 — facts only, no code reuse. | | D328 | **Mod support is viable and D327's "ruled out" was wrong: GE's file table IS in the ROM, inside the 1172-compressed data segment, and locating it needs no hardcoded offsets (PD dabs fork review + verification on vanilla + 2 mods, 2026-09-22).** — full `## D328` entry at file tail | CLOSED as a question — table at `0x252c4` inside the segment inflated from ROM `0x21990` (727 rows; 681 exact offset+size matches vs filelist, 0 mismatches). Hardcode-free scan reads Random Eye's own table -> **704/726 files byte-identical** (vs 0/803 at vanilla offsets in D326) and GF64's 24 MB table correctly. PD's architecture is an offline importer + mod-dir override, not a runtime ROM loader; its MIT `rompatch.c` removes the GPL xdelta3 constraint. Remaining work is the mod-dir override layer. | | D329 | **The D318/D320 guard anim pin is a tick-granularity divergence: it exists only at 1 sim tick per rendered frame (port at 60 fps); at 2+ ticks/frame (GE's normal N64 regime) it cannot persist (lockstep A/B, 2026-09-23).** — full `## D329` entry at file tail | FIXED — Rule-2 high-FPS fix in model.c; clk=1 no longer pins (WSL + native Windows), 0 triggers at clk=2; bunker1 golden failure predates it (on/off A/B); user playtest confirms N64-matching Ourumov timing; watchdog stays shipped for now. | +| D330 | **#103 (v0.3.0, Win 11): Auto-Aim / Look Ahead "won't stay on" in the in-game watch options: the mouse drove the analog stick inside the watch, and a small leftward flick selected OFF.** — full `## D330` entry at file tail | FIXED (port/src/input.c), build clean, bunker1 golden 3/3; mouse feel-check owed (a human must run it: select Auto-Aim, set ON, move the mouse; it must stay ON). | Phase 2 replaced the Phase-1 demo loop with the real `mainproc()` on real OS threads, compiled GE's real `src/sched.c`, and brought in PD's fast3d software @@ -13170,3 +13171,23 @@ Logs: `scratch/d318b_{fac,ctrl}_clk{1,2}.log`. Summariser: `scratch/d318b_summ.p **Fix applied (2026-09-23, RULE-2-SIGNOFF: user approved option (c)).** `src/game/model.c` `modelSetAnimFrame2WithChrStuff`, `#ifdef PORT`: when the per-frame loop did not run, progress was requested, and `vb == va`, re-derive `frameb = constrain(va±1)` against the CURRENT endframe (as `modelSetAnimFrame` does); applied only if that next frame is unclamped. Genuine holds, anim ends and looping wraps are untouched. Env: `GE_D329=0` disables (A/B), `GE_D329LOG=1` logs triggers (cached getenv). **Verified (WSL, lockstep):** clk=1 with watchdog off (`GE_D318W=0`): Facility ai_19 and Control ai_9 no longer pin (frame 19→19.5→20…, 0.5/tick = the N64 clk=2 trajectory; aim→fire→re-aim cycle ≈192 ticks vs ≈195 at clk=2/3); authored holds intact. Trigger census: clk=1 Facility, 9 triggers, all on the repro guard, one per attack update; **clk=2: 0 triggers** over ~5300 ticks (Facility) and ~3600 (Control), so the fix is inert in the N64 regime. **Windows-native validation (2026-09-23):** Facility forced repro, native exe, `GE_D318B_CLK=1`, watchdog off: 48 post-update fire episodes, **0 pinned**, 23 D329 triggers. Bunker 1 golden (640x480 pinned, direct launch): REGRESSION with the fix ON (worst dmean 113.6) and equally with it OFF (`GE_D329=0`, 126.4), so it predates this fix (stale golden; re-baseline already owed). **Interactive playtest (2026-09-23, user, native Windows, shipping config + GE_D329LOG):** Ourumov reacts with N64-matching timing (user-confirmed on screen). Log: 4 D329 triggers (four guards, same tick, stale-pin state, released at 0.5/tick), 3 watchdog detections that cleared on their own, 0 watchdog rescues, no crash (`scratch/d329_playtest.log`). **Status:** FIXED + playtest-verified (2026-09-23). Next: demote the D318 watchdog to detect-and-log post-release (M-192 item 7), then remove the TEMP probes (d318bprobe, d320repro). Watchdog stays shipped on `release/v0.4.0`; plan to demote it to detect-and-log after playtests. Cross-ref: D318, D320, D321, M-199, D155, D186/D193. + +## D330 — #103 (v0.3.0, Win 11): Auto-Aim / Look Ahead "won't stay on" in the in-game watch options (2026-09-23) + +**Report:** "Look ahead and Auto-aim both won't remain 'on' in game or in menu options. They simply seem to not work?" + +**Root cause (port input layer, not game logic).** The in-level watch runs with `current_menu == RUN_STAGE`, so `inputComputePad`'s `menuMode` is 0 and the mouse took the in-stage look path. `hipDirectCompute` declines while the watch is open (`!outside_watch_menu || pause_state`, the GEPD `!watch && !pause` gate), and control fell through to the **legacy stick fallback**: `sx += dx * hipSens * MOUSE_TURN_GAIN` (defaults 0.5 × 1.0 × 6 = 3 counts/px/poll; SOLITAIRE also routes mouse Y to `sy`). The watch reads that stick exactly like an N64 stick: +- `options.c game_option_toggle_input`: while an option is selected, stick X < −0x2D selects value 0 (OFF) and > 0x2D selects 1 (ON). `controlstick_lr_enabled` re-arms each time the stick recentres, so every mouse flick of about 15 px per poll re-selects a value. +- With no item selected, the same stick flips watch pages, and mouse Y scrolls rows. + +So a player sets Auto-Aim ON with a key, touches the mouse, and it snaps back OFF. An RMB hold in the watch also ran the GEPD aim writes (`aimGepdCompute` has no watch gate; GEPD itself gates on `!watch`). + +**Ruled out.** Save/load round trip: `fileSaveSettingsForFolder`/`fileLoadSettingsForFolder` pack/unpack symmetrically (`OPTION_AUTOAIM 0x2`, `OPTION_LOOKAHEAD 0x10`). The save runs through the misnamed `deleteCurrentSelectedFolder` → `fileClearSavefileForFolder`, which really means "write the current watch options into the selected folder" and is called on watch close and on abort. The load runs at each stage start (`options.c:415`). This machine's `ge007.eep` has the bits set in every populated folder (`opts=003a`). No port code writes `game_options_entries`, `autoxaimenabled`, `autoyaimenabled` or `automovecentreenabled`. + +**Fix (`port/src/input.c`, port-only, no Rule-2).** A new branch in the mouse block, ahead of the aim/hipfire paths: while in-stage with the watch open or paused, the mouse contributes nothing. Keyboard (A/D = C-left/right, turn keys = stick) and gamepad still drive the watch exactly as on N64. + +**Verification.** Build clean. Bunker 1 golden 3/3 (no-input path unchanged). A runtime check of the mouse path needs real mouse motion (scripted input replaces the whole controller-0 source), so **a human must run it**: in a level, START → Control Options → select Auto-Aim → ON → move the mouse around → still ON; close and reopen the watch → still ON; next level → still ON. + +**Related but not changed (faithful behaviour worth knowing if the "doesn't seem to work" part persists):** look-ahead (`bondview2.c` ~6060) only arms on `analogWalk > 60` (analog stick walking) and cancels on any manual pitch. Under the port's default SOLITAIRE scheme, W/S are digital C-up/C-down and pitch is continuous mouse/right stick, so look-ahead rarely engages. That is N64 1.2/SOLITAIRE behaviour, not a port bug. Auto-aim's crosshair pull is overwritten by the GEPD aim-mode writes while RMB is held, which is the D194 design, but it still applies in hipfire. + +**Status:** FIXED, feel-check owed. Cross-ref: D194, D261, WI-1 (`hipDirectCompute`). diff --git a/docs/porting-notes.md b/docs/porting-notes.md index 8eadeca2..3c24d07e 100644 --- a/docs/porting-notes.md +++ b/docs/porting-notes.md @@ -564,6 +564,14 @@ through a converter or a runtime bswap fixup reads scrambled. - Controller state has one source: `port/src/input.c`. `libultra.c`'s SI section marshals `inputComputePad()` into `g_contPad[]`; it is driven by `osContStartReadData` (per logic tick), no separate `video.c` frame hook. +- **The in-level watch/pause is not a "menu" to `input.c`** (`current_menu` + stays `RUN_STAGE`), but it reads the stick as **discrete** input: ±0x2E + on X picks an option value or flips a page, and Y scrolls rows. Any path + that turns a continuous device (the mouse) into stick deflection must be + gated off there (`!outside_watch_menu || pause_state`), or small hand + motion becomes phantom menu choices. D330/#103: mouse → legacy stick + fallback silently reset Auto-Aim/Look-Ahead to OFF. The direct-write + look paths already gate on this; the fallbacks must too. ## D. N64 hardware idioms fast3d does not emulate diff --git a/port/src/input.c b/port/src/input.c index 639adec1..b9c4b25a 100644 --- a/port/src/input.c +++ b/port/src/input.c @@ -1012,6 +1012,21 @@ unsigned inputComputePad(int idx, signed char *stick_x, signed char *stick_y) "GE_INPUTLOG menuptr abs=%d cursor=(%.1f,%.1f) stick=(%d,%d)", haveAbs, (double)cursor_h_pos, (double)cursor_v_pos, sx, sy); } + } else if (g_CurrentPlayer != NULL && + (!g_CurrentPlayer->outside_watch_menu || + g_CurrentPlayer->pause_state != 0)) { + /* D330 (#103): in-stage watch / pause -- the mouse drives + * nothing. menuMode is 0 here (current_menu is still + * RUN_STAGE) and hipDirectCompute declines on this same gate, + * so the legacy stick fallbacks below used to turn mouse + * motion into stick deflection (and an RMB hold ran the GEPD + * aim writes, which GEPD itself gates on !watch): a ~15 px flick + * crossed the watch's +/-0x2E left/right threshold, which on + * a selected option means "pick OFF/ON" (options.c + * game_option_toggle_input) and otherwise flips watch pages, + * while mouse Y scrolled the rows. Auto-aim / look-ahead + * "wouldn't stay on". Keyboard and pad still drive the watch + * exactly as before. */ } else if (aimHeld) { /* D194 GEPD-mirror aim: direct crosshair/camera writes, no * look stick (see aimGepdCompute). Keyboard turn (sx/sy set