Files
ShaguTweaks-ClassicAPI/mods/worldmap-colors.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

150 lines
4.5 KiB
Lua

local _G = ShaguTweaks.GetGlobalEnv()
local T = ShaguTweaks.T
local module = ShaguTweaks:register({
title = T["WorldMap Class Colors"],
description = T["Show class colored circles on world and battlefield map."],
expansions = { ["vanilla"] = true },
category = T["Minimap & World Map"],
enabled = true,
})
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 partyUnits = { "party1", "party2", "party3", "party4" }
local UPDATE_INTERVAL = .1
local function SetAllPointsOffset(frame, parent, offset)
frame:SetPoint("TOPLEFT", parent, "TOPLEFT", offset, -offset)
frame:SetPoint("BOTTOMRIGHT", parent, "BOTTOMRIGHT", -offset, offset)
end
-- Icon names for one map ("WorldMap" or "BattlefieldMinimap"), each with the
-- unit the icon shows when the map does not say otherwise.
local function BuildButtons(prefix)
local buttons = {}
for i = 1, 4 do
table.insert(buttons, { prefix .. "Party" .. i, "party" .. i })
end
for i = 1, 40 do
table.insert(buttons, { prefix .. "Raid" .. i, "raid" .. i })
end
return buttons
end
-- In a raid the map packs its raid icons: WorldMapRaid1 is the first raid
-- member who is on this map (yourself excluded), not raid1. The map stores
-- the real unit on the icon as .unit, and battleground team members outside
-- the raid only as .name. Using raidN for WorldMapRaidN showed other players'
-- class colors.
local function ResolveIcon(frame, fallback)
if frame.name then
return nil, frame.name
elseif frame.unit and UnitExists(frame.unit) then
return frame.unit, UnitName(frame.unit)
elseif not frame.unit and UnitExists(fallback) then
return fallback, UnitName(fallback)
end
end
local function UpdateIcon(frame, fallback)
local unit, name = ResolveIcon(frame, fallback)
if not unit and not name then return end
local icon = _G[frame:GetName() .. "Icon"]
if icon then icon:SetTexture() end
-- create icon if not yet existing
if not frame.texture then
frame.texture = frame:CreateTexture(nil, "OVERLAY")
SetAllPointsOffset(frame.texture, frame, 12)
end
-- check if unit is in same party group
local ingroup
for i = 1, 4 do
if UnitName(partyUnits[i]) == name then
ingroup = true
break
end
end
-- update texture according to raid/group state
if ingroup and frame.texture.ingroup ~= "PARTY" then
frame.texture:SetTexture(addonpath .. "\\img\\circleparty")
frame.texture.ingroup = "PARTY"
elseif not ingroup and frame.texture.ingroup ~= "RAID" then
frame.texture:SetTexture(addonpath .. "\\img\\circleraid")
frame.texture.ingroup = "RAID"
end
-- detect unit class and set color
local class
if unit then
local _, token = UnitClass(unit)
class = token
elseif name and ShaguTweaks.GetUnitData then
class = ShaguTweaks.GetUnitData(name)
end
local color = class and RAID_CLASS_COLORS[class]
if color then
frame.texture:SetVertexColor(color.r, color.g, color.b)
else
frame.texture:SetVertexColor(.5,1,.5)
end
end
local function UpdateMapColors()
this.elapsed = (this.elapsed or UPDATE_INTERVAL) + arg1
if this.elapsed < UPDATE_INTERVAL then return end
this.elapsed = 0
local buttons = this.buttons
for i = 1, table.getn(buttons) do
local frame = _G[buttons[i][1]]
if frame and frame:IsVisible() then
UpdateIcon(frame, buttons[i][2])
end
end
end
-- The updater is a child of its map, so its OnUpdate only runs while that
-- map is shown. This module used to run a frame-wide updater all the time.
local updaters = {}
local function AttachUpdater(map, prefix)
if not map or updaters[prefix] then return end
local updater = CreateFrame("Frame", nil, map)
updater.buttons = BuildButtons(prefix)
updater:SetScript("OnUpdate", UpdateMapColors)
updaters[prefix] = updater
end
module.enable = function(self)
AttachUpdater(_G.WorldMapFrame, "WorldMap")
-- The battlefield minimap is a load-on-demand Blizzard addon.
if _G.BattlefieldMinimap then
AttachUpdater(_G.BattlefieldMinimap, "BattlefieldMinimap")
else
local loader = CreateFrame("Frame")
loader:RegisterEvent("ADDON_LOADED")
loader:SetScript("OnEvent", function()
if _G.BattlefieldMinimap then
AttachUpdater(_G.BattlefieldMinimap, "BattlefieldMinimap")
this:UnregisterEvent("ADDON_LOADED")
end
end)
end
end