- the bank window is the reference's own `BankFrame.xml` run off the player's chain — our 890-line copy is deleted whole; putting bags into the bank's bag slots works (the two verbs its buttons are wired to now exist), the purse is a real `SmallMoneyFrameTemplate` splitting 987654321 copper into three coin buttons instead of one string, `BANKFRAME_OPENED` loses the argument the real client never fires, and the bank stops auto-opening your bags while the vendor and mailbox keep doing it; the empty-slot click still plays the bag sound, because the reference's own line plays it on every click the cursor did not consume — named as faithful rather than quietly fixed - underneath it, the bank's inventory band is a VIEW over the container feed, not a second copy — inventory ids 40..69 had answered nil because the doll array stops at 24, so the stock file swapped in would have drawn an empty bank; the six bank bag slots become real inventory slots (64..=69) fed off the player descriptor, the three bag verbs are carved from the binary (`PutItemInBackpack` IS `PutItemInBag(0xFF)` through a ten-byte thunk, the occupied-by-an-ordinary-item leg auto-stores with no destination slot at all, and the return is the Lua number 1 or nil — the nil being the whole reason a plain left-click opens a bag), and the swap caught `BankButtonIDToInvSlotID`'s bag arm six slots wrong: it took a bag number 1..6 where the reference passes the button's own id, which is the container id - far sight freezes your own body while the view is out — the fifth conjunct of the movement predicates' shared precondition, joining the health term so the walk, the turn and the follow teardown go down together; both halves are load-bearing: the `IsActivePlayer` half is what keeps a Mind Control victim walking (possession rides the same PLAYER_FARSIGHT field), and the latch half is the RESOLVED engage rather than the raw field — and the camera is untouched, you still look around freely, which is the whole point of the spell - the reset offer no longer outlives the character that earned it — log out after walking out of the Deadmines and your next character was offered a reset the first one had earned — and the one lockout leg no probe can ever reach (vmangos writes a last-instance map only from raid binds, which the client's handler discards as not-a-party-dungeon) is now covered end to end through the real system, its module doc saying plainly: tested and never observed Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
257 lines
14 KiB
XML
257 lines
14 KiB
XML
<!--
|
|
Our implementation of the eight `SetItemButton*` globals every 1.12 window uses to paint an item
|
|
slot: its icon, its stack count, its merchant stock line, and the four vertex-colour verbs. The
|
|
specification is the reference's `ItemButtonTemplate.lua` — the names, the argument order and the
|
|
behaviour of every branch are its contract; the code below is ours, and it is factored the way
|
|
eight near-identical verbs want to be factored (one region lookup, one tint verb) rather than the
|
|
way eight separately-written ones came out.
|
|
|
|
**Why this file exists, and what it cost to be without it.** The director installed Bagnon, hit
|
|
B, and got a frame with a title and a gold line and NO BAG SLOTS. `Bagnon_Core/core/Item.lua`
|
|
ends every slot update with
|
|
|
|
l.204 SetItemButtonDesaturated(item, locked, 0.5, 0.5, 0.5)
|
|
l.209 SetItemButtonTexture(item, texture)
|
|
l.210 SetItemButtonCount(item, itemCount)
|
|
|
|
and all three were nil. The raise lands inside `BagnonFrame_AddBag`'s per-slot loop, so the
|
|
FIRST slot aborts the loop, `frame.size` never advances, `BagnonFrame_Layout` never runs and
|
|
the window is left exactly as wide as its own border. `ui_script::bagnon_render_tests` is the
|
|
reproduction.
|
|
|
|
These are FrameXML, not engine (decision 1190's split): the real client ships them as Lua in
|
|
`Interface\FrameXML\ItemButtonTemplate.lua`, so ours belong here in our own interface rather
|
|
than in a Rust binding. The specification was read off the install's own copy with the
|
|
`mpqcat` example over `WoW/Data/interface.MPQ` — not from memory.
|
|
(An XML comment may not contain a double hyphen, which is why no command line is spelled out
|
|
here; `benilla-mpq/examples/mpqcat.rs`'s own header carries the invocation.)
|
|
|
|
**Corpus demand, counted rather than assumed** (the 218-addon vanilla corpus; `grep -rl`, one
|
|
row per verb, addons/sites):
|
|
|
|
SetItemButtonTexture 4 addons / 19 sites
|
|
SetItemButtonDesaturated 3 addons / 8 sites
|
|
SetItemButtonCount 3 addons / 7 sites
|
|
SetItemButtonTextureVertexColor 3 addons / 7 sites
|
|
SetItemButtonNormalTextureVertexColor 1 addon / 2 sites
|
|
SetItemButtonNameFrameVertexColor 1 addon / 2 sites
|
|
SetItemButtonSlotVertexColor 0 / 0
|
|
SetItemButtonStock 0 / 0
|
|
|
|
Six addons in all — Bagnon_Core, CT_MailMod, CT_MasterMod, AdvancedTradeSkillWindow,
|
|
QuestHistory, SpecialTalentUI. The last two rows have **zero** corpus callers and are here
|
|
anyway because the family is one contract and, once the tint verb below is shared, they are one
|
|
line each; that is stated rather than dressed up as demand (1210).
|
|
|
|
**The virtual `ItemButtonTemplate` Button IS here now — 1719: a prior measured decision is
|
|
revisited when the measurement changes, and it changed.** The paragraph below is kept because
|
|
it records how the number was wrong before it was also incomplete. It used to say
|
|
"the harness's template demand ranks it at zero on both axes", full stop. The zero was real;
|
|
the demand was not. pfUI asks for `ContainerFrameItemButtonTemplate` — which the reference
|
|
declares as `inherits="ItemButtonTemplate"` (ContainerFrame.xml l.4), so it wants this one
|
|
transitively — through a shape the census could not see:
|
|
|
|
local tpl = "ContainerFrameItemButtonTemplate"
|
|
if bag == -1 then tpl = "BankItemButtonGenericTemplate" end
|
|
CreateFrame("Button", "pfBag" .. bag .. "item" .. slot, pfUI.bags[bag], tpl)
|
|
|
|
The instrument said so at itself the whole time (`missing_templates`' doc named the literal-only
|
|
bound), which is the difference between an honest under-report and a wrong number — but a bound
|
|
stated at the function and forgotten at the call site is only half a guard. The census now
|
|
resolves a local bound to a literal, and a test asserts both what it sees and what it still
|
|
cannot (concatenation, table fields, parameters).
|
|
|
|
That paragraph then weighed the chain as "a real unit of work" and left it. **The weighing was
|
|
wrong, and wrong for a reason worth recording: it priced the SCRIPTS, which we already have.**
|
|
`ContainerFrameItemButtonTemplate`'s handlers are `ContainerFrameItemButton_OnLoad/_OnClick/
|
|
_OnEnter/_OnUpdate` and `KeyRingItemButton_OnClick` — and this client does not transcribe those,
|
|
it EXECUTES the reference's own `ContainerFrame.lua` off the player's patch chain
|
|
(`ui_script::reference_ui`, SOURCED). They have been live the whole time. What was missing was
|
|
never the behaviour; it was two XML declarations, because the reference ships its templates in
|
|
`ContainerFrame.xml` and that file is not in the sourced set.
|
|
|
|
So the chain cost what it actually cost: this Button, and — at the time — a transcribed
|
|
`ContainerFrameItemButtonTemplate` beside the adapter that already wrapped one of those very
|
|
handlers. **Decision 1751 then took the last step and made both moot for the windows
|
|
themselves**: `Interface\FrameXML\ContainerFrame.xml` is a manifest entry now, so the
|
|
reference's OWN declaration of that template is the live one and the transcription is deleted.
|
|
This Button stays, because it is the reference's PUBLISHED surface for addons that build their
|
|
own slots and it has to exist whether or not any window of ours uses it.
|
|
|
|
**The bank arm resolves too, since 1751's second window.** `BankItemButtonGenericTemplate`
|
|
inherits `BankItemButtonTemplate`, whose handlers live in `BankFrame.lua` — and that file
|
|
arrives through `Interface\FrameXML\BankFrame.xml`'s own `<Script file=>`, off the chain, the
|
|
same way. This paragraph used to say the arm was a named omission (1134 §4) whose real cost was
|
|
the unsourced Lua plus `this:GetInventorySlot()`, a widget method we do not bind. Neither is
|
|
true any more: the Lua is sourced, and `GetInventorySlot` is not a widget method at all — it is
|
|
a plain field `BankFrameBaseButton_OnLoad` assigns (`this.GetInventorySlot =
|
|
ButtonInventorySlot`). pfUI reaches the bank arm for `bag == -1`, and it now finds a real
|
|
template.
|
|
-->
|
|
<Ui xmlns="http://www.blizzard.com/wow/ui/">
|
|
<Script><![CDATA[
|
|
-- The naming contract every verb below rests on: an "item button" is a Button whose own
|
|
-- name prefixes its regions — `<name>IconTexture`, `<name>Count`, `<name>Stock`,
|
|
-- `<name>NormalTexture`, `<name>NameFrame`, `<name>SlotTexture`. That is the reference's
|
|
-- `$parent…` template shape, and it is why these are free functions taking a button rather
|
|
-- than widget methods: any button that follows the naming works, whoever built it.
|
|
--
|
|
-- TWO DIFFERENT NILS, and the difference is the whole error policy of this file.
|
|
-- A nil BUTTON is a no-op everywhere: callers hand us whatever their loop is holding, and
|
|
-- the reference tolerates that in all eight verbs, so we do too. A button that is missing
|
|
-- the REGION is not tolerated — the lookup yields nil and the very next `:` raises. That
|
|
-- loudness is deliberate: "this is not an item button" is a real mistake by the caller, and
|
|
-- 1203's lesson is that the silent version of a failure is the expensive one.
|
|
|
|
-- One named region off a button, which is the only address these verbs ever compute. Nil in,
|
|
-- nil out, so the button guard reads as one expression instead of eight copied early
|
|
-- returns.
|
|
local function region(button, suffix)
|
|
return button and getglobal(button:GetName() .. suffix)
|
|
end
|
|
|
|
-- Four of the eight differ in nothing but which region they tint, so they share the verb
|
|
-- and name the region. The guard is on the BUTTON and deliberately not on the region it
|
|
-- resolved: a nil button is the tolerated case, a button carrying no such region is the
|
|
-- loud one. `texture` is a local rather than an inline call only so that the raise names
|
|
-- which lookup came back empty.
|
|
local function tint(button, suffix, r, g, b)
|
|
local texture = region(button, suffix)
|
|
if button then
|
|
texture:SetVertexColor(r, g, b)
|
|
end
|
|
end
|
|
|
|
function SetItemButtonCount(button, count)
|
|
if not button then
|
|
return
|
|
end
|
|
count = count or 0
|
|
button.count = count
|
|
local label = region(button, "Count")
|
|
-- `isBag` is the reference's own flag for a bag button, where even a single item
|
|
-- counts as a number worth showing. Above 999 the slot shows a bare `*` — the stack
|
|
-- is "more than the label can say" — while `button.count` keeps the real number.
|
|
if count > 1 or (button.isBag and count > 0) then
|
|
label:SetText(count > 999 and "*" or count)
|
|
label:Show()
|
|
else
|
|
label:Hide()
|
|
end
|
|
end
|
|
|
|
function SetItemButtonStock(button, numInStock)
|
|
if not button then
|
|
return
|
|
end
|
|
-- The reference defaults this to `""` and then compares it with `> 0`, which is a
|
|
-- string/number comparison Lua refuses — a raise on the only path the default can
|
|
-- reach. Ours defaults to 0, which is what the branch below actually means. Not aping
|
|
-- a quirk (method step 3); the behaviour on every reachable path is identical.
|
|
numInStock = numInStock or 0
|
|
button.numInStock = numInStock
|
|
local label = region(button, "Stock")
|
|
if numInStock > 0 then
|
|
label:SetText(format(MERCHANT_STOCK, numInStock))
|
|
label:Show()
|
|
else
|
|
label:Hide()
|
|
end
|
|
end
|
|
|
|
function SetItemButtonTexture(button, texture)
|
|
if not button then
|
|
return
|
|
end
|
|
local icon = region(button, "IconTexture")
|
|
-- Visibility tracks the ARGUMENT, not the region's contents, and it is settled before
|
|
-- the texture is assigned: clearing a slot with a nil texture must leave the region
|
|
-- hidden, never an empty quad standing where the icon was.
|
|
if texture then
|
|
icon:Show()
|
|
else
|
|
icon:Hide()
|
|
end
|
|
icon:SetTexture(texture)
|
|
end
|
|
|
|
function SetItemButtonTextureVertexColor(button, r, g, b)
|
|
tint(button, "IconTexture", r, g, b)
|
|
end
|
|
|
|
function SetItemButtonNormalTextureVertexColor(button, r, g, b)
|
|
tint(button, "NormalTexture", r, g, b)
|
|
end
|
|
|
|
function SetItemButtonNameFrameVertexColor(button, r, g, b)
|
|
tint(button, "NameFrame", r, g, b)
|
|
end
|
|
|
|
function SetItemButtonSlotVertexColor(button, r, g, b)
|
|
tint(button, "SlotTexture", r, g, b)
|
|
end
|
|
|
|
function SetItemButtonDesaturated(button, desaturated, r, g, b)
|
|
-- The one verb whose own nil guard is on the ICON rather than the button: the
|
|
-- reference checks both, and `region()` collapses them into this single test without
|
|
-- changing which calls no-op and which raise.
|
|
local icon = region(button, "IconTexture")
|
|
if not icon then
|
|
return
|
|
end
|
|
-- The reference asks the texture itself whether the desaturating shader is available
|
|
-- (`local shaderSupported = icon:SetDesaturated(desaturated)`) and falls through to a
|
|
-- flat grey tint when it is not — 1.12 ran on fixed-function hardware where that was
|
|
-- the ordinary answer. Since decision 1327 benilla's answer is YES: the icon greys to
|
|
-- luminance in the shader and wears the caller's own tint on top, which is what the
|
|
-- ref's own `(1, 0.65, 0.65, 0.65)` and `(locked, 0.5, 0.5, 0.5)` calls are asking
|
|
-- for. The `icon.SetDesaturated and` guard stays for a region leaf without the verb;
|
|
-- the flat-grey arm below is then the ref's own fallback, unchanged.
|
|
local shaderSupported = icon.SetDesaturated and icon:SetDesaturated(desaturated)
|
|
-- Undesaturated is full colour whatever the caller passed; desaturated is the caller's
|
|
-- own tint when they named one AND the shader took it, and the flat grey otherwise.
|
|
if not desaturated then
|
|
r, g, b = 1.0, 1.0, 1.0
|
|
elseif not r or not shaderSupported then
|
|
r, g, b = 0.5, 0.5, 0.5
|
|
end
|
|
icon:SetVertexColor(r, g, b)
|
|
end
|
|
]]></Script>
|
|
|
|
<!-- Ref `Interface\FrameXML\ItemButtonTemplate.xml` l.4-46, structure verbatim: the region
|
|
NAMES are the whole point of the file, because they are the contract the eight verbs above
|
|
and every addon both address. `$parentIconTexture`, `$parentCount`, `$parentStock`,
|
|
`$parentNormalTexture` — pfUI reads three of those four off a button it built itself
|
|
(modules/bags.lua l.418-431) and never asks who built it.
|
|
|
|
The reference declares NO scripts on this Button, and neither do we; it is a shape, and
|
|
whoever inherits it brings the behaviour. -->
|
|
<Button name="ItemButtonTemplate" virtual="true">
|
|
<Size><AbsDimension x="37" y="37"/></Size>
|
|
<Layers>
|
|
<Layer level="BORDER">
|
|
<Texture name="$parentIconTexture"/>
|
|
<FontString name="$parentCount" inherits="NumberFontNormal" justifyH="RIGHT" hidden="true">
|
|
<Anchors>
|
|
<Anchor point="BOTTOMRIGHT"><Offset><AbsDimension x="-5" y="2"/></Offset></Anchor>
|
|
</Anchors>
|
|
</FontString>
|
|
<FontString name="$parentStock" inherits="NumberFontNormalYellow" justifyH="LEFT" hidden="true">
|
|
<Anchors>
|
|
<Anchor point="TOPLEFT"><Offset><AbsDimension x="0" y="-2"/></Offset></Anchor>
|
|
</Anchors>
|
|
</FontString>
|
|
</Layer>
|
|
</Layers>
|
|
<NormalTexture name="$parentNormalTexture" file="Interface\Buttons\UI-Quickslot2">
|
|
<Size><AbsDimension x="64" y="64"/></Size>
|
|
<Anchors>
|
|
<Anchor point="CENTER"><Offset><AbsDimension x="0" y="-1"/></Offset></Anchor>
|
|
</Anchors>
|
|
</NormalTexture>
|
|
<PushedTexture file="Interface\Buttons\UI-Quickslot-Depress"/>
|
|
<HighlightTexture file="Interface\Buttons\ButtonHilight-Square" alphaMode="ADD"/>
|
|
</Button>
|
|
</Ui>
|