fix(#187): drop the "Name == Door" special-case — register any entity with a resolvable MotionTableId
The door-swing animation rescue (GameWindow.cs:3897, for entities whose rest pose
is a static single frame but which still carry a reactive MotionTable) was gated on
an exact display-name string match. Sliding doors, gates, portcullises, and disguised
secret-passage props ("Magic Wall") all fail that check because their in-game Name
isn't literally "Door" -- so ACE's UpdateMotion for them was silently dropped forever
by the _animatedEntities.TryGetValue bail-out in OnLiveUpdateMotion.
Retail's own dispatch chain (ACCObjectMaint::CreateObject -> CPhysicsObj::
set_description -> SetMotionTableID -> CPartArray::SetMotionTableID 0x005186e0 ->
MotionTableManager::PerformMovement) is unconditionally data-driven: the only gate
for creating a motion dispatcher anywhere in that chain is "motion table id != 0" --
no CDoor class, no WeenieType switch, no name check. Production weenie data confirms
Sliding Door / Portcullis / Gate / Magic Wall all carry the identical WeenieType=Door
+ non-zero-MotionTableId shape as a plain "Door", differing only in display name.
Fix: the branch's existing `mtableId != 0` check (already computed one line later)
is now the entire gate, matching retail exactly. IsDoorSpawn deleted (dead code);
IsDoorName kept only for an unrelated diagnostic log-label filter.
Live-verified: sliding doors now animate open/closed correctly. Full regression
green (App 741 / Core 2631).
docs(#188): file the fading-wall render gap surfaced during #187's live gate
A "Pedestal Weak Spot" secret-passage door dispatches correctly (proving #187's fix
reaches it) but never visibly changes. Decoded its actual dat MotionTable directly
(0x090000F9): its open cycle carries EtherealHook + TransparentPartHook +
SoundTableHook -- a translucency-fade effect, not part-transform motion. acdream's
IAnimationHookSink documents these hook types as intended for "GfxObjMesh / renderer
state mutations" but no sink anywhere consumes them (only Particle/Lighting/Audio are
wired) -- confirmed via full-repo grep. Collision already works correctly via a
separate server-authoritative SetState wire message, independent of the animation
hook. This is feature-shaped rendering work (a per-part runtime alpha under the
mandatory N.5 bindless pipeline), not a quick fix -- filed for its own design pass.
Kept Issue188FadingDoorMotionTableInspectionTests.cs as a reusable MotionTable/hook
decoder for future "why doesn't this animate" questions.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
0efa7ed2a1
commit
e2e285b855
3 changed files with 271 additions and 47 deletions
107
docs/ISSUES.md
107
docs/ISSUES.md
|
|
@ -46,42 +46,93 @@ Copy this block when adding a new issue:
|
|||
|
||||
---
|
||||
|
||||
## #187 — Non-hinged doors (sliding doors, "fading wall" gates) don't play their open animation
|
||||
## #187 — [DONE 2026-07-08] Non-hinged doors (sliding doors, gates) don't play their open animation
|
||||
|
||||
**Status:** OPEN — under investigation
|
||||
**Status:** CLOSED 2026-07-08 — user-confirmed live gate: "sliding doors now work."
|
||||
**Severity:** MEDIUM (visual correctness; collision + interaction already work — #137)
|
||||
**Filed:** 2026-07-08
|
||||
**Component:** render — entity animation registration (`GameWindow.cs` live-spawn dispatch)
|
||||
|
||||
**Description (user):** door COLLISION is confirmed fixed (#137) — all doors can be clicked
|
||||
and passed through correctly. But not all door TYPES play their open animation: normal
|
||||
hinged doors swing open, but sliding doors and "fading wall" style dungeon gates do not
|
||||
animate — the player passes through as if the door/wall just isn't there.
|
||||
**Root cause (CONFIRMED via retail decomp + a live weenie-data survey):**
|
||||
`GameWindow.cs:3897` registered the reactive-motion-table rescue sequencer only when
|
||||
`spawn.Name == "Door"` (an exact display-name string match). Retail's own client-side
|
||||
motion dispatch chain (`ACCObjectMaint::CreateObject` → `CPhysicsObj::set_description` →
|
||||
`SetMotionTableID` → `CPartArray::SetMotionTableID` 0x005186e0 → `MotionTableManager::
|
||||
PerformMovement`) is unconditionally data-driven — the only gate anywhere in that chain
|
||||
is "does this object have a non-zero MotionTableId" (`if (ebx != 0)`); there is no
|
||||
`CDoor` class and no name/type check. Production weenie data confirms Sliding Door /
|
||||
Portcullis / Gate / "Magic Wall" all carry the identical WeenieType=Door +
|
||||
non-zero-MotionTableId shape as a plain "Door", differing only in display name — so the
|
||||
name-string gate silently dropped every door-like object not literally named "Door".
|
||||
|
||||
**Suspected root cause (pre-investigation read, NOT yet decomp-verified):**
|
||||
`GameWindow.cs:3897` registers the door-swing sequencer only when
|
||||
`IsDoorSpawn(spawn)` (`GameWindow.cs:3128`: `spawn.Name == "Door"`, an exact
|
||||
display-name string match). The branch above it (`:3818`) already handles ANY entity
|
||||
generically via its resolved idle cycle (multi-frame, framerate != 0) — no name check.
|
||||
Doors need the special branch only because their REST pose is a static single frame
|
||||
(fails the generic gate) while their MotionTable still carries reactive On/Off cycles
|
||||
ACE drives via `UpdateMotion`. That "static rest + reactive motion-table cycle" shape
|
||||
is not unique to hinged doors — any sliding/lever/gate prop with the same shape would
|
||||
need the identical rescue, but only literal name `"Door"` gets it. Retail's own decomp
|
||||
has ZERO `CDoor`-class or door-specific dispatch (only `ObjCollisionProfile::SetDoor`,
|
||||
a collision-only concept) — suggesting retail's client-side animation dispatch is
|
||||
entity-type-agnostic (MotionTable-driven), and the name-string gate is an acdream-only
|
||||
divergence, not a port of anything retail does.
|
||||
**Fix:** `GameWindow.cs:3897` — dropped the name check; the branch's existing
|
||||
`mtableId != 0` test (already computed one line later) is now the entire gate, matching
|
||||
retail exactly. `IsDoorSpawn` deleted (dead code); `IsDoorName` kept only for an
|
||||
unrelated diagnostic log-label filter. Full regression green (App 741 / Core 2631).
|
||||
Live-verified: sliding doors now animate open/closed correctly.
|
||||
|
||||
**Open question for the investigation:** are "fading wall" gates even MotionTable/
|
||||
skeletal objects, or a structurally different mechanism (an ObjectDescription /
|
||||
translucency-toggle triggered by a script, no motion involved)? Needs dat/weenie-data
|
||||
survey before designing the fix — must not just add more name strings (same class of
|
||||
bug, just deferred).
|
||||
**Scope note:** the investigation also surfaced a SEPARATE, deeper gap — some
|
||||
door-family objects (confirmed: "Pedestal Weak Spot", a fading-wall secret passage)
|
||||
don't use ordinary part-transform motion at all; their open cycle is a translucency-fade
|
||||
+ ethereal-toggle effect that acdream has no rendering sink for. That is NOT a
|
||||
registration problem (this fix's dispatch reaches it correctly) — filed separately as
|
||||
**#188**.
|
||||
|
||||
**Acceptance:** sliding doors + fading-wall gates play their open/close transition
|
||||
matching the door's actual open/closed wire state, same as hinged doors today. Hinged
|
||||
doors keep working (no regression). Suites green.
|
||||
---
|
||||
|
||||
## #188 — "Fading wall" secret passages don't visually fade (missing TransparentHook/EtherealHook render sink)
|
||||
|
||||
**Status:** OPEN
|
||||
**Severity:** MEDIUM (a real but narrow class of dungeon secret-door objects; collision
|
||||
already correct via a separate wire channel)
|
||||
**Filed:** 2026-07-08 (surfaced during #187's live gate)
|
||||
**Component:** render — animation hook dispatch (`IAnimationHookSink` / render-state sinks)
|
||||
|
||||
**Description (user):** a "fading wall" style secret-passage door ("Pedestal Weak Spot")
|
||||
can be used and passed through, but never visibly changes — no fade, no motion, nothing.
|
||||
Confirmed distinct from #187 (registration/dispatch already reaches this entity
|
||||
correctly).
|
||||
|
||||
**Root cause (CONFIRMED via a live dat decode of the actual entity, not inference):**
|
||||
loaded the real MotionTable (`0x090000F9`) for the live-identified "Pedestal Weak Spot"
|
||||
(guid `0x7C95B03B`) and dumped its open cycle's animation hooks directly — the cycle
|
||||
(`anim 0x03000919`, 10 frames) carries **`EtherealHook`, `TransparentPartHook`,
|
||||
`SoundTableHook`** — i.e. this door type's "open" isn't skeletal motion at all, it's a
|
||||
per-part translucency fade + a collision-passthrough toggle + a sound cue, fired as
|
||||
keyframe hooks during the animation.
|
||||
|
||||
`src/AcDream.Core/Physics/IAnimationHookSink.cs`'s own doc comment documents
|
||||
`TransparentHook`/`NoDrawHook`/`ScaleHook`/`ReplaceObjectHook`/etc. as intended to route
|
||||
to "GfxObjMesh / renderer state mutations on the target entity" — but only three sinks
|
||||
are ever registered (`_particleSink`, `_lightingSink`, `_audioSink`; `GameWindow.cs`
|
||||
~1414-1441). No sink anywhere in the codebase pattern-matches `TransparentHook` /
|
||||
`TransparentPartHook` / `EtherealHook` / `NoDrawHook` / `ScaleHook` /
|
||||
`ReplaceObjectHook` (verified by a full-repo grep) — the sequencer correctly parses and
|
||||
fires these hooks every tick, the router fans them out, and all three registered sinks
|
||||
silently ignore them. Nothing crashes (the router swallows exceptions per-sink); nothing
|
||||
renders differently either.
|
||||
|
||||
**Confirmed NOT a collision bug:** the object's ethereal/collision-passthrough state is
|
||||
applied correctly via a SEPARATE server-authoritative wire message (`SetState` →
|
||||
`OnLiveStateUpdated`, `GameWindow.cs:5517`) — independent of the animation-hook
|
||||
mechanism. `EtherealHook` firing client-side during the animation is very likely a
|
||||
redundant/cosmetic signal in retail's own design; the real remaining gap is purely
|
||||
**`TransparentPartHook` → no visible fade**.
|
||||
|
||||
**Scope (not yet designed):** implementing this touches the render pipeline (a per-part
|
||||
runtime alpha under the mandatory N.5 bindless/MDI pipeline — see
|
||||
`memory/reference_modern_rendering_pipeline.md` for the existing SSBO layout
|
||||
constraints) — this is feature-shaped work, not a one-line fix. Needs its own design
|
||||
pass (grep retail's `TransparentHook::Execute`/`SetTranslucency2`/`SetPartTranslucency`
|
||||
decomp for the exact interpolation semantics) before implementation.
|
||||
|
||||
**Apparatus (kept):** `tests/AcDream.Core.Tests/Physics/Issue187FadingDoorMotionTableInspectionTests.cs`
|
||||
— reflects the real `DatReaderWriter` hook-type shapes + decodes a live MotionTable's
|
||||
hook contents directly (no guessing). Reusable for any future "why doesn't this animate"
|
||||
question — just swap the MotionTableId.
|
||||
|
||||
**Acceptance:** the Pedestal Weak Spot (and similar fading-wall objects) visibly fades
|
||||
out/in when triggered, matching retail. No regression to #187's fix or any other door type.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue