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:
Erik 2026-07-17 20:32:34 +02:00
parent e6dd8bf6fa
commit 0f82a08f0a
6 changed files with 199 additions and 50 deletions

View file

@ -13529,6 +13529,7 @@ public sealed class GameWindow : IDisposable
private bool IsWithinExternalContainerUseRange(uint targetGuid)
{
if (_playerController is null
|| !_entitiesByServerGuid.TryGetValue(_playerServerGuid, out var playerEntity)
|| !_visibleEntitiesByServerGuid.TryGetValue(targetGuid, out var entity))
{
// The server remains authoritative for forced close. An entity can be
@ -13540,10 +13541,24 @@ public sealed class GameWindow : IDisposable
if (!LastSpawns.TryGetValue(targetGuid, out var spawn)
|| spawn.UseRadius is not > 0f)
return true;
float useRadius = spawn.UseRadius.Value;
float dx = entity.Position.X - _playerController.Position.X;
float dy = entity.Position.Y - _playerController.Position.Y;
return dx * dx + dy * dy <= useRadius * useRadius;
// gmExternalContainerUI::SetGroundObject @ 0x004CBBD0 registers the
// root's UseRadius with useRadii=true and ignoreZDelta=false.
// CPlayerSystem::CalculateObjectRangeChecks @ 0x0055F360 then calls
// ACCWeenieObject::ObjectsInRange @ 0x0058C1A0, whose use-radii branch
// compares the gap between both physics cylinders, not their centers.
var playerCylinder = GetSetupCylinder(_playerServerGuid, playerEntity);
var targetCylinder = GetSetupCylinder(targetGuid, entity);
return AcDream.Core.Physics.ObjectRangeMath.ObjectsInRange(
_playerController.Position,
playerCylinder.Radius,
playerCylinder.Height,
entity.Position,
targetCylinder.Radius,
targetCylinder.Height,
spawn.UseRadius.Value,
useRadii: true,
ignoreZDelta: false);
}
private void InstallSpeculativeTurnToTarget(uint targetGuid)