Skip to content

Commit 44cf1a4

Browse files
xoxorwrWires77
andauthored
Update gem control to lazily sort by DPS (#9959)
Co-authored-by: Wires77 <Wires77@users.noreply.github.com>
1 parent 0120ad2 commit 44cf1a4

1 file changed

Lines changed: 115 additions & 32 deletions

File tree

src/Classes/GemSelectControl.lua

Lines changed: 115 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ local GemSelectClass = newClass("GemSelectControl", "EditControl", function(self
5353
lifeReservationPercent = "LifePercent",
5454
}
5555
self.imbuedSelect = imbued
56+
self.dpsBuildFlag = false
5657
end)
5758

5859
function GemSelectClass:CalcOutputWithThisGem(calcFunc, gemData, useFullDPS)
@@ -168,11 +169,12 @@ function GemSelectClass:BuildList(buf)
168169
t_remove(tagsList, 1)
169170

170171
-- Search for gem name using increasingly broad search patterns
172+
local lowerSearch = searchTerm:lower()
171173
local patternList = {
172-
"^ " .. searchTerm:lower().."$", -- Exact match
173-
"^" .. searchTerm:lower():gsub("%a", " %0%%l+") .. "$", -- Simple abbreviation ("CtF" -> "Cold to Fire")
174-
"^ " .. searchTerm:lower(), -- Starts with
175-
searchTerm:lower(), -- Contains
174+
"^ " .. lowerSearch.."$", -- Exact match
175+
"^" .. lowerSearch:gsub("%a", " %0%%l+") .. "$", -- Simple abbreviation ("CtF" -> "Cold to Fire")
176+
"^ " .. lowerSearch, -- Starts with
177+
lowerSearch, -- Contains
176178
}
177179
for i, pattern in ipairs(patternList) do
178180
local matchList = { }
@@ -340,44 +342,90 @@ function GemSelectClass:UpdateSortCache()
340342
-- Check for nil because some fields may not be populated, default to 0
341343
local baseDPS = (dpsField == "FullDPS" and calcBase[dpsField] ~= nil and calcBase[dpsField]) or (calcBase.Minion and calcBase.Minion.CombinedDPS) or (calcBase[dpsField] ~= nil and calcBase[dpsField]) or 0
342344

345+
sortCache.calcFunc = calcFunc
346+
sortCache.useFullDPS = useFullDPS
347+
sortCache.baseDPS = baseDPS
348+
sortCache.dpsField = dpsField
349+
sortCache.pendingGems = { }
350+
343351
for gemId, gemData in pairs(self.gems) do
344352
sortCache.dps[gemId] = baseDPS
345-
-- Ignore gems that don't support the active skill
353+
-- Gems that support the active skill or have global effects need DPS calc
346354
if sortCache.canSupport[gemId] or (gemData.grantedEffect.hasGlobalEffect and not gemData.grantedEffect.support) then
347-
local output = self:CalcOutputWithThisGem(calcFunc, gemData, useFullDPS)
348-
-- Check for nil because some fields may not be populated, default to 0
349-
sortCache.dps[gemId] = (dpsField == "FullDPS" and output[dpsField] ~= nil and output[dpsField]) or (output.Minion and output.Minion.CombinedDPS) or (output[dpsField] ~= nil and output[dpsField]) or 0
350-
end
351-
-- Color based on the DPS
352-
if sortCache.dps[gemId] > baseDPS then
353-
sortCache.dpsColor[gemId] = "^x228866"
354-
elseif sortCache.dps[gemId] < baseDPS then
355-
sortCache.dpsColor[gemId] = "^xFF4422"
356-
else
357-
sortCache.dpsColor[gemId] = "^xFFFF66"
355+
sortCache.pendingGems[#sortCache.pendingGems + 1] = gemId
358356
end
357+
-- Neutral color until DPS is computed
358+
sortCache.dpsColor[gemId] = ""
359359
end
360360

361-
--ConPrintf("Gem Selector time: %d ms", GetTime() - start)
361+
self.dpsBuildFlag = true
362362
end
363363

364364
function GemSelectClass:SortGemList(gemList)
365365
local sortCache = self.sortCache
366+
local gems = self.gems
367+
-- cache names to avoid repeated table lookups in comparator
368+
local names = {}
369+
for _, gemId in ipairs(gemList) do
370+
local gem = gems[gemId]
371+
names[gemId] = gem and gem.name or gemId
372+
end
366373
t_sort(gemList, function(a, b)
367374
if sortCache.canSupport[a] == sortCache.canSupport[b] then
368375
if self.skillsTab.sortGemsByDPS and sortCache.dps[a] ~= sortCache.dps[b] then
369376
return sortCache.dps[a] > sortCache.dps[b]
370377
else
371-
local nameA = (self.gems[a] and self.gems[a].name) or a
372-
local nameB = (self.gems[b] and self.gems[b].name) or b
373-
return nameA < nameB
378+
return names[a] < names[b]
374379
end
375380
else
376381
return sortCache.canSupport[a]
377382
end
378383
end)
379384
end
380385

386+
function GemSelectClass:DPSBuilder()
387+
local sortCache = self.sortCache
388+
if not sortCache or not sortCache.pendingGems then return end
389+
390+
local pending = sortCache.pendingGems
391+
local calcFunc = sortCache.calcFunc
392+
local useFullDPS = sortCache.useFullDPS
393+
local baseDPS = sortCache.baseDPS
394+
local dpsField = sortCache.dpsField
395+
local start = GetTime()
396+
397+
for index, gemId in ipairs(pending) do
398+
local gemData = self.gems[gemId]
399+
if gemData then
400+
local output = self:CalcOutputWithThisGem(calcFunc, gemData, useFullDPS)
401+
sortCache.dps[gemId] = (dpsField == "FullDPS" and output[dpsField] ~= nil and output[dpsField]) or (output.Minion and output.Minion.CombinedDPS) or (output[dpsField] ~= nil and output[dpsField]) or 0
402+
if sortCache.dps[gemId] > baseDPS then
403+
sortCache.dpsColor[gemId] = "^x228866"
404+
elseif sortCache.dps[gemId] < baseDPS then
405+
sortCache.dpsColor[gemId] = "^xFF4422"
406+
else
407+
sortCache.dpsColor[gemId] = "^xFFFF66"
408+
end
409+
end
410+
local now = GetTime()
411+
if now - start > 50 then
412+
if #self.searchStr == 0 then
413+
self:SortGemList(self.list)
414+
end
415+
if self.dpsBuilderCallback then
416+
self.dpsBuilderCallback(m_floor(index/#pending*100))
417+
end
418+
coroutine.yield()
419+
start = now
420+
end
421+
end
422+
423+
if #self.searchStr == 0 then
424+
self:SortGemList(self.list)
425+
end
426+
sortCache.pendingGems = nil
427+
end
428+
381429
function GemSelectClass:UpdateGem(setText, addUndo, focusLost)
382430
local gemId = self.list[m_max(self.selIndex, 1)]
383431
-- don't process unless the buffer equals an actual gem, whether typed, clicked, or navigated with arrows
@@ -428,6 +476,24 @@ function GemSelectClass:IsMouseOver()
428476
end
429477

430478
function GemSelectClass:Draw(viewPort, noTooltip)
479+
self.sortPercentage = self.sortPercentage or ""
480+
if self.dpsBuildFlag then
481+
self.dpsBuildFlag = false
482+
self.dpsBuilder = coroutine.create(self.DPSBuilder)
483+
self.dpsBuilderCallback = function(percentage)
484+
self.sortPercentage = ("%d%%"):format(percentage)
485+
end
486+
end
487+
if self.dpsBuilder then
488+
local res, errMsg = coroutine.resume(self.dpsBuilder, self)
489+
if launch.devMode and not res then
490+
error(errMsg)
491+
end
492+
if coroutine.status(self.dpsBuilder) == "dead" then
493+
self.dpsBuilder = nil
494+
end
495+
end
496+
431497
self.EditControl:Draw(viewPort, noTooltip and not self.forceTooltip)
432498
local x, y = self:GetPos()
433499
local width, height = self:GetSize()
@@ -446,6 +512,10 @@ function GemSelectClass:Draw(viewPort, noTooltip)
446512
end
447513
if self.dropped then
448514
SetDrawLayer(nil, 5)
515+
if self.dpsBuilder then
516+
SetDrawColor(0.75, 0.75, 0.75)
517+
DrawString(x + width - 4, y, "RIGHT_X", height - 2, "VAR", "Sorting " .. self.sortPercentage)
518+
end
449519
local cursorX, cursorY = GetCursorPos()
450520
self.hoverSel = mOverComp == "DROP" and math.floor((cursorY - y - height + scrollBar.offset) / (height - 4)) + 1
451521
if self.hoverSel and not self.gems[self.list[self.hoverSel]] then
@@ -475,10 +545,10 @@ function GemSelectClass:Draw(viewPort, noTooltip)
475545
local gemText = gemData and gemData.name or "<No matches>"
476546
DrawString(0, y, "LEFT", height - 4, "VAR", gemText)
477547
if gemData then
478-
if gemData.grantedEffect.support and self.sortCache.canSupport[gemId] then
548+
if gemData.grantedEffect.support and self.sortCache.canSupport[gemId] and self.sortCache.dpsColor[gemId] ~= "" then
479549
SetDrawColor(self.sortCache.dpsColor[gemId])
480550
main:DrawCheckMark(width - 4 - height / 2 - (scrollBar.enabled and 18 or 0), y + (height - 4) / 2, (height - 4) * 0.8)
481-
elseif gemData.grantedEffect.hasGlobalEffect then
551+
elseif gemData.grantedEffect.hasGlobalEffect and self.sortCache.dpsColor[gemId] ~= "" then
482552
SetDrawColor(self.sortCache.dpsColor[gemId])
483553
DrawString(width - 4 - height / 2 - (scrollBar.enabled and 18 or 0), y - 2, "CENTER_X", height, "VAR", "+")
484554
end
@@ -487,14 +557,23 @@ function GemSelectClass:Draw(viewPort, noTooltip)
487557
SetViewport()
488558
self:DrawControls(viewPort, (noTooltip and not self.forceTooltip) and self)
489559
if self.hoverSel then
490-
local calcFunc, calcBase = self.skillsTab.build.calcsTab:GetMiscCalculator(self.build)
491-
if calcFunc then
492-
self.tooltip:Clear()
493-
local gemData = self.gems[self.list[self.hoverSel]]
494-
local output= self:CalcOutputWithThisGem(calcFunc, gemData, self.skillsTab.sortGemsByDPSField == "FullDPS")
495-
local gemInstance = {
560+
-- Debounce hover
561+
if self.hoverSel == self.lastHoverSel then
562+
self.hoverFrameCount = (self.hoverFrameCount or 0) + 1
563+
else
564+
self.lastHoverSel = self.hoverSel
565+
self.hoverFrameCount = 0
566+
end
567+
if self.hoverFrameCount >= 2 then
568+
local calcFunc, calcBase = self.skillsTab.build.calcsTab:GetMiscCalculator(self.build)
569+
if calcFunc then
570+
self.tooltip:Clear()
571+
local gemData = self.gems[self.list[self.hoverSel]]
572+
local output= self:CalcOutputWithThisGem(calcFunc, gemData, self.skillsTab.sortGemsByDPSField == "FullDPS")
573+
local gemInstance = {
496574
level = self.skillsTab:ProcessGemLevel(gemData, self.imbuedSelect),
497575
quality = self.skillsTab.defaultGemQuality or 0,
576+
qualityId = "Default",
498577
count = 1,
499578
enabled = true,
500579
enableGlobal1 = true,
@@ -505,11 +584,15 @@ function GemSelectClass:Draw(viewPort, noTooltip)
505584
displayEffect = nil,
506585
gemData = gemData
507586
}
508-
self:AddGemTooltip(gemInstance)
509-
self.tooltip:AddSeparator(10)
510-
self.skillsTab.build:AddStatComparesToTooltip(self.tooltip, calcBase, output, "^7Selecting this gem will give you:")
511-
self.tooltip:Draw(x, y + height + 2 + (self.hoverSel - 1) * (height - 4) - scrollBar.offset, width, height - 4, viewPort)
587+
self:AddGemTooltip(gemInstance)
588+
self.tooltip:AddSeparator(10)
589+
self.skillsTab.build:AddStatComparesToTooltip(self.tooltip, calcBase, output, "^7Selecting this gem will give you:")
590+
self.tooltip:Draw(x, y + height + 2 + (self.hoverSel - 1) * (height - 4) - scrollBar.offset, width, height - 4, viewPort)
591+
end
512592
end
593+
else
594+
self.lastHoverSel = nil
595+
self.hoverFrameCount = 0
513596
end
514597
SetDrawLayer(nil, 0)
515598
else

0 commit comments

Comments
 (0)