Files
coa-shadowedunitframes/ShadowedUnitFrames/modules/totems.lua
T
florian.berthold c0a23544f1 feat(totems): right-click dismiss, Witchdoctor 3-slot cap, seconds counter
- Right-click on a populated totem slot calls DestroyTotem(slot).
  EnableMouse on the strip absorbs clicks (loses click-to-target on
  the totem-bar pixels only; health/power bar still targets normally).
- COA_CLASS_TOTEMS table overrides MAX_TOTEMS per class. WITCHDOCTOR
  capped to 3 so the 4th unused slot no longer renders. Easy to extend
  for any future custom totem classes.
- Integer-seconds countdown text centred on each slot. totemMonitor
  only repaints the FontString when the integer changes so OnUpdate
  stays cheap. Cleared on expiry / dismiss.
2026-05-25 11:51:11 +02:00

163 lines
5.3 KiB
Lua

local Totems = {}
local totemColors = {}
local MAX_TOTEMS = MAX_TOTEMS
-- CoA: per-class slot count override (different from shaman's 4).
local COA_CLASS_TOTEMS = {
WITCHDOCTOR = 3,
}
-- Death Knights untalented ghouls are guardians and are considered totems........... so set it up for them
local playerClass = select(2, UnitClass("player"))
if( playerClass == "DEATHKNIGHT" ) then
MAX_TOTEMS = 1
ShadowUF:RegisterModule(Totems, "totemBar", ShadowUF.L["Guardian bar"], true, "DEATHKNIGHT")
else
-- CoA: SHAMAN is the vanilla totem class, but CoA custom classes (Witchdoctor, …) also use the
-- multi-cast totem bar. Detect via MAX_TOTEMS > 1 (set by FrameXML for totem classes) or the
-- HasMultiCastActionBar API (Bartender uses the same probe). Register with class=nil so the
-- module is available to any CoA totem class, not just SHAMAN.
local hasTotems = (MAX_TOTEMS and MAX_TOTEMS > 1)
or (type(HasMultiCastActionBar) == "function" and HasMultiCastActionBar())
or playerClass == "SHAMAN"
if COA_CLASS_TOTEMS[playerClass] then
MAX_TOTEMS = COA_CLASS_TOTEMS[playerClass]
end
if( hasTotems ) then
ShadowUF:RegisterModule(Totems, "totemBar", ShadowUF.L["Totem bar"], true)
else
ShadowUF:RegisterModule(Totems, "totemBar", ShadowUF.L["Totem bar"], true, "SHAMAN")
end
end
function Totems:OnEnable(frame)
if( not frame.totemBar ) then
frame.totemBar = CreateFrame("Frame", nil, frame)
frame.totemBar.totems = {}
for id=1, MAX_TOTEMS do
local totem = ShadowUF.Units:CreateBar(frame)
totem:SetFrameLevel(1)
totem:SetMinMaxValues(0, 1)
totem:SetValue(0)
totem.id = MAX_TOTEMS == 1 and 1 or TOTEM_PRIORITIES[id]
-- Right-click dismisses the totem in this slot. EnableMouse on the strip
-- absorbs clicks here (loses click-to-target on the totem bar only).
totem:EnableMouse(true)
totem:SetScript("OnMouseUp", function(self, button)
if button == "RightButton" and self.have then
DestroyTotem(self.id)
end
end)
-- Seconds-remaining text, centred on the bar; updated by totemMonitor.
totem.timeText = totem:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
totem.timeText:SetPoint("CENTER", totem, "CENTER", 0, 0)
totem.timeText:SetText("")
if( id > 1 ) then
totem:SetPoint("TOPLEFT", frame.totemBar.totems[id - 1], "TOPRIGHT", 1, 0)
else
totem:SetPoint("TOPLEFT", frame.totemBar, "TOPLEFT", 0, 0)
end
table.insert(frame.totemBar.totems, totem)
end
if( MAX_TOTEMS == 1 ) then
totemColors[1] = ShadowUF.db.profile.classColors.PET
else
totemColors[1] = {r = 1, g = 0, b = 0.4}
totemColors[2] = {r = 0, g = 1, b = 0.4}
totemColors[3] = {r = 0, g = 0.4, b = 1}
totemColors[4] = {r = 0.90, g = 0.90, b = 0.90}
end
end
frame:RegisterNormalEvent("PLAYER_TOTEM_UPDATE", self, "Update")
frame:RegisterUpdateFunc(self, "Update")
end
function Totems:OnDisable(frame)
frame:UnregisterAll(self)
end
function Totems:OnLayoutApplied(frame)
if( frame.visibility.totemBar ) then
local barWidth = (frame.totemBar:GetWidth() - (MAX_TOTEMS - 1)) / MAX_TOTEMS
for _, totem in pairs(frame.totemBar.totems) do
if( ShadowUF.db.profile.units[frame.unitType].totemBar.background ) then
local color = ShadowUF.db.profile.bars.backgroundColor or ShadowUF.db.profile.units[frame.unitType].totemBar.backgroundColor or totemColors[totem.id]
totem.background:SetTexture(ShadowUF.Layout.mediaPath.statusbar)
totem.background:SetVertexColor(color.r, color.g, color.b, ShadowUF.db.profile.bars.backgroundAlpha)
totem.background:Show()
else
totem.background:Hide()
end
totem:SetHeight(frame.totemBar:GetHeight())
totem:SetWidth(barWidth)
totem:SetStatusBarTexture(ShadowUF.Layout.mediaPath.statusbar)
totem:SetStatusBarColor(totemColors[totem.id].r, totemColors[totem.id].g, totemColors[totem.id].b, ShadowUF.db.profile.bars.alpha)
totem:GetStatusBarTexture():SetHorizTile(false)
end
end
end
local function totemMonitor(self, elapsed)
local time = GetTime()
local remaining = self.endTime - time
self:SetValue(remaining)
if self.timeText then
local intSec = remaining > 0 and math.floor(remaining) or 0
if intSec ~= self.lastIntSec then
self.lastIntSec = intSec
self.timeText:SetText(intSec > 0 and intSec or "")
end
end
if( time >= self.endTime ) then
self:SetValue(0)
if self.timeText then
self.timeText:SetText("")
self.lastIntSec = nil
end
self:SetScript("OnUpdate", nil)
end
end
function Totems:Update(frame)
local totalActive = 0
for _, indicator in pairs(frame.totemBar.totems) do
local have, name, start, duration = GetTotemInfo(indicator.id)
if( have and start > 0 ) then
indicator.have = true
indicator.endTime = start + duration
indicator:SetMinMaxValues(0, duration)
indicator:SetValue(indicator.endTime - GetTime())
indicator:SetScript("OnUpdate", totemMonitor)
indicator:SetAlpha(1.0)
totalActive = totalActive + 1
elseif( indicator.have ) then
indicator.have = nil
indicator:SetScript("OnUpdate", nil)
indicator:SetMinMaxValues(0, 1)
indicator:SetValue(0)
if indicator.timeText then
indicator.timeText:SetText("")
indicator.lastIntSec = nil
end
end
end
-- Only guardian timers should auto hide, nothing else
if( MAX_TOTEMS == 1 ) then
ShadowUF.Layout:SetBarVisibility(frame, "totemBar", totalActive > 0)
end
end