Files
pfUI/modules/marktracking.lua
T
2026-09-13 09:52:00 +00:00

316 lines
11 KiB
Lua

pfUI:RegisterNewModule("marktracking", "Mark Tracker")
pfUI:RegisterModule("marktracking", "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
-- Migrate position from old frame name (RaidMarkers -> MarkTracking)
if C.position and C.position["pfMarkerTracker"] and not C.position["pfMarkTracking"] then
C.position["pfMarkTracking"] = C.position["pfMarkerTracker"]
end
pfUI.marktracking = CreateFrame("Frame", "pfMarkTracking", UIParent)
pfUI.marktracking:SetFrameStrata("MEDIUM")
if GROW == "up" then
pfUI.marktracking:SetPoint("BOTTOM", UIParent, "CENTER", 0, 0)
else
pfUI.marktracking:SetPoint("TOP", UIParent, "CENTER", 0, 0)
end
pfUI.marktracking:SetWidth(TOTAL_ROW_WIDTH)
pfUI.marktracking:SetHeight(8 * (ROW_HEIGHT + 1) + border * 2 - 1)
pfUI.marktracking:Hide()
CreateBackdrop(pfUI.marktracking)
CreateBackdropShadow(pfUI.marktracking)
UpdateMovable(pfUI.marktracking)
pfUI.marktracking: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["pfMarkTracking"] = C.position["pfMarkTracking"] or {}
C.position["pfMarkTracking"]["anchor"] = "BOTTOMRIGHT"
C.position["pfMarkTracking"]["xpos"] = math.floor(x + 0.5)
C.position["pfMarkTracking"]["ypos"] = math.floor(y + 0.5)
end
end)
-- Create 8 marker rows
pfUI.marktracking.rows = {}
for idx = 1, 8 do
local i = markerOrder[idx]
local row = CreateFrame("Button", nil, pfUI.marktracking)
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)
local markTex = C.unitframes.blizzard_raidicons == "1" and "Interface\\TargetingFrame\\UI-RaidTargetingIcons" or pfUI.media["img:raidicons"]
row.icon:SetTexture(markTex)
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.marktracking.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.marktracking.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.ceil(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.marktracking, "BOTTOM", 0, border)
end
else
if prevRow then
row:SetPoint("TOP", prevRow, "BOTTOM", 0, -1)
else
row:SetPoint("TOP", pfUI.marktracking, "TOP", 0, -border)
end
end
row:Show()
SetRaidTargetIconTexture(row.icon, i)
prevRow = row
anyActive = true
visibleCount = visibleCount + 1
else
row:Hide()
end
else
nameCache[i] = nil
row:Hide()
end
end
if anyActive then
pfUI.marktracking:SetHeight(visibleCount * (ROW_HEIGHT + 1) + border * 2 - 1)
pfUI.marktracking:Show()
elseif not (pfUI.unlock and pfUI.unlock:IsShown()) then
pfUI.marktracking: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.marktracking.rows[i]:Hide()
end
pfUI.marktracking:SetHeight(ROW_HEIGHT + border * 2)
pfUI.marktracking: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
-- UNIT_HEALTH/UNIT_MAXHEALTH: fires on HP changes for real-time bar updates
scanner:RegisterEvent("RAID_TARGET_UPDATE")
scanner:RegisterEvent("PLAYER_ENTERING_WORLD")
scanner:RegisterEvent("UNIT_HEALTH")
scanner:RegisterEvent("UNIT_MAXHEALTH")
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)