103 lines
4.1 KiB
Markdown
103 lines
4.1 KiB
Markdown
# 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:
|
||
|
||
- `gmRadarUI` fields: `named-retail/acclient.h:54506-54548`
|
||
- color/shape: `gmRadarUI::GetBlipColor` `0x004D76F0`,
|
||
`gmRadarUI::GetBlipShape` `0x004D7B60`
|
||
- coordinates/compass/draw: `UpdateCoordinates` `0x004D8C80`,
|
||
`UpdateCompassTokens` `0x004D9060`, `DrawObjects` `0x004D9380`,
|
||
`DrawChildren` `0x004D9720`
|
||
- cadence/list maintenance: `UseTime` `0x004D98A0`, `AddObject`
|
||
`0x004D9A00`, `Init` `0x004D9B90`, `PostInit` `0x004D9D30`
|
||
- production DAT layout: `LayoutDesc 0x21000074` in
|
||
`2026-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
|
||
|
||
```text
|
||
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
|
||
```
|
||
|
||
```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.
|