fix(items): use retail cylinder range for loot windows
Keep external-container visibility tied to the signed surface gap between the player and container physics cylinders, matching gmExternalContainerUI's authored UseRadius range watcher. This prevents a valid ViewContents response from being closed at ACE's natural approach endpoint. Port Position::cylinder_distance and ACCWeenieObject::ObjectsInRange into Core with conformance coverage for 3-D separation, overlap, mode precedence, and inclusive boundaries. Release build succeeds and all 5,895 tests pass with five intentional skips. Co-authored-by: OpenAI Codex <codex@openai.com>
This commit is contained in:
parent
e6dd8bf6fa
commit
0f82a08f0a
6 changed files with 199 additions and 50 deletions
|
|
@ -203,31 +203,30 @@ public static class MoveToMath
|
|||
/// (wire bit 0x400) is set — object moves use edge-to-edge cylinder
|
||||
/// distance; position moves use plain center (Euclidean) distance
|
||||
/// instead (not ported here — <c>Vector3.Distance</c> already covers
|
||||
/// it). BN garbles the x87 plumbing in the raw, so the exact
|
||||
/// radius-combination arithmetic is not directly visible; ported per
|
||||
/// the PDB argument ORDER (own radius/height/position, target
|
||||
/// radius/height/position) with the standard cylinder-distance shape:
|
||||
/// planar (X/Y) center distance minus the sum of both radii, clamped
|
||||
/// at zero (overlapping cylinders report 0, never negative). Height is
|
||||
/// accepted for signature parity with the retail call (own/target
|
||||
/// height feed the caller's contact-plane logic elsewhere) but does
|
||||
/// NOT participate in this edge-to-edge planar computation — matching
|
||||
/// retail's use of cylinder_distance purely for the horizontal arrival
|
||||
/// gate.
|
||||
/// it). Binary Ninja garbles the x87 result plumbing, but the complete
|
||||
/// branch structure is visible in
|
||||
/// <c>Position::cylinder_distance @ 0x005A97F0</c> and is independently
|
||||
/// preserved by ACE <c>Physics.Common.Position.CylinderDistance</c>:
|
||||
/// subtract both radii from the full 3D center distance, compute the
|
||||
/// signed vertical gap between the two cylinders, then combine gaps only
|
||||
/// when their signs agree. Overlap is therefore a negative distance
|
||||
/// rather than a value clamped to zero.
|
||||
/// </summary>
|
||||
public static float CylinderDistance(
|
||||
float ownRadius, float ownHeight, Vector3 ownPos,
|
||||
float targetRadius, float targetHeight, Vector3 targetPos)
|
||||
{
|
||||
_ = ownHeight;
|
||||
_ = targetHeight;
|
||||
float radialGap = Vector3.Distance(ownPos, targetPos)
|
||||
- (ownRadius + targetRadius);
|
||||
float verticalGap = ownPos.Z <= targetPos.Z
|
||||
? targetPos.Z - (ownPos.Z + ownHeight)
|
||||
: ownPos.Z - (targetPos.Z + targetHeight);
|
||||
|
||||
float dx = targetPos.X - ownPos.X;
|
||||
float dy = targetPos.Y - ownPos.Y;
|
||||
float centerDist = MathF.Sqrt(dx * dx + dy * dy);
|
||||
|
||||
float edgeDist = centerDist - ownRadius - targetRadius;
|
||||
return edgeDist > 0f ? edgeDist : 0f;
|
||||
if (verticalGap > 0f && radialGap > 0f)
|
||||
return MathF.Sqrt(verticalGap * verticalGap + radialGap * radialGap);
|
||||
if (verticalGap < 0f && radialGap < 0f)
|
||||
return -MathF.Sqrt(verticalGap * verticalGap + radialGap * radialGap);
|
||||
return radialGap;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
53
src/AcDream.Core/Physics/ObjectRangeMath.cs
Normal file
53
src/AcDream.Core/Physics/ObjectRangeMath.cs
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
using System.Numerics;
|
||||
using AcDream.Core.Physics.Motion;
|
||||
|
||||
namespace AcDream.Core.Physics;
|
||||
|
||||
/// <summary>
|
||||
/// Retail object-range predicate used by <c>CPlayerSystem</c> range handlers.
|
||||
/// Port of <c>ACCWeenieObject::ObjectsInRange @ 0x0058C1A0</c>.
|
||||
/// </summary>
|
||||
public static class ObjectRangeMath
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests two live physics objects against a caller-supplied range. Retail
|
||||
/// gives <paramref name="ignoreZDelta"/> precedence; otherwise
|
||||
/// <paramref name="useRadii"/> selects cylinder-gap distance instead of
|
||||
/// ordinary center distance.
|
||||
/// </summary>
|
||||
public static bool ObjectsInRange(
|
||||
Vector3 firstPosition,
|
||||
float firstRadius,
|
||||
float firstHeight,
|
||||
Vector3 secondPosition,
|
||||
float secondRadius,
|
||||
float secondHeight,
|
||||
double range,
|
||||
bool useRadii,
|
||||
bool ignoreZDelta)
|
||||
{
|
||||
double distance;
|
||||
if (ignoreZDelta)
|
||||
{
|
||||
double dx = secondPosition.X - firstPosition.X;
|
||||
double dy = secondPosition.Y - firstPosition.Y;
|
||||
distance = Math.Sqrt(dx * dx + dy * dy);
|
||||
}
|
||||
else if (useRadii)
|
||||
{
|
||||
distance = MoveToMath.CylinderDistance(
|
||||
firstRadius,
|
||||
firstHeight,
|
||||
firstPosition,
|
||||
secondRadius,
|
||||
secondHeight,
|
||||
secondPosition);
|
||||
}
|
||||
else
|
||||
{
|
||||
distance = Vector3.Distance(firstPosition, secondPosition);
|
||||
}
|
||||
|
||||
return distance <= range;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue