Skip to content

Commit 3f2ad78

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 3f2ad78

1 file changed

Lines changed: 107 additions & 41 deletions

File tree

src/Classes/PassiveTreeView.lua

Lines changed: 107 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,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
14851486
end
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

Comments
 (0)