Files
PathOfBuilding/Classes/TreeTab.lua
LocalIdentity 7d699394a7 Existing files
2020-03-15 13:04:02 +11:00

364 lines
13 KiB
Lua

-- Path of Building
--
-- Module: Tree Tab
-- Passive skill tree tab for the current build.
--
local ipairs = ipairs
local t_insert = table.insert
local m_min = math.min
local TreeTabClass = newClass("TreeTab", "ControlHost", function(self, build)
self.ControlHost()
self.build = build
self.viewer = new("PassiveTreeView")
self.specList = { }
self.specList[1] = new("PassiveSpec", build, build.targetVersionData.latestTreeVersion)
self:SetActiveSpec(1)
self.anchorControls = new("Control", nil, 0, 0, 0, 20)
self.controls.specSelect = new("DropDownControl", {"LEFT",self.anchorControls,"RIGHT"}, 0, 0, 190, 20, nil, function(index, value)
if self.specList[index] then
self.build.modFlag = true
self:SetActiveSpec(index)
else
self:OpenSpecManagePopup()
end
end)
self.controls.specSelect.tooltipFunc = function(tooltip, mode, selIndex, selVal)
tooltip:Clear()
if mode ~= "OUT" then
local spec = self.specList[selIndex]
if spec then
local used, ascUsed, sockets = spec:CountAllocNodes()
tooltip:AddLine(16, "Class: "..spec.curClassName)
tooltip:AddLine(16, "Ascendancy: "..spec.curAscendClassName)
tooltip:AddLine(16, "Points used: "..used)
if sockets > 0 then
tooltip:AddLine(16, "Jewel sockets: "..sockets)
end
if selIndex ~= self.activeSpec then
local calcFunc, calcBase = self.build.calcsTab:GetMiscCalculator()
if calcFunc then
local output = calcFunc({ spec = spec })
self.build:AddStatComparesToTooltip(tooltip, calcBase, output, "^7Switching to this tree will give you:")
end
if spec.curClassId == self.build.spec.curClassId then
local respec = 0
for nodeId, node in pairs(self.build.spec.allocNodes) do
if node.type ~= "ClassStart" and node.type ~= "AscendClassStart" and not spec.allocNodes[nodeId] then
if node.ascendancyName then
respec = respec + 5
else
respec = respec + 1
end
end
end
if respec > 0 then
tooltip:AddLine(16, "^7Switching to this tree requires "..respec.." refund points.")
end
end
end
tooltip:AddLine(16, "Game Version: "..treeVersions[spec.treeVersion].short)
end
end
end
self.controls.reset = new("ButtonControl", {"LEFT",self.controls.specSelect,"RIGHT"}, 8, 0, 60, 20, "Reset", function()
main:OpenConfirmPopup("Reset Tree", "Are you sure you want to reset your passive tree?", "Reset", function()
self.build.spec:ResetNodes()
self.build.spec:AddUndoState()
self.build.buildFlag = true
end)
end)
self.controls.import = new("ButtonControl", {"LEFT",self.controls.reset,"RIGHT"}, 8, 0, 90, 20, "Import Tree", function()
self:OpenImportPopup()
end)
self.controls.export = new("ButtonControl", {"LEFT",self.controls.import,"RIGHT"}, 8, 0, 90, 20, "Export Tree", function()
self:OpenExportPopup()
end)
self.controls.treeSearch = new("EditControl", {"LEFT",self.controls.export,"RIGHT"}, 8, 0, 300, 20, "", "Search", "%c%(%)", 100, function(buf)
self.viewer.searchStr = buf
end)
self.controls.treeHeatMap = new("CheckBoxControl", {"LEFT",self.controls.treeSearch,"RIGHT"}, 130, 0, 20, "Show Node Power:", function(state)
self.viewer.showHeatMap = state
end)
self.controls.treeHeatMapStatSelect = new("DropDownControl", {"LEFT",self.controls.treeHeatMap,"RIGHT"}, 8, 0, 150, 20, nil, function(index, value)
self:SetPowerCalc(value)
end)
self.controls.treeHeatMap.tooltipText = function()
local offCol, defCol = main.nodePowerTheme:match("(%a+)/(%a+)")
return "When enabled, an estimate of the offensive and defensive strength of\neach unallocated passive is calculated and displayed visually.\nOffensive power shows as "..offCol:lower()..", defensive power as "..defCol:lower().."."
end
self.powerStatList = { }
for _, stat in ipairs(data.powerStatList) do
if not stat.ignoreForNodes then
t_insert(self.powerStatList, stat)
end
end
self.controls.specConvertText = new("LabelControl", {"BOTTOMLEFT",self.controls.specSelect,"TOPLEFT"}, 0, -14, 0, 16, "^7This is an older tree version, which may not be fully compatible with the current game version.")
self.controls.specConvertText.shown = function()
return self.showConvert
end
self.controls.specConvert = new("ButtonControl", {"LEFT",self.controls.specConvertText,"RIGHT"}, 8, 0, 120, 20, "^2Convert to "..treeVersions[self.build.targetVersionData.latestTreeVersion].short, function()
local newSpec = new("PassiveSpec", self.build, self.build.targetVersionData.latestTreeVersion)
newSpec.title = self.build.spec.title
newSpec.jewels = copyTable(self.build.spec.jewels)
newSpec:DecodeURL(self.build.spec:EncodeURL())
t_insert(self.specList, self.activeSpec + 1, newSpec)
self:SetActiveSpec(self.activeSpec + 1)
self.modFlag = true
main:OpenMessagePopup("Tree Converted", "The tree has been converted to "..treeVersions[self.build.targetVersionData.latestTreeVersion].short..".\nNote that some or all of the passives may have been de-allocated due to changes in the tree.\n\nYou can switch back to the old tree using the tree selector at the bottom left.")
end)
end)
function TreeTabClass:Draw(viewPort, inputEvents)
self.anchorControls.x = viewPort.x + 4
self.anchorControls.y = viewPort.y + viewPort.height - 24
for id, event in ipairs(inputEvents) do
if event.type == "KeyDown" then
if event.key == "z" and IsKeyDown("CTRL") then
self.build.spec:Undo()
self.build.buildFlag = true
inputEvents[id] = nil
elseif event.key == "y" and IsKeyDown("CTRL") then
self.build.spec:Redo()
self.build.buildFlag = true
inputEvents[id] = nil
elseif event.key == "f" and IsKeyDown("CTRL") then
self:SelectControl(self.controls.treeSearch)
end
end
end
self:ProcessControlsInput(inputEvents, viewPort)
local treeViewPort = { x = viewPort.x, y = viewPort.y, width = viewPort.width, height = viewPort.height - (self.showConvert and 64 or 32) }
self.viewer:Draw(self.build, treeViewPort, inputEvents)
self.controls.specSelect.selIndex = self.activeSpec
wipeTable(self.controls.specSelect.list)
for id, spec in ipairs(self.specList) do
t_insert(self.controls.specSelect.list, (spec.treeVersion ~= self.build.targetVersionData.latestTreeVersion and ("["..treeVersions[spec.treeVersion].short.."] ") or "")..(spec.title or "Default"))
end
t_insert(self.controls.specSelect.list, "Manage trees...")
if not self.controls.treeSearch.hasFocus then
self.controls.treeSearch:SetText(self.viewer.searchStr)
end
self.controls.treeHeatMap.state = self.viewer.showHeatMap
self.controls.treeHeatMapStatSelect.list = self.powerStatList
self.controls.treeHeatMapStatSelect.selIndex = 1
if self.build.calcsTab.powerStat then
self.controls.treeHeatMapStatSelect:SelByValue(self.build.calcsTab.powerStat.stat, "stat")
end
SetDrawLayer(1)
SetDrawColor(0.05, 0.05, 0.05)
DrawImage(nil, viewPort.x, viewPort.y + viewPort.height - 28, viewPort.width, 28)
SetDrawColor(0.85, 0.85, 0.85)
DrawImage(nil, viewPort.x, viewPort.y + viewPort.height - 32, viewPort.width, 4)
if self.showConvert then
SetDrawColor(0.05, 0.05, 0.05)
DrawImage(nil, viewPort.x, viewPort.y + viewPort.height - 60, viewPort.width, 28)
SetDrawColor(0.85, 0.85, 0.85)
DrawImage(nil, viewPort.x, viewPort.y + viewPort.height - 64, viewPort.width, 4)
end
self:DrawControls(viewPort)
end
function TreeTabClass:Load(xml, dbFileName)
self.specList = { }
if xml.elem == "Spec" then
-- Import single spec from old build
self.specList[1] = new("PassiveSpec", self.build, self.build.targetVersionData.defaultTreeVersion)
self.specList[1]:Load(xml, dbFileName)
self.activeSpec = 1
self.build.spec = self.specList[1]
return
end
for _, node in pairs(xml) do
if type(node) == "table" then
if node.elem == "Spec" then
if node.attrib.treeVersion and not treeVersions[node.attrib.treeVersion] then
main:OpenMessagePopup("Unknown Passive Tree Version", "The build you are trying to load uses an unrecognised version of the passive skill tree.\nYou may need to update the program before loading this build.")
return true
end
local newSpec = new("PassiveSpec", self.build, node.attrib.treeVersion or self.build.targetVersionData.defaultTreeVersion)
newSpec:Load(node, dbFileName)
t_insert(self.specList, newSpec)
end
end
end
if not self.specList[1] then
self.specList[1] = new("PassiveSpec", self.build, self.build.targetVersionData.latestTreeVersion)
end
self:SetActiveSpec(tonumber(xml.attrib.activeSpec) or 1)
end
function TreeTabClass:PostLoad()
for _, spec in ipairs(self.specList) do
spec:PostLoad()
end
end
function TreeTabClass:Save(xml)
xml.attrib = {
activeSpec = tostring(self.activeSpec)
}
for specId, spec in ipairs(self.specList) do
if specId == self.activeSpec then
-- Update this spec's jewels from the socket slots
for _, slot in pairs(self.build.itemsTab.slots) do
if slot.nodeId then
spec.jewels[slot.nodeId] = slot.selItemId
end
end
end
local child = {
elem = "Spec"
}
spec:Save(child)
t_insert(xml, child)
end
self.modFlag = false
end
function TreeTabClass:SetActiveSpec(specId)
local prevSpec = self.build.spec
self.activeSpec = m_min(specId, #self.specList)
local curSpec = self.specList[self.activeSpec]
self.build.spec = curSpec
self.build.buildFlag = true
for _, slot in pairs(self.build.itemsTab.slots) do
if slot.nodeId then
if prevSpec then
-- Update the previous spec's jewel for this slot
prevSpec.jewels[slot.nodeId] = slot.selItemId
end
if curSpec.jewels[slot.nodeId] then
-- Socket the jewel for the new spec
slot.selItemId = curSpec.jewels[slot.nodeId]
end
end
end
self.showConvert = curSpec.treeVersion ~= self.build.targetVersionData.latestTreeVersion
if self.build.itemsTab.itemOrderList[1] then
-- Update item slots if items have been loaded already
self.build.itemsTab:PopulateSlots()
end
end
function TreeTabClass:SetPowerCalc(selection)
self.viewer.showHeatMap = true
self.build.buildFlag = true
self.build.powerBuildFlag = true
self.build.calcsTab.powerStat = selection
self.build.calcsTab:BuildPower()
end
function TreeTabClass:OpenSpecManagePopup()
main:OpenPopup(370, 290, "Manage Passive Trees", {
new("PassiveSpecListControl", nil, 0, 50, 350, 200, self),
new("ButtonControl", nil, 0, 260, 90, 20, "Done", function()
main:ClosePopup()
end),
})
end
function TreeTabClass:OpenImportPopup()
local controls = { }
local function decodeTreeLink(treeLink)
local errMsg = self.build.spec:DecodeURL(treeLink)
if errMsg then
controls.msg.label = "^1"..errMsg
else
self.build.spec:AddUndoState()
self.build.buildFlag = true
main:ClosePopup()
end
end
controls.editLabel = new("LabelControl", nil, 0, 20, 0, 16, "Enter passive tree link:")
controls.edit = new("EditControl", nil, 0, 40, 350, 18, "", nil, nil, nil, function(buf)
controls.msg.label = ""
end)
controls.msg = new("LabelControl", nil, 0, 58, 0, 16, "")
controls.import = new("ButtonControl", nil, -45, 80, 80, 20, "Import", function()
local treeLink = controls.edit.buf
if #treeLink == 0 then
return
end
if treeLink:match("poeurl%.com/") then
controls.import.enabled = false
controls.msg.label = "Resolving PoEURL link..."
local id = LaunchSubScript([[
local treeLink = ...
local curl = require("lcurl.safe")
local easy = curl.easy()
easy:setopt_url(treeLink)
easy:setopt_writefunction(function(data)
return true
end)
easy:perform()
local redirect = easy:getinfo(curl.INFO_REDIRECT_URL)
easy:close()
if not redirect or redirect:match("poeurl%.com/") then
return nil, "Failed to resolve PoEURL link"
end
return redirect
]], "", "", treeLink)
if id then
launch:RegisterSubScript(id, function(treeLink, errMsg)
if errMsg then
controls.msg.label = "^1"..errMsg
controls.import.enabled = true
else
decodeTreeLink(treeLink)
end
end)
end
else
decodeTreeLink(treeLink)
end
end)
controls.cancel = new("ButtonControl", nil, 45, 80, 80, 20, "Cancel", function()
main:ClosePopup()
end)
main:OpenPopup(380, 110, "Import Tree", controls, "import", "edit")
end
function TreeTabClass:OpenExportPopup()
local treeLink = self.build.spec:EncodeURL(treeVersions[self.build.spec.treeVersion].export)
local popup
local controls = { }
controls.label = new("LabelControl", nil, 0, 20, 0, 16, "Passive tree link:")
controls.edit = new("EditControl", nil, 0, 40, 350, 18, treeLink, nil, "%Z")
controls.shrink = new("ButtonControl", nil, -90, 70, 140, 20, "Shrink with PoEURL", function()
controls.shrink.enabled = false
controls.shrink.label = "Shrinking..."
launch:DownloadPage("http://poeurl.com/shrink.php?url="..treeLink, function(page, errMsg)
controls.shrink.label = "Done"
if errMsg or not page:match("%S") then
main:OpenMessagePopup("PoEURL Shortener", "Failed to get PoEURL link. Try again later.")
else
treeLink = "http://poeurl.com/"..page
controls.edit:SetText(treeLink)
popup:SelectControl(controls.edit)
end
end)
end)
controls.copy = new("ButtonControl", nil, 30, 70, 80, 20, "Copy", function()
Copy(treeLink)
end)
controls.done = new("ButtonControl", nil, 120, 70, 80, 20, "Done", function()
main:ClosePopup()
end)
popup = main:OpenPopup(380, 100, "Export Tree", controls, "done", "edit")
end