Files
ShaguTweaks-ClassicAPI/mods/target-debufftimer.lua
T

171 lines
5.9 KiB
Lua

local _G = ShaguTweaks.GetGlobalEnv()
local T = ShaguTweaks.T
local API = ShaguTweaks.API
local module = ShaguTweaks:register({
title = T["Debuff Timer"],
description = T["Show debuff durations on the target unit frame."],
expansions = { ["vanilla"] = true },
category = T["Unit Frames"],
enabled = true,
})
local libdebuff = ShaguTweaks.libdebuff
local TimeConvert = ShaguTweaks.TimeConvert
local function GetLegacyDebuff(index)
if libdebuff and libdebuff.UnitDebuff then
return libdebuff:UnitDebuff("target", index)
end
end
local function NormalizeAuraString(value)
return value and value ~= "" and value or nil
end
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(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
-- fix own alpha value (should be inherited, but somehow isn't always)
this:SetAlpha(parent:GetAlpha())
local remaining = this.duration - (GetTime() - this.start)
if remaining >= 0 then
this.text:SetText(TimeConvert(remaining))
else
this:Hide()
end
end)
return cooldown.readable
end
module.enable = function(self)
local HookTargetDebuffButton_Update = TargetDebuffButton_Update
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, now)
local button = _G["TargetFrameDebuff"..i]
if button and not button.cd then
button.cd = CreateFrame("Model", "TargetFrameDebuff"..i.."Cooldown", button, "CooldownFrameTemplate")
button.cd.noCooldownCount = true
button.cd:SetAllPoints()
button.cd:SetScale(.6)
button.cd:SetAlpha(.8)
end
local dCount = _G["TargetFrameDebuff" .. i .. "Count"]
if button and dCount then
if not dCount.fixup then
dCount.fixup = true
dCount:SetPoint("BOTTOMRIGHT", "TargetFrameDebuff" .. i, "BOTTOMRIGHT", 6, -3)
end
if stacks and stacks > 1 then
dCount:SetText("|c0000ff3b" .. stacks)
dCount:Show()
else
dCount:Hide()
end
end
local dBorder = _G["TargetFrameDebuff" .. i .. "Border"]
if button and dBorder then
local color = dtype and DebuffTypeColor[dtype] or DebuffTypeColor["none"]
dBorder:SetVertexColor(color.r, color.g, color.b)
end
if button and effect and duration and timeleft and timeleft >= 0 then
local start = now + timeleft - duration
local readable = CreateTextCooldown(button.cd)
CooldownFrame_SetTimer(button.cd, start, duration, 1)
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)
if button.cd.readable then button.cd.readable:Hide() end
end
end
end
end