1116 lines
43 KiB
Lua
1116 lines
43 KiB
Lua
SFrames.TalentTree = {}
|
||
|
||
local ICON_SIZE = 36
|
||
local ICON_SPACING_X = 14
|
||
local ICON_SPACING_Y = 14
|
||
local TAB_WIDTH = 220
|
||
local FRAME_WIDTH = (TAB_WIDTH * 3) + 40
|
||
local FRAME_HEIGHT = 520
|
||
|
||
--------------------------------------------------------------------------------
|
||
-- Theme: Pink Cat-Paw
|
||
--------------------------------------------------------------------------------
|
||
local T = SFrames.Theme:Extend()
|
||
local function GetHex()
|
||
return (SFrames.Theme and SFrames.Theme:GetAccentHex()) or "ffffb3d9"
|
||
end
|
||
|
||
--------------------------------------------------------------------------------
|
||
-- Class definitions
|
||
--------------------------------------------------------------------------------
|
||
local CLASS_LIST = {
|
||
{ key = "WARRIOR", name = "战士", color = { 0.78, 0.61, 0.43 } },
|
||
{ key = "PALADIN", name = "圣骑士", color = { 0.96, 0.55, 0.73 } },
|
||
{ key = "HUNTER", name = "猎人", color = { 0.67, 0.83, 0.45 } },
|
||
{ key = "ROGUE", name = "盗贼", color = { 1.00, 0.96, 0.41 } },
|
||
{ key = "PRIEST", name = "牧师", color = { 1.00, 1.00, 1.00 } },
|
||
{ key = "SHAMAN", name = "萨满", color = { 0.00, 0.44, 0.87 } },
|
||
{ key = "MAGE", name = "法师", color = { 0.41, 0.80, 0.94 } },
|
||
{ key = "WARLOCK", name = "术士", color = { 0.58, 0.51, 0.79 } },
|
||
{ key = "DRUID", name = "德鲁伊", color = { 1.00, 0.49, 0.04 } },
|
||
}
|
||
|
||
--------------------------------------------------------------------------------
|
||
-- 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.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 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.55)
|
||
s:SetBackdropBorderColor(0, 0, 0, 0.4)
|
||
return s
|
||
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 StyleButton(btn, label)
|
||
SetRoundBackdrop(btn, T.btnBg, T.btnBorder)
|
||
|
||
local fs = MakeFS(btn, 12, "CENTER", T.btnText)
|
||
fs:SetPoint("CENTER", btn, "CENTER", 0, 0)
|
||
if label then fs:SetText(label) end
|
||
btn.nanamiLabel = fs
|
||
|
||
btn: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])
|
||
if this.nanamiLabel then
|
||
this.nanamiLabel:SetTextColor(T.btnActiveText[1], T.btnActiveText[2], T.btnActiveText[3])
|
||
end
|
||
if this.nanamiTooltip then
|
||
GameTooltip:SetOwner(this, "ANCHOR_TOP")
|
||
GameTooltip:AddLine(this.nanamiTooltip[1], 1, 1, 1)
|
||
if this.nanamiTooltip[2] then
|
||
GameTooltip:AddLine(this.nanamiTooltip[2], 0.7, 0.7, 0.7)
|
||
end
|
||
GameTooltip:Show()
|
||
end
|
||
end)
|
||
|
||
btn:SetScript("OnLeave", function()
|
||
this:SetBackdropColor(T.btnBg[1], T.btnBg[2], T.btnBg[3], T.btnBg[4])
|
||
this:SetBackdropBorderColor(T.btnBorder[1], T.btnBorder[2], T.btnBorder[3], T.btnBorder[4])
|
||
if this.nanamiLabel then
|
||
this.nanamiLabel:SetTextColor(T.btnText[1], T.btnText[2], T.btnText[3])
|
||
end
|
||
GameTooltip:Hide()
|
||
end)
|
||
|
||
btn:SetScript("OnMouseDown", function()
|
||
this:SetBackdropColor(T.btnDownBg[1], T.btnDownBg[2], T.btnDownBg[3], T.btnDownBg[4])
|
||
end)
|
||
|
||
btn:SetScript("OnMouseUp", function()
|
||
this:SetBackdropColor(T.btnHoverBg[1], T.btnHoverBg[2], T.btnHoverBg[3], T.btnHoverBg[4])
|
||
end)
|
||
|
||
return fs
|
||
end
|
||
|
||
--------------------------------------------------------------------------------
|
||
-- Talent data cache (stores talent trees per class in SFramesGlobalDB)
|
||
--------------------------------------------------------------------------------
|
||
local function GetCache()
|
||
if not SFramesGlobalDB then SFramesGlobalDB = {} end
|
||
if not SFramesGlobalDB.talentCache then SFramesGlobalDB.talentCache = {} end
|
||
return SFramesGlobalDB.talentCache
|
||
end
|
||
|
||
local function CacheCurrentClassData()
|
||
local _, classEn = UnitClass("player")
|
||
if not classEn then return end
|
||
local numTabs = GetNumTalentTabs()
|
||
if numTabs == 0 then return end
|
||
|
||
local cache = GetCache()
|
||
local classData = {}
|
||
|
||
for t = 1, numTabs do
|
||
local tabName, tabIcon, pointsSpent, background = GetTalentTabInfo(t)
|
||
local treeData = { name = tabName, icon = tabIcon, background = background or "", talents = {}, numTalents = 0 }
|
||
local numTalents = GetNumTalents(t)
|
||
treeData.numTalents = numTalents
|
||
|
||
for i = 1, numTalents do
|
||
local tName, tIcon, tier, column, rank, maxRank = GetTalentInfo(t, i)
|
||
local prereqTier, prereqCol = GetTalentPrereqs(t, i)
|
||
|
||
local descLines = {}
|
||
local scanTip = _G["NanamiTalentScanTip"]
|
||
if not scanTip then
|
||
scanTip = CreateFrame("GameTooltip", "NanamiTalentScanTip", UIParent, "GameTooltipTemplate")
|
||
scanTip:SetOwner(UIParent, "ANCHOR_NONE")
|
||
end
|
||
scanTip:ClearLines()
|
||
scanTip:SetTalent(t, i)
|
||
local numLines = scanTip:NumLines()
|
||
for li = 2, numLines do
|
||
local lineObj = _G["NanamiTalentScanTipTextLeft" .. li]
|
||
if lineObj then
|
||
local txt = lineObj:GetText()
|
||
if txt and txt ~= "" then
|
||
table.insert(descLines, txt)
|
||
end
|
||
end
|
||
end
|
||
|
||
treeData.talents[i] = {
|
||
name = tName or "",
|
||
icon = tIcon or "",
|
||
tier = tier or 1,
|
||
column = column or 1,
|
||
maxRank = maxRank or 1,
|
||
prereqTier = prereqTier,
|
||
prereqColumn = prereqCol,
|
||
desc = descLines,
|
||
}
|
||
end
|
||
classData[t] = treeData
|
||
end
|
||
classData.numTabs = numTabs
|
||
cache[classEn] = classData
|
||
end
|
||
|
||
--------------------------------------------------------------------------------
|
||
-- Data access wrappers (API for own class, cache for others)
|
||
--------------------------------------------------------------------------------
|
||
local TT = SFrames.TalentTree
|
||
|
||
local function IsViewingOwnClass(self)
|
||
return not self.viewingClass or self.viewingClass == self.playerClass
|
||
end
|
||
|
||
local function TT_GetNumTabs(self)
|
||
if IsViewingOwnClass(self) then return GetNumTalentTabs() end
|
||
local cache = GetCache()
|
||
local cd = cache[self.viewingClass]
|
||
return cd and cd.numTabs or 0
|
||
end
|
||
|
||
local function TT_GetTabInfo(self, tab)
|
||
if IsViewingOwnClass(self) then return GetTalentTabInfo(tab) end
|
||
local cache = GetCache()
|
||
local cd = cache[self.viewingClass]
|
||
if cd and cd[tab] then
|
||
return cd[tab].name, cd[tab].icon, 0, cd[tab].background
|
||
end
|
||
return "", "", 0, ""
|
||
end
|
||
|
||
local function TT_GetNumTalents(self, tab)
|
||
if IsViewingOwnClass(self) then return GetNumTalents(tab) end
|
||
local cache = GetCache()
|
||
local cd = cache[self.viewingClass]
|
||
if cd and cd[tab] then return cd[tab].numTalents or 0 end
|
||
return 0
|
||
end
|
||
|
||
local function TT_GetTalentInfo(self, tab, index)
|
||
if IsViewingOwnClass(self) then return GetTalentInfo(tab, index) end
|
||
local cache = GetCache()
|
||
local cd = cache[self.viewingClass]
|
||
if cd and cd[tab] and cd[tab].talents[index] then
|
||
local t = cd[tab].talents[index]
|
||
return t.name, t.icon, t.tier, t.column, 0, t.maxRank, nil, true
|
||
end
|
||
return nil
|
||
end
|
||
|
||
local function TT_GetTalentPrereqs(self, tab, index)
|
||
if IsViewingOwnClass(self) then return GetTalentPrereqs(tab, index) end
|
||
local cache = GetCache()
|
||
local cd = cache[self.viewingClass]
|
||
if cd and cd[tab] and cd[tab].talents[index] then
|
||
local t = cd[tab].talents[index]
|
||
return t.prereqTier, t.prereqColumn
|
||
end
|
||
return nil, nil
|
||
end
|
||
|
||
--------------------------------------------------------------------------------
|
||
-- Initialize
|
||
--------------------------------------------------------------------------------
|
||
function SFrames.TalentTree:Initialize()
|
||
local _, classEn = UnitClass("player")
|
||
self.playerClass = classEn
|
||
|
||
self:CreateMainFrame()
|
||
self:HookVanillaUI()
|
||
|
||
SFrames:RegisterEvent("CHARACTER_POINTS_CHANGED", function()
|
||
if self.frame and self.frame:IsShown() then self:Update() end
|
||
end)
|
||
SFrames:RegisterEvent("SPELLS_CHANGED", function()
|
||
if self.frame and self.frame:IsShown() then self:Update() end
|
||
end)
|
||
end
|
||
|
||
--------------------------------------------------------------------------------
|
||
-- Main Frame
|
||
--------------------------------------------------------------------------------
|
||
function SFrames.TalentTree:CreateMainFrame()
|
||
local f = CreateFrame("Frame", "SFramesTalentFrame", UIParent)
|
||
f:SetWidth(FRAME_WIDTH)
|
||
f:SetHeight(FRAME_HEIGHT)
|
||
f:SetPoint("CENTER", UIParent, "CENTER", 0, 0)
|
||
SetRoundBackdrop(f, T.panelBg, T.panelBorder)
|
||
CreateShadow(f, 5)
|
||
f:EnableMouse(true)
|
||
f:SetMovable(true)
|
||
f:RegisterForDrag("LeftButton")
|
||
f:SetScript("OnDragStart", function() this:StartMoving() end)
|
||
f:SetScript("OnDragStop", function() this:StopMovingOrSizing() end)
|
||
f:SetFrameStrata("HIGH")
|
||
f:Hide()
|
||
|
||
local titleIcoSize = 14
|
||
local titleGap = 4
|
||
f.title = MakeFS(f, 14, "CENTER", T.titleColor)
|
||
f.title:SetPoint("TOP", f, "TOP", (titleIcoSize + titleGap) / 2, -10)
|
||
f.title:SetText("|c" .. GetHex() .. "Nanami|r 天赋系统")
|
||
local titleIco = SFrames:CreateIcon(f, "talent", titleIcoSize)
|
||
titleIco:SetDrawLayer("OVERLAY")
|
||
titleIco:SetVertexColor(T.titleColor[1], T.titleColor[2], T.titleColor[3])
|
||
titleIco:SetPoint("RIGHT", f.title, "LEFT", -titleGap, 0)
|
||
|
||
-- Close button
|
||
local closeBtn = CreateFrame("Button", nil, f)
|
||
closeBtn:SetWidth(18)
|
||
closeBtn:SetHeight(18)
|
||
closeBtn:SetPoint("TOPRIGHT", f, "TOPRIGHT", -8, -8)
|
||
closeBtn:SetFrameLevel(f:GetFrameLevel() + 3)
|
||
SetRoundBackdrop(closeBtn, T.buttonDownBg, T.btnBorder)
|
||
local closeTxt = MakeFS(closeBtn, 10, "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)
|
||
f.close = closeBtn
|
||
|
||
-- Class selector bar (shown in sim mode)
|
||
local classBar = CreateFrame("Frame", nil, f)
|
||
classBar:SetHeight(20)
|
||
classBar:SetPoint("TOPLEFT", f, "TOPLEFT", 10, -28)
|
||
classBar:SetPoint("TOPRIGHT", f, "TOPRIGHT", -10, -28)
|
||
classBar:SetFrameLevel(f:GetFrameLevel() + 2)
|
||
classBar:Hide()
|
||
f.classBar = classBar
|
||
|
||
local numClasses = table.getn(CLASS_LIST)
|
||
local gap = 2
|
||
local barW = FRAME_WIDTH - 20
|
||
local cbW = math.floor((barW - (numClasses - 1) * gap) / numClasses)
|
||
f.classBtns = {}
|
||
|
||
for ci, cinfo in ipairs(CLASS_LIST) do
|
||
local cb = CreateFrame("Button", nil, classBar)
|
||
cb:SetWidth(cbW)
|
||
cb:SetHeight(18)
|
||
cb:SetPoint("TOPLEFT", classBar, "TOPLEFT", (ci - 1) * (cbW + gap), 0)
|
||
SetPixelBackdrop(cb, T.slotBg, T.slotBorder)
|
||
|
||
local cIcon = SFrames:CreateClassIcon(cb, 14)
|
||
cIcon.overlay:SetPoint("LEFT", cb, "LEFT", 3, 0)
|
||
SFrames:SetClassIcon(cIcon, cinfo.key)
|
||
cb.classIconTex = cIcon
|
||
|
||
local cbt = MakeFS(cb, 9, "CENTER", cinfo.color)
|
||
cbt:SetPoint("LEFT", cIcon.overlay, "RIGHT", 1, 0)
|
||
cbt:SetPoint("RIGHT", cb, "RIGHT", -2, 0)
|
||
cbt:SetText(cinfo.name)
|
||
cb.label = cbt
|
||
cb.classKey = cinfo.key
|
||
cb.classColor = cinfo.color
|
||
cb:SetScript("OnClick", function()
|
||
SFrames.TalentTree:SwitchViewClass(this.classKey)
|
||
end)
|
||
cb:SetScript("OnEnter", function()
|
||
this:SetBackdropColor(T.slotHover[1], T.slotHover[2], T.slotHover[3], T.slotHover[4])
|
||
local cache = GetCache()
|
||
if not cache[this.classKey] and this.classKey ~= SFrames.TalentTree.playerClass then
|
||
GameTooltip:SetOwner(this, "ANCHOR_BOTTOM")
|
||
GameTooltip:AddLine("需先用该职业角色打开天赋面板以缓存数据", 1, 0.5, 0.5)
|
||
GameTooltip:Show()
|
||
end
|
||
end)
|
||
cb:SetScript("OnLeave", function()
|
||
this:SetBackdropColor(T.slotBg[1], T.slotBg[2], T.slotBg[3], T.slotBg[4])
|
||
GameTooltip:Hide()
|
||
end)
|
||
f.classBtns[ci] = cb
|
||
end
|
||
|
||
-- Overlay for text on top of children
|
||
f.overlay = CreateFrame("Frame", nil, f)
|
||
f.overlay:SetAllPoints(f)
|
||
f.overlay:SetFrameLevel(f:GetFrameLevel() + 10)
|
||
|
||
f.pointsText = MakeFS(f.overlay, 13, "LEFT", T.titleColor)
|
||
f.pointsText:SetPoint("BOTTOMLEFT", f.overlay, "BOTTOMLEFT", 14, 25)
|
||
|
||
f.simLabel = MakeFS(f.overlay, 11, "LEFT", T.titleColor)
|
||
f.simLabel:SetPoint("LEFT", f.pointsText, "LEFT", 0, -18)
|
||
f.simLabel:SetText("")
|
||
|
||
-- Bottom buttons
|
||
f.btnApply = CreateFrame("Button", nil, f)
|
||
f.btnApply:SetWidth(100)
|
||
f.btnApply:SetHeight(26)
|
||
f.btnApply:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", -12, 12)
|
||
StyleButton(f.btnApply, "应用天赋")
|
||
f.btnApply.nanamiTooltip = { "应用天赋", "将所有模拟点数提交至服务器。" }
|
||
f.btnApply:SetScript("OnClick", function() SFrames.TalentTree:ApplyVirtualPoints() end)
|
||
|
||
f.btnReset = CreateFrame("Button", nil, f)
|
||
f.btnReset:SetWidth(100)
|
||
f.btnReset:SetHeight(26)
|
||
f.btnReset:SetPoint("RIGHT", f.btnApply, "LEFT", -8, 0)
|
||
StyleButton(f.btnReset, "重置预览")
|
||
f.btnReset:SetScript("OnClick", function() SFrames.TalentTree:ResetVirtualPoints() end)
|
||
|
||
f.btnSimMode = CreateFrame("Button", nil, f)
|
||
f.btnSimMode:SetWidth(110)
|
||
f.btnSimMode:SetHeight(26)
|
||
f.btnSimMode:SetPoint("RIGHT", f.btnReset, "LEFT", -8, 0)
|
||
StyleButton(f.btnSimMode, "|cff888888模拟加点: 关|r")
|
||
f.simModeText = f.btnSimMode.nanamiLabel
|
||
f.btnSimMode.nanamiTooltip = { "模拟加点模式", "开启后可用左键虚拟加点、右键取消,\n确认后点「应用天赋」才会实际扣分。\n可切换职业模拟其他职业加点。" }
|
||
f.btnSimMode:SetScript("OnClick", function()
|
||
SFrames.TalentTree.simMode = not SFrames.TalentTree.simMode
|
||
SFrames.TalentTree:UpdateSimModeLabel()
|
||
end)
|
||
|
||
self.frame = f
|
||
self.tabs = {}
|
||
self.virtualPoints = {}
|
||
self.simMode = false
|
||
self.viewingClass = nil
|
||
|
||
tinsert(UISpecialFrames, "SFramesTalentFrame")
|
||
end
|
||
|
||
--------------------------------------------------------------------------------
|
||
-- Class bar update
|
||
--------------------------------------------------------------------------------
|
||
function SFrames.TalentTree:UpdateClassBar()
|
||
if not self.frame or not self.frame.classBtns then return end
|
||
local viewing = self.viewingClass or self.playerClass
|
||
local cache = GetCache()
|
||
|
||
for ci, cinfo in ipairs(CLASS_LIST) do
|
||
local cb = self.frame.classBtns[ci]
|
||
if not cb then break end
|
||
|
||
local hasData = (cinfo.key == self.playerClass) or (cache[cinfo.key] ~= nil)
|
||
|
||
if cinfo.key == viewing then
|
||
SetPixelBackdrop(cb, T.tabActiveBg, cinfo.color)
|
||
cb.label:SetTextColor(cinfo.color[1], cinfo.color[2], cinfo.color[3])
|
||
elseif hasData then
|
||
SetPixelBackdrop(cb, T.slotBg, T.slotBorder)
|
||
cb.label:SetTextColor(cinfo.color[1] * 0.8, cinfo.color[2] * 0.8, cinfo.color[3] * 0.8)
|
||
else
|
||
SetPixelBackdrop(cb, T.emptySlotBg, T.emptySlotBd)
|
||
cb.label:SetTextColor(T.passive[1], T.passive[2], T.passive[3])
|
||
end
|
||
end
|
||
end
|
||
|
||
function SFrames.TalentTree:UpdateSimModeLabel()
|
||
if self.simMode then
|
||
local viewing = self.viewingClass or self.playerClass
|
||
local viewName = viewing or "?"
|
||
for _, c in ipairs(CLASS_LIST) do
|
||
if c.key == viewing then viewName = c.name; break end
|
||
end
|
||
self.frame.simModeText:SetText("|c" .. GetHex() .. "模拟: " .. viewName .. "|r")
|
||
self.frame.simLabel:SetText("|c" .. GetHex() .. "[模拟] 总点数: 51 左键: 加点 右键: 撤销|r")
|
||
self.frame.classBar:Show()
|
||
self:UpdateClassBar()
|
||
if not IsViewingOwnClass(self) then
|
||
self.frame.btnApply:Hide()
|
||
else
|
||
self.frame.btnApply:Show()
|
||
end
|
||
else
|
||
self.frame.simModeText:SetText("|cff888888模拟加点: 关|r")
|
||
self.frame.simLabel:SetText("")
|
||
self.frame.classBar:Hide()
|
||
self.frame.btnApply:Show()
|
||
if self.viewingClass and self.viewingClass ~= self.playerClass then
|
||
self.viewingClass = self.playerClass
|
||
self.virtualPoints = {}
|
||
self:DestroyTrees()
|
||
self:BuildTrees()
|
||
self:Update()
|
||
return
|
||
end
|
||
end
|
||
self:ResetVirtualPoints()
|
||
end
|
||
|
||
--------------------------------------------------------------------------------
|
||
-- Switch class
|
||
--------------------------------------------------------------------------------
|
||
function SFrames.TalentTree:SwitchViewClass(classKey)
|
||
if not self.simMode then return end
|
||
if classKey == (self.viewingClass or self.playerClass) then return end
|
||
|
||
if classKey ~= self.playerClass then
|
||
local cache = GetCache()
|
||
if not cache[classKey] then
|
||
UIErrorsFrame:AddMessage("该职业天赋数据尚未缓存,请先用该职业角色打开天赋面板", 1, 0.5, 0.5, 1)
|
||
return
|
||
end
|
||
end
|
||
|
||
self.viewingClass = classKey
|
||
self.virtualPoints = {}
|
||
self:DestroyTrees()
|
||
self:BuildTrees()
|
||
self:UpdateSimModeLabel()
|
||
self:Update()
|
||
end
|
||
|
||
--------------------------------------------------------------------------------
|
||
-- Destroy / Rebuild trees
|
||
--------------------------------------------------------------------------------
|
||
function SFrames.TalentTree:DestroyTrees()
|
||
if self.tabs then
|
||
for _, tabData in pairs(self.tabs) do
|
||
if type(tabData) == "table" and tabData.frame then
|
||
tabData.frame:Hide()
|
||
tabData.frame:ClearAllPoints()
|
||
tabData.frame:SetParent(nil)
|
||
end
|
||
end
|
||
end
|
||
self.tabs = {}
|
||
self.treesBuilt = false
|
||
end
|
||
|
||
--------------------------------------------------------------------------------
|
||
-- Build trees (from API or cache)
|
||
--------------------------------------------------------------------------------
|
||
function SFrames.TalentTree:BuildTrees()
|
||
if self.treesBuilt then return end
|
||
self.treesBuilt = true
|
||
|
||
if IsViewingOwnClass(self) then
|
||
CacheCurrentClassData()
|
||
end
|
||
|
||
local treeTop = -38
|
||
if self.simMode then treeTop = -52 end
|
||
|
||
local numTabs = TT_GetNumTabs(self)
|
||
for t = 1, numTabs do
|
||
local name, icon, pointsSpent, background = TT_GetTabInfo(self, t)
|
||
|
||
local tabFrame = CreateFrame("Frame", nil, self.frame)
|
||
tabFrame:SetWidth(TAB_WIDTH)
|
||
tabFrame:SetHeight(FRAME_HEIGHT - 90)
|
||
local offsetX = 10 + ((t - 1) * (TAB_WIDTH + 5))
|
||
tabFrame:SetPoint("TOPLEFT", self.frame, "TOPLEFT", offsetX, treeTop)
|
||
|
||
SetRoundBackdrop(tabFrame, T.tabBg, T.tabBorder)
|
||
|
||
if background and background ~= "" then
|
||
local bg = tabFrame:CreateTexture(nil, "BACKGROUND")
|
||
bg:SetTexture("Interface\\TalentFrame\\" .. background)
|
||
bg:SetPoint("TOPLEFT", tabFrame, "TOPLEFT", 3, -3)
|
||
bg:SetPoint("BOTTOMRIGHT", tabFrame, "BOTTOMRIGHT", -3, 3)
|
||
bg:SetAlpha(0.35)
|
||
end
|
||
|
||
local tTitle = MakeFS(tabFrame, 14, "CENTER", T.titleColor)
|
||
tTitle:SetPoint("TOP", tabFrame, "TOP", 0, -10)
|
||
tTitle:SetText("|c" .. GetHex() .. (name or "") .. "|r")
|
||
|
||
local tPoints = MakeFS(tabFrame, 12, "CENTER", T.dimText)
|
||
tPoints:SetPoint("TOP", tTitle, "BOTTOM", 0, -2)
|
||
tabFrame.pointsText = tPoints
|
||
|
||
self.tabs[t] = { frame = tabFrame, talents = {}, grid = {} }
|
||
|
||
local GRID_PAD_X = math.floor((TAB_WIDTH - (4 * ICON_SIZE + 3 * ICON_SPACING_X)) / 2)
|
||
|
||
local numTalents = TT_GetNumTalents(self, t)
|
||
for i = 1, numTalents do
|
||
local tName, tIcon, tier, column, rank, maxRank = TT_GetTalentInfo(self, t, i)
|
||
if not tName then break end
|
||
|
||
if not self.tabs[t].grid[tier] then self.tabs[t].grid[tier] = {} end
|
||
|
||
local btn = CreateFrame("Button", "SFramesTalent_" .. (self.viewingClass or "own") .. "_" .. t .. "_" .. i, tabFrame)
|
||
btn:SetWidth(ICON_SIZE)
|
||
btn:SetHeight(ICON_SIZE)
|
||
|
||
local x = (column - 1) * (ICON_SIZE + ICON_SPACING_X) + GRID_PAD_X
|
||
local y = -(tier - 1) * (ICON_SIZE + ICON_SPACING_Y) - 44
|
||
btn:SetPoint("TOPLEFT", tabFrame, "TOPLEFT", x, y)
|
||
|
||
btn.icon = btn:CreateTexture(nil, "ARTWORK")
|
||
btn.icon:SetTexture(tIcon)
|
||
btn.icon:SetAllPoints()
|
||
|
||
btn.borderTex = btn:CreateTexture(nil, "OVERLAY")
|
||
btn.borderTex:SetTexture("Interface\\Buttons\\UI-Quickslot2")
|
||
btn.borderTex:SetWidth(ICON_SIZE * 1.5)
|
||
btn.borderTex:SetHeight(ICON_SIZE * 1.5)
|
||
btn.borderTex:SetPoint("CENTER", btn, "CENTER", 0, 0)
|
||
|
||
local rankFrame = CreateFrame("Frame", nil, btn)
|
||
rankFrame:SetAllPoints(btn)
|
||
rankFrame:SetFrameLevel(btn:GetFrameLevel() + 2)
|
||
|
||
btn.rankBg = rankFrame:CreateTexture(nil, "BACKGROUND")
|
||
btn.rankBg:SetTexture(0, 0, 0, 0.75)
|
||
btn.rankBg:SetWidth(20)
|
||
btn.rankBg:SetHeight(12)
|
||
btn.rankBg:SetPoint("BOTTOMRIGHT", rankFrame, "BOTTOMRIGHT", -1, 1)
|
||
btn.rankBg:Hide()
|
||
|
||
btn.rankText = MakeFS(rankFrame, 10, "RIGHT")
|
||
btn.rankText:SetPoint("BOTTOMRIGHT", rankFrame, "BOTTOMRIGHT", -2, 2)
|
||
btn.rankText:SetShadowColor(0, 0, 0, 1)
|
||
btn.rankText:SetShadowOffset(1, -1)
|
||
btn.rankText:SetText("")
|
||
|
||
btn.tab = t
|
||
btn.index = i
|
||
btn.tier = tier
|
||
btn.maxRank = maxRank
|
||
btn.talentName = tName
|
||
|
||
local cachedDesc = nil
|
||
if not IsViewingOwnClass(self) then
|
||
local cd = GetCache()[self.viewingClass]
|
||
if cd and cd[t] and cd[t].talents[i] then
|
||
cachedDesc = cd[t].talents[i].desc
|
||
end
|
||
end
|
||
btn.cachedDesc = cachedDesc
|
||
|
||
if IsViewingOwnClass(self) then
|
||
btn:SetScript("OnEnter", function()
|
||
GameTooltip_SetDefaultAnchor(GameTooltip, this)
|
||
GameTooltip:SetTalent(this.tab, this.index)
|
||
GameTooltip:Show()
|
||
end)
|
||
else
|
||
btn:SetScript("OnEnter", function()
|
||
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
|
||
GameTooltip:AddLine(this.talentName or "?", 1, 1, 1)
|
||
local vr = SFrames.TalentTree:GetVirtualRank(this.tab, this.index)
|
||
GameTooltip:AddLine("等级 " .. vr .. "/" .. (this.maxRank or "?"), 0.7, 0.7, 0.7)
|
||
if this.cachedDesc then
|
||
GameTooltip:AddLine(" ")
|
||
for _, line in ipairs(this.cachedDesc) do
|
||
GameTooltip:AddLine(line, 1, 0.82, 0, 1)
|
||
end
|
||
end
|
||
GameTooltip:Show()
|
||
end)
|
||
end
|
||
btn:SetScript("OnLeave", function() GameTooltip:Hide() end)
|
||
|
||
btn:RegisterForClicks("LeftButtonUp", "RightButtonUp")
|
||
btn:SetScript("OnClick", function()
|
||
SFrames.TalentTree:OnTalentClick(this, arg1)
|
||
end)
|
||
|
||
self.tabs[t].talents[i] = btn
|
||
self.tabs[t].grid[tier][column] = btn
|
||
end
|
||
|
||
-- Dependency lines
|
||
for i = 1, numTalents do
|
||
local prereqTier, prereqColumn = TT_GetTalentPrereqs(self, t, i)
|
||
if prereqTier and prereqColumn then
|
||
local btn = self.tabs[t].talents[i]
|
||
local pBtn = self.tabs[t].grid[prereqTier] and self.tabs[t].grid[prereqTier][prereqColumn]
|
||
if pBtn then
|
||
btn.prereqIndex = pBtn.index
|
||
btn.prereqLines = {}
|
||
|
||
local _, _, tier, column = TT_GetTalentInfo(self, t, i)
|
||
local pX = (prereqColumn - 1) * (ICON_SIZE + ICON_SPACING_X) + GRID_PAD_X
|
||
local pY = -(prereqTier - 1) * (ICON_SIZE + ICON_SPACING_Y) - 44
|
||
local cX = (column - 1) * (ICON_SIZE + ICON_SPACING_X) + GRID_PAD_X
|
||
local cY = -(tier - 1) * (ICON_SIZE + ICON_SPACING_Y) - 44
|
||
|
||
local pCenterX = pX + (ICON_SIZE / 2)
|
||
local pCenterY = pY - (ICON_SIZE / 2)
|
||
local pBottomY = pY - ICON_SIZE
|
||
local pRightX = pX + ICON_SIZE
|
||
|
||
local tCenterX = cX + (ICON_SIZE / 2)
|
||
local tTopY = cY
|
||
local tLeftX = cX
|
||
local tRightX = cX + ICON_SIZE
|
||
|
||
local function CreateLine(x1, y1, x2, y2)
|
||
local line = tabFrame:CreateTexture(nil, "BACKGROUND")
|
||
line:SetTexture(T.sepColor[1], T.sepColor[2], T.sepColor[3], T.sepColor[4])
|
||
if math.abs(x1 - x2) < 0.1 then
|
||
line:SetWidth(2)
|
||
line:SetHeight(math.abs(y1 - y2))
|
||
line:SetPoint("TOP", tabFrame, "TOPLEFT", x1, math.max(y1, y2))
|
||
else
|
||
line:SetHeight(2)
|
||
line:SetWidth(math.abs(x1 - x2))
|
||
line:SetPoint("LEFT", tabFrame, "TOPLEFT", math.min(x1, x2), y1)
|
||
end
|
||
table.insert(btn.prereqLines, line)
|
||
end
|
||
|
||
if prereqTier == tier then
|
||
if prereqColumn < column then
|
||
CreateLine(pRightX, pCenterY, tLeftX, pCenterY)
|
||
else
|
||
CreateLine(pX, pCenterY, tRightX, pCenterY)
|
||
end
|
||
elseif prereqColumn == column then
|
||
CreateLine(pCenterX, pBottomY, tCenterX, tTopY)
|
||
else
|
||
local midY = pBottomY - (ICON_SPACING_Y / 2) + 2
|
||
CreateLine(pCenterX, pBottomY, pCenterX, midY)
|
||
CreateLine(pCenterX, midY, tCenterX, midY)
|
||
CreateLine(tCenterX, midY, tCenterX, tTopY)
|
||
end
|
||
end
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
--------------------------------------------------------------------------------
|
||
-- Hooks
|
||
--------------------------------------------------------------------------------
|
||
function SFrames.TalentTree:HookVanillaUI()
|
||
local self = SFrames.TalentTree
|
||
|
||
local function ShowCustomTalentFrame()
|
||
if not self.frame then return end
|
||
if not self.frame:IsShown() then
|
||
self:BuildTrees()
|
||
self.frame:Show()
|
||
self:Update()
|
||
end
|
||
end
|
||
|
||
local function ToggleCustomTalentFrame()
|
||
if not self.frame then return end
|
||
if self.frame:IsShown() then
|
||
self.frame:Hide()
|
||
else
|
||
ShowCustomTalentFrame()
|
||
end
|
||
end
|
||
|
||
ToggleTalentFrame = ToggleCustomTalentFrame
|
||
|
||
if not self.hookedShowUIPanel then
|
||
self.hookedShowUIPanel = true
|
||
local orig_ShowUIPanel = ShowUIPanel
|
||
ShowUIPanel = function(frame)
|
||
if frame and frame.GetName then
|
||
local fName = frame:GetName()
|
||
if fName == "TalentFrame" or fName == "TalentMicroButtonAlert" then
|
||
ShowCustomTalentFrame()
|
||
return
|
||
end
|
||
end
|
||
if orig_ShowUIPanel then
|
||
orig_ShowUIPanel(frame)
|
||
end
|
||
end
|
||
|
||
local orig_HideUIPanel = HideUIPanel
|
||
HideUIPanel = function(frame)
|
||
if frame and frame.GetName and frame:GetName() == "TalentFrame" then
|
||
if self.frame then self.frame:Hide() end
|
||
return
|
||
end
|
||
if orig_HideUIPanel then
|
||
orig_HideUIPanel(frame)
|
||
end
|
||
end
|
||
end
|
||
|
||
local orig_TalentFrame_LoadUI = TalentFrame_LoadUI
|
||
if orig_TalentFrame_LoadUI then
|
||
TalentFrame_LoadUI = function()
|
||
orig_TalentFrame_LoadUI()
|
||
end
|
||
end
|
||
end
|
||
|
||
--------------------------------------------------------------------------------
|
||
-- Virtual point helpers
|
||
--------------------------------------------------------------------------------
|
||
function SFrames.TalentTree:GetVirtualRank(tab, index)
|
||
if self.virtualPoints[tab] and self.virtualPoints[tab][index] then
|
||
return self.virtualPoints[tab][index]
|
||
end
|
||
if self.simMode then return 0 end
|
||
if IsViewingOwnClass(self) then
|
||
local name, icon, tier, column, rank, maxRank = GetTalentInfo(tab, index)
|
||
return rank or 0
|
||
end
|
||
return 0
|
||
end
|
||
|
||
function SFrames.TalentTree:GetVirtualTreePoints(tab)
|
||
local total = 0
|
||
local n = TT_GetNumTalents(self, tab)
|
||
for i = 1, n do
|
||
total = total + self:GetVirtualRank(tab, i)
|
||
end
|
||
return total
|
||
end
|
||
|
||
function SFrames.TalentTree:GetRemainingUnspent()
|
||
if self.simMode then
|
||
local unspent = 51
|
||
local numTabs = TT_GetNumTabs(self)
|
||
for tb = 1, numTabs do
|
||
unspent = unspent - self:GetVirtualTreePoints(tb)
|
||
end
|
||
return unspent
|
||
else
|
||
local unspent = UnitCharacterPoints("player")
|
||
for tb = 1, GetNumTalentTabs() do
|
||
for idx = 1, GetNumTalents(tb) do
|
||
local name, icon, tier, column, realRank = GetTalentInfo(tb, idx)
|
||
local virtRank = self:GetVirtualRank(tb, idx)
|
||
unspent = unspent - (virtRank - (realRank or 0))
|
||
end
|
||
end
|
||
return unspent
|
||
end
|
||
end
|
||
|
||
--------------------------------------------------------------------------------
|
||
-- Talent click
|
||
--------------------------------------------------------------------------------
|
||
function SFrames.TalentTree:OnTalentClick(btn, buttonType)
|
||
if not self.simMode and IsViewingOwnClass(self) then
|
||
if buttonType == "LeftButton" then
|
||
local tName, tIcon, tier, column, rank, maxRank, isExceptional, meetsPrereq = GetTalentInfo(btn.tab, btn.index)
|
||
local unspent = UnitCharacterPoints("player")
|
||
if rank < maxRank and meetsPrereq and unspent > 0 then
|
||
StaticPopupDialogs["NANAMI_CONFIRM_TALENT"] = {
|
||
text = "确定学习天赋 [" .. tName .. "] ?\n(等级 " .. rank .. " → " .. (rank+1) .. " / " .. maxRank .. ")",
|
||
button1 = "确认",
|
||
button2 = "取消",
|
||
OnAccept = function()
|
||
LearnTalent(btn.tab, btn.index)
|
||
end,
|
||
timeout = 0,
|
||
whileDead = true,
|
||
hideOnEscape = true,
|
||
}
|
||
StaticPopup_Show("NANAMI_CONFIRM_TALENT")
|
||
end
|
||
end
|
||
return
|
||
end
|
||
|
||
if not self.simMode then return end
|
||
|
||
local _, _, tier, _, _, maxRank = TT_GetTalentInfo(self, btn.tab, btn.index)
|
||
local virtRank = self:GetVirtualRank(btn.tab, btn.index)
|
||
maxRank = maxRank or btn.maxRank
|
||
|
||
if not self.virtualPoints[btn.tab] then self.virtualPoints[btn.tab] = {} end
|
||
if not self.virtualPoints[btn.tab][btn.index] then
|
||
self.virtualPoints[btn.tab][btn.index] = self:GetVirtualRank(btn.tab, btn.index)
|
||
end
|
||
|
||
if buttonType == "LeftButton" then
|
||
local unspent = self:GetRemainingUnspent()
|
||
if unspent > 0 and virtRank < maxRank then
|
||
local treePts = self:GetVirtualTreePoints(btn.tab)
|
||
local prereqMet = true
|
||
if btn.prereqIndex then
|
||
local pVirtRank = self:GetVirtualRank(btn.tab, btn.prereqIndex)
|
||
local pMaxRank = self.tabs[btn.tab].talents[btn.prereqIndex].maxRank
|
||
if pVirtRank < pMaxRank then prereqMet = false end
|
||
end
|
||
|
||
if treePts >= (tier - 1) * 5 and prereqMet then
|
||
self.virtualPoints[btn.tab][btn.index] = virtRank + 1
|
||
end
|
||
end
|
||
elseif buttonType == "RightButton" then
|
||
if virtRank > 0 then
|
||
local canRevert = true
|
||
local treePts = self:GetVirtualTreePoints(btn.tab)
|
||
local numT = TT_GetNumTalents(self, btn.tab)
|
||
for i = 1, numT do
|
||
local childVirtRank = self:GetVirtualRank(btn.tab, i)
|
||
if childVirtRank > 0 then
|
||
local _, _, qTier = TT_GetTalentInfo(self, btn.tab, i)
|
||
if qTier and qTier > tier and (treePts - 1) < (qTier - 1) * 5 then
|
||
canRevert = false
|
||
end
|
||
local cBtn = self.tabs[btn.tab].talents[i]
|
||
if cBtn and cBtn.prereqIndex == btn.index then
|
||
if childVirtRank > 0 and (virtRank - 1) < maxRank then
|
||
canRevert = false
|
||
end
|
||
end
|
||
end
|
||
end
|
||
if canRevert then
|
||
self.virtualPoints[btn.tab][btn.index] = virtRank - 1
|
||
else
|
||
UIErrorsFrame:AddMessage("无法取消点数:其他已点天赋依赖于此天赋", 1, 0, 0, 1)
|
||
end
|
||
end
|
||
end
|
||
|
||
self:Update()
|
||
end
|
||
|
||
function SFrames.TalentTree:ResetVirtualPoints()
|
||
self.virtualPoints = {}
|
||
self:Update()
|
||
end
|
||
|
||
function SFrames.TalentTree:ApplyVirtualPoints()
|
||
if not self.simMode then return end
|
||
if not IsViewingOwnClass(self) then
|
||
DEFAULT_CHAT_FRAME:AddMessage("|cffff0000[错误]|r 只能应用本职业的天赋。")
|
||
return
|
||
end
|
||
|
||
local canApply = true
|
||
for tb = 1, GetNumTalentTabs() do
|
||
for idx = 1, GetNumTalents(tb) do
|
||
local name, icon, tier, column, realRank = GetTalentInfo(tb, idx)
|
||
local virtRank = self:GetVirtualRank(tb, idx)
|
||
if realRank > virtRank then
|
||
canApply = false
|
||
end
|
||
end
|
||
end
|
||
|
||
if not canApply then
|
||
DEFAULT_CHAT_FRAME:AddMessage("|cffff0000[错误]|r 模拟的点数未包含您已学习的天赋,无法直接应用。")
|
||
return
|
||
end
|
||
|
||
self.applyQueue = {}
|
||
|
||
for tb = 1, GetNumTalentTabs() do
|
||
local treeTalents = {}
|
||
for idx = 1, GetNumTalents(tb) do
|
||
local name, icon, tier, column, realRank = GetTalentInfo(tb, idx)
|
||
local virtRank = self:GetVirtualRank(tb, idx)
|
||
local diff = virtRank - realRank
|
||
if diff > 0 then
|
||
for i = 1, diff do
|
||
table.insert(treeTalents, {tab = tb, index = idx, tier = tier or 1})
|
||
end
|
||
end
|
||
end
|
||
table.sort(treeTalents, function(a, b) return a.tier < b.tier end)
|
||
for _, entry in ipairs(treeTalents) do
|
||
table.insert(self.applyQueue, entry)
|
||
end
|
||
end
|
||
|
||
if table.getn(self.applyQueue) == 0 then
|
||
DEFAULT_CHAT_FRAME:AddMessage("|c" .. GetHex() .. "Nanami:|r 没有新的天赋点数需要应用。")
|
||
return
|
||
end
|
||
|
||
local total = table.getn(self.applyQueue)
|
||
DEFAULT_CHAT_FRAME:AddMessage("|c" .. GetHex() .. "Nanami:|r 开始应用 " .. total .. " 个天赋点...")
|
||
|
||
if not self.applyEventFrame then
|
||
self.applyEventFrame = CreateFrame("Frame", "NanamiTalentApplyFrame")
|
||
end
|
||
self.applyEventFrame:UnregisterAllEvents()
|
||
|
||
self.applyStallTimer = 0
|
||
self.applyWaiting = false
|
||
|
||
local function FinishApply()
|
||
self.applyEventFrame:UnregisterAllEvents()
|
||
self.applyEventFrame:SetScript("OnUpdate", nil)
|
||
self.applyQueue = {}
|
||
self.applyWaiting = false
|
||
self.simMode = false
|
||
self:UpdateSimModeLabel()
|
||
DEFAULT_CHAT_FRAME:AddMessage("|c" .. GetHex() .. "Nanami:|r 天赋应用完成。")
|
||
end
|
||
|
||
local function TryNextTalent()
|
||
if table.getn(self.applyQueue) == 0 then
|
||
FinishApply()
|
||
return
|
||
end
|
||
local t = table.remove(self.applyQueue, 1)
|
||
LearnTalent(t.tab, t.index)
|
||
self.applyWaiting = true
|
||
self.applyStallTimer = 0
|
||
end
|
||
|
||
self.applyEventFrame:RegisterEvent("CHARACTER_POINTS_CHANGED")
|
||
self.applyEventFrame:SetScript("OnEvent", function()
|
||
if not SFrames.TalentTree.applyWaiting then return end
|
||
SFrames.TalentTree.applyWaiting = false
|
||
SFrames.TalentTree.applyStallTimer = 0
|
||
TryNextTalent()
|
||
end)
|
||
|
||
self.applyEventFrame:SetScript("OnUpdate", function()
|
||
if not SFrames.TalentTree.applyWaiting then return end
|
||
SFrames.TalentTree.applyStallTimer = (SFrames.TalentTree.applyStallTimer or 0) + (arg1 or 0)
|
||
if SFrames.TalentTree.applyStallTimer >= 2.0 then
|
||
SFrames.TalentTree.applyStallTimer = 0
|
||
SFrames.TalentTree.applyWaiting = false
|
||
if table.getn(SFrames.TalentTree.applyQueue) > 0 then
|
||
TryNextTalent()
|
||
else
|
||
FinishApply()
|
||
end
|
||
end
|
||
end)
|
||
|
||
TryNextTalent()
|
||
end
|
||
|
||
--------------------------------------------------------------------------------
|
||
-- Update display
|
||
--------------------------------------------------------------------------------
|
||
function SFrames.TalentTree:Update()
|
||
if not self.frame or not self.frame:IsShown() then return end
|
||
if not self.tabs or not self.tabs[1] then return end
|
||
|
||
local unspent = self:GetRemainingUnspent()
|
||
local realUnspent = UnitCharacterPoints("player")
|
||
local numTabs = TT_GetNumTabs(self)
|
||
|
||
for tb = 1, numTabs do
|
||
if not self.tabs[tb] then break end
|
||
local treePts = self:GetVirtualTreePoints(tb)
|
||
self.tabs[tb].frame.pointsText:SetText("已用: |c" .. GetHex() .. treePts .. "|r")
|
||
|
||
local numT = TT_GetNumTalents(self, tb)
|
||
for idx = 1, numT do
|
||
local btn = self.tabs[tb].talents[idx]
|
||
if btn then
|
||
local _, _, tier, column, rank, maxRank = TT_GetTalentInfo(self, tb, idx)
|
||
if not IsViewingOwnClass(self) then rank = 0 end
|
||
local virtRank = self:GetVirtualRank(tb, idx)
|
||
|
||
if virtRank > 0 or (not self.simMode and rank and rank > 0) then
|
||
local displayRank = virtRank
|
||
if not self.simMode and displayRank == 0 and rank and rank > 0 then
|
||
displayRank = rank
|
||
end
|
||
btn.rankText:SetText(displayRank .. "/" .. maxRank)
|
||
btn.rankBg:Show()
|
||
btn.rankText:Show()
|
||
else
|
||
btn.rankText:SetText("")
|
||
btn.rankText:Hide()
|
||
btn.rankBg:Hide()
|
||
end
|
||
|
||
local prereqMet = true
|
||
if btn.prereqIndex then
|
||
local pVirtRank = self:GetVirtualRank(tb, btn.prereqIndex)
|
||
local pMaxRank = self.tabs[tb].talents[btn.prereqIndex].maxRank
|
||
if pVirtRank < pMaxRank then
|
||
prereqMet = false
|
||
end
|
||
end
|
||
|
||
local isLearnable = (unspent > 0 and treePts >= (tier - 1) * 5 and virtRank < maxRank and prereqMet)
|
||
|
||
if btn.prereqLines then
|
||
for _, line in ipairs(btn.prereqLines) do
|
||
if prereqMet then
|
||
line:SetTexture(T.accent[1], T.accent[2], T.accent[3], 0.8)
|
||
else
|
||
line:SetTexture(T.sepColor[1], T.sepColor[2], T.sepColor[3], T.sepColor[4])
|
||
end
|
||
end
|
||
end
|
||
|
||
local isVirtual = (not self.simMode and virtRank > (rank or 0)) or (self.simMode and virtRank > 0)
|
||
|
||
if (not self.simMode and isVirtual) or (self.simMode and isVirtual and virtRank < maxRank) then
|
||
btn.rankText:SetTextColor(T.title[1], T.title[2], T.title[3])
|
||
btn.icon:SetVertexColor(T.accentLight[1], T.accentLight[2], T.accentLight[3])
|
||
btn.borderTex:SetVertexColor(T.accent[1], T.accent[2], T.accent[3])
|
||
elseif virtRank >= maxRank then
|
||
btn.rankText:SetTextColor(T.title[1], T.title[2], T.title[3])
|
||
btn.icon:SetVertexColor(1, 1, 1)
|
||
btn.borderTex:SetVertexColor(T.accent[1], T.accent[2], T.accent[3])
|
||
elseif virtRank > 0 then
|
||
btn.rankText:SetTextColor(0, 1, 0)
|
||
btn.icon:SetVertexColor(1, 1, 1)
|
||
btn.borderTex:SetVertexColor(0, 0.7, 0)
|
||
elseif isLearnable then
|
||
btn.rankText:SetTextColor(T.dimText[1], T.dimText[2], T.dimText[3])
|
||
btn.icon:SetVertexColor(1, 1, 1)
|
||
btn.borderTex:SetVertexColor(T.passive[1], T.passive[2], T.passive[3])
|
||
else
|
||
btn.rankText:SetTextColor(T.passive[1], T.passive[2], T.passive[3])
|
||
btn.icon:SetVertexColor(T.passive[1], T.passive[2], T.passive[3])
|
||
btn.borderTex:SetVertexColor(T.passive[1], T.passive[2], T.passive[3])
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
if self.simMode then
|
||
if IsViewingOwnClass(self) then
|
||
self.frame.pointsText:SetText("剩余天赋点: |c" .. GetHex() .. unspent .. " |cff888888(总 " .. realUnspent .. ")|r")
|
||
else
|
||
self.frame.pointsText:SetText("剩余天赋点: |c" .. GetHex() .. unspent .. "|r")
|
||
end
|
||
else
|
||
self.frame.pointsText:SetText("剩余天赋点: |c" .. GetHex() .. realUnspent .. "|r")
|
||
end
|
||
end
|