Files
moon-well-ui/data/addon/ForeverUI/Buffs.lua
T
Adrestie 5842eb7a2b ForeverUI : code en anglais, commentaires resserres, code mort retire
Traduction
- Identifiants en anglais dans tout le module : addon, ecrans d'accueil,
  outils Python, banc d'essai et patcheur C#.
- Fichiers au nom francais renommes (Gabarits.lua -> Templates.lua,
  Textes_*.lua -> Texts_*.lua, deployer.py -> deploy.py,
  tools/patcheur -> tools/patcher, etc.).
- Commandes /fui et options de deploy.py en anglais (--check, --server,
  --remove-dbc).
- Cles des variables sauvegardees et de data/dbc/statistics.json
  traduites, sans reprise des anciennes cles.
- API de la micro-barre : ForeverUI.AddMicroButton(def) avec les champs
  name, atlasSet, after, tooltip, onClick, ready, et
  ForeverUI.UpdateMicro(name, isOpen).
- Commentaires reecrits en anglais, courts, sans dates ni historique ;
  les fonctions au nom peu parlant et leurs parametres sont expliques.

Code mort
- Exports, fonctions et variables jamais lus retires.
- Retires a la demande des modules : ForeverUI.PvPJauge, les exports
  FondEntete / TuilerEntete / Polices de TradeSkill, l'art legacy-up-c60,
  honorfill(half)blue et inv_misc_book_09, leurs entrees d'atlas et les
  parties d'outils correspondantes.

Verifications : liaisons des noms comparees avant et apres, banc vert,
chemins de textures inchanges, generateurs reproduisant les fichiers,
patcheur identique octet pour octet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 09:15:52 +02:00

620 lines
20 KiB
Lua

-- ForeverUI: buff and debuff bars in camelot style (blizzard_buffframe, AuraContainerMixin).
-- Data and options come from 3.3.5; the client's BuffFrame, enchants and consolidation are hidden.
-- CancelUnitBuff and CancelItemTempEnchantment are not protected in 3.3.5.
-- Not in 3.3.5, so not here: external defensives, deadly debuffs, private auras, Bleed type.
local ForeverUI = ForeverUI or {}
_G.ForeverUI = ForeverUI
local B = {}
ForeverUI.Buffs = B
local SEP = string.char(92)
local L = ForeverUI.L
local G = {
width = 30, height = 40, icon = 30,
border = 40, enchantBorder = 32,
space = 5,
collapseW = 15, collapseH = 30, arrowW = 10, arrowH = 16, highlight = 0.4,
alert = 31, alertDuration = 90,
period = 1.5, alphaMin = 0.3, alphaMax = 1,
check = 0.2, tooltip = 0.2,
tooltipPerRow = 5, tooltipScale = 0.8, tooltipMargin = 16, tooltipRight = 9, tooltipBottom = 2,
tooltipExitMargin = 10,
}
local BARS = {
buffs = { name = "ForeverUIBuffFrame", filter = "HELPFUL", max = 32, perRow = 11,
x = -255, y = -10, caption = L.BUFFS_EDIT_LABEL_BUFFS },
debuffs = { name = "ForeverUIDebuffFrame", filter = "HARMFUL", max = 16, perRow = 8,
x = -270, y = -155, caption = L.BUFFS_EDIT_LABEL_DEBUFFS, showType = true },
}
-- Dishonored debuff: the client only shows an aura its own Spell.dbc knows, and no stock spell
-- fits. ForeverUI draws it from the state mod-pvp-titles-ext sends over the addon channel
-- (PvPTab.lua, ForeverUI.Dishonor): first among debuffs, no type.
local DISHONOR_ICON = "Interface" .. SEP .. "Icons" .. SEP .. "Ability_Hunter_MarkedForDeath"
-- Border per type (camelot DEBUFF_DISPLAY_INFO): no icon, with icon, colorblind symbol.
local TYPES = {
Magic = { "ui-debuff-border-magic-noicon", "ui-debuff-border-magic-icon", "DEBUFF_SYMBOL_MAGIC" },
Curse = { "ui-debuff-border-curse-noicon", "ui-debuff-border-curse-icon", "DEBUFF_SYMBOL_CURSE" },
Disease = { "ui-debuff-border-disease-noicon", "ui-debuff-border-disease-icon", "DEBUFF_SYMBOL_DISEASE" },
Poison = { "ui-debuff-border-poison-noicon", "ui-debuff-border-poison-icon", "DEBUFF_SYMBOL_POISON" },
None = { "ui-debuff-border-default-noicon" },
}
local ENCHANT_BORDER = "Interface" .. SEP .. "Buttons" .. SEP .. "UI-TempEnchant-Border"
local CONSOLIDATION = "Interface" .. SEP .. "Buttons" .. SEP .. "BuffConsolidation"
-- ---------- Options
local function durations() return SHOW_BUFF_DURATIONS == "1" end
local function consolidateEnabled() return CONSOLIDATE_BUFFS == "1" end
local function colorblindMode() return ENABLE_COLORBLIND_MODE == "1" end
-- camelot: collapse and consolidation exclude each other; 3.3.5 has no collapse option.
local function collapseEnabled() return not consolidateEnabled() end
-- Shown unit: the vehicle while in one. PlayerFrame is suppressed, so PlayerFrame.unit
-- does not follow the vehicle and the unit is computed here.
function B.unit()
if UnitHasVehicleUI and UnitHasVehicleUI("player") and UnitExists("vehicle") then
return "vehicle"
end
return "player"
end
-- ---------- Warning blink
-- AuraContainerWarningFader: under 31 s, alpha goes 0.3 -> 1 -> 0.3 over 1.5 s, the same
-- for all icons. rest: seconds left.
function B.warningAlpha(rest)
if rest and rest < G.alert then
local half = G.period / 2
local t = (GetTime() or 0) % G.period
local p = (t < half) and (t / half) or ((G.period - t) / half)
return G.alphaMin + (G.alphaMax - G.alphaMin) * p
end
return G.alphaMax
end
-- ---------- One button
local function filterOf(b)
if b.auraType == "Buff" or b.auraType == "TempEnchant" then return "HELPFUL" end
if b.auraType == "Debuff" then return "HARMFUL" end
end
local function tooltip(b)
if b.auraType == "TempEnchant" then
GameTooltip:SetOwner(b, "ANCHOR_BOTTOMLEFT")
GameTooltip:SetInventoryItem("player", b.info.ID)
return
end
GameTooltip:SetOwner(b, "ANCHOR_BOTTOMLEFT")
GameTooltip:SetFrameLevel(b:GetFrameLevel() + 2)
if b.info.dishonor then
local c = NORMAL_FONT_COLOR
GameTooltip:SetText(ForeverUI.Dishonor.name(), 1, 1, 1)
GameTooltip:AddLine(L.BUFFS_DISHONORED_DESC, c.r, c.g, c.b, true)
GameTooltip:Show()
return
end
GameTooltip:SetUnitAura(b.unit, b.info.index, filterOf(b))
end
local function updateDuration(b, rest)
local showRegion = rest and durations()
if showRegion then
b.Duration:SetFormattedText(SecondsToTimeAbbrev(rest))
local c = (rest < G.alertDuration) and HIGHLIGHT_FONT_COLOR or NORMAL_FONT_COLOR
b.Duration:SetVertexColor(c.r, c.g, c.b)
b.Duration:Show()
else
b.Duration:Hide()
end
end
local function onUpdate(b, elapsed)
if not b.info then return end
if b.auraType == "TempEnchant" and B.unit() ~= "player" then
b:Hide()
return
end
b:SetAlpha(B.warningAlpha(b.rest))
b.rest = math.max((b.info.expirationTime or 0) - GetTime(), 0)
updateDuration(b, b.rest)
b.tooltipTimer = (b.tooltipTimer or 0) - (elapsed or 0)
if b.tooltipTimer <= 0 then
b.tooltipTimer = G.tooltip
if GameTooltip:IsOwned(b) then tooltip(b) end
end
end
local function applyType(b, auraType)
b.auraType = auraType
b.Symbol:Hide()
if auraType == "Buff" then
b.DebuffBorder:Hide()
b.TempEnchantBorder:Hide()
elseif auraType == "Debuff" then
ForeverUI.SetAtlas(b.DebuffBorder, TYPES.None[1])
b.DebuffBorder:Show()
b.TempEnchantBorder:Hide()
elseif auraType == "TempEnchant" then
b.DebuffBorder:Hide()
b.TempEnchantBorder:Show()
end
end
local function applyExpiration(b, info)
if info.expirationTime and info.expirationTime > 0 then
if durations() then b.Duration:Show() else b.Duration:Hide() end
b.rest = info.expirationTime - GetTime()
b:SetScript("OnUpdate", onUpdate)
else
b.Duration:Hide()
b:SetScript("OnUpdate", nil)
b.rest = nil
b:SetAlpha(1)
end
end
-- AuraButtonMixin:Update. info: aura data; showType: debuff border with its type icon.
function B.populate(b, info, showType)
applyType(b, info.auraType)
b.info = info
b.unit = B.unit()
if info.auraType == "Debuff" then
local t = TYPES[info.debuffType or ""] or TYPES.None
ForeverUI.SetAtlas(b.DebuffBorder, (showType and t[2]) or t[1])
if colorblindMode() and t[3] then
b.Symbol:SetText(_G[t[3]] or "")
b.Symbol:Show()
end
end
applyExpiration(b, info)
b.Icon:SetTexture(info.texture)
if (info.count or 0) > 1 then
b.Count:SetText(info.count)
b.Count:Show()
else
b.Count:Hide()
end
if GameTooltip:IsOwned(b) then tooltip(b) end
end
-- AuraButtonArtTemplate: 30 x 40, icon 30 on top, duration below; right-click cancels.
function B.createButton(parent, name)
local b = CreateFrame("Button", name, parent)
b:SetWidth(G.width)
b:SetHeight(G.height)
b.Icon = b:CreateTexture(nil, "BACKGROUND")
b.Icon:SetWidth(G.icon)
b.Icon:SetHeight(G.icon)
b.Icon:SetPoint("TOP", b, "TOP", 0, 0)
b.Duration = b:CreateFontString(nil, "BACKGROUND", "GameFontNormalSmall")
b.Duration:SetPoint("TOP", b.Icon, "BOTTOM", 0, 0)
b.Duration:Hide()
b.DebuffBorder = b:CreateTexture(nil, "OVERLAY")
b.DebuffBorder:SetWidth(G.border)
b.DebuffBorder:SetHeight(G.border)
b.DebuffBorder:SetPoint("CENTER", b.Icon, "CENTER", 0, 0)
b.TempEnchantBorder = b:CreateTexture(nil, "OVERLAY")
b.TempEnchantBorder:SetTexture(ENCHANT_BORDER)
b.TempEnchantBorder:SetWidth(G.enchantBorder)
b.TempEnchantBorder:SetHeight(G.enchantBorder)
b.TempEnchantBorder:SetPoint("CENTER", b.Icon, "CENTER", 0, 0)
b.Symbol = b:CreateFontString(nil, "OVERLAY", "TextStatusBarText")
b.Symbol:SetPoint("TOPLEFT", b, "TOPLEFT", 2, -2)
b.Count = b:CreateFontString(nil, "OVERLAY", "NumberFontNormal")
b.Count:SetPoint("BOTTOMRIGHT", b.Icon, "BOTTOMRIGHT", -2, 2)
applyType(b, nil)
b:RegisterForClicks("RightButtonUp")
b:SetScript("OnClick", function(self, button)
if button ~= "RightButton" or not self.info then return end
if self.auraType == "Buff" then
CancelUnitBuff(self.unit, self.info.index, "HELPFUL")
elseif self.auraType == "TempEnchant" then
CancelItemTempEnchantment(self.info.ID == 16 and 1 or 2)
end
end)
b:SetScript("OnEnter", function(self) if self.info then tooltip(self) end end)
b:SetScript("OnLeave", function() GameTooltip:Hide() end)
b:Hide()
return b
end
-- ---------- Grid
-- Right to left, then down (AuraContainerMixin). buttons: the buttons to place.
local function arrange(container, buttons, perRow)
for i, b in ipairs(buttons) do
local col, row = (i - 1) % perRow, math.floor((i - 1) / perRow)
b:ClearAllPoints()
b:SetPoint("TOPRIGHT", container, "TOPRIGHT",
-col * (G.width + G.space), -row * (G.height + G.space))
end
end
-- ---------- One bar
local function createBar(key)
local c = BARS[key]
local f = CreateFrame("Frame", c.name, UIParent)
f:SetFrameStrata("LOW")
f.key, f.c = key, c
f.container = CreateFrame("Frame", nil, f)
f.container:SetWidth(1)
f.container:SetHeight(1)
f.buttons = {}
for i = 1, c.max do
f.buttons[i] = B.createButton(f.container, c.name .. "Button" .. i)
end
local rows = math.ceil(c.max / c.perRow)
local width = (G.width + G.space) * c.perRow
if key == "buffs" then width = width + G.collapseW end
f:SetWidth(width)
f:SetHeight((G.height + G.space) * rows)
f.auras = {}
return f
end
-- Collapse button: bag-arrow, flipped when the bar is expanded. t: arrow texture.
local function orientArrow(t, flipped)
local e = ForeverUI.AtlasEntry("bag-arrow")
if not e then return end
t:SetTexture(e[1])
local u1, u2, v1, v2 = e[2], e[3], e[4], e[5]
if flipped then
t:SetTexCoord(u2, v2, u2, v1, u1, v2, u1, v1)
else
t:SetTexCoord(u1, v1, u1, v2, u2, v1, u2, v2)
end
end
function B.orientCollapse()
local r = B.buffs.collapse
-- Icons grow to the left: expanded, the arrow is rotated by pi.
local expanded = r:GetChecked() and true or false
for _, t in ipairs({ r:GetNormalTexture(), r:GetPushedTexture(), r:GetHighlightTexture() }) do
orientArrow(t, expanded)
end
end
local function createCollapse(f)
local r = CreateFrame("CheckButton", f.c.name .. "CollapseAndExpandButton", f)
r:SetWidth(G.collapseW)
r:SetHeight(G.collapseH)
r:SetPoint("TOPRIGHT", f, "TOPRIGHT", 0, 0)
local e = ForeverUI.AtlasEntry("bag-arrow")
r:SetNormalTexture(e and e[1] or "")
r:SetPushedTexture(e and e[1] or "")
r:SetHighlightTexture(e and e[1] or "", "ADD")
for _, t in ipairs({ r:GetNormalTexture(), r:GetPushedTexture(), r:GetHighlightTexture() }) do
t:ClearAllPoints()
t:SetWidth(G.arrowW)
t:SetHeight(G.arrowH)
t:SetPoint("CENTER", r, "CENTER", 0, 0)
end
r:GetHighlightTexture():SetAlpha(G.highlight)
r:SetChecked(true)
r:SetScript("OnClick", function(self)
B.expanded = self:GetChecked() and true or false
B.orientCollapse()
B.update()
end)
f.collapse = r
f.container:SetPoint("TOPRIGHT", r, "TOPLEFT", 0, 0)
return r
end
-- Consolidation icon (BuffConsolidation) and its popup.
local function createConsolidation(f)
local b = B.createButton(f.container, f.c.name .. "ConsolidatedBuffs")
applyType(b, "Buff")
b.Icon:SetTexture(CONSOLIDATION)
b.Icon:SetTexCoord(0.109375, 0.390625, 0.21875, 0.78125)
b:SetScript("OnClick", nil)
b:SetScript("OnLeave", nil)
b.count = 0
local tooltipFrame = CreateFrame("Frame", f.c.name .. "ConsolidatedBuffsTooltip", b)
tooltipFrame:SetFrameStrata("TOOLTIP")
tooltipFrame:SetClampedToScreen(true)
tooltipFrame:SetPoint("TOPLEFT", b.Icon, "BOTTOMLEFT", 0, 0)
tooltipFrame:SetBackdrop({
bgFile = "Interface" .. SEP .. "Tooltips" .. SEP .. "UI-Tooltip-Background",
edgeFile = "Interface" .. SEP .. "Tooltips" .. SEP .. "UI-Tooltip-Border",
tile = true, tileSize = 16, edgeSize = 16,
insets = { left = 4, right = 4, top = 4, bottom = 4 },
})
tooltipFrame:SetBackdropBorderColor(TOOLTIP_DEFAULT_COLOR.r, TOOLTIP_DEFAULT_COLOR.g, TOOLTIP_DEFAULT_COLOR.b)
tooltipFrame:SetBackdropColor(TOOLTIP_DEFAULT_BACKGROUND_COLOR.r, TOOLTIP_DEFAULT_BACKGROUND_COLOR.g, TOOLTIP_DEFAULT_BACKGROUND_COLOR.b)
tooltipFrame:Hide()
local auras = CreateFrame("Frame", nil, tooltipFrame)
auras:SetScale(G.tooltipScale)
auras:SetPoint("TOPLEFT", tooltipFrame, "TOPLEFT", G.tooltipMargin, -G.tooltipMargin)
auras:SetWidth(1)
auras:SetHeight(1)
tooltipFrame.auras = auras
tooltipFrame.buttons = {}
for i = 1, BARS.buffs.max do
tooltipFrame.buttons[i] = B.createButton(auras, f.c.name .. "ConsolidatedBuffsTooltipButton" .. i)
end
tooltipFrame:SetScript("OnUpdate", function(self)
local m = G.tooltipExitMargin
if not self:IsMouseOver(m, -m, -m, m) and not b:IsMouseOver(m, -m, -m, m) then
self:Hide()
end
end)
b:SetScript("OnEnter", function() B.fillTooltip(); tooltipFrame:Show() end)
b:SetScript("OnHide", function() tooltipFrame:Hide() end)
b.tooltipFrame = tooltipFrame
f.consolidated = b
return b
end
-- Popup: the hidden buffs, 5 per row, left to right.
function B.fillTooltip()
local b = B.buffs.consolidated
local tooltipFrame = b.tooltipFrame
local n = 0
for _, info in ipairs(B.buffs.auras) do
if info.hideable then
n = n + 1
local button = tooltipFrame.buttons[n]
if not button then break end
B.populate(button, info)
button:Show()
local col, row = (n - 1) % G.tooltipPerRow, math.floor((n - 1) / G.tooltipPerRow)
button:ClearAllPoints()
button:SetPoint("TOPLEFT", tooltipFrame.auras, "TOPLEFT",
col * (G.width + G.space), -row * (G.height + G.space))
end
end
for i = n + 1, #tooltipFrame.buttons do
tooltipFrame.buttons[i].info = nil
tooltipFrame.buttons[i]:Hide()
end
local l = (G.width + G.space) * math.min(G.tooltipPerRow, n)
local h = (G.height + G.space) * math.ceil(n / G.tooltipPerRow)
tooltipFrame:SetWidth((G.tooltipMargin + l) * G.tooltipScale + G.tooltipRight)
tooltipFrame:SetHeight((G.tooltipMargin + h) * G.tooltipScale + G.tooltipBottom)
end
-- ---------- Data
-- Weapon enchants: main hand, then off hand, listed before the buffs.
local function enchantments(list)
local now = GetTime()
local hasMain, expMain, chMain, hasOff, expOff, chOff = GetWeaponEnchantInfo()
for _, e in ipairs({ { hasMain, expMain, chMain, 16 }, { hasOff, expOff, chOff, 17 } }) do
if e[1] then
local rest = (e[2] or 0) / 1000
local info = {
auraType = "TempEnchant",
texture = GetInventoryItemTexture("player", e[4]),
count = e[3] or 0,
expirationTime = now + rest,
ID = e[4],
}
info.hideable = rest > G.alertDuration
table.insert(list, info)
end
end
end
-- Appends up to max auras of unit to list. hideable: no duration or more than 90 s left.
local function auras(unit, filter, max, list)
local now = GetTime()
for i = 1, max do
local name, _, icon, stacks, dispelType, duration, expiration = UnitAura(unit, i, filter)
if not name then break end
local info = {
auraType = (filter == "HELPFUL") and "Buff" or "Debuff",
index = i,
texture = icon,
count = stacks or 0,
debuffType = dispelType,
duration = duration or 0,
expirationTime = expiration or 0,
}
if filter == "HELPFUL" then
info.hideable = (info.duration == 0) or (info.expirationTime == 0)
or ((info.expirationTime - now) > G.alertDuration)
end
table.insert(list, info)
end
end
-- The Dishonored debuff, first, while the state lasts (player only, not in a vehicle).
local function dishonor(list)
local state = ForeverUI.Dishonor
if not state or not state.finish or B.unit() ~= "player" or state.finish <= GetTime() then return end
table.insert(list, 1, {
auraType = "Debuff",
texture = DISHONOR_ICON,
count = 0,
duration = 0,
expirationTime = state.finish,
dishonor = true,
})
end
-- ---------- Update
B.expanded = true
local function expanded()
if collapseEnabled() then return B.expanded end
return false
end
function B.updateBuffs()
local f = B.buffs
local list = {}
if B.unit() == "player" then enchantments(list) end
auras(B.unit(), "HELPFUL", f.c.max, list)
f.auras = list
local hideableCount = 0
for _, info in ipairs(list) do
if info.hideable then hideableCount = hideableCount + 1 end
end
f.hideableCount = hideableCount
-- Consolidation
local r = f.consolidated
r.count = hideableCount
r.Count:SetText(hideableCount)
local withConsolidation = consolidateEnabled() and hideableCount > 0
if withConsolidation then r:Show() else r:Hide() end
-- Buttons
local isOpen = expanded()
local following = 1
for _, b in ipairs(f.buttons) do
local info
while following <= #list do
local candidate = list[following]
following = following + 1
if isOpen or not candidate.hideable then
info = candidate
break
end
end
if info then
B.populate(b, info)
b:Show()
else
b.info = nil
b:SetScript("OnUpdate", nil)
b:Hide()
end
end
local toPlace = {}
if withConsolidation then table.insert(toPlace, r) end
for _, b in ipairs(f.buttons) do table.insert(toPlace, b) end
arrange(f.container, toPlace, f.c.perRow)
-- Collapse button only if something can be hidden
if collapseEnabled() and hideableCount > 0 then f.collapse:Show() else f.collapse:Hide() end
f.collapse:SetChecked(B.expanded)
B.orientCollapse()
if r.tooltipFrame:IsShown() then B.fillTooltip() end
-- Watch for hidden buffs that drop under 90 s and return to the bar
f.pending = (not isOpen and hideableCount > 0) and G.check or nil
end
function B.updateDebuffs()
local f = B.debuffs
local list = {}
auras(B.unit(), "HARMFUL", f.c.max, list)
dishonor(list)
f.auras = list
local toPlace = {}
for i, b in ipairs(f.buttons) do
local info = list[i]
if info then
B.populate(b, info, f.c.showType)
b:Show()
table.insert(toPlace, b)
else
b.info = nil
b:SetScript("OnUpdate", nil)
b:Hide()
end
end
arrange(f.container, toPlace, f.c.perRow)
end
function B.update()
B.updateBuffs()
B.updateDebuffs()
end
-- ---------- Build
B.buffs = createBar("buffs")
B.debuffs = createBar("debuffs")
createCollapse(B.buffs)
createConsolidation(B.buffs)
B.orientCollapse()
B.debuffs.container:SetPoint("TOPRIGHT", B.debuffs, "TOPRIGHT", 0, 0)
for _, key in ipairs({ "buffs", "debuffs" }) do
local c = BARS[key]
ForeverUI.Layout.Register(B[key], key, c.caption, "TOPRIGHT", "TOPRIGHT", c.x, c.y)
end
-- The client's BuffFrame, weapon enchants and consolidation
for _, name in ipairs({ "BuffFrame", "TemporaryEnchantFrame", "ConsolidatedBuffs", "ConsolidatedBuffsTooltip" }) do
ForeverUI.Suppress(_G[name])
end
-- Interface options (durations, consolidation) go through these two
if BuffFrame_UpdatePositions then hooksecurefunc("BuffFrame_UpdatePositions", function() B.update() end) end
if BuffFrame_Update then hooksecurefunc("BuffFrame_Update", function() B.update() end) end
local watcher = CreateFrame("Frame")
watcher:RegisterEvent("UNIT_AURA")
watcher:RegisterEvent("PLAYER_ENTERING_WORLD")
watcher:RegisterEvent("UNIT_ENTERED_VEHICLE")
watcher:RegisterEvent("UNIT_EXITED_VEHICLE")
watcher:SetScript("OnEvent", function(self, event, unit)
if event == "UNIT_AURA" then
if unit == B.unit() then B.update() end
elseif event == "PLAYER_ENTERING_WORLD" or unit == "player" then
B.update()
end
end)
-- Every 0.2 s: weapon enchants (3.3.5 has no event; its TemporaryEnchantFrame polls every
-- frame) and hidden buffs that return to the collapsed bar.
local function enchantsSignature()
if B.unit() ~= "player" then return "" end
local a, _, ca, g, _, cg = GetWeaponEnchantInfo()
return tostring(a) .. ":" .. tostring(ca) .. ":" .. tostring(GetInventoryItemTexture("player", 16))
.. "|" .. tostring(g) .. ":" .. tostring(cg) .. ":" .. tostring(GetInventoryItemTexture("player", 17))
end
watcher.clock = 0
watcher:SetScript("OnUpdate", function(self, elapsed)
self.clock = self.clock - (elapsed or 0)
if self.clock > 0 then return end
self.clock = G.check
local s = enchantsSignature()
if s ~= self.enchants then
self.enchants = s
B.update()
return
end
local f = B.buffs
if f.pending then
local now = GetTime()
for _, info in ipairs(f.auras) do
if info.hideable and info.expirationTime and info.expirationTime > 0
and (info.expirationTime - now) <= G.alertDuration then
B.update()
return
end
end
end
end)
B.watcher = watcher
B.update()
-- /fui buffs: prints the state of both bars.
function ForeverUI.BuffsDebug()
local say = function(t) DEFAULT_CHAT_FRAME:AddMessage("|cff66ccffForeverUI|r " .. t) end
local f = B.buffs
local shownItems = 0
for _, b in ipairs(f.buttons) do if b:IsShown() then shownItems = shownItems + 1 end end
say(string.format(L.BUFFS_DEBUG_BUFFS,
B.unit(), #f.auras, f.hideableCount or 0, shownItems, f.collapse:IsShown() and L.BUFFS_DEBUG_VISIBLE or L.BUFFS_DEBUG_HIDDEN, tostring(B.expanded),
f.consolidated:IsShown() and L.BUFFS_DEBUG_VISIBLE or L.BUFFS_DEBUG_HIDDEN, f.consolidated.count or 0, tostring(durations())))
local d = B.debuffs
local types = {}
for _, info in ipairs(d.auras) do table.insert(types, tostring(info.debuffType or NONE)) end
say(string.format(L.BUFFS_DEBUG_DEBUFFS, #d.auras, table.concat(types, ", ")))
end