Files
ShaguTweaks-ClassicAPI/mods/totem-timers.lua
T

295 lines
8.2 KiB
Lua

-- Totem Timers (Shaman)
--
-- One icon per active totem (Fire, Earth, Water, Air) with the time left.
-- Left-click targets the totem, hovering shows its tooltip. The data comes
-- from ClassicAPI's GetTotemInfo / GetTotemTimeLeft, and the display is
-- redrawn on PLAYER_TOTEM_UPDATE, with a light timer only while a totem is up.
local _G = ShaguTweaks.GetGlobalEnv()
local T = ShaguTweaks.T
local module = ShaguTweaks:register({
title = T["Totem Timers"],
description = T["Shamans: shows the totems you have out as icons with the time left. Left-click targets a totem. Hold <Ctrl>, <Shift> and <Alt> to move them; right-click to reset."],
expansions = { ["vanilla"] = true },
category = T["Interface"],
enabled = false,
})
local SIZE = 34
local GAP = 4
local THROTTLE = .1
local POSITION_KEY = "TotemTimersPosition"
-- slot 1 Fire, 2 Earth, 3 Water, 4 Air
local SLOT_COLORS = {
{ 1, .45, .1 }, -- fire
{ .7, .55, .3 }, -- earth
{ .3, .6, 1 }, -- water
{ .8, .85, 1 }, -- air
}
local PREVIEW_ICONS = {
"Interface\\Icons\\Spell_Fire_SearingTotem",
"Interface\\Icons\\Spell_Nature_StrengthOfEarthTotem02",
"Interface\\Icons\\Spell_Nature_ManaRegenTotem",
"Interface\\Icons\\Spell_Nature_InvisibilityTotem",
}
-- One table for state: Lua 5.0 allows only 32 upvalues per function.
local S = { unlocked = false, elapsed = 0, buttons = {} }
local function Supported()
return type(_G.GetTotemInfo) == "function" and type(_G.GetTotemTimeLeft) == "function"
end
local function FormatTime(seconds)
if seconds >= 60 then
return string.format("%d:%02d", math.floor(seconds / 60), math.floor(seconds - math.floor(seconds / 60) * 60))
end
return string.format("%d", seconds)
end
local function Layout()
local shown = 0
for slot = 1, 4 do
local b = S.buttons[slot]
if b:IsShown() then
b:ClearAllPoints()
b:SetPoint("LEFT", S.frame, "LEFT", (slot - 1) * (SIZE + GAP), 0)
shown = shown + 1
end
end
return shown
end
-- Redraw the time and bar of the visible buttons; returns how many are up.
local function UpdateTimes()
local active = 0
for slot = 1, 4 do
local b = S.buttons[slot]
if b:IsShown() and not S.unlocked then
local left = _G.GetTotemTimeLeft(slot) or 0
if left > 0 then
active = active + 1
b.time:SetText(FormatTime(left))
if left <= 5 then
b.time:SetTextColor(1, .2, .2)
elseif left <= 10 then
b.time:SetTextColor(1, .9, .2)
else
b.time:SetTextColor(1, 1, 1)
end
if b.duration and b.duration > 0 then
b.bar:SetWidth(math.max(1, SIZE * math.min(left / b.duration, 1)))
end
else
-- duration ran out, the creature is about to go
b.time:SetText("")
b.bar:SetWidth(1)
end
end
end
return active
end
-- Re-read the four slots. Active totem = start time above 0; "have a totem"
-- is also true when you only carry the totem tool.
local function Refresh()
if S.unlocked then return end
local any = false
for slot = 1, 4 do
local b = S.buttons[slot]
local have, name, start, duration, icon = _G.GetTotemInfo(slot)
if start and start > 0 then
any = true
b.duration = duration
if icon and icon ~= "" then b.icon:SetTexture(icon) end
b:Show()
else
b.duration = nil
b:Hide()
end
end
if any then
Layout()
UpdateTimes()
if not S.frame:IsShown() then S.frame:Show() end
else
S.frame:Hide()
end
end
local function OnUpdate()
S.elapsed = S.elapsed + (arg1 or 0)
if S.elapsed < THROTTLE then return end
S.elapsed = 0
UpdateTimes()
end
local function RestorePosition()
local pos = ShaguTweaks_config and ShaguTweaks_config[POSITION_KEY]
S.frame:ClearAllPoints()
if pos and pos[1] and pos[2] then
S.frame:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", pos[1], pos[2])
else
S.frame:SetPoint("BOTTOM", UIParent, "BOTTOM", 0, 200)
end
end
local function ModifiersDown()
local API = ShaguTweaks.API or {}
local shift = API.IsShiftKeyDown or IsShiftKeyDown
local ctrl = API.IsControlKeyDown or IsControlKeyDown
local alt = API.IsAltKeyDown or IsAltKeyDown
return shift() and ctrl() and alt() and true or false
end
-- Hold Ctrl+Shift+Alt to show four preview icons and drag them.
local function SetUnlocked(on)
if S.unlocked == on then return end
S.unlocked = on
S.frame:EnableMouse(on)
if on then
for slot = 1, 4 do
local b = S.buttons[slot]
b:EnableMouse(false) -- let the drag reach the container
b.icon:SetTexture(PREVIEW_ICONS[slot])
b.time:SetText(FormatTime(60 - slot * 10))
b.time:SetTextColor(1, 1, 1)
b.bar:SetWidth(SIZE)
b:Show()
end
Layout()
S.frame:Show()
else
S.frame:StopMovingOrSizing()
for slot = 1, 4 do S.buttons[slot]:EnableMouse(true); S.buttons[slot]:Hide() end
S.frame:Hide()
Refresh()
end
end
local function CreateButton(slot)
local b = CreateFrame("Button", "ShaguTweaksTotem" .. slot, S.frame)
b:SetWidth(SIZE)
b:SetHeight(SIZE)
b:Hide()
b.border = b:CreateTexture(nil, "BACKGROUND")
b.border:SetTexture("Interface\\Buttons\\WHITE8X8")
b.border:SetPoint("TOPLEFT", b, "TOPLEFT", -1, 1)
b.border:SetPoint("BOTTOMRIGHT", b, "BOTTOMRIGHT", 1, -1)
b.border:SetVertexColor(SLOT_COLORS[slot][1], SLOT_COLORS[slot][2], SLOT_COLORS[slot][3])
b.icon = b:CreateTexture(nil, "ARTWORK")
b.icon:SetAllPoints(b)
b.icon:SetTexCoord(.08, .92, .08, .92)
b.time = b:CreateFontString(nil, "OVERLAY")
b.time:SetFont("Fonts\\FRIZQT__.TTF", 14, "OUTLINE")
b.time:SetPoint("CENTER", b, "CENTER", 0, 0)
b.time:SetTextColor(1, 1, 1)
-- remaining time as a thin bar under the icon
b.bar = b:CreateTexture(nil, "OVERLAY")
b.bar:SetTexture("Interface\\Buttons\\WHITE8X8")
b.bar:SetHeight(3)
b.bar:SetPoint("TOPLEFT", b, "BOTTOMLEFT", 0, -2)
b.bar:SetVertexColor(SLOT_COLORS[slot][1], SLOT_COLORS[slot][2], SLOT_COLORS[slot][3])
b:RegisterForClicks("LeftButtonUp")
b:SetScript("OnClick", function()
if S.unlocked then return end
if type(_G.TargetTotem) == "function" then _G.TargetTotem(slot) end
end)
b:SetScript("OnEnter", function()
if S.unlocked then return end
if GameTooltip.SetTotem then
-- shows itself, no :Show() needed
GameTooltip:SetOwner(this, "ANCHOR_TOP")
GameTooltip:SetTotem(slot)
end
end)
b:SetScript("OnLeave", function() GameTooltip:Hide() end)
S.buttons[slot] = b
end
local function CreateTimers()
if S.frame then return end
local f = CreateFrame("Frame", "ShaguTweaksTotemTimers", UIParent)
f:SetWidth(4 * SIZE + 3 * GAP)
f:SetHeight(SIZE)
f:SetFrameStrata("MEDIUM")
f:SetMovable(true)
f:SetClampedToScreen(true)
f:EnableMouse(false)
f:Hide()
S.frame = f
for slot = 1, 4 do CreateButton(slot) end
f:SetScript("OnMouseDown", function()
if arg1 == "LeftButton" then this:StartMoving() end
end)
f:SetScript("OnMouseUp", function()
this:StopMovingOrSizing()
ShaguTweaks_config = ShaguTweaks_config or {}
if arg1 == "RightButton" then
ShaguTweaks_config[POSITION_KEY] = nil
else
ShaguTweaks_config[POSITION_KEY] = { this:GetLeft(), this:GetBottom() }
end
RestorePosition()
end)
-- Runs only while a totem is out (a hidden frame gets no OnUpdate).
f:SetScript("OnUpdate", OnUpdate)
S.events = CreateFrame("Frame")
S.events:SetScript("OnEvent", function()
if event == "MODIFIER_STATE_CHANGED" then
SetUnlocked(ModifiersDown())
else
Refresh()
end
end)
RestorePosition()
end
module.enable = function(self)
local _, class = UnitClass("player")
if class ~= "SHAMAN" or not Supported() then return end
CreateTimers()
S.events:RegisterEvent("PLAYER_TOTEM_UPDATE")
S.events:RegisterEvent("PLAYER_ENTERING_WORLD")
S.events:RegisterEvent("MODIFIER_STATE_CHANGED")
SLASH_SHAGUTOTEMTIMERS1 = "/totemtimers"
SlashCmdList["SHAGUTOTEMTIMERS"] = function(msg)
msg = string.lower(msg or "")
if msg == "reset" then
ShaguTweaks_config = ShaguTweaks_config or {}
ShaguTweaks_config[POSITION_KEY] = nil
RestorePosition()
end
end
Refresh()
end
module.disable = function(self)
if not S.frame then return end
if S.unlocked then SetUnlocked(false) end
S.events:UnregisterAllEvents()
S.frame:Hide()
end