fix(B.4c): correct NonCombat stance value (0x3D, not 0x01) + read spawn.MotionState
Visual test revealed doors rendered halfway in the ground because the spawn-time SetCycle seed never fired: - Spec specified NonCombat stance = 0x01, but ACE's MotionStance.NonCombat is 0x3D (61). The cycle key is `0x80000000 | stance`, so the correct initial style is 0x8000003D, not 0x80000001. - HasCycle(0x80000001, ...) always returned false -> SetCycle was skipped -> sequencer left with no current motion -> Advance(dt) returned empty frames -> per-frame MeshRefs rebuild at line 7691 set every part to (origin, identity) -> door parts collapsed to the entity origin (which sits at the door's pivot, halfway underground for inn doors). Fix: 1. Rename inline `NonCombatStance` -> `NonCombatStyle` and use the correct 0x8000003D value. 2. Defensively prefer spawn.MotionState?.Stance when present (the wire may carry an explicit non-NonCombat stance for unusual doors), falling back to NonCombat. Mirrors OnLiveMotionUpdated's existing pattern at line 3148: `uint fullStyle = stance != 0 ? (0x80000000u | (uint)stance) : ae.Sequencer.CurrentStyle`. 3. Extend [door-anim] registered diagnostic to include initialStyle so future visual tests can verify the stance value at a glance. Verified by reading the prior visual test's log: ACE broadcasts UMs with stance=0x003D and the runtime sequencer keyed cycles by style=0x8000003D. Same value now used at spawn. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
8a9b15e6a9
commit
454d88ed8e
1 changed files with 19 additions and 4 deletions
|
|
@ -2832,14 +2832,29 @@ public sealed class GameWindow : IDisposable
|
||||||
{
|
{
|
||||||
var sequencer = new AcDream.Core.Physics.AnimationSequencer(setup, mtable, _animLoader);
|
var sequencer = new AcDream.Core.Physics.AnimationSequencer(setup, mtable, _animLoader);
|
||||||
|
|
||||||
const uint NonCombatStance = 0x80000001u;
|
// Style key is `0x80000000 | stance`. ACE's MotionStance.NonCombat
|
||||||
|
// is 0x3D (61 decimal), NOT 0x01. Verified live: ACE broadcasts
|
||||||
|
// UpdateMotion with stance=0x003D and the sequencer keys cycles
|
||||||
|
// by style=0x8000003D. An earlier B.4c seed used the wrong
|
||||||
|
// 0x80000001 value, which made HasCycle always return false ->
|
||||||
|
// SetCycle never fired -> sequencer empty -> Advance returned
|
||||||
|
// no frames -> per-frame tick collapsed all door parts to the
|
||||||
|
// entity origin (visible as "door halfway in the ground").
|
||||||
|
const uint NonCombatStyle = 0x8000003Du;
|
||||||
const uint MotionOn = 0x4000000Bu; // ACE MotionCommand.On (door open)
|
const uint MotionOn = 0x4000000Bu; // ACE MotionCommand.On (door open)
|
||||||
const uint MotionOff = 0x4000000Cu; // ACE MotionCommand.Off (door closed)
|
const uint MotionOff = 0x4000000Cu; // ACE MotionCommand.Off (door closed)
|
||||||
const uint EtherealPs = 0x4u;
|
const uint EtherealPs = 0x4u;
|
||||||
|
// Prefer the spawn's wire-level stance if provided; else default
|
||||||
|
// to NonCombat. (Doors normally don't carry an initial MotionState
|
||||||
|
// on spawn — falling back to NonCombat matches ACE Door.cs:43.)
|
||||||
|
ushort spawnStance = spawn.MotionState?.Stance ?? 0;
|
||||||
|
uint initialStyle = spawnStance != 0
|
||||||
|
? (0x80000000u | (uint)spawnStance)
|
||||||
|
: NonCombatStyle;
|
||||||
uint spawnState = spawn.PhysicsState ?? 0u;
|
uint spawnState = spawn.PhysicsState ?? 0u;
|
||||||
uint initialCycle = (spawnState & EtherealPs) != 0 ? MotionOn : MotionOff;
|
uint initialCycle = (spawnState & EtherealPs) != 0 ? MotionOn : MotionOff;
|
||||||
if (sequencer.HasCycle(NonCombatStance, initialCycle))
|
if (sequencer.HasCycle(initialStyle, initialCycle))
|
||||||
sequencer.SetCycle(NonCombatStance, initialCycle);
|
sequencer.SetCycle(initialStyle, initialCycle);
|
||||||
|
|
||||||
var template = new (uint, IReadOnlyDictionary<uint, uint>?)[meshRefs.Count];
|
var template = new (uint, IReadOnlyDictionary<uint, uint>?)[meshRefs.Count];
|
||||||
for (int i = 0; i < meshRefs.Count; i++)
|
for (int i = 0; i < meshRefs.Count; i++)
|
||||||
|
|
@ -2861,7 +2876,7 @@ public sealed class GameWindow : IDisposable
|
||||||
|
|
||||||
if (AcDream.Core.Physics.PhysicsDiagnostics.ProbeBuildingEnabled)
|
if (AcDream.Core.Physics.PhysicsDiagnostics.ProbeBuildingEnabled)
|
||||||
Console.WriteLine(System.FormattableString.Invariant(
|
Console.WriteLine(System.FormattableString.Invariant(
|
||||||
$"[door-anim] registered guid=0x{spawn.Guid:X8} entityId=0x{entity.Id:X8} mtable=0x{mtableId:X8} initialCycle=0x{initialCycle:X8}"));
|
$"[door-anim] registered guid=0x{spawn.Guid:X8} entityId=0x{entity.Id:X8} mtable=0x{mtableId:X8} initialStyle=0x{initialStyle:X8} initialCycle=0x{initialCycle:X8}"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue