diff --git a/spec/System/TestTradeQueryGenerator_spec.lua b/spec/System/TestTradeQueryGenerator_spec.lua index 97fb12934b..d234231191 100644 --- a/spec/System/TestTradeQueryGenerator_spec.lua +++ b/spec/System/TestTradeQueryGenerator_spec.lua @@ -104,23 +104,37 @@ describe("TradeQueryGenerator", function() end) describe("Filter prioritization", function() - -- Pass: Limits mods to MAX_FILTERS (2 in test), preserving top priorities - -- Fail: Exceeds limit, indicating over-generation of filters, risking API query size errors or rate limits - it("respects MAX_FILTERS", function() - local orig_max = _G.MAX_FILTERS - _G.MAX_FILTERS = 2 - mock_queryGen.modWeights = { { weight = 10, tradeModId = "id1" }, { weight = 5, tradeModId = "id2" } } - table.sort(mock_queryGen.modWeights, function(a, b) - return math.abs(a.weight) > math.abs(b.weight) - end) - local prioritized = {} - for i, entry in ipairs(mock_queryGen.modWeights) do - if #prioritized < _G.MAX_FILTERS then - table.insert(prioritized, entry) - end + it("counts socket constraints against MAX_FILTERS", function() + local queryGen = new("TradeQueryGenerator"):TradeQueryGenerator({ itemsTab = { items = {} } }) + queryGen.modWeights = {} + for index = 1, 40 do + table.insert(queryGen.modWeights, { + tradeModId = "explicit.stat_" .. index, + weight = 1, + meanStatDiff = 41 - index, + }) end - assert.are.equal(#prioritized, 2) - _G.MAX_FILTERS = orig_max + queryGen.calcContext = { + testItem = new("Item"):Item("Rarity: RARE\nNew Item\nGold Ring\nImplicits: 0"), + baseOutput = {}, + baseStatValue = 0, + itemCategoryQueryStr = "accessory.ring", + special = {}, + options = { + statWeights = {}, + includeMirrored = false, + sockets = 3, + }, + } + queryGen.tradeTypeIndex = 1 + local query + queryGen.requesterCallback = function(_, queryJson) + query = require("dkjson").decode(queryJson).query + end + queryGen:FinishQuery() + + assert.are.equal(31, #query.stats[1].filters) + assert.is_not_nil(query.filters.equipment_filters.filters.rune_sockets) end) end) end) diff --git a/src/Classes/TradeQueryGenerator.lua b/src/Classes/TradeQueryGenerator.lua index 8504674eeb..2882a80339 100644 --- a/src/Classes/TradeQueryGenerator.lua +++ b/src/Classes/TradeQueryGenerator.lua @@ -1016,23 +1016,62 @@ function TradeQueryGeneratorClass:FinishQuery() local effective_max = MAX_FILTERS - num_extra - local prioritizedMods = {} + local pseudoMap = { + -- pseudo stats are disabled for PoE2 due to the trade site counting augment mods in them, + -- which would skew results significantly. however, the feature is kept here for PoB1 parity reasons + } + local ignoredStats = { + } + -- block all hybrid resistance stats + local resElements = {} + for _, elem1 in ipairs(resElements) do + for _, elem2 in ipairs(resElements) do + local stats = { string.format("%s_and_%s_damage_resistance_%%", elem1, elem2) } + ignoredStats[tostring(HashStats(stats))] = true + end + end + -- block all hybrid attribute stats + local attributeElements = {} + for _, elem1 in ipairs(attributeElements) do + for _, elem2 in ipairs(attributeElements) do + local stats = { string.format("base_%s_and_%s", elem1, elem2) } + ignoredStats[tostring(HashStats(stats))] = true + stats = { string.format("additional_%s_and_%s", elem1, elem2) } + ignoredStats[tostring(HashStats(stats))] = true + end + end + local statFilters = {} + local pseudoMods = {} for _, entry in ipairs(self.modWeights) do - if #prioritizedMods < effective_max then - table.insert(prioritizedMods, entry) + local hash = entry.tradeModId:match("stat_(%d+)") + local filterEntry = { id = entry.tradeModId, value = { weight = (entry.invert == true and entry.weight * -1 or entry.weight) } } + -- avoid adding hybrid stats since we get the weight for them from + -- individual stats + if ignoredStats[hash] then + goto weightContinue + elseif pseudoMap[hash] then + local tradeId = pseudoMap[hash] + filterEntry.id = tradeId + -- avoid adding duplicate pseudo filters: update existing + if pseudoMods[tradeId] then + pseudoMods[tradeId].value.weight = math.max(filterEntry.value.weight, pseudoMods[tradeId].value.weight) + else + pseudoMods[tradeId] = filterEntry + table.insert(statFilters, filterEntry) + end else - break + table.insert(statFilters, filterEntry) end - end - self.modWeights = prioritizedMods + ::weightContinue:: + end for k, v in pairs(self.calcContext.special.queryExtra or {}) do queryTable.query[k] = v end - for _, entry in ipairs(self.modWeights) do - t_insert(queryTable.query.stats[1].filters, { id = entry.tradeModId, value = { weight = (entry.invert == true and entry.weight * -1 or entry.weight) } }) + for _, entry in ipairs(statFilters) do + t_insert(queryTable.query.stats[1].filters, entry) filters = filters + 1 if filters == effective_max then break diff --git a/src/Modules/Common.lua b/src/Modules/Common.lua index e3e6c35abe..d4f1ce179d 100644 --- a/src/Modules/Common.lua +++ b/src/Modules/Common.lua @@ -1088,7 +1088,7 @@ local GGG_STAT_HASH32_SEED = 0xC58F1A7B -- used for calculating the trade hash from stat hash fields local GGG_TRADE_SEED = 0x02312233 ---@param stats string[] ----@param extraStat string extra stat for time-lost jewels +---@param extraStat string? extra stat for time-lost jewels ---@return integer function HashStats(stats, extraStat) if extraStat then