forked from Dusk/ShaguTweaks-ClassicAPI
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
150 lines
4.5 KiB
Lua
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
|