The player's interface
Decision #130. Everything a player sees over the world: the HUD, the windows,
the world map. All of it uses one toolkit, crates/uo-client/src/ui_kit.rs,
in the #59 design system (docs/ui-concepts/README.md). That means smoked
glass, brass hairlines and corner brackets, a rune box and spaced capitals in
each title, dense tables with right-aligned numbers, and colour only as
signal.
Code: crates/uo-client/src/player_ui/ (the HUD panels, journal, skills, map,
help, context menus, layout), character_ui.rs (paperdoll, status, stats),
online/econ/ (containers, market, crafting) and hud.rs (the HUD model,
the journal history, the bars and names over mobiles).
The HUD
| Part | Where | What |
|---|---|---|
| Top bar | top, full width | where you stand (town and realm, the sanctuary, open country), ping, how many are near, coin, load, hunger, the route (to Skartor: 812 t, 3m 23s north-west), game time and wall clock |
| Icon rail | left edge | one rune per window: C character, I backpack, U skills, J journal, M map, K crafting, B market, V overview, O settings, ? keys. Lit when open; hovering names it and its key |
| Target | top centre | the hovered or selected mobile: an illuminated initial in its standing's colour, name, kind, standing, distance, hits and poise bars, its states with their timers and its crit counter (§ 16: counters are open to all), Attack, Clear. Offline in a battle it sits under the tempo timeline |
| Minimap | top right | the atlas around you, turned like the camera (screen up is north-west, as UO's radar); towns, pins, the route, N; z zooms |
| Overview | right, under the minimap | who is within 24 tiles: name, distance, hits %, state icons. Tabs all / hostile / players / creatures; click a header to sort (again: reverse); click a row to select, double-click to attack; the row's tooltip names kind and standing |
| Vitals gauge | bottom left | a segmented ring: hits on the left (red), mana on the right (blue), stamina inside (amber); the stance (run, fight, ghost) and speed in the middle; the numbers in its corners. Your own states, stances, charges and marks sit beside it with their timers |
| Action bar | bottom centre | the build system's keys (below) |
| Journal | top left | the combat log of the concept; see below |
Health bars and names over mobiles, floating combat text and the outcome banner stay as they were. The offline battle's tempo timeline sits under the top bar.
The combat HUD of the build system
Decisions #147–#150, concept docs/ui-concepts/v3-combat-hud.png (#161). One model,
player_ui/buildview.rs::BuildHud, filled by the offline battle (combat/hud.rs) and online
(online/hud.rs) from the rules' own state (the battle's, or the server's EntityState: cooldowns
as ready times, counters, states, and LiveView with the states' end times, worn triggers'
counts, stances, charges, marks and the Tome's spell); player_ui/bar.rs draws it.
- Action bar
Q W E | R D F | 7 Z X(Classic keys:1–6): the icon sheets of #151 at x3, so the slot frames read (Q steel, W copper, E gold, R/D/F pewter). A cooling key is shaded by a clock sweep with the seconds left; its cost sits in the corner, red with a greyed icon when you can't pay it; a key the school level hasn't opened shows "Lv n" over its locked ability; an empowered key (momentum 3, from school level 7) or an Execute on a broken target glows gold. 7 is the Tome's spare spell, shown only with a Tome worn; Z the heal potion with how many are left, X hold; the old spellbook's spells follow. Click a key as you'd press it; the tooltip says what the ability does, its tags, shape, cost, AP, wind-up, cooldown and the rune in its socket. - Counter pips above it: CRIT and BLOCK (as many pips as hits it takes at your chance; no dice, at 100 % it happens), each worn clothing piece or jewel that fires every Nth time (its short name: STORMS, ECHO), MOMENTUM as diamonds.
- Over the world: state icons under mobiles' hit bars, and the reaction word (SHATTER, ELECTROCUTE) over whoever a reaction struck.
The loadout window
Opens with the character window (C), or alone from the rail (L). Everything a build is made of
(player_ui/loadout.rs):
- the 20 slots grouped as
docs/builds.md§ 5 (weapon, off-hand, armour, clothing, jewellery), each with its icon (a weapon shows its own E) and the item's tooltip; - the weapon family's Q and W pools as icon cards: the picked one gold, locked ones dark with
their level; a click sends
SetLoadout(out of a fight only, the server checks too); then E (the item's) and R, D, F (the armour designs'); - passives of the school (one, two from level 7; a new pick pushes out the oldest);
- the armour profile: pieces per class as pips, set bonuses lit at 3 and 6, total weight and what it costs escapes (2 % slower per point, Light Step takes 25 % off, one tile shorter from 20);
- runes: the weapon's sockets with their runes (x takes one out: it is destroyed) and the runes in the pack to socket;
- the buckets with their icons and totals (class, speed, tags, crit "x1.75 every 4th hit", triggers, synergy, keystone, armour, hits, sustain, toughness);
- five templates: save the build you wear, load it out of a fight (sets the loadout and puts on its gear that you carry). They live in the character's layout file;
- build codes: "copy mine" puts
FO1-…on the clipboard (base64url of append-only ids: Q and W, passives, every worn piece with tier, design, affixes and runes); paste one and "read" lists line by line what you have and what you'd need, "take its loadout" sets it.
Every item tooltip also speaks the build (player_ui/tips.rs): tier and material; for a weapon its
family, its E and the Q/W pools, sockets and runes; for armour the class numbers per piece this tier
and next, the set bonuses, the D/R/F ability or the affix; for clothing and jewels the event, how
often, the ICD and this tier's and the next tier's number; keystones' gain and price; runes,
off-hands, belts, cloak auras, surcoat drills and consumables.
Windows
Every window drags by its title bar, closes with its x or with Esc
(newest first; Esc disarms an armed spell before anything else, and quits only
when nothing is open and you are not in a fight).
Windows stack by when they were last opened or clicked: that one is drawn
on top (#137). All ui_kit windows share GlobalZIndex(10); their rank in
WindowPlaces' order is their ZIndex, set when a window is built and kept
by ui_kit::stack_windows (PostUpdate), so a window its owner rebuilds
from data (the economy rebuilds the containers, market and crafting every
second) keeps its place instead of jumping above the others. The order is
saved in the layout.
Text fields (ui_kit::text_input): a click gives a field the keyboard,
and while it has it the game sees no keys. Cmd + V / C / X (Ctrl off macOS)
paste, copy and cut with the system clipboard (a password field never copies
out), Cmd + Backspace clears, Tab / Shift + Tab move to the next /
previous field of the same window in reading order (a lone field lets go),
Enter presses the form's ui_kit::submit_button (its gold main button,
listing the fields it submits) or, without one, hands the line to the field's
owner (TextInputs::take_submitted: chat, journal); Esc lets go.
- Character (C): the paperdoll is two columns of slot boxes, UO's layers, around the figure (initial in the standing's colour, name, title, standing, weapon, armour). Drag gear from the backpack onto the window to wear it, drag a worn piece into a bag to take it off, out onto the world to drop it. Double-click takes off, right-click opens the menu, hovering shows the tooltip. The status column shows standing (with its timer), hits, mana, stamina, satiety, load, coin, task, town and work patents with their time left. The stats column shows base and effective Str/Dex/Int, the maxima and the best six skills.
- Skills (U): every skill by category (collapsible), or by value; base,
effective (green above base, red below), cap, patent mark (P held, - needed),
lock. Clicking the lock cycles up, down, locked. The server command is the
progression work's (#125) and goes into
player_ui/skills.rs::lock_commandwhen it lands; until then the journal says locks can't change yet. - Journal (J): every line of the session (2000 kept) with the local time.
Tabs: all, system, combat, speech, and the chat channels (party, guild,
trade, npc) once something was said there. In "all" the combat and system
chips hide those channels (kept in the layout). The line at the bottom
speaks (
Command::Say); online, Enter in the game focuses it. Other modules write lines withGameLog::say(system),combat,speechandchannel(Channel::…). - Map (M): see below.
- Keys (
/or?): every binding by group, buttons to open each window. - Settings (O): interface (UI scale 75–200 %, minimap on/off, tactical grid, keys: Albion or Classic) and the graphics settings of #67, now in the toolkit's style.
- Backpack (I), bags, corpses, market (B), crafting (K): the economy's
windows (#121), unchanged apart from tooltips and drops. Container
windows (backpack, bags, corpses, chests) draw their gump floating above
the framed list with nothing behind it (
ui_kit::window_over, #137); dragging the gump's empty parts moves the window.
Items
Wherever an item shows (gump, list row, paperdoll), hovering it gives the
tooltip. It shows the name and amount, weight, quality, damage with AP and
reach or range, the skill, armour, slot and two-handedness, requirements,
food, the tool's skill, durability (amber below a quarter, red when broken),
uses left, material, maker, every magic property in words ("+10 strength",
"+15% weapon damage") and the stolen taint with its time left. Market and recipe rows can
carry container::ItemTip(item_id) for the catalog version.
Right-click opens the item's menu: open, eat, wield/wear, take off, move to the backpack, drop on the ground; from a corpse: take, take everything. Stolen goods say how to get rid of them instead of offering a drop.
Dropping (drag out of every window onto the world, or the menu) sends
Command::Drop { uid, at }. The server puts the item, with what it holds, in a
pile on the tile you let it go over when that is free ground within two tiles
(#134; the menu and anything farther: at your feet), or into a pile already
lying there: a runtime object with the backpack_abandoned sprite, looted
like a corpse by anyone and without crime, rotting after 10 minutes, and the
journal says where it went. Worn items come off with it; the backpack and
stolen goods stay. A corpse window the server refused to open says why ("That
is too far away.") instead of waiting.
The world map
The chart is the bake's atlas pictures: overview.png (1 px = 16 tiles)
zoomed out, atlas-4.png (1 px = 4) closer. Over them it draws:
- Towns by tier, coloured by realm (Karrak red, Aelwyn green, the March amber, the free port gold, the isles teal, the wilds grey), labelled as the zoom allows. Click one to route there.
- The sanctuary of Aquais as a gold ring. The guarded highways are already in the picture (gold edges).
- Realms: province names in the realm colours (
realmschip), with a legend. - Places of interest you have found, dungeons in violet.
- Pins: right-click the map; listed in the sidebar (click to centre,
xto remove). - You, and the route.
Wheel zooms around the pointer, dragging pans, me centres on you, the
footer names the tile under the pointer and the nearest town.
Fog of war (decision #130): the colonial company hands every colonist its survey. Coasts, terrain, roads and every town are known from the start, because a trade run needs them and a blank map would only hide the economy. Dungeons and other places of interest show once you come within 48 tiles of them. Each character keeps this in its layout. Exploring finds the dangerous and rewarding places; it never finds the road to market.
Routes follow the road network. The bake writes roads.json next to
world.json: every road's node points before smoothing, so roads meeting in
a macro cell share a point and the graph joins there. For an existing bake,
uo-atlas --roads-only assets/world/felucca writes just that file (about 45 s,
same seed and size as the bake). Without it the map routes along the guarded
highways and straight legs and warns once. The route is the cheapest way:
road tiles cost 1 (roads 1.05, tracks 1.15), off-road legs onto and off the
network cost 3 (wild ground, water and danger make cross-country slow). It
shows its length in tiles and the running time out of a fight (250 ms a
tile, #92), and the time in a fight (twice that). The route is replanned when
you stray from it. The top bar and the minimap follow it.
Layout per character
player_ui/layout.rs keeps one JSON file per character in ui/ next to the
settings file (~/Library/Application Support/UO Remastered/ui/<name>.json
on macOS). It holds where every fixed window was dragged, which windows are
open (journal, overview, skills, map, character, backpack), the journal's
hidden channels, the overview's sort and tab, and the map's pins, route and
discovered places, and the windows' stacking order. It loads once the
character's sheet names it. Windows from
a bigger screen move back onto this one. It is written a second after the
last change (write, then rename). Reproducible runs (--screenshot,
--headless) keep no layout unless UO_UI_LAYOUT=<dir> names one.
Keys
| Key | Does |
|---|---|
| C, I, U, J, M, K, B, O | character, backpack, skills, journal, map, crafting, market, settings |
/ | keys and mouse |
| Esc | disarm the spell, else close the newest window, else quit (never in a fight) |
| Enter | online: speak (the journal's say line) |
| Q W E R D F | the six ability keys (Classic preset: 1–6; setting keys) |
| 7, Z, X, G, Space, Tab | the Tome's spell, potion, hold, guard (held), dodge, next target |
| 1–6 | the old spellbook's spells (Albion preset) |
| right mouse, arrows | run (arrows in screen directions, in every preset); WASD too in the Classic preset; H roofs. The arrows pan the camera only in galleries and reviews, where there is no character |
| F7 | GI debug views (offline, or with UO_DEV_KEYS=1; never on I) |
The development keys (T pause time, , . scrub it, N night, L cursor light,
[ ] - = the sun, F7 GI views) work offline and with
UO_DEV_KEYS=1, never in a player's online game.
Captures and tests
UO_UI_OPEN=journal,skills,map,help,character,backpack,…opens windows at start;UO_UI_HOVER=x,yshows the tooltip of the item at that point;UO_ROADS=<file>reads a road network made apart;--set ui_scale=1.25.- Over the live client:
cargo run --release -p uo-server -- --world assets/world/felucca --listen 127.0.0.1:7788, thenuo-client --server ws://127.0.0.1:7788 --name Tester --headless --screenshot out.png --seconds 25with the variables above. The screenshots in the post were made this way. - Unit tests: layout files round trip, broken and newer files fall back,
windows move onto smaller screens, and a running app saves a moved window
and an open one and restores both in the next run
(
player_ui::tests::layout_persists_across_runs). There are also tests for the overview's sort and filter, journal tabs, skill locks, context menus, routing (junctions merge, short hops go straight), times, compass words and the minimap's turn.