fix(physics): AP-10 - restore retail's 0.1m dry-corner water sink-in; wire WATER_CONTACT_TS
Campaign P Slice P4 item 2. TerrainSurface.SampleWaterDepth now returns 0.1
(was collapsed to 0) for a partially-water cell's dry corner, matching
retail's ObjCell.get_water_depth / calc_water_depth (via ACE's unambiguous
C# port). ValidateWalkable's formula was already byte-for-byte verbatim
(ACE ObjectInfo.ValidateWalkable line 124); only the constant was collapsed.
The old collapse's justification ("0.1 destabilizes the feet-exactly-on-plane
contact-touch check because dist > EPSILON skips SetContactPlane that tick")
is structurally true of retail too - traced and confirmed this slice: in ALL
THREE implementations (retail, ACE, acdream) a skipped touch-reassertion is
NOT a fall, because Contact/OnWalkable are STICKY -
PhysicsEngine.ResolveWithTransition's onGround computation ORs the fresh
per-call ContactPlaneValid with the seeded, persistent
PhysicsBody.TransientState.OnWalkable bit (itself written back by the
caller's own sticky TransientState). PhysicsEngine.SampleTerrainWalkable's
isWater = waterDepth >= 0.45f threshold means the restore does not flip the
dry corner's water classification (0.1 still < 0.45) - only the sink-in
depth changes. Full Core.Tests suite green (4038/2 skips, up from 4026)
proves the sticky-bit argument held in practice.
WATER_CONTACT_TS (TransientStateFlags.WaterContact, declared but never
written) is now mirrored alongside CONTACT_TS/ON_WALKABLE_TS at every commit
point that writes them: PhysicsObjUpdate.ApplySetPositionContact (projectiles
+ remote teleport), PhysicsObjUpdate.CommitSetPositionTransition (remote
teleport placement), and PhysicsEngine's per-resolve body-state commit (local
player + remote dead-reckoning + ordinary movers via ResolveWithTransition -
the actual SetPositionInternal-equivalent path). No signature changes needed:
body.ContactPlaneIsWater is already fresh by the time each function runs.
CollisionShadowVerifier audit: no change needed. It diffs graph-vs-flat BSP
traversal outcomes (ObjectInfo/CollisionInfo/SpherePath fields already
including ContactPlaneIsWater); it never touches PhysicsBody.TransientState,
and the water-depth constant is computed identically upstream of both
traversal modes, so it cannot introduce a new graph/flat divergence.
Filed #264 for the three items research explicitly left open (none block
this port): no confirmed retail consumer of WATER_CONTACT_TS was found (an
xref scan wasn't attempted - bitmask reads aren't text-greppable); the
CLandCell ENTIRELY_WATER ethereal/swim exemption from terrain collision was
not cross-checked; jump-in-water/swim-animation effects were not
investigated (out of physics/collision scope).
Conformance: Ap10WaterSemanticsTests covers SampleWaterDepth golden values
(NotWater/EntirelyWater/PartiallyWater wet+dry corners), the isWater
threshold non-flip, WaterContact mirroring in both PhysicsObjUpdate
functions, and two settle-to-rest end-to-end PhysicsEngine.ResolveWithTransition
scenarios (water: sinks exactly waterDepth below the plane and sets
WaterContact; dry: rests exactly on the plane and clears any stale
WaterContact bit).
Register: retired AP-10 (92 active AP rows, down from 93).
AcDream.Core.Tests: 4038 passed, 2 skipped, 0 failed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
d6c3f8657a
commit
cc8d57a26e
7 changed files with 439 additions and 18 deletions
|
|
@ -42,6 +42,16 @@ public static class PhysicsObjUpdate
|
|||
else
|
||||
body.TransientState &= ~TransientStateFlags.OnWalkable;
|
||||
|
||||
// AP-10 (Campaign P Slice P4, 2026-07-30): retail SetPositionInternal
|
||||
// (0x005153e5-0051545f) writes WATER_CONTACT_TS in the same statement
|
||||
// block as CONTACT_TS, immediately after. body.ContactPlaneIsWater is
|
||||
// already current by the time this runs (every caller sets it, or it
|
||||
// carries over from the prior tick, before calling here).
|
||||
if (body.ContactPlaneIsWater)
|
||||
body.TransientState |= TransientStateFlags.WaterContact;
|
||||
else
|
||||
body.TransientState &= ~TransientStateFlags.WaterContact;
|
||||
|
||||
body.calc_acceleration();
|
||||
}
|
||||
|
||||
|
|
@ -96,6 +106,15 @@ public static class PhysicsObjUpdate
|
|||
else
|
||||
body.TransientState &= ~TransientStateFlags.OnWalkable;
|
||||
|
||||
// AP-10 (Campaign P Slice P4, 2026-07-30): mirror WATER_CONTACT_TS
|
||||
// alongside CONTACT_TS/ON_WALKABLE_TS, same as ApplySetPositionContact.
|
||||
// Callers (e.g. RemoteTeleportPlacement) already set body.ContactPlaneIsWater
|
||||
// before invoking this commit.
|
||||
if (body.ContactPlaneIsWater)
|
||||
body.TransientState |= TransientStateFlags.WaterContact;
|
||||
else
|
||||
body.TransientState &= ~TransientStateFlags.WaterContact;
|
||||
|
||||
if (!previousOnWalkable && finalOnWalkable)
|
||||
{
|
||||
hitGround?.Invoke();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue