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

@ -15,6 +15,11 @@ Named Sept-2013 retail symbols:
- `gmExternalContainerUI::ListenToElementMessage @ 0x004CBAD0`
- `gmExternalContainerUI::SetGroundObject @ 0x004CBBD0`
- `gmExternalContainerUI::CloseCurrentContainer @ 0x004CBCB0`
- `CPlayerSystem::CalculateObjectRangeChecks @ 0x0055F360`
- `CPlayerSystem::RegisterObjectRangeHandler @ 0x005602D0`
- `ACCWeenieObject::ObjectsInRange @ 0x0058C1A0`
- `CPhysicsObj::get_distance_to_object @ 0x0050F7A0`
- `Position::cylinder_distance @ 0x005A97F0`
- `gmExternalContainerUI::PostInit @ 0x004CBDE0`
- `gmExternalContainerUI::OnVisibilityChanged @ 0x004CBF50`
- `gmExternalContainerUI::RecvNotice_SetGroundObject @ 0x004CBFD0`
@ -128,9 +133,19 @@ ExternalPanel.SetGroundObject(containerId):
if containerId == 0:
hide panel
else:
register range watch using root UseRadius
register range watch(root, root.UseRadius,
useRadii = true, ignoreZDelta = false)
show panel
CalculateExternalRangeWatch(root, player):
// CPlayerSystem routes the authored range registration through
// ACCWeenieObject::ObjectsInRange. With useRadii=true, this is the gap
// between the two live physics cylinders, not center-to-center distance.
distance = Position.cylinder_distance(
root.radius, root.height, root.position,
player.radius, player.height, player.position)
return distance <= root.UseRadius
ExternalPanel.CloseCurrentContainer():
if currentExternalRoot == 0:
return