docs(B.4c): correct handoff fabrications surfaced by final review

Opus final review of B.4c flagged that Task 4's handoff doc invented
implementation details that don't exist in the code:

1. IsDoorSpawn claimed to check "spawn.WeenieObj.WeenieType == 8 OR
   IsDoorName(spawn.Name)" — the actual code is just IsDoorName(spawn.Name)
   delegating to "name == "Door"". No WeenieType lookup exists.

2. A "_doorSequencers" per-door dict was referenced in three places — that
   dict doesn't exist. The actual code reuses the existing
   _animatedEntities[entity.Id] dict (same one that holds creatures + the
   player), with Animation = null! per the existing pattern at line 7885.

3. The UM dispatch path was described as a new B.4c-added branch with
   pseudocode — that's wrong. B.4c does NOT add a new dispatch path;
   OnLiveMotionUpdated's existing TryGetValue against _animatedEntities
   handles doors automatically once Task 1's spawn-time branch registers
   them. The only UM-dispatch B.4c contribution is the [door-cycle]
   diagnostic line, gated on IsDoorName.

Corrects sections "At world load (spawn time)", "When the door opens",
"Per-frame mesh rebuild", and "Door types covered" to reflect the actual
shipped code. cmd→motion mapping (cmd=0x000C → open, cmd=0x000B → close)
left as-is — it was correct.

No code change. Verified by re-reading GameWindow.cs IsDoorSpawn /
IsDoorName helpers, the Task 1 spawn-time branch body, and the
TickAnimations sequencer dispatch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-05-14 08:03:45 +02:00
parent ebdbf821dc
commit 8bb81db659

View file

@ -66,9 +66,11 @@ happens inside `GameWindow.OnLiveEntitySpawnedLocked`, which branches on
### At world load (spawn time)
1. `IsDoorSpawn(spawn)` — checks `spawn.WeenieObj.WeenieType == 8` (the
`Door` weenie type) OR `IsDoorName(spawn.Name)` (fallback for servers that
tag door-weenies with non-8 types). If true, the entity is a door.
1. `IsDoorSpawn(spawn)` — delegates to `IsDoorName(spawn.Name)`, which
returns `name == "Door"`. Detection by server-sent name string only.
Cheap, exact, no WeenieType lookup. If a future ACE localizes "Door"
or sends a different name, those entities silently won't animate —
acceptable per B.4c's "doors only at English Holtburg" scope.
2. **Initial state seed** — the door's `PhysicsState` from `spawn` carries the
open/closed bit. The code reads `spawn.PhysicsState` (or
@ -83,8 +85,13 @@ happens inside `GameWindow.OnLiveEntitySpawnedLocked`, which branches on
var cycleCmd = isOpen ? MotionCommand.On : MotionCommand.Off;
sequencer.SetCycle(style, (uint)cycleCmd, speed: 0f);
```
The sequencer is registered in a new per-door side-dict on `GameWindow`
keyed by `entity.Id`. At first `Advance(dt)` call, it produces the correct
The fully-initialized `AnimatedEntity` (with the seeded `Sequencer`) is
registered into the existing `_animatedEntities` dict keyed by `entity.Id`
— same dict that holds creatures and the player. `Animation = null!`
(the null-forgiving suppression matches an existing pattern at
`GameWindow.cs:7885` for sequencer-driven entities where the legacy
`Animation` field is unused). At the first per-frame `Advance(dt)`
call from `TickAnimations`, the sequencer produces the correct
rest-pose frames for the door's current state.
4. **Log evidence at spawn:**
@ -97,17 +104,17 @@ happens inside `GameWindow.OnLiveEntitySpawnedLocked`, which branches on
### When the door opens (UpdateMotion arrives)
ACE broadcasts `UpdateMotion (0xF74D)` with `stance=0x003D` (NonCombat) and
`cmd=0x000C` (On = open). The existing `OnLiveMotionUpdated` handler previously
dropped this silently for non-creature entities. B.4c adds a `IsDoorName`-gated
branch:
wire `cmd=0x000C` (which `MotionCommandResolver.ReconstructFullCommand`
maps to full motion `0x4000000B` = `MotionCommand.On` = door open).
```csharp
if (_doorSequencers.TryGetValue(entity.Id, out var seq))
{
var style = 0x80000000u | (uint)um.Stance;
seq.SetCycle(style, (uint)um.ForwardCommand, um.ForwardSpeed);
}
```
B.4c does NOT add a new dispatch path here — the existing
`OnLiveMotionUpdated` handler already routes via the `_animatedEntities`
dict + per-entity `Sequencer`, the same code path creatures use. The
only B.4c contribution at UM dispatch is the new `[door-cycle]`
diagnostic gated on `IsDoorName(doorInfo.Name)`. Before B.4c, doors
silently dropped at the `_animatedEntities.TryGetValue` check at
`GameWindow.cs:3036` because doors weren't registered; B.4c's Task 1
spawn-time branch fixed that.
The sequencer transitions from the `Off` cycle (static closed pose) through
the door-swing link animation to the `On` cycle (static open pose).
@ -148,13 +155,14 @@ UM guid=0x7A9B403A mt=... cmd=0x000B ... motion=0x4000000C
### Per-frame mesh rebuild
The door sequencer integrates into `GameWindow.TickAnimations` via the same
`_doorSequencers` dict. Each frame, `Advance(dt)` is called on the sequencer
and the resulting `PartFrames` drive the same `MeshRefs` rebuild that creature
entities use. This is the reason the stance-value bug produced underground doors:
with the wrong style key (`0x80000001`) `HasCycle` returned false, the sequencer
was empty, `Advance` returned no frames, and the per-frame part-matrix rebuild
at `GameWindow.cs:7691` received zero frames — collapsing every part to the
entity origin.
`_animatedEntities` dict that holds creatures. Each frame, `ae.Sequencer.Advance(dt)`
is called and the resulting per-part transforms drive the same `MeshRefs` rebuild
that creature entities use (sequencer branch at `GameWindow.cs:7497`; doors
never enter the legacy slerp `else` branch). This is the reason the stance-value
bug produced underground doors: with the wrong style key (`0x80000001`)
`HasCycle` returned false, the sequencer was empty at spawn, `Advance` returned
identity frames, and the per-frame part-matrix rebuild received `Vector3.Zero /
Quaternion.Identity` for every part — collapsing them all to the entity origin.
---
@ -244,8 +252,9 @@ Other interactable non-creature entities (chests, levers, traps) will still
silently drop their `UpdateMotion` commands — they are not covered by B.4c and
no issue has been filed for them yet. When those animations become relevant
(M2/M3 inventory + dungeon content), the same spawn-time registration pattern
can be extended by widening `IsDoorSpawn` and reusing the `_doorSequencers`
infrastructure.
can be extended: broaden the detection predicate beyond `name == "Door"` and
register additional entity types in the existing `_animatedEntities` dict via
the same sibling branch.
### Door toggle behavior