Skip to content

Commit 4d5a2cc

Browse files
committed
Prevent weapon set path crossing allocation
• Add GetNodeBlockingReason() with detailed validation for different blocking scenarios • Unify allocation/deallocation blocking in shouldBlockNodeAllocation() and shouldBlockNodeDeallocation() • Implement connection-based validation that checks entire path connectivity • Add specific tooltip warnings for global nodes, path crossing, and path through globals • Support directional weapon set compatibility (weapon sets connect to main tree, not vice versa)
1 parent aeea3ce commit 4d5a2cc

1 file changed

Lines changed: 108 additions & 41 deletions

File tree

src/Classes/PassiveTreeView.lua

Lines changed: 108 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -168,11 +168,11 @@ function PassiveTreeViewClass:Draw(build, viewPort, inputEvents)
168168
local offsetY = self.zoomY + viewPort.y + viewPort.height/2
169169
local function treeToScreen(x, y)
170170
return x * scale + offsetX,
171-
y * scale + offsetY
171+
y * scale + offsetY
172172
end
173173
local function screenToTree(x, y)
174174
return (x - offsetX) / scale,
175-
(y - offsetY) / scale
175+
(y - offsetY) / scale
176176
end
177177

178178
if IsKeyDown("SHIFT") then
@@ -271,42 +271,43 @@ function PassiveTreeViewClass:Draw(build, viewPort, inputEvents)
271271

272272
local hotkeyPressed = IsKeyDown("1") or IsKeyDown("I") or IsKeyDown("2") or IsKeyDown("S") or IsKeyDown("3") or IsKeyDown("D")
273273

274-
-- Helper function to determine if global node allocation should be blocked
275-
local function shouldBlockGlobalNodeAllocation(node)
276-
local isGlobalNode = node.type == "Keystone" or node.type == "Socket" or node.containJewelSocket
277-
278-
if not isGlobalNode or node.alloc or not node.path then
274+
-- Unified helper function to determine if node allocation should be blocked
275+
local function shouldBlockNodeAllocation(node)
276+
if not node.path or node.alloc then
279277
return false
280278
end
281279

282-
local weaponSetMode = spec.allocMode > 0
283-
local connectedToWeaponSetNodes = self:IsConnectedToWeaponSetNodes(node)
284-
285-
-- Only allow allocation from main tree AND node must not be connected to weapon set nodes
286-
local shouldBlock = weaponSetMode or connectedToWeaponSetNodes
280+
local isGlobalNode = node.type == "Keystone" or node.type == "Socket" or node.containJewelSocket
287281

288-
return shouldBlock
282+
if isGlobalNode then
283+
local weaponSetMode = spec.allocMode > 0
284+
local connectedToWeaponSetNodes = self:IsConnectedToWeaponSetNodes(node)
285+
return weaponSetMode or connectedToWeaponSetNodes
286+
else
287+
local _, canAllocate = self:GetNodeBlockingReason(node, spec)
288+
return not canAllocate
289+
end
289290
end
290291

291-
-- Helper function to determine if global node deallocation should be blocked
292-
local function shouldBlockGlobalNodeDeallocation(node)
293-
local isGlobalNode = node.type == "Keystone" or node.type == "Socket" or node.containJewelSocket
294-
295-
if not isGlobalNode or not node.alloc then
292+
-- Unified helper function to determine if node deallocation should be blocked
293+
local function shouldBlockNodeDeallocation(node)
294+
if not node.alloc then
296295
return false
297296
end
298297

299-
-- Main-tree global nodes can only be deallocated from main tree
300-
-- Legacy weapon-set global nodes can be deallocated from any mode
301-
local shouldBlock = node.allocMode == 0 and spec.allocMode > 0
298+
local isGlobalNode = node.type == "Keystone" or node.type == "Socket" or node.containJewelSocket
302299

303-
return shouldBlock
300+
if isGlobalNode then
301+
return node.allocMode == 0 and spec.allocMode > 0
302+
else
303+
return node.allocMode ~= spec.allocMode and node.allocMode ~= 0
304+
end
304305
end
305306

306307
if treeClick == "LEFT" then
307308
if hoverNode then
308309
-- User left-clicked on a node
309-
if hoverNode.alloc and not shouldBlockGlobalNodeDeallocation(hoverNode) then
310+
if hoverNode.alloc and not shouldBlockNodeDeallocation(hoverNode) then
310311
-- Handle deallocation of allocated nodes
311312
if hoverNode.isAttribute then
312313
-- change to other attribute without needing to deallocate
@@ -323,7 +324,7 @@ function PassiveTreeViewClass:Draw(build, viewPort, inputEvents)
323324
end
324325
spec:AddUndoState()
325326
build.buildFlag = true
326-
elseif hoverNode.path and not shouldBlockGlobalNodeAllocation(hoverNode) then
327+
elseif hoverNode.path and not shouldBlockNodeAllocation(hoverNode) then
327328
-- Handle allocation of unallocated nodes
328329
if hoverNode.isAttribute and not hotkeyPressed then
329330
build.treeTab:ModifyAttributePopup(hoverNode)
@@ -350,7 +351,8 @@ function PassiveTreeViewClass:Draw(build, viewPort, inputEvents)
350351
build.itemsTab:SelectControl(slot)
351352
build.viewMode = "ITEMS"
352353
end
353-
else
354+
elseif not hoverNode.alloc and not shouldBlockNodeAllocation(hoverNode) then
355+
-- Only proceed with allocation if it's not blocked
354356
-- a way for us to bypass the popup when allocating attribute nodes, last used hotkey + RMB
355357
-- RMB + non attribute node logic
356358
-- RMB hot-swap logic
@@ -1183,7 +1185,7 @@ function PassiveTreeViewClass:AddNodeTooltip(tooltip, node, build, incSmallPassi
11831185
tooltip:AddLine(14, colorCodes.TIP.."Tip: Right click this socket to go to the items page and choose the jewel for this socket.")
11841186
end
11851187

1186-
self:AddGlobalNodeWarningsToTooltip(tooltip, node, build)
1188+
self:AddNodeWarningsToTooltip(tooltip, node, build)
11871189

11881190
tooltip:AddLine(14, colorCodes.TIP.."Tip: Hold Shift or Ctrl to hide this tooltip.")
11891191
return
@@ -1448,7 +1450,7 @@ function PassiveTreeViewClass:AddNodeTooltip(tooltip, node, build, incSmallPassi
14481450
tooltip:AddLine(14, colorCodes.TIP)
14491451
end
14501452

1451-
self:AddGlobalNodeWarningsToTooltip(tooltip, node, build)
1453+
self:AddNodeWarningsToTooltip(tooltip, node, build)
14521454

14531455
if node.type == "Socket" then
14541456
tooltip:AddLine(14, colorCodes.TIP.."Tip: Hold Shift or Ctrl to hide this tooltip.")
@@ -1484,31 +1486,96 @@ function PassiveTreeViewClass:IsConnectedToWeaponSetNodes(node)
14841486
return false
14851487
end
14861488

1487-
-- Helper function to add warnings in the tooltip for global nodes (keystones/jewel sockets)
1488-
function PassiveTreeViewClass:AddGlobalNodeWarningsToTooltip(tooltip, node, build)
1489+
-- Helper function to get detailed blocking reason for a node
1490+
-- Returns: reason, canAllocate
1491+
-- reason: "global_in_weapon_set", "path_through_global", "weapon_set_crossing", nil
1492+
function PassiveTreeViewClass:GetNodeBlockingReason(node, spec)
1493+
if not node or node.alloc or not node.path then
1494+
return nil, true
1495+
end
1496+
14891497
local isGlobalNode = node.type == "Keystone" or node.type == "Socket" or node.containJewelSocket
1498+
if isGlobalNode and spec.allocMode > 0 then
1499+
return "global_in_weapon_set", false
1500+
end
14901501

1491-
if not isGlobalNode then
1492-
return -- No warning needed for non-global nodes
1502+
-- Check if a node conflicts with the current weapon set
1503+
local function isWeaponSetCompatible(allocatedNode)
1504+
return allocatedNode.allocMode == 0 or allocatedNode.allocMode == spec.allocMode
14931505
end
14941506

1495-
local nodeTypeText = node.type == "Keystone" and "keystones" or "jewel sockets"
1507+
-- Check path or direct node for conflicts
1508+
local nodesToCheck = node.path and #node.path > 1 and node.path or { node }
1509+
for i, currentNode in ipairs(nodesToCheck) do
1510+
-- Skip target node for path-specific checks if part of a multi-node path
1511+
if i > 1 or #nodesToCheck == 1 then
1512+
-- Block path through unallocated global nodes in weapon set mode
1513+
if spec.allocMode > 0 and not currentNode.alloc then
1514+
local isGlobalNode = currentNode.type == "Keystone" or currentNode.type == "Socket" or currentNode.containJewelSocket
1515+
if isGlobalNode then
1516+
return "path_through_global", false
1517+
end
1518+
end
1519+
1520+
-- Check for weapon set conflicts with allocated nodes
1521+
if currentNode.alloc and not isWeaponSetCompatible(currentNode) then
1522+
return "weapon_set_crossing", false
1523+
end
1524+
end
1525+
1526+
-- Check linked nodes for conflicts, excluding those in our path
1527+
if currentNode.linked then
1528+
for _, linkedNode in ipairs(currentNode.linked) do
1529+
if not (i == 1 and node.path and linkedNode == node) and linkedNode.alloc and not isWeaponSetCompatible(linkedNode) then
1530+
return "weapon_set_crossing", false
1531+
end
1532+
end
1533+
end
1534+
end
1535+
1536+
return nil, true
1537+
end
1538+
1539+
-- Helper function to add warnings in the tooltip for nodes affected by weapon set rules
1540+
function PassiveTreeViewClass:AddNodeWarningsToTooltip(tooltip, node, build)
1541+
local isGlobalNode = node.type == "Keystone" or node.type == "Socket" or node.containJewelSocket
1542+
local spec = build.spec
1543+
14961544
local warningText = ""
14971545
local tipText = ""
14981546

14991547
if not node.alloc and node.path then
1500-
-- Unallocated global node - check allocation conditions
1501-
if build.spec.allocMode > 0 then
1502-
warningText = "Cannot allocate " .. nodeTypeText .. " while weapon set " .. build.spec.allocMode .. " is selected"
1503-
tipText = "Tip: Switch to main tree (Alt+scroll) to allocate " .. nodeTypeText
1504-
elseif self:IsConnectedToWeaponSetNodes(node) then
1548+
-- Get detailed blocking reason
1549+
local blockingReason, canAllocate = self:GetNodeBlockingReason(node, spec)
1550+
1551+
if not canAllocate then
1552+
if blockingReason == "global_in_weapon_set" then
1553+
local nodeTypeText = node.type == "Keystone" and "keystones" or "jewel sockets"
1554+
warningText = "Cannot allocate " .. nodeTypeText .. " while weapon set " .. spec.allocMode .. " is selected"
1555+
tipText = "Tip: Switch to main tree (Alt+scroll) to allocate " .. nodeTypeText
1556+
elseif blockingReason == "path_through_global" then
1557+
warningText = "Cannot allocate while weapon set " .. spec.allocMode .. " is selected - path goes through global nodes"
1558+
tipText = "Tip: Switch to main tree (Alt+scroll) or find an alternative path"
1559+
elseif blockingReason == "weapon_set_crossing" then
1560+
warningText = "Cannot allocate - path crosses between different weapon sets"
1561+
tipText = "Tip: Deallocate conflicting weapon set nodes in the path first"
1562+
end
1563+
elseif isGlobalNode and self:IsConnectedToWeaponSetNodes(node) then
1564+
-- Special case: global node connected to weapon set nodes (existing logic)
1565+
local nodeTypeText = node.type == "Keystone" and "keystones" or "jewel sockets"
15051566
warningText = "Cannot allocate " .. nodeTypeText .. " - connected to weapon set nodes"
15061567
tipText = "Tip: Deallocate weapon set nodes in the connection path to allow allocation"
15071568
end
1508-
elseif node.alloc and node.allocMode == 0 and build.spec.allocMode > 0 then
1509-
-- Allocated main-tree global node viewed from weapon set
1510-
warningText = "Cannot deallocate global " .. nodeTypeText .. " from weapon set " .. build.spec.allocMode
1511-
tipText = "Tip: Switch to main tree (Alt+scroll) to deallocate " .. nodeTypeText
1569+
elseif node.alloc then
1570+
-- Allocated node warnings
1571+
if isGlobalNode and node.allocMode == 0 and spec.allocMode > 0 then
1572+
local nodeTypeText = node.type == "Keystone" and "keystones" or "jewel sockets"
1573+
warningText = "Cannot deallocate global " .. nodeTypeText .. " from weapon set " .. spec.allocMode
1574+
tipText = "Tip: Switch to main tree (Alt+scroll) to deallocate " .. nodeTypeText
1575+
elseif (node.isAttribute or not isGlobalNode) and node.allocMode ~= spec.allocMode and node.allocMode ~= 0 then
1576+
warningText = "Cannot deallocate - node was allocated by weapon set " .. node.allocMode
1577+
tipText = "Tip: Switch to weapon set " .. node.allocMode .. " (Alt+scroll) to deallocate this node"
1578+
end
15121579
end
15131580

15141581
if warningText ~= "" then

0 commit comments

Comments
 (0)