mirror of
https://github.com/brues-code/pfUI.git
synced 2026-11-02 00:46:57 +00:00
The cooldown module no longer builds its own text frame with a 10 Hz OnUpdate per cooldown. The CooldownFrame_SetTimer hook now configures each Cooldown frame's own countdown instead: SetHideCountdownNumbers for the per-frame text toggles and omnicc's noCooldownCount, SetMinimumCountdownDuration for the GCD threshold, and GetCountdownFontString for the pfUI font and size. The engine ticks the numbers, gated by the countdownForCooldowns cvar. The cvar gets a checkbox in the Cooldown settings, and pfUI.SetupCVars turns it on. The text comes from one shared C_StringUtil numeric rule formatter, given to each frame with SetCountdownFormatter. Its breakpoints keep pfUI's format: the number in its unit's cooldown color, a unit (m/h/d) once the unit below passes 99, and tenths in the low color when milliseconds are on. Values now round up. GetColoredTimeString formats through the same formatter, so the buff, buffwatch and tooltip timers match. A config:changed callback rebuilds the breakpoints, and every frame picks them up on its next update. The paper-doll slots swap their indicator Models for Cooldown frames, as the spellbook and bags already do. They and the spellbook take the BLIZZARD tag when they are created, replacing the per-update hooks in the skin module. Models get no numbers. Hide Foreign Cooldown Animations clears the draw flags instead of setting alpha to 0, so the numbers stay visible. The buff module's sweep hides its numbers because buff.timer carries the text.
412 lines
15 KiB
Lua
412 lines
15 KiB
Lua
pfUI:RegisterModule("buff", function ()
|
|
-- Hide Blizz
|
|
BuffFrame:Hide()
|
|
BuffFrame:UnregisterAllEvents()
|
|
TemporaryEnchantFrame:Hide()
|
|
TemporaryEnchantFrame:UnregisterAllEvents()
|
|
|
|
local br, bg, bb, ba = GetStringColor(pfUI_config.appearance.border.color)
|
|
|
|
-- Player aura slot ids, enumerated once per refresh pass (ScanPlayerAuraSlots)
|
|
-- and read per button by its aura index: one GetAuraSlots walk per range
|
|
-- instead of a by-index walk per button.
|
|
local helpfulSlots, harmfulSlots = {}, {}
|
|
local function ScanPlayerAuraSlots()
|
|
ScanAuraSlots("player", "HELPFUL", helpfulSlots, 32)
|
|
ScanAuraSlots("player", "HARMFUL", harmfulSlots, 16)
|
|
end
|
|
|
|
local function RefreshBuffButton(buff)
|
|
if buff.btype == "HELPFUL" then
|
|
if C.buffs.separateweapons == "1" then
|
|
buff.id = buff.gid - (buff.weapon ~= nil and buff.gid or 0)
|
|
else
|
|
buff.id = buff.gid - pfUI.buff.wepbuffs.count
|
|
end
|
|
else
|
|
buff.id = buff.gid
|
|
end
|
|
|
|
if not buff.backdrop then
|
|
CreateBackdrop(buff)
|
|
CreateBackdropShadow(buff)
|
|
end
|
|
|
|
local slots = buff.btype == "HELPFUL" and helpfulSlots or harmfulSlots
|
|
local name, icon, count, dispelType, duration, expirationTime, _, _, _, spellId = C_UnitAuras.UnitAuraBySlot("player", slots[buff.id])
|
|
|
|
--detect weapon buffs
|
|
if buff.btype == "HELPFUL" and ((C.buffs.separateweapons == "0" and buff.gid <= pfUI.buff.wepbuffs.count) or (pfUI.buff.wepbuffs.count > 0 and buff.weapon ~= nil)) then
|
|
local mh, mhtime, mhcharge, oh, ohtime, ohcharge = GetWeaponEnchantInfo()
|
|
if pfUI.buff.wepbuffs.count == 2 then
|
|
if buff.gid == 1 then
|
|
buff.mode = "MAINHAND"
|
|
else
|
|
buff.mode = "OFFHAND"
|
|
end
|
|
else
|
|
if C.buffs.separateweapons == "0" then
|
|
buff.mode = mh and "MAINHAND" or oh and "OFFHAND"
|
|
else
|
|
if buff.gid == 1 then
|
|
buff.mode = mh and "MAINHAND" or oh and "OFFHAND"
|
|
else
|
|
buff:Hide()
|
|
return
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Set Weapon Texture and Border
|
|
if buff.mode == "MAINHAND" then
|
|
buff.texture:SetTexture(GetInventoryItemTexture("player", 16))
|
|
buff.backdrop:SetBackdropBorderColor(GetItemQualityColor(GetInventoryItemQuality("player", 16) or 1))
|
|
elseif buff.mode == "OFFHAND" then
|
|
buff.texture:SetTexture(GetInventoryItemTexture("player", 17))
|
|
buff.backdrop:SetBackdropBorderColor(GetItemQualityColor(GetInventoryItemQuality("player", 17) or 1))
|
|
end
|
|
elseif name and (( buff.btype == "HARMFUL" and C.buffs.debuffs == "1" ) or ( buff.btype == "HELPFUL" and C.buffs.buffs == "1" )) then
|
|
-- Set Buff Texture and Border
|
|
buff.mode = buff.btype
|
|
buff.expirationTime = expirationTime
|
|
buff.stackCount = count
|
|
buff.spellId = spellId
|
|
buff.texture:SetTexture(icon)
|
|
|
|
if buff.btype == "HARMFUL" then
|
|
local dispelColor = C_UnitAuras.GetAuraDispelTypeColor(dispelType)
|
|
buff.backdrop:SetBackdropBorderColor(dispelColor:GetRGBA())
|
|
else
|
|
buff.backdrop:SetBackdropBorderColor(br,bg,bb,ba)
|
|
end
|
|
else
|
|
buff:Hide()
|
|
return
|
|
end
|
|
|
|
-- drive the sweep from the aura duration (weapon enchants and permanent
|
|
-- auras report no usable total, so those buttons stay text-only)
|
|
if C.buffs.cooldown_anim == "1" and buff.mode == buff.btype and duration and duration > 0 and expirationTime and expirationTime > 0 then
|
|
local now = GetTime()
|
|
local timeleft = expirationTime - now
|
|
if timeleft > 0 then
|
|
local effDuration = duration > timeleft and duration or timeleft
|
|
local newStart = now + timeleft - effDuration
|
|
if buff.cdStart == nil or abs(buff.cdStart - newStart) > .5 or buff.cdDuration ~= effDuration then
|
|
buff.cd:SetCooldown(newStart, effDuration)
|
|
buff.cdStart = newStart
|
|
buff.cdDuration = effDuration
|
|
end
|
|
else
|
|
buff.cd:Clear()
|
|
buff.cdStart = nil
|
|
end
|
|
else
|
|
buff.cd:Clear()
|
|
buff.cdStart = nil
|
|
end
|
|
|
|
buff:Show()
|
|
end
|
|
|
|
local function CreateBuffButton(i, btype, weapon)
|
|
local buttonName, buttonParent
|
|
if btype == "HELPFUL" then
|
|
if weapon == 1 then
|
|
buttonName = "pfWepBuffFrame" .. i
|
|
buttonParent = pfUI.buff.wepbuffs
|
|
else
|
|
buttonName = "pfBuffFrameBuff" .. i
|
|
buttonParent = pfUI.buff.buffs
|
|
end
|
|
else
|
|
buttonName = "pfDebuffFrameBuff" .. i
|
|
buttonParent = pfUI.buff.debuffs
|
|
end
|
|
local buff = CreateFrame("Button", buttonName, buttonParent)
|
|
buff.texture = buff:CreateTexture("BuffIcon" .. i, "BACKGROUND")
|
|
buff.texture:SetTexCoord(.07,.93,.07,.93)
|
|
buff.texture:SetAllPoints(buff)
|
|
|
|
-- overlay lifts the texts above the sweep: a child frame renders above
|
|
-- all of its parent's own regions, so the timer would dim under the
|
|
-- dark wedge otherwise
|
|
buff.overlay = CreateFrame("Frame", nil, buff)
|
|
buff.overlay:SetAllPoints(buff)
|
|
|
|
buff.timer = buff.overlay:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
|
buff.timer:SetTextColor(1,1,1,1)
|
|
buff.timer:SetJustifyH("CENTER")
|
|
buff.timer:SetJustifyV("CENTER")
|
|
|
|
buff.stacks = buff.overlay:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
|
buff.stacks:SetTextColor(1,1,1,1)
|
|
buff.stacks:SetJustifyH("RIGHT")
|
|
buff.stacks:SetJustifyV("BOTTOM")
|
|
buff.stacks:SetAllPoints(buff)
|
|
|
|
-- radial sweep (the template covers the button and starts hidden).
|
|
-- RefreshBuffButton drives it through the frame's own methods, never
|
|
-- CooldownFrame_SetTimer, so the cooldown module's hook leaves it alone;
|
|
-- text stays with buff.timer, so the frame's own numbers are hidden
|
|
buff.cd = CreateFrame("Cooldown", buttonName .. "Cooldown", buff, "CooldownFrameTemplate")
|
|
buff.cd:SetHideCountdownNumbers(true)
|
|
buff.cd:SetFrameLevel(buff:GetFrameLevel() + 1)
|
|
buff.overlay:SetFrameLevel(buff:GetFrameLevel() + 2)
|
|
|
|
buff:RegisterForClicks("RightButtonUp")
|
|
|
|
buff.weapon = weapon
|
|
buff.btype = btype
|
|
buff.gid = i
|
|
|
|
-- PERF: OnUpdate moved to consolidated parent frame handler (see pfUI.buff:SetScript("OnUpdate"))
|
|
-- Individual buff frames no longer have their own OnUpdate
|
|
|
|
buff:SetScript("OnEnter", function()
|
|
GameTooltip:SetOwner(this, "ANCHOR_BOTTOMRIGHT")
|
|
if this.mode == this.btype then
|
|
GameTooltip:SetUnitAura("player", this.id, this.btype)
|
|
|
|
if IsShiftKeyDown() then
|
|
local aura = C_UnitAuras.GetAuraDataByIndex("player", this.id, this.btype)
|
|
local playerlist = aura and GetUnbuffedRoster(aura.name) or ""
|
|
if strlen(playerlist) > 0 then
|
|
GameTooltip:AddLine(" ")
|
|
GameTooltip:AddLine(T["Unbuffed"] .. ":", .3, 1, .8)
|
|
GameTooltip:AddLine(playerlist,1,1,1,1)
|
|
GameTooltip:Show()
|
|
end
|
|
end
|
|
elseif this.mode == "MAINHAND" then
|
|
GameTooltip:SetInventoryItem("player", 16)
|
|
elseif this.mode == "OFFHAND" then
|
|
GameTooltip:SetInventoryItem("player", 17)
|
|
end
|
|
end)
|
|
|
|
buff:SetScript("OnLeave", GameTooltip_Hide)
|
|
|
|
buff:SetScript("OnClick", function()
|
|
if this.spellId then
|
|
C_Spell.CancelSpellByID(this.spellId)
|
|
end
|
|
end)
|
|
|
|
RefreshBuffButton(buff)
|
|
|
|
return buff
|
|
end
|
|
|
|
pfUI.buff = CreateFrame("Frame", "pfGlobalBuffFrame", UIParent)
|
|
pfUI.buff:RegisterEvent("PLAYER_AURAS_CHANGED")
|
|
pfUI.buff:RegisterEvent("PLAYER_EQUIPMENT_CHANGED")
|
|
pfUI.buff:RegisterUnitEvent("UNIT_MODEL_CHANGED", "player")
|
|
pfUI.buff:RegisterEvent("BUFF_UPDATE_DURATION_SELF")
|
|
pfUI.buff:RegisterEvent("DEBUFF_UPDATE_DURATION_SELF")
|
|
pfUI.buff:SetScript("OnEvent", function()
|
|
|
|
if C.buffs.weapons == "1" then
|
|
local mh, mhtime, mhcharge, oh, ohtime, ohcharge = GetWeaponEnchantInfo()
|
|
pfUI.buff.wepbuffs.count = (mh and 1 or 0) + (oh and 1 or 0)
|
|
else
|
|
pfUI.buff.wepbuffs.count = 0
|
|
end
|
|
|
|
ScanPlayerAuraSlots()
|
|
|
|
for i=1,32 do
|
|
RefreshBuffButton(pfUI.buff.buffs.buttons[i])
|
|
end
|
|
|
|
for i=1,16 do
|
|
RefreshBuffButton(pfUI.buff.debuffs.buttons[i])
|
|
end
|
|
|
|
if C.buffs.separateweapons == "1" then
|
|
for i=1,2 do
|
|
RefreshBuffButton(pfUI.buff.wepbuffs.buttons[i])
|
|
end
|
|
end
|
|
end)
|
|
|
|
-- PERF: Consolidated OnUpdate handler for all buff timers
|
|
-- This replaces 50 individual OnUpdate handlers with a single one
|
|
pfUI.buff:SetScript("OnUpdate", function()
|
|
local now = GetTime()
|
|
if not this.nextUpdate then this.nextUpdate = now + 0.1 end
|
|
if this.nextUpdate > now then return end
|
|
this.nextUpdate = now + 0.1
|
|
|
|
-- Cache weapon enchant info once per update cycle
|
|
local mh, mhtime, mhcharge, oh, ohtime, ohcharge = GetWeaponEnchantInfo()
|
|
|
|
-- Update all visible buff buttons
|
|
local buttons = pfUI.buff.buffs.buttons
|
|
for i = 1, 32 do
|
|
local buff = buttons[i]
|
|
if buff:IsShown() then
|
|
local timeleft, stacks = 0, 0
|
|
if buff.mode == buff.btype then
|
|
timeleft = buff.expirationTime > 0 and (buff.expirationTime - now) or 0
|
|
stacks = buff.stackCount or 0
|
|
elseif buff.mode == "MAINHAND" then
|
|
timeleft = mhtime and mhtime / 1000 or 0
|
|
stacks = mhcharge or 0
|
|
elseif buff.mode == "OFFHAND" then
|
|
timeleft = ohtime and ohtime / 1000 or 0
|
|
stacks = ohcharge or 0
|
|
end
|
|
buff.timer:SetText(timeleft > 0 and GetColoredTimeString(timeleft) or "")
|
|
buff.stacks:SetText(stacks > 1 and stacks or "")
|
|
end
|
|
end
|
|
|
|
-- Update all visible debuff buttons
|
|
buttons = pfUI.buff.debuffs.buttons
|
|
for i = 1, 16 do
|
|
local buff = buttons[i]
|
|
if buff:IsShown() then
|
|
local timeleft = buff.expirationTime > 0 and (buff.expirationTime - now) or 0
|
|
local stacks = buff.stackCount or 0
|
|
buff.timer:SetText(timeleft > 0 and GetColoredTimeString(timeleft) or "")
|
|
buff.stacks:SetText(stacks > 1 and stacks or "")
|
|
end
|
|
end
|
|
|
|
-- Update weapon buff buttons if separate
|
|
if C.buffs.separateweapons == "1" then
|
|
buttons = pfUI.buff.wepbuffs.buttons
|
|
for i = 1, 2 do
|
|
local buff = buttons[i]
|
|
if buff:IsShown() then
|
|
local timeleft, stacks = 0, 0
|
|
if buff.mode == "MAINHAND" then
|
|
timeleft = mhtime and mhtime / 1000 or 0
|
|
stacks = mhcharge or 0
|
|
elseif buff.mode == "OFFHAND" then
|
|
timeleft = ohtime and ohtime / 1000 or 0
|
|
stacks = ohcharge or 0
|
|
end
|
|
buff.timer:SetText(timeleft > 0 and GetColoredTimeString(timeleft) or "")
|
|
buff.stacks:SetText(stacks > 1 and stacks or "")
|
|
end
|
|
end
|
|
end
|
|
end)
|
|
|
|
-- CreateBuffButton refreshes each new button from the slot buffers
|
|
ScanPlayerAuraSlots()
|
|
|
|
-- Weapon Buffs
|
|
pfUI.buff.wepbuffs = CreateFrame("Frame", "pfWepBuffFrame", UIParent)
|
|
pfUI.buff.wepbuffs.count = 0
|
|
pfUI.buff.wepbuffs.buttons = {}
|
|
for i=1,2 do
|
|
pfUI.buff.wepbuffs.buttons[i] = CreateBuffButton(i, "HELPFUL", 1)
|
|
end
|
|
|
|
-- Buff Frame
|
|
pfUI.buff.buffs = CreateFrame("Frame", "pfBuffFrame", UIParent)
|
|
pfUI.buff.buffs.buttons = {}
|
|
for i=1,32 do
|
|
pfUI.buff.buffs.buttons[i] = CreateBuffButton(i, "HELPFUL")
|
|
end
|
|
|
|
-- Debuffs
|
|
pfUI.buff.debuffs = CreateFrame("Frame", "pfDebuffFrame", UIParent)
|
|
pfUI.buff.debuffs.buttons = {}
|
|
for i=1,16 do
|
|
pfUI.buff.debuffs.buttons[i] = CreateBuffButton(i, "HARMFUL")
|
|
end
|
|
|
|
-- config loading
|
|
function pfUI.buff:UpdateConfigBuffButton(buff)
|
|
local fontsize = C.buffs.fontsize == "-1" and C.global.font_size or C.buffs.fontsize
|
|
local buffSize, buffSpacing = tonumber(C.buffs.size), tonumber(C.buffs.spacing)
|
|
local rowcount, relFrame, offsetX, offsetY
|
|
if buff.btype == "HELPFUL" then
|
|
if buff.weapon == 1 and C.buffs.separateweapons == "1" then
|
|
rowcount = floor((buff.gid-1) / tonumber(C.buffs.wepbuffrowsize))
|
|
relFrame = pfUI.buff.wepbuffs
|
|
offsetX = -(buff.gid-1-rowcount*tonumber(C.buffs.wepbuffrowsize))*(buffSize+2*buffSpacing)
|
|
offsetY = -(rowcount) * ((C.buffs.textinside == "1" and 0 or (fontsize*1.5))+buffSize+2*buffSpacing)
|
|
else
|
|
rowcount = floor((buff.gid-1) / tonumber(C.buffs.buffrowsize))
|
|
relFrame = pfUI.buff.buffs
|
|
offsetX = -(buff.gid-1-rowcount*tonumber(C.buffs.buffrowsize))*(buffSize+2*buffSpacing)
|
|
offsetY = -(rowcount) * ((C.buffs.textinside == "1" and 0 or (fontsize*1.5))+buffSize+2*buffSpacing)
|
|
end
|
|
else
|
|
rowcount = floor((buff.gid-1) / tonumber(C.buffs.debuffrowsize))
|
|
relFrame = pfUI.buff.debuffs
|
|
offsetX = -(buff.gid-1-rowcount*tonumber(C.buffs.debuffrowsize))*(buffSize+2*buffSpacing)
|
|
offsetY = -(rowcount) * ((C.buffs.textinside == "1" and 0 or (fontsize*1.5))+buffSize+2*buffSpacing)
|
|
end
|
|
buff:SetSize(buffSize, buffSize)
|
|
buff.cd:SetReverse(C.buffs.cooldown_reverse == "1")
|
|
buff:ClearAllPoints()
|
|
buff:SetPoint("TOPRIGHT", relFrame, "TOPRIGHT",offsetX, offsetY)
|
|
|
|
buff.timer:SetFont(pfUI.font_default, fontsize, "OUTLINE")
|
|
buff.stacks:SetFont(pfUI.font_default, fontsize+1, "OUTLINE")
|
|
|
|
buff.timer:SetHeight(fontsize * 1.3)
|
|
|
|
buff.timer:ClearAllPoints()
|
|
if C.buffs.textinside == "1" then
|
|
buff.timer:SetAllPoints(buff)
|
|
else
|
|
buff.timer:SetPoint("TOP", buff, "BOTTOM", 0, -3)
|
|
end
|
|
end
|
|
|
|
function pfUI.buff:UpdateConfig()
|
|
local fontsize = C.buffs.fontsize == "-1" and C.global.font_size or C.buffs.fontsize
|
|
|
|
local spacing = tonumber(C.buffs.spacing)
|
|
local cell = tonumber(C.buffs.size) + 2 * spacing
|
|
local rowextra = C.buffs.textinside == "1" and 0 or (fontsize * 1.5)
|
|
|
|
local function SizeBuffFrame(frame, rowsize, count)
|
|
rowsize = tonumber(rowsize)
|
|
frame:SetSize(rowsize * cell, ceil(count / rowsize) * (rowextra + cell))
|
|
end
|
|
|
|
SizeBuffFrame(pfUI.buff.buffs, C.buffs.buffrowsize, 32)
|
|
pfUI.buff.buffs:SetPoint("TOPRIGHT", pfUI.minimap or UIParent, "TOPLEFT", -4 * spacing, 0)
|
|
UpdateMovable(pfUI.buff.buffs)
|
|
|
|
SizeBuffFrame(pfUI.buff.debuffs, C.buffs.debuffrowsize, 16)
|
|
pfUI.buff.debuffs:SetPoint("TOPRIGHT", pfUI.buff.buffs, "BOTTOMRIGHT", 0, 0)
|
|
UpdateMovable(pfUI.buff.debuffs)
|
|
|
|
if C.buffs.separateweapons == "1" then
|
|
pfUI.buff.wepbuffs:ClearAllPoints()
|
|
SizeBuffFrame(pfUI.buff.wepbuffs, C.buffs.wepbuffrowsize, 2)
|
|
pfUI.buff.wepbuffs:SetPoint("TOPRIGHT", pfUI.buff.debuffs, "BOTTOMRIGHT", 0, 0)
|
|
pfUI.buff.wepbuffs:Show()
|
|
UpdateMovable(pfUI.buff.wepbuffs)
|
|
else
|
|
pfUI.buff.wepbuffs:Hide()
|
|
RemoveMovable(pfUI.buff.wepbuffs)
|
|
end
|
|
|
|
for i=1,32 do
|
|
pfUI.buff:UpdateConfigBuffButton(pfUI.buff.buffs.buttons[i])
|
|
end
|
|
|
|
for i=1,16 do
|
|
pfUI.buff:UpdateConfigBuffButton(pfUI.buff.debuffs.buttons[i])
|
|
end
|
|
|
|
for i=1,2 do
|
|
pfUI.buff:UpdateConfigBuffButton(pfUI.buff.wepbuffs.buttons[i])
|
|
end
|
|
|
|
pfUI.buff:GetScript("OnEvent")()
|
|
end
|
|
|
|
pfUI.buff:UpdateConfig()
|
|
end)
|