mirror of
https://github.com/brues-code/pfUI.git
synced 2026-09-21 23:26:56 +00:00
f497c71523
Eight commits off classicapi_next. ClassicAPI's RegisterUnitEvent registers for an event but only delivers it when arg1 is one of the given units, so a handler for one unit stops waking for every other one in the world. The 26 registrations whose unit set is fixed at registration time now name it. The rule throughout is register the superset and keep the handler's own check -- the filter narrows what arrives, it does not decide what to act on. Guards that look unreachable stay put: the filter applies only when arg1 is a string, so an event that fires with a number or no argument is delivered as if plainly registered. Frames whose unit changes at runtime own their subscriptions instead of sorting events out per event. unitframes points them at the unitstr UpdateVisibility already computes -- replacing a string concat, and on a miss a second concat plus a UnitGUID call, for every frame on every unit event in the world -- and a frame that is not in use drops its unit events entirely. nameplates registers per plate against the plate's own token, which is also the only workable shape: slots have no cap, so any nameplate1..N list would have been a guess that fails in exactly the crowded scenes where plates matter. marktracking names mark1 through mark8. A registration keeps its kind, so none of these can be plain-registered first. Both teardown paths PLAYER_LOGOUT guards -- the crash 132 -- now cover the per-frame subscriptions: plates tear down rather than dispatching through logout, and a unit frame takes itself off the visibility scan so it cannot re-register what it just dropped. marktracking also drops its once-a-second full rebuild, which ran for the whole session whether or not a marker existed anywhere. The ticker is created and cancelled with group membership. It is deliberately not keyed on a mark being visible -- a marker on an out-of-range unit shows no row, and that is the case the poll exists to catch. nameplates gates the per-plate update against the floor across all four throttle categories before classifying it, instead of running a GetAlpha, a castbar IsShown, a cast lookup and up to two libthrottle:Get resolutions on plates throttled to 10fps that were going to return anyway. Nothing that would have updated can be turned away by a floor. The four throttles resolve in CacheConfig, where config changes already land. energytick sweeps the clock the server actually runs. There is one regen timer for every power, re-armed every 2s by Player::RegenerateAll and never touched by casting; the five-second rule changes what a tick pays, not when it lands. The sweep is a free-running phase lock on that clock, so Illumination refunds, potions and a Mana Spring totem on its own phase no longer snap the spark mid-cycle, and an 80ms band keeps a correct tick from hitching it at the wrap. The FSR window shades rather than predicting a share of spirit the client cannot compute -- the Casting Regen item ladder is equip auras absent from the buff list. The energy period is summed from SPELL_AURA_MOD_ENERGY_REGEN_TIME across the spellbook and buffs, so Blade Rush is found without GetTalentInfo(2, 16), an ordinal that does not fail when the tree changes but reads another talent's rank. macrotweak is gone -- ClassicAPI 1.15 covers it -- with its config entry, its GUI block, its translations in all eight locales, and actionbar's ButtonMacroScan, the #showtooltip scanner that fed it.
374 lines
13 KiB
Lua
374 lines
13 KiB
Lua
pfUI:RegisterModule("buff", function ()
|
|
-- Hide Blizz
|
|
BuffFrame:Hide()
|
|
BuffFrame:UnregisterAllEvents()
|
|
TemporaryEnchantFrame:Hide()
|
|
TemporaryEnchantFrame:UnregisterAllEvents()
|
|
|
|
local br, bg, bb, ba = GetStringColor(pfUI_config.appearance.border.color)
|
|
|
|
-- Player aura slot ids, enumerated once per refresh pass (ScanPlayerAuraSlots)
|
|
-- and read per button by its aura index: one GetAuraSlots walk per range
|
|
-- instead of a by-index walk per button.
|
|
local helpfulSlots, harmfulSlots = {}, {}
|
|
local function ScanPlayerAuraSlots()
|
|
ScanAuraSlots("player", "HELPFUL", helpfulSlots, 32)
|
|
ScanAuraSlots("player", "HARMFUL", harmfulSlots, 16)
|
|
end
|
|
|
|
local function RefreshBuffButton(buff)
|
|
if buff.btype == "HELPFUL" then
|
|
if C.buffs.separateweapons == "1" then
|
|
buff.id = buff.gid - (buff.weapon ~= nill and buff.gid or 0)
|
|
else
|
|
buff.id = buff.gid - pfUI.buff.wepbuffs.count
|
|
end
|
|
else
|
|
buff.id = buff.gid
|
|
end
|
|
|
|
if not buff.backdrop then
|
|
CreateBackdrop(buff)
|
|
CreateBackdropShadow(buff)
|
|
end
|
|
|
|
local slots = buff.btype == "HELPFUL" and helpfulSlots or harmfulSlots
|
|
local name, icon, count, dispelType, _, expirationTime, _, _, _, spellId = C_UnitAuras.UnitAuraBySlot("player", slots[buff.id])
|
|
|
|
--detect weapon buffs
|
|
if buff.btype == "HELPFUL" and ((C.buffs.separateweapons == "0" and buff.gid <= pfUI.buff.wepbuffs.count) or (pfUI.buff.wepbuffs.count > 0 and buff.weapon ~= nil)) then
|
|
local mh, mhtime, mhcharge, oh, ohtime, ohcharge = GetWeaponEnchantInfo()
|
|
if pfUI.buff.wepbuffs.count == 2 then
|
|
if buff.gid == 1 then
|
|
buff.mode = "MAINHAND"
|
|
else
|
|
buff.mode = "OFFHAND"
|
|
end
|
|
else
|
|
if C.buffs.separateweapons == "0" then
|
|
buff.mode = mh and "MAINHAND" or oh and "OFFHAND"
|
|
else
|
|
if buff.gid == 1 then
|
|
buff.mode = mh and "MAINHAND" or oh and "OFFHAND"
|
|
else
|
|
buff:Hide()
|
|
return
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Set Weapon Texture and Border
|
|
if buff.mode == "MAINHAND" then
|
|
buff.texture:SetTexture(GetInventoryItemTexture("player", 16))
|
|
buff.backdrop:SetBackdropBorderColor(GetItemQualityColor(GetInventoryItemQuality("player", 16) or 1))
|
|
elseif buff.mode == "OFFHAND" then
|
|
buff.texture:SetTexture(GetInventoryItemTexture("player", 17))
|
|
buff.backdrop:SetBackdropBorderColor(GetItemQualityColor(GetInventoryItemQuality("player", 17) or 1))
|
|
end
|
|
elseif name and (( buff.btype == "HARMFUL" and C.buffs.debuffs == "1" ) or ( buff.btype == "HELPFUL" and C.buffs.buffs == "1" )) then
|
|
-- Set Buff Texture and Border
|
|
buff.mode = buff.btype
|
|
buff.expirationTime = expirationTime
|
|
buff.stackCount = count
|
|
buff.spellId = spellId
|
|
buff.texture:SetTexture(icon)
|
|
|
|
if buff.btype == "HARMFUL" then
|
|
local dispelColor = C_UnitAuras.GetAuraDispelTypeColor(dispelType)
|
|
buff.backdrop:SetBackdropBorderColor(dispelColor:GetRGBA())
|
|
else
|
|
buff.backdrop:SetBackdropBorderColor(br,bg,bb,ba)
|
|
end
|
|
else
|
|
buff:Hide()
|
|
return
|
|
end
|
|
|
|
buff:Show()
|
|
end
|
|
|
|
local function CreateBuffButton(i, btype, weapon)
|
|
local buttonName, buttonParent
|
|
if btype == "HELPFUL" then
|
|
if weapon == 1 then
|
|
buttonName = "pfWepBuffFrame" .. i
|
|
buttonParent = pfUI.buff.wepbuffs
|
|
else
|
|
buttonName = "pfBuffFrameBuff" .. i
|
|
buttonParent = pfUI.buff.buffs
|
|
end
|
|
else
|
|
buttonName = "pfDebuffFrameBuff" .. i
|
|
buttonParent = pfUI.buff.debuffs
|
|
end
|
|
local buff = CreateFrame("Button", buttonName, buttonParent)
|
|
buff.texture = buff:CreateTexture("BuffIcon" .. i, "BACKGROUND")
|
|
buff.texture:SetTexCoord(.07,.93,.07,.93)
|
|
buff.texture:SetAllPoints(buff)
|
|
|
|
buff.timer = buff:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
|
buff.timer:SetTextColor(1,1,1,1)
|
|
buff.timer:SetJustifyH("CENTER")
|
|
buff.timer:SetJustifyV("CENTER")
|
|
|
|
buff.stacks = buff:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
|
buff.stacks:SetTextColor(1,1,1,1)
|
|
buff.stacks:SetJustifyH("RIGHT")
|
|
buff.stacks:SetJustifyV("BOTTOM")
|
|
buff.stacks:SetAllPoints(buff)
|
|
|
|
buff:RegisterForClicks("RightButtonUp")
|
|
|
|
buff.weapon = weapon
|
|
buff.btype = btype
|
|
buff.gid = i
|
|
|
|
-- PERF: OnUpdate moved to consolidated parent frame handler (see pfUI.buff:SetScript("OnUpdate"))
|
|
-- Individual buff frames no longer have their own OnUpdate
|
|
|
|
buff:SetScript("OnEnter", function()
|
|
GameTooltip:SetOwner(this, "ANCHOR_BOTTOMRIGHT")
|
|
if this.mode == this.btype then
|
|
GameTooltip:SetUnitAura("player", this.id, this.btype)
|
|
|
|
if IsShiftKeyDown() then
|
|
local aura = C_UnitAuras.GetAuraDataByIndex("player", this.id, this.btype)
|
|
local playerlist = aura and GetUnbuffedRoster(aura.name) or ""
|
|
if strlen(playerlist) > 0 then
|
|
GameTooltip:AddLine(" ")
|
|
GameTooltip:AddLine(T["Unbuffed"] .. ":", .3, 1, .8)
|
|
GameTooltip:AddLine(playerlist,1,1,1,1)
|
|
GameTooltip:Show()
|
|
end
|
|
end
|
|
elseif this.mode == "MAINHAND" then
|
|
GameTooltip:SetInventoryItem("player", 16)
|
|
elseif this.mode == "OFFHAND" then
|
|
GameTooltip:SetInventoryItem("player", 17)
|
|
end
|
|
end)
|
|
|
|
buff:SetScript("OnLeave", GameTooltip_Hide)
|
|
|
|
buff:SetScript("OnClick", function()
|
|
if this.spellId then
|
|
C_Spell.CancelSpellByID(this.spellId)
|
|
end
|
|
end)
|
|
|
|
RefreshBuffButton(buff)
|
|
|
|
return buff
|
|
end
|
|
|
|
pfUI.buff = CreateFrame("Frame", "pfGlobalBuffFrame", UIParent)
|
|
pfUI.buff:RegisterEvent("PLAYER_AURAS_CHANGED")
|
|
pfUI.buff:RegisterEvent("PLAYER_EQUIPMENT_CHANGED")
|
|
pfUI.buff:RegisterUnitEvent("UNIT_MODEL_CHANGED", "player")
|
|
pfUI.buff:RegisterEvent("BUFF_UPDATE_DURATION_SELF")
|
|
pfUI.buff:RegisterEvent("DEBUFF_UPDATE_DURATION_SELF")
|
|
pfUI.buff:SetScript("OnEvent", function()
|
|
|
|
if C.buffs.weapons == "1" then
|
|
local mh, mhtime, mhcharge, oh, ohtime, ohcharge = GetWeaponEnchantInfo()
|
|
pfUI.buff.wepbuffs.count = (mh and 1 or 0) + (oh and 1 or 0)
|
|
else
|
|
pfUI.buff.wepbuffs.count = 0
|
|
end
|
|
|
|
ScanPlayerAuraSlots()
|
|
|
|
for i=1,32 do
|
|
RefreshBuffButton(pfUI.buff.buffs.buttons[i])
|
|
end
|
|
|
|
for i=1,16 do
|
|
RefreshBuffButton(pfUI.buff.debuffs.buttons[i])
|
|
end
|
|
|
|
if C.buffs.separateweapons == "1" then
|
|
for i=1,2 do
|
|
RefreshBuffButton(pfUI.buff.wepbuffs.buttons[i])
|
|
end
|
|
end
|
|
end)
|
|
|
|
-- PERF: Consolidated OnUpdate handler for all buff timers
|
|
-- This replaces 50 individual OnUpdate handlers with a single one
|
|
pfUI.buff:SetScript("OnUpdate", function()
|
|
local now = GetTime()
|
|
if not this.nextUpdate then this.nextUpdate = now + 0.1 end
|
|
if this.nextUpdate > now then return end
|
|
this.nextUpdate = now + 0.1
|
|
|
|
-- Cache weapon enchant info once per update cycle
|
|
local mh, mhtime, mhcharge, oh, ohtime, ohcharge = GetWeaponEnchantInfo()
|
|
|
|
-- Update all visible buff buttons
|
|
local buttons = pfUI.buff.buffs.buttons
|
|
for i = 1, 32 do
|
|
local buff = buttons[i]
|
|
if buff:IsShown() then
|
|
local timeleft, stacks = 0, 0
|
|
if buff.mode == buff.btype then
|
|
timeleft = buff.expirationTime > 0 and (buff.expirationTime - now) or 0
|
|
stacks = buff.stackCount or 0
|
|
elseif buff.mode == "MAINHAND" then
|
|
timeleft = mhtime and mhtime / 1000 or 0
|
|
stacks = mhcharge or 0
|
|
elseif buff.mode == "OFFHAND" then
|
|
timeleft = ohtime and ohtime / 1000 or 0
|
|
stacks = ohcharge or 0
|
|
end
|
|
buff.timer:SetText(timeleft > 0 and GetColoredTimeString(timeleft) or "")
|
|
buff.stacks:SetText(stacks > 1 and stacks or "")
|
|
end
|
|
end
|
|
|
|
-- Update all visible debuff buttons
|
|
buttons = pfUI.buff.debuffs.buttons
|
|
for i = 1, 16 do
|
|
local buff = buttons[i]
|
|
if buff:IsShown() then
|
|
local timeleft = buff.expirationTime > 0 and (buff.expirationTime - now) or 0
|
|
local stacks = buff.stackCount or 0
|
|
buff.timer:SetText(timeleft > 0 and GetColoredTimeString(timeleft) or "")
|
|
buff.stacks:SetText(stacks > 1 and stacks or "")
|
|
end
|
|
end
|
|
|
|
-- Update weapon buff buttons if separate
|
|
if C.buffs.separateweapons == "1" then
|
|
buttons = pfUI.buff.wepbuffs.buttons
|
|
for i = 1, 2 do
|
|
local buff = buttons[i]
|
|
if buff:IsShown() then
|
|
local timeleft, stacks = 0, 0
|
|
if buff.mode == "MAINHAND" then
|
|
timeleft = mhtime and mhtime / 1000 or 0
|
|
stacks = mhcharge or 0
|
|
elseif buff.mode == "OFFHAND" then
|
|
timeleft = ohtime and ohtime / 1000 or 0
|
|
stacks = ohcharge or 0
|
|
end
|
|
buff.timer:SetText(timeleft > 0 and GetColoredTimeString(timeleft) or "")
|
|
buff.stacks:SetText(stacks > 1 and stacks or "")
|
|
end
|
|
end
|
|
end
|
|
end)
|
|
|
|
-- CreateBuffButton refreshes each new button from the slot buffers
|
|
ScanPlayerAuraSlots()
|
|
|
|
-- Weapon Buffs
|
|
pfUI.buff.wepbuffs = CreateFrame("Frame", "pfWepBuffFrame", UIParent)
|
|
pfUI.buff.wepbuffs.count = 0
|
|
pfUI.buff.wepbuffs.buttons = {}
|
|
for i=1,2 do
|
|
pfUI.buff.wepbuffs.buttons[i] = CreateBuffButton(i, "HELPFUL", 1)
|
|
end
|
|
|
|
-- Buff Frame
|
|
pfUI.buff.buffs = CreateFrame("Frame", "pfBuffFrame", UIParent)
|
|
pfUI.buff.buffs.buttons = {}
|
|
for i=1,32 do
|
|
pfUI.buff.buffs.buttons[i] = CreateBuffButton(i, "HELPFUL")
|
|
end
|
|
|
|
-- Debuffs
|
|
pfUI.buff.debuffs = CreateFrame("Frame", "pfDebuffFrame", UIParent)
|
|
pfUI.buff.debuffs.buttons = {}
|
|
for i=1,16 do
|
|
pfUI.buff.debuffs.buttons[i] = CreateBuffButton(i, "HARMFUL")
|
|
end
|
|
|
|
-- config loading
|
|
function pfUI.buff:UpdateConfigBuffButton(buff)
|
|
local fontsize = C.buffs.fontsize == "-1" and C.global.font_size or C.buffs.fontsize
|
|
local buffSize, buffSpacing = tonumber(C.buffs.size), tonumber(C.buffs.spacing)
|
|
local rowcount, relFrame, offsetX, offsetY
|
|
if buff.btype == "HELPFUL" then
|
|
if buff.weapon == 1 and C.buffs.separateweapons == "1" then
|
|
rowcount = floor((buff.gid-1) / tonumber(C.buffs.wepbuffrowsize))
|
|
relFrame = pfUI.buff.wepbuffs
|
|
offsetX = -(buff.gid-1-rowcount*tonumber(C.buffs.wepbuffrowsize))*(buffSize+2*buffSpacing)
|
|
offsetY = -(rowcount) * ((C.buffs.textinside == "1" and 0 or (fontsize*1.5))+buffSize+2*buffSpacing)
|
|
else
|
|
rowcount = floor((buff.gid-1) / tonumber(C.buffs.buffrowsize))
|
|
relFrame = pfUI.buff.buffs
|
|
offsetX = -(buff.gid-1-rowcount*tonumber(C.buffs.buffrowsize))*(buffSize+2*buffSpacing)
|
|
offsetY = -(rowcount) * ((C.buffs.textinside == "1" and 0 or (fontsize*1.5))+buffSize+2*buffSpacing)
|
|
end
|
|
else
|
|
rowcount = floor((buff.gid-1) / tonumber(C.buffs.debuffrowsize))
|
|
relFrame = pfUI.buff.debuffs
|
|
offsetX = -(buff.gid-1-rowcount*tonumber(C.buffs.debuffrowsize))*(buffSize+2*buffSpacing)
|
|
offsetY = -(rowcount) * ((C.buffs.textinside == "1" and 0 or (fontsize*1.5))+buffSize+2*buffSpacing)
|
|
end
|
|
buff:SetSize(buffSize, buffSize)
|
|
buff:ClearAllPoints()
|
|
buff:SetPoint("TOPRIGHT", relFrame, "TOPRIGHT",offsetX, offsetY)
|
|
|
|
buff.timer:SetFont(pfUI.font_default, fontsize, "OUTLINE")
|
|
buff.stacks:SetFont(pfUI.font_default, fontsize+1, "OUTLINE")
|
|
|
|
buff.timer:SetHeight(fontsize * 1.3)
|
|
|
|
buff.timer:ClearAllPoints()
|
|
if C.buffs.textinside == "1" then
|
|
buff.timer:SetAllPoints(buff)
|
|
else
|
|
buff.timer:SetPoint("TOP", buff, "BOTTOM", 0, -3)
|
|
end
|
|
end
|
|
|
|
function pfUI.buff:UpdateConfig()
|
|
local fontsize = C.buffs.fontsize == "-1" and C.global.font_size or C.buffs.fontsize
|
|
|
|
local spacing = tonumber(C.buffs.spacing)
|
|
local cell = tonumber(C.buffs.size) + 2 * spacing
|
|
local rowextra = C.buffs.textinside == "1" and 0 or (fontsize * 1.5)
|
|
|
|
local function SizeBuffFrame(frame, rowsize, count)
|
|
rowsize = tonumber(rowsize)
|
|
frame:SetSize(rowsize * cell, ceil(count / rowsize) * (rowextra + cell))
|
|
end
|
|
|
|
SizeBuffFrame(pfUI.buff.buffs, C.buffs.buffrowsize, 32)
|
|
pfUI.buff.buffs:SetPoint("TOPRIGHT", pfUI.minimap or UIParent, "TOPLEFT", -4 * spacing, 0)
|
|
UpdateMovable(pfUI.buff.buffs)
|
|
|
|
SizeBuffFrame(pfUI.buff.debuffs, C.buffs.debuffrowsize, 16)
|
|
pfUI.buff.debuffs:SetPoint("TOPRIGHT", pfUI.buff.buffs, "BOTTOMRIGHT", 0, 0)
|
|
UpdateMovable(pfUI.buff.debuffs)
|
|
|
|
if C.buffs.separateweapons == "1" then
|
|
pfUI.buff.wepbuffs:ClearAllPoints()
|
|
SizeBuffFrame(pfUI.buff.wepbuffs, C.buffs.wepbuffrowsize, 2)
|
|
pfUI.buff.wepbuffs:SetPoint("TOPRIGHT", pfUI.buff.debuffs, "BOTTOMRIGHT", 0, 0)
|
|
pfUI.buff.wepbuffs:Show()
|
|
UpdateMovable(pfUI.buff.wepbuffs)
|
|
else
|
|
pfUI.buff.wepbuffs:Hide()
|
|
RemoveMovable(pfUI.buff.wepbuffs)
|
|
end
|
|
|
|
for i=1,32 do
|
|
pfUI.buff:UpdateConfigBuffButton(pfUI.buff.buffs.buttons[i])
|
|
end
|
|
|
|
for i=1,16 do
|
|
pfUI.buff:UpdateConfigBuffButton(pfUI.buff.debuffs.buttons[i])
|
|
end
|
|
|
|
for i=1,2 do
|
|
pfUI.buff:UpdateConfigBuffButton(pfUI.buff.wepbuffs.buttons[i])
|
|
end
|
|
|
|
pfUI.buff:GetScript("OnEvent")()
|
|
end
|
|
|
|
pfUI.buff:UpdateConfig()
|
|
end)
|