3 Commits
Author SHA1 Message Date
Brues 10b028b0c2 Bump ClassicAPI minimum version 2026-08-21 15:00:25 -05:00
Brues 7ccc56c241 utlize inline icons and CreateGoldString in repair module 2026-08-21 15:00:25 -05:00
Brues c64a501e6f Utilize SetFormattedText
This change updates several text setters to use `SetFormattedText` instead of passing `string.format(...)` into `SetText`. It also switches a few regex captures from `string.find` to `string.match` for cleaner capture handling, and replaces explicit width/height setters with `SetSize` in the first-run dialog for consistency.
2026-08-21 15:00:25 -05:00
10 changed files with 194 additions and 234 deletions
-22
View File
@@ -1,22 +0,0 @@
{
"runtime": {
"version": "Lua 5.1"
},
"diagnostics": {
"disable": ["deprecated"],
"globals": [
"this",
"event",
"arg",
"arg1",
"arg2",
"arg3",
"arg4",
"arg5",
"arg6",
"arg7",
"arg8",
"arg9"
]
}
}
-4
View File
@@ -1,4 +0,0 @@
package-as: pfUI
ignore:
- .luarc.json
+53 -66
View File
@@ -1638,40 +1638,39 @@ function pfUI.uf:RefreshUnit(unit, component)
for i=1, unit.config.bufflimit do for i=1, unit.config.bufflimit do
if not unit.buffs[i] then break end if not unit.buffs[i] then break end
-- positional UnitBuff allocates nothing (vs a table per icon per refresh) local aura = C_UnitAuras.GetBuffDataByIndex(unitstr, i)
local name, icon, count, _, duration, expirationTime, _, _, _, spellId = C_UnitAuras.UnitBuff(unitstr, i)
if name then if aura then
unit.buffs[i].texture:SetTexture(icon) unit.buffs[i].texture:SetTexture(aura.icon)
unit.buffs[i]:Show() unit.buffs[i]:Show()
if count > 1 then if aura.applications > 1 then
unit.buffs[i].stacks:SetText(count) unit.buffs[i].stacks:SetText(aura.applications)
else else
unit.buffs[i].stacks:SetText("") unit.buffs[i].stacks:SetText("")
end end
if expirationTime > 0 then if aura.expirationTime > 0 then
-- pfUI's cooldown text falls into a 2^32 wraparound branch when start > GetTime(), -- pfUI's cooldown text falls into a 2^32 wraparound branch when start > GetTime(),
-- which happens for talent-extended buffs where the real duration exceeds the -- which happens for talent-extended buffs where the real duration exceeds aura.duration
-- Spell.dbc base. Anchor start to now in that case to keep the remaining math sane. -- (the Spell.dbc base). Anchor start to now in that case to keep the remaining math sane.
local now = GetTime() local now = GetTime()
local start = expirationTime - duration local start = aura.expirationTime - aura.duration
local dur = duration local duration = aura.duration
if start > now or dur <= 0 then if start > now or duration <= 0 then
start, dur = now, expirationTime - now start, duration = now, aura.expirationTime - now
end end
if dur > 0 then if duration > 0 then
CooldownFrame_SetTimer(unit.buffs[i].cd, start, dur, 1) CooldownFrame_SetTimer(unit.buffs[i].cd, start, duration, 1)
else else
CooldownFrame_SetTimer(unit.buffs[i].cd, 0, 0, 0) CooldownFrame_SetTimer(unit.buffs[i].cd, 0, 0, 0)
end end
elseif duration > 0 then elseif aura.duration > 0 then
local guid = UnitGUID(unitstr) local guid = UnitGUID(unitstr)
local guidStarts = guid and pfUI.uf.aura_starts[guid] local guidStarts = guid and pfUI.uf.aura_starts[guid]
local start = guidStarts and guidStarts[spellId] local start = guidStarts and guidStarts[aura.spellId]
if start then if start then
CooldownFrame_SetTimer(unit.buffs[i].cd, start, duration, 1) CooldownFrame_SetTimer(unit.buffs[i].cd, start, aura.duration, 1)
else else
CooldownFrame_SetTimer(unit.buffs[i].cd, 0, 0, 0) CooldownFrame_SetTimer(unit.buffs[i].cd, 0, 0, 0)
end end
@@ -1742,11 +1741,10 @@ function pfUI.uf:RefreshUnit(unit, component)
-- selfdebuff narrows to player-cast harmful auras via the PLAYER filter. -- selfdebuff narrows to player-cast harmful auras via the PLAYER filter.
-- Player-frame debuffs aren't gated on it (it'd hide most party-applied -- Player-frame debuffs aren't gated on it (it'd hide most party-applied
-- effects on you). -- effects on you).
-- positional UnitDebuff allocates nothing; PLAYER predicate honored for selfdebuff local filter = (unit.label ~= "player" and selfdebuff == "1") and "HARMFUL|PLAYER" or "HARMFUL"
local filter = (unit.label ~= "player" and selfdebuff == "1") and "PLAYER" or nil local aura = C_UnitAuras.GetAuraDataByIndex(unitstr, i, filter)
local name, icon, count, dispelType, duration, expirationTime = C_UnitAuras.UnitDebuff(unitstr, i, filter) if aura then
if name then texture, stacks, dtype = aura.icon, aura.applications, aura.dispelName
texture, stacks, dtype = icon, count, dispelType
else else
texture, stacks, dtype = nil, 0, nil texture, stacks, dtype = nil, 0, nil
end end
@@ -1759,18 +1757,18 @@ function pfUI.uf:RefreshUnit(unit, component)
if texture then if texture then
unit.debuffs[i]:Show() unit.debuffs[i]:Show()
if expirationTime > 0 then if aura and aura.expirationTime > 0 then
-- Cap start to now so talent-extended debuffs (expirationTime past -- Cap start to now so talent-extended debuffs (expirationTime past
-- the dbc base duration) don't push start into the future and trip -- the dbc base duration) don't push start into the future and trip
-- CooldownFrame_SetTimer's 2^32-wraparound branch. -- CooldownFrame_SetTimer's 2^32-wraparound branch.
local now = GetTime() local now = GetTime()
local start = expirationTime - duration local start = aura.expirationTime - aura.duration
local dur = duration local duration = aura.duration
if start > now or dur <= 0 then if start > now or duration <= 0 then
start, dur = now, expirationTime - now start, duration = now, aura.expirationTime - now
end end
if dur > 0 then if duration > 0 then
CooldownFrame_SetTimer(unit.debuffs[i].cd, start, dur, 1) CooldownFrame_SetTimer(unit.debuffs[i].cd, start, duration, 1)
else else
CooldownFrame_SetTimer(unit.debuffs[i].cd, 0, 0, 0) CooldownFrame_SetTimer(unit.debuffs[i].cd, 0, 0, 0)
end end
@@ -1828,17 +1826,6 @@ function pfUI.uf:RefreshUnit(unit, component)
end end
end end
-- Scan the 16 harmful slots once into a reusable set, instead of
-- re-scanning all 16 for every dispellable type below. Reused across
-- frames (populated and consumed within this single RefreshUnit call).
local present = pfUI.uf.dispelPresent or {}
pfUI.uf.dispelPresent = present
for k in pairs(present) do present[k] = nil end
for i=1,16 do
local name, _, _, dispelType = C_UnitAuras.UnitDebuff(unitstr, i)
if name and dispelType and dispelType ~= "" then present[dispelType] = true end
end
for _, debuff in pairs(unit.dispellable) do for _, debuff in pairs(unit.dispellable) do
indicator[debuff] = indicator[debuff] or CreateFrame("Frame", nil, indicator) indicator[debuff] = indicator[debuff] or CreateFrame("Frame", nil, indicator)
indicator[debuff]:SetParent(indicator) indicator[debuff]:SetParent(indicator)
@@ -1878,7 +1865,15 @@ function pfUI.uf:RefreshUnit(unit, component)
indicator[debuff].disp = indicator.disp indicator[debuff].disp = indicator.disp
end end
indicator[debuff].visible = present[debuff] indicator[debuff].visible = nil
for i=1,16 do
local a = C_UnitAuras.GetDebuffDataByIndex(unitstr, i)
local dtype = a and a.dispelName
if dtype == debuff then
indicator[debuff].visible = true
end
end
if indicator[debuff].visible then if indicator[debuff].visible then
indicator[debuff]:Show() indicator[debuff]:Show()
@@ -1925,58 +1920,50 @@ function pfUI.uf:RefreshUnit(unit, component)
local pos = 1 local pos = 1
if table.getn(unit.indicators) > 0 then if table.getn(unit.indicators) > 0 then
local i = 1 for _, aura in ipairs(C_UnitAuras.GetUnitAuras(unitstr, "HELPFUL")) do
while true do local texLower = string.lower(aura.icon)
local name, icon, count, _, _, expirationTime = C_UnitAuras.UnitBuff(unitstr, i) local timeleft = aura.expirationTime > 0 and (aura.expirationTime - GetTime()) or nil
if not name then break end
local texLower = string.lower(icon)
local timeleft = expirationTime > 0 and (expirationTime - GetTime()) or nil
for _, filter in pairs(unit.indicators) do for _, filter in pairs(unit.indicators) do
if filter == texLower then if filter == texLower then
local hot = HOT_INDICATORS[texLower] local hot = HOT_INDICATORS[texLower]
if hot and string.lower(name) ~= hot.name then if hot and string.lower(aura.name) ~= hot.name then
break -- texture matches but name disambiguates (e.g. shared icon) break -- texture matches but name disambiguates (e.g. shared icon)
end end
if hot then if hot then
local start, duration, prediction = libpredict:GetHotDuration(unitstr, hot.predict) local start, duration, prediction = libpredict:GetHotDuration(unitstr, hot.predict)
pfUI.uf:AddIcon(unit, pos, icon, timeleft or prediction, count, tonumber(start), tonumber(duration)) pfUI.uf:AddIcon(unit, pos, aura.icon, timeleft or prediction, aura.applications, tonumber(start), tonumber(duration))
else else
pfUI.uf:AddIcon(unit, pos, icon, timeleft, count) pfUI.uf:AddIcon(unit, pos, aura.icon, timeleft, aura.applications)
end end
pos = pos + 1 pos = pos + 1
break break
end end
end end
i = i + 1
end end
end end
if table.getn(unit.indicator_custom) > 0 then if table.getn(unit.indicator_custom) > 0 then
local ai = 1 for _, aura in ipairs(C_UnitAuras.GetUnitAuras(unitstr, "HELPFUL")) do
while true do local timeleft = aura.expirationTime > 0 and (aura.expirationTime - GetTime()) or nil
local name, icon, count, _, _, expirationTime = C_UnitAuras.UnitBuff(unitstr, ai) local lowerName = string.lower(aura.name)
if not name then break end
local timeleft = expirationTime > 0 and (expirationTime - GetTime()) or nil
local lowerName = string.lower(name)
for _, filter in pairs(unit.indicator_custom) do for _, filter in pairs(unit.indicator_custom) do
if filter == lowerName then if filter == lowerName then
pfUI.uf:AddIcon(unit, pos, icon, timeleft, count) pfUI.uf:AddIcon(unit, pos, aura.icon, timeleft, aura.applications)
pos = pos + 1 pos = pos + 1
break break
end end
end end
ai = ai + 1
end end
local debuffFilter = unit.config.selfdebuff == "1" and "PLAYER" or nil local debuffFilter = unit.config.selfdebuff == "1" and "HARMFUL|PLAYER" or "HARMFUL"
for i=1,16 do -- scan for custom debuffs for i=1,16 do -- scan for custom debuffs
local name, icon, count, _, _, expirationTime = C_UnitAuras.UnitDebuff(unitstr, i, debuffFilter) local aura = C_UnitAuras.GetAuraDataByIndex(unitstr, i, debuffFilter)
if name then if aura then
local timeleft = expirationTime > 0 and (expirationTime - GetTime()) or nil local timeleft = aura.expirationTime > 0 and (aura.expirationTime - GetTime()) or nil
for _, filter in pairs(unit.indicator_custom) do for _, filter in pairs(unit.indicator_custom) do
if filter == string.lower(name) then if filter == string.lower(aura.name) then
pfUI.uf:AddIcon(unit, pos, icon, timeleft, count) pfUI.uf:AddIcon(unit, pos, aura.icon, timeleft, aura.applications)
pos = pos + 1 pos = pos + 1
break break
end end
-3
View File
@@ -168,9 +168,6 @@ pfUI.libdebuff_spell_start_self_hooks["libpredict"] = function(spellId, casterGu
return return
end end
spell_queue[1] = spellName
spell_queue[2] = spellName .. (C_Spell.GetSpellSubtext(spellId) or "")
if spell_queue[1] == spellName and cache[spell_queue[2]] then if spell_queue[1] == spellName and cache[spell_queue[2]] then
local amount = cache[spell_queue[2]][1] local amount = cache[spell_queue[2]][1]
local casttime = castTime local casttime = castTime
+7 -7
View File
@@ -23,7 +23,7 @@ pfUI:RegisterModule("buff", function ()
CreateBackdropShadow(buff) CreateBackdropShadow(buff)
end end
local name, icon, count, dispelType, _, expirationTime, _, _, _, spellId = C_UnitAuras.UnitAura("player", buff.id, buff.btype) local aura = C_UnitAuras.GetAuraDataByIndex("player", buff.id, buff.btype)
--detect weapon buffs --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 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
@@ -55,16 +55,16 @@ pfUI:RegisterModule("buff", function ()
buff.texture:SetTexture(GetInventoryItemTexture("player", 17)) buff.texture:SetTexture(GetInventoryItemTexture("player", 17))
buff.backdrop:SetBackdropBorderColor(GetItemQualityColor(GetInventoryItemQuality("player", 17) or 1)) buff.backdrop:SetBackdropBorderColor(GetItemQualityColor(GetInventoryItemQuality("player", 17) or 1))
end end
elseif name and (( buff.btype == "HARMFUL" and C.buffs.debuffs == "1" ) or ( buff.btype == "HELPFUL" and C.buffs.buffs == "1" )) then elseif aura and (( buff.btype == "HARMFUL" and C.buffs.debuffs == "1" ) or ( buff.btype == "HELPFUL" and C.buffs.buffs == "1" )) then
-- Set Buff Texture and Border -- Set Buff Texture and Border
buff.mode = buff.btype buff.mode = buff.btype
buff.expirationTime = expirationTime buff.expirationTime = aura.expirationTime
buff.stackCount = count buff.stackCount = aura.applications
buff.spellId = spellId buff.spellId = aura.spellId
buff.texture:SetTexture(icon) buff.texture:SetTexture(aura.icon)
if buff.btype == "HARMFUL" then if buff.btype == "HARMFUL" then
local dispelColor = C_UnitAuras.GetAuraDispelTypeColor(dispelType) local dispelColor = C_UnitAuras.GetAuraDispelTypeColor(aura.dispelName)
buff.backdrop:SetBackdropBorderColor(dispelColor:GetRGBA()) buff.backdrop:SetBackdropBorderColor(dispelColor:GetRGBA())
else else
buff.backdrop:SetBackdropBorderColor(br,bg,bb,ba) buff.backdrop:SetBackdropBorderColor(br,bg,bb,ba)
+11 -10
View File
@@ -75,10 +75,10 @@ pfUI:RegisterModule("buffwatch", function ()
local function GetBuffData(unit, id, type, selfdebuff) local function GetBuffData(unit, id, type, selfdebuff)
local filter = (selfdebuff and type == "HARMFUL") and "HARMFUL|PLAYER" or type local filter = (selfdebuff and type == "HARMFUL") and "HARMFUL|PLAYER" or type
local name, icon, count, dispelType, _, expirationTime = C_UnitAuras.UnitAura(unit, id, filter) local aura = C_UnitAuras.GetAuraDataByIndex(unit, id, filter)
if not name then return end if not aura then return end
local remaining = expirationTime > 0 and (expirationTime - GetTime()) or 0 local remaining = aura.expirationTime > 0 and (aura.expirationTime - GetTime()) or 0
return remaining, icon, name, count, dispelType return remaining, aura.icon, aura.name, aura.applications
end end
local function StatusBarOnClick() local function StatusBarOnClick()
@@ -236,7 +236,7 @@ pfUI:RegisterModule("buffwatch", function ()
local selfdebuff = frame.config.selfdebuff == "1" local selfdebuff = frame.config.selfdebuff == "1"
for i=1,32 do for i=1,32 do
local timeleft, texture, name, stacks, dtype = GetBuffData(frame.unit, i, frame.type, selfdebuff) local timeleft, texture, name, stacks = GetBuffData(frame.unit, i, frame.type, selfdebuff)
timeleft = timeleft or 0 timeleft = timeleft or 0
if texture and name and name ~= "" and BuffIsVisible(frame.config, name) then if texture and name and name ~= "" and BuffIsVisible(frame.config, name) then
@@ -245,14 +245,12 @@ pfUI:RegisterModule("buffwatch", function ()
frame.buffs[i][3] = name frame.buffs[i][3] = name
frame.buffs[i][4] = texture frame.buffs[i][4] = texture
frame.buffs[i][5] = stacks frame.buffs[i][5] = stacks
frame.buffs[i][6] = dtype
else else
frame.buffs[i][1] = 0 frame.buffs[i][1] = 0
frame.buffs[i][2] = nil frame.buffs[i][2] = nil
frame.buffs[i][3] = nil frame.buffs[i][3] = nil
frame.buffs[i][4] = nil frame.buffs[i][4] = nil
frame.buffs[i][5] = 0 frame.buffs[i][5] = 0
frame.buffs[i][6] = nil
end end
end end
@@ -296,9 +294,12 @@ pfUI:RegisterModule("buffwatch", function ()
-- calculate dynamic auto color -- calculate dynamic auto color
local r, g, b local r, g, b
if frame.type == "HARMFUL" then if frame.type == "HARMFUL" then
-- official Blizzard dispel-type colors (matches unitframes/buff); r, g, b = 1, .2, .2
-- nil/unknown type falls back to the DEBUFF_TYPE_NONE colour local a = C_UnitAuras.GetDebuffDataByIndex(frame.unit, data[2])
r, g, b = C_UnitAuras.GetAuraDispelTypeColor(data[6] or ""):GetRGBA() local dtype = a and a.dispelName
if dtype and DebuffTypeColor[dtype] then
r,g,b = DebuffTypeColor[dtype].r,DebuffTypeColor[dtype].g,DebuffTypeColor[dtype].b
end
else else
r,g,b = str2rgb(data[3]) r,g,b = str2rgb(data[3])
end end
+3 -58
View File
@@ -296,54 +296,6 @@ pfUI:RegisterModule("chat", function ()
end end
end end
-- [ Chat Background Alpha ]
-- Blizzard's per-window transparency slider drives FCF_SetWindowAlpha, which only
-- touches the ChatFrame*Background textures that pfUI hides on docked frames. Mirror
-- the window's stored alpha onto the visible pfUI backdrop so the native slider
-- controls it directly. The slider is authoritative for the panel's alpha.
local function ApplyChatBackgroundAlpha(panel, frame)
if not (panel and panel.backdrop and frame) then return end
local _, _, _, _, _, alpha = GetChatWindowInfo(frame:GetID())
alpha = tonumber(alpha)
if not alpha then return end
local r, g, b = panel.backdrop:GetBackdropColor()
panel.backdrop:SetBackdropColor(r, g, b, alpha)
end
function pfUI.chat:RefreshBackgroundAlpha()
-- left panel follows the currently selected docked tab
local selected = SELECTED_CHAT_FRAME
if not (selected and selected:GetParent() == pfUI.chat.left) then
selected = ChatFrame1
end
ApplyChatBackgroundAlpha(pfUI.chat.left, selected)
if C.chat.right.enable == "1" then
ApplyChatBackgroundAlpha(pfUI.chat.right, ChatFrame3)
end
end
function pfUI.chat:MigrateBackgroundAlpha()
-- Runs once per character. The previous SetupPositions default stored a hard 0 window
-- alpha, which now renders the pfUI backdrop fully transparent. Restore the historical
-- 0.8 look for any pfUI-managed window still sitting at 0. Only existing installs that
-- already ran chat setup carry that legacy 0; fresh installs get 0.8 from SetupPositions.
if pfUI_init.chatbgalpha then return end
if not pfUI_init["chat_position"] then return end
pfUI_init.chatbgalpha = true
local frames = { ChatFrame1, ChatFrame2 }
if C.chat.right.enable == "1" then table.insert(frames, ChatFrame3) end
for _, frame in ipairs(frames) do
local _, _, _, _, _, alpha = GetChatWindowInfo(frame:GetID())
alpha = tonumber(alpha)
if alpha and alpha <= 0 then
FCF_SetWindowAlpha(frame, 0.8)
end
end
end
function pfUI.chat:RefreshChat() function pfUI.chat:RefreshChat()
local panelheight = C.global.font_size*1.5 + default_border*2 + 2 local panelheight = C.global.font_size*1.5 + default_border*2 + 2
@@ -495,13 +447,9 @@ pfUI:RegisterModule("chat", function ()
for index, value in pairs(DOCKED_CHAT_FRAMES) do for index, value in pairs(DOCKED_CHAT_FRAMES) do
FCF_UpdateButtonSide(value) FCF_UpdateButtonSide(value)
end end
pfUI.chat:RefreshBackgroundAlpha()
end end
hooksecurefunc("FCF_SaveDock", pfUI.chat.RefreshChat) hooksecurefunc("FCF_SaveDock", pfUI.chat.RefreshChat)
hooksecurefunc("FCF_SetWindowAlpha", function() pfUI.chat:RefreshBackgroundAlpha() end)
hooksecurefunc("FCF_SelectDockFrame", function() pfUI.chat:RefreshBackgroundAlpha() end)
if C.chat.global.tabmouse == "1" then if C.chat.global.tabmouse == "1" then
pfUI.chat.mouseovertab = CreateFrame("Frame") pfUI.chat.mouseovertab = CreateFrame("Frame")
@@ -552,7 +500,7 @@ pfUI:RegisterModule("chat", function ()
FCF_SetLocked(ChatFrame1, 1) FCF_SetLocked(ChatFrame1, 1)
FCF_SetWindowName(ChatFrame1, GENERAL) FCF_SetWindowName(ChatFrame1, GENERAL)
FCF_SetWindowColor(ChatFrame1, 0, 0, 0) FCF_SetWindowColor(ChatFrame1, 0, 0, 0)
FCF_SetWindowAlpha(ChatFrame1, 0.8) FCF_SetWindowAlpha(ChatFrame1, 0)
FCF_SetChatWindowFontSize(ChatFrame1, 12) FCF_SetChatWindowFontSize(ChatFrame1, 12)
ChatFrame1:SetUserPlaced(1) ChatFrame1:SetUserPlaced(1)
@@ -560,7 +508,7 @@ pfUI:RegisterModule("chat", function ()
FCF_SetLocked(ChatFrame2, 1) FCF_SetLocked(ChatFrame2, 1)
FCF_SetWindowName(ChatFrame2, COMBAT_LOG) FCF_SetWindowName(ChatFrame2, COMBAT_LOG)
FCF_SetWindowColor(ChatFrame2, 0, 0, 0) FCF_SetWindowColor(ChatFrame2, 0, 0, 0)
FCF_SetWindowAlpha(ChatFrame2, 0.8) FCF_SetWindowAlpha(ChatFrame2, 0)
FCF_SetChatWindowFontSize(ChatFrame2, 12) FCF_SetChatWindowFontSize(ChatFrame2, 12)
ChatFrame2:SetUserPlaced(1) ChatFrame2:SetUserPlaced(1)
@@ -570,7 +518,7 @@ pfUI:RegisterModule("chat", function ()
FCF_SetLocked(ChatFrame3, 1) FCF_SetLocked(ChatFrame3, 1)
FCF_SetWindowName(ChatFrame3, T["Loot & Spam"]) FCF_SetWindowName(ChatFrame3, T["Loot & Spam"])
FCF_SetWindowColor(ChatFrame3, 0, 0, 0) FCF_SetWindowColor(ChatFrame3, 0, 0, 0)
FCF_SetWindowAlpha(ChatFrame3, 0.8) FCF_SetWindowAlpha(ChatFrame3, 0)
FCF_SetChatWindowFontSize(ChatFrame3, 12) FCF_SetChatWindowFontSize(ChatFrame3, 12)
FCF_UnDockFrame(ChatFrame3) FCF_UnDockFrame(ChatFrame3)
FCF_SetTabPosition(ChatFrame3, 0) FCF_SetTabPosition(ChatFrame3, 0)
@@ -618,9 +566,6 @@ pfUI:RegisterModule("chat", function ()
end end
pfUI.chat:SetScript("OnEvent", function() pfUI.chat:SetScript("OnEvent", function()
-- restore legacy chat windows stuck at 0 alpha before anything reads it
pfUI.chat:MigrateBackgroundAlpha()
-- set the default chat -- set the default chat
FCF_SelectDockFrame(SELECTED_CHAT_FRAME) FCF_SelectDockFrame(SELECTED_CHAT_FRAME)
+20 -32
View File
@@ -1039,29 +1039,13 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
local mouseover = plate.cachedGuid and plate.cachedGuid == frameState.mouseoverGuid or nil local mouseover = plate.cachedGuid and plate.cachedGuid == frameState.mouseoverGuid or nil
local unitstr = target and "target" or mouseover and "mouseover" or plate.cachedGuid or nil local unitstr = target and "target" or mouseover and "mouseover" or plate.cachedGuid or nil
-- target event sometimes fires too quickly, where nameplate identifiers are not
-- yet updated. So while being inside this event, we cannot trust the unitstr.
if event == "PLAYER_TARGET_CHANGED" then unitstr = nil end
-- remove unitstr when it doesn't resolve to this plate's unit (stale istarget,
-- stale mouseover guid or a despawned unit). Must run before the cache fills
-- below -- an unrelated unitstr would poison cache.player/minion with the
-- *other* unit's answer, and the nil-gate would then never recompute it.
if unitstr and UnitName(unitstr) ~= name then unitstr = nil end
-- resolve player vs npc from plate's own unit so libunitscan can't return -- resolve player vs npc from plate's own unit so libunitscan can't return
-- a player record for an NPC sharing the same name (e.g. Chromie). Stored -- a player record for an NPC sharing the same name (e.g. Chromie). Stored
-- as true/false/nil so it doubles as the GetUnitInfo hint. -- as true/false/nil so it doubles as the GetUnitInfo hint.
if plate.cache.player == nil and unitstr then if plate.cache.player == nil and unitstr then
plate.cache.player = UnitIsPlayer(unitstr) and true or false plate.cache.player = UnitIsPlayer(unitstr) and true or false
end end
if plate.cache.minion == nil and unitstr then local class, ulevel, elite, player, guild = GetUnitInfo(name, true, plate.cache.player)
plate.cache.minion = UnitIsMinion(unitstr) and true or false
end
local class, ulevel, elite, player, guild
if not plate.cache.minion then
class, ulevel, elite, player, guild = GetUnitInfo(name, true, plate.cache.player)
end
if plate.cache.player ~= nil then player = plate.cache.player or nil end if plate.cache.player ~= nil then player = plate.cache.player or nil end
-- Use database level ONLY if current level is ?? (fixes ?? after reload, but doesn't override visible levels) -- Use database level ONLY if current level is ?? (fixes ?? after reload, but doesn't override visible levels)
@@ -1081,11 +1065,18 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
if player and unittype == "ENEMY_NPC" then unittype = "ENEMY_PLAYER" end if player and unittype == "ENEMY_NPC" then unittype = "ENEMY_PLAYER" end
if player and unittype == "FRIENDLY_NPC" then unittype = "FRIENDLY_PLAYER" end if player and unittype == "FRIENDLY_NPC" then unittype = "FRIENDLY_PLAYER" end
elite = plate.original.levelicon:IsShown() and not player and "boss" or elite elite = plate.original.levelicon:IsShown() and not player and "boss" or elite
if not class and not plate.cache.minion then plate.wait_for_scan = true end if not class then plate.wait_for_scan = true end
-- skip data updates on invisible frames -- skip data updates on invisible frames
if not visible then return end if not visible then return end
-- target event sometimes fires too quickly, where nameplate identifiers are not
-- yet updated. So while being inside this event, we cannot trust the unitstr.
if event == "PLAYER_TARGET_CHANGED" then unitstr = nil end
-- remove unitstr on unit name mismatch
if unitstr and UnitName(unitstr) ~= name then unitstr = nil end
-- always make sure to keep plate visible -- always make sure to keep plate visible
plate:Show() plate:Show()
@@ -1322,22 +1313,19 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
for i = 1, 16 do debuffDisplayBuf[i].effect = nil end for i = 1, 16 do debuffDisplayBuf[i].effect = nil end
if unitstr then if unitstr then
local filter = cfg.owndebuffs and "HARMFUL|PLAYER" or "HARMFUL" local filter = cfg.owndebuffs and "HARMFUL|PLAYER" or "HARMFUL"
local auras = C_UnitAuras.GetUnitAuras(unitstr, filter)
local now = GetTime() local now = GetTime()
-- positional UnitAura writes straight into the reusable buffer, so the for _, aura in ipairs(auras) do
-- per-plate scan allocates nothing (no per-aura table, no result array) if debuffCount >= 16 then break end
local i = 1
while debuffCount < 16 do
local aname, icon, count, dispelType, duration, expirationTime = C_UnitAuras.UnitAura(unitstr, i, filter)
if not aname then break end
debuffCount = debuffCount + 1 debuffCount = debuffCount + 1
local timeleft = (aura.expirationTime and aura.expirationTime > 0) and (aura.expirationTime - now) or nil
local b = debuffDisplayBuf[debuffCount] local b = debuffDisplayBuf[debuffCount]
b.effect = aname b.effect = aura.name
b.texture = icon b.texture = aura.icon
b.stacks = count b.stacks = aura.applications
b.dtype = dispelType b.dtype = aura.dispelName
b.duration = duration b.duration = aura.duration
b.timeleft = (expirationTime and expirationTime > 0) and (expirationTime - now) or nil b.timeleft = timeleft
i = i + 1
end end
end end
for i = 1, 16 do for i = 1, 16 do
@@ -1560,7 +1548,7 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
-- otherwise an NPC sharing a name with a known player flips wait_for_scan -- otherwise an NPC sharing a name with a known player flips wait_for_scan
-- off here, then OnDataChanged re-sets it, every frame until the mob scan -- off here, then OnDataChanged re-sets it, every frame until the mob scan
-- lands. -- lands.
if nameplate.wait_for_scan and not nameplate.cache.minion and GetUnitInfo(name, true, nameplate.cache.player) then if nameplate.wait_for_scan and GetUnitInfo(name, true, nameplate.cache.player) then
nameplate.wait_for_scan = nil nameplate.wait_for_scan = nil
update = true update = true
end end
+90 -23
View File
@@ -3,15 +3,56 @@ pfUI:RegisterModule("tracking", function ()
MINIMAP_TRACKING_FRAME:UnregisterAllEvents() MINIMAP_TRACKING_FRAME:UnregisterAllEvents()
MINIMAP_TRACKING_FRAME:Hide() MINIMAP_TRACKING_FRAME:Hide()
local function HasEntries(tbl)
for _ in pairs(tbl) do
return true
end
return nil
end
local rawborder, border = GetBorderSize() local rawborder, border = GetBorderSize()
local size = tonumber(C.appearance.minimap.tracking_size) local size = tonumber(C.appearance.minimap.tracking_size)
local pulse = C.appearance.minimap.tracking_pulse == "1" local pulse = C.appearance.minimap.tracking_pulse == "1"
-- Tracking spells come from ClassicAPI's native GetNumTrackingTypes / -- Tracking spells identified by SpellID + expected icon path.
-- GetTrackingInfo (see the DLL's docs/API.md "Tracking"). The DLL -- SpellID is the primary identifier (stable, locale-independent).
-- enumerates them straight from the spellbook by tracking-aura effect, -- Icon path is used as secondary confirmation when scanning the spellbook.
-- so there is no spell table to maintain here and server-custom trackers local knownTrackingSpells = {
-- (e.g. Turtle's Find Trees) are picked up automatically. any = {
{ id = 2481, icon = "Racial_Dwarf_FindTreasure" }, -- Find Treasure
{ id = 2580, icon = "Spell_Nature_Earthquake" }, -- Find Minerals
{ id = 2383, icon = "INV_Misc_Flower_02" }, -- Find Herbs (Rank 1)
{ id = 8387, icon = "INV_Misc_Flower_02" }, -- Find Herbs (Rank 2)
{ id = 52917, icon = "INV_TradeSkillItem_03" }, -- Find Trees (TurtleWow)
},
HUNTER = {
{ id = 1494, icon = "Ability_Tracking" }, -- Track Beasts
{ id = 19883, icon = "Spell_Holy_PrayerOfHealing" }, -- Track Humanoids
{ id = 19884, icon = "Spell_Shadow_DarkSummoning" }, -- Track Undead
{ id = 19885, icon = "Ability_Stealth" }, -- Track Hidden
{ id = 19880, icon = "Spell_Frost_SummonWaterElemental" }, -- Track Elementals
{ id = 19878, icon = "Spell_Shadow_SummonFelHunter" }, -- Track Demons
{ id = 19882, icon = "Ability_Racial_Avatar" }, -- Track Giants
{ id = 19879, icon = "INV_Misc_Head_Dragon_01" }, -- Track Dragonkin
},
PALADIN = {
{ id = 5502, icon = "Spell_Holy_SenseUndead" }, -- Sense Undead
},
WARLOCK = {
{ id = 5500, icon = "Spell_Shadow_Metamorphosis" }, -- Sense Demons
},
DRUID = {
{ id = 5225, icon = "Ability_Tracking" }, -- Track Humanoids (Cat Form only)
},
}
-- Build a flat lookup: spellId -> entry, for fast spellbook matching
local spellIdLookup = {}
for _, entries in pairs(knownTrackingSpells) do
for _, entry in ipairs(entries) do
spellIdLookup[entry.id] = entry
end
end
local state = { local state = {
texture = nil, texture = nil,
@@ -87,24 +128,50 @@ pfUI:RegisterModule("tracking", function ()
end) end)
function pfUI.tracking:RefreshSpells() function pfUI.tracking:RefreshSpells()
state.spells = {} local playerClass = UnitClassBase("player")
if not GetNumTrackingTypes then return end local isCatForm = pfUI.tracking:PlayerIsDruidInCatForm(playerClass)
-- Druid Track Humanoids (5225) only casts in Cat Form. Hide it out of -- Build set of valid SpellIDs for this class
-- form so the menu never offers a tracker that would fail to cast. local validIds = {}
local isCatForm = pfUI.tracking:PlayerIsDruidInCatForm(UnitClassBase("player")) for _, entry in ipairs(knownTrackingSpells.any) do
validIds[entry.id] = true
end
if knownTrackingSpells[playerClass] then
for _, entry in ipairs(knownTrackingSpells[playerClass]) do
validIds[entry.id] = true
end
end
for i = 1, GetNumTrackingTypes() do -- Druids only get Track Humanoids in Cat Form
local name, texture, _, _, spellId = GetTrackingInfo(i) if playerClass == "DRUID" and not isCatForm then
local castable = not (spellId == 5225 and not isCatForm) validIds[5225] = nil
if name and texture and castable end
and not pfUI.tracking.invalidSpells[name] then
state.spells[i] = { -- Scan spellbook: match by icon path, confirm SpellID is valid for this class
index = i, -- tracking index, passed to SetTracking() for tabIndex = 1, GetNumSpellTabs() do
name = name, local _, _, offset, numSpells = GetSpellTabInfo(tabIndex)
texture = texture, for spellIndex = offset + 1, offset + numSpells do
spellId = spellId, local spellTexture = GetSpellTexture(spellIndex, BOOKTYPE_SPELL)
} local spellName = GetSpellName(spellIndex, BOOKTYPE_SPELL)
if pfUI.tracking.invalidSpells[spellName] then
spellTexture = nil
end
if spellTexture then
local lowerTexture = string.lower(spellTexture)
for spellId, entry in pairs(spellIdLookup) do
if validIds[spellId] and not state.spells[spellId]
and strfind(lowerTexture, string.lower(entry.icon)) then
state.spells[spellId] = {
index = spellIndex,
name = spellName,
texture = spellTexture,
spellId = spellId,
}
end
end
end
end end
end end
end end
@@ -120,7 +187,7 @@ pfUI:RegisterModule("tracking", function ()
elseif not texture then elseif not texture then
state.texture = nil state.texture = nil
if pulse and next(state.spells) ~= nil then if pulse and HasEntries(state.spells) then
pfUI.tracking.pulse = true pfUI.tracking.pulse = true
pfUI.tracking.icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark") pfUI.tracking.icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark")
pfUI.tracking.icon:SetVertexColor(1,1,1,1) pfUI.tracking.icon:SetVertexColor(1,1,1,1)
@@ -150,7 +217,7 @@ pfUI:RegisterModule("tracking", function ()
checked = spell.texture == state.texture, checked = spell.texture == state.texture,
arg1 = spell, arg1 = spell,
func = function (arg1) func = function (arg1)
SetTracking(arg1.index) CastSpell(arg1.index, BOOKTYPE_SPELL)
CloseDropDownMenus() CloseDropDownMenus()
end end
}) })
+10 -9
View File
@@ -23,7 +23,7 @@ do
-- ClassicAPI dependency check. -- ClassicAPI dependency check.
-- pfUI relies pervasively on the modern C_* / SuperWoW / nameplate / focus -- pfUI relies pervasively on the modern C_* / SuperWoW / nameplate / focus
-- API surface that ClassicAPI polyfills, so presence is required. -- API surface that ClassicAPI polyfills, so presence is required.
local PFUI_CLASSIC_API_MIN = 11204 -- (X*10000 + Y*100 + Z) local PFUI_CLASSIC_API_MIN = 11100 -- (X*10000 + Y*100 + Z)
local PFUI_CLASSIC_API_LATEST = PFUI_CLASSIC_API_MIN local PFUI_CLASSIC_API_LATEST = PFUI_CLASSIC_API_MIN
local PFUI_CLASSIC_API_WEBSITE = "https://github.com/brues-code/ClassicAPI" local PFUI_CLASSIC_API_WEBSITE = "https://github.com/brues-code/ClassicAPI"
local PFUI_CLASSIC_API_LATEST_URL = PFUI_CLASSIC_API_WEBSITE .. "/releases/latest" local PFUI_CLASSIC_API_LATEST_URL = PFUI_CLASSIC_API_WEBSITE .. "/releases/latest"
@@ -86,13 +86,14 @@ do
end end
end end
pfUI_playerDB = pfUI_playerDB or {} -- initialize saved variables
pfUI_config = pfUI_config or {} pfUI_playerDB = {}
pfUI_init = pfUI_init or {} pfUI_config = {}
pfUI_profiles = pfUI_profiles or {} pfUI_init = {}
pfUI_addon_profiles = pfUI_addon_profiles or {} pfUI_profiles = {}
pfUI_cache = pfUI_cache or {} pfUI_addon_profiles = {}
pfUI_throttle = pfUI_throttle or {} pfUI_cache = {}
pfUI_throttle = {}
-- localization -- localization
pfUI_locale = {} pfUI_locale = {}
@@ -576,4 +577,4 @@ function pfUI.SetupCVars()
COMBAT_TEXT_SHOW_HONOR_GAINED = "1" COMBAT_TEXT_SHOW_HONOR_GAINED = "1"
end end
UIParentLoadAddOn("Blizzard_CombatText") UIParentLoadAddOn("Blizzard_CombatText")
end end