Numbers you can read, and the client stops drawing its own #48

Manually merged
tvidal merged 4 commits from feat/048-numbers-you-can-read-and-the-client-stops-draw into master 2026-07-09 09:30:00 +02:00
Owner

Numbers you can read, and the client stops drawing its own

Along the way:

  • The character sheet is the screen the player stands in, not a window
  • The gear row is the camera's again, and the button is the icon
  • A blow that arrives, on the side of the screen it happened on

Touches 30 files: docs/CHANGELOG.md, docs/README.md, scripts/harness/sections/16-options-window.lua, scripts/harness/sections/33-anchors.lua, scripts/harness/sections/51-placing.lua, scripts/harness/sections/52-character.lua and 24 more.

Numbers you can read, and the client stops drawing its own Along the way: - The character sheet is the screen the player stands in, not a window - The gear row is the camera's again, and the button is the icon - A blow that arrives, on the side of the screen it happened on Touches 30 files: `docs/CHANGELOG.md`, `docs/README.md`, `scripts/harness/sections/16-options-window.lua`, `scripts/harness/sections/33-anchors.lua`, `scripts/harness/sections/51-placing.lua`, `scripts/harness/sections/52-character.lua` and 24 more.
sokonkwo approved these changes 2026-07-09 09:00:00 +02:00
sokonkwo left a comment

The client's own numbers going away is what makes ours readable; two sets of damage text over one mob is worse than either. Three notes.

The client's own numbers going away is what makes ours readable; two sets of damage text over one mob is worse than either. Three notes.
@ -283,0 +289,4 @@
check(row and row["RightButton"] and row["MiddleButton"],
"the gear row keeps the camera's buttons, so a right drag on a row does not turn the camera")
check(head.button:GetWidth() < head:GetWidth(),
("the secure button is %s wide on a row of %s, so the action is the whole row again")
Owner

The check is that the secure button is narrower than the row, and the message says the action is the whole row again. Those are opposite statements; one of them is worded backwards.

The check is that the secure button is narrower than the row, and the message says the action is the whole row again. Those are opposite statements; one of them is worded backwards.
@ -555,3 +645,2 @@
local function Portrait(parent)
local panel = UI.Box(parent, C.sunken, C.edge)
local level = (panel:GetFrameLevel() or 0) + 5
local panel = CreateFrame("Frame", nil, parent)
Owner

A plain frame wrapping the model, so the model can be absent without the layout changing. That is the shape I would want on every client call that might not be there.

A plain frame wrapping the model, so the model can be absent without the layout changing. That is the shape I would want on every client call that might not be there.
@ -0,0 +170,4 @@
end
-- How many of the four are down, for the harness.
function Blizzard.Quiet()
Owner

A count of how many of the four are down, for the harness. Four is written twice, here and in TAKEN. Read the length.

A count of how many of the four are down, for the harness. Four is written twice, here and in TAKEN. Read the length.
Author
Owner

#TAKEN, and the loop below it already did.

`#TAKEN`, and the loop below it already did.
Author
Owner

PlayerModel being a frame type rather than a template is what makes it cheap. Both calls on it are probed.

PlayerModel being a frame type rather than a template is what makes it cheap. Both calls on it are probed.
Author
Owner

The message was written for the failure and reads as the assertion. Reworded to say what went wrong rather than what should be true.

The message was written for the failure and reads as the assertion. Reworded to say what went wrong rather than what should be true.
Two things a gate could not have found and one look at a screen did.

They were far too small. 22 was chosen against the loot captions at 20,
and both of those are read while standing still; this is read out of the
corner of an eye in the second before it goes, over a mob, while you are
pressing something. 36 now, and the range runs to 72.

Two shrinkings were compounding under it. A number ended its life at 0.72
of its start, and an ordinary hit late in a fight was weighted to 0.78 of
that against the fight's biggest, so a plain hit finished at 12 pixels of
a setting that said 22. The envelope ends at 0.85 and the weight band is
0.88 to 1.18. The band exists to say which of two numbers was the bigger
hit, and the setting says how big numbers are; the band was doing both.
A critical starts at 1.45 rather than 1.3 with it.

And the client is no longer drawing the same hit underneath. Four CVars,
read off Blizzard_SettingsDefinitions_Frame on the 2.5.6 source rather
than typed: damage on the target, periodic spells and pet melee under it,
and healing beside them. The `_v2` on three of those is why they were
read rather than remembered, because the names without it are the retail
spelling and resolve to nothing here, which fails the way a CVar always
fails, silently and looking like it worked.

Not enableFloatingCombatText, which looks like the switch and is the
wrong one. On this client it gates the text beside your character: the
dodges and parries, aura gains and fades, combat state, combo points,
energy, honour and reputation. Blizzard_CombatText registers eight events
behind it and not one is a damage event, so taking it would delete a
dozen readouts to stop one duplicate.

What was there is remembered per character before the first write and put
back when the part goes off, which is the shape Charge/SoftTarget.lua
already uses and for its reason: a part that leaves a CVar wherever it
landed quietly edits your client config. `/wk hits quiet off` leaves the
client alone for anyone who wants both.

The section asserts all four go down, that the master does not, that the
prior values come back rather than the defaults, and that quiet off takes
nothing at all.
It was a dialog: a title bar, a hairline, an opaque ground and eight hundred and
sixty units of fixed width, with a person standing in a box the size of a bank
teller's window. Every number on it was right and it read as a spreadsheet with
a portrait stapled to the corner.

Now it has no chrome at all. UI/Window.lua takes `screen`, which is no ground,
no border, no title bar, no drag and no saved point, the size of the monitor
less a photograph's margin, at the floor of the frame pile so everything the
player opens flows over the top, and with the mouse left alone so a mob behind
it is still a mob you can click. It cannot be moved because there is nowhere to
move it to; Escape and the key that opened it are the two ways out, as before.

Every width on the page is a share of its height, held between the two widths
each is worth having. A character sheet is a person standing up with two lists
beside him: sized off the width instead, an ultrawide gets a giant and a four by
three panel gets a doll. The figure's own frame is narrower than a person is,
because the client scales a model to the width of the frame holding it.

With it: the weapons become rows in the left column rather than three nameless
discs, so all nineteen slots say what is in them; the shirt and tabard go to the
foot of their columns; your name and the four readings come off the bands of
shadow that were laid across the model and become discs at the head of the stats
column; and every stat row is banded, two tones alternating, which is the only
ground a column on a backdrop has.
The sheet is the whole screen, and a square was a secure button the full width
of its column. Nineteen of those in two columns is most of the left and right of
the monitor with no camera in it: a mouse enabled frame takes every button that
lands on it, and the right button drag that turns the camera is one of those.
The file said so itself and called it the price of the right click carrying
`/use <slot>`.

It is a fair price on thirty-six pixels and a bad one on a row two hundred wide,
so the button is now the disc. That is where the client puts a slot's button and
where a bag puts an item's. The row it sits on is an ordinary frame that takes
the mouse for the hover and hands the right and middle buttons back with
UI.PassCamera, so a right drag anywhere on a row turns the camera and the
tooltip still opens on the whole row. The hover is one function hung on both, and
it anchors to the row either way, so crossing onto the icon does not move the
tooltip. The four readings at the head of the stats column hand the camera back
too; they answer no click at all.

What it costs: a left click, a drag out and a drop in all want the icon rather
than the item's name beside it, which is what every other item in the game
already wants. `/use` is untouched and still a right click on the disc.

The skills, reputation and loadouts pages still eat the mouse. Their cover is
opaque and it is the one thing standing between a click and the gear page
underneath, which cannot be hidden in a fight.

52-character.lua gates all three: the button still refuses to pass the right
button, the row has to pass it, and the button has to be narrower than the row
it is on.
Five things wrong with the floating numbers, and the two that mattered are the
two a gate could not have found.

The columns were inverted and named for the wrong thing. `mine` meant blows
landing on me and came off the left anchor, so every identifier in the feature
read as its own opposite and nothing looked wrong. They are `dealt` and `taken`
now, named for the blow rather than for whose it was, and dealt is on the left,
because what you are doing is what is read most and what is read most goes where
a left-to-right reader looks first. hitsMinePoint and hitsTheirsPoint are
retired rather than carried across: the numbers under them are the corner a drag
last left the frame on, and reading them into the new names would put a player's
two columns back exactly where the inversion had them.

They start forty pixels above the character rather than thirty below it. A
number falls the whole of `hits fall` from where it is born, so an anchor under
the feet spent the entire flight in the grass.

The numbers were never too small, which is why raising the size from 22 to 36
did not help and raising it again would not have either. They were rasterised at
fourteen pixels, the outline floor, and then stretched to sixty by the frame's
scale, so what reached the screen was a fourteen pixel picture of a digit with a
fourteen pixel rim smeared over it. The face is thirty-two now and the setting is
still a multiplier over it, which keeps the size scripts/check.sh has to be able
to read as a literal.

They were also the only readout this addon draws over the world still riding
UIParent's scale, so "size 36" meant thirty-six of UIParent's units and what
that came out as on the screen was whatever the player last left the UI scale
slider on. ns.UI.Adrift is Adopt without the grid: off the parent's scale, but
not promising that one unit is one pixel, because ns.Ck.Stream writes this
frame's scale on every tick. What Adopt would have written is the style's
`ground` instead, which the envelope multiplies onto rather than replacing, and
the offsets are divided by the envelope alone so a fall of ninety is ninety of
the caller's own pixels at any font size. With that there is a zoom row, which
took the on-screen list to fourteen and split it: what a pull puts over the
world, and what was there before it.

The envelope had no attack. Every channel decayed from the instant of birth, so
a number appeared at its largest and shrank, which is the shape of something
receding rather than of something landing. A number now appears at six tenths of
its resting size, snaps past that size in fifty milliseconds, settles back over
a tenth of a second, holds still for a beat and only then begins to fall and
fade. A critical is the same four numbers, all larger, with a beat twice as long
so the one that mattered is the one that holds. A number merged into swells from
the size it is being drawn at rather than dropping back to six tenths first,
which would read as a bleed flinching on every tick.

And they no longer mesh. The bow was zero at both ends of the fall, and with a
beat in front of it that is where a burst crowds: everything arriving together
is near the start of its own flight at the same moment. A number leaves its
anchor already out to its own side, at one of five heights off the row, and the
reach cycles over seven tenths to all of it rather than four. The bow's default
is forty-four, a little over the width of a three digit number at the shipped
size, which is the distance two of them have to be apart to be two of them.

Section 80 gates the sides by name, the anchors starting above the middle, that
a number grows before it shrinks, that it overshoots rather than easing into
place, that it is still where it was born through all of that, that two born
together start on different rows, and that a merge swells rather than flinches.
It reads position back multiplied by the frame's own scale, because a number
holding still while it swells writes a smaller offset on every frame.

Section 33 asks ns.UI.OnGrid rather than sniffing the client flag Adopt leaves
behind. A frame whose scale is written by an animation is a different fraction
of a pixel on every frame it is drawn, so the whole-pixel rule cannot be applied
to it and would mean nothing if it were; it is the anchored twin of the moving
fill that gate already excuses.
tvidal manually merged commit 4fc9c39314 into master 2026-07-09 09:30:00 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
northgale/warriorkit!48
No description provided.