From ffc04b24a9dab5e52cb8359da861b431dda1a01d Mon Sep 17 00:00:00 2001 From: mcagnion Date: Tue, 26 May 2026 07:44:31 +0000 Subject: [PATCH 1/2] Apply changes from https://github.com/PathOfBuildingCommunity/PathOfBuilding/pull/9848 --- src/Classes/ItemsTab.lua | 26 +- src/Classes/ItemsTab.lua.rej | 566 +++++++++++++++++++++++++++++++++++ 2 files changed, 567 insertions(+), 25 deletions(-) create mode 100644 src/Classes/ItemsTab.lua.rej diff --git a/src/Classes/ItemsTab.lua b/src/Classes/ItemsTab.lua index f0c43b6b4f..420ba76586 100644 --- a/src/Classes/ItemsTab.lua +++ b/src/Classes/ItemsTab.lua @@ -2668,14 +2668,7 @@ function ItemsTabClass:AddCustomModifierToDisplayItem() local controls = { } local sourceList = { } local modList = { } - local sortList = { { label = "Default", stat = nil } } - local sortTransforms = { } - for _, entry in ipairs(data.powerStatList) do - if entry.stat and not entry.ignoreForNodes then - t_insert(sortList, { label = entry.label, stat = entry.stat }) - sortTransforms[entry.stat] = entry.transform - end - end + local sortList, sortTransforms = buildModSortList() local function setDefaultSortOrder() for index, listMod in ipairs(modList) do listMod.defaultSortOrder = index @@ -2683,23 +2676,6 @@ function ItemsTabClass:AddCustomModifierToDisplayItem() listMod.sortValues = nil end end - local function getOutputStatValue(output, stat) - if stat == "FullDPS" then - if output[stat] ~= nil then - return output[stat] - end - if output.Minion and output.Minion.CombinedDPS ~= nil then - return output.Minion.CombinedDPS - end - end - if output.Minion and output.Minion[stat] ~= nil then - return output.Minion[stat] - end - if output[stat] ~= nil then - return output[stat] - end - return 0 - end local function getSortValue(listMod, stat, calcFunc, slotName, useFullDPS) listMod.sortValues = listMod.sortValues or { } if listMod.sortValues[stat] ~= nil then diff --git a/src/Classes/ItemsTab.lua.rej b/src/Classes/ItemsTab.lua.rej new file mode 100644 index 0000000000..bf17e78380 --- /dev/null +++ b/src/Classes/ItemsTab.lua.rej @@ -0,0 +1,566 @@ +diff a/src/Classes/ItemsTab.lua b/src/Classes/ItemsTab.lua (rejected hunks) +@@ -62,6 +62,36 @@ local function isAnointable(item) + return (item.canBeAnointed or item.base.type == "Amulet") + end + ++local function buildModSortList() ++ local sortList = { { label = "Default", stat = nil } } ++ local sortTransforms = { } ++ for _, entry in ipairs(data.powerStatList) do ++ if entry.stat and not entry.ignoreForNodes then ++ t_insert(sortList, { label = entry.label, stat = entry.stat }) ++ sortTransforms[entry.stat] = entry.transform ++ end ++ end ++ return sortList, sortTransforms ++end ++ ++local function getOutputStatValue(output, stat) ++ if stat == "FullDPS" then ++ if output[stat] ~= nil then ++ return output[stat] ++ end ++ if output.Minion and output.Minion.CombinedDPS ~= nil then ++ return output.Minion.CombinedDPS ++ end ++ end ++ if output.Minion and output.Minion[stat] ~= nil then ++ return output.Minion[stat] ++ end ++ if output[stat] ~= nil then ++ return output[stat] ++ end ++ return 0 ++end ++ + local ItemsTabClass = newClass("ItemsTab", "UndoHandler", "ControlHost", "Control", function(self, build) + self.UndoHandler() + self.ControlHost() +@@ -2278,12 +2308,21 @@ function ItemsTabClass:EnchantDisplayItem(enchantSlot) + end + end + local enchantmentList = { } ++ local sortList, sortTransforms = buildModSortList() ++ local function setDefaultSortOrder() ++ for index, entry in ipairs(enchantmentList) do ++ entry.defaultSortOrder = index ++ entry.sortValue = nil ++ entry.sortValues = nil ++ end ++ end + local function buildEnchantmentList() + wipeTable(enchantmentList) + local list = haveSkills and enchantments[skillList[controls.skill and controls.skill.selIndex or 1]] or enchantments + for _, enchantment in ipairs(list[enchantmentSourceList[controls.enchantmentSource and controls.enchantmentSource.selIndex or 1].name]) do +- t_insert(enchantmentList, enchantment) ++ t_insert(enchantmentList, { label = enchantment, line = enchantment }) + end ++ setDefaultSortOrder() + end + if haveSkills then + buildSkillList(true) +@@ -2294,12 +2333,37 @@ function ItemsTabClass:EnchantDisplayItem(enchantSlot) + local item = new("Item", self.displayItem:BuildRaw()) + local index = idx or controls.enchantment.selIndex + item.id = self.displayItem.id +- local list = haveSkills and enchantments[controls.skill.list[controls.skill.selIndex]] or enchantments +- local line = list[controls.enchantmentSource.list[controls.enchantmentSource.selIndex].name][index] +- local first, second = line:match("([^/]+)/([^/]+)") ++ local entry = enchantmentList[index] + if remove then + t_remove(item.enchantModLines, self.enchantSlot) +- elseif first then ++ elseif entry then ++ local line = entry.line ++ local first, second = line:match("([^/]+)/([^/]+)") ++ if first then ++ item.enchantModLines = { { crafted = true, line = first }, { crafted = true, line = second } } ++ else ++ if not item.canHaveTwoEnchants and #item.enchantModLines > 1 then ++ item.enchantModLines = { item.enchantModLines[1] } ++ end ++ if #item.enchantModLines >= self.enchantSlot then ++ t_remove(item.enchantModLines, self.enchantSlot) ++ end ++ t_insert(item.enchantModLines, self.enchantSlot, { crafted = true, line = line }) ++ end ++ end ++ item:BuildAndParseRaw() ++ return item ++ end ++ local function getSortValue(entry, stat, calcFunc, slotName, useFullDPS) ++ entry.sortValues = entry.sortValues or { } ++ if entry.sortValues[stat] ~= nil then ++ return entry.sortValues[stat] ++ end ++ local item = new("Item", self.displayItem:BuildRaw()) ++ item.id = self.displayItem.id ++ local line = entry.line ++ local first, second = line:match("([^/]+)/([^/]+)") ++ if first then + item.enchantModLines = { { crafted = true, line = first }, { crafted = true, line = second } } + else + if not item.canHaveTwoEnchants and #item.enchantModLines > 1 then +@@ -2308,10 +2372,51 @@ function ItemsTabClass:EnchantDisplayItem(enchantSlot) + if #item.enchantModLines >= self.enchantSlot then + t_remove(item.enchantModLines, self.enchantSlot) + end +- t_insert(item.enchantModLines, self.enchantSlot, { crafted = true, line = line}) ++ t_insert(item.enchantModLines, self.enchantSlot, { crafted = true, line = line }) + end + item:BuildAndParseRaw() +- return item ++ local output = calcFunc({ repSlotName = slotName, repItem = item }, useFullDPS) ++ local value = getOutputStatValue(output, stat) ++ if sortTransforms[stat] then ++ value = sortTransforms[stat](value) ++ end ++ entry.sortValues[stat] = value ++ return value ++ end ++ local function applySort(stat, selectFirst) ++ if not controls.enchantment or not controls.enchantment:IsShown() then ++ return ++ end ++ local selected = not selectFirst and enchantmentList[controls.enchantment.selIndex] or nil ++ if stat then ++ local slotName = self.displayItem:GetPrimarySlot() ++ local calcFunc = self.build.calcsTab:GetMiscCalculator() ++ local useFullDPS = stat == "FullDPS" ++ for _, entry in ipairs(enchantmentList) do ++ entry.sortValue = getSortValue(entry, stat, calcFunc, slotName, useFullDPS) ++ end ++ table.sort(enchantmentList, function(a, b) ++ if a.sortValue ~= b.sortValue then ++ return a.sortValue > b.sortValue ++ end ++ return (a.defaultSortOrder or 0) < (b.defaultSortOrder or 0) ++ end) ++ else ++ table.sort(enchantmentList, function(a, b) ++ return (a.defaultSortOrder or 0) < (b.defaultSortOrder or 0) ++ end) ++ end ++ controls.enchantment:UpdateSearch() ++ if selected then ++ for index, entry in ipairs(enchantmentList) do ++ if entry == selected then ++ controls.enchantment.selIndex = index ++ break ++ end ++ end ++ else ++ controls.enchantment:SetSel(1, true) ++ end + end + if haveSkills then + controls.skillLabel = new("LabelControl", {"TOPRIGHT",nil,"TOPLEFT"}, {95, 20, 0, 16}, "^7Skill:") +@@ -2319,12 +2424,18 @@ function ItemsTabClass:EnchantDisplayItem(enchantSlot) + buildEnchantmentSourceList() + buildEnchantmentList() + controls.enchantment:SetSel(1) ++ if controls.sort then ++ applySort(controls.sort.list[controls.sort.selIndex].stat, true) ++ end + end) + controls.allSkills = new("CheckBoxControl", {"TOPLEFT",nil,"TOPLEFT"}, {350, 20, 18}, "All skills:", function(state) + buildSkillList(not state) + controls.skill:SetSel(1) + buildEnchantmentList() + controls.enchantment:SetSel(1) ++ if controls.sort then ++ applySort(controls.sort.list[controls.sort.selIndex].stat, true) ++ end + end) + controls.allSkills.tooltipText = "Show all skills, not just those used by this build." + if not next(skillsUsed) then +@@ -2336,9 +2447,16 @@ function ItemsTabClass:EnchantDisplayItem(enchantSlot) + controls.enchantmentSource = new("DropDownControl", {"TOPLEFT",nil,"TOPLEFT"}, {100, 45, 180, 18}, enchantmentSourceList, function(index, value) + buildEnchantmentList() + controls.enchantment:SetSel(m_min(controls.enchantment.selIndex, #enchantmentList)) ++ if controls.sort then ++ applySort(controls.sort.list[controls.sort.selIndex].stat, true) ++ end ++ end) ++ controls.sortLabel = new("LabelControl", {"TOPRIGHT",nil,"TOPLEFT"}, {350, 45, 0, 16}, "^7Sort by:") ++ controls.sort = new("DropDownControl", {"TOPLEFT",nil,"TOPLEFT"}, {355, 45, 240, 18}, sortList, function(index, value) ++ applySort(value.stat, true) + end) + controls.enchantmentLabel = new("LabelControl", {"TOPRIGHT",nil,"TOPLEFT"}, {95, 70, 0, 16}, "^7Enchantment:") +- controls.enchantment = new("DropDownControl", {"TOPLEFT",nil,"TOPLEFT"}, {100, 70, 440, 18}, enchantmentList) ++ controls.enchantment = new("DropDownControl", {"TOPLEFT",nil,"TOPLEFT"}, {100, 70, 495, 18}, enchantmentList) + controls.enchantment.tooltipFunc = function(tooltip, mode, index) + tooltip:Clear() + self:AddItemTooltip(tooltip, enchantItem(index), nil, true) +@@ -2354,7 +2472,7 @@ function ItemsTabClass:EnchantDisplayItem(enchantSlot) + controls.close = new("ButtonControl", nil, {88, 100, 80, 20}, "Cancel", function() + main:ClosePopup() + end) +- main:OpenPopup(550, 130, "Enchant Item", controls) ++ main:OpenPopup(605, 130, "Enchant Item", controls) + end + + ---Gets the name of the anointed node on an item +@@ -2505,9 +2623,10 @@ end + -- Opens the item corrupting popup + function ItemsTabClass:CorruptDisplayItem(modType) + local currentModType = modType or "Corrupted" +- local controls = { } ++ local controls = { } + local implicitList = { } + local sourceList = { "Corrupted", "Scourge" } ++ local sortList, sortTransforms = buildModSortList() + local function buildImplicitList(modType) + if implicitList[modType] then + return +@@ -2515,18 +2634,21 @@ function ItemsTabClass:CorruptDisplayItem(modType) + implicitList[modType] = {} + for modId, mod in pairs(self.displayItem.affixes) do + if mod.type == modType and self.displayItem:GetModSpawnWeight(mod) > 0 then +- t_insert(implicitList[modType], mod) ++ t_insert(implicitList[modType], { mod = mod }) + end + end + table.sort(implicitList[modType], function(a, b) +- local an = a[1]:lower():gsub("%(.-%)","$"):gsub("[%+%-%%]",""):gsub("%d+","$") +- local bn = b[1]:lower():gsub("%(.-%)","$"):gsub("[%+%-%%]",""):gsub("%d+","$") ++ local an = a.mod[1]:lower():gsub("%(.-%)","$"):gsub("[%+%-%%]",""):gsub("%d+","$") ++ local bn = b.mod[1]:lower():gsub("%(.-%)","$"):gsub("[%+%-%%]",""):gsub("%d+","$") + if an ~= bn then + return an < bn + else +- return a.level < b.level ++ return a.mod.level < b.mod.level + end + end) ++ for index, entry in ipairs(implicitList[modType]) do ++ entry.defaultSortOrder = index ++ end + end + buildImplicitList(currentModType) + local function buildList(control, other, modType) +@@ -2534,13 +2656,89 @@ function ItemsTabClass:CorruptDisplayItem(modType) + local otherMod = other and other.selIndex and other.selIndex > 1 and other.list[other.selIndex].mod + wipeTable(control.list) + t_insert(control.list, { label = "None" }) +- for _, mod in ipairs(implicitList[modType]) do ++ for _, entry in ipairs(implicitList[modType]) do ++ local mod = entry.mod + if not otherMod or mod.group ~= otherMod.group then + t_insert(control.list, { label = table.concat(mod, "/"), mod = mod }) + end + end + control:SelByValue(selfMod, "mod") + end ++ local function getSortValue(entry, modType, stat, calcFunc, slotName, useFullDPS) ++ entry.sortValues = entry.sortValues or { } ++ if entry.sortValues[stat] ~= nil then ++ return entry.sortValues[stat] ++ end ++ local item = new("Item", self.displayItem:BuildRaw()) ++ item.id = self.displayItem.id ++ item.corrupted = true ++ local mod = entry.mod ++ local targetLines = modType == "Corrupted" and item.implicitModLines or item.scourgeModLines ++ wipeTable(targetLines) ++ for _, modLine in ipairs(mod) do ++ modLine = (currentModType == "ScourgeUpside" and "{scourge}" or "") .. modLine ++ if mod.modTags[1] then ++ t_insert(targetLines, { line = "{tags:" .. table.concat(mod.modTags, ",") .. "}" .. modLine }) ++ else ++ t_insert(targetLines, { line = modLine }) ++ end ++ end ++ item:BuildAndParseRaw() ++ local output = calcFunc({ repSlotName = slotName, repItem = item }, useFullDPS) ++ local value = getOutputStatValue(output, stat) ++ if sortTransforms[stat] then ++ value = sortTransforms[stat](value) ++ end ++ entry.sortValues[stat] = value ++ return value ++ end ++ local function sortModType(modType, stat, calcFunc, slotName, useFullDPS) ++ if not implicitList[modType] then ++ return ++ end ++ if stat then ++ for _, entry in ipairs(implicitList[modType]) do ++ entry.sortValue = getSortValue(entry, modType, stat, calcFunc, slotName, useFullDPS) ++ end ++ table.sort(implicitList[modType], function(a, b) ++ if a.sortValue ~= b.sortValue then ++ return a.sortValue > b.sortValue ++ end ++ return (a.defaultSortOrder or 0) < (b.defaultSortOrder or 0) ++ end) ++ else ++ table.sort(implicitList[modType], function(a, b) ++ return (a.defaultSortOrder or 0) < (b.defaultSortOrder or 0) ++ end) ++ end ++ end ++ local function applySort(stat) ++ if not controls.implicit then ++ return ++ end ++ local slotName = self.displayItem:GetPrimarySlot() ++ local calcFunc = stat and self.build.calcsTab:GetMiscCalculator() or nil ++ local useFullDPS = stat == "FullDPS" ++ if currentModType == "Corrupted" then ++ sortModType("Corrupted", stat, calcFunc, slotName, useFullDPS) ++ else ++ sortModType("ScourgeUpside", stat, calcFunc, slotName, useFullDPS) ++ sortModType("ScourgeDownside", stat, calcFunc, slotName, useFullDPS) ++ end ++ if currentModType == "Corrupted" then ++ buildList(controls.implicit, controls.implicit2, currentModType) ++ buildList(controls.implicit2, controls.implicit, currentModType) ++ else ++ buildList(controls.implicit, controls.implicit2, "ScourgeUpside") ++ buildList(controls.implicit2, controls.implicit, "ScourgeUpside") ++ buildList(controls.implicit3, controls.implicit4, "ScourgeDownside") ++ buildList(controls.implicit4, controls.implicit3, "ScourgeDownside") ++ end ++ controls.implicit:UpdateSearch() ++ controls.implicit2:UpdateSearch() ++ if controls.implicit3 then controls.implicit3:UpdateSearch() end ++ if controls.implicit4 then controls.implicit4:UpdateSearch() end ++ end + local function corruptItem() + local item = new("Item", self.displayItem:BuildRaw()) + item.id = self.displayItem.id +@@ -2610,14 +2808,22 @@ function ItemsTabClass:CorruptDisplayItem(modType) + controls.close.y = 99 + controls.save.y = 99 + end +- buildList(controls.implicit, controls.implicit2, currentModType) +- buildList(controls.implicit2, controls.implicit, currentModType) ++ if controls.sort then ++ applySort(controls.sort.list[controls.sort.selIndex].stat) ++ else ++ buildList(controls.implicit, controls.implicit2, currentModType) ++ buildList(controls.implicit2, controls.implicit, currentModType) ++ end + controls.implicit:SetSel(1) + controls.implicit2:SetSel(1) + controls.implicit3:SetSel(1) + controls.implicit4:SetSel(1) + end) + controls.source.enabled = #sourceList > 1 ++ controls.sortLabel = new("LabelControl", {"TOPRIGHT",nil,"TOPLEFT"}, {350, 20, 0, 16}, "^7Sort by:") ++ controls.sort = new("DropDownControl", {"TOPLEFT",nil,"TOPLEFT"}, {355, 20, 240, 18}, sortList, function(index, value) ++ applySort(value.stat) ++ end) + controls.implicitLabel = new("LabelControl", {"TOPRIGHT",nil,"TOPLEFT"}, {75, 45, 0, 16}, "^7Implicit #1:") + controls.implicit = new("DropDownControl", {"TOPLEFT",nil,"TOPLEFT"}, {80, 45, 440, 18}, nil, function() + buildList(controls.implicit2, controls.implicit, currentModType) +@@ -2693,7 +2899,7 @@ function ItemsTabClass:CorruptDisplayItem(modType) + controls.close = new("ButtonControl", nil, {45, 99, 80, 20}, "Cancel", function() + main:ClosePopup() + end) +- main:OpenPopup(540, 129, modType .. " Item", controls) ++ main:OpenPopup(605, 129, modType .. " Item", controls) + end + + -- Opens the custom modifier popup +@@ -3166,6 +3348,20 @@ function ItemsTabClass:AddImplicitToDisplayItem() + local sourceList = { } + local modList = { } + local modGroups = {} ++ local sortList, sortTransforms = buildModSortList() ++ local function setDefaultSortOrder() ++ for groupIndex, group in ipairs(modGroups) do ++ group.defaultSortOrder = groupIndex ++ group.sortValue = nil ++ end ++ for _, listMods in ipairs(modList) do ++ for index, listMod in ipairs(listMods) do ++ listMod.defaultSortOrder = index ++ listMod.sortValue = nil ++ listMod.sortValues = nil ++ end ++ end ++ end + ---Mutates modList to contain mods from the specified source + ---@param sourceId string @The crafting source id to build the list of mods for + local function buildMods(sourceId) +@@ -3291,6 +3487,7 @@ function ItemsTabClass:AddImplicitToDisplayItem() + end) + end + end ++ setDefaultSortOrder() + end + if (self.displayItem.rarity ~= "UNIQUE" and self.displayItem.rarity ~= "RELIC") and (self.displayItem.type == "Helmet" or self.displayItem.type == "Body Armour" or self.displayItem.type == "Gloves" or self.displayItem.type == "Boots") then + if self.displayItem.cleansing then +@@ -3306,21 +3503,11 @@ function ItemsTabClass:AddImplicitToDisplayItem() + end + t_insert(sourceList, { label = "Custom", sourceId = "CUSTOM" }) + buildMods(sourceList[1].sourceId) +- local function addModifier() +- local item = new("Item", self.displayItem:BuildRaw()) +- item.id = self.displayItem.id +- local sourceId = sourceList[controls.source.selIndex].sourceId +- if sourceId == "CUSTOM" then +- if controls.custom.buf:match("%S") then +- t_insert(item.implicitModLines, { line = controls.custom.buf, custom = true }) +- end +- elseif sourceId == "SYNTHESIS" then +- local listMod = modList[controls.modSelect.selIndex] +- for _, line in ipairs(listMod.mod) do +- t_insert(item.implicitModLines, { line = line, modTags = listMod.mod.modTags, [listMod.type] = true }) +- end +- elseif sourceId == "EXARCH" or sourceId == "EATER" then +- local listMod = modList[modGroups[controls.modGroupSelect.selIndex].modListIndex][controls.modSelect.selIndex] ++ ---Applies a candidate listMod to the item, mirroring the mutation addModifier() ++ ---performs at click-time. Eldritch (exarch/eater) sources replace an existing ++ ---implicit of the same type when present; other sources append. ++ local function applyCandidateMod(item, listMod) ++ if listMod.type == "exarch" or listMod.type == "eater" then + local index + for i, implicitMod in ipairs(item.implicitModLines) do + if implicitMod[listMod.type] then +@@ -3332,16 +3519,112 @@ function ItemsTabClass:AddImplicitToDisplayItem() + for i, line in ipairs(listMod.mod) do + item.implicitModLines[index + i - 1] = { line = line, modTags = listMod.mod.modTags, [listMod.type] = true } + end +- else +- for _, line in ipairs(listMod.mod) do +- t_insert(item.implicitModLines, { line = line, modTags = listMod.mod.modTags, [listMod.type] = true }) ++ return ++ end ++ end ++ for _, line in ipairs(listMod.mod) do ++ t_insert(item.implicitModLines, { line = line, modTags = listMod.mod.modTags, [listMod.type] = true }) ++ end ++ end ++ local function getSortValue(listMod, stat, calcFunc, slotName, useFullDPS) ++ listMod.sortValues = listMod.sortValues or { } ++ if listMod.sortValues[stat] ~= nil then ++ return listMod.sortValues[stat] ++ end ++ local item = new("Item", self.displayItem:BuildRaw()) ++ item.id = self.displayItem.id ++ applyCandidateMod(item, listMod) ++ item:BuildAndParseRaw() ++ local output = calcFunc({ repSlotName = slotName, repItem = item }, useFullDPS) ++ local value = getOutputStatValue(output, stat) ++ if sortTransforms[stat] then ++ value = sortTransforms[stat](value) ++ end ++ listMod.sortValues[stat] = value ++ return value ++ end ++ local function applySort(stat, selectFirst) ++ if not controls.modSelect or not controls.modGroupSelect or not controls.modSelect:IsShown() then ++ return ++ end ++ local selectedGroup = not selectFirst and modGroups[controls.modGroupSelect.selIndex] or nil ++ local selectedMod = not selectFirst and controls.modSelect.list and controls.modSelect.list[controls.modSelect.selIndex] or nil ++ if stat then ++ local slotName = self.displayItem:GetPrimarySlot() ++ local calcFunc = self.build.calcsTab:GetMiscCalculator() ++ local useFullDPS = stat == "FullDPS" ++ for _, listMods in ipairs(modList) do ++ for _, listMod in ipairs(listMods) do ++ listMod.sortValue = getSortValue(listMod, stat, calcFunc, slotName, useFullDPS) + end ++ table.sort(listMods, function(a, b) ++ if a.sortValue ~= b.sortValue then ++ return a.sortValue > b.sortValue ++ end ++ return (a.defaultSortOrder or 0) < (b.defaultSortOrder or 0) ++ end) ++ end ++ for _, group in ipairs(modGroups) do ++ local best ++ for _, listMod in ipairs(modList[group.modListIndex] or { }) do ++ if not best or listMod.sortValue > best then ++ best = listMod.sortValue ++ end ++ end ++ group.sortValue = best or 0 + end ++ table.sort(modGroups, function(a, b) ++ if a.sortValue ~= b.sortValue then ++ return a.sortValue > b.sortValue ++ end ++ return (a.defaultSortOrder or 0) < (b.defaultSortOrder or 0) ++ end) + else +- local listMod = modList[modGroups[controls.modGroupSelect.selIndex].modListIndex][controls.modSelect.selIndex] +- for _, line in ipairs(listMod.mod) do +- t_insert(item.implicitModLines, { line = line, modTags = listMod.mod.modTags, [listMod.type] = true }) ++ for _, listMods in ipairs(modList) do ++ table.sort(listMods, function(a, b) ++ return (a.defaultSortOrder or 0) < (b.defaultSortOrder or 0) ++ end) + end ++ table.sort(modGroups, function(a, b) ++ return (a.defaultSortOrder or 0) < (b.defaultSortOrder or 0) ++ end) ++ end ++ controls.modGroupSelect:UpdateSearch() ++ if selectedGroup then ++ for index, group in ipairs(modGroups) do ++ if group == selectedGroup then ++ controls.modGroupSelect.selIndex = index ++ break ++ end ++ end ++ else ++ controls.modGroupSelect:SetSel(1, true) ++ end ++ controls.modSelect.list = modList[modGroups[controls.modGroupSelect.selIndex].modListIndex] ++ controls.modSelect:UpdateSearch() ++ if selectedMod then ++ for index, listMod in ipairs(controls.modSelect.list) do ++ if listMod == selectedMod then ++ controls.modSelect.selIndex = index ++ break ++ end ++ end ++ else ++ controls.modSelect:SetSel(1, true) ++ end ++ end ++ local function addModifier() ++ local item = new("Item", self.displayItem:BuildRaw()) ++ item.id = self.displayItem.id ++ local sourceId = sourceList[controls.source.selIndex].sourceId ++ if sourceId == "CUSTOM" then ++ if controls.custom.buf:match("%S") then ++ t_insert(item.implicitModLines, { line = controls.custom.buf, custom = true }) ++ end ++ elseif sourceId == "SYNTHESIS" then ++ applyCandidateMod(item, modList[controls.modSelect.selIndex]) ++ else ++ applyCandidateMod(item, modList[modGroups[controls.modGroupSelect.selIndex].modListIndex][controls.modSelect.selIndex]) + end + item:BuildAndParseRaw() + return item +@@ -3354,11 +3637,24 @@ function ItemsTabClass:AddImplicitToDisplayItem() + controls.modGroupSelect:SetSel(1) + controls.modSelect.list = modList[modGroups[1].modListIndex] + controls.modSelect:SetSel(1) ++ if controls.sort then ++ applySort(controls.sort.list[controls.sort.selIndex].stat, true) ++ end + else + controls.modSelectLabel.y = 45 + end + end) + controls.source.enabled = #sourceList > 1 ++ controls.sortLabel = new("LabelControl", {"TOPRIGHT",nil,"TOPLEFT"}, {350, 20, 0, 16}, "^7Sort by:") ++ controls.sortLabel.shown = function() ++ return sourceList[controls.source.selIndex].sourceId ~= "CUSTOM" ++ end ++ controls.sort = new("DropDownControl", {"TOPLEFT",nil,"TOPLEFT"}, {355, 20, 240, 18}, sortList, function(index, value) ++ applySort(value.stat, true) ++ end) ++ controls.sort.shown = function() ++ return sourceList[controls.source.selIndex].sourceId ~= "CUSTOM" ++ end + controls.modGroupSelectLabel = new("LabelControl", {"TOPRIGHT",nil,"TOPLEFT"}, {95, 45, 0, 16}, "^7Type:") + controls.modGroupSelect = new("DropDownControl", {"TOPLEFT",nil,"TOPLEFT"}, {100, 45, 600, 18}, modGroups, function(index, value) + controls.modSelect.list = modList[value.modListIndex] From cbc4d72b4b425279f5d4ffaf69e26c9e7f6f45eb Mon Sep 17 00:00:00 2001 From: LocalIdentity Date: Tue, 26 May 2026 18:40:04 +1000 Subject: [PATCH 2/2] Fix issues and reuse code --- src/Classes/ItemsTab.lua | 258 +++++++++------- src/Classes/ItemsTab.lua.rej | 566 ----------------------------------- 2 files changed, 158 insertions(+), 666 deletions(-) delete mode 100644 src/Classes/ItemsTab.lua.rej diff --git a/src/Classes/ItemsTab.lua b/src/Classes/ItemsTab.lua index 420ba76586..fa523fcdab 100644 --- a/src/Classes/ItemsTab.lua +++ b/src/Classes/ItemsTab.lua @@ -58,6 +58,80 @@ local function isAnointable(item) and not item.corrupted and not item.mirrored end +local function buildModSortList() + local sortList = { { label = "Default", stat = nil } } + local sortTransforms = { } + for _, entry in ipairs(data.powerStatList) do + if entry.stat and not entry.ignoreForNodes then + t_insert(sortList, { label = entry.label, stat = entry.stat }) + sortTransforms[entry.stat] = entry.transform + end + end + return sortList, sortTransforms +end + +local function getOutputStatValue(output, stat) + if stat == "FullDPS" then + if output[stat] ~= nil then + return output[stat] + end + if output.Minion and output.Minion.CombinedDPS ~= nil then + return output.Minion.CombinedDPS + end + end + if output.Minion and output.Minion[stat] ~= nil then + return output.Minion[stat] + end + if output[stat] ~= nil then + return output[stat] + end + return 0 +end + +local function setDefaultSortOrder(modList) + for index, listMod in ipairs(modList) do + listMod.defaultSortOrder = index + listMod.sortValue = nil + listMod.sortValues = nil + end +end + +local function getSortedModValue(item, listMod, stat, sortTransforms, calcFunc, slotName, useFullDPS, addModToItem) + listMod.sortValues = listMod.sortValues or { } + if listMod.sortValues[stat] ~= nil then + return listMod.sortValues[stat] + end + local testItem = new("Item", item:BuildRaw()) + testItem.id = item.id + addModToItem(testItem, listMod) + testItem:BuildAndParseRaw() + local output = calcFunc({ repSlotName = slotName, repItem = testItem }, useFullDPS) + local value = getOutputStatValue(output, stat) + if sortTransforms[stat] then + value = sortTransforms[stat](value) + end + listMod.sortValues[stat] = value + return value +end + +local function sortModList(modList, stat, getSortValue) + if stat then + for _, listMod in ipairs(modList) do + listMod.sortValue = getSortValue(listMod) + end + table.sort(modList, function(a, b) + if a.sortValue ~= b.sortValue then + return a.sortValue > b.sortValue + end + return (a.defaultSortOrder or 0) < (b.defaultSortOrder or 0) + end) + else + table.sort(modList, function(a, b) + return (a.defaultSortOrder or 0) < (b.defaultSortOrder or 0) + end) + end +end + local ItemsTabClass = newClass("ItemsTab", "UndoHandler", "ControlHost", "Control", function(self, build) self.UndoHandler() self.ControlHost() @@ -2389,7 +2463,7 @@ end -- Opens the item corrupting popup function ItemsTabClass:CorruptDisplayItem() -- todo implement vaal orb new outcomes this code is for poe 1 - local controls = { } + local controls = { } local enchantList = { } local enchantNum = 1 local shownExplicits = {} @@ -2397,6 +2471,7 @@ function ItemsTabClass:CorruptDisplayItem() -- todo implement vaal orb new outco local corruptedRanges = {} local currentModType = "Corrupted" local sourceList = { "Corrupted" } + local sortList, sortTransforms = buildModSortList() if self.displayItem.base.type == "Helmet" then t_insert(sourceList, "Glimpse of Chaos") end @@ -2407,18 +2482,19 @@ function ItemsTabClass:CorruptDisplayItem() -- todo implement vaal orb new outco enchantList[modType] = {} for modId, mod in pairs(data.itemMods.Corruption) do if mod.type == modType and self.displayItem:GetModSpawnWeight(mod) > 0 then - t_insert(enchantList[modType], mod) + t_insert(enchantList[modType], { mod = mod }) end end table.sort(enchantList[modType], function(a, b) - local an = a[1]:lower():gsub("%(.-%)","$"):gsub("[%+%-%%]",""):gsub("%d+","$") - local bn = b[1]:lower():gsub("%(.-%)","$"):gsub("[%+%-%%]",""):gsub("%d+","$") + local an = a.mod[1]:lower():gsub("%(.-%)","$"):gsub("[%+%-%%]",""):gsub("%d+","$") + local bn = b.mod[1]:lower():gsub("%(.-%)","$"):gsub("[%+%-%%]",""):gsub("%d+","$") if an ~= bn then return an < bn else - return a.level < b.level + return a.mod.level < b.mod.level end end) + setDefaultSortOrder(enchantList[modType]) end buildEnchantList("Corrupted") buildEnchantList("SpecialCorrupted") @@ -2426,7 +2502,8 @@ function ItemsTabClass:CorruptDisplayItem() -- todo implement vaal orb new outco local selfMod = control.selIndex and control.selIndex > 1 and control.list[control.selIndex].mod wipeTable(control.list) t_insert(control.list, { label = "None" }) - for _, mod in ipairs(enchantList[modType]) do + for _, listMod in ipairs(enchantList[modType]) do + local mod = listMod.mod local alreadySelected = false for _, other in ipairs(others) do local otherMod = other and other.selIndex and other.selIndex > 1 and other.list[other.selIndex].mod @@ -2440,21 +2517,14 @@ function ItemsTabClass:CorruptDisplayItem() -- todo implement vaal orb new outco end control:SelByValue(selfMod, "mod") end - local function corruptItem() - local item = new("Item", self.displayItem:BuildRaw()) - item.id = self.displayItem.id - item.corrupted = true + local function applyCorruptionMods(item, mods) local newEnchant = { } - for i = 1, enchantNum do - local control = controls["enchant"..i] - if control.selIndex > 1 then - local mod = control.list[control.selIndex].mod - for i, modLine in ipairs(mod) do - if mod.modTags[1] then - t_insert(newEnchant, { line = "{tags:" .. table.concat(mod.modTags, ",") .. "}" .. modLine, enchant = true, order = mod.statOrder[i] }) - else - t_insert(newEnchant, { line = modLine, enchant = true, order = mod.statOrder[i] }) - end + for _, mod in ipairs(mods) do + for i, modLine in ipairs(mod) do + if mod.modTags[1] then + t_insert(newEnchant, { line = "{tags:" .. table.concat(mod.modTags, ",") .. "}" .. modLine, enchant = true, order = mod.statOrder[i] }) + else + t_insert(newEnchant, { line = modLine, enchant = true, order = mod.statOrder[i] }) end end end @@ -2468,6 +2538,52 @@ function ItemsTabClass:CorruptDisplayItem() -- todo implement vaal orb new outco t_insert( item.enchantModLines, i, enchant) end end + end + local function sortEnchantList(stat) + if stat then + local slotName = self.displayItem:GetPrimarySlot() + local calcFunc = self.build.calcsTab:GetMiscCalculator() + local useFullDPS = stat == "FullDPS" + sortModList(enchantList[currentModType], stat, function(listMod) + return getSortedModValue(self.displayItem, listMod, stat, sortTransforms, calcFunc, slotName, useFullDPS, function(item, sortedMod) + applyCorruptionMods(item, { sortedMod.mod }) + end) + end) + else + sortModList(enchantList[currentModType]) + end + end + local function rebuildEnchantControls(resetSelection) + for i = 1, 8 do + local shown = i <= enchantNum + controls["enchant"..i].shown = shown + controls["enchant"..i.."Label"].shown = shown + if shown then + if resetSelection then + controls["enchant"..i]:SetSel(1) + end + local others = { } + for j = 1, enchantNum do + if i ~= j then + t_insert(others, controls["enchant"..j]) + end + end + buildList(controls["enchant"..i], others, currentModType) + end + end + end + local function corruptItem() + local item = new("Item", self.displayItem:BuildRaw()) + item.id = self.displayItem.id + item.corrupted = true + local mods = { } + for i = 1, enchantNum do + local control = controls["enchant"..i] + if control.selIndex > 1 then + t_insert(mods, control.list[control.selIndex].mod) + end + end + applyCorruptionMods(item, mods) for i, modLine in ipairs(item.explicitModLines) do if corruptedRanges[i] ~= 1 then modLine.corruptedRange = corruptedRanges[i] end end @@ -2538,6 +2654,8 @@ function ItemsTabClass:CorruptDisplayItem() -- todo implement vaal orb new outco end controls.source.shown = true controls.sourceLabel.shown = true + controls.sort.shown = true + controls.sortLabel.shown = true main.popups[1].height = 103 + 20 * enchantNum controls.close.y = 73 + 20 * enchantNum controls.save.y = 73 + 20 * enchantNum @@ -2557,6 +2675,8 @@ function ItemsTabClass:CorruptDisplayItem() -- todo implement vaal orb new outco end controls.source.shown = false controls.sourceLabel.shown = false + controls.sort.shown = false + controls.sortLabel.shown = false main.popups[1].height = 55 + explicitOffset controls.close.y = 25 + explicitOffset controls.save.y = 25 + explicitOffset @@ -2578,27 +2698,19 @@ function ItemsTabClass:CorruptDisplayItem() -- todo implement vaal orb new outco end end - for i = 1, 8 do - if i <= enchantNum then - controls["enchant"..i].shown = true - controls["enchant"..i.."Label"].shown = true - local others = { } - for j = 1, enchantNum do - if i ~= j then - t_insert(others, controls["enchant"..j]) - end - end - buildList(controls["enchant"..i], others, currentModType) - else - controls["enchant"..i].shown = false - controls["enchant"..i.."Label"].shown = false - end - controls["enchant"..i]:SetSel(1) + if controls.sort then + sortEnchantList(controls.sort.list[controls.sort.selIndex].stat) end + rebuildEnchantControls(true) main.popups[1].height = 103 + 20 * enchantNum controls.close.y = 73 + 20 * enchantNum controls.save.y = 73 + 20 * enchantNum end) + controls.sortLabel = new("LabelControl", {"TOPRIGHT",nil,"TOPLEFT"}, {350, 30, 0, 16}, "^7Sort by:") + controls.sort = new("DropDownControl", {"TOPLEFT",nil,"TOPLEFT"}, {355, 30, 240, 18}, sortList, function(index, value) + sortEnchantList(value.stat) + rebuildEnchantControls() + end) for i = 1, 8 do if i == 1 then controls.enchant1Label = new("LabelControl", {"TOPRIGHT",nil,"TOPLEFT"}, {95, 55, 0, 16}, function() @@ -2612,15 +2724,7 @@ function ItemsTabClass:CorruptDisplayItem() -- todo implement vaal orb new outco controls["enchant"..i.."Label"] = new("LabelControl", {"TOPRIGHT",nil,"TOPLEFT"}, {95, 35 + i * 20 , 0, 16}, "^7Enchant #"..i..":") end controls["enchant"..i] = new("DropDownControl", {"TOPLEFT",nil,"TOPLEFT"}, {100, 35 + i * 20, 440, 18}, nil, function() - for i = 1, enchantNum do - local others = { } - for j = 1, enchantNum do - if i ~= j then - t_insert(others, controls["enchant"..j]) - end - end - buildList(controls["enchant"..i], others, currentModType) - end + rebuildEnchantControls() end) controls["enchant"..i].tooltipFunc = function(tooltip, mode, index, value) tooltip:Clear() @@ -2631,24 +2735,8 @@ function ItemsTabClass:CorruptDisplayItem() -- todo implement vaal orb new outco self:AddModComparisonTooltip(tooltip, value.mod) end end - if i == 1 then - controls["enchant"..i].shown = true - controls["enchant"..i.."Label"].shown = true - else - controls["enchant"..i].shown = false - controls["enchant"..i.."Label"].shown = false - end - - if i <= enchantNum then - local others = { } - for j = 1, enchantNum do - if i ~= j then - t_insert(others, controls["enchant"..j]) - end - end - buildList(controls["enchant"..i], others, currentModType) - end end + rebuildEnchantControls() controls.save = new("ButtonControl", nil, {-45, 69 + enchantNum * 20, 80, 20}, "Corrupted", function() self:SetDisplayItem(corruptItem()) main:ClosePopup() @@ -2669,32 +2757,6 @@ function ItemsTabClass:AddCustomModifierToDisplayItem() local sourceList = { } local modList = { } local sortList, sortTransforms = buildModSortList() - local function setDefaultSortOrder() - for index, listMod in ipairs(modList) do - listMod.defaultSortOrder = index - listMod.sortValue = nil - listMod.sortValues = nil - end - end - local function getSortValue(listMod, stat, calcFunc, slotName, useFullDPS) - listMod.sortValues = listMod.sortValues or { } - if listMod.sortValues[stat] ~= nil then - return listMod.sortValues[stat] - end - local item = new("Item", self.displayItem:BuildRaw()) - item.id = self.displayItem.id - for _, line in ipairs(listMod.mod) do - t_insert(item.explicitModLines, { line = checkLineForAllocates(line, self.build.spec.nodes), modTags = listMod.mod.modTags, [listMod.type] = true }) - end - item:BuildAndParseRaw() - local output = calcFunc({ repSlotName = slotName, repItem = item }, useFullDPS) - local value = getOutputStatValue(output, stat) - if sortTransforms[stat] then - value = sortTransforms[stat](value) - end - listMod.sortValues[stat] = value - return value - end local function applySort(stat, selectFirst) if not controls.modSelect or not controls.modSelect:IsShown() then return @@ -2704,19 +2766,15 @@ function ItemsTabClass:AddCustomModifierToDisplayItem() local slotName = self.displayItem:GetPrimarySlot() local calcFunc = self.build.calcsTab:GetMiscCalculator() local useFullDPS = stat == "FullDPS" - for _, listMod in ipairs(modList) do - listMod.sortValue = getSortValue(listMod, stat, calcFunc, slotName, useFullDPS) - end - table.sort(modList, function(a, b) - if a.sortValue ~= b.sortValue then - return a.sortValue > b.sortValue - end - return (a.defaultSortOrder or 0) < (b.defaultSortOrder or 0) + sortModList(modList, stat, function(listMod) + return getSortedModValue(self.displayItem, listMod, stat, sortTransforms, calcFunc, slotName, useFullDPS, function(item, sortedMod) + for _, line in ipairs(sortedMod.mod) do + t_insert(item.explicitModLines, { line = checkLineForAllocates(line, self.build.spec.nodes), modTags = sortedMod.mod.modTags, [sortedMod.type] = true }) + end + end) end) else - table.sort(modList, function(a, b) - return (a.defaultSortOrder or 0) < (b.defaultSortOrder or 0) - end) + sortModList(modList) end controls.modSelect:UpdateSearch() if selected then @@ -2781,7 +2839,7 @@ function ItemsTabClass:AddCustomModifierToDisplayItem() end end) end - setDefaultSortOrder() + setDefaultSortOrder(modList) end if not self.displayItem.crafted then t_insert(sourceList, { label = "Prefix", sourceId = "PREFIX" }) diff --git a/src/Classes/ItemsTab.lua.rej b/src/Classes/ItemsTab.lua.rej deleted file mode 100644 index bf17e78380..0000000000 --- a/src/Classes/ItemsTab.lua.rej +++ /dev/null @@ -1,566 +0,0 @@ -diff a/src/Classes/ItemsTab.lua b/src/Classes/ItemsTab.lua (rejected hunks) -@@ -62,6 +62,36 @@ local function isAnointable(item) - return (item.canBeAnointed or item.base.type == "Amulet") - end - -+local function buildModSortList() -+ local sortList = { { label = "Default", stat = nil } } -+ local sortTransforms = { } -+ for _, entry in ipairs(data.powerStatList) do -+ if entry.stat and not entry.ignoreForNodes then -+ t_insert(sortList, { label = entry.label, stat = entry.stat }) -+ sortTransforms[entry.stat] = entry.transform -+ end -+ end -+ return sortList, sortTransforms -+end -+ -+local function getOutputStatValue(output, stat) -+ if stat == "FullDPS" then -+ if output[stat] ~= nil then -+ return output[stat] -+ end -+ if output.Minion and output.Minion.CombinedDPS ~= nil then -+ return output.Minion.CombinedDPS -+ end -+ end -+ if output.Minion and output.Minion[stat] ~= nil then -+ return output.Minion[stat] -+ end -+ if output[stat] ~= nil then -+ return output[stat] -+ end -+ return 0 -+end -+ - local ItemsTabClass = newClass("ItemsTab", "UndoHandler", "ControlHost", "Control", function(self, build) - self.UndoHandler() - self.ControlHost() -@@ -2278,12 +2308,21 @@ function ItemsTabClass:EnchantDisplayItem(enchantSlot) - end - end - local enchantmentList = { } -+ local sortList, sortTransforms = buildModSortList() -+ local function setDefaultSortOrder() -+ for index, entry in ipairs(enchantmentList) do -+ entry.defaultSortOrder = index -+ entry.sortValue = nil -+ entry.sortValues = nil -+ end -+ end - local function buildEnchantmentList() - wipeTable(enchantmentList) - local list = haveSkills and enchantments[skillList[controls.skill and controls.skill.selIndex or 1]] or enchantments - for _, enchantment in ipairs(list[enchantmentSourceList[controls.enchantmentSource and controls.enchantmentSource.selIndex or 1].name]) do -- t_insert(enchantmentList, enchantment) -+ t_insert(enchantmentList, { label = enchantment, line = enchantment }) - end -+ setDefaultSortOrder() - end - if haveSkills then - buildSkillList(true) -@@ -2294,12 +2333,37 @@ function ItemsTabClass:EnchantDisplayItem(enchantSlot) - local item = new("Item", self.displayItem:BuildRaw()) - local index = idx or controls.enchantment.selIndex - item.id = self.displayItem.id -- local list = haveSkills and enchantments[controls.skill.list[controls.skill.selIndex]] or enchantments -- local line = list[controls.enchantmentSource.list[controls.enchantmentSource.selIndex].name][index] -- local first, second = line:match("([^/]+)/([^/]+)") -+ local entry = enchantmentList[index] - if remove then - t_remove(item.enchantModLines, self.enchantSlot) -- elseif first then -+ elseif entry then -+ local line = entry.line -+ local first, second = line:match("([^/]+)/([^/]+)") -+ if first then -+ item.enchantModLines = { { crafted = true, line = first }, { crafted = true, line = second } } -+ else -+ if not item.canHaveTwoEnchants and #item.enchantModLines > 1 then -+ item.enchantModLines = { item.enchantModLines[1] } -+ end -+ if #item.enchantModLines >= self.enchantSlot then -+ t_remove(item.enchantModLines, self.enchantSlot) -+ end -+ t_insert(item.enchantModLines, self.enchantSlot, { crafted = true, line = line }) -+ end -+ end -+ item:BuildAndParseRaw() -+ return item -+ end -+ local function getSortValue(entry, stat, calcFunc, slotName, useFullDPS) -+ entry.sortValues = entry.sortValues or { } -+ if entry.sortValues[stat] ~= nil then -+ return entry.sortValues[stat] -+ end -+ local item = new("Item", self.displayItem:BuildRaw()) -+ item.id = self.displayItem.id -+ local line = entry.line -+ local first, second = line:match("([^/]+)/([^/]+)") -+ if first then - item.enchantModLines = { { crafted = true, line = first }, { crafted = true, line = second } } - else - if not item.canHaveTwoEnchants and #item.enchantModLines > 1 then -@@ -2308,10 +2372,51 @@ function ItemsTabClass:EnchantDisplayItem(enchantSlot) - if #item.enchantModLines >= self.enchantSlot then - t_remove(item.enchantModLines, self.enchantSlot) - end -- t_insert(item.enchantModLines, self.enchantSlot, { crafted = true, line = line}) -+ t_insert(item.enchantModLines, self.enchantSlot, { crafted = true, line = line }) - end - item:BuildAndParseRaw() -- return item -+ local output = calcFunc({ repSlotName = slotName, repItem = item }, useFullDPS) -+ local value = getOutputStatValue(output, stat) -+ if sortTransforms[stat] then -+ value = sortTransforms[stat](value) -+ end -+ entry.sortValues[stat] = value -+ return value -+ end -+ local function applySort(stat, selectFirst) -+ if not controls.enchantment or not controls.enchantment:IsShown() then -+ return -+ end -+ local selected = not selectFirst and enchantmentList[controls.enchantment.selIndex] or nil -+ if stat then -+ local slotName = self.displayItem:GetPrimarySlot() -+ local calcFunc = self.build.calcsTab:GetMiscCalculator() -+ local useFullDPS = stat == "FullDPS" -+ for _, entry in ipairs(enchantmentList) do -+ entry.sortValue = getSortValue(entry, stat, calcFunc, slotName, useFullDPS) -+ end -+ table.sort(enchantmentList, function(a, b) -+ if a.sortValue ~= b.sortValue then -+ return a.sortValue > b.sortValue -+ end -+ return (a.defaultSortOrder or 0) < (b.defaultSortOrder or 0) -+ end) -+ else -+ table.sort(enchantmentList, function(a, b) -+ return (a.defaultSortOrder or 0) < (b.defaultSortOrder or 0) -+ end) -+ end -+ controls.enchantment:UpdateSearch() -+ if selected then -+ for index, entry in ipairs(enchantmentList) do -+ if entry == selected then -+ controls.enchantment.selIndex = index -+ break -+ end -+ end -+ else -+ controls.enchantment:SetSel(1, true) -+ end - end - if haveSkills then - controls.skillLabel = new("LabelControl", {"TOPRIGHT",nil,"TOPLEFT"}, {95, 20, 0, 16}, "^7Skill:") -@@ -2319,12 +2424,18 @@ function ItemsTabClass:EnchantDisplayItem(enchantSlot) - buildEnchantmentSourceList() - buildEnchantmentList() - controls.enchantment:SetSel(1) -+ if controls.sort then -+ applySort(controls.sort.list[controls.sort.selIndex].stat, true) -+ end - end) - controls.allSkills = new("CheckBoxControl", {"TOPLEFT",nil,"TOPLEFT"}, {350, 20, 18}, "All skills:", function(state) - buildSkillList(not state) - controls.skill:SetSel(1) - buildEnchantmentList() - controls.enchantment:SetSel(1) -+ if controls.sort then -+ applySort(controls.sort.list[controls.sort.selIndex].stat, true) -+ end - end) - controls.allSkills.tooltipText = "Show all skills, not just those used by this build." - if not next(skillsUsed) then -@@ -2336,9 +2447,16 @@ function ItemsTabClass:EnchantDisplayItem(enchantSlot) - controls.enchantmentSource = new("DropDownControl", {"TOPLEFT",nil,"TOPLEFT"}, {100, 45, 180, 18}, enchantmentSourceList, function(index, value) - buildEnchantmentList() - controls.enchantment:SetSel(m_min(controls.enchantment.selIndex, #enchantmentList)) -+ if controls.sort then -+ applySort(controls.sort.list[controls.sort.selIndex].stat, true) -+ end -+ end) -+ controls.sortLabel = new("LabelControl", {"TOPRIGHT",nil,"TOPLEFT"}, {350, 45, 0, 16}, "^7Sort by:") -+ controls.sort = new("DropDownControl", {"TOPLEFT",nil,"TOPLEFT"}, {355, 45, 240, 18}, sortList, function(index, value) -+ applySort(value.stat, true) - end) - controls.enchantmentLabel = new("LabelControl", {"TOPRIGHT",nil,"TOPLEFT"}, {95, 70, 0, 16}, "^7Enchantment:") -- controls.enchantment = new("DropDownControl", {"TOPLEFT",nil,"TOPLEFT"}, {100, 70, 440, 18}, enchantmentList) -+ controls.enchantment = new("DropDownControl", {"TOPLEFT",nil,"TOPLEFT"}, {100, 70, 495, 18}, enchantmentList) - controls.enchantment.tooltipFunc = function(tooltip, mode, index) - tooltip:Clear() - self:AddItemTooltip(tooltip, enchantItem(index), nil, true) -@@ -2354,7 +2472,7 @@ function ItemsTabClass:EnchantDisplayItem(enchantSlot) - controls.close = new("ButtonControl", nil, {88, 100, 80, 20}, "Cancel", function() - main:ClosePopup() - end) -- main:OpenPopup(550, 130, "Enchant Item", controls) -+ main:OpenPopup(605, 130, "Enchant Item", controls) - end - - ---Gets the name of the anointed node on an item -@@ -2505,9 +2623,10 @@ end - -- Opens the item corrupting popup - function ItemsTabClass:CorruptDisplayItem(modType) - local currentModType = modType or "Corrupted" -- local controls = { } -+ local controls = { } - local implicitList = { } - local sourceList = { "Corrupted", "Scourge" } -+ local sortList, sortTransforms = buildModSortList() - local function buildImplicitList(modType) - if implicitList[modType] then - return -@@ -2515,18 +2634,21 @@ function ItemsTabClass:CorruptDisplayItem(modType) - implicitList[modType] = {} - for modId, mod in pairs(self.displayItem.affixes) do - if mod.type == modType and self.displayItem:GetModSpawnWeight(mod) > 0 then -- t_insert(implicitList[modType], mod) -+ t_insert(implicitList[modType], { mod = mod }) - end - end - table.sort(implicitList[modType], function(a, b) -- local an = a[1]:lower():gsub("%(.-%)","$"):gsub("[%+%-%%]",""):gsub("%d+","$") -- local bn = b[1]:lower():gsub("%(.-%)","$"):gsub("[%+%-%%]",""):gsub("%d+","$") -+ local an = a.mod[1]:lower():gsub("%(.-%)","$"):gsub("[%+%-%%]",""):gsub("%d+","$") -+ local bn = b.mod[1]:lower():gsub("%(.-%)","$"):gsub("[%+%-%%]",""):gsub("%d+","$") - if an ~= bn then - return an < bn - else -- return a.level < b.level -+ return a.mod.level < b.mod.level - end - end) -+ for index, entry in ipairs(implicitList[modType]) do -+ entry.defaultSortOrder = index -+ end - end - buildImplicitList(currentModType) - local function buildList(control, other, modType) -@@ -2534,13 +2656,89 @@ function ItemsTabClass:CorruptDisplayItem(modType) - local otherMod = other and other.selIndex and other.selIndex > 1 and other.list[other.selIndex].mod - wipeTable(control.list) - t_insert(control.list, { label = "None" }) -- for _, mod in ipairs(implicitList[modType]) do -+ for _, entry in ipairs(implicitList[modType]) do -+ local mod = entry.mod - if not otherMod or mod.group ~= otherMod.group then - t_insert(control.list, { label = table.concat(mod, "/"), mod = mod }) - end - end - control:SelByValue(selfMod, "mod") - end -+ local function getSortValue(entry, modType, stat, calcFunc, slotName, useFullDPS) -+ entry.sortValues = entry.sortValues or { } -+ if entry.sortValues[stat] ~= nil then -+ return entry.sortValues[stat] -+ end -+ local item = new("Item", self.displayItem:BuildRaw()) -+ item.id = self.displayItem.id -+ item.corrupted = true -+ local mod = entry.mod -+ local targetLines = modType == "Corrupted" and item.implicitModLines or item.scourgeModLines -+ wipeTable(targetLines) -+ for _, modLine in ipairs(mod) do -+ modLine = (currentModType == "ScourgeUpside" and "{scourge}" or "") .. modLine -+ if mod.modTags[1] then -+ t_insert(targetLines, { line = "{tags:" .. table.concat(mod.modTags, ",") .. "}" .. modLine }) -+ else -+ t_insert(targetLines, { line = modLine }) -+ end -+ end -+ item:BuildAndParseRaw() -+ local output = calcFunc({ repSlotName = slotName, repItem = item }, useFullDPS) -+ local value = getOutputStatValue(output, stat) -+ if sortTransforms[stat] then -+ value = sortTransforms[stat](value) -+ end -+ entry.sortValues[stat] = value -+ return value -+ end -+ local function sortModType(modType, stat, calcFunc, slotName, useFullDPS) -+ if not implicitList[modType] then -+ return -+ end -+ if stat then -+ for _, entry in ipairs(implicitList[modType]) do -+ entry.sortValue = getSortValue(entry, modType, stat, calcFunc, slotName, useFullDPS) -+ end -+ table.sort(implicitList[modType], function(a, b) -+ if a.sortValue ~= b.sortValue then -+ return a.sortValue > b.sortValue -+ end -+ return (a.defaultSortOrder or 0) < (b.defaultSortOrder or 0) -+ end) -+ else -+ table.sort(implicitList[modType], function(a, b) -+ return (a.defaultSortOrder or 0) < (b.defaultSortOrder or 0) -+ end) -+ end -+ end -+ local function applySort(stat) -+ if not controls.implicit then -+ return -+ end -+ local slotName = self.displayItem:GetPrimarySlot() -+ local calcFunc = stat and self.build.calcsTab:GetMiscCalculator() or nil -+ local useFullDPS = stat == "FullDPS" -+ if currentModType == "Corrupted" then -+ sortModType("Corrupted", stat, calcFunc, slotName, useFullDPS) -+ else -+ sortModType("ScourgeUpside", stat, calcFunc, slotName, useFullDPS) -+ sortModType("ScourgeDownside", stat, calcFunc, slotName, useFullDPS) -+ end -+ if currentModType == "Corrupted" then -+ buildList(controls.implicit, controls.implicit2, currentModType) -+ buildList(controls.implicit2, controls.implicit, currentModType) -+ else -+ buildList(controls.implicit, controls.implicit2, "ScourgeUpside") -+ buildList(controls.implicit2, controls.implicit, "ScourgeUpside") -+ buildList(controls.implicit3, controls.implicit4, "ScourgeDownside") -+ buildList(controls.implicit4, controls.implicit3, "ScourgeDownside") -+ end -+ controls.implicit:UpdateSearch() -+ controls.implicit2:UpdateSearch() -+ if controls.implicit3 then controls.implicit3:UpdateSearch() end -+ if controls.implicit4 then controls.implicit4:UpdateSearch() end -+ end - local function corruptItem() - local item = new("Item", self.displayItem:BuildRaw()) - item.id = self.displayItem.id -@@ -2610,14 +2808,22 @@ function ItemsTabClass:CorruptDisplayItem(modType) - controls.close.y = 99 - controls.save.y = 99 - end -- buildList(controls.implicit, controls.implicit2, currentModType) -- buildList(controls.implicit2, controls.implicit, currentModType) -+ if controls.sort then -+ applySort(controls.sort.list[controls.sort.selIndex].stat) -+ else -+ buildList(controls.implicit, controls.implicit2, currentModType) -+ buildList(controls.implicit2, controls.implicit, currentModType) -+ end - controls.implicit:SetSel(1) - controls.implicit2:SetSel(1) - controls.implicit3:SetSel(1) - controls.implicit4:SetSel(1) - end) - controls.source.enabled = #sourceList > 1 -+ controls.sortLabel = new("LabelControl", {"TOPRIGHT",nil,"TOPLEFT"}, {350, 20, 0, 16}, "^7Sort by:") -+ controls.sort = new("DropDownControl", {"TOPLEFT",nil,"TOPLEFT"}, {355, 20, 240, 18}, sortList, function(index, value) -+ applySort(value.stat) -+ end) - controls.implicitLabel = new("LabelControl", {"TOPRIGHT",nil,"TOPLEFT"}, {75, 45, 0, 16}, "^7Implicit #1:") - controls.implicit = new("DropDownControl", {"TOPLEFT",nil,"TOPLEFT"}, {80, 45, 440, 18}, nil, function() - buildList(controls.implicit2, controls.implicit, currentModType) -@@ -2693,7 +2899,7 @@ function ItemsTabClass:CorruptDisplayItem(modType) - controls.close = new("ButtonControl", nil, {45, 99, 80, 20}, "Cancel", function() - main:ClosePopup() - end) -- main:OpenPopup(540, 129, modType .. " Item", controls) -+ main:OpenPopup(605, 129, modType .. " Item", controls) - end - - -- Opens the custom modifier popup -@@ -3166,6 +3348,20 @@ function ItemsTabClass:AddImplicitToDisplayItem() - local sourceList = { } - local modList = { } - local modGroups = {} -+ local sortList, sortTransforms = buildModSortList() -+ local function setDefaultSortOrder() -+ for groupIndex, group in ipairs(modGroups) do -+ group.defaultSortOrder = groupIndex -+ group.sortValue = nil -+ end -+ for _, listMods in ipairs(modList) do -+ for index, listMod in ipairs(listMods) do -+ listMod.defaultSortOrder = index -+ listMod.sortValue = nil -+ listMod.sortValues = nil -+ end -+ end -+ end - ---Mutates modList to contain mods from the specified source - ---@param sourceId string @The crafting source id to build the list of mods for - local function buildMods(sourceId) -@@ -3291,6 +3487,7 @@ function ItemsTabClass:AddImplicitToDisplayItem() - end) - end - end -+ setDefaultSortOrder() - end - if (self.displayItem.rarity ~= "UNIQUE" and self.displayItem.rarity ~= "RELIC") and (self.displayItem.type == "Helmet" or self.displayItem.type == "Body Armour" or self.displayItem.type == "Gloves" or self.displayItem.type == "Boots") then - if self.displayItem.cleansing then -@@ -3306,21 +3503,11 @@ function ItemsTabClass:AddImplicitToDisplayItem() - end - t_insert(sourceList, { label = "Custom", sourceId = "CUSTOM" }) - buildMods(sourceList[1].sourceId) -- local function addModifier() -- local item = new("Item", self.displayItem:BuildRaw()) -- item.id = self.displayItem.id -- local sourceId = sourceList[controls.source.selIndex].sourceId -- if sourceId == "CUSTOM" then -- if controls.custom.buf:match("%S") then -- t_insert(item.implicitModLines, { line = controls.custom.buf, custom = true }) -- end -- elseif sourceId == "SYNTHESIS" then -- local listMod = modList[controls.modSelect.selIndex] -- for _, line in ipairs(listMod.mod) do -- t_insert(item.implicitModLines, { line = line, modTags = listMod.mod.modTags, [listMod.type] = true }) -- end -- elseif sourceId == "EXARCH" or sourceId == "EATER" then -- local listMod = modList[modGroups[controls.modGroupSelect.selIndex].modListIndex][controls.modSelect.selIndex] -+ ---Applies a candidate listMod to the item, mirroring the mutation addModifier() -+ ---performs at click-time. Eldritch (exarch/eater) sources replace an existing -+ ---implicit of the same type when present; other sources append. -+ local function applyCandidateMod(item, listMod) -+ if listMod.type == "exarch" or listMod.type == "eater" then - local index - for i, implicitMod in ipairs(item.implicitModLines) do - if implicitMod[listMod.type] then -@@ -3332,16 +3519,112 @@ function ItemsTabClass:AddImplicitToDisplayItem() - for i, line in ipairs(listMod.mod) do - item.implicitModLines[index + i - 1] = { line = line, modTags = listMod.mod.modTags, [listMod.type] = true } - end -- else -- for _, line in ipairs(listMod.mod) do -- t_insert(item.implicitModLines, { line = line, modTags = listMod.mod.modTags, [listMod.type] = true }) -+ return -+ end -+ end -+ for _, line in ipairs(listMod.mod) do -+ t_insert(item.implicitModLines, { line = line, modTags = listMod.mod.modTags, [listMod.type] = true }) -+ end -+ end -+ local function getSortValue(listMod, stat, calcFunc, slotName, useFullDPS) -+ listMod.sortValues = listMod.sortValues or { } -+ if listMod.sortValues[stat] ~= nil then -+ return listMod.sortValues[stat] -+ end -+ local item = new("Item", self.displayItem:BuildRaw()) -+ item.id = self.displayItem.id -+ applyCandidateMod(item, listMod) -+ item:BuildAndParseRaw() -+ local output = calcFunc({ repSlotName = slotName, repItem = item }, useFullDPS) -+ local value = getOutputStatValue(output, stat) -+ if sortTransforms[stat] then -+ value = sortTransforms[stat](value) -+ end -+ listMod.sortValues[stat] = value -+ return value -+ end -+ local function applySort(stat, selectFirst) -+ if not controls.modSelect or not controls.modGroupSelect or not controls.modSelect:IsShown() then -+ return -+ end -+ local selectedGroup = not selectFirst and modGroups[controls.modGroupSelect.selIndex] or nil -+ local selectedMod = not selectFirst and controls.modSelect.list and controls.modSelect.list[controls.modSelect.selIndex] or nil -+ if stat then -+ local slotName = self.displayItem:GetPrimarySlot() -+ local calcFunc = self.build.calcsTab:GetMiscCalculator() -+ local useFullDPS = stat == "FullDPS" -+ for _, listMods in ipairs(modList) do -+ for _, listMod in ipairs(listMods) do -+ listMod.sortValue = getSortValue(listMod, stat, calcFunc, slotName, useFullDPS) - end -+ table.sort(listMods, function(a, b) -+ if a.sortValue ~= b.sortValue then -+ return a.sortValue > b.sortValue -+ end -+ return (a.defaultSortOrder or 0) < (b.defaultSortOrder or 0) -+ end) -+ end -+ for _, group in ipairs(modGroups) do -+ local best -+ for _, listMod in ipairs(modList[group.modListIndex] or { }) do -+ if not best or listMod.sortValue > best then -+ best = listMod.sortValue -+ end -+ end -+ group.sortValue = best or 0 - end -+ table.sort(modGroups, function(a, b) -+ if a.sortValue ~= b.sortValue then -+ return a.sortValue > b.sortValue -+ end -+ return (a.defaultSortOrder or 0) < (b.defaultSortOrder or 0) -+ end) - else -- local listMod = modList[modGroups[controls.modGroupSelect.selIndex].modListIndex][controls.modSelect.selIndex] -- for _, line in ipairs(listMod.mod) do -- t_insert(item.implicitModLines, { line = line, modTags = listMod.mod.modTags, [listMod.type] = true }) -+ for _, listMods in ipairs(modList) do -+ table.sort(listMods, function(a, b) -+ return (a.defaultSortOrder or 0) < (b.defaultSortOrder or 0) -+ end) - end -+ table.sort(modGroups, function(a, b) -+ return (a.defaultSortOrder or 0) < (b.defaultSortOrder or 0) -+ end) -+ end -+ controls.modGroupSelect:UpdateSearch() -+ if selectedGroup then -+ for index, group in ipairs(modGroups) do -+ if group == selectedGroup then -+ controls.modGroupSelect.selIndex = index -+ break -+ end -+ end -+ else -+ controls.modGroupSelect:SetSel(1, true) -+ end -+ controls.modSelect.list = modList[modGroups[controls.modGroupSelect.selIndex].modListIndex] -+ controls.modSelect:UpdateSearch() -+ if selectedMod then -+ for index, listMod in ipairs(controls.modSelect.list) do -+ if listMod == selectedMod then -+ controls.modSelect.selIndex = index -+ break -+ end -+ end -+ else -+ controls.modSelect:SetSel(1, true) -+ end -+ end -+ local function addModifier() -+ local item = new("Item", self.displayItem:BuildRaw()) -+ item.id = self.displayItem.id -+ local sourceId = sourceList[controls.source.selIndex].sourceId -+ if sourceId == "CUSTOM" then -+ if controls.custom.buf:match("%S") then -+ t_insert(item.implicitModLines, { line = controls.custom.buf, custom = true }) -+ end -+ elseif sourceId == "SYNTHESIS" then -+ applyCandidateMod(item, modList[controls.modSelect.selIndex]) -+ else -+ applyCandidateMod(item, modList[modGroups[controls.modGroupSelect.selIndex].modListIndex][controls.modSelect.selIndex]) - end - item:BuildAndParseRaw() - return item -@@ -3354,11 +3637,24 @@ function ItemsTabClass:AddImplicitToDisplayItem() - controls.modGroupSelect:SetSel(1) - controls.modSelect.list = modList[modGroups[1].modListIndex] - controls.modSelect:SetSel(1) -+ if controls.sort then -+ applySort(controls.sort.list[controls.sort.selIndex].stat, true) -+ end - else - controls.modSelectLabel.y = 45 - end - end) - controls.source.enabled = #sourceList > 1 -+ controls.sortLabel = new("LabelControl", {"TOPRIGHT",nil,"TOPLEFT"}, {350, 20, 0, 16}, "^7Sort by:") -+ controls.sortLabel.shown = function() -+ return sourceList[controls.source.selIndex].sourceId ~= "CUSTOM" -+ end -+ controls.sort = new("DropDownControl", {"TOPLEFT",nil,"TOPLEFT"}, {355, 20, 240, 18}, sortList, function(index, value) -+ applySort(value.stat, true) -+ end) -+ controls.sort.shown = function() -+ return sourceList[controls.source.selIndex].sourceId ~= "CUSTOM" -+ end - controls.modGroupSelectLabel = new("LabelControl", {"TOPRIGHT",nil,"TOPLEFT"}, {95, 45, 0, 16}, "^7Type:") - controls.modGroupSelect = new("DropDownControl", {"TOPLEFT",nil,"TOPLEFT"}, {100, 45, 600, 18}, modGroups, function(index, value) - controls.modSelect.list = modList[value.modListIndex]