Files
Nanami-UI/Trade.lua
2026-03-18 02:01:36 +08:00

1338 lines
51 KiB
Lua

local AddOnName = "Nanami-UI"
SFrames = SFrames or {}
local TradeUI = CreateFrame("Frame", "SFramesTradeUI", UIParent)
TradeUI:RegisterEvent("TRADE_SHOW")
TradeUI:RegisterEvent("TRADE_CLOSED")
TradeUI:RegisterEvent("TRADE_UPDATE")
TradeUI:RegisterEvent("TRADE_PLAYER_ITEM_CHANGED")
TradeUI:RegisterEvent("TRADE_TARGET_ITEM_CHANGED")
TradeUI:RegisterEvent("TRADE_ACCEPT_UPDATE")
TradeUI:RegisterEvent("TRADE_MONEY_CHANGED")
TradeUI:RegisterEvent("PLAYER_TRADE_MONEY")
TradeUI:RegisterEvent("UI_INFO_MESSAGE")
TradeUI:RegisterEvent("CHAT_MSG_SYSTEM")
local L = {
NOT_TRADED = "不会被交易",
WHISPER_CHECK = "发送清单",
CONFIRMED = "已确认",
WAITING = "等待中...",
}
local TRADE_DATA = {
active = false,
playerItems = {},
targetItems = {},
playerMoney = 0,
targetMoney = 0,
targetName = "",
playerAccepted = false,
targetAccepted = false,
}
SFramesDB = SFramesDB or {}
--------------------------------------------------------------------------------
-- Theme: Pink Cat-Paw (matching GameMenu / Nanami-UI)
--------------------------------------------------------------------------------
local T = SFrames.Theme:Extend({
labelDim = { 0.7, 0.5, 0.6 },
tradeBg = { 0.10, 0.22, 0.12, 0.95 },
tradeBorder = { 0.30, 0.70, 0.35, 0.90 },
tradeText = { 0.7, 1, 0.75 },
moneyBg = { 0.08, 0.04, 0.06, 0.85 },
moneyBorder = { 0.40, 0.25, 0.35, 0.6 },
confirmOverlay = { 0.15, 0.85, 0.25, 0.55 },
confirmBorder = { 0.30, 1.0, 0.40, 0.95 },
confirmText = { 0.3, 1, 0.4 },
statusConfirmBg = { 0.08, 0.35, 0.12, 0.92 },
statusConfirmBd = { 0.25, 0.95, 0.35, 0.95 },
statusWaitBg = { 0.15, 0.08, 0.12, 0.8 },
statusWaitBd = { 0.45, 0.28, 0.38, 0.6 },
statusWaitText = { 0.6, 0.45, 0.55 },
})
local FRAME_W = 440
local SLOT_W = 188
local SLOT_H = 42
local SLOT_GAP = 2
local SIDE_PAD = 16
local HEADER_H = 46
local BOTTOM_H = 54
local NOT_TRADED_GAP = 38
local STATUS_BAR_H = 22
-- FRAME_H computed: HEADER_H+8 + 6*SLOT_H+5*GAP + NOT_TRADED_GAP + SLOT_H + 6 + STATUS_BAR_H + 6 + BOTTOM_H
local FRAME_H = HEADER_H + 8 + 6 * SLOT_H + 5 * SLOT_GAP + NOT_TRADED_GAP + SLOT_H + 6 + STATUS_BAR_H + 6 + BOTTOM_H
local DEFAULT_SLOT_BORDER = { 0.25, 0.25, 0.3, 0.8 }
local function GetFont()
if SFrames and SFrames.GetFont then return SFrames:GetFont() end
return "Fonts\\ARIALN.TTF"
end
--------------------------------------------------------------------------------
-- Data logic
--------------------------------------------------------------------------------
local function SaveTradeState()
if not TradeFrame or not TradeFrame:IsVisible() then return end
local pItems = {}
for i = 1, 6 do
local link = nil
if GetTradePlayerItemLink then
link = GetTradePlayerItemLink(i)
end
if not link and GetTradePlayerItemInfo then
local name, texture, numItems = GetTradePlayerItemInfo(i)
if name and name ~= "" then
link = name
end
end
if link then
local _, _, numItems = GetTradePlayerItemInfo(i)
table.insert(pItems, { link = link, count = numItems or 1 })
end
end
if table.getn(pItems) > 0 then
TRADE_DATA.playerItems = pItems
end
local tItems = {}
for i = 1, 6 do
local link = nil
if GetTradeTargetItemLink then
link = GetTradeTargetItemLink(i)
end
if not link and GetTradeTargetItemInfo then
local name, texture, numItems = GetTradeTargetItemInfo(i)
if name and name ~= "" then
link = name
end
end
if link then
local _, _, numItems = GetTradeTargetItemInfo(i)
table.insert(tItems, { link = link, count = numItems or 1 })
end
end
if table.getn(tItems) > 0 then
TRADE_DATA.targetItems = tItems
end
local pm = GetPlayerTradeMoney and GetPlayerTradeMoney() or 0
local tm = GetTargetTradeMoney and GetTargetTradeMoney() or 0
if pm >= 0 and pm > TRADE_DATA.playerMoney then
TRADE_DATA.playerMoney = pm
end
if tm >= 0 and tm > TRADE_DATA.targetMoney then
TRADE_DATA.targetMoney = tm
end
local name = UnitName("NPC")
if name and name ~= "" then TRADE_DATA.targetName = name end
end
local function FormatMoneyZH(copper)
if not copper or copper == 0 then return nil end
local g = math.floor(copper / 10000)
local s = math.floor((copper - (g * 10000)) / 100)
local c = math.mod(copper, 100)
local text = ""
if g > 0 then text = text .. g .. "g " end
if s > 0 then text = text .. s .. "s " end
if c > 0 then text = text .. c .. "c " end
return text
end
local function SendLine(msg, channel, target)
if channel == "WHISPER" then
if target and target ~= "" and target ~= "Unknown" then
SendChatMessage(msg, "WHISPER", nil, target)
end
else
SendChatMessage(msg, channel)
end
end
local function SendTradeWhisper()
if not SFramesDB.TradeWhisperEnable then return end
local target = TRADE_DATA.targetName
local channel = SFramesDB.TradeWhisperChannel or "WHISPER"
local outLines = {}
local playerMoneyStr = FormatMoneyZH(TRADE_DATA.playerMoney)
local targetMoneyStr = FormatMoneyZH(TRADE_DATA.targetMoney)
local giveItems = ""
for _, item in ipairs(TRADE_DATA.playerItems) do
giveItems = giveItems .. item.link .. (item.count > 1 and ("x" .. item.count) or "") .. " "
end
local getItems = ""
for _, item in ipairs(TRADE_DATA.targetItems) do
getItems = getItems .. item.link .. (item.count > 1 and ("x" .. item.count) or "") .. " "
end
if not playerMoneyStr and giveItems == "" and not targetMoneyStr and getItems == "" then
return
end
local useCN = (SFramesDB.TradeWhisperLang == "ZH")
local header = useCN and "=== 交易完成清单 ===" or "=== Trade Summary ==="
local lblGiveG = useCN and "我方金币: " or "I gave gold: "
local lblGiveI = useCN and "我方物品: " or "I gave items: "
local lblGotG = useCN and "对方金币: " or "I got gold: "
local lblGotI = useCN and "对方物品: " or "I got items: "
table.insert(outLines, header)
if playerMoneyStr then table.insert(outLines, lblGiveG .. playerMoneyStr) end
if giveItems ~= "" then table.insert(outLines, lblGiveI .. giveItems) end
if targetMoneyStr then table.insert(outLines, lblGotG .. targetMoneyStr) end
if getItems ~= "" then table.insert(outLines, lblGotI .. getItems) end
for _, line in ipairs(outLines) do
SendLine(line, channel, target)
end
end
local function ClearTradeData()
TRADE_DATA.playerItems = {}
TRADE_DATA.targetItems = {}
TRADE_DATA.playerMoney = 0
TRADE_DATA.targetMoney = 0
end
local tradeWhisperSent = false
local function IsTradeCompleteMsg(msg)
if not msg then return false end
if string.find(msg, "Trade successful") then return true end
if string.find(msg, "Trade complete") then return true end
if string.find(msg, "交易完成") then return true end
if string.find(msg, "交易成功") then return true end
return false
end
local sfTradeRefreshing = false
local function ForceRefreshTradeVisuals()
if not TradeFrame or not TradeFrame:IsVisible() then return end
if sfTradeRefreshing then return end
if TradeFrame_Update then
sfTradeRefreshing = true
pcall(function() TradeFrame_Update() end)
sfTradeRefreshing = false
end
end
TradeUI:SetScript("OnEvent", function()
if event == "TRADE_SHOW" then
tradeWhisperSent = false
TRADE_DATA.active = true
TRADE_DATA.targetName = UnitName("NPC") or ""
TRADE_DATA.playerItems = {}
TRADE_DATA.targetItems = {}
TRADE_DATA.playerMoney = 0
TRADE_DATA.targetMoney = 0
TRADE_DATA.playerAccepted = false
TRADE_DATA.targetAccepted = false
SaveTradeState()
ForceRefreshTradeVisuals()
elseif event == "TRADE_PLAYER_ITEM_CHANGED" or event == "TRADE_TARGET_ITEM_CHANGED" then
TRADE_DATA.playerAccepted = false
TRADE_DATA.targetAccepted = false
SaveTradeState()
ForceRefreshTradeVisuals()
elseif event == "TRADE_UPDATE" then
SaveTradeState()
ForceRefreshTradeVisuals()
elseif event == "TRADE_ACCEPT_UPDATE" then
TRADE_DATA.playerAccepted = (arg1 and arg1 == 1)
TRADE_DATA.targetAccepted = (arg2 and arg2 == 1)
SaveTradeState()
ForceRefreshTradeVisuals()
elseif event == "TRADE_MONEY_CHANGED" or event == "PLAYER_TRADE_MONEY" then
SaveTradeState()
ForceRefreshTradeVisuals()
elseif event == "TRADE_CLOSED" then
if TRADE_DATA.playerAccepted and TRADE_DATA.targetAccepted and not tradeWhisperSent then
tradeWhisperSent = true
SendTradeWhisper()
ClearTradeData()
end
TRADE_DATA.active = false
TRADE_DATA.playerAccepted = false
TRADE_DATA.targetAccepted = false
elseif event == "UI_INFO_MESSAGE" then
if IsTradeCompleteMsg(arg1) and not tradeWhisperSent then
tradeWhisperSent = true
SendTradeWhisper()
ClearTradeData()
end
elseif event == "CHAT_MSG_SYSTEM" then
if IsTradeCompleteMsg(arg1) and not tradeWhisperSent then
tradeWhisperSent = true
SendTradeWhisper()
ClearTradeData()
end
end
end)
--------------------------------------------------------------------------------
-- UI Helpers (matching GameMenu / Nanami-UI style)
--------------------------------------------------------------------------------
local function SetRoundBackdrop(frame, bgColor, borderColor)
frame:SetBackdrop({
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
tile = true, tileSize = 16, edgeSize = 14,
insets = { left = 3, right = 3, top = 3, bottom = 3 },
})
local bg = bgColor or T.panelBg
local bd = borderColor or T.panelBorder
frame:SetBackdropColor(bg[1], bg[2], bg[3], bg[4] or 1)
frame:SetBackdropBorderColor(bd[1], bd[2], bd[3], bd[4] or 1)
end
local function CreateShadow(parent, size)
local s = CreateFrame("Frame", nil, parent)
local sz = size or 4
s:SetPoint("TOPLEFT", parent, "TOPLEFT", -sz, sz)
s:SetPoint("BOTTOMRIGHT", parent, "BOTTOMRIGHT", sz, -sz)
s:SetFrameLevel(math.max(parent:GetFrameLevel() - 1, 0))
s:SetBackdrop({
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
tile = true, tileSize = 16, edgeSize = 16,
insets = { left = 4, right = 4, top = 4, bottom = 4 },
})
s:SetBackdropColor(0, 0, 0, 0.55)
s:SetBackdropBorderColor(0, 0, 0, 0.4)
return s
end
--------------------------------------------------------------------------------
-- Tooltip scanner for item level
--------------------------------------------------------------------------------
local tooltipScanner = CreateFrame("GameTooltip", "SFramesTradeTooltipScan", nil, "GameTooltipTemplate")
tooltipScanner:SetOwner(UIParent, "ANCHOR_NONE")
local function ScanItemLevelFromTooltip()
for i = 2, tooltipScanner:NumLines() do
local line = _G["SFramesTradeTooltipScanTextLeft" .. i]
if line then
local text = line:GetText()
if text then
local _, _, ilvl = string.find(text, "(%d+)")
if string.find(text, "Item Level") or string.find(text, "iLvl") or string.find(text, "ilvl") or string.find(text, "物品等级") then
if ilvl then return tonumber(ilvl) end
end
end
end
end
return nil
end
local function GetTradePlayerItemLevel(slot)
tooltipScanner:SetOwner(UIParent, "ANCHOR_NONE")
tooltipScanner:ClearLines()
local ok = pcall(function() tooltipScanner:SetTradePlayerItem(slot) end)
if ok then return ScanItemLevelFromTooltip() end
local link = GetTradePlayerItemLink and GetTradePlayerItemLink(slot)
if link then
tooltipScanner:SetOwner(UIParent, "ANCHOR_NONE")
tooltipScanner:ClearLines()
ok = pcall(function() tooltipScanner:SetHyperlink(link) end)
if ok then return ScanItemLevelFromTooltip() end
end
return nil
end
local function GetTradeTargetItemLevel(slot)
tooltipScanner:SetOwner(UIParent, "ANCHOR_NONE")
tooltipScanner:ClearLines()
local ok = pcall(function() tooltipScanner:SetTradeTargetItem(slot) end)
if ok then return ScanItemLevelFromTooltip() end
local link = GetTradeTargetItemLink and GetTradeTargetItemLink(slot)
if link then
tooltipScanner:SetOwner(UIParent, "ANCHOR_NONE")
tooltipScanner:ClearLines()
ok = pcall(function() tooltipScanner:SetHyperlink(link) end)
if ok then return ScanItemLevelFromTooltip() end
end
return nil
end
--------------------------------------------------------------------------------
-- Quality color from item link
--------------------------------------------------------------------------------
local function GetQualityColorFromLink(link)
if not link then return nil, nil, nil end
local _, _, hex = string.find(link, "|c(%x+)|H")
if hex and string.len(hex) == 8 then
local r = tonumber(string.sub(hex, 3, 4), 16) / 255
local g = tonumber(string.sub(hex, 5, 6), 16) / 255
local b = tonumber(string.sub(hex, 7, 8), 16) / 255
return r, g, b
end
return nil, nil, nil
end
local function GetQualityColorFromRarity(rarity)
if type(rarity) ~= "number" then return nil, nil, nil end
if not GetItemQualityColor then return nil, nil, nil end
local ok, r, g, b = pcall(function() return GetItemQualityColor(rarity) end)
if ok and r and g and b then
return r, g, b
end
return nil, nil, nil
end
local function ResolveTradeItemQuality(link, quality, name)
if type(quality) == "number" then
return quality
end
if GetItemInfo then
if link then
local _, _, q = GetItemInfo(link)
if type(q) == "number" then return q end
end
if name then
local _, _, q = GetItemInfo(name)
if type(q) == "number" then return q end
end
end
return nil
end
local function IsCommonOrPoor(link)
if not link then return true end
local _, _, hex = string.find(link, "|c(%x+)|H")
if hex then
local hexLower = string.lower(hex)
return hexLower == "ffffffff" or hexLower == "ff9d9d9d"
end
return true
end
--------------------------------------------------------------------------------
-- Right-click to remove player trade items
--------------------------------------------------------------------------------
local function HookSingleTradeSlot(slotIndex)
local itemBtn = _G["TradePlayerItem" .. slotIndex .. "ItemButton"]
if not itemBtn or itemBtn.sfRightClickHooked then return end
itemBtn.sfRightClickHooked = true
itemBtn:RegisterForClicks("LeftButtonUp", "RightButtonUp")
local origScript = itemBtn:GetScript("OnClick")
itemBtn:SetScript("OnClick", function()
if arg1 == "RightButton" then
local hasItem = false
if GetTradePlayerItemLink then
local link = GetTradePlayerItemLink(slotIndex)
if link then hasItem = true end
end
if not hasItem then
local name = GetTradePlayerItemInfo(slotIndex)
if name and name ~= "" then hasItem = true end
end
if hasItem then
ClearCursor()
ClickTradeButton(slotIndex)
return
end
end
if origScript then origScript() end
end)
end
local function HookTradeItemRightClick()
for i = 1, 6 do
HookSingleTradeSlot(i)
end
end
--------------------------------------------------------------------------------
-- Re-hide Blizzard visuals (called every TradeFrame_Update)
--------------------------------------------------------------------------------
local function ReHideBlizzardSlot(itemPrefix, index)
local btnName = itemPrefix .. index .. "ItemButton"
local icon = _G[btnName .. "IconTexture"]
if icon then icon:SetAlpha(0); icon:Hide() end
local nt = _G[btnName .. "NormalTexture"]
if nt then nt:SetAlpha(0); nt:Hide() end
local slot = _G[btnName .. "SlotTexture"]
if slot then slot:SetAlpha(0); slot:Hide() end
local cnt = _G[btnName .. "Count"]
if cnt then cnt:SetAlpha(0) end
local bgSlot = _G[itemPrefix .. index .. "ItemButtonSlotTexture"]
if bgSlot then bgSlot:SetAlpha(0); bgSlot:Hide() end
local bgSlot2 = _G[itemPrefix .. index .. "SlotTexture"]
if bgSlot2 then bgSlot2:SetAlpha(0); bgSlot2:Hide() end
local bgSlot3 = _G[itemPrefix .. index .. "ItemButtonBackground"]
if bgSlot3 then bgSlot3:SetAlpha(0); bgSlot3:Hide() end
local nf = _G[itemPrefix .. index .. "NameFrame"]
if nf then nf:SetAlpha(0); nf:Hide() end
end
--------------------------------------------------------------------------------
-- Skin Trade Frame
--------------------------------------------------------------------------------
local function SkinTradeFrame()
if TradeFrame.sfSkinned then return end
TradeFrame.sfSkinned = true
TradeFrame:SetMovable(true)
TradeFrame:EnableMouse(true)
TradeFrame:RegisterForDrag("LeftButton")
TradeFrame:SetScript("OnDragStart", function() this:StartMoving() end)
TradeFrame:SetScript("OnDragStop", function() this:StopMovingOrSizing() end)
-- Hide ALL default textures
local regions = { TradeFrame:GetRegions() }
local bottomTexts = {}
for _, r in ipairs(regions) do
if r:IsObjectType("Texture") then
r:SetTexture(nil)
r:SetAlpha(0)
elseif r:IsObjectType("FontString") then
local text = r:GetText()
if text and (string.find(text, "不会被交易") or string.find(text, "Will not be traded") or text == L.NOT_TRADED) then
table.insert(bottomTexts, r)
end
end
end
TradeFrame.sfBottomTexts = bottomTexts
TradeFrame:SetWidth(FRAME_W)
TradeFrame:SetHeight(FRAME_H)
-- Main backdrop - Nanami-UI rounded style (matching GameMenu)
SetRoundBackdrop(TradeFrame, T.panelBg, T.panelBorder)
CreateShadow(TradeFrame, 5)
-- Header separator
local headerSep = TradeFrame:CreateTexture(nil, "ARTWORK")
headerSep:SetTexture("Interface\\Buttons\\WHITE8X8")
headerSep:SetVertexColor(T.sepColor[1], T.sepColor[2], T.sepColor[3], T.sepColor[4])
headerSep:SetHeight(1)
headerSep:SetPoint("TOPLEFT", TradeFrame, "TOPLEFT", 4, -HEADER_H)
headerSep:SetPoint("TOPRIGHT", TradeFrame, "TOPRIGHT", -4, -HEADER_H)
-- Center vertical divider (pink tinted)
local divLine = TradeFrame:CreateTexture(nil, "ARTWORK")
divLine:SetTexture("Interface\\Buttons\\WHITE8X8")
divLine:SetVertexColor(T.divider[1], T.divider[2], T.divider[3], T.divider[4])
divLine:SetWidth(1)
divLine:SetPoint("TOP", TradeFrame, "TOP", 0, -(HEADER_H + 2))
divLine:SetPoint("BOTTOM", TradeFrame, "BOTTOM", 0, BOTTOM_H + 2)
-- Bottom separator
local bottomSep = TradeFrame:CreateTexture(nil, "ARTWORK")
bottomSep:SetTexture("Interface\\Buttons\\WHITE8X8")
bottomSep:SetVertexColor(T.sepColor[1], T.sepColor[2], T.sepColor[3], T.sepColor[4])
bottomSep:SetHeight(1)
bottomSep:SetPoint("BOTTOMLEFT", TradeFrame, "BOTTOMLEFT", 4, BOTTOM_H)
bottomSep:SetPoint("BOTTOMRIGHT", TradeFrame, "BOTTOMRIGHT", -4, BOTTOM_H)
-- Player name (pink/gold tint)
TradeFramePlayerNameText:ClearAllPoints()
TradeFramePlayerNameText:SetPoint("TOPLEFT", TradeFrame, "TOPLEFT", SIDE_PAD, -8)
TradeFramePlayerNameText:SetFont(GetFont(), 12, "OUTLINE")
TradeFramePlayerNameText:SetTextColor(T.gold[1], T.gold[2], T.gold[3])
-- Target name
TradeFrameRecipientNameText:ClearAllPoints()
TradeFrameRecipientNameText:SetPoint("TOPRIGHT", TradeFrame, "TOPRIGHT", -SIDE_PAD, -8)
TradeFrameRecipientNameText:SetFont(GetFont(), 12, "OUTLINE")
TradeFrameRecipientNameText:SetTextColor(T.gold[1], T.gold[2], T.gold[3])
if TradeFrameRecipientPortrait then TradeFrameRecipientPortrait:Hide() end
if TradeFramePlayerPortrait then TradeFramePlayerPortrait:Hide() end
-- Confirmation status bars (created here, positioned after item layout)
local function CreateStatusBar(parent, anchorSide)
local bar = CreateFrame("Frame", nil, parent)
bar:SetWidth(SLOT_W)
bar:SetHeight(STATUS_BAR_H)
bar:SetFrameLevel(parent:GetFrameLevel() + 5)
SetRoundBackdrop(bar, T.statusWaitBg, T.statusWaitBd)
local icon = bar:CreateFontString(nil, "OVERLAY")
icon:SetFont(GetFont(), 13, "OUTLINE")
icon:SetPoint("LEFT", bar, "LEFT", 8, 0)
bar.sfIcon = icon
local label = bar:CreateFontString(nil, "OVERLAY")
label:SetFont(GetFont(), 12, "OUTLINE")
label:SetPoint("LEFT", icon, "RIGHT", 4, 0)
label:SetPoint("RIGHT", bar, "RIGHT", -6, 0)
label:SetJustifyH(anchorSide)
bar.sfLabel = label
local glow = bar:CreateTexture(nil, "BACKGROUND")
glow:SetTexture("Interface\\Buttons\\WHITE8X8")
glow:SetAllPoints(bar)
glow:SetAlpha(0)
bar.sfGlow = glow
bar.sfElapsed = 0
bar:SetScript("OnUpdate", function()
if not this.sfConfirmed then return end
this.sfElapsed = (this.sfElapsed or 0) + arg1
local a = 0.35 + 0.2 * math.sin(this.sfElapsed * 3.5)
if this.sfGlow then this.sfGlow:SetAlpha(a) end
end)
bar:Hide()
return bar
end
local playerStatusBar = CreateStatusBar(TradeFrame, "LEFT")
TradeFrame.sfPlayerStatusBar = playerStatusBar
local targetStatusBar = CreateStatusBar(TradeFrame, "LEFT")
TradeFrame.sfTargetStatusBar = targetStatusBar
-- Money frames
TradePlayerInputMoneyFrame:ClearAllPoints()
TradePlayerInputMoneyFrame:SetPoint("TOPLEFT", TradeFrame, "TOPLEFT", SIDE_PAD, -24)
local pMoneyBg = CreateFrame("Frame", nil, TradeFrame)
pMoneyBg:SetPoint("TOPLEFT", TradePlayerInputMoneyFrame, "TOPLEFT", -3, 3)
pMoneyBg:SetPoint("BOTTOMRIGHT", TradePlayerInputMoneyFrame, "BOTTOMRIGHT", 3, -3)
pMoneyBg:SetFrameLevel(math.max(TradePlayerInputMoneyFrame:GetFrameLevel() - 1, 0))
SetRoundBackdrop(pMoneyBg, T.moneyBg, T.moneyBorder)
TradeRecipientMoneyFrame:ClearAllPoints()
TradeRecipientMoneyFrame:SetPoint("TOPRIGHT", TradeFrame, "TOPRIGHT", -SIDE_PAD, -24)
local rMoneyBg = CreateFrame("Frame", nil, TradeFrame)
rMoneyBg:SetPoint("TOPLEFT", TradeRecipientMoneyFrame, "TOPLEFT", -3, 3)
rMoneyBg:SetPoint("BOTTOMRIGHT", TradeRecipientMoneyFrame, "BOTTOMRIGHT", 3, -3)
rMoneyBg:SetFrameLevel(math.max(TradeRecipientMoneyFrame:GetFrameLevel() - 1, 0))
SetRoundBackdrop(rMoneyBg, T.moneyBg, T.moneyBorder)
-- Style money input text
local moneyEditNames = { "Gold", "Silver", "Copper" }
for _, suffix in ipairs(moneyEditNames) do
local eb = _G["TradePlayerInputMoneyFrame" .. suffix]
if eb and eb.SetFont then
eb:SetFont(GetFont(), 12, "OUTLINE")
eb:SetTextColor(1, 1, 1, 1)
end
local btn = _G["TradePlayerInputMoneyFrame" .. suffix .. "Button"]
if btn then
local icon = btn:GetNormalTexture()
if icon then icon:SetVertexColor(1, 1, 1, 1) end
end
local rBtn = _G["TradeRecipientMoneyFrame" .. suffix .. "Button"]
if rBtn then
local rText = _G["TradeRecipientMoneyFrame" .. suffix .. "ButtonText"]
if rText and rText.SetFont then
rText:SetFont(GetFont(), 11, "OUTLINE")
rText:SetTextColor(1, 1, 1, 1)
end
end
end
----------------------------------------------------------------------------
-- Item slots - fully custom, Blizzard button is invisible click receiver
----------------------------------------------------------------------------
local function HideBlizzardButton(itemBtn)
local btnName = itemBtn:GetName()
-- Kill every known named child texture by explicit name
local suffixes = { "IconTexture", "NormalTexture", "SlotTexture", "Count" }
for _, suf in ipairs(suffixes) do
local obj = _G[btnName .. suf]
if obj then
if obj.SetTexture then obj:SetTexture(nil) end
if obj.SetAlpha then obj:SetAlpha(0) end
if obj.SetTextColor then obj:SetTextColor(0,0,0,0) end
if obj.Hide then obj:Hide() end
end
end
-- Kill template textures via API
if itemBtn.GetNormalTexture then
local nt = itemBtn:GetNormalTexture()
if nt then nt:SetTexture(nil); nt:SetAlpha(0) end
end
if itemBtn.GetPushedTexture then
local pt = itemBtn:GetPushedTexture()
if pt then pt:SetTexture(nil); pt:SetAlpha(0) end
end
if itemBtn.GetHighlightTexture then
local ht = itemBtn:GetHighlightTexture()
if ht then ht:SetTexture(nil); ht:SetAlpha(0) end
end
-- Kill all remaining child regions
local regions = { itemBtn:GetRegions() }
for _, r in ipairs(regions) do
if r then
if r.SetTexture then r:SetTexture(nil) end
if r.SetAlpha then r:SetAlpha(0) end
if r.SetTextColor then r:SetTextColor(0,0,0,0) end
if r.Hide then r:Hide() end
end
end
itemBtn:SetBackdrop(nil)
end
local function CreateSfSlot(parent)
local slot = CreateFrame("Frame", nil, parent)
slot:SetWidth(SLOT_H)
slot:SetHeight(SLOT_H)
slot:SetBackdrop({
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
tile = true, tileSize = 16, edgeSize = 12,
insets = { left = 2, right = 2, top = 2, bottom = 2 }
})
slot:SetBackdropColor(T.slotBg[1], T.slotBg[2], T.slotBg[3], T.slotBg[4])
slot:SetBackdropBorderColor(DEFAULT_SLOT_BORDER[1], DEFAULT_SLOT_BORDER[2], DEFAULT_SLOT_BORDER[3], DEFAULT_SLOT_BORDER[4])
local icon = slot:CreateTexture(nil, "ARTWORK")
icon:SetWidth(SLOT_H - 4)
icon:SetHeight(SLOT_H - 4)
icon:SetPoint("CENTER", slot, "CENTER", 0, 0)
icon:SetTexCoord(0.08, 0.92, 0.08, 0.92)
icon:Hide()
slot.icon = icon
local highlight = slot:CreateTexture(nil, "OVERLAY")
highlight:SetTexture("Interface\\Buttons\\ButtonHilight-Square")
highlight:SetBlendMode("ADD")
highlight:SetWidth(SLOT_H - 4)
highlight:SetHeight(SLOT_H - 4)
highlight:SetPoint("CENTER", slot, "CENTER", 0, 0)
highlight:SetAlpha(0)
slot.highlight = highlight
local ilvl = slot:CreateFontString(nil, "OVERLAY")
ilvl:SetFont(GetFont(), 9, "OUTLINE")
ilvl:SetPoint("BOTTOMRIGHT", slot, "BOTTOMRIGHT", -2, 2)
ilvl:SetTextColor(1, 0.82, 0)
ilvl:SetJustifyH("RIGHT")
ilvl:Hide()
slot.ilvl = ilvl
local count = slot:CreateFontString(nil, "OVERLAY")
count:SetFont(GetFont(), 11, "OUTLINE")
count:SetPoint("BOTTOMRIGHT", slot, "BOTTOMRIGHT", -2, 2)
count:SetTextColor(1, 1, 1)
count:SetJustifyH("RIGHT")
count:Hide()
slot.count = count
local qualGlow = slot:CreateTexture(nil, "OVERLAY")
qualGlow:SetTexture("Interface\\Buttons\\UI-ActionButton-Border")
qualGlow:SetBlendMode("ADD")
qualGlow:SetAlpha(0.8)
qualGlow:SetWidth(SLOT_H * 1.8)
qualGlow:SetHeight(SLOT_H * 1.8)
qualGlow:SetPoint("CENTER", slot, "CENTER", 0, 0)
qualGlow:Hide()
slot.qualGlow = qualGlow
return slot
end
local function StyleTradeItem(itemPrefix, index)
local itemFrame = _G[itemPrefix .. index]
local itemBtn = _G[itemPrefix .. index .. "ItemButton"]
local itemName = _G[itemPrefix .. index .. "Name"]
local bgSlot = _G[itemPrefix .. index .. "ItemButtonSlotTexture"]
local bgSlot2 = _G[itemPrefix .. index .. "SlotTexture"]
local bgSlot3 = _G[itemPrefix .. index .. "ItemButtonBackground"]
local nameFrame = _G[itemPrefix .. index .. "NameFrame"]
if bgSlot then
bgSlot:SetTexture(nil); bgSlot:Hide()
bgSlot.Show = function() end
end
if bgSlot2 then
bgSlot2:SetTexture(nil); bgSlot2:Hide()
bgSlot2.Show = function() end
end
if bgSlot3 then
bgSlot3:SetTexture(nil); bgSlot3:Hide()
bgSlot3.Show = function() end
end
if nameFrame then
nameFrame:SetTexture(nil); nameFrame:Hide()
nameFrame.Show = function() end
end
itemFrame:SetWidth(SLOT_W)
itemFrame:SetHeight(SLOT_H)
-- Row background (create once)
if not itemFrame.sfRowBg then
local slotBg = itemFrame:CreateTexture(nil, "BACKGROUND")
slotBg:SetTexture("Interface\\Tooltips\\UI-Tooltip-Background")
slotBg:SetVertexColor(0.08, 0.04, 0.06, 0.4)
slotBg:SetAllPoints(itemFrame)
itemFrame.sfRowBg = slotBg
end
-- Create our pure custom slot (once)
if not itemFrame.sfSlot then
itemFrame.sfSlot = CreateSfSlot(itemFrame)
end
local sfSlot = itemFrame.sfSlot
sfSlot:ClearAllPoints()
sfSlot:SetPoint("LEFT", itemFrame, "LEFT", 1, 0)
sfSlot:SetFrameLevel(itemFrame:GetFrameLevel() + 1)
sfSlot:Show()
-- Make Blizzard button completely invisible
HideBlizzardButton(itemBtn)
-- Position invisible Blizzard button exactly over sfSlot for click/drag
itemBtn:ClearAllPoints()
itemBtn:SetWidth(SLOT_H)
itemBtn:SetHeight(SLOT_H)
itemBtn:SetPoint("CENTER", sfSlot, "CENTER", 0, 0)
itemBtn:SetFrameLevel(sfSlot:GetFrameLevel() + 2)
-- Hover glow: show/hide highlight on sfSlot when mouse enters Blizzard button
if not itemBtn.sfHoverHooked then
itemBtn.sfHoverHooked = true
local origEnter = itemBtn:GetScript("OnEnter")
local origLeave = itemBtn:GetScript("OnLeave")
itemBtn:SetScript("OnEnter", function()
if origEnter then origEnter() end
local sf = this:GetParent() and this:GetParent().sfSlot
if sf and sf.highlight then sf.highlight:SetAlpha(0.35) end
end)
itemBtn:SetScript("OnLeave", function()
if origLeave then origLeave() end
local sf = this:GetParent() and this:GetParent().sfSlot
if sf and sf.highlight then sf.highlight:SetAlpha(0) end
end)
end
-- Name text anchored to sfSlot
itemName:ClearAllPoints()
itemName:SetPoint("LEFT", sfSlot, "RIGHT", 6, 0)
itemName:SetPoint("RIGHT", itemFrame, "RIGHT", -4, 0)
itemName:SetJustifyH("LEFT")
itemName:SetFont(GetFont(), 11, "OUTLINE")
end
-- Layout items in two columns
for i = 1, 7 do
StyleTradeItem("TradePlayerItem", i)
StyleTradeItem("TradeRecipientItem", i)
local pf = _G["TradePlayerItem" .. i]
local rf = _G["TradeRecipientItem" .. i]
pf:ClearAllPoints()
rf:ClearAllPoints()
if i == 1 then
pf:SetPoint("TOPLEFT", TradeFrame, "TOPLEFT", SIDE_PAD, -(HEADER_H + 8))
rf:SetPoint("TOPRIGHT", TradeFrame, "TOPRIGHT", -SIDE_PAD, -(HEADER_H + 8))
elseif i == 7 then
pf:SetPoint("TOPLEFT", _G["TradePlayerItem6"], "BOTTOMLEFT", 0, -NOT_TRADED_GAP)
rf:SetPoint("TOPLEFT", _G["TradeRecipientItem6"], "BOTTOMLEFT", 0, -NOT_TRADED_GAP)
if TradeFrame.sfBottomTexts then
for idx, lbl in ipairs(TradeFrame.sfBottomTexts) do
lbl:ClearAllPoints()
lbl:SetFont(GetFont(), 10, "OUTLINE")
lbl:SetTextColor(T.labelDim[1], T.labelDim[2], T.labelDim[3])
if idx == 1 then
lbl:SetPoint("BOTTOMLEFT", pf, "TOPLEFT", 0, 5)
else
lbl:SetPoint("BOTTOMLEFT", rf, "TOPLEFT", 0, 5)
end
end
end
else
pf:SetPoint("TOPLEFT", _G["TradePlayerItem" .. (i - 1)], "BOTTOMLEFT", 0, -SLOT_GAP)
rf:SetPoint("TOPLEFT", _G["TradeRecipientItem" .. (i - 1)], "BOTTOMLEFT", 0, -SLOT_GAP)
end
end
-- Position status bars below item 7
if TradeFrame.sfPlayerStatusBar then
TradeFrame.sfPlayerStatusBar:ClearAllPoints()
TradeFrame.sfPlayerStatusBar:SetPoint("TOPLEFT", _G["TradePlayerItem7"], "BOTTOMLEFT", 0, -4)
end
if TradeFrame.sfTargetStatusBar then
TradeFrame.sfTargetStatusBar:ClearAllPoints()
TradeFrame.sfTargetStatusBar:SetPoint("TOPLEFT", _G["TradeRecipientItem7"], "BOTTOMLEFT", 0, -4)
end
-- Thin separator in the gap between slot 6 and not-traded label
local function MakeThinSep(anchor, w)
local sep = TradeFrame:CreateTexture(nil, "ARTWORK")
sep:SetTexture("Interface\\Buttons\\WHITE8X8")
sep:SetVertexColor(T.sepColor[1], T.sepColor[2], T.sepColor[3], T.sepColor[4])
sep:SetHeight(1)
sep:SetWidth(w)
sep:SetPoint("BOTTOMLEFT", anchor, "BOTTOMLEFT", 0, -(NOT_TRADED_GAP / 2.5))
end
MakeThinSep(_G["TradePlayerItem6"], SLOT_W)
MakeThinSep(_G["TradeRecipientItem6"], SLOT_W)
----------------------------------------------------------------------------
-- Hide Blizzard confirm highlights (we handle this ourselves)
----------------------------------------------------------------------------
local function KillBlizzardHighlight(name)
local obj = _G[name]
if not obj then return end
if obj:IsObjectType("Texture") then
obj:SetTexture(nil); obj:SetAlpha(0); obj:Hide()
obj.Show = function() end
elseif obj:IsObjectType("Frame") then
local regs = { obj:GetRegions() }
for _, r in ipairs(regs) do
if r:IsObjectType("Texture") then r:SetTexture(nil); r:Hide() end
end
obj:SetAlpha(0); obj:Hide()
obj.Show = function() end
end
end
KillBlizzardHighlight("TradeHighlightPlayer")
KillBlizzardHighlight("TradeHighlightRecipient")
KillBlizzardHighlight("TradeHighlightPlayerEnchant")
KillBlizzardHighlight("TradeHighlightRecipientEnchant")
----------------------------------------------------------------------------
-- Custom confirm overlay (our own, with proper FrameLevel + pulse)
----------------------------------------------------------------------------
local function CreateConfirmOverlay(parent, item1, item2)
local overlay = CreateFrame("Frame", nil, parent)
overlay:SetFrameLevel(parent:GetFrameLevel() + 8)
overlay:SetPoint("TOPLEFT", item1, "TOPLEFT", -4, 4)
overlay:SetPoint("BOTTOMRIGHT", item2, "BOTTOMRIGHT", 4, -4)
SetRoundBackdrop(overlay, { 0, 0, 0, 0 }, T.confirmBorder)
overlay.sfElapsed = 0
overlay:SetScript("OnUpdate", function()
this.sfElapsed = (this.sfElapsed or 0) + arg1
local pulse = 0.6 + 0.4 * math.sin(this.sfElapsed * 3)
this:SetBackdropBorderColor(
T.confirmBorder[1], T.confirmBorder[2], T.confirmBorder[3], pulse)
end)
overlay:EnableMouse(false)
overlay:Hide()
return overlay
end
TradeFrame.sfPlayerOverlay = CreateConfirmOverlay(TradeFrame,
_G["TradePlayerItem1"], _G["TradePlayerItem6"])
TradeFrame.sfPlayerOverlayEnchant = CreateConfirmOverlay(TradeFrame,
_G["TradePlayerItem7"], _G["TradePlayerItem7"])
TradeFrame.sfTargetOverlay = CreateConfirmOverlay(TradeFrame,
_G["TradeRecipientItem1"], _G["TradeRecipientItem6"])
TradeFrame.sfTargetOverlayEnchant = CreateConfirmOverlay(TradeFrame,
_G["TradeRecipientItem7"], _G["TradeRecipientItem7"])
----------------------------------------------------------------------------
-- Buttons - Nanami-UI GameMenu style
----------------------------------------------------------------------------
local function SkinBtn(btn, bgCol, borderCol, textCol, label)
if not btn then return end
btn:SetWidth(72)
btn:SetHeight(26)
local nt = btn:GetNormalTexture()
if nt then nt:SetTexture(nil) end
local pt = btn:GetPushedTexture()
if pt then pt:SetTexture(nil) end
local ht = btn:GetHighlightTexture()
if ht then ht:SetTexture(nil) end
local dt = btn:GetDisabledTexture()
if dt then dt:SetTexture(nil) end
SetRoundBackdrop(btn, bgCol, borderCol)
local origEnter = btn:GetScript("OnEnter")
local origLeave = btn:GetScript("OnLeave")
if not btn.sfHoverHooked then
btn.sfHoverHooked = true
btn.sfBgCol = bgCol
btn.sfBorderCol = borderCol
btn:SetScript("OnEnter", function()
if origEnter then origEnter() end
this:SetBackdropColor(T.btnHoverBg[1], T.btnHoverBg[2], T.btnHoverBg[3], T.btnHoverBg[4])
this:SetBackdropBorderColor(T.btnHoverBorder[1], T.btnHoverBorder[2], T.btnHoverBorder[3], T.btnHoverBorder[4])
local fs = this:GetFontString()
if fs then fs:SetTextColor(T.btnActiveText[1], T.btnActiveText[2], T.btnActiveText[3]) end
end)
btn:SetScript("OnLeave", function()
if origLeave then origLeave() end
local bg = this.sfBgCol or T.btnBg
local bd = this.sfBorderCol or T.btnBorder
this:SetBackdropColor(bg[1], bg[2], bg[3], bg[4] or 1)
this:SetBackdropBorderColor(bd[1], bd[2], bd[3], bd[4] or 1)
local fs = this:GetFontString()
if fs and this.sfTextCol then fs:SetTextColor(unpack(this.sfTextCol)) end
end)
end
local fs = btn:GetFontString()
if fs then
fs:SetFont(GetFont(), 11, "OUTLINE")
fs:SetTextColor(unpack(textCol or T.btnText))
if label then fs:SetText(label) end
end
btn.sfTextCol = textCol or T.btnText
end
SkinBtn(TradeFrameTradeButton, T.tradeBg, T.tradeBorder, T.tradeText, "交易")
SkinBtn(TradeFrameCancelButton, T.btnBg, T.btnBorder, T.btnText, "取消")
TradeFrameTradeButton:ClearAllPoints()
TradeFrameCancelButton:ClearAllPoints()
TradeFrameCancelButton:SetPoint("BOTTOMRIGHT", TradeFrame, "BOTTOMRIGHT", -SIDE_PAD, 14)
TradeFrameTradeButton:SetPoint("RIGHT", TradeFrameCancelButton, "LEFT", -6, 0)
----------------------------------------------------------------------------
-- Whisper checkbox + channel dropdown
----------------------------------------------------------------------------
local cbObj = _G["SFramesTradeWhisperObj"]
if not cbObj then
cbObj = CreateFrame("CheckButton", "SFramesTradeWhisperObj", TradeFrame, "UICheckButtonTemplate")
cbObj:SetWidth(20)
cbObj:SetHeight(20)
cbObj:SetPoint("BOTTOMLEFT", TradeFrame, "BOTTOMLEFT", SIDE_PAD, 14)
local cbText = _G[cbObj:GetName() .. "Text"]
if cbText then
cbText:SetFont(GetFont(), 11, "OUTLINE")
cbText:SetText(L.WHISPER_CHECK)
cbText:SetTextColor(T.nameText[1], T.nameText[2], T.nameText[3])
end
if SFramesDB.TradeWhisperEnable == nil then SFramesDB.TradeWhisperEnable = false end
cbObj:SetChecked(SFramesDB.TradeWhisperEnable and 1 or 0)
cbObj:SetScript("OnClick", function()
SFramesDB = SFramesDB or {}
SFramesDB.TradeWhisperEnable = (this:GetChecked() == 1)
end)
local drop = CreateFrame("Frame", "SFramesTradeChannelObj", TradeFrame, "UIDropDownMenuTemplate")
drop:SetPoint("LEFT", cbText or cbObj, "RIGHT", -8, -1)
UIDropDownMenu_SetWidth(60, drop)
local dropText = _G[drop:GetName() .. "Text"]
if dropText then
dropText:SetFont(GetFont(), 10, "OUTLINE")
dropText:SetTextColor(T.nameText[1], T.nameText[2], T.nameText[3])
end
local channels = {
{ text = "密语", value = "WHISPER" },
{ text = "小队", value = "PARTY" },
{ text = "当前", value = "SAY" },
}
local function TradeDropDownInit()
SFramesDB = SFramesDB or {}
local selected = SFramesDB.TradeWhisperChannel or "WHISPER"
for _, info in ipairs(channels) do
local capturedText = info.text
local capturedValue = info.value
local d = {}
d.text = capturedText
d.value = capturedValue
d.func = function()
SFramesDB = SFramesDB or {}
SFramesDB.TradeWhisperChannel = capturedValue
UIDropDownMenu_SetSelectedValue(drop, capturedValue)
local txt = _G[drop:GetName() .. "Text"]
if txt then txt:SetText(capturedText) end
end
d.checked = (capturedValue == selected)
UIDropDownMenu_AddButton(d)
end
end
UIDropDownMenu_Initialize(drop, TradeDropDownInit)
SFramesDB = SFramesDB or {}
UIDropDownMenu_SetSelectedValue(drop, SFramesDB.TradeWhisperChannel or "WHISPER")
if dropText then
for _, info in ipairs(channels) do
if info.value == (SFramesDB.TradeWhisperChannel or "WHISPER") then
dropText:SetText(info.text)
break
end
end
end
-- Language dropdown (EN / ZH)
local langDrop = CreateFrame("Frame", "SFramesTradeLangObj", TradeFrame, "UIDropDownMenuTemplate")
langDrop:SetPoint("LEFT", drop, "RIGHT", -16, 0)
UIDropDownMenu_SetWidth(50, langDrop)
local langDropText = _G[langDrop:GetName() .. "Text"]
if langDropText then
langDropText:SetFont(GetFont(), 10, "OUTLINE")
langDropText:SetTextColor(T.nameText[1], T.nameText[2], T.nameText[3])
end
local langs = {
{ text = "EN", value = "EN" },
{ text = "中文", value = "ZH" },
}
local function TradeLangDropInit()
SFramesDB = SFramesDB or {}
local selected = SFramesDB.TradeWhisperLang or "EN"
for _, info in ipairs(langs) do
local capText = info.text
local capValue = info.value
local d = {}
d.text = capText
d.value = capValue
d.func = function()
SFramesDB = SFramesDB or {}
SFramesDB.TradeWhisperLang = capValue
UIDropDownMenu_SetSelectedValue(langDrop, capValue)
local txt = _G[langDrop:GetName() .. "Text"]
if txt then txt:SetText(capText) end
end
d.checked = (capValue == selected)
UIDropDownMenu_AddButton(d)
end
end
UIDropDownMenu_Initialize(langDrop, TradeLangDropInit)
SFramesDB = SFramesDB or {}
UIDropDownMenu_SetSelectedValue(langDrop, SFramesDB.TradeWhisperLang or "EN")
if langDropText then
for _, info in ipairs(langs) do
if info.value == (SFramesDB.TradeWhisperLang or "EN") then
langDropText:SetText(info.text)
break
end
end
end
end
-- Close button
local closeBtn = _G["TradeFrameCloseButton"]
if closeBtn then
closeBtn:ClearAllPoints()
closeBtn:SetPoint("TOPRIGHT", TradeFrame, "TOPRIGHT", -2, -2)
end
HookTradeItemRightClick()
end
--------------------------------------------------------------------------------
-- Hooks
--------------------------------------------------------------------------------
local Hook_TradeFrame_OnShow = TradeFrame_OnShow
function TradeFrame_OnShow()
if Hook_TradeFrame_OnShow then Hook_TradeFrame_OnShow() end
SkinTradeFrame()
ForceRefreshTradeVisuals()
end
local function UpdateSfSlot(sfSlot, texture, numItems, link, ilvl, quality)
if not sfSlot then return end
-- Icon texture
if texture then
sfSlot.icon:SetTexture(texture)
sfSlot.icon:Show()
else
sfSlot.icon:SetTexture(nil)
sfSlot.icon:Hide()
end
-- Stack count
if numItems and numItems > 1 then
sfSlot.count:SetText(numItems)
sfSlot.count:Show()
sfSlot.ilvl:Hide()
else
sfSlot.count:Hide()
-- Item level (only show if no stack count)
if ilvl and ilvl > 0 and texture then
sfSlot.ilvl:SetText(ilvl)
sfSlot.ilvl:Show()
else
sfSlot.ilvl:Hide()
end
end
-- Quality border
local r, g, b = nil, nil, nil
if link and not IsCommonOrPoor(link) then
r, g, b = GetQualityColorFromLink(link)
end
if (not r) and type(quality) == "number" and quality ~= 1 then
r, g, b = GetQualityColorFromRarity(quality)
end
if r then
if sfSlot.qualGlow then
sfSlot.qualGlow:SetVertexColor(r, g, b)
sfSlot.qualGlow:Show()
end
else
if sfSlot.qualGlow then
sfSlot.qualGlow:Hide()
end
end
end
local function UpdateSlotNameColor(nameObj, link, quality)
if not nameObj then return end
local r, g, b = nil, nil, nil
if link then
r, g, b = GetQualityColorFromLink(link)
end
if (not r) and type(quality) == "number" and quality ~= 1 then
r, g, b = GetQualityColorFromRarity(quality)
end
if r then
nameObj:SetTextColor(r, g, b)
return
end
nameObj:SetTextColor(T.nameText[1], T.nameText[2], T.nameText[3])
end
local function SetStatusBarState(bar, confirmed)
if not bar then return end
if confirmed then
bar.sfConfirmed = true
bar.sfElapsed = 0
SetRoundBackdrop(bar, T.statusConfirmBg, T.statusConfirmBd)
if bar.sfGlow then
bar.sfGlow:SetVertexColor(T.confirmOverlay[1], T.confirmOverlay[2], T.confirmOverlay[3], 1)
bar.sfGlow:SetAlpha(0.35)
end
if bar.sfIcon then
bar.sfIcon:SetText("|cff33ff55>>|r")
bar.sfIcon:SetTextColor(0.3, 1, 0.4)
end
if bar.sfLabel then
bar.sfLabel:SetText(L.CONFIRMED)
bar.sfLabel:SetTextColor(T.confirmText[1], T.confirmText[2], T.confirmText[3])
end
else
bar.sfConfirmed = false
SetRoundBackdrop(bar, T.statusWaitBg, T.statusWaitBd)
if bar.sfGlow then bar.sfGlow:SetAlpha(0) end
if bar.sfIcon then
bar.sfIcon:SetText("...")
bar.sfIcon:SetTextColor(T.statusWaitText[1], T.statusWaitText[2], T.statusWaitText[3])
end
if bar.sfLabel then
bar.sfLabel:SetText(L.WAITING)
bar.sfLabel:SetTextColor(T.statusWaitText[1], T.statusWaitText[2], T.statusWaitText[3])
end
end
bar:Show()
end
local function UpdateConfirmStatus()
if not TradeFrame then return end
local playerConfirmed = TRADE_DATA.playerAccepted
local targetConfirmed = TRADE_DATA.targetAccepted
-- Status bars
SetStatusBarState(TradeFrame.sfPlayerStatusBar, playerConfirmed)
SetStatusBarState(TradeFrame.sfTargetStatusBar, targetConfirmed)
-- Green overlays on item columns
if TradeFrame.sfPlayerOverlay then
if playerConfirmed then TradeFrame.sfPlayerOverlay:Show()
else TradeFrame.sfPlayerOverlay:Hide() end
end
if TradeFrame.sfPlayerOverlayEnchant then
if playerConfirmed then TradeFrame.sfPlayerOverlayEnchant:Show()
else TradeFrame.sfPlayerOverlayEnchant:Hide() end
end
if TradeFrame.sfTargetOverlay then
if targetConfirmed then TradeFrame.sfTargetOverlay:Show()
else TradeFrame.sfTargetOverlay:Hide() end
end
if TradeFrame.sfTargetOverlayEnchant then
if targetConfirmed then TradeFrame.sfTargetOverlayEnchant:Show()
else TradeFrame.sfTargetOverlayEnchant:Hide() end
end
end
local Hook_TradeFrame_Update = TradeFrame_Update
function TradeFrame_Update()
if Hook_TradeFrame_Update then Hook_TradeFrame_Update() end
for i = 1, 7 do
-- Re-hide Blizzard visuals (Blizzard code re-shows them every update)
ReHideBlizzardSlot("TradePlayerItem", i)
ReHideBlizzardSlot("TradeRecipientItem", i)
-- Read from Blizzard API
local pName, pTex, pNum, pQuality = GetTradePlayerItemInfo(i)
local rName, rTex, rNum, rQuality = GetTradeTargetItemInfo(i)
local pLink = GetTradePlayerItemLink(i)
local rLink = GetTradeTargetItemLink and GetTradeTargetItemLink(i)
if (not pTex or pTex == "") then
local pIconObj = _G["TradePlayerItem" .. i .. "ItemButtonIconTexture"]
if pIconObj and pIconObj.GetTexture then
pTex = pIconObj:GetTexture()
end
end
if (not rTex or rTex == "") then
local rIconObj = _G["TradeRecipientItem" .. i .. "ItemButtonIconTexture"]
if rIconObj and rIconObj.GetTexture then
rTex = rIconObj:GetTexture()
end
end
if (not pTex or pTex == "") and pLink and GetItemIcon then
pTex = GetItemIcon(pLink)
end
if (not rTex or rTex == "") and rLink and GetItemIcon then
rTex = GetItemIcon(rLink)
end
if (not pTex or pTex == "") and pName and GetItemInfo then
local _, _, _, _, _, _, _, _, pInfoTex = GetItemInfo(pName)
pTex = pInfoTex or pTex
end
if (not rTex or rTex == "") and rName and GetItemInfo then
local _, _, _, _, _, _, _, _, rInfoTex = GetItemInfo(rName)
rTex = rInfoTex or rTex
end
pQuality = ResolveTradeItemQuality(pLink, pQuality, pName)
rQuality = ResolveTradeItemQuality(rLink, rQuality, rName)
local pIlvl = (pTex or pName or pLink) and GetTradePlayerItemLevel(i) or nil
local rIlvl = (rTex or rName or rLink) and GetTradeTargetItemLevel(i) or nil
-- Write to custom sfSlot
local pFrame = _G["TradePlayerItem" .. i]
local rFrame = _G["TradeRecipientItem" .. i]
if pFrame then UpdateSfSlot(pFrame.sfSlot, pTex, pNum, pLink, pIlvl, pQuality) end
if rFrame then UpdateSfSlot(rFrame.sfSlot, rTex, rNum, rLink, rIlvl, rQuality) end
UpdateSlotNameColor(_G["TradePlayerItem" .. i .. "Name"], pLink, pQuality)
UpdateSlotNameColor(_G["TradeRecipientItem" .. i .. "Name"], rLink, rQuality)
end
UpdateConfirmStatus()
end
local Hook_MoneyFrame_Update = MoneyFrame_Update
function MoneyFrame_Update(frameName, money)
if Hook_MoneyFrame_Update then Hook_MoneyFrame_Update(frameName, money) end
end