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)