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

144 lines
5.6 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."],
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()
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()
end