4.1 KiB
Retail radar/compass pseudocode — 2026-07-10
Oracle and scope
This note records the primary retail behavior used by acdream's retained radar port. The oracle is the September 2013 named client:
gmRadarUIfields:named-retail/acclient.h:54506-54548- color/shape:
gmRadarUI::GetBlipColor0x004D76F0,gmRadarUI::GetBlipShape0x004D7B60 - coordinates/compass/draw:
UpdateCoordinates0x004D8C80,UpdateCompassTokens0x004D9060,DrawObjects0x004D9380,DrawChildren0x004D9720 - cadence/list maintenance:
UseTime0x004D98A0,AddObject0x004D9A00,Init0x004D9B90,PostInit0x004D9D30 - production DAT layout:
LayoutDesc 0x21000074in2026-06-25-retail-ui-layout-dump.json
The older AC2D-derived 1.18 × range, rotating player arrow, and horizontal
scrolling compass strip are rejected by the named retail implementation.
Retained UI ownership
The production root is a 120×140 gmRadarUI element of class
0x10000010. Custom properties set center (60,60) (0x1000002E,
nested 0x1000002F/30) and pixel radius 50 (0x1000002D).
LayoutImporter owns the static DAT children and media.
A dedicated UiRadar owns the dynamic draw/tick/input behavior. Core owns
the pure projection, classification, marker geometry, compass, and coordinate
algorithms. GameWindow supplies live snapshots and selection/settings
actions only.
This is a screen-space retained panel. It is not an ImGui panel and does not use the raw world-space TextRenderer overlay path.
Per-tick pseudocode
every 0.025 seconds:
range = player is outdoors ? 75 : 25
heading = physical player heading in degrees
move N/E/S/W child tokens around radar center
if CoordinatesOnRadar and LandDefs.gid_to_lcoord(playerCell):
ns = (lcoordY - 1024) * 0.1 + 0.5
ew = (lcoordX - 1024) * 0.1 + 0.5
coordinateText = abs(ns) + N/S + "," + abs(ew) + E/W
show footer/text
else:
hide footer/text
for each maintained object except the player:
require a physics/world entity
require RadarBehavior in { ShowMovement=2, ShowAttacking=3, ShowAlways=4 }
relative = convert object position into player's oriented frame
if relative.X² + relative.Y² >= (range - 1)²:
continue
scale = radarPixelRadius / range
x = trunc(centerX + relative.X * scale)
y = trunc(centerY - relative.Y * scale)
rgbBrightness = abs(relative.Z) < 5 ? 1.0 : 0.65
color = exact _blipColor override 1..10, otherwise retail classification
shape = fellowship/allegiance/PK relationship shape, otherwise Plus
draw marker pixels
if selected: draw the open selection outline at offsets ±3
After all object blips, draw the player's fixed bright-green center plus. Hover selects the nearest projected blip within squared distance 36; its name is the tooltip. A left click selects that object.
The lock button drives the existing retained-window drag gate; the drag
affordance is hidden while locked. Completed moves are clamped inside the UI
root and persisted per character through SettingsStore.
Production assets
| Role | Element | Rect | RenderSurface |
|---|---|---|---|
| Face | 0x1000003F |
(0,0) 120×120 |
0x06004CC1 |
| Coordinate footer | 0x1000003E |
(0,120) 120×18 |
0x06004CC0 |
| N / E / S / W | 0x10000040..43 |
four 10×9 tokens | 0x060011FB, 0x06001938, 0x0600193A, 0x0600193C |
| Lock | 0x10000619 |
(6,6) 27×27 |
0x060074B7 / 0x060074B8 |
| Drag | 0x100006A3 |
(87,6) 27×27 |
0x060074C9 |
The child token orbit uses center + (sin(angle), cos(angle)) * magnitude
with angles N=heading+π, E=heading+π/2, S=heading, W=heading+3π/2.
Wire contract
PublicWeenieDesc flag 0x00100000 carries nullable byte
RadarBlipColor; flag 0x00800000 carries nullable byte RadarBehavior.
Absent is distinct from an explicitly transmitted zero. Both values flow
through CreateObject.Parsed, WorldSession.EntitySpawn, and
ClientObjectTable.
Do not infer ShowMovement/ShowAttacking from local motion. Retail's
InqShowableOnRadar accepts values 2, 3, and 4 directly; server/quality
changes are authoritative.