feat(physics): P5 commit 1 - port ConstraintManager leash distance constants (#167)
Add ConstraintDistance (outdoor/indoor start=10/5, max=50/20), byte-decoded from the matching retail binary (GetStartConstraintDistance 0x0050ebc0, GetMaxConstraintDistance 0x0050ec10 - both x87-return functions BN elided). Deliberately omits the vestigial player-vs-remote branch the disassembly shows loads identical constants either way. Pins the ACE-inversion (ACE's start mapping is outdoor 5/indoor 10, the opposite of the binary - the binary wins). Adds a full-chain conformance test proving an armed, over-strained leash actually blocks jump_is_allowed (0x47), not just the bare stub-property regression already covered. See docs/research/2026-07-30-constraint-leash-constants.md.
This commit is contained in:
parent
b2b44954d7
commit
e0629145ef
3 changed files with 169 additions and 0 deletions
|
|
@ -0,0 +1,72 @@
|
|||
using AcDream.Core.Physics.Motion;
|
||||
using Xunit;
|
||||
|
||||
namespace AcDream.Core.Tests.Physics.Motion;
|
||||
|
||||
/// <summary>
|
||||
/// Campaign P P5 (#167 / TS-35) golden values for
|
||||
/// <see cref="ConstraintDistance"/> — byte-decoded from the matching retail
|
||||
/// binary (docs/research/2026-07-30-constraint-leash-constants.md §1), not
|
||||
/// guessed and not copied from ACE (whose start mapping is INVERTED).
|
||||
/// </summary>
|
||||
public sealed class ConstraintDistanceTests
|
||||
{
|
||||
// Outdoor landblock cell indices are 0x0001-0x00FF (low16 < 0x0100).
|
||||
private const uint OutdoorCell = 0x12340007u;
|
||||
// EnvCell (indoor) indices are 0x0100+ .
|
||||
private const uint IndoorCell = 0x12340105u;
|
||||
|
||||
[Fact]
|
||||
public void GetStartConstraintDistance_Outdoor_Is10NotAcesInverted5()
|
||||
{
|
||||
// ACE PhysicsObj.cs:620 maps outdoor start -> 5. The disassembly
|
||||
// (0x0050ebc0) says 10. The binary wins.
|
||||
Assert.Equal(10.0f, ConstraintDistance.GetStartConstraintDistance(OutdoorCell));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetStartConstraintDistance_Indoor_Is5()
|
||||
{
|
||||
Assert.Equal(5.0f, ConstraintDistance.GetStartConstraintDistance(IndoorCell));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetMaxConstraintDistance_Outdoor_Is50()
|
||||
{
|
||||
Assert.Equal(50.0f, ConstraintDistance.GetMaxConstraintDistance(OutdoorCell));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetMaxConstraintDistance_Indoor_Is20()
|
||||
{
|
||||
Assert.Equal(20.0f, ConstraintDistance.GetMaxConstraintDistance(IndoorCell));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0x00FFu, false)]
|
||||
[InlineData(0x0100u, true)]
|
||||
[InlineData(0x0000u, false)]
|
||||
[InlineData(0xFFFFu, true)]
|
||||
public void IsIndoorCell_BoundaryIsLow16Ox0100(uint objCellId, bool expectedIndoor)
|
||||
{
|
||||
Assert.Equal(expectedIndoor, ConstraintDistance.IsIndoorCell(objCellId));
|
||||
}
|
||||
|
||||
// The disassembly's "this == player_object" branch loads IDENTICAL
|
||||
// constants on both sides (research doc §1, "vestigial" finding) — there
|
||||
// is deliberately no player/remote parameter on this API. Pin that the
|
||||
// SAME cell always yields the SAME band regardless of which kind of
|
||||
// object (player or remote) is asking, by construction (no such
|
||||
// parameter exists to differentiate them).
|
||||
[Fact]
|
||||
public void NoPlayerVsRemoteSplit_SameCellAlwaysYieldsSameBand()
|
||||
{
|
||||
float startA = ConstraintDistance.GetStartConstraintDistance(OutdoorCell);
|
||||
float startB = ConstraintDistance.GetStartConstraintDistance(OutdoorCell);
|
||||
float maxA = ConstraintDistance.GetMaxConstraintDistance(OutdoorCell);
|
||||
float maxB = ConstraintDistance.GetMaxConstraintDistance(OutdoorCell);
|
||||
|
||||
Assert.Equal(startA, startB);
|
||||
Assert.Equal(maxA, maxB);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue