Timer bars: buffwatch, loot roll, infobox and five-second rule drain from duration objects

Each bar hands its fill to StatusBar:SetTimerDuration instead of a per-frame
SetValue. Buffwatch's time text moves to a duration text binding holding the
cooldown formatter, now exposed as GetTimeFormatter (replacing
GetColoredTimeString, whose only caller this was). The five-second rule's
overlay style becomes a color-filled StatusBar too, so both styles share one
path; energytick's OnUpdate keeps only the tick spark.
This commit is contained in:
Brues
2026-11-03 01:07:02 -06:00
parent 2909679d62
commit d7e732981b
5 changed files with 68 additions and 68 deletions
+24 -15
View File
@@ -130,15 +130,14 @@ pfUI:RegisterModule("buffwatch", function ()
GameTooltip:Hide()
end
local function StatusBarOnUpdate()
local remaining = this.endtime - GetTime()
this.bar:SetValue(remaining > 0 and remaining or 0)
if ( this.tick or 1) > GetTime() then return else this.tick = GetTime() + .1 end
this.time:SetText(remaining > 0 and GetColoredTimeString(remaining) or "")
-- an enabled text binding writes every frame, shown or not
local function StatusBarOnShow()
this.timer:Enable()
this.parent:RefreshPosition()
end
local function StatusBarRefreshParent()
local function StatusBarOnHide()
this.timer:Disable()
this.parent:RefreshPosition()
end
@@ -183,6 +182,16 @@ pfUI:RegisterModule("buffwatch", function ()
frame.time:SetTextColor(1,1,1,1)
frame.time:SetJustifyH("RIGHT")
-- the bar drains and the time counts down from this, engine-side
frame.duration = C_DurationUtil.CreateDuration()
frame.bar:SetTimerDuration(frame.duration, Enum.StatusBarInterpolation.Immediate, Enum.StatusBarTimerDirection.RemainingTime)
frame.timer = C_DurationUtil.CreateDurationTextBinding()
frame.timer:SetFontString(frame.time)
frame.timer:SetFormatter(GetTimeFormatter())
frame.timer:SetDuration(frame.duration)
frame.timer:SetExpiredText("")
frame.timer:SetZeroDurationText("")
frame.icon = frame:CreateTexture(nil, "OVERLAY")
frame.icon:SetSize(height, height)
frame.icon:SetPoint("LEFT", frame, "LEFT", 0, 0)
@@ -195,9 +204,8 @@ pfUI:RegisterModule("buffwatch", function ()
frame.stacks:SetJustifyV("CENTER")
frame.parent = parent
frame:SetScript("OnUpdate", StatusBarOnUpdate)
frame:SetScript("OnShow", StatusBarRefreshParent)
frame:SetScript("OnHide", StatusBarRefreshParent)
frame:SetScript("OnShow", StatusBarOnShow)
frame:SetScript("OnHide", StatusBarOnHide)
frame:RegisterForClicks("LeftButtonUp", "RightButtonUp")
frame:SetScript("OnClick", StatusBarOnClick)
@@ -261,7 +269,6 @@ pfUI:RegisterModule("buffwatch", function ()
frame.bars[bar].id = data[2]
frame.bars[bar].unit = frame.unit
frame.bars[bar].type = frame.type
frame.bars[bar].endtime = GetTime() + ( data[1] > 0 and data[1] or -1 )
-- update max duration the cached remaining values is less than
-- the real one, indicates a buff renewal
@@ -313,10 +320,12 @@ pfUI:RegisterModule("buffwatch", function ()
frame.bars[bar].icon:SetTexture(data[4])
end
-- cache maxduration
if frame.bars[bar].cacheMaxDuration ~= frame.durations[uuid][2] then
frame.bars[bar].cacheMaxDuration = frame.durations[uuid][2]
frame.bars[bar].bar:SetMinMaxValues(0, frame.durations[uuid][2])
-- the bar spans the longest time left seen for this aura; one without
-- a time left (threshold -1) shows an empty bar and no time
if data[1] > 0 then
frame.bars[bar].duration:SetTimeFromEnd(GetTime() + data[1], frame.durations[uuid][2])
else
frame.bars[bar].duration:Reset()
end
-- set stacks
+28 -29
View File
@@ -166,8 +166,8 @@ pfUI:RegisterModule("energytick", function()
local cost = C_Spell.GetSpellPowerCost(arg3)
cost = cost and cost[1]
if cost and cost.type == Enum.PowerType.Mana and cost.cost > 0 then
this.fsrSpell, this.fsrEnd = arg3, GetTime() + FIVE_SECOND_RULE
this.fsrGain = nil
this.fsrSpell, this.fsrGain = arg3, nil
this.fsr:SetTimeFromStart(GetTime(), FIVE_SECOND_RULE)
end
return
end
@@ -175,7 +175,8 @@ pfUI:RegisterModule("energytick", function()
-- Unit::Update won't expire the rule while the spending spell still channels
if event == "UNIT_SPELLCAST_CHANNEL_STOP" and arg1 == "player" then
if this.fsrSpell and this.fsrSpell == arg3 then
this.fsrEnd, this.fsrGain = GetTime() + FIVE_SECOND_RULE, nil
this.fsrGain = nil
this.fsr:SetTimeFromStart(GetTime(), FIVE_SECOND_RULE)
end
return
end
@@ -189,7 +190,7 @@ pfUI:RegisterModule("energytick", function()
-- only a gain can be the tick; a spend never touches the phase
if diff > 0 and lockTick(this) then
-- a tick inside the window proves regen continues through it
if this.fsrEnd and this.fsrEnd > GetTime() then
if this.fsr:IsActive() then
this.fsrGain = true
end
end
@@ -208,25 +209,15 @@ pfUI:RegisterModule("energytick", function()
end
this.tick = GetTime() + 0.020 -- ~50 FPS
-- five-second rule drains to nothing. The display is cosmetic and can be
-- hidden on its own; the window itself (fsrEnd/fsrGain) keeps running
-- either way -- it still dims the sweep spark below.
local remaining = this.fsrEnd and (this.fsrEnd - GetTime()) or 0
if this.mode == "MANA" and remaining > 0 then
if C.unitframes.player.showfsr == "1" then
if fsrOverlay then
this.fsrbar:SetWidth(getBarWidth() * remaining / FIVE_SECOND_RULE)
else
this.fsrbar:SetValue(remaining)
end
this.fsrbar:Show()
else
this.fsrbar:Hide()
end
else
this.fsrSpell, this.fsrEnd, this.fsrGain = nil, nil, nil
this.fsrbar:Hide()
-- five-second rule: fsrbar drains from this.fsr by itself. The display is
-- cosmetic and can be hidden on its own; the window itself (fsr/fsrGain)
-- keeps running either way -- it still dims the sweep spark below.
local fsrUp = this.mode == "MANA" and this.fsr:IsActive()
if not fsrUp and this.fsrSpell then
this.fsrSpell, this.fsrGain = nil, nil
this.fsr:Reset()
end
this.fsrbar:SetShown(fsrUp and C.unitframes.player.showfsr == "1")
if not this.start then
this.spark:SetAlpha(0)
@@ -249,7 +240,7 @@ pfUI:RegisterModule("energytick", function()
end
-- dim while the rule is up and nothing has ticked inside it yet
this.spark:SetAlpha((remaining > 0 and not this.fsrGain) and .4 or 1)
this.spark:SetAlpha((fsrUp and not this.fsrGain) and .4 or 1)
if not C.unitframes.player.pheight then
return
@@ -259,19 +250,21 @@ pfUI:RegisterModule("energytick", function()
this.spark:SetPoint("LEFT", pos - ((C.unitframes.player.pheight + 5) / 2), 0)
end)
energytick.fsr = C_DurationUtil.CreateDuration()
if fsrOverlay then
-- classic style: a translucent shade on top of the power bar's own fill
energytick.fsrbar = energytick:CreateTexture(nil, "ARTWORK")
energytick.fsrbar:SetColorTexture(1, 1, 1, .15)
energytick.fsrbar:SetPoint("TOPLEFT", 0, 0)
energytick.fsrbar:SetPoint("BOTTOMLEFT", 0, 0)
-- classic style: a translucent shade on top of the power bar's own fill,
-- under the spark
energytick.fsrbar = CreateFrame("StatusBar", nil, pfUI.uf.player.power.bar)
energytick.fsrbar:SetAllPoints(energytick)
energytick.fsrbar:SetColorFill(1, 1, 1, .15)
energytick:SetFrameLevel(energytick.fsrbar:GetFrameLevel() + 1)
else
-- a real bar below the power bar, not an overlay on top of it -- the same
-- treatment nameplate castbars get relative to the health bar, so the
-- window can't be mistaken for missing mana
energytick.fsrbar = CreateFrame("StatusBar", nil, pfUI.uf.player)
energytick.fsrbar:SetStatusBarTexture(cbtexture)
energytick.fsrbar:SetMinMaxValues(0, FIVE_SECOND_RULE)
energytick.fsrbar:SetPoint("TOPLEFT", pfUI.uf.player.power.bar, "BOTTOMLEFT", 0, -default_border * 2)
energytick.fsrbar:SetPoint("TOPRIGHT", pfUI.uf.player.power.bar, "BOTTOMRIGHT", 0, -default_border * 2)
energytick.fsrbar:SetHeight(tonumber(C.unitframes.player.fsrheight) or 4)
@@ -279,8 +272,14 @@ pfUI:RegisterModule("energytick", function()
CreateBackdrop(energytick.fsrbar, default_border)
CreateBackdropShadow(energytick.fsrbar)
end
energytick.fsrbar:SetTimerDuration(energytick.fsr, Enum.StatusBarInterpolation.Immediate, Enum.StatusBarTimerDirection.RemainingTime)
energytick.fsrbar:Hide()
-- the bar isn't parented to energytick, and OnUpdate stops with it
energytick:SetScript("OnHide", function()
this.fsrbar:Hide()
end)
energytick.spark = energytick:CreateTexture(nil, "OVERLAY")
energytick.spark:SetTexture("Interface\\CastingBar\\UI-CastingBar-Spark")
energytick.spark:SetHeight(C.unitframes.player.pheight + 15)
+6 -10
View File
@@ -238,17 +238,11 @@ pfUI:RegisterModule("roll", function ()
f.time.bar = CreateFrame("StatusBar", "pfLootRollFrame" .. id .. "TimeBar", f.time)
f.time.bar:SetAllPoints(f.time)
f.time.bar:SetStatusBarTexture(pfUI.media["img:bar"])
f.time.bar:SetMinMaxValues(0, 100)
local r, g, b, a = GetStringColor(C.appearance.border.color)
f.time.bar:SetStatusBarColor(r, g, b)
f.time.bar:SetValue(20)
f.time.bar:SetScript("OnUpdate", function()
if not this:GetParent():GetParent().rollID then return end
local left = GetLootRollTimeLeft(this:GetParent():GetParent().rollID)
local min, max = this:GetMinMaxValues()
if left < min or left > max then left = min end
this:SetValue(left)
end)
-- drains by itself; UpdateLootRoll sets the roll's time span
f.time.duration = C_DurationUtil.CreateDuration()
f.time.bar:SetTimerDuration(f.time.duration, Enum.StatusBarInterpolation.Immediate, Enum.StatusBarTimerDirection.RemainingTime)
return f
end
@@ -299,7 +293,9 @@ pfUI:RegisterModule("roll", function ()
pfUI.roll.frames[id].name.text:SetTextColor(color.r, color.g, color.b, 1)
pfUI.roll.frames[id].icon.tex:SetTexture(texture)
pfUI.roll.frames[id].backdrop:SetBackdropBorderColor(color.r, color.g, color.b)
pfUI.roll.frames[id].time.bar:SetMinMaxValues(0, pfUI.roll.frames[id].rollTime)
-- both in milliseconds
local left = GetLootRollTimeLeft(pfUI.roll.frames[id].rollID)
pfUI.roll.frames[id].time.duration:SetTimeFromEnd(GetTime() + left / 1000, pfUI.roll.frames[id].rollTime / 1000)
if C.loot.raritytimer == "1" then
pfUI.roll.frames[id].time.bar:SetStatusBarColor(color.r, color.g, color.b, .5)