1591 lines
59 KiB
Lua
1591 lines
59 KiB
Lua
--------------------------------------------------------------------------------
|
|
-- Nanami-UI: Inspect Panel (InspectPanel.lua)
|
|
-- Equipment-focused inspect panel with modern rounded UI
|
|
-- Talent viewing delegates to native InspectFrame
|
|
--------------------------------------------------------------------------------
|
|
|
|
SFrames.InspectPanel = {}
|
|
|
|
local IP = SFrames.InspectPanel
|
|
local ipanel, equipPage
|
|
local inspectUnit = nil
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- Theme (matches CharacterPanel)
|
|
--------------------------------------------------------------------------------
|
|
local T = SFrames.ActiveTheme
|
|
|
|
local FRAME_W = 340
|
|
local FRAME_H = 380
|
|
local HEADER_H = 28
|
|
local CONTENT_TOP = -(HEADER_H + 2)
|
|
local SIDE_PAD = 8
|
|
local INNER_PAD = 4
|
|
local SLOT_SIZE = 32
|
|
local SLOT_GAP = 2
|
|
|
|
local QUALITY_COLORS = {
|
|
[0] = { 0.62, 0.62, 0.62 }, [1] = { 1, 1, 1 },
|
|
[2] = { 0.12, 1, 0 }, [3] = { 0.0, 0.44, 0.87 },
|
|
[4] = { 0.64, 0.21, 0.93 }, [5] = { 1, 0.5, 0 },
|
|
}
|
|
|
|
local SLOT_LABEL = {
|
|
HeadSlot = "头部", NeckSlot = "颈部", ShoulderSlot = "肩部",
|
|
BackSlot = "背部", ChestSlot = "胸部", ShirtSlot = "衬衣",
|
|
WristSlot = "手腕", HandsSlot = "手套", WaistSlot = "腰带",
|
|
LegsSlot = "腿部", FeetSlot = "脚部",
|
|
Finger0Slot = "戒指1", Finger1Slot = "戒指2",
|
|
Trinket0Slot = "饰品1", Trinket1Slot = "饰品2",
|
|
MainHandSlot = "主手", SecondaryHandSlot = "副手",
|
|
RangedSlot = "远程", AmmoSlot = "弹药", TabardSlot = "战袍",
|
|
}
|
|
|
|
local EQUIP_SLOTS_LEFT = {
|
|
{ id = 1, name = "HeadSlot" }, { id = 2, name = "NeckSlot" },
|
|
{ id = 3, name = "ShoulderSlot" }, { id = 15, name = "BackSlot" },
|
|
{ id = 5, name = "ChestSlot" }, { id = 4, name = "ShirtSlot" },
|
|
{ id = 19, name = "TabardSlot" }, { id = 9, name = "WristSlot" },
|
|
}
|
|
local EQUIP_SLOTS_RIGHT = {
|
|
{ id = 10, name = "HandsSlot" }, { id = 6, name = "WaistSlot" },
|
|
{ id = 7, name = "LegsSlot" }, { id = 8, name = "FeetSlot" },
|
|
{ id = 11, name = "Finger0Slot" }, { id = 12, name = "Finger1Slot" },
|
|
{ id = 13, name = "Trinket0Slot" }, { id = 14, name = "Trinket1Slot" },
|
|
}
|
|
local EQUIP_SLOTS_BOTTOM = {
|
|
{ id = 16, name = "MainHandSlot" }, { id = 17, name = "SecondaryHandSlot" },
|
|
{ id = 18, name = "RangedSlot" }, { id = 0, name = "AmmoSlot" },
|
|
}
|
|
|
|
local widgetId = 0
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- Helpers
|
|
--------------------------------------------------------------------------------
|
|
local function GetFont()
|
|
if SFrames and SFrames.GetFont then return SFrames:GetFont() end
|
|
return "Fonts\\ARIALN.TTF"
|
|
end
|
|
|
|
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.bg
|
|
local bd = borderColor or T.border
|
|
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 SetPixelBackdrop(frame, bgColor, borderColor)
|
|
frame:SetBackdrop({
|
|
bgFile = "Interface\\Buttons\\WHITE8X8",
|
|
edgeFile = "Interface\\Buttons\\WHITE8X8",
|
|
tile = false, tileSize = 0, edgeSize = 1,
|
|
insets = { left = 1, right = 1, top = 1, bottom = 1 }
|
|
})
|
|
if bgColor then frame:SetBackdropColor(bgColor[1], bgColor[2], bgColor[3], bgColor[4] or 1) end
|
|
if borderColor then frame:SetBackdropBorderColor(borderColor[1], borderColor[2], borderColor[3], borderColor[4] or 1) end
|
|
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.6)
|
|
s:SetBackdropBorderColor(0, 0, 0, 0.45)
|
|
return s
|
|
end
|
|
|
|
local function MakeSep(parent, x1, y1, x2, y2)
|
|
local sep = parent: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:SetPoint("TOPLEFT", parent, "TOPLEFT", x1, y1)
|
|
sep:SetPoint("TOPRIGHT", parent, "TOPRIGHT", x2, y1)
|
|
return sep
|
|
end
|
|
|
|
local function MakeFS(parent, size, justifyH, color)
|
|
local fs = parent:CreateFontString(nil, "OVERLAY")
|
|
fs:SetFont(GetFont(), size or 11, "OUTLINE")
|
|
fs:SetJustifyH(justifyH or "LEFT")
|
|
local c = color or T.valueText
|
|
fs:SetTextColor(c[1], c[2], c[3])
|
|
return fs
|
|
end
|
|
|
|
local function NextName(p)
|
|
widgetId = widgetId + 1
|
|
return "SFramesIP" .. (p or "") .. tostring(widgetId)
|
|
end
|
|
|
|
local function GetItemQualityFromLink(link)
|
|
if not link then return nil end
|
|
local _, _, q = string.find(link, "|c(%x+)|H")
|
|
if not q then return nil end
|
|
local m = {
|
|
["ff9d9d9d"] = 0, ["ffffffff"] = 1, ["ff1eff00"] = 2,
|
|
["ff0070dd"] = 3, ["ffa335ee"] = 4, ["ffff8000"] = 5,
|
|
}
|
|
return m[q]
|
|
end
|
|
|
|
local function GetInspectUnit()
|
|
return inspectUnit or "target"
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- Scroll helper
|
|
--------------------------------------------------------------------------------
|
|
local function CreateScrollFrame(parent, width, height)
|
|
local holder = CreateFrame("Frame", NextName("SH"), parent)
|
|
holder:SetWidth(width)
|
|
holder:SetHeight(height)
|
|
|
|
local scroll = CreateFrame("ScrollFrame", NextName("SF"), holder)
|
|
scroll:SetPoint("TOPLEFT", holder, "TOPLEFT", 0, 0)
|
|
scroll:SetPoint("BOTTOMRIGHT", holder, "BOTTOMRIGHT", -10, 0)
|
|
|
|
local child = CreateFrame("Frame", NextName("SC"), scroll)
|
|
child:SetWidth(width - 14)
|
|
child:SetHeight(1)
|
|
scroll:SetScrollChild(child)
|
|
|
|
local slider = CreateFrame("Slider", NextName("SB"), holder)
|
|
slider:SetWidth(6)
|
|
slider:SetPoint("TOPRIGHT", holder, "TOPRIGHT", -1, -2)
|
|
slider:SetPoint("BOTTOMRIGHT", holder, "BOTTOMRIGHT", -1, 2)
|
|
slider:SetOrientation("VERTICAL")
|
|
slider:SetMinMaxValues(0, 1)
|
|
slider:SetValue(0)
|
|
SetPixelBackdrop(slider, { 0.1, 0.1, 0.12, 0.4 }, { 0.15, 0.15, 0.18, 0.3 })
|
|
|
|
local thumb = slider:CreateTexture(nil, "OVERLAY")
|
|
thumb:SetTexture("Interface\\Buttons\\WHITE8X8")
|
|
thumb:SetVertexColor(0.4, 0.4, 0.48, 0.7)
|
|
thumb:SetWidth(6)
|
|
thumb:SetHeight(28)
|
|
slider:SetThumbTexture(thumb)
|
|
|
|
slider:SetScript("OnValueChanged", function()
|
|
scroll:SetVerticalScroll(this:GetValue())
|
|
end)
|
|
scroll:EnableMouseWheel(1)
|
|
scroll:SetScript("OnMouseWheel", function()
|
|
local cur = slider:GetValue()
|
|
local step = 28
|
|
local _, mx = slider:GetMinMaxValues()
|
|
if arg1 > 0 then
|
|
slider:SetValue(math.max(cur - step, 0))
|
|
else
|
|
slider:SetValue(math.min(cur + step, mx))
|
|
end
|
|
end)
|
|
|
|
holder.scroll = scroll
|
|
holder.child = child
|
|
holder.slider = slider
|
|
holder.SetContentHeight = function(self, h)
|
|
child:SetHeight(h)
|
|
local visH = scroll:GetHeight()
|
|
local maxS = math.max(h - visH, 0)
|
|
slider:SetMinMaxValues(0, maxS)
|
|
if maxS == 0 then slider:Hide() else slider:Show() end
|
|
slider:SetValue(math.min(slider:GetValue(), maxS))
|
|
end
|
|
return holder
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- Main Frame (no tabs, single equipment view)
|
|
--------------------------------------------------------------------------------
|
|
local function CreateMainFrame()
|
|
if ipanel then return ipanel end
|
|
|
|
local f = CreateFrame("Frame", "SFramesInspectPanel", UIParent)
|
|
f:SetWidth(FRAME_W)
|
|
f:SetHeight(FRAME_H)
|
|
f:SetPoint("CENTER", UIParent, "CENTER", 0, 0)
|
|
f:SetFrameStrata("HIGH")
|
|
f:EnableMouse(true)
|
|
f:SetMovable(true)
|
|
f:RegisterForDrag("LeftButton")
|
|
f:SetScript("OnDragStart", function() this:StartMoving() end)
|
|
f:SetScript("OnDragStop", function() this:StopMovingOrSizing() end)
|
|
f:SetClampedToScreen(true)
|
|
SetRoundBackdrop(f, T.bg, T.border)
|
|
f.shadow = CreateShadow(f, 4)
|
|
f:Hide()
|
|
f:SetScript("OnHide", function()
|
|
if not IP.switchingToNative then
|
|
if ClearInspectPlayer then ClearInspectPlayer() end
|
|
inspectUnit = nil
|
|
end
|
|
IP:HideSummary()
|
|
end)
|
|
|
|
table.insert(UISpecialFrames, "SFramesInspectPanel")
|
|
|
|
f.classIcon = SFrames:CreateClassIcon(f, 16)
|
|
f.classIcon.overlay:SetPoint("TOPLEFT", f, "TOPLEFT", SIDE_PAD + 2, -6)
|
|
|
|
f.titleText = MakeFS(f, 12, "LEFT", T.gold)
|
|
f.titleText:SetPoint("LEFT", f.classIcon.overlay, "RIGHT", 4, 0)
|
|
|
|
f.subtitleText = MakeFS(f, 9, "LEFT", T.dimText)
|
|
f.subtitleText:SetPoint("LEFT", f.titleText, "RIGHT", 6, 0)
|
|
|
|
local function MakeHeaderBtn(parent, label, tooltip, offsetX, onClick)
|
|
local btn = CreateFrame("Button", nil, parent)
|
|
btn:SetWidth(46)
|
|
btn:SetHeight(16)
|
|
btn:SetPoint("TOPRIGHT", parent, "TOPRIGHT", offsetX, -6)
|
|
btn:SetFrameLevel(parent:GetFrameLevel() + 3)
|
|
SetRoundBackdrop(btn, T.btnBg, T.btnBorder)
|
|
local txt = MakeFS(btn, 9, "CENTER", T.dimText)
|
|
txt:SetPoint("CENTER", btn, "CENTER", 0, 0)
|
|
txt:SetText(label)
|
|
btn:SetScript("OnClick", onClick)
|
|
btn:SetScript("OnEnter", function()
|
|
this:SetBackdropColor(T.btnHover[1], T.btnHover[2], T.btnHover[3], T.btnHover[4])
|
|
GameTooltip:SetOwner(this, "ANCHOR_BOTTOM")
|
|
GameTooltip:AddLine(tooltip, 1, 1, 1)
|
|
GameTooltip:Show()
|
|
end)
|
|
btn:SetScript("OnLeave", function()
|
|
this:SetBackdropColor(T.btnBg[1], T.btnBg[2], T.btnBg[3], T.btnBg[4])
|
|
GameTooltip:Hide()
|
|
end)
|
|
return btn
|
|
end
|
|
|
|
MakeHeaderBtn(f, "荣誉", "打开原生荣誉面板", -77, function() IP:OpenNativeTab("honor") end)
|
|
MakeHeaderBtn(f, "天赋", "打开原生天赋面板", -27, function() IP:OpenNativeTab("talent") end)
|
|
|
|
local closeBtn = CreateFrame("Button", nil, f)
|
|
closeBtn:SetWidth(16)
|
|
closeBtn:SetHeight(16)
|
|
closeBtn:SetPoint("TOPRIGHT", f, "TOPRIGHT", -7, -6)
|
|
closeBtn:SetFrameLevel(f:GetFrameLevel() + 3)
|
|
SetRoundBackdrop(closeBtn, T.buttonDownBg, T.btnBorder)
|
|
local closeTxt = MakeFS(closeBtn, 9, "CENTER", T.title)
|
|
closeTxt:SetPoint("CENTER", closeBtn, "CENTER", 0, 0)
|
|
closeTxt:SetText("x")
|
|
closeBtn:SetScript("OnClick", function() f:Hide() end)
|
|
closeBtn:SetScript("OnEnter", function()
|
|
this:SetBackdropColor(T.btnHoverBg[1], T.btnHoverBg[2], T.btnHoverBg[3], T.btnHoverBg[4])
|
|
this:SetBackdropBorderColor(T.btnHoverBd[1], T.btnHoverBd[2], T.btnHoverBd[3], T.btnHoverBd[4])
|
|
end)
|
|
closeBtn:SetScript("OnLeave", function()
|
|
this:SetBackdropColor(T.buttonDownBg[1], T.buttonDownBg[2], T.buttonDownBg[3], T.buttonDownBg[4])
|
|
this:SetBackdropBorderColor(T.btnBorder[1], T.btnBorder[2], T.btnBorder[3], T.btnBorder[4])
|
|
end)
|
|
|
|
MakeSep(f, 6, -HEADER_H, -6, -HEADER_H)
|
|
|
|
equipPage = CreateFrame("Frame", NextName("Page"), f)
|
|
equipPage:SetPoint("TOPLEFT", f, "TOPLEFT", 4, CONTENT_TOP)
|
|
equipPage:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", -4, 4)
|
|
|
|
ipanel = f
|
|
return f
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- Title
|
|
--------------------------------------------------------------------------------
|
|
function IP:UpdateTitle()
|
|
if not ipanel then return end
|
|
local unit = GetInspectUnit()
|
|
local name = UnitName(unit) or "未知"
|
|
local level = UnitLevel(unit) or "?"
|
|
local classLocal, classEn = UnitClass(unit)
|
|
classLocal = classLocal or ""
|
|
classEn = classEn or ""
|
|
local raceLocal = UnitRace(unit) or ""
|
|
|
|
local cc = SFrames.Config and SFrames.Config.colors and SFrames.Config.colors.class and SFrames.Config.colors.class[classEn]
|
|
|
|
if equipPage and equipPage.modelNameText then
|
|
if cc then
|
|
equipPage.modelNameText:SetTextColor(cc.r, cc.g, cc.b)
|
|
else
|
|
equipPage.modelNameText:SetTextColor(T.gold[1], T.gold[2], T.gold[3])
|
|
end
|
|
equipPage.modelNameText:SetText(name)
|
|
end
|
|
|
|
if equipPage and equipPage.modelGuildText then
|
|
local guildName = GetGuildInfo and GetGuildInfo(unit) or nil
|
|
if guildName and guildName ~= "" then
|
|
equipPage.modelGuildText:SetText("<" .. guildName .. ">")
|
|
else
|
|
equipPage.modelGuildText:SetText("")
|
|
end
|
|
end
|
|
|
|
local parts = {}
|
|
table.insert(parts, "Lv." .. tostring(level))
|
|
table.insert(parts, raceLocal .. classLocal)
|
|
|
|
if equipPage and equipPage.avgIlvl then
|
|
table.insert(parts, string.format("iLvl:%.1f", equipPage.avgIlvl))
|
|
end
|
|
|
|
if cc then
|
|
ipanel.titleText:SetTextColor(cc.r, cc.g, cc.b)
|
|
else
|
|
ipanel.titleText:SetTextColor(T.gold[1], T.gold[2], T.gold[3])
|
|
end
|
|
ipanel.titleText:SetText(table.concat(parts, " "))
|
|
ipanel.subtitleText:SetText("")
|
|
|
|
if ipanel.classIcon then
|
|
SFrames:SetClassIcon(ipanel.classIcon, classEn)
|
|
end
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- Equipment Slot creation
|
|
--------------------------------------------------------------------------------
|
|
function IP:CreateEquipSlot(parent, slotID, slotName)
|
|
local frame = CreateFrame("Button", NextName("Slot"), parent)
|
|
frame:SetWidth(SLOT_SIZE)
|
|
frame:SetHeight(SLOT_SIZE)
|
|
SetRoundBackdrop(frame, T.slotBg, T.slotBorder)
|
|
|
|
local icon = frame:CreateTexture(nil, "ARTWORK")
|
|
icon:SetPoint("TOPLEFT", frame, "TOPLEFT", 4, -4)
|
|
icon:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -4, 4)
|
|
icon:SetTexCoord(0.08, 0.92, 0.08, 0.92)
|
|
frame.icon = icon
|
|
|
|
local ilvlText = frame:CreateFontString(nil, "OVERLAY")
|
|
ilvlText:SetFont(GetFont(), 8, "OUTLINE")
|
|
ilvlText:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -2, 2)
|
|
ilvlText:SetTextColor(1, 0.82, 0)
|
|
ilvlText:SetText("")
|
|
frame.ilvlText = ilvlText
|
|
|
|
local glow = frame:CreateTexture(nil, "OVERLAY")
|
|
glow:SetTexture("Interface\\Buttons\\UI-ActionButton-Border")
|
|
glow:SetBlendMode("ADD")
|
|
glow:SetAlpha(0.8)
|
|
glow:SetWidth(SLOT_SIZE * 2)
|
|
glow:SetHeight(SLOT_SIZE * 2)
|
|
glow:SetPoint("CENTER", frame, "CENTER", 0, 0)
|
|
glow:Hide()
|
|
frame.qualGlow = glow
|
|
|
|
frame.slotID = slotID
|
|
frame.slotName = slotName
|
|
local _, emptyTex = GetInventorySlotInfo(slotName)
|
|
frame.emptyTexture = emptyTex
|
|
|
|
frame:SetScript("OnEnter", function()
|
|
this:SetBackdropBorderColor(T.slotHover[1], T.slotHover[2], T.slotHover[3], T.slotHover[4])
|
|
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
|
|
local unit = GetInspectUnit()
|
|
if GetInventoryItemLink(unit, this.slotID) then
|
|
GameTooltip:SetInventoryItem(unit, this.slotID)
|
|
else
|
|
local label = SLOT_LABEL[this.slotName] or this.slotName
|
|
GameTooltip:AddLine(label .. " - 未装备", 0.5, 0.5, 0.5)
|
|
end
|
|
GameTooltip:Show()
|
|
end)
|
|
|
|
frame:SetScript("OnLeave", function()
|
|
this:SetBackdropBorderColor(T.slotBorder[1], T.slotBorder[2], T.slotBorder[3], T.slotBorder[4])
|
|
GameTooltip:Hide()
|
|
end)
|
|
|
|
frame:SetScript("OnClick", function()
|
|
if IsShiftKeyDown() then
|
|
local unit = GetInspectUnit()
|
|
local link = GetInventoryItemLink(unit, this.slotID)
|
|
if link and ChatFrameEditBox and ChatFrameEditBox:IsVisible() then
|
|
ChatFrameEditBox:Insert(link)
|
|
elseif link then
|
|
if DEFAULT_CHAT_FRAME then
|
|
DEFAULT_CHAT_FRAME:AddMessage(link)
|
|
end
|
|
end
|
|
end
|
|
end)
|
|
|
|
return frame
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- Equipment Page
|
|
--------------------------------------------------------------------------------
|
|
function IP:BuildEquipmentPage()
|
|
if not equipPage or equipPage.built then return end
|
|
equipPage.built = true
|
|
|
|
local CONTENT_W = FRAME_W - INNER_PAD * 2
|
|
local SCROLL_W = CONTENT_W - 10
|
|
local contentH = FRAME_H - HEADER_H - 2 - INNER_PAD - 4
|
|
local scrollArea = CreateScrollFrame(equipPage, CONTENT_W, contentH)
|
|
scrollArea:SetPoint("TOPLEFT", equipPage, "TOPLEFT", 0, 0)
|
|
equipPage.scrollArea = scrollArea
|
|
|
|
local child = scrollArea.child
|
|
local cw = SCROLL_W
|
|
|
|
local slotColW = SLOT_SIZE + 6
|
|
local modelW = cw - slotColW * 2 - 26
|
|
if modelW < 80 then modelW = 80 end
|
|
local modelH = 8 * (SLOT_SIZE + SLOT_GAP) - SLOT_GAP
|
|
|
|
local modelBg = CreateFrame("Frame", nil, child)
|
|
modelBg:SetWidth(modelW)
|
|
modelBg:SetHeight(modelH)
|
|
modelBg:SetPoint("TOP", child, "TOP", 0, -4)
|
|
SetRoundBackdrop(modelBg, T.modelBg, T.modelBorder)
|
|
equipPage.modelBgFrame = modelBg
|
|
|
|
local modelFrame = CreateFrame("Frame", nil, equipPage)
|
|
modelFrame:SetWidth(modelW - 8)
|
|
modelFrame:SetHeight(modelH - 24)
|
|
modelFrame:SetPoint("TOP", modelBg, "TOP", 0, -4)
|
|
modelFrame:SetFrameLevel(equipPage:GetFrameLevel() + 5)
|
|
|
|
local model = CreateFrame("PlayerModel", NextName("Model"), modelFrame)
|
|
model:SetAllPoints(modelFrame)
|
|
equipPage.model = model
|
|
equipPage.modelFrame = modelFrame
|
|
|
|
model:EnableMouse(true)
|
|
model:EnableMouseWheel(1)
|
|
model.rotating = false
|
|
model.panning = false
|
|
model.curFacing = 0.4
|
|
model.curScale = 1.0
|
|
model.posX = 0
|
|
model.posY = 0
|
|
|
|
model:SetScript("OnMouseDown", function()
|
|
if arg1 == "LeftButton" then
|
|
this.rotating = true
|
|
this.startX = GetCursorPosition()
|
|
this.startFacing = this.curFacing or 0
|
|
elseif arg1 == "RightButton" then
|
|
this.panning = true
|
|
local cx, cy = GetCursorPosition()
|
|
this.panStartX = cx
|
|
this.panStartY = cy
|
|
this.panOriginX = this.posX or 0
|
|
this.panOriginY = this.posY or 0
|
|
end
|
|
end)
|
|
model:SetScript("OnMouseUp", function()
|
|
if arg1 == "LeftButton" then
|
|
this.rotating = false
|
|
elseif arg1 == "RightButton" then
|
|
this.panning = false
|
|
end
|
|
end)
|
|
model:SetScript("OnMouseWheel", function()
|
|
local step = 0.1
|
|
local ns = (this.curScale or 1) + arg1 * step
|
|
if ns < 0.2 then ns = 0.2 end
|
|
if ns > 4.0 then ns = 4.0 end
|
|
this.curScale = ns
|
|
this:SetModelScale(ns)
|
|
end)
|
|
model:SetScript("OnUpdate", function()
|
|
if this.rotating then
|
|
local cx = GetCursorPosition()
|
|
local diff = (cx - (this.startX or cx)) * 0.01
|
|
local nf = (this.startFacing or 0) + diff
|
|
this.curFacing = nf
|
|
this:SetFacing(nf)
|
|
elseif this.panning then
|
|
local cx, cy = GetCursorPosition()
|
|
local es = this:GetEffectiveScale()
|
|
if es < 0.01 then es = 1 end
|
|
local dx = (cx - (this.panStartX or cx)) / (es * 35)
|
|
local dy = (cy - (this.panStartY or cy)) / (es * 35)
|
|
this.posX = (this.panOriginX or 0) + dx
|
|
this.posY = (this.panOriginY or 0) + dy
|
|
this:SetPosition(this.posY, 0, this.posX)
|
|
elseif this.autoRotateDir then
|
|
local speed = 1.5 * (arg1 or 0.016)
|
|
this.curFacing = (this.curFacing or 0) + speed * this.autoRotateDir
|
|
this:SetFacing(this.curFacing)
|
|
end
|
|
end)
|
|
model.ResetView = function(self)
|
|
self.curFacing = 0.4
|
|
self.curScale = 1.0
|
|
self.posX = 0
|
|
self.posY = 0
|
|
self:SetFacing(0.4)
|
|
self:SetModelScale(1.0)
|
|
self:SetPosition(0, 0, 0)
|
|
end
|
|
|
|
-- Name/guild overlay floats on top of 3D model
|
|
local nameOverlay = CreateFrame("Frame", nil, equipPage)
|
|
nameOverlay:SetWidth(modelW)
|
|
nameOverlay:SetHeight(30)
|
|
nameOverlay:SetPoint("TOP", modelBg, "TOP", 0, -4)
|
|
nameOverlay:SetFrameLevel(equipPage:GetFrameLevel() + 7)
|
|
nameOverlay:EnableMouse(false)
|
|
|
|
local modelNameText = nameOverlay:CreateFontString(nil, "OVERLAY")
|
|
modelNameText:SetFont(GetFont(), 11, "OUTLINE")
|
|
modelNameText:SetPoint("TOP", nameOverlay, "TOP", 0, -5)
|
|
modelNameText:SetJustifyH("CENTER")
|
|
modelNameText:SetTextColor(T.gold[1], T.gold[2], T.gold[3])
|
|
equipPage.modelNameText = modelNameText
|
|
|
|
local modelGuildText = nameOverlay:CreateFontString(nil, "OVERLAY")
|
|
modelGuildText:SetFont(GetFont(), 9, "OUTLINE")
|
|
modelGuildText:SetPoint("TOP", modelNameText, "BOTTOM", 0, -1)
|
|
modelGuildText:SetJustifyH("CENTER")
|
|
modelGuildText:SetTextColor(T.dimText[1], T.dimText[2], T.dimText[3])
|
|
equipPage.modelGuildText = modelGuildText
|
|
|
|
-- Model toolbar: unified strip with 3 segments (matches CharacterPanel)
|
|
local tbH = 14
|
|
local segW = 24
|
|
local tbW = segW * 3 + 2
|
|
local toolbar = CreateFrame("Frame", nil, equipPage)
|
|
toolbar:SetWidth(tbW)
|
|
toolbar:SetHeight(tbH)
|
|
toolbar:SetPoint("BOTTOM", modelBg, "BOTTOM", 0, 5)
|
|
toolbar:SetFrameLevel(equipPage:GetFrameLevel() + 6)
|
|
SetPixelBackdrop(toolbar, { 0.04, 0.04, 0.06, 0.75 }, { 0.22, 0.22, 0.28, 0.5 })
|
|
equipPage.modelToolbar = toolbar
|
|
|
|
local function MakeToolBtn(parent, w, text, ox)
|
|
local btn = CreateFrame("Button", nil, parent)
|
|
btn:SetWidth(w)
|
|
btn:SetHeight(tbH - 2)
|
|
btn:SetPoint("LEFT", parent, "LEFT", ox, 0)
|
|
btn:SetFrameLevel(parent:GetFrameLevel() + 1)
|
|
btn:SetBackdrop({
|
|
bgFile = "Interface\\Buttons\\WHITE8X8",
|
|
tile = false, tileSize = 0, edgeSize = 0,
|
|
})
|
|
btn:SetBackdropColor(0, 0, 0, 0)
|
|
local fs = MakeFS(btn, 9, "CENTER", { 0.5, 0.5, 0.55 })
|
|
fs:SetPoint("CENTER", btn, "CENTER", 0, 0)
|
|
fs:SetText(text)
|
|
btn.label = fs
|
|
btn:SetScript("OnEnter", function()
|
|
this:SetBackdropColor(T.slotBg[1], T.slotBg[2], T.slotBg[3], T.slotBg[4] or 0.5)
|
|
this.label:SetTextColor(T.nameText[1], T.nameText[2], T.nameText[3])
|
|
end)
|
|
btn:SetScript("OnLeave", function()
|
|
this:SetBackdropColor(0, 0, 0, 0)
|
|
this.label:SetTextColor(T.dimText[1], T.dimText[2], T.dimText[3])
|
|
end)
|
|
return btn
|
|
end
|
|
|
|
local rotLeft = MakeToolBtn(toolbar, segW, "<", 1)
|
|
rotLeft:SetScript("OnMouseDown", function()
|
|
model.autoRotateDir = 1
|
|
this:SetBackdropColor(T.slotHover[1], T.slotHover[2], T.slotHover[3], T.slotHover[4] or 0.6)
|
|
end)
|
|
rotLeft:SetScript("OnMouseUp", function()
|
|
model.autoRotateDir = nil
|
|
this:SetBackdropColor(T.slotBg[1], T.slotBg[2], T.slotBg[3], T.slotBg[4] or 0.5)
|
|
end)
|
|
rotLeft:SetScript("OnLeave", function()
|
|
this:SetBackdropColor(0, 0, 0, 0)
|
|
this.label:SetTextColor(T.dimText[1], T.dimText[2], T.dimText[3])
|
|
model.autoRotateDir = nil
|
|
end)
|
|
equipPage.rotLeft = rotLeft
|
|
|
|
local sep1 = toolbar:CreateTexture(nil, "OVERLAY")
|
|
sep1:SetTexture("Interface\\Buttons\\WHITE8X8")
|
|
sep1:SetVertexColor(T.sepColor[1], T.sepColor[2], T.sepColor[3], T.sepColor[4] or 0.4)
|
|
sep1:SetWidth(1)
|
|
sep1:SetHeight(tbH - 4)
|
|
sep1:SetPoint("LEFT", toolbar, "LEFT", segW + 1, 0)
|
|
|
|
local resetBtn = MakeToolBtn(toolbar, segW, "O", segW + 1)
|
|
resetBtn:SetScript("OnClick", function() model:ResetView() end)
|
|
resetBtn:SetScript("OnEnter", function()
|
|
this:SetBackdropColor(T.slotBg[1], T.slotBg[2], T.slotBg[3], T.slotBg[4] or 0.5)
|
|
this.label:SetTextColor(T.nameText[1], T.nameText[2], T.nameText[3])
|
|
GameTooltip:SetOwner(this, "ANCHOR_BOTTOM")
|
|
GameTooltip:AddLine("重置视角", 1, 1, 1)
|
|
GameTooltip:Show()
|
|
end)
|
|
resetBtn:SetScript("OnLeave", function()
|
|
this:SetBackdropColor(0, 0, 0, 0)
|
|
this.label:SetTextColor(T.dimText[1], T.dimText[2], T.dimText[3])
|
|
GameTooltip:Hide()
|
|
end)
|
|
equipPage.resetBtn = resetBtn
|
|
|
|
local sep2 = toolbar:CreateTexture(nil, "OVERLAY")
|
|
sep2:SetTexture("Interface\\Buttons\\WHITE8X8")
|
|
sep2:SetVertexColor(T.sepColor[1], T.sepColor[2], T.sepColor[3], T.sepColor[4] or 0.4)
|
|
sep2:SetWidth(1)
|
|
sep2:SetHeight(tbH - 4)
|
|
sep2:SetPoint("LEFT", toolbar, "LEFT", segW * 2 + 1, 0)
|
|
|
|
local rotRight = MakeToolBtn(toolbar, segW, ">", segW * 2 + 1)
|
|
rotRight:SetScript("OnMouseDown", function()
|
|
model.autoRotateDir = -1
|
|
this:SetBackdropColor(T.slotHover[1], T.slotHover[2], T.slotHover[3], T.slotHover[4] or 0.6)
|
|
end)
|
|
rotRight:SetScript("OnMouseUp", function()
|
|
model.autoRotateDir = nil
|
|
this:SetBackdropColor(T.slotBg[1], T.slotBg[2], T.slotBg[3], T.slotBg[4] or 0.5)
|
|
end)
|
|
rotRight:SetScript("OnLeave", function()
|
|
this:SetBackdropColor(0, 0, 0, 0)
|
|
this.label:SetTextColor(T.dimText[1], T.dimText[2], T.dimText[3])
|
|
model.autoRotateDir = nil
|
|
end)
|
|
equipPage.rotRight = rotRight
|
|
|
|
local slotY = -6
|
|
equipPage.equipSlots = {}
|
|
|
|
for idx, si in ipairs(EQUIP_SLOTS_LEFT) do
|
|
local slot = self:CreateEquipSlot(child, si.id, si.name)
|
|
slot:SetPoint("TOPLEFT", child, "TOPLEFT", 2, slotY - (idx - 1) * (SLOT_SIZE + SLOT_GAP))
|
|
equipPage.equipSlots[si.id] = slot
|
|
end
|
|
|
|
for idx, si in ipairs(EQUIP_SLOTS_RIGHT) do
|
|
local slot = self:CreateEquipSlot(child, si.id, si.name)
|
|
slot:SetPoint("TOPRIGHT", child, "TOPRIGHT", -2, slotY - (idx - 1) * (SLOT_SIZE + SLOT_GAP))
|
|
equipPage.equipSlots[si.id] = slot
|
|
end
|
|
|
|
local bottomY = slotY - table.getn(EQUIP_SLOTS_LEFT) * (SLOT_SIZE + SLOT_GAP) - 4
|
|
local bottomCount = table.getn(EQUIP_SLOTS_BOTTOM)
|
|
local totalBW = bottomCount * SLOT_SIZE + (bottomCount - 1) * (SLOT_GAP + 2)
|
|
local bsx = math.max((cw - totalBW) / 2, 4)
|
|
|
|
for idx, si in ipairs(EQUIP_SLOTS_BOTTOM) do
|
|
local slot = self:CreateEquipSlot(child, si.id, si.name)
|
|
slot:SetPoint("TOPLEFT", child, "TOPLEFT", bsx + (idx - 1) * (SLOT_SIZE + SLOT_GAP + 1), bottomY)
|
|
equipPage.equipSlots[si.id] = slot
|
|
end
|
|
|
|
local infoY = bottomY - SLOT_SIZE - 2
|
|
equipPage.totalContentH = math.abs(infoY) + 8
|
|
scrollArea:SetContentHeight(equipPage.totalContentH)
|
|
end
|
|
|
|
local ALL_EQUIP_SLOTS = nil
|
|
local function GetAllEquipSlots()
|
|
if not ALL_EQUIP_SLOTS then
|
|
ALL_EQUIP_SLOTS = {}
|
|
for _, t in ipairs({ EQUIP_SLOTS_LEFT, EQUIP_SLOTS_RIGHT, EQUIP_SLOTS_BOTTOM }) do
|
|
for _, s in ipairs(t) do table.insert(ALL_EQUIP_SLOTS, s) end
|
|
end
|
|
end
|
|
return ALL_EQUIP_SLOTS
|
|
end
|
|
|
|
function IP:UpdateEquipment()
|
|
if not equipPage or not equipPage.built then return end
|
|
local unit = GetInspectUnit()
|
|
|
|
if equipPage.model then
|
|
equipPage.model:Show()
|
|
if equipPage.modelFrame then equipPage.modelFrame:Show() end
|
|
if equipPage.modelBgFrame then equipPage.modelBgFrame:Show() end
|
|
if equipPage.modelToolbar then equipPage.modelToolbar:Show() end
|
|
equipPage.model:SetUnit(unit)
|
|
equipPage.model:SetFacing(equipPage.model.curFacing or 0.4)
|
|
equipPage.model:SetModelScale(equipPage.model.curScale or 1.0)
|
|
equipPage.model:SetPosition(equipPage.model.posY or 0, 0, equipPage.model.posX or 0)
|
|
end
|
|
|
|
local allSlots = GetAllEquipSlots()
|
|
local showIlvl = (not SFramesDB or SFramesDB.showItemLevel ~= false) and LibItem_Level
|
|
local totalIlvl, slotCount = 0, 0
|
|
local scoreSlots = { [1]=true,[2]=true,[3]=true,[5]=true,[6]=true,[7]=true,[8]=true,
|
|
[9]=true,[10]=true,[11]=true,[12]=true,[13]=true,[14]=true,[15]=true,[16]=true,[17]=true,[18]=true }
|
|
|
|
for _, si in ipairs(allSlots) do
|
|
local slot = equipPage.equipSlots[si.id]
|
|
if slot then
|
|
local tex = GetInventoryItemTexture(unit, si.id)
|
|
local link = GetInventoryItemLink(unit, si.id)
|
|
if tex then
|
|
slot.icon:SetTexture(tex)
|
|
slot.icon:SetVertexColor(1, 1, 1)
|
|
if slot.ilvlText then
|
|
if showIlvl and link then
|
|
local _, _, itemId = string.find(link, "item:(%d+)")
|
|
local ilvl = itemId and LibItem_Level[tonumber(itemId)]
|
|
slot.ilvlText:SetText(ilvl and tostring(ilvl) or "")
|
|
if ilvl and ilvl > 0 and scoreSlots[si.id] then
|
|
totalIlvl = totalIlvl + ilvl
|
|
slotCount = slotCount + 1
|
|
end
|
|
else
|
|
slot.ilvlText:SetText("")
|
|
end
|
|
end
|
|
local quality = GetItemQualityFromLink(link)
|
|
if quality and quality >= 2 and QUALITY_COLORS[quality] then
|
|
local qc = QUALITY_COLORS[quality]
|
|
slot.qualGlow:SetVertexColor(qc[1], qc[2], qc[3])
|
|
slot.qualGlow:Show()
|
|
else
|
|
slot.qualGlow:Hide()
|
|
end
|
|
else
|
|
if slot.emptyTexture then
|
|
slot.icon:SetTexture(slot.emptyTexture)
|
|
slot.icon:SetVertexColor(T.emptySlot[1], T.emptySlot[2], T.emptySlot[3], T.emptySlot[4])
|
|
else slot.icon:SetTexture(nil) end
|
|
slot:SetBackdropBorderColor(T.slotBorder[1], T.slotBorder[2], T.slotBorder[3], T.slotBorder[4])
|
|
slot.currentBorderColor = nil
|
|
slot.qualGlow:Hide()
|
|
if slot.ilvlText then slot.ilvlText:SetText("") end
|
|
end
|
|
end
|
|
end
|
|
|
|
equipPage.avgIlvl = nil
|
|
if slotCount > 0 then
|
|
equipPage.avgIlvl = totalIlvl / slotCount
|
|
end
|
|
IP:UpdateTitle()
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- Inspect Summary Side Panel (gear stats + equipment list)
|
|
--------------------------------------------------------------------------------
|
|
local inspSummary
|
|
local SUMMARY_W = 220
|
|
local SUMMARY_H = 380
|
|
local SUMMARY_HEADER_H = 24
|
|
local SUMMARY_ROW_H = 14
|
|
local SUMMARY_SECTION_GAP = 4
|
|
|
|
local ENCHANTABLE_INSPECT_SLOTS = {
|
|
{ id = 1, name = "HeadSlot", label = "头部" },
|
|
{ id = 3, name = "ShoulderSlot", label = "肩部" },
|
|
{ id = 15, name = "BackSlot", label = "背部" },
|
|
{ id = 5, name = "ChestSlot", label = "胸部" },
|
|
{ id = 9, name = "WristSlot", label = "手腕" },
|
|
{ id = 10, name = "HandsSlot", label = "手套" },
|
|
{ id = 7, name = "LegsSlot", label = "腿部" },
|
|
{ id = 8, name = "FeetSlot", label = "脚部" },
|
|
{ id = 11, name = "Finger0Slot", label = "戒指1" },
|
|
{ id = 12, name = "Finger1Slot", label = "戒指2" },
|
|
{ id = 16, name = "MainHandSlot", label = "主手" },
|
|
{ id = 17, name = "SecondaryHandSlot", label = "副手" },
|
|
{ id = 18, name = "RangedSlot", label = "远程" },
|
|
}
|
|
|
|
local inspScanTip
|
|
|
|
local function EnsureInspTip()
|
|
if not inspScanTip then
|
|
inspScanTip = CreateFrame("GameTooltip", "SFramesIPScanTip", nil, "GameTooltipTemplate")
|
|
end
|
|
inspScanTip:SetOwner(UIParent, "ANCHOR_NONE")
|
|
return inspScanTip
|
|
end
|
|
|
|
local STAT_PATS = {
|
|
{ p = "%+(%d+)%s*力量", k = "str" },
|
|
{ p = "%+(%d+)%s*敏捷", k = "agi" },
|
|
{ p = "%+(%d+)%s*耐力", k = "sta" },
|
|
{ p = "%+(%d+)%s*智力", k = "int" },
|
|
{ p = "%+(%d+)%s*精神", k = "spi" },
|
|
{ p = "%+(%d+)%s*Strength", k = "str" },
|
|
{ p = "%+(%d+)%s*Agility", k = "agi" },
|
|
{ p = "%+(%d+)%s*Stamina", k = "sta" },
|
|
{ p = "%+(%d+)%s*Intellect", k = "int" },
|
|
{ p = "%+(%d+)%s*Spirit", k = "spi" },
|
|
{ p = "%+(%d+)%s*火焰抗性", k = "fireres" },
|
|
{ p = "%+(%d+)%s*自然抗性", k = "natureres" },
|
|
{ p = "%+(%d+)%s*冰霜抗性", k = "frostres" },
|
|
{ p = "%+(%d+)%s*暗影抗性", k = "shadowres" },
|
|
{ p = "%+(%d+)%s*奥术抗性", k = "arcaneres" },
|
|
{ p = "%+(%d+)%s*所有抗性", k = "allres" },
|
|
{ p = "%+(%d+)%s*Fire Resistance", k = "fireres" },
|
|
{ p = "%+(%d+)%s*Nature Resistance", k = "natureres" },
|
|
{ p = "%+(%d+)%s*Frost Resistance", k = "frostres" },
|
|
{ p = "%+(%d+)%s*Shadow Resistance", k = "shadowres" },
|
|
{ p = "%+(%d+)%s*Arcane Resistance", k = "arcaneres" },
|
|
{ p = "%+(%d+)%s*All Resistances", k = "allres" },
|
|
{ p = "%+(%d+)%s*攻击强度", k = "ap" },
|
|
{ p = "%+(%d+)%s*Attack Power", k = "ap" },
|
|
}
|
|
|
|
local EQUIP_PATS = {
|
|
{ p = "伤害和治疗效果.-最多(%d+)点", k = "spelldmg" },
|
|
{ p = "法术造成的治疗效果.-最多(%d+)点", k = "healing" },
|
|
{ p = "击中几率提高(%d+)%%", k = "hit" },
|
|
{ p = "暴击几率提高(%d+)%%", k = "crit" },
|
|
{ p = "法术暴击几率提高(%d+)%%", k = "spellcrit" },
|
|
{ p = "法术命中几率提高(%d+)%%", k = "spellhit" },
|
|
{ p = "每5秒恢复(%d+)点法力", k = "mp5" },
|
|
{ p = "防御技能提高(%d+)", k = "defense" },
|
|
{ p = "躲闪几率提高(%d+)%%", k = "dodge" },
|
|
{ p = "招架几率提高(%d+)%%", k = "parry" },
|
|
{ p = "格挡几率提高(%d+)%%", k = "block" },
|
|
{ p = "damage and healing done by magical spells.-by up to (%d+)", k = "spelldmg" },
|
|
{ p = "healing done by spells.-by up to (%d+)", k = "healing" },
|
|
{ p = "chance to hit by (%d+)%%", k = "hit" },
|
|
{ p = "critical strike by (%d+)%%", k = "crit" },
|
|
{ p = "spell critical chance by (%d+)%%", k = "spellcrit" },
|
|
{ p = "Restores (%d+) mana per 5 sec", k = "mp5" },
|
|
{ p = "defense skill by (%d+)", k = "defense" },
|
|
{ p = "chance to dodge.-by (%d+)%%", k = "dodge" },
|
|
{ p = "chance to parry.-by (%d+)%%", k = "parry" },
|
|
{ p = "chance to block.-by (%d+)%%", k = "block" },
|
|
}
|
|
|
|
local PROC_INSP_ENCHANTS = {
|
|
"十字军", "Crusader", "吸取生命", "Lifestealing",
|
|
"灼热武器", "Fiery Weapon", "火焰武器",
|
|
"寒冰", "Icy Chill", "邪恶武器", "Unholy Weapon",
|
|
"恶魔杀手", "Demonslaying", "无法被缴械", "Cannot be Disarmed",
|
|
}
|
|
|
|
local function IsInspEnchantLine(txt)
|
|
if string.find(txt, "%+%d") then return true end
|
|
if string.find(txt, "%d+%%") then return true end
|
|
for i = 1, table.getn(PROC_INSP_ENCHANTS) do
|
|
if string.find(txt, PROC_INSP_ENCHANTS[i]) then return true end
|
|
end
|
|
return false
|
|
end
|
|
|
|
local function ScanInspectGearStats()
|
|
local unit = GetInspectUnit()
|
|
local stats = {}
|
|
local allSlots = GetAllEquipSlots()
|
|
local tip = EnsureInspTip()
|
|
|
|
for _, si in ipairs(allSlots) do
|
|
local link = GetInventoryItemLink(unit, si.id)
|
|
if link then
|
|
tip:ClearLines()
|
|
tip:SetInventoryItem(unit, si.id)
|
|
local n = tip:NumLines()
|
|
if n and n > 0 then
|
|
for li = 1, n do
|
|
local obj = _G["SFramesIPScanTipTextLeft" .. li]
|
|
if obj then
|
|
local txt = obj:GetText()
|
|
if txt and txt ~= "" then
|
|
for _, sp in ipairs(STAT_PATS) do
|
|
local _, _, val = string.find(txt, sp.p)
|
|
if val then
|
|
stats[sp.k] = (stats[sp.k] or 0) + tonumber(val)
|
|
end
|
|
end
|
|
local r, g, b = obj:GetTextColor()
|
|
if g > 0.8 and r < 0.5 and b < 0.5 then
|
|
for _, ep in ipairs(EQUIP_PATS) do
|
|
local _, _, val = string.find(txt, ep.p)
|
|
if val then
|
|
stats[ep.k] = (stats[ep.k] or 0) + tonumber(val)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
if stats.allres and stats.allres > 0 then
|
|
local ar = stats.allres
|
|
stats.fireres = (stats.fireres or 0) + ar
|
|
stats.natureres = (stats.natureres or 0) + ar
|
|
stats.frostres = (stats.frostres or 0) + ar
|
|
stats.shadowres = (stats.shadowres or 0) + ar
|
|
stats.arcaneres = (stats.arcaneres or 0) + ar
|
|
stats.allres = nil
|
|
end
|
|
|
|
return stats
|
|
end
|
|
|
|
local function HasAnyStats(stats)
|
|
if not stats then return false end
|
|
for k, v in pairs(stats) do
|
|
if v and v > 0 then return true end
|
|
end
|
|
return false
|
|
end
|
|
|
|
local function GetInspectEnchant(slotId)
|
|
local unit = GetInspectUnit()
|
|
local tip = EnsureInspTip()
|
|
tip:ClearLines()
|
|
tip:SetInventoryItem(unit, slotId)
|
|
local n = tip:NumLines()
|
|
if not n or n < 2 then return false, nil end
|
|
|
|
for i = 2, n do
|
|
local obj = _G["SFramesIPScanTipTextLeft" .. i]
|
|
if obj then
|
|
local txt = obj:GetText()
|
|
if txt and txt ~= "" then
|
|
local r, g, b = obj:GetTextColor()
|
|
if g > 0.8 and r < 0.5 and b < 0.5 then
|
|
local skip = false
|
|
if string.find(txt, "装备:") or string.find(txt, "装备:") or string.find(txt, "Equip:") then
|
|
skip = true
|
|
elseif string.find(txt, "使用:") or string.find(txt, "使用:") or string.find(txt, "Use:") then
|
|
skip = true
|
|
elseif string.find(txt, "击中时可能") or string.find(txt, "Chance on hit") then
|
|
skip = true
|
|
elseif string.find(txt, "^%(") then
|
|
skip = true
|
|
elseif string.find(txt, "套装:") or string.find(txt, "套装:") or string.find(txt, "Set:") then
|
|
skip = true
|
|
end
|
|
if not skip and IsInspEnchantLine(txt) then
|
|
return true, txt
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return false, nil
|
|
end
|
|
|
|
-- Row helpers for summary panel
|
|
local function HideSummaryRows(parent)
|
|
if parent._r then
|
|
for i = 1, table.getn(parent._r) do
|
|
local r = parent._r[i]
|
|
if r and r.Hide then r:Hide() end
|
|
end
|
|
end
|
|
parent._r = {}
|
|
end
|
|
|
|
local function AddSummaryHeader(p, txt, y, clr)
|
|
local f1 = MakeFS(p, 10, "LEFT", clr or T.sectionTitle)
|
|
f1:SetPoint("TOPLEFT", p, "TOPLEFT", 4, y)
|
|
f1:SetText(txt)
|
|
table.insert(p._r, f1)
|
|
|
|
local s = p:CreateTexture(nil, "ARTWORK")
|
|
s:SetTexture("Interface\\Buttons\\WHITE8X8")
|
|
s:SetVertexColor(T.sepColor[1], T.sepColor[2], T.sepColor[3], T.sepColor[4])
|
|
s:SetHeight(1)
|
|
s:SetPoint("TOPLEFT", p, "TOPLEFT", 4, y - 13)
|
|
s:SetPoint("TOPRIGHT", p, "TOPRIGHT", -4, y - 13)
|
|
table.insert(p._r, s)
|
|
return y - 16
|
|
end
|
|
|
|
local function AddSummaryRow(p, lbl, val, y, lc, vc)
|
|
local f1 = MakeFS(p, 9, "LEFT", lc or T.labelText)
|
|
f1:SetPoint("TOPLEFT", p, "TOPLEFT", 8, y)
|
|
f1:SetText(lbl)
|
|
table.insert(p._r, f1)
|
|
|
|
local f2 = MakeFS(p, 9, "RIGHT", vc or T.valueText)
|
|
f2:SetPoint("TOPRIGHT", p, "TOPRIGHT", -12, y)
|
|
f2:SetWidth(80)
|
|
f2:SetJustifyH("RIGHT")
|
|
f2:SetText(val)
|
|
table.insert(p._r, f2)
|
|
return y - SUMMARY_ROW_H
|
|
end
|
|
|
|
local INSP_STAT_COLORS = {
|
|
str = { 0.78, 0.61, 0.43 }, agi = { 0.52, 1, 0.52 },
|
|
sta = { 0.75, 0.55, 0.25 }, int = { 0.41, 0.80, 0.94 },
|
|
spi = { 1, 1, 1 },
|
|
}
|
|
local INSP_RESIST_COLORS = {
|
|
arcaneres = { 0.95, 0.90, 0.40 }, fireres = { 1, 0.50, 0.15 },
|
|
natureres = { 0.35, 0.90, 0.25 }, frostres = { 0.45, 0.70, 1.00 },
|
|
shadowres = { 0.60, 0.35, 0.90 },
|
|
}
|
|
local INSP_PHYS_COLOR = { 0.90, 0.75, 0.55 }
|
|
local INSP_SPELL_COLOR = { 0.60, 0.80, 1.00 }
|
|
local INSP_REGEN_COLOR = { 0.40, 0.90, 0.70 }
|
|
local INSP_DEF_COLOR = { 0.35, 0.90, 0.25 }
|
|
local INSP_ENCHANTED = { 0.30, 1, 0.30 }
|
|
local INSP_NO_ENCHANT = { 1, 0.35, 0.35 }
|
|
|
|
local function BuildInspectSummary()
|
|
if inspSummary then return inspSummary end
|
|
|
|
local f = CreateFrame("Frame", "SFramesInspectSummary", UIParent)
|
|
f:SetWidth(SUMMARY_W)
|
|
f:SetHeight(SUMMARY_H)
|
|
f:SetFrameStrata("HIGH")
|
|
f:EnableMouse(true)
|
|
f:SetMovable(true)
|
|
f:RegisterForDrag("LeftButton")
|
|
f:SetScript("OnDragStart", function() this:StartMoving() end)
|
|
f:SetScript("OnDragStop", function() this:StopMovingOrSizing() end)
|
|
f:SetClampedToScreen(true)
|
|
SetRoundBackdrop(f, T.bg, T.border)
|
|
CreateShadow(f, 4)
|
|
f:Hide()
|
|
|
|
local hdr = MakeFS(f, 11, "LEFT", T.gold)
|
|
hdr:SetPoint("TOPLEFT", f, "TOPLEFT", 8, -5)
|
|
hdr:SetText("装备属性")
|
|
|
|
local tabW = 52
|
|
f.tabs = {}
|
|
f.curTab = 1
|
|
local tNames = { "属性", "装备" }
|
|
for i = 1, 2 do
|
|
local btn = CreateFrame("Button", NextName("IST"), f)
|
|
btn:SetWidth(tabW)
|
|
btn:SetHeight(16)
|
|
btn:SetPoint("TOPRIGHT", f, "TOPRIGHT", -(8 + (2 - i) * (tabW + 2) + 18), -4)
|
|
btn:SetFrameLevel(f:GetFrameLevel() + 2)
|
|
SetPixelBackdrop(btn, T.btnBg, T.btnBorder)
|
|
local lbl = MakeFS(btn, 9, "CENTER", T.dimText)
|
|
lbl:SetPoint("CENTER", btn, "CENTER", 0, 0)
|
|
lbl:SetText(tNames[i])
|
|
btn.lbl = lbl
|
|
btn.idx = i
|
|
btn:SetScript("OnClick", function() IP:SetSummaryTab(this.idx) end)
|
|
btn:SetScript("OnEnter", function()
|
|
if this.idx ~= (inspSummary.curTab or 1) then
|
|
this:SetBackdropColor(T.btnHover[1], T.btnHover[2], T.btnHover[3], T.btnHover[4])
|
|
end
|
|
end)
|
|
btn:SetScript("OnLeave", function()
|
|
if this.idx ~= (inspSummary.curTab or 1) then
|
|
SetPixelBackdrop(this, T.btnBg, T.btnBorder)
|
|
end
|
|
end)
|
|
f.tabs[i] = btn
|
|
end
|
|
|
|
local closeBtn = CreateFrame("Button", nil, f)
|
|
closeBtn:SetWidth(14)
|
|
closeBtn:SetHeight(14)
|
|
closeBtn:SetPoint("TOPRIGHT", f, "TOPRIGHT", -4, -5)
|
|
closeBtn:SetFrameLevel(f:GetFrameLevel() + 3)
|
|
SetRoundBackdrop(closeBtn, T.buttonDownBg, T.btnBorder)
|
|
local closeTxt = MakeFS(closeBtn, 8, "CENTER", T.title)
|
|
closeTxt:SetPoint("CENTER", closeBtn, "CENTER", 0, 0)
|
|
closeTxt:SetText("x")
|
|
closeBtn:SetScript("OnClick", function() f:Hide() end)
|
|
closeBtn:SetScript("OnEnter", function()
|
|
this:SetBackdropColor(T.btnHoverBg[1], T.btnHoverBg[2], T.btnHoverBg[3], T.btnHoverBg[4])
|
|
this:SetBackdropBorderColor(T.btnHoverBd[1], T.btnHoverBd[2], T.btnHoverBd[3], T.btnHoverBd[4])
|
|
end)
|
|
closeBtn:SetScript("OnLeave", function()
|
|
this:SetBackdropColor(T.buttonDownBg[1], T.buttonDownBg[2], T.buttonDownBg[3], T.buttonDownBg[4])
|
|
this:SetBackdropBorderColor(T.btnBorder[1], T.btnBorder[2], T.btnBorder[3], T.btnBorder[4])
|
|
end)
|
|
|
|
MakeSep(f, 4, -SUMMARY_HEADER_H, -4, -SUMMARY_HEADER_H)
|
|
|
|
local cH = SUMMARY_H - SUMMARY_HEADER_H - 8
|
|
local cW = SUMMARY_W - 8
|
|
|
|
f.statsScroll = CreateScrollFrame(f, cW, cH)
|
|
f.statsScroll:SetPoint("TOPLEFT", f, "TOPLEFT", 4, -(SUMMARY_HEADER_H + 2))
|
|
|
|
f.equipScroll = CreateScrollFrame(f, cW, cH)
|
|
f.equipScroll:SetPoint("TOPLEFT", f, "TOPLEFT", 4, -(SUMMARY_HEADER_H + 2))
|
|
f.equipScroll:Hide()
|
|
|
|
inspSummary = f
|
|
return f
|
|
end
|
|
|
|
function IP:SetSummaryTab(idx)
|
|
if not inspSummary then return end
|
|
inspSummary.curTab = idx
|
|
for i = 1, 2 do
|
|
local btn = inspSummary.tabs[i]
|
|
if i == idx then
|
|
SetPixelBackdrop(btn, T.tabActiveBg, T.tabActiveBorder)
|
|
btn.lbl:SetTextColor(T.gold[1], T.gold[2], T.gold[3])
|
|
else
|
|
SetPixelBackdrop(btn, T.btnBg, T.btnBorder)
|
|
btn.lbl:SetTextColor(T.dimText[1], T.dimText[2], T.dimText[3])
|
|
end
|
|
end
|
|
if idx == 1 then
|
|
inspSummary.statsScroll:Show()
|
|
inspSummary.equipScroll:Hide()
|
|
local ok, err = pcall(function() IP:BuildInspectStats() end)
|
|
if not ok then
|
|
DEFAULT_CHAT_FRAME:AddMessage("|cffff4444IP BuildStats err: " .. tostring(err) .. "|r")
|
|
end
|
|
else
|
|
inspSummary.statsScroll:Hide()
|
|
inspSummary.equipScroll:Show()
|
|
local ok, err = pcall(function() IP:BuildInspectEquipList() end)
|
|
if not ok then
|
|
DEFAULT_CHAT_FRAME:AddMessage("|cffff4444IP BuildEquip err: " .. tostring(err) .. "|r")
|
|
end
|
|
end
|
|
end
|
|
|
|
function IP:BuildInspectStats()
|
|
if not inspSummary then return end
|
|
local child = inspSummary.statsScroll.child
|
|
HideSummaryRows(child)
|
|
local y = -4
|
|
|
|
local stats = ScanInspectGearStats()
|
|
|
|
local pNames = {
|
|
{ k = "str", n = "力量" }, { k = "agi", n = "敏捷" },
|
|
{ k = "sta", n = "耐力" }, { k = "int", n = "智力" },
|
|
{ k = "spi", n = "精神" },
|
|
}
|
|
local hasP = false
|
|
for _, p in ipairs(pNames) do
|
|
if stats[p.k] and stats[p.k] > 0 then hasP = true; break end
|
|
end
|
|
if hasP then
|
|
y = AddSummaryHeader(child, "装备主属性:", y)
|
|
for _, p in ipairs(pNames) do
|
|
local v = stats[p.k] or 0
|
|
if v > 0 then
|
|
local c = INSP_STAT_COLORS[p.k] or T.valueText
|
|
y = AddSummaryRow(child, p.n, "+" .. tostring(v), y, c, c)
|
|
end
|
|
end
|
|
y = y - SUMMARY_SECTION_GAP
|
|
end
|
|
|
|
local hasPhys = (stats.ap and stats.ap > 0) or (stats.hit and stats.hit > 0) or (stats.crit and stats.crit > 0)
|
|
if hasPhys then
|
|
y = AddSummaryHeader(child, "物理:", y, INSP_PHYS_COLOR)
|
|
if stats.ap and stats.ap > 0 then
|
|
y = AddSummaryRow(child, "攻击强度", "+" .. tostring(stats.ap), y, nil, INSP_PHYS_COLOR)
|
|
end
|
|
if stats.hit and stats.hit > 0 then
|
|
y = AddSummaryRow(child, "命中", "+" .. tostring(stats.hit) .. "%", y)
|
|
end
|
|
if stats.crit and stats.crit > 0 then
|
|
y = AddSummaryRow(child, "暴击", "+" .. tostring(stats.crit) .. "%", y)
|
|
end
|
|
y = y - SUMMARY_SECTION_GAP
|
|
end
|
|
|
|
local hasSpell = (stats.spelldmg and stats.spelldmg > 0) or (stats.healing and stats.healing > 0)
|
|
or (stats.spellcrit and stats.spellcrit > 0) or (stats.spellhit and stats.spellhit > 0)
|
|
if hasSpell then
|
|
y = AddSummaryHeader(child, "法术:", y, INSP_SPELL_COLOR)
|
|
if stats.spelldmg and stats.spelldmg > 0 then
|
|
y = AddSummaryRow(child, "法术伤害", "+" .. tostring(stats.spelldmg), y, nil, INSP_SPELL_COLOR)
|
|
end
|
|
if stats.healing and stats.healing > 0 then
|
|
y = AddSummaryRow(child, "法术治疗", "+" .. tostring(stats.healing), y)
|
|
end
|
|
if stats.spellcrit and stats.spellcrit > 0 then
|
|
y = AddSummaryRow(child, "法术暴击", "+" .. tostring(stats.spellcrit) .. "%", y)
|
|
end
|
|
if stats.spellhit and stats.spellhit > 0 then
|
|
y = AddSummaryRow(child, "法术命中", "+" .. tostring(stats.spellhit) .. "%", y)
|
|
end
|
|
y = y - SUMMARY_SECTION_GAP
|
|
end
|
|
|
|
local hasDef = (stats.defense and stats.defense > 0) or (stats.dodge and stats.dodge > 0)
|
|
or (stats.parry and stats.parry > 0) or (stats.block and stats.block > 0)
|
|
if hasDef then
|
|
y = AddSummaryHeader(child, "防御:", y, INSP_DEF_COLOR)
|
|
if stats.defense and stats.defense > 0 then
|
|
y = AddSummaryRow(child, "防御技能", "+" .. tostring(stats.defense), y, nil, INSP_DEF_COLOR)
|
|
end
|
|
if stats.dodge and stats.dodge > 0 then
|
|
y = AddSummaryRow(child, "躲闪", "+" .. tostring(stats.dodge) .. "%", y)
|
|
end
|
|
if stats.parry and stats.parry > 0 then
|
|
y = AddSummaryRow(child, "招架", "+" .. tostring(stats.parry) .. "%", y)
|
|
end
|
|
if stats.block and stats.block > 0 then
|
|
y = AddSummaryRow(child, "格挡", "+" .. tostring(stats.block) .. "%", y)
|
|
end
|
|
y = y - SUMMARY_SECTION_GAP
|
|
end
|
|
|
|
if stats.mp5 and stats.mp5 > 0 then
|
|
y = AddSummaryHeader(child, "回复:", y, INSP_REGEN_COLOR)
|
|
y = AddSummaryRow(child, "装备回蓝", tostring(stats.mp5) .. " MP/5s", y, nil, INSP_REGEN_COLOR)
|
|
y = y - SUMMARY_SECTION_GAP
|
|
end
|
|
|
|
local resInfo = {
|
|
{ k = "arcaneres", n = "奥术抗性" }, { k = "fireres", n = "火焰抗性" },
|
|
{ k = "natureres", n = "自然抗性" }, { k = "frostres", n = "冰霜抗性" },
|
|
{ k = "shadowres", n = "暗影抗性" },
|
|
}
|
|
local hasRes = false
|
|
for _, ri in ipairs(resInfo) do
|
|
if stats[ri.k] and stats[ri.k] > 0 then hasRes = true; break end
|
|
end
|
|
if hasRes then
|
|
y = AddSummaryHeader(child, "抗性:", y, INSP_RESIST_COLORS.arcaneres)
|
|
for _, ri in ipairs(resInfo) do
|
|
local v = stats[ri.k] or 0
|
|
if v > 0 then
|
|
local c = INSP_RESIST_COLORS[ri.k] or T.valueText
|
|
y = AddSummaryRow(child, ri.n, "+" .. tostring(v), y, c, c)
|
|
end
|
|
end
|
|
y = y - SUMMARY_SECTION_GAP
|
|
end
|
|
|
|
if not HasAnyStats(stats) then
|
|
y = AddSummaryHeader(child, "提示:", y, T.dimText)
|
|
local noData = MakeFS(child, 9, "LEFT", T.dimText)
|
|
noData:SetPoint("TOPLEFT", child, "TOPLEFT", 8, y)
|
|
noData:SetText("未获取到装备属性数据")
|
|
table.insert(child._r, noData)
|
|
y = y - SUMMARY_ROW_H
|
|
end
|
|
|
|
inspSummary.statsScroll:SetContentHeight(math.abs(y) + 12)
|
|
end
|
|
|
|
function IP:BuildInspectEquipList()
|
|
if not inspSummary then return end
|
|
local child = inspSummary.equipScroll.child
|
|
HideSummaryRows(child)
|
|
local y = -4
|
|
|
|
y = AddSummaryHeader(child, "装备列表 & 附魔检查:", y, T.gold)
|
|
|
|
local unit = GetInspectUnit()
|
|
local totalSlots = 0
|
|
local enchCount = 0
|
|
|
|
for si = 1, table.getn(ENCHANTABLE_INSPECT_SLOTS) do
|
|
local slot = ENCHANTABLE_INSPECT_SLOTS[si]
|
|
local link = GetInventoryItemLink(unit, slot.id)
|
|
|
|
if link then
|
|
totalSlots = totalSlots + 1
|
|
local _, _, rawName = string.find(link, "%[(.-)%]")
|
|
local itemName = rawName or slot.label
|
|
|
|
local quality = nil
|
|
local _, _, qH = string.find(link, "|c(%x+)|H")
|
|
local qMap = {
|
|
["ff9d9d9d"] = 0, ["ffffffff"] = 1, ["ff1eff00"] = 2,
|
|
["ff0070dd"] = 3, ["ffa335ee"] = 4, ["ffff8000"] = 5,
|
|
}
|
|
if qH then quality = qMap[qH] end
|
|
local nc = (quality and QUALITY_COLORS[quality]) or T.valueText
|
|
|
|
local sf = MakeFS(child, 8, "LEFT", T.dimText)
|
|
sf:SetPoint("TOPLEFT", child, "TOPLEFT", 4, y)
|
|
sf:SetText(slot.label)
|
|
sf:SetWidth(32)
|
|
table.insert(child._r, sf)
|
|
|
|
local nf = MakeFS(child, 9, "LEFT", nc)
|
|
nf:SetPoint("TOPLEFT", child, "TOPLEFT", 38, y)
|
|
nf:SetWidth(120)
|
|
if string.len(itemName) > 16 then
|
|
itemName = string.sub(itemName, 1, 14) .. ".."
|
|
end
|
|
nf:SetText(itemName)
|
|
table.insert(child._r, nf)
|
|
|
|
local hasE, eTxt = false, nil
|
|
local eOk, eR1, eR2 = pcall(GetInspectEnchant, slot.id)
|
|
if eOk then hasE = eR1; eTxt = eR2 end
|
|
|
|
local ico = MakeFS(child, 9, "RIGHT")
|
|
ico:SetPoint("TOPRIGHT", child, "TOPRIGHT", -8, y)
|
|
if hasE then
|
|
ico:SetTextColor(INSP_ENCHANTED[1], INSP_ENCHANTED[2], INSP_ENCHANTED[3])
|
|
ico:SetText("*")
|
|
enchCount = enchCount + 1
|
|
else
|
|
ico:SetTextColor(INSP_NO_ENCHANT[1], INSP_NO_ENCHANT[2], INSP_NO_ENCHANT[3])
|
|
ico:SetText("-")
|
|
end
|
|
table.insert(child._r, ico)
|
|
|
|
y = y - SUMMARY_ROW_H
|
|
|
|
if hasE and eTxt then
|
|
local ef = MakeFS(child, 8, "LEFT", INSP_ENCHANTED)
|
|
ef:SetPoint("TOPLEFT", child, "TOPLEFT", 38, y)
|
|
ef:SetWidth(145)
|
|
ef:SetText(" " .. eTxt)
|
|
table.insert(child._r, ef)
|
|
y = y - 12
|
|
elseif not hasE then
|
|
local ef = MakeFS(child, 8, "LEFT", INSP_NO_ENCHANT)
|
|
ef:SetPoint("TOPLEFT", child, "TOPLEFT", 38, y)
|
|
ef:SetText(" 未附魔")
|
|
table.insert(child._r, ef)
|
|
y = y - 12
|
|
end
|
|
y = y - 2
|
|
else
|
|
local sf = MakeFS(child, 8, "LEFT", T.dimText)
|
|
sf:SetPoint("TOPLEFT", child, "TOPLEFT", 4, y)
|
|
sf:SetText(slot.label)
|
|
table.insert(child._r, sf)
|
|
|
|
local ef = MakeFS(child, 9, "LEFT", T.dimText)
|
|
ef:SetPoint("TOPLEFT", child, "TOPLEFT", 38, y)
|
|
ef:SetText("-- 未装备 --")
|
|
table.insert(child._r, ef)
|
|
y = y - SUMMARY_ROW_H - 2
|
|
end
|
|
end
|
|
|
|
y = y - SUMMARY_SECTION_GAP
|
|
y = AddSummaryHeader(child, "附魔统计:", y, T.gold)
|
|
local sc2 = (enchCount == totalSlots and totalSlots > 0) and INSP_ENCHANTED or INSP_NO_ENCHANT
|
|
y = AddSummaryRow(child, "已附魔/总装备", enchCount .. "/" .. totalSlots, y, nil, sc2)
|
|
if enchCount < totalSlots then
|
|
y = AddSummaryRow(child, "缺少附魔", tostring(totalSlots - enchCount) .. " 件", y, INSP_NO_ENCHANT, INSP_NO_ENCHANT)
|
|
end
|
|
|
|
inspSummary.equipScroll:SetContentHeight(math.abs(y) + 12)
|
|
end
|
|
|
|
function IP:ShowSummary()
|
|
BuildInspectSummary()
|
|
if ipanel and ipanel:IsShown() then
|
|
inspSummary:ClearAllPoints()
|
|
inspSummary:SetPoint("TOPLEFT", ipanel, "TOPRIGHT", 2, 0)
|
|
else
|
|
inspSummary:ClearAllPoints()
|
|
inspSummary:SetPoint("CENTER", UIParent, "CENTER", 200, 0)
|
|
end
|
|
inspSummary:Show()
|
|
|
|
IP:SetSummaryTab(2)
|
|
end
|
|
|
|
function IP:HideSummary()
|
|
if inspSummary then inspSummary:Hide() end
|
|
end
|
|
|
|
function IP:RefreshSummary()
|
|
if not inspSummary or not inspSummary:IsShown() then return end
|
|
IP:SetSummaryTab(inspSummary.curTab or 1)
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- Open native InspectFrame for a specific tab (honor / talent)
|
|
-- Turtle WoW tabs: 1=角色, 2=荣誉, 3=天赋, ...
|
|
--------------------------------------------------------------------------------
|
|
local NATIVE_TAB_INDEX = { honor = 2, talent = 3 }
|
|
|
|
function IP:OpenNativeTab(which)
|
|
local unit = inspectUnit or "target"
|
|
if not UnitExists(unit) then return end
|
|
|
|
local tabIdx = NATIVE_TAB_INDEX[which]
|
|
|
|
self.switchingToNative = true
|
|
self.allowNativeFrame = true
|
|
|
|
if ipanel then ipanel:Hide() end
|
|
self.switchingToNative = false
|
|
|
|
if not IsAddOnLoaded("Blizzard_InspectUI") then
|
|
pcall(function() LoadAddOn("Blizzard_InspectUI") end)
|
|
end
|
|
|
|
local inspF = _G["InspectFrame"]
|
|
if inspF and inspF._origShow then
|
|
inspF.Show = inspF._origShow
|
|
inspF._origShow = nil
|
|
end
|
|
|
|
if IP._origInspectUnit then
|
|
IP._origInspectUnit(unit)
|
|
end
|
|
|
|
inspF = _G["InspectFrame"]
|
|
if inspF then
|
|
inspF:SetAlpha(1)
|
|
inspF:Show()
|
|
end
|
|
|
|
if tabIdx then
|
|
local tab = _G["InspectFrameTab" .. tabIdx]
|
|
if tab and tab.Click then
|
|
tab:Click()
|
|
elseif tab and tab:GetScript("OnClick") then
|
|
tab:GetScript("OnClick")()
|
|
end
|
|
end
|
|
|
|
if not self._resetFrame then
|
|
self._resetFrame = CreateFrame("Frame", nil, UIParent)
|
|
end
|
|
self._resetFrame.elapsed = 0
|
|
self._resetFrame:SetScript("OnUpdate", function()
|
|
this.elapsed = (this.elapsed or 0) + (arg1 or 0)
|
|
if this.elapsed > 1.0 then
|
|
IP.allowNativeFrame = false
|
|
this:SetScript("OnUpdate", nil)
|
|
end
|
|
end)
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- Open / Close / Toggle
|
|
--------------------------------------------------------------------------------
|
|
function IP:Open(unit)
|
|
inspectUnit = unit or "target"
|
|
|
|
if not UnitExists(inspectUnit) then
|
|
if SFrames and SFrames.Print then
|
|
SFrames:Print("没有有效的观察目标。")
|
|
end
|
|
return
|
|
end
|
|
if not UnitIsPlayer(inspectUnit) then
|
|
if SFrames and SFrames.Print then
|
|
SFrames:Print("只能观察其他玩家。")
|
|
end
|
|
return
|
|
end
|
|
|
|
CreateMainFrame()
|
|
self:BuildEquipmentPage()
|
|
|
|
NotifyInspect(inspectUnit)
|
|
|
|
ipanel:Show()
|
|
self:UpdateTitle()
|
|
self:UpdateEquipment()
|
|
self:ShowSummary()
|
|
end
|
|
|
|
function IP:Close()
|
|
self:HideSummary()
|
|
if ipanel then ipanel:Hide() end
|
|
if ClearInspectPlayer then ClearInspectPlayer() end
|
|
inspectUnit = nil
|
|
end
|
|
|
|
function IP:Toggle(unit)
|
|
if ipanel and ipanel:IsShown() then
|
|
self:Close()
|
|
else
|
|
self:Open(unit)
|
|
end
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- Events & Hooking
|
|
--------------------------------------------------------------------------------
|
|
local eventFrame = CreateFrame("Frame", "SFramesIPEvents", UIParent)
|
|
eventFrame:RegisterEvent("UNIT_INVENTORY_CHANGED")
|
|
eventFrame:RegisterEvent("ADDON_LOADED")
|
|
|
|
eventFrame:SetScript("OnEvent", function()
|
|
if event == "ADDON_LOADED" then
|
|
-- Re-hook InspectUnit whenever a new addon loads (in case it replaces our hook)
|
|
if type(InspectUnit) == "function" and InspectUnit ~= IP._ourHook then
|
|
IP._origInspectUnit = InspectUnit
|
|
local hook = function(unit)
|
|
if (SFramesDB and SFramesDB.enableInspect == false) or IP.allowNativeFrame then
|
|
if IP._origInspectUnit then
|
|
IP._origInspectUnit(unit)
|
|
end
|
|
return
|
|
end
|
|
IP:Open(unit or "target")
|
|
end
|
|
IP._ourHook = hook
|
|
InspectUnit = hook
|
|
end
|
|
return
|
|
end
|
|
|
|
if event == "UNIT_INVENTORY_CHANGED" then
|
|
if not ipanel or not ipanel:IsShown() then return end
|
|
local unit = GetInspectUnit()
|
|
if arg1 and UnitIsUnit(arg1, unit) then
|
|
IP:UpdateEquipment()
|
|
IP:RefreshSummary()
|
|
end
|
|
end
|
|
end)
|
|
|
|
-- Hook UnitPopup_OnClick to catch right-click menu "Inspect"
|
|
if UnitPopup_OnClick then
|
|
local orig_UnitPopup_OnClick = UnitPopup_OnClick
|
|
UnitPopup_OnClick = function()
|
|
local button = this and this.value
|
|
if button == "INSPECT" and not (SFramesDB and SFramesDB.enableInspect == false) then
|
|
local dropdownFrame = _G[UIDROPDOWNMENU_INIT_MENU]
|
|
local unit = (dropdownFrame and dropdownFrame.unit) or "target"
|
|
IP:Open(unit)
|
|
return
|
|
end
|
|
return orig_UnitPopup_OnClick()
|
|
end
|
|
end
|
|
|
|
-- Hook InspectUnit
|
|
local function HookInspectUnit()
|
|
if type(InspectUnit) ~= "function" then return end
|
|
if InspectUnit == IP._ourHook then return end
|
|
IP._origInspectUnit = InspectUnit
|
|
local hook = function(unit)
|
|
if (SFramesDB and SFramesDB.enableInspect == false) or IP.allowNativeFrame then
|
|
if IP._origInspectUnit then
|
|
IP._origInspectUnit(unit)
|
|
end
|
|
return
|
|
end
|
|
IP:Open(unit or "target")
|
|
end
|
|
IP._ourHook = hook
|
|
InspectUnit = hook
|
|
end
|
|
|
|
-- Hook ShowUIPanel to block native InspectFrame when we don't want it
|
|
if ShowUIPanel then
|
|
local _origShowUIPanel = ShowUIPanel
|
|
ShowUIPanel = function(frame)
|
|
if frame and frame.GetName and not IP.allowNativeFrame and not (SFramesDB and SFramesDB.enableInspect == false) then
|
|
local name = frame:GetName() or ""
|
|
if string.find(name, "Inspect") then
|
|
return
|
|
end
|
|
end
|
|
return _origShowUIPanel(frame)
|
|
end
|
|
end
|
|
|
|
HookInspectUnit()
|