fix(B.7): square indicator box + bigger pick sphere for doors/lifestones/portals + diag

Visual test surfaced three follow-ups:

1. Square box, not 1:2 rectangle.
   WidthHeightRatio: 0.5 → 1.0. Retail's Vivid Target Indicator draws
   a square; the earlier humanoid-aspect ratio looked wrong for
   non-humanoids and didn't match retail screenshots.

2. Large flat objects (doors / lifestones / portals / corpses)
   weren't selectable with the new tight 0.7 m pick sphere.
   WorldPicker.Pick now takes an optional radiusForGuid callback so
   the host can per-entity decide a larger radius. GameWindow's pick
   site supplies a lambda that bumps to 2.0 m for any entity with
   BF_DOOR (0x1000), BF_LIFESTONE (0x4000), BF_PORTAL (0x40000), or
   BF_CORPSE (0x2000) set in ObjectDescriptionFlags. Default stays
   at 0.7 m for humanoids and items.

3. New [B.7] pick-info diagnostic on each successful pick:
     [B.7] pick-info guid=0x... itemType=0x... pwd=0x... color=(r,g,b)
   Lets us verify e.g. whether a 'green NPC' really is server-side
   flagged as Vendor (BF_VENDOR=0x200, retail-defined green) vs a
   bug in our colour lookup. The pwd bit table is acclient.h:6431-
   6463 — same flags retail's gmRadarUI::GetBlipColor branches on.

Note: textured retail-sprite corner triangles remain a B.7 follow-up
deferred per the spec. MVP uses procedural fills.
This commit is contained in:
Erik 2026-05-15 07:13:23 +02:00
parent 631571a6ef
commit 23cb1e9636
3 changed files with 51 additions and 8 deletions

View file

@ -89,10 +89,10 @@ public static class WorldPicker
Vector3 origin, Vector3 direction,
IEnumerable<WorldEntity> candidates,
uint skipServerGuid,
float maxDistance = 50f)
float maxDistance = 50f,
Func<uint, float>? radiusForGuid = null)
{
const float Radius = 0.7f;
const float Radius2 = Radius * Radius;
const float DefaultRadius = 0.7f;
if (direction.LengthSquared() < 1e-10f) return null;
@ -103,13 +103,20 @@ public static class WorldPicker
if (entity.ServerGuid == 0u) continue;
if (entity.ServerGuid == skipServerGuid) continue;
// Per-entity radius (caller-supplied) lets large flat objects
// like doors, lifestones, and portals use a bigger sphere
// than the 0.7 m humanoid/item default — their visible
// surface extends well beyond their origin point.
float r = radiusForGuid?.Invoke(entity.ServerGuid) ?? DefaultRadius;
float r2 = r * r;
// Geometric ray-sphere: oc = origin - center, b = dot(oc, dir),
// c = |oc|^2 - r^2, discriminant = b^2 - c. If discriminant < 0
// the ray misses the sphere. Otherwise nearest intersection is
// t = -b - sqrt(discriminant).
var oc = origin - entity.Position;
float b = Vector3.Dot(oc, direction);
float c = Vector3.Dot(oc, oc) - Radius2;
float c = Vector3.Dot(oc, oc) - r2;
float d = b * b - c;
if (d < 0f) continue;