Files
ShaguTweaks-ClassicAPI/mods/item-colors.lua
T

572 lines
19 KiB
Lua

local _G = ShaguTweaks.GetGlobalEnv()
local T = ShaguTweaks.T
local API = ShaguTweaks.API
local AddBorder = ShaguTweaks.AddBorder
local HookScript = ShaguTweaks.HookScript
local hooksecurefunc = ShaguTweaks.hooksecurefunc
local HookAddonOrVariable = ShaguTweaks.HookAddonOrVariable
local borderModule = ShaguTweaks:register({
title = T["Item Rarity Borders"],
description = T["Show item rarity as colored borders on bags, bank, character, inspect, merchant, quest, mail, trade, craft, tradeskill and loot frames."],
expansions = { ["vanilla"] = true },
category = T["Tooltip & Items"],
enabled = true,
})
local glowModule = ShaguTweaks:register({
title = T["Item Rarity Glows"],
description = T["Show a rarity-colored glow on item buttons independently from Item Rarity Borders."],
expansions = { ["vanilla"] = true },
category = T["Tooltip & Items"],
enabled = nil,
})
local Engine = ShaguTweaks.ItemRarityEngine or {}
ShaguTweaks.ItemRarityEngine = Engine
Engine.borders = Engine.borders or false
Engine.glows = Engine.glows or false
Engine.installed = Engine.installed or false
Engine.glowTextures = Engine.glowTextures or {}
local paperdollSlots = {
[0] = "AmmoSlot", "HeadSlot", "NeckSlot", "ShoulderSlot", "ShirtSlot",
"ChestSlot", "WaistSlot", "LegsSlot", "FeetSlot", "WristSlot", "HandsSlot",
"Finger0Slot", "Finger1Slot", "Trinket0Slot", "Trinket1Slot", "BackSlot",
"MainHandSlot", "SecondaryHandSlot", "RangedSlot", "TabardSlot",
}
local inspectSlots = {
"HeadSlot", "NeckSlot", "ShoulderSlot", "ShirtSlot", "ChestSlot",
"WaistSlot", "LegsSlot", "FeetSlot", "WristSlot", "HandsSlot",
"Finger0Slot", "Finger1Slot", "Trinket0Slot", "Trinket1Slot", "BackSlot",
"MainHandSlot", "SecondaryHandSlot", "RangedSlot", "TabardSlot",
}
local function Quality(itemID)
if not itemID then return end
return API.GetItemQualityByID(itemID)
end
local function IsQuestItem(itemID)
if not itemID then return false end
local _, _, _, _, _, itemType = API.GetItemInfo(itemID)
return itemType == "Quest"
end
local function NormalizeInsets(inset)
if type(inset) == "table" then
local top, right, bottom, left = unpack(inset)
return left and -left or 0, top or 0, right or 0, bottom and -bottom or 0
end
inset = inset or 0
return -inset, inset, inset, -inset
end
local function EnsureGlow(button, inset)
if not button then return end
if button.ShaguTweaks_itemRarityGlow then return button.ShaguTweaks_itemRarityGlow end
local left, top, right, bottom = NormalizeInsets(inset or 14)
local texture = button:CreateTexture(nil, "OVERLAY")
texture:SetTexture("Interface\\Buttons\\UI-ActionButton-Border")
texture:SetBlendMode("ADD")
texture:SetPoint("TOPLEFT", button, "TOPLEFT", left, top)
texture:SetPoint("BOTTOMRIGHT", button, "BOTTOMRIGHT", right, bottom)
texture:Hide()
button.ShaguTweaks_itemRarityGlow = texture
table.insert(Engine.glowTextures, texture)
return texture
end
local function SetVisualColor(button, itemID, opts)
if not button then return end
opts = opts or {}
local default = opts.default or { r = .5, g = .5, b = .46 }
local quality = Quality(itemID)
local quest = opts.quest ~= false and IsQuestItem(itemID)
local r, g, b
local hasRarity = false
if quest then
r, g, b = 1, 1, 0
hasRarity = true
elseif quality ~= nil and quality >= (opts.borderMin or 0) then
r, g, b = GetItemQualityColor(quality)
hasRarity = true
end
if Engine.borders then
local border = AddBorder(button, opts.borderInset or 3, default)
if border then
if hasRarity then
border:SetBackdropBorderColor(r, g, b, 1)
else
border:SetBackdropBorderColor(default.r, default.g, default.b, opts.defaultAlpha or 1)
end
end
end
if Engine.glows then
local glow = EnsureGlow(button, opts.glowInset or 14)
if glow then
local glowRarity = quest or (quality ~= nil and quality >= (opts.glowMin or 2))
if glowRarity then
if not quest then r, g, b = GetItemQualityColor(quality) end
glow:SetVertexColor(r, g, b, .7)
glow:Show()
else
glow:Hide()
end
end
end
end
local function ReanchorToIcon(button, icon, borderInset, glowInset)
if not button or not icon then return end
if Engine.borders and button.ShaguTweaks_border then
local border = button.ShaguTweaks_border
border:ClearAllPoints()
border:SetPoint("TOPLEFT", icon, "TOPLEFT", -(borderInset or 2), borderInset or 2)
border:SetPoint("BOTTOMRIGHT", icon, "BOTTOMRIGHT", borderInset or 2, -(borderInset or 2))
end
if Engine.glows and button.ShaguTweaks_itemRarityGlow then
local glow = button.ShaguTweaks_itemRarityGlow
glow:ClearAllPoints()
glow:SetPoint("TOPLEFT", icon, "TOPLEFT", -(glowInset or 14), glowInset or 14)
glow:SetPoint("BOTTOMRIGHT", icon, "BOTTOMRIGHT", glowInset or 14, -(glowInset or 14))
end
end
local function ApplyIconButton(button, icon, itemID, opts)
SetVisualColor(button, itemID, opts)
ReanchorToIcon(button, icon, opts and opts.borderInset or 2, opts and opts.glowInset or 14)
end
function Engine:Install()
if self.installed then return end
self.installed = true
local function RefreshPaperdoll()
for slotID, slotName in pairs(paperdollSlots) do
local button = _G["Character" .. slotName]
if button then
SetVisualColor(button, API.GetInventoryItemID("player", slotID), {
default = { r = .5, g = .5, b = .5 },
borderMin = 0,
glowMin = 2,
})
end
end
end
local function RefreshInspect()
for slotID, slotName in pairs(inspectSlots) do
local button = _G["Inspect" .. slotName]
if button then
SetVisualColor(button, API.GetInventoryItemID("target", slotID), {
default = { r = .5, g = .5, b = .5 },
borderMin = 0,
glowMin = 2,
})
end
end
end
local function RefreshBags(bagID)
bagID = bagID and tonumber(bagID) or nil
for frameIndex = 1, 12 do
local frame = _G["ContainerFrame" .. frameIndex]
if frame and frame:IsShown() then
local id = frame:GetID()
if not bagID or id == bagID then
local name = frame:GetName()
for slot = 1, MAX_CONTAINER_ITEMS do
local button = _G[name .. "Item" .. slot]
if button and button:IsShown() then
SetVisualColor(button, API.GetContainerItemID(id, button:GetID()), {
borderMin = 0,
glowMin = 2,
})
end
end
end
end
end
for bag = 0, 3 do
local button = _G["CharacterBag" .. bag .. "Slot"]
if button then
local inventorySlot = bag + 20
SetVisualColor(button, API.GetInventoryItemID("player", inventorySlot), {
borderMin = 0,
glowMin = 2,
})
end
end
end
local function RefreshBank()
for slot = 1, 28 do
local button = _G["BankFrameItem" .. slot]
if button then
SetVisualColor(button, API.GetContainerItemID(-1, slot), {
borderMin = 2,
glowMin = 2,
})
end
end
end
local function RefreshWeapons()
local mainID = API.GetInventoryItemID("player", TempEnchant1:GetID())
local offID = API.GetInventoryItemID("player", TempEnchant2:GetID())
SetVisualColor(TempEnchant1, mainID, {
default = { r = .2, g = .2, b = .2 },
borderMin = 0, glowMin = 2,
})
SetVisualColor(TempEnchant2, offID, {
default = { r = .2, g = .2, b = .2 },
borderMin = 0, glowMin = 2,
})
if TempEnchant1Border then TempEnchant1Border:SetAlpha(0) end
if TempEnchant2Border then TempEnchant2Border:SetAlpha(0) end
end
local function RefreshMerchant()
if not MerchantFrame or not MerchantFrame:IsShown() then return end
if MerchantFrame.selectedTab == 1 then
local page = MerchantFrame.page or 1
local perPage = MERCHANT_ITEMS_PER_PAGE or 10
local startIndex = (page - 1) * perPage + 1
for i = 1, perPage do
local button = _G["MerchantItem" .. i .. "ItemButton"]
if button then
local index = startIndex + i - 1
local itemID
if index <= GetMerchantNumItems() then itemID = API.GetMerchantItemID(index) end
SetVisualColor(button, itemID, { borderMin = 2, glowMin = 2, defaultAlpha = 0 })
end
end
local buybackButton = _G["MerchantBuyBackItemItemButton"]
if buybackButton then
local count = GetNumBuybackItems()
local itemID = count > 0 and API.GetBuybackItemID(count) or nil
SetVisualColor(buybackButton, itemID, { borderMin = 2, glowMin = 2, defaultAlpha = 0 })
end
else
local perPage = MERCHANT_ITEMS_PER_PAGE or 10
for i = 1, perPage do
local button = _G["MerchantItem" .. i .. "ItemButton"]
if button then
SetVisualColor(button, API.GetBuybackItemID(i), {
borderMin = 2, glowMin = 2, defaultAlpha = 0,
})
end
end
end
end
local function QuestButtonItemID(i)
local choices = GetNumQuestChoices() or 0
local rewards = GetNumQuestRewards() or 0
if i <= choices then return API.GetQuestItemID("choice", i) end
if i <= choices + rewards then return API.GetQuestItemID("reward", i - choices) end
end
local function RefreshQuest()
for i = 1, 4 do
local itemID = QuestButtonItemID(i)
local detail = _G["QuestDetailItem" .. i]
local detailIcon = _G["QuestDetailItem" .. i .. "IconTexture"]
if detail and detailIcon then
ApplyIconButton(detail, detailIcon, itemID, {
borderInset = 2, glowInset = 14, borderMin = 2, glowMin = 2,
defaultAlpha = 0,
})
end
local reward = _G["QuestRewardItem" .. i]
local rewardIcon = _G["QuestRewardItem" .. i .. "IconTexture"]
if reward and rewardIcon then
ApplyIconButton(reward, rewardIcon, itemID, {
borderInset = 2, glowInset = 14, borderMin = 2, glowMin = 2,
defaultAlpha = 0,
})
end
end
end
local function RefreshQuestLog()
if not QuestLogFrame or not QuestLogFrame:IsShown() then return end
for i = 1, 4 do
local itemID = API.GetQuestLogItemID("reward", i)
or API.GetQuestLogItemID("choice", i)
local button = _G["QuestLogItem" .. i]
local icon = _G["QuestLogItem" .. i .. "IconTexture"]
if button and icon then
ApplyIconButton(button, icon, itemID, {
borderInset = 2, glowInset = 14, borderMin = 2, glowMin = 2,
defaultAlpha = 0,
})
end
end
end
local function RefreshMail()
-- The inbox list uses seven 37x37 MailItemXButton frames. Their native
-- slot texture is colored only by read/unread state, so apply our rarity
-- border/glow directly to those item buttons after Blizzard refreshes them.
if InboxFrame then
local page = InboxFrame.pageNum or 1
local numItems = GetInboxNumItems() or 0
for row = 1, 7 do
local button = _G["MailItem" .. row .. "Button"]
local icon = _G["MailItem" .. row .. "ButtonIcon"]
local absolute = (page - 1) * 7 + row
local itemID
if absolute <= numItems then
itemID = API.GetInboxItemID(absolute)
end
if button and icon then
ApplyIconButton(button, icon, itemID, {
borderInset = 2,
glowInset = 12,
borderMin = 2,
glowMin = 2,
defaultAlpha = 0,
})
end
end
end
-- Keep the opened-mail attachment colored as well.
local openButton = _G.OpenMailPackageButton
if openButton then
local mailID = InboxFrame and InboxFrame.openMailID
local itemID = mailID and mailID > 0 and API.GetInboxItemID(mailID) or nil
SetVisualColor(openButton, itemID, {
borderMin = 2, glowMin = 2, defaultAlpha = 0,
})
end
end
local function RefreshTrade()
for i = 1, 7 do
local player = _G["TradePlayerItem" .. i .. "ItemButton"]
local target = _G["TradeRecipientItem" .. i .. "ItemButton"]
if player then
SetVisualColor(player, API.GetTradePlayerItemID(i), {
borderMin = 2, glowMin = 2, defaultAlpha = 0,
})
end
if target then
SetVisualColor(target, API.GetTradeTargetItemID(i), {
borderMin = 2, glowMin = 2, defaultAlpha = 0,
})
end
end
end
local function RefreshTradeSkill()
if not TradeSkillFrame or not TradeSkillFrame:IsShown() then return end
local id = TradeSkillFrame.selectedSkill
if not id then return end
local output = _G.TradeSkillSkillIcon
if output then
SetVisualColor(output, API.GetTradeSkillItemID(id), {
borderMin = 2, glowMin = 2, defaultAlpha = 0,
})
end
local num = GetTradeSkillNumReagents(id) or 0
for i = 1, num do
local button = _G["TradeSkillReagent" .. i]
local icon = _G["TradeSkillReagent" .. i .. "IconTexture"]
if button and icon then
ApplyIconButton(button, icon, API.GetTradeSkillReagentItemID(id, i), {
borderInset = 2, glowInset = 12, borderMin = 2, glowMin = 2,
defaultAlpha = 0,
})
end
end
end
local function RefreshCraft()
if not CraftFrame or not CraftFrame:IsShown() then return end
local id = GetCraftSelectionIndex()
if not id or id <= 0 then return end
local output = _G.CraftIcon
if output then
local name = GetCraftInfo(id)
local link = name and ShaguTweaks.GetItemLinkByName and ShaguTweaks.GetItemLinkByName(name)
local itemID = link and API.GetItemIDFromLink(link)
SetVisualColor(output, itemID, {
borderMin = 2, glowMin = 2, defaultAlpha = 0,
})
end
local num = GetCraftNumReagents(id) or 0
for i = 1, num do
local button = _G["CraftReagent" .. i]
local icon = _G["CraftReagent" .. i .. "IconTexture"]
if button and icon then
ApplyIconButton(button, icon, API.GetCraftReagentItemID(id, i), {
borderInset = 2, glowInset = 12, borderMin = 2, glowMin = 2,
defaultAlpha = 0,
})
end
end
end
local function RefreshLoot()
if not LootFrame or not LootFrame:IsShown() then return end
local numLoot = GetNumLootItems() or 0
local page = LootFrame.page or 1
local perPage = numLoot <= 4 and 4 or 3
local startSlot = (page - 1) * perPage + 1
for buttonIndex = 1, 4 do
local button = _G["LootButton" .. buttonIndex]
local slot = startSlot + buttonIndex - 1
local itemID = slot <= numLoot and API.GetLootSlotItemID(slot) or nil
if button then
SetVisualColor(button, itemID, {
borderMin = 1, glowMin = 2, defaultAlpha = 0,
})
end
end
end
self.RefreshAll = function()
RefreshPaperdoll()
RefreshWeapons()
RefreshBags()
if BankFrame and BankFrame:IsShown() then RefreshBank() end
if MerchantFrame and MerchantFrame:IsShown() then RefreshMerchant() end
if QuestFrame and QuestFrame:IsShown() then RefreshQuest() end
if QuestLogFrame and QuestLogFrame:IsShown() then RefreshQuestLog() end
if TradeFrame and TradeFrame:IsShown() then RefreshTrade() end
if LootFrame and LootFrame:IsShown() then RefreshLoot() end
if TradeSkillFrame and TradeSkillFrame:IsShown() then RefreshTradeSkill() end
if CraftFrame and CraftFrame:IsShown() then RefreshCraft() end
end
local paperdoll = CreateFrame("Frame", nil, CharacterFrame)
paperdoll:RegisterEvent("UNIT_INVENTORY_CHANGED")
paperdoll:SetScript("OnEvent", function()
if not arg1 or arg1 == "player" then
RefreshPaperdoll()
RefreshWeapons()
end
end)
paperdoll:SetScript("OnShow", RefreshPaperdoll)
HookAddonOrVariable("Blizzard_InspectUI", function()
hooksecurefunc("InspectPaperDollItemSlotButton_Update", RefreshInspect)
if InspectFrame then HookScript(InspectFrame, "OnShow", RefreshInspect) end
end)
local bags = CreateFrame("Frame", nil, ContainerFrame1)
bags:RegisterEvent("BAG_UPDATE")
bags:SetScript("OnEvent", function() RefreshBags(arg1) end)
hooksecurefunc("ContainerFrame_OnShow", function()
RefreshBags(this and this:GetID())
end)
local bank = CreateFrame("Frame", nil, BankFrame)
bank:RegisterEvent("PLAYERBANKSLOTS_CHANGED")
bank:SetScript("OnEvent", RefreshBank)
bank:SetScript("OnShow", RefreshBank)
local merchant = CreateFrame("Frame", nil, MerchantFrame)
merchant:RegisterEvent("MERCHANT_SHOW")
merchant:RegisterEvent("MERCHANT_UPDATE")
merchant:SetScript("OnEvent", RefreshMerchant)
hooksecurefunc("MerchantFrame_Update", RefreshMerchant)
local quest = CreateFrame("Frame", nil, QuestFrame)
quest:RegisterEvent("QUEST_DETAIL")
quest:RegisterEvent("QUEST_COMPLETE")
quest:SetScript("OnEvent", function() ShaguTweaks.QueueFunction(RefreshQuest) end)
quest:SetScript("OnShow", function() ShaguTweaks.QueueFunction(RefreshQuest) end)
if QuestLogFrame then
HookScript(QuestLogFrame, "OnShow", function() ShaguTweaks.QueueFunction(RefreshQuestLog) end)
end
hooksecurefunc("QuestLog_Update", RefreshQuestLog)
if MailFrame then
HookScript(MailFrame, "OnShow", function()
ShaguTweaks.QueueFunction(RefreshMail)
end)
end
-- These native refresh functions already run for inbox changes, page changes
-- and opening a message. Post-hook them so rarity state always wins after
-- Blizzard has updated the reused mail buttons.
hooksecurefunc("InboxFrame_Update", RefreshMail)
hooksecurefunc("OpenMail_Update", RefreshMail)
local trade = CreateFrame("Frame", nil, TradeFrame)
trade:RegisterEvent("TRADE_SHOW")
trade:RegisterEvent("TRADE_PLAYER_ITEM_CHANGED")
trade:RegisterEvent("TRADE_TARGET_ITEM_CHANGED")
trade:SetScript("OnEvent", RefreshTrade)
HookAddonOrVariable("Blizzard_TradeSkillUI", function()
hooksecurefunc("TradeSkillFrame_Update", RefreshTradeSkill)
if TradeSkillFrame then HookScript(TradeSkillFrame, "OnShow", RefreshTradeSkill) end
end)
HookAddonOrVariable("Blizzard_CraftUI", function()
hooksecurefunc("CraftFrame_Update", RefreshCraft)
if CraftFrame then HookScript(CraftFrame, "OnShow", RefreshCraft) end
end)
local loot = CreateFrame("Frame", nil, LootFrame)
loot:RegisterEvent("LOOT_OPENED")
loot:RegisterEvent("LOOT_SLOT_CLEARED")
loot:RegisterEvent("LOOT_CLOSED")
loot:SetScript("OnEvent", function()
if event == "LOOT_CLOSED" then
for i = 1, 4 do
local button = _G["LootButton" .. i]
if button then SetVisualColor(button, nil, { borderMin = 1, glowMin = 2, defaultAlpha = 0 }) end
end
else
ShaguTweaks.QueueFunction(RefreshLoot)
end
end)
hooksecurefunc("LootFrame_Update", RefreshLoot)
self:RefreshAll()
end
borderModule.enable = function(self)
Engine.borders = true
Engine:Install()
if Engine.RefreshAll then Engine:RefreshAll() end
end
glowModule.enable = function(self)
Engine.glows = true
Engine:Install()
if Engine.RefreshAll then Engine:RefreshAll() end
end