local _G = ShaguTweaks.GetGlobalEnv() local T = ShaguTweaks.T local hooksecurefunc = hooksecurefunc or ShaguTweaks.hooksecurefunc local FALLBACK_CLASS_COLOR = { r = .5, g = .5, b = .5, a = 1 } local MODERN_HEALTH_TEXTURE = "Interface\\AddOns\\ShaguTweaks\\img\\BlizzardRetailBar" local module = ShaguTweaks:register({ title = T["Unit Frame Class Colors"], description = T["Adds class colors to the player, target and party unit frames."], expansions = { ["vanilla"] = true }, category = T["Unit Frames"], enabled = nil, }) local function GetUnitClassColor(unit) local _, class = UnitClass(unit) local palette = ShaguTweaks.ModernClassColors or RAID_CLASS_COLORS return palette[class] or FALLBACK_CLASS_COLOR end local function SetTextColorIfChanged(fontString, r, g, b, a) local cr, cg, cb, ca = fontString:GetTextColor() if cr ~= r or cg ~= g or cb ~= b or ca ~= a then fontString:SetTextColor(r, g, b, a) end end local function SetVertexColorIfChanged(texture, r, g, b, a) local cr, cg, cb, ca = texture:GetVertexColor() if cr ~= r or cg ~= g or cb ~= b or ca ~= a then texture:SetVertexColor(r, g, b, a) end end local function UpdatePartyColors() for id = 1, MAX_PARTY_MEMBERS do local name = _G["PartyMemberFrame" .. id .. "Name"] if name then local color = GetUnitClassColor("party" .. id) SetTextColorIfChanged(name, color.r, color.g, color.b, 1) end end end module.enable = function(self) local function UpdateTargetClassColor() if not TargetFrameNameBackground then return end local reaction = UnitReaction("target", "player") if UnitIsPlayer("target") then local color = GetUnitClassColor("target") SetVertexColorIfChanged(TargetFrameNameBackground, color.r, color.g, color.b, 1) TargetFrameNameBackground:Show() elseif reaction and reaction > 4 then TargetFrameNameBackground:Hide() else TargetFrameNameBackground:Show() end end -- Let Blizzard and other addons finish their target-faction handling first, -- then apply the class-color background instead of replacing the global. hooksecurefunc("TargetFrame_CheckFaction", UpdateTargetClassColor) local playerColor = GetUnitClassColor("player") -- add name background to player frame PlayerFrameNameBackground = PlayerFrame:CreateTexture(nil, "BACKGROUND") PlayerFrameNameBackground:SetTexture(ShaguTweaks.ModernClassColors and MODERN_HEALTH_TEXTURE or "Interface\\TargetingFrame\\UI-TargetingFrame-LevelBackground") PlayerFrameNameBackground:SetWidth(119) PlayerFrameNameBackground:SetHeight(19) PlayerFrameNameBackground:SetPoint("TOPLEFT", 106, -22) PlayerFrameNameBackground:SetVertexColor(playerColor.r, playerColor.g, playerColor.b, 1) local wait = CreateFrame("Frame") wait:RegisterEvent("PLAYER_ENTERING_WORLD") wait:SetScript("OnEvent", function() local color = GetUnitClassColor("player") SetVertexColorIfChanged(PlayerFrameNameBackground, color.r, color.g, color.b, 1) this:UnregisterAllEvents() -- make sure to keep name background above frame shadow PlayerFrameNameBackground:SetDrawLayer("BORDER") TargetFrameNameBackground:SetDrawLayer("BORDER") end) -- add font outline local font, size = PlayerFrame.name:GetFont() PlayerFrame.name:SetFont(font, size, "NONE") TargetFrame.name:SetFont(font, size, "NONE") -- Update party name colors after the stock member refresh instead of -- replacing PartyMemberFrame_UpdateMember. Cached color writes avoid doing -- extra rendering work when the class did not change. hooksecurefunc("PartyMemberFrame_UpdateMember", UpdatePartyColors) UpdateTargetClassColor() UpdatePartyColors() end -- Retail resource artwork contains its own colors, including light-blue mana. -- A white tint preserves those native colors and gradients. local MODERN_POWER_TEXTURES = { [0] = "Interface\\AddOns\\ShaguTweaks\\img\\ModernPowerMana", [1] = "Interface\\AddOns\\ShaguTweaks\\img\\ModernPowerRage", [2] = "Interface\\AddOns\\ShaguTweaks\\img\\ModernPowerFocus", [3] = "Interface\\AddOns\\ShaguTweaks\\img\\ModernPowerEnergy", } local DEFAULT_BAR_TEXTURE = "Interface\\TargetingFrame\\UI-StatusBar" local function SetupModernPowerBars() local bars = {} local function ApplyPowerStyle(bar) local state = bars[bar] if not state then return end local unit = bar.unit or state.unit if not ShaguTweaks.ModernClassColors or not UnitExists(unit) then state.background:Hide() return end -- The druid's secondary bar stays mana while the primary bar is rage/energy. local powerType = state.powerType if powerType == nil then powerType = UnitPowerType(unit) end local texture = MODERN_POWER_TEXTURES[powerType] if texture then local suffix = state.family if suffix == "Player" then suffix = "" local _, class = UnitClass(unit) -- Keep the retail energy variant for combo-point users. if powerType == 3 and (class == "ROGUE" or class == "DRUID") then suffix = "Class" end end -- Vanilla mana slots are rectangular. Retail target/small-frame fills -- have cut-out corners that leave a visible wedge inside those slots. -- Use the native rectangular mana artwork for every mana frame. if powerType == 0 then suffix = "Class" end bar:SetStatusBarTexture(texture .. suffix) state.background:Show() -- Call the method captured before our hook to avoid recursive updates. state.setColor(bar, 1, 1, 1, 1) else -- Unknown/custom power types retain their original texture and tint. bar:SetStatusBarTexture(state.originalTexture) state.background:Hide() end end local definitions = { {"PlayerFrameManaBar", "player", "Player"}, {"PlayerFrameAlternatePowerBar", "player", "Player", 0}, {"TargetFrameManaBar", "target", "Target"}, {"PetFrameManaBar", "pet", "Small"}, {"TargetofTargetManaBar", "targettarget", "Small"}, } for id = 1, MAX_PARTY_MEMBERS do table.insert(definitions, {"PartyMemberFrame" .. id .. "ManaBar", "party" .. id, "Party"}) end for _, definition in ipairs(definitions) do local bar = _G[definition[1]] if bar then local texture = bar:GetStatusBarTexture() -- Opaque black behind the native alpha edges prevents the old frame's -- colored backing from changing the retail gradient's apparent color. local background = bar:CreateTexture(nil, "BACKGROUND") background:SetAllPoints(bar) background:SetTexture(0, 0, 0) background:Hide() bars[bar] = { unit = definition[2], family = definition[3], powerType = definition[4], background = background, setColor = bar.SetStatusBarColor, originalTexture = (texture and texture:GetTexture()) or DEFAULT_BAR_TEXTURE, } hooksecurefunc(bar, "SetStatusBarColor", function() ApplyPowerStyle(bar) end) end end local function RefreshPowerBars() for bar in pairs(bars) do ApplyPowerStyle(bar) end end -- Vanilla refreshes power type here. Appending preserves the normal frame -- updates, including shapeshifts and targets with a different power type. hooksecurefunc("UnitFrame_UpdateManaType", RefreshPowerBars) RefreshPowerBars() return RefreshPowerBars end local modern = ShaguTweaks:register({ title = "Modern Class Colors", description = "Use modern class colors and retail health/power textures. Enable Unit Frame Class Colors for class-colored unit frames.", expansions = { ["vanilla"] = true }, category = T["Unit Frames"], enabled = nil, }) modern.enable = function(self) if self.installed then return end self.installed = true ShaguTweaks.ModernClassColors = { DRUID = {r=1, g=0.49, b=0.04}, HUNTER = {r=0.67, g=0.83, b=0.45}, MAGE = {r=0.25, g=0.78, b=0.92}, PALADIN = {r=0.96, g=0.55, b=0.73}, PRIEST = {r=1, g=1, b=1}, ROGUE = {r=1, g=0.96, b=0.41}, SHAMAN = {r=0, g=0.44, b=0.87}, WARLOCK = {r=0.53, g=0.53, b=0.93}, WARRIOR = {r=0.78, g=0.61, b=0.43}, } local RefreshPowerBars = SetupModernPowerBars() -- Refresh after every module has initialized, regardless of enable order. local deferred = CreateFrame("Frame") deferred:SetScript("OnUpdate", function() this:SetScript("OnUpdate", nil) this:Hide() RefreshPowerBars() for _, prefix in ipairs({"PlayerFrame", "TargetFrame", "PetFrame", "TargetofTarget", "PartyMemberFrame1", "PartyMemberFrame2", "PartyMemberFrame3", "PartyMemberFrame4"}) do local bar = _G[prefix .. "HealthBar"] if bar then bar:SetStatusBarTexture(MODERN_HEALTH_TEXTURE) end end if not PlayerFrameNameBackground then return end PlayerFrameNameBackground:SetTexture(MODERN_HEALTH_TEXTURE) if TargetFrameNameBackground then TargetFrameNameBackground:SetTexture(MODERN_HEALTH_TEXTURE) end local color = GetUnitClassColor("player") PlayerFrameNameBackground:SetVertexColor(color.r, color.g, color.b, 1) if TargetFrame_CheckFaction then TargetFrame_CheckFaction() end UpdatePartyColors() -- Big Health reads its tint from the name background. if PlayerFrameHealthBar then local r, g, b, a = PlayerFrameHealthBar:GetStatusBarColor() PlayerFrameHealthBar:SetStatusBarColor(r, g, b, a) end end) end