perf/fix: port experimental optimizations + focus rework + raidmarkers

Performance optimizations ported from experimental:
- libdebuff: replace 5s startup timer with next-frame defer
- libdebuff: use arg6 auraSlot directly (Nampower 2.29+) instead of GetDebuffSlotMap lookup
- libdebuff: invalidate slotMapCache on PLAYER_TARGET_CHANGED
- nameplates: detect Nampower 2.27.2+ for GUID-based nameplate lookup

Raidmarkers module ported from experimental:
- added raidmarkers.lua
- added config defaults and GUI section
- registered in modules.xml

focus.lua rework:
- removed CastSpellByNameNoQueue (Nampower's CastSpellByName now supports unit tokens/GUIDs natively)
- /focus Name: TargetByName with correct previous target restore + ClearTarget() fallback
- /focus Name: prefix match via SlashCmdList.TARGET when exact match fails
- suppress UI_ERROR_MESSAGE during targeting attempts

swingtimer fixes:
- non-Hunter ranged bar now fills left->right like MH/OH
- fix 1px artifact on Hunter bar center by using Hide() instead of SetWidth(0.1)
This commit is contained in:
Meow
2026-02-22 19:08:37 +01:00
parent a5db589b61
commit 236a2fce6c
7 changed files with 445 additions and 100 deletions
+17
View File
@@ -241,6 +241,23 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("unitframes", nil, "swingtimerrangedcolor",".3,.6,1,1")
pfUI:UpdateConfig("unitframes", nil, "swingtimerrangedwarncolor",".9,0,0,1")
pfUI:UpdateConfig("unitframes", nil, "swingtimerhsqueue","1")
pfUI:UpdateConfig("unitframes", nil, "raidmarkerwidth", "80")
pfUI:UpdateConfig("unitframes", nil, "raidmarkerheight", "14")
pfUI:UpdateConfig("unitframes", nil, "raidmarkergrow", "down")
pfUI:UpdateConfig("unitframes", nil, "raidmarkertexture", "Interface\\AddOns\\pfUI\\img\\bar")
pfUI:UpdateConfig("unitframes", nil, "raidmarkerfontsize","12")
pfUI:UpdateConfig("unitframes", nil, "raidmarkershowname","1")
pfUI:UpdateConfig("unitframes", nil, "raidmarkershowpct","1")
pfUI:UpdateConfig("unitframes", nil, "raidmarkershownumhp","0")
pfUI:UpdateConfig("unitframes", nil, "raidmarkershowportrait","0")
pfUI:UpdateConfig("unitframes", nil, "raidmarkercolor_star", "1,.9,0,1")
pfUI:UpdateConfig("unitframes", nil, "raidmarkercolor_circle", "1,.5,0,1")
pfUI:UpdateConfig("unitframes", nil, "raidmarkercolor_diamond", ".8,0,.8,1")
pfUI:UpdateConfig("unitframes", nil, "raidmarkercolor_triangle", "0,.8,0,1")
pfUI:UpdateConfig("unitframes", nil, "raidmarkercolor_moon", ".7,.7,.7,1")
pfUI:UpdateConfig("unitframes", nil, "raidmarkercolor_square", "0,.4,.9,1")
pfUI:UpdateConfig("unitframes", nil, "raidmarkercolor_cross", ".9,0,0,1")
pfUI:UpdateConfig("unitframes", nil, "raidmarkercolor_skull", "1,1,1,1")
pfUI:UpdateConfig("unitframes", nil, "abbrevnum", "1")
pfUI:UpdateConfig("unitframes", nil, "castbardecimals", "2")
pfUI:UpdateConfig("unitframes", nil, "abbrevname", "1")
+1
View File
@@ -18,6 +18,7 @@
<Include file="..\modules\target.lua"/>
<Include file="..\modules\combopoints.lua"/>
<Include file="..\modules\swingtimer.lua"/>
<Include file="..\modules\raidmarkers.lua"/>
<Include file="..\modules\targettarget.lua"/>
<Include file="..\modules\targettargettarget.lua"/>
<Include file="..\modules\pet.lua"/>
+84 -97
View File
@@ -44,95 +44,68 @@ if GetNampowerVersion then
end
end
-- Delayed Nampower version check (5 seconds after PLAYER_ENTERING_WORLD)
-- Nampower startup check: show version info and ensure CVars are set.
-- Runs on first OnUpdate after PLAYER_ENTERING_WORLD to give Nampower time to initialize.
local nampowerCheckFrame = CreateFrame("Frame")
local nampowerCheckTimer = 0
local nampowerCheckDone = false
nampowerCheckFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
nampowerCheckFrame:RegisterEvent("PLAYER_LOGOUT")
nampowerCheckFrame:SetScript("OnEvent", function()
-- Handle shutdown to prevent crash 132
if event == "PLAYER_LOGOUT" then
-- Defer to next frame so Nampower is fully initialized
this:SetScript("OnUpdate", function()
this:SetScript("OnUpdate", nil)
this:UnregisterAllEvents()
this:SetScript("OnEvent", nil)
this:SetScript("OnUpdate", nil)
return
end
nampowerCheckFrame:SetScript("OnUpdate", function()
nampowerCheckTimer = nampowerCheckTimer + arg1
if nampowerCheckTimer >= 5 and not nampowerCheckDone then
nampowerCheckDone = true
if GetNampowerVersion then
local major, minor, patch = GetNampowerVersion()
patch = patch or 0
local versionString = major .. "." .. minor .. "." .. patch
if major > 2 or (major == 2 and minor > 38) or (major == 2 and minor == 38 and patch >= 0) then
DEFAULT_CHAT_FRAME:AddMessage("|cff33ff99[libdebuff]|r Nampower v" .. versionString .. " detected - GetUnitField mode enabled!")
-- Enable required Nampower CVars
if SetCVar and GetCVar then
local cvarsToEnable = {
"NP_EnableSpellStartEvents",
"NP_EnableSpellGoEvents",
"NP_EnableAuraCastEvents",
"NP_EnableAutoAttackEvents"
}
local totalCvars = table.getn(cvarsToEnable)
local enabledCount = 0
local alreadyEnabledCount = 0
local failedCount = 0
for _, cvar in ipairs(cvarsToEnable) do
local success, currentValue = pcall(GetCVar, cvar)
if success and currentValue then
if currentValue == "1" then
alreadyEnabledCount = alreadyEnabledCount + 1
else
local setSuccess = pcall(SetCVar, cvar, "1")
if setSuccess then
enabledCount = enabledCount + 1
else
failedCount = failedCount + 1
end
end
if GetNampowerVersion then
local major, minor, patch = GetNampowerVersion()
patch = patch or 0
local versionString = major .. "." .. minor .. "." .. patch
if major > 2 or (major == 2 and minor > 38) or (major == 2 and minor == 38 and patch >= 0) then
DEFAULT_CHAT_FRAME:AddMessage("|cff33ff99[libdebuff]|r Nampower v" .. versionString .. " detected - GetUnitField mode enabled!")
if SetCVar and GetCVar then
local cvarsToEnable = {
"NP_EnableSpellStartEvents",
"NP_EnableSpellGoEvents",
"NP_EnableAuraCastEvents",
"NP_EnableAutoAttackEvents",
}
local enabledCount = 0
local alreadyEnabledCount = 0
local failedCount = 0
for _, cvar in ipairs(cvarsToEnable) do
local success, currentValue = pcall(GetCVar, cvar)
if success and currentValue then
if currentValue == "1" then
alreadyEnabledCount = alreadyEnabledCount + 1
else
failedCount = failedCount + 1
local setSuccess = pcall(SetCVar, cvar, "1")
if setSuccess then enabledCount = enabledCount + 1
else failedCount = failedCount + 1 end
end
end
if enabledCount > 0 then
DEFAULT_CHAT_FRAME:AddMessage("|cff33ff99[libdebuff]|r Enabled " .. enabledCount .. " Nampower CVars")
end
if alreadyEnabledCount == totalCvars then
DEFAULT_CHAT_FRAME:AddMessage("|cff33ff99[libdebuff]|r All required Nampower CVars already enabled")
elseif alreadyEnabledCount > 0 then
DEFAULT_CHAT_FRAME:AddMessage("|cff33ff99[libdebuff]|r " .. alreadyEnabledCount .. " CVars were already enabled")
end
if failedCount > 0 then
DEFAULT_CHAT_FRAME:AddMessage("|cffffcc00[libdebuff]|r Warning: Could not check/set " .. failedCount .. " CVars")
else
failedCount = failedCount + 1
end
end
elseif major == 2 and minor == 38 and patch == 0 then
DEFAULT_CHAT_FRAME:AddMessage("|cffffcc00[libdebuff] WARNING: Nampower v2.38.0 detected!|r")
DEFAULT_CHAT_FRAME:AddMessage("|cffffcc00[libdebuff] Please update to v2.38.0 or higher!|r")
StaticPopup_Show("LIBDEBUFF_NAMPOWER_UPDATE", versionString)
else
DEFAULT_CHAT_FRAME:AddMessage("|cffff0000[libdebuff] Debuff tracking disabled! Please update Nampower to v2.38.0 or higher.|r")
StaticPopup_Show("LIBDEBUFF_NAMPOWER_UPDATE", versionString)
if enabledCount > 0 then
DEFAULT_CHAT_FRAME:AddMessage("|cff33ff99[libdebuff]|r Enabled " .. enabledCount .. " Nampower CVars")
elseif alreadyEnabledCount == table.getn(cvarsToEnable) then
DEFAULT_CHAT_FRAME:AddMessage("|cff33ff99[libdebuff]|r All required Nampower CVars already enabled")
end
if failedCount > 0 then
DEFAULT_CHAT_FRAME:AddMessage("|cffffcc00[libdebuff]|r Warning: Could not check/set " .. failedCount .. " CVars")
end
end
else
DEFAULT_CHAT_FRAME:AddMessage("|cffff0000[libdebuff] Nampower not found! Debuff tracking disabled.|r")
StaticPopup_Show("LIBDEBUFF_NAMPOWER_MISSING")
DEFAULT_CHAT_FRAME:AddMessage("|cffff0000[libdebuff] Debuff tracking disabled! Please update Nampower to v2.38.0 or higher.|r")
StaticPopup_Show("LIBDEBUFF_NAMPOWER_UPDATE", versionString)
end
nampowerCheckFrame:SetScript("OnUpdate", nil)
else
DEFAULT_CHAT_FRAME:AddMessage("|cffff0000[libdebuff] Nampower not found! Debuff tracking disabled.|r")
StaticPopup_Show("LIBDEBUFF_NAMPOWER_MISSING")
end
end)
end)
@@ -1588,10 +1561,14 @@ if hasNampower then
elseif event == "DEBUFF_ADDED_OTHER" then
local guid = arg1
local displaySlot = arg2 -- This is DISPLAY slot (1-16), NOT aura slot!
local displaySlot = arg2 -- Display slot (1-16), compacted
local spellId = arg3
local stacks = arg4
local auraSlot_0based = arg6 -- Nampower 2.29+: raw slot 0-based (32-47)
-- Convert 0-based (Nampower event) to 1-based (Lua GetUnitField array)
local auraSlot = auraSlot_0based and (auraSlot_0based + 1) or nil
-- Invalidate slot map cache for this GUID
slotMapCache[guid] = nil
@@ -1608,11 +1585,13 @@ if hasNampower then
return
end
-- Find the REAL aura slot (33-48) via GetUnitField
local auraSlot = nil
local slotMap = GetDebuffSlotMap(guid)
if slotMap and slotMap[displaySlot] then
auraSlot = slotMap[displaySlot].auraSlot
-- Get auraSlot from event parameter (Nampower 2.29+)
-- Fallback to GetUnitField lookup if not available
if not auraSlot then
local slotMap = GetDebuffSlotMap(guid)
if slotMap and slotMap[displaySlot] then
auraSlot = slotMap[displaySlot].auraSlot
end
end
-- Fallback: Calculate aura slot if GetUnitField didn't work
@@ -1714,9 +1693,13 @@ if hasNampower then
elseif event == "DEBUFF_REMOVED_OTHER" then
local guid = arg1
local displaySlot = arg2 -- This is DISPLAY slot (1-16), NOT aura slot!
local displaySlot = arg2 -- Display slot (1-16), compacted
local spellId = arg3
local auraSlot_0based = arg6 -- Nampower 2.29+: raw slot 0-based (32-47)
-- Convert 0-based (Nampower event) to 1-based (Lua GetUnitField array)
local auraSlot = auraSlot_0based and (auraSlot_0based + 1) or nil
-- Invalidate slot map cache for this GUID
slotMapCache[guid] = nil
@@ -1725,8 +1708,8 @@ if hasNampower then
if debugStats.enabled then
debugStats.debuff_removed = debugStats.debuff_removed + 1
if IsCurrentTarget(guid) then
DEFAULT_CHAT_FRAME:AddMessage(string.format("%s |cffff9900[DEBUFF_REMOVED]|r display=%d %s",
GetDebugTimestamp(), displaySlot, spellName))
DEFAULT_CHAT_FRAME:AddMessage(string.format("%s |cffff9900[DEBUFF_REMOVED]|r display=%d aura=%d (0based=%d) %s",
GetDebugTimestamp(), displaySlot, auraSlot or -1, auraSlot_0based or -1, spellName))
end
end
@@ -1736,15 +1719,17 @@ if hasNampower then
return
end
-- Find the auraSlot using displaySlot mapping
-- Get auraSlot from event parameter (Nampower 2.29+)
-- Fallback to displayToAura mapping if not available
local foundAuraSlot = auraSlot
if not foundAuraSlot and displayToAura[guid] and displayToAura[guid][displaySlot] then
foundAuraSlot = displayToAura[guid][displaySlot]
end
local wasOurs = false
local removedCasterGuid = nil
local foundAuraSlot = nil
-- Use displayToAura mapping to find the correct auraSlot
if displayToAura[guid] and displayToAura[guid][displaySlot] then
foundAuraSlot = displayToAura[guid][displaySlot]
if foundAuraSlot then
-- Get ownership info for this specific slot
if slotOwnership[guid] and slotOwnership[guid][foundAuraSlot] then
local ownership = slotOwnership[guid][foundAuraSlot]
@@ -1761,7 +1746,7 @@ if hasNampower then
end
if debugStats.enabled and IsCurrentTarget(guid) then
DEFAULT_CHAT_FRAME:AddMessage(string.format("%s |cffff9900[SLOT CLEARED]|r aura=%d %s wasOurs=%s caster=%s",
DEFAULT_CHAT_FRAME:AddMessage(string.format("%s |cffff9900[SLOT CLEARED]|r aura=%d [arg6] %s wasOurs=%s caster=%s",
GetDebugTimestamp(), foundAuraSlot, spellName, tostring(wasOurs), DebugGuid(removedCasterGuid)))
end
end
@@ -1796,11 +1781,13 @@ if hasNampower then
end
elseif event == "PLAYER_TARGET_CHANGED" then
-- Nothing special needed - GetUnitField will get fresh data on next query
if not UnitExists then return end
local _, targetGuid = UnitExists("target")
if targetGuid and targetGuid ~= "" then
-- Invalidate slot map cache on retarget
-- Prevents stale slot mappings after untarget/retarget cycles
slotMapCache[targetGuid] = nil
-- Cleanup expired timers for new target
CleanupExpiredTimers(targetGuid)
end
+1 -1
View File
@@ -242,4 +242,4 @@ function SlashCmdList.PFSWAPFOCUS(msg)
pfUI.uf.focus.unitname = oldunit
end
end
end
end
+22
View File
@@ -1032,6 +1032,10 @@ pfUI:RegisterModule("gui", "vanilla:tbc", function ()
"default:" .. T["Default"],
"tukui:TukUI"
},
["raidmarker_grow"] = {
"down:Down",
"up:Up",
},
["uf_color"] = {
"0:" .. T["Class"],
"1:" .. T["Custom"],
@@ -1969,6 +1973,24 @@ pfUI:RegisterModule("gui", "vanilla:tbc", function ()
CreateConfig(nil, T["Ranged Warn Color (Hunter)"], C.unitframes, "swingtimerrangedwarncolor", "color")
CreateConfig(nil, T["Show HS/Cleave Queue Color (Warrior)"], C.unitframes, "swingtimerhsqueue", "checkbox")
CreateConfig(nil, "Raid Markers", nil, nil, "header")
CreateConfig(nil, "Raid Marker Width", C.unitframes, "raidmarkerwidth")
CreateConfig(nil, "Raid Marker Height", C.unitframes, "raidmarkerheight")
CreateConfig(nil, "Raid Marker Grow", C.unitframes, "raidmarkergrow", "dropdown", pfUI.gui.dropdowns.raidmarker_grow)
CreateConfig(nil, "Raid Marker Texture", C.unitframes, "raidmarkertexture", "dropdown", pfUI.gui.dropdowns.uf_bartexture)
CreateConfig(nil, "Raid Marker Font Size", C.unitframes, "raidmarkerfontsize")
CreateConfig(nil, "Show Name", C.unitframes, "raidmarkershowname", "checkbox")
CreateConfig(nil, "Show Percent HP", C.unitframes, "raidmarkershowpct", "checkbox")
CreateConfig(nil, "Show Portrait", C.unitframes, "raidmarkershowportrait", "checkbox")
CreateConfig(nil, "Star Color", C.unitframes, "raidmarkercolor_star", "color")
CreateConfig(nil, "Circle Color", C.unitframes, "raidmarkercolor_circle", "color")
CreateConfig(nil, "Diamond Color", C.unitframes, "raidmarkercolor_diamond", "color")
CreateConfig(nil, "Triangle Color", C.unitframes, "raidmarkercolor_triangle", "color")
CreateConfig(nil, "Moon Color", C.unitframes, "raidmarkercolor_moon", "color")
CreateConfig(nil, "Square Color", C.unitframes, "raidmarkercolor_square", "color")
CreateConfig(nil, "Cross Color", C.unitframes, "raidmarkercolor_cross", "color")
CreateConfig(nil, "Skull Color", C.unitframes, "raidmarkercolor_skull", "color")
CreateConfig(U[c], T["Font Options"], nil, nil, "header")
CreateConfig(nil, T["Unit Frame Text Font"], C.global, "font_unit", "dropdown", pfUI.gui.dropdowns.fonts)
CreateConfig(nil, T["Unit Frame Text Size"], C.global, "font_unit_size")
+15 -2
View File
@@ -2,8 +2,21 @@ pfUI:RegisterModule("nameplates", "vanilla", function ()
-- disable original castbars
pcall(SetCVar, "ShowVKeyCastbar", 0)
-- check for SuperWoW support (use SUPERWOW_VERSION global)
local superwow_active = SUPERWOW_VERSION ~= nil
-- Check for Nampower support (preferred)
local hasNampower = false
if GetNampowerVersion then
local major, minor, patch = GetNampowerVersion()
patch = patch or 0
if major > 2 or (major == 2 and minor > 27) or (major == 2 and minor == 27 and patch >= 2) then
hasNampower = true
end
end
-- Check for SuperWoW support (fallback)
local hasSuperwow = SUPERWOW_VERSION ~= nil
-- Use either Nampower or SuperWoW for GetName(1) GUID support
local superwow_active = hasNampower or hasSuperwow
-- Local function references for performance
local GetTime = GetTime
+305
View File
@@ -0,0 +1,305 @@
pfUI:RegisterModule("raidmarkers", "vanilla:tbc", function ()
-- Requires mark1-mark8 unit tokens (Turtle WoW / Nampower)
if not UnitExists("mark1") and not UnitExists("mark8") then
if not pcall(function() UnitExists("mark1") end) then return end
end
local rawborder, border = GetBorderSize()
-- Parse color strings "r,g,b,a" into components
local function ParseColor(str, dr, dg, db, da)
if not str or str == "" then return dr, dg, db, da end
local _, _, r, g, b, a = string.find(str, "([%d%.]+),([%d%.]+),([%d%.]+),([%d%.]+)")
if r then
return tonumber(r) or dr, tonumber(g) or dg, tonumber(b) or db, tonumber(a) or da
end
return dr, dg, db, da
end
local markerOrder = { 8, 7, 6, 5, 4, 3, 2, 1 } -- skull, cross, square, moon, triangle, diamond, circle, star
local markerTokens = {}
for i = 1, 8 do markerTokens[i] = "mark" .. i end
-- Default colors per marker
local defaultColors = {
[1] = { 1.0, 0.9, 0.0, 1 }, -- star: yellow
[2] = { 1.0, 0.5, 0.0, 1 }, -- circle: orange
[3] = { 0.8, 0.0, 0.8, 1 }, -- diamond: purple
[4] = { 0.0, 0.8, 0.0, 1 }, -- triangle: green
[5] = { 0.7, 0.7, 0.7, 1 }, -- moon: silver
[6] = { 0.0, 0.4, 0.9, 1 }, -- square: blue
[7] = { 0.9, 0.0, 0.0, 1 }, -- cross: red
[8] = { 1.0, 1.0, 1.0, 1 }, -- skull: white
}
local markerConfigKeys = {
[1] = "raidmarkercolor_star",
[2] = "raidmarkercolor_circle",
[3] = "raidmarkercolor_diamond",
[4] = "raidmarkercolor_triangle",
[5] = "raidmarkercolor_moon",
[6] = "raidmarkercolor_square",
[7] = "raidmarkercolor_cross",
[8] = "raidmarkercolor_skull",
}
local markerColors = {}
for i = 1, 8 do
local d = defaultColors[i]
local r, g, b, a = ParseColor(C.unitframes[markerConfigKeys[i]], d[1], d[2], d[3], d[4])
markerColors[i] = { r, g, b, a }
end
local FALLBACK_INTERVAL = 1.0 -- safety net for units that come into range after marker was set
local elapsed = 0
local isUnlocked = false
local ROW_HEIGHT = tonumber(C.unitframes.raidmarkerheight) or 14
local BAR_WIDTH = tonumber(C.unitframes.raidmarkerwidth) or 80
local GROW = C.unitframes.raidmarkergrow or "down"
local rm_texture = C.unitframes.raidmarkertexture or "Interface\\AddOns\\pfUI\\img\\bar"
local rm_fontsize = tonumber(C.unitframes.raidmarkerfontsize) or 12
local rm_showpct = C.unitframes.raidmarkershowpct ~= "0"
local rm_showname = C.unitframes.raidmarkershowname ~= "0"
local rm_showportrait = C.unitframes.raidmarkershowportrait ~= "0"
local PORTRAIT_SIZE = ROW_HEIGHT
-- Cache for shortened names: markerIndex -> { name, short }
local nameCache = {}
local function ShortenName(name, row)
if not name or name == "" then return "" end
local barWidth = row.health:GetWidth()
if barWidth < 1 then barWidth = BAR_WIDTH end
local available = barWidth - 4
if rm_showpct then available = available - 32 end
if available < 10 then return nil end
row.nametext:SetText(name)
if row.nametext:GetStringWidth() <= available then return name end
for len = strlen(name) - 1, 1, -1 do
local short = strsub(name, 1, len) .. "."
row.nametext:SetText(short)
if row.nametext:GetStringWidth() <= available then return short end
end
return strsub(name, 1, 1) .. "."
end
local TOTAL_ROW_WIDTH = BAR_WIDTH + 20 + (rm_showportrait and (PORTRAIT_SIZE + 2) or 0)
-- Container frame
pfUI.raidmarkers = CreateFrame("Frame", "pfMarkerTracker", UIParent)
pfUI.raidmarkers:SetFrameStrata("MEDIUM")
if GROW == "up" then
pfUI.raidmarkers:SetPoint("BOTTOMRIGHT", UIParent, "BOTTOMRIGHT", -20, 200)
else
pfUI.raidmarkers:SetPoint("TOPRIGHT", UIParent, "TOPRIGHT", -20, -200)
end
pfUI.raidmarkers:SetWidth(TOTAL_ROW_WIDTH)
pfUI.raidmarkers:SetHeight(8 * (ROW_HEIGHT + 1) + border * 2 - 1)
pfUI.raidmarkers:Hide()
CreateBackdrop(pfUI.raidmarkers)
CreateBackdropShadow(pfUI.raidmarkers)
UpdateMovable(pfUI.raidmarkers)
pfUI.raidmarkers:SetScript("OnMouseUp", function()
if pfUI.unlock and pfUI.unlock:IsShown() then
this:StopMovingOrSizing()
local _, _, _, x, y = this:GetPoint()
this:ClearAllPoints()
this:SetPoint("BOTTOMRIGHT", UIParent, "BOTTOMRIGHT", math.floor(x + 0.5), math.floor(y + 0.5))
C.position["pfMarkerTracker"] = C.position["pfMarkerTracker"] or {}
C.position["pfMarkerTracker"]["anchor"] = "BOTTOMRIGHT"
C.position["pfMarkerTracker"]["xpos"] = math.floor(x + 0.5)
C.position["pfMarkerTracker"]["ypos"] = math.floor(y + 0.5)
end
end)
-- Create 8 marker rows
pfUI.raidmarkers.rows = {}
for idx = 1, 8 do
local i = markerOrder[idx]
local row = CreateFrame("Button", nil, pfUI.raidmarkers)
row:SetWidth(TOTAL_ROW_WIDTH)
row:SetHeight(ROW_HEIGHT)
row:Hide()
row:RegisterForClicks("LeftButtonUp")
row:SetScript("OnClick", function()
TargetUnit(markerTokens[this.markerIndex])
end)
-- raid icon
row.icon = row:CreateTexture(nil, "ARTWORK")
row.icon:SetWidth(ROW_HEIGHT)
row.icon:SetHeight(ROW_HEIGHT)
row.icon:SetPoint("LEFT", row, "LEFT", 1, 0)
row.icon:SetTexture(pfUI.media["img:raidicons"])
SetRaidTargetIconTexture(row.icon, i)
-- portrait (right side)
row.portrait = row:CreateTexture(nil, "ARTWORK")
row.portrait:SetWidth(PORTRAIT_SIZE)
row.portrait:SetHeight(PORTRAIT_SIZE)
row.portrait:SetPoint("RIGHT", row, "RIGHT", -1, 0)
row.portrait:SetTexCoord(.1, .9, .1, .9)
if not rm_showportrait then row.portrait:Hide() end
-- health bar
row.health = CreateFrame("StatusBar", nil, row)
row.health:SetPoint("LEFT", row.icon, "RIGHT", 2, 0)
if rm_showportrait then
row.health:SetPoint("RIGHT", row.portrait, "LEFT", -2, 0)
else
row.health:SetPoint("RIGHT", row, "RIGHT", -1, 0)
end
row.health:SetHeight(ROW_HEIGHT)
row.health:SetMinMaxValues(0, 1)
row.health:SetValue(1)
row.health:SetStatusBarTexture(rm_texture)
local c = markerColors[i]
row.health:SetStatusBarColor(c[1] * 0.5, c[2] * 0.5, c[3] * 0.5, c[4] or 0.9)
-- name text (left)
row.nametext = row.health:CreateFontString(nil, "OVERLAY", "GameFontNormal")
row.nametext:SetPoint("LEFT", row.health, "LEFT", 2, 0)
row.nametext:SetFont(pfUI.font_default, rm_fontsize, "OUTLINE")
row.nametext:SetTextColor(1, 1, 1, 1)
row.nametext:SetJustifyH("LEFT")
row.nametext:SetText("")
if not rm_showname then row.nametext:Hide() end
-- hp text (right)
row.hptext = row.health:CreateFontString(nil, "OVERLAY", "GameFontNormal")
row.hptext:SetPoint("RIGHT", row.health, "RIGHT", -2, 0)
row.hptext:SetFont(pfUI.font_default, rm_fontsize, "OUTLINE")
row.hptext:SetTextColor(1, 1, 1, 1)
row.hptext:SetJustifyH("RIGHT")
row.hptext:SetText("")
if not rm_showpct then row.hptext:Hide() end
CreateBackdrop(row.health)
row.markerIndex = i
row.label = "mark" -- enables /pfcast mouseover support via GetMouseFocus()
row.id = i
pfUI.raidmarkers.rows[i] = row
end
local function UpdateDisplay()
if isUnlocked then return end
local anyActive = false
local visibleCount = 0
local prevRow
for idx = 1, 8 do
local i = markerOrder[idx]
local row = pfUI.raidmarkers.rows[i]
local token = markerTokens[i]
if UnitExists(token) and not UnitIsDead(token) then
local hp = UnitHealth(token)
local maxhp = UnitHealthMax(token)
if hp and maxhp and maxhp > 0 and hp > 0 then
local pct = hp / maxhp
row.health:SetValue(pct)
if rm_showname then
local name = UnitName(token)
local cached = nameCache[i]
if not cached or cached.name ~= name then
local short = ShortenName(name, row)
if short then
nameCache[i] = { name = name, short = short }
end
end
row.nametext:SetText(nameCache[i] and nameCache[i].short or "")
end
if rm_showpct then
row.hptext:SetText(math.floor(pct * 100) .. "%")
end
if rm_showportrait then
SetPortraitTexture(row.portrait, token)
end
row:ClearAllPoints()
if GROW == "up" then
if prevRow then
row:SetPoint("BOTTOM", prevRow, "TOP", 0, 1)
else
row:SetPoint("BOTTOM", pfUI.raidmarkers, "BOTTOM", 0, border)
end
else
if prevRow then
row:SetPoint("TOP", prevRow, "BOTTOM", 0, -1)
else
row:SetPoint("TOP", pfUI.raidmarkers, "TOP", 0, -border)
end
end
row:Show()
prevRow = row
anyActive = true
visibleCount = visibleCount + 1
else
row:Hide()
end
else
nameCache[i] = nil
row:Hide()
end
end
if anyActive then
pfUI.raidmarkers:SetHeight(visibleCount * (ROW_HEIGHT + 1) + border * 2 - 1)
pfUI.raidmarkers:Show()
elseif not (pfUI.unlock and pfUI.unlock:IsShown()) then
pfUI.raidmarkers:Hide()
end
end
-- Unlock mode: show fixed 1-row placeholder so positioning works correctly
if pfUI.unlock then
local origShow = pfUI.unlock:GetScript("OnShow")
pfUI.unlock:SetScript("OnShow", function()
if origShow then origShow() end
isUnlocked = true
-- hide all rows, show container at 1-row height as drag handle
for i = 1, 8 do
pfUI.raidmarkers.rows[i]:Hide()
end
pfUI.raidmarkers:SetHeight(ROW_HEIGHT + border * 2)
pfUI.raidmarkers:Show()
end)
local origHide = pfUI.unlock:GetScript("OnHide")
pfUI.unlock:SetScript("OnHide", function()
if origHide then origHide() end
isUnlocked = false
UpdateDisplay()
end)
end
-- Event-driven scanner frame
local scanner = CreateFrame("Frame")
-- RAID_TARGET_UPDATE: fires when a raid marker is set/cleared
-- PLAYER_ENTERING_WORLD: fires on login, reload, and zone transitions
scanner:RegisterEvent("RAID_TARGET_UPDATE")
scanner:RegisterEvent("PLAYER_ENTERING_WORLD")
scanner:SetScript("OnEvent", function()
UpdateDisplay()
end)
-- Fallback poll at 1s: catches units that come into range AFTER a marker was set
-- (no event fires for that case, so we need this safety net)
scanner:SetScript("OnUpdate", function()
elapsed = elapsed + arg1
if elapsed < FALLBACK_INTERVAL then return end
elapsed = 0
UpdateDisplay()
end)
end)