Skip to content

Commit 2e23485

Browse files
Thread of Hope ring cycle / all-rings planning toggle
Tree view: T cycles a ToH socket through Small/Medium/Large/Very Large/ Massive rings; R toggles "show all 5 rings". Both modes feed the calc engine via a shared GetEffectiveRadiusIndices helper so allocation, path finding, dependency tracking, and orphan pruning all agree on which ring(s) the jewel covers. A top-left badge labels the active mode so the inflated calcs aren't silent. State is session-only (not persisted to build XML) to avoid loading a saved build with mystery state.
1 parent 58a5e24 commit 2e23485

2 files changed

Lines changed: 113 additions & 29 deletions

File tree

src/Classes/PassiveSpec.lua

Lines changed: 54 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1031,16 +1031,36 @@ function PassiveSpecClass:AddMasteryEffectOptionsToNode(node)
10311031
node.allMasteryOptions = true
10321032
end
10331033

1034+
-- Returns the list of jewelRadius indices to consider for this item, honouring the
1035+
-- Thread of Hope tree-view override: cycle mode 1..5 -> jewelRadius 6..10, "all" -> all 5,
1036+
-- otherwise the item's own jewelRadiusIndex.
1037+
function PassiveSpecClass:GetEffectiveRadiusIndices(item)
1038+
local toHMode = self.build.treeTab and self.build.treeTab.viewer
1039+
and self.build.treeTab.viewer.toHRingMode
1040+
if toHMode and item.jewelRadiusLabel == "Variable" then
1041+
if toHMode == "all" then
1042+
return { 6, 7, 8, 9, 10 }
1043+
else
1044+
return { 5 + toHMode }
1045+
end
1046+
end
1047+
return { item.jewelRadiusIndex }
1048+
end
1049+
10341050
function PassiveSpecClass:NodesInIntuitiveLeapLikeRadius(node)
10351051
local result = { }
10361052
if self.jewels[node.id] and self.jewels[node.id] > 0 then
10371053
local item = self.build.itemsTab.items[self.jewels[node.id]]
10381054
local radiusIndex = item.jewelRadiusIndex
10391055
if item and item.jewelData and item.jewelData.intuitiveLeapLike then
1040-
local inRadius = self.nodes[node.id].nodesInRadius and self.nodes[node.id].nodesInRadius[radiusIndex]
1041-
for affectedNodeId, affectedNode in pairs(inRadius or {}) do
1042-
if self.nodes[affectedNodeId].alloc then
1043-
t_insert(result, self.nodes[affectedNodeId])
1056+
local seen = { }
1057+
for _, idx in ipairs(self:GetEffectiveRadiusIndices(item)) do
1058+
local inRadius = self.nodes[node.id].nodesInRadius and self.nodes[node.id].nodesInRadius[idx]
1059+
for affectedNodeId, affectedNode in pairs(inRadius or {}) do
1060+
if self.nodes[affectedNodeId].alloc and not seen[affectedNodeId] then
1061+
seen[affectedNodeId] = true
1062+
t_insert(result, self.nodes[affectedNodeId])
1063+
end
10441064
end
10451065
end
10461066
end
@@ -1082,18 +1102,21 @@ function PassiveSpecClass:BuildAllDependsAndPaths()
10821102
local item = self.build.itemsTab.items[itemId]
10831103
if item and item.jewelRadiusIndex and self.allocNodes[nodeId] and item.jewelData and not item.jewelData.limitDisabled then
10841104
local radiusIndex = item.jewelRadiusIndex
1085-
if self.nodes[nodeId].nodesInRadius and self.nodes[nodeId].nodesInRadius[radiusIndex][node.id] then
1086-
if itemId ~= 0 then
1087-
if item.jewelData.intuitiveLeapLike and not (item.jewelData.intuitiveLeapKeystoneOnly and node.type ~= "Keystone") then
1088-
-- This node depends on Intuitive Leap-like behaviour
1089-
-- This flag:
1090-
-- 1. Prevents generation of paths from this node unless it's also connected to the start
1091-
-- 2. Prevents allocation of path nodes when this node is being allocated
1092-
t_insert(node.intuitiveLeapLikesAffecting, self.nodes[nodeId])
1093-
end
1094-
if item.jewelData.conqueredBy then
1095-
node.conqueredBy = item.jewelData.conqueredBy
1105+
for _, idx in ipairs(self:GetEffectiveRadiusIndices(item)) do
1106+
if self.nodes[nodeId].nodesInRadius and self.nodes[nodeId].nodesInRadius[idx][node.id] then
1107+
if itemId ~= 0 then
1108+
if item.jewelData.intuitiveLeapLike and not (item.jewelData.intuitiveLeapKeystoneOnly and node.type ~= "Keystone") then
1109+
-- This node depends on Intuitive Leap-like behaviour
1110+
-- This flag:
1111+
-- 1. Prevents generation of paths from this node unless it's also connected to the start
1112+
-- 2. Prevents allocation of path nodes when this node is being allocated
1113+
t_insert(node.intuitiveLeapLikesAffecting, self.nodes[nodeId])
1114+
end
1115+
if item.jewelData.conqueredBy then
1116+
node.conqueredBy = item.jewelData.conqueredBy
1117+
end
10961118
end
1119+
break
10971120
end
10981121
end
10991122

@@ -1447,19 +1470,22 @@ function PassiveSpecClass:BuildAllDependsAndPaths()
14471470
local prune = true
14481471
for nodeId, itemId in pairs(self.jewels) do
14491472
if self.allocNodes[nodeId] then
1450-
if itemId ~= 0 and (
1451-
self.build.itemsTab.items[itemId] and (
1452-
self.build.itemsTab.items[itemId].jewelData
1453-
and self.build.itemsTab.items[itemId].jewelData.intuitiveLeapLike
1454-
and self.build.itemsTab.items[itemId].jewelRadiusIndex
1455-
and self.nodes[nodeId].nodesInRadius
1456-
and self.nodes[nodeId].nodesInRadius[self.build.itemsTab.items[itemId].jewelRadiusIndex][depNode.id]
1457-
) or (
1458-
self.build.itemsTab.items[itemId].jewelData
1459-
and self.build.itemsTab.items[itemId].jewelData.impossibleEscapeKeystones
1460-
and self:NodeInKeystoneRadius(self.build.itemsTab.items[itemId].jewelData.impossibleEscapeKeystones, depNode.id, self.build.itemsTab.items[itemId].jewelRadiusIndex)
1461-
)
1462-
) then
1473+
local item = self.build.itemsTab.items[itemId]
1474+
local socketNode = self.nodes[nodeId]
1475+
local leapHit = false
1476+
if itemId ~= 0 and item and item.jewelData and item.jewelData.intuitiveLeapLike
1477+
and item.jewelRadiusIndex and socketNode.nodesInRadius then
1478+
for _, idx in ipairs(self:GetEffectiveRadiusIndices(item)) do
1479+
if socketNode.nodesInRadius[idx] and socketNode.nodesInRadius[idx][depNode.id] then
1480+
leapHit = true
1481+
break
1482+
end
1483+
end
1484+
end
1485+
local keyHit = itemId ~= 0 and item and item.jewelData
1486+
and item.jewelData.impossibleEscapeKeystones
1487+
and self:NodeInKeystoneRadius(item.jewelData.impossibleEscapeKeystones, depNode.id, item.jewelRadiusIndex)
1488+
if leapHit or keyHit then
14631489
-- Hold off on the pruning; this node could be supported by Intuitive Leap-like jewel
14641490
prune = false
14651491
if not intuitiveLeaps[nodeId] then

src/Classes/PassiveTreeView.lua

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ local PassiveTreeViewClass = newClass("PassiveTreeView", function(self)
6565
self.searchStrCached = ""
6666
self.searchStrResults = {}
6767
self.showStatDifferences = true
68+
self.toHRingMode = nil -- nil | 1..5 (cycle: Small..Massive) | "all"
6869
self.hoverNode = nil
6970
end)
7071

@@ -116,6 +117,22 @@ function PassiveTreeViewClass:Draw(build, viewPort, inputEvents)
116117
end
117118
elseif event.key == "p" then
118119
self.showHeatMap = not self.showHeatMap
120+
elseif event.key == "t" then
121+
-- Cycle through Thread of Hope ring sizes: nil -> 1 (Small) -> ... -> 5 (Massive) -> nil
122+
if self.toHRingMode == nil or self.toHRingMode == "all" then
123+
self.toHRingMode = 1
124+
elseif self.toHRingMode == 5 then
125+
self.toHRingMode = nil
126+
else
127+
self.toHRingMode = self.toHRingMode + 1
128+
end
129+
build.spec:BuildAllDependsAndPaths()
130+
build.buildFlag = true
131+
elseif event.key == "r" then
132+
-- Toggle "all rings" view for Thread of Hope
133+
self.toHRingMode = self.toHRingMode == "all" and nil or "all"
134+
build.spec:BuildAllDependsAndPaths()
135+
build.buildFlag = true
119136
elseif event.key == "d" and IsKeyDown("CTRL") then
120137
self.showStatDifferences = not self.showStatDifferences
121138
elseif event.key == "c" and IsKeyDown("CTRL") and self.hoverNode and self.hoverNode.type ~= "Socket" then
@@ -959,7 +976,29 @@ function PassiveTreeViewClass:Draw(build, viewPort, inputEvents)
959976
end
960977
end
961978
elseif node.alloc then
962-
if jewel and jewel.jewelRadiusIndex then
979+
if self.toHRingMode and jewel and jewel.jewelRadiusLabel == "Variable" then
980+
-- Override mode active for Thread of Hope: draw the cycled ring (1-5) or all rings
981+
local ringSizeLabels = { "Small", "Medium", "Large", "Very Large", "Massive" }
982+
local fontHeight = m_max(14, 24 * scale)
983+
local variableRingIndex = 0
984+
for _, radData in ipairs(build.data.jewelRadius) do
985+
local outerSize = radData.outer * scale
986+
local innerSize = radData.inner * scale
987+
if innerSize ~= 0 then
988+
variableRingIndex = variableRingIndex + 1
989+
if self.toHRingMode == "all" or self.toHRingMode == variableRingIndex then
990+
SetDrawColor(radData.col)
991+
DrawImage(self.ring, scrX - outerSize, scrY - outerSize, outerSize * 2, outerSize * 2)
992+
DrawImage(self.ring, scrX - innerSize, scrY - innerSize, innerSize * 2, innerSize * 2)
993+
local label = ringSizeLabels[variableRingIndex]
994+
if label then
995+
local labelY = scrY - (outerSize + innerSize) / 2 - fontHeight / 2
996+
DrawString(scrX, labelY, "CENTER", fontHeight, "FONTIN", radData.col .. label)
997+
end
998+
end
999+
end
1000+
end
1001+
elseif jewel and jewel.jewelRadiusIndex then
9631002
-- Draw only the selected jewel radius
9641003
local radData = build.data.jewelRadius[jewel.jewelRadiusIndex]
9651004
local outerSize = radData.outer * scale
@@ -1006,6 +1045,25 @@ function PassiveTreeViewClass:Draw(build, viewPort, inputEvents)
10061045
end
10071046
end
10081047
end
1048+
1049+
-- Indicator: planning mode is active so the user doesn't forget the jewel calcs are inflated.
1050+
if self.toHRingMode then
1051+
SetDrawLayer(nil, 100)
1052+
local ringSizeLabels = { "Small", "Medium", "Large", "Very Large", "Massive" }
1053+
local ringColors = { "^xD35400", "^x66FFCC", "^x2222CC", "^xC100FF", "^x0B9300" }
1054+
local headerText
1055+
if self.toHRingMode == "all" then
1056+
headerText = "^xFFCC00[Thread of Hope: All Rings Mode] ^7T cycles, R toggles all"
1057+
DrawString(viewPort.x + 12, viewPort.y + 12, "LEFT", 18, "FONTIN", headerText)
1058+
local legend = "^xD35400Small ^x66FFCCMedium ^x2222CCLarge ^xC100FFVery Large ^x0B9300Massive"
1059+
DrawString(viewPort.x + 12, viewPort.y + 34, "LEFT", 16, "FONTIN", legend)
1060+
else
1061+
local size = ringSizeLabels[self.toHRingMode] or "?"
1062+
local color = ringColors[self.toHRingMode] or "^7"
1063+
headerText = "^xFFCC00[Thread of Hope: " .. color .. size .. " Ring^xFFCC00 (" .. self.toHRingMode .. "/5)] ^7T cycles, R = all"
1064+
DrawString(viewPort.x + 12, viewPort.y + 12, "LEFT", 18, "FONTIN", headerText)
1065+
end
1066+
end
10091067
end
10101068
function PassiveTreeViewClass:DrawImageRotated(handle, x, y, width, height, angle, ...)
10111069
if main.showAnimations == false then

0 commit comments

Comments
 (0)