forked from Ports/goldeneye-pc-port
D243 M-170/M-171 + D173 M-172: playanim probe refutes empty-driver theory; first quantified D173 measurement
D243: added a GE_D243M-gated probe on AI_PlayAnimation. Live data shows the abseil's scripted fall animation (decoded anim_id 0xb1) lands on Bond's own real player chr/model (isPlayerChr=1), directly refuting the standing M-145 "empty, model-less driver chr" theory. The animation plays and loops correctly (aidx stable, animframe cycling normally) -- this reframes the whole bug: the render-position "cycling" this session's GE_D243X3/X4 freeze experiments were built to suppress is very likely the animation's own legitimate looping root motion, not corruption. GE_D243X4 (Bond's own render_pos freeze) is flagged as actively wrong and should not ship; GE_D243X3 (camera field_488.pos) is on firmer ground but should ideally damp on loop-wrap, not freeze-forever. Next step (camera-shake vs loop-wrap correlation) needs a fresh unfiltered capture, logged as open. D173: used the same probes to capture the intro/pre-control puppet cutscene autonomously (no player input needed to progress it) on Dam. First-ever quantified measurement: prop->pos.y is rock-steady at the correct ground level (60.3) for the puppet's whole on-screen lifetime, while its rendered root sits ~324-328 units higher throughout -- and the gap barely moves across three different attached animations, arguing against an animation-root-motion cause and pointing at a fixed root-joint/base-transform offset in the puppet's spawn setup (solo_char_load) instead. Measurement only, no code change; capture archived at scratch/d173/ (gitignored, local).
This commit is contained in:
1 parent
8ba1be14f2
commit
6f604c14cf
2 files changed
+56
No files matched your search
@@ -10444,6 +10444,8 @@ crash class C2 (Facility `-level_34`, Runway `-level_35`): `import_texture_i8` A
|
||||
|
||||
**Status (from §F table):** OPEN — observed, not investigated. **M-87 user playtest: still present** — "Bond floating in the sky / cutscene glitch thing still exists." Related, newly reported: **in some cutscenes Bond spins in circles** (named instance: end of Cradle, when Trevelyan falls) — likely the same actor-position/anim-root class rather than a new bug; logged under D148/D160 (`GRAPHICS-BACKLOG.md`) since it's cutscene-triggered. **M-106 investigation (static only — no build/runtime access in this worktree, see note below):** checked the two most D209-shaped candidates for a puppet Y-position aliasing bug and RULED OUT both. (1) `chrSpawnAtPad`/`chrSpawnAtCoord`/`chrAdjustPosForSpawn` (`src/game/chraction.c:10653-10794`) — the puppet-spawn path a cutscene `guard_play_animation`-style AI cmd would use. `PadRecord.stan` (`src/bondtypes.h:1744-1751`) is a ROM-serialized *pointer* field (`StandTile *stan` at offset 0x28) — exactly the tail-pointer shape D122/D126/D132 warn about — and `tools_pc/d88_emit.py`'s `emit_pad()` zeroes it on conversion with a comment claiming it's "dead-on-load, always set before use." **Verified this claim is TRUE, not a converter bug**: every `PadRecord` literal in the N64 source asset tables (checked `assets/obseg/setup/UsetupdamZ.c:37-51`) initialises `stan` to the literal `0` — the ROM binary never carries a live pointer there on N64 either, so zeroing it on PC is byte-identical behaviour, not a regression. `chrAdjustPosForSpawn` receiving a NULL seed `StandTile*` is therefore the original game's normal case, not a port defect. (2) The anim-root-motion Y-accumulation chain (`modelTickAnim`→`modelSetAnimFrame2WithChrStuff`, `model.c:3060+`, `pos24 = pos34 + rotate(pos)` at `model.c:3322`) was already exhaustively audited for exactly this class of bug during D193/D209 (M-80/M-81) and found self-consistent, with the `padding[]`-union sweep that found D209/D210 explicitly noting `act_anim`/`act_dead` (the union arms active during a played-animation/dead puppet) are "layout-stable" (`porting-notes.md` §A, D210 entry) — i.e. that sweep already covers the cutscene-puppet actiontype, and it came back clean. So the two most probable D209-style aliasing sites for this specific symptom are both cold. **Did not find the actual root cause.** Did not get to: the *separate* Cuba end-credits case (`front.c` `init_menu18_displaycast`/`constructor_menu18_displaycast`, `front.c:7821-8320`+ — a **different**, single-actor "cast roster" display model shared by the intro roster and the credits roster, using `getsuboffset`/`subcalcpos`/`cast_rootpos_smoothed` root-motion smoothing, not the in-level chr path at all) — spot-checked `getsuboffset` (`model.c:684-700`) and it is `#MATCH`-verified clean, but did not trace the smoothing math or confirm this is even the code path the "JB above Natalya" report hits (that report implies two actors on screen at once, which this single-`cast_model` code doesn't obviously support — may be a distinct, not-yet-located two-actor cutscene-puppet path). **Confidence: LOW-MEDIUM that this is the D209-style struct-aliasing family at all** — the two most likely aliasing sites check out clean, which argues *against* the shared-root-cause theory for D173 specifically (unlike D160, where the analogous check also came back clean but for a different reason — no pointer fields to alias in the first place). Next investigator: get a live Cradle (`-level_41`) capture of Trevelyan's chr during the death sequence with a position-logging probe (none shipped yet — would need a new `#ifdef PORT` env-gated trace, not added this session per the no-build-verification constraint), and separately locate the actual two-actor Cuba credits code path (grep for wherever `intro_char_table`/cast display is invoked with two simultaneous models, if it exists — not found this session). **M-141: no new lead this session** (see D243's M-141 entry) — checked whether D243's stale-memory front-end/menu bug could share a mechanism with this puppet-position bug; no code-level link found, kept as an unconfirmed hypothesis to watch for during the next live cutscene capture, not a new suspect to chase on its own. **M-141: Steam Deck playtest confirmation** — user reports Bond floating at level start on both Dam and Facility ("chemical plant"), "but fine when it moves to first person" (matches this row's own framing exactly: player spawn/first-person is correct, only the third-person render-side puppet floats). No new mechanism; corroborates the bug is general across levels, not Dam-specific. **User confirms D292** (Bond sunk into the floor on death) **is the same underlying bug**, a third instance alongside level-start floating and the Cradle cutscene spin — see D292 for the consolidated cross-reference. **M-145 (see D243):** the Dam end-cutscene double-Bond + camera shake is now traced to the same family — the exit script moves Bond's own third-person model vLine truncated
|
||||
|
||||
**M-172 (2026-09-17, this session, autonomous/no live human needed): first-ever quantified, reproducible measurement of the level-start float, isolated to the render side and ruled independent of which animation is attached.** The intro/pre-control puppet cutscene runs unattended (`CAMERAMODE_INTRO`/`SWIRL`, no player input required to progress), so `GE_D243M`'s existing tick/onscreen probes — built for D243, this session — could just be pointed at it directly: launched `-level_33` (Dam) with `GE_D243M=1` only (no X3/X4), captured the whole intro-puppet lifetime headlessly (`scratch/d173/m172_dam_intro_capture.log`, CREATE frame=427 → REMOVE frame=924, 499 `onscreen` samples for the tracked chr). **Result: `prop->pos.y` is rock-steady at 60.3 for the entire window (never changes) — this is the correct ground-level Y — while the `onscreen` probe's actual rendered root Y sits at 384–389 the whole time, a consistent ~324–328 unit gap.** Camera-side fields (`f488pos`/`f3C4`, the tick probe) track `prop->pos` exactly (`20197.7,60.3,16902.1`, correct) — so **the camera/game-logic position is right; only the model's own rendered skeleton root is floating**, and by a near-constant amount. Critically, **the puppet cycles through three distinct animations during this window** (`aidx=61`→`106`→`99`, `actiontype` 23→1→3) **and the Y-gap barely moves (388.4 → 388.8 → 384.3)** — this argues against "wrong/stale animation root motion" (M-160/M-164/M-165's leading theory below) as the primary driver here, since a genuinely animation-root-motion-driven offset should track *which* animation is attached, not stay flat across three different ones. `getsuboffset`/`setsuboffset` themselves are `#MATCH`-verified decomp-verbatim (`model.c:684/705`) — not the bug site. **Leading candidate now: a fixed root-joint/base-transform offset baked into how the puppet's skeleton is set up at spawn** (`solo_char_load`, `bondview2.c:401`, not yet traced in full — a long function, stopped after confirming the accessor functions are clean rather than push further without a specific lead to chase). **Next, concrete:** trace `solo_char_load`'s model/animation initialization order for wherever the root joint's rest-pose offset would normally get zeroed/corrected against `prop->pos`, and compare against how a normal in-level walking guard (whose third-person render is known-correct) initializes the same fields — the flat, animation-independent offset is the strongest lead D173 has had. No build/game-code changes made this pass — measurement only.
|
||||
|
||||
**M-160/M-164/M-165 (2026-09-17, see D243): the shared mechanism is now understood in much more depth, though still not confirmed as the actual PC/N64 divergence.** D243's investigation this session traced the general pattern this whole family plausibly shares: `chrUpdateAnim` (`chr.c:2398-2424`) doesn't move a puppet's world position directly — it advances whatever animation is currently attached (`modelTickAnim`), then reads the model's root-joint transform back into `prop->pos` via `getsuboffset` (`model.c:684`). When something else (a teleport, a spawn, a death transition) moves the puppet's *logical* position without also resetting/updating its *animation state* to match, the next `chrUpdateAnim` call overwrites the new position with wherever the stale animation's root motion happens to be — producing exactly "floats/sinks/shakes relative to where it should be, in a way that tracks the animation, not the intended position." This is a strong candidate mechanism for D173's level-start float (an animation not yet settled/attached correctly at spawn) and D292's death-sink (the death-pose transition possibly not resetting the model's root anchor the way the collision/death logic expects) — **but D243's own parallel investigation (M-165) found this same mechanism may be entirely decomp-faithful**, with the actual PC-specific divergence being *which phase* of an animation a chr happens to be on at the moment of transition (inherited from real-time-dependent prior gameplay, not deterministic) rather than a code-path difference. **Do not treat this as solved** — it reframes what to look for (an animation-identity/phase mismatch at the transition moment, not a position-write bug per se) and gives a concrete technique (`GE_D193A`'s `aidx=` reverse-resolve + a `model->animframe1` phase read, see D243 M-166) for whoever gets live access to test D173/D292 the same way D243's `GE_D243M`/`GE_D243X2` probes now do.
|
||||
|
||||
## D179 — Packaged build crashes after the logos — the D43/D69 model + bg sidecars are ROM-derived and absent from any build that has no ROM (M-34, alpha-release QA).
|
||||
@@ -11008,6 +11010,22 @@ This entry is long and multi-pass (M-107 through M-168). This block is the singl
|
||||
|
||||
**Next (live, needs the user):** `GE_D243M=1 GE_D243X3=1 GE_D243X4=1`, play Dam to the abseil, watch for (a) whether the camera now looks the right direction across all three shots (M-167's regression this was meant to fix) and (b) whether Bond's body still looks stuck/duplicated across shots. Send the log — `x3skip=`/`epoch=` fields make the re-baseline timing directly readable without eyeballing alone (same "don't judge by eye alone" lesson M-161 already taught this bug). If this closes both the shake and the duplication cleanly, the next real step is turning X3/X4 from gated experiments into the shipped default (drop the `GE_D243X3`/`GE_D243X4` getenv gates, keep only the `d243mProbeActive()`/`CAMERAMODE_POSEND` gate) — do not do that until live-confirmed.
|
||||
|
||||
### M-171 (2026-09-17, same session, live-verified): M-145's "empty driver, no model to animate" claim is WRONG — the abseil's scripted animation lands on Bond's real player chr and plays correctly; the actual bug is very likely camera-side overreaction to a legitimate looping animation, not a corrupted/stale render position
|
||||
|
||||
**User pushback that triggered this:** after seeing the M-169 (X3/X4 freeze) build live, the user reported the camera now cuts correctly but Bond "stays still" — and clarified, when asked, that on real N64 Bond visibly "stands on the edge and then jumps... camera tracks him as he descends," repeated for all 3 shots (this is their memory of the real game, not what they saw in the build). That directly contradicts M-145's claim that `guard_play_animation`'s target has no model to animate — worth checking empirically rather than trusting a 2026-09-16 static-only finding.
|
||||
|
||||
**Probe added (`chrai.c`, `AI_PlayAnimation` case, `GE_D243M`-gated, cheap):** logs `ChrEntityp`'s chrnum/model/whether it equals `g_CurrentPlayer->prop->chr`, on every `AI_PlayAnimation` dispatch. First pass filtered on `anim_id==0xb100` (the script's literal) and got **zero hits** despite the cutscene clearly running (3 successful teleports, `GE_D243X3`/`X4`'s epoch counter 0→3) — a real, reportable negative result, not a dead end: it meant the filter itself was wrong, not that the case never fires. Removed the filter for a second live capture (same session, same user).
|
||||
|
||||
**Result: `guard_play_animation`'s decoded `anim_id` is `0xb1` (177), not `0xb100` (45312) — the low byte is 0 in the script's literal but `ntohs()` only sees the field as a plain 16-bit value, so `0xb100` really does decode to `0xb1`... more precisely, 177 is `0xb1`, and `0xb100 == 0xb1 << 8` — this is a **display/reading gotcha in this write-up, not a game bug**: the script literal `0xb100` was always going to decode to itself (45312) if genuinely read as a full 16-bit value, so getting `0xb1` back means the true wire value differs from the source literal by an 8-bit shift somewhere in the asset pipeline's encoding of `guard_play_animation`'s first argument — not chased further this session, doesn't matter for the actual finding below (worth a note in `porting-notes.md` if it recurs elsewhere).** What matters: **`chr=00000000701dbebc chrnum=5037 model=00000000702644dc isPlayerChr=1`** — this chr IS `g_CurrentPlayer->prop->chr` (the exact pointer-equality check), **with a real, valid model pointer.** M-145's claim #1 ("the exit script runs on an invisible, model-less false-GUARD chr... `guard_play_animation` acts on the empty driver record") is **REFUTED** by live data, at least for this specific call. (M-145's claim #2 — Bond's visible chr is teleported by `chr_try_teleporting_to_pad` — stands; both claims were about different parts of the same script and aren't mutually exclusive, but claim #1 is simply wrong.)
|
||||
|
||||
**Cross-referencing against the same capture's `D243M: onscreen` probe (`aidx=`/`animframe=`, M-166's fields) confirms the animation is genuinely, continuously playing, not stuck or corrupted:** `aidx=177` (matches the decoded `0xb1`) is the sole active index throughout the whole POSEND window (1077 consecutive `onscreen` samples, no other index interleaved), and `animframe=` cycles smoothly through a repeating pattern (0.00 → 87.78 → 175.86 → 211.33 → 0.00 → ...) — a normal **looping** animation advancing tick over tick, not a frozen or corrupted value.
|
||||
|
||||
**This reframes the whole bug.** M-153/M-160's "period-4 cycle through fixed world points" — the finding that originally looked like corruption — is now much better explained as **the intentional root motion of a short animation clip playing on loop for the duration of each held shot** (a common cutscene technique: loop a few seconds of "falling" motion under a static camera to imply a longer continuous descent). If so, `chrTick`'s per-tick `chrUpdateAnim`/`modelTickAnim` write to `prop->pos`/`render_pos` (items 2/3 in the CONSOLIDATED NEXT STEPS above) isn't corrupting anything — it's correctly playing a real, intended, looping animation. **The actual visible "shake" is much more likely the camera's own leaky-integrator look-at filter (`field_3B8`/`field_3C4-8-C`, M-143) overreacting every time the loop wraps** — chasing a legitimate periodic root-motion reset as if it were erratic motion to smooth away, producing the "violent shake/spazz" symptom, rather than the position itself being wrong.
|
||||
|
||||
**Consequence for this session's own X3/X4 fix: `GE_D243X4` (freezing Bond's own `render_pos`) is very likely the WRONG tool and should not ship** — it suppresses a real, correctly-looping, decomp-faithful animation, which is exactly why the user saw Bond "stay still" instead of visibly falling. `GE_D243X3` (freezing the camera's `field_488.pos` copy, with M-169's teleport-epoch re-baseline) is on more solid ground — it targets the camera's *reaction* to the loop, not Bond's own motion — but given this new understanding, a smarter fix would specifically damp the filter's response to a same-shot animation-loop wrap (a real, identifiable event — the `animframe` reset) instead of either freezing forever or only re-baselining at teleports.
|
||||
|
||||
**Not yet attempted, next concrete step:** confirm the loop-wrap correlates with the visible shake directly — correlate `animframe`'s reset-to-0 moments (already logged) against `field_3B8`/`field_3C4-8-C`'s largest per-tick deltas in the *pre-fix* (`GE_D243X3`/`X4` both unset) capture data this session already has (`ge007_d243_m169_capture_run1/2/3.log`, `ge007_d243_m170_capture_run1.log` — none of those had X3/X4 forcing anything, so the raw shake should be visible in them) — this is pure log analysis, no live access needed. If confirmed, the real fix is a filter-side damping keyed on animation-loop-wrap, replacing X3 entirely; `GE_D243X4` should be dropped, not shipped. **Do not re-enable `GE_D243X4` by default without redoing this analysis** — it currently does real harm to a working decomp-faithful animation.
|
||||
|
||||
### Applies to the wider family too
|
||||
|
||||
D173 (level-start floating), D292 (death sinking), and the Cradle spin (also under D148) are cross-referenced as the same "third-person world model transform/lifecycle" family (M-145, and the M-160/M-164/M-165 addendum on D173/D292's own entries). The general lesson from item 1 above — trace the actual render-consumer's read site, don't assume the field you found a bug in is what's on screen — and the technique from item 5's postmortem — never make a pointer's validity contingent on guessing from its value or a cached identity, when the thing that keeps it valid can simply always be allowed to run — both apply directly if either of those bugs gets picked up next.
|
||||
|
||||
@@ -1079,6 +1079,44 @@ void ai(PropDefHeaderRecord *Entityp, PROP_TYPE EntityType)
|
||||
endframe = -1;
|
||||
}
|
||||
|
||||
#ifdef PORT
|
||||
/* D243 M-170: which chr does a scripted guard_play_animation
|
||||
* actually land on during the Dam abseil (anim_id 0xb100)?
|
||||
* M-145 (static-only) claimed this targets an empty,
|
||||
* model-less "false GUARD" driver chr -- but that theory's
|
||||
* own stated binding rule (ailist ID >= 0x1000) doesn't
|
||||
* apply to ai_17's registered ID (0x412), and the user's
|
||||
* live memory of the real N64 cutscene has Bond visibly
|
||||
* jumping/falling with the camera tracking him, which is
|
||||
* hard to reconcile with "no model to animate." One-shot
|
||||
* empirical check: log ChrEntityp's chrnum + whether it
|
||||
* has a model + whether it IS the visible player chr,
|
||||
* every time this specific anim_id fires. Env-gated
|
||||
* (GE_D243M, already cached elsewhere), essentially free
|
||||
* when unset. Diagnosis only. */
|
||||
if (getenv("GE_D243M") != NULL)
|
||||
{
|
||||
/* M-170 correction: the first version of this probe
|
||||
* filtered on anim_id==0xb100 and got ZERO hits
|
||||
* across a full Dam-abseil capture that otherwise
|
||||
* clearly exercised ai_17 (3 successful teleports,
|
||||
* GE_D243X3/X4's epoch counter went 0->3). Rather
|
||||
* than guess why, log every AI_PlayAnimation call
|
||||
* unfiltered so the next capture shows what anim_id
|
||||
* values (if any) actually arrive at this case
|
||||
* during the cutscene -- confirms/refutes whether
|
||||
* the filter itself was wrong (byte-order, wrong
|
||||
* literal) or the case is never reached at all for
|
||||
* this cutscene's actor. */
|
||||
osSyncPrintf("D243M: playanim anim=0x%x chr=%p chrnum=%d model=%p "
|
||||
"isPlayerChr=%d\n",
|
||||
(unsigned) anim_id, (void *) ChrEntityp,
|
||||
ChrEntityp ? (int) ChrEntityp->chrnum : -1,
|
||||
ChrEntityp ? (void *) ChrEntityp->model : NULL,
|
||||
(int) (ChrEntityp && g_CurrentPlayer && g_CurrentPlayer->prop
|
||||
&& (void *) ChrEntityp == (void *) g_CurrentPlayer->prop->chr));
|
||||
}
|
||||
#endif
|
||||
if (ChrEntityp)
|
||||
{
|
||||
check_if_able_to_then_perform_animation(ChrEntityp, anim_id, startframe, endframe, ai->BITFIELD, ai->INTERPOL_TIME60);
|
||||
|
||||
Reference in new issue
Block a user