From 1fc32708c51474e7eb60ea44fa6571aafff38897 Mon Sep 17 00:00:00 2001 From: LocalIdentity Date: Thu, 18 Jun 2026 06:29:57 +1000 Subject: [PATCH] Fix power report and trade search not working with Minions When using the Power Report of a weighted sum trade search, it would not work with minions unless you included them in FullDPS The power report logic was broken due to the invalid lua logic of using "and not selection.stat ==" For the trade search I used the same logic we have in the items tab for using combined DPS as a fallback incase someone selects fullDPS but doesn't add the minion to it --- spec/System/TestTradeQueryCurrency_spec.lua | 18 ++++++++ spec/System/TestTradeQueryGenerator_spec.lua | 44 ++++++++++++++++++++ spec/System/TestTreeTab_spec.lua | 18 ++++++++ src/Classes/CalcsTab.lua | 2 +- src/Classes/TradeQuery.lua | 2 +- src/Classes/TradeQueryGenerator.lua | 24 +++++++++-- 6 files changed, 102 insertions(+), 6 deletions(-) diff --git a/spec/System/TestTradeQueryCurrency_spec.lua b/spec/System/TestTradeQueryCurrency_spec.lua index 2758a84363..6052a9759f 100644 --- a/spec/System/TestTradeQueryCurrency_spec.lua +++ b/spec/System/TestTradeQueryCurrency_spec.lua @@ -23,6 +23,24 @@ describe("TradeQuery Currency Conversion", function() end) end) + describe("ReduceOutput", function() + it("uses selected minion stats for weighted result comparison", function() + mock_tradeQuery.statSortSelectionList = { { stat = "AverageDamage" } } + + local result = mock_tradeQuery:ReduceOutput({ + AverageDamage = 10, + Life = 100, + Minion = { + AverageDamage = 250, + Life = 200, + }, + }) + + assert.are.equals(250, result.AverageDamage) + assert.is_nil(result.Life) + end) + end) + describe("PriceBuilderProcessPoENinjaResponse", function() -- Pass: Processes without error, restoring map while adding a notice -- Fail: Corrupts map or crashes, indicating fragile API response handling, breaking future conversions diff --git a/spec/System/TestTradeQueryGenerator_spec.lua b/spec/System/TestTradeQueryGenerator_spec.lua index bc7edc9f6b..fa25d5eb35 100644 --- a/spec/System/TestTradeQueryGenerator_spec.lua +++ b/spec/System/TestTradeQueryGenerator_spec.lua @@ -34,6 +34,50 @@ describe("TradeQueryGenerator", function() local result = mock_queryGen.WeightedRatioOutputs(baseOutput, newOutput, statWeights) assert.are.equal(result, 100) end) + + it("uses minion output for non-FullDPS stats when minion output is available", function() + local baseOutput = { AverageDamage = 10, Minion = { AverageDamage = 100 } } + local newOutput = { AverageDamage = 10, Minion = { AverageDamage = 250 } } + local statWeights = { { stat = "AverageDamage", weightMult = 1 } } + data.misc.maxStatIncrease = 1000 + + local result = mock_queryGen.WeightedRatioOutputs(baseOutput, newOutput, statWeights) + + assert.are.equal(result, 2.5) + end) + + it("uses player output for FullDPS even when minion output is available", function() + local baseOutput = { FullDPS = 100, Minion = { FullDPS = 100 } } + local newOutput = { FullDPS = 250, Minion = { FullDPS = 1000 } } + local statWeights = { { stat = "FullDPS", weightMult = 1 } } + data.misc.maxStatIncrease = 1000 + + local result = mock_queryGen.WeightedRatioOutputs(baseOutput, newOutput, statWeights) + + assert.are.equal(result, 2.5) + end) + + it("uses the fallback DPS ratio once when FullDPS is unavailable", function() + local baseOutput = { Minion = { TotalDPS = 10, TotalDotDPS = 0, CombinedDPS = 10 } } + local newOutput = { Minion = { TotalDPS = 25, TotalDotDPS = 0, CombinedDPS = 25 } } + local statWeights = { { stat = "FullDPS", weightMult = 1 } } + data.misc.maxStatIncrease = 1000 + + local result = mock_queryGen.WeightedRatioOutputs(baseOutput, newOutput, statWeights) + + assert.are.equal(result, 2.5) + end) + + it("falls back to player output when the selected stat is not on minion output", function() + local baseOutput = { Spirit = 100, Minion = { AverageDamage = 100 } } + local newOutput = { Spirit = 120, Minion = { AverageDamage = 100 } } + local statWeights = { { stat = "Spirit", weightMult = 1 } } + data.misc.maxStatIncrease = 1000 + + local result = mock_queryGen.WeightedRatioOutputs(baseOutput, newOutput, statWeights) + + assert.are.equal(result, 1.2) + end) end) describe("Filter prioritization", function() diff --git a/spec/System/TestTreeTab_spec.lua b/spec/System/TestTreeTab_spec.lua index f556365229..c1ab42dbff 100644 --- a/spec/System/TestTreeTab_spec.lua +++ b/spec/System/TestTreeTab_spec.lua @@ -146,4 +146,22 @@ describe("TreeTab", function() assert.is_nil(build.treeTab.specList[1].allocNodes[2]) end) end) + + describe("Power stat calculation", function() + it("uses minion output for non-FullDPS stats when minion output is available", function() + local selection = { stat = "AverageDamage" } + local outputWithNode = { AverageDamage = 10, Minion = { AverageDamage = 250 } } + local baseOutput = { AverageDamage = 10, Minion = { AverageDamage = 100 } } + + assert.are.equals(150, build.calcsTab:CalculatePowerStat(selection, outputWithNode, baseOutput)) + end) + + it("uses player output for FullDPS even when minion output is available", function() + local selection = { stat = "FullDPS" } + local outputWithNode = { FullDPS = 250, Minion = { FullDPS = 1000 } } + local baseOutput = { FullDPS = 100, Minion = { FullDPS = 1000 } } + + assert.are.equals(150, build.calcsTab:CalculatePowerStat(selection, outputWithNode, baseOutput)) + end) + end) end) diff --git a/src/Classes/CalcsTab.lua b/src/Classes/CalcsTab.lua index 7f9f85fd51..aabaec1bd8 100644 --- a/src/Classes/CalcsTab.lua +++ b/src/Classes/CalcsTab.lua @@ -672,7 +672,7 @@ function CalcsTabClass:PowerBuilder() end function CalcsTabClass:CalculatePowerStat(selection, original, modified) - if modified.Minion and not selection.stat == "FullDPS" then + if modified.Minion and selection.stat ~= "FullDPS" then original = original.Minion modified = modified.Minion end diff --git a/src/Classes/TradeQuery.lua b/src/Classes/TradeQuery.lua index d7ad205284..e708d0cece 100644 --- a/src/Classes/TradeQuery.lua +++ b/src/Classes/TradeQuery.lua @@ -770,7 +770,7 @@ end function TradeQueryClass:ReduceOutput(output) local smallOutput = {} for _, statTable in ipairs(self.statSortSelectionList) do - smallOutput[statTable.stat] = output[statTable.stat] + smallOutput[statTable.stat] = output.Minion and output.Minion[statTable.stat] or output[statTable.stat] end return smallOutput end diff --git a/src/Classes/TradeQueryGenerator.lua b/src/Classes/TradeQueryGenerator.lua index fb62cd748f..a158860803 100644 --- a/src/Classes/TradeQueryGenerator.lua +++ b/src/Classes/TradeQueryGenerator.lua @@ -157,12 +157,26 @@ end function TradeQueryGeneratorClass.WeightedRatioOutputs(baseOutput, newOutput, statWeights) local meanStatDiff = 0 + 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 + return output[stat] or 0 + end local function ratioModSums(...) local baseModSum = 0 local newModSum = 0 for _, mod in ipairs({ ... }) do - baseModSum = baseModSum + (baseOutput[mod] or 0) - newModSum = newModSum + (newOutput[mod] or 0) + baseModSum = baseModSum + getOutputStatValue(baseOutput, mod) + newModSum = newModSum + getOutputStatValue(newOutput, mod) end if baseModSum == math.huge then @@ -176,10 +190,12 @@ function TradeQueryGeneratorClass.WeightedRatioOutputs(baseOutput, newOutput, st end end for _, statTable in ipairs(statWeights) do + local modSumRatio if statTable.stat == "FullDPS" and not (baseOutput["FullDPS"] and newOutput["FullDPS"]) then - meanStatDiff = meanStatDiff + ratioModSums("TotalDPS", "TotalDotDPS", "CombinedDPS") * statTable.weightMult + modSumRatio = ratioModSums("TotalDPS", "TotalDotDPS", "CombinedDPS") + else + modSumRatio = ratioModSums(statTable.stat) end - local modSumRatio = ratioModSums(statTable.stat) -- some weights, such as damage taken from hit need to be negated as lower is better for them if statTable.transform then modSumRatio = statTable.transform(modSumRatio)