Files
Nanami-UI/StatSummary.lua
2026-03-31 18:03:23 +08:00

869 lines
30 KiB
Lua

--------------------------------------------------------------------------------
-- Nanami-UI: Stat Summary & Equipment List (StatSummary.lua)
--------------------------------------------------------------------------------
SFrames.StatSummary = {}
local SS = SFrames.StatSummary
local summaryFrame
--------------------------------------------------------------------------------
-- Theme (reuse CharacterPanel colors)
--------------------------------------------------------------------------------
local T = SFrames.Theme:Extend({
enchanted = { 0.30, 1, 0.30 },
noEnchant = { 1, 0.35, 0.35 },
setColor = { 1, 0.85, 0.0 },
defColor = { 0.35, 0.90, 0.25 },
physColor = { 0.90, 0.75, 0.55 },
spellColor = { 0.60, 0.80, 1.00 },
regenColor = { 0.40, 0.90, 0.70 },
statColors = {
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 },
},
resistColors = {
arcane = { 0.95, 0.90, 0.40 },
fire = { 1, 0.50, 0.15 },
nature = { 0.35, 0.90, 0.25 },
frost = { 0.45, 0.70, 1.00 },
shadow = { 0.60, 0.35, 0.90 },
},
})
local PANEL_W = 220
local PANEL_H = 490
local HEADER_H = 24
local ROW_H = 14
local SECTION_GAP = 4
local SIDE_PAD = 6
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 ENCHANTABLE_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 widgetId = 0
--------------------------------------------------------------------------------
-- Helpers
--------------------------------------------------------------------------------
local function GetFont()
if SFrames and SFrames.GetFont then return SFrames:GetFont() end
return "Fonts\\ARIALN.TTF"
end
local function NN(p)
widgetId = widgetId + 1
return "SFramesSS" .. (p or "") .. widgetId
end
local function SetBD(f, bg, bd)
f: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 b = bg or T.bg
local d = bd or T.border
f:SetBackdropColor(b[1], b[2], b[3], b[4] or 1)
f:SetBackdropBorderColor(d[1], d[2], d[3], d[4] or 1)
end
local function SetPBD(f, bg, bd)
f: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 bg then f:SetBackdropColor(bg[1], bg[2], bg[3], bg[4] or 1) end
if bd then f:SetBackdropBorderColor(bd[1], bd[2], bd[3], bd[4] or 1) end
end
local function FS(parent, size, jh, color)
local fs = parent:CreateFontString(nil, "OVERLAY")
fs:SetFont(GetFont(), size or 10, "OUTLINE")
fs:SetJustifyH(jh or "LEFT")
local c = color or T.valueText
fs:SetTextColor(c[1], c[2], c[3])
return fs
end
--------------------------------------------------------------------------------
-- Stat helpers
--------------------------------------------------------------------------------
local function GetIBL()
if AceLibrary and AceLibrary.HasInstance and AceLibrary:HasInstance("ItemBonusLib-1.0") then
local ok, lib = pcall(function() return AceLibrary("ItemBonusLib-1.0") end)
if ok and lib then return lib end
end
return nil
end
local function GearB(key)
local lib = GetIBL()
if lib and lib.GetBonus then return lib:GetBonus(key) or 0 end
return 0
end
local function TryAPI(names)
for i = 1, table.getn(names) do
local fn = _G[names[i]]
if fn then
local ok, val = pcall(fn)
if ok and type(val) == "number" and val > 0 then return val end
end
end
return 0
end
local function TryAPIa(names, a1)
for i = 1, table.getn(names) do
local fn = _G[names[i]]
if fn then
local ok, val = pcall(fn, a1)
if ok and type(val) == "number" and val > 0 then return val end
end
end
return 0
end
local function GetCS()
if SFrames and SFrames.CharacterPanel and SFrames.CharacterPanel.CS then
return SFrames.CharacterPanel.CS
end
return nil
end
--------------------------------------------------------------------------------
-- Scroll frame
--------------------------------------------------------------------------------
local function MakeScroll(parent, w, h)
local holder = CreateFrame("Frame", NN("H"), parent)
holder:SetWidth(w)
holder:SetHeight(h)
local sf = CreateFrame("ScrollFrame", NN("S"), holder)
sf:SetPoint("TOPLEFT", holder, "TOPLEFT", 0, 0)
sf:SetPoint("BOTTOMRIGHT", holder, "BOTTOMRIGHT", -8, 0)
local child = CreateFrame("Frame", NN("C"), sf)
child:SetWidth(w - 12)
child:SetHeight(1)
sf:SetScrollChild(child)
local sb = CreateFrame("Slider", NN("B"), holder)
sb:SetWidth(5)
sb:SetPoint("TOPRIGHT", holder, "TOPRIGHT", -1, -2)
sb:SetPoint("BOTTOMRIGHT", holder, "BOTTOMRIGHT", -1, 2)
sb:SetOrientation("VERTICAL")
sb:SetMinMaxValues(0, 1)
sb:SetValue(0)
SetPBD(sb, { 0.1, 0.1, 0.12, 0.4 }, { 0.15, 0.15, 0.18, 0.3 })
local thumb = sb:CreateTexture(nil, "OVERLAY")
thumb:SetTexture("Interface\\Buttons\\WHITE8X8")
thumb:SetVertexColor(0.4, 0.4, 0.48, 0.7)
thumb:SetWidth(5)
thumb:SetHeight(24)
sb:SetThumbTexture(thumb)
sb:SetScript("OnValueChanged", function()
sf:SetVerticalScroll(this:GetValue())
end)
holder:EnableMouseWheel(1)
holder:SetScript("OnMouseWheel", function()
local cur = sb:GetValue()
local step = 24
local lo, mx = sb:GetMinMaxValues()
if arg1 > 0 then
sb:SetValue(math.max(cur - step, 0))
else
sb:SetValue(math.min(cur + step, mx))
end
end)
holder.sf = sf
holder.child = child
holder.sb = sb
holder.UpdateH = function(_, ch)
child:SetHeight(ch)
local visH = sf:GetHeight()
local maxS = math.max(ch - visH, 0)
sb:SetMinMaxValues(0, maxS)
if maxS == 0 then sb:Hide() else sb:Show() end
sb:SetValue(math.min(sb:GetValue(), maxS))
end
return holder
end
--------------------------------------------------------------------------------
-- Enchant detection (comprehensive for vanilla 1.12 / Turtle WoW)
--
-- Vanilla WoW tooltip green text types:
-- 1. Enchant effects (what we detect)
-- 2. "装备:" / "Equip:" item innate effects
-- 3. "使用:" / "Use:" item use effects
-- 4. "击中时可能:" / "Chance on hit:" proc effects
-- 5. "(X) 套装:" active set bonuses
-- Strategy: exclude #2-5, then positive-match enchant patterns.
--
-- Covers: standard enchanting, libram/arcanum (head/legs), ZG class enchants,
-- ZG/Naxx shoulder augments, armor kits, weapon chains, scopes, spurs,
-- counterweights, and Turtle WoW custom enchants.
--------------------------------------------------------------------------------
local scanTip
local function EnsureTip()
if not scanTip then
scanTip = CreateFrame("GameTooltip", "SFramesSScanTip", nil, "GameTooltipTemplate")
end
scanTip:SetOwner(UIParent, "ANCHOR_NONE")
return scanTip
end
local PROC_ENCHANTS = {
"十字军", "Crusader",
"吸取生命", "Lifestealing",
"灼热武器", "Fiery Weapon", "火焰武器",
"寒冰", "Icy Chill",
"邪恶武器", "Unholy Weapon",
"恶魔杀手", "Demonslaying",
"无法被缴械", "Cannot be Disarmed",
}
local function IsEnchantLine(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_ENCHANTS) do
if string.find(txt, PROC_ENCHANTS[i]) then return true end
end
return false
end
local function GetEnchant(slotId)
local tip = EnsureTip()
tip:ClearLines()
tip:SetInventoryItem("player", slotId)
local n = tip:NumLines()
if not n or n < 2 then return false, nil end
for i = 2, n do
local obj = _G["SFramesSScanTipTextLeft" .. 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 IsEnchantLine(txt) then
return true, txt
end
end
end
end
end
return false, nil
end
--------------------------------------------------------------------------------
-- Set bonus detection
--------------------------------------------------------------------------------
local function GetSets()
local tip = EnsureTip()
local sets = {}
local seen = {}
local slots = { 1,2,3,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19 }
for si = 1, table.getn(slots) do
local sid = slots[si]
local link = GetInventoryItemLink("player", sid)
if link then
tip:ClearLines()
tip:SetOwner(UIParent, "ANCHOR_NONE")
tip:SetInventoryItem("player", sid)
local nl = tip:NumLines()
if nl then
for li = 1, nl do
local obj = _G["SFramesSScanTipTextLeft" .. li]
if obj then
local txt = obj:GetText()
if txt then
local a, b, sn, sc, sm
a, b, sn, sc, sm = string.find(txt, "^(.-)%s*%((%d+)/(%d+)%)$")
if sn and sc and sm then
sn = string.gsub(sn, "^%s+", "")
sn = string.gsub(sn, "%s+$", "")
if sn ~= "" and not seen[sn] then
seen[sn] = true
table.insert(sets, {
name = sn,
current = tonumber(sc) or 0,
max = tonumber(sm) or 0,
})
end
end
end
end
end
end
end
end
return sets
end
--------------------------------------------------------------------------------
-- Build the panel
--------------------------------------------------------------------------------
local function BuildPanel()
if summaryFrame then return summaryFrame end
local f = CreateFrame("Frame", "SFramesStatSummary", UIParent)
f:SetWidth(PANEL_W)
f:SetHeight(PANEL_H)
f:SetPoint("CENTER", UIParent, "CENTER", 200, 0)
f:SetFrameStrata("HIGH")
f:SetFrameLevel(100)
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)
SetBD(f, T.bg, T.border)
f:Hide()
-- Header
local hdr = FS(f, 11, "LEFT", T.gold)
hdr:SetPoint("TOPLEFT", f, "TOPLEFT", SIDE_PAD + 2, -6)
hdr:SetText("属性总览")
-- Tab buttons
local tabW = 60
f.tabs = {}
f.curTab = 1
local tNames = { "属性", "装备" }
for i = 1, 2 do
local btn = CreateFrame("Button", NN("T"), f)
btn:SetWidth(tabW)
btn:SetHeight(16)
btn:SetPoint("TOPRIGHT", f, "TOPRIGHT", -(SIDE_PAD + (2 - i) * (tabW + 2) + 20), -5)
btn:SetFrameLevel(f:GetFrameLevel() + 2)
SetPBD(btn, T.tabBg, T.tabBorder)
local lbl = FS(btn, 9, "CENTER", T.tabText)
lbl:SetPoint("CENTER", btn, "CENTER", 0, 0)
lbl:SetText(tNames[i])
btn.lbl = lbl
btn.idx = i
btn:SetScript("OnClick", function() SS:SetTab(this.idx) end)
f.tabs[i] = btn
end
-- Close
local cb = CreateFrame("Button", nil, f)
cb:SetWidth(14)
cb:SetHeight(14)
cb:SetPoint("TOPRIGHT", f, "TOPRIGHT", -5, -5)
cb:SetFrameLevel(f:GetFrameLevel() + 3)
SetBD(cb, T.buttonDownBg or { 0.35, 0.06, 0.06, 0.85 }, T.btnBorder or { 0.45, 0.1, 0.1, 0.6 })
local cx = FS(cb, 8, "CENTER", { 1, 0.7, 0.7 })
cx:SetPoint("CENTER", cb, "CENTER", 0, 0)
cx:SetText("x")
cb:SetScript("OnClick", function() f:Hide() end)
-- Sep
local sep = f: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", f, "TOPLEFT", 4, -HEADER_H)
sep:SetPoint("TOPRIGHT", f, "TOPRIGHT", -4, -HEADER_H)
-- Content
local cH = PANEL_H - HEADER_H - 8
local cW = PANEL_W - 8
f.statsScroll = MakeScroll(f, cW, cH)
f.statsScroll:SetPoint("TOPLEFT", f, "TOPLEFT", 4, -(HEADER_H + 2))
f.equipScroll = MakeScroll(f, cW, cH)
f.equipScroll:SetPoint("TOPLEFT", f, "TOPLEFT", 4, -(HEADER_H + 2))
f.equipScroll:Hide()
summaryFrame = f
return f
end
--------------------------------------------------------------------------------
-- Tab switching
--------------------------------------------------------------------------------
function SS:SetTab(idx)
if not summaryFrame then return end
summaryFrame.curTab = idx
for i = 1, 2 do
local btn = summaryFrame.tabs[i]
if i == idx then
SetPBD(btn, T.tabActiveBg, T.tabActiveBorder)
btn.lbl:SetTextColor(T.tabActiveText[1], T.tabActiveText[2], T.tabActiveText[3])
else
SetPBD(btn, T.tabBg, T.tabBorder)
btn.lbl:SetTextColor(T.tabText[1], T.tabText[2], T.tabText[3])
end
end
if idx == 1 then
summaryFrame.statsScroll:Show()
summaryFrame.equipScroll:Hide()
local ok, err = pcall(function() SS:BuildStats() end)
if not ok then
DEFAULT_CHAT_FRAME:AddMessage("|cffff4444SS BuildStats err: " .. tostring(err) .. "|r")
end
else
summaryFrame.statsScroll:Hide()
summaryFrame.equipScroll:Show()
local ok, err = pcall(function() SS:BuildEquip() end)
if not ok then
DEFAULT_CHAT_FRAME:AddMessage("|cffff4444SS BuildEquip err: " .. tostring(err) .. "|r")
end
end
end
--------------------------------------------------------------------------------
-- Helpers for building rows
--------------------------------------------------------------------------------
local function HideRows(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 AddHeader(p, txt, y, clr)
local f1 = FS(p, 10, "LEFT", clr or T.sectionTitle)
f1:SetPoint("TOPLEFT", p, "TOPLEFT", 4, y)
f1:SetText(txt)
tinsert(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)
tinsert(p._r, s)
return y - 16
end
local function AddRow(p, lbl, val, y, lc, vc)
local f1 = FS(p, 9, "LEFT", lc or T.labelText)
f1:SetPoint("TOPLEFT", p, "TOPLEFT", 8, y)
f1:SetText(lbl)
tinsert(p._r, f1)
local f2 = FS(p, 9, "RIGHT", vc or T.valueText)
f2:SetPoint("TOPRIGHT", p, "TOPRIGHT", -12, y)
f2:SetWidth(100)
f2:SetJustifyH("RIGHT")
f2:SetText(val)
tinsert(p._r, f2)
return y - ROW_H
end
--------------------------------------------------------------------------------
-- Stats page
--------------------------------------------------------------------------------
function SS:BuildStats()
local child = summaryFrame.statsScroll.child
HideRows(child)
local y = -4
-- Primary Stats
y = AddHeader(child, "主属性:", y)
local sn = { "力量", "敏捷", "耐力", "智力", "精神" }
local sc = { T.statColors.str, T.statColors.agi, T.statColors.sta, T.statColors.int, T.statColors.spi }
for i = 1, 5 do
local _, eff = UnitStat("player", i)
y = AddRow(child, sn[i], tostring(math.floor(eff or 0)), y, sc[i], sc[i])
end
y = y - SECTION_GAP
-- Defense
y = AddHeader(child, "防御属性:", y, T.defColor)
local hp = UnitHealthMax("player") or 0
y = AddRow(child, "生命值", tostring(hp), y, nil, T.defColor)
local dodge = 0
if GetDodgeChance then dodge = GetDodgeChance() or 0 end
if dodge == 0 then dodge = GearB("DODGE") end
y = AddRow(child, "躲闪", string.format("%.2f%%", dodge), y)
local baseA, effA = UnitArmor("player")
effA = math.floor(effA or baseA or 0)
y = AddRow(child, "护甲", tostring(effA), y)
local lvl = UnitLevel("player") or 60
local k1 = 400 + 85 * lvl
local p1 = effA / (effA + k1) * 100
if p1 > 75 then p1 = 75 end
y = AddRow(child, "物理免伤同级", string.format("%.2f%%", p1), y)
local k2 = 400 + 85 * (lvl + 3)
local p2 = effA / (effA + k2) * 100
if p2 > 75 then p2 = 75 end
y = AddRow(child, "物理免伤骷髅级", string.format("%.2f%%", p2), y)
y = y - SECTION_GAP
-- Resistances
y = AddHeader(child, "抗性:", y, T.resistColors.arcane)
local rInfo = {
{ 6, "奥术抗性", T.resistColors.arcane },
{ 2, "火焰抗性", T.resistColors.fire },
{ 3, "自然抗性", T.resistColors.nature },
{ 4, "冰霜抗性", T.resistColors.frost },
{ 5, "暗影抗性", T.resistColors.shadow },
}
for i = 1, table.getn(rInfo) do
local ri = rInfo[i]
local _, tot = UnitResistance("player", ri[1])
y = AddRow(child, ri[2], tostring(math.floor(tot or 0)), y, ri[3], ri[3])
end
y = y - SECTION_GAP
-- Physical
y = AddHeader(child, "物理:", y, T.physColor)
local mb, mp, mn = UnitAttackPower("player")
local mAP = math.floor((mb or 0) + (mp or 0) + (mn or 0))
local rb, rp, rn = UnitRangedAttackPower("player")
local rAP = math.floor((rb or 0) + (rp or 0) + (rn or 0))
y = AddRow(child, "近战/远程攻强", mAP .. "/" .. rAP, y, nil, T.physColor)
local cs = GetCS()
local mHit = cs and cs.SafeGetMeleeHit and cs.SafeGetMeleeHit() or 0
if mHit == 0 then
mHit = TryAPI({ "GetHitModifier", "GetMeleeHitModifier" })
if mHit == 0 then mHit = GearB("TOHIT") end
end
y = AddRow(child, "近战/远程命中", string.format("+%d%%/+%d%%", mHit, mHit), y)
local mCrit = cs and cs.SafeGetMeleeCrit and cs.SafeGetMeleeCrit() or 0
if mCrit == 0 then
mCrit = TryAPI({ "GetCritChance", "GetMeleeCritChance" })
if mCrit == 0 then mCrit = GearB("CRIT") end
end
local rCrit = cs and cs.SafeGetRangedCrit and cs.SafeGetRangedCrit() or 0
if rCrit == 0 then
rCrit = TryAPI({ "GetRangedCritChance" })
if rCrit == 0 then rCrit = mCrit end
end
y = AddRow(child, "近战/远程暴击", string.format("%.2f%%/%.2f%%", mCrit, rCrit), y)
y = y - SECTION_GAP
-- Spell
y = AddHeader(child, "法术:", y, T.spellColor)
local mana = UnitManaMax("player") or 0
y = AddRow(child, "法力值", tostring(mana), y, nil, T.spellColor)
local sDmg = 0
if GetSpellBonusDamage then
for s = 2, 7 do
local d = GetSpellBonusDamage(s) or 0
if d > sDmg then sDmg = d end
end
end
if sDmg == 0 then
local lib = GetIBL()
if lib and lib.GetBonus then
local baseDmg = lib:GetBonus("DMG") or 0
sDmg = baseDmg
local schools = { "FIREDMG","FROSTDMG","SHADOWDMG","ARCANEDMG","NATUREDMG","HOLYDMG" }
for _, sk in ipairs(schools) do
local sv = baseDmg + (lib:GetBonus(sk) or 0)
if sv > sDmg then sDmg = sv end
end
end
end
y = AddRow(child, "法术伤害", tostring(math.floor(sDmg)), y)
local sHeal = 0
if GetSpellBonusHealing then sHeal = GetSpellBonusHealing() or 0 end
if sHeal == 0 then sHeal = GearB("HEAL") end
y = AddRow(child, "法术治疗", tostring(math.floor(sHeal)), y)
local sCrit = cs and cs.SafeGetSpellCrit and cs.SafeGetSpellCrit() or 0
if sCrit == 0 then
sCrit = TryAPIa({ "GetSpellCritChance" }, 2)
if sCrit == 0 then sCrit = GearB("SPELLCRIT") end
end
y = AddRow(child, "法术暴击", string.format("%.2f%%", sCrit), y)
local sHit = cs and cs.SafeGetSpellHit and cs.SafeGetSpellHit() or 0
if sHit == 0 then
sHit = TryAPI({ "GetSpellHitModifier" })
if sHit == 0 then sHit = GearB("SPELLTOHIT") end
end
y = AddRow(child, "法术命中", string.format("+%d%%", sHit), y)
local sHaste = GearB("SPELLHASTE")
if sHaste > 0 then
y = AddRow(child, "急速", string.format("+%.2f%%", sHaste), y)
end
local sPen = GearB("SPELLPEN")
if sPen > 0 then
y = AddRow(child, "法术穿透", "+" .. tostring(math.floor(sPen)), y)
end
y = y - SECTION_GAP
-- Regen
y = AddHeader(child, "回复:", y, T.regenColor)
local mp5 = GearB("MANAREG")
y = AddRow(child, "装备回蓝", tostring(math.floor(mp5)) .. " MP/5s", y, nil, T.regenColor)
local _, spi = UnitStat("player", 5)
spi = math.floor(spi or 0)
local spReg = math.floor(15 + spi / 5)
y = AddRow(child, "精神回蓝", spReg .. " MP/2s", y)
y = y - SECTION_GAP
-- Set bonuses
local ok2, sets = pcall(GetSets)
if ok2 and sets and table.getn(sets) > 0 then
y = AddHeader(child, "套装:", y, T.setColor)
for i = 1, table.getn(sets) do
local s = sets[i]
y = AddRow(child, s.name, "(" .. s.current .. "/" .. s.max .. ")", y, T.setColor, T.setColor)
end
end
summaryFrame.statsScroll:UpdateH(math.abs(y) + 12)
end
--------------------------------------------------------------------------------
-- Equipment page
--------------------------------------------------------------------------------
function SS:BuildEquip()
local child = summaryFrame.equipScroll.child
HideRows(child)
local y = -4
y = AddHeader(child, "装备列表 & 附魔检查:", y, T.gold)
local totalSlots = 0
local enchCount = 0
for si = 1, table.getn(ENCHANTABLE_SLOTS) do
local slot = ENCHANTABLE_SLOTS[si]
local link = GetInventoryItemLink("player", 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 = {}
qMap["ff9d9d9d"] = 0
qMap["ffffffff"] = 1
qMap["ff1eff00"] = 2
qMap["ff0070dd"] = 3
qMap["ffa335ee"] = 4
qMap["ffff8000"] = 5
if qH then quality = qMap[qH] end
local nc = (quality and QUALITY_COLORS[quality]) or T.valueText
-- Slot label
local sf = FS(child, 8, "LEFT", T.dimText)
sf:SetPoint("TOPLEFT", child, "TOPLEFT", 4, y)
sf:SetText(slot.label)
sf:SetWidth(32)
tinsert(child._r, sf)
-- Item name
local nf = FS(child, 9, "LEFT", nc)
nf:SetPoint("TOPLEFT", child, "TOPLEFT", 38, y)
nf:SetWidth(130)
if string.len(itemName) > 18 then
itemName = string.sub(itemName, 1, 16) .. ".."
end
nf:SetText(itemName)
tinsert(child._r, nf)
-- Enchant check
local hasE, eTxt = false, nil
local eOk, eR1, eR2 = pcall(GetEnchant, slot.id)
if eOk then hasE = eR1; eTxt = eR2 end
local ico = FS(child, 9, "RIGHT")
ico:SetPoint("TOPRIGHT", child, "TOPRIGHT", -8, y)
if hasE then
ico:SetTextColor(T.enchanted[1], T.enchanted[2], T.enchanted[3])
ico:SetText("*")
enchCount = enchCount + 1
else
ico:SetTextColor(T.noEnchant[1], T.noEnchant[2], T.noEnchant[3])
ico:SetText("-")
end
tinsert(child._r, ico)
y = y - ROW_H
if hasE and eTxt then
local ef = FS(child, 8, "LEFT", T.enchanted)
ef:SetPoint("TOPLEFT", child, "TOPLEFT", 38, y)
ef:SetWidth(155)
ef:SetText(" " .. eTxt)
tinsert(child._r, ef)
y = y - 12
elseif not hasE then
local ef = FS(child, 8, "LEFT", T.noEnchant)
ef:SetPoint("TOPLEFT", child, "TOPLEFT", 38, y)
ef:SetText(" 未附魔")
tinsert(child._r, ef)
y = y - 12
end
y = y - 2
else
local sf = FS(child, 8, "LEFT", T.dimText)
sf:SetPoint("TOPLEFT", child, "TOPLEFT", 4, y)
sf:SetText(slot.label)
tinsert(child._r, sf)
local ef = FS(child, 9, "LEFT", T.dimText)
ef:SetPoint("TOPLEFT", child, "TOPLEFT", 38, y)
ef:SetText("-- 未装备 --")
tinsert(child._r, ef)
y = y - ROW_H - 2
end
end
y = y - SECTION_GAP
y = AddHeader(child, "附魔统计:", y, T.gold)
local sc2 = (enchCount == totalSlots) and T.enchanted or T.noEnchant
y = AddRow(child, "已附魔/总装备", enchCount .. "/" .. totalSlots, y, nil, sc2)
if enchCount < totalSlots then
y = AddRow(child, "缺少附魔", tostring(totalSlots - enchCount) .. "", y, T.noEnchant, T.noEnchant)
end
summaryFrame.equipScroll:UpdateH(math.abs(y) + 12)
end
--------------------------------------------------------------------------------
-- Public API
--------------------------------------------------------------------------------
function SS:Toggle()
local ok, err = pcall(function()
BuildPanel()
if summaryFrame:IsShown() then
summaryFrame:Hide()
return
end
local cpf = _G["SFramesCharacterPanel"]
if cpf and cpf:IsShown() then
summaryFrame:ClearAllPoints()
summaryFrame:SetPoint("TOPLEFT", cpf, "TOPRIGHT", 2, 0)
else
summaryFrame:ClearAllPoints()
summaryFrame:SetPoint("CENTER", UIParent, "CENTER", 200, 0)
end
local scale = SFramesDB and SFramesDB.charPanelScale or 1.0
summaryFrame:SetScale(scale)
summaryFrame:Show()
SS:SetTab(summaryFrame.curTab or 1)
end)
if not ok then
DEFAULT_CHAT_FRAME:AddMessage("|cffff4444[Nanami-UI] SS:Toggle error: " .. tostring(err) .. "|r")
end
end
function SS:Show()
local ok, err = pcall(function()
BuildPanel()
local cpf = _G["SFramesCharacterPanel"]
if cpf and cpf:IsShown() then
summaryFrame:ClearAllPoints()
summaryFrame:SetPoint("TOPLEFT", cpf, "TOPRIGHT", 2, 0)
end
local scale = SFramesDB and SFramesDB.charPanelScale or 1.0
summaryFrame:SetScale(scale)
summaryFrame:Show()
SS:SetTab(summaryFrame.curTab or 1)
end)
if not ok then
DEFAULT_CHAT_FRAME:AddMessage("|cffff4444[Nanami-UI] SS:Show error: " .. tostring(err) .. "|r")
end
end
function SS:Hide()
if summaryFrame then summaryFrame:Hide() end
end
function SS:IsShown()
return summaryFrame and summaryFrame:IsShown()
end
function SS:Refresh()
if not summaryFrame or not summaryFrame:IsShown() then return end
SS:SetTab(summaryFrame.curTab or 1)
end
--------------------------------------------------------------------------------
-- Events
--------------------------------------------------------------------------------
local evf = CreateFrame("Frame", "SFramesSSEv", UIParent)
evf:RegisterEvent("UNIT_INVENTORY_CHANGED")
evf:RegisterEvent("PLAYER_AURAS_CHANGED")
evf:RegisterEvent("UNIT_ATTACK_POWER")
evf:RegisterEvent("UNIT_RESISTANCES")
evf:SetScript("OnEvent", function()
if summaryFrame and summaryFrame:IsShown() then
SS:Refresh()
end
end)
DEFAULT_CHAT_FRAME:AddMessage("SF: Loading StatSummary.lua...")