feat(phys L.2g slice 1): ShadowObjectRegistry.UpdatePhysicsState
New mutator that overwrites cached PhysicsState bits on every shadow copy of the named entity. The existing CollisionExemption.ShouldSkip(...) check (acclient_2013_pseudo_c.txt:276782) reads the same cached field, so a post-spawn ETHEREAL flip is now honored on the next resolver tick without any resolver-path change. Retail anchor: CPhysicsObj::set_state at acclient_2013_pseudo_c.txt:283044. Slice 1 scopes to the bare state-write — retail's cosmetic side-effect handlers (0x800 lighting, 0x20 nodraw, 0x4000 hidden) don't fire for the ETHEREAL bit and stay deferred. Three TDD tests cover: ETHEREAL flip from 0->0x4; unregistered-entity no-op; entity spanning multiple cells gets all copies updated. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
2459f287e4
commit
d53891557d
2 changed files with 90 additions and 0 deletions
|
|
@ -1,4 +1,5 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using AcDream.Core.Physics;
|
||||
using Xunit;
|
||||
|
|
@ -252,4 +253,52 @@ public class ShadowObjectRegistryTests
|
|||
Assert.Equal(0u, entry.State);
|
||||
Assert.Equal(EntityCollisionFlags.None, entry.Flags);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// UpdatePhysicsState — L.2g slice 1 (doors flip ETHEREAL post-spawn)
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
[Fact]
|
||||
public void UpdatePhysicsState_FlipsEthereal_NextLookupSeesNewBits()
|
||||
{
|
||||
var reg = new ShadowObjectRegistry();
|
||||
const uint doorId = 0x000F4244u;
|
||||
reg.Register(doorId, 0x020019FFu, new Vector3(12f, 12f, 50f),
|
||||
Quaternion.Identity, 1f, OffX, OffY, LbId,
|
||||
state: 0u, flags: EntityCollisionFlags.None);
|
||||
|
||||
var before = reg.AllEntriesForDebug().Single(e => e.EntityId == doorId);
|
||||
Assert.Equal(0u, before.State);
|
||||
|
||||
reg.UpdatePhysicsState(doorId, 0x00000004u);
|
||||
|
||||
var after = reg.AllEntriesForDebug().Single(e => e.EntityId == doorId);
|
||||
Assert.Equal(0x00000004u, after.State);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UpdatePhysicsState_UnregisteredEntity_IsNoOp()
|
||||
{
|
||||
var reg = new ShadowObjectRegistry();
|
||||
reg.UpdatePhysicsState(0xDEADBEEFu, 0x00000004u);
|
||||
Assert.Equal(0, reg.TotalRegistered);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UpdatePhysicsState_EntitySpanningMultipleCells_AllCellsUpdated()
|
||||
{
|
||||
var reg = new ShadowObjectRegistry();
|
||||
reg.Register(99u, 0x01000099u, new Vector3(24f, 12f, 50f),
|
||||
Quaternion.Identity, 2f, OffX, OffY, LbId,
|
||||
state: 0u);
|
||||
|
||||
reg.UpdatePhysicsState(99u, 0x00000004u);
|
||||
|
||||
uint cellA = LbId | 1u;
|
||||
uint cellB = LbId | (1u*8 + 0 + 1);
|
||||
var inA = reg.GetObjectsInCell(cellA).Single(e => e.EntityId == 99u);
|
||||
var inB = reg.GetObjectsInCell(cellB).Single(e => e.EntityId == 99u);
|
||||
Assert.Equal(0x00000004u, inA.State);
|
||||
Assert.Equal(0x00000004u, inB.State);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue