Files
ShaguTweaks-ClassicAPI/mods/unitframes-classportrait.lua
T
OEVGandClaude Opus 5.5 4ef534fabb Fix raid colors on the world map and drop two per-frame checks
WorldMap Class Colors assumed WorldMapRaidN shows raidN. In a raid the
map packs its icons and stores the real unit on each icon, so icons
showed other players' class colors. Use the icon's unit, or its name
for battleground team members outside the raid. The updaters are now
children of the maps, so they only run while a map is open.

Class Portraits checked the target-of-target GUID on every frame. Check
when the stock handler reports a new name, and otherwise five times a
second.

Enlarged Minimap is unchanged: the stock UI re-anchors the buff frame
every frame, so its check has to as well. Documented in
Docs/PERFORMANCE.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019W3rwusM4i1CYaHQAYixJ7
2026-11-02 00:32:33 +00:00

122 lines
4.2 KiB
Lua

local _G = ShaguTweaks.GetGlobalEnv()
local T = ShaguTweaks.T
local API = ShaguTweaks.API or {}
local addonpath
local tocs = { "", "-master", "-tbc", "-wotlk" }
for _, name in pairs(tocs) do
local current = string.format("ShaguTweaks%s", name)
local _, title = GetAddOnInfo(current)
if title then
addonpath = "Interface\\AddOns\\" .. current
break
end
end
local CLASS_ICON_TCOORDS = {
["WARRIOR"] = { 0, 0.25, 0, 0.25 },
["MAGE"] = { 0.25, 0.49609375, 0, 0.25 },
["ROGUE"] = { 0.49609375, 0.7421875, 0, 0.25 },
["DRUID"] = { 0.7421875, 0.98828125, 0, 0.25 },
["HUNTER"] = { 0, 0.25, 0.25, 0.5 },
["SHAMAN"] = { 0.25, 0.49609375, 0.25, 0.5 },
["PRIEST"] = { 0.49609375, 0.7421875, 0.25, 0.5 },
["WARLOCK"] = { 0.7421875, 0.98828125, 0.25, 0.5 },
["PALADIN"] = { 0, 0.25, 0.5, 0.75 },
["DEATHKNIGHT"] = { 0.25, .5, 0.5, .75 },
}
local module = ShaguTweaks:register({
title = T["Unit Frame Class Portraits"],
description = T["Replace unitframe portraits with class icons."],
expansions = { ["vanilla"] = true },
category = T["Unit Frames"],
enabled = nil,
})
local function UpdatePortraits(frame)
if not frame or not frame.unit or not frame.portrait then return end
-- detect unit class or remove for non-player units
local _, class = UnitClass(frame.unit)
class = UnitIsPlayer(frame.unit) and class or nil
-- Reapply after Blizzard's UnitFrame_Update: that function can replace the
-- portrait texture even when the new target has the same class.
if class then
local iconCoords = CLASS_ICON_TCOORDS[class]
if iconCoords then
frame.portrait:SetTexture(addonpath .. "\\img\\UI-Classes-Circles")
frame.portrait:SetTexCoord(unpack(iconCoords))
end
else
frame.portrait:SetTexCoord(0, 1, 0, 1)
end
end
module.enable = function(self)
-- overwrite portraits set by the game
local original = UnitFrame_Update
UnitFrame_Update = function(arg1, arg2, arg3)
original(arg1, arg2, arg3)
UpdatePortraits(this)
end
-- handle portrait update events
local events = CreateFrame("Frame")
events:RegisterEvent("PLAYER_ENTERING_WORLD")
events:RegisterEvent("UNIT_PORTRAIT_UPDATE")
events:SetScript("OnEvent", function()
UpdatePortraits(PlayerFrame)
UpdatePortraits(TargetFrame)
UpdatePortraits(PartyMemberFrame1)
UpdatePortraits(PartyMemberFrame2)
UpdatePortraits(PartyMemberFrame3)
UpdatePortraits(PartyMemberFrame4)
end)
-- Turtle/Vanilla already runs TargetofTarget_OnUpdate for the target-of-target
-- frame. Reuse that existing update path instead of adding a second poller.
-- Keep a GUID cache when ClassicAPI is available so two units with the same
-- display name are still detected as different units.
local lasttotguid = nil
if type(TargetofTarget_OnUpdate) == "function" then
local originalTargetofTargetOnUpdate = TargetofTarget_OnUpdate
-- The stock handler replaces the portrait when CURRENT_TARGETTARGET (the
-- target-of-target's name) changes; reapply the class icon in that same
-- frame. A GUID check five times a second covers same-name units and
-- clients whose handler does not track that global, instead of calling
-- UnitGUID on every frame.
local totelapsed = 0
TargetofTarget_OnUpdate = function(elapsed)
local before = CURRENT_TARGETTARGET
originalTargetofTargetOnUpdate(elapsed)
totelapsed = totelapsed + (elapsed or arg1 or 0)
if CURRENT_TARGETTARGET == before and totelapsed < .2 then return end
totelapsed = 0
local guid = API.UnitGUID and API.UnitGUID("targettarget") or UnitName("targettarget")
if guid ~= lasttotguid or CURRENT_TARGETTARGET ~= before then
lasttotguid = guid
UpdatePortraits(TargetofTargetFrame)
end
end
else
-- Compatibility fallback for clients that do not expose the stock handler.
local tot = CreateFrame("Frame", nil, TargetFrame)
tot.elapsed = 0
tot:SetScript("OnUpdate", function()
this.elapsed = this.elapsed + arg1
if this.elapsed < .2 then return end
this.elapsed = 0
local guid = API.UnitGUID and API.UnitGUID("targettarget") or UnitName("targettarget")
if guid ~= lasttotguid then
lasttotguid = guid
UpdatePortraits(TargetofTargetFrame)
end
end)
end
end