Files
ShaguTweaks-ClassicAPI/mods/unitframes-energy-tick.lua

247 lines
10 KiB
Lua

local _G = ShaguTweaks.GetGlobalEnv()
local T = ShaguTweaks.T
local API = ShaguTweaks.API
local module = ShaguTweaks:register({
title = T["Unit Frame Energy & Mana Tick"],
description = T["Adds an energy & mana tick to the player frame and Personal Resource Display."],
expansions = { ["vanilla"] = true }, category = T["Unit Frames"], enabled = nil,
})
module.enable = function(self)
if self.tick then return end
local tick = CreateFrame("StatusBar", nil, PlayerFrameManaBar)
self.tick = tick
tick:SetAllPoints(PlayerFrameManaBar)
tick:SetStatusBarTexture("Interface\\TargetingFrame\\UI-StatusBar")
tick:SetStatusBarColor(1, 1, 1, 0)
tick:SetMinMaxValues(0, 1)
tick:Hide()
tick.spark = tick:CreateTexture(nil, "OVERLAY")
tick.spark:SetTexture("Interface\\CastingBar\\UI-CastingBar-Spark")
tick.spark:SetBlendMode("ADD")
tick.spark:SetPoint("CENTER", tick:GetStatusBarTexture(), "RIGHT", 0, 0)
tick.spark:Hide()
local native = API.durationtimers and type(tick.SetTimerDuration) == "function"
if native then tick.duration = _G.C_DurationUtil.CreateDuration() end
local function CancelWake()
if tick.wake then tick.wake:Cancel(); tick.wake = nil end
end
local function SizeSpark()
local height = PlayerFrameManaBar:GetHeight()
tick.spark:SetHeight(height + 10)
tick.spark:SetWidth(height + 4)
end
-- Observe the regen phase separately from the five-second-rule display.
-- Mid-period gains (potions/procs) do not move an established clock.
local function LockTick(now)
if tick.phase then
local error = math.mod(now - tick.phase, 2)
if error > 1 then error = error - 2 end
if math.abs(error) <= .25 then
if error > .08 then tick.phase = tick.phase + error - .08
elseif error < -.08 then tick.phase = tick.phase + error + .08 end
tick.rejected = nil
return
end
if not tick.rejected or math.abs(now - tick.rejected - 2) > .25 then
tick.rejected = now
return
end
end
tick.phase, tick.rejected = now, nil
end
local Sweep
local function Wrap() tick.wake = nil; Sweep() end
function Sweep()
CancelWake()
if not tick:IsShown() then return end
local now = GetTime()
local start, duration
if tick.mode == 0 and tick.ruleEnd and tick.ruleEnd > now then
-- Retain the five-second spark appearance for mana-costing casts.
start, duration = tick.ruleEnd - 5, 5
elseif tick.phase then
start = tick.phase + math.floor((now - tick.phase) / 2) * 2
duration = 2
end
if not start then tick.start = nil; tick.spark:Hide(); return end
tick.start, tick.period = start, duration
tick.spark:Show()
if native then
tick.duration:SetTimeFromStart(start, duration)
tick:SetTimerDuration(tick.duration, _G.Enum.StatusBarInterpolation.Immediate,
_G.Enum.StatusBarTimerDirection.ElapsedTime)
tick.wake = _G.C_Timer.NewTimer(math.max(.001, start + duration - now), Wrap)
end
end
local function Visible()
if tick.mode == 3 or (tick.mode == 0 and
(UnitMana("player") < UnitManaMax("player") or UnitAffectingCombat("player"))) then
tick:Show()
Sweep()
else tick:Hide() end
end
tick:SetScript("OnHide", CancelWake)
if not native then
tick:SetScript("OnUpdate", function()
if not tick.start then return end
local now = GetTime()
if now >= tick.start + tick.period then Sweep() end
if tick.start and tick.spark:IsShown() then
tick:SetValue(math.max(0, math.min(1, (now - tick.start) / tick.period)))
end
end)
end
ShaguTweaks.HookScript(PlayerFrameManaBar, "OnSizeChanged", SizeSpark)
ShaguTweaks.HookScript(tick, "OnShow", Sweep)
local function ArmRule(spellID)
if not spellID then return end
if API.GetSpellInfo then
local name, rank, icon, cost, funnel, powerType = API.GetSpellInfo(spellID)
if name then
if powerType == 0 and cost and cost > 0 then
tick.ruleSpell, tick.ruleEnd = spellID, GetTime() + 5
end
return
end
end
local spells = _G.C_Spell
if not spells or type(spells.GetSpellPowerCost) ~= "function" then return end
local costs = spells.GetSpellPowerCost(spellID)
local cost = costs and costs[1]
if cost and cost.type == 0 and cost.cost and cost.cost > 0 then
tick.ruleSpell, tick.ruleEnd = spellID, GetTime() + 5
end
end
tick:SetScript("OnEvent", function()
local world = event == "PLAYER_ENTERING_WORLD"
local display = event == "UNIT_DISPLAYPOWER"
-- ClassicAPI world-entry payloads are booleans, not unit tokens.
if not world and string.find(event, "^UNIT_") and arg1 ~= "player" then return end
local mode = UnitPowerType("player")
if world or display or mode ~= tick.mode then
tick.mode, tick.lastPower = mode, UnitMana("player")
tick.phase, tick.rejected, tick.start = nil, nil, nil
tick.spark:Hide()
end
if event == "UNIT_SPELLCAST_SUCCEEDED" then ArmRule(arg3)
elseif event == "UNIT_SPELLCAST_CHANNEL_STOP" then
if tick.ruleSpell == arg3 then tick.ruleEnd = GetTime() + 5 end
elseif (event == "UNIT_MANA" and mode == 0) or (event == "UNIT_ENERGY" and mode == 3) then
local power = UnitMana("player")
if tick.lastPower and power > tick.lastPower then LockTick(GetTime()) end
tick.lastPower = power
end
Visible()
if self.RefreshDruidManaTick then self.RefreshDruidManaTick() end
end)
tick:RegisterEvent("PLAYER_ENTERING_WORLD")
tick:RegisterEvent("PLAYER_REGEN_DISABLED")
tick:RegisterEvent("PLAYER_REGEN_ENABLED")
local events = { "UNIT_DISPLAYPOWER", "UNIT_MANA", "UNIT_ENERGY",
"UNIT_SPELLCAST_SUCCEEDED", "UNIT_SPELLCAST_CHANNEL_STOP" }
for _, name in ipairs(events) do API.RegisterUnitEvent(tick, name, "player") end
SizeSpark()
-- Mirror the existing sweep; do not create another clock or event listener.
ShaguTweaks.RegisterResourceTickBar = function(parent)
if self.resourceTick then return end
local copy = CreateFrame("StatusBar", nil, parent)
self.resourceTick = copy
copy:SetAllPoints(parent)
copy:SetStatusBarTexture("Interface\\TargetingFrame\\UI-StatusBar")
copy:SetStatusBarColor(1, 1, 1, 0)
copy:SetMinMaxValues(0, 1)
copy.spark = copy:CreateTexture(nil, "OVERLAY")
copy.spark:SetTexture("Interface\\CastingBar\\UI-CastingBar-Spark")
copy.spark:SetBlendMode("ADD")
copy.spark:SetPoint("CENTER", copy:GetStatusBarTexture(), "RIGHT", 0, 0)
local function Resize()
copy.spark:SetHeight(parent:GetHeight() + 10)
copy.spark:SetWidth(parent:GetHeight() + 4)
end
local function Visibility()
if tick:IsShown() then copy:Show() else copy:Hide() end
if tick.spark:IsShown() then copy.spark:Show() else copy.spark:Hide() end
end
ShaguTweaks.HookScript(parent, "OnSizeChanged", Resize)
ShaguTweaks.HookScript(tick, "OnShow", Visibility)
ShaguTweaks.HookScript(tick, "OnHide", Visibility)
ShaguTweaks.hooksecurefunc(tick.spark, "Show", Visibility)
ShaguTweaks.hooksecurefunc(tick.spark, "Hide", Visibility)
if native then
ShaguTweaks.hooksecurefunc(tick, "SetTimerDuration", function(bar, duration, interpolation, direction)
copy:SetTimerDuration(duration, interpolation, direction)
end)
if tick.start then copy:SetTimerDuration(tick.duration, _G.Enum.StatusBarInterpolation.Immediate,
_G.Enum.StatusBarTimerDirection.ElapsedTime) end
else
ShaguTweaks.hooksecurefunc(tick, "SetValue", function(bar, value) copy:SetValue(value) end)
copy:SetValue(tick:GetValue() or 0)
end
Resize(); Visibility()
end
local display = ShaguTweaks.PersonalResourceDisplay
if display then ShaguTweaks.RegisterResourceTickBar(display.power.bar) end
ShaguTweaks.RegisterDruidManaTickBar = function(parent, source)
if self.druidTick then return end
local bar = CreateFrame("StatusBar", nil, parent)
self.druidTick = bar
bar:SetAllPoints(parent)
bar:SetStatusBarTexture("Interface\\TargetingFrame\\UI-StatusBar")
bar:SetStatusBarColor(1, 1, 1, 0)
bar:SetMinMaxValues(0, 1)
local spark = bar:CreateTexture(nil, "OVERLAY")
spark:SetTexture("Interface\\CastingBar\\UI-CastingBar-Spark")
spark:SetBlendMode("ADD")
spark:SetPoint("CENTER", bar:GetStatusBarTexture(), "RIGHT", 0, 0)
local function Resize()
spark:SetHeight(parent:GetHeight() + 10); spark:SetWidth(parent:GetHeight() + 4)
end
ShaguTweaks.HookScript(parent, "OnSizeChanged", Resize)
Resize()
local duration = native and _G.C_DurationUtil.CreateDuration()
local phase, last, start, period, wake
local Refresh, Animate
local function Cancel()
if wake then wake:Cancel(); wake = nil end
end
local function Wrap() wake = nil; Refresh() end
Refresh = function()
Cancel()
local now = GetTime()
if not parent:IsVisible() then spark:Hide(); bar:SetScript("OnUpdate", nil); return end
if tick.ruleEnd and tick.ruleEnd > now then start, period = tick.ruleEnd - 5, 5
elseif phase then start, period = phase + math.floor((now-phase)/2)*2, 2
else spark:Hide(); bar:SetScript("OnUpdate", nil); return end
spark:Show()
if native then
duration:SetTimeFromStart(start, period)
bar:SetTimerDuration(duration, _G.Enum.StatusBarInterpolation.Immediate, _G.Enum.StatusBarTimerDirection.ElapsedTime)
wake = _G.C_Timer.NewTimer(math.max(.001, start+period-now), Wrap)
else bar:SetScript("OnUpdate", Animate)
end
end
local function Sample()
local value = source:GetValue()
if last and value > last then phase = GetTime() end
last = value
Refresh()
end
if not native then
Animate = function()
if spark:IsShown() and start then
if GetTime() >= start+period then Refresh() end
bar:SetValue(math.max(0, math.min(1, (GetTime()-start)/period)))
end
end
end
ShaguTweaks.hooksecurefunc(source, "SetValue", Sample)
ShaguTweaks.HookScript(parent, "OnShow", Refresh)
ShaguTweaks.HookScript(parent, "OnHide", function() Cancel(); phase, last = nil, nil; spark:Hide(); bar:SetScript("OnUpdate", nil) end)
self.RefreshDruidManaTick = Refresh
Sample()
end
if display and display.mana then
ShaguTweaks.RegisterDruidManaTickBar(display.mana.bar, PlayerFrameAlternatePowerBar)
end
end