Add a loss of control alert

A movable alert in the middle of the screen for the strongest active
effect from C_LossOfControl: icon, cooldown sweep, the effect's name and
the seconds left. A school lockout reads "<School> Locked". It opens on
LOSS_OF_CONTROL_ADDED / _UPDATE and rescans every 0.1s while shown,
because a refreshed debuff or a second kick changes the timer without an
event.

The labels are Classic Era's LOSS_OF_CONTROL_DISPLAY_* GlobalStrings,
added to each locale file. The README lists the alert and the dressing
room controls.
This commit is contained in:
Brues
2026-10-30 23:16:20 -05:00
parent 70cc994a97
commit f56720dccc
10 changed files with 216 additions and 7 deletions
+94
View File
@@ -0,0 +1,94 @@
pfUI:RegisterModule("lossofcontrol", function ()
-- 5.x's loss of control alert. ClassicAPI's C_LossOfControl builds the
-- effects from the player's debuffs and from the school lockout of an
-- interrupt.
local strings = L["lossofcontrol"]
-- how much each effect takes away; the alert shows the strongest one
local PRIORITY = {
STUN = 3, FEAR = 3, CONFUSE = 3, CHARM = 3, POSSESS = 3, PACIFYSILENCE = 3,
SILENCE = 2, SCHOOL_INTERRUPT = 2, PACIFY = 2,
DISARM = 1, ROOT = 1,
}
-- the lockout packet does not name the interrupting spell
local INTERRUPT_ICON = "Interface\\Icons\\Ability_Kick"
local function IsStronger(data, best)
local priority, bestPriority = PRIORITY[data.locType], PRIORITY[best.locType]
if priority ~= bestPriority then return priority > bestPriority end
return (data.timeRemaining or math.huge) > (best.timeRemaining or math.huge)
end
local frame = CreateFrame("Frame", "pfLossOfControl", UIParent)
frame:SetSize(220, 48)
frame:SetPoint("CENTER", 0, 120)
frame:SetFrameStrata("HIGH")
frame:Hide()
frame.icon = CreateFrame("Frame", nil, frame)
frame.icon:SetSize(48, 48)
frame.icon:SetPoint("LEFT", 0, 0)
CreateBackdrop(frame.icon, nil, true)
frame.icon.tex = frame.icon:CreateTexture(nil, "ARTWORK")
HandleIcon(frame.icon, frame.icon.tex)
frame.cd = CreateFrame("Cooldown", nil, frame.icon, "CooldownFrameTemplate")
frame.cd:SetAllPoints(frame.icon.tex)
frame.cd.pfCooldownStyleAnimation = 1
-- the alert prints the time itself
frame.cd.noCooldownCount = true
frame.label = frame:CreateFontString(nil, "OVERLAY")
frame.label:SetFont(pfUI.font_default, 20, "OUTLINE")
frame.label:SetTextColor(NORMAL_FONT_COLOR:GetRGB())
frame.label:SetPoint("TOPLEFT", frame.icon, "TOPRIGHT", 8, -4)
frame.time = frame:CreateFontString(nil, "OVERLAY")
frame.time:SetFont(pfUI.font_default, 14, "OUTLINE")
frame.time:SetPoint("BOTTOMLEFT", frame.icon, "BOTTOMRIGHT", 8, 4)
frame.Refresh = function(self)
local best
for i = 1, C_LossOfControl.GetActiveLossOfControlDataCount() do
local data = C_LossOfControl.GetActiveLossOfControlData(i)
if not best or IsStronger(data, best) then best = data end
end
if not best then
if pfUI.unlock and pfUI.unlock:IsShown() then return end
self:Hide()
return
end
if best.locType == "SCHOOL_INTERRUPT" then
self.label:SetText(format(strings.LOSS_OF_CONTROL_DISPLAY_INTERRUPT_SCHOOL, C_Spell.GetSchoolString(best.lockoutSchool)))
self.icon.tex:SetTexture(INTERRUPT_ICON)
else
self.label:SetText(strings["LOSS_OF_CONTROL_DISPLAY_" .. best.locType])
self.icon.tex:SetTexture(best.iconTexture)
end
-- a refreshed effect or a second kick on the same school changes the
-- timer without an event, so this runs again while the alert shows
if self.start ~= best.startTime or self.duration ~= best.duration then
self.start, self.duration = best.startTime, best.duration
CooldownFrame_SetTimer(self.cd, best.startTime or 0, best.duration or 0, best.startTime and 1 or 0)
end
self.time:SetText(best.timeRemaining and format("%.1f %s", best.timeRemaining, strings.LOSS_OF_CONTROL_SECONDS) or "")
self:Show()
end
frame:SetScript("OnUpdate", function()
if ( this.tick or 0) > GetTime() then return else this.tick = GetTime() + .1 end
this:Refresh()
end)
pfUI.events:RegisterFrameEventAndCallback("LOSS_OF_CONTROL_ADDED", function() frame:Refresh() end)
pfUI.events:RegisterFrameEventAndCallback("LOSS_OF_CONTROL_UPDATE", function() frame:Refresh() end)
UpdateMovable(frame)
pfUI.lossofcontrol = frame
end)