Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
203 changes: 203 additions & 0 deletions spec/System/TestRadiusJewelStatDiff_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,35 @@ local function setupAllocatedSocket()
return spec, socketNode
end

local function setupAllocatedSockets(count)
local spec = build.spec
local sockets = { }
local sortedSockets = { }
for _, node in pairs(spec.nodes) do
if node.isJewelSocket then
sortedSockets[#sortedSockets + 1] = node
end
end
table.sort(sortedSockets, function(a, b)
return a.id < b.id
end)
for _, socketNode in ipairs(sortedSockets) do
if allocatePathToNode(spec, socketNode) then
sockets[#sockets + 1] = socketNode
if #sockets >= count then
break
end
end
end
if #sockets < count then
pending("Could not allocate the requested number of jewel sockets for this tree layout")
return spec, sockets
end
spec:BuildAllDependsAndPaths()
runCallback("OnFrame")
return spec, sockets
end

local function rebuildBuild()
build.buildFlag = true
runCallback("OnFrame")
Expand Down Expand Up @@ -172,6 +201,15 @@ local function newPlainJewel()
"Implicits: 0\n")
end

local function newSplitPersonality()
return new("Item", "Rarity: UNIQUE\n" ..
"Split Personality\n" ..
"Crimson Jewel\n" ..
"Implicits: 0\n" ..
"+5 to Strength\n" ..
"This Jewel's Socket has 25% increased effect per Allocated Passive Skill between it and your Class' starting location\n")
end

-- Helper: minimal Impossible Escape item. Uses "Radius: Small" and targets
-- a specific keystone. The parser populates both impossibleEscapeKeystone
-- and impossibleEscapeKeystones from the "in Radius of X" mod.
Expand Down Expand Up @@ -597,4 +635,169 @@ describe("TestRadiusJewelStatDiff", function()
"tooltip should contain a 'Removing this item' comparison header")
end)

it("AddItemTooltip avoids rebuilding unused limited-unique socket comparisons without a target slot", function()
local spec, sockets = setupAllocatedSockets(2)

local item = newThreadOfHope()
item.limit = 1
equipJewelInSocket(item, sockets[1])
spec:BuildAllDependsAndPaths()
runCallback("OnFrame")

local specClass = getmetatable(spec)
local originalBuildAllDependsAndPaths = specClass.BuildAllDependsAndPaths
local rebuilds = 0
specClass.BuildAllDependsAndPaths = function(self, ...)
rebuilds = rebuilds + 1
return originalBuildAllDependsAndPaths(self, ...)
end

local ok, err = pcall(function()
local tooltip = new("Tooltip")
build.itemsTab:AddItemTooltip(tooltip, item)
end)
specClass.BuildAllDependsAndPaths = originalBuildAllDependsAndPaths
if not ok then
error(err)
end

assert.are.equals(1, rebuilds,
"limited unique radius jewels should rebuild only the same-unique slot that will be displayed")
end)

it("AddItemTooltip reuses targeted radius jewel comparison specs until output changes", function()
local spec, sockets = setupAllocatedSockets(2)

local item = newCustomLeapJewel("Cached Leap")
local slot = equipJewelInSocket(item, sockets[1])
spec:BuildAllDependsAndPaths()
runCallback("OnFrame")

local originalSlotOnlyTooltips = main.slotOnlyTooltips
main.slotOnlyTooltips = true
local specClass = getmetatable(spec)
local originalBuildAllDependsAndPaths = specClass.BuildAllDependsAndPaths
local rebuilds = 0
specClass.BuildAllDependsAndPaths = function(self, ...)
rebuilds = rebuilds + 1
return originalBuildAllDependsAndPaths(self, ...)
end

local ok, err = pcall(function()
local tooltip = new("Tooltip")
build.itemsTab:AddItemTooltip(tooltip, item, slot)
tooltip = new("Tooltip")
build.itemsTab:AddItemTooltip(tooltip, item, slot)
assert.are.equals(1, rebuilds,
"targeted radius jewel hover should reuse its cached comparison spec")

build.outputRevision = build.outputRevision + 1
tooltip = new("Tooltip")
build.itemsTab:AddItemTooltip(tooltip, item, slot)
assert.are.equals(2, rebuilds,
"targeted radius jewel comparison spec cache should reset when output changes")
end)
specClass.BuildAllDependsAndPaths = originalBuildAllDependsAndPaths
main.slotOnlyTooltips = originalSlotOnlyTooltips
if not ok then
error(err)
end
end)

it("AddItemTooltip skips UI path rebuilds for temporary radius jewel specs", function()
local spec, sockets = setupAllocatedSockets(2)

local radiusItem = newThreadOfHope()
local radiusSlot = equipJewelInSocket(radiusItem, sockets[1])
local splitItem = newSplitPersonality()
equipJewelInSocket(splitItem, sockets[2])
spec:BuildAllDependsAndPaths()
runCallback("OnFrame")

assert.is_true((spec.nodes[sockets[2].id].distanceToClassStart or 0) > 0,
"Split Personality socket should have a class-start distance in the base spec")

local originalSlotOnlyTooltips = main.slotOnlyTooltips
main.slotOnlyTooltips = true
local specClass = getmetatable(spec)
local originalBuildPathFromNode = specClass.BuildPathFromNode
local originalSetNodeDistanceToClassStart = specClass.SetNodeDistanceToClassStart
local buildPathCalls = 0
local distanceCalls = 0
specClass.BuildPathFromNode = function(self, ...)
buildPathCalls = buildPathCalls + 1
return originalBuildPathFromNode(self, ...)
end
specClass.SetNodeDistanceToClassStart = function(self, ...)
distanceCalls = distanceCalls + 1
return originalSetNodeDistanceToClassStart(self, ...)
end

local ok, err = pcall(function()
local tooltip = new("Tooltip")
build.itemsTab:AddItemTooltip(tooltip, radiusItem, radiusSlot)
end)
specClass.BuildPathFromNode = originalBuildPathFromNode
specClass.SetNodeDistanceToClassStart = originalSetNodeDistanceToClassStart
main.slotOnlyTooltips = originalSlotOnlyTooltips
if not ok then
error(err)
end

assert.are.equals(0, buildPathCalls,
"temporary tooltip specs should not rebuild UI node paths")
assert.is_true(distanceCalls > 0,
"temporary tooltip specs should still refresh jewel socket distances used by calc")
end)

it("AddItemTooltip reuses full radius jewel comparison outputs until output changes", function()
local spec, sockets = setupAllocatedSockets(2)

local item = newCustomLeapJewel("Cached Full Leap")
local slot = equipJewelInSocket(item, sockets[1])
spec:BuildAllDependsAndPaths()
runCallback("OnFrame")

local originalSlotOnlyTooltips = main.slotOnlyTooltips
main.slotOnlyTooltips = false
build.itemsTab.jewelComparisonOutputCache = nil
build.itemsTab.targetedJewelComparisonSpecCache = nil

local originalGetMiscCalculator = build.calcsTab.GetMiscCalculator
local calcCalls = 0
build.calcsTab.GetMiscCalculator = function(self, ...)
local calcFunc, calcBase = originalGetMiscCalculator(self, ...)
return function(...)
calcCalls = calcCalls + 1
return calcFunc(...)
end, calcBase
end

local ok, err = pcall(function()
local tooltip = new("Tooltip")
build.itemsTab:AddItemTooltip(tooltip, item, slot)
local firstPassCalcCalls = calcCalls
assert.is_true(firstPassCalcCalls > 0,
"full radius jewel tooltip should calculate outputs on first pass")

tooltip = new("Tooltip")
build.itemsTab:AddItemTooltip(tooltip, item, slot)
local secondPassCalcCalls = calcCalls - firstPassCalcCalls
assert.is_true(secondPassCalcCalls < firstPassCalcCalls,
"full radius jewel tooltip should reuse cached radius outputs on second pass")

build.outputRevision = build.outputRevision + 1
local beforeInvalidationCalcCalls = calcCalls
tooltip = new("Tooltip")
build.itemsTab:AddItemTooltip(tooltip, item, slot)
assert.is_true(calcCalls - beforeInvalidationCalcCalls > secondPassCalcCalls,
"full radius jewel output cache should reset when output changes")
end)
build.calcsTab.GetMiscCalculator = originalGetMiscCalculator
main.slotOnlyTooltips = originalSlotOnlyTooltips
if not ok then
error(err)
end
end)

end)
8 changes: 6 additions & 2 deletions src/Classes/CompareTab.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3727,6 +3727,7 @@ function CompareTabClass:DrawItems(vp, compareEntry, inputEvents)
local hoverX, hoverY = 0, 0
local hoverW, hoverH = 0, 0
local hoverItemsTab = nil
local hoverSlotName = nil

-- Track item copy button clicks
local clickedCopySlot = nil
Expand Down Expand Up @@ -3808,6 +3809,7 @@ function CompareTabClass:DrawItems(vp, compareEntry, inputEvents)
if rowHoverItem then
hoverItem = rowHoverItem
hoverItemsTab = rowHoverItemsTab
hoverSlotName = pHover and equipSlotName or cHover and copySlotName or nil
hoverX, hoverY = rowHoverX, rowHoverY
hoverW, hoverH = rowHoverW, rowHoverH
end
Expand Down Expand Up @@ -3890,8 +3892,10 @@ function CompareTabClass:DrawItems(vp, compareEntry, inputEvents)
SetViewport()
local maxTooltipWidth = m_min(600, m_max(260, vp.width - 24))
if hoverItem and hoverItemsTab then
self.itemTooltip:Clear()
hoverItemsTab:AddItemTooltip(self.itemTooltip, hoverItem, nil, nil, maxTooltipWidth)
local hoverBuild = hoverItemsTab.build
if self.itemTooltip:CheckForUpdate(hoverItemsTab, hoverItem, hoverSlotName, maxTooltipWidth, main.slotOnlyTooltips, launch.devModeAlt, hoverBuild and hoverBuild.outputRevision) then
hoverItemsTab:AddItemTooltip(self.itemTooltip, hoverItem, hoverSlotName, nil, maxTooltipWidth)
end
SetDrawLayer(nil, 100)
self.itemTooltip:Draw(vp.x + hoverX, vp.y + checkboxOffset + hoverY, hoverW, hoverH, vp)
SetDrawLayer(nil, 0)
Expand Down
Loading
Loading