mirror of
https://github.com/brues-code/pfUI.git
synced 2026-11-01 08:26:57 +00:00
Drive auras from UNIT_AURA_DELTA
UNIT_AURA says nothing about what changed and stays silent when a buff is refreshed or its duration packet lands, so a recast HoT kept its old timer until something else rebuilt the row. UNIT_AURA_DELTA reports added, updated and removed aura instances, refreshes included. Unit frames register it on their own token. An add, a removal or a full update rebuilds the auras as before; an update-only delta re-reads the slot behind each shown icon for its stacks and timer and redraws the buff indicators, without a relayout or a dispel scan. Frames that follow a GUID (the target-of-X chain) stay on UNIT_AURA, since the delta only fires for tokens. A finished aura layout announces itself as "unitframe:auras". buffwatch drops its 0.4s rescan: bars rebuild on the delta, on login and on a target change, and a timer wakes the frame when the next aura crosses the bar threshold. Bars re-anchor when the player or target frame lays out its aura rows, and a whitelist or blacklist click applies at once. Nameplates, the loss of control alert and the equipment manager's mount/stealth/dining check listen to the delta as well.
This commit is contained in:
+54
-2
@@ -98,6 +98,25 @@ local function StartAuraTimers(icons)
|
||||
end
|
||||
end
|
||||
|
||||
-- Re-reads the stacks and timer of each shown icon of an aura block from the
|
||||
-- slot RefreshUnit filled it from, then restarts the timers. For auras that
|
||||
-- refreshed or restacked in place, which leaves every aura in its slot.
|
||||
local function RetimeAuras(icons, unitstr)
|
||||
if not icons then return end
|
||||
|
||||
local i = 1
|
||||
while icons[i] and icons[i]:IsShown() do
|
||||
local _, _, count, _, duration, expirationTime = C_UnitAuras.UnitAuraBySlot(unitstr, icons[i].slot)
|
||||
if count then
|
||||
icons[i].stacks:SetText(count > 1 and count or "")
|
||||
icons[i].auraStart, icons[i].auraDuration = AuraTimer(duration, expirationTime)
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
StartAuraTimers(icons)
|
||||
end
|
||||
|
||||
-- ============================================================================
|
||||
-- GUID-based Roster Tracking for Smart Updates
|
||||
-- Only updates frames where the unit actually changed, not ALL 40 frames
|
||||
@@ -1110,6 +1129,14 @@ function pfUI.uf.OnEvent()
|
||||
this.update_portrait = true
|
||||
elseif event == "UNIT_AURA" then
|
||||
this.update_aura = true
|
||||
elseif event == "UNIT_AURA_DELTA" then
|
||||
-- an aura that only refreshed or restacked leaves every aura in its
|
||||
-- slot, so the icons need their timers and stacks, not a rebuild
|
||||
if arg2.isFullUpdate or arg2.addedAuraInstanceIDs or arg2.removedAuraInstanceIDs then
|
||||
this.update_aura = true
|
||||
else
|
||||
this.update_auratime = true
|
||||
end
|
||||
elseif event == "UNIT_FACTION" then
|
||||
this.update_pvp = true
|
||||
elseif event == "UNIT_COMBAT" then
|
||||
@@ -1298,14 +1325,19 @@ function pfUI.uf.OnUpdate()
|
||||
this.update_full = nil
|
||||
this.update_base = nil
|
||||
this.update_aura = nil
|
||||
this.update_auratime = nil
|
||||
this.update_portrait = nil
|
||||
this.update_pvp = nil
|
||||
else
|
||||
-- process individual events
|
||||
-- process individual events; a rebuild re-times the icons as well
|
||||
if this.update_aura then
|
||||
pfUI.uf:RefreshUnit(this, "aura")
|
||||
this.update_aura = nil
|
||||
this.update_auratime = nil
|
||||
this.update_base = true
|
||||
elseif this.update_auratime then
|
||||
pfUI.uf:RefreshUnit(this, "auratime")
|
||||
this.update_auratime = nil
|
||||
end
|
||||
|
||||
if this.update_portrait then
|
||||
@@ -1350,7 +1382,6 @@ local UNIT_EVENTS = {
|
||||
"UNIT_FOCUS",
|
||||
"UNIT_PORTRAIT_UPDATE", "UNIT_MODEL_CHANGED",
|
||||
"UNIT_FACTION",
|
||||
"UNIT_AURA", -- frame=buff, frame=debuff
|
||||
"INCOMING_RESURRECT_CHANGED", -- frame=ressIcon
|
||||
}
|
||||
|
||||
@@ -1370,6 +1401,18 @@ function pfUI.uf:RegisterUnitEvents(unit)
|
||||
end
|
||||
end
|
||||
|
||||
-- auras (frame=buff, frame=debuff, indicators): UNIT_AURA_DELTA, which also
|
||||
-- reports refreshes and new durations and says what changed. It fires for
|
||||
-- tokens only, so a chained unit, followed by GUID, keeps UNIT_AURA.
|
||||
local aura, other = "UNIT_AURA_DELTA", "UNIT_AURA"
|
||||
if self.chain then aura, other = other, aura end
|
||||
self:UnregisterEvent(other)
|
||||
if unit then
|
||||
self:RegisterUnitEvent(aura, unit)
|
||||
else
|
||||
self:UnregisterEvent(aura)
|
||||
end
|
||||
|
||||
-- UNIT_COMBAT rides along only while the frame draws combat feedback text.
|
||||
-- UpdateConfig clears eventunit when it toggles that, so the next
|
||||
-- UpdateVisibility re-runs this.
|
||||
@@ -1830,6 +1873,7 @@ function pfUI.uf:RefreshUnit(unit, component)
|
||||
local name, icon, count, _, duration, expirationTime, _, _, _, spellId = C_UnitAuras.UnitAuraBySlot(unitstr, auraSlots[i])
|
||||
|
||||
if name then
|
||||
unit.buffs[i].slot = auraSlots[i]
|
||||
unit.buffs[i].texture:SetTexture(icon)
|
||||
unit.buffs[i]:Show()
|
||||
|
||||
@@ -1880,6 +1924,7 @@ function pfUI.uf:RefreshUnit(unit, component)
|
||||
unit.debuffs[i].backdrop:SetBackdropBorderColor(dispelColor:GetRGBA())
|
||||
|
||||
if texture then
|
||||
unit.debuffs[i].slot = auraSlots[i]
|
||||
unit.debuffs[i]:Show()
|
||||
|
||||
unit.debuffs[i].auraStart, unit.debuffs[i].auraDuration = AuraTimer(duration, expirationTime)
|
||||
@@ -1905,6 +1950,10 @@ function pfUI.uf:RefreshUnit(unit, component)
|
||||
LayoutAuras(unit, unitstr)
|
||||
StartAuraTimers(unit.buffs)
|
||||
StartAuraTimers(unit.debuffs)
|
||||
pfUI.events:TriggerEvent("unitframe:auras", unit)
|
||||
elseif component == "auratime" then
|
||||
RetimeAuras(unit.buffs, unitstr)
|
||||
RetimeAuras(unit.debuffs, unitstr)
|
||||
end
|
||||
|
||||
-- indicators
|
||||
@@ -2026,7 +2075,10 @@ function pfUI.uf:RefreshUnit(unit, component)
|
||||
elseif unit.hp.bar.debuffindicators then
|
||||
unit.hp.bar.debuffindicators:Hide()
|
||||
end
|
||||
end
|
||||
|
||||
-- buff indicators carry timers and stacks, so a re-time redraws them too
|
||||
if component == "all" or component == "aura" or component == "auratime" then
|
||||
if not unit.indicators and unit.config.buff_indicator == "1" then
|
||||
unit.indicators = pfUI.uf:SetupBuffIndicators(unit.config)
|
||||
elseif not unit.indicators then
|
||||
|
||||
+38
-9
@@ -103,6 +103,7 @@ pfUI:RegisterModule("buffwatch", function ()
|
||||
fcache[tostring(config)] = nil -- invalidate so the new entry takes effect immediately
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cff33ffcc" .. skill .. "|r" .. T["is now blacklisted."])
|
||||
end
|
||||
this.parent.Refresh()
|
||||
elseif this.parent.unit == "player" then
|
||||
local aura = C_UnitAuras.GetAuraDataByIndex("player", this.id, this.type)
|
||||
if aura and aura.spellId then C_Spell.CancelSpellByID(aura.spellId) end
|
||||
@@ -246,7 +247,7 @@ pfUI:RegisterModule("buffwatch", function ()
|
||||
table.sort(frame.buffs, frame.buffcmp)
|
||||
|
||||
-- create a buff bar for each below threshold
|
||||
local bar = 1
|
||||
local bar, crossing = 1, nil
|
||||
for id, data in pairs(frame.buffs) do
|
||||
if data[1] and ((data[1] ~= 0 and data[1] < frame.threshold) or frame.threshold == -1) -- timeleft checks
|
||||
and data[3] and data[3] ~= "" -- buff has a name
|
||||
@@ -340,6 +341,10 @@ pfUI:RegisterModule("buffwatch", function ()
|
||||
|
||||
frame.bars[bar]:Show()
|
||||
bar = bar + 1
|
||||
elseif data[1] and data[1] > 0 and data[3] and data[4] and frame.threshold ~= -1 then
|
||||
-- still above the threshold: its bar shows once it drops under
|
||||
local wait = data[1] - frame.threshold
|
||||
if not crossing or wait < crossing then crossing = wait end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -347,13 +352,11 @@ pfUI:RegisterModule("buffwatch", function ()
|
||||
for i = bar, table.getn(frame.bars) do
|
||||
frame.bars[i]:Hide()
|
||||
end
|
||||
end
|
||||
|
||||
-- Create a new Buff Bar
|
||||
local function BuffBarFrameOnUpdate()
|
||||
if ( this.tick or 1) > GetTime() then return else this.tick = GetTime() + .4 end
|
||||
RefreshBuffBarFrame(this)
|
||||
this:RefreshPosition()
|
||||
-- no event marks an aura's time left dropping under the threshold, so
|
||||
-- wake when the next one does
|
||||
if frame.crossing then frame.crossing:Cancel() end
|
||||
frame.crossing = crossing and C_Timer.NewTimer(crossing + .1, frame.Refresh) or nil
|
||||
end
|
||||
|
||||
local function RefreshPosition(self)
|
||||
@@ -373,6 +376,19 @@ pfUI:RegisterModule("buffwatch", function ()
|
||||
end
|
||||
end
|
||||
|
||||
-- every watch frame: one stacks on the bars of another, so all of them
|
||||
-- re-anchor together
|
||||
local watched = {}
|
||||
local function RefreshPositions()
|
||||
for _, frame in pairs(watched) do frame:RefreshPosition() end
|
||||
end
|
||||
|
||||
-- the bars sit above their unit frame's aura rows, which move when the
|
||||
-- rows lay out again
|
||||
pfUI.events:RegisterCallback("unitframe:auras", function(_, unit)
|
||||
if unit == pfUI.uf.player or unit == pfUI.uf.target then RefreshPositions() end
|
||||
end, "buffwatch")
|
||||
|
||||
local function CreateBuffBarFrame(unit, type)
|
||||
local framename = "pf" .. unit .. ( type == "HARMFUL" and "Debuff" or "Buff" ) .. "Bar"
|
||||
local frame = _G[framename] or CreateFrame("Frame", framename, UIParent)
|
||||
@@ -388,9 +404,22 @@ pfUI:RegisterModule("buffwatch", function ()
|
||||
end
|
||||
|
||||
frame.RefreshPosition = RefreshPosition
|
||||
frame.Refresh = function()
|
||||
RefreshBuffBarFrame(frame)
|
||||
RefreshPositions()
|
||||
end
|
||||
|
||||
-- OnEvent (UNIT_AURA) doesn't trigger properly on buff refresh
|
||||
frame:SetScript("OnUpdate", BuffBarFrameOnUpdate)
|
||||
-- UNIT_AURA_DELTA reports refreshes and new durations as well, so the bars
|
||||
-- follow the auras on events alone. A new target is another unit, whose
|
||||
-- auras no delta describes.
|
||||
frame:RegisterUnitEvent("UNIT_AURA_DELTA", frame.unit)
|
||||
frame:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
if frame.unit == "target" then frame:RegisterEvent("PLAYER_TARGET_CHANGED") end
|
||||
frame:SetScript("OnEvent", frame.Refresh)
|
||||
table.insert(watched, frame)
|
||||
|
||||
-- the first read waits until the caller has configured the frame
|
||||
RunNextFrame(frame.Refresh)
|
||||
|
||||
return frame
|
||||
end
|
||||
|
||||
@@ -1536,7 +1536,7 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
|
||||
engineEvents:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
engineEvents:RegisterEvent("PLAYER_LEAVING_WORLD")
|
||||
engineEvents:RegisterUnitEvent("UNIT_AURA", "player") -- mounted, stealth, dining
|
||||
engineEvents:RegisterUnitEvent("UNIT_AURA_DELTA", "player") -- mounted, stealth, dining
|
||||
engineEvents:RegisterEvent("UPDATE_SHAPESHIFT_FORM")
|
||||
engineEvents:RegisterEvent("ZONE_CHANGED")
|
||||
engineEvents:RegisterEvent("ZONE_CHANGED_INDOORS")
|
||||
|
||||
@@ -69,10 +69,10 @@ pfUI:RegisterModule("lossofcontrol", function ()
|
||||
self:Show()
|
||||
end
|
||||
|
||||
-- a refreshed debuff (UNIT_AURA) or a second kick on the same school
|
||||
-- a refreshed debuff (UNIT_AURA_DELTA) or a second kick on the same school
|
||||
-- (SPELL_UPDATE_COOLDOWN) changes the timer without a loss of control event
|
||||
frame:SetScript("OnShow", function()
|
||||
this:RegisterUnitEvent("UNIT_AURA", "player")
|
||||
this:RegisterUnitEvent("UNIT_AURA_DELTA", "player")
|
||||
this:RegisterEvent("SPELL_UPDATE_COOLDOWN")
|
||||
end)
|
||||
frame:SetScript("OnHide", function()
|
||||
|
||||
@@ -501,7 +501,7 @@ nameplates:RegisterEvent("NAME_PLATE_CREATED")
|
||||
nameplates:RegisterEvent("NAME_PLATE_UNIT_ADDED")
|
||||
nameplates:RegisterEvent("NAME_PLATE_UNIT_REMOVED")
|
||||
nameplates:RegisterEvent("UPDATE_MOUSEOVER_UNIT")
|
||||
-- UNIT_AURA / UNIT_FLAGS / UNIT_SPELLCAST_* are registered per plate, on the
|
||||
-- UNIT_AURA_DELTA / UNIT_FLAGS / UNIT_SPELLCAST_* are registered per plate, on the
|
||||
-- plate's own frame, against its own token -- see OnCreate and
|
||||
-- NAME_PLATE_UNIT_ADDED.
|
||||
nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
|
||||
@@ -606,7 +606,7 @@ nameplates:RegisterUnitEvent("UNIT_SPELLCAST_NOT_INTERRUPTIBLE", "target", "focu
|
||||
-- Point this plate's own subscriptions at the token it just took. On a
|
||||
-- recycled frame these replace the previous unit rather than stacking:
|
||||
-- RegisterUnitEvent on an already-filtered registration swaps the units.
|
||||
plate.nameplate:RegisterUnitEvent("UNIT_AURA", arg1)
|
||||
plate.nameplate:RegisterUnitEvent("UNIT_AURA_DELTA", arg1)
|
||||
plate.nameplate:RegisterUnitEvent("UNIT_FLAGS", arg1)
|
||||
plate.nameplate:RegisterUnitEvent("UNIT_TARGET", arg1)
|
||||
plate.nameplate:RegisterUnitEvent("UNIT_SPELLCAST_START", arg1)
|
||||
@@ -760,7 +760,7 @@ nameplates:RegisterUnitEvent("UNIT_SPELLCAST_NOT_INTERRUPTIBLE", "target", "focu
|
||||
-- new token; _REMOVED drops it, which matters because freed slots are
|
||||
-- reused and a stale token would feed this frame another unit's events.
|
||||
nameplate:SetScript("OnEvent", function()
|
||||
if event == "UNIT_AURA" then
|
||||
if event == "UNIT_AURA_DELTA" then
|
||||
-- a fresh C_UnitAuras read next tick rather than waiting out the 0.5s
|
||||
-- throttle -- covers expiry, dispels, refreshes and stack changes
|
||||
this.auraUpdate = true
|
||||
|
||||
Reference in New Issue
Block a user