fix(D210): chrToPatrol leaves act_patrol.lastvisible60 uninitialised on PC

Sibling of D209 (A1 raw-byte union-alias class). chrToPatrol writes
act_init.padding[0x13] (union byte 0x4c) = -1, which is act_patrol.lastvisible60
on N64. act_patrol starts with a patrol_path* that widens 4->8 on x86-64, so
0x4c now lands in waydata and lastvisible60 (@0x50) stays uninitialised. The
patrol 'haven't seen the player recently' gate (chraction.c:~9347) then reads
garbage on the first ticks after entering PATROL.

Fix: write the named field under #ifdef PORT; N64 line verbatim under #else.
ABI/pointer-width only, no game-logic change.

Found by the post-D209 A1 sweep; the only new instance (act_anim/act_dead
padding aliases are pointer-free -> layout-stable).

Build clean; bunker1 + facility verify PASS (both patrol-heavy), no crash.
findings D210 + porting-notes A1 + findings-index regen.
This commit is contained in:
James Dansereau committed 2026-09-08 20:51:18 -04:00
1 parent 97a09665a1
commit b47eea65e3
4 files changed
+25

No files matched your search

+12
View File
@@ -68,6 +68,18 @@ for hardcoded indices into any `padding[]`/`u8[]` union arm.
walk pace on every level. No crash, no log, no NaN — it took a runtime
probe comparing the *commanded* tier (named field, correct) against the
*bound* animation (alias, wrong) to see it.
- **D210** (sibling, found by the post-D209 sweep): `chrToPatrol` inits
`act_patrol.lastvisible60` via `act_init.padding[0x13]` (union byte `0x4c`).
`act_patrol` starts with a `patrol_path *path` — widens 4→8, so `padding[0x13]`
no longer hits `lastvisible60` (now `0x50`); it lands in `waydata` and
`lastvisible60` is left uninitialised → the patrol "haven't seen the player
recently" timer reads garbage for the first ticks. Fix: named field under
`#ifdef PORT`. **NB the alias need not land on a *pointer byte*** (as D209
did) — here it lands on a plain float that just isn't the field intended.
The tell is the same: a hardcoded index into `padding[]` right next to
named writes of the *same* arm. The rest of the `padding[N]` sweep
(`chrlvTickAnim`/`chrlvTickDead` → `act_anim`/`act_dead`, both pointer-free)
is layout-stable.
**Lesson.** When a value reads as a clean constant (0, 1) rather than
garbage, a pointer byte is a prime suspect — the high bytes of a low-4GB