forked from Dusk/ShaguTweaks-ClassicAPI
144 lines
5.6 KiB
Lua
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
|