nameplates are frames, guild names go overhead, and the camera pivots

- a nameplate is a real frame under WorldFrame, six regions and a health bar in the reference's order, so pfUI, ShaguTweaks and _Nameplates find and restyle plates instead of seeing none; the plate stays out of the mouse's way in this cut (2148)
- a player's guild shows under their overhead name in angle brackets, the line an NPC's subname already used, gated by the UnitNamePlayerGuild option at its stock on (2149)
- smart camera pivot is on, as 1.12 ships it: a mostly vertical drag with the camera pressed against something tilts the view instead of swinging the arm, easing home on release; in water the pivot rises for a swimmer (2149)
- Follow Terrain and head bob are built and ship off, as in 1.12: Follow Terrain leans the camera up or down the slope you are walking onto, and head bob sways the first-person eye in a figure of eight while you move (2149)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Sam
2026-09-11 20:55:15 +07:00
co-authored by Claude Fable 5.1
parent 7d100d8a36
commit 38e45badb9
20 changed files with 4166 additions and 548 deletions
@@ -394,6 +394,7 @@ fn verify_splice(
s: f32,
w: f32,
h: f32,
dpi: f32,
assets: &mut Option<ResMut<WorldAssets>>,
images: &mut Assets<Image>,
font_atlas: &mut Option<ResMut<UiFontAtlas>>,
@@ -411,6 +412,7 @@ fn verify_splice(
s,
w,
h,
dpi,
assets,
images,
font_atlas,
@@ -1001,6 +1003,7 @@ pub(super) fn drive_script(
s,
w,
h,
dpi,
&mut assets,
&mut images,
&mut font_atlas,
@@ -1092,6 +1095,7 @@ pub(super) fn drive_script(
s,
w,
h,
dpi,
&mut assets,
&mut images,
&mut font_atlas,
@@ -1153,6 +1157,7 @@ pub(super) fn drive_script(
s,
w,
h,
dpi,
&mut assets,
&mut images,
&mut font_atlas,
@@ -1281,6 +1286,9 @@ fn convert_entry(
// particle unit run on the screen diagonal (decision 2013).
w: f32,
h: f32,
// The window's DEVICE scale, for the one arm that resamples its art to physical pixels: the
// nameplate border's 0188 sharpen, which is a no-op at any other size.
dpi: f32,
assets: &mut Option<ResMut<WorldAssets>>,
images: &mut Assets<Image>,
font_atlas: &mut Option<ResMut<UiFontAtlas>>,
@@ -1587,7 +1595,22 @@ fn convert_entry(
// generated 128×64 / 128×32 image — white RGB carrying the emblem BLP's own
// alpha — into the Texture the setter was handed; the region carries that as
// a token path and the resolver builds exactly that image.
let resolved = if let Some(blp) = benilla_ui::script::emblem_mask_path(p) {
let resolved = if p == benilla_ui::script::nameplate::BORDER_TEXTURE {
// The V-plate's frame art, resampled to the quad's exact PHYSICAL size
// with the sharp kernel instead of GPU-magnified (0188 — the director's
// "sharpen the same frame", carried across decision 2148's move of the
// plate into the frame system). Keyed by that size, so it re-rasterises on
// a resize and is a cache hit on every frame in between. An addon that
// blanks the border with `SetTexture("")` never reaches here at all: the
// path is gone, and the arm above draws nothing.
let px = |v: f32| (v * dpi).round().max(1.0) as u32;
a.resampled_sprite(
p,
(px(rect.width()), px(rect.height())),
images,
crate::vplates::border::resample_sharp,
)
} else if let Some(blp) = benilla_ui::script::emblem_mask_path(p) {
a.emblem_mask_texture(blp, images)
} else if circular {
a.portrait_texture(p, images)
@@ -4395,16 +4395,6 @@ const UNBACKED_REFERENCE_CVARS: &[(&str, &str)] = &[
keyboard controller and /follow (mode 3). Registered default is \"0\" on every locale but \
koKR (`0x603374` selects on the locale index), so stock West ships it OFF",
),
(
"UnitNamePlayerGuild",
"the `\\n<%s>` guild line in the OVERHEAD name stack — slot a5 of `0x608f50`, gated by bit \
`0x10` of the render mask `[0xce8720]`, player branch only, text from `0x5e09f0` through \
the WGLD guild-identity cache. NOT the V-key nameplate (`ShouldShowName 0x6070a0` \
returns 0 whenever a plate frame is live) and NOT the tooltip (FrameXML's own \
`GetGuildInfo`). The data resolves here now — `ui_guild::GuildState` serves \
`GetGuildInfo` — so what is missing is the third line in `nameplates::drive_nameplates` \
plus its `lines_current` comparator arm and that comparator's differential test",
),
(
"UnitNamePlayerPVPTitle",
"the PvP rank prefix on the overhead name line — slot a4 of `0x608f50`, bit `0x20` of the \
@@ -4412,44 +4402,6 @@ const UNBACKED_REFERENCE_CVARS: &[(&str, &str)] = &[
one step further back than its guild twin: the rank byte streams, but the key's second \
index is a FACTION SIDE that `ui_unit` does not resolve for an arbitrary player yet",
),
(
"cameraTerrainTilt",
"the camera's ground-pitch channel. VERIFIED end to end by the §5 round this work \
dispatched (wow-re `ui/scratch/camera-cvar-gates.md`): the CVar gates predicate \
`0x5105a0`, whose false leg zeroes `[cam+0xa0]` at `0x50d922` and skips both `0x672170` \
terrain raycasts; `0x50d900` forms `slope = (Δz + 5/3)/√(Δx²+Δy²)`, walks the 10-entry \
slope→pitch table at `0x808a40`, clamps ±20°, throttled to 100 ms. Registered \"0\", so \
building it changes nothing until a player asks",
),
(
"cameraBobbing",
"head bob. VERIFIED by the same §5 round, including the threshold nobody had read: \
`[0x8089ac] = 1/6` is a camera DISTANCE and the compare is inclusive, so head bob is \
FIRST-PERSON ONLY. `0x511920` computes `ampH/ampV` from the two amplitude CVars × 1/36, \
`speed = clamp(moveSpeed/7.2, [0.5,1.5])`, horizontal at `freq` and vertical at `2×freq`, \
into the same eye-translation accumulator as the shake; `0x5106f0` is the DECAY \
predicate and fades the residual rather than snapping it. Registered \"0\"",
),
(
"cameraWaterCollision",
"two consumers, both VERIFIED by the §5 round. (1) the collision sweep's class mask \
becomes `0x1f0171` instead of `0x100171` — the `0xf0000` nibble is the ADT liquid layers \
— and `0x511ad0`'s bands re-base the pivot floor/cap. (2) a band CROSSING (not a level \
test: `0x50eb14` snapshots the previous frame's bits before `0x511ad0` rewrites them) \
arms a 5° pitch through the `+0xf4` channel. Registered \"1\", so this one is ON in the \
reference and benilla is the divergence — but the eye/arm verdict of \
`camera-arm-liquid-blind.md` still stands: it is the PIVOT that is liquid-aware",
),
(
"cameraPivot",
"smart pivot. VERIFIED by the §5 round, which also corrected the ±89° claim: predicate \
`0x510690` is `enabled ∧ TYPEMASK_UNIT ∧ not moving/strafing (`&0xf`, so turning in place \
passes) ∧ pitched down ∧ the collision solver's own clip flags `0x30000` nonzero` — i.e. \
it only engages in a frame the camera was ACTUALLY clipped. A mostly-vertical drag then \
routes into the pitch-BIAS channel `cam+0x104` instead of the pitch integrator, with a \
one-sided floor, and `0x5107f0` eases it back at `cameraTargetSmoothSpeed` when the \
predicate drops. Registered \"1\"",
),
// **The SLIDERS' half is empty, and that is the point.** 2115 extended this census from
// `UIOptionsFrameCheckButtons` to `UIOptionsFrameSliders` and it caught exactly one row —
// `cameraYawMoveSpeed`, the MOUSE_LOOK_SPEED slider — which was not cosmetic:
@@ -216,6 +216,11 @@ mod tests {
// re-seats off `!script.has_text_measurer()` — it interrogates the VM instead of a memo,
// which is the same guarantee arrived at the other way.
("ui_script/extract/mod.rs", "last_seam"),
// The plate driver's anti-overlap scratch (decision 2148 put the VM in that system's
// hands). It is cleared at the top of every run and rebuilt from this frame's plates —
// a reused allocation, not memory: nothing in it survives the call it was filled in, so
// a new VM has nothing to stale.
("vplates.rs", "bucket"),
// The window's `scale_factor` beside it (decision 1342) — the other term a measure is
// only correct under, since a logical height becomes an integer DEVICE-pixel raster size.
// A fact about the window, not about the VM; it gates the same re-seat `last_seam` does,