Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Correctly generate dependencies for nodes affected by intuitive leap-like jewels #7719

Merged
merged 2 commits into from
Jul 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 127 additions & 29 deletions src/Classes/PassiveSpec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -669,7 +669,7 @@ function PassiveSpecClass:AllocNode(node, altPath)
end

-- Allocate all nodes along the path
if node.dependsOnIntuitiveLeapLike then
if #node.intuitiveLeapLikesAffecting > 0 then
node.alloc = true
self.allocNodes[node.id] = node
else
Expand Down Expand Up @@ -788,7 +788,7 @@ function PassiveSpecClass:BuildPathFromNode(root)
-- All nodes that are 1 node away from the root will be processed first, then all nodes that are 2 nodes away, etc
local node = queue[o]
o = o + 1
local curDist = node.pathDist + 1
local curDist = node.pathDist
-- Iterate through all nodes that are connected to this one
for _, other in ipairs(node.linked) do
-- Paths must obey these rules:
Expand All @@ -803,6 +803,9 @@ function PassiveSpecClass:BuildPathFromNode(root)
if node.type ~= "Mastery" and other.type ~= "ClassStart" and other.type ~= "AscendClassStart" and other.pathDist > curDist and (node.ascendancyName == other.ascendancyName or (curDist == 1 and not other.ascendancyName)) then
-- The shortest path to the other node is through the current node
other.pathDist = curDist
if not other.alloc then
other.pathDist = other.pathDist + 1
end
other.path = wipeTable(other.path)
other.path[1] = other
for i, n in ipairs(node.path) do
Expand All @@ -820,7 +823,7 @@ end
-- Only allocated nodes can be traversed
function PassiveSpecClass:SetNodeDistanceToClassStart(root)
root.distanceToClassStart = 0
if not root.alloc or root.dependsOnIntuitiveLeapLike then
if not root.alloc or not root.connectedToStart then
return
end

Expand Down Expand Up @@ -876,6 +879,36 @@ function PassiveSpecClass:AddMasteryEffectOptionsToNode(node)
node.allMasteryOptions = true
end

function PassiveSpecClass:NodesInIntuitiveLeapLikeRadius(node)
local result = { }
if self.jewels[node.id] and self.jewels[node.id] > 0 then
local item = self.build.itemsTab.items[self.jewels[node.id]]
local radiusIndex = item.jewelRadiusIndex
if item and item.jewelData and item.jewelData.intuitiveLeapLike then
local inRadius = self.nodes[node.id].nodesInRadius and self.nodes[node.id].nodesInRadius[radiusIndex]
for affectedNodeId, affectedNode in pairs(inRadius) do
if self.nodes[affectedNodeId].alloc then
t_insert(result, self.nodes[affectedNodeId])
end
end
end

if item.jewelData and item.jewelData.impossibleEscapeKeystone then
for keyName, keyNode in pairs(item.jewelData.impossibleEscapeKeystones) do
if self.tree.keystoneMap[keyName].nodesInRadius then
for affectedNodeId in pairs(self.tree.keystoneMap[keyName].nodesInRadius[radiusIndex]) do
if self.nodes[affectedNodeId].alloc then
t_insert(result, self.nodes[affectedNodeId])
end
end
end
end
end
end

return result
end

-- Rebuilds dependencies and paths for all nodes
function PassiveSpecClass:BuildAllDependsAndPaths()
-- This table will keep track of which nodes have been visited during each path-finding attempt
Expand All @@ -884,7 +917,7 @@ function PassiveSpecClass:BuildAllDependsAndPaths()
-- Check all nodes for other nodes which depend on them (i.e. are only connected to the tree through that node)
for id, node in pairs(self.nodes) do
node.depends = wipeTable(node.depends)
node.dependsOnIntuitiveLeapLike = false
node.intuitiveLeapLikesAffecting = { }
node.conqueredBy = nil

-- ignore cluster jewel nodes that don't have an id in the tree
Expand All @@ -902,10 +935,9 @@ function PassiveSpecClass:BuildAllDependsAndPaths()
if item.jewelData.intuitiveLeapLike then
-- This node depends on Intuitive Leap-like behaviour
-- This flag:
-- 1. Prevents generation of paths from this node
-- 2. Prevents this node from being deallocted via dependency
-- 3. Prevents allocation of path nodes when this node is being allocated
node.dependsOnIntuitiveLeapLike = true
-- 1. Prevents generation of paths from this node unless it's also connected to the start
-- 2. Prevents allocation of path nodes when this node is being allocated
t_insert(node.intuitiveLeapLikesAffecting, self.nodes[nodeId])
end
if item.jewelData.conqueredBy then
node.conqueredBy = item.jewelData.conqueredBy
Expand All @@ -915,9 +947,9 @@ function PassiveSpecClass:BuildAllDependsAndPaths()

if item.jewelData and item.jewelData.impossibleEscapeKeystone then
for keyName, keyNode in pairs(self.tree.keystoneMap) do
if item.jewelData.impossibleEscapeKeystones[keyName:lower()] and keyNode.nodesInRadius then
if item.jewelData.impossibleEscapeKeystones[keyName] and keyNode.nodesInRadius then
if keyNode.nodesInRadius[radiusIndex][node.id] then
node.dependsOnIntuitiveLeapLike = true
t_insert(node.intuitiveLeapLikesAffecting, self.nodes[nodeId])
end
end
end
Expand Down Expand Up @@ -1158,18 +1190,23 @@ function PassiveSpecClass:BuildAllDependsAndPaths()
end
end

local potentialDeps = { }
local intuitiveLeaps = { }
for id, node in pairs(self.allocNodes) do
node.visited = true
node.connectedToStart = false
local anyStartFound = (node.type == "ClassStart" or node.type == "AscendClassStart")
for _, other in ipairs(node.linked) do
if other.alloc and not isValueInArray(node.depends, other) then
-- The other node is allocated and isn't already dependent on this node, so try and find a path to a start node through it
if other.type == "ClassStart" or other.type == "AscendClassStart" then
-- Well that was easy!
anyStartFound = true
node.connectedToStart = true
elseif self:FindStartFromNode(other, visited) then
-- We found a path through the other node, therefore the other node cannot be dependent on this node
anyStartFound = true
node.connectedToStart = true
for i, n in ipairs(visited) do
n.visited = false
visited[i] = nil
Expand All @@ -1179,33 +1216,50 @@ function PassiveSpecClass:BuildAllDependsAndPaths()
-- except for mastery nodes that have linked allocated nodes that weren't visited
local depIds = { }
for _, n in ipairs(visited) do
if not n.dependsOnIntuitiveLeapLike then
if #n.intuitiveLeapLikesAffecting == 0 then
depIds[n.id] = true
end
end
for i, n in ipairs(visited) do
if not n.dependsOnIntuitiveLeapLike then
if n.type == "Mastery" then
local otherPath = false
local allocatedLinkCount = 0
for _, linkedNode in ipairs(n.linked) do
if linkedNode.alloc then
allocatedLinkCount = allocatedLinkCount + 1
end
if n.type == "Mastery" then
local otherPath = false
local allocatedLinkCount = 0
for _, linkedNode in ipairs(n.linked) do
if linkedNode.alloc then
allocatedLinkCount = allocatedLinkCount + 1
end
if allocatedLinkCount > 1 then
for _, linkedNode in ipairs(n.linked) do
if linkedNode.alloc and not depIds[linkedNode.id] then
otherPath = true
end
end
if allocatedLinkCount > 1 then
for _, linkedNode in ipairs(n.linked) do
if linkedNode.alloc and not depIds[linkedNode.id] then
otherPath = true
end
end
if not otherPath then
t_insert(node.depends, n)
end
if not otherPath then
t_insert(node.depends, n)
end
else
-- If n is dependent on intuitive leap then it may be dependent on this node
if #n.intuitiveLeapLikesAffecting > 0 then
if not potentialDeps[n.id] then
potentialDeps[n.id] = { }
end

t_insert(potentialDeps[n.id], node)
else
t_insert(node.depends, n)
end

-- If n is a jewel socket containing an intuitive leap-like jewel, nodes in its radius (or the radius of the keystone)
-- may be dependent on this node if they're found to be unconnected to the start
if not intuitiveLeaps[node.id] then
intuitiveLeaps[node.id] = self:NodesInIntuitiveLeapLikeRadius(n)
else
for _, affectedNode in ipairs(self:NodesInIntuitiveLeapLikeRadius(n)) do
t_insert(intuitiveLeaps[node.id], affectedNode)
end
end
end
n.visited = false
visited[i] = nil
Expand Down Expand Up @@ -1236,7 +1290,10 @@ function PassiveSpecClass:BuildAllDependsAndPaths()
) then
-- Hold off on the pruning; this node could be supported by Intuitive Leap-like jewel
prune = false
t_insert(self.nodes[nodeId].depends, depNode)
if not intuitiveLeaps[nodeId] then
intuitiveLeaps[nodeId] = { }
end
t_insert(intuitiveLeaps[nodeId], depNode)
break
end
end
Expand All @@ -1248,16 +1305,57 @@ function PassiveSpecClass:BuildAllDependsAndPaths()
end
end

-- All other dependencies resolved, add dependencies to nodes affected by intuitive leap-like jewels
-- Nodes that are unconnected to the start depend on all nodes that connect the jewel socket to the start
-- Nodes that are connected to the start depend on all nodes that connect them *and* the jewel socket to the start
-- In both cases, the nodes can be affected by multiple jewels at the same time
for id, deps in pairs(potentialDeps) do
local potentialNode = self.nodes[id]
local seen = { }
for _, node in ipairs(deps) do
local allDep = true
for _, intuitiveLeapLikeProvider in pairs(potentialNode.intuitiveLeapLikesAffecting) do
if not isValueInArray(node.depends, intuitiveLeapLikeProvider) then
allDep = false
end
end
if allDep and not seen[node.id] then
t_insert(node.depends, potentialNode)
seen[node.id] = true
end
end
end

for id, deps in pairs(intuitiveLeaps) do
local node = self.nodes[id]
local seen = { }
for _, dep in ipairs(deps) do
if not dep.connectedToStart then
local allDep = true
for _, intuitiveDep in ipairs(dep.intuitiveLeapLikesAffecting) do
if not isValueInArray(node.depends, intuitiveDep) then
allDep = false
break
end
end
if allDep and not seen[dep.id] then
t_insert(node.depends, dep)
seen[dep.id] = true
end
end
end
end

-- Reset and rebuild all node paths
for id, node in pairs(self.nodes) do
node.pathDist = (node.alloc and not node.dependsOnIntuitiveLeapLike) and 0 or 1000
node.pathDist = (node.alloc and #node.intuitiveLeapLikesAffecting == 0) and 0 or 1000
node.path = nil
if node.isJewelSocket or node.expansionJewel then
node.distanceToClassStart = 0
end
end
for id, node in pairs(self.allocNodes) do
if not node.dependsOnIntuitiveLeapLike then
if #node.intuitiveLeapLikesAffecting == 0 or node.connectedToStart then
self:BuildPathFromNode(node)
if node.isJewelSocket or node.expansionJewel then
self:SetNodeDistanceToClassStart(node)
Expand Down
20 changes: 13 additions & 7 deletions src/Classes/PassiveTreeView.lua
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ function PassiveTreeViewClass:Draw(build, viewPort, inputEvents)
elseif hoverNode and hoverNode.path then
-- Use the node's own path and dependence list
hoverPath = { }
if not hoverNode.dependsOnIntuitiveLeapLike then
if #hoverNode.intuitiveLeapLikesAffecting == 0 then
for _, pathNode in pairs(hoverNode.path) do
hoverPath[pathNode] = true
end
Expand Down Expand Up @@ -632,7 +632,13 @@ function PassiveTreeViewClass:Draw(build, viewPort, inputEvents)

-- Draw base artwork
if base then
self:DrawAsset(base, scrX, scrY, scale)
if node.type == "Socket" and hoverDep and hoverDep[node] then
SetDrawColor(1, 0, 0);
self:DrawAsset(base, scrX, scrY, scale)
SetDrawColor(1, 1, 1);
else
self:DrawAsset(base, scrX, scrY, scale)
end
end

if overlay then
Expand Down Expand Up @@ -1076,7 +1082,7 @@ function PassiveTreeViewClass:AddNodeTooltip(tooltip, node, build)
elseif node.alloc then
-- Calculate the differences caused by deallocating this node and its dependent nodes
nodeOutput = calcFunc({ removeNodes = { [node] = true } })
if not node.dependsOnIntuitiveLeapLike and pathLength > 1 then
if pathLength > 1 then
pathOutput = calcFunc({ removeNodes = pathNodes })
end
elseif isGranted then
Expand All @@ -1090,19 +1096,19 @@ function PassiveTreeViewClass:AddNodeTooltip(tooltip, node, build)
else
nodeOutput = calcFunc({ addNodes = { [node] = true } })
end
if not node.dependsOnIntuitiveLeapLike and pathLength > 1 then
if pathLength > 1 then
pathOutput = calcFunc({ addNodes = pathNodes })
end
end
local count = build:AddStatComparesToTooltip(tooltip, calcBase, nodeOutput, realloc and "^7Reallocating this node will give you:" or node.alloc and "^7Unallocating this node will give you:" or isGranted and "^7This node is granted by an item. Removing it will give you:" or "^7Allocating this node will give you:")
if not node.dependsOnIntuitiveLeapLike and pathLength > 1 and not isGranted then
if pathLength > 1 and not isGranted then
count = count + build:AddStatComparesToTooltip(tooltip, calcBase, pathOutput, node.alloc and "^7Unallocating this node and all nodes depending on it will give you:" or "^7Allocating this node and all nodes leading to it will give you:", pathLength)
end
if count == 0 then
if isGranted then
tooltip:AddLine(14, string.format("^7This node is granted by an item. Removing it will cause no changes"))
else
tooltip:AddLine(14, string.format("^7No changes from %s this node%s.", node.alloc and "unallocating" or "allocating", not node.dependsOnIntuitiveLeapLike and pathLength > 1 and " or the nodes leading to it" or ""))
tooltip:AddLine(14, string.format("^7No changes from %s this node%s.", node.alloc and "unallocating" or "allocating", node.intuitiveLeapLikesAffecting == 0 and pathLength > 1 and " or the nodes leading to it" or ""))
end
end
tooltip:AddLine(14, colorCodes.TIP.."Tip: Press Ctrl+D to disable the display of stat differences.")
Expand All @@ -1118,7 +1124,7 @@ function PassiveTreeViewClass:AddNodeTooltip(tooltip, node, build)
tooltip:AddLine(14, "^7"..#self.tracePath .. " nodes in trace path")
tooltip:AddLine(14, colorCodes.TIP)
else
tooltip:AddLine(14, "^7"..#node.path .. " points to node" .. (node.dependsOnIntuitiveLeapLike and " ^8(Can be allocated without pathing to it)" or ""))
tooltip:AddLine(14, "^7"..node.pathDist .. " points to node" .. (#node.intuitiveLeapLikesAffecting > 0 and " ^8(Can be allocated without pathing to it)" or ""))
tooltip:AddLine(14, colorCodes.TIP)
if #node.path > 1 then
-- Handy hint!
Expand Down
Loading