Re-add Oils to tree Add support for Ghost Shrouds Add support for increased Ancestor Totem buff effect Add support for Ritual of Awakening Hierophant node Add support for 3 mods on Watchers Eye Add Impale damage to Combined DPS sort option Update Boss resistance values to 3.9 levels Add Bleed DPS to sorting option Add new spectres to the spectre library Kraityn's Sentry, Chrome-infused Chimeral, Vaal Slayer, Primeval Hunter, Archer Statue, Crazed Driver Add new spectre skills to library Blast Rain, Elemental Hit Fire, Barrage, Ice Shot, Unearth, Tornado Shot, Flame Surge Fix mod support for Chains of Command Add Astral Projector Unique Ring (3.9 preview) Fix rage granting damage to unarmed attacks Fix UI on passive tree being too large for some resolutions Fix node power not respecting your colour choice for highlighting Add area tag to Perforate Fix uniques with duplicate mods Re-add Death's Harp back in Update Snakepit Unique Ring Add Vaal Arc Lucky buff to export script
360 lines
13 KiB
Lua
360 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
|
|
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:BuildAllDependsAndPaths()
|
|
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 |