From 8d9f9bf7ffe8e95ce8c7d92688be51536add1594 Mon Sep 17 00:00:00 2001 From: vaisest Date: Sat, 29 Aug 2026 20:34:28 +0000 Subject: [PATCH 1/2] Apply changes from https://github.com/PathOfBuildingCommunity/PathOfBuilding/pull/10120 --- spec/System/TestItemMods_spec.lua.rej | 82 +++++++++++++++++++++++++++ src/Classes/ItemsTab.lua | 1 + src/Classes/ItemsTab.lua.rej | 10 ++++ 3 files changed, 93 insertions(+) create mode 100644 spec/System/TestItemMods_spec.lua.rej create mode 100644 src/Classes/ItemsTab.lua.rej diff --git a/spec/System/TestItemMods_spec.lua.rej b/spec/System/TestItemMods_spec.lua.rej new file mode 100644 index 0000000000..8576a6acac --- /dev/null +++ b/spec/System/TestItemMods_spec.lua.rej @@ -0,0 +1,82 @@ +diff a/spec/System/TestItemMods_spec.lua b/spec/System/TestItemMods_spec.lua (rejected hunks) +@@ -803,6 +803,80 @@ describe("TetsItemMods", function() + end + assert.are.equals(2, count) + end) ++ ++ it("does not sort cluster jewel modifiers when the sorting control is hidden", function() ++ local item = new("Item", [[ ++ Rarity: RARE ++ New Item ++ Large Cluster Jewel ++ Crafted: true ++ Prefix: {range:0.5}AfflictionNotableWickedPall_ ++ Prefix: {range:0.5}AfflictionNotableMiseryEverlasting ++ Suffix: {range:0.5}AfflictionNotableUnholyGrace_ ++ Suffix: None ++ Cluster Jewel Skill: affliction_chaos_damage ++ Cluster Jewel Node Count: 8 ++ Quality: 0 ++ LevelReq: 40 ++ Implicits: 3 ++ {crafted}Adds 8 Passive Skills ++ {crafted}2 Added Passive Skills are Jewel Sockets ++ {crafted}Added Small Passive Skills grant: 12% increased Chaos Damage ++ 1 Added Passive Skill is Misery Everlasting ++ 1 Added Passive Skill is Unholy Grace ++ 1 Added Passive Skill is Wicked Pall ++ ]]) ++ local calcCount = 0 ++ build.itemsTab.displayItem = item ++ build.itemsTab.controls.craftingSorting:SetSel(2, true) ++ build.calcsTab.GetMiscCalculator = function() ++ return function() ++ calcCount = calcCount + 1 ++ return { } ++ end ++ end ++ ++ assert.is_false(build.itemsTab.controls.craftingSortingLabel.shown()) ++ build.itemsTab:UpdateAffixControls() ++ assert.are.equals(0, calcCount) ++ end) ++ ++ it("sorts crafted modifier replacements without retaining the selected modifier", function() ++ local item = new("Item", [[ ++ Rarity: RARE ++ New Item ++ Cobalt Jewel ++ Crafted: true ++ Prefix: {range:1}PercentIncreasedLifeJewel ++ Prefix: None ++ Suffix: None ++ Suffix: None ++ Quality: 0 ++ LevelReq: 0 ++ Implicits: 0 ++ 7% increased maximum Life ++ ]]) ++ local calcCount = 0 ++ local retainedCount = 0 ++ build.itemsTab.displayItem = item ++ build.itemsTab.controls.craftingSorting:SetSel(2, true) ++ build.calcsTab.GetMiscCalculator = function() ++ return function(args) ++ calcCount = calcCount + 1 ++ for _, modLine in ipairs(args.repItem.explicitModLines) do ++ if modLine.line == "7% increased maximum Life" then ++ retainedCount = retainedCount + 1 ++ break ++ end ++ end ++ return { } ++ end ++ end ++ ++ build.itemsTab:UpdateAffixControl(build.itemsTab.controls.displayItemAffix1, item, "Prefix", "prefixes", 1, { }) ++ assert.is_true(calcCount > 1) ++ assert.are.equals(0, retainedCount) ++ end) + + it("shows a fallback tooltip when an item's base is no longer supported", function() + local item = new("Item", [[ diff --git a/src/Classes/ItemsTab.lua b/src/Classes/ItemsTab.lua index 81b5fad9bd..2756c8aec1 100644 --- a/src/Classes/ItemsTab.lua +++ b/src/Classes/ItemsTab.lua @@ -747,6 +747,7 @@ holding Shift will put it in the second.]]) end return h end}) + for i = 1, maxModCount do local prev = self.controls["displayItemAffix"..(i-1)] or self.controls.displayItemSectionAffix local drop, slider diff --git a/src/Classes/ItemsTab.lua.rej b/src/Classes/ItemsTab.lua.rej new file mode 100644 index 0000000000..f8123a85cc --- /dev/null +++ b/src/Classes/ItemsTab.lua.rej @@ -0,0 +1,10 @@ +diff a/src/Classes/ItemsTab.lua b/src/Classes/ItemsTab.lua (rejected hunks) +@@ -717,7 +717,7 @@ holding Shift will put it in the second.]]) + -- cluster jewels don't have good comparison support and sorting would be misleading + not (self.displayItem.base.type == "Jewel" and self.displayItem.base.subType == "Cluster") + end +- self.controls.craftingSorting = new("DropDownControl", { "LEFT", self.controls.craftingSortingLabel, "RIGHT" }, { 4, 0, 120, 20 }, sortingOptions, function() ++ self.controls.craftingSorting = new("DropDownControl", { "LEFT", self.controls.craftingSortingLabel, "RIGHT" }, { 4, 0, 200, 20 }, sortingOptions, function() + self:UpdateAffixControls() + end) + From 16c8ed084371dc08d92d874e0bf399cce45f4b95 Mon Sep 17 00:00:00 2001 From: LocalIdentity Date: Sun, 30 Aug 2026 06:59:24 +1000 Subject: [PATCH 2/2] Fix port --- spec/System/TestItemMods_spec.lua | 47 ++++++++++ spec/System/TestItemMods_spec.lua.rej | 82 ----------------- src/Classes/ItemsTab.lua | 124 ++++++++++++++++---------- src/Classes/ItemsTab.lua.rej | 10 --- 4 files changed, 122 insertions(+), 141 deletions(-) delete mode 100644 spec/System/TestItemMods_spec.lua.rej delete mode 100644 src/Classes/ItemsTab.lua.rej diff --git a/spec/System/TestItemMods_spec.lua b/spec/System/TestItemMods_spec.lua index 7137e28264..29a5b1d017 100644 --- a/spec/System/TestItemMods_spec.lua +++ b/spec/System/TestItemMods_spec.lua @@ -106,6 +106,53 @@ describe("TetsItemMods", function() assert.are.equals(-math.huge, itemDB.list[#itemDB.list].measuredPower) end) + it("sorts crafted modifier replacements without retaining the selected modifier", function() + local item = new("Item"):Item([[ + Rarity: RARE + Armour Chest + Champion Cuirass + Armour: 526 + Crafted: true + Prefix: {range:1}IncreasedLife1 + Prefix: None + Prefix: None + Suffix: None + Suffix: None + Suffix: None + Quality: 18 + Item Level: 100 + LevelReq: 65 + Implicits: 0 + +19 to maximum Life + ]]) + local calcCount = 0 + local retainedCount = 0 + assert.are.equals("+19 to maximum Life", item.explicitModLines[1].line) + build.itemsTab.displayItem = item + build.itemsTab.controls.craftingSorting:SelByValue("Life", "stat") + build.calcsTab.GetMiscCalculator = function() + return function(args) + calcCount += 1 + local life = 0 + for _, modLine in ipairs(args.repItem.explicitModLines) do + if modLine.line == "+19 to maximum Life" then + retainedCount += 1 + end + life += tonumber(modLine.line:match("%+(%d+) to maximum Life")) or 0 + end + return { Life = life } + end + end + + local control = build.itemsTab.controls.displayItemAffix1 + build.itemsTab:UpdateAffixControl(control, item, "Prefix", "prefixes", 1, { }) + + assert.is_true(calcCount > 1) + assert.are.equals(0, retainedCount) + assert.is_truthy(control.list[2].label:find("maximum Life", 1, true)) + assert.is_truthy(isValueInArray(control.list[control.selIndex].modList, "IncreasedLife1")) + end) + it("Both slots mod (evasion and es mastery)", function() build.configTab.input.customMods = "\z diff --git a/spec/System/TestItemMods_spec.lua.rej b/spec/System/TestItemMods_spec.lua.rej deleted file mode 100644 index 8576a6acac..0000000000 --- a/spec/System/TestItemMods_spec.lua.rej +++ /dev/null @@ -1,82 +0,0 @@ -diff a/spec/System/TestItemMods_spec.lua b/spec/System/TestItemMods_spec.lua (rejected hunks) -@@ -803,6 +803,80 @@ describe("TetsItemMods", function() - end - assert.are.equals(2, count) - end) -+ -+ it("does not sort cluster jewel modifiers when the sorting control is hidden", function() -+ local item = new("Item", [[ -+ Rarity: RARE -+ New Item -+ Large Cluster Jewel -+ Crafted: true -+ Prefix: {range:0.5}AfflictionNotableWickedPall_ -+ Prefix: {range:0.5}AfflictionNotableMiseryEverlasting -+ Suffix: {range:0.5}AfflictionNotableUnholyGrace_ -+ Suffix: None -+ Cluster Jewel Skill: affliction_chaos_damage -+ Cluster Jewel Node Count: 8 -+ Quality: 0 -+ LevelReq: 40 -+ Implicits: 3 -+ {crafted}Adds 8 Passive Skills -+ {crafted}2 Added Passive Skills are Jewel Sockets -+ {crafted}Added Small Passive Skills grant: 12% increased Chaos Damage -+ 1 Added Passive Skill is Misery Everlasting -+ 1 Added Passive Skill is Unholy Grace -+ 1 Added Passive Skill is Wicked Pall -+ ]]) -+ local calcCount = 0 -+ build.itemsTab.displayItem = item -+ build.itemsTab.controls.craftingSorting:SetSel(2, true) -+ build.calcsTab.GetMiscCalculator = function() -+ return function() -+ calcCount = calcCount + 1 -+ return { } -+ end -+ end -+ -+ assert.is_false(build.itemsTab.controls.craftingSortingLabel.shown()) -+ build.itemsTab:UpdateAffixControls() -+ assert.are.equals(0, calcCount) -+ end) -+ -+ it("sorts crafted modifier replacements without retaining the selected modifier", function() -+ local item = new("Item", [[ -+ Rarity: RARE -+ New Item -+ Cobalt Jewel -+ Crafted: true -+ Prefix: {range:1}PercentIncreasedLifeJewel -+ Prefix: None -+ Suffix: None -+ Suffix: None -+ Quality: 0 -+ LevelReq: 0 -+ Implicits: 0 -+ 7% increased maximum Life -+ ]]) -+ local calcCount = 0 -+ local retainedCount = 0 -+ build.itemsTab.displayItem = item -+ build.itemsTab.controls.craftingSorting:SetSel(2, true) -+ build.calcsTab.GetMiscCalculator = function() -+ return function(args) -+ calcCount = calcCount + 1 -+ for _, modLine in ipairs(args.repItem.explicitModLines) do -+ if modLine.line == "7% increased maximum Life" then -+ retainedCount = retainedCount + 1 -+ break -+ end -+ end -+ return { } -+ end -+ end -+ -+ build.itemsTab:UpdateAffixControl(build.itemsTab.controls.displayItemAffix1, item, "Prefix", "prefixes", 1, { }) -+ assert.is_true(calcCount > 1) -+ assert.are.equals(0, retainedCount) -+ end) - - it("shows a fallback tooltip when an item's base is no longer supported", function() - local item = new("Item", [[ diff --git a/src/Classes/ItemsTab.lua b/src/Classes/ItemsTab.lua index 2756c8aec1..c08435c170 100644 --- a/src/Classes/ItemsTab.lua +++ b/src/Classes/ItemsTab.lua @@ -74,32 +74,12 @@ 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 + if entry.stat and not entry.ignoreForItems then + t_insert(sortList, entry) 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 + return sortList end local function setDefaultSortOrder(modList) @@ -110,7 +90,8 @@ local function setDefaultSortOrder(modList) end end -local function getSortedModValue(item, listMod, stat, sortTransforms, calcFunc, slotName, useFullDPS, addModToItem) +local function getSortedModValue(item, listMod, sortOption, calcFunc, slotName, addModToItem) + local stat = sortOption.stat listMod.sortValues = listMod.sortValues or { } if listMod.sortValues[stat] ~= nil then return listMod.sortValues[stat] @@ -119,11 +100,8 @@ local function getSortedModValue(item, listMod, stat, sortTransforms, calcFunc, 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 + local output = calcFunc({ repSlotName = slotName, repItem = testItem }, stat == "FullDPS") + local value = data.powerStatList.GetFromOutput(output, sortOption) listMod.sortValues[stat] = value return value end @@ -730,9 +708,24 @@ holding Shift will put it in the second.]]) self.controls["displayItemRuneLabel" .. i] = new("LabelControl"):LabelControl({ "RIGHT", drop, "LEFT" }, { -4, 0, 0, 14 }, "^7Rune #" .. i) end + -- Section: Crafting modifier sorting + local sortList = buildModSortList() + local function craftingSortingShown() + return self.displayItem and self.displayItem.crafted and not self.displayItem.clusterJewel + end + self.controls.displayItemSectionCraftingSort = new("Control"):Control({ "TOPLEFT", self.controls.displayItemSectionRune, "BOTTOMLEFT" }, { 0, 0, 0, function() + return craftingSortingShown() and 28 or 0 + end }) + self.controls.craftingSortingLabel = new("LabelControl"):LabelControl({ "TOPLEFT", self.controls.displayItemSectionCraftingSort, "TOPLEFT" }, { 0, 6, 0, 16 }, "^7Modifier sorting:") + self.controls.craftingSortingLabel.shown = craftingSortingShown + self.controls.craftingSorting = new("DropDownControl"):DropDownControl({ "LEFT", self.controls.craftingSortingLabel, "RIGHT" }, { 4, 0, 200, 20 }, sortList, function() + self:UpdateAffixControls() + end) + self.controls.craftingSorting.shown = craftingSortingShown + -- Section: Affix Selection local maxModCount = 9 - self.controls.displayItemSectionAffix = new("Control"):Control({ "TOPLEFT", self.controls.displayItemSectionRune, "BOTTOMLEFT" }, { 0, 0, 0, function() + self.controls.displayItemSectionAffix = new("Control"):Control({ "TOPLEFT", self.controls.displayItemSectionCraftingSort, "BOTTOMLEFT" }, { 0, 0, 0, function() if not self.displayItem or not self.displayItem.crafted then return 0 end @@ -1940,11 +1933,12 @@ end function ItemsTabClass:UpdateAffixControls() local item = self.displayItem local prefixLimit = item.prefixes.limit or (item.affixLimit / 2) + local powerCache = { } for i = 1, item.affixLimit do if i <= prefixLimit then - self:UpdateAffixControl(self.controls["displayItemAffix"..i], item, "Prefix", "prefixes", i) + self:UpdateAffixControl(self.controls["displayItemAffix"..i], item, "Prefix", "prefixes", i, powerCache) else - self:UpdateAffixControl(self.controls["displayItemAffix"..i], item, "Suffix", "suffixes", i - prefixLimit) + self:UpdateAffixControl(self.controls["displayItemAffix"..i], item, "Suffix", "suffixes", i - prefixLimit, powerCache) end end -- The custom affixes may have had their indexes changed, so the custom control UI is also rebuilt so that it will @@ -2044,7 +2038,7 @@ function ItemsTabClass:UpdateRuneControls() end end -function ItemsTabClass:UpdateAffixControl(control, item, type, outputTable, outputIndex) +function ItemsTabClass:UpdateAffixControl(control, item, type, outputTable, outputIndex, powerCache) local extraTags = { } local excludeGroups = { } for _, table in ipairs({"prefixes","suffixes"}) do @@ -2143,6 +2137,40 @@ function ItemsTabClass:UpdateAffixControl(control, item, type, outputTable, outp end end end + local sortOption = self.controls.craftingSorting:GetSelValue() + if sortOption.stat and self.controls.craftingSorting:IsShown() then + local modList = { } + for index = 2, #control.list do + t_insert(modList, control.list[index]) + end + setDefaultSortOrder(modList) + local calcFunc = self.build.calcsTab:GetMiscCalculator() + local slotName = self:GetComparisonSlotNameForItem(item) + local controlPowerCache = selAffix ~= "None" and { } or powerCache or { } + sortModList(modList, sortOption.stat, function(listMod) + local modId = listMod.modList[1 + round((#listMod.modList - 1) * main.defaultItemAffixQuality)] + local cacheEntry = controlPowerCache[modId] + if not cacheEntry then + cacheEntry = { modId = modId } + controlPowerCache[modId] = cacheEntry + end + return getSortedModValue(item, cacheEntry, sortOption, calcFunc, slotName, function(testItem, sortedMod) + testItem[outputTable][outputIndex] = { modId = sortedMod.modId, range = main.defaultItemAffixQuality } + testItem:Craft() + end) + end) + wipeTable(control.list) + t_insert(control.list, "None") + for _, listMod in ipairs(modList) do + t_insert(control.list, listMod) + end + for index, listMod in ipairs(control.list) do + if listMod.modList and isValueInArray(listMod.modList, selAffix) then + control.selIndex = index + break + end + end + end if control.list[control.selIndex].haveRange then control.slider.divCount = #control.list[control.selIndex].modList local index = isValueInArray(control.list[control.selIndex].modList, selAffix) @@ -2707,7 +2735,7 @@ function ItemsTabClass:CorruptDisplayItem() -- todo implement vaal orb new outco local corruptedRanges = {} local currentModType = "Corrupted" local sourceList = { "Corrupted" } - local sortList, sortTransforms = buildModSortList() + local sortList = buildModSortList() if self.displayItem.base.type == "Helmet" then t_insert(sourceList, "Glimpse of Chaos") @@ -2790,13 +2818,12 @@ function ItemsTabClass:CorruptDisplayItem() -- todo implement vaal orb new outco end end end - local function sortEnchantList(stat) - if stat then + local function sortEnchantList(sortOption) + if sortOption.stat then local slotName = self:GetComparisonSlotNameForItem(self.displayItem) 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) + sortModList(enchantList[currentModType], sortOption.stat, function(listMod) + return getSortedModValue(self.displayItem, listMod, sortOption, calcFunc, slotName, function(item, sortedMod) applyCorruptionMods(item, { sortedMod.mod }) end) end) @@ -2957,7 +2984,7 @@ function ItemsTabClass:CorruptDisplayItem() -- todo implement vaal orb new outco end end if controls.sort then - sortEnchantList(controls.sort.list[controls.sort.selIndex].stat) + sortEnchantList(controls.sort:GetSelValue()) end rebuildEnchantControls(true) main.popups[1].height = 103 + 20 * enchantNum @@ -2967,7 +2994,7 @@ function ItemsTabClass:CorruptDisplayItem() -- todo implement vaal orb new outco controls.source:SelByValue(currentModType == "SpecialCorrupted" and "Glimpse of Chaos" or "Corrupted") controls.sortLabel = new("LabelControl"):LabelControl({ "TOPRIGHT", nil, "TOPLEFT" }, { 350, 30, 0, 16 }, "^7Sort by:") controls.sort = new("DropDownControl"):DropDownControl({ "TOPLEFT", nil, "TOPLEFT" }, { 355, 30, 240, 18 }, sortList, function(index, value) - sortEnchantList(value.stat) + sortEnchantList(value) rebuildEnchantControls() end) for i = 1, 8 do @@ -3015,18 +3042,17 @@ function ItemsTabClass:AddCustomModifierToDisplayItem() local controls = { } local sourceList = { } local modList = { } - local sortList, sortTransforms = buildModSortList() - local function applySort(stat, selectFirst) + local sortList = buildModSortList() + local function applySort(sortOption, selectFirst) if not controls.modSelect or not controls.modSelect:IsShown() then return end local selected = not selectFirst and modList[controls.modSelect.selIndex] or nil - if stat then + if sortOption.stat then local slotName = self:GetComparisonSlotNameForItem(self.displayItem) local calcFunc = self.build.calcsTab:GetMiscCalculator() - local useFullDPS = stat == "FullDPS" - sortModList(modList, stat, function(listMod) - return getSortedModValue(self.displayItem, listMod, stat, sortTransforms, calcFunc, slotName, useFullDPS, function(item, sortedMod) + sortModList(modList, sortOption.stat, function(listMod) + return getSortedModValue(self.displayItem, listMod, sortOption, calcFunc, slotName, 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 @@ -3234,7 +3260,7 @@ function ItemsTabClass:AddCustomModifierToDisplayItem() buildMods(value.sourceId) controls.modSelect:SetSel(1) if controls.sort then - applySort(controls.sort.list[controls.sort.selIndex].stat, true) + applySort(controls.sort:GetSelValue(), true) end end) controls.source.enabled = #sourceList > 1 @@ -3243,7 +3269,7 @@ function ItemsTabClass:AddCustomModifierToDisplayItem() return sourceList[controls.source.selIndex].sourceId ~= "CUSTOM" end controls.sort = new("DropDownControl"):DropDownControl({ "TOPLEFT", nil, "TOPLEFT" }, { 355, 20, 240, 18 }, sortList, function(index, value) - applySort(value.stat, true) + applySort(value, true) end) controls.sort.shown = function() return sourceList[controls.source.selIndex].sourceId ~= "CUSTOM" diff --git a/src/Classes/ItemsTab.lua.rej b/src/Classes/ItemsTab.lua.rej deleted file mode 100644 index f8123a85cc..0000000000 --- a/src/Classes/ItemsTab.lua.rej +++ /dev/null @@ -1,10 +0,0 @@ -diff a/src/Classes/ItemsTab.lua b/src/Classes/ItemsTab.lua (rejected hunks) -@@ -717,7 +717,7 @@ holding Shift will put it in the second.]]) - -- cluster jewels don't have good comparison support and sorting would be misleading - not (self.displayItem.base.type == "Jewel" and self.displayItem.base.subType == "Cluster") - end -- self.controls.craftingSorting = new("DropDownControl", { "LEFT", self.controls.craftingSortingLabel, "RIGHT" }, { 4, 0, 120, 20 }, sortingOptions, function() -+ self.controls.craftingSorting = new("DropDownControl", { "LEFT", self.controls.craftingSortingLabel, "RIGHT" }, { 4, 0, 200, 20 }, sortingOptions, function() - self:UpdateAffixControls() - end) -