Energy tick: the spark rides a timer-driven fill, drop the OnUpdate

The tick frame is a status bar whose invisible fill sweeps each period
from a duration object, with the spark anchored to the fill's edge
(ClassicAPI v1.19.1). A timer at each period's end rolls the sweep over
by whole periods, paused while hidden and caught up as it shows. The
five-second rule ends on its own timer, and the spark's alpha and the
rule bar update on the events that change them. Full mana now hides the
frame on every event, not just on a power update.
This commit is contained in:
Brues
2026-11-03 01:07:02 -06:00
parent dd4ebda0a9
commit 465e87d7d2
+63 -67
View File
@@ -84,11 +84,6 @@ pfUI:RegisterModule("energytick", function()
return
end
-- inside the module body on purpose: C is on pfUI.env, not _G
local function getBarWidth()
return C.unitframes.player.pwidth ~= "-1" and C.unitframes.player.pwidth or C.unitframes.player.width
end
local rawborder, default_border = GetBorderSize("unitframes")
local cbtexture = pfUI.media[C.appearance.castbar.texture]
@@ -128,8 +123,13 @@ pfUI:RegisterModule("energytick", function()
return true
end
local energytick = CreateFrame("Frame", nil, pfUI.uf.player.power.bar)
-- the bar sweeps its fill over one tick period from a duration object; the
-- fill is invisible and carries the spark on its edge
local energytick = CreateFrame("StatusBar", nil, pfUI.uf.player.power.bar)
energytick:SetAllPoints(pfUI.uf.player.power.bar)
energytick:SetColorFill(1, 1, 1, 0)
energytick.sweep = C_DurationUtil.CreateDuration()
energytick:SetTimerDuration(energytick.sweep, Enum.StatusBarInterpolation.Immediate, Enum.StatusBarTimerDirection.ElapsedTime)
energytick:RegisterEvent("PLAYER_ENTERING_WORLD")
energytick:RegisterUnitEvent("UNIT_DISPLAYPOWER", "player")
energytick:RegisterUnitEvent("UNIT_POWER_UPDATE", "player")
@@ -138,21 +138,61 @@ pfUI:RegisterModule("energytick", function()
energytick:RegisterEvent("SPELLS_CHANGED")
energytick:RegisterEvent("PLAYER_AURAS_CHANGED")
-- the spark hides until a tick is seen, and dims while the rule is up and
-- nothing has ticked inside it yet
local function UpdateRule()
local fsrUp = energytick.mode == "MANA" and energytick.fsr:IsActive()
energytick.fsrbar:SetShown(fsrUp and energytick:IsShown() and C.unitframes.player.showfsr == "1")
energytick.spark:SetAlpha(not energytick.start and 0 or (fsrUp and not energytick.fsrGain) and .4 or 1)
end
-- the window closed without being re-armed
local function RuleEnd()
energytick.fsrSpell, energytick.fsrGain = nil, nil
energytick.fsr:Reset()
UpdateRule()
end
local function ArmRule()
energytick.fsr:SetTimeFromStart(GetTime(), FIVE_SECOND_RULE)
if energytick.ruletimer then energytick.ruletimer:Cancel() end
energytick.ruletimer = C_Timer.NewTimer(FIVE_SECOND_RULE, RuleEnd)
end
-- sweeps the period that holds now and wakes at its end. Rolls over by
-- whole periods, not from now: restarting bakes timer overshoot into the
-- phase as drift the lock then has to chase. Runs only while shown; a
-- hidden bar keeps its phase and catches up as it shows.
local Sweep
local function Wrap() Sweep() end
function Sweep()
if energytick.wrap then energytick.wrap:Cancel() end
if not energytick.start or not energytick:IsShown() then return end
local now = GetTime()
while now - energytick.start >= energytick.max do
energytick.start = energytick.start + energytick.max * math.floor((now - energytick.start) / energytick.max)
energytick.max = getAdjustedTickTimer()
end
energytick.sweep:SetTimeFromStart(energytick.start, energytick.max)
energytick.wrap = C_Timer.NewTimer(energytick.start + energytick.max - now, Wrap)
end
energytick:SetScript("OnEvent", function()
if UnitPowerType("player") == Enum.PowerType.Mana and C.unitframes.player.manatick == "1" then
this.mode = "MANA"
this:Show()
elseif UnitPowerType("player") == Enum.PowerType.Energy and C.unitframes.player.energy == "1" then
this.mode = "ENERGY"
this:Show()
else
this.mode = nil
this:Hide()
end
-- mana regen stops showing at full mana
this:SetShown(this.mode and not (this.mode == "MANA" and UnitPower("player") >= UnitPowerMax("player")))
if event == "SPELLS_CHANGED" or event == "PLAYER_AURAS_CHANGED" then
energyRegenTimeMod = nil
return
end
-- a shift swaps the power the gains are measured on
@@ -167,18 +207,16 @@ pfUI:RegisterModule("energytick", function()
cost = cost and cost[1]
if cost and cost.type == Enum.PowerType.Mana and cost.cost > 0 then
this.fsrSpell, this.fsrGain = arg3, nil
this.fsr:SetTimeFromStart(GetTime(), FIVE_SECOND_RULE)
ArmRule()
end
return
end
-- 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.fsrGain = nil
this.fsr:SetTimeFromStart(GetTime(), FIVE_SECOND_RULE)
ArmRule()
end
return
end
-- only the shown power: a cat druid's mana regen is not an energy gain
@@ -193,61 +231,11 @@ pfUI:RegisterModule("energytick", function()
if this.fsr:IsActive() then
this.fsrGain = true
end
end
-- phase is kept while hidden; OnUpdate catches up by whole periods
if this.mode == "MANA" and power >= UnitPowerMax("player") then
this:Hide()
Sweep()
end
end
end)
energytick:SetScript("OnUpdate", function()
-- Throttle for performance
if (this.tick or 0) > GetTime() then
return
end
this.tick = GetTime() + 0.020 -- ~50 FPS
-- 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)
return
end
if this.mode == "MANA" and UnitPower("player") >= UnitPowerMax("player") then
this.spark:SetAlpha(0)
return
end
this.current = GetTime() - this.start
-- roll over by whole periods, not from now: restarting bakes frame
-- overshoot into the phase as drift the lock then has to chase
if this.current > this.max then
this.start = this.start + this.max * math.floor(this.current / this.max)
this.max = getAdjustedTickTimer()
this.current = GetTime() - this.start
end
-- dim while the rule is up and nothing has ticked inside it yet
this.spark:SetAlpha((fsrUp and not this.fsrGain) and .4 or 1)
if not C.unitframes.player.pheight then
return
end
local pos = getBarWidth() * (this.current / this.max)
this.spark:SetPoint("LEFT", pos - ((C.unitframes.player.pheight + 5) / 2), 0)
UpdateRule()
end)
energytick.fsr = C_DurationUtil.CreateDuration()
@@ -275,8 +263,14 @@ pfUI:RegisterModule("energytick", function()
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
-- the bar isn't parented to energytick, so it follows it by hand
energytick:SetScript("OnShow", function()
Sweep()
UpdateRule()
end)
energytick:SetScript("OnHide", function()
Sweep()
this.fsrbar:Hide()
end)
@@ -285,6 +279,8 @@ pfUI:RegisterModule("energytick", function()
energytick.spark:SetHeight(C.unitframes.player.pheight + 15)
energytick.spark:SetWidth(C.unitframes.player.pheight + 5)
energytick.spark:SetBlendMode("ADD")
energytick.spark:SetPoint("CENTER", energytick:GetStatusBarTexture(), "RIGHT")
energytick.spark:SetAlpha(0)
local hookUpdateConfig = pfUI.uf.player.UpdateConfig
function pfUI.uf.player.UpdateConfig()