Skip to content

Commit a61e723

Browse files
Thread of Hope all-rings planning toggle
Adds a tree-view toggle (T) that draws all five Variable jewel rings around each allocated Thread of Hope socket and tints nodes by the ring they sit in, so a planner can see where every possible roll would land before settling on a radius. - Spec dependency/path math respects the toggle for Variable jewels - Massive radius recoloured from dark green to gold so all five rings stay visually distinct alongside the teal Medium ring - First-time top-left hint surfaces the T hotkey and self-dismisses on first press (persisted via main.toHHintDismissed in Misc) - Jewel tooltip gains a display-only Tip line for Variable radius; not added to rawLines, so import/export are unaffected
1 parent 58a5e24 commit a61e723

5 files changed

Lines changed: 128 additions & 31 deletions

File tree

src/Classes/ItemsTab.lua

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3564,6 +3564,11 @@ function ItemsTabClass:AddItemTooltip(tooltip, item, slot, dbMode)
35643564
end
35653565
if item.jewelRadiusLabel then
35663566
tooltip:AddLine(fontSizeBig, "^x7F7F7FRadius: ^7"..item.jewelRadiusLabel, "FONTIN SC")
3567+
if item.jewelRadiusLabel == "Variable" then
3568+
-- Display-only hint, not added to rawLines, so item import/export is unaffected.
3569+
tooltip:AddSeparator(4)
3570+
tooltip:AddLine(fontSizeBig, colorCodes.MAGIC.."Tip: Press ^x7F7F7FT"..colorCodes.MAGIC.." in the tree view to toggle all five ring sizes.", "FONTIN")
3571+
end
35673572
end
35683573
if item.jewelRadiusData and slot and item.jewelRadiusData[slot.nodeId] then
35693574
local radiusData = item.jewelRadiusData[slot.nodeId]

src/Classes/PassiveSpec.lua

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

1034+
-- Returns jewelRadius indices to consider for this item. When the tree-view planning
1035+
-- toggle is on for a Variable-radius jewel, all 5 Variable rings are treated as candidate
1036+
-- radii so dependency/path calculations cover every possible socketed roll.
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+
return { 6, 7, 8, 9, 10 }
1042+
end
1043+
return { item.jewelRadiusIndex }
1044+
end
1045+
10341046
function PassiveSpecClass:NodesInIntuitiveLeapLikeRadius(node)
10351047
local result = { }
10361048
if self.jewels[node.id] and self.jewels[node.id] > 0 then
10371049
local item = self.build.itemsTab.items[self.jewels[node.id]]
10381050
local radiusIndex = item.jewelRadiusIndex
10391051
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])
1052+
local seen = { }
1053+
for _, idx in ipairs(self:GetEffectiveRadiusIndices(item)) do
1054+
local inRadius = self.nodes[node.id].nodesInRadius and self.nodes[node.id].nodesInRadius[idx]
1055+
for affectedNodeId, affectedNode in pairs(inRadius or {}) do
1056+
if self.nodes[affectedNodeId].alloc and not seen[affectedNodeId] then
1057+
seen[affectedNodeId] = true
1058+
t_insert(result, self.nodes[affectedNodeId])
1059+
end
10441060
end
10451061
end
10461062
end
@@ -1082,18 +1098,21 @@ function PassiveSpecClass:BuildAllDependsAndPaths()
10821098
local item = self.build.itemsTab.items[itemId]
10831099
if item and item.jewelRadiusIndex and self.allocNodes[nodeId] and item.jewelData and not item.jewelData.limitDisabled then
10841100
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
1101+
for _, idx in ipairs(self:GetEffectiveRadiusIndices(item)) do
1102+
if self.nodes[nodeId].nodesInRadius and self.nodes[nodeId].nodesInRadius[idx][node.id] then
1103+
if itemId ~= 0 then
1104+
if item.jewelData.intuitiveLeapLike and not (item.jewelData.intuitiveLeapKeystoneOnly and node.type ~= "Keystone") then
1105+
-- This node depends on Intuitive Leap-like behaviour
1106+
-- This flag:
1107+
-- 1. Prevents generation of paths from this node unless it's also connected to the start
1108+
-- 2. Prevents allocation of path nodes when this node is being allocated
1109+
t_insert(node.intuitiveLeapLikesAffecting, self.nodes[nodeId])
1110+
end
1111+
if item.jewelData.conqueredBy then
1112+
node.conqueredBy = item.jewelData.conqueredBy
1113+
end
10961114
end
1115+
break
10971116
end
10981117
end
10991118

@@ -1447,19 +1466,22 @@ function PassiveSpecClass:BuildAllDependsAndPaths()
14471466
local prune = true
14481467
for nodeId, itemId in pairs(self.jewels) do
14491468
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
1469+
local item = self.build.itemsTab.items[itemId]
1470+
local socketNode = self.nodes[nodeId]
1471+
local leapHit = false
1472+
if itemId ~= 0 and item and item.jewelData and item.jewelData.intuitiveLeapLike
1473+
and item.jewelRadiusIndex and socketNode.nodesInRadius then
1474+
for _, idx in ipairs(self:GetEffectiveRadiusIndices(item)) do
1475+
if socketNode.nodesInRadius[idx] and socketNode.nodesInRadius[idx][depNode.id] then
1476+
leapHit = true
1477+
break
1478+
end
1479+
end
1480+
end
1481+
local keyHit = itemId ~= 0 and item and item.jewelData
1482+
and item.jewelData.impossibleEscapeKeystones
1483+
and self:NodeInKeystoneRadius(item.jewelData.impossibleEscapeKeystones, depNode.id, item.jewelRadiusIndex)
1484+
if leapHit or keyHit then
14631485
-- Hold off on the pruning; this node could be supported by Intuitive Leap-like jewel
14641486
prune = false
14651487
if not intuitiveLeaps[nodeId] then

src/Classes/PassiveTreeView.lua

Lines changed: 67 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 | true (planning toggle: show all Variable rings)
6869
self.hoverNode = nil
6970
end)
7071

@@ -116,6 +117,15 @@ 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+
-- Toggle Thread of Hope all-rings planning view
122+
self.toHRingMode = not self.toHRingMode or nil
123+
if not main.toHHintDismissed then
124+
main.toHHintDismissed = true
125+
main:SaveSettings()
126+
end
127+
build.spec:BuildAllDependsAndPaths()
128+
build.buildFlag = true
119129
elseif event.key == "d" and IsKeyDown("CTRL") then
120130
self.showStatDifferences = not self.showStatDifferences
121131
elseif event.key == "c" and IsKeyDown("CTRL") and self.hoverNode and self.hoverNode.type ~= "Socket" then
@@ -635,6 +645,35 @@ function PassiveTreeViewClass:Draw(build, viewPort, inputEvents)
635645
end
636646
end
637647

648+
-- One pass over allocated jewel sockets: detects whether any Variable-radius jewel is
649+
-- socketed (used to gate the first-time hint), and when the planning toggle is on, builds
650+
-- a nodeId -> ring colour tint map. First-seen wins when sockets overlap; rings within one
651+
-- socket are non-overlapping so per-socket order is deterministic.
652+
local toHTintMap = self.toHRingMode and { } or nil
653+
local hasVariableJewel = false
654+
for socketNodeId, itemId in pairs(spec.jewels) do
655+
if itemId ~= 0 and spec.allocNodes[socketNodeId] then
656+
local item = build.itemsTab.items[itemId]
657+
local socketNode = spec.nodes[socketNodeId]
658+
if item and item.jewelRadiusLabel == "Variable" then
659+
hasVariableJewel = true
660+
if toHTintMap and socketNode and socketNode.nodesInRadius then
661+
for ringIdx = 6, 10 do
662+
local nodesInRing = socketNode.nodesInRadius[ringIdx]
663+
local radData = build.data.jewelRadius[ringIdx]
664+
if nodesInRing and radData then
665+
for nId in pairs(nodesInRing) do
666+
if not toHTintMap[nId] then
667+
toHTintMap[nId] = radData.col
668+
end
669+
end
670+
end
671+
end
672+
end
673+
end
674+
end
675+
end
676+
638677
-- Draw the nodes
639678
for nodeId, node in pairs(spec.nodes) do
640679
-- Determine the base and overlay images for this node based on type and state
@@ -850,11 +889,13 @@ function PassiveTreeViewClass:Draw(build, viewPort, inputEvents)
850889
if overlay then
851890
-- Draw overlay
852891
if node.type ~= "ClassStart" and node.type ~= "AscendClassStart" then
892+
local hoverTinted = false
853893
if hoverNode and hoverNode ~= node then
854894
-- Mouse is hovering over a different node
855895
if hoverDep and hoverDep[node] then
856896
-- This node depends on the hover node, turn it red
857897
SetDrawColor(1, 0, 0)
898+
hoverTinted = true
858899
elseif hoverNode.type == "Socket" and hoverNode.nodesInRadius then
859900
-- Hover node is a socket, check if this node falls within its radius and color it accordingly
860901
local socket, jewel = build.itemsTab:GetSocketAndJewelForNodeID(hoverNode.id)
@@ -866,6 +907,7 @@ function PassiveTreeViewClass:Draw(build, viewPort, inputEvents)
866907
-- Draw Thread of Hope's annuli
867908
if data.inner ~= 0 then
868909
SetDrawColor(data.col)
910+
hoverTinted = true
869911
break
870912
end
871913
end
@@ -877,13 +919,18 @@ function PassiveTreeViewClass:Draw(build, viewPort, inputEvents)
877919
-- Draw normal jewel radii
878920
if data.inner == 0 then
879921
SetDrawColor(data.col)
922+
hoverTinted = true
880923
break
881924
end
882925
end
883926
end
884927
end
885928
end
886929
end
930+
if not hoverTinted and toHTintMap and toHTintMap[nodeId] then
931+
-- Planning toggle is on: tint nodes by the Thread of Hope ring they sit in
932+
SetDrawColor(toHTintMap[nodeId])
933+
end
887934
end
888935
self:DrawAsset(tree.assets[overlay], scrX, scrY, scale)
889936
SetDrawColor(1, 1, 1)
@@ -959,7 +1006,18 @@ function PassiveTreeViewClass:Draw(build, viewPort, inputEvents)
9591006
end
9601007
end
9611008
elseif node.alloc then
962-
if jewel and jewel.jewelRadiusIndex then
1009+
if self.toHRingMode and jewel and jewel.jewelRadiusLabel == "Variable" then
1010+
-- Planning toggle: draw all five Variable-radius annuli around the socket
1011+
for _, radData in ipairs(build.data.jewelRadius) do
1012+
local outerSize = radData.outer * scale
1013+
local innerSize = radData.inner * scale
1014+
if innerSize ~= 0 then
1015+
SetDrawColor(radData.col)
1016+
DrawImage(self.ring, scrX - outerSize, scrY - outerSize, outerSize * 2, outerSize * 2)
1017+
DrawImage(self.ring, scrX - innerSize, scrY - innerSize, innerSize * 2, innerSize * 2)
1018+
end
1019+
end
1020+
elseif jewel and jewel.jewelRadiusIndex then
9631021
-- Draw only the selected jewel radius
9641022
local radData = build.data.jewelRadius[jewel.jewelRadiusIndex]
9651023
local outerSize = radData.outer * scale
@@ -1006,6 +1064,14 @@ function PassiveTreeViewClass:Draw(build, viewPort, inputEvents)
10061064
end
10071065
end
10081066
end
1067+
1068+
-- First-time hint: shown only while the toggle is off, the user has never pressed T,
1069+
-- and the build has at least one allocated Variable-radius jewel.
1070+
if hasVariableJewel and not self.toHRingMode and not main.toHHintDismissed then
1071+
SetDrawLayer(nil, 100)
1072+
DrawString(viewPort.x + 12, viewPort.y + 12, "LEFT", 18, "FONTIN",
1073+
"^xFFCC00Tip: ^7Press ^xFFCC00T^7 to view all Thread of Hope ring sizes.")
1074+
end
10091075
end
10101076
function PassiveTreeViewClass:DrawImageRotated(handle, x, y, width, height, angle, ...)
10111077
if main.showAnimations == false then

src/Modules/Data.lua

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -540,13 +540,13 @@ data.jewelRadii = {
540540
{ inner = 0, outer = 1440, col = "^x66FFCC", label = "Medium" },
541541
{ inner = 0, outer = 1800, col = "^x2222CC", label = "Large" },
542542
{ inner = 0, outer = 2400, col = "^xC100FF", label = "Very Large" },
543-
{ inner = 0, outer = 2880, col = "^x0B9300", label = "Massive" },
543+
{ inner = 0, outer = 2880, col = "^xFFD700", label = "Massive" },
544544

545545
{ inner = 960, outer = 1320, col = "^xD35400", label = "Variable" },
546546
{ inner = 1320, outer = 1680, col = "^x66FFCC", label = "Variable" },
547547
{ inner = 1680, outer = 2040, col = "^x2222CC", label = "Variable" },
548548
{ inner = 2040, outer = 2400, col = "^xC100FF", label = "Variable" },
549-
{ inner = 2400, outer = 2880, col = "^x0B9300", label = "Variable" },
549+
{ inner = 2400, outer = 2880, col = "^xFFD700", label = "Variable" },
550550
}
551551
}
552552

src/Modules/Main.lua

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -580,6 +580,9 @@ function main:LoadSettings(ignoreBuild)
580580
if node.attrib.edgeSearchHighlight then
581581
self.edgeSearchHighlight = node.attrib.edgeSearchHighlight == "true"
582582
end
583+
if node.attrib.toHHintDismissed then
584+
self.toHHintDismissed = node.attrib.toHHintDismissed == "true"
585+
end
583586
if node.attrib.defaultGemQuality then
584587
self.defaultGemQuality = m_min(tonumber(node.attrib.defaultGemQuality) or 0, 23)
585588
end
@@ -745,6 +748,7 @@ function main:SaveSettings()
745748
showTitlebarName = tostring(self.showTitlebarName),
746749
betaTest = tostring(self.betaTest),
747750
edgeSearchHighlight = tostring(self.edgeSearchHighlight),
751+
toHHintDismissed = tostring(self.toHHintDismissed or false),
748752
defaultGemQuality = tostring(self.defaultGemQuality or 0),
749753
defaultCharLevel = tostring(self.defaultCharLevel or 1),
750754
defaultItemAffixQuality = tostring(self.defaultItemAffixQuality or 0.5),

0 commit comments

Comments
 (0)