- a text write names its own label, so the per-frame measure sweep drains a short ledger instead of hashing every font string twice — ~0.3 ms a frame at the Stormwind gates, gone - the idle interface tick stops re-writing settled state: buff durations and the weapon-enchant frame write only on change, and the fade and unit-popup drivers park until woken - the interface extract skips a hidden region before cloning its row, not after - WOW_UPLOAD_BUDGET=<MB> brackets the arrival burst's upload pacing — a lever for measuring world entry, not a fix yet Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
830 lines
49 KiB
XML
830 lines
49 KiB
XML
<Ui>
|
||
<!--
|
||
The player buff/debuff bar and the temporary-weapon-enchant row (decisions 0255/0257 — the
|
||
player aura arc). benilla's own code against the real 1.12 BuffFrame's contract: the
|
||
extracted Interface\FrameXML\BuffFrame.xml/.lua is the SPECIFICATION, and geometry,
|
||
textures, colours, every timing constant AND the data path are read off it.
|
||
|
||
THE INDEX SPACE — the thing to understand before touching a handler. There are TWO numbers
|
||
here and they are not the same number:
|
||
|
||
* a button's `id` is a 0-BASED ORDINAL WITHIN ITS OWN FILTER. BuffButton0..15 are
|
||
id="0".."15" under HELPFUL; BuffButton16..23 are id="0".."7" under HARMFUL. It is a
|
||
loop counter and nothing else.
|
||
* `GetPlayerBuff(id, filter)` turns that ordinal into a CACHE POSITION — the aura's
|
||
physical index in the player's display cache — plus `untilCancelled`. **Every** sibling
|
||
verb (GetPlayerBuffTexture/…DispelType/…Applications/…TimeLeft, CancelPlayerBuff,
|
||
GameTooltip:SetPlayerBuff) consumes that position, never the button's id.
|
||
|
||
So a button is a two-step: enumerate to a position on the event, then read the position. The
|
||
position is ABSOLUTE — the number a "HARMFUL" walk hands back is the number an unfiltered
|
||
walk would — which is why a debuff button's handle can never resolve against the buff list,
|
||
structurally, with no filter argument threaded through the read path. A miss is `-1`, never
|
||
nil, and the accessors all tolerate `-1` directly (byte-verified; the family's signature
|
||
table and the traps are `benilla_ui::script::aura`'s module header).
|
||
|
||
This file used to read the Era `UnitBuff`/`UnitDebuff` pair because the 1.12 verbs did not
|
||
exist. They landed in b2ede294, so the adaptation is gone: the handlers below run on the
|
||
reference's own verbs and the reference's own numbers.
|
||
|
||
SCOPE. The LOCAL PLAYER's auras, plus the reference's two temporary-weapon-enchant slots.
|
||
0255 left TemporaryEnchantFrame out ("there is no weapon-enchant wire yet"); there is —
|
||
`ITEM_FIELD_ENCHANTMENT`'s TEMP slot for the charges and `SMSG_ITEM_ENCHANT_TIME_UPDATE`
|
||
for the countdown, both already feeding the item tooltip (decisions 0915/0920) — so
|
||
`GetWeaponEnchantInfo` reads them and the row is here. The bar's resting place is
|
||
unchanged: right-aligned under the minimap at UIParent TOPRIGHT (-175,-13), which is where
|
||
BuffFrame_Enchant_OnUpdate's no-enchant branch parks it. With an enchant equipped the TOP
|
||
buff row alone slides left to clear the enchant icons (BuffFrame re-anchors to
|
||
TemporaryEnchantFrame's TOPLEFT); rows 2 and 3 hang off TempEnchant1/TemporaryEnchantFrame
|
||
and do not move. That is the reference's behaviour, not a choice made here.
|
||
|
||
DURATIONS ARE A SETTING (decision 1139). 0255 shipped the durations-shown geometry
|
||
unconditionally — the director's call, and there was no panel to hang a switch on — and said
|
||
in as many words that "if a CVar system later arrives, the geometry is already the branch it
|
||
would select". It arrived, as the saved-variable store rather than a CVar (1134): the store
|
||
below is SHOW_BUFF_DURATIONS, the Interface page's Buff Durations row drives it, and BOTH of
|
||
the reference's geometries are live again — rows at y = 0/−45/−90 with timers, 0/−35/−70
|
||
without (BuffButtons_UpdatePositions). Our default is "1", the reference's "0": the
|
||
director's look is what the bar comes up wearing, and what Defaults walks it back to.
|
||
|
||
ORDER IS STATE. The player's list the app pushes (crate::ui_aura, decision 0257) IS the
|
||
reference's insertion-ordered display cache: a new aura appends at the end, a dropped one
|
||
repacks — NOT ascending slot. That is precisely why a "cache position" needs no structure of
|
||
its own on our side; position N of that list is position N of the reference's cache.
|
||
|
||
DURATION / PERMANENCE is `untilCancelled`, GetPlayerBuff's second return — NOT
|
||
"expirationTime is 0", which is what this file tested before the verbs landed. They are
|
||
different questions and the difference is visible: untilCancelled is derived from the
|
||
SPELL (SpellDuration.dbc's infinite row, plus the area-aura override —
|
||
`benilla::ui_aura::until_cancelled`, byte-verified at 0x4e452e-0x4e45c5), so it is right on
|
||
the frame an aura appears, BEFORE any duration packet has landed. An aura that really is
|
||
timed but whose duration has not arrived yet therefore behaves as the reference does — it
|
||
counts from "0 s" and pulses for the frame or two before the stamp joins — instead of
|
||
silently showing no timer at all, which is what an expirationTime test cannot tell apart
|
||
from a permanent aura.
|
||
|
||
THE COUNTDOWN IS A POLL, NOT A CACHE. The aura EVENT is the discrete-change edge (icon,
|
||
stacks, dispel class); the remaining time is re-read every frame by every visible button.
|
||
That is the reference's own division of labour and this file keeps it exactly —
|
||
BuffButton_Update caches the buff POSITION on PLAYER_AURAS_CHANGED, BuffButton_OnUpdate
|
||
calls GetPlayerBuffTimeLeft with it each frame (ref BuffFrame.lua l.128-130) — and caching
|
||
the expiry on the event instead is what made refreshed buffs freeze at "0 s" and freshly
|
||
cast ones show no timer (decision 0846).
|
||
|
||
RIGHT-CLICK CANCEL. RegisterForClicks("RightButtonUp") + CancelPlayerBuff(this.buffIndex).
|
||
The server cancels by spell id and refuses anything the wire's cancelable bit didn't allow,
|
||
so a debuff right-click is a silent no-op, as in 1.12. `CancelUnitBuff` (the Era name) is
|
||
the same verb over the same cache behind the same gate and the same CMSG_CANCEL_AURA queue;
|
||
this file simply no longer needs the Era spelling.
|
||
|
||
BEHAVIOURAL DEPARTURES from the specification (noted, not bugs — everything else observable
|
||
is the reference's, however differently this file spells it):
|
||
- The count FontString is BLANKED ("") rather than hidden — an empty string draws nothing,
|
||
same look. A site that predates region Show/Hide; equivalent either way. (The DURATION
|
||
strings used the same shortcut until 1139 and no longer do: a hidden setting has to hide
|
||
a region, not write an empty string into a visible one.)
|
||
- The dispel-class lookup sits INSIDE the `if (debuffSlot)` guard. The reference runs
|
||
`GetPlayerBuffDispelType(GetPlayerBuff(this:GetID(), "HARMFUL"))` for all 24 buttons and
|
||
then throws the answer away for the 16 helpful ones, which have no $parentBorder to tint.
|
||
Provably invisible (the value's only consumer is that SetVertexColor), and it drops 16
|
||
cache walks per aura event.
|
||
- The colour is `DebuffTypeColor[debuffType] or DebuffTypeColor["none"]` where the
|
||
reference branches on `if (debuffType)`. Same answer for the five known classes; ours also
|
||
survives a dispel name the table has no row for, where the reference indexes nil.
|
||
- An icon path we cannot resolve falls back to BENILLA_FALLBACK_ICON rather than
|
||
SetTexture(nil) — carried forward from 0255, and the same fallback the action bar uses.
|
||
The reference has no such art; ours would otherwise draw an invisible button.
|
||
- BuffButtons_UpdatePositions is CALLED ONCE AT LOAD (from TemporaryEnchantFrame's OnLoad,
|
||
the first moment every frame it names exists). The reference never calls it at load
|
||
because its default is "0" and its static XML anchors already ARE the "0" geometry; ours
|
||
defaults to "1", so the load-time call is what makes the default honest.
|
||
- TempEnchant1/2 ship `hidden="true"`. The reference leaves them shown and lets the first
|
||
BuffFrame_Enchant_OnUpdate hide them — one frame of two empty enchant borders at the top
|
||
right, which is a flicker worth not having.
|
||
- BuffButtonTempEnchant is STANDALONE rather than inheriting BuffButtonTemplate and blanking
|
||
its handlers with empty elements — our loader keeps the inherited handler where the
|
||
reference clears it. Measured, not assumed; see the template's own note.
|
||
- `hasEnchant = 1` (ref BuffFrame.lua l.185/208) is dropped: a leaked local that becomes a
|
||
global, is never cleared, and is read by nothing.
|
||
- GameTooltip is nil-guarded. Engine tests load file subsets without GameTooltip.xml.
|
||
-->
|
||
<Script><![CDATA[
|
||
-- The timer switch (ref uvar, UIOptionsFrame.lua l.21/104 — its `default = "0"`). This
|
||
-- assignment is the ONLY statement of our default: the saved chunk executes over it at load
|
||
-- (1128) and the Interface page's row captures it as its Defaults value at OnLoad, which
|
||
-- runs first. "1" because the director asked for visible timers (0255) — the divergence is
|
||
-- written down once, here, and Defaults honours it rather than the reference's table.
|
||
SHOW_BUFF_DURATIONS = "1"
|
||
RegisterForSave("SHOW_BUFF_DURATIONS")
|
||
|
||
-- The reference's flash cadence + thresholds (BuffFrame.lua l.1-14). An aura in its last 31s
|
||
-- pulses between full and BUFF_MIN_ALPHA; its duration text turns highlight-white under 60s.
|
||
BUFF_FLASH_TIME_ON = 0.75
|
||
BUFF_FLASH_TIME_OFF = 0.75
|
||
BUFF_MIN_ALPHA = 0.3
|
||
BUFF_WARNING_TIME = 31
|
||
BUFF_DURATION_WARNING_TIME = 60
|
||
|
||
-- The dispel-class border tints (BuffFrame.lua l.9-14); "none" is the undispellable red the
|
||
-- debuff overlay wears by default. GetPlayerBuffDispelType's return keys this table — it
|
||
-- hands back the SpellDispelType.dbc name, or nil, never "" and never "none".
|
||
DebuffTypeColor = {
|
||
["none"] = { r = 0.80, g = 0.00, b = 0.00 },
|
||
["Magic"] = { r = 0.20, g = 0.60, b = 1.00 },
|
||
["Curse"] = { r = 0.60, g = 0.00, b = 1.00 },
|
||
["Disease"] = { r = 0.60, g = 0.40, b = 0.00 },
|
||
["Poison"] = { r = 0.00, g = 0.60, b = 0.00 },
|
||
}
|
||
|
||
-- SecondsToTimeAbbrev + its GlobalStrings (UIParent.lua l.1034 / GlobalStrings.lua). We load
|
||
-- no UIParent/GlobalStrings, so define them here (guarded, in case a later file ships them).
|
||
DAY_ONELETTER_ABBR = DAY_ONELETTER_ABBR or "%d d"
|
||
HOUR_ONELETTER_ABBR = HOUR_ONELETTER_ABBR or "%d h"
|
||
MINUTE_ONELETTER_ABBR = MINUTE_ONELETTER_ABBR or "%d m"
|
||
SECOND_ONELETTER_ABBR = SECOND_ONELETTER_ABBR or "%d s"
|
||
|
||
if not SecondsToTimeAbbrev then
|
||
function SecondsToTimeAbbrev(seconds)
|
||
if seconds > 86400 then
|
||
return format(DAY_ONELETTER_ABBR, ceil(seconds / 86400))
|
||
elseif seconds > 3600 then
|
||
return format(HOUR_ONELETTER_ABBR, ceil(seconds / 3600))
|
||
elseif seconds > 60 then
|
||
return format(MINUTE_ONELETTER_ABBR, ceil(seconds / 60))
|
||
end
|
||
return format(SECOND_ONELETTER_ABBR, seconds)
|
||
end
|
||
end
|
||
|
||
-- The frame-wide flash pulse (BuffFrame_OnLoad / _OnUpdate, BuffFrame.lua l.17-56). One
|
||
-- BUFF_ALPHA_VALUE is computed per frame and every low-on-time button reads it, so they blink
|
||
-- in unison. The bar also seats each duration string under its button here.
|
||
function BuffFrame_OnLoad()
|
||
BuffFrameUpdateTime = 0
|
||
BuffFrameFlashTime = 0
|
||
BuffFrameFlashState = 1
|
||
BUFF_ALPHA_VALUE = 1
|
||
for i = 0, 23 do
|
||
getglobal("BuffButton" .. i .. "Duration"):SetPoint("TOP", "BuffButton" .. i, "BOTTOM", 0, 0)
|
||
end
|
||
end
|
||
|
||
-- Half of the square wave, run down by one frame. BuffFrameFlashTime is the time LEFT in
|
||
-- the current half; when it goes under, the state flips and the counter reloads with the
|
||
-- new half's length. The overshoot is carried into that half — unless it is longer than
|
||
-- the half itself, in which case the whole period is kept rather than skipped, so a
|
||
-- stalled frame costs at most one beat of drift instead of racing the pulse forward.
|
||
local function buffFlashAdvance(elapsed)
|
||
BuffFrameFlashTime = BuffFrameFlashTime - elapsed
|
||
if BuffFrameFlashTime >= 0 then
|
||
return
|
||
end
|
||
local overtime = -BuffFrameFlashTime
|
||
if BuffFrameFlashState == 0 then
|
||
BuffFrameFlashState = 1
|
||
BuffFrameFlashTime = BUFF_FLASH_TIME_ON
|
||
else
|
||
BuffFrameFlashState = 0
|
||
BuffFrameFlashTime = BUFF_FLASH_TIME_OFF
|
||
end
|
||
if overtime < BuffFrameFlashTime then
|
||
BuffFrameFlashTime = BuffFrameFlashTime - overtime
|
||
end
|
||
end
|
||
|
||
function BuffFrame_OnUpdate(elapsed)
|
||
-- The tooltip re-poll's throttle (ref l.29-33): oscillates around zero with a
|
||
-- TOOLTIP_UPDATE_TIME period, and BuffButton_OnUpdate refreshes a hovered plate only on
|
||
-- the frames it is non-positive. ActionBar.xml declares TOOLTIP_UPDATE_TIME and loads
|
||
-- first (benilla.toc), so it is always a number by the time this runs.
|
||
if BuffFrameUpdateTime > 0 then
|
||
BuffFrameUpdateTime = BuffFrameUpdateTime - elapsed
|
||
else
|
||
BuffFrameUpdateTime = BuffFrameUpdateTime + TOOLTIP_UPDATE_TIME
|
||
end
|
||
|
||
-- The pulse's level for this frame: a 0..1 ramp up across the ON half and back down
|
||
-- across the OFF half (both scaled by BUFF_FLASH_TIME_ON — the reference's own choice,
|
||
-- and invisible while the two halves are the same length), squashed into
|
||
-- [BUFF_MIN_ALPHA, 1] so a warning icon dims rather than disappears.
|
||
--
|
||
-- Computed into a LOCAL and published once. BUFF_ALPHA_VALUE is read by every visible
|
||
-- button, so it is not scratch space: the reference assigns the raw ramp to it and then
|
||
-- squashes it in a second statement, which leaves the global holding an un-squashed
|
||
-- value for the length of one statement.
|
||
buffFlashAdvance(elapsed)
|
||
local level
|
||
if BuffFrameFlashState == 1 then
|
||
level = (BUFF_FLASH_TIME_ON - BuffFrameFlashTime) / BUFF_FLASH_TIME_ON
|
||
else
|
||
level = BuffFrameFlashTime / BUFF_FLASH_TIME_ON
|
||
end
|
||
BUFF_ALPHA_VALUE = (level * (1 - BUFF_MIN_ALPHA)) + BUFF_MIN_ALPHA
|
||
end
|
||
|
||
-- BuffButtons_UpdatePositions (BuffFrame.lua l.152-160): the ROW PITCH depends on whether a
|
||
-- timer sits under each icon. Shown, the three rows land at y = 0/−45/−90 (a 30px icon + a
|
||
-- 15px gutter); hidden, at 0/−35/−70 — the icons close up to the same 5px the columns use.
|
||
-- The anchors are the reference's own, through the TempEnchant chain: row 2 hangs off
|
||
-- TempEnchant1 and row 3 off TemporaryEnchantFrame, which is what keeps them still while the
|
||
-- top row slides left to clear an equipped enchant. SetPoint on a point already held
|
||
-- overwrites it (layout.rs — the client's own SetPoint law).
|
||
--
|
||
-- This is the Buff Durations row's `applyFunc`, and the ONLY thing the setting needs applied:
|
||
-- the timer text itself is re-decided every frame by the poll below, so a click changes what
|
||
-- the next frame draws without anyone telling it. Geometry is the part that is stated once.
|
||
function BuffButtons_UpdatePositions()
|
||
if SHOW_BUFF_DURATIONS == "1" then
|
||
BuffButton8:SetPoint("TOP", "TempEnchant1", "BOTTOM", 0, -15)
|
||
BuffButton16:SetPoint("TOPRIGHT", "TemporaryEnchantFrame", "TOPRIGHT", 0, -90)
|
||
else
|
||
BuffButton8:SetPoint("TOP", "TempEnchant1", "BOTTOM", 0, -5)
|
||
BuffButton16:SetPoint("TOPRIGHT", "TemporaryEnchantFrame", "TOPRIGHT", 0, -70)
|
||
end
|
||
end
|
||
|
||
-- Void a button's memory of what its duration region last had WRITTEN to it (the
|
||
-- benillaDur* fields BuffFrame_UpdateDuration gates on). Called by everything ELSE that
|
||
-- touches the region — the aura event's repaint here, the enchant row's clear — so the
|
||
-- per-frame gate can never skip a write against a region somebody moved under it; a
|
||
-- forgotten state just means the next poll writes once.
|
||
local function buffDurationForget(button)
|
||
button.benillaDurShown = nil
|
||
button.benillaDurText = nil
|
||
button.benillaDurWarn = nil
|
||
button.benillaDurSetting = nil
|
||
end
|
||
|
||
-- The button's aura, resolved on the discrete-change edge and CACHED AS A POSITION
|
||
-- (ref BuffButton_Update, l.58-107). `this:GetID()` is the 0-based ordinal within
|
||
-- this.buffFilter; buffIndex is the cache position every read below uses.
|
||
function BuffButton_Update()
|
||
local buffIndex, untilCancelled = GetPlayerBuff(this:GetID(), this.buffFilter)
|
||
this.buffIndex = buffIndex
|
||
this.untilCancelled = untilCancelled
|
||
local buffDuration = this.durationFS or getglobal(this:GetName() .. "Duration")
|
||
this.durationFS = buffDuration
|
||
|
||
-- -1, not nil: the miss sentinel the whole family agrees on.
|
||
if buffIndex < 0 then
|
||
this:Hide()
|
||
buffDuration:Hide()
|
||
buffDurationForget(this)
|
||
return
|
||
end
|
||
this:SetAlpha(1.0)
|
||
this.benillaLastAlpha = 1.0
|
||
this:Show()
|
||
if SHOW_BUFF_DURATIONS == "1" then
|
||
buffDuration:Show()
|
||
else
|
||
buffDuration:Hide()
|
||
end
|
||
buffDurationForget(this)
|
||
|
||
getglobal(this:GetName() .. "Icon"):SetTexture(GetPlayerBuffTexture(buffIndex) or BENILLA_FALLBACK_ICON)
|
||
|
||
-- Debuff border, tinted by dispel class (harmful buttons only — helpful ones have no
|
||
-- $parentBorder, so getglobal is nil and the whole lookup is skipped; see the header).
|
||
local debuffSlot = getglobal(this:GetName() .. "Border")
|
||
if debuffSlot then
|
||
local debuffType = GetPlayerBuffDispelType(GetPlayerBuff(this:GetID(), "HARMFUL"))
|
||
local color = DebuffTypeColor[debuffType] or DebuffTypeColor["none"]
|
||
debuffSlot:SetVertexColor(color.r, color.g, color.b)
|
||
end
|
||
|
||
-- Stack count, shown only above 1 (ref l.94-102). GetPlayerBuffApplications is a NUMBER
|
||
-- on every path — 1 for an absent position — so `> 1` needs no nil guard.
|
||
local buffCount = getglobal(this:GetName() .. "Count")
|
||
local count = GetPlayerBuffApplications(buffIndex)
|
||
if count > 1 then
|
||
buffCount:SetText(count)
|
||
else
|
||
buffCount:SetText("")
|
||
end
|
||
|
||
if GameTooltip and GameTooltip:IsOwned(this) then
|
||
GameTooltip:SetPlayerBuff(buffIndex)
|
||
end
|
||
end
|
||
|
||
-- A button takes right-clicks and listens for the aura rebuild. The first paint hides it
|
||
-- until the feed reports an aura. PLAYER_AURAS_CHANGED is the reference's event and the app
|
||
-- fires it beside the Era-shaped UNIT_AURA on the same rebuild (`ui_aura`).
|
||
function BuffButton_OnLoad()
|
||
-- Valid tokens for "buffFilter": HELPFUL, HARMFUL, CANCELABLE, NOT_CANCELABLE. Note the
|
||
-- mask starts at ZERO once a filter string is given, so a bare "CANCELABLE" would match
|
||
-- nothing — the sign must always be named.
|
||
BuffButton_Update()
|
||
this:RegisterForClicks("RightButtonUp")
|
||
this:RegisterEvent("PLAYER_AURAS_CHANGED")
|
||
end
|
||
|
||
function BuffButton_OnEvent(event)
|
||
if event == "PLAYER_AURAS_CHANGED" then
|
||
BuffButton_Update()
|
||
end
|
||
end
|
||
|
||
-- Per-frame: RE-READ the remaining time from the cached POSITION (never a cached duration),
|
||
-- flash in the last 31s, write the abbreviated timer, and tick a hovered tooltip.
|
||
--
|
||
-- The re-read every frame is the reference's own shape, and it is load-bearing: a duration
|
||
-- that changes without any discrete change — a recast, a DoT reapplied, cast pushback, the
|
||
-- server's replay of every duration at world entry — fires no aura event, so a button that
|
||
-- cached its expiry on the event would keep counting the OLD one down, sit at "0 s" while
|
||
-- the buff is still live, and never show a timer for an aura whose duration landed after
|
||
-- its icon did. Both were live defects; the poll is what makes them unrepresentable
|
||
-- (decision 0846).
|
||
--
|
||
-- The untilCancelled early-out (ref l.124-127) is what keeps a permanent aura from flashing
|
||
-- a "0 s" countdown while waiting for a duration packet that is never coming: it is answered
|
||
-- from the SPELL, so it is right before any packet arrives. This frame runs only on visible
|
||
-- frames (`UiScript::tick`), exactly as the client's does, so a hidden button's stale
|
||
-- buffIndex is never read.
|
||
function BuffButton_OnUpdate()
|
||
local button = this
|
||
-- A permanent aura has nothing to count down and nothing to flash toward, so it is
|
||
-- done in one line: UpdateDuration with no time left is exactly "hide the timer",
|
||
-- which is the whole of the reference's early-out here. (The reference reaches for the
|
||
-- $parentDuration region itself to do it, on every frame of every visible button, and
|
||
-- then throws that region away and lets UpdateDuration look it up again.)
|
||
if button.untilCancelled == 1 then
|
||
BuffFrame_UpdateDuration(button, nil)
|
||
return
|
||
end
|
||
|
||
local buffIndex = button.buffIndex
|
||
local timeLeft = GetPlayerBuffTimeLeft(buffIndex)
|
||
-- The flash is NOT gated on the timer setting: the reference pulses a button in its last
|
||
-- 31s whether or not a number is drawn under it, and with the timers off it is the only
|
||
-- thing left saying "about to drop".
|
||
--
|
||
-- Written only when it moves (decision 1396's class — a settled bar must not rewrite
|
||
-- per frame what changes only on an edge). Inside the warning window BUFF_ALPHA_VALUE
|
||
-- is a fresh ramp value every frame, so the pulse flows through the compare untouched;
|
||
-- outside it the value is the 1.0 that is already there, forever.
|
||
local alpha
|
||
if timeLeft < BUFF_WARNING_TIME then
|
||
alpha = BUFF_ALPHA_VALUE
|
||
else
|
||
alpha = 1.0
|
||
end
|
||
if alpha ~= button.benillaLastAlpha then
|
||
button.benillaLastAlpha = alpha
|
||
button:SetAlpha(alpha)
|
||
end
|
||
|
||
BuffFrame_UpdateDuration(button, timeLeft)
|
||
|
||
-- A tooltip we own is re-read on the throttle's beat, not every frame.
|
||
if BuffFrameUpdateTime <= 0 and GameTooltip and GameTooltip:IsOwned(button) then
|
||
GameTooltip:SetPlayerBuff(buffIndex)
|
||
end
|
||
end
|
||
|
||
-- Right-click cancels by CACHE POSITION (ref l.148-150). The app sends CMSG_CANCEL_AURA for
|
||
-- a cancelable aura and drops the rest, so a debuff right-click is a silent no-op.
|
||
function BuffButton_OnClick()
|
||
CancelPlayerBuff(this.buffIndex)
|
||
end
|
||
|
||
-- Clear enchant slots `first`..2 — the tail of the row nothing is using this frame. The
|
||
-- Hide moves the Duration region under the poll's write gate, so the gate's memory is
|
||
-- voided with it.
|
||
local function buffEnchantClear(first)
|
||
for slot = first, 2 do
|
||
local button = getglobal("TempEnchant" .. slot)
|
||
button:Hide()
|
||
getglobal("TempEnchant" .. slot .. "Duration"):Hide()
|
||
buffDurationForget(button)
|
||
end
|
||
end
|
||
|
||
-- One hand's six steps (the same block the reference spells out twice, ref l.180-207):
|
||
-- an enchanted hand packs into the next slot rightwards and keeps its countdown and
|
||
-- warning pulse live; a bare hand packs nothing. Returns the new fill count. A plain
|
||
-- function of its arguments so the per-frame path allocates nothing — this used to be a
|
||
-- two-row table built fresh every frame.
|
||
--
|
||
-- `slot` is the live API's inventory id, which is also what the button's hover reads
|
||
-- back off itself to ask for the ITEM's tooltip.
|
||
local function buffEnchantHand(filled, enchanted, expiration, slot)
|
||
if not enchanted then
|
||
return filled
|
||
end
|
||
filled = filled + 1
|
||
local button = getglobal("TempEnchant" .. filled)
|
||
button:SetID(slot)
|
||
getglobal(button:GetName() .. "Icon"):SetTexture(
|
||
GetInventoryItemTexture("player", slot) or BENILLA_FALLBACK_ICON)
|
||
button:Show()
|
||
|
||
-- The wire counts an enchant down in MILLISECONDS; everything the row draws is
|
||
-- in seconds. A nil expiration is "no countdown known", which the timer and the
|
||
-- warning flash both read as "nothing to warn about".
|
||
local timeLeft = expiration and (expiration / 1000)
|
||
BuffFrame_UpdateDuration(button, timeLeft)
|
||
if timeLeft and timeLeft < BUFF_WARNING_TIME then
|
||
button:SetAlpha(BUFF_ALPHA_VALUE)
|
||
else
|
||
button:SetAlpha(1.0)
|
||
end
|
||
return filled
|
||
end
|
||
|
||
-- The temporary-weapon-enchant row (ref l.162-233). GetWeaponEnchantInfo is the whole data
|
||
-- path: two flags, two remaining-MILLISECOND counts, two charge counts (the charges are
|
||
-- read by nobody here, and are named only to spell the API's shape out).
|
||
--
|
||
-- OFF HAND FIRST is the part that matters: the row packs from slot 1 rightwards, so with
|
||
-- both weapons enchanted the main hand lands in slot 2 and therefore sits to the LEFT of
|
||
-- the off hand.
|
||
function BuffFrame_Enchant_OnUpdate(elapsed)
|
||
local hasMainHandEnchant, mainHandExpiration, mainHandCharges,
|
||
hasOffHandEnchant, offHandExpiration, offHandCharges = GetWeaponEnchantInfo()
|
||
|
||
-- Nothing enchanted: clear the whole row and park BuffFrame back on its resting
|
||
-- point — ONCE per transition, latched on benillaCleared (decision 1396's class:
|
||
-- re-deriving per frame what changes only on an edge). The latch starts unset, so
|
||
-- the FIRST pass still writes — the load-time hide the header's TempEnchant note
|
||
-- leans on — and any enchant appearing clears it below.
|
||
if not hasMainHandEnchant and not hasOffHandEnchant then
|
||
if not TemporaryEnchantFrame.benillaCleared then
|
||
TemporaryEnchantFrame.benillaCleared = true
|
||
-- Forget the last row width too: after a park, the next enchanted pass must
|
||
-- re-write the width AND the bar's TOPLEFT anchor whatever its fill count is.
|
||
TemporaryEnchantFrame.benillaFilled = nil
|
||
buffEnchantClear(1)
|
||
BuffFrame:SetPoint("TOPRIGHT", "TemporaryEnchantFrame", "TOPRIGHT", 0, 0)
|
||
end
|
||
return
|
||
end
|
||
TemporaryEnchantFrame.benillaCleared = nil
|
||
|
||
local filled = buffEnchantHand(0, hasOffHandEnchant, offHandExpiration, 17)
|
||
filled = buffEnchantHand(filled, hasMainHandEnchant, mainHandExpiration, 16)
|
||
buffEnchantClear(filled + 1)
|
||
|
||
-- Position buff frame: the row is `filled` icons wide, and the bar starts one 5px
|
||
-- gutter to its left. The geometry depends on nothing but `filled`, so it is written
|
||
-- only when that changes.
|
||
if TemporaryEnchantFrame.benillaFilled ~= filled then
|
||
TemporaryEnchantFrame.benillaFilled = filled
|
||
TemporaryEnchantFrame:SetWidth(filled * 32)
|
||
BuffFrame:SetPoint("TOPRIGHT", "TemporaryEnchantFrame", "TOPLEFT", -5, 0)
|
||
end
|
||
end
|
||
|
||
function BuffFrame_EnchantButton_OnUpdate()
|
||
if GameTooltip and GameTooltip:IsOwned(this) then
|
||
BuffFrame_EnchantButton_OnEnter()
|
||
end
|
||
end
|
||
|
||
-- The enchant hover is the ITEM's tooltip, not an aura's — the plate hangs below-left like
|
||
-- the buff buttons' (ref l.242-245).
|
||
function BuffFrame_EnchantButton_OnEnter()
|
||
if not GameTooltip then return end
|
||
GameTooltip:SetOwner(this, "ANCHOR_BOTTOMLEFT")
|
||
GameTooltip:SetInventoryItem("player", this:GetID())
|
||
end
|
||
|
||
-- BuffFrame_UpdateDuration (ref l.247-260), gate and all: the timer draws only when the
|
||
-- setting is on AND there is a live one. `timeLeft` nil is the enchant row's "no countdown
|
||
-- known" case. Hidden, not blanked — see the header's deviation note.
|
||
--
|
||
-- The POLL stays per-frame (the re-read is load-bearing — decision 0846); only the WRITES
|
||
-- are gated, on what was last written: shown, the abbreviated text, the warning band, and
|
||
-- the SHOW_BUFF_DURATIONS value itself (so the settings toggle stays live). Deliberately
|
||
-- never on a measured extent (1396's trap: a fixture with no font engine measures 0
|
||
-- forever); the compare is against the STRING this function computed, nothing the host
|
||
-- had to shape. The benillaDur* memory is voided by whoever else touches the region
|
||
-- (buffDurationForget), so a stale skip is unrepresentable.
|
||
function BuffFrame_UpdateDuration(buffButton, timeLeft)
|
||
local duration = buffButton.durationFS or getglobal(buffButton:GetName() .. "Duration")
|
||
buffButton.durationFS = duration
|
||
local setting = SHOW_BUFF_DURATIONS
|
||
-- Two ways to have no number to draw: the setting is off, or nothing has told us how
|
||
-- long is left (the enchant row's "no countdown known").
|
||
if setting ~= "1" or not timeLeft then
|
||
-- == false, not falsey: nil is "no memory", and the first pass must write.
|
||
if buffButton.benillaDurShown == false and buffButton.benillaDurSetting == setting then
|
||
return
|
||
end
|
||
buffButton.benillaDurShown = false
|
||
buffButton.benillaDurText = nil
|
||
buffButton.benillaDurWarn = nil
|
||
buffButton.benillaDurSetting = setting
|
||
duration:Hide()
|
||
return
|
||
end
|
||
local text = SecondsToTimeAbbrev(timeLeft)
|
||
-- Gold most of the way, white inside the last minute — the number's own warning, a
|
||
-- half-minute ahead of the icon starting to pulse.
|
||
local warning = timeLeft < BUFF_DURATION_WARNING_TIME
|
||
if buffButton.benillaDurShown == true and buffButton.benillaDurText == text
|
||
and buffButton.benillaDurWarn == warning and buffButton.benillaDurSetting == setting then
|
||
return
|
||
end
|
||
buffButton.benillaDurShown = true
|
||
buffButton.benillaDurText = text
|
||
buffButton.benillaDurWarn = warning
|
||
buffButton.benillaDurSetting = setting
|
||
duration:SetText(text)
|
||
local color = NORMAL_FONT_COLOR
|
||
if warning then
|
||
color = HIGHLIGHT_FONT_COLOR
|
||
end
|
||
duration:SetVertexColor(color.r, color.g, color.b)
|
||
duration:Show()
|
||
end
|
||
]]></Script>
|
||
|
||
<!-- 30x30 icon + a bottom-right stack count (ref BuffButtonTemplate, BuffFrame.xml l.4-44).
|
||
The base template's own filter is the reference's HELPFUL|HARMFUL; the two concrete
|
||
variants below pin one sign each. -->
|
||
<Button name="BuffButtonTemplate" virtual="true">
|
||
<Size><AbsDimension x="30" y="30"/></Size>
|
||
<Layers>
|
||
<Layer level="BACKGROUND">
|
||
<Texture name="$parentIcon"/>
|
||
<FontString name="$parentCount" inherits="NumberFontNormal">
|
||
<Anchors>
|
||
<Anchor point="BOTTOMRIGHT"><Offset><AbsDimension x="-2" y="2"/></Offset></Anchor>
|
||
</Anchors>
|
||
</FontString>
|
||
</Layer>
|
||
</Layers>
|
||
<Scripts>
|
||
<OnLoad>
|
||
this.buffFilter = "HELPFUL|HARMFUL"
|
||
BuffButton_OnLoad()
|
||
</OnLoad>
|
||
<OnEvent>BuffButton_OnEvent(event)</OnEvent>
|
||
<OnUpdate>BuffButton_OnUpdate()</OnUpdate>
|
||
<OnClick>BuffButton_OnClick()</OnClick>
|
||
<OnEnter>
|
||
if GameTooltip then
|
||
GameTooltip:SetOwner(this, "ANCHOR_BOTTOMLEFT")
|
||
GameTooltip:SetPlayerBuff(this.buffIndex)
|
||
end
|
||
</OnEnter>
|
||
<OnLeave>
|
||
if GameTooltip then GameTooltip:Hide() end
|
||
</OnLeave>
|
||
</Scripts>
|
||
</Button>
|
||
|
||
<!-- Helpful variant: filter HELPFUL, no border (ref l.50-57). -->
|
||
<Button name="BuffButtonHelpful" inherits="BuffButtonTemplate" virtual="true">
|
||
<Scripts>
|
||
<OnLoad>
|
||
this.buffFilter = "HELPFUL"
|
||
BuffButton_OnLoad()
|
||
</OnLoad>
|
||
</Scripts>
|
||
</Button>
|
||
|
||
<!-- Harmful variant: filter HARMFUL + the dispel-tinted border overlay (ref l.58-82).
|
||
33x32 UI-Debuff-Overlays, the exact reference TexCoords, tint set at runtime. -->
|
||
<Button name="BuffButtonHarmful" inherits="BuffButtonTemplate" virtual="true">
|
||
<Layers>
|
||
<Layer level="OVERLAY">
|
||
<Texture name="$parentBorder" file="Interface\Buttons\UI-Debuff-Overlays">
|
||
<Size><AbsDimension x="33" y="32"/></Size>
|
||
<Anchors>
|
||
<Anchor point="CENTER"><Offset><AbsDimension x="0" y="0"/></Offset></Anchor>
|
||
</Anchors>
|
||
<TexCoords left="0.296875" right="0.5703125" top="0" bottom="0.515625"/>
|
||
</Texture>
|
||
</Layer>
|
||
</Layers>
|
||
<Scripts>
|
||
<OnLoad>
|
||
this.buffFilter = "HARMFUL"
|
||
BuffButton_OnLoad()
|
||
</OnLoad>
|
||
</Scripts>
|
||
</Button>
|
||
|
||
<!-- Temp-enchant slot: the same 30x30 icon under a 32x32 UI-TempEnchant-Border (ref l.83-117).
|
||
It carries no filter and no buffIndex — its data is GetWeaponEnchantInfo and its hover is
|
||
the ITEM's tooltip.
|
||
|
||
**STANDALONE, where the reference inherits BuffButtonTemplate and then blanks the four aura
|
||
handlers with empty `<OnLoad></OnLoad>` / `<OnEvent>` / `<OnClick>` elements.** Our loader
|
||
does not implement that idiom: `Loader::compile_handler` returns None for an empty body, so
|
||
`apply_scripts` skips the SetScript entirely and the INHERITED handler survives. Written
|
||
the reference's way and MEASURED, TempEnchant1 ran `BuffButton_OnLoad`, registered
|
||
PLAYER_AURAS_CHANGED, and painted the player's first buff in the corner — and a right-click
|
||
on it would have cancelled that aura. Writing the three regions out removes the dependence
|
||
on a loader semantic we have not byte-verified; the engine gap is real and worth closing on
|
||
its own evidence, not silently here. -->
|
||
<Button name="BuffButtonTempEnchant" virtual="true">
|
||
<Size><AbsDimension x="30" y="30"/></Size>
|
||
<Layers>
|
||
<Layer level="BACKGROUND">
|
||
<Texture name="$parentIcon"/>
|
||
<FontString name="$parentCount" inherits="NumberFontNormal">
|
||
<Anchors>
|
||
<Anchor point="BOTTOMRIGHT"><Offset><AbsDimension x="-2" y="2"/></Offset></Anchor>
|
||
</Anchors>
|
||
</FontString>
|
||
</Layer>
|
||
<Layer level="OVERLAY">
|
||
<Texture name="$parentBorder" file="Interface\Buttons\UI-TempEnchant-Border">
|
||
<Size><AbsDimension x="32" y="32"/></Size>
|
||
<Anchors>
|
||
<Anchor point="CENTER"><Offset><AbsDimension x="0" y="0"/></Offset></Anchor>
|
||
</Anchors>
|
||
</Texture>
|
||
</Layer>
|
||
</Layers>
|
||
<Scripts>
|
||
<OnUpdate>BuffFrame_EnchantButton_OnUpdate()</OnUpdate>
|
||
<OnEnter>BuffFrame_EnchantButton_OnEnter()</OnEnter>
|
||
<OnLeave>
|
||
if GameTooltip then GameTooltip:Hide() end
|
||
</OnLeave>
|
||
</Scripts>
|
||
</Button>
|
||
|
||
<!-- The duration text under a button: the ref routes these through a virtual FontString
|
||
template (BuffButtonDurationTemplate, l.45-49 — GameFontNormalSmall + a bare TOP anchor).
|
||
Region templates DO exist in the loader now (Loader::expand_region), but chaining to a FONT
|
||
OBJECT through one does not: `framexml::merge` lets the instance's own `inherits=` win, so
|
||
the spliced element still reads `inherits="BuffButtonDurationTemplate"` and
|
||
`apply_fontstring_font` looks that up in the FONT registry — a separate namespace — and
|
||
misses. Each instance therefore names GameFontNormalSmall directly; the anchor is re-seated
|
||
by BuffFrame_OnLoad / TemporaryEnchantFrame's OnLoad anyway. Same resolved font, no
|
||
indirection. -->
|
||
<!--
|
||
The bar. Right-aligned under the minimap (UIParent TOPRIGHT -175,-13 — the reference's XML
|
||
says -205 and lets the first Enchant_OnUpdate snap it here; ours starts where it rests, so
|
||
the bar is never one frame out of place). 16 helpful buttons in two rows of 8 (ids 0-15),
|
||
then 8 harmful (ids 0-7). The 24 duration strings live in the frame's ARTWORK layer (over the
|
||
icons' BACKGROUND), named to pair with their button. BuffButton8's and BuffButton16's static
|
||
anchors here are the no-timer geometry; BuffButtons_UpdatePositions re-seats both at load.
|
||
-->
|
||
<Frame name="BuffFrame" parent="UIParent" frameStrata="LOW" toplevel="true">
|
||
<Size><AbsDimension x="50" y="50"/></Size>
|
||
<Anchors>
|
||
<Anchor point="TOPRIGHT" relativeTo="UIParent" relativePoint="TOPRIGHT">
|
||
<Offset><AbsDimension x="-175" y="-13"/></Offset>
|
||
</Anchor>
|
||
</Anchors>
|
||
<Layers>
|
||
<Layer level="ARTWORK">
|
||
<FontString name="BuffButton0Duration" inherits="GameFontNormalSmall"/>
|
||
<FontString name="BuffButton1Duration" inherits="GameFontNormalSmall"/>
|
||
<FontString name="BuffButton2Duration" inherits="GameFontNormalSmall"/>
|
||
<FontString name="BuffButton3Duration" inherits="GameFontNormalSmall"/>
|
||
<FontString name="BuffButton4Duration" inherits="GameFontNormalSmall"/>
|
||
<FontString name="BuffButton5Duration" inherits="GameFontNormalSmall"/>
|
||
<FontString name="BuffButton6Duration" inherits="GameFontNormalSmall"/>
|
||
<FontString name="BuffButton7Duration" inherits="GameFontNormalSmall"/>
|
||
<FontString name="BuffButton8Duration" inherits="GameFontNormalSmall"/>
|
||
<FontString name="BuffButton9Duration" inherits="GameFontNormalSmall"/>
|
||
<FontString name="BuffButton10Duration" inherits="GameFontNormalSmall"/>
|
||
<FontString name="BuffButton11Duration" inherits="GameFontNormalSmall"/>
|
||
<FontString name="BuffButton12Duration" inherits="GameFontNormalSmall"/>
|
||
<FontString name="BuffButton13Duration" inherits="GameFontNormalSmall"/>
|
||
<FontString name="BuffButton14Duration" inherits="GameFontNormalSmall"/>
|
||
<FontString name="BuffButton15Duration" inherits="GameFontNormalSmall"/>
|
||
<FontString name="BuffButton16Duration" inherits="GameFontNormalSmall"/>
|
||
<FontString name="BuffButton17Duration" inherits="GameFontNormalSmall"/>
|
||
<FontString name="BuffButton18Duration" inherits="GameFontNormalSmall"/>
|
||
<FontString name="BuffButton19Duration" inherits="GameFontNormalSmall"/>
|
||
<FontString name="BuffButton20Duration" inherits="GameFontNormalSmall"/>
|
||
<FontString name="BuffButton21Duration" inherits="GameFontNormalSmall"/>
|
||
<FontString name="BuffButton22Duration" inherits="GameFontNormalSmall"/>
|
||
<FontString name="BuffButton23Duration" inherits="GameFontNormalSmall"/>
|
||
</Layer>
|
||
</Layers>
|
||
<Frames>
|
||
<!-- Row 0: helpful ordinals 0-7, right to left. -->
|
||
<Button name="BuffButton0" inherits="BuffButtonHelpful" id="0">
|
||
<Anchors><Anchor point="TOPRIGHT"><Offset><AbsDimension x="0" y="0"/></Offset></Anchor></Anchors>
|
||
</Button>
|
||
<Button name="BuffButton1" inherits="BuffButtonHelpful" id="1">
|
||
<Anchors><Anchor point="RIGHT" relativeTo="BuffButton0" relativePoint="LEFT"><Offset><AbsDimension x="-5" y="0"/></Offset></Anchor></Anchors>
|
||
</Button>
|
||
<Button name="BuffButton2" inherits="BuffButtonHelpful" id="2">
|
||
<Anchors><Anchor point="RIGHT" relativeTo="BuffButton1" relativePoint="LEFT"><Offset><AbsDimension x="-5" y="0"/></Offset></Anchor></Anchors>
|
||
</Button>
|
||
<Button name="BuffButton3" inherits="BuffButtonHelpful" id="3">
|
||
<Anchors><Anchor point="RIGHT" relativeTo="BuffButton2" relativePoint="LEFT"><Offset><AbsDimension x="-5" y="0"/></Offset></Anchor></Anchors>
|
||
</Button>
|
||
<Button name="BuffButton4" inherits="BuffButtonHelpful" id="4">
|
||
<Anchors><Anchor point="RIGHT" relativeTo="BuffButton3" relativePoint="LEFT"><Offset><AbsDimension x="-5" y="0"/></Offset></Anchor></Anchors>
|
||
</Button>
|
||
<Button name="BuffButton5" inherits="BuffButtonHelpful" id="5">
|
||
<Anchors><Anchor point="RIGHT" relativeTo="BuffButton4" relativePoint="LEFT"><Offset><AbsDimension x="-5" y="0"/></Offset></Anchor></Anchors>
|
||
</Button>
|
||
<Button name="BuffButton6" inherits="BuffButtonHelpful" id="6">
|
||
<Anchors><Anchor point="RIGHT" relativeTo="BuffButton5" relativePoint="LEFT"><Offset><AbsDimension x="-5" y="0"/></Offset></Anchor></Anchors>
|
||
</Button>
|
||
<Button name="BuffButton7" inherits="BuffButtonHelpful" id="7">
|
||
<Anchors><Anchor point="RIGHT" relativeTo="BuffButton6" relativePoint="LEFT"><Offset><AbsDimension x="-5" y="0"/></Offset></Anchor></Anchors>
|
||
</Button>
|
||
<!-- Row 1: helpful ordinals 8-15. Re-anchored to TempEnchant1 by BuffButtons_UpdatePositions. -->
|
||
<Button name="BuffButton8" inherits="BuffButtonHelpful" id="8">
|
||
<Anchors><Anchor point="TOP" relativeTo="BuffButton0" relativePoint="BOTTOM"><Offset><AbsDimension x="0" y="-5"/></Offset></Anchor></Anchors>
|
||
</Button>
|
||
<Button name="BuffButton9" inherits="BuffButtonHelpful" id="9">
|
||
<Anchors><Anchor point="RIGHT" relativeTo="BuffButton8" relativePoint="LEFT"><Offset><AbsDimension x="-5" y="0"/></Offset></Anchor></Anchors>
|
||
</Button>
|
||
<Button name="BuffButton10" inherits="BuffButtonHelpful" id="10">
|
||
<Anchors><Anchor point="RIGHT" relativeTo="BuffButton9" relativePoint="LEFT"><Offset><AbsDimension x="-5" y="0"/></Offset></Anchor></Anchors>
|
||
</Button>
|
||
<Button name="BuffButton11" inherits="BuffButtonHelpful" id="11">
|
||
<Anchors><Anchor point="RIGHT" relativeTo="BuffButton10" relativePoint="LEFT"><Offset><AbsDimension x="-5" y="0"/></Offset></Anchor></Anchors>
|
||
</Button>
|
||
<Button name="BuffButton12" inherits="BuffButtonHelpful" id="12">
|
||
<Anchors><Anchor point="RIGHT" relativeTo="BuffButton11" relativePoint="LEFT"><Offset><AbsDimension x="-5" y="0"/></Offset></Anchor></Anchors>
|
||
</Button>
|
||
<Button name="BuffButton13" inherits="BuffButtonHelpful" id="13">
|
||
<Anchors><Anchor point="RIGHT" relativeTo="BuffButton12" relativePoint="LEFT"><Offset><AbsDimension x="-5" y="0"/></Offset></Anchor></Anchors>
|
||
</Button>
|
||
<Button name="BuffButton14" inherits="BuffButtonHelpful" id="14">
|
||
<Anchors><Anchor point="RIGHT" relativeTo="BuffButton13" relativePoint="LEFT"><Offset><AbsDimension x="-5" y="0"/></Offset></Anchor></Anchors>
|
||
</Button>
|
||
<Button name="BuffButton15" inherits="BuffButtonHelpful" id="15">
|
||
<Anchors><Anchor point="RIGHT" relativeTo="BuffButton14" relativePoint="LEFT"><Offset><AbsDimension x="-5" y="0"/></Offset></Anchor></Anchors>
|
||
</Button>
|
||
<!-- Row 2: harmful ordinals 0-7. Re-anchored to TemporaryEnchantFrame by
|
||
BuffButtons_UpdatePositions. (The reference's static anchor here names
|
||
relativePoint="BOTTOM" against a TOPRIGHT point — a typo it never notices because
|
||
UpdatePositions overwrites it; BOTTOMRIGHT is what it meant.) -->
|
||
<Button name="BuffButton16" inherits="BuffButtonHarmful" id="0">
|
||
<Anchors><Anchor point="TOPRIGHT" relativeTo="BuffButton8" relativePoint="BOTTOMRIGHT"><Offset><AbsDimension x="0" y="-5"/></Offset></Anchor></Anchors>
|
||
</Button>
|
||
<Button name="BuffButton17" inherits="BuffButtonHarmful" id="1">
|
||
<Anchors><Anchor point="RIGHT" relativeTo="BuffButton16" relativePoint="LEFT"><Offset><AbsDimension x="-5" y="0"/></Offset></Anchor></Anchors>
|
||
</Button>
|
||
<Button name="BuffButton18" inherits="BuffButtonHarmful" id="2">
|
||
<Anchors><Anchor point="RIGHT" relativeTo="BuffButton17" relativePoint="LEFT"><Offset><AbsDimension x="-5" y="0"/></Offset></Anchor></Anchors>
|
||
</Button>
|
||
<Button name="BuffButton19" inherits="BuffButtonHarmful" id="3">
|
||
<Anchors><Anchor point="RIGHT" relativeTo="BuffButton18" relativePoint="LEFT"><Offset><AbsDimension x="-5" y="0"/></Offset></Anchor></Anchors>
|
||
</Button>
|
||
<Button name="BuffButton20" inherits="BuffButtonHarmful" id="4">
|
||
<Anchors><Anchor point="RIGHT" relativeTo="BuffButton19" relativePoint="LEFT"><Offset><AbsDimension x="-5" y="0"/></Offset></Anchor></Anchors>
|
||
</Button>
|
||
<Button name="BuffButton21" inherits="BuffButtonHarmful" id="5">
|
||
<Anchors><Anchor point="RIGHT" relativeTo="BuffButton20" relativePoint="LEFT"><Offset><AbsDimension x="-5" y="0"/></Offset></Anchor></Anchors>
|
||
</Button>
|
||
<Button name="BuffButton22" inherits="BuffButtonHarmful" id="6">
|
||
<Anchors><Anchor point="RIGHT" relativeTo="BuffButton21" relativePoint="LEFT"><Offset><AbsDimension x="-5" y="0"/></Offset></Anchor></Anchors>
|
||
</Button>
|
||
<Button name="BuffButton23" inherits="BuffButtonHarmful" id="7">
|
||
<Anchors><Anchor point="RIGHT" relativeTo="BuffButton22" relativePoint="LEFT"><Offset><AbsDimension x="-5" y="0"/></Offset></Anchor></Anchors>
|
||
</Button>
|
||
</Frames>
|
||
<Scripts>
|
||
<OnLoad>BuffFrame_OnLoad()</OnLoad>
|
||
<OnUpdate>BuffFrame_OnUpdate(arg1)</OnUpdate>
|
||
</Scripts>
|
||
</Frame>
|
||
|
||
<!-- The temporary-weapon-enchant row (ref BuffFrame.xml l.385-431). It owns the top-right
|
||
corner; BuffFrame hangs off it, which is why the bar's own anchor above is this frame's
|
||
point. Two slots, right to left, so with both weapons enchanted the main hand sits left of
|
||
the off hand.
|
||
|
||
Declared AFTER BuffFrame so that by the time this OnLoad runs every name
|
||
BuffButtons_UpdatePositions touches exists — the reference gets away with never calling it
|
||
at load because its default is the geometry its static anchors already hold; ours is not. -->
|
||
<Frame name="TemporaryEnchantFrame" parent="UIParent" frameStrata="LOW" toplevel="true">
|
||
<Size><AbsDimension x="36" y="36"/></Size>
|
||
<Anchors>
|
||
<Anchor point="TOPRIGHT" relativeTo="UIParent" relativePoint="TOPRIGHT">
|
||
<Offset><AbsDimension x="-175" y="-13"/></Offset>
|
||
</Anchor>
|
||
</Anchors>
|
||
<Layers>
|
||
<Layer level="BACKGROUND">
|
||
<FontString name="TempEnchant1Duration" inherits="GameFontNormalSmall"/>
|
||
<FontString name="TempEnchant2Duration" inherits="GameFontNormalSmall"/>
|
||
</Layer>
|
||
</Layers>
|
||
<Frames>
|
||
<Button name="TempEnchant1" inherits="BuffButtonTempEnchant" hidden="true">
|
||
<Anchors><Anchor point="TOPRIGHT"><Offset><AbsDimension x="0" y="0"/></Offset></Anchor></Anchors>
|
||
</Button>
|
||
<Button name="TempEnchant2" inherits="BuffButtonTempEnchant" hidden="true">
|
||
<Anchors><Anchor point="RIGHT" relativeTo="TempEnchant1" relativePoint="LEFT"><Offset><AbsDimension x="-5" y="0"/></Offset></Anchor></Anchors>
|
||
</Button>
|
||
</Frames>
|
||
<Scripts>
|
||
<OnLoad>
|
||
TempEnchant1Duration:SetPoint("TOP", "TempEnchant1", "BOTTOM", 0, 0)
|
||
TempEnchant2Duration:SetPoint("TOP", "TempEnchant2", "BOTTOM", 0, 0)
|
||
BuffButtons_UpdatePositions()
|
||
</OnLoad>
|
||
<OnUpdate>BuffFrame_Enchant_OnUpdate(arg1)</OnUpdate>
|
||
</Scripts>
|
||
</Frame>
|
||
</Ui>
|