@@ -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,42 @@ 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+ return not self :PathCrossesWeaponSets (node , spec )
288+ end
289289 end
290290
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
291+ -- Unified helper function to determine if node deallocation should be blocked
292+ local function shouldBlockNodeDeallocation (node )
293+ if not node .alloc then
296294 return false
297295 end
298296
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
297+ local isGlobalNode = node .type == " Keystone" or node .type == " Socket" or node .containJewelSocket
302298
303- return shouldBlock
299+ if isGlobalNode then
300+ return node .allocMode == 0 and spec .allocMode > 0
301+ else
302+ return node .allocMode ~= spec .allocMode and node .allocMode ~= 0
303+ end
304304 end
305305
306306 if treeClick == " LEFT" then
307307 if hoverNode then
308308 -- User left-clicked on a node
309- if hoverNode .alloc and not shouldBlockGlobalNodeDeallocation (hoverNode ) then
309+ if hoverNode .alloc and not shouldBlockNodeDeallocation (hoverNode ) then
310310 -- Handle deallocation of allocated nodes
311311 if hoverNode .isAttribute then
312312 -- change to other attribute without needing to deallocate
@@ -323,7 +323,7 @@ function PassiveTreeViewClass:Draw(build, viewPort, inputEvents)
323323 end
324324 spec :AddUndoState ()
325325 build .buildFlag = true
326- elseif hoverNode .path and not shouldBlockGlobalNodeAllocation (hoverNode ) then
326+ elseif hoverNode .path and not shouldBlockNodeAllocation (hoverNode ) then
327327 -- Handle allocation of unallocated nodes
328328 if hoverNode .isAttribute and not hotkeyPressed then
329329 build .treeTab :ModifyAttributePopup (hoverNode )
@@ -350,7 +350,8 @@ function PassiveTreeViewClass:Draw(build, viewPort, inputEvents)
350350 build .itemsTab :SelectControl (slot )
351351 build .viewMode = " ITEMS"
352352 end
353- else
353+ elseif not hoverNode .alloc and not shouldBlockNodeAllocation (hoverNode ) then
354+ -- Only proceed with allocation if it's not blocked
354355 -- a way for us to bypass the popup when allocating attribute nodes, last used hotkey + RMB
355356 -- RMB + non attribute node logic
356357 -- RMB hot-swap logic
@@ -1183,7 +1184,7 @@ function PassiveTreeViewClass:AddNodeTooltip(tooltip, node, build, incSmallPassi
11831184 tooltip :AddLine (14 , colorCodes .TIP .. " Tip: Right click this socket to go to the items page and choose the jewel for this socket." )
11841185 end
11851186
1186- self :AddGlobalNodeWarningsToTooltip (tooltip , node , build )
1187+ self :AddNodeWarningsToTooltip (tooltip , node , build )
11871188
11881189 tooltip :AddLine (14 , colorCodes .TIP .. " Tip: Hold Shift or Ctrl to hide this tooltip." )
11891190 return
@@ -1448,7 +1449,7 @@ function PassiveTreeViewClass:AddNodeTooltip(tooltip, node, build, incSmallPassi
14481449 tooltip :AddLine (14 , colorCodes .TIP )
14491450 end
14501451
1451- self :AddGlobalNodeWarningsToTooltip (tooltip , node , build )
1452+ self :AddNodeWarningsToTooltip (tooltip , node , build )
14521453
14531454 if node .type == " Socket" then
14541455 tooltip :AddLine (14 , colorCodes .TIP .. " Tip: Hold Shift or Ctrl to hide this tooltip." )
@@ -1484,31 +1485,96 @@ function PassiveTreeViewClass:IsConnectedToWeaponSetNodes(node)
14841485 return false
14851486end
14861487
1487- -- Helper function to add warnings in the tooltip for global nodes (keystones/jewel sockets)
1488- function PassiveTreeViewClass :AddGlobalNodeWarningsToTooltip (tooltip , node , build )
1488+ -- Helper function to get detailed blocking reason for a node
1489+ -- Returns: reason, canAllocate
1490+ -- reason: "global_in_weapon_set", "path_through_global", "weapon_set_crossing", nil
1491+ function PassiveTreeViewClass :GetNodeBlockingReason (node , spec )
1492+ if not node or node .alloc or not node .path then
1493+ return nil , true
1494+ end
1495+
14891496 local isGlobalNode = node .type == " Keystone" or node .type == " Socket" or node .containJewelSocket
1497+ if isGlobalNode and spec .allocMode > 0 then
1498+ return " global_in_weapon_set" , false
1499+ end
14901500
1491- if not isGlobalNode then
1492- return -- No warning needed for non-global nodes
1501+ -- Check if a node conflicts with the current weapon set
1502+ local function isWeaponSetCompatible (allocatedNode )
1503+ return allocatedNode .allocMode == 0 or allocatedNode .allocMode == spec .allocMode
14931504 end
14941505
1495- local nodeTypeText = node .type == " Keystone" and " keystones" or " jewel sockets"
1506+ -- Check path or direct node for conflicts
1507+ local nodesToCheck = node .path and # node .path > 1 and node .path or { node }
1508+ for i , currentNode in ipairs (nodesToCheck ) do
1509+ -- Skip target node for path-specific checks if part of a multi-node path
1510+ if i > 1 or # nodesToCheck == 1 then
1511+ -- Block path through unallocated global nodes in weapon set mode
1512+ if spec .allocMode > 0 and not currentNode .alloc then
1513+ local isGlobalNode = currentNode .type == " Keystone" or currentNode .type == " Socket" or currentNode .containJewelSocket
1514+ if isGlobalNode then
1515+ return " path_through_global" , false
1516+ end
1517+ end
1518+
1519+ -- Check for weapon set conflicts with allocated nodes
1520+ if currentNode .alloc and not isWeaponSetCompatible (currentNode ) then
1521+ return " weapon_set_crossing" , false
1522+ end
1523+ end
1524+
1525+ -- Check linked nodes for conflicts, excluding those in our path
1526+ if currentNode .linked then
1527+ for _ , linkedNode in ipairs (currentNode .linked ) do
1528+ if not (i == 1 and node .path and linkedNode == node ) and linkedNode .alloc and not isWeaponSetCompatible (linkedNode ) then
1529+ return " weapon_set_crossing" , false
1530+ end
1531+ end
1532+ end
1533+ end
1534+
1535+ return nil , true
1536+ end
1537+
1538+ -- Helper function to add warnings in the tooltip for nodes affected by weapon set rules
1539+ function PassiveTreeViewClass :AddNodeWarningsToTooltip (tooltip , node , build )
1540+ local isGlobalNode = node .type == " Keystone" or node .type == " Socket" or node .containJewelSocket
1541+ local spec = build .spec
1542+
14961543 local warningText = " "
14971544 local tipText = " "
14981545
14991546 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
1547+ -- Get detailed blocking reason
1548+ local blockingReason , canAllocate = self :GetNodeBlockingReason (node , spec )
1549+
1550+ if not canAllocate then
1551+ if blockingReason == " global_in_weapon_set" then
1552+ local nodeTypeText = node .type == " Keystone" and " keystones" or " jewel sockets"
1553+ warningText = " Cannot allocate " .. nodeTypeText .. " while weapon set " .. spec .allocMode .. " is selected"
1554+ tipText = " Tip: Switch to main tree (Alt+scroll) to allocate " .. nodeTypeText
1555+ elseif blockingReason == " path_through_global" then
1556+ warningText = " Cannot allocate while weapon set " .. spec .allocMode .. " is selected - path goes through global nodes"
1557+ tipText = " Tip: Switch to main tree (Alt+scroll) or find an alternative path"
1558+ elseif blockingReason == " weapon_set_crossing" then
1559+ warningText = " Cannot allocate - path crosses between different weapon sets"
1560+ tipText = " Tip: Deallocate conflicting weapon set nodes in the path first"
1561+ end
1562+ elseif isGlobalNode and self :IsConnectedToWeaponSetNodes (node ) then
1563+ -- Special case: global node connected to weapon set nodes (existing logic)
1564+ local nodeTypeText = node .type == " Keystone" and " keystones" or " jewel sockets"
15051565 warningText = " Cannot allocate " .. nodeTypeText .. " - connected to weapon set nodes"
15061566 tipText = " Tip: Deallocate weapon set nodes in the connection path to allow allocation"
15071567 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
1568+ elseif node .alloc then
1569+ -- Allocated node warnings
1570+ if isGlobalNode and node .allocMode == 0 and spec .allocMode > 0 then
1571+ local nodeTypeText = node .type == " Keystone" and " keystones" or " jewel sockets"
1572+ warningText = " Cannot deallocate global " .. nodeTypeText .. " from weapon set " .. spec .allocMode
1573+ tipText = " Tip: Switch to main tree (Alt+scroll) to deallocate " .. nodeTypeText
1574+ elseif (node .isAttribute or not isGlobalNode ) and node .allocMode ~= spec .allocMode and node .allocMode ~= 0 then
1575+ warningText = " Cannot deallocate - node was allocated by weapon set " .. node .allocMode
1576+ tipText = " Tip: Switch to weapon set " .. node .allocMode .. " (Alt+scroll) to deallocate this node"
1577+ end
15121578 end
15131579
15141580 if warningText ~= " " then
0 commit comments