diff --git a/docs/dev/findings-index.csv b/docs/dev/findings-index.csv index 7297a7e1..a28c057c 100644 --- a/docs/dev/findings-index.csv +++ b/docs/dev/findings-index.csv @@ -208,3 +208,4 @@ D327,RESOLVED,"Mod support, resolved plan:",RESOLVED — superseded by D328; app 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:" +D331,FIXED,Ejected shell casings never rendered on PC:,"FIXED (gunfire.c #ifdef PORT, explicit template); PP7 casing verified on native Windows (before:" diff --git a/docs/dev/findings.md b/docs/dev/findings.md index b0db4b93..ef0341a4 100644 --- a/docs/dev/findings.md +++ b/docs/dev/findings.md @@ -616,6 +616,7 @@ covers D24–D69; the log continues in §H (D32 procedure, D70–D121). | 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). | +| D331 | **Ejected shell casings never rendered on PC: `g_DefaultCasingModelRenderData` (u32[15] template) read as the wider PC `ModelRenderData` gave flags 0 (third instance of the D264 pattern; found by the D290 sibling sweep, never user-reported).** — full `## D331` entry at file tail | FIXED (gunfire.c `#ifdef PORT`, explicit template); PP7 casing verified on native Windows (before: none; after: brass casing ejects and tumbles); golden 3/3; Win + Linux builds clean. | 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 @@ -13204,3 +13205,7 @@ So a player sets Auto-Aim ON with a key, touches the mouse, and it snaps back OF **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`). + +## D331 — Ejected shell casings never rendered on PC (D264 pattern, third site) (2026-09-23) + +Found by the sibling-site sweep that D290 prompted (porting-notes §B "one layout bug often has sibling call sites"); never user-reported, likely because a missing casing is easy to overlook. `sub_GAME_7F068EC4` (`gunfire.c`, casing draw) initialises its render data with `*(ModelRenderData *)g_DefaultCasingModelRenderData`, where the global is `u32[15] = {0, 1, 3, 0…}` (`gun.c`): the N64 template basemtx NULL, zbufferenabled TRUE, flags 3. On PC `basemtx` is 8 bytes, so the words shift: zbufferenabled = 3, **flags = 0**, and `subdraw()` gates off every node. The copy also reads past the 60-byte array into whatever the linker put next. **Fix (`#ifdef PORT`, ABI/layout class, no Rule-2):** the explicit template, as in D264/D290. **Verified (native Windows, headless):** `-level_09`, `GE_INPUTSCRIPT="1000:Z;1020:Z"`, `GE_PCDUMP=850-1000:3`, shots at frames ~970/991. Before: impact sparks only, no casing. After: a brass casing ejects right of the PP7 and tumbles across frames 973–979. Bunker 1 golden 3/3; Windows + Linux builds clean. Sweep status: every `*(ModelRenderData *)` read in `src/game` is now covered (D215 :1592, D264, D290, D331). Cross-ref: D264, D290, porting-notes §A1/§B. diff --git a/src/game/gunfire.c b/src/game/gunfire.c index 026cee77..0e53f3b9 100644 --- a/src/game/gunfire.c +++ b/src/game/gunfire.c @@ -5761,7 +5761,19 @@ void sub_GAME_7F068EC4(CasingRecord *casing, Gfx **gdl) ModelFileHeader *model_header = casing->header; RenderPosView *model_matrices = dynAllocate(model_header->numMatrices * sizeof(RenderPosView)); ModelHead model; +#ifdef PORT + /* D331 (ABI/layout class; D264/D290 pattern): g_DefaultCasingModelRenderData + * is a u32[15] {0, 1, 3, 0..} holding the N64 ModelRenderData template + * (basemtx NULL, zbufferenabled TRUE, flags 3). Read as the PC struct + * (8-byte basemtx) it gives zbufferenabled 3 / flags 0 -- subdraw() then + * draws nothing, so ejected shell casings were invisible -- and the read + * runs past the 60-byte array. Use the explicit template. */ + ModelRenderData render_data = {0}; + render_data.zbufferenabled = TRUE; + render_data.flags = 3; +#else ModelRenderData render_data = *(ModelRenderData *)g_DefaultCasingModelRenderData; +#endif Mtxf casing_model_mtx; s32 axis_offset; s32 matrix_translation_in_range = TRUE;