1338 lines
51 KiB
Lua
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
|
|
|