coins ride the cursor, and the trade window and combat text go stock
- coins ride the cursor: `MoneyFrame.lua`/`.xml` load off the player's chain, and the stock kit reads the cursor's coins at load, so the money cursor — payload mode 2, the copper at `[0xb4e2f0]` — is the engine's: `PickupPlayerMoney` puts coins on the cursor and the purse is never debited, and `CoinPickupFrame.xml`, the shift-click coin dialog nothing of ours had ever answered, joins; wow-re's round then set the law: the pickup raises on a non-number and refuses silently on zero, lost control and more than the purse; `AddTradeMoney` folds the coins into the offer as an absolute `CMSG_SET_TRADE_GOLD` gated on the purse; the send-mail setters and `GetCoinIcon` answer as the bytes do; the coinage-change reflex trims an offer the purse no longer covers; and the four send-mail pickup/drop verbs the stock table names do not exist in 5875, so they are not built (1962, 1965) - the trade window is the reference's own file: `TradeFrame.xml` + `TradeFrame.lua` off the chain, ours deleted; `GameTooltip:SetTradePlayerItem`/`SetTradeTargetItem` (`0x5341e0`/`0x534410`) render the slot's item the way the loot slot's is, and `TRADE_PLAYER_ITEM_CHANGED`/`TRADE_TARGET_ITEM_CHANGED` — which the stock file repaints one slot on, and nothing had fired — fire per changed slot beside `TRADE_UPDATE`; the stock file brings the chain's own `MoneyInputFrame`, a TARGET_TRADE `SmallMoneyFrame`, `ItemButtonTemplate` slots and the enchant slot's own wording — "Item not yet modified" where ours had read "Will Not Be Traded"; the wire's enchant id is not carried into the slot yet, so the tooltip shows the item without its enchant (1966) - the dialogs' verbs, as the bytes define them: nine of the ten stock `StaticPopup.lua` bindings 1960 had left named are built off wow-re's round with their packets, events and UIParent arms — the bare trade pair, `ForceLogout`, the instance-boot timer (whole seconds), the area spirit healer's accept/cancel/time, `AcceptBattlefieldPort` out of a three-slot queue `SMSG_BATTLEFIELD_STATUS` fills, `CancelMeetingStoneRequest`, and the pet trainer's pair as the talent wipe's twin, latch for latch — `SMSG_PET_UNLEARN_CONFIRM` latches and asks, `ConfirmPetUnlearn` sends behind the money gate, `CheckPetUntrainerDist` reads the leash; not built, named: the healer's proximity scan (`0x4923b0`: 20 yd to acquire, 22 to retain), so no healer is cached yet, and `ReplaceTradeEnchant`, which rides trade-slot enchant targeting this engine does not build (1963) - the scrolling combat text is the reference's own `Blizzard_CombatText`, LoadOnDemand off the chain: our transcription is deleted, and the addon loads through `UIParentLoadAddOn` at the reference's own sites; the family's fourteen defaults move to the options file where the reference keeps them, because a LoadOnDemand addon cannot define the options that decide whether it loads (1964); and the Looking-for-Group pair is what the bytes define, correcting 1959's flag: `SetLookingForGroup` takes three (type, entry) pairs and a comment, packs each slot as `(type << 24) & entry` — an AND (`0x4e9713`), so every admissible input stores zero — and sends `CMSG_SET_LOOKING_FOR_GROUP` only when the commit changed something; `GetLookingForGroup` answers three nilable slot names then the comment, never 1|nil; and the stock 1.12.1 UI never calls either — both call sites sit in an XML comment (1961) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -1,723 +0,0 @@
|
||||
<Ui>
|
||||
<!--
|
||||
The center-screen scrolling combat text (decision 0578) — the 1.11+ "Floating Combat Text"
|
||||
option, written here in our own terms. The shipped load-on-demand addon
|
||||
`Blizzard_CombatText` (patch.MPQ `Interface\AddOns\Blizzard_CombatText\`, extracted to
|
||||
WoW/_extracted_addons/Blizzard_CombatText/) is the SPECIFICATION, and what it fixes is
|
||||
contract: the message-type keys, the option family, the scroll/fade/crit envelope and
|
||||
every number in them are all addressed by name from addons, so they are reproduced exactly
|
||||
while the code that carries them is ours. The picture: screen-space FontStrings scrolling
|
||||
up from UIParent-bottom +384 (≈ over the character), driven by the engine events this
|
||||
frame registers. Where our behaviour departs from the spec, it is named:
|
||||
- The ref is load-on-demand (`UIParentLoadAddOn` when SHOW_COMBAT_TEXT == "1"); we load
|
||||
always and let CombatText_UpdateDisplayedMessages gate registration, ref-identically.
|
||||
- SHOW_COMBAT_TEXT defaulted "1" here against the ref's "0" from 0578 (the director's ask
|
||||
was the enabled experience) until 1804 put the whole option family back on the
|
||||
reference's own values. All fourteen now sit at the spec's defaults
|
||||
(UIOptionsFrame.lua l.69-84), and the Combat page's checkbox is where it turns on.
|
||||
- The ref's virtual FontString template (CombatTextTemplate) expands to 20 identical
|
||||
strings; region templates are outside the v1 loader's object model (BuffFrame.xml has
|
||||
the precedent note), so the 20 instances inherit the CombatTextFont Font object and
|
||||
carry the template's anchor directly.
|
||||
- UNIT_MANA, the spec's own event. This used to read UNIT_POWER_UPDATE + the Era Unit* API
|
||||
(0068's surface); 1819 retired that pair — 1.12 names the resource in the event, and the
|
||||
Era spelling was a deviation this file's own header already flagged. Same low-mana law,
|
||||
and now the handler is not woken by rage or energy at all. PLAYER_COMBO_POINTS rides the combo wire (0869); its arm reads
|
||||
`GetComboPoints()`, so it is rogue/druid-only and per-target for free — the binding
|
||||
gates, not this file (0875).
|
||||
- Lua 5.0 `for k,v in t` iteration becomes explicit index loops (5.1 VM), backwards
|
||||
where the body removes.
|
||||
- COMBAT_TEXT_TYPE_INFO is grown from a compact spec (colour · gate · flags · the types
|
||||
that share them) walked by the loop under it, instead of one literal per row: the same
|
||||
facts with the repetition taken out. The table it produces IS the contract — the same
|
||||
keys with the same values, and one fresh entry table per key, because `show` is
|
||||
rewritten per entry and an addon may edit any of them.
|
||||
- The two low-resource warnings, the two flow directions in CombatText_AddMessage, and the
|
||||
"park a string back at the seat" step all appear once here; the spec writes each of them
|
||||
out twice.
|
||||
The wire → messageType mapping feeding COMBAT_TEXT_UPDATE lives app-side
|
||||
(net/apply/combat_log.rs + ui_unit.rs) and is PROVISIONAL pending the dispatched wow-re
|
||||
emission pin (decision 0578).
|
||||
-->
|
||||
<Script><![CDATA[
|
||||
-- ── The pool ────────────────────────────────────────────────────────────────────────────
|
||||
-- One screen line per pool string, and the list of the ones currently in flight. The pool
|
||||
-- is fixed: a message arriving with all twenty busy evicts the oldest.
|
||||
NUM_COMBAT_TEXT_LINES = 20
|
||||
COMBAT_TEXT_TO_ANIMATE = {}
|
||||
|
||||
-- ── Where a line sits ───────────────────────────────────────────────────────────────────
|
||||
-- COMBAT_TEXT_LOCATIONS is the seat and the target the current float mode implies; it is
|
||||
-- filled in by CombatText_UpdateDisplayedMessages, not here. A new line is pushed clear of
|
||||
-- everything already in flight by one line plus COMBAT_TEXT_SPACING, but the stack never
|
||||
-- runs further than COMBAT_TEXT_MAX_OFFSET from the seat — past that it starts over. The
|
||||
-- staggered types jitter sideways by up to half COMBAT_TEXT_STAGGER_RANGE either way, so a
|
||||
-- flurry of hits does not print as one column.
|
||||
COMBAT_TEXT_LOCATIONS = {}
|
||||
COMBAT_TEXT_SPACING = 10
|
||||
COMBAT_TEXT_MAX_OFFSET = 130
|
||||
COMBAT_TEXT_STAGGER_RANGE = 20
|
||||
|
||||
-- ── How long a line lives ───────────────────────────────────────────────────────────────
|
||||
-- It travels for COMBAT_TEXT_SCROLLSPEED seconds; COMBAT_TEXT_FADEOUT_TIME is when the
|
||||
-- fade STARTS, not how long it takes, so the fade is the tail of the same window.
|
||||
COMBAT_TEXT_SCROLLSPEED = 1.9
|
||||
COMBAT_TEXT_FADEOUT_TIME = 1.3
|
||||
|
||||
-- ── How big it is drawn ─────────────────────────────────────────────────────────────────
|
||||
-- An ordinary line is COMBAT_TEXT_HEIGHT and stays there. A crit "pops": seeded at the
|
||||
-- minimum, grown to the maximum across CRIT_SCALE_TIME, and back down to ordinary size by
|
||||
-- CRIT_SHRINKTIME (CombatText_OnUpdate walks that envelope).
|
||||
COMBAT_TEXT_HEIGHT = 25
|
||||
COMBAT_TEXT_CRIT_MINHEIGHT = 30
|
||||
COMBAT_TEXT_CRIT_MAXHEIGHT = 60
|
||||
COMBAT_TEXT_CRIT_SCALE_TIME = 0.05
|
||||
COMBAT_TEXT_CRIT_SHRINKTIME = 0.2
|
||||
|
||||
-- ── When the low-resource warnings latch ────────────────────────────────────────────────
|
||||
-- Fractions of the bar, tested on the way down; see CombatText_LowResourceWarning.
|
||||
COMBAT_TEXT_LOW_HEALTH_THRESHOLD = 0.2
|
||||
COMBAT_TEXT_LOW_MANA_THRESHOLD = 0.2
|
||||
|
||||
-- The option family: every saved global this feature owns, next to the value it ships
|
||||
-- with. **All fourteen are the spec's own defaults** (UIOptionsFrame.lua l.69-84), the
|
||||
-- master SHOW_COMBAT_TEXT included — it shipped "1" against the spec's "0" from 0578 to
|
||||
-- 1804 (see the header). Held to that by
|
||||
-- `reference_ui::tests::our_saved_ui_globals_default_to_the_references_own_values`, which
|
||||
-- reads the reference's file off the player's chain rather than trusting this comment.
|
||||
--
|
||||
-- Each name is assigned AT FILE SCOPE and registered for saving in the same pass, because
|
||||
-- the load order is what makes a saved value stick (decision 1128): defaults first, the
|
||||
-- saved chunk over the top, consumers third; reverse any two and a saved value can never
|
||||
-- win. The Options window's Combat page drives all fourteen (decision 1134) and takes its
|
||||
-- "Defaults" from these very globals, read at each row's OnLoad — which runs before the
|
||||
-- saved chunk — so a default lives in exactly one place, this table, and the panel cannot
|
||||
-- drift from it.
|
||||
local combatTextOptions = {
|
||||
{"SHOW_COMBAT_TEXT", "0"},
|
||||
{"COMBAT_TEXT_SHOW_LOW_HEALTH_MANA", "1"},
|
||||
{"COMBAT_TEXT_SHOW_AURAS", "1"},
|
||||
{"COMBAT_TEXT_SHOW_AURA_FADE", "0"},
|
||||
{"COMBAT_TEXT_SHOW_COMBAT_STATE", "1"},
|
||||
{"COMBAT_TEXT_SHOW_DODGE_PARRY_MISS", "0"},
|
||||
{"COMBAT_TEXT_SHOW_RESISTANCES", "0"},
|
||||
{"COMBAT_TEXT_SHOW_REPUTATION", "0"},
|
||||
{"COMBAT_TEXT_SHOW_REACTIVES", "0"},
|
||||
{"COMBAT_TEXT_SHOW_FRIENDLY_NAMES", "0"},
|
||||
{"COMBAT_TEXT_SHOW_COMBO_POINTS", "0"},
|
||||
{"COMBAT_TEXT_SHOW_MANA", "0"},
|
||||
{"COMBAT_TEXT_SHOW_HONOR_GAINED", "1"},
|
||||
{"COMBAT_TEXT_FLOAT_MODE", "1"},
|
||||
}
|
||||
for i = 1, getn(combatTextOptions) do
|
||||
local name, default = combatTextOptions[i][1], combatTextOptions[i][2]
|
||||
setglobal(name, default)
|
||||
RegisterForSave(name)
|
||||
end
|
||||
|
||||
-- The message words (shipped enUS GlobalStrings values, hardcoded like the rest of our
|
||||
-- enUS-only data; or-guarded so an earlier definition wins).
|
||||
MANA = MANA or "Mana"
|
||||
RAGE = RAGE or "Rage"
|
||||
FOCUS = FOCUS or "Focus"
|
||||
ENERGY = ENERGY or "Energy"
|
||||
HEALTH_LOW = HEALTH_LOW or "Health Low"
|
||||
MANA_LOW = MANA_LOW or "Mana Low"
|
||||
ENTERING_COMBAT = ENTERING_COMBAT or "Entering Combat"
|
||||
LEAVING_COMBAT = LEAVING_COMBAT or "Leaving Combat"
|
||||
BLOCK = BLOCK or "Block"
|
||||
ABSORB = ABSORB or "Absorb"
|
||||
RESIST = RESIST or "Resist"
|
||||
SPELL_MISSED = SPELL_MISSED or "Miss"
|
||||
SPELL_DODGED = SPELL_DODGED or "Dodge"
|
||||
SPELL_PARRIED = SPELL_PARRIED or "Parry"
|
||||
SPELL_BLOCKED = SPELL_BLOCKED or "Block"
|
||||
SPELL_EVADED = SPELL_EVADED or "Evade"
|
||||
SPELL_IMMUNE = SPELL_IMMUNE or "Immune"
|
||||
SPELL_DEFLECTED = SPELL_DEFLECTED or "Deflect"
|
||||
SPELL_REFLECTED = SPELL_REFLECTED or "Reflect"
|
||||
SPELL_RESISTED = SPELL_RESISTED or "Resist"
|
||||
SPELL_ABSORBED = SPELL_ABSORBED or "Absorb"
|
||||
AURA_END = AURA_END or "<%s> fades"
|
||||
BLOCK_TRAILER = BLOCK_TRAILER or " (%d blocked)"
|
||||
ABSORB_TRAILER = ABSORB_TRAILER or " (%d absorbed)"
|
||||
RESIST_TRAILER = RESIST_TRAILER or " (%d resisted)"
|
||||
COMBAT_TEXT_HONOR_GAINED = COMBAT_TEXT_HONOR_GAINED or "Honor %s"
|
||||
|
||||
--[[
|
||||
COMBAT_TEXT_TYPE_INFO — one entry per message type, and the four things an entry says:
|
||||
|
||||
r, g, b the colour the line prints in, as 0..1 floats
|
||||
show 1 while this type is currently being printed, nil while it is not.
|
||||
CombatText_UpdateDisplayedMessages rewrites it for every gated type from
|
||||
the option `var` names, so an entry carrying `var` and no `show` has
|
||||
merely not been refreshed yet, while an entry carrying `show` and no
|
||||
`var` is on for as long as the feature itself is
|
||||
isStaggered 1 to jitter the line sideways instead of printing it on the centre line
|
||||
var the name of the saved global that gates this type; nil = ungated
|
||||
|
||||
This table is API: addons index it by message-type key and read and write these fields, so
|
||||
the keys and the values are exactly the specification's. The rows below are OURS — almost
|
||||
every type shares its colour and its gate with several others, so the facts are written
|
||||
once per group and the loop underneath expands them into one entry per type. Fresh table
|
||||
per entry, never a shared one: `show` is rewritten per entry, and an addon may edit any
|
||||
single entry without that leaking into its neighbours.
|
||||
]]
|
||||
|
||||
-- The six colours the whole table draws from.
|
||||
local CT_WHITE = {1, 1, 1}
|
||||
local CT_RED = {1, 0.1, 0.1}
|
||||
local CT_GREEN = {0.1, 1, 0.1}
|
||||
local CT_BLUE = {0.1, 0.1, 1}
|
||||
local CT_VIOLET = {0.79, 0.3, 0.85}
|
||||
local CT_GOLD = {1, 0.82, 0}
|
||||
|
||||
-- Colour · gate · flags · the message types that share them. `always` is the `show` an
|
||||
-- ungated type is born with; `jitter` is `isStaggered`; `gate` is `var`.
|
||||
local combatTextSpec = {
|
||||
-- Always on. Only the master toggle takes these away.
|
||||
{colour = CT_RED, always = 1, jitter = 1, types = {"DAMAGE"}},
|
||||
{colour = CT_RED, always = 1, types = {"DAMAGE_CRIT"}},
|
||||
{colour = CT_VIOLET, always = 1, types = {"SPELL_DAMAGE"}},
|
||||
{colour = CT_WHITE, always = 1, types = {"SPLIT_DAMAGE"}},
|
||||
{colour = CT_GREEN, always = 1, types = {"HEAL", "HEAL_CRIT", "PERIODIC_HEAL", "SPELL_CAST"}},
|
||||
|
||||
-- Never on by themselves: no `always`, no gate, so `show` is nil and stays nil. They
|
||||
-- carry a colour so that anything which does switch one on has one ready.
|
||||
{colour = CT_WHITE, types = {"INTERRUPT", "ENCHANTMENT_ADDED", "ENCHANTMENT_REMOVED",
|
||||
"DISPEL_FAILED", "SPELL_CAST_START", "SPELL_DISPELLED", "DAMAGE_SHIELD",
|
||||
"EXTRA_ATTACKS"}},
|
||||
|
||||
-- "Show dodge/parry/miss": swings that went nowhere, melee in red and spells in white.
|
||||
-- The melee whiffs jitter because several can land in the same instant.
|
||||
{colour = CT_RED, gate = "COMBAT_TEXT_SHOW_DODGE_PARRY_MISS", jitter = 1,
|
||||
types = {"MISS", "DODGE", "PARRY", "EVADE"}},
|
||||
{colour = CT_RED, gate = "COMBAT_TEXT_SHOW_DODGE_PARRY_MISS",
|
||||
types = {"IMMUNE", "DEFLECT"}},
|
||||
{colour = CT_WHITE, gate = "COMBAT_TEXT_SHOW_DODGE_PARRY_MISS",
|
||||
types = {"SPELL_MISSED", "SPELL_DODGED", "SPELL_PARRIED", "SPELL_EVADED",
|
||||
"SPELL_IMMUNE", "SPELL_DEFLECTED", "SPELL_REFLECTED"}},
|
||||
|
||||
-- "Show resistances": a hit that landed but was partly eaten.
|
||||
{colour = CT_RED, gate = "COMBAT_TEXT_SHOW_RESISTANCES",
|
||||
types = {"BLOCK", "ABSORB", "RESIST", "PROC_RESISTED"}},
|
||||
{colour = CT_WHITE, gate = "COMBAT_TEXT_SHOW_RESISTANCES", types = {"SPELL_BLOCKED"}},
|
||||
{colour = CT_VIOLET, gate = "COMBAT_TEXT_SHOW_RESISTANCES",
|
||||
types = {"SPELL_ABSORBED", "SPELL_RESISTED"}},
|
||||
|
||||
-- The aura pair — what landed, and what wore off — each split by whose side it helps.
|
||||
{colour = CT_GREEN, gate = "COMBAT_TEXT_SHOW_AURAS", types = {"AURA_START"}},
|
||||
{colour = CT_RED, gate = "COMBAT_TEXT_SHOW_AURAS", types = {"AURA_START_HARMFUL"}},
|
||||
{colour = CT_GREEN, gate = "COMBAT_TEXT_SHOW_AURA_FADE", types = {"AURA_END"}},
|
||||
{colour = CT_RED, gate = "COMBAT_TEXT_SHOW_AURA_FADE", types = {"AURA_END_HARMFUL"}},
|
||||
|
||||
-- Resources and standing: everything you gained rather than took.
|
||||
{colour = CT_BLUE, gate = "COMBAT_TEXT_SHOW_MANA",
|
||||
types = {"MANA", "RAGE", "FOCUS", "ENERGY"}},
|
||||
{colour = CT_BLUE, gate = "COMBAT_TEXT_SHOW_COMBO_POINTS", types = {"COMBO_POINTS"}},
|
||||
{colour = CT_BLUE, gate = "COMBAT_TEXT_SHOW_REPUTATION", types = {"FACTION"}},
|
||||
{colour = CT_BLUE, gate = "COMBAT_TEXT_SHOW_HONOR_GAINED", types = {"HONOR_GAINED"}},
|
||||
|
||||
-- The state warnings, each on its own switch.
|
||||
{colour = CT_RED, gate = "COMBAT_TEXT_SHOW_LOW_HEALTH_MANA",
|
||||
types = {"HEALTH_LOW", "MANA_LOW"}},
|
||||
{colour = CT_RED, gate = "COMBAT_TEXT_SHOW_COMBAT_STATE",
|
||||
types = {"ENTERING_COMBAT", "LEAVING_COMBAT"}},
|
||||
{colour = CT_GOLD, gate = "COMBAT_TEXT_SHOW_REACTIVES", types = {"SPELL_ACTIVE"}},
|
||||
}
|
||||
|
||||
COMBAT_TEXT_TYPE_INFO = {}
|
||||
for i = 1, getn(combatTextSpec) do
|
||||
local row = combatTextSpec[i]
|
||||
for j = 1, getn(row.types) do
|
||||
COMBAT_TEXT_TYPE_INFO[row.types[j]] = {
|
||||
r = row.colour[1],
|
||||
g = row.colour[2],
|
||||
b = row.colour[3],
|
||||
show = row.always,
|
||||
isStaggered = row.jitter,
|
||||
var = row.gate,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
-- A latched low-resource warning. The line prints on the crossing INTO the danger band and
|
||||
-- not again until the bar has climbed back out of it — otherwise every tick of a fight
|
||||
-- spent under 20% would print another one. `flag` names the field on the CombatText frame
|
||||
-- that remembers we already warned; `low` is the caller's verdict, because "low" is not
|
||||
-- the same question for the two bars (mana additionally requires the bar to BE mana: a
|
||||
-- rage or energy user is never low on mana). Returns the message type to print, or nil.
|
||||
local function CombatText_LowResourceWarning(flag, low, messageType)
|
||||
if not low then
|
||||
CombatText[flag] = nil
|
||||
return nil
|
||||
end
|
||||
if CombatText[flag] then
|
||||
return nil
|
||||
end
|
||||
CombatText[flag] = 1
|
||||
return messageType
|
||||
end
|
||||
|
||||
-- The engine events whose entire meaning is "print this message type". Everything else
|
||||
-- either carries its own type in arg1 (COMBAT_TEXT_UPDATE) or IS its own type.
|
||||
local combatTextStateMessage = {
|
||||
PLAYER_REGEN_DISABLED = "ENTERING_COMBAT",
|
||||
PLAYER_REGEN_ENABLED = "LEAVING_COMBAT",
|
||||
}
|
||||
|
||||
function CombatText_OnLoad()
|
||||
CombatText_UpdateDisplayedMessages()
|
||||
CombatText.previousMana = {}
|
||||
end
|
||||
|
||||
function CombatText_OnEvent(event)
|
||||
-- What we are about to print: the type (which picks the colour and the gating), the
|
||||
-- payload it formats from, the finished line, and the optional "crit" presentation.
|
||||
local messageType, message
|
||||
local data = arg2
|
||||
local displayType
|
||||
|
||||
if event == "UNIT_HEALTH" then
|
||||
if arg1 == "player" then
|
||||
local low = UnitHealth("player") / UnitHealthMax("player") <= COMBAT_TEXT_LOW_HEALTH_THRESHOLD
|
||||
messageType = CombatText_LowResourceWarning("lowHealth", low, "HEALTH_LOW")
|
||||
end
|
||||
if not messageType then
|
||||
return
|
||||
end
|
||||
elseif event == "UNIT_MANA" then
|
||||
-- The powerType == 0 guard still earns its place: `power_token` reports an UNKNOWN
|
||||
-- power type as MANA (its descriptor default), so this event can arrive for a unit
|
||||
-- whose track is not really mana. Rage, focus and energy now have their own event
|
||||
-- names and never reach here at all (1819).
|
||||
local mana = UnitMana("player")
|
||||
local powerType = UnitPowerType("player")
|
||||
if arg1 == "player" then
|
||||
local low = mana / UnitManaMax("player") <= COMBAT_TEXT_LOW_MANA_THRESHOLD and powerType == 0
|
||||
messageType = CombatText_LowResourceWarning("lowMana", low, "MANA_LOW")
|
||||
end
|
||||
if not messageType then
|
||||
return
|
||||
end
|
||||
elseif event == "PLAYER_COMBO_POINTS" then
|
||||
-- The count itself is the payload, and a full bar is worth a crit-sized line.
|
||||
-- Spending them fires this too, with nothing left to announce.
|
||||
data = GetComboPoints()
|
||||
if data <= 0 then
|
||||
return
|
||||
end
|
||||
messageType = "COMBO_POINTS"
|
||||
if data == MAX_COMBO_POINTS then
|
||||
displayType = "crit"
|
||||
end
|
||||
elseif event == "COMBAT_TEXT_UPDATE" then
|
||||
messageType = arg1
|
||||
else
|
||||
messageType = combatTextStateMessage[event] or event
|
||||
end
|
||||
|
||||
-- The type decides the colour and whether this is printed at all. An unknown type
|
||||
-- is white and, having no `show`, prints nothing — which is how a message type we do
|
||||
-- not know about stays silent instead of erroring.
|
||||
local info = COMBAT_TEXT_TYPE_INFO[messageType]
|
||||
if not info then
|
||||
info = {r = 1, g = 1, b = 1}
|
||||
end
|
||||
if not info.show then
|
||||
return
|
||||
end
|
||||
|
||||
local isStaggered = info.isStaggered
|
||||
if messageType == "" then
|
||||
message = nil
|
||||
elseif messageType == "DAMAGE_CRIT" then
|
||||
displayType = "crit"
|
||||
message = "-" .. data
|
||||
elseif messageType == "DAMAGE" or messageType == "SPELL_DAMAGE" then
|
||||
message = "-" .. data
|
||||
elseif messageType == "SPELL_CAST" or messageType == "AURA_START" or messageType == "AURA_START_HARMFUL" then
|
||||
message = "<" .. data .. ">"
|
||||
elseif messageType == "AURA_END" or messageType == "AURA_END_HARMFUL" then
|
||||
message = format(AURA_END, data)
|
||||
elseif messageType == "HEAL" or messageType == "PERIODIC_HEAL" then
|
||||
if COMBAT_TEXT_SHOW_FRIENDLY_NAMES == "1" and messageType == "HEAL" and UnitName("player") ~= data then
|
||||
message = "+" .. arg3 .. " [" .. data .. "]"
|
||||
else
|
||||
message = "+" .. arg3
|
||||
end
|
||||
elseif messageType == "HEAL_CRIT" then
|
||||
displayType = "crit"
|
||||
if COMBAT_TEXT_SHOW_FRIENDLY_NAMES == "1" and UnitName("player") ~= data then
|
||||
message = "+" .. arg3 .. " [" .. data .. "]"
|
||||
else
|
||||
message = "+" .. arg3
|
||||
end
|
||||
elseif messageType == "MANA" then
|
||||
if tonumber(data) > 0 then
|
||||
data = "+" .. data
|
||||
end
|
||||
message = data .. " " .. MANA
|
||||
elseif messageType == "RAGE" then
|
||||
message = "+" .. data .. " " .. RAGE
|
||||
elseif messageType == "FOCUS" then
|
||||
message = "+" .. data .. " " .. FOCUS
|
||||
elseif messageType == "ENERGY" then
|
||||
message = "+" .. data .. " " .. ENERGY
|
||||
elseif messageType == "FACTION" then
|
||||
if tonumber(arg3) > 0 then
|
||||
arg3 = "+" .. arg3
|
||||
end
|
||||
message = "(" .. data .. " " .. arg3 .. ")"
|
||||
elseif messageType == "BLOCK" then
|
||||
if arg3 then
|
||||
message = data .. " " .. format(BLOCK_TRAILER, arg3)
|
||||
else
|
||||
message = BLOCK
|
||||
end
|
||||
elseif messageType == "ABSORB" then
|
||||
if arg3 then
|
||||
message = arg2 .. " " .. format(ABSORB_TRAILER, arg3)
|
||||
else
|
||||
message = ABSORB
|
||||
end
|
||||
elseif messageType == "RESIST" or messageType == "SPELL_RESISTED" then
|
||||
if arg3 then
|
||||
message = data .. " " .. format(RESIST_TRAILER, arg3)
|
||||
else
|
||||
message = RESIST
|
||||
end
|
||||
elseif messageType == "HONOR_GAINED" then
|
||||
if tonumber(data) > 0 then
|
||||
data = "+" .. data
|
||||
end
|
||||
message = format(COMBAT_TEXT_HONOR_GAINED, data)
|
||||
elseif messageType == "SPELL_ACTIVE" then
|
||||
displayType = "crit"
|
||||
message = "<" .. data .. ">"
|
||||
elseif messageType == "COMBO_POINTS" then
|
||||
message = data .. " Combo Points"
|
||||
else
|
||||
message = getglobal(messageType)
|
||||
if not message then
|
||||
message = getglobal("COMBAT_TEXT_" .. messageType)
|
||||
end
|
||||
end
|
||||
|
||||
-- A branch that produced no line (the empty type, or a global-string lookup that
|
||||
-- came back nil) simply prints nothing.
|
||||
if message then
|
||||
CombatText_AddMessage(message, COMBAT_TEXT_SCROLL_FUNCTION, info.r, info.g, info.b, displayType, isStaggered)
|
||||
end
|
||||
end
|
||||
|
||||
function CombatText_OnUpdate(elapsed)
|
||||
-- Backwards, so the removal inside the body can't skip a neighbor (the ref's 5.0
|
||||
-- `for in` + tremove tolerated it; a 5.1 index loop must not).
|
||||
for i = getn(COMBAT_TEXT_TO_ANIMATE), 1, -1 do
|
||||
local value = COMBAT_TEXT_TO_ANIMATE[i]
|
||||
if value.scrollTime >= COMBAT_TEXT_SCROLLSPEED then
|
||||
CombatText_RemoveMessage(value)
|
||||
else
|
||||
value.scrollTime = value.scrollTime + elapsed
|
||||
local xPos, yPos = value.scrollFunction(value)
|
||||
value.yPos = yPos
|
||||
value:SetPoint("TOP", UIParent, "BOTTOM", xPos, yPos)
|
||||
if value.scrollTime >= COMBAT_TEXT_FADEOUT_TIME then
|
||||
local alpha = 1 - ((value.scrollTime - COMBAT_TEXT_FADEOUT_TIME) / (COMBAT_TEXT_SCROLLSPEED - COMBAT_TEXT_FADEOUT_TIME))
|
||||
value:SetAlpha(max(alpha, 0))
|
||||
end
|
||||
-- The crit pop: up to the maximum inside the scale window, back down by the
|
||||
-- shrink time, then it stops being a crit and holds its size.
|
||||
if value.isCrit then
|
||||
if value.scrollTime <= COMBAT_TEXT_CRIT_SCALE_TIME then
|
||||
value:SetTextHeight(floor(COMBAT_TEXT_CRIT_MINHEIGHT + ((COMBAT_TEXT_CRIT_MAXHEIGHT - COMBAT_TEXT_CRIT_MINHEIGHT) * value.scrollTime / COMBAT_TEXT_CRIT_SCALE_TIME)))
|
||||
elseif value.scrollTime <= COMBAT_TEXT_CRIT_SHRINKTIME then
|
||||
value:SetTextHeight(floor(COMBAT_TEXT_CRIT_MAXHEIGHT - ((COMBAT_TEXT_CRIT_MAXHEIGHT - COMBAT_TEXT_CRIT_MINHEIGHT) * (value.scrollTime - COMBAT_TEXT_CRIT_SCALE_TIME) / (COMBAT_TEXT_CRIT_SHRINKTIME - COMBAT_TEXT_CRIT_SCALE_TIME))))
|
||||
else
|
||||
value.isCrit = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- A string that is not in flight: invisible, hidden, and back on the seat so the next
|
||||
-- caller measures it from a known place. Both ways out of the animation list end here.
|
||||
local function CombatText_ParkString(string)
|
||||
string:SetAlpha(0)
|
||||
string:Hide()
|
||||
string:SetPoint("TOP", UIParent, "BOTTOM", COMBAT_TEXT_LOCATIONS.startX, COMBAT_TEXT_LOCATIONS.startY)
|
||||
end
|
||||
|
||||
function CombatText_AddMessage(message, scrollFunction, r, g, b, displayType, isStaggered)
|
||||
local string, noStringsAvailable = CombatText_GetAvailableString()
|
||||
if noStringsAvailable then
|
||||
return
|
||||
end
|
||||
|
||||
string:SetText(message)
|
||||
string:SetTextColor(r, g, b)
|
||||
string.scrollTime = 0
|
||||
|
||||
-- Where the line is seated. `flow` is the direction the STACK grows — back down the
|
||||
-- way the run came, so -1 when the mode floats upward and +1 when it floats down —
|
||||
-- and having it as a number is what lets one walk serve both modes. Start at the
|
||||
-- string's own parked edge, then push clear of every line already in flight by one
|
||||
-- 16-unit line plus COMBAT_TEXT_SPACING; if that has carried the stack further than
|
||||
-- COMBAT_TEXT_MAX_OFFSET from the seat, start the column over at the parked edge
|
||||
-- instead of marching off the screen. (`GetBottom`/`GetTop` are nil before the first
|
||||
-- layout resolve — the parked seat stands in; identical after frame 1.)
|
||||
local flow, parkedY
|
||||
if COMBAT_TEXT_LOCATIONS.startY < COMBAT_TEXT_LOCATIONS.endY then
|
||||
flow = -1
|
||||
parkedY = string:GetBottom()
|
||||
else
|
||||
flow = 1
|
||||
parkedY = string:GetTop()
|
||||
end
|
||||
parkedY = parkedY or COMBAT_TEXT_LOCATIONS.startY
|
||||
|
||||
local seatY = parkedY
|
||||
for i = 1, getn(COMBAT_TEXT_TO_ANIMATE) do
|
||||
local clearOf = COMBAT_TEXT_TO_ANIMATE[i].yPos + flow * (16 + COMBAT_TEXT_SPACING)
|
||||
if (clearOf - seatY) * flow >= 0 then
|
||||
seatY = clearOf
|
||||
end
|
||||
end
|
||||
if (seatY - COMBAT_TEXT_LOCATIONS.startY) * flow > COMBAT_TEXT_MAX_OFFSET then
|
||||
seatY = parkedY
|
||||
end
|
||||
|
||||
-- Everything `displayType` decides, in one place. A crit pops through its own scale
|
||||
-- envelope and always takes the straight scroll (an arc would fight the pop); a crit or
|
||||
-- a sticky ends where it began, so it holds the seat instead of travelling; anything
|
||||
-- else takes the caller's scroll and runs the whole way.
|
||||
if displayType == "crit" then
|
||||
string.scrollFunction = CombatText_StandardScroll
|
||||
string.isCrit = 1
|
||||
string.endY = COMBAT_TEXT_LOCATIONS.startY
|
||||
string:SetTextHeight(COMBAT_TEXT_CRIT_MINHEIGHT)
|
||||
else
|
||||
string.scrollFunction = scrollFunction
|
||||
string:SetTextHeight(COMBAT_TEXT_HEIGHT)
|
||||
if displayType == "sticky" then
|
||||
string.endY = COMBAT_TEXT_LOCATIONS.startY
|
||||
else
|
||||
string.endY = COMBAT_TEXT_LOCATIONS.endY
|
||||
end
|
||||
end
|
||||
|
||||
-- The fountain arcs alternate sides, so consecutive lines do not trace one path.
|
||||
if CombatText.xDir == 1 then
|
||||
CombatText.xDir = -1
|
||||
else
|
||||
CombatText.xDir = 1
|
||||
end
|
||||
string.xDir = CombatText.xDir
|
||||
|
||||
-- Staggered types jitter up to half COMBAT_TEXT_STAGGER_RANGE either side of centre.
|
||||
string.startX = COMBAT_TEXT_LOCATIONS.startX
|
||||
if isStaggered then
|
||||
string.startX = string.startX + random(0, COMBAT_TEXT_STAGGER_RANGE) - COMBAT_TEXT_STAGGER_RANGE / 2
|
||||
end
|
||||
|
||||
string.startY = seatY
|
||||
string.yPos = seatY
|
||||
string:SetPoint("TOP", UIParent, "BOTTOM", string.startX, seatY)
|
||||
string:SetAlpha(1)
|
||||
string:Show()
|
||||
tinsert(COMBAT_TEXT_TO_ANIMATE, string)
|
||||
end
|
||||
|
||||
function CombatText_RemoveMessage(string)
|
||||
for i = 1, getn(COMBAT_TEXT_TO_ANIMATE) do
|
||||
if COMBAT_TEXT_TO_ANIMATE[i] == string then
|
||||
tremove(COMBAT_TEXT_TO_ANIMATE, i)
|
||||
CombatText_ParkString(string)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function CombatText_GetAvailableString()
|
||||
local string
|
||||
for i = 1, NUM_COMBAT_TEXT_LINES do
|
||||
string = getglobal("CombatText" .. i)
|
||||
if not string:IsShown() then
|
||||
return string
|
||||
end
|
||||
end
|
||||
return CombatText_GetOldestString(), 1
|
||||
end
|
||||
|
||||
function CombatText_GetOldestString()
|
||||
local oldestString = COMBAT_TEXT_TO_ANIMATE[1]
|
||||
CombatText_RemoveMessage(oldestString)
|
||||
return oldestString
|
||||
end
|
||||
|
||||
function CombatText_ClearAnimationList()
|
||||
COMBAT_TEXT_TO_ANIMATE = {}
|
||||
for i = 1, NUM_COMBAT_TEXT_LINES do
|
||||
CombatText_ParkString(getglobal("CombatText" .. i))
|
||||
end
|
||||
end
|
||||
|
||||
function CombatText_UpdateDisplayedMessages()
|
||||
-- The master toggle is enforced at the source: with the feature off we stop
|
||||
-- listening entirely rather than filtering every message as it arrives.
|
||||
if SHOW_COMBAT_TEXT == "0" then
|
||||
CombatText:UnregisterEvent("COMBAT_TEXT_UPDATE")
|
||||
CombatText:UnregisterEvent("UNIT_HEALTH")
|
||||
CombatText:UnregisterEvent("UNIT_MANA")
|
||||
CombatText:UnregisterEvent("PLAYER_COMBO_POINTS")
|
||||
CombatText:UnregisterEvent("PLAYER_REGEN_DISABLED")
|
||||
CombatText:UnregisterEvent("PLAYER_REGEN_ENABLED")
|
||||
return
|
||||
else
|
||||
CombatText:RegisterEvent("COMBAT_TEXT_UPDATE")
|
||||
CombatText:RegisterEvent("UNIT_HEALTH")
|
||||
CombatText:RegisterEvent("UNIT_MANA")
|
||||
CombatText:RegisterEvent("PLAYER_COMBO_POINTS")
|
||||
CombatText:RegisterEvent("PLAYER_REGEN_DISABLED")
|
||||
CombatText:RegisterEvent("PLAYER_REGEN_ENABLED")
|
||||
end
|
||||
-- Re-read every gated type's option into its `show`. This is the only writer of
|
||||
-- `show` for a type that carries a `var`, so it runs on every option change.
|
||||
for messageType, value in pairs(COMBAT_TEXT_TYPE_INFO) do
|
||||
if value.var then
|
||||
if getglobal(value.var) == "1" then
|
||||
value.show = 1
|
||||
else
|
||||
value.show = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
-- The engine's UI space is the 768-virtual coordinate system itself (decision
|
||||
-- 0582), so the ref constants stand verbatim (0581's px shim retired). The ref's
|
||||
-- uiScale-cvar division joins when a cvar system exists.
|
||||
local uiScale = 1
|
||||
-- The float mode picks the path and the run: modes 1 and 3 rise to 609 (3 on an
|
||||
-- arc), mode 2 falls to 159. All three start at the same seat.
|
||||
if COMBAT_TEXT_FLOAT_MODE == "1" then
|
||||
COMBAT_TEXT_SCROLL_FUNCTION = CombatText_StandardScroll
|
||||
COMBAT_TEXT_LOCATIONS = {
|
||||
startX = 0,
|
||||
startY = 384 / uiScale,
|
||||
endX = 0,
|
||||
endY = 609 / uiScale
|
||||
}
|
||||
elseif COMBAT_TEXT_FLOAT_MODE == "2" then
|
||||
COMBAT_TEXT_SCROLL_FUNCTION = CombatText_StandardScroll
|
||||
COMBAT_TEXT_LOCATIONS = {
|
||||
startX = 0,
|
||||
startY = 384 / uiScale,
|
||||
endX = 0,
|
||||
endY = 159 / uiScale
|
||||
}
|
||||
else
|
||||
COMBAT_TEXT_SCROLL_FUNCTION = CombatText_FountainScroll
|
||||
COMBAT_TEXT_LOCATIONS = {
|
||||
startX = 0,
|
||||
startY = 384 / uiScale,
|
||||
endX = 0,
|
||||
endY = 609 / uiScale
|
||||
}
|
||||
end
|
||||
CombatText_ClearAnimationList()
|
||||
end
|
||||
|
||||
function CombatText_StandardScroll(value)
|
||||
local xPos = value.startX + ((COMBAT_TEXT_LOCATIONS.endX - COMBAT_TEXT_LOCATIONS.startX) * value.scrollTime / COMBAT_TEXT_SCROLLSPEED)
|
||||
local yPos = value.startY + ((value.endY - COMBAT_TEXT_LOCATIONS.startY) * value.scrollTime / COMBAT_TEXT_SCROLLSPEED)
|
||||
return xPos, yPos
|
||||
end
|
||||
|
||||
function CombatText_FountainScroll(value)
|
||||
local radius = 150
|
||||
local xPos = value.xDir * (value.startX + radius * cos(90 * value.scrollTime / COMBAT_TEXT_SCROLLSPEED)) - value.xDir * radius
|
||||
local yPos = value.startY + radius * sin(90 * value.scrollTime / COMBAT_TEXT_SCROLLSPEED)
|
||||
return xPos, yPos
|
||||
end
|
||||
]]></Script>
|
||||
|
||||
<!-- The 20-string pool (ref CombatText1..20, each an expansion of CombatTextTemplate:
|
||||
CombatTextFont, hidden, TOP anchored to UIParent BOTTOM (0,384) — re-seated per frame by
|
||||
CombatText_OnUpdate). -->
|
||||
<Frame name="CombatText" toplevel="true" parent="UIParent" hidden="false">
|
||||
<Size><AbsDimension x="10" y="10"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="CENTER"><Offset><AbsDimension x="0" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
<Layers>
|
||||
<Layer level="BACKGROUND">
|
||||
<FontString name="CombatText1" inherits="CombatTextFont" hidden="true">
|
||||
<Anchors><Anchor point="TOP" relativeTo="UIParent" relativePoint="BOTTOM"><Offset><AbsDimension x="0" y="384"/></Offset></Anchor></Anchors>
|
||||
</FontString>
|
||||
<FontString name="CombatText2" inherits="CombatTextFont" hidden="true">
|
||||
<Anchors><Anchor point="TOP" relativeTo="UIParent" relativePoint="BOTTOM"><Offset><AbsDimension x="0" y="384"/></Offset></Anchor></Anchors>
|
||||
</FontString>
|
||||
<FontString name="CombatText3" inherits="CombatTextFont" hidden="true">
|
||||
<Anchors><Anchor point="TOP" relativeTo="UIParent" relativePoint="BOTTOM"><Offset><AbsDimension x="0" y="384"/></Offset></Anchor></Anchors>
|
||||
</FontString>
|
||||
<FontString name="CombatText4" inherits="CombatTextFont" hidden="true">
|
||||
<Anchors><Anchor point="TOP" relativeTo="UIParent" relativePoint="BOTTOM"><Offset><AbsDimension x="0" y="384"/></Offset></Anchor></Anchors>
|
||||
</FontString>
|
||||
<FontString name="CombatText5" inherits="CombatTextFont" hidden="true">
|
||||
<Anchors><Anchor point="TOP" relativeTo="UIParent" relativePoint="BOTTOM"><Offset><AbsDimension x="0" y="384"/></Offset></Anchor></Anchors>
|
||||
</FontString>
|
||||
<FontString name="CombatText6" inherits="CombatTextFont" hidden="true">
|
||||
<Anchors><Anchor point="TOP" relativeTo="UIParent" relativePoint="BOTTOM"><Offset><AbsDimension x="0" y="384"/></Offset></Anchor></Anchors>
|
||||
</FontString>
|
||||
<FontString name="CombatText7" inherits="CombatTextFont" hidden="true">
|
||||
<Anchors><Anchor point="TOP" relativeTo="UIParent" relativePoint="BOTTOM"><Offset><AbsDimension x="0" y="384"/></Offset></Anchor></Anchors>
|
||||
</FontString>
|
||||
<FontString name="CombatText8" inherits="CombatTextFont" hidden="true">
|
||||
<Anchors><Anchor point="TOP" relativeTo="UIParent" relativePoint="BOTTOM"><Offset><AbsDimension x="0" y="384"/></Offset></Anchor></Anchors>
|
||||
</FontString>
|
||||
<FontString name="CombatText9" inherits="CombatTextFont" hidden="true">
|
||||
<Anchors><Anchor point="TOP" relativeTo="UIParent" relativePoint="BOTTOM"><Offset><AbsDimension x="0" y="384"/></Offset></Anchor></Anchors>
|
||||
</FontString>
|
||||
<FontString name="CombatText10" inherits="CombatTextFont" hidden="true">
|
||||
<Anchors><Anchor point="TOP" relativeTo="UIParent" relativePoint="BOTTOM"><Offset><AbsDimension x="0" y="384"/></Offset></Anchor></Anchors>
|
||||
</FontString>
|
||||
<FontString name="CombatText11" inherits="CombatTextFont" hidden="true">
|
||||
<Anchors><Anchor point="TOP" relativeTo="UIParent" relativePoint="BOTTOM"><Offset><AbsDimension x="0" y="384"/></Offset></Anchor></Anchors>
|
||||
</FontString>
|
||||
<FontString name="CombatText12" inherits="CombatTextFont" hidden="true">
|
||||
<Anchors><Anchor point="TOP" relativeTo="UIParent" relativePoint="BOTTOM"><Offset><AbsDimension x="0" y="384"/></Offset></Anchor></Anchors>
|
||||
</FontString>
|
||||
<FontString name="CombatText13" inherits="CombatTextFont" hidden="true">
|
||||
<Anchors><Anchor point="TOP" relativeTo="UIParent" relativePoint="BOTTOM"><Offset><AbsDimension x="0" y="384"/></Offset></Anchor></Anchors>
|
||||
</FontString>
|
||||
<FontString name="CombatText14" inherits="CombatTextFont" hidden="true">
|
||||
<Anchors><Anchor point="TOP" relativeTo="UIParent" relativePoint="BOTTOM"><Offset><AbsDimension x="0" y="384"/></Offset></Anchor></Anchors>
|
||||
</FontString>
|
||||
<FontString name="CombatText15" inherits="CombatTextFont" hidden="true">
|
||||
<Anchors><Anchor point="TOP" relativeTo="UIParent" relativePoint="BOTTOM"><Offset><AbsDimension x="0" y="384"/></Offset></Anchor></Anchors>
|
||||
</FontString>
|
||||
<FontString name="CombatText16" inherits="CombatTextFont" hidden="true">
|
||||
<Anchors><Anchor point="TOP" relativeTo="UIParent" relativePoint="BOTTOM"><Offset><AbsDimension x="0" y="384"/></Offset></Anchor></Anchors>
|
||||
</FontString>
|
||||
<FontString name="CombatText17" inherits="CombatTextFont" hidden="true">
|
||||
<Anchors><Anchor point="TOP" relativeTo="UIParent" relativePoint="BOTTOM"><Offset><AbsDimension x="0" y="384"/></Offset></Anchor></Anchors>
|
||||
</FontString>
|
||||
<FontString name="CombatText18" inherits="CombatTextFont" hidden="true">
|
||||
<Anchors><Anchor point="TOP" relativeTo="UIParent" relativePoint="BOTTOM"><Offset><AbsDimension x="0" y="384"/></Offset></Anchor></Anchors>
|
||||
</FontString>
|
||||
<FontString name="CombatText19" inherits="CombatTextFont" hidden="true">
|
||||
<Anchors><Anchor point="TOP" relativeTo="UIParent" relativePoint="BOTTOM"><Offset><AbsDimension x="0" y="384"/></Offset></Anchor></Anchors>
|
||||
</FontString>
|
||||
<FontString name="CombatText20" inherits="CombatTextFont" hidden="true">
|
||||
<Anchors><Anchor point="TOP" relativeTo="UIParent" relativePoint="BOTTOM"><Offset><AbsDimension x="0" y="384"/></Offset></Anchor></Anchors>
|
||||
</FontString>
|
||||
</Layer>
|
||||
</Layers>
|
||||
<Scripts>
|
||||
<OnLoad>CombatText_OnLoad()</OnLoad>
|
||||
<OnEvent>CombatText_OnEvent(event)</OnEvent>
|
||||
<OnUpdate>CombatText_OnUpdate(arg1)</OnUpdate>
|
||||
</Scripts>
|
||||
</Frame>
|
||||
</Ui>
|
||||
@@ -1,659 +0,0 @@
|
||||
<Ui>
|
||||
<!--
|
||||
THE MONEY FRAME KIT — `MoneyFrameTemplate` / `SmallMoneyFrameTemplate`, the `MoneyTypeInfo`
|
||||
table and the `MoneyFrame_*` API that drives them.
|
||||
|
||||
This is **our implementation** of that kit. The specification it implements is the extracted
|
||||
1.12 `Interface\FrameXML\MoneyFrame.lua` (356 lines) and `MoneyFrame.xml` (297), cited below
|
||||
as `ref l.N`: the names, the published field names, the constants and every branch of the
|
||||
collapse rule are the specification's, because addons call this by name and a compatible kit
|
||||
has no freedom in any of that. How it is written — the structure, the helpers, the local
|
||||
names, the comments — is ours.
|
||||
|
||||
WHY IT IS HERE: it is FrameXML's surface, not Rust's (decision 1190 — a name the shipped 1.12
|
||||
UI defines belongs in a UI file, never hardcoded), and the addon corpus asks for it by name.
|
||||
`MoneyFrame_SetType` is wanted by 11 addons, `MoneyFrame_Update` by 9, `SmallMoneyFrame_OnLoad`
|
||||
by 9, and 13 declare a frame `inherits="SmallMoneyFrameTemplate"`. `MoneyTypeInfo` was one of
|
||||
the top runtime walls the frame rename left standing.
|
||||
|
||||
── THE OTHER COIN KIT, and why there are still two ──────────────────────────────────────────
|
||||
There WAS a second coin kit — `BenillaMoney_Set`/`_Clear`/`_SetColor`, a three-SLOT chain
|
||||
(`<prefix>Coin1..3`) that filled only the non-zero denominations in display order and
|
||||
re-flowed, born in MerchantFrame.xml and used by eight windows, with a layout the director had
|
||||
signed off across them. The 2026-09-03 ruling — faithful wins, do not park on the director
|
||||
what the reference already answers — superseded that sign-off: 1928 (quest reward money) and
|
||||
1937 (quest required money, mail, trade, tooltip, quest log, trainer) moved every window onto
|
||||
this file's templates, and the helpers were deleted with the last consumer. This file is the
|
||||
only money kit now.
|
||||
|
||||
The auction house is the exception, and the reason the reconciliation started there rather
|
||||
than anywhere else: the three-slot kit has no notion of a money TYPE, so it cannot express
|
||||
`showSmallerCoins` or `fixedWidth` — and those two flags ARE the auction house's price column
|
||||
(`MoneyTypeInfo["AUCTION"]`, declared below and, until AuctionFrame.xml moved, used by
|
||||
nothing). On the three-slot kit "1g" showed as one lone gold coin where the reference shows
|
||||
`1g 0s 0c`, and a column of prices packed tight instead of lining up. That is not a layout
|
||||
preference to re-approve, it is the kit being the wrong kit. See decision 1522.
|
||||
|
||||
So the two still coexist, and the split is now by KIT CAPABILITY as much as by audience:
|
||||
· `BenillaMoney_*` + `<prefix>Coin1..3` — the eight windows whose look is signed off and
|
||||
whose money is all plain collapsing STATIC amounts, which that kit renders correctly.
|
||||
· `MoneyFrame_*` + `MoneyTypeInfo` here — the names and structure an addon writes against,
|
||||
what an addon's `inherits="SmallMoneyFrameTemplate"` resolves to, and what the auction
|
||||
house is laid out on.
|
||||
Finishing the migration — moving those eight onto `SmallMoneyFrameTemplate` and retiring
|
||||
`BenillaMoney_*` — is still the right end state; it is a re-layout of hand-authored art in
|
||||
eight files (an approved look), so it stays named here rather than done in passing.
|
||||
|
||||
── WHAT IS NOT CARRIED, and why (each is guarded at its own site below) ─────────────────────
|
||||
· THE COIN PICKUP. `OpenCoinPickupFrame`/`UpdateCoinPickupFrame`/`CoinPickupFrame` belong to
|
||||
CoinPickupFrame.lua/.xml, which we do not ship — and the engine verbs underneath them
|
||||
(`PickupPlayerMoney`, `DropCursorMoney`, `PickupTradeMoney`, `AddTradeMoney`) are not built
|
||||
either, so a picked-up coin would have nowhere to go. The coin buttons still take the mouse
|
||||
for a `canPickup` type (that is the specified state) and the click is guarded, so it does
|
||||
nothing.
|
||||
· `GetCursorMoney` (PLAYER's amount, ref l.17) — the engine verb for coins held on the cursor.
|
||||
With no coin pickup there is no cursor money, so its term is not approximated away: it is
|
||||
structurally zero. Stated at the spot.
|
||||
· `GetSendMailMoney` / `GetSendMailCOD` (ref l.73, l.90) — 1.12 engine verbs we have not
|
||||
built. We ship their SETTERS (`SetSendMailMoney`/`SetSendMailCOD`, which MailFrame.xml
|
||||
drives); nothing reads the amount back yet, so those two types read 0.
|
||||
· `message()` (ref l.145, l.173, l.181) — the one-line StaticPopup shim, which belongs to
|
||||
BasicControls.xml, a file family we have not built. Guarded, so a misuse of the API stays
|
||||
silent instead of erroring on the diagnostic.
|
||||
· `Button:GetTextWidth()` (ref l.202 etc.) — BUILT, and called here, exactly as the
|
||||
specification calls it. It forwards to the label FontString's `GetStringWidth`, which the
|
||||
engine now answers inside the tick that asked (`script::measure`).
|
||||
· The four `Pickup*`/`Add*` mail verbs the pickup closures name are not in 1.12's `_G` either
|
||||
(they are absent from reference/1.12-globals.tsv) — so those closures are as unreachable
|
||||
here as they are there. They exist because the table's shape is the contract.
|
||||
-->
|
||||
<Script><![CDATA[
|
||||
-- ══ THE THREE-SLOT COIN KIT (`BenillaMoney_*`), rehomed from MerchantFrame.xml ═══════════
|
||||
--
|
||||
-- The OTHER kit — see this file's header for why there are two and what the split is. It
|
||||
-- lives beside `MoneyFrame_*` rather than inside a window because eight windows share it:
|
||||
-- merchant, bank, trade, mail, quest, quest log, taxi and trainer. Nothing about the kit
|
||||
-- changed in the move; the `SmallMoneyFrameTemplate` migration the header names is still
|
||||
-- outstanding and still the director's call.
|
||||
-- The coin display, quoted from the real SmallMoneyFrame (ref MoneyFrame.xml/lua): a copper
|
||||
-- amount renders as a number + a 13px coin icon per NONZERO denomination, cut from the one
|
||||
-- shared texture Interface\MoneyFrame\UI-MoneyIcons by TexCoord (gold 0..0.25, silver
|
||||
-- 0.25..0.5, copper 0.5..0.75 — ref l.115/74/33), the real collapse that hides zero coins
|
||||
-- (MoneyFrame.lua l.221-267). Each money display owns three position-"slots"; the XML chains
|
||||
-- them either left-to-right (row prices + the quest reward: Coin1 leftmost, gold first) or
|
||||
-- right-to-left (the purses: Coin1 rightmost, copper first). BenillaMoney_Set fills the
|
||||
-- visible denominations into slots 1.. in DISPLAY order (highestFirst=true for the
|
||||
-- left-aligned chains) and — the real shrink, MoneyFrame.lua l.202/269 — sizes each slot to
|
||||
-- exactly its number + its 13px coin (`SetWidth(GetTextWidth() + iconWidth)` there; the
|
||||
-- same read here, off the number FontString the slot already owns). The static ±4px
|
||||
-- chain-anchor offsets are the real MONEY_BUTTON_SPACING gap, so a shrunk group packs tight,
|
||||
-- left-flush in the ltr chains and right-flush in the rtl ones. BenillaMoney_Clear hides
|
||||
-- all three slots (the real collapse hides, see the helper).
|
||||
--
|
||||
-- NOTE (for the loot window's fold): the reusable helpers are
|
||||
-- BenillaMoney_Set(prefix, copper) and BenillaMoney_Clear(prefix)
|
||||
-- where `prefix` names the slot frames `<prefix>Coin1..3` (each with a `<prefix>CoinNIcon`
|
||||
-- texture + `<prefix>CoinNNum` fontstring child). They live in this file.
|
||||
--
|
||||
-- They lived in MerchantFrame.xml until 1751 came for that window: the stock
|
||||
-- `MerchantFrame.xml` off the player's chain has no room for eight other windows'
|
||||
-- shared helpers, and this file is where the reference keeps money code anyway. A pure
|
||||
-- move — not the `SmallMoneyFrameTemplate` migration named at the top of this file,
|
||||
-- which is a re-layout of an approved look and is the director's to call.
|
||||
BENILLA_COIN_TC = {
|
||||
{ 0.0, 0.25 }, -- [1] gold
|
||||
{ 0.25, 0.5 }, -- [2] silver
|
||||
{ 0.5, 0.75 }, -- [3] copper
|
||||
}
|
||||
|
||||
-- ── What a coin is worth ─────────────────────────────────────────────────────────────────
|
||||
-- Published globals: an addon that formats a price does the same split we do below, and
|
||||
-- reads these rather than spelling 100 twice.
|
||||
COPPER_PER_SILVER = 100
|
||||
SILVER_PER_GOLD = 100
|
||||
COPPER_PER_GOLD = COPPER_PER_SILVER * SILVER_PER_GOLD
|
||||
|
||||
-- ── How wide a coin draws ────────────────────────────────────────────────────────────────
|
||||
-- One coin icon's width at each of the two sizes the templates below come in, and the gap
|
||||
-- between two coin buttons — NEGATIVE, because the three deliberately bite into each other
|
||||
-- so a group reads as one number rather than as three. `COIN_BUTTON_WIDTH` is the fixed slot
|
||||
-- a `fixedWidth` type pads its smaller coins out to, so a column of prices lines up.
|
||||
-- The values are the specification's (ref l.2-12): measurements of the shipped coin art, not
|
||||
-- ours to re-derive.
|
||||
MONEY_ICON_WIDTH = 19
|
||||
MONEY_ICON_WIDTH_SMALL = 13
|
||||
MONEY_BUTTON_SPACING = -4
|
||||
MONEY_BUTTON_SPACING_SMALL = -4
|
||||
COIN_BUTTON_WIDTH = 32
|
||||
|
||||
-- ── The money types (ref l.14-103) ───────────────────────────────────────────────────────
|
||||
--
|
||||
-- A money frame carries no amount of its own: it carries a TYPE, and the type says where the
|
||||
-- number comes from and how the coins behave. An entry has three parts:
|
||||
--
|
||||
-- · `UpdateFunc()` returns the amount, in copper. It is called with the global `this` bound
|
||||
-- to the frame being painted, so a type may read the frame as well as the engine.
|
||||
-- · `PickupFunc(amount)` / `DropFunc()` are the cursor verbs a click-to-pick-up type drives.
|
||||
-- · the display flags `MoneyFrame_Update` reads: `collapse` (drop leading zero
|
||||
-- denominations), `showSmallerCoins` (keep the trailing ones on even at zero; the value
|
||||
-- "Backpack" additionally keeps copper on by itself), `canPickup` (the coins take the
|
||||
-- mouse), `fixedWidth` (pad the smaller coins to COIN_BUTTON_WIDTH), `truncateSmallCoins`
|
||||
-- (drop copper once there is gold).
|
||||
--
|
||||
-- Nothing else is consulted. That is why those field names and the two function signatures
|
||||
-- are the published contract and not an implementation detail: addons register types of their
|
||||
-- own into this table and expect exactly these parts to be honoured.
|
||||
MoneyTypeInfo = { }
|
||||
|
||||
-- Read an amount through an engine verb that may not be built here yet: absent, it reads 0
|
||||
-- rather than calling into nil. Two of the seven types below need it — see the header.
|
||||
local function AmountFrom(verbName)
|
||||
local verb = getglobal(verbName)
|
||||
if verb then
|
||||
return verb()
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
-- The purse. The specification subtracts a third term here (ref l.17,
|
||||
-- `GetMoney() - GetCursorMoney() - GetPlayerTradeMoney()`): coins lifted onto the cursor by
|
||||
-- the coin-pickup frame. benilla ships neither that frame nor the `PickupPlayerMoney` verb
|
||||
-- behind it, so the term is structurally 0 rather than approximated away — it comes back the
|
||||
-- moment either lands.
|
||||
MoneyTypeInfo["PLAYER"] = {
|
||||
UpdateFunc = function() return GetMoney() - GetPlayerTradeMoney() end,
|
||||
PickupFunc = function(amount) PickupPlayerMoney(amount) end,
|
||||
DropFunc = function() DropCursorMoney() end,
|
||||
collapse = 1,
|
||||
canPickup = 1,
|
||||
showSmallerCoins = "Backpack",
|
||||
}
|
||||
|
||||
-- A number the owner parked on the frame itself — what `MoneyFrame_Update(name, amount)`
|
||||
-- leaves behind, and how an addon shows a price it worked out for itself.
|
||||
MoneyTypeInfo["STATIC"] = {
|
||||
UpdateFunc = function() return this.staticMoney end,
|
||||
collapse = 1,
|
||||
}
|
||||
|
||||
-- The auction house's price column: the same source as STATIC, but the smaller coins pad out
|
||||
-- to a fixed slot and stay on at zero, so a stack of prices reads as a column of digits.
|
||||
MoneyTypeInfo["AUCTION"] = {
|
||||
UpdateFunc = function() return this.staticMoney end,
|
||||
collapse = 1,
|
||||
showSmallerCoins = "Backpack",
|
||||
fixedWidth = 1,
|
||||
truncateSmallCoins = nil,
|
||||
}
|
||||
|
||||
-- Our side of an open trade, and the other party's. Only ours is click-to-pick-up.
|
||||
MoneyTypeInfo["PLAYER_TRADE"] = {
|
||||
UpdateFunc = function() return GetPlayerTradeMoney() end,
|
||||
PickupFunc = function(amount) PickupTradeMoney(amount) end,
|
||||
DropFunc = function() AddTradeMoney() end,
|
||||
collapse = 1,
|
||||
canPickup = 1,
|
||||
}
|
||||
|
||||
MoneyTypeInfo["TARGET_TRADE"] = {
|
||||
UpdateFunc = function() return GetTargetTradeMoney() end,
|
||||
collapse = 1,
|
||||
}
|
||||
|
||||
-- Mail postage and cash-on-delivery. Postage explicitly does NOT collapse (ref l.84): the
|
||||
-- send-mail frame wants all three coins standing whatever the amount, because you are typing
|
||||
-- into them. Both read 0 until their engine getters exist — see the header.
|
||||
MoneyTypeInfo["SEND_MAIL"] = {
|
||||
UpdateFunc = function() return AmountFrom("GetSendMailMoney") end,
|
||||
PickupFunc = function(amount) PickupSendMailMoney(amount) end,
|
||||
DropFunc = function() AddSendMailMoney() end,
|
||||
collapse = nil,
|
||||
canPickup = 1,
|
||||
showSmallerCoins = "Backpack",
|
||||
}
|
||||
|
||||
MoneyTypeInfo["SEND_MAIL_COD"] = {
|
||||
UpdateFunc = function() return AmountFrom("GetSendMailCOD") end,
|
||||
PickupFunc = function(amount) PickupSendMailCOD(amount) end,
|
||||
DropFunc = function() AddSendMailCOD() end,
|
||||
collapse = 1,
|
||||
canPickup = 1,
|
||||
}
|
||||
|
||||
-- ── Hearing about a change (ref l.105-140) ───────────────────────────────────────────────
|
||||
--
|
||||
-- Every money frame registers all five money events; each then decides whether the one that
|
||||
-- arrived is about the number IT is showing. That pairing is data, not control flow, so it is
|
||||
-- written as data: one row per event, in the order the templates register them, naming the
|
||||
-- money types that event can change.
|
||||
local MONEY_EVENTS = {
|
||||
{ "PLAYER_MONEY", { PLAYER = 1 } },
|
||||
{ "PLAYER_TRADE_MONEY", { PLAYER = 1, PLAYER_TRADE = 1 } },
|
||||
{ "TRADE_MONEY_CHANGED", { TARGET_TRADE = 1 } },
|
||||
{ "SEND_MAIL_MONEY_CHANGED", { PLAYER = 1, SEND_MAIL = 1 } },
|
||||
{ "SEND_MAIL_COD_CHANGED", { PLAYER = 1, SEND_MAIL_COD = 1 } },
|
||||
}
|
||||
|
||||
-- The same rows keyed by event name, built once, so the handler below is a lookup.
|
||||
local moneyEventTypes = { }
|
||||
for _, row in ipairs(MONEY_EVENTS) do
|
||||
moneyEventTypes[row[1]] = row[2]
|
||||
end
|
||||
|
||||
local function MoneyFrameListen(frame)
|
||||
for _, row in ipairs(MONEY_EVENTS) do
|
||||
frame:RegisterEvent(row[1])
|
||||
end
|
||||
end
|
||||
|
||||
-- The two OnLoads differ in exactly one thing: `small`, which is what picks the 13px coin and
|
||||
-- its tighter spacing over the 19px one in MoneyFrame_Update below. Both then take type
|
||||
-- PLAYER, so a frame nobody tells otherwise shows the purse.
|
||||
function MoneyFrame_OnLoad()
|
||||
MoneyFrameListen(this)
|
||||
MoneyFrame_SetType("PLAYER")
|
||||
end
|
||||
|
||||
function SmallMoneyFrame_OnLoad()
|
||||
MoneyFrameListen(this)
|
||||
this.small = 1
|
||||
MoneyFrame_SetType("PLAYER")
|
||||
end
|
||||
|
||||
function MoneyFrame_OnEvent()
|
||||
if not this.info or not this:IsVisible() then
|
||||
return
|
||||
end
|
||||
local affected = moneyEventTypes[event]
|
||||
if affected and affected[this.moneyType] then
|
||||
MoneyFrame_UpdateMoney()
|
||||
end
|
||||
end
|
||||
|
||||
-- ── Painting ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
-- A money frame's three coin buttons, biggest first. Every verb below addresses them as a
|
||||
-- trio, so this is the one place their `$parent<Denomination>Button` names are spelled.
|
||||
local function MoneyFrameCoins(frameName)
|
||||
return getglobal(frameName .. "GoldButton"),
|
||||
getglobal(frameName .. "SilverButton"),
|
||||
getglobal(frameName .. "CopperButton")
|
||||
end
|
||||
|
||||
-- Point a frame at a money type (ref l.142-162): remember it, wire the mouse to match, and
|
||||
-- repaint. Reads `this` rather than taking a frame — that is the convention the eleven corpus
|
||||
-- callers write against.
|
||||
function MoneyFrame_SetType(type)
|
||||
local info = MoneyTypeInfo[type]
|
||||
if not info then
|
||||
-- ref l.145 diagnoses this with `message()`, which we DO have: it comes off the
|
||||
-- chain in `Interface\FrameXML\BasicControls.xml` (1881 migrated that file; before
|
||||
-- it, our own copy carried the same function). The `if` is therefore belt-and-braces
|
||||
-- for a harness that loads this file without BasicControls, not a missing verb.
|
||||
if message then
|
||||
message("Invalid money type: " .. type)
|
||||
end
|
||||
return
|
||||
end
|
||||
this.info = info
|
||||
this.moneyType = type
|
||||
-- Only a click-to-pick-up type wants the mouse; on the rest a click falls through to
|
||||
-- whatever is behind the coins.
|
||||
local gold, silver, copper = MoneyFrameCoins(this:GetName())
|
||||
local pickable = info.canPickup and true or false
|
||||
gold:EnableMouse(pickable)
|
||||
silver:EnableMouse(pickable)
|
||||
copper:EnableMouse(pickable)
|
||||
MoneyFrame_UpdateMoney()
|
||||
end
|
||||
|
||||
-- Re-read the amount from the type and repaint (ref l.164-175). `this` again.
|
||||
function MoneyFrame_UpdateMoney()
|
||||
if not this.info then
|
||||
if message then
|
||||
message("Error moneyType not set")
|
||||
end
|
||||
return
|
||||
end
|
||||
local money = this.info.UpdateFunc()
|
||||
MoneyFrame_Update(this:GetName(), money)
|
||||
-- ref l.169-171 also feeds the coin-pickup frame while one is open. `hasPickup` is set
|
||||
-- only by the coin click, which is guarded off (see the header), so this is unreachable
|
||||
-- today rather than removed.
|
||||
if this.hasPickup == 1 and UpdateCoinPickupFrame then
|
||||
UpdateCoinPickupFrame(money)
|
||||
end
|
||||
end
|
||||
|
||||
-- Lay one denomination out: its digits, and the width those digits plus the coin icon need.
|
||||
--
|
||||
-- `Button:GetTextWidth()` right after `SetText`, which is what the specification writes
|
||||
-- (ref l.202 and kin) and what the real client answers synchronously off its inline font
|
||||
-- engine, so the coin is the right width on the frame that set its text.
|
||||
--
|
||||
-- This read used to return 0 — our measure landed at extract, a frame after the tick — and
|
||||
-- left all three coins at exactly one bare icon width with their digits overlapping: the
|
||||
-- director's "gold numbers are all cramped up when I first open the bags; close and open
|
||||
-- again and it looks good", the reopen being the first open's cached measure. It was served
|
||||
-- for a while by a digits-only advance table, since a number's width is a pure sum of
|
||||
-- per-glyph advances. The engine answers the general question now (`script::measure`), so
|
||||
-- this asks it the way the specification does.
|
||||
local function ShowCoin(coin, coinName, amount, iconWidth)
|
||||
coin:SetText(amount)
|
||||
coin:SetWidth(coin:GetTextWidth() + iconWidth)
|
||||
coin:Show()
|
||||
end
|
||||
|
||||
-- Chain the next-bigger coin onto `coin`, and report what `coin` contributes to the group's
|
||||
-- width.
|
||||
--
|
||||
-- Showing: the bigger coin hangs off this one's LEFT edge by `spacing` (negative — they
|
||||
-- overlap), and the group grows by this coin's width less that overlap, but only when the
|
||||
-- bigger coin is actually on screen to overlap with. Not showing: this coin hides and the
|
||||
-- bigger one collapses onto its RIGHT edge, contributing nothing. `padToSlot` is the
|
||||
-- `fixedWidth` exception, which sizes the coin to the fixed column slot first.
|
||||
local function ChainCoin(bigger, coin, coinName, showing, padToSlot, spacing)
|
||||
if not showing then
|
||||
coin:Hide()
|
||||
bigger:SetPoint("RIGHT", coinName, "RIGHT", 0, 0)
|
||||
return 0
|
||||
end
|
||||
if padToSlot then
|
||||
coin:SetWidth(COIN_BUTTON_WIDTH)
|
||||
end
|
||||
local grown = coin:GetWidth()
|
||||
bigger:SetPoint("RIGHT", coinName, "LEFT", spacing, 0)
|
||||
if bigger:IsVisible() then
|
||||
grown = grown - spacing
|
||||
end
|
||||
return grown
|
||||
end
|
||||
|
||||
-- THE PAINTER (ref l.177-270). Split a copper amount into the three denominations, size each
|
||||
-- coin to its own digits plus its icon, and then — if the type collapses — drop the leading
|
||||
-- zeros, re-chain what is left so the group packs tight, and report the frame's true width.
|
||||
--
|
||||
-- This one takes a frame NAME, not `this`: it is the only entry point in the family an owner
|
||||
-- can call about a frame other than the one currently dispatching, which is how an addon
|
||||
-- paints a STATIC frame it has just filled.
|
||||
function MoneyFrame_Update(frameName, money)
|
||||
local frame = getglobal(frameName)
|
||||
local info = frame.info
|
||||
if not info then
|
||||
-- ref l.181 diagnoses and, notably, does NOT return: the coins below are still laid
|
||||
-- out, and the collapse read further down is what actually stops it. Kept as-is.
|
||||
if message then
|
||||
message("Error moneyType not set")
|
||||
end
|
||||
end
|
||||
|
||||
-- Copper is the low two digits, silver the next two, gold everything above. Silver comes
|
||||
-- out by subtracting the gold back off rather than by a second `mod`: `mod` here is 1.12's
|
||||
-- `math.mod`, which truncates towards zero, so `mod(-1, 100)` is -1 and not 99 — the two
|
||||
-- forms part company the moment `money` goes negative (which `GetMoney()` minus a trade
|
||||
-- offer can), and this one is the specified answer.
|
||||
-- A STATIC/AUCTION frame's amount is whatever its owner last parked on it, and on the
|
||||
-- very first paint — `MoneyFrame_SetType`'s own repaint, in OnLoad, before any owner has
|
||||
-- filled it — that is nil. The specification divides straight into it and throws there;
|
||||
-- we answer 0, because the throw would take the whole `<OnLoad>`/`<OnShow>` chain down
|
||||
-- with it and an empty price column is the honest reading of "nothing set yet".
|
||||
money = money or 0
|
||||
local gold = floor(money / COPPER_PER_GOLD)
|
||||
local silver = floor((money - gold * COPPER_PER_GOLD) / COPPER_PER_SILVER)
|
||||
local copper = mod(money, COPPER_PER_SILVER)
|
||||
|
||||
local goldCoin, silverCoin, copperCoin = MoneyFrameCoins(frameName)
|
||||
local iconWidth, spacing = MONEY_ICON_WIDTH, MONEY_BUTTON_SPACING
|
||||
if frame.small then
|
||||
iconWidth, spacing = MONEY_ICON_WIDTH_SMALL, MONEY_BUTTON_SPACING_SMALL
|
||||
end
|
||||
|
||||
ShowCoin(goldCoin, frameName .. "GoldButton", gold, iconWidth)
|
||||
ShowCoin(silverCoin, frameName .. "SilverButton", silver, iconWidth)
|
||||
ShowCoin(copperCoin, frameName .. "CopperButton", copper, iconWidth)
|
||||
|
||||
-- Remember what the frame is displaying — STATIC's own reader reads it back.
|
||||
frame.staticMoney = money
|
||||
|
||||
-- A type that does not collapse is finished: all three coins stand, at the width their
|
||||
-- digits asked for, and the frame keeps whatever width it was given.
|
||||
if not info.collapse then
|
||||
return
|
||||
end
|
||||
|
||||
-- Gold leads. Present, it stays and sets the terms for the two below it; absent, it goes.
|
||||
-- The running width starts at one icon's worth of frame padding.
|
||||
local width = iconWidth
|
||||
local keepSmaller, dropCopper
|
||||
if gold > 0 then
|
||||
width = width + goldCoin:GetWidth()
|
||||
keepSmaller = info.showSmallerCoins and 1 or nil
|
||||
dropCopper = info.truncateSmallCoins and 1 or nil
|
||||
else
|
||||
goldCoin:Hide()
|
||||
end
|
||||
|
||||
-- Silver, then copper: each either shows — padded, chained and counted — or hides and lets
|
||||
-- the coin above it collapse onto its edge. Silver showing under a `showSmallerCoins` type
|
||||
-- is what carries the exemption down to copper; copper also survives at zero for a
|
||||
-- "Backpack" type, and dies whatever it is once gold truncated it.
|
||||
local showSilver = silver > 0 or keepSmaller
|
||||
width = width + ChainCoin(goldCoin, silverCoin, frameName .. "SilverButton",
|
||||
showSilver, keepSmaller and info.fixedWidth, spacing)
|
||||
if showSilver and info.showSmallerCoins then
|
||||
keepSmaller = 1
|
||||
end
|
||||
|
||||
local showCopper = (copper > 0 or keepSmaller or info.showSmallerCoins == "Backpack")
|
||||
and not dropCopper
|
||||
width = width + ChainCoin(silverCoin, copperCoin, frameName .. "CopperButton",
|
||||
showCopper, keepSmaller and info.fixedWidth, spacing)
|
||||
|
||||
frame:SetWidth(width)
|
||||
end
|
||||
|
||||
-- A NO-OP, deliberately. The specification's whole body for this one sits inside a `--[[ ]]`
|
||||
-- block (ref l.272-346) — an earlier painter that MoneyFrame_Update replaced. The name is in
|
||||
-- the shipped client's `_G`, and an addon that calls it there gets exactly this: nothing.
|
||||
function RefreshMoneyFrame(frameName, money, small, collapse, showSmallerCoins)
|
||||
end
|
||||
|
||||
-- Recolour the DIGITS; the coin icons keep their own texture (ref l.348-356). This is how a
|
||||
-- price you cannot afford turns red.
|
||||
function SetMoneyFrameColor(frameName, r, g, b)
|
||||
local gold, silver, copper = MoneyFrameCoins(frameName)
|
||||
gold:SetTextColor(r, g, b)
|
||||
silver:SetTextColor(r, g, b)
|
||||
copper:SetTextColor(r, g, b)
|
||||
end
|
||||
|
||||
-- A click on a coin opens the pickup frame on that denomination, seeded with what the frame is
|
||||
-- currently showing; `denomination` is one coin's worth in copper, which is the unit the
|
||||
-- pickup frame counts in.
|
||||
--
|
||||
-- Guarded, because we ship no coin pickup (see the header) — so today the click does nothing,
|
||||
-- and the mouse-enabled state a `canPickup` type gives the coins is all that is observable.
|
||||
-- Note the amount is read by calling the type's own `UpdateFunc` while `this` is still the
|
||||
-- BUTTON rather than the frame, which is what the specification's own coin `<OnClick>` does
|
||||
-- (ref MoneyFrame.xml l.20-23) and what a frame-reading type would see through.
|
||||
function BenillaMoneyFrameCoin_OnClick(coin, denomination)
|
||||
if not OpenCoinPickupFrame then
|
||||
return
|
||||
end
|
||||
local frame = coin:GetParent()
|
||||
OpenCoinPickupFrame(denomination, MoneyTypeInfo[frame.moneyType].UpdateFunc(), frame)
|
||||
frame.hasPickup = 1
|
||||
end
|
||||
|
||||
-- Closing a money frame takes any pickup it opened down with it.
|
||||
function BenillaMoneyFrame_OnHide()
|
||||
if this.hasPickup == 1 and CoinPickupFrame then
|
||||
CoinPickupFrame:Hide()
|
||||
this.hasPickup = 0
|
||||
end
|
||||
end
|
||||
]]></Script>
|
||||
|
||||
<!--
|
||||
MoneyFrameTemplate (ref MoneyFrame.xml l.3-149) — the LARGE kit: 192x28, 19px coins, the
|
||||
NumberFontNormalLarge digits. The three buttons chain RIGHT-to-LEFT from the copper end, which
|
||||
is what lets MoneyFrame_Update above re-point them past a hidden denomination.
|
||||
|
||||
`<PushedTextOffset>` is carried for provenance; our loader does not model it and ignores it.
|
||||
-->
|
||||
<Frame name="MoneyFrameTemplate" virtual="true">
|
||||
<Size><AbsDimension x="192" y="28"/></Size>
|
||||
<Frames>
|
||||
<Button name="$parentCopperButton">
|
||||
<Size><AbsDimension x="47" y="19"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="RIGHT"><Offset><AbsDimension x="-13" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnClick>BenillaMoneyFrameCoin_OnClick(this, 1)</OnClick>
|
||||
</Scripts>
|
||||
<NormalTexture file="Interface\MoneyFrame\UI-MoneyIcons">
|
||||
<Size><AbsDimension x="19" y="19"/></Size>
|
||||
<Anchors><Anchor point="RIGHT"/></Anchors>
|
||||
<TexCoords left="0.5" right="0.75" top="0" bottom="1"/>
|
||||
</NormalTexture>
|
||||
<ButtonText name="$parentText">
|
||||
<Anchors>
|
||||
<Anchor point="RIGHT"><Offset><AbsDimension x="-19" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</ButtonText>
|
||||
<NormalFont inherits="NumberFontNormalLarge" justifyH="RIGHT"/>
|
||||
<PushedTextOffset><AbsDimension x="0" y="0"/></PushedTextOffset>
|
||||
</Button>
|
||||
<Button name="$parentSilverButton">
|
||||
<Size><AbsDimension x="47" y="19"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="RIGHT" relativeTo="$parentCopperButton" relativePoint="LEFT"><Offset><AbsDimension x="-4" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnClick>BenillaMoneyFrameCoin_OnClick(this, COPPER_PER_SILVER)</OnClick>
|
||||
</Scripts>
|
||||
<NormalTexture file="Interface\MoneyFrame\UI-MoneyIcons">
|
||||
<Size><AbsDimension x="19" y="19"/></Size>
|
||||
<Anchors><Anchor point="RIGHT"/></Anchors>
|
||||
<TexCoords left="0.25" right="0.5" top="0" bottom="1"/>
|
||||
</NormalTexture>
|
||||
<ButtonText name="$parentText">
|
||||
<Anchors>
|
||||
<Anchor point="RIGHT"><Offset><AbsDimension x="-19" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</ButtonText>
|
||||
<NormalFont inherits="NumberFontNormalLarge" justifyH="RIGHT"/>
|
||||
<PushedTextOffset><AbsDimension x="0" y="0"/></PushedTextOffset>
|
||||
</Button>
|
||||
<Button name="$parentGoldButton">
|
||||
<Size><AbsDimension x="47" y="19"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="RIGHT" relativeTo="$parentSilverButton" relativePoint="LEFT"><Offset><AbsDimension x="-4" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnClick>BenillaMoneyFrameCoin_OnClick(this, COPPER_PER_GOLD)</OnClick>
|
||||
</Scripts>
|
||||
<NormalTexture file="Interface\MoneyFrame\UI-MoneyIcons">
|
||||
<Size><AbsDimension x="19" y="19"/></Size>
|
||||
<Anchors><Anchor point="RIGHT"/></Anchors>
|
||||
<TexCoords left="0" right="0.25" top="0" bottom="1"/>
|
||||
</NormalTexture>
|
||||
<ButtonText name="$parentText">
|
||||
<Anchors>
|
||||
<Anchor point="RIGHT"><Offset><AbsDimension x="-19" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</ButtonText>
|
||||
<NormalFont inherits="NumberFontNormalLarge" justifyH="RIGHT"/>
|
||||
<PushedTextOffset><AbsDimension x="0" y="0"/></PushedTextOffset>
|
||||
</Button>
|
||||
</Frames>
|
||||
<Scripts>
|
||||
<OnLoad>MoneyFrame_OnLoad()</OnLoad>
|
||||
<OnEvent>MoneyFrame_OnEvent()</OnEvent>
|
||||
<OnShow>MoneyFrame_UpdateMoney()</OnShow>
|
||||
<OnHide>BenillaMoneyFrame_OnHide()</OnHide>
|
||||
</Scripts>
|
||||
</Frame>
|
||||
|
||||
<!--
|
||||
SmallMoneyFrameTemplate (ref MoneyFrame.xml l.150-296) — the same kit at 128x13 with 13px
|
||||
coins and NumberFontNormal digits, and `SmallMoneyFrame_OnLoad` in place of `MoneyFrame_OnLoad`
|
||||
so `this.small` is set. This is the one the shipped UI uses nearly everywhere (merchant prices,
|
||||
the purse, mail postage, trade offers) and the one 13 corpus addons inherit.
|
||||
-->
|
||||
<Frame name="SmallMoneyFrameTemplate" virtual="true">
|
||||
<Size><AbsDimension x="128" y="13"/></Size>
|
||||
<Frames>
|
||||
<Button name="$parentCopperButton">
|
||||
<Size><AbsDimension x="32" y="13"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="RIGHT"><Offset><AbsDimension x="-13" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnClick>BenillaMoneyFrameCoin_OnClick(this, 1)</OnClick>
|
||||
</Scripts>
|
||||
<NormalTexture file="Interface\MoneyFrame\UI-MoneyIcons">
|
||||
<Size><AbsDimension x="13" y="13"/></Size>
|
||||
<Anchors><Anchor point="RIGHT"/></Anchors>
|
||||
<TexCoords left="0.5" right="0.75" top="0" bottom="1"/>
|
||||
</NormalTexture>
|
||||
<ButtonText name="$parentText">
|
||||
<Anchors>
|
||||
<Anchor point="RIGHT"><Offset><AbsDimension x="-13" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</ButtonText>
|
||||
<NormalFont inherits="NumberFontNormal" justifyH="RIGHT"/>
|
||||
<PushedTextOffset><AbsDimension x="0" y="0"/></PushedTextOffset>
|
||||
</Button>
|
||||
<Button name="$parentSilverButton">
|
||||
<Size><AbsDimension x="32" y="13"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="RIGHT" relativeTo="$parentCopperButton" relativePoint="LEFT"><Offset><AbsDimension x="-4" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnClick>BenillaMoneyFrameCoin_OnClick(this, COPPER_PER_SILVER)</OnClick>
|
||||
</Scripts>
|
||||
<NormalTexture file="Interface\MoneyFrame\UI-MoneyIcons">
|
||||
<Size><AbsDimension x="13" y="13"/></Size>
|
||||
<Anchors><Anchor point="RIGHT"/></Anchors>
|
||||
<TexCoords left="0.25" right="0.5" top="0" bottom="1"/>
|
||||
</NormalTexture>
|
||||
<ButtonText name="$parentText">
|
||||
<Anchors>
|
||||
<Anchor point="RIGHT"><Offset><AbsDimension x="-13" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</ButtonText>
|
||||
<NormalFont inherits="NumberFontNormal" justifyH="RIGHT"/>
|
||||
<PushedTextOffset><AbsDimension x="0" y="0"/></PushedTextOffset>
|
||||
</Button>
|
||||
<Button name="$parentGoldButton">
|
||||
<Size><AbsDimension x="32" y="13"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="RIGHT" relativeTo="$parentSilverButton" relativePoint="LEFT"><Offset><AbsDimension x="-4" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnClick>BenillaMoneyFrameCoin_OnClick(this, COPPER_PER_GOLD)</OnClick>
|
||||
</Scripts>
|
||||
<NormalTexture file="Interface\MoneyFrame\UI-MoneyIcons">
|
||||
<Size><AbsDimension x="13" y="13"/></Size>
|
||||
<Anchors><Anchor point="RIGHT"/></Anchors>
|
||||
<TexCoords left="0" right="0.25" top="0" bottom="1"/>
|
||||
</NormalTexture>
|
||||
<ButtonText name="$parentText">
|
||||
<Anchors>
|
||||
<Anchor point="RIGHT"><Offset><AbsDimension x="-13" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</ButtonText>
|
||||
<NormalFont inherits="NumberFontNormal" justifyH="RIGHT"/>
|
||||
<PushedTextOffset><AbsDimension x="0" y="0"/></PushedTextOffset>
|
||||
</Button>
|
||||
</Frames>
|
||||
<Scripts>
|
||||
<OnLoad>SmallMoneyFrame_OnLoad()</OnLoad>
|
||||
<OnEvent>MoneyFrame_OnEvent()</OnEvent>
|
||||
<OnShow>MoneyFrame_UpdateMoney()</OnShow>
|
||||
<OnHide>BenillaMoneyFrame_OnHide()</OnHide>
|
||||
</Scripts>
|
||||
</Frame>
|
||||
</Ui>
|
||||
@@ -220,6 +220,50 @@
|
||||
RegisterForSave("QUEST_FADING_DISABLE")
|
||||
AUTO_QUEST_WATCH = "1"
|
||||
RegisterForSave("AUTO_QUEST_WATCH")
|
||||
-- Combat text uvars (ref UIOptionsFrame.lua l.135-152, verbatim). They lived in our
|
||||
-- CombatText.xml while that file stood in for the addon; the addon is the reference's
|
||||
-- own `Blizzard_CombatText` off the chain now, LoadOnDemand as the reference ships it,
|
||||
-- so its option family's defaults sit where the reference keeps them (1964).
|
||||
SHOW_COMBAT_TEXT = "0"
|
||||
RegisterForSave("SHOW_COMBAT_TEXT")
|
||||
COMBAT_TEXT_SHOW_LOW_HEALTH_MANA = "1"
|
||||
RegisterForSave("COMBAT_TEXT_SHOW_LOW_HEALTH_MANA")
|
||||
COMBAT_TEXT_SHOW_AURAS = "1"
|
||||
RegisterForSave("COMBAT_TEXT_SHOW_AURAS")
|
||||
COMBAT_TEXT_SHOW_AURA_FADE = "0"
|
||||
RegisterForSave("COMBAT_TEXT_SHOW_AURA_FADE")
|
||||
COMBAT_TEXT_SHOW_COMBAT_STATE = "1"
|
||||
RegisterForSave("COMBAT_TEXT_SHOW_COMBAT_STATE")
|
||||
COMBAT_TEXT_SHOW_DODGE_PARRY_MISS = "0"
|
||||
RegisterForSave("COMBAT_TEXT_SHOW_DODGE_PARRY_MISS")
|
||||
COMBAT_TEXT_SHOW_RESISTANCES = "0"
|
||||
RegisterForSave("COMBAT_TEXT_SHOW_RESISTANCES")
|
||||
COMBAT_TEXT_SHOW_REPUTATION = "0"
|
||||
RegisterForSave("COMBAT_TEXT_SHOW_REPUTATION")
|
||||
COMBAT_TEXT_SHOW_REACTIVES = "0"
|
||||
RegisterForSave("COMBAT_TEXT_SHOW_REACTIVES")
|
||||
COMBAT_TEXT_SHOW_FRIENDLY_NAMES = "0"
|
||||
RegisterForSave("COMBAT_TEXT_SHOW_FRIENDLY_NAMES")
|
||||
COMBAT_TEXT_SHOW_COMBO_POINTS = "0"
|
||||
RegisterForSave("COMBAT_TEXT_SHOW_COMBO_POINTS")
|
||||
COMBAT_TEXT_SHOW_MANA = "0"
|
||||
RegisterForSave("COMBAT_TEXT_SHOW_MANA")
|
||||
COMBAT_TEXT_FLOAT_MODE = "1"
|
||||
RegisterForSave("COMBAT_TEXT_FLOAT_MODE")
|
||||
COMBAT_TEXT_SHOW_HONOR_GAINED = "1"
|
||||
RegisterForSave("COMBAT_TEXT_SHOW_HONOR_GAINED")
|
||||
|
||||
-- The family's apply, ref UIOptionsFrame_Save l.371-376 verbatim: the addon is
|
||||
-- LoadOnDemand, so turning the master on loads it, and the update runs only once it is
|
||||
-- there. Every combat-text row's `applyFunc` is this (1964).
|
||||
function CombatText_UpdateOrLoad()
|
||||
if ( SHOW_COMBAT_TEXT == "1" ) then
|
||||
UIParentLoadAddOn("Blizzard_CombatText")
|
||||
end
|
||||
if ( CombatText_UpdateDisplayedMessages ) then
|
||||
CombatText_UpdateDisplayedMessages()
|
||||
end
|
||||
end
|
||||
-- CHAT_LOCKED / REMOVE_CHAT_DELAY — the chat window's two uvars, the reference's own
|
||||
-- UIOptionsFrame.lua l.96-99 defaults ("0" and "0"), read every frame by
|
||||
-- FloatingChatFrame.lua's FCF_Get_ChatLocked and FCF_OnUpdate since the chat window went
|
||||
@@ -1073,6 +1117,11 @@
|
||||
end
|
||||
|
||||
function OptionsFrame_ApplySavedSettings()
|
||||
-- ref UIOptionsFrame_OnEvent's VARIABLES_LOADED arm, l.224-226: the combat text
|
||||
-- addon loads at login when the saved master is on (1964).
|
||||
if ( SHOW_COMBAT_TEXT == "1" ) then
|
||||
UIParentLoadAddOn("Blizzard_CombatText")
|
||||
end
|
||||
local ran = {}
|
||||
for key, rows in pairs(OPTIONS_PAGE_ROWS) do
|
||||
for _, rkey in ipairs(rows) do
|
||||
@@ -2792,7 +2841,7 @@
|
||||
<Scripts><OnLoad>
|
||||
OptionsGlobalRow_OnLoad(self, "SHOW_COMBAT_TEXT",
|
||||
"Enable Floating Combat Text", "OPTION_TOOLTIP_SHOW_COMBAT_TEXT")
|
||||
self.applyFunc = "CombatText_UpdateDisplayedMessages" -- by NAME, resolved when applied: this file loads before its definer now (1938)
|
||||
self.applyFunc = "CombatText_UpdateOrLoad" -- by NAME, resolved when applied: this file loads before its definer now (1938)
|
||||
</OnLoad></Scripts>
|
||||
</Frame>
|
||||
<!-- The scroll mode. 1.12's own dropdown, values and all
|
||||
@@ -2812,7 +2861,7 @@
|
||||
{ text = "Arc", value = "3" },
|
||||
})
|
||||
self.parentRow = "RowCombatText"
|
||||
self.applyFunc = "CombatText_UpdateDisplayedMessages" -- by NAME, resolved when applied: this file loads before its definer now (1938)
|
||||
self.applyFunc = "CombatText_UpdateOrLoad" -- by NAME, resolved when applied: this file loads before its definer now (1938)
|
||||
</OnLoad></Scripts>
|
||||
</Frame>
|
||||
<Frame name="$parentRowLowHealthMana" inherits="OptionsCheckboxRowTemplate">
|
||||
@@ -2825,7 +2874,7 @@
|
||||
"Show Low Mana and Health",
|
||||
"OPTION_TOOLTIP_COMBAT_TEXT_SHOW_LOW_HEALTH_MANA", 1)
|
||||
self.parentRow = "RowCombatText"
|
||||
self.applyFunc = "CombatText_UpdateDisplayedMessages" -- by NAME, resolved when applied: this file loads before its definer now (1938)
|
||||
self.applyFunc = "CombatText_UpdateOrLoad" -- by NAME, resolved when applied: this file loads before its definer now (1938)
|
||||
</OnLoad></Scripts>
|
||||
</Frame>
|
||||
<Frame name="$parentRowAuras" inherits="OptionsCheckboxRowTemplate">
|
||||
@@ -2837,7 +2886,7 @@
|
||||
OptionsGlobalRow_OnLoad(self, "COMBAT_TEXT_SHOW_AURAS",
|
||||
"Show Auras", "OPTION_TOOLTIP_COMBAT_TEXT_SHOW_AURAS", 1)
|
||||
self.parentRow = "RowCombatText"
|
||||
self.applyFunc = "CombatText_UpdateDisplayedMessages" -- by NAME, resolved when applied: this file loads before its definer now (1938)
|
||||
self.applyFunc = "CombatText_UpdateOrLoad" -- by NAME, resolved when applied: this file loads before its definer now (1938)
|
||||
</OnLoad></Scripts>
|
||||
</Frame>
|
||||
<Frame name="$parentRowAuraFade" inherits="OptionsCheckboxRowTemplate">
|
||||
@@ -2849,7 +2898,7 @@
|
||||
OptionsGlobalRow_OnLoad(self, "COMBAT_TEXT_SHOW_AURA_FADE",
|
||||
"Show Fading Auras", "OPTION_TOOLTIP_COMBAT_TEXT_SHOW_AURA_FADE", 1)
|
||||
self.parentRow = "RowCombatText"
|
||||
self.applyFunc = "CombatText_UpdateDisplayedMessages" -- by NAME, resolved when applied: this file loads before its definer now (1938)
|
||||
self.applyFunc = "CombatText_UpdateOrLoad" -- by NAME, resolved when applied: this file loads before its definer now (1938)
|
||||
</OnLoad></Scripts>
|
||||
</Frame>
|
||||
<Frame name="$parentRowCombatState" inherits="OptionsCheckboxRowTemplate">
|
||||
@@ -2862,7 +2911,7 @@
|
||||
"Show Combat State",
|
||||
"OPTION_TOOLTIP_COMBAT_TEXT_SHOW_COMBAT_STATE", 1)
|
||||
self.parentRow = "RowCombatText"
|
||||
self.applyFunc = "CombatText_UpdateDisplayedMessages" -- by NAME, resolved when applied: this file loads before its definer now (1938)
|
||||
self.applyFunc = "CombatText_UpdateOrLoad" -- by NAME, resolved when applied: this file loads before its definer now (1938)
|
||||
</OnLoad></Scripts>
|
||||
</Frame>
|
||||
<Frame name="$parentRowDodgeParryMiss" inherits="OptionsCheckboxRowTemplate">
|
||||
@@ -2875,7 +2924,7 @@
|
||||
"Show Dodge/Parries/Misses",
|
||||
"OPTION_TOOLTIP_COMBAT_TEXT_SHOW_DODGE_PARRY_MISS", 1)
|
||||
self.parentRow = "RowCombatText"
|
||||
self.applyFunc = "CombatText_UpdateDisplayedMessages" -- by NAME, resolved when applied: this file loads before its definer now (1938)
|
||||
self.applyFunc = "CombatText_UpdateOrLoad" -- by NAME, resolved when applied: this file loads before its definer now (1938)
|
||||
</OnLoad></Scripts>
|
||||
</Frame>
|
||||
<Frame name="$parentRowResistances" inherits="OptionsCheckboxRowTemplate">
|
||||
@@ -2888,7 +2937,7 @@
|
||||
"Show Damage Reduction",
|
||||
"OPTION_TOOLTIP_COMBAT_TEXT_SHOW_RESISTANCES", 1)
|
||||
self.parentRow = "RowCombatText"
|
||||
self.applyFunc = "CombatText_UpdateDisplayedMessages" -- by NAME, resolved when applied: this file loads before its definer now (1938)
|
||||
self.applyFunc = "CombatText_UpdateOrLoad" -- by NAME, resolved when applied: this file loads before its definer now (1938)
|
||||
</OnLoad></Scripts>
|
||||
</Frame>
|
||||
<Frame name="$parentRowReputation" inherits="OptionsCheckboxRowTemplate">
|
||||
@@ -2901,7 +2950,7 @@
|
||||
"Show Reputation",
|
||||
"OPTION_TOOLTIP_COMBAT_TEXT_SHOW_REPUTATION", 1)
|
||||
self.parentRow = "RowCombatText"
|
||||
self.applyFunc = "CombatText_UpdateDisplayedMessages" -- by NAME, resolved when applied: this file loads before its definer now (1938)
|
||||
self.applyFunc = "CombatText_UpdateOrLoad" -- by NAME, resolved when applied: this file loads before its definer now (1938)
|
||||
</OnLoad></Scripts>
|
||||
</Frame>
|
||||
<Frame name="$parentRowReactives" inherits="OptionsCheckboxRowTemplate">
|
||||
@@ -2914,7 +2963,7 @@
|
||||
"Show Reactive Spells and Abilities",
|
||||
"OPTION_TOOLTIP_COMBAT_TEXT_SHOW_REACTIVES", 1)
|
||||
self.parentRow = "RowCombatText"
|
||||
self.applyFunc = "CombatText_UpdateDisplayedMessages" -- by NAME, resolved when applied: this file loads before its definer now (1938)
|
||||
self.applyFunc = "CombatText_UpdateOrLoad" -- by NAME, resolved when applied: this file loads before its definer now (1938)
|
||||
</OnLoad></Scripts>
|
||||
</Frame>
|
||||
<Frame name="$parentRowFriendlyNames" inherits="OptionsCheckboxRowTemplate">
|
||||
@@ -2927,7 +2976,7 @@
|
||||
"Show Friendly Healer Names",
|
||||
"OPTION_TOOLTIP_COMBAT_TEXT_SHOW_FRIENDLY_NAMES", 1)
|
||||
self.parentRow = "RowCombatText"
|
||||
self.applyFunc = "CombatText_UpdateDisplayedMessages" -- by NAME, resolved when applied: this file loads before its definer now (1938)
|
||||
self.applyFunc = "CombatText_UpdateOrLoad" -- by NAME, resolved when applied: this file loads before its definer now (1938)
|
||||
</OnLoad></Scripts>
|
||||
</Frame>
|
||||
<!-- Combo Points carries the reference's SECOND gate: greyed
|
||||
@@ -2943,7 +2992,7 @@
|
||||
"Show Combo Points",
|
||||
"OPTION_TOOLTIP_COMBAT_TEXT_SHOW_COMBO_POINTS", 1)
|
||||
self.parentRow = "RowCombatText"
|
||||
self.applyFunc = "CombatText_UpdateDisplayedMessages" -- by NAME, resolved when applied: this file loads before its definer now (1938)
|
||||
self.applyFunc = "CombatText_UpdateOrLoad" -- by NAME, resolved when applied: this file loads before its definer now (1938)
|
||||
</OnLoad></Scripts>
|
||||
</Frame>
|
||||
<Frame name="$parentRowEnergyGains" inherits="OptionsCheckboxRowTemplate">
|
||||
@@ -2956,7 +3005,7 @@
|
||||
"Show Energy Gains",
|
||||
"OPTION_TOOLTIP_COMBAT_TEXT_SHOW_MANA", 1)
|
||||
self.parentRow = "RowCombatText"
|
||||
self.applyFunc = "CombatText_UpdateDisplayedMessages" -- by NAME, resolved when applied: this file loads before its definer now (1938)
|
||||
self.applyFunc = "CombatText_UpdateOrLoad" -- by NAME, resolved when applied: this file loads before its definer now (1938)
|
||||
</OnLoad></Scripts>
|
||||
</Frame>
|
||||
<Frame name="$parentRowHonorGained" inherits="OptionsCheckboxRowTemplate">
|
||||
@@ -2969,7 +3018,7 @@
|
||||
"Show Honor Gained",
|
||||
"OPTION_TOOLTIP_COMBAT_TEXT_SHOW_HONOR_GAINED", 1)
|
||||
self.parentRow = "RowCombatText"
|
||||
self.applyFunc = "CombatText_UpdateDisplayedMessages" -- by NAME, resolved when applied: this file loads before its definer now (1938)
|
||||
self.applyFunc = "CombatText_UpdateOrLoad" -- by NAME, resolved when applied: this file loads before its definer now (1938)
|
||||
</OnLoad></Scripts>
|
||||
</Frame>
|
||||
</Frames>
|
||||
|
||||
@@ -1,512 +0,0 @@
|
||||
<Ui>
|
||||
<!--
|
||||
benilla's player-to-player trade window (decision 0592 P1). The geometry/art/texcoords are
|
||||
transcribed from the reference `TradeFrame.xml` (WoW/_extracted_framexml/TradeFrame.xml) —
|
||||
sizes, offsets, anchors, art files, texcoords — adapted only in (a) naming (every `Foo` →
|
||||
`BenillaFoo`) and (b) feed/intent wiring onto the trade script API
|
||||
(`benilla-ui/src/script/trade.rs`) + `crate::ui_trade`'s feed/drain. The house dressing
|
||||
convention (decisions 0084/0112/0120/0544).
|
||||
|
||||
Where the reference inherits a shared template benilla does NOT ship (ItemButtonTemplate,
|
||||
UIPanelButtonTemplate, UIPanelCloseButton, MoneyInputFrameTemplate, SmallMoneyFrameTemplate),
|
||||
the body is inlined with the reference's own geometry/art (the MailFrame.xml precedent). The
|
||||
two money displays reuse `MerchantFrame.xml`'s global `BenillaMoney_*` coin trio (read-only in
|
||||
P1); the reference's editable `MoneyInputFrame` on the player's side is decision 0592 P2.
|
||||
|
||||
SCOPE (decision 0592 P1): the window opens on an incoming/initiated trade and mirrors both
|
||||
sides' items + gold live, the partner's portrait (the shared "npc" booth) + name, and the
|
||||
accept glow; the Trade/Cancel buttons drive the accept/cancel verbs. DEFERRED to P2: setting
|
||||
items/gold onto the window (the drag-drop slots + the editable money widget), so the item
|
||||
slots carry NO OnClick/OnEnter here, and the slot tooltips (GameTooltip:SetTradePlayerItem)
|
||||
are P2. The enchant-slot spell name (slot 7's "enchantment") is decision 0592 P3.
|
||||
-->
|
||||
<Script><![CDATA[
|
||||
MAX_TRADE_ITEMS = 7 -- MAX_TRADE_ITEMS (ref TradeFrame.lua l.1)
|
||||
|
||||
-- One side's accept glow. A column's highlight and the enchant slot's below it are one
|
||||
-- indicator in two frames — they light and drop together, never apart — so both the repaint
|
||||
-- and TradeFrame_SetAcceptState go through here rather than each spelling out four calls.
|
||||
-- The four frames are TradeHighlight{Player,Recipient}[Enchant], declared at the bottom.
|
||||
local function setTradeGlow(column, enchantSlot, lit)
|
||||
if lit then
|
||||
column:Show()
|
||||
enchantSlot:Show()
|
||||
else
|
||||
column:Hide()
|
||||
enchantSlot:Hide()
|
||||
end
|
||||
end
|
||||
|
||||
-- ── Load / event dispatch (ref TradeFrame_OnLoad l.4 / TradeFrame_OnEvent l.18) ───────────
|
||||
function TradeFrame_OnLoad()
|
||||
local frame = this
|
||||
frame:RegisterEvent("TRADE_SHOW")
|
||||
frame:RegisterEvent("TRADE_UPDATE")
|
||||
frame:RegisterEvent("TRADE_CLOSED")
|
||||
frame:RegisterEvent("TRADE_PLAYER_ITEM_CHANGED")
|
||||
frame:RegisterEvent("TRADE_TARGET_ITEM_CHANGED")
|
||||
frame:RegisterEvent("TRADE_ACCEPT_UPDATE")
|
||||
-- Our accepted gold, echoed back so the input reflects the server's clamp (ref
|
||||
-- TradePlayerInputMoneyFrame's own PLAYER_TRADE_MONEY registration, l.565). Kept off the
|
||||
-- general TRADE_UPDATE repaint so it never overwrites what the player is mid-typing.
|
||||
frame:RegisterEvent("PLAYER_TRADE_MONEY")
|
||||
end
|
||||
|
||||
function TradeFrame_OnEvent()
|
||||
local frame = this
|
||||
if event == "TRADE_SHOW" or event == "TRADE_UPDATE" then
|
||||
ShowUIPanel(frame)
|
||||
if not frame:IsVisible() then
|
||||
CloseTrade()
|
||||
return
|
||||
end
|
||||
TradeFrameTradeButton:Enable()
|
||||
TradeFrame_Update()
|
||||
elseif event == "PLAYER_TRADE_MONEY" then
|
||||
MoneyInputFrame_SetCopper(TradePlayerInputMoney, GetPlayerTradeMoney())
|
||||
elseif event == "TRADE_CLOSED" then
|
||||
HideUIPanel(frame)
|
||||
elseif event == "TRADE_PLAYER_ITEM_CHANGED" then
|
||||
TradeFrame_UpdatePlayerItem(arg1)
|
||||
elseif event == "TRADE_TARGET_ITEM_CHANGED" then
|
||||
TradeFrame_UpdateTargetItem(arg1)
|
||||
elseif event == "TRADE_ACCEPT_UPDATE" then
|
||||
TradeFrame_SetAcceptState(arg1, arg2)
|
||||
end
|
||||
end
|
||||
|
||||
function TradeFrame_OnShow()
|
||||
TradeFrame.acceptState = 0
|
||||
end
|
||||
|
||||
function TradeFrame_OnHide()
|
||||
-- The client-side close verb (ref TradeFrame_OnHide l.144) → CMSG_CANCEL_TRADE + a local
|
||||
-- clear (the app's drain); clear our money entry so a later trade opens fresh (ref l.146).
|
||||
MoneyInputFrame_ResetMoney(TradePlayerInputMoney)
|
||||
CloseTrade()
|
||||
end
|
||||
|
||||
-- A keystroke in our money input (ref TradeFrame_UpdateMoney l.162): read the copper total,
|
||||
-- clamp to what we actually carry, and offer it → SetTradeMoney → CMSG_SET_TRADE_GOLD.
|
||||
function TradeFrame_UpdateMoney()
|
||||
local copper = MoneyInputFrame_GetCopper(TradePlayerInputMoney)
|
||||
if GetMoney and copper > GetMoney() then
|
||||
copper = GetPlayerTradeMoney()
|
||||
MoneyInputFrame_SetCopper(TradePlayerInputMoney, copper)
|
||||
end
|
||||
SetTradeMoney(copper)
|
||||
end
|
||||
|
||||
-- ── Full repaint (ref TradeFrame_Update l.39): portraits, names, all 7 slots each side, the
|
||||
-- two money displays, the accept highlights hidden. ────────────────────────────────────
|
||||
function TradeFrame_Update()
|
||||
SetPortraitTexture(TradeFramePlayerPortrait, "player")
|
||||
-- The partner rides the shared "npc" portrait booth (the app points it at the partner
|
||||
-- entity, decision 0592); the name comes from GetTradePartnerName() in place of the
|
||||
-- reference's UnitName("NPC").
|
||||
SetPortraitTexture(TradeFrameRecipientPortrait, "npc")
|
||||
TradeFramePlayerNameText:SetText(UnitName("player") or "")
|
||||
TradeFrameRecipientNameText:SetText(GetTradePartnerName() or "")
|
||||
for i = 1, MAX_TRADE_ITEMS do
|
||||
TradeFrame_UpdatePlayerItem(i)
|
||||
TradeFrame_UpdateTargetItem(i)
|
||||
end
|
||||
-- Our gold is the editable input, reflected via PLAYER_TRADE_MONEY (not repainted here, so
|
||||
-- the repaint never clobbers mid-typing); only the partner's read-only gold repaints.
|
||||
-- The recipient's money frame is type TARGET_TRADE and repaints itself on
|
||||
-- TRADE_MONEY_CHANGED (MoneyFrame_OnEvent); the reference issues no explicit update
|
||||
-- here and neither do we (1937).
|
||||
-- A repaint puts both glows down. TRADE_ACCEPT_UPDATE is the only thing that raises
|
||||
-- them, so whatever is standing comes straight back on the next one.
|
||||
setTradeGlow(TradeHighlightPlayer, TradeHighlightPlayerEnchant, false)
|
||||
setTradeGlow(TradeHighlightRecipient, TradeHighlightRecipientEnchant, false)
|
||||
end
|
||||
|
||||
-- One slot's paint, shared by both columns (ref TradeFrame_UpdatePlayerItem l.54 /
|
||||
-- UpdateTargetItem l.83). `isTarget` gates the recipient-only not-usable red tint.
|
||||
function BenillaTradeSlot_SetItem(prefix, id, name, texture, numItems, isUsable, enchantment, isTarget)
|
||||
local nameText = getglobal(prefix.."Name")
|
||||
local icon = getglobal(prefix.."ItemButtonIcon")
|
||||
local count = getglobal(prefix.."ItemButtonCount")
|
||||
-- The enchant slot (id 7): the enchant name (P3) or the "will not be traded" note.
|
||||
if id == MAX_TRADE_ITEMS then
|
||||
if name then
|
||||
if enchantment then
|
||||
nameText:SetText("|cff20ff20"..enchantment.."|r")
|
||||
else
|
||||
nameText:SetText("|cffffffffWill Not Be Traded|r")
|
||||
end
|
||||
else
|
||||
nameText:SetText("")
|
||||
end
|
||||
else
|
||||
nameText:SetText(name or "")
|
||||
end
|
||||
if texture then
|
||||
icon:SetTexture(texture)
|
||||
icon:Show()
|
||||
else
|
||||
icon:Hide()
|
||||
end
|
||||
if numItems and numItems > 1 then
|
||||
count:SetText(numItems)
|
||||
count:Show()
|
||||
else
|
||||
count:SetText("")
|
||||
count:Hide()
|
||||
end
|
||||
-- The recipient not-usable red tint (ref l.105-113): red when the partner offers an item
|
||||
-- our class/level/skill can't use; white otherwise (and always white on our own column).
|
||||
local slotTex = getglobal(prefix.."SlotTexture")
|
||||
local nameFrame = getglobal(prefix.."NameFrame")
|
||||
if isTarget and name and not isUsable then
|
||||
icon:SetVertexColor(0.9, 0, 0)
|
||||
nameFrame:SetVertexColor(0.9, 0, 0)
|
||||
slotTex:SetVertexColor(1.0, 0, 0)
|
||||
else
|
||||
icon:SetVertexColor(1, 1, 1)
|
||||
nameFrame:SetVertexColor(1, 1, 1)
|
||||
slotTex:SetVertexColor(1, 1, 1)
|
||||
end
|
||||
end
|
||||
|
||||
function TradeFrame_UpdatePlayerItem(id)
|
||||
local name, texture, numItems, isUsable, enchantment = GetTradePlayerItemInfo(id)
|
||||
BenillaTradeSlot_SetItem("TradePlayerItem"..id, id, name, texture, numItems, isUsable, enchantment, nil)
|
||||
end
|
||||
|
||||
function TradeFrame_UpdateTargetItem(id)
|
||||
local name, texture, numItems, quality, isUsable, enchantment = GetTradeTargetItemInfo(id)
|
||||
BenillaTradeSlot_SetItem("TradeRecipientItem"..id, id, name, texture, numItems, isUsable, enchantment, 1)
|
||||
end
|
||||
|
||||
-- A trade slot's item button as a cursor drop target (decision 0592 P2, ref ClickTradeButton /
|
||||
-- ClickTargetTradeButton, TradeFrame.xml l.135/101). Both columns share the slot template, so
|
||||
-- player-vs-recipient is read off the slot frame's name; the slot number is its frame id. Our
|
||||
-- slot drops/clears a bag item (SET/CLEAR_TRADE_ITEM); the partner's column is read-only.
|
||||
function BenillaTradeSlot_OnClick(button)
|
||||
local slot = button:GetParent()
|
||||
local id = slot:GetID()
|
||||
if string.find(slot:GetName(), "Player", 1, true) then
|
||||
ClickTradeButton(id)
|
||||
else
|
||||
ClickTargetTradeButton(id)
|
||||
end
|
||||
end
|
||||
|
||||
-- Who has pressed Trade (spec: ref TradeFrame_SetAcceptState l.116). The event carries BOTH
|
||||
-- sides' states, so this is a straight repaint of two indicators rather than a toggle — and
|
||||
-- our own half is kept on the frame, because the Cancel button below is the only reader of
|
||||
-- whether there is an accept of ours to withdraw.
|
||||
--
|
||||
-- The extra thing our own accept does is lock the Trade button, so a standing accept cannot
|
||||
-- be sent a second time; Cancel is what takes it back.
|
||||
function TradeFrame_SetAcceptState(playerState, targetState)
|
||||
TradeFrame.acceptState = playerState
|
||||
local weAccepted = playerState == 1
|
||||
setTradeGlow(TradeHighlightPlayer, TradeHighlightPlayerEnchant, weAccepted)
|
||||
setTradeGlow(TradeHighlightRecipient, TradeHighlightRecipientEnchant, targetState == 1)
|
||||
if weAccepted then
|
||||
TradeFrameTradeButton:Disable()
|
||||
else
|
||||
TradeFrameTradeButton:Enable()
|
||||
end
|
||||
end
|
||||
|
||||
-- Cancel is two verbs on one button (spec: ref TradeFrameCancelButton_OnClick l.136). With
|
||||
-- an accept of ours standing it withdraws only THAT and the trade stays open — the button
|
||||
-- is the only way back out of the lock above. With no accept up there is nothing to
|
||||
-- withdraw, so it means "leave", and closing the window is what sends that (→ OnHide →
|
||||
-- CloseTrade).
|
||||
function TradeFrameCancelButton_OnClick()
|
||||
if TradeFrame.acceptState ~= 1 then
|
||||
HideUIPanel(TradeFrame)
|
||||
return
|
||||
end
|
||||
CancelTradeAccept()
|
||||
end
|
||||
]]></Script>
|
||||
|
||||
<!-- The accept-glow highlight (ref TradeHighlightTemplate l.4): a 3-slice UI-TradeFrame-Highlight
|
||||
(top/bottom caps + a stretched middle), sized per-instance. -->
|
||||
<Frame name="BenillaTradeHighlightTemplate" hidden="false" virtual="true">
|
||||
<Layers>
|
||||
<Layer level="BACKGROUND">
|
||||
<Texture name="$parentTop" file="Interface\TradeFrame\UI-TradeFrame-Highlight">
|
||||
<Size><AbsDimension x="161" y="16"/></Size>
|
||||
<Anchors><Anchor point="TOPLEFT"/></Anchors>
|
||||
<TexCoords left="0" right="0.62890625" top="0" bottom="0.0625"/>
|
||||
</Texture>
|
||||
<Texture name="$parentBottom" file="Interface\TradeFrame\UI-TradeFrame-Highlight">
|
||||
<Size><AbsDimension x="161" y="16"/></Size>
|
||||
<Anchors><Anchor point="BOTTOMLEFT"/></Anchors>
|
||||
<TexCoords left="0" right="0.62890625" top="0.9375" bottom="1.0"/>
|
||||
</Texture>
|
||||
<Texture name="$parentMiddle" file="Interface\TradeFrame\UI-TradeFrame-Highlight">
|
||||
<Size><AbsDimension x="161" y="10"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOP" relativeTo="$parentTop" relativePoint="BOTTOM"/>
|
||||
<Anchor point="BOTTOM" relativeTo="$parentBottom" relativePoint="TOP"/>
|
||||
</Anchors>
|
||||
<TexCoords left="0" right="0.62890625" top="0.0625" bottom="0.9375"/>
|
||||
</Texture>
|
||||
</Layer>
|
||||
</Layers>
|
||||
</Frame>
|
||||
|
||||
<!-- One trade slot (ref TradeItemTemplate l.38 + PlayerTradeItemTemplate's inlined ItemButton
|
||||
l.120): the empty-slot art, the name plate, the item name, and a 37px item button (icon +
|
||||
stack count). Display-only in P1 — no OnClick/OnEnter (decision 0592 P2). -->
|
||||
<Frame name="BenillaTradeItemTemplate" virtual="true">
|
||||
<Size><AbsDimension x="153" y="37"/></Size>
|
||||
<Layers>
|
||||
<Layer level="BACKGROUND">
|
||||
<Texture name="$parentSlotTexture" file="Interface\Buttons\UI-EmptySlot">
|
||||
<Size><AbsDimension x="64" y="64"/></Size>
|
||||
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="-13" y="13"/></Offset></Anchor></Anchors>
|
||||
</Texture>
|
||||
<Texture name="$parentNameFrame" file="Interface\QuestFrame\UI-QuestItemNameFrame">
|
||||
<Size><AbsDimension x="124" y="64"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="LEFT" relativeTo="$parentSlotTexture" relativePoint="RIGHT"><Offset><AbsDimension x="-20" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</Texture>
|
||||
<FontString name="$parentName" inherits="GameFontNormalSmall" text="" justifyH="LEFT">
|
||||
<Size><AbsDimension x="90" y="30"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="LEFT" relativeTo="$parentNameFrame"><Offset><AbsDimension x="15" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</FontString>
|
||||
</Layer>
|
||||
</Layers>
|
||||
<Frames>
|
||||
<Button name="$parentItemButton">
|
||||
<Size><AbsDimension x="37" y="37"/></Size>
|
||||
<Anchors><Anchor point="TOPLEFT" relativeTo="$parent"/></Anchors>
|
||||
<Layers>
|
||||
<Layer level="BACKGROUND">
|
||||
<Texture name="$parentIcon" hidden="true" setAllPoints="true"/>
|
||||
</Layer>
|
||||
<Layer level="ARTWORK">
|
||||
<FontString name="$parentCount" inherits="NumberFontNormal" hidden="true" justifyH="RIGHT">
|
||||
<Anchors><Anchor point="BOTTOMRIGHT"><Offset><AbsDimension x="-2" y="2"/></Offset></Anchor></Anchors>
|
||||
</FontString>
|
||||
</Layer>
|
||||
</Layers>
|
||||
<HighlightTexture file="Interface\Buttons\ButtonHilight-Square" alphaMode="ADD"/>
|
||||
<!-- A cursor drop target (decision 0592 P2): a click or a drag-release routes through
|
||||
the shared slot handler, which places/clears on our side and is inert on the
|
||||
partner's. Both gestures share one path, the reference's drag/click contract. -->
|
||||
<Scripts>
|
||||
<OnClick>BenillaTradeSlot_OnClick(self)</OnClick>
|
||||
<OnReceiveDrag>BenillaTradeSlot_OnClick(self)</OnReceiveDrag>
|
||||
</Scripts>
|
||||
</Button>
|
||||
</Frames>
|
||||
</Frame>
|
||||
|
||||
<!-- The enchant / non-traded slot art (ref TradeRecipientItem7/TradePlayerItem7 l.393): the
|
||||
EnchantIcon overlay sits over slot 7's item button. A virtual twin of the base slot with the
|
||||
extra icon, instantiated only for id 7. -->
|
||||
<Frame name="BenillaTradeEnchantItemTemplate" inherits="BenillaTradeItemTemplate" virtual="true">
|
||||
<Frames>
|
||||
<Frame name="$parentEnchant" setAllPoints="false">
|
||||
<Size><AbsDimension x="62" y="62"/></Size>
|
||||
<Anchors><Anchor point="TOPLEFT"/></Anchors>
|
||||
<Layers>
|
||||
<Layer level="OVERLAY">
|
||||
<Texture file="Interface\TradeFrame\UI-TradeFrame-EnchantIcon" setAllPoints="true"/>
|
||||
</Layer>
|
||||
</Layers>
|
||||
</Frame>
|
||||
</Frames>
|
||||
</Frame>
|
||||
|
||||
<Frame name="TradeFrame" parent="UIParent" toplevel="true" movable="true" enableMouse="true" hidden="true">
|
||||
<!-- ref TradeFrame l.164: 384×512; ShowUIPanel slots it left. -->
|
||||
<Size><AbsDimension x="384" y="512"/></Size>
|
||||
<HitRectInsets><AbsInset left="0" right="35" top="0" bottom="72"/></HitRectInsets>
|
||||
<Layers>
|
||||
<Layer level="BACKGROUND">
|
||||
<!-- ref l.180: the two 60×60 unit portraits (recipient right, player left). -->
|
||||
<Texture name="TradeFrameRecipientPortrait">
|
||||
<Size><AbsDimension x="60" y="60"/></Size>
|
||||
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="183" y="-6"/></Offset></Anchor></Anchors>
|
||||
</Texture>
|
||||
<Texture name="TradeFramePlayerPortrait">
|
||||
<Size><AbsDimension x="60" y="60"/></Size>
|
||||
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="7" y="-6"/></Offset></Anchor></Anchors>
|
||||
</Texture>
|
||||
</Layer>
|
||||
<Layer level="ARTWORK">
|
||||
<!-- ref l.205: the four corner slabs. -->
|
||||
<Texture file="Interface\TradeFrame\UI-TradeFrame-TopLeft">
|
||||
<Size><AbsDimension x="256" y="256"/></Size>
|
||||
<Anchors><Anchor point="TOPLEFT"/></Anchors>
|
||||
</Texture>
|
||||
<Texture file="Interface\TradeFrame\UI-TradeFrame-TopRight">
|
||||
<Size><AbsDimension x="128" y="256"/></Size>
|
||||
<Anchors><Anchor point="TOPRIGHT"/></Anchors>
|
||||
</Texture>
|
||||
<Texture file="Interface\TradeFrame\UI-TradeFrame-BotLeft">
|
||||
<Size><AbsDimension x="256" y="256"/></Size>
|
||||
<Anchors><Anchor point="BOTTOMLEFT"/></Anchors>
|
||||
</Texture>
|
||||
<Texture file="Interface\TradeFrame\UI-TradeFrame-BotRight">
|
||||
<Size><AbsDimension x="128" y="256"/></Size>
|
||||
<Anchors><Anchor point="BOTTOMRIGHT"/></Anchors>
|
||||
</Texture>
|
||||
<FontString name="TradeFrameRecipientNameText" inherits="GameFontNormal" text="">
|
||||
<Size><AbsDimension x="80" y="12"/></Size>
|
||||
<Anchors><Anchor point="TOPLEFT" relativeTo="TradeFrame"><Offset><AbsDimension x="245" y="-17"/></Offset></Anchor></Anchors>
|
||||
</FontString>
|
||||
<FontString name="TradeFramePlayerNameText" inherits="GameFontNormal" text="">
|
||||
<Size><AbsDimension x="100" y="12"/></Size>
|
||||
<Anchors><Anchor point="TOPLEFT" relativeTo="TradeFrame"><Offset><AbsDimension x="75" y="-17"/></Offset></Anchor></Anchors>
|
||||
</FontString>
|
||||
<!-- ref l.262: the "Will Be Enchanted" column labels above the enchant slots. -->
|
||||
<FontString name="TradeFramePlayerEnchantText" inherits="GameFontHighlightSmall" text="Will Be Enchanted">
|
||||
<Anchors><Anchor point="TOPLEFT" relativeTo="TradeFrame"><Offset><AbsDimension x="26" y="-374"/></Offset></Anchor></Anchors>
|
||||
</FontString>
|
||||
<FontString name="TradeFrameRecipientEnchantText" inherits="GameFontHighlightSmall" text="Will Be Enchanted">
|
||||
<Anchors><Anchor point="LEFT" relativeTo="TradeFramePlayerEnchantText"><Offset><AbsDimension x="170" y="0"/></Offset></Anchor></Anchors>
|
||||
</FontString>
|
||||
</Layer>
|
||||
</Layers>
|
||||
<Frames>
|
||||
<!-- The accept-glow highlights, one per column + its enchant slot (ref l.283). -->
|
||||
<Frame name="TradeHighlightPlayer" inherits="BenillaTradeHighlightTemplate" hidden="true">
|
||||
<Size><AbsDimension x="161" y="266"/></Size>
|
||||
<Anchors><Anchor point="TOPLEFT" relativeTo="TradeFrame"><Offset><AbsDimension x="19" y="-100"/></Offset></Anchor></Anchors>
|
||||
</Frame>
|
||||
<Frame name="TradeHighlightRecipient" inherits="BenillaTradeHighlightTemplate" hidden="true">
|
||||
<Size><AbsDimension x="161" y="266"/></Size>
|
||||
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="189" y="-100"/></Offset></Anchor></Anchors>
|
||||
</Frame>
|
||||
<Frame name="TradeHighlightPlayerEnchant" inherits="BenillaTradeHighlightTemplate" hidden="true">
|
||||
<Size><AbsDimension x="161" y="61"/></Size>
|
||||
<Anchors><Anchor point="TOPLEFT" relativeTo="TradeHighlightPlayer" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-4"/></Offset></Anchor></Anchors>
|
||||
</Frame>
|
||||
<Frame name="TradeHighlightRecipientEnchant" inherits="BenillaTradeHighlightTemplate" hidden="true">
|
||||
<Size><AbsDimension x="161" y="61"/></Size>
|
||||
<Anchors><Anchor point="TOPLEFT" relativeTo="TradeHighlightRecipient" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-4"/></Offset></Anchor></Anchors>
|
||||
</Frame>
|
||||
|
||||
<!-- The partner's 6 trade slots + the 7th enchant slot (ref TradeRecipientItem1..7 l.331). -->
|
||||
<Frame name="TradeRecipientItem1" inherits="BenillaTradeItemTemplate" id="1">
|
||||
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="195" y="-104"/></Offset></Anchor></Anchors>
|
||||
</Frame>
|
||||
<Frame name="TradeRecipientItem2" inherits="BenillaTradeItemTemplate" id="2">
|
||||
<Anchors><Anchor point="TOPLEFT" relativeTo="TradeRecipientItem1" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-7"/></Offset></Anchor></Anchors>
|
||||
</Frame>
|
||||
<Frame name="TradeRecipientItem3" inherits="BenillaTradeItemTemplate" id="3">
|
||||
<Anchors><Anchor point="TOPLEFT" relativeTo="TradeRecipientItem2" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-7"/></Offset></Anchor></Anchors>
|
||||
</Frame>
|
||||
<Frame name="TradeRecipientItem4" inherits="BenillaTradeItemTemplate" id="4">
|
||||
<Anchors><Anchor point="TOPLEFT" relativeTo="TradeRecipientItem3" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-7"/></Offset></Anchor></Anchors>
|
||||
</Frame>
|
||||
<Frame name="TradeRecipientItem5" inherits="BenillaTradeItemTemplate" id="5">
|
||||
<Anchors><Anchor point="TOPLEFT" relativeTo="TradeRecipientItem4" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-7"/></Offset></Anchor></Anchors>
|
||||
</Frame>
|
||||
<Frame name="TradeRecipientItem6" inherits="BenillaTradeItemTemplate" id="6">
|
||||
<Anchors><Anchor point="TOPLEFT" relativeTo="TradeRecipientItem5" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-7"/></Offset></Anchor></Anchors>
|
||||
</Frame>
|
||||
<Frame name="TradeRecipientItem7" inherits="BenillaTradeEnchantItemTemplate" id="7">
|
||||
<Anchors><Anchor point="TOPLEFT" relativeTo="TradeRecipientItem6" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-28"/></Offset></Anchor></Anchors>
|
||||
</Frame>
|
||||
|
||||
<!-- Our 6 trade slots + the 7th enchant slot (ref TradePlayerItem1..7 l.410). -->
|
||||
<Frame name="TradePlayerItem1" inherits="BenillaTradeItemTemplate" id="1">
|
||||
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="26" y="-104"/></Offset></Anchor></Anchors>
|
||||
</Frame>
|
||||
<Frame name="TradePlayerItem2" inherits="BenillaTradeItemTemplate" id="2">
|
||||
<Anchors><Anchor point="TOPLEFT" relativeTo="TradePlayerItem1" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-7"/></Offset></Anchor></Anchors>
|
||||
</Frame>
|
||||
<Frame name="TradePlayerItem3" inherits="BenillaTradeItemTemplate" id="3">
|
||||
<Anchors><Anchor point="TOPLEFT" relativeTo="TradePlayerItem2" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-7"/></Offset></Anchor></Anchors>
|
||||
</Frame>
|
||||
<Frame name="TradePlayerItem4" inherits="BenillaTradeItemTemplate" id="4">
|
||||
<Anchors><Anchor point="TOPLEFT" relativeTo="TradePlayerItem3" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-7"/></Offset></Anchor></Anchors>
|
||||
</Frame>
|
||||
<Frame name="TradePlayerItem5" inherits="BenillaTradeItemTemplate" id="5">
|
||||
<Anchors><Anchor point="TOPLEFT" relativeTo="TradePlayerItem4" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-7"/></Offset></Anchor></Anchors>
|
||||
</Frame>
|
||||
<Frame name="TradePlayerItem6" inherits="BenillaTradeItemTemplate" id="6">
|
||||
<Anchors><Anchor point="TOPLEFT" relativeTo="TradePlayerItem5" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-7"/></Offset></Anchor></Anchors>
|
||||
</Frame>
|
||||
<Frame name="TradePlayerItem7" inherits="BenillaTradeEnchantItemTemplate" id="7">
|
||||
<Anchors><Anchor point="TOPLEFT" relativeTo="TradePlayerItem6" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-28"/></Offset></Anchor></Anchors>
|
||||
</Frame>
|
||||
|
||||
<!-- The Trade button (ref l.502, UIPanelButtonTemplate inlined): OnClick → AcceptTrade. -->
|
||||
<Button name="TradeFrameTradeButton" text="Trade">
|
||||
<Size><AbsDimension x="85" y="22"/></Size>
|
||||
<Anchors><Anchor point="BOTTOMRIGHT" relativeTo="TradeFrame" relativePoint="BOTTOMRIGHT"><Offset><AbsDimension x="-113" y="55"/></Offset></Anchor></Anchors>
|
||||
<NormalTexture file="Interface\Buttons\UI-Panel-Button-Up"><TexCoords left="0" right="0.625" top="0" bottom="0.6875"/></NormalTexture>
|
||||
<PushedTexture file="Interface\Buttons\UI-Panel-Button-Down"><TexCoords left="0" right="0.625" top="0" bottom="0.6875"/></PushedTexture>
|
||||
<DisabledTexture file="Interface\Buttons\UI-Panel-Button-Disabled"><TexCoords left="0" right="0.625" top="0" bottom="0.6875"/></DisabledTexture>
|
||||
<HighlightTexture file="Interface\Buttons\UI-Panel-Button-Highlight" alphaMode="ADD"><TexCoords left="0" right="0.625" top="0" bottom="0.6875"/></HighlightTexture>
|
||||
<ButtonText name="$parentText"/>
|
||||
<NormalFont inherits="GameFontNormal"/><HighlightFont inherits="GameFontHighlight"/><DisabledFont inherits="GameFontDisable"/>
|
||||
<Scripts><OnClick>AcceptTrade()</OnClick></Scripts>
|
||||
</Button>
|
||||
<Button name="TradeFrameCancelButton" text="Cancel">
|
||||
<Size><AbsDimension x="77" y="22"/></Size>
|
||||
<Anchors><Anchor point="TOPLEFT" relativeTo="TradeFrameTradeButton" relativePoint="TOPRIGHT"><Offset><AbsDimension x="3" y="0"/></Offset></Anchor></Anchors>
|
||||
<NormalTexture file="Interface\Buttons\UI-Panel-Button-Up"><TexCoords left="0" right="0.625" top="0" bottom="0.6875"/></NormalTexture>
|
||||
<PushedTexture file="Interface\Buttons\UI-Panel-Button-Down"><TexCoords left="0" right="0.625" top="0" bottom="0.6875"/></PushedTexture>
|
||||
<HighlightTexture file="Interface\Buttons\UI-Panel-Button-Highlight" alphaMode="ADD"><TexCoords left="0" right="0.625" top="0" bottom="0.6875"/></HighlightTexture>
|
||||
<ButtonText name="$parentText"/>
|
||||
<NormalFont inherits="GameFontNormal"/><HighlightFont inherits="GameFontHighlight"/><DisabledFont inherits="GameFontDisable"/>
|
||||
<Scripts><OnClick>TradeFrameCancelButton_OnClick()</OnClick></Scripts>
|
||||
</Button>
|
||||
<!-- The window close X (ref l.544, UIPanelCloseButton inlined). -->
|
||||
<Button name="TradeFrameCloseButton">
|
||||
<Size><AbsDimension x="32" y="32"/></Size>
|
||||
<Anchors><Anchor point="TOPRIGHT" relativeTo="TradeFrame" relativePoint="TOPRIGHT"><Offset><AbsDimension x="-25" y="-8"/></Offset></Anchor></Anchors>
|
||||
<NormalTexture file="Interface\Buttons\UI-Panel-MinimizeButton-Up"/>
|
||||
<PushedTexture file="Interface\Buttons\UI-Panel-MinimizeButton-Down"/>
|
||||
<HighlightTexture file="Interface\Buttons\UI-Panel-MinimizeButton-Highlight" alphaMode="ADD"/>
|
||||
<Scripts><OnClick>HideUIPanel(TradeFrame)</OnClick></Scripts>
|
||||
</Button>
|
||||
|
||||
<!-- Our gold — EDITABLE (ref TradeFrame.xml l.553 `TradePlayerInputMoneyFrame`). The three
|
||||
boxes come off the chain now: `MoneyInputFrameTemplate` out of
|
||||
`Interface\FrameXML\MoneyInputFrame.xml`, which benilla.toc has sourced since 1751
|
||||
window 23. Our inline trio was a verbatim copy of it — same child names, same
|
||||
textures and TexCoords, same tab chain, same 58/30/30 geometry — so this is the
|
||||
rare swap that changes no pixel by itself (1882).
|
||||
`SetMode("compact")` is the reference's own call on this frame and DOES move
|
||||
things: Gold 58->56 wide, Silver's offset 26->22, Copper's 16->11. We did not have
|
||||
it, so the trade money boxes tighten by a few pixels. That is the migration
|
||||
restoring the reference's layout, not a new choice.
|
||||
A keystroke drives TradeFrame_UpdateMoney -> SetTradeMoney -> CMSG_SET_TRADE_GOLD,
|
||||
registered through the reference's own callback slot rather than a hand-written
|
||||
OnTextChanged on each box; the server's echo comes back on PLAYER_TRADE_MONEY. -->
|
||||
<Frame name="TradePlayerInputMoney" inherits="MoneyInputFrameTemplate">
|
||||
<Size><AbsDimension x="176" y="18"/></Size>
|
||||
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="26" y="-73"/></Offset></Anchor></Anchors>
|
||||
<Scripts>
|
||||
<OnLoad>
|
||||
MoneyInputFrame_SetMode(this, "compact")
|
||||
MoneyInputFrame_SetOnvalueChangedFunc(this, TradeFrame_UpdateMoney)
|
||||
</OnLoad>
|
||||
</Scripts>
|
||||
</Frame>
|
||||
<!-- The partner's gold (read-only — GetTargetTradeMoney; ref TradeRecipientMoneyFrame l.574). -->
|
||||
<!-- ref TradeFrame.xml:574-588: a SmallMoneyFrameTemplate of type TARGET_TRADE, which repaints
|
||||
itself on TRADE_MONEY_CHANGED through MoneyFrame_OnEvent — no explicit update anywhere in
|
||||
the reference's Lua, and none here now. Same TOPRIGHT (-40,-80) seat as before (1937). -->
|
||||
<Frame name="TradeRecipientMoneyFrame" inherits="SmallMoneyFrameTemplate">
|
||||
<Anchors><Anchor point="TOPRIGHT"><Offset><AbsDimension x="-40" y="-80"/></Offset></Anchor></Anchors>
|
||||
<Scripts><OnLoad>SmallMoneyFrame_OnLoad(); MoneyFrame_SetType("TARGET_TRADE");</OnLoad></Scripts>
|
||||
</Frame>
|
||||
</Frames>
|
||||
<Scripts>
|
||||
<OnLoad>TradeFrame_OnLoad()</OnLoad>
|
||||
<OnEvent>TradeFrame_OnEvent()</OnEvent>
|
||||
<OnShow>TradeFrame_OnShow(); PlaySound("igCharacterInfoOpen")</OnShow>
|
||||
<OnHide>TradeFrame_OnHide(); PlaySound("igCharacterInfoClose")</OnHide>
|
||||
</Scripts>
|
||||
</Frame>
|
||||
</Ui>
|
||||
@@ -75,6 +75,12 @@
|
||||
this:RegisterEvent("CONFIRM_TALENT_WIPE")
|
||||
this:RegisterEvent("CONFIRM_BINDER")
|
||||
this:RegisterEvent("CONFIRM_SUMMON")
|
||||
-- The dialog engine's verbs (1963): the feeds behind these exist now.
|
||||
this:RegisterEvent("INSTANCE_BOOT_START")
|
||||
this:RegisterEvent("INSTANCE_BOOT_STOP")
|
||||
this:RegisterEvent("CONFIRM_PET_UNLEARN")
|
||||
this:RegisterEvent("AREA_SPIRIT_HEALER_IN_RANGE")
|
||||
this:RegisterEvent("AREA_SPIRIT_HEALER_OUT_OF_RANGE")
|
||||
</OnLoad>
|
||||
<OnEvent>UIParent_OnEvent(event)</OnEvent>
|
||||
<!-- The reference's own UIParent OnUpdate line (its UIParent.xml l.19): the floating
|
||||
@@ -685,6 +691,30 @@
|
||||
StaticPopup_Show("CONFIRM_SUMMON")
|
||||
return
|
||||
end
|
||||
-- ref UIParent.lua l.429-436 and l.525-546, verbatim (1963).
|
||||
if ( event == "AREA_SPIRIT_HEALER_IN_RANGE" ) then
|
||||
StaticPopup_Show("AREA_SPIRIT_HEAL")
|
||||
return
|
||||
end
|
||||
if ( event == "AREA_SPIRIT_HEALER_OUT_OF_RANGE" ) then
|
||||
StaticPopup_Hide("AREA_SPIRIT_HEAL")
|
||||
return
|
||||
end
|
||||
if ( event == "INSTANCE_BOOT_START" ) then
|
||||
StaticPopup_Show("INSTANCE_BOOT")
|
||||
return
|
||||
end
|
||||
if ( event == "INSTANCE_BOOT_STOP" ) then
|
||||
StaticPopup_Hide("INSTANCE_BOOT")
|
||||
return
|
||||
end
|
||||
if ( event == "CONFIRM_PET_UNLEARN" ) then
|
||||
local dialog = StaticPopup_Show("CONFIRM_PET_UNLEARN")
|
||||
if ( dialog ) then
|
||||
MoneyFrame_Update(dialog:GetName().."MoneyFrame", arg1)
|
||||
end
|
||||
return
|
||||
end
|
||||
-- The ready-check popup's opener (decision 1549), ref UIParent.lua l.570-572. The
|
||||
-- event is the engine's own `0x218`, fired by the `MSG_RAID_READY_CHECK` open
|
||||
-- handler; the frame and `ShowReadyCheck` live in RaidFrame.xml, so this is the one
|
||||
|
||||
@@ -128,17 +128,15 @@ UIParent.xml
|
||||
# `$parentNormalTexture`, 37x37) — which is what makes this the safest of the foundational swaps
|
||||
# rather than the widest: everything that inherits it asks for the same four names.
|
||||
Interface\FrameXML\ItemButtonTemplate.xml
|
||||
# The reference's money-frame kit: MoneyFrameTemplate/SmallMoneyFrameTemplate + MoneyTypeInfo
|
||||
# + the MoneyFrame_* API. Needs only Fonts (NumberFontNormal/NumberFontNormalLarge) — and it must
|
||||
# come BEFORE UiPanels.xml, because the shared StaticPopup's coin row is a child frame declaring
|
||||
# `inherits="SmallMoneyFrameTemplate"`, which is resolved at LOAD (decision 1580). That is the
|
||||
# reference's own order too (FrameXML.toc: MoneyFrame.xml l.10, StaticPopup.xml l.23); this file
|
||||
# used to sit 140 lines lower, beside the other shared widget kits, which was only ever tenable
|
||||
# while nothing above it showed coins. AuctionFrame far below is laid out on it as well (its price
|
||||
# columns need a money TYPE, which the other kit has no notion of); our other eight coin displays
|
||||
# still run on the BenillaMoney_* slot kit (which now lives HERE), and this file's header is where
|
||||
# the two are reconciled.
|
||||
MoneyFrame.xml
|
||||
# ══ THE MONEY KIT — the reference's own, off the player's chain (1751; decision 1962) ══
|
||||
# MoneyFrame.lua + MoneyFrame.xml (two rows: the .xml sources no .lua): MoneyTypeInfo, the
|
||||
# MoneyFrame_* API, MoneyFrameTemplate / SmallMoneyFrameTemplate. Needs only Fonts, and must come
|
||||
# BEFORE the dialog engine (StaticPopup's coin row inherits SmallMoneyFrameTemplate at LOAD), which
|
||||
# is the reference's own order too (FrameXML.toc l.9-10 against l.23). The money CURSOR family the
|
||||
# kit's PickupFunc/DropFunc rows drive (PickupPlayerMoney, DropCursorMoney, GetCursorMoney, the
|
||||
# trade and send-mail pairs) is this engine's now. 1190's transcription is gone.
|
||||
Interface\FrameXML\MoneyFrame.lua
|
||||
Interface\FrameXML\MoneyFrame.xml
|
||||
# ══ THE MONEY INPUT KIT — the reference's own, off the player's chain (1751, window 23) ══
|
||||
# `MoneyInputFrameTemplate` and the ten `MoneyInputFrame_*` verbs out of `MoneyInputFrame.lua` —
|
||||
# the three-box gold/silver/copper editor a player types an amount into. Nothing of ours ever
|
||||
@@ -312,10 +310,10 @@ Interface\FrameXML\PetFrame.xml
|
||||
# below TargetFrame.xml. Its fade chain needs UiPanels.xml's `UIFrameFade` family, far above, at
|
||||
# call time. `MAX_COMBO_POINTS` comes from TargetFrame.lua now, which is where 1.12 declares it.
|
||||
Interface\FrameXML\ComboFrame.xml
|
||||
# The center-screen scrolling combat text (decision 0578) — the Blizzard_CombatText
|
||||
# transcription; consumes COMBAT_TEXT_UPDATE + the regen/health/power events, needs
|
||||
# only Fonts.xml (CombatTextFont) + UIParent before it.
|
||||
CombatText.xml
|
||||
# The scrolling combat text is the reference's own `Blizzard_CombatText`, a LoadOnDemand addon
|
||||
# off the chain (its toc says so), loaded by the options window when SHOW_COMBAT_TEXT is "1" —
|
||||
# at login and on the switch, the reference's own two sites. No row here (decision 1964; 0578's
|
||||
# transcription is gone).
|
||||
# ── MEASUREMENT (1938): the options window at the reference's slot (FrameXML.toc l.21, before
|
||||
# MainMenuBar l.34) with the four rows it inherits from. ──
|
||||
# The faux-scroll kit (decisions 0247/0250/0251): BenillaScrollBarTemplate (the draggable
|
||||
@@ -358,6 +356,10 @@ Interface\FrameXML\UIPanelTemplates.xml
|
||||
# the 37 dialogs UiPanels.xml carried and the seven free-standing dialog files (Death, Duel,
|
||||
# QuestShare, EnchantConfirm, BinderConfirm, ConfirmSummon, TalentWipeConfirm) are gone.
|
||||
Interface\FrameXML\StaticPopup.xml
|
||||
# The coin pickup dialog (the reference's toc l.24, right here): shift-click a purse coin and pick
|
||||
# how many. Pure gain with the money kit — nothing of ours ever answered `OpenCoinPickupFrame`
|
||||
# (decision 1962).
|
||||
Interface\FrameXML\CoinPickupFrame.xml
|
||||
|
||||
# The Keybindings page MODULE (decision 1008, superseding 0997's standalone window):
|
||||
# the templates + script of the Options window's Keybindings category, over the
|
||||
@@ -859,11 +861,15 @@ Interface\FrameXML\MerchantFrame.xml
|
||||
# beside MerchantFrame in the NPC-window cluster; its postage/enclosed/COD coin displays use
|
||||
# the BenillaMoney_* kit, which is defined in MoneyFrame.xml far above.
|
||||
MailFrame.xml
|
||||
# The player-to-player trade window (decision 0592 P1): the two-sided offer over the trade
|
||||
# API (`benilla-ui/src/script/trade.rs`) + `crate::ui_trade`'s feed/drain. Kept beside
|
||||
# MerchantFrame in the NPC-window cluster; its two gold displays use the BenillaMoney_* kit,
|
||||
# defined in MoneyFrame.xml far above.
|
||||
TradeFrame.xml
|
||||
# ══ THE TRADE WINDOW — the reference's own, off the player's chain (1751; decision 1966) ══
|
||||
# TradeFrame.xml + TradeFrame.lua: both columns' seven slots over the trade API
|
||||
# (`benilla-ui/src/script/trade.rs`) + `crate::ui_trade`'s feed/drain, the recipient's
|
||||
# SmallMoneyFrame (TARGET_TRADE) and the player's MoneyInputFrame, the accept glows. Its slot
|
||||
# tooltips are this engine's SetTradePlayerItem / SetTradeTargetItem; its per-slot events
|
||||
# (TRADE_PLAYER_ITEM_CHANGED / TRADE_TARGET_ITEM_CHANGED) the feed's. Loaded here, after
|
||||
# ItemButtonTemplate, MoneyInputFrame, the money kit and the dialog engine it inherits from.
|
||||
# 0592's transcription is gone.
|
||||
Interface\FrameXML\TradeFrame.xml
|
||||
# ══ THE ITEM-TEXT READER — the reference's own, off the player's chain (1751, window 9) ══
|
||||
# `ItemTextFrame` and its page chrome, over the `ItemTextGet*` seam
|
||||
# (`benilla-ui/src/script/item_text.rs`) and `crate::ui_item_text`'s feed/drain. Letters (mail-made
|
||||
|
||||
@@ -45,6 +45,10 @@ fn payload_icon(script: &benilla_ui::script::UiScript) -> Option<String> {
|
||||
// Mode 10 — the stabled pet's family icon (decision 1677). Always present: a non-empty
|
||||
// icon path is the grab's own gate.
|
||||
CursorPayload::StablePet(p) => Some(p.texture),
|
||||
// Mode 2 (1965) — the coin bitmap by magnitude, `GetCoinIcon`'s own table.
|
||||
CursorPayload::Money(m) => {
|
||||
Some(benilla_ui::script::coin_icon(i64::from(m.copper)).to_string())
|
||||
}
|
||||
// Mode 5 — the vendor row's icon. The reference stores the row's `ItemDisplayInfo` id
|
||||
// (`0xb4d8ec`) and resolves the art from it; we carry the resolved path, so this is the
|
||||
// same picture one hop later.
|
||||
|
||||
@@ -126,6 +126,7 @@ mod ui_cast;
|
||||
mod ui_char;
|
||||
mod ui_chat;
|
||||
mod ui_craft;
|
||||
mod ui_dialog_verbs;
|
||||
mod ui_dressup;
|
||||
mod ui_duel;
|
||||
mod ui_follow;
|
||||
@@ -213,6 +214,7 @@ use ui_cast::UiCastPlugin;
|
||||
use ui_char::UiCharPlugin;
|
||||
use ui_chat::UiChatPlugin;
|
||||
use ui_craft::UiCraftPlugin;
|
||||
use ui_dialog_verbs::UiDialogVerbsPlugin;
|
||||
use ui_duel::UiDuelPlugin;
|
||||
use ui_follow::UiFollowPlugin;
|
||||
use ui_gm_ticket::UiGmTicketPlugin;
|
||||
@@ -636,6 +638,9 @@ pub fn run(build: BuildId) -> AppExit {
|
||||
// CONFIRM_BINDER dialog it raises, and the CMSG_BINDER_ACTIVATE its Accept sends — the only
|
||||
// packet in the flow that actually binds anything.
|
||||
.add_plugins(UiBinderPlugin)
|
||||
// The dialog engine's own verbs (decision 1963): the pet trainer's question, the instance
|
||||
// boot clock, the area spirit healer, the battleground queue, the meeting stone.
|
||||
.add_plugins(UiDialogVerbsPlugin)
|
||||
// Being summoned (decision 1747): SMSG_SUMMON_REQUEST's latch, the CONFIRM_SUMMON dialog it
|
||||
// raises, and the CMSG_SUMMON_RESPONSE its Accept sends. The binder's twin one line up — a
|
||||
// server-asked question whose only wire answer is yes — and here for that reason.
|
||||
|
||||
@@ -1260,6 +1260,26 @@ pub(crate) enum ClientCommand {
|
||||
TalentWipeConfirm {
|
||||
trainer: u64,
|
||||
},
|
||||
// ── The dialog engine's verbs (decision 1963) ──
|
||||
/// `ForceLogout()` — `CMSG_PLAYER_LOGOUT` (`0x4A`, empty), the forced flavour of the logout
|
||||
/// dispatcher; sent only with a live world session.
|
||||
ForceLogout,
|
||||
/// `AcceptAreaSpiritHeal()` — `CMSG_AREA_SPIRIT_HEALER_QUEUE` with the cached healer.
|
||||
AreaSpiritHealerQueue {
|
||||
healer: u64,
|
||||
},
|
||||
/// `AcceptBattlefieldPort(index, accept)` — `CMSG_BATTLEFIELD_PORT`: the slot's map id and
|
||||
/// the answer as one byte.
|
||||
BattlefieldPort {
|
||||
map_id: u32,
|
||||
accept: bool,
|
||||
},
|
||||
/// `CancelMeetingStoneRequest()` — `CMSG 0x293`, empty.
|
||||
MeetingStoneLeave,
|
||||
/// `ConfirmPetUnlearn()` — `CMSG_PET_UNLEARN` with the latched trainer guid.
|
||||
PetUnlearn {
|
||||
trainer: u64,
|
||||
},
|
||||
/// Open the bank (`CMSG_BANKER_ACTIVATE`, decision 0604): the direct opener a right-click on
|
||||
/// a pure banker (bit 8 the lowest service bit) uses — a gossip-flagged banker routes through
|
||||
/// the gossip menu instead, whose bank option makes the server volunteer the same answer.
|
||||
@@ -1998,6 +2018,12 @@ pub(crate) enum ClientCommand {
|
||||
AddFriend {
|
||||
name: String,
|
||||
},
|
||||
/// The client's LFG slots and comment (`CMSG_SET_LOOKING_FOR_GROUP`), sent by
|
||||
/// `SetLookingForGroup` only when the commit changed something (1961).
|
||||
SetLookingForGroup {
|
||||
slots: [u32; 3],
|
||||
comment: String,
|
||||
},
|
||||
/// Drop a friend by guid (`CMSG_DEL_FRIEND`) — the caller resolves the name first.
|
||||
DelFriend {
|
||||
guid: u64,
|
||||
|
||||
@@ -246,7 +246,20 @@ pub(super) fn apply_net_updates(
|
||||
// `MSG_TALENT_WIPE_CONFIRM` parks the trainer's guid + cost here and
|
||||
// `crate::ui_talent_wipe` turns it into the CONFIRM_TALENT_WIPE dialog, whose
|
||||
// Accept is the only thing that unlearns anything. The binder's twin above.
|
||||
ResMut<crate::ui_talent_wipe::TalentWipeState>,
|
||||
(
|
||||
ResMut<crate::ui_talent_wipe::TalentWipeState>,
|
||||
// The dialog engine's verbs (decision 1963): the pet trainer's latch, the
|
||||
// instance-boot clock, the area spirit healer, the battleground queue and
|
||||
// the meeting-stone queue — each a feed for a stock dialog. Nested beside
|
||||
// the talent wipe because this tuple sits at Bevy's sixteen-entry limit.
|
||||
(
|
||||
ResMut<crate::ui_dialog_verbs::PetUnlearnState>,
|
||||
ResMut<crate::ui_dialog_verbs::InstanceBoot>,
|
||||
ResMut<crate::ui_dialog_verbs::AreaSpiritHealer>,
|
||||
ResMut<crate::ui_dialog_verbs::BattlefieldQueue>,
|
||||
ResMut<crate::ui_dialog_verbs::MeetingStone>,
|
||||
),
|
||||
),
|
||||
// The guard's directions marker (`SMSG_GOSSIP_POI`) and the map id it has to be
|
||||
// stamped with — the wire carries no map field, so "where you were standing when
|
||||
// the guard told you" is the client's to remember (`crate::poi_marker`).
|
||||
@@ -460,7 +473,16 @@ pub(super) fn apply_net_updates(
|
||||
mut played_time_answer,
|
||||
mut guild,
|
||||
mut binder,
|
||||
mut talent_wipe,
|
||||
(
|
||||
mut talent_wipe,
|
||||
(
|
||||
mut pet_unlearn,
|
||||
mut instance_boot,
|
||||
mut area_spirit,
|
||||
mut battlefield_queue,
|
||||
mut meeting_stone,
|
||||
),
|
||||
),
|
||||
mut poi_marker,
|
||||
current_map,
|
||||
mut inspect_honor,
|
||||
@@ -917,6 +939,29 @@ pub(super) fn apply_net_updates(
|
||||
talent_wipe.ask(trainer, cost);
|
||||
}
|
||||
}
|
||||
// The pet trainer's question (decision 1963) — the talent-wipe twin above; a zero
|
||||
// guid is the reference's own `ERR_TALENT_WIPE_ERROR` leg, carried over as observed.
|
||||
SessionEvent::PetUnlearnConfirm { trainer, cost } => {
|
||||
if trainer == 0 {
|
||||
debug!("net: pet unlearn refused (zero trainer) — no dialog");
|
||||
ui_error_keys
|
||||
.0
|
||||
.push(crate::ui_action::UiError::key("ERR_TALENT_WIPE_ERROR"));
|
||||
} else {
|
||||
debug!("net: trainer {trainer:#x} asks to unlearn the pet for {cost} copper");
|
||||
pet_unlearn.ask(trainer, cost);
|
||||
}
|
||||
}
|
||||
SessionEvent::RaidGroupOnly { delay_ms, reason } => {
|
||||
instance_boot.apply(delay_ms, reason, std::time::Instant::now());
|
||||
}
|
||||
SessionEvent::AreaSpiritHealerTime { healer, ms } => {
|
||||
area_spirit.on_time(healer, ms, std::time::Instant::now());
|
||||
}
|
||||
SessionEvent::BattlefieldStatus(status) => battlefield_queue.apply(status),
|
||||
SessionEvent::MeetingStoneSetQueue { area, status } => {
|
||||
meeting_stone.apply(area, status);
|
||||
}
|
||||
SessionEvent::PlayerBound { binder: npc, area } => {
|
||||
debug!("net: bound to area {area} by {npc:#x}");
|
||||
crate::ui_binder::apply::bound(
|
||||
|
||||
@@ -1064,6 +1064,15 @@ fn writer_loop(
|
||||
ClientCommand::BinderActivate { binder } => w.binder_activate(binder),
|
||||
ClientCommand::SummonResponse { summoner } => w.summon_response(summoner),
|
||||
ClientCommand::TalentWipeConfirm { trainer } => w.talent_wipe_confirm(trainer),
|
||||
ClientCommand::ForceLogout => w.player_logout(),
|
||||
ClientCommand::AreaSpiritHealerQueue { healer } => {
|
||||
w.area_spirit_healer_queue(healer)
|
||||
}
|
||||
ClientCommand::BattlefieldPort { map_id, accept } => {
|
||||
w.battlefield_port(map_id, accept)
|
||||
}
|
||||
ClientCommand::MeetingStoneLeave => w.meeting_stone_leave(),
|
||||
ClientCommand::PetUnlearn { trainer } => w.pet_unlearn(trainer),
|
||||
ClientCommand::BankerActivate { guid } => w.banker_activate(guid),
|
||||
ClientCommand::BuyBankSlot { guid } => w.buy_bank_slot(guid),
|
||||
ClientCommand::AutoBankItem { bag, slot } => w.autobank_item(bag, slot),
|
||||
@@ -1326,6 +1335,9 @@ fn writer_loop(
|
||||
ClientCommand::ToggleCloak => w.toggle_cloak(),
|
||||
ClientCommand::FriendListRequest => w.friend_list(),
|
||||
ClientCommand::AddFriend { name } => w.add_friend(&name),
|
||||
ClientCommand::SetLookingForGroup { slots, comment } => {
|
||||
w.set_looking_for_group(slots, &comment)
|
||||
}
|
||||
ClientCommand::DelFriend { guid } => w.del_friend(guid),
|
||||
ClientCommand::AddIgnore { name } => w.add_ignore(&name),
|
||||
ClientCommand::DelIgnore { guid } => w.del_ignore(guid),
|
||||
|
||||
@@ -128,6 +128,8 @@ fn load_item_sounds(mut commands: Commands, assets: Option<Res<WorldAssets>>) {
|
||||
/// payload transition (decision 0216) — an Item transition always plays its own per-item kit
|
||||
/// instead, never this pair.
|
||||
const INTERFACESOUND_CURSORGRABOBJECT: u32 = 902;
|
||||
/// `LOOTWINDOWCOINSOUND` — SoundEntries kit 895, the coin clink the money pickup names (1962).
|
||||
const LOOTWINDOWCOINSOUND: u32 = 895;
|
||||
const INTERFACESOUND_CURSORDROPOBJECT: u32 = 903;
|
||||
|
||||
/// Which half of a gesture pair a transition plays: the payload landing on the cursor (`Gain`,
|
||||
@@ -224,10 +226,15 @@ fn play_item_gesture_sounds(
|
||||
| CursorPayload::PetAction(_)
|
||||
// Mode 10 joins them: the stabled-pet grab `0x495010` calls the same generic path and
|
||||
// names no per-item kit (decision 1677).
|
||||
| CursorPayload::StablePet(_) => {
|
||||
let kit_id = match gesture {
|
||||
CursorGesture::Gain => INTERFACESOUND_CURSORGRABOBJECT,
|
||||
CursorGesture::Loss => INTERFACESOUND_CURSORDROPOBJECT,
|
||||
| CursorPayload::StablePet(_)
|
||||
// Mode 2 (1962, 1965): the money pickup AND drop both play `LOOTWINDOWCOINSOUND` — the
|
||||
// same kit 895 the purse plays on a change (`sound/money.rs`) — and never the generic drop
|
||||
// kit (wow-re `money-cursor-law.md` §2).
|
||||
| CursorPayload::Money(_) => {
|
||||
let kit_id = match (&payload, gesture) {
|
||||
(CursorPayload::Money(_), _) => LOOTWINDOWCOINSOUND,
|
||||
(_, CursorGesture::Gain) => INTERFACESOUND_CURSORGRABOBJECT,
|
||||
(_, CursorGesture::Loss) => INTERFACESOUND_CURSORDROPOBJECT,
|
||||
};
|
||||
if let Err(e) = kit::play_kit(
|
||||
&mut kits,
|
||||
|
||||
@@ -439,7 +439,8 @@ fn chat_vm() -> benilla_ui::script::UiScript {
|
||||
// dependency — its MenuBackdrop reads `TOOLTIP_DEFAULT_COLOR`.)
|
||||
for file in [
|
||||
"Interface\\FrameXML\\Fonts.xml",
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
"GameTooltip.xml",
|
||||
"Interface\\FrameXML\\UIDropDownMenu.xml",
|
||||
// The UIMenu kit is the reference's own file since 1751 window 21, so this reads both
|
||||
|
||||
@@ -0,0 +1,443 @@
|
||||
//! The dialog engine's verbs, app half (decision 1963): the feeds behind the stock
|
||||
//! `StaticPopup.lua` dialogs benilla never raised and the drains behind their buttons, each to
|
||||
//! wow-re's `staticpopup-dialog-bindings.md` (VERIFIED at the bytes unless a line says INFERRED).
|
||||
//!
|
||||
//! * **Pet trainer** — `SMSG_PET_UNLEARN_CONFIRM {guid, cost}` latches both and owes
|
||||
//! `CONFIRM_PET_UNLEARN(cost)`; `ConfirmPetUnlearn()` answers with `CMSG_PET_UNLEARN {guid}` unless
|
||||
//! the cost outruns the purse (`ERR_NOT_ENOUGH_MONEY`, nothing sent). The talent-wipe twin
|
||||
//! (`crate::ui_talent_wipe`), latch for latch, leash for leash: the trainer walking out of
|
||||
//! `INTERACT_DISTANCE` closes the question, which is what `CheckPetUntrainerDist()` polls.
|
||||
//! * **Instance boot** — every `SMSG_RAID_GROUP_ONLY {delayMs, reason}` fires an event: a positive
|
||||
//! delay arms the deadline and `INSTANCE_BOOT_START`, zero clears it and `INSTANCE_BOOT_STOP`,
|
||||
//! and only the zero leg names reason 1/2 on screen. `GetInstanceBootTimeRemaining()` reads
|
||||
//! whole seconds off the deadline; nothing clears it but a zero packet.
|
||||
//! * **Area spirit healer** — `SMSG_AREA_SPIRIT_HEALER_TIME {guid, ms}` arms the wave clock and
|
||||
//! fires `AREA_SPIRIT_HEALER_IN_RANGE` when the guid is the cached healer's; Accept sends
|
||||
//! `0x2E3` with that guid, Cancel is the cancel-aura of spell 2584 plus `_OUT_OF_RANGE`. **The
|
||||
//! cache has no writer yet**: the reference's per-frame proximity scan (20 yd to acquire, 22 to
|
||||
//! retain, `0x4923b0`) is not carved for its unit filter, so no healer is ever cached here and
|
||||
//! Accept stays silent — named in the record, not guessed at.
|
||||
//! * **Battlefield queue** — `SMSG_BATTLEFIELD_STATUS` fills one of three slots and fires
|
||||
//! `UPDATE_BATTLEFIELD_STATUS`; `AcceptBattlefieldPort(index, accept)` sends the slot's map id
|
||||
//! with the answer as one byte.
|
||||
//! * **Meeting stone** — `SMSG 0x295 {areaId, status}` stores the queued area and fires
|
||||
//! `MEETINGSTONE_CHANGED`; `CancelMeetingStoneRequest()` sends `0x293` unless in a party led by
|
||||
//! someone else (`ERR_MEETING_STONE_NOT_LEADER`). The five status messages the reference prints
|
||||
//! are not built: the carve lists the five strings but not which status byte picks which.
|
||||
|
||||
use std::time::Instant;
|
||||
|
||||
use benilla_protocol::messages::BattlefieldStatus;
|
||||
use benilla_ui::script::{ScriptValue, UiScript};
|
||||
use bevy::prelude::*;
|
||||
|
||||
use crate::net::{ClientCommand, NetCommands, ObjectStore, SelfGuid, SelfPlayer};
|
||||
use crate::ui_party::GroupState;
|
||||
use crate::ui_script::UiInput;
|
||||
use crate::ui_session::{close_npc_session_out_of_range, NpcSession};
|
||||
|
||||
/// The pet trainer's pending question: the latch (`0xc4d7b0/b4`) and the cost (`0xc4d7b8`).
|
||||
#[derive(Resource, Default)]
|
||||
pub(crate) struct PetUnlearnState {
|
||||
npc: Option<u64>,
|
||||
cost: u32,
|
||||
ask: bool,
|
||||
}
|
||||
|
||||
impl PetUnlearnState {
|
||||
/// The inbound `SMSG_PET_UNLEARN_CONFIRM`: latch and owe the dialog.
|
||||
pub(crate) fn ask(&mut self, npc: u64, cost: u32) {
|
||||
self.npc = Some(npc);
|
||||
self.cost = cost;
|
||||
self.ask = true;
|
||||
}
|
||||
|
||||
fn pending(&self) -> Option<u64> {
|
||||
self.npc
|
||||
}
|
||||
}
|
||||
|
||||
impl NpcSession for PetUnlearnState {
|
||||
fn npc(&self) -> Option<u64> {
|
||||
self.npc
|
||||
}
|
||||
fn close(&mut self) {
|
||||
self.npc = None;
|
||||
self.cost = 0;
|
||||
self.ask = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// The instance-boot clock (`[0xb4e34c]`), and what the last packet owes the UI.
|
||||
#[derive(Resource, Default)]
|
||||
pub(crate) struct InstanceBoot {
|
||||
deadline: Option<Instant>,
|
||||
/// Events owed, in arrival order (`INSTANCE_BOOT_START` / `_STOP`).
|
||||
events: Vec<&'static str>,
|
||||
/// Error lines owed (`ERR_RAID_GROUP_ONLY` / `_FULL`), the zero-delay leg's.
|
||||
errors: Vec<&'static str>,
|
||||
}
|
||||
|
||||
impl InstanceBoot {
|
||||
/// `SMSG_RAID_GROUP_ONLY`: `delay > 0` arms, else clears — and the event fires either way.
|
||||
pub(crate) fn apply(&mut self, delay_ms: u32, reason: u32, now: Instant) {
|
||||
if delay_ms > 0 {
|
||||
self.deadline = Some(now + std::time::Duration::from_millis(u64::from(delay_ms)));
|
||||
self.events.push("INSTANCE_BOOT_START");
|
||||
} else {
|
||||
self.deadline = None;
|
||||
self.events.push("INSTANCE_BOOT_STOP");
|
||||
match reason {
|
||||
1 => self.errors.push("ERR_RAID_GROUP_ONLY"),
|
||||
2 => self.errors.push("ERR_RAID_GROUP_FULL"),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Whole seconds left, 0 when idle or past — the reference's unsigned divide of a clamped
|
||||
/// millisecond remainder.
|
||||
pub(crate) fn secs(&self, now: Instant) -> u32 {
|
||||
self.deadline
|
||||
.map(|d| d.saturating_duration_since(now).as_secs())
|
||||
.map_or(0, |s| u32::try_from(s).unwrap_or(u32::MAX))
|
||||
}
|
||||
}
|
||||
|
||||
/// The current-area spirit healer (`[0xb4e330/334]`) and its wave clock (`[0xb4e338]`).
|
||||
#[derive(Resource, Default)]
|
||||
pub(crate) struct AreaSpiritHealer {
|
||||
/// The cached healer. No writer yet — see the module doc.
|
||||
healer: Option<u64>,
|
||||
deadline: Option<Instant>,
|
||||
in_range: bool,
|
||||
}
|
||||
|
||||
impl AreaSpiritHealer {
|
||||
/// `SMSG_AREA_SPIRIT_HEALER_TIME`: for the cached healer with a positive time, arm the clock
|
||||
/// (a zero-landing deadline reads as 1 ms in the reference) and owe `_IN_RANGE`.
|
||||
pub(crate) fn on_time(&mut self, healer: u64, ms: u32, now: Instant) {
|
||||
if self.healer == Some(healer) && ms > 0 {
|
||||
self.deadline = Some(now + std::time::Duration::from_millis(u64::from(ms)));
|
||||
self.in_range = true;
|
||||
}
|
||||
}
|
||||
|
||||
fn secs(&self, now: Instant) -> u32 {
|
||||
self.deadline
|
||||
.map(|d| d.saturating_duration_since(now).as_secs())
|
||||
.map_or(0, |s| u32::try_from(s).unwrap_or(u32::MAX))
|
||||
}
|
||||
}
|
||||
|
||||
/// The three battleground queue slots (`0xb6e9d0`, stride `0x20`).
|
||||
#[derive(Resource, Default)]
|
||||
pub(crate) struct BattlefieldQueue {
|
||||
slots: [Option<BattlefieldStatus>; 3],
|
||||
changed: bool,
|
||||
}
|
||||
|
||||
impl BattlefieldQueue {
|
||||
/// `SMSG_BATTLEFIELD_STATUS`: an out-of-range slot aborts the handler; a zero map clears.
|
||||
pub(crate) fn apply(&mut self, status: BattlefieldStatus) {
|
||||
let Some(slot) = self.slots.get_mut(status.slot as usize) else {
|
||||
return;
|
||||
};
|
||||
*slot = (status.map_id != 0).then_some(status);
|
||||
self.changed = true;
|
||||
}
|
||||
|
||||
/// The map id `AcceptBattlefieldPort` sends for a 1-based slot, if the slot holds a queue.
|
||||
fn map_id(&self, index: u8) -> Option<u32> {
|
||||
self.slots
|
||||
.get(usize::from(index).checked_sub(1)?)
|
||||
.and_then(|s| s.as_ref())
|
||||
.map(|s| s.map_id)
|
||||
}
|
||||
}
|
||||
|
||||
/// The meeting-stone queue (`[0xb72038]`).
|
||||
#[derive(Resource, Default)]
|
||||
pub(crate) struct MeetingStone {
|
||||
pub(crate) area: u32,
|
||||
pub(crate) status: Option<u8>,
|
||||
changed: bool,
|
||||
}
|
||||
|
||||
impl MeetingStone {
|
||||
/// `SMSG 0x295`: the area is stored unconditionally; the status picks the (unbuilt) message.
|
||||
pub(crate) fn apply(&mut self, area: u32, status: u8) {
|
||||
self.area = area;
|
||||
self.status = Some(status);
|
||||
self.changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
fn feed_dialog_verbs(
|
||||
script: Option<NonSendMut<UiScript>>,
|
||||
mut pet: ResMut<PetUnlearnState>,
|
||||
mut boot: ResMut<InstanceBoot>,
|
||||
mut spirit: ResMut<AreaSpiritHealer>,
|
||||
mut queue: ResMut<BattlefieldQueue>,
|
||||
mut stone: ResMut<MeetingStone>,
|
||||
mut sink: crate::ui_action::MessageSink,
|
||||
) {
|
||||
let Some(mut script) = script else {
|
||||
return;
|
||||
};
|
||||
let now = Instant::now();
|
||||
|
||||
script.set_pet_untrainer_pending(pet.pending().is_some());
|
||||
if pet.ask {
|
||||
pet.ask = false;
|
||||
script.fire_event(
|
||||
"CONFIRM_PET_UNLEARN",
|
||||
vec![ScriptValue::Int(i64::from(pet.cost))],
|
||||
);
|
||||
}
|
||||
|
||||
script.set_instance_boot_secs(boot.secs(now));
|
||||
for event in std::mem::take(&mut boot.events) {
|
||||
script.fire_event(event, vec![]);
|
||||
}
|
||||
let lines: Vec<_> = std::mem::take(&mut boot.errors)
|
||||
.into_iter()
|
||||
.filter_map(|key| crate::ui_action::keyed_line(&script, key))
|
||||
.collect();
|
||||
if !lines.is_empty() {
|
||||
crate::ui_action::show_messages(&mut script, &mut sink, "ui_dialog_verbs", lines);
|
||||
}
|
||||
|
||||
let secs = spirit.secs(now);
|
||||
script.set_area_spirit_healer(spirit.healer.is_some(), secs);
|
||||
if std::mem::take(&mut spirit.in_range) {
|
||||
script.fire_event("AREA_SPIRIT_HEALER_IN_RANGE", vec![]);
|
||||
}
|
||||
|
||||
if std::mem::take(&mut queue.changed) {
|
||||
script.fire_event("UPDATE_BATTLEFIELD_STATUS", vec![]);
|
||||
}
|
||||
if std::mem::take(&mut stone.changed) {
|
||||
script.fire_event("MEETINGSTONE_CHANGED", vec![]);
|
||||
}
|
||||
}
|
||||
|
||||
/// The pet trainer's confirm and the spirit healer's accept — the two drains over a latch.
|
||||
fn drain_latch_verbs(
|
||||
script: Option<NonSendMut<UiScript>>,
|
||||
pet: Res<PetUnlearnState>,
|
||||
spirit: Res<AreaSpiritHealer>,
|
||||
self_q: Query<&ObjectStore, With<SelfPlayer>>,
|
||||
commands: Res<NetCommands>,
|
||||
mut sink: crate::ui_action::MessageSink,
|
||||
) {
|
||||
let Some(mut script) = script else {
|
||||
return;
|
||||
};
|
||||
|
||||
// ConfirmPetUnlearn: the latch, then the money gate — `cost > coinage` shows
|
||||
// ERR_NOT_ENOUGH_MONEY and sends nothing; otherwise `0x2F0` with the latched guid.
|
||||
let confirms = script.take_pet_unlearn_confirms();
|
||||
if confirms > 0 {
|
||||
if let Some(npc) = pet.pending() {
|
||||
let money = self_q
|
||||
.single()
|
||||
.ok()
|
||||
.and_then(|store| store.0.player_money())
|
||||
.unwrap_or(0);
|
||||
if pet.cost > money {
|
||||
if let Some(line) = crate::ui_action::keyed_line(&script, "ERR_NOT_ENOUGH_MONEY") {
|
||||
crate::ui_action::show_messages(
|
||||
&mut script,
|
||||
&mut sink,
|
||||
"ui_dialog_verbs",
|
||||
[line],
|
||||
);
|
||||
}
|
||||
} else {
|
||||
for _ in 0..confirms {
|
||||
let _ = commands.0.send(ClientCommand::PetUnlearn { trainer: npc });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AcceptAreaSpiritHeal: the cached healer's guid (the binding was silent without one).
|
||||
let accepts = script.take_area_spirit_accepts();
|
||||
if let Some(healer) = spirit.healer {
|
||||
for _ in 0..accepts {
|
||||
let _ = commands
|
||||
.0
|
||||
.send(ClientCommand::AreaSpiritHealerQueue { healer });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The battleground port and the meeting-stone leave — the two drains over a queue.
|
||||
fn drain_queue_verbs(
|
||||
script: Option<NonSendMut<UiScript>>,
|
||||
queue: Res<BattlefieldQueue>,
|
||||
group: Option<Res<GroupState>>,
|
||||
self_guid: Res<SelfGuid>,
|
||||
commands: Res<NetCommands>,
|
||||
mut sink: crate::ui_action::MessageSink,
|
||||
) {
|
||||
let Some(mut script) = script else {
|
||||
return;
|
||||
};
|
||||
|
||||
// AcceptBattlefieldPort: the slot's map id and the one-byte answer.
|
||||
for (index, accept) in script.take_battlefield_port_requests() {
|
||||
if let Some(map_id) = queue.map_id(index) {
|
||||
let _ = commands
|
||||
.0
|
||||
.send(ClientCommand::BattlefieldPort { map_id, accept });
|
||||
}
|
||||
}
|
||||
|
||||
// CancelMeetingStoneRequest: in a party and not its leader → ERR_MEETING_STONE_NOT_LEADER;
|
||||
// otherwise `0x293`, whatever is or is not queued.
|
||||
let cancels = script.take_meeting_stone_cancels();
|
||||
if cancels > 0 {
|
||||
let not_leader = group
|
||||
.as_deref()
|
||||
.is_some_and(|g| g.in_group && Some(g.leader) != self_guid.0);
|
||||
if not_leader {
|
||||
if let Some(line) =
|
||||
crate::ui_action::keyed_line(&script, "ERR_MEETING_STONE_NOT_LEADER")
|
||||
{
|
||||
crate::ui_action::show_messages(&mut script, &mut sink, "ui_dialog_verbs", [line]);
|
||||
}
|
||||
} else {
|
||||
for _ in 0..cancels {
|
||||
let _ = commands.0.send(ClientCommand::MeetingStoneLeave);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct UiDialogVerbsPlugin;
|
||||
|
||||
impl Plugin for UiDialogVerbsPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.init_resource::<PetUnlearnState>()
|
||||
.init_resource::<InstanceBoot>()
|
||||
.init_resource::<AreaSpiritHealer>()
|
||||
.init_resource::<BattlefieldQueue>()
|
||||
.init_resource::<MeetingStone>()
|
||||
.add_systems(
|
||||
Update,
|
||||
(
|
||||
close_npc_session_out_of_range::<PetUnlearnState>.before(feed_dialog_verbs),
|
||||
feed_dialog_verbs.before(UiInput),
|
||||
drain_latch_verbs.after(UiInput),
|
||||
drain_queue_verbs.after(UiInput),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// The area spirit healer's aura, `0xA18` — `CancelAreaSpiritHeal`'s one spell (the engine's
|
||||
/// `dialog_verbs::AREA_SPIRIT_HEALER_SPELL`), read here only to check its flags against the data.
|
||||
#[cfg(test)]
|
||||
const AREA_SPIRIT_HEALER_SPELL: u32 = 2584;
|
||||
|
||||
/// The generic cancel-aura routine's refusal (`0x6e7040`, wow-re `staticpopup-dialog-bindings.md`
|
||||
/// §6): it returns without sending when the spell's `AttributesEx` has bit 13 set and bit 2
|
||||
/// clear **and** `0x5ee290(player)` holds. Whether the third leg ever matters for spell 2584 is
|
||||
/// decided by the first two, read off the shipped Spell.dbc in [`tests::spell_2584_never_trips_the_cancel_gate`].
|
||||
#[cfg(test)]
|
||||
fn cancel_gate_could_apply(attributes_ex: u32) -> bool {
|
||||
attributes_ex & 0x2000 != 0 && attributes_ex & 0x4 == 0
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
/// Spell 2584's flags off the shipped Spell.dbc: the cancel-aura gate's first two legs.
|
||||
#[test]
|
||||
fn spell_2584_never_trips_the_cancel_gate() {
|
||||
let data = benilla_formats::wow_data_or_skip!();
|
||||
let mut chain = benilla_formats::open_chain(&data).expect("open chain");
|
||||
let catalog = benilla_formats::load_spell_catalog(&mut chain).expect("Spell.dbc");
|
||||
let ex = catalog
|
||||
.get(AREA_SPIRIT_HEALER_SPELL)
|
||||
.map(|d| d.attributes_ex)
|
||||
.expect("spell 2584 in Spell.dbc");
|
||||
assert!(
|
||||
!cancel_gate_could_apply(ex),
|
||||
"spell 2584 AttributesEx = {ex:#x}: the gate's third leg would decide, and it is uncarved"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_boot_clock_arms_on_a_delay_and_clears_on_zero_with_the_reason() {
|
||||
let mut boot = InstanceBoot::default();
|
||||
let now = Instant::now();
|
||||
boot.apply(60_500, 0, now);
|
||||
assert_eq!(boot.events, vec!["INSTANCE_BOOT_START"]);
|
||||
assert_eq!(boot.secs(now), 60, "whole seconds, truncated");
|
||||
boot.apply(0, 1, now);
|
||||
assert_eq!(
|
||||
boot.events,
|
||||
vec!["INSTANCE_BOOT_START", "INSTANCE_BOOT_STOP"]
|
||||
);
|
||||
assert_eq!(boot.errors, vec!["ERR_RAID_GROUP_ONLY"]);
|
||||
assert_eq!(boot.secs(now), 0);
|
||||
boot.apply(0, 7, now);
|
||||
assert_eq!(boot.errors.len(), 1, "a reason outside 1/2 names nothing");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_spirit_healer_clock_only_arms_for_the_cached_healer() {
|
||||
let mut spirit = AreaSpiritHealer::default();
|
||||
let now = Instant::now();
|
||||
spirit.on_time(0x77, 30_000, now);
|
||||
assert!(!spirit.in_range, "no healer cached: the packet is ignored");
|
||||
spirit.healer = Some(0x77);
|
||||
spirit.on_time(0x78, 30_000, now);
|
||||
assert!(!spirit.in_range, "another guid: ignored");
|
||||
spirit.on_time(0x77, 0, now);
|
||||
assert!(!spirit.in_range, "a zero time: ignored");
|
||||
spirit.on_time(0x77, 30_000, now);
|
||||
assert!(spirit.in_range);
|
||||
assert_eq!(spirit.secs(now), 30);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_queue_keeps_three_slots_and_answers_a_port_by_map() {
|
||||
let mut q = BattlefieldQueue::default();
|
||||
let status = |slot, map_id| BattlefieldStatus {
|
||||
slot,
|
||||
map_id,
|
||||
bracket: 0,
|
||||
unknown: 0,
|
||||
status: 2,
|
||||
time_ms: Some(0),
|
||||
in_progress: None,
|
||||
};
|
||||
q.apply(status(1, 489));
|
||||
q.apply(status(5, 30));
|
||||
assert_eq!(
|
||||
q.map_id(2),
|
||||
Some(489),
|
||||
"1-based from Lua, 0-based on the wire"
|
||||
);
|
||||
assert_eq!(q.map_id(1), None);
|
||||
assert_eq!(q.map_id(4), None);
|
||||
q.apply(status(1, 0));
|
||||
assert_eq!(q.map_id(2), None, "a zero map clears the slot");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_pet_question_latches_and_closes_like_the_talent_wipe() {
|
||||
let mut pet = PetUnlearnState::default();
|
||||
pet.ask(0x2b, 10_000);
|
||||
assert_eq!(pet.pending(), Some(0x2b));
|
||||
assert!(pet.ask);
|
||||
pet.close();
|
||||
assert_eq!(pet.pending(), None);
|
||||
assert_eq!(pet.cost, 0);
|
||||
}
|
||||
}
|
||||
@@ -1174,7 +1174,8 @@ mod bind_confirm_tests {
|
||||
// reader that joins `assets/ui` cannot resolve one (1838, 1887, 1888).
|
||||
for file in [
|
||||
"Interface\\FrameXML\\Fonts.xml",
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
"UiPanels.xml",
|
||||
"Interface\\FrameXML\\GlobalStrings.lua",
|
||||
"Interface\\FrameXML\\BasicControls.xml",
|
||||
|
||||
@@ -189,6 +189,14 @@ fn drain_logout(
|
||||
info!("logout: force quit");
|
||||
exit.write(AppExit::Success);
|
||||
}
|
||||
// `CMSG_PLAYER_LOGOUT`, the forced flavour: the dispatcher's own gate is a live
|
||||
// in-world session, and nothing happens without one (decision 1963).
|
||||
SessionRequest::ForceLogout => {
|
||||
if self_guid.0.is_some() {
|
||||
info!("logout: forced");
|
||||
let _ = commands.0.send(ClientCommand::ForceLogout);
|
||||
}
|
||||
}
|
||||
// Not this module's business beyond routing: the rebuild itself is
|
||||
// [`crate::ui_script::run_pending_reload`]'s, at the top of the next frame.
|
||||
SessionRequest::ReloadUi => reload.0 = true,
|
||||
|
||||
@@ -49,14 +49,6 @@ use crate::ui_script::UiInput;
|
||||
/// The icon *order* is the client's own and it is not the numeric one — `_05, _06, _03, _04, _01,
|
||||
/// _02` as the amount climbs. What each of the six pieces of art depicts is not claimed here; the
|
||||
/// ladder is byte-derived and the art is whatever the reference picks at that step.
|
||||
const COIN_ICONS: [(u32, &str); 6] = [
|
||||
(10, "Interface\\Icons\\INV_Misc_Coin_05"),
|
||||
(100, "Interface\\Icons\\INV_Misc_Coin_06"),
|
||||
(1_000, "Interface\\Icons\\INV_Misc_Coin_03"),
|
||||
(10_000, "Interface\\Icons\\INV_Misc_Coin_04"),
|
||||
(100_000, "Interface\\Icons\\INV_Misc_Coin_01"),
|
||||
(u32::MAX, "Interface\\Icons\\INV_Misc_Coin_02"),
|
||||
];
|
||||
/// `item_template.bonding == BIND_WHEN_PICKED_UP` — the first of the two conjuncts that defer a
|
||||
/// loot take behind the LOOT_BIND confirm (VERIFIED vmangos `ItemPrototype.h`'s `ItemBondingType`:
|
||||
/// `NO_BIND` 0, `BIND_WHEN_PICKED_UP` 1, `BIND_WHEN_EQUIPPED` 2, `BIND_WHEN_USE` 3, `QUEST_ITEM` 4;
|
||||
@@ -677,14 +669,10 @@ fn format_money(copper: u32) -> String {
|
||||
parts.join(" ")
|
||||
}
|
||||
|
||||
/// The coin-pile icon for a copper amount — the reference's six-step ladder, see [`COIN_ICONS`].
|
||||
/// `u32::MAX` is the last step's bound, so the `map_or` fallback is unreachable and is there only
|
||||
/// because the table is data rather than a match.
|
||||
/// The coin-pile icon for a copper amount — the reference's six-step ladder, one table for the
|
||||
/// loot slot, `GetCoinIcon` and the money cursor's bitmap (`benilla_ui::script::coin_icon`, 1965).
|
||||
fn coin_icon(copper: u32) -> &'static str {
|
||||
COIN_ICONS
|
||||
.iter()
|
||||
.find(|(below, _)| copper < *below)
|
||||
.map_or(COIN_ICONS[5].1, |(_, icon)| icon)
|
||||
benilla_ui::script::coin_icon(i64::from(copper))
|
||||
}
|
||||
|
||||
/// Resolve one wire [`LootItem`] into the Lua-facing [`LootRow`]: the icon comes straight from the
|
||||
|
||||
@@ -22,7 +22,8 @@ fn shipped_action_bar_drives_end_to_end() {
|
||||
super::test_ui::load_ui(&s, "Interface\\FrameXML\\TextStatusBar.xml");
|
||||
super::test_ui::load_ui(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
super::test_ui::load_ui(&s, "UIParent.xml");
|
||||
super::test_ui::load_ui(&s, "MoneyFrame.xml");
|
||||
super::test_ui::load_ui(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
super::test_ui::load_ui(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
super::test_ui::load_ui(&s, "GameTooltip.xml");
|
||||
super::test_ui::load_ui(&s, "Interface\\FrameXML\\GlobalStrings.lua");
|
||||
let frames = super::test_ui::load_ui(&s, "Interface\\FrameXML\\MainMenuBar.xml")
|
||||
@@ -187,7 +188,8 @@ fn load_action_bar(s: &UiScript) {
|
||||
super::test_ui::load_ui(s, "UIParent.xml");
|
||||
super::test_ui::load_ui(s, "Interface\\FrameXML\\GlobalStrings.lua");
|
||||
super::test_ui::load_ui(s, "Interface\\FrameXML\\MainMenuBar.xml");
|
||||
super::test_ui::load_ui(s, "MoneyFrame.xml");
|
||||
super::test_ui::load_ui(s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
super::test_ui::load_ui(s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
super::test_ui::load_ui(s, "GameTooltip.xml");
|
||||
super::test_ui::load_ui(s, "Interface\\FrameXML\\ActionBarFrame.xml");
|
||||
super::test_ui::load_ui(s, "Interface\\FrameXML\\BonusActionBarFrame.xml");
|
||||
@@ -836,8 +838,6 @@ fn shipped_bag_frame_drives_end_to_end() {
|
||||
load_ui(&s, "UIParent.xml");
|
||||
load_ui(&s, "Interface\\FrameXML\\GlobalStrings.lua");
|
||||
load_ui(&s, "Interface\\FrameXML\\MainMenuBar.xml");
|
||||
load_ui(&s, "MoneyFrame.xml");
|
||||
load_ui(&s, "GameTooltip.xml");
|
||||
load_ui(&s, "Interface\\FrameXML\\ActionBarFrame.xml");
|
||||
load_ui(&s, "Interface\\FrameXML\\BonusActionBarFrame.xml");
|
||||
}
|
||||
@@ -1357,7 +1357,8 @@ fn the_main_bar_pages_and_a_bonus_page_still_outranks_it() {
|
||||
"Interface\\FrameXML\\TextStatusBar.xml",
|
||||
"Interface\\FrameXML\\GlobalStrings.lua",
|
||||
"Interface\\FrameXML\\MainMenuBar.xml",
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
"GameTooltip.xml",
|
||||
"Interface\\FrameXML\\ActionBarFrame.xml",
|
||||
"Interface\\FrameXML\\BonusActionBarFrame.xml",
|
||||
@@ -1525,7 +1526,8 @@ fn bonus_bar_slides_up_with_sound_and_down_without() {
|
||||
super::test_ui::load_ui(&s, "UIParent.xml");
|
||||
super::test_ui::load_ui(&s, "Interface\\FrameXML\\GlobalStrings.lua");
|
||||
super::test_ui::load_ui(&s, "Interface\\FrameXML\\MainMenuBar.xml");
|
||||
super::test_ui::load_ui(&s, "MoneyFrame.xml");
|
||||
super::test_ui::load_ui(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
super::test_ui::load_ui(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
super::test_ui::load_ui(&s, "GameTooltip.xml");
|
||||
super::test_ui::load_ui(&s, "Interface\\FrameXML\\ActionBarFrame.xml");
|
||||
super::test_ui::load_ui(&s, "Interface\\FrameXML\\BonusActionBarFrame.xml");
|
||||
@@ -1746,7 +1748,8 @@ fn bonus_bar_turnaround_continues_from_position() {
|
||||
super::test_ui::load_ui(&s, "UIParent.xml");
|
||||
super::test_ui::load_ui(&s, "Interface\\FrameXML\\GlobalStrings.lua");
|
||||
super::test_ui::load_ui(&s, "Interface\\FrameXML\\MainMenuBar.xml");
|
||||
super::test_ui::load_ui(&s, "MoneyFrame.xml");
|
||||
super::test_ui::load_ui(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
super::test_ui::load_ui(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
super::test_ui::load_ui(&s, "GameTooltip.xml");
|
||||
super::test_ui::load_ui(&s, "Interface\\FrameXML\\ActionBarFrame.xml");
|
||||
super::test_ui::load_ui(&s, "Interface\\FrameXML\\BonusActionBarFrame.xml");
|
||||
@@ -1827,7 +1830,8 @@ fn the_page_arrows_do_not_steal_each_other_s_clicks() {
|
||||
super::test_ui::load_ui(&s, "UIParent.xml");
|
||||
super::test_ui::load_ui(&s, "Interface\\FrameXML\\GlobalStrings.lua");
|
||||
super::test_ui::load_ui(&s, "Interface\\FrameXML\\MainMenuBar.xml");
|
||||
super::test_ui::load_ui(&s, "MoneyFrame.xml");
|
||||
super::test_ui::load_ui(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
super::test_ui::load_ui(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
super::test_ui::load_ui(&s, "GameTooltip.xml");
|
||||
super::test_ui::load_ui(&s, "Interface\\FrameXML\\ActionBarFrame.xml");
|
||||
super::test_ui::load_ui(&s, "Interface\\FrameXML\\BonusActionBarFrame.xml");
|
||||
|
||||
@@ -12,7 +12,8 @@ fn setup() -> UiScript {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.xml");
|
||||
|
||||
@@ -30,10 +30,12 @@ fn harness() -> UiScript {
|
||||
// `GameTooltip`, which the reference's BuffButton_Update indexes on EVERY repaint to ask
|
||||
// `IsOwned(this)` (BuffFrame.lua l.104) — not just on hover. Ours guarded it; the reference
|
||||
// does not, so a session without the tooltip loses the whole repaint.
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
// `SecondsToTimeAbbrev`, which 1.12 keeps in UIParent.lua and so do we since window 18.
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.xml");
|
||||
|
||||
@@ -392,7 +392,8 @@ fn managed_positions_track_the_bottom_bar_stack() {
|
||||
load_xml(&s, "Interface\\FrameXML\\GlobalStrings.lua");
|
||||
load_xml(&s, "Interface\\FrameXML\\BasicControls.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\ChatFrame.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "GameTooltip.xml"); // the dropdown kit's MenuBackdrop reads TOOLTIP_DEFAULT_COLOR
|
||||
load_xml(&s, "Interface\\FrameXML\\UIDropDownMenu.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\UIPanelTemplates.lua");
|
||||
|
||||
@@ -1201,7 +1201,8 @@ fn an_addons_tab_click_selects_through_the_generic_entry_point() {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
// The reference's `PanelTemplates_SelectTab` ends with `if GameTooltip:IsOwned(tab)` —
|
||||
// an arm our deleted copy omitted ("our tabs set no tooltip"), so selecting a tab now needs
|
||||
|
||||
@@ -40,7 +40,8 @@ fn chat_with_menu() -> UiScript {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
for file in [
|
||||
"Interface\\FrameXML\\Fonts.xml",
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
"UiPanels.xml",
|
||||
"UIParent.xml",
|
||||
"GameTooltip.xml",
|
||||
|
||||
@@ -18,7 +18,8 @@ fn chat_ui() -> UiScript {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
for file in [
|
||||
"Interface\\FrameXML\\Fonts.xml",
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
"UiPanels.xml",
|
||||
"UIParent.xml",
|
||||
"GameTooltip.xml",
|
||||
|
||||
@@ -19,7 +19,8 @@ fn chat_frame() -> UiScript {
|
||||
// `benilla.toc` already orders both ahead of ChatFrame.xml (l.60/64 vs l.399); the harness
|
||||
// says so too, rather than a guard that would hide a real ordering fault. (The tooltip file is
|
||||
// the dropdown kit's own dependency — its MenuBackdrop reads `TOOLTIP_DEFAULT_COLOR`.)
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\UIDropDownMenu.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\UIMenu.xml"); // the kit the chat menus build from
|
||||
@@ -992,7 +993,8 @@ fn the_chat_menu_builds_its_rows_on_the_references_kit() {
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
for f in [
|
||||
"Interface\\FrameXML\\Fonts.xml",
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
"UiPanels.xml",
|
||||
r"Interface\FrameXML\UIPanelTemplates.lua",
|
||||
r"Interface\FrameXML\UIPanelTemplates.xml",
|
||||
|
||||
@@ -24,7 +24,8 @@ fn ui_with_the_cinematic_frame() -> UiScript {
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "UIParent.xml");
|
||||
load_xml(&s, "MoneyFrame.xml"); // StaticPopup's money row, or UiPanels errors at load
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua"); // StaticPopup's money row, or UiPanels errors at load
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.xml");
|
||||
|
||||
@@ -18,7 +18,8 @@ fn picker() -> UiScript {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
for file in [
|
||||
"Interface\\FrameXML\\Fonts.xml",
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
"UiPanels.xml",
|
||||
"UIParent.xml",
|
||||
"GameTooltip.xml",
|
||||
|
||||
@@ -18,11 +18,35 @@ use super::test_ui::load_ui as load_xml;
|
||||
fn load_combat_text() -> UiScript {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "Interface\\FrameXML\\GlobalStrings.lua"); // ENTERING_COMBAT & co.
|
||||
load_xml(&s, "Interface\\FrameXML\\BasicControls.xml"); // TEXT()
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "UIParent.xml");
|
||||
load_xml(&s, "CombatText.xml");
|
||||
s.run("SHOW_COMBAT_TEXT = \"1\"; CombatText_UpdateDisplayedMessages()")
|
||||
.unwrap();
|
||||
// The reference's own addon, LoadOnDemand off the chain, loaded the way the options window
|
||||
// loads it (1964).
|
||||
super::test_ui::seat_chain_addon(&mut s, "Blizzard_CombatText");
|
||||
// The family's saved-variable defaults are the options window's file-scope block (the
|
||||
// reference's UIOptionsFrame.lua l.135-152, carried by our OptionsFrame.xml since 1964); a
|
||||
// harness without that window seats the same block by hand, the master switched on.
|
||||
s.run(
|
||||
r#"SHOW_COMBAT_TEXT = "1"
|
||||
COMBAT_TEXT_SHOW_LOW_HEALTH_MANA = "1"
|
||||
COMBAT_TEXT_SHOW_AURAS = "1"
|
||||
COMBAT_TEXT_SHOW_AURA_FADE = "0"
|
||||
COMBAT_TEXT_SHOW_COMBAT_STATE = "1"
|
||||
COMBAT_TEXT_SHOW_DODGE_PARRY_MISS = "0"
|
||||
COMBAT_TEXT_SHOW_RESISTANCES = "0"
|
||||
COMBAT_TEXT_SHOW_REPUTATION = "0"
|
||||
COMBAT_TEXT_SHOW_REACTIVES = "0"
|
||||
COMBAT_TEXT_SHOW_FRIENDLY_NAMES = "0"
|
||||
COMBAT_TEXT_SHOW_COMBO_POINTS = "0"
|
||||
COMBAT_TEXT_SHOW_MANA = "0"
|
||||
COMBAT_TEXT_FLOAT_MODE = "1"
|
||||
COMBAT_TEXT_SHOW_HONOR_GAINED = "1"
|
||||
UIParentLoadAddOn("Blizzard_CombatText")
|
||||
CombatText_UpdateDisplayedMessages()"#,
|
||||
)
|
||||
.unwrap();
|
||||
s.resolve();
|
||||
assert!(s.errors().is_empty(), "script errors: {:?}", s.errors());
|
||||
s
|
||||
@@ -33,6 +57,7 @@ fn load_combat_text() -> UiScript {
|
||||
/// shows nothing.
|
||||
#[test]
|
||||
fn combat_text_damage_scrolls_and_expires() {
|
||||
let _data = benilla_formats::wow_data_or_skip!();
|
||||
let mut s = load_combat_text();
|
||||
|
||||
// All 20 pool strings start hidden.
|
||||
@@ -113,6 +138,7 @@ fn combat_text_damage_scrolls_and_expires() {
|
||||
/// scrolls away from the seat).
|
||||
#[test]
|
||||
fn combat_text_crit_pops_and_parks() {
|
||||
let _data = benilla_formats::wow_data_or_skip!();
|
||||
let mut s = load_combat_text();
|
||||
s.fire_event(
|
||||
"COMBAT_TEXT_UPDATE",
|
||||
@@ -163,6 +189,7 @@ fn extracted_text_height(s: &mut UiScript, text: &str) -> Option<f32> {
|
||||
/// paints "Health Low" once and re-arms only after recovering above the threshold.
|
||||
#[test]
|
||||
fn combat_text_state_and_low_health_triggers() {
|
||||
let _data = benilla_formats::wow_data_or_skip!();
|
||||
let mut s = load_combat_text();
|
||||
s.fire_event("PLAYER_REGEN_DISABLED", vec![]);
|
||||
let ok: bool = s
|
||||
@@ -231,6 +258,7 @@ fn combat_text_state_and_low_health_triggers() {
|
||||
/// so the ref constants stand verbatim.
|
||||
#[test]
|
||||
fn combat_text_crit_peak_is_uncapped() {
|
||||
let _data = benilla_formats::wow_data_or_skip!();
|
||||
let mut s = load_combat_text();
|
||||
let ok: bool = s
|
||||
.eval("return COMBAT_TEXT_LOCATIONS.startY == 384 and COMBAT_TEXT_LOCATIONS.endY == 609")
|
||||
@@ -263,6 +291,7 @@ fn combat_text_crit_peak_is_uncapped() {
|
||||
/// something would otherwise have.
|
||||
#[test]
|
||||
fn combat_text_master_toggle_unregisters() {
|
||||
let _data = benilla_formats::wow_data_or_skip!();
|
||||
let mut s = load_combat_text();
|
||||
s.fire_event(
|
||||
"COMBAT_TEXT_UPDATE",
|
||||
@@ -281,6 +310,10 @@ fn combat_text_master_toggle_unregisters() {
|
||||
s.run("SHOW_COMBAT_TEXT = \"0\"; CombatText_UpdateDisplayedMessages()")
|
||||
.unwrap();
|
||||
s.run("CombatText_ClearAnimationList()").unwrap();
|
||||
// The stock `CombatText_ClearAnimationList` hides the strings and leaves the list to the
|
||||
// ticker (our transcription had emptied it): "nothing paints" is the list not growing and no
|
||||
// string on screen.
|
||||
let before: i64 = s.eval("return getn(COMBAT_TEXT_TO_ANIMATE)").unwrap();
|
||||
s.fire_event(
|
||||
"COMBAT_TEXT_UPDATE",
|
||||
vec![
|
||||
@@ -289,7 +322,9 @@ fn combat_text_master_toggle_unregisters() {
|
||||
],
|
||||
);
|
||||
let none: bool = s
|
||||
.eval("return getn(COMBAT_TEXT_TO_ANIMATE) == 0 and CombatText1:IsShown() == nil")
|
||||
.eval(&format!(
|
||||
"return getn(COMBAT_TEXT_TO_ANIMATE) == {before} and CombatText1:IsShown() == nil"
|
||||
))
|
||||
.unwrap();
|
||||
assert!(none, "disabled: nothing paints ({:?})", s.errors());
|
||||
assert!(s.errors().is_empty(), "script errors: {:?}", s.errors());
|
||||
|
||||
@@ -20,12 +20,14 @@ fn load_combo_frame() -> UiScript {
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "UIParent.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\UIDropDownMenu.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\BasicControls.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\UnitPopup.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.xml");
|
||||
|
||||
@@ -11,7 +11,8 @@ fn setup() -> UiScript {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.xml");
|
||||
|
||||
@@ -57,7 +57,8 @@ fn setup() -> UiScript {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.xml");
|
||||
|
||||
@@ -49,7 +49,6 @@ const ROOM_UI: &[&str] = &[
|
||||
"Interface\\FrameXML\\UIPanelTemplates.xml",
|
||||
"UiPanels.xml",
|
||||
"Interface\\FrameXML\\LocaleProperties.lua",
|
||||
"Interface\\FrameXML\\StaticPopup.xml",
|
||||
"Interface\\FrameXML\\FloatingChatFrame.xml",
|
||||
];
|
||||
|
||||
@@ -60,7 +59,8 @@ const ROOM_UI: &[&str] = &[
|
||||
fn load_room(s: &UiScript) {
|
||||
for file in [
|
||||
"Interface\\FrameXML\\Fonts.xml",
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
"UiPanels.xml",
|
||||
r"Interface\FrameXML\UIPanelTemplates.lua",
|
||||
r"Interface\FrameXML\UIPanelTemplates.xml",
|
||||
@@ -124,7 +124,6 @@ fn shown_paper_doll() -> UiScript {
|
||||
"Interface\\FrameXML\\UIPanelTemplates.xml",
|
||||
"UiPanels.xml",
|
||||
"Interface\\FrameXML\\LocaleProperties.lua",
|
||||
"Interface\\FrameXML\\StaticPopup.xml",
|
||||
"Interface\\FrameXML\\FloatingChatFrame.xml",
|
||||
] {
|
||||
super::test_ui::load_ui_strict(&s, file);
|
||||
|
||||
@@ -22,7 +22,8 @@ fn load_dropdown_kit(s: &UiScript) {
|
||||
for file in [
|
||||
"Interface\\FrameXML\\Fonts.xml",
|
||||
"UIParent.xml",
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
"GameTooltip.xml",
|
||||
"Interface\\FrameXML\\UIDropDownMenu.xml",
|
||||
] {
|
||||
|
||||
@@ -11,7 +11,8 @@ fn setup() -> UiScript {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.xml");
|
||||
|
||||
@@ -16,7 +16,8 @@ fn harness() -> UiScript {
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
for f in [
|
||||
"Interface\\FrameXML\\Fonts.xml",
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
"UiPanels.xml",
|
||||
r"Interface\FrameXML\UIPanelTemplates.lua",
|
||||
r"Interface\FrameXML\UIPanelTemplates.xml",
|
||||
@@ -392,7 +393,8 @@ fn the_quest_tracker_stacks_below_the_durability_guy() {
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
for f in [
|
||||
"Interface\\FrameXML\\Fonts.xml",
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
"Interface\\FrameXML\\GlobalStrings.lua",
|
||||
"UiPanels.xml",
|
||||
r"Interface\FrameXML\UIPanelTemplates.lua",
|
||||
|
||||
@@ -16,7 +16,8 @@ fn setup() -> UiScript {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.xml");
|
||||
|
||||
@@ -110,7 +110,8 @@ fn an_error_toast_draws_over_an_open_panel_window() {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\GlobalStrings.lua");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
|
||||
@@ -71,6 +71,9 @@ fn escape_closes_bag_and_panel_releases_loot_and_clears_cursor() {
|
||||
// `PartyFrame.xml` is needed at LOAD (MAX_PARTY_MEMBERS) and skipping it fails loudly, but
|
||||
// `ItemButtonTemplate` is only a warning and skipping it fails silently.
|
||||
for file in super::test_ui::LOOT_UI {
|
||||
if BAG_UI.contains(file) {
|
||||
continue; // a file loads once — the bag chain above carried it
|
||||
}
|
||||
load_xml(&s, file);
|
||||
}
|
||||
load_xml(&s, "Interface\\FrameXML\\LootFrame.xml");
|
||||
@@ -156,7 +159,6 @@ fn escape_is_consumed_by_a_focused_editbox_and_leaves_windows_open() {
|
||||
load_xml(&s, "Interface\\FrameXML\\UIPanelTemplates.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\LocaleProperties.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\StaticPopup.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\FloatingChatFrame.xml");
|
||||
s.set_money(0);
|
||||
s.set_container(0, Some(one_item_backpack()));
|
||||
@@ -189,7 +191,8 @@ fn escape_closes_the_options_window_before_opening_the_menu() {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.xml");
|
||||
@@ -258,7 +261,6 @@ fn escape_closes_an_open_stack_split_frame() {
|
||||
load_xml(&s, "Interface\\FrameXML\\UIPanelTemplates.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\LocaleProperties.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\StaticPopup.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\FloatingChatFrame.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\StackSplitFrame.xml");
|
||||
s.set_money(0);
|
||||
@@ -503,7 +505,8 @@ fn an_addon_frame_registered_in_uispecialframes_closes_on_escape() {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.xml");
|
||||
|
||||
@@ -17,7 +17,8 @@ fn exp_bar_harness() -> UiScript {
|
||||
for file in [
|
||||
"Interface\\FrameXML\\Fonts.xml",
|
||||
"UIParent.xml",
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
"GameTooltip.xml",
|
||||
"Interface\\FrameXML\\TextStatusBar.lua",
|
||||
"Interface\\FrameXML\\TextStatusBar.xml",
|
||||
|
||||
@@ -1163,6 +1163,9 @@ pub(super) fn drive_script(
|
||||
CursorPayload::PetAction(p) => p.texture,
|
||||
CursorPayload::StablePet(p) => Some(p.texture),
|
||||
CursorPayload::Merchant(m) => m.texture,
|
||||
CursorPayload::Money(m) => {
|
||||
Some(benilla_ui::script::coin_icon(i64::from(m.copper)).to_string())
|
||||
}
|
||||
});
|
||||
if let (Some(texture), Some(pos)) = (texture, window.cursor_position()) {
|
||||
if let Some(handle) = assets
|
||||
|
||||
@@ -16,7 +16,8 @@ use super::test_ui::load_ui as load_xml;
|
||||
fn an_idle_fade_driver_parks_itself_off_the_tick() {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.xml");
|
||||
@@ -59,7 +60,8 @@ fn an_idle_fade_driver_parks_itself_off_the_tick() {
|
||||
fn a_started_fade_still_ramps_and_the_driver_reparks_after() {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.xml");
|
||||
@@ -124,7 +126,8 @@ fn a_started_fade_still_ramps_and_the_driver_reparks_after() {
|
||||
fn a_flash_alternates_then_stops_and_the_driver_reparks() {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.xml");
|
||||
|
||||
@@ -185,13 +185,6 @@ const KNOWN: &[Known] = &[
|
||||
// the reference inherits `TextStatusBar` — true of our transcription, and no longer true of
|
||||
// anything. `TargetFrame.xml` is the reference's own now and its ToT bars inherit
|
||||
// `TextStatusBar` like every other unit bar. The gate found them itself, which is its job.
|
||||
Known {
|
||||
frame: "TradePlayerItem7",
|
||||
flag: Flag::Mouse,
|
||||
why:
|
||||
"the enchant slot: ours inherits BenillaTradeEnchantItemTemplate, the reference's the \
|
||||
ordinary PlayerTradeItemTemplate. A structural difference in our own trade window.",
|
||||
},
|
||||
Known {
|
||||
frame: "WorldStateAlwaysUpFrame",
|
||||
flag: Flag::Mouse,
|
||||
|
||||
@@ -41,7 +41,8 @@ fn harness_with(extra: &[&str]) -> UiScript {
|
||||
// `GameMenuFrame` and the panels it opens declare `parent="UIParent"`, resolved at LOAD
|
||||
// (decision 1734) — UIParent must already be there, as it is in the manifest.
|
||||
"UIParent.xml",
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
"UiPanels.xml",
|
||||
r"Interface\FrameXML\UIPanelTemplates.lua",
|
||||
r"Interface\FrameXML\UIPanelTemplates.xml",
|
||||
@@ -562,7 +563,6 @@ fn escape_during_a_countdown_cancels_it_and_does_not_open_the_menu() {
|
||||
fn the_world_map_cannot_open_behind_the_menu_and_gives_its_slot_back() {
|
||||
let _data = benilla_formats::wow_data_or_skip!();
|
||||
let s = harness_with(&[
|
||||
"MoneyFrame.xml",
|
||||
"GameTooltip.xml",
|
||||
"Interface\\FrameXML\\UIDropDownMenu.xml", // the map's continent/zone pickers initialize into it at OnLoad
|
||||
"ScrollTemplates.xml",
|
||||
@@ -614,7 +614,6 @@ fn the_world_map_cannot_open_behind_the_menu_and_gives_its_slot_back() {
|
||||
fn nothing_opens_behind_the_world_map_and_escape_closes_it_first() {
|
||||
let _data = benilla_formats::wow_data_or_skip!();
|
||||
let s = harness_with(&[
|
||||
"MoneyFrame.xml",
|
||||
"GameTooltip.xml",
|
||||
"Interface\\FrameXML\\UIDropDownMenu.xml",
|
||||
"ScrollTemplates.xml",
|
||||
@@ -681,7 +680,6 @@ fn the_bag_row_greys_under_the_menu_without_any_of_it_disappearing() {
|
||||
"UIParent.xml",
|
||||
"Interface\\FrameXML\\GlobalStrings.lua",
|
||||
"Interface\\FrameXML\\MainMenuBar.xml",
|
||||
"MoneyFrame.xml",
|
||||
"GameTooltip.xml",
|
||||
"Interface\\FrameXML\\ActionBarFrame.xml",
|
||||
"Interface\\FrameXML\\BonusActionBarFrame.xml",
|
||||
@@ -811,7 +809,6 @@ fn backpack() -> ContainerState {
|
||||
fn the_menu_rides_the_shared_era_window_scale() {
|
||||
let _data = benilla_formats::wow_data_or_skip!();
|
||||
let mut s = harness_with(&[
|
||||
"MoneyFrame.xml",
|
||||
"GameTooltip.xml",
|
||||
"Interface\\FrameXML\\UIDropDownMenu.xml",
|
||||
"ScrollTemplates.xml",
|
||||
|
||||
@@ -76,7 +76,8 @@ fn setup() -> UiScript {
|
||||
// `UIPanelCloseButton`, which the loot window's four close buttons inherit.
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\BasicControls.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\LocaleProperties.lua");
|
||||
@@ -547,20 +548,21 @@ fn managed_positions_engage_for_the_bare_frame_name() {
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "UIParent.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
// UiPanels.xml before GroupLootFrame.xml, mirroring the shipped manifest order
|
||||
// (`ui_script::load_default_ui`): the roll file's CONFIRM_LOOT_ROLL entry indexes
|
||||
// `StaticPopupDialogs`, and indexing a nil there aborts the WHOLE inline <Script> chunk —
|
||||
// taking every BenillaGroupLootFrame_* function down with it, not just the popup.
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\GlobalStrings.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\BasicControls.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\LocaleProperties.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\StaticPopup.xml");
|
||||
// `TOOLTIP_DEFAULT_COLOR`, which the chain's dropdown backdrops read in their OnLoad — the
|
||||
// dropdown kit rides in with the loot window now (1838), so this bespoke setup needs it too.
|
||||
load_xml(&s, r"Interface\FrameXML\StaticPopup.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
load_group_loot(&s);
|
||||
|
||||
@@ -626,7 +628,6 @@ fn ctrl_and_shift_on_the_roll_icon_preview_and_post_its_link() {
|
||||
load_xml(&s, "Interface\\FrameXML\\UIPanelTemplates.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\LocaleProperties.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\StaticPopup.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\FloatingChatFrame.xml");
|
||||
s.set_loot_rolls(rolls());
|
||||
|
||||
|
||||
@@ -24,12 +24,14 @@ fn setup() -> UiScript {
|
||||
r"Interface\FrameXML\UIPanelTemplates.lua",
|
||||
r"Interface\FrameXML\UIPanelTemplates.xml",
|
||||
"ScrollTemplates.xml",
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
"GameTooltip.xml",
|
||||
// Before UiPanels.xml: the shared StaticPopup carries a `SmallMoneyFrameTemplate` coin
|
||||
// row, whose OnLoad calls `SmallMoneyFrame_OnLoad` — the TOC's own order (1580's
|
||||
// talent-wipe fixture hit this first).
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
"Interface\\FrameXML\\GlobalStrings.lua",
|
||||
"UiPanels.xml",
|
||||
"Interface\\FrameXML\\LocaleProperties.lua",
|
||||
|
||||
@@ -369,7 +369,8 @@ fn shown_inspect_honor_page() -> UiScript {
|
||||
"Interface\\FrameXML\\Fonts.xml",
|
||||
// `TEXT`, the stock level line's formatter.
|
||||
"Interface\\FrameXML\\BasicControls.xml",
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
"UiPanels.xml",
|
||||
r"Interface\FrameXML\UIPanelTemplates.lua",
|
||||
r"Interface\FrameXML\UIPanelTemplates.xml",
|
||||
|
||||
@@ -117,7 +117,8 @@ fn armed() -> UiScript {
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
// `TEXT`, which the stock `InspectPaperDollFrame_SetLevel` formats its level line through.
|
||||
load_xml(&s, "Interface\\FrameXML\\BasicControls.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.xml");
|
||||
@@ -166,7 +167,8 @@ fn shipped_inspect_frame_loads_clean() {
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
// `TEXT`, which the stock `InspectPaperDollFrame_SetLevel` formats its level line through.
|
||||
load_xml(&s, "Interface\\FrameXML\\BasicControls.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.xml");
|
||||
|
||||
@@ -89,7 +89,8 @@ fn the_self_menu_row_gates_on_the_binding_and_confirms_before_sending() {
|
||||
for file in [
|
||||
"Interface\\FrameXML\\Fonts.xml",
|
||||
"UIParent.xml",
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
"UiPanels.xml",
|
||||
r"Interface\FrameXML\UIPanelTemplates.lua",
|
||||
r"Interface\FrameXML\UIPanelTemplates.xml",
|
||||
|
||||
@@ -45,7 +45,8 @@ pub(crate) fn harness() -> UiScript {
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
for file in [
|
||||
"Interface\\FrameXML\\Fonts.xml",
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
"UiPanels.xml",
|
||||
r"Interface\FrameXML\UIPanelTemplates.lua",
|
||||
r"Interface\FrameXML\UIPanelTemplates.xml",
|
||||
|
||||
@@ -478,8 +478,6 @@ fn shipped_loot_pushed_to_center_by_merchant() {
|
||||
load_xml(&s, f);
|
||||
}
|
||||
load_xml(&s, "Interface\\FrameXML\\LootFrame.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, "GameTooltip.xml"); // app load order: tooltip before merchant
|
||||
load_xml(&s, "Interface\\FrameXML\\MerchantFrame.xml");
|
||||
|
||||
// Loot opens onto the empty left slot.
|
||||
@@ -621,7 +619,6 @@ fn ctrl_and_shift_on_a_loot_row_preview_and_post_without_looting() {
|
||||
"Interface\\FrameXML\\UIPanelTemplates.xml",
|
||||
"UiPanels.xml",
|
||||
"Interface\\FrameXML\\LocaleProperties.lua",
|
||||
"Interface\\FrameXML\\StaticPopup.xml",
|
||||
"Interface\\FrameXML\\FloatingChatFrame.xml",
|
||||
] {
|
||||
load_xml(&s, file);
|
||||
|
||||
@@ -91,7 +91,8 @@ fn harness_with(player: &str) -> UiScript {
|
||||
r"Interface\FrameXML\GlobalStrings.lua",
|
||||
"Interface\\FrameXML\\Fonts.xml",
|
||||
"Interface\\FrameXML\\BasicControls.xml", // `TEXT`
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
"UiPanels.xml",
|
||||
"Interface\\FrameXML\\LocaleProperties.lua",
|
||||
"Interface\\FrameXML\\StaticPopup.xml",
|
||||
|
||||
@@ -1081,7 +1081,7 @@ fn trade_recipient_money_renders_the_digit_not_ellipsis() {
|
||||
// TradeFrame's money entry is the chain's own since 1882 — its OnLoad calls MoneyInputFrame_*.
|
||||
load_xml(&s, "Interface\\FrameXML\\MoneyInputFrame.lua");
|
||||
load_xml(&s, "Interface\\FrameXML\\MoneyInputFrame.xml");
|
||||
load_xml(&s, "TradeFrame.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\TradeFrame.xml");
|
||||
s.set_text_measurer(Box::new(super::FixedWidthFont(6.0)));
|
||||
|
||||
let target = benilla_ui::script::TradeSideState {
|
||||
@@ -1144,11 +1144,8 @@ fn ctrl_and_shift_on_a_vendor_row_preview_and_post_without_buying() {
|
||||
"Interface\\FrameXML\\BasicControls.xml",
|
||||
"Interface\\FrameXML\\ChatFrame.xml",
|
||||
"Interface\\FrameXML\\UIDropDownMenu.xml",
|
||||
"Interface\\FrameXML\\UIPanelTemplates.lua",
|
||||
"Interface\\FrameXML\\UIPanelTemplates.xml",
|
||||
"UiPanels.xml",
|
||||
"Interface\\FrameXML\\LocaleProperties.lua",
|
||||
"Interface\\FrameXML\\StaticPopup.xml",
|
||||
// (the panel templates, UiPanels and the dialog engine are MERCHANT_UI's already — a
|
||||
// second load of the stock money kit's frames trips its own global-named update)
|
||||
"Interface\\FrameXML\\FloatingChatFrame.xml",
|
||||
] {
|
||||
load_xml(&s, file);
|
||||
|
||||
@@ -30,7 +30,8 @@ fn harness_with(extra: &[&str]) -> UiScript {
|
||||
"UIParent.xml",
|
||||
"Interface\\FrameXML\\GlobalStrings.lua",
|
||||
"Interface\\FrameXML\\MainMenuBar.xml",
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
"GameTooltip.xml",
|
||||
"Interface\\FrameXML\\ActionBarFrame.xml",
|
||||
"Interface\\FrameXML\\BonusActionBarFrame.xml",
|
||||
@@ -255,7 +256,7 @@ fn the_talent_button_appears_at_level_ten_and_the_row_closes_up_below_it() {
|
||||
/// single missing argument is invisible until someone hovers that one button.
|
||||
#[test]
|
||||
fn every_micro_button_hovers_with_its_reference_explanation() {
|
||||
let mut s = harness_with(&["MoneyFrame.xml", "UIParent.xml", "GameTooltip.xml"]);
|
||||
let mut s = harness_with(&["UIParent.xml"]);
|
||||
set_player_level(&mut s, 10);
|
||||
s.resolve();
|
||||
|
||||
|
||||
@@ -24,7 +24,8 @@ fn minimap_zoom_buttons_resync_when_switching_inside_and_outside() {
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
// The shipped load order provides GameTooltip before the cluster; Minimap_Update's tooltip
|
||||
// half (the PVP tint slice, decision 0287) touches it from OnLoad on.
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
load_xml(&s, "MinimapCluster.xml");
|
||||
|
||||
@@ -88,7 +89,8 @@ fn tracking_frame_follows_get_tracking_texture_across_player_auras_changed() {
|
||||
.unwrap();
|
||||
s.run("function PlaySound() end").unwrap();
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
load_xml(&s, "MinimapCluster.xml");
|
||||
|
||||
@@ -137,7 +139,8 @@ fn game_time_session(hour: u32, minute: u32) -> UiScript {
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
// `TEXT()`, which the reference's own tooltip and label formatting passes every string through.
|
||||
load_xml(&s, "Interface\\FrameXML\\BasicControls.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
load_xml(&s, "MinimapCluster.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\GameTime.xml");
|
||||
@@ -249,7 +252,8 @@ fn a_click_on_the_minimap_parks_a_centre_relative_ping_request() {
|
||||
.unwrap();
|
||||
s.run("function PlaySound() end").unwrap();
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
load_xml(&s, "MinimapCluster.xml");
|
||||
s.resolve();
|
||||
@@ -294,7 +298,8 @@ fn get_ping_position_is_nil_until_there_is_a_ping() {
|
||||
.unwrap();
|
||||
s.run("function PlaySound() end").unwrap();
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
load_xml(&s, "MinimapCluster.xml");
|
||||
|
||||
|
||||
@@ -23,7 +23,8 @@ fn harness() -> UiScript {
|
||||
// `STATICPOPUP_NUMDIALOGS`, which the reference's own `MirrorTimer_Show` bounds its free-bar
|
||||
// search by (MirrorTimer.lua l.32 — a copy-paste from StaticPopup.lua, and its own bug). A
|
||||
// session without it searches `1, nil` and finds no free bar at all, so this is not scenery.
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.xml");
|
||||
|
||||
@@ -27,7 +27,9 @@ fn harness(money: u64) -> UiScript {
|
||||
// every expected number readable as `digits x 8 + icon`.
|
||||
s.set_text_measurer(Box::new(super::FixedWidthFont(8.0)));
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\BasicControls.xml"); // `message`, the kit's own error path
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
|
||||
let doc = benilla_ui::framexml::parse(
|
||||
r#"<Ui>
|
||||
@@ -375,8 +377,9 @@ fn the_chains_money_input_frame_splits_an_amount_across_its_three_boxes() {
|
||||
let _data = benilla_formats::wow_data_or_skip!();
|
||||
let s = UiScript::new().unwrap();
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml"); // COPPER_PER_GOLD / COPPER_PER_SILVER live here
|
||||
// The manifest's own order: the `.lua` brings the ten verbs, the `.xml` the template.
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua"); // COPPER_PER_GOLD / COPPER_PER_SILVER live here
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
// The manifest's own order: the `.lua` brings the ten verbs, the `.xml` the template.
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyInputFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyInputFrame.xml");
|
||||
|
||||
|
||||
@@ -20,7 +20,8 @@ fn load_action_bar(s: &UiScript) {
|
||||
for file in [
|
||||
"Interface\\FrameXML\\Fonts.xml",
|
||||
"UIParent.xml",
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
"GameTooltip.xml",
|
||||
"Cooldown.xml",
|
||||
"Interface\\FrameXML\\ActionButtonTemplate.xml",
|
||||
@@ -278,8 +279,6 @@ fn shipped_stance_bar_drives_end_to_end() {
|
||||
// other state.
|
||||
load_xml(&s, "Cooldown.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\ActionButtonTemplate.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "UIParent.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
@@ -290,7 +289,6 @@ fn shipped_stance_bar_drives_end_to_end() {
|
||||
load_xml(&s, r"Interface\FrameXML\GlobalStrings.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\BasicControls.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\LocaleProperties.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\StaticPopup.xml");
|
||||
load_xml(&s, "KeyBindingsPage.xml");
|
||||
load_xml(&s, "OptionsFrame.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\MultiActionBars.xml");
|
||||
@@ -466,7 +464,8 @@ fn multibar_hover_renders_the_buttons_own_action() {
|
||||
for file in [
|
||||
"Interface\\FrameXML\\Fonts.xml",
|
||||
"UIParent.xml",
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
"GameTooltip.xml",
|
||||
"Cooldown.xml",
|
||||
"Interface\\FrameXML\\ActionButtonTemplate.xml",
|
||||
@@ -585,8 +584,6 @@ fn the_vertical_multibars_exist_hidden_on_the_reference_pages() {
|
||||
load_action_bar(&s);
|
||||
load_xml(&s, "Cooldown.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\ActionButtonTemplate.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "UIParent.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
@@ -597,7 +594,6 @@ fn the_vertical_multibars_exist_hidden_on_the_reference_pages() {
|
||||
load_xml(&s, r"Interface\FrameXML\GlobalStrings.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\BasicControls.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\LocaleProperties.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\StaticPopup.xml");
|
||||
load_xml(&s, "KeyBindingsPage.xml");
|
||||
load_xml(&s, "OptionsFrame.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\MultiActionBars.xml");
|
||||
@@ -651,8 +647,6 @@ fn every_extra_bar_stays_down_until_its_own_toggle_is_set() {
|
||||
load_action_bar(&s);
|
||||
load_xml(&s, "Cooldown.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\ActionButtonTemplate.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "UIParent.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
@@ -663,7 +657,6 @@ fn every_extra_bar_stays_down_until_its_own_toggle_is_set() {
|
||||
load_xml(&s, r"Interface\FrameXML\GlobalStrings.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\BasicControls.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\LocaleProperties.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\StaticPopup.xml");
|
||||
load_xml(&s, "KeyBindingsPage.xml");
|
||||
load_xml(&s, "OptionsFrame.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\MultiActionBars.xml");
|
||||
@@ -742,8 +735,6 @@ fn raising_a_bottom_bar_moves_the_managed_bottom_stack() {
|
||||
load_action_bar(&s);
|
||||
load_xml(&s, "Cooldown.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\ActionButtonTemplate.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "UIParent.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
@@ -754,7 +745,6 @@ fn raising_a_bottom_bar_moves_the_managed_bottom_stack() {
|
||||
load_xml(&s, r"Interface\FrameXML\GlobalStrings.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\BasicControls.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\LocaleProperties.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\StaticPopup.xml");
|
||||
load_xml(&s, "KeyBindingsPage.xml");
|
||||
load_xml(&s, "OptionsFrame.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\MultiActionBars.xml");
|
||||
@@ -810,8 +800,6 @@ fn viewable_action_bar_pages_follow_the_bar_toggles() {
|
||||
load_action_bar(&s);
|
||||
load_xml(&s, "Cooldown.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\ActionButtonTemplate.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "UIParent.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
@@ -822,7 +810,6 @@ fn viewable_action_bar_pages_follow_the_bar_toggles() {
|
||||
load_xml(&s, r"Interface\FrameXML\GlobalStrings.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\BasicControls.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\LocaleProperties.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\StaticPopup.xml");
|
||||
load_xml(&s, "KeyBindingsPage.xml");
|
||||
load_xml(&s, "OptionsFrame.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\MultiActionBars.xml");
|
||||
@@ -880,8 +867,6 @@ fn the_grid_option_holds_the_extra_bars_empty_wells_open() {
|
||||
load_action_bar(&s);
|
||||
load_xml(&s, "Cooldown.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\ActionButtonTemplate.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "UIParent.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
@@ -892,7 +877,6 @@ fn the_grid_option_holds_the_extra_bars_empty_wells_open() {
|
||||
load_xml(&s, r"Interface\FrameXML\GlobalStrings.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\BasicControls.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\LocaleProperties.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\StaticPopup.xml");
|
||||
load_xml(&s, "KeyBindingsPage.xml");
|
||||
load_xml(&s, "OptionsFrame.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\MultiActionBars.xml");
|
||||
@@ -987,8 +971,6 @@ fn a_held_payload_ghosts_the_empty_wells_it_opens() {
|
||||
load_action_bar(&s);
|
||||
load_xml(&s, "Cooldown.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\ActionButtonTemplate.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "UIParent.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
@@ -999,7 +981,6 @@ fn a_held_payload_ghosts_the_empty_wells_it_opens() {
|
||||
load_xml(&s, r"Interface\FrameXML\GlobalStrings.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\BasicControls.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\LocaleProperties.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\StaticPopup.xml");
|
||||
load_xml(&s, "KeyBindingsPage.xml");
|
||||
load_xml(&s, "OptionsFrame.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\MultiActionBars.xml");
|
||||
@@ -1134,8 +1115,6 @@ fn a_bar_toggle_sends_the_byte_its_globals_pack_to() {
|
||||
load_action_bar(&s);
|
||||
load_xml(&s, "Cooldown.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\ActionButtonTemplate.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "UIParent.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
@@ -1146,7 +1125,6 @@ fn a_bar_toggle_sends_the_byte_its_globals_pack_to() {
|
||||
load_xml(&s, r"Interface\FrameXML\GlobalStrings.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\BasicControls.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\LocaleProperties.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\StaticPopup.xml");
|
||||
load_xml(&s, "KeyBindingsPage.xml");
|
||||
load_xml(&s, "OptionsFrame.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\MultiActionBars.xml");
|
||||
@@ -1225,8 +1203,6 @@ fn the_shipped_setter_passes_exactly_four_arguments() {
|
||||
load_action_bar(&s);
|
||||
load_xml(&s, "Cooldown.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\ActionButtonTemplate.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "UIParent.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
@@ -1237,7 +1213,6 @@ fn the_shipped_setter_passes_exactly_four_arguments() {
|
||||
load_xml(&s, r"Interface\FrameXML\GlobalStrings.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\BasicControls.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\LocaleProperties.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\StaticPopup.xml");
|
||||
load_xml(&s, "KeyBindingsPage.xml");
|
||||
load_xml(&s, "OptionsFrame.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\MultiActionBars.xml");
|
||||
@@ -1281,8 +1256,6 @@ fn the_stance_bar_sits_where_the_pass_puts_it() {
|
||||
load_action_bar(&s);
|
||||
load_xml(&s, "Cooldown.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\ActionButtonTemplate.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "UIParent.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
@@ -1293,7 +1266,6 @@ fn the_stance_bar_sits_where_the_pass_puts_it() {
|
||||
load_xml(&s, r"Interface\FrameXML\GlobalStrings.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\BasicControls.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\LocaleProperties.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\StaticPopup.xml");
|
||||
load_xml(&s, "KeyBindingsPage.xml");
|
||||
load_xml(&s, "OptionsFrame.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\MultiActionBars.xml");
|
||||
@@ -1357,8 +1329,6 @@ fn the_stance_shelf_follows_the_bottom_left_bar() {
|
||||
load_action_bar(&s);
|
||||
load_xml(&s, "Cooldown.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\ActionButtonTemplate.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "UIParent.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
@@ -1369,7 +1339,6 @@ fn the_stance_shelf_follows_the_bottom_left_bar() {
|
||||
load_xml(&s, r"Interface\FrameXML\GlobalStrings.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\BasicControls.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\LocaleProperties.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\StaticPopup.xml");
|
||||
load_xml(&s, "KeyBindingsPage.xml");
|
||||
load_xml(&s, "OptionsFrame.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\MultiActionBars.xml");
|
||||
@@ -1482,8 +1451,6 @@ fn the_stance_shelf_is_as_long_as_the_form_count() {
|
||||
load_action_bar(&s);
|
||||
load_xml(&s, "Cooldown.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\ActionButtonTemplate.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "UIParent.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
@@ -1494,7 +1461,6 @@ fn the_stance_shelf_is_as_long_as_the_form_count() {
|
||||
load_xml(&s, r"Interface\FrameXML\GlobalStrings.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\BasicControls.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\LocaleProperties.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\StaticPopup.xml");
|
||||
load_xml(&s, "KeyBindingsPage.xml");
|
||||
load_xml(&s, "OptionsFrame.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\MultiActionBars.xml");
|
||||
@@ -1603,8 +1569,6 @@ fn an_extra_bars_empty_well_keeps_its_bound_hotkey_label() {
|
||||
load_action_bar(&s);
|
||||
load_xml(&s, "Cooldown.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\ActionButtonTemplate.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "UIParent.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
@@ -1615,7 +1579,6 @@ fn an_extra_bars_empty_well_keeps_its_bound_hotkey_label() {
|
||||
load_xml(&s, r"Interface\FrameXML\GlobalStrings.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\BasicControls.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\LocaleProperties.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\StaticPopup.xml");
|
||||
load_xml(&s, "KeyBindingsPage.xml");
|
||||
load_xml(&s, "OptionsFrame.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\MultiActionBars.xml");
|
||||
|
||||
@@ -36,12 +36,23 @@ fn harness() -> UiScript {
|
||||
/// before it for the kit's TOOLTIP_DEFAULT_COLOR — the app's own order.
|
||||
fn harness_on(mut s: UiScript) -> UiScript {
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
// A file loads ONCE, as in the client: the definer harnesses below load some of these ahead
|
||||
// of this list to capture file-scope values, and a second load of the stock money kit's
|
||||
// frames trips its own global-named update (`MoneyFrame_Update` writes the amount onto
|
||||
// `getglobal(name)` — the first instance — while the second instance reads its own).
|
||||
let loaded = |s: &UiScript, file: &str| -> bool {
|
||||
s.eval::<bool>(&format!(
|
||||
"return (BENILLA_TEST_LOADED or {{}})[{file:?}] == true"
|
||||
))
|
||||
.unwrap_or(false)
|
||||
};
|
||||
for file in [
|
||||
"Interface\\FrameXML\\Fonts.xml",
|
||||
// Every panel window declares `parent="UIParent"`, resolved at LOAD — so UIParent has to
|
||||
// exist by the time they are read, exactly as it does in the manifest (decision 1734).
|
||||
"UIParent.xml",
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
"UiPanels.xml",
|
||||
r"Interface\FrameXML\UIPanelTemplates.lua",
|
||||
r"Interface\FrameXML\UIPanelTemplates.xml",
|
||||
@@ -64,6 +75,9 @@ fn harness_on(mut s: UiScript) -> UiScript {
|
||||
// here is what made this kit break the moment a file it loads migrated (1751).
|
||||
// The dialect announces DROPPED subtrees as warnings, not errors — for the new file,
|
||||
// a warning is a silently-missing piece of chrome, so it fails there.
|
||||
if loaded(&s, file) {
|
||||
continue;
|
||||
}
|
||||
if file == "OptionsFrame.xml" {
|
||||
super::test_ui::load_ui_strict(&s, file);
|
||||
} else {
|
||||
@@ -680,15 +694,17 @@ fn audio_harness() -> UiScript {
|
||||
s
|
||||
}
|
||||
|
||||
/// The Combat page's harness (decision 1134): the **real** `CombatText.xml` loaded ahead of the
|
||||
/// window, in the manifest's own order (it is file 59, OptionsFrame.xml file 281), so the rows
|
||||
/// capture the same file-scope defaults and the same `applyFunc` they capture in the client —
|
||||
/// nothing about the family is restated here. `UIParent.xml` comes first because CombatText's
|
||||
/// strings anchor to it.
|
||||
/// The Combat page's harness (decision 1134, 1964): the family's defaults are the window's own
|
||||
/// file-scope block (the reference's UIOptionsFrame.lua l.135-152), and the addon they drive is
|
||||
/// `Blizzard_CombatText` off the chain, loaded on demand by the master row's apply.
|
||||
fn combat_harness() -> UiScript {
|
||||
let mut s = audio_harness();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_definers(&s, &["UIParent.xml", "CombatText.xml"]);
|
||||
load_definers(&s, &["UIParent.xml"]);
|
||||
// The family's definers are the options window's own since 1964 (the reference's
|
||||
// UIOptionsFrame.lua block); the addon itself is LoadOnDemand off the chain, seated so the
|
||||
// master row's apply can load it the way the client does.
|
||||
super::test_ui::seat_chain_addon(&mut s, "Blizzard_CombatText");
|
||||
harness_on(s)
|
||||
}
|
||||
|
||||
@@ -698,9 +714,12 @@ fn combat_harness() -> UiScript {
|
||||
/// exactly the ordering the real manifest guarantees.
|
||||
fn load_definers(s: &UiScript, files: &[&str]) {
|
||||
for file in files {
|
||||
// Both stores: a definer can be the reference's own file now (BuffFrame.xml since 1751
|
||||
// window 18), and `test_ui::load_ui` is the reader that speaks either.
|
||||
super::test_ui::load_ui(s, file);
|
||||
// Remembered so `harness_on` loads each file once (see there).
|
||||
s.run(&format!(
|
||||
"BENILLA_TEST_LOADED = BENILLA_TEST_LOADED or {{}} BENILLA_TEST_LOADED[{file:?}] = true"
|
||||
))
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -721,7 +740,8 @@ fn interface_harness() -> UiScript {
|
||||
&s,
|
||||
&[
|
||||
"Interface\\FrameXML\\Fonts.xml",
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
"Interface\\FrameXML\\GlobalStrings.lua",
|
||||
"UiPanels.xml",
|
||||
r"Interface\FrameXML\UIPanelTemplates.lua",
|
||||
@@ -796,7 +816,8 @@ fn chat_harness() -> UiScript {
|
||||
&s,
|
||||
&[
|
||||
"Interface\\FrameXML\\Fonts.xml",
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
"UiPanels.xml",
|
||||
r"Interface\FrameXML\UIPanelTemplates.lua",
|
||||
r"Interface\FrameXML\UIPanelTemplates.xml",
|
||||
@@ -838,7 +859,8 @@ fn actionbars_harness() -> UiScript {
|
||||
"Interface\\FrameXML\\TextStatusBar.xml",
|
||||
"Interface\\FrameXML\\GlobalStrings.lua",
|
||||
"Interface\\FrameXML\\MainMenuBar.xml",
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
"GameTooltip.xml",
|
||||
"Interface\\FrameXML\\ActionBarFrame.xml",
|
||||
"Interface\\FrameXML\\BonusActionBarFrame.xml",
|
||||
@@ -3150,6 +3172,9 @@ fn a_saved_switch_with_a_side_effect_is_applied_when_the_variables_land() {
|
||||
// What the saved chunk does, verbatim: assign over the file-scope default, then the event.
|
||||
s.run("SHOW_COMBAT_TEXT = \"1\"").unwrap();
|
||||
s.fire_event("VARIABLES_LOADED", vec![]);
|
||||
// The walk loaded the addon (1964); a frame passes before any message can arrive, and the
|
||||
// stock message placement reads the strings' screen positions, which that frame lays out.
|
||||
s.resolve();
|
||||
|
||||
s.fire_event(
|
||||
"COMBAT_TEXT_UPDATE",
|
||||
|
||||
@@ -45,7 +45,8 @@ fn harness() -> UiScript {
|
||||
for file in [
|
||||
"Interface\\FrameXML\\Fonts.xml",
|
||||
"UIParent.xml",
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
"UiPanels.xml",
|
||||
"GameTooltip.xml",
|
||||
"ScrollTemplates.xml",
|
||||
|
||||
@@ -48,7 +48,8 @@ fn shipped_gossip_frame_drives_end_to_end() {
|
||||
// `shipped_gossip_rows_grow_to_their_wrapped_labels` for why this is a harness gap and not an
|
||||
// engine one.
|
||||
s.set_text_measurer(Box::new(super::FixedWidthFont(6.0)));
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, "ScrollTemplates.xml"); // the shared scroll kit the window rides
|
||||
// UIPanelScrollFrameTemplate lives here, and the gossip scroll frame inherits it. NOT
|
||||
@@ -253,7 +254,8 @@ fn shipped_gossip_frame_renders_quest_rows_above_options() {
|
||||
let _data = benilla_formats::wow_data_or_skip!();
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, "ScrollTemplates.xml"); // the shared scroll kit the window rides
|
||||
// UIPanelScrollFrameTemplate lives here, and the gossip scroll frame inherits it. NOT
|
||||
@@ -416,7 +418,8 @@ fn shipped_gossip_rows_grow_to_their_wrapped_labels() {
|
||||
// (`ui_script::extract`'s `AtlasMeasurer`); only a bare test VM does not. The gap was in the
|
||||
// harness, and it hid behind a window we had written to measure a frame later.
|
||||
s.set_text_measurer(Box::new(super::FixedWidthFont(6.0)));
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, "ScrollTemplates.xml"); // the shared scroll kit the window rides
|
||||
// UIPanelScrollFrameTemplate lives here, and the gossip scroll frame inherits it. NOT
|
||||
@@ -534,7 +537,8 @@ fn gossip_show_hide_plays_open_and_close_kits() {
|
||||
let _data = benilla_formats::wow_data_or_skip!();
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, "ScrollTemplates.xml"); // the shared scroll kit the window rides
|
||||
// UIPanelScrollFrameTemplate lives here, and the gossip scroll frame inherits it. NOT
|
||||
@@ -582,7 +586,6 @@ fn shipped_panel_slot_replaces_gossip_with_merchant() {
|
||||
let _data = benilla_formats::wow_data_or_skip!();
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, "ScrollTemplates.xml"); // the shared scroll kit the window rides
|
||||
// UIPanelScrollFrameTemplate lives here, and the gossip scroll frame inherits it. NOT
|
||||
@@ -593,12 +596,10 @@ fn shipped_panel_slot_replaces_gossip_with_merchant() {
|
||||
load_xml(&s, r"Interface\FrameXML\GlobalStrings.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\BasicControls.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\LocaleProperties.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\StaticPopup.xml");
|
||||
load_xml(&s, "UIParent.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\GossipFrame.xml");
|
||||
load_xml(&s, "GameTooltip.xml"); // app load order: tooltip before merchant
|
||||
// The vendor window is the reference's own since 1751, and its `MerchantFrame_UpdateMerchantInfo`
|
||||
// calls `TEXT()` while building every row — see `test_ui::MERCHANT_UI` for the rest.
|
||||
// The vendor window is the reference's own since 1751, and its `MerchantFrame_UpdateMerchantInfo`
|
||||
// calls `TEXT()` while building every row — see `test_ui::MERCHANT_UI` for the rest.
|
||||
for f in super::test_ui::MERCHANT_UI {
|
||||
load_xml(&s, f);
|
||||
}
|
||||
@@ -681,7 +682,6 @@ fn displacing_an_npc_window_ends_the_displaced_session() {
|
||||
let _data = benilla_formats::wow_data_or_skip!();
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, "ScrollTemplates.xml"); // the shared scroll kit the window rides
|
||||
// UIPanelScrollFrameTemplate lives here, and the gossip scroll frame inherits it. NOT
|
||||
@@ -692,12 +692,10 @@ fn displacing_an_npc_window_ends_the_displaced_session() {
|
||||
load_xml(&s, r"Interface\FrameXML\GlobalStrings.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\BasicControls.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\LocaleProperties.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\StaticPopup.xml");
|
||||
load_xml(&s, "UIParent.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\GossipFrame.xml");
|
||||
load_xml(&s, "GameTooltip.xml"); // app load order: tooltip before merchant
|
||||
// The vendor window is the reference's own since 1751, and its `MerchantFrame_UpdateMerchantInfo`
|
||||
// calls `TEXT()` while building every row — see `test_ui::MERCHANT_UI` for the rest.
|
||||
// The vendor window is the reference's own since 1751, and its `MerchantFrame_UpdateMerchantInfo`
|
||||
// calls `TEXT()` while building every row — see `test_ui::MERCHANT_UI` for the rest.
|
||||
for f in super::test_ui::MERCHANT_UI {
|
||||
load_xml(&s, f);
|
||||
}
|
||||
@@ -744,18 +742,15 @@ fn shipped_panel_slot_pushable_promotes_to_center() {
|
||||
let _data = benilla_formats::wow_data_or_skip!();
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\GlobalStrings.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\BasicControls.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\LocaleProperties.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\StaticPopup.xml");
|
||||
load_xml(&s, "UIParent.xml");
|
||||
load_xml(&s, "GameTooltip.xml"); // app load order: tooltip before merchant
|
||||
// The vendor window is the reference's own since 1751, and its `MerchantFrame_UpdateMerchantInfo`
|
||||
// calls `TEXT()` while building every row — see `test_ui::MERCHANT_UI` for the rest.
|
||||
// The vendor window is the reference's own since 1751, and its `MerchantFrame_UpdateMerchantInfo`
|
||||
// calls `TEXT()` while building every row — see `test_ui::MERCHANT_UI` for the rest.
|
||||
for f in super::test_ui::MERCHANT_UI {
|
||||
load_xml(&s, f);
|
||||
}
|
||||
@@ -854,7 +849,8 @@ fn gossip_bank_option_hands_the_left_slot_to_the_bank() {
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\ItemButtonTemplate.xml"); // the reference bank's slot buttons inherit it
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\GlobalStrings.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\BasicControls.xml");
|
||||
@@ -942,7 +938,8 @@ fn an_overflowing_gossip_menu_scrolls_instead_of_spilling() {
|
||||
// `shipped_gossip_rows_grow_to_their_wrapped_labels` for why this is a harness gap and not an
|
||||
// engine one.
|
||||
s.set_text_measurer(Box::new(super::FixedWidthFont(6.0)));
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, "ScrollTemplates.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.lua");
|
||||
@@ -1103,7 +1100,8 @@ fn an_addons_own_frame_registered_in_uipanelwindows_takes_the_left_slot() {
|
||||
let _data = benilla_formats::wow_data_or_skip!();
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, "ScrollTemplates.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.lua");
|
||||
@@ -1184,7 +1182,8 @@ fn an_addons_own_frame_registered_in_uipanelwindows_takes_the_left_slot() {
|
||||
fn the_1507_registry_rows_match_the_reference_bytes() {
|
||||
let _data = benilla_formats::wow_data_or_skip!();
|
||||
let s = UiScript::new().unwrap();
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.xml");
|
||||
@@ -1228,7 +1227,8 @@ fn a_dead_player_opens_only_whiledead_windows() {
|
||||
let _data = benilla_formats::wow_data_or_skip!();
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.xml");
|
||||
@@ -1301,7 +1301,8 @@ fn a_frame_arriving_at_center_puts_the_child_windows_away() {
|
||||
let _data = benilla_formats::wow_data_or_skip!();
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.xml");
|
||||
|
||||
@@ -22,7 +22,8 @@ fn harness(extra: &[&str]) -> UiScript {
|
||||
load_xml(&s, "UIParent.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\GlobalStrings.lua");
|
||||
load_xml(&s, "Interface\\FrameXML\\MainMenuBar.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\ActionBarFrame.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\BonusActionBarFrame.xml");
|
||||
@@ -228,7 +229,7 @@ fn get_net_stats_reports_the_pushed_latency() {
|
||||
/// tooltip that is quietly one line short.
|
||||
#[test]
|
||||
fn hovering_the_meter_shows_the_live_latency() {
|
||||
let mut s = harness(&["MoneyFrame.xml", "UIParent.xml", "GameTooltip.xml"]);
|
||||
let mut s = harness(&["UIParent.xml"]);
|
||||
s.set_latency_ms(Some(42));
|
||||
s.resolve();
|
||||
|
||||
|
||||
@@ -26,7 +26,8 @@ pub(super) fn load_pet_bar(s: &UiScript) {
|
||||
"Interface\\FrameXML\\GlobalStrings.lua",
|
||||
"Interface\\FrameXML\\BasicControls.xml",
|
||||
"Interface\\FrameXML\\MainMenuBar.xml",
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
"GameTooltip.xml",
|
||||
"Interface\\FrameXML\\ActionBarFrame.xml",
|
||||
"Interface\\FrameXML\\BonusActionBarFrame.xml",
|
||||
|
||||
@@ -28,7 +28,8 @@ fn load_pet_frame() -> UiScript {
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "UIParent.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\UIDropDownMenu.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\BasicControls.xml");
|
||||
|
||||
@@ -92,7 +92,8 @@ fn setup() -> UiScript {
|
||||
load_xml(&s, "Interface\\FrameXML\\GlobalStrings.lua");
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\BasicControls.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.xml");
|
||||
|
||||
@@ -14,7 +14,8 @@ fn setup() -> UiScript {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.xml");
|
||||
|
||||
@@ -18,7 +18,8 @@ fn questgiver_show_hide_plays_open_and_close_kits() {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\GlobalStrings.lua");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
@@ -88,7 +89,8 @@ fn panel_events_show_exactly_one_child_panel_and_hide_the_others() {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\GlobalStrings.lua");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
@@ -186,7 +188,8 @@ fn detail_panel_reward_grid_follows_the_refs_two_per_row_layout() {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\GlobalStrings.lua");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
@@ -288,7 +291,8 @@ fn reward_panel_choice_click_selects_and_completes_with_zero_based_index() {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\GlobalStrings.lua");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
@@ -367,7 +371,8 @@ fn greeting_goodbye_button_closes_the_window() {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\GlobalStrings.lua");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
@@ -424,7 +429,8 @@ fn detail_panel_action_buttons_resolve_to_real_onscreen_rects() {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\GlobalStrings.lua");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
@@ -552,7 +558,8 @@ fn write_on_still_fades_when_instant_text_is_off() {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\GlobalStrings.lua");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
@@ -629,7 +636,8 @@ fn npc_name_reaches_the_title_bar_on_open_and_on_refresh() {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\GlobalStrings.lua");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
@@ -712,7 +720,8 @@ fn greeting_panel_title_rows_grow_to_their_wrapped_titles() {
|
||||
// So is the app's (measure.rs); the harness installs the same seam's stand-in.
|
||||
s.set_text_measurer(Box::new(super::FixedWidthFont(7.0)));
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\GlobalStrings.lua");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
@@ -812,7 +821,8 @@ fn reward_rows_preview_and_post_without_selecting_the_choice() {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\GlobalStrings.lua");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
|
||||
@@ -32,7 +32,8 @@ fn harness() -> UiScript {
|
||||
// `MAX_QUESTS`, the loop bound the reference's repaint hides its spare rows with. 1.12
|
||||
// declares it on QuestLogFrame.lua:2 and so do we (1751 window 16) — a nil there is
|
||||
// `'for' limit must be a number` on the first repaint, not a missing row.
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.xml");
|
||||
|
||||
@@ -85,7 +85,8 @@ fn shipped_questlog_frame_loads_clean() {
|
||||
let _data = benilla_formats::wow_data_or_skip!();
|
||||
let s = UiScript::new().unwrap();
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\GlobalStrings.lua");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
@@ -118,7 +119,8 @@ fn shipped_questlog_frame_drives_end_to_end() {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\GlobalStrings.lua");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
@@ -327,7 +329,8 @@ fn shift_click_toggles_the_watch_checkbox_and_the_tracker_hud() {
|
||||
// synchronously — the app's measure seam is synchronous too (measure.rs); its stand-in here.
|
||||
s.set_text_measurer(Box::new(super::FixedWidthFont(6.0)));
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\GlobalStrings.lua");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
@@ -474,7 +477,8 @@ fn watch_guards_no_op_without_erroring() {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\GlobalStrings.lua");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
@@ -590,7 +594,8 @@ fn progress_auto_watches_for_five_minutes() {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\GlobalStrings.lua");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
@@ -674,7 +679,8 @@ fn the_auto_watch_flag_is_the_references_uvar_and_gates_the_watch() {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\GlobalStrings.lua");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
@@ -740,7 +746,8 @@ fn the_row_tag_is_its_own_right_flush_string_and_the_state_word_wins() {
|
||||
// ref-verbatim tag handling — the app loads GlobalStrings.lua as `benilla.toc`'s second entry,
|
||||
// so this harness must too or the override silently reads nil.
|
||||
load_xml(&s, "Interface\\FrameXML\\GlobalStrings.lua");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.lua");
|
||||
@@ -817,7 +824,8 @@ fn empty_quest_log_hides_rows_and_disables_abandon() {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\GlobalStrings.lua");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
@@ -881,7 +889,8 @@ fn reward_rows_follow_the_refs_two_per_row_layout() {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\GlobalStrings.lua");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
@@ -1069,7 +1078,8 @@ fn overflowing_detail_content_clips_to_the_scrollframe_rect() {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\GlobalStrings.lua");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
@@ -1143,7 +1153,8 @@ fn wheel_over_the_detail_pane_changes_vertical_scroll() {
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
s.set_text_measurer(Box::new(super::FixedWidthFont(6.0)));
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\GlobalStrings.lua");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
@@ -1207,7 +1218,8 @@ fn selection_change_resets_detail_scroll_but_a_quest_log_update_refresh_does_not
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
s.set_text_measurer(Box::new(super::FixedWidthFont(6.0)));
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\GlobalStrings.lua");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
@@ -1283,7 +1295,8 @@ fn reward_row_hover_serves_the_shared_item_tooltip() {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\GlobalStrings.lua");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
@@ -1386,7 +1399,8 @@ fn popup_children_inherit_the_dialog_stratum() {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.xml");
|
||||
@@ -1441,7 +1455,8 @@ fn reward_rows_preview_and_post_and_a_plain_click_stays_inert() {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\GlobalStrings.lua");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
@@ -1539,7 +1554,8 @@ fn shift_click_on_a_title_posts_the_quest_name_with_chat_open_and_watches_with_i
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\GlobalStrings.lua");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
@@ -1648,7 +1664,8 @@ fn share_quest_needs_both_a_sharable_selection_and_a_party() {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\GlobalStrings.lua");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
@@ -1734,7 +1751,8 @@ fn share_quest_is_dark_on_an_empty_log_even_in_a_party() {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\GlobalStrings.lua");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
@@ -1774,7 +1792,8 @@ fn share_quest_click_queues_the_selected_quests_id() {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\GlobalStrings.lua");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
|
||||
@@ -1523,72 +1523,12 @@ mod tests {
|
||||
update; our `inventory_alerts` snapshot is recomputed on every inventory push \
|
||||
instead, so the recompute exists and only the Lua verb that forces one does not.",
|
||||
),
|
||||
(
|
||||
"StaticPopup.xml",
|
||||
"AcceptAreaSpiritHeal",
|
||||
"a registered 1.12 binding whose body is uncarved (wow-re `bindings.md`, structural row only); a \
|
||||
wow-re orchestrator is out on it and it is built when the carve lands (1960). Reached by AREA_SPIRIT_HEAL's Accept, an event this engine does not fire yet.",
|
||||
),
|
||||
(
|
||||
"StaticPopup.xml",
|
||||
"CancelAreaSpiritHeal",
|
||||
"a registered 1.12 binding whose body is uncarved (wow-re `bindings.md`, structural row only); a \
|
||||
wow-re orchestrator is out on it and it is built when the carve lands (1960). Reached by AREA_SPIRIT_HEAL's Cancel, an event this engine does not fire yet.",
|
||||
),
|
||||
(
|
||||
"StaticPopup.xml",
|
||||
"GetAreaSpiritHealerTime",
|
||||
"a registered 1.12 binding whose body is uncarved (wow-re `bindings.md`, structural row only); a \
|
||||
wow-re orchestrator is out on it and it is built when the carve lands (1960). Reached by AREA_SPIRIT_HEAL's OnShow, an event this engine does not fire yet.",
|
||||
),
|
||||
(
|
||||
"StaticPopup.xml",
|
||||
"AcceptBattlefieldPort",
|
||||
"a registered 1.12 binding whose body is uncarved (wow-re `bindings.md`, structural row only); a \
|
||||
wow-re orchestrator is out on it and it is built when the carve lands (1960). Reached by CONFIRM_BATTLEFIELD_ENTRY / CONFIRM_LEAVE_QUEUE, raised only by BattlefieldFrame.lua, which is not on the chain yet.",
|
||||
),
|
||||
(
|
||||
"StaticPopup.xml",
|
||||
"BeginTrade",
|
||||
"a registered 1.12 binding whose body is uncarved (wow-re `bindings.md`, structural row only); a \
|
||||
wow-re orchestrator is out on it and it is built when the carve lands (1960). Reached by the TRADE dialog, which 1.12.1 can never show (wow-re `incoming-trade-request-law.md`: TRADE_REQUEST is signalled by nothing).",
|
||||
),
|
||||
(
|
||||
"StaticPopup.xml",
|
||||
"CancelTrade",
|
||||
"a registered 1.12 binding whose body is uncarved (wow-re `bindings.md`, structural row only); a \
|
||||
wow-re orchestrator is out on it and it is built when the carve lands (1960). Reached by the TRADE dialog, which 1.12.1 can never show (wow-re `incoming-trade-request-law.md`: TRADE_REQUEST is signalled by nothing).",
|
||||
),
|
||||
(
|
||||
"StaticPopup.xml",
|
||||
"ReplaceTradeEnchant",
|
||||
"a registered 1.12 binding whose body is uncarved (wow-re `bindings.md`, structural row only); a \
|
||||
wow-re orchestrator is out on it and it is built when the carve lands (1960). Reached by TRADE_REPLACE_ENCHANT's Accept, an event this engine does not fire yet.",
|
||||
),
|
||||
(
|
||||
"StaticPopup.xml",
|
||||
"CancelMeetingStoneRequest",
|
||||
"a registered 1.12 binding whose body is uncarved (wow-re `bindings.md`, structural row only); a \
|
||||
wow-re orchestrator is out on it and it is built when the carve lands (1960). Reached by CONFIRM_LEAVE_QUEUE's meeting-stone arm, raised only by BattlefieldFrame.lua / the meeting-stone gossip, neither on the chain yet.",
|
||||
),
|
||||
(
|
||||
"StaticPopup.xml",
|
||||
"CheckPetUntrainerDist",
|
||||
"a registered 1.12 binding whose body is uncarved (wow-re `bindings.md`, structural row only); a \
|
||||
wow-re orchestrator is out on it and it is built when the carve lands (1960). Reached by CONFIRM_PET_UNLEARN's OnUpdate, an event this engine does not fire yet.",
|
||||
),
|
||||
(
|
||||
"StaticPopup.xml",
|
||||
"ConfirmPetUnlearn",
|
||||
"a registered 1.12 binding whose body is uncarved (wow-re `bindings.md`, structural row only); a \
|
||||
wow-re orchestrator is out on it and it is built when the carve lands (1960). Reached by CONFIRM_PET_UNLEARN's Accept, an event this engine does not fire yet.",
|
||||
),
|
||||
(
|
||||
"StaticPopup.xml",
|
||||
"GetInstanceBootTimeRemaining",
|
||||
"a registered 1.12 binding whose body is uncarved (wow-re `bindings.md`, structural row only); a \
|
||||
wow-re orchestrator is out on it and it is built when the carve lands (1960). Reached by INSTANCE_BOOT's OnShow, an event this engine does not fire yet.",
|
||||
),
|
||||
];
|
||||
|
||||
let toc = &super::super::addons::Addon::builtin().toc.files;
|
||||
@@ -2147,6 +2087,25 @@ mod tests {
|
||||
stale.join("\n ")
|
||||
);
|
||||
}
|
||||
/// `text` with every whitespace run that precedes a `.` removed — a method chain rustfmt
|
||||
/// broke across lines reads as one call again.
|
||||
fn glue_chains(text: &str) -> String {
|
||||
let mut out = String::with_capacity(text.len());
|
||||
let mut pending = String::new();
|
||||
for c in text.chars() {
|
||||
if c.is_whitespace() {
|
||||
pending.push(c);
|
||||
} else {
|
||||
if c != '.' {
|
||||
out.push_str(&pending);
|
||||
}
|
||||
pending.clear();
|
||||
out.push(c);
|
||||
}
|
||||
}
|
||||
out.push_str(&pending);
|
||||
out
|
||||
}
|
||||
|
||||
/// **A stock file listening for an event nothing produces is silent on both sides.**
|
||||
///
|
||||
@@ -2291,6 +2250,9 @@ mod tests {
|
||||
stack.push(p);
|
||||
} else if p.extension().is_some_and(|x| x == "rs") {
|
||||
let text = std::fs::read_to_string(&p).unwrap_or_default();
|
||||
// rustfmt splits a long chain at its dots (`model\n.pending_events\n.push((`), so
|
||||
// the three shapes are matched with the whitespace before each `.` removed.
|
||||
let text = glue_chains(&text);
|
||||
// The engine's deferred lane (`pending_events.push((name, args))`,
|
||||
// `cursor.rs`) is a fire too — the pet grid pair rides it (1953).
|
||||
const CALLS: [&str; 3] = [
|
||||
|
||||
@@ -15,7 +15,8 @@ fn harness() -> UiScript {
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
for f in [
|
||||
"Interface\\FrameXML\\Fonts.xml",
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
"UiPanels.xml",
|
||||
r"Interface\FrameXML\UIPanelTemplates.lua",
|
||||
r"Interface\FrameXML\UIPanelTemplates.xml",
|
||||
|
||||
@@ -97,7 +97,8 @@ pub(super) fn spellbook_ui(w: f32, h: f32) -> UiScript {
|
||||
"Interface\\FrameXML\\GlobalStrings.lua",
|
||||
"Interface\\FrameXML\\BasicControls.xml",
|
||||
"Interface\\FrameXML\\MainMenuBar.xml",
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
"GameTooltip.xml",
|
||||
"Interface\\FrameXML\\ActionBarFrame.xml",
|
||||
"Interface\\FrameXML\\BonusActionBarFrame.xml",
|
||||
@@ -129,7 +130,8 @@ pub(super) fn spellbook_ui(w: f32, h: f32) -> UiScript {
|
||||
fn shipped_spellbook_loads_clean() {
|
||||
let s = UiScript::new().unwrap();
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.xml");
|
||||
@@ -683,7 +685,8 @@ fn the_macro_editor_takes_a_shift_click_and_only_a_shift_click() {
|
||||
r"Interface\FrameXML\GlobalStrings.lua",
|
||||
"Interface\\FrameXML\\Fonts.xml",
|
||||
"Interface\\FrameXML\\BasicControls.xml", // `TEXT`
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
"UiPanels.xml",
|
||||
"Interface\\FrameXML\\LocaleProperties.lua",
|
||||
"Interface\\FrameXML\\StaticPopup.xml",
|
||||
|
||||
@@ -11,7 +11,8 @@ fn setup() -> UiScript {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.xml");
|
||||
@@ -262,3 +263,56 @@ fn hide_and_find_address_one_instance_by_data_only_for_a_multiple_dialog() {
|
||||
.unwrap());
|
||||
assert!(s.errors().is_empty(), "{:?}", s.errors());
|
||||
}
|
||||
|
||||
/// The dialog engine's verbs (decision 1963): the five UIParent arms the feeds behind them now
|
||||
/// reach — each verbatim from UIParent.lua, each raising a stock dialog whose Accept calls a
|
||||
/// binding this engine answers.
|
||||
#[test]
|
||||
fn the_verb_dialogs_open_from_their_events_and_call_their_verbs() {
|
||||
use benilla_ui::script::ScriptValue;
|
||||
let mut s = setup();
|
||||
load_xml(&s, "UIParent.xml"); // the arms
|
||||
s.set_money(50_000);
|
||||
// The pet trainer's question: the dialog, its money frame at the cost, Accept → the confirm.
|
||||
s.fire_event("CONFIRM_PET_UNLEARN", vec![ScriptValue::Int(12_345)]);
|
||||
s.tick(0.0);
|
||||
assert_eq!(
|
||||
s.eval::<String>("return StaticPopup1.which").unwrap(),
|
||||
"CONFIRM_PET_UNLEARN"
|
||||
);
|
||||
assert_eq!(
|
||||
s.eval::<f64>("return StaticPopup1MoneyFrame.staticMoney")
|
||||
.unwrap(),
|
||||
12_345.0
|
||||
);
|
||||
s.run("StaticPopup1Button1:Click()").unwrap();
|
||||
assert_eq!(s.take_pet_unlearn_confirms(), 1);
|
||||
// The instance boot: START shows, STOP hides.
|
||||
s.set_instance_boot_secs(30);
|
||||
s.fire_event("INSTANCE_BOOT_START", vec![]);
|
||||
s.tick(0.0);
|
||||
assert!(s
|
||||
.eval::<bool>("return StaticPopup_Visible(\"INSTANCE_BOOT\") ~= nil")
|
||||
.unwrap());
|
||||
s.fire_event("INSTANCE_BOOT_STOP", vec![]);
|
||||
s.tick(0.0);
|
||||
assert!(!s
|
||||
.eval::<bool>("return StaticPopup_Visible(\"INSTANCE_BOOT\") ~= nil")
|
||||
.unwrap());
|
||||
// The area spirit healer: in range shows, out of range hides; Accept queues the heal.
|
||||
s.set_area_spirit_healer(true, 20);
|
||||
s.fire_event("AREA_SPIRIT_HEALER_IN_RANGE", vec![]);
|
||||
s.tick(0.0);
|
||||
assert!(s
|
||||
.eval::<bool>("return StaticPopup_Visible(\"AREA_SPIRIT_HEAL\") ~= nil")
|
||||
.unwrap());
|
||||
s.run("StaticPopup_OnClick(StaticPopup_FindVisible(\"AREA_SPIRIT_HEAL\"), 1)")
|
||||
.unwrap();
|
||||
assert_eq!(s.take_area_spirit_accepts(), 1);
|
||||
s.fire_event("AREA_SPIRIT_HEALER_OUT_OF_RANGE", vec![]);
|
||||
s.tick(0.0);
|
||||
assert!(!s
|
||||
.eval::<bool>("return StaticPopup_Visible(\"AREA_SPIRIT_HEAL\") ~= nil")
|
||||
.unwrap());
|
||||
assert!(s.errors().is_empty(), "script errors: {:?}", s.errors());
|
||||
}
|
||||
|
||||
@@ -27,7 +27,8 @@ fn setup() -> UiScript {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.xml");
|
||||
|
||||
@@ -13,7 +13,8 @@ fn setup() -> UiScript {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.xml");
|
||||
|
||||
@@ -20,7 +20,8 @@ fn harness() -> UiScript {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "GameTooltip.xml"); // TOOLTIP_DEFAULT_* (the dropdown kit's MenuBackdrop)
|
||||
load_xml(&s, "Interface\\FrameXML\\UIDropDownMenu.xml"); // the unit popups' kit (TargetFrameDropDown's template)
|
||||
load_xml(&s, "Cooldown.xml");
|
||||
|
||||
@@ -15,7 +15,8 @@ fn taxi_script() -> UiScript {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\GlobalStrings.lua");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\BasicControls.xml");
|
||||
|
||||
@@ -145,7 +145,8 @@ pub(super) const MERCHANT_UI: &[&str] = &[
|
||||
"Interface\\FrameXML\\Fonts.xml",
|
||||
"Interface\\FrameXML\\BasicControls.xml", // TEXT()
|
||||
"Interface\\FrameXML\\ItemButtonTemplate.xml",
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
"UiPanels.xml",
|
||||
r"Interface\FrameXML\UIPanelTemplates.lua",
|
||||
r"Interface\FrameXML\UIPanelTemplates.xml",
|
||||
@@ -158,7 +159,8 @@ pub(super) const LOOT_UI: &[&str] = &[
|
||||
"Interface\\FrameXML\\GlobalStrings.lua",
|
||||
"Interface\\FrameXML\\Fonts.xml",
|
||||
"Interface\\FrameXML\\ItemButtonTemplate.xml",
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
"UiPanels.xml", // StaticPopup, and the LOOT_BIND / CONFIRM_LOOT_DISTRIBUTION dialogs
|
||||
r"Interface\FrameXML\UIPanelTemplates.lua",
|
||||
r"Interface\FrameXML\UIPanelTemplates.xml",
|
||||
@@ -237,7 +239,8 @@ pub(super) const CHARACTER_UI: &[&str] = &[
|
||||
r"Interface\FrameXML\LocaleProperties.lua",
|
||||
"Interface\\FrameXML\\BasicControls.xml", // TEXT(), which every one of those label sets goes through
|
||||
"Interface\\FrameXML\\ItemButtonTemplate.xml", // PaperDollItemSlotButtonTemplate's base
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
"UIParent.xml", // Model_OnLoad/_Rotate*/_OnUpdate — the model panes' turntable
|
||||
"UiPanels.xml", // CharacterFrameTabButtonTemplate, UIPanelWindows, Show/HideUIPanel
|
||||
"GameTooltip.xml",
|
||||
@@ -312,7 +315,8 @@ pub(super) const SOCIAL_UI: &[&str] = &[
|
||||
"Interface\\FrameXML\\TextStatusBar.lua",
|
||||
"Interface\\FrameXML\\TextStatusBar.xml",
|
||||
"Interface\\FrameXML\\MainMenuBar.xml",
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
"GameTooltip.xml",
|
||||
"Interface\\FrameXML\\ActionBarFrame.xml",
|
||||
"Interface\\FrameXML\\BonusActionBarFrame.xml",
|
||||
@@ -362,7 +366,8 @@ pub(super) const BAG_UI: &[&str] = &[
|
||||
// windows fall out of the cascade and the reference's own layout pass has nothing to measure.
|
||||
"UIParent.xml",
|
||||
"Interface\\FrameXML\\ItemButtonTemplate.xml",
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
"UiPanels.xml",
|
||||
"Interface\\FrameXML\\LocaleProperties.lua",
|
||||
"GameTooltip.xml",
|
||||
@@ -500,3 +505,16 @@ pub(super) fn click(s: &mut UiScript, name: &str, button: &str) {
|
||||
s.mouse_button(x, y, button, true);
|
||||
s.mouse_button(x, y, button, false);
|
||||
}
|
||||
|
||||
/// Seat one of the reference's LoadOnDemand Blizzard addons off the chain, so a harness's
|
||||
/// `UIParentLoadAddOn(name)` can load it the way the app does (1957; the combat text since
|
||||
/// 1964). Needs client data — the caller has already checked with `wow_data_or_skip!`.
|
||||
pub(super) fn seat_chain_addon(s: &mut UiScript, name: &str) {
|
||||
let toc = super::reference_ui::read(&format!("Interface/AddOns/{name}/{name}.toc"))
|
||||
.map(|b| benilla_ui::toc::Toc::parse(&benilla_ui::source::decode(&b)))
|
||||
.unwrap_or_else(|| panic!("{name}: no toc off the chain"));
|
||||
let mut info = super::addons::info_from_toc(name, &toc);
|
||||
info.chain = true;
|
||||
s.set_addon_chain_reader(Box::new(super::reference_ui::read));
|
||||
s.register_addons(vec![info], None, None, None);
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@ fn harness(extra: &[&str]) -> UiScript {
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "UIParent.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
for f in extra {
|
||||
load_xml(&s, f);
|
||||
@@ -239,8 +240,6 @@ fn action_button_hover_takes_the_default_corner() {
|
||||
"UIParent.xml",
|
||||
"Interface\\FrameXML\\GlobalStrings.lua",
|
||||
"Interface\\FrameXML\\MainMenuBar.xml",
|
||||
"MoneyFrame.xml",
|
||||
"GameTooltip.xml",
|
||||
"Interface\\FrameXML\\ActionBarFrame.xml",
|
||||
"Interface\\FrameXML\\BonusActionBarFrame.xml",
|
||||
]);
|
||||
@@ -282,8 +281,6 @@ fn ubertooltips_off_seats_action_bar_plates_beside_the_button() {
|
||||
"UIParent.xml",
|
||||
"Interface\\FrameXML\\GlobalStrings.lua",
|
||||
"Interface\\FrameXML\\MainMenuBar.xml",
|
||||
"MoneyFrame.xml",
|
||||
"GameTooltip.xml",
|
||||
"Interface\\FrameXML\\ActionBarFrame.xml",
|
||||
"Interface\\FrameXML\\BonusActionBarFrame.xml",
|
||||
"Interface\\FrameXML\\ActionBarFrame.xml",
|
||||
@@ -365,8 +362,6 @@ fn ubertooltips_off_seats_stance_plates_beside_the_button() {
|
||||
"UIParent.xml",
|
||||
"Interface\\FrameXML\\GlobalStrings.lua",
|
||||
"Interface\\FrameXML\\MainMenuBar.xml",
|
||||
"MoneyFrame.xml",
|
||||
"GameTooltip.xml",
|
||||
"Interface\\FrameXML\\ActionBarFrame.xml",
|
||||
"Interface\\FrameXML\\BonusActionBarFrame.xml",
|
||||
"Interface\\FrameXML\\ActionBarFrame.xml",
|
||||
@@ -420,8 +415,6 @@ fn buff_hover_hangs_below_left_of_the_button() {
|
||||
"UIParent.xml",
|
||||
"Interface\\FrameXML\\GlobalStrings.lua",
|
||||
"Interface\\FrameXML\\MainMenuBar.xml",
|
||||
"MoneyFrame.xml",
|
||||
"GameTooltip.xml",
|
||||
"Interface\\FrameXML\\ActionBarFrame.xml",
|
||||
"Interface\\FrameXML\\BonusActionBarFrame.xml",
|
||||
"Interface\\FrameXML\\TextStatusBar.lua",
|
||||
|
||||
@@ -44,7 +44,8 @@ fn harness() -> UiScript {
|
||||
for f in [
|
||||
"Interface\\FrameXML\\Fonts.xml",
|
||||
"UIParent.xml",
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
"GameTooltip.xml",
|
||||
] {
|
||||
load_xml(&s, f);
|
||||
|
||||
@@ -62,7 +62,8 @@ fn load_tot() -> UiScript {
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "UIParent.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\UIDropDownMenu.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\BasicControls.xml");
|
||||
|
||||
@@ -54,7 +54,8 @@ fn trainer_script_base() -> UiScript {
|
||||
"Interface\\FrameXML\\TextStatusBar.lua",
|
||||
"Interface\\FrameXML\\TextStatusBar.xml",
|
||||
"Interface\\FrameXML\\MainMenuBar.xml",
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
"GameTooltip.xml",
|
||||
"Interface\\FrameXML\\ActionBarFrame.xml",
|
||||
"Interface\\FrameXML\\BonusActionBarFrame.xml",
|
||||
|
||||
@@ -25,7 +25,8 @@ fn load_unit_frames(s: &UiScript) {
|
||||
// UnitFrames.xml and which every bar's OnLoad wires into since 1143.
|
||||
load_xml(s, "Interface\\FrameXML\\TextStatusBar.lua");
|
||||
load_xml(s, "Interface\\FrameXML\\TextStatusBar.xml");
|
||||
load_xml(s, "MoneyFrame.xml");
|
||||
load_xml(s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(s, "GameTooltip.xml");
|
||||
load_xml(s, "Interface\\FrameXML\\UIDropDownMenu.xml");
|
||||
load_xml(s, "Interface\\FrameXML\\BasicControls.xml"); // `TEXT`, which UnitPopup.lua reads at file scope
|
||||
@@ -646,7 +647,6 @@ fn shipped_target_frame_runs_the_level_law() {
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_unit_frames(&s);
|
||||
// GetDifficultyColor's own load chain (the quest log window, its ref home).
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, "Interface\\FrameXML\\GlobalStrings.lua");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.lua");
|
||||
@@ -1212,7 +1212,8 @@ fn the_party_art_paints_over_the_bars() {
|
||||
// files resolve it at LOAD (`CombatFeedback.lua` l.7-17, `UnitFrame.lua` l.1-6).
|
||||
load_xml(&s, "Interface\\FrameXML\\GlobalStrings.lua");
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.xml");
|
||||
|
||||
@@ -18,7 +18,8 @@ fn load_popup_frames(s: &UiScript) {
|
||||
"Interface\\FrameXML\\Fonts.xml",
|
||||
"UIParent.xml",
|
||||
// `SmallMoneyFrame_OnLoad`, which UiPanels' own StaticPopup money rows call at load.
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
// `StaticPopupDialogs` and the `PanelTemplates_*` family, both of which FriendsFrame.xml
|
||||
// reaches at LOAD (its tab row and its confirm dialogs).
|
||||
"UiPanels.xml",
|
||||
@@ -484,7 +485,8 @@ fn load_pet_menu_frames(s: &UiScript) {
|
||||
for file in [
|
||||
"Interface\\FrameXML\\Fonts.xml",
|
||||
"UIParent.xml",
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
"Interface\\FrameXML\\GlobalStrings.lua",
|
||||
"UiPanels.xml",
|
||||
r"Interface\FrameXML\UIPanelTemplates.lua",
|
||||
|
||||
@@ -14,7 +14,8 @@ fn harness() -> UiScript {
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
for file in [
|
||||
"Interface\\FrameXML\\Fonts.xml",
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
"UiPanels.xml",
|
||||
r"Interface\FrameXML\UIPanelTemplates.lua",
|
||||
r"Interface\FrameXML\UIPanelTemplates.xml",
|
||||
|
||||
@@ -16,7 +16,8 @@ pub(crate) fn harness() -> UiScript {
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
for f in [
|
||||
"Interface\\FrameXML\\Fonts.xml",
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
"UiPanels.xml",
|
||||
r"Interface\FrameXML\UIPanelTemplates.lua",
|
||||
r"Interface\FrameXML\UIPanelTemplates.xml",
|
||||
|
||||
@@ -43,7 +43,8 @@ fn harness() -> UiScript {
|
||||
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
|
||||
// `TEXT()`, which AutoFollowStatus_OnEvent puts its message through.
|
||||
load_xml(&s, "Interface\\FrameXML\\BasicControls.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.lua");
|
||||
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.xml");
|
||||
|
||||
@@ -488,6 +488,8 @@ mod tests {
|
||||
// the registrar's case, the server does not close the menu first.
|
||||
("TalentWipeState", "rides the still-open gossip session"),
|
||||
("BinderState", "rides the still-open gossip session"),
|
||||
// The pet trainer's question (1963): the talent wipe's twin, reached the same way.
|
||||
("PetUnlearnState", "rides the still-open gossip session"),
|
||||
];
|
||||
|
||||
// The chain's own source is the authority on what is wired — not a hand-copied list here,
|
||||
|
||||
@@ -325,6 +325,11 @@ pub(super) fn drain_social(
|
||||
SocialRequest::AddFriend(name) => {
|
||||
let _ = commands.0.send(ClientCommand::AddFriend { name });
|
||||
}
|
||||
SocialRequest::SetLookingForGroup { slots, comment } => {
|
||||
let _ = commands
|
||||
.0
|
||||
.send(ClientCommand::SetLookingForGroup { slots, comment });
|
||||
}
|
||||
SocialRequest::RemoveFriendIndex(index) => {
|
||||
if let Some(guid) = row_guid(&social.display_order, index) {
|
||||
let _ = commands.0.send(ClientCommand::DelFriend { guid });
|
||||
|
||||
@@ -299,6 +299,10 @@ impl Plugin for UiTradePlugin {
|
||||
// engine state — so it sits ahead of the feed, and an accepted request's
|
||||
// `partner` is on screen the same frame the window opens.
|
||||
answer_trade_request.before(feed_trade),
|
||||
// The coinage-change reflex's trade half (1965): after the world's fields land.
|
||||
trim_offer_to_purse
|
||||
.after(crate::ui_unit::UnitFeed)
|
||||
.before(feed_trade),
|
||||
drain_trade.after(UiInput),
|
||||
),
|
||||
);
|
||||
@@ -608,7 +612,7 @@ fn snapshot(
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn feed_trade(
|
||||
script: Option<NonSendMut<UiScript>>,
|
||||
trade: Res<TradeSession>,
|
||||
mut trade: ResMut<TradeSession>,
|
||||
mut items: ResMut<Items>,
|
||||
icons: Option<Res<ItemDisplays>>,
|
||||
mut names: ResMut<NameCache>,
|
||||
@@ -617,6 +621,7 @@ fn feed_trade(
|
||||
mut last_open: Local<crate::ui_script::VmMemo<bool>>,
|
||||
mut last_accept: Local<crate::ui_script::VmMemo<(bool, bool)>>,
|
||||
mut last_player_gold: Local<crate::ui_script::VmMemo<u32>>,
|
||||
mut last_their_gold: Local<crate::ui_script::VmMemo<u32>>,
|
||||
) {
|
||||
let Some(mut script) = script else {
|
||||
return;
|
||||
@@ -625,6 +630,7 @@ fn feed_trade(
|
||||
let last_open = last_open.get(&script);
|
||||
let last_accept = last_accept.get(&script);
|
||||
let last_player_gold = last_player_gold.get(&script);
|
||||
let last_their_gold = last_their_gold.get(&script);
|
||||
|
||||
let fresh = snapshot(&trade, &mut items, icons.as_deref(), &mut names, &commands);
|
||||
let opened = !*last_open && trade.is_open();
|
||||
@@ -633,7 +639,40 @@ fn feed_trade(
|
||||
if changed {
|
||||
script.set_trade(fresh.clone());
|
||||
}
|
||||
// The per-slot events the stock TradeFrame.lua repaints one slot on (`0x4bf414`/`0x4bf452`
|
||||
// TRADE_TARGET_ITEM_CHANGED, `0x4bf487`/`0x4bfaef` TRADE_PLAYER_ITEM_CHANGED, arg1 the 1-based
|
||||
// slot — wow-re `event-firesites.tsv`; decision 1966). The full TRADE_UPDATE below is kept:
|
||||
// the reference fires it too, from `0x4c034f`.
|
||||
if changed && trade.is_open() && !opened {
|
||||
let empty = TradeState::default();
|
||||
let old = last.as_ref().unwrap_or(&empty);
|
||||
if let Some(new) = fresh.as_ref() {
|
||||
let changed_slots = |mine: &[Option<TradeSlotItem>],
|
||||
theirs: &[Option<TradeSlotItem>]| {
|
||||
mine.iter()
|
||||
.zip(theirs.iter())
|
||||
.enumerate()
|
||||
.filter(|(_, (a, b))| a != b)
|
||||
.map(|(i, _)| ScriptValue::Int(i as i64 + 1))
|
||||
.collect::<Vec<_>>()
|
||||
};
|
||||
for slot in changed_slots(&new.player.slots, &old.player.slots) {
|
||||
script.fire_event("TRADE_PLAYER_ITEM_CHANGED", vec![slot]);
|
||||
}
|
||||
for slot in changed_slots(&new.target.slots, &old.target.slots) {
|
||||
script.fire_event("TRADE_TARGET_ITEM_CHANGED", vec![slot]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if opened {
|
||||
// `SetTradePartner 0x4bf4e0`'s open leg (decision 1965): coins held on the cursor fold
|
||||
// into the offer before anything else — the one leg that fires the two money events
|
||||
// locally, the send following through the money drain below.
|
||||
if let Some(offer) = script.fold_cursor_money_into_trade() {
|
||||
trade.set_own_gold(offer);
|
||||
script.fire_event("PLAYER_TRADE_MONEY", vec![]);
|
||||
script.fire_event("PLAYER_MONEY", vec![]);
|
||||
}
|
||||
script.fire_event("TRADE_SHOW", vec![]);
|
||||
} else if closed {
|
||||
script.fire_event("TRADE_CLOSED", vec![]);
|
||||
@@ -666,6 +705,16 @@ fn feed_trade(
|
||||
script.fire_event("PLAYER_TRADE_MONEY", vec![]);
|
||||
}
|
||||
*last_player_gold = if trade.is_open() { player_gold } else { 0 };
|
||||
// The partner's gold: TRADE_MONEY_CHANGED, the event the stock money kit's TARGET_TRADE
|
||||
// frame repaints on (MoneyFrame.lua's OnEvent pairs the two exactly this way — ours on
|
||||
// PLAYER_TRADE_MONEY, theirs on TRADE_MONEY_CHANGED; the reference's fire site `0x4bf4d6`
|
||||
// sits beside PLAYER_TRADE_MONEY's `0x4bf4ab` in the status handler). Which side each site
|
||||
// reads is INFERRED from the consumer; flagged (1962).
|
||||
let their_gold = trade.their.gold;
|
||||
if trade.is_open() && their_gold != *last_their_gold {
|
||||
script.fire_event("TRADE_MONEY_CHANGED", vec![]);
|
||||
}
|
||||
*last_their_gold = if trade.is_open() { their_gold } else { 0 };
|
||||
|
||||
*last = fresh;
|
||||
*last_open = trade.is_open();
|
||||
@@ -768,6 +817,50 @@ fn drain_trade(
|
||||
}
|
||||
trade.close();
|
||||
}
|
||||
// The TRADE dialog's pair (decision 1963): `BeginTrade` is the empty `0x117`, `CancelTrade`
|
||||
// the bare `0x11C` — no teardown of ours, the server's status reply drives the window.
|
||||
if script.take_trade_begin() {
|
||||
let _ = commands.0.send(ClientCommand::BeginTrade);
|
||||
}
|
||||
if script.take_trade_cancel() {
|
||||
let _ = commands.0.send(ClientCommand::CancelTrade);
|
||||
}
|
||||
}
|
||||
|
||||
/// The offer the purse can no longer cover, if any — the coinage-change reflex (`0x5ddf30`,
|
||||
/// decision 1965): when the purse drops below the standing offer, the client trims the offer to
|
||||
/// the purse and sends it.
|
||||
fn trimmed_offer(offer: u32, purse: u32) -> Option<u32> {
|
||||
(offer > purse).then_some(purse)
|
||||
}
|
||||
|
||||
/// The coinage-change reflex's trade half: on every change of `PLAYER_FIELD_COINAGE` with a trade
|
||||
/// open, an offer past the purse is trimmed to it and re-sent as an absolute `CMSG_SET_TRADE_GOLD`
|
||||
/// (the coin sound and `PLAYER_MONEY` are `sound/money.rs`'s and the aura feed's).
|
||||
fn trim_offer_to_purse(
|
||||
self_q: Query<&ObjectStore, With<SelfPlayer>>,
|
||||
mut trade: ResMut<TradeSession>,
|
||||
commands: Res<NetCommands>,
|
||||
mut prev: Local<Option<u32>>,
|
||||
) {
|
||||
let Some(money) = self_q
|
||||
.iter()
|
||||
.next()
|
||||
.and_then(|store| store.0.player_money())
|
||||
else {
|
||||
*prev = None;
|
||||
return;
|
||||
};
|
||||
let old = prev.replace(money);
|
||||
if !matches!(old, Some(p) if p != money) || !trade.is_open() {
|
||||
return;
|
||||
}
|
||||
if let Some(trimmed) = trimmed_offer(trade.our.gold, money) {
|
||||
trade.set_own_gold(trimmed);
|
||||
let _ = commands
|
||||
.0
|
||||
.send(ClientCommand::SetTradeGold { copper: trimmed });
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolve a UnitPopup unit token to the player guid to trade with (decision 0592 P1). The
|
||||
@@ -1270,7 +1363,9 @@ mod tests {
|
||||
.init_resource::<Items>()
|
||||
.insert_resource(NetCommands(tx))
|
||||
.insert_resource(Selection::default());
|
||||
app.insert_non_send_resource(UiScript::new().unwrap());
|
||||
let mut vm = UiScript::new().unwrap();
|
||||
vm.set_money(20_000); // the engine's purse gate on SetTradeMoney (1965)
|
||||
app.insert_non_send_resource(vm);
|
||||
|
||||
// No window yet → the offer is dropped.
|
||||
app.world_mut()
|
||||
|
||||
@@ -22,7 +22,8 @@ mod common;
|
||||
/// on its way to the chain, so the dependency is gone.
|
||||
const FILES: &[&str] = &[
|
||||
"Interface\\FrameXML\\Fonts.xml",
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
// The four money-entry frames come off the chain since 1882 — `MoneyInputFrameTemplate` and
|
||||
// the `MoneyInputFrame_*` verbs, replacing our own verbatim copy of both.
|
||||
r"Interface\FrameXML\MoneyInputFrame.lua",
|
||||
|
||||
@@ -39,7 +39,8 @@ const FILES: &[&str] = &[
|
||||
"Interface\\FrameXML\\BasicControls.xml",
|
||||
// The reference's own since 1751 window 24 — `common::load_ui` speaks both stores.
|
||||
"Interface\\FrameXML\\ItemButtonTemplate.xml",
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
// `Model_OnLoad` — the model pane's `<OnLoad>` calls it, so this is a LOAD-time dependency of
|
||||
// the paper doll, not of the window. The reference declares it in `UIParent.lua`; ours lives
|
||||
// in our counterpart of that file.
|
||||
|
||||
@@ -41,7 +41,8 @@ const FILES: &[&str] = &[
|
||||
// `UnitFrame.lua` fails the same way (`TEXT(MANA)`). Structural dependencies are not the only
|
||||
// kind.
|
||||
"Interface\\FrameXML\\FadingFrame.xml",
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
"UiPanels.xml",
|
||||
r"Interface\FrameXML\UIPanelTemplates.lua",
|
||||
r"Interface\FrameXML\UIPanelTemplates.xml",
|
||||
|
||||
@@ -24,7 +24,8 @@ const FILES: &[&str] = &[
|
||||
// so the window simply never opens. Our deleted copy carried the string as a local fallback.
|
||||
"Interface\\FrameXML\\GlobalStrings.lua",
|
||||
"Interface\\FrameXML\\Fonts.xml",
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
"UiPanels.xml",
|
||||
// `GetMaterialTextColors`, which the reference's own `ItemTextFrame_OnEvent` calls to pick the
|
||||
// page and title ink. 1.12 keeps it in UIParent.lua and ours does the same (1751 window 9).
|
||||
|
||||
@@ -14,7 +14,8 @@ const UI_DIR: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/assets/ui");
|
||||
/// helpers (postage display), so a load error in either fails here.
|
||||
const FILES: &[&str] = &[
|
||||
"Interface\\FrameXML\\Fonts.xml",
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
// The send tab's money entry comes off the chain since 1882 — `MoneyInputFrameTemplate` and
|
||||
// the `MoneyInputFrame_*` verbs. Seated straight after MoneyFrame.xml, benilla.toc's order.
|
||||
r"Interface\FrameXML\MoneyInputFrame.lua",
|
||||
|
||||
@@ -27,7 +27,8 @@ const FILES: &[&str] = &[
|
||||
// `TEXT`.
|
||||
"Interface\\FrameXML\\BasicControls.xml",
|
||||
"Interface\\FrameXML\\ItemButtonTemplate.xml",
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
"UiPanels.xml",
|
||||
// `ToggleTalentFrame` lives here now, not in the window's own file (decision 1833).
|
||||
"UIParent.xml",
|
||||
|
||||
@@ -15,7 +15,8 @@ const UI_DIR: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/assets/ui");
|
||||
/// helpers (the two gold displays), so a load error in either fails here.
|
||||
const FILES: &[&str] = &[
|
||||
"Interface\\FrameXML\\Fonts.xml",
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
// The money entry comes off the chain since 1882 — `MoneyInputFrameTemplate` and the
|
||||
// `MoneyInputFrame_*` verbs the window's OnLoad calls. Seated straight after MoneyFrame.xml,
|
||||
// which is benilla.toc's own order.
|
||||
@@ -29,7 +30,9 @@ const FILES: &[&str] = &[
|
||||
"Interface\\FrameXML\\LocaleProperties.lua",
|
||||
"Interface\\FrameXML\\StaticPopup.xml", // the dialog engine (1960)
|
||||
"GameTooltip.xml",
|
||||
"TradeFrame.xml",
|
||||
// The stock slot updates go through ItemButtonTemplate.lua's SetItemButton* helpers (1966).
|
||||
r"Interface\FrameXML\ItemButtonTemplate.xml",
|
||||
"Interface\\FrameXML\\TradeFrame.xml",
|
||||
];
|
||||
|
||||
fn load_ui(script: &UiScript) {
|
||||
@@ -123,7 +126,7 @@ fn trade_frame_loads_and_key_regions_exist() {
|
||||
"TradeRecipientItem7",
|
||||
"TradeFrameTradeButton",
|
||||
"TradeFrameCancelButton",
|
||||
"TradePlayerInputMoneyGold", // our gold is now the editable input (P2)
|
||||
"TradePlayerInputMoneyFrameGold", // our gold is now the editable input (P2)
|
||||
"TradeRecipientMoneyFrameCopperButton",
|
||||
"TradeHighlightPlayer",
|
||||
"TradeHighlightRecipientEnchant",
|
||||
@@ -144,6 +147,16 @@ fn trade_show_opens_and_both_columns_populate() {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
load_ui(&s);
|
||||
s.set_trade(Some(state()));
|
||||
// The stock header reads `UnitName("NPC")` — the partner rides the shared "npc" booth the
|
||||
// app's session feed points at them (1966).
|
||||
s.set_unit(
|
||||
"npc",
|
||||
Some(benilla_ui::script::UnitState {
|
||||
exists: true,
|
||||
name: Some("Thrall".into()),
|
||||
..Default::default()
|
||||
}),
|
||||
);
|
||||
|
||||
s.fire_event("TRADE_SHOW", vec![]);
|
||||
assert!(
|
||||
@@ -174,18 +187,18 @@ fn trade_show_opens_and_both_columns_populate() {
|
||||
"Silk Cloth"
|
||||
);
|
||||
assert!(
|
||||
s.eval::<bool>("return TradePlayerItem1ItemButtonIcon:IsShown()")
|
||||
s.eval::<bool>("return TradePlayerItem1ItemButtonIconTexture:IsShown()")
|
||||
.unwrap(),
|
||||
"a filled slot shows its icon"
|
||||
);
|
||||
// An empty slot clears its name + hides its icon.
|
||||
assert_eq!(
|
||||
s.eval::<String>("return TradePlayerItem2Name:GetText()")
|
||||
.unwrap(),
|
||||
""
|
||||
);
|
||||
// An empty slot clears its name (the stock's `SetText(nil)`) + hides its icon.
|
||||
assert!(s
|
||||
.eval::<Option<String>>("return TradePlayerItem2Name:GetText()")
|
||||
.unwrap()
|
||||
.unwrap_or_default()
|
||||
.is_empty());
|
||||
assert!(!s
|
||||
.eval::<bool>("return TradePlayerItem2ItemButtonIcon:IsShown()")
|
||||
.eval::<bool>("return TradePlayerItem2ItemButtonIconTexture:IsShown()")
|
||||
.unwrap());
|
||||
|
||||
// The partner's name paints from GetTradePartnerName().
|
||||
@@ -224,7 +237,8 @@ fn enchant_slot_shows_the_not_traded_note() {
|
||||
assert_eq!(
|
||||
s.eval::<String>("return TradePlayerItem7Name:GetText()")
|
||||
.unwrap(),
|
||||
"|cffffffffWill Not Be Traded|r"
|
||||
// The stock wording, TRADEFRAME_NOT_MODIFIED_TEXT (ours had read "Will Not Be Traded").
|
||||
"|cffffffffItem not yet modified|r"
|
||||
);
|
||||
assert!(s.take_errors().is_empty());
|
||||
}
|
||||
@@ -313,6 +327,7 @@ fn trade_button_click_queues_accept() {
|
||||
fn player_money_input_reflects_then_offers() {
|
||||
let _data = benilla_formats::wow_data_or_skip!();
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_money(1_000_000); // SetTradeMoney is purse-gated (1965)
|
||||
load_ui(&s);
|
||||
s.set_trade(Some(state())); // state().player.gold == 12345 (1g 23s 45c)
|
||||
s.fire_event("TRADE_SHOW", vec![]);
|
||||
@@ -321,7 +336,7 @@ fn player_money_input_reflects_then_offers() {
|
||||
for box_ in ["Gold", "Silver", "Copper"] {
|
||||
assert!(
|
||||
s.eval::<bool>(&format!(
|
||||
"return getglobal('TradePlayerInputMoney{box_}') ~= nil"
|
||||
"return getglobal('TradePlayerInputMoneyFrame{box_}') ~= nil"
|
||||
))
|
||||
.unwrap(),
|
||||
"money box {box_} exists"
|
||||
@@ -334,9 +349,9 @@ fn player_money_input_reflects_then_offers() {
|
||||
s.fire_event("PLAYER_TRADE_MONEY", vec![]);
|
||||
assert_eq!(
|
||||
s.eval::<(String, String, String)>(
|
||||
"return TradePlayerInputMoneyGold:GetText(), \
|
||||
TradePlayerInputMoneySilver:GetText(), \
|
||||
TradePlayerInputMoneyCopper:GetText()"
|
||||
"return TradePlayerInputMoneyFrameGold:GetText(), \
|
||||
TradePlayerInputMoneyFrameSilver:GetText(), \
|
||||
TradePlayerInputMoneyFrameCopper:GetText()"
|
||||
)
|
||||
.unwrap(),
|
||||
("1".into(), "23".into(), "45".into()),
|
||||
@@ -351,7 +366,8 @@ fn player_money_input_reflects_then_offers() {
|
||||
// A genuine keystroke offers the running total (bypass the affordability clamp — a bare harness
|
||||
// has no purse).
|
||||
s.run("GetMoney = function() return 100000000 end").unwrap();
|
||||
s.run("TradePlayerInputMoneyGold:SetText('2')").unwrap();
|
||||
s.run("TradePlayerInputMoneyFrameGold:SetText('2')")
|
||||
.unwrap();
|
||||
s.tick(0.0); // the deferred OnTextChanged drains here (decision 1831)
|
||||
assert_eq!(
|
||||
s.take_trade_money(),
|
||||
|
||||
@@ -22,7 +22,8 @@ mod common;
|
||||
/// its guarded-global utilities) so a load error in EITHER window fails here.
|
||||
const FILES: &[&str] = &[
|
||||
"Interface\\FrameXML\\Fonts.xml",
|
||||
"MoneyFrame.xml",
|
||||
r"Interface\FrameXML\MoneyFrame.lua",
|
||||
r"Interface\FrameXML\MoneyFrame.xml",
|
||||
"UiPanels.xml",
|
||||
r"Interface\FrameXML\UIPanelTemplates.lua",
|
||||
r"Interface\FrameXML\UIPanelTemplates.xml",
|
||||
|
||||
@@ -465,6 +465,24 @@ pub enum SessionEvent {
|
||||
/// `trainer` to answer with and the `cost` in copper its money frame shows. Answering means
|
||||
/// sending the SAME opcode back with the guid; declining sends nothing (decision 1580).
|
||||
TalentWipeConfirm { trainer: u64, cost: u32 },
|
||||
/// A pet trainer is asking whether to unlearn the pet's skills (`SMSG_PET_UNLEARN_CONFIRM`) —
|
||||
/// the `CONFIRM_PET_UNLEARN(cost)` dialog's question, the talent-wipe twin for a pet
|
||||
/// (decision 1963). Answering sends `CMSG_PET_UNLEARN` with the guid; declining sends nothing.
|
||||
PetUnlearnConfirm { trainer: u64, cost: u32 },
|
||||
/// The instance-boot clock (`SMSG_RAID_GROUP_ONLY`): a positive delay arms it
|
||||
/// (`INSTANCE_BOOT_START`), zero clears it (`INSTANCE_BOOT_STOP`) and names `reason` 1/2 on
|
||||
/// screen (decision 1963).
|
||||
RaidGroupOnly { delay_ms: u32, reason: u32 },
|
||||
/// A battleground spirit healer's next resurrection wave (`SMSG_AREA_SPIRIT_HEALER_TIME`):
|
||||
/// arms `GetAreaSpiritHealerTime` and fires `AREA_SPIRIT_HEALER_IN_RANGE` for the cached
|
||||
/// healer (decision 1963).
|
||||
AreaSpiritHealerTime { healer: u64, ms: u32 },
|
||||
/// One battleground queue slot's state (`SMSG_BATTLEFIELD_STATUS`), the client's three-slot
|
||||
/// queue that `AcceptBattlefieldPort` answers out of (decision 1963).
|
||||
BattlefieldStatus(crate::messages::BattlefieldStatus),
|
||||
/// The meeting-stone queue state (`SMSG 0x295`): the area queued for and a status byte
|
||||
/// (decision 1963).
|
||||
MeetingStoneSetQueue { area: u32, status: u8 },
|
||||
/// The bind took (`SMSG_PLAYERBOUND`): `area` is the AreaTable id we are now bound in, the
|
||||
/// same one [`Self::BindPoint`] carries in the packet beside it.
|
||||
PlayerBound { binder: u64, area: u32 },
|
||||
|
||||
@@ -774,6 +774,19 @@ pub fn decode(packet: ServerPacket) -> Vec<SessionEvent> {
|
||||
ServerPacket::TalentWipeConfirm { trainer, cost } => {
|
||||
vec![SessionEvent::TalentWipeConfirm { trainer, cost }]
|
||||
}
|
||||
ServerPacket::PetUnlearnConfirm { trainer, cost } => {
|
||||
vec![SessionEvent::PetUnlearnConfirm { trainer, cost }]
|
||||
}
|
||||
ServerPacket::RaidGroupOnly { delay_ms, reason } => {
|
||||
vec![SessionEvent::RaidGroupOnly { delay_ms, reason }]
|
||||
}
|
||||
ServerPacket::AreaSpiritHealerTime { healer, ms } => {
|
||||
vec![SessionEvent::AreaSpiritHealerTime { healer, ms }]
|
||||
}
|
||||
ServerPacket::BattlefieldStatus(status) => vec![SessionEvent::BattlefieldStatus(status)],
|
||||
ServerPacket::MeetingStoneSetQueue { area, status } => {
|
||||
vec![SessionEvent::MeetingStoneSetQueue { area, status }]
|
||||
}
|
||||
ServerPacket::PlayerBound { binder, area } => {
|
||||
vec![SessionEvent::PlayerBound { binder, area }]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
//! The battleground queue's wire (decision 1963; wow-re `staticpopup-dialog-bindings.md` §7):
|
||||
//! the server's per-slot status and the port answer `AcceptBattlefieldPort` sends. Three queue
|
||||
//! slots exist in the client (`0xb6e9d0`, stride `0x20`), addressed 1-based from Lua.
|
||||
|
||||
use std::io::{self, Read};
|
||||
|
||||
use crate::wire::{read_u32_le, read_u8};
|
||||
|
||||
/// `SMSG_BATTLEFIELD_STATUS`, one slot's update (VERIFIED at the bytes, handler `0x4aa850`).
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct BattlefieldStatus {
|
||||
/// Which of the three slots, 0-based on the wire.
|
||||
pub slot: u32,
|
||||
/// The battleground's Map.dbc row id; zero clears the slot.
|
||||
pub map_id: u32,
|
||||
pub bracket: u8,
|
||||
/// Read into `+0x10`; no Lua reader was found for it (parked by the carve).
|
||||
pub unknown: u32,
|
||||
pub status: u32,
|
||||
/// Status 2: the queue wait so far, in ms (`+0x14 = now + v`).
|
||||
pub time_ms: Option<u32>,
|
||||
/// Status 3: the two dwords the client parks beside the selected battlefield.
|
||||
pub in_progress: Option<(u32, u32)>,
|
||||
}
|
||||
|
||||
/// Parse `SMSG_BATTLEFIELD_STATUS`: `u32 slot`, `u32 mapId`, and — only for a non-zero map —
|
||||
/// `u8 bracket`, `u32`, `u32 status`, then the status-conditional tail.
|
||||
pub(super) fn read_battlefield_status(r: &mut impl Read) -> io::Result<BattlefieldStatus> {
|
||||
let slot = read_u32_le(r)?;
|
||||
let map_id = read_u32_le(r)?;
|
||||
if map_id == 0 {
|
||||
return Ok(BattlefieldStatus {
|
||||
slot,
|
||||
map_id,
|
||||
bracket: 0,
|
||||
unknown: 0,
|
||||
status: 0,
|
||||
time_ms: None,
|
||||
in_progress: None,
|
||||
});
|
||||
}
|
||||
let bracket = read_u8(r)?;
|
||||
let unknown = read_u32_le(r)?;
|
||||
let status = read_u32_le(r)?;
|
||||
let time_ms = if status == 2 {
|
||||
Some(read_u32_le(r)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let in_progress = if status == 3 {
|
||||
Some((read_u32_le(r)?, read_u32_le(r)?))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Ok(BattlefieldStatus {
|
||||
slot,
|
||||
map_id,
|
||||
bracket,
|
||||
unknown,
|
||||
status,
|
||||
time_ms,
|
||||
in_progress,
|
||||
})
|
||||
}
|
||||
|
||||
/// Body of `CMSG_BATTLEFIELD_PORT` (VERIFIED, `0x4ab3b0`): `u32 mapId` then a genuinely
|
||||
/// one-byte `accept`, normalised to 0/1 before it reaches the wire.
|
||||
pub fn battlefield_port(map_id: u32, accept: bool) -> Vec<u8> {
|
||||
let mut body = map_id.to_le_bytes().to_vec();
|
||||
body.push(u8::from(accept));
|
||||
body
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn status_reads_the_conditional_tails() {
|
||||
let mut body = vec![
|
||||
1u8, 0, 0, 0, 30, 0, 0, 0, 5, 7, 0, 0, 0, 2, 0, 0, 0, 100, 0, 0, 0,
|
||||
];
|
||||
let s = read_battlefield_status(&mut body.as_slice()).unwrap();
|
||||
assert_eq!(
|
||||
(s.slot, s.map_id, s.bracket, s.unknown, s.status),
|
||||
(1, 30, 5, 7, 2)
|
||||
);
|
||||
assert_eq!(s.time_ms, Some(100));
|
||||
body = vec![0u8, 0, 0, 0, 0, 0, 0, 0];
|
||||
let s = read_battlefield_status(&mut body.as_slice()).unwrap();
|
||||
assert_eq!(
|
||||
s.map_id, 0,
|
||||
"a zero map clears the slot and ends the packet"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn port_is_a_map_id_and_one_byte() {
|
||||
assert_eq!(battlefield_port(489, true), vec![0xE9, 1, 0, 0, 1]);
|
||||
assert_eq!(battlefield_port(489, false), vec![0xE9, 1, 0, 0, 0]);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user