From 124b44525f1d440e3399142c49dbe38125079641 Mon Sep 17 00:00:00 2001 From: vaisest <4550061+vaisest@users.noreply.github.com> Date: Sun, 23 Aug 2026 17:32:32 +0300 Subject: [PATCH 1/7] Fix various timeless calculator issues: - Fix mana cost efficiency missing from militant faith - Convert the janky node protection selectors to checkboxes - Add stat differences to resulting jewel list --- src/Classes/CheckBoxControl.lua | 5 +- src/Classes/ItemsTab.lua | 50 +++--- src/Classes/PassiveTreeView.lua | 8 +- src/Classes/TimelessJewelListControl.lua | 150 ++++++++++-------- src/Classes/TreeTab.lua | 91 +++++------ src/Data/TimelessJewelData/LegionTradeIds.lua | 3 +- 6 files changed, 165 insertions(+), 142 deletions(-) diff --git a/src/Classes/CheckBoxControl.lua b/src/Classes/CheckBoxControl.lua index 1c285db5fd8..93e2eabdf1d 100644 --- a/src/Classes/CheckBoxControl.lua +++ b/src/Classes/CheckBoxControl.lua @@ -12,6 +12,7 @@ function CheckBoxClass:CheckBoxControl(anchor, rect, label, changeFunc, tooltipT self:TooltipHost(tooltipText) self.label = label self.labelWidth = DrawStringWidth(self.width - 4, "VAR", label or "") + 5 + self.labelRight = false self.changeFunc = changeFunc self.state = initialState return self @@ -76,7 +77,9 @@ function CheckBoxClass:Draw(viewPort, noTooltip) SetDrawColor(0.33, 0.33, 0.33) end local label = self:GetProperty("label") - if label then + if label and self.labelRight then + DrawString(x + self.width + 5, y + 2, nil, size - 4, "VAR", label) + elseif label then DrawString(x - 5, y + 2, "RIGHT_X", size - 4, "VAR", label) end if mOver and not noTooltip then diff --git a/src/Classes/ItemsTab.lua b/src/Classes/ItemsTab.lua index b9dd8b79cca..779d7d6ba78 100644 --- a/src/Classes/ItemsTab.lua +++ b/src/Classes/ItemsTab.lua @@ -4925,11 +4925,28 @@ function ItemsTabClass:AddItemTooltip(tooltip, item, slot, dbMode, maxWidth) tooltip:AddSeparator(14) tooltip:AddLine(14, colorCodes.TIP.."Tip: Press Ctrl+D"..itemTabHint.." to enable the display of stat differences.") return + elseif not (base.flask or base.tincture) then + tooltip:AddLine(14, colorCodes.TIP .. "Tip: Press Ctrl+D" .. itemTabHint .. " to disable the display of stat differences.") end + self:AddItemStatDifferences(tooltip, item, base, slot) + + if launch.devModeAlt then + -- Modifier debugging info + tooltip:AddSeparator(10) + for _, mod in ipairs(modList) do + tooltip:AddLine(14, "^7" .. modLib.formatMod(mod)) + end + end +end + +---@param tooltip Tooltip +---@param item Item +---@param base any +function ItemsTabClass:AddItemStatDifferences(tooltip, item, base, slot) local calcFunc, calcBase = self.build.calcsTab:GetMiscCalculator() if base.flask then -- Special handling for flasks - local stats = { } + local stats = {} local flaskData = item.flaskData local modDB = self.build.calcsTab.mainEnv.modDB local output = self.build.calcsTab.mainOutput @@ -4964,28 +4981,28 @@ function ItemsTabClass:AddItemTooltip(tooltip, item, slot, dbMode, maxWidth) -- LocalLifeFlaskAdditionalLifeRecovery flask mods if flaskData.lifeAdditional > 0 and not self.build.configTab.input.conditionFullLife then - local totalAdditionalAmount = (flaskData.lifeAdditional/100) * flaskData.lifeTotal * output.LifeRecoveryRateMod - local additionalGrad = (lifeDur/10) * totalAdditionalAmount + local totalAdditionalAmount = (flaskData.lifeAdditional / 100) * flaskData.lifeTotal * output.LifeRecoveryRateMod + local additionalGrad = (lifeDur / 10) * totalAdditionalAmount local leftoverDur = 10 - lifeDur local leftoverAmount = totalAdditionalAmount - additionalGrad if inst > 0 then if grad > 0 then t_insert(stats, s_format("^8Life recovered: ^7%d ^8(^7%d^8 instantly, plus ^7%d ^8over^7 %.2fs^8, and an additional ^7%d ^8over subsequent ^7%.2fs^8)", - inst + grad + totalAdditionalAmount, inst, grad + additionalGrad, lifeDur, leftoverAmount, leftoverDur)) + inst + grad + totalAdditionalAmount, inst, grad + additionalGrad, lifeDur, leftoverAmount, leftoverDur)) else lifeDur = 0 t_insert(stats, s_format("^8Life recovered: ^7%d ^8(^7%d^8 instantly, and an additional ^7%d ^8over ^7%.2fs^8)", - inst + totalAdditionalAmount, inst, totalAdditionalAmount, 10)) + inst + totalAdditionalAmount, inst, totalAdditionalAmount, 10)) end else t_insert(stats, s_format("^8Life recovered: ^7%d ^8(^7%d ^8over ^7%.2fs^8, and an additional ^7%d ^8over subsequent ^7%.2fs^8)", - grad + totalAdditionalAmount, grad + additionalGrad, lifeDur, leftoverAmount, leftoverDur)) + grad + totalAdditionalAmount, grad + additionalGrad, lifeDur, leftoverAmount, leftoverDur)) end else if inst > 0 and grad > 0 then t_insert(stats, s_format("^8Life recovered: ^7%d ^8(^7%d^8 instantly, plus ^7%d ^8over^7 %.2fs^8)", inst + grad, inst, grad, lifeDur)) - -- modifiers to recovery amount or duration + -- modifiers to recovery amount or duration elseif inst + grad ~= flaskData.lifeTotal or (inst == 0 and lifeDur ~= flaskData.duration) then if inst > 0 then lifeDur = 0 @@ -5137,7 +5154,7 @@ function ItemsTabClass:AddItemTooltip(tooltip, item, slot, dbMode, maxWidth) self.build:AddStatComparesToTooltip(tooltip, calcBase, output, header) elseif base.tincture then -- Special handling for tinctures - local stats = { } + local stats = {} local tinctureData = item.tinctureData local modDB = self.build.calcsTab.mainEnv.modDB local output = self.build.calcsTab.mainOutput @@ -5174,15 +5191,13 @@ function ItemsTabClass:AddItemTooltip(tooltip, item, slot, dbMode, maxWidth) else self:UpdateSockets() -- Build sorted list of slots to compare with - local compareSlots = { } + local compareSlots = {} for slotName, slot in pairs(self.slots) do if self:IsItemValidForSlot(item, slotName) and not slot.inactive and (not slot.weaponSet or slot.weaponSet == (self.activeItemSet.useSecondWeaponSet and 2 or 1)) and slot.shown() then t_insert(compareSlots, slot) end end - tooltip:AddLine(14, colorCodes.TIP .. "Tip: Press Ctrl+D"..itemTabHint.." to disable the display of stat differences.") - local function getReplacedItemAndOutput(compareSlot) local selItem = self.items[compareSlot.selItemId] local override = { repSlotName = compareSlot.slotName, repItem = item ~= selItem and item or nil } @@ -5198,9 +5213,9 @@ function ItemsTabClass:AddItemTooltip(tooltip, item, slot, dbMode, maxWidth) end local header if item == selItem then - header = "^7Removing this item from "..compareSlot.label.." will give you:" + header = "^7Removing this item from " .. compareSlot.label .. " will give you:" else - header = string.format("^7Equipping this item in %s will give you:%s", compareSlot.label or compareSlot.slotName, selItem and "\n(replacing "..colorCodes[selItem.rarity]..selItem.name.."^7)" or "") + header = string.format("^7Equipping this item in %s will give you:%s", compareSlot.label or compareSlot.slotName, selItem and "\n(replacing " .. colorCodes[selItem.rarity] .. selItem.name .. "^7)" or "") end self.build:AddStatComparesToTooltip(tooltip, calcBase, output, header) end @@ -5281,15 +5296,6 @@ function ItemsTabClass:AddItemTooltip(tooltip, item, slot, dbMode, maxWidth) for _, slotEntry in ipairs(slots) do addCompareForSlot(slotEntry.compareSlot, slotEntry.selItem, slotEntry.output) end - - end - - if launch.devModeAlt then - -- Modifier debugging info - tooltip:AddSeparator(10) - for _, mod in ipairs(modList) do - tooltip:AddLine(14, "^7"..modLib.formatMod(mod)) - end end end diff --git a/src/Classes/PassiveTreeView.lua b/src/Classes/PassiveTreeView.lua index ea7bef3f111..d514456703e 100644 --- a/src/Classes/PassiveTreeView.lua +++ b/src/Classes/PassiveTreeView.lua @@ -1460,7 +1460,8 @@ function PassiveTreeViewClass:AddNodeName(tooltip, node, build) end end -function PassiveTreeViewClass:AddNodeTooltip(tooltip, node, build) +---@param returnEarly boolean? Whether the function should stop after writing the mod info, before any allocation-specific info +function PassiveTreeViewClass:AddNodeTooltip(tooltip, node, build, returnEarly) local fontSizeBig = main.showFlavourText and 18 or 16 self.skillTooltip:Clear() tooltip.center = true @@ -1632,6 +1633,7 @@ function PassiveTreeViewClass:AddNodeTooltip(tooltip, node, build) end end + -- Reminder text if node.reminderText then tooltip:AddSeparator(14) @@ -1648,6 +1650,10 @@ function PassiveTreeViewClass:AddNodeTooltip(tooltip, node, build) end end + if returnEarly then + return + end + -- Tattoo Editing if node and (node.isTattoo or (node.type == "Normal" and (node.dn == "Strength" or node.dn == "Dexterity" or node.dn == "Intelligence")) diff --git a/src/Classes/TimelessJewelListControl.lua b/src/Classes/TimelessJewelListControl.lua index fb985769b5d..2d690ca624a 100644 --- a/src/Classes/TimelessJewelListControl.lua +++ b/src/Classes/TimelessJewelListControl.lua @@ -12,6 +12,7 @@ local t_concat = table.concat ---@class TimelessJewelListControl: ListControl local TimelessJewelListControlClass = newClass("TimelessJewelListControl", "ListControl") +---@param build Build function TimelessJewelListControlClass:TimelessJewelListControl(anchor, rect, build) self.build = build self.sharedList = self.build.timelessData.sharedResults or { } @@ -68,45 +69,14 @@ function TimelessJewelListControlClass:GetRowValue(column, index, data) end end -function TimelessJewelListControlClass:AddValueTooltip(tooltip, index, data) - tooltip:Clear() - if not self.noTooltip then - if self.list[index].label:match("B2B2B2") == nil then - tooltip:AddLine(16, "^7Double click to add this jewel to your build.") - else - tooltip:AddLine(16, "^7" .. self.sharedList.type.label .. " " .. data.seed .. " was successfully added to your build.") - end - local treeData = self.build.spec.tree - local sortedNodeLists = { } - for legionId, desiredNode in pairs(self.sharedList.desiredNodes or { }) do - if self.list[index][legionId] then - if self.list[index][legionId].targetNodeNames and #self.list[index][legionId].targetNodeNames > 0 then - sortedNodeLists[desiredNode.desiredIdx] = "^7 " .. desiredNode.displayName .. ":\n^8 " .. t_concat(self.list[index][legionId].targetNodeNames, "\n ") - else - sortedNodeLists[desiredNode.desiredIdx] = "^7 " .. desiredNode.displayName .. ":\n^8 None" - end - end - end - if next(sortedNodeLists) then - tooltip:AddLine(16, "^7Node List:") - for _, sortedNodeList in pairs(sortedNodeLists) do - tooltip:AddLine(16, sortedNodeList) - end - end - if data.total > 0 then - tooltip:AddLine(16, "^7Combined Node Weight: " .. data.total) - end - end -end - -function TimelessJewelListControlClass:OnSelClick(index, data, doubleClick) - if doubleClick and self.list[index].label:match("B2B2B2") == nil then - local socketInfo = data.socketLabel or (self.sharedList.socket and self.sharedList.socket.keystone) or "Unknown" - local label = "[" .. data.seed .. "; " .. data.total.. "; " .. socketInfo .. "]\n" - local variant = self.sharedList.conqueror.id == 1 and 1 or (self.sharedList.conqueror.id - 1) .. "\n" - local itemData - if self.sharedList.type.id == 1 then - itemData = [[ +---@return Item item +function TimelessJewelListControlClass:GetJewelItem(data) + local socketInfo = data.socketLabel or (self.sharedList.socket and self.sharedList.socket.keystone) or "Unknown" + local label = "[" .. data.seed .. "; " .. data.total .. "; " .. socketInfo .. "]\n" + local variant = self.sharedList.conqueror.id == 1 and 1 or (self.sharedList.conqueror.id - 1) .. "\n" + local itemData + if self.sharedList.type.id == 1 then + itemData = [[ Glorious Vanity ]] .. label .. [[ Timeless Jewel League: Legion @@ -114,7 +84,7 @@ Limited to: 1 Historic Variant: Doryani (Corrupted Soul) Variant: Xibaqua (Divine Flesh) Variant: Ahuana (Immortal Ambition) -Selected Variant: ]] .. variant .. "\n" ..[[ +Selected Variant: ]] .. variant .. "\n" .. [[ Radius: Large Implicits: 0 {variant:1}Bathed in the blood of ]] .. data.seed .. [[ sacrificed in the name of Doryani @@ -123,8 +93,8 @@ Implicits: 0 Passives in radius are Conquered by the Vaal Historic ]] - elseif self.sharedList.type.id == 2 then - itemData = [[ + elseif self.sharedList.type.id == 2 then + itemData = [[ Lethal Pride ]] .. label .. [[ Timeless Jewel League: Legion @@ -141,8 +111,8 @@ Implicits: 0 Passives in radius are Conquered by the Karui Historic ]] - elseif self.sharedList.type.id == 3 then - itemData = [[ + elseif self.sharedList.type.id == 3 then + itemData = [[ Brutal Restraint ]] .. label .. [[ Timeless Jewel League: Legion @@ -159,13 +129,13 @@ Implicits: 0 Passives in radius are Conquered by the Maraketh Historic ]] - elseif self.sharedList.type.id == 4 then - local altVariant = self.sharedList.devotionVariant1.id ~= 1 and self.sharedList.devotionVariant1.id or m_random(2, 16) - local altVariant2 = self.sharedList.devotionVariant2.id ~= 1 and self.sharedList.devotionVariant2.id or m_random(2, 16) - if altVariant == altVariant2 then - altVariant = altVariant % 15 + 2 - end - itemData = [[ + elseif self.sharedList.type.id == 4 then + local altVariant = self.sharedList.devotionVariant1.id ~= 1 and self.sharedList.devotionVariant1.id or m_random(2, 16) + local altVariant2 = self.sharedList.devotionVariant2.id ~= 1 and self.sharedList.devotionVariant2.id or m_random(2, 16) + if altVariant == altVariant2 then + altVariant = altVariant % 15 + 2 + end + itemData = [[ Militant Faith ]] .. label .. [[ Timeless Jewel League: Legion @@ -187,9 +157,10 @@ Variant: Duration of Curses Variant: Minion Attack and Cast Speed Variant: Minions Accuracy Rating Variant: Mana Regen -Variant: Skill Cost +Variant: Mana Cost (legacy) Variant: Non-Curse Aura Effect Variant: Defences from Shield +Variant: Mana Cost Efficiency Selected Variant: ]] .. variant .. "\n" .. [[ Selected Alt Variant: ]] .. altVariant + 2 .. "\n" .. [[ Selected Alt Variant Two: ]] .. altVariant2 + 2 .. "\n" .. [[ @@ -213,11 +184,12 @@ Implicits: 0 {variant:16}1% reduced Mana Cost of Skills per 10 Devotion {variant:17}1% increased effect of Non-Curse Auras per 10 Devotion {variant:18}3% increased Defences from Equipped Shield per 10 Devotion +{variant:19}3% increased Mana Cost Efficiency per 10 Devotion Passives in radius are Conquered by the Templars Historic ]] - elseif self.sharedList.type.id == 5 then - itemData = [[ + elseif self.sharedList.type.id == 5 then + itemData = [[ Elegant Hubris ]] .. label .. [[ Timeless Jewel League: Legion @@ -234,8 +206,8 @@ Implicits: 0 Passives in radius are Conquered by the Eternal Empire Historic ]] - elseif self.sharedList.type.id == 6 then - itemData = [[ + elseif self.sharedList.type.id == 6 then + itemData = [[ Heroic Tragedy ]] .. label .. [[ Timeless Jewel League: Legion @@ -252,8 +224,8 @@ Implicits: 0 Passives in radius are Conquered by the Kalguur Historic ]] - elseif self.sharedList.type.id == 7 then - itemData = [[ + elseif self.sharedList.type.id == 7 then + itemData = [[ Festering Vengeance ]] .. label .. [[ Murderous Eye Jewel League: Allflame @@ -263,8 +235,8 @@ Subjugating ]] .. data.seed .. [[ souls in the thrall of Tecrod Passives affected are Conquered by the Abyssal Historic ]] - elseif self.sharedList.type.id == 8 then - itemData = [[ + elseif self.sharedList.type.id == 8 then + itemData = [[ Extinguishing Grasp ]] .. label .. [[ Searching Eye Jewel League: Allflame @@ -274,8 +246,8 @@ Subjugating ]] .. data.seed .. [[ souls in the thrall of Ulaman Passives affected are Conquered by the Abyssal Historic ]] - elseif self.sharedList.type.id == 9 then - itemData = [[ + elseif self.sharedList.type.id == 9 then + itemData = [[ Baleful Dominion ]] .. label .. [[ Hypnotic Eye Jewel League: Allflame @@ -285,8 +257,8 @@ Subjugating ]] .. data.seed .. [[ souls in the thrall of Kurgal Passives affected are Conquered by the Abyssal Historic ]] - elseif self.sharedList.type.id == 10 then - itemData = [[ + elseif self.sharedList.type.id == 10 then + itemData = [[ Destructive Aspiration ]] .. label .. [[ Ghastly Eye Jewel League: Allflame @@ -296,8 +268,8 @@ Subjugating ]] .. data.seed .. [[ souls in the thrall of Amanamu Passives affected are Conquered by the Abyssal Historic ]] - elseif self.sharedList.type.id == 11 then - itemData = [[ + elseif self.sharedList.type.id == 11 then + itemData = [[ Reclaimed Malevolence ]] .. label .. [[ Assembled Eye Jewel League: Allflame @@ -307,8 +279,52 @@ Binding ]] .. data.seed .. [[ souls to phylacteries to sustain Zorath Passives affected are Conquered by the Abyssal Historic ]] + end + return new("Item"):Item(itemData) +end + +---@param tooltip Tooltip +---@param index any +---@param data any +function TimelessJewelListControlClass:AddValueTooltip(tooltip, index, data) + if not tooltip:CheckForUpdate(self.build.outputRevision, data) then + return + end + tooltip:Clear() + if not self.noTooltip then + if self.list[index].label:match("B2B2B2") == nil then + tooltip:AddLine(16, "^7Double click to add this jewel to your build.") + else + tooltip:AddLine(16, "^7" .. self.sharedList.type.label .. " " .. data.seed .. " was successfully added to your build.") end - local item = new("Item"):Item(itemData) + local sortedNodeLists = {} + for legionId, desiredNode in pairs(self.sharedList.desiredNodes or {}) do + if self.list[index][legionId] then + if self.list[index][legionId].targetNodeNames and #self.list[index][legionId].targetNodeNames > 0 then + sortedNodeLists[desiredNode.desiredIdx] = "^7 " .. desiredNode.displayName .. ":\n^8 " .. t_concat(self.list[index][legionId].targetNodeNames, "\n ") + else + sortedNodeLists[desiredNode.desiredIdx] = "^7 " .. desiredNode.displayName .. ":\n^8 None" + end + end + end + if next(sortedNodeLists) then + tooltip:AddLine(16, "^7Node List:") + for _, sortedNodeList in pairs(sortedNodeLists) do + tooltip:AddLine(16, sortedNodeList) + end + end + if data.total > 0 then + tooltip:AddLine(16, "^7Combined Node Weight: " .. data.total) + end + local jewel = self:GetJewelItem(data) + local socket = self.sharedList.socket and self.build.itemsTab:GetSocketAndJewelForNodeID(self.sharedList.socket.id) + self.build.itemsTab:AddItemStatDifferences(tooltip, jewel, jewel.base, socket) + end +end + +function TimelessJewelListControlClass:OnSelClick(index, data, doubleClick) + if doubleClick and self.list[index].label:match("B2B2B2") == nil then + local item = self:GetJewelItem(data) self.build.itemsTab:AddItem(item, true) self.build.itemsTab:PopulateSlots() self.list[index].label = "^xB2B2B2" .. self.list[index].label diff --git a/src/Classes/TreeTab.lua b/src/Classes/TreeTab.lua index c9cb48c2111..3b0a093c528 100644 --- a/src/Classes/TreeTab.lua +++ b/src/Classes/TreeTab.lua @@ -1281,7 +1281,8 @@ function TreeTabClass:FindTimelessJewel() { id = 11, label = "Minion Attack and Cast Speed" }, { id = 12, label = "Minions Accuracy Rating" }, { id = 13, label = "Mana Regen" }, - { id = 14, label = "Skill Cost" }, + { id = 14, label = "Mana Cost (legacy)" }, + { id = 17, label = "Mana Cost Efficiency" }, { id = 15, label = "Non-Curse Aura Effect" }, { id = 16, label = "Defences from Shield" } } @@ -1528,10 +1529,36 @@ function TreeTabClass:FindTimelessJewel() local protectedNodes = { } local protectedNodesCount = 0 local setAllocatedNodes - local clearProtected + -- local clearProtected self.allocatedNodesInRadiusCount = 0 - controls.jewelSelect = new("DropDownControl"):DropDownControl({"TOPLEFT", nil, "TOPLEFT"}, {520, 25, 200, rowHeight}, jewelTypes, function(index, value) + local function buildNodeOptionCheckboxes(nodes) + local i = 1 + -- clear old boxes + while controls["protectAllocatedBox" .. i] do + controls["protectAllocatedBox" .. i] = nil + i = i + 1 + end + if not nodes then + return + end + for i, nodeInfo in ipairs(nodes) do + local prevBox = controls["protectAllocatedBox" .. (i - 1)] + local anchor = prevBox or controls.protectAllocatedLabel + local box = new("CheckBoxControl"):CheckBoxControl({ "TOPLEFT", anchor, "BOTTOMLEFT" }, { 0, 4, 18, 18 }, nodeInfo.label, function(state) + protectedNodes[nodeInfo.label] = state + protectedNodesCount = protectedNodesCount + (state and 1 or -1) + end) + box.labelRight = true + box.tooltipFunc = function(tooltip, _) + tooltip:Clear() + self.viewer:AddNodeTooltip(tooltip, nodeInfo.node, self.build, true) + end + controls["protectAllocatedBox" .. i] = box + end + end + + controls.jewelSelect = new("DropDownControl"):DropDownControl({ "TOPLEFT", nil, "TOPLEFT" }, { 520, 25, 200, rowHeight }, jewelTypes, function(index, value) timelessData.jewelType = value controls.devotionSelectLabel.shown = value.id == 4 -- Militant Faith controls.abyssAscendancyLabel.shown = value.id == 11 @@ -1545,7 +1572,7 @@ function TreeTabClass:FindTimelessJewel() updateSearchList("", false) updateSearchList("", true) if controls.socketFilter.state then - clearProtected() + buildNodeOptionCheckboxes() setAllocatedNodes() end end) @@ -1572,7 +1599,7 @@ function TreeTabClass:FindTimelessJewel() local baseNode = treeData.nodes[nodeId] if baseNode and baseNode.ascendancyName == self.build.spec.curAscendClassName and baseNode.type == "Notable" then allocatedNodes[nodeId] = true - t_insert(nodeOptions, { label = baseNode.dn, descriptions = copyTable(baseNode.sd) }) + t_insert(nodeOptions, { label = baseNode.dn, node = baseNode }) end end else @@ -1582,13 +1609,14 @@ function TreeTabClass:FindTimelessJewel() allocatedNodes[nodeId] = true if treeData.nodes[nodeId] and treeData.nodes[nodeId].isNotable then local baseNode = treeData.nodes[nodeId] - t_insert(nodeOptions, { label = baseNode.dn, descriptions = copyTable(baseNode.sd) }) + t_insert(nodeOptions, { label = baseNode.dn, node = baseNode }) end end end end t_sort(nodeOptions, function(a, b) return a.label < b.label end) - controls.protectAllocatedSelect:SetList(nodeOptions) + buildNodeOptionCheckboxes(nodeOptions) + self.nodeOptions = nodeOptions self.allocatedNodesInRadiusCount = #nodeOptions end @@ -1606,17 +1634,7 @@ function TreeTabClass:FindTimelessJewel() end end controls.socketSelectLabel = new("LabelControl"):LabelControl({"RIGHT", controls.socketSelect, "LEFT"}, {-labelSpacing, 0, 0, labelHeight}, "^7Jewel Socket:") - - clearProtected = function() - protectedNodesCount = 0 - protectedNodes = { } - for index, _ in pairs(controls) do - if index:find("protected:") then - controls[index] = nil - end - end - end - + controls.socketFilter = new("CheckBoxControl"):CheckBoxControl({"TOPLEFT", controls.socketSelect, "BOTTOMLEFT"}, {0, rowSpacing, rowHeight}, nil, function(value) timelessData.socketFilter = value self.build.modFlag = true @@ -1628,7 +1646,7 @@ function TreeTabClass:FindTimelessJewel() if value then setAllocatedNodes() else - clearProtected() + buildNodeOptionCheckboxes() end end) controls.socketFilterLabel = new("LabelControl"):LabelControl({"RIGHT", controls.socketFilter, "LEFT"}, {-labelSpacing, 0, 0, labelHeight}, "^7Filter Nodes:") @@ -1648,19 +1666,6 @@ function TreeTabClass:FindTimelessJewel() 0, 16, }, "^7Protect allocated nodes from changing:") - controls.protectAllocatedSelect = new("DropDownControl"):DropDownControl({ "TOPLEFT", controls.protectAllocatedLabel, "BOTTOMLEFT" }, { 0, 8, 200, 18 }, nil, nil) - controls.protectAllocatedButtonAdd = new("ButtonControl"):ButtonControl({ "LEFT", controls.protectAllocatedSelect, "RIGHT" }, { 5, 0, 44, 18 }, "Add", function() - local selValue = controls.protectAllocatedSelect:GetSelValue() - local nodeName = selValue and selValue.label - if nodeName and not controls["protected:"..nodeName] then - protectedNodesCount = protectedNodesCount + 1 - t_insert(protectedNodes, nodeName) - controls["protected:"..nodeName] = new("LabelControl"):LabelControl({ "TOPLEFT", controls.protectAllocatedSelect, "BOTTOMLEFT" }, { 0, 16 * protectedNodesCount - 10, 0, 16 }, "^7"..nodeName) - end - end) - controls.protectAllocatedButtonClear = new("ButtonControl"):ButtonControl({ "LEFT", controls.protectAllocatedButtonAdd, "RIGHT" }, { 5, 0, 44, 18 }, "Clear", function() - clearProtected() - end) -- set shown and list on load if controls.socketFilter.state then setAllocatedNodes() @@ -1700,20 +1705,6 @@ function TreeTabClass:FindTimelessJewel() end end end - controls.protectAllocatedSelect.tooltipFunc = function(tooltip, mode, index, value) - tooltip:Clear() - if mode ~= "OUT" and value and value.descriptions then - for _, line in ipairs(value.descriptions) do - tooltip:AddLine(16, "^7" .. line) - end - end - end - - controls.protectAllocatedButtonAdd.tooltipFunc = function(tooltip, mode, index, value) - tooltip:Clear() - tooltip:AddLine(16, "^7Protect allocated nodes during search.") - tooltip:AddLine(16, "^7This can be useful if transforming certain notables would break your build.") - end local socketFilterAdditionalDistanceMAX = 10 controls.socketFilterAdditionalDistanceLabel = new("LabelControl"):LabelControl({"LEFT", controls.socketFilter, "RIGHT"}, {10, 0, 0, 16}, "^7Node Distance:") @@ -2569,7 +2560,7 @@ function TreeTabClass:FindTimelessJewel() for targetNode, modification in pairs(data.readAbyssJewelLUT(curSeed, socketId, timelessData.jewelType.id, zorathPath, self.build.spec.curAscendClassName)) do local treeNode = treeData.nodes[targetNode] local scoreNode = treeNode and not rootNodes[targetNode] and not treeNode.isJewelSocket and not treeNode.isKeystone - if scoreNode and treeNode.ascendancyName and isValueInTable(protectedNodes, treeNode.dn) then + if scoreNode and treeNode.ascendancyName and protectedNodes[treeNode.dn] then resultNodes[curSeed] = nil break end @@ -2609,7 +2600,7 @@ function TreeTabClass:FindTimelessJewel() else local curNode = nil local curNodeId = nil - if (timelessData.jewelType.id == 4 and isValueInTable(protectedNodes, treeData.nodes[targetNode].dn)) then -- protected + if (timelessData.jewelType.id == 4 and protectedNodes[treeData.nodes[targetNode].dn]) then -- protected if jewelDataTbl[1] >= data.timelessJewelAdditions then -- protected node is a replacement, invalidate seed resultNodes[curSeed] = nil break @@ -2624,7 +2615,7 @@ function TreeTabClass:FindTimelessJewel() end curNodeId = "totalStat" end - if jewelDataTbl[1] >= data.timelessJewelAdditions and not isValueInTable(protectedNodes, treeData.nodes[targetNode].dn) then -- replace + if jewelDataTbl[1] >= data.timelessJewelAdditions and not protectedNodes[treeData.nodes[targetNode].dn] then -- replace curNode = legionNodes[jewelDataTbl[1] + 1 - data.timelessJewelAdditions] curNodeId = curNode and legionNodes[jewelDataTbl[1] + 1 - data.timelessJewelAdditions].id or nil else -- add @@ -2791,7 +2782,7 @@ function TreeTabClass:FindTimelessJewel() controls.abyssAscendancySelect.selIndex = 1 wipeTable(timelessData.searchResults) controls.searchTradeButton.enabled = false - clearProtected() + buildNodeOptionCheckboxes() end) controls.closeButton = new("ButtonControl"):ButtonControl({"LEFT", controls.resetButton, "RIGHT"}, {buttonDivider, 0, buttonWidth, buttonHeight}, "Cancel", function() main:ClosePopup() diff --git a/src/Data/TimelessJewelData/LegionTradeIds.lua b/src/Data/TimelessJewelData/LegionTradeIds.lua index 44fe6788eeb..a4d7741725c 100644 --- a/src/Data/TimelessJewelData/LegionTradeIds.lua +++ b/src/Data/TimelessJewelData/LegionTradeIds.lua @@ -44,7 +44,8 @@ return { [12] = "explicit.stat_2042813020", [13] = "explicit.stat_3293275880", [14] = "explicit.stat_2585926696", - [15] = "explicit.stat_2803981661" + [15] = "explicit.stat_2803981661", + [16] = "explicit.stat_1355915086 ", } }, [5] = { From 9482ccf72ce2604f35909efae0c22a0ac287c396 Mon Sep 17 00:00:00 2001 From: vaisest <4550061+vaisest@users.noreply.github.com> Date: Sun, 23 Aug 2026 17:43:33 +0300 Subject: [PATCH 2/7] Fix all sockets comparison and hide node protection for all sockets --- src/Classes/TimelessJewelListControl.lua | 2 +- src/Classes/TreeTab.lua | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Classes/TimelessJewelListControl.lua b/src/Classes/TimelessJewelListControl.lua index 2d690ca624a..dffe813671a 100644 --- a/src/Classes/TimelessJewelListControl.lua +++ b/src/Classes/TimelessJewelListControl.lua @@ -287,6 +287,7 @@ end ---@param index any ---@param data any function TimelessJewelListControlClass:AddValueTooltip(tooltip, index, data) + local socket = self.build.itemsTab:GetSocketAndJewelForNodeID(data.socketId) if not tooltip:CheckForUpdate(self.build.outputRevision, data) then return end @@ -317,7 +318,6 @@ function TimelessJewelListControlClass:AddValueTooltip(tooltip, index, data) tooltip:AddLine(16, "^7Combined Node Weight: " .. data.total) end local jewel = self:GetJewelItem(data) - local socket = self.sharedList.socket and self.build.itemsTab:GetSocketAndJewelForNodeID(self.sharedList.socket.id) self.build.itemsTab:AddItemStatDifferences(tooltip, jewel, jewel.base, socket) end end diff --git a/src/Classes/TreeTab.lua b/src/Classes/TreeTab.lua index 3b0a093c528..07e29c5bfc1 100644 --- a/src/Classes/TreeTab.lua +++ b/src/Classes/TreeTab.lua @@ -1670,7 +1670,9 @@ function TreeTabClass:FindTimelessJewel() if controls.socketFilter.state then setAllocatedNodes() end - controls.protectAllocatedLabel.shown = (controls.jewelSelect.selIndex == 4 or controls.jewelSelect.selIndex == 11) and controls.socketFilter.state + controls.protectAllocatedLabel.shown = function() + return (controls.jewelSelect.selIndex == 4 or controls.jewelSelect.selIndex == 11) and controls.socketFilter.state and not (timelessData.jewelSocket and timelessData.jewelSocket.id == -1) + end -- This requirement is separate from ordinary node weights and remains above -- the protection list so adding protected nodes does not move the selector. From 539f59a9f6ebe0d049ef9d88406fe4cfbf5ca115 Mon Sep 17 00:00:00 2001 From: vaisest <4550061+vaisest@users.noreply.github.com> Date: Sun, 23 Aug 2026 19:02:26 +0300 Subject: [PATCH 3/7] Fix abyss test and add fancy tooltip for abyss dropdown --- spec/System/TestAbyssTimelessJewel_spec.lua | 27 ++++++++++++++------- src/Classes/TreeTab.lua | 10 ++++---- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/spec/System/TestAbyssTimelessJewel_spec.lua b/spec/System/TestAbyssTimelessJewel_spec.lua index 4ec1714b46e..1f64037c529 100644 --- a/spec/System/TestAbyssTimelessJewel_spec.lua +++ b/spec/System/TestAbyssTimelessJewel_spec.lua @@ -415,20 +415,29 @@ describe("Abyss timeless jewels", function() controls.searchButton.onClick() assert.are.equal(1, #build.timelessData.searchResults) - local protectedOption - for index, option in ipairs(controls.protectAllocatedSelect.list) do - if option.label == baseNodeName then - controls.protectAllocatedSelect.selIndex = index - protectedOption = option + local protectBox + local boxIndex = 1 + while controls["protectAllocatedBox" .. boxIndex] do + if controls["protectAllocatedBox" .. boxIndex].label == baseNodeName then + protectBox = controls["protectAllocatedBox" .. boxIndex] break end + boxIndex = boxIndex + 1 end - assert.are.equal(baseNodeName, controls.protectAllocatedSelect:GetSelValue().label) + assert.is_truthy(protectBox, "Expected a protection checkbox for " .. baseNodeName) local tooltip = new("Tooltip"):Tooltip() - controls.protectAllocatedSelect.tooltipFunc(tooltip, "DROP", controls.protectAllocatedSelect.selIndex, protectedOption) + protectBox.tooltipFunc(tooltip) assert.is_true(#tooltip.lines > 0) - assert.are.same(spec.tree.nodes[53884].sd, protectedOption.descriptions) - controls.protectAllocatedButtonAdd.onClick() + local tooltipText = {} + for _, line in ipairs(tooltip.lines) do + table.insert(tooltipText, line.text or "") + end + tooltipText = table.concat(tooltipText, "\n") + for _, line in ipairs(spec.tree.nodes[53884].sd) do + assert.is_truthy(tooltipText:find(line, 1, true), "Expected tooltip to describe " .. line) + end + protectBox.state = true + protectBox.changeFunc(true) local _, requiredAscendancyYAfterAdd = controls.abyssAscendancySelect:GetPos() assert.are.equal(requiredAscendancyY, requiredAscendancyYAfterAdd) controls.searchButton.onClick() diff --git a/src/Classes/TreeTab.lua b/src/Classes/TreeTab.lua index 07e29c5bfc1..a93619beb2a 100644 --- a/src/Classes/TreeTab.lua +++ b/src/Classes/TreeTab.lua @@ -1291,7 +1291,7 @@ function TreeTabClass:FindTimelessJewel() if node.id:match("^abyss_special_ascendancy_notable_%d+$") then t_insert(abyssAscendancyOptions, { label = node.dn, - descriptions = copyTable(node.sd), + node = node, id = node.id, }) end @@ -1534,6 +1534,8 @@ function TreeTabClass:FindTimelessJewel() local function buildNodeOptionCheckboxes(nodes) local i = 1 + protectedNodes = {} + protectedNodesCount = 0 -- clear old boxes while controls["protectAllocatedBox" .. i] do controls["protectAllocatedBox" .. i] = nil @@ -1701,10 +1703,8 @@ function TreeTabClass:FindTimelessJewel() end controls.abyssAscendancySelect.tooltipFunc = function(tooltip, mode, index, value) tooltip:Clear() - if mode ~= "OUT" and value.descriptions then - for _, line in ipairs(value.descriptions) do - tooltip:AddLine(16, "^7" .. line) - end + if value.node then + self.viewer:AddNodeTooltip(tooltip, value.node, self.build, true) end end From 8cef1f5ba048814ee7e3d69cada178c4c2ab6a87 Mon Sep 17 00:00:00 2001 From: vaisest <4550061+vaisest@users.noreply.github.com> Date: Sun, 23 Aug 2026 21:15:22 +0300 Subject: [PATCH 4/7] Fix space in trade hash, fix nil socket crash, add tooltip for keystone --- src/Classes/TimelessJewelListControl.lua | 3 ++- src/Classes/TreeTab.lua | 10 ++++++++++ src/Data/TimelessJewelData/LegionTradeIds.lua | 2 +- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/Classes/TimelessJewelListControl.lua b/src/Classes/TimelessJewelListControl.lua index dffe813671a..59b1a3bf54f 100644 --- a/src/Classes/TimelessJewelListControl.lua +++ b/src/Classes/TimelessJewelListControl.lua @@ -287,7 +287,8 @@ end ---@param index any ---@param data any function TimelessJewelListControlClass:AddValueTooltip(tooltip, index, data) - local socket = self.build.itemsTab:GetSocketAndJewelForNodeID(data.socketId) + local socketId = data.socketId or self.sharedList.socket.id + local socket = socketId and socketId ~= -1 and self.build.itemsTab:GetSocketAndJewelForNodeID(socketId) if not tooltip:CheckForUpdate(self.build.outputRevision, data) then return end diff --git a/src/Classes/TreeTab.lua b/src/Classes/TreeTab.lua index a93619beb2a..9302c82be35 100644 --- a/src/Classes/TreeTab.lua +++ b/src/Classes/TreeTab.lua @@ -1588,6 +1588,16 @@ function TreeTabClass:FindTimelessJewel() end) controls.conquerorSelect.selIndex = timelessData.conquerorType.id controls.conquerorSelectLabel = new("LabelControl"):LabelControl({"RIGHT", controls.conquerorSelect, "LEFT"}, {-labelSpacing, 0, 0, labelHeight}, "^7Conqueror:") + controls.conquerorSelect.tooltipFunc = function(tt, _, idx, val) + tt:Clear() + local keystoneName = val.label:match("%((.+)%)$") + if keystoneName then + local keyStoneNode = self.build.spec.tree.keystoneMap[keystoneName] + if keyStoneNode then + self.viewer:AddNodeTooltip(tt, keyStoneNode, self.build, true) + end + end + end setAllocatedNodes = function() if timelessData.jewelSocket.id == -1 or not treeData.nodes[timelessData.jewelSocket.id] then diff --git a/src/Data/TimelessJewelData/LegionTradeIds.lua b/src/Data/TimelessJewelData/LegionTradeIds.lua index a4d7741725c..609becb7b76 100644 --- a/src/Data/TimelessJewelData/LegionTradeIds.lua +++ b/src/Data/TimelessJewelData/LegionTradeIds.lua @@ -45,7 +45,7 @@ return { [13] = "explicit.stat_3293275880", [14] = "explicit.stat_2585926696", [15] = "explicit.stat_2803981661", - [16] = "explicit.stat_1355915086 ", + [16] = "explicit.stat_1355915086", } }, [5] = { From 151a7af9557d063db20fcc5ae09d22174c10badf Mon Sep 17 00:00:00 2001 From: vaisest <4550061+vaisest@users.noreply.github.com> Date: Sun, 23 Aug 2026 21:53:48 +0300 Subject: [PATCH 5/7] Fix allocated label shown func --- src/Classes/TreeTab.lua | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Classes/TreeTab.lua b/src/Classes/TreeTab.lua index 9302c82be35..d3e99590a83 100644 --- a/src/Classes/TreeTab.lua +++ b/src/Classes/TreeTab.lua @@ -1529,7 +1529,6 @@ function TreeTabClass:FindTimelessJewel() local protectedNodes = { } local protectedNodesCount = 0 local setAllocatedNodes - -- local clearProtected self.allocatedNodesInRadiusCount = 0 local function buildNodeOptionCheckboxes(nodes) @@ -1565,7 +1564,6 @@ function TreeTabClass:FindTimelessJewel() controls.devotionSelectLabel.shown = value.id == 4 -- Militant Faith controls.abyssAscendancyLabel.shown = value.id == 11 controls.abyssAscendancySelect.selIndex = 1 - controls.protectAllocatedLabel.shown = (value.id == 4 or value.id == 11) and controls.socketFilter.state controls.conquerorSelect.list = conquerorTypes[timelessData.jewelType.id] controls.conquerorSelect.selIndex = 1 timelessData.conquerorType = conquerorTypes[timelessData.jewelType.id][1] @@ -1653,7 +1651,6 @@ function TreeTabClass:FindTimelessJewel() controls.socketFilterAdditionalDistanceLabel.shown = value controls.socketFilterAdditionalDistance.shown = value controls.socketFilterAdditionalDistanceValue.shown = value - controls.protectAllocatedLabel.shown = value and (timelessData.jewelType.id == 4 or timelessData.jewelType.id == 11) if value then setAllocatedNodes() @@ -1683,7 +1680,7 @@ function TreeTabClass:FindTimelessJewel() setAllocatedNodes() end controls.protectAllocatedLabel.shown = function() - return (controls.jewelSelect.selIndex == 4 or controls.jewelSelect.selIndex == 11) and controls.socketFilter.state and not (timelessData.jewelSocket and timelessData.jewelSocket.id == -1) + return (timelessData.jewelType.id == 4 or timelessData.jewelType.id == 11) and controls.socketFilter.state and not (timelessData.jewelSocket and timelessData.jewelSocket.id == -1) end -- This requirement is separate from ordinary node weights and remains above From 16f255987a2e207795c7dc042f1f6979967a7fee Mon Sep 17 00:00:00 2001 From: LocalIdentity Date: Mon, 24 Aug 2026 06:32:17 +1000 Subject: [PATCH 6/7] Fix 2 issues The new cost efficiency mod means we need to up the max index value Clear protected nodes when selecting all sockets so it doesn't apply them in the background --- src/Classes/TimelessJewelListControl.lua | 6 +++--- src/Classes/TreeTab.lua | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Classes/TimelessJewelListControl.lua b/src/Classes/TimelessJewelListControl.lua index 59b1a3bf54f..ab82c128b53 100644 --- a/src/Classes/TimelessJewelListControl.lua +++ b/src/Classes/TimelessJewelListControl.lua @@ -130,10 +130,10 @@ Passives in radius are Conquered by the Maraketh Historic ]] elseif self.sharedList.type.id == 4 then - local altVariant = self.sharedList.devotionVariant1.id ~= 1 and self.sharedList.devotionVariant1.id or m_random(2, 16) - local altVariant2 = self.sharedList.devotionVariant2.id ~= 1 and self.sharedList.devotionVariant2.id or m_random(2, 16) + local altVariant = self.sharedList.devotionVariant1.id ~= 1 and self.sharedList.devotionVariant1.id or m_random(2, 17) + local altVariant2 = self.sharedList.devotionVariant2.id ~= 1 and self.sharedList.devotionVariant2.id or m_random(2, 17) if altVariant == altVariant2 then - altVariant = altVariant % 15 + 2 + altVariant = altVariant % 16 + 2 end itemData = [[ Militant Faith ]] .. label .. [[ diff --git a/src/Classes/TreeTab.lua b/src/Classes/TreeTab.lua index d3e99590a83..1f9a72a35a4 100644 --- a/src/Classes/TreeTab.lua +++ b/src/Classes/TreeTab.lua @@ -1598,10 +1598,12 @@ function TreeTabClass:FindTimelessJewel() end setAllocatedNodes = function() + wipeTable(allocatedNodes) + self.allocatedNodesInRadiusCount = 0 if timelessData.jewelSocket.id == -1 or not treeData.nodes[timelessData.jewelSocket.id] then + buildNodeOptionCheckboxes() return end - wipeTable(allocatedNodes) local nodeOptions = { } if timelessData.jewelType.id == 11 then -- Reclaimed Malevolence can replace an allocated notable in the selected ascendancy. From 1ae1e66d7f78e0348bc15c25552894fe258bbc83 Mon Sep 17 00:00:00 2001 From: LocalIdentity Date: Mon, 24 Aug 2026 07:06:00 +1000 Subject: [PATCH 7/7] Fix timeless jewel finder UI edge cases MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Preserve saved Militant Faith devotion selections Keep “Any” modifier previews consistent with added jewels Restore protection checkboxes after reset Fix right-side checkbox label click areas Remove redundant checkbox setup --- src/Classes/CheckBoxControl.lua | 6 ++++-- src/Classes/TimelessJewelListControl.lua | 6 ++++-- src/Classes/TreeTab.lua | 4 +--- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/Classes/CheckBoxControl.lua b/src/Classes/CheckBoxControl.lua index 93e2eabdf1d..048d2582a2b 100644 --- a/src/Classes/CheckBoxControl.lua +++ b/src/Classes/CheckBoxControl.lua @@ -26,10 +26,12 @@ function CheckBoxClass:IsMouseOver() local width, height = self:GetSize() local cursorX, cursorY = GetCursorPos() - -- move x left by label width, increase width by label width + -- Include the label in the clickable area. local label = self:GetProperty("label") if label then - x = x - self.labelWidth + if not self.labelRight then + x = x - self.labelWidth + end width = width + self.labelWidth end return cursorX >= x and cursorY >= y and cursorX < x + width and cursorY < y + height diff --git a/src/Classes/TimelessJewelListControl.lua b/src/Classes/TimelessJewelListControl.lua index ab82c128b53..eb494dfe632 100644 --- a/src/Classes/TimelessJewelListControl.lua +++ b/src/Classes/TimelessJewelListControl.lua @@ -130,11 +130,13 @@ Passives in radius are Conquered by the Maraketh Historic ]] elseif self.sharedList.type.id == 4 then - local altVariant = self.sharedList.devotionVariant1.id ~= 1 and self.sharedList.devotionVariant1.id or m_random(2, 17) - local altVariant2 = self.sharedList.devotionVariant2.id ~= 1 and self.sharedList.devotionVariant2.id or m_random(2, 17) + local altVariant = self.sharedList.devotionVariant1.id ~= 1 and self.sharedList.devotionVariant1.id or data.altVariant or m_random(2, 17) + local altVariant2 = self.sharedList.devotionVariant2.id ~= 1 and self.sharedList.devotionVariant2.id or data.altVariant2 or m_random(2, 17) if altVariant == altVariant2 then altVariant = altVariant % 16 + 2 end + data.altVariant = altVariant + data.altVariant2 = altVariant2 itemData = [[ Militant Faith ]] .. label .. [[ Timeless Jewel diff --git a/src/Classes/TreeTab.lua b/src/Classes/TreeTab.lua index 1f9a72a35a4..8a41ff82eb2 100644 --- a/src/Classes/TreeTab.lua +++ b/src/Classes/TreeTab.lua @@ -1572,7 +1572,6 @@ function TreeTabClass:FindTimelessJewel() updateSearchList("", false) updateSearchList("", true) if controls.socketFilter.state then - buildNodeOptionCheckboxes() setAllocatedNodes() end end) @@ -1628,7 +1627,6 @@ function TreeTabClass:FindTimelessJewel() end t_sort(nodeOptions, function(a, b) return a.label < b.label end) buildNodeOptionCheckboxes(nodeOptions) - self.nodeOptions = nodeOptions self.allocatedNodesInRadiusCount = #nodeOptions end @@ -2793,7 +2791,7 @@ function TreeTabClass:FindTimelessJewel() controls.abyssAscendancySelect.selIndex = 1 wipeTable(timelessData.searchResults) controls.searchTradeButton.enabled = false - buildNodeOptionCheckboxes() + setAllocatedNodes() end) controls.closeButton = new("ButtonControl"):ButtonControl({"LEFT", controls.resetButton, "RIGHT"}, {buttonDivider, 0, buttonWidth, buttonHeight}, "Cancel", function() main:ClosePopup()