diff --git a/src/Classes/PassiveSpec.lua b/src/Classes/PassiveSpec.lua index 9ee45f4762..028109e9f0 100644 --- a/src/Classes/PassiveSpec.lua +++ b/src/Classes/PassiveSpec.lua @@ -58,6 +58,17 @@ local PassiveSpecClass = newClass("PassiveSpec", "UndoHandler", function(self, b -- Keys are mastery node IDs, values are mastery effect IDs self.masterySelections = { } + -- List of node allocation order + -- Keys are order indexes, values are node IDs + self.allocationOrder = { } + self.ascendancyAllocationOrder = { } + + -- Keys are mastery node IDs, values are mastery effect IDs + self.masterySelections = { } + + --for postload handler + self.initialLoad = true + self:SelectClass(0) end) @@ -116,6 +127,16 @@ function PassiveSpecClass:Load(xml, dbFileName) end end self:ImportFromNodeList(tonumber(xml.attrib.classId), tonumber(xml.attrib.ascendClassId), hashList, masteryEffects) + if xml.attrib.allocationOrder then + for nodeId in string.gmatch(xml.attrib.allocationOrder, ("%d+")) do + table.insert(self.allocationOrder, tonumber(nodeId)) + end + end + if xml.attrib.ascendancyAllocationOrder then + for nodeId in string.gmatch(xml.attrib.ascendancyAllocationOrder, ("%d+")) do + table.insert(self.ascendancyAllocationOrder, tonumber(nodeId)) + end + end elseif url then self:DecodeURL(url) end @@ -201,7 +222,9 @@ function PassiveSpecClass:Save(xml) classId = tostring(self.curClassId), ascendClassId = tostring(self.curAscendClassId), nodes = table.concat(allocNodeIdList, ","), - masteryEffects = table.concat(masterySelections, ",") + masteryEffects = table.concat(masterySelections, ","), + allocationOrder = table.concat(self.allocationOrder, ","), + ascendancyAllocationOrder = table.concat(self.ascendancyAllocationOrder, ",") } t_insert(xml, { -- Legacy format @@ -226,6 +249,9 @@ end function PassiveSpecClass:PostLoad() self:BuildClusterJewelGraphs() + self:ReIndexNodeAllocationOrder("allocationOrder") + self:ReIndexNodeAllocationOrder("ascendancyAllocationOrder") + self.initialLoad = false end -- Import passive spec from the provided class IDs and node hash list @@ -330,10 +356,10 @@ function PassiveSpecClass:SelectAscendClass(ascendClassId) -- Deallocate any allocated ascendancy nodes that don't belong to the new ascendancy class for id, node in pairs(self.allocNodes) do if node.ascendancyName and node.ascendancyName ~= ascendClass.name then - node.alloc = false - self.allocNodes[id] = nil + self:DeallocNode(node) end end + self:ReIndexNodeAllocationOrder("ascendancyAllocationOrder") if ascendClass.startNodeId then -- Allocate the new ascendancy class's start node @@ -376,10 +402,74 @@ function PassiveSpecClass:ResetNodes() for id, node in pairs(self.nodes) do if node.type ~= "ClassStart" and node.type ~= "AscendClassStart" then node.alloc = false + node.allocationOrder = nil + node.ascendancyAllocationOrder = nil self.allocNodes[id] = nil end end wipeTable(self.masterySelections) + self.allocationOrder = { } + self.ascendancyAllocationOrder = { } +end + +function PassiveSpecClass:SetAllocationOrder(node) + if node.ascendancyName then + t_insert(self.ascendancyAllocationOrder, node.id) + node.ascendancyAllocationOrder = #self.ascendancyAllocationOrder + else + t_insert(self.allocationOrder, node.id) + node.allocationOrder = #self.allocationOrder + end +end + +function PassiveSpecClass:RemoveFromAllocationOrder(node) + if node.ascendancyName then + t_remove(self.ascendancyAllocationOrder, node.ascendancyAllocationOrder) + node.ascendancyAllocationOrder = nil + else + t_remove(self.allocationOrder, node.allocationOrder) + node.allocationOrder = nil + end +end + +function PassiveSpecClass:ReIndexNodeAllocationOrder(allocationOrder) + for i=#self[allocationOrder],1,-1 do + nodeId = self[allocationOrder][i] + if self.nodes[nodeId] then + self.nodes[nodeId][allocationOrder] = i + else + t_remove(self[allocationOrder], i) + end + end +end + +function PassiveSpecClass:SortNodesForRemovingFromAllocationOrder(node) + depNodes = node.depends + if node.ascendancyName then + table.sort(depNodes, function(a, b) + if a.ascendancyAllocationOrder == nil and b.ascendancyAllocationOrder == nil then + return false + elseif a.ascendancyAllocationOrder == nil then + return true + elseif b.ascendancyAllocationOrder == nil then + return false + else + return a.ascendancyAllocationOrder > b.ascendancyAllocationOrder + end + end) + else + table.sort(depNodes, function(a, b) + if a.allocationOrder == nil and b.allocationOrder == nil then + return false + elseif a.allocationOrder == nil then + return true + elseif b.allocationOrder == nil then + return false + else + return a.allocationOrder > b.allocationOrder + end + end) + end end -- Allocate the given node, if possible, and all nodes along the path to the node @@ -395,10 +485,14 @@ function PassiveSpecClass:AllocNode(node, altPath) if node.dependsOnIntuitiveLeapLike then node.alloc = true self.allocNodes[node.id] = node + self:SetAllocationOrder(node) else - for _, pathNode in ipairs(altPath or node.path) do + local path = altPath or node.path + for i = #(path), 1, -1 do + local pathNode = path[i] pathNode.alloc = true self.allocNodes[pathNode.id] = pathNode + self:SetAllocationOrder(pathNode) end end @@ -407,8 +501,12 @@ function PassiveSpecClass:AllocNode(node, altPath) local parent = node.linked[1] for _, optNode in ipairs(parent.linked) do if optNode.isMultipleChoiceOption and optNode.alloc and optNode ~= node then - optNode.alloc = false - self.allocNodes[optNode.id] = nil + self.DeallocSingleNode(optNode) + if optNode.ascendancyName then + self:ReIndexNodeAllocationOrder("ascendancyAllocationOrder") + else + self:ReIndexNodeAllocationOrder("allocationOrder") + end end end end @@ -424,14 +522,23 @@ function PassiveSpecClass:DeallocSingleNode(node) self:AddMasteryEffectOptionsToNode(node) self.masterySelections[node.id] = nil end + if self.allocationOrder[node.allocationOrder] or self.ascendancyAllocationOrder[node.ascendancyAllocationOrder] then + self:RemoveFromAllocationOrder(node) + end end -- Deallocate the given node, and all nodes which depend on it (i.e. which are only connected to the tree through this node) function PassiveSpecClass:DeallocNode(node) - local effect + self:SortNodesForRemovingFromAllocationOrder(node) + for _, depNode in ipairs(node.depends) do self:DeallocSingleNode(depNode) end + if node.ascendancyName then + self:ReIndexNodeAllocationOrder("ascendancyAllocationOrder") + else + self:ReIndexNodeAllocationOrder("allocationOrder") + end -- Rebuild all paths and dependencies for all allocated nodes self:BuildAllDependsAndPaths() @@ -774,6 +881,7 @@ function PassiveSpecClass:BuildAllDependsAndPaths() if not anyStartFound then -- No start nodes were found through ANY nodes -- Therefore this node and all nodes depending on it are orphans and should be pruned + self:SortNodesForRemovingFromAllocationOrder(node) for _, depNode in ipairs(node.depends) do local prune = true for nodeId, itemId in pairs(self.jewels) do @@ -797,9 +905,14 @@ function PassiveSpecClass:BuildAllDependsAndPaths() end end if prune then - self:DeallocSingleNode(depNode) + self:DeallocNode(depNode) end end + if node.ascendancyName then + self:ReIndexNodeAllocationOrder("ascendancyAllocationOrder") + else + self:ReIndexNodeAllocationOrder("allocationOrder") + end end end @@ -853,9 +966,27 @@ end function PassiveSpecClass:BuildClusterJewelGraphs() -- Remove old subgraphs + local reserverdAllocationOrders = {} for id, subGraph in pairs(self.subGraphs) do + table.sort(subGraph.nodes, function(a, b) + if a.allocationOrder == nil and b.allocationOrder == nil then + return false + elseif a.allocationOrder == nil then + return true + elseif b.allocationOrder == nil then + return false + else + return a.allocationOrder > b.allocationOrder + end + end) for _, node in ipairs(subGraph.nodes) do if node.id then + if node.alloc then + if node.allocationOrder ~= nil then + reserverdAllocationOrders[node.id] = node.allocationOrder + node.allocationOrder = nil + end + end self.nodes[node.id] = nil if self.allocNodes[node.id] then -- Reserve the allocation in case the node is regenerated @@ -893,8 +1024,13 @@ function PassiveSpecClass:BuildClusterJewelGraphs() end end + ConPrintTable(reserverdAllocationOrders) -- (Re-)allocate subgraph nodes - for _, nodeId in ipairs(self.allocSubgraphNodes) do + local sortedAllocSubgraphNodes = {} + for i=#self.allocSubgraphNodes, 1, -1 do + sortedAllocSubgraphNodes[#sortedAllocSubgraphNodes+1] = self.allocSubgraphNodes[i] + end + for _, nodeId in ipairs(sortedAllocSubgraphNodes) do local node = self.nodes[nodeId] if node then node.alloc = true @@ -902,8 +1038,22 @@ function PassiveSpecClass:BuildClusterJewelGraphs() self.allocNodes[nodeId] = node t_insert(self.allocExtendedNodes, nodeId) end + if not self.initialLoad then + if reserverdAllocationOrders[nodeId] then + self.allocationOrder[reserverdAllocationOrders[nodeId]] = nodeId + node.allocationOrder = reserverdAllocationOrders[nodeId] + reserverdAllocationOrders[nodeId] = nil + else + self:SetAllocationOrder(node) + end + end end end + for nodeId, unallocated in pairs(reserverdAllocationOrders) do + self:RemoveFromAllocationOrder(self.nodes[nodeId]) + end + ConPrintTable(self.allocationOrder) + self:ReIndexNodeAllocationOrder("allocationOrder") wipeTable(self.allocSubgraphNodes) -- Rebuild paths to account for new/removed nodes @@ -1319,16 +1469,24 @@ function PassiveSpecClass:CreateUndoState() for mastery, effect in pairs(self.masterySelections) do selections[mastery] = effect end + local allocationOrder = copyTable(self.allocationOrder) + local ascendancyAllocationOrder = copyTable(self.ascendancyAllocationOrder) return { classId = self.curClassId, ascendClassId = self.curAscendClassId, hashList = allocNodeIdList, - masteryEffects = selections + masteryEffects = selections, + allocationOrder = allocationOrder, + ascendancyAllocationOrder = ascendancyAllocationOrder } end function PassiveSpecClass:RestoreUndoState(state) self:ImportFromNodeList(state.classId, state.ascendClassId, state.hashList, state.masteryEffects) + self.allocationOrder = state.allocationOrder + self.ascendancyAllocationOrder = state.ascendancyAllocationOrder + self:ReIndexNodeAllocationOrder("allocationOrder") + self:ReIndexNodeAllocationOrder("ascendancyAllocationOrder") self:SetWindowTitleWithBuildClass() end diff --git a/src/Classes/PassiveTreeView.lua b/src/Classes/PassiveTreeView.lua index 1cfdef49ca..1b2c1dcc62 100644 --- a/src/Classes/PassiveTreeView.lua +++ b/src/Classes/PassiveTreeView.lua @@ -75,6 +75,9 @@ function PassiveTreeViewClass:Load(xml, fileName) if xml.attrib.searchStr then self.searchStr = xml.attrib.searchStr end + if xml.attrib.showAllocationOrder then + self.showAllocationOrder = xml.attrib.showAllocationOrder == "true" + end if xml.attrib.showHeatMap then self.showHeatMap = xml.attrib.showHeatMap == "true" end @@ -89,6 +92,7 @@ function PassiveTreeViewClass:Save(xml) zoomX = tostring(self.zoomX), zoomY = tostring(self.zoomY), searchStr = self.searchStr, + showAllocationOrder = tostring(self.showAllocationOrder), showHeatMap = tostring(self.showHeatMap), showStatDifferences = tostring(self.showStatDifferences), } @@ -670,6 +674,20 @@ function PassiveTreeViewClass:Draw(build, viewPort, inputEvents) local size = 175 * scale / self.zoom ^ 0.4 DrawImage(self.highlightRing, scrX - size, scrY - size, size * 2, size * 2) end + if self.showAllocationOrder and node.allocationOrder ~= nil then + SetDrawLayer(nile, 29) + SetDrawColor(1, 0, 0) + local size = 100 * scale + local offset = node.size * 1.8 * scale + DrawString(m_floor(scrX - offset), m_floor(scrY - offset), "LEFT", size, "VAR", tostring(node.allocationOrder)) + end + if self.showAllocationOrder and node.ascendancyAllocationOrder ~= nil then + SetDrawLayer(nile, 29) + SetDrawColor(1, 0, 0) + local size = 100 * scale + local offset = node.size * 1.8 * scale + DrawString(m_floor(scrX - offset), m_floor(scrY - offset), "LEFT", size, "VAR", tostring(node.ascendancyAllocationOrder)) + end if node == hoverNode and (node.type ~= "Socket" or not IsKeyDown("SHIFT")) and (node.type ~= "Mastery" or node.masteryEffects) and not IsKeyDown("CTRL") and not main.popups[1] then -- Draw tooltip SetDrawLayer(nil, 100) diff --git a/src/Classes/TreeTab.lua b/src/Classes/TreeTab.lua index af41f5588c..a4db52b7b5 100644 --- a/src/Classes/TreeTab.lua +++ b/src/Classes/TreeTab.lua @@ -107,8 +107,11 @@ local TreeTabClass = newClass("TreeTab", "ControlHost", function(self, build) self.controls.treeSearch = new("EditControl", {"LEFT",self.controls.export,"RIGHT"}, 8, 0, main.portraitMode and 200 or 300, 20, "", "Search", "%c%(%)", 100, function(buf) self.viewer.searchStr = buf end) - self.controls.treeSearch.tooltipText = "Uses Lua pattern matching for complex searches" - self.controls.treeHeatMap = new("CheckBoxControl", {"LEFT",self.controls.treeSearch,"RIGHT"}, 130, 0, 20, "Show Node Power:", function(state) + self.controls.treeSearch.tooltipText = "Uses Lua pattern matching for complex searches" + self.controls.treeAllocationOrder = new("CheckBoxControl", {"LEFT",self.controls.treeSearch,"RIGHT"}, 194, 0, 20, "Show Node Allocation Order:", function(state) + self.viewer.showAllocationOrder = state + end) + self.controls.treeHeatMap = new("CheckBoxControl", {"LEFT",self.controls.treeAllocationOrder,"RIGHT"}, 130, 0, 20, "Show Node Power:", function(state) self.viewer.showHeatMap = state self.controls.treeHeatMapStatSelect.shown = state end) @@ -228,6 +231,8 @@ function TreeTabClass:Draw(viewPort, inputEvents) self.controls.treeSearch:SetText(self.viewer.searchStr) end + self.controls.treeAllocationOrder.state = self.viewer.showAllocationOrder + self.controls.treeHeatMap.state = self.viewer.showHeatMap self.controls.treeHeatMapStatSelect.list = self.powerStatList self.controls.treeHeatMapStatSelect.selIndex = 1