Use zero-GC ClassicAPI debuff data
This commit is contained in:
+82
-41
@@ -13,56 +13,88 @@ local module = ShaguTweaks:register({
|
||||
local libdebuff = ShaguTweaks.libdebuff
|
||||
local TimeConvert = ShaguTweaks.TimeConvert
|
||||
|
||||
local function GetTargetDebuff(index)
|
||||
-- ClassicAPI exposes the real aura name, stack count, dispel type and,
|
||||
-- when known by the client, the exact applied duration/expiration time.
|
||||
-- Keep libdebuff as the compatibility fallback for missing timing data.
|
||||
local aura = API and API.GetDebuffDataByIndex and API.GetDebuffDataByIndex("target", index)
|
||||
if aura then
|
||||
local duration = aura.duration and aura.duration > 0 and aura.duration or nil
|
||||
local dtype = aura.dispelName and aura.dispelName ~= "" and aura.dispelName or nil
|
||||
|
||||
if duration and aura.expirationTime and aura.expirationTime > 0 then
|
||||
local timeleft = aura.expirationTime - GetTime()
|
||||
if timeleft < 0 then timeleft = 0 end
|
||||
return aura.name, nil, aura.icon, aura.applications, dtype, duration, timeleft
|
||||
end
|
||||
|
||||
-- ClassicAPI may know the aura but not its exact expiration when the cast
|
||||
-- was not observed. Preserve ShaguTweaks' old estimated timer in that case.
|
||||
if duration and libdebuff and libdebuff.UnitDebuff then
|
||||
local effect, rank, texture, stacks, legacyType, legacyDuration, legacyTimeleft = libdebuff:UnitDebuff("target", index)
|
||||
return aura.name or effect, rank, aura.icon or texture,
|
||||
aura.applications or stacks, dtype or legacyType, legacyDuration, legacyTimeleft
|
||||
end
|
||||
|
||||
-- Duration 0 means an indefinite aura: show metadata, but no cooldown.
|
||||
return aura.name, nil, aura.icon, aura.applications, dtype, nil, -1
|
||||
end
|
||||
|
||||
local function GetLegacyDebuff(index)
|
||||
if libdebuff and libdebuff.UnitDebuff then
|
||||
return libdebuff:UnitDebuff("target", index)
|
||||
end
|
||||
end
|
||||
|
||||
local function CreateTextCooldown(cooldown)
|
||||
if cooldown.readable then return end
|
||||
local function NormalizeAuraString(value)
|
||||
return value and value ~= "" and value or nil
|
||||
end
|
||||
|
||||
cooldown.readable = CreateFrame("Frame", "pfCooldownFrame", cooldown:GetParent())
|
||||
local function ResolveDebuffTiming(name, icon, stacks, dtype, duration, expirationTime, index, now)
|
||||
icon = NormalizeAuraString(icon)
|
||||
dtype = NormalizeAuraString(dtype)
|
||||
duration = duration and duration > 0 and duration or nil
|
||||
|
||||
if duration and expirationTime and expirationTime > 0 then
|
||||
local timeleft = expirationTime - now
|
||||
if timeleft < 0 then timeleft = 0 end
|
||||
return name, nil, icon, stacks, dtype, duration, timeleft
|
||||
end
|
||||
|
||||
-- ClassicAPI always knows the aura metadata, but expiration can be unknown
|
||||
-- when the client did not observe its application. Keep ShaguTweaks' old
|
||||
-- estimate only for that missing-timing case.
|
||||
if duration then
|
||||
local effect, rank, texture, legacyStacks, legacyType,
|
||||
legacyDuration, legacyTimeleft = GetLegacyDebuff(index)
|
||||
if effect then
|
||||
return name or effect, rank, icon or texture, stacks or legacyStacks,
|
||||
dtype or legacyType, legacyDuration, legacyTimeleft
|
||||
end
|
||||
end
|
||||
|
||||
-- Duration 0 is a permanent/indefinite aura. Keep its metadata visible but
|
||||
-- do not create a fake cooldown.
|
||||
return name, nil, icon, stacks, dtype, nil, -1
|
||||
end
|
||||
|
||||
local function GetTargetDebuff(index, now)
|
||||
-- Prefer ClassicAPI's positional accessor: same useful aura/timing data as
|
||||
-- AuraData, but no temporary Lua table allocation for every debuff button.
|
||||
if API and API.aurapositional and API.UnitDebuff then
|
||||
local name, icon, stacks, dtype, duration, expirationTime =
|
||||
API.UnitDebuff("target", index)
|
||||
if name then
|
||||
return ResolveDebuffTiming(name, icon, stacks, dtype, duration,
|
||||
expirationTime, index, now)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- Compatibility with older ClassicAPI builds that only expose AuraData.
|
||||
local aura = API and API.GetDebuffDataByIndex and
|
||||
API.GetDebuffDataByIndex("target", index)
|
||||
if aura then
|
||||
return ResolveDebuffTiming(aura.name, aura.icon, aura.applications,
|
||||
aura.dispelName, aura.duration, aura.expirationTime, index, now)
|
||||
end
|
||||
|
||||
return GetLegacyDebuff(index)
|
||||
end
|
||||
|
||||
local function CreateTextCooldown(cooldown)
|
||||
if cooldown.readable then return cooldown.readable end
|
||||
|
||||
-- These overlays never need global frame names. Keeping them anonymous
|
||||
-- avoids collisions when several target debuffs are visible at once.
|
||||
cooldown.readable = CreateFrame("Frame", nil, cooldown:GetParent())
|
||||
cooldown.readable:SetAllPoints(cooldown)
|
||||
cooldown.readable:SetFrameLevel(cooldown:GetParent():GetFrameLevel() + 1)
|
||||
cooldown.readable.text = cooldown.readable:CreateFontString("pfCooldownFrameText", "OVERLAY")
|
||||
cooldown.readable.text = cooldown.readable:CreateFontString(nil, "OVERLAY")
|
||||
|
||||
cooldown.readable.text:SetFont(STANDARD_TEXT_FONT, 10, "OUTLINE")
|
||||
cooldown.readable.text:SetPoint("CENTER", cooldown.readable, "CENTER", 0, 0)
|
||||
cooldown.readable:SetScript("OnUpdate", function()
|
||||
this.elapsed = (this.elapsed or 0) + arg1
|
||||
if this.elapsed < .1 then return end
|
||||
this.elapsed = 0
|
||||
|
||||
local parent = this:GetParent()
|
||||
if not parent then this:Hide() return end
|
||||
|
||||
if not this.next then this.next = GetTime() + .1 end
|
||||
if this.next > GetTime() then return end
|
||||
this.next = GetTime() + .1
|
||||
|
||||
-- fix own alpha value (should be inherited, but somehow isn't always)
|
||||
this:SetAlpha(parent:GetAlpha())
|
||||
|
||||
@@ -73,6 +105,8 @@ local function CreateTextCooldown(cooldown)
|
||||
this:Hide()
|
||||
end
|
||||
end)
|
||||
|
||||
return cooldown.readable
|
||||
end
|
||||
|
||||
module.enable = function(self)
|
||||
@@ -80,8 +114,13 @@ module.enable = function(self)
|
||||
TargetDebuffButton_Update = function()
|
||||
HookTargetDebuffButton_Update()
|
||||
|
||||
-- All buttons in this refresh share the same clock sample. Besides being
|
||||
-- cheaper, this keeps their displayed countdowns perfectly aligned.
|
||||
local now = GetTime()
|
||||
|
||||
for i=1, MAX_TARGET_DEBUFFS do
|
||||
local effect, rank, texture, stacks, dtype, duration, timeleft = GetTargetDebuff(i)
|
||||
local effect, rank, texture, stacks, dtype, duration, timeleft =
|
||||
GetTargetDebuff(i, now)
|
||||
local button = _G["TargetFrameDebuff"..i]
|
||||
|
||||
if button and not button.cd then
|
||||
@@ -113,12 +152,14 @@ module.enable = function(self)
|
||||
end
|
||||
|
||||
if button and effect and duration and timeleft and timeleft >= 0 then
|
||||
local start = GetTime() + timeleft - duration
|
||||
CreateTextCooldown(button.cd)
|
||||
local start = now + timeleft - duration
|
||||
local readable = CreateTextCooldown(button.cd)
|
||||
CooldownFrame_SetTimer(button.cd, start, duration, 1)
|
||||
button.cd.readable.start = start
|
||||
button.cd.readable.duration = duration
|
||||
button.cd.readable:Show()
|
||||
readable.start = start
|
||||
readable.duration = duration
|
||||
readable.elapsed = 0
|
||||
readable.text:SetText(TimeConvert(timeleft))
|
||||
readable:Show()
|
||||
button.cd:Show()
|
||||
elseif button then
|
||||
CooldownFrame_SetTimer(button.cd,0,0,0)
|
||||
|
||||
Reference in New Issue
Block a user