Skip to content

Commit 7a48393

Browse files
BeardyBarberclaude
andcommitted
feat: enhance trade query checkbox controls and fix eldritch mods handling
- Fix Eldritch mods (Eater/Exarch): now properly included in require-current-mods filters for modes 2-3 by merging eldritch text lookups into explicit lookup when checkbox is enabled - Add Implicit Mods checkbox: gates implicit weight generation (modes 1-3) and implicit require-current-mods filtering (modes 2-3), giving users fine-grained control over implicit mod inclusion - Fix popup layout: shorten "Search mode:" label to "Mode:" to prevent label from extending off-screen in the TOPRIGHT-anchored control chain All checkbox behaviors now respect the four search modes: Mode 1 (Standard): weighted search Mode 2 (Upgrade): weighted + require current mods Mode 3 (Exact): require current mods only Mode 4 (Implicit Upgrade): implicit weights only Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
1 parent e3da60f commit 7a48393

1 file changed

Lines changed: 125 additions & 14 deletions

File tree

src/Classes/TradeQueryGenerator.lua

Lines changed: 125 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -927,15 +927,20 @@ function TradeQueryGeneratorClass:ExecuteQuery()
927927
self:GeneratePassiveNodeWeights(self.modData.PassiveNode)
928928
return
929929
end
930-
self:GenerateModWeights(self.modData["Explicit"])
931-
self:GenerateModWeights(self.modData["Implicit"])
930+
local tradeMode = self.calcContext.options.tradeMode or 1
931+
if tradeMode ~= 4 then
932+
self:GenerateModWeights(self.modData["Explicit"])
933+
if self.calcContext.options.includeImplicits then
934+
self:GenerateModWeights(self.modData["Implicit"])
935+
end
936+
end
932937
if self.calcContext.options.includeCorrupted then
933938
self:GenerateModWeights(self.modData["Corrupted"])
934939
end
935940
if self.calcContext.options.includeScourge then
936941
self:GenerateModWeights(self.modData["Scourge"])
937942
end
938-
if self.calcContext.options.includeEldritch then
943+
if self.calcContext.options.includeEldritch and not (tradeMode == 4 and self.calcContext.options.isUnique) then
939944
self:GenerateModWeights(self.modData["Eater"])
940945
self:GenerateModWeights(self.modData["Exarch"])
941946
end
@@ -1040,6 +1045,52 @@ local function consolidateToPseudo(modWeights)
10401045
return result
10411046
end
10421047

1048+
-- Returns {prefix=N, suffix=M} counting how many affixes on the item are crafted (bench) mods.
1049+
-- Crafted mods are identified by having a 'types' table instead of weightKey/weightVal.
1050+
function TradeQueryGeneratorClass:CountCraftedAffixes(prefixes, suffixes, affixes)
1051+
local prefixCount, suffixCount = 0, 0
1052+
for _, affix in ipairs(prefixes) do
1053+
local modId = affix.modId
1054+
if modId and modId ~= "None" and affixes[modId] and affixes[modId].types ~= nil then
1055+
prefixCount = prefixCount + 1
1056+
end
1057+
end
1058+
for _, affix in ipairs(suffixes) do
1059+
local modId = affix.modId
1060+
if modId and modId ~= "None" and affixes[modId] and affixes[modId].types ~= nil then
1061+
suffixCount = suffixCount + 1
1062+
end
1063+
end
1064+
return { prefix = prefixCount, suffix = suffixCount }
1065+
end
1066+
1067+
-- Builds count-type stat groups requiring the target item to have available crafting slots.
1068+
-- Each crafted prefix/suffix on the current item requires the buyer to have an empty OR crafted slot.
1069+
function TradeQueryGeneratorClass:BuildCraftedSlotFilters(prefixCount, suffixCount)
1070+
local result = {}
1071+
if prefixCount > 0 then
1072+
t_insert(result, {
1073+
type = "count",
1074+
value = { min = prefixCount },
1075+
filters = {
1076+
{ id = "pseudo.pseudo_number_of_empty_prefix_modifiers" },
1077+
{ id = "pseudo.pseudo_number_of_crafted_prefixes" },
1078+
}
1079+
})
1080+
end
1081+
if suffixCount > 0 then
1082+
t_insert(result, {
1083+
type = "count",
1084+
value = { min = suffixCount },
1085+
filters = {
1086+
{ id = "pseudo.pseudo_number_of_empty_suffix_modifiers" },
1087+
{ id = "pseudo.pseudo_number_of_crafted_suffixes" },
1088+
}
1089+
})
1090+
end
1091+
return result
1092+
end
1093+
10431094
function TradeQueryGeneratorClass:FinishQuery()
10441095
-- Calc original item Stats without anoint or enchant, and use that diff as a basis for default min sum.
10451096
local originalItem = self.calcContext.slot and self.itemsTab.items[self.calcContext.slot.selItemId]
@@ -1123,9 +1174,16 @@ function TradeQueryGeneratorClass:FinishQuery()
11231174

11241175
local effective_max = MAX_FILTERS - num_extra
11251176

1177+
-- Derive mode flags from the selected trade mode (1=Standard, 2=Upgrade, 3=Exact, 4=Implicit Upgrade)
1178+
local tradeMode = options.tradeMode or 1
1179+
local requireCurrentMods = tradeMode >= 2 and not (tradeMode == 4 and options.isUnique)
1180+
local includeWeights = tradeMode == 1 or tradeMode == 2
1181+
local includeImplicitWeights = tradeMode == 4
1182+
11261183
-- Collect required mod filters from the current item's mods if requested
11271184
local requiredModFilters = {}
1128-
if options.requireCurrentMods and originalItem then
1185+
local craftedSlotFilters = {}
1186+
if requireCurrentMods and originalItem then
11291187
-- Build separate normalized text -> tradeModId lookups so explicit and implicit
11301188
-- stat IDs are never confused with each other.
11311189
-- Also indexes overrideModLineSingular so "Has 1 Abyssal Socket" matches
@@ -1149,6 +1207,14 @@ function TradeQueryGeneratorClass:FinishQuery()
11491207
end
11501208
local explicitTextToId = buildTextLookup(self.modData.Explicit)
11511209
local implicitTextToId = buildTextLookup(self.modData.Implicit)
1210+
if options.includeEldritch then
1211+
for k, v in pairs(buildTextLookup(self.modData.Eater)) do
1212+
explicitTextToId[k] = explicitTextToId[k] or v
1213+
end
1214+
for k, v in pairs(buildTextLookup(self.modData.Exarch)) do
1215+
explicitTextToId[k] = explicitTextToId[k] or v
1216+
end
1217+
end
11521218

11531219
-- Build a tradeModId -> modTags lookup so we can check catalyst affectedness
11541220
-- from the trade stat ID alone, independent of line text or prefix metadata.
@@ -1221,8 +1287,19 @@ function TradeQueryGeneratorClass:FinishQuery()
12211287
end
12221288
end
12231289
addModLines(originalItem.explicitModLines, explicitTextToId, nil)
1224-
addModLines(originalItem.implicitModLines, implicitTextToId, explicitTextToId)
1225-
effective_max = math.max(0, effective_max - #requiredModFilters)
1290+
if tradeMode ~= 4 and options.includeImplicits then
1291+
addModLines(originalItem.implicitModLines, implicitTextToId, explicitTextToId)
1292+
end
1293+
if originalItem.prefixes and originalItem.suffixes and originalItem.affixes then
1294+
local craftedCounts = self:CountCraftedAffixes(originalItem.prefixes, originalItem.suffixes, originalItem.affixes)
1295+
craftedSlotFilters = self:BuildCraftedSlotFilters(craftedCounts.prefix, craftedCounts.suffix)
1296+
end
1297+
effective_max = math.max(0, effective_max - #requiredModFilters - #craftedSlotFilters)
1298+
end
1299+
1300+
-- Mode 3 (Exact): no weight search at all; Mode 4 keeps only the implicit weights already generated
1301+
if not includeWeights and not includeImplicitWeights then
1302+
self.modWeights = {}
12261303
end
12271304

12281305
local prioritizedMods = {}
@@ -1259,6 +1336,10 @@ function TradeQueryGeneratorClass:FinishQuery()
12591336
if #andFilters.filters > 0 then
12601337
t_insert(queryTable.query.stats, andFilters)
12611338
end
1339+
for _, slotFilter in ipairs(craftedSlotFilters) do
1340+
t_insert(queryTable.query.stats, slotFilter)
1341+
filters = filters + 1
1342+
end
12621343

12631344
for _, entry in ipairs(self.modWeights) do
12641345
t_insert(queryTable.query.stats[1].filters, { id = entry.tradeModId, value = { weight = (entry.invert == true and entry.weight * -1 or entry.weight) } })
@@ -1267,6 +1348,10 @@ function TradeQueryGeneratorClass:FinishQuery()
12671348
break
12681349
end
12691350
end
1351+
-- Remove the weight group if it ended up empty (e.g. Exact mode produces no weight filters)
1352+
if #queryTable.query.stats[1].filters == 0 then
1353+
table.remove(queryTable.query.stats, 1)
1354+
end
12701355
if not options.includeMirrored then
12711356
queryTable.query.filters.misc_filters = {
12721357
disabled = false,
@@ -1330,7 +1415,7 @@ function TradeQueryGeneratorClass:FinishQuery()
13301415
end
13311416

13321417
local errMsg = nil
1333-
if #queryTable.query.stats[1].filters == 0 then
1418+
if #queryTable.query.stats == 0 then
13341419
-- No mods to filter
13351420
errMsg = "Could not generate search, found no mods to search for"
13361421
end
@@ -1380,11 +1465,10 @@ function TradeQueryGeneratorClass:RequestQuery(slot, context, statWeights, callb
13801465
updateLastAnchor(controls.includeMirrored)
13811466

13821467
local existingItemForSlot = slot and self.itemsTab.items[slot.selItemId]
1383-
if existingItemForSlot and not context.slotTbl.unique then
1384-
controls.requireCurrentMods = new("CheckBoxControl", {"TOPRIGHT",lastItemAnchor,"BOTTOMRIGHT"}, {0, 5, 18}, "Require current mods:", function(state) end)
1385-
controls.requireCurrentMods.state = (self.lastRequireCurrentMods == true)
1386-
controls.requireCurrentMods.tooltipText = "Add all mods on the current item as required minimum filters in the search."
1387-
updateLastAnchor(controls.requireCurrentMods)
1468+
if existingItemForSlot then
1469+
controls.includeImplicits = new("CheckBoxControl", {"TOPRIGHT",lastItemAnchor,"BOTTOMRIGHT"}, {0, 5, 18}, "Implicit Mods:", function(state) end)
1470+
controls.includeImplicits.state = (self.lastIncludeImplicits == nil or self.lastIncludeImplicits == true)
1471+
updateLastAnchor(controls.includeImplicits)
13881472
end
13891473

13901474
if not isJewelSlot and not isAbyssalJewelSlot and includeScourge then
@@ -1405,6 +1489,26 @@ function TradeQueryGeneratorClass:RequestQuery(slot, context, statWeights, callb
14051489
updateLastAnchor(controls.includeEldritch)
14061490
end
14071491

1492+
if existingItemForSlot then
1493+
controls.tradeMode = new("DropDownControl", {"TOPLEFT",lastItemAnchor,"BOTTOMLEFT"}, {0, 5, 160, 18}, {"Standard", "Upgrade", "Exact", "Implicit Upgrade"}, function(index, value)
1494+
if context.slotTbl.unique and (index == 2 or index == 3) then
1495+
controls.tradeMode.tooltipText = "^1This mode is intended for rare items only."
1496+
else
1497+
controls.tradeMode.tooltipText = nil
1498+
end
1499+
end)
1500+
controls.tradeMode.selIndex = self.lastTradeMode or 1
1501+
-- Initialise tooltip for the persisted selection
1502+
if context.slotTbl.unique then
1503+
local sel = self.lastTradeMode or 1
1504+
if sel == 2 or sel == 3 then
1505+
controls.tradeMode.tooltipText = "^1This mode is intended for rare items only."
1506+
end
1507+
end
1508+
controls.tradeModeLabel = new("LabelControl", {"RIGHT",controls.tradeMode,"LEFT"}, {-5, 0, 0, 16}, "Mode:")
1509+
updateLastAnchor(controls.tradeMode)
1510+
end
1511+
14081512
if isJewelSlot then
14091513
controls.jewelType = new("DropDownControl", {"TOPLEFT",lastItemAnchor,"BOTTOMLEFT"}, {0, 5, 100, 18}, { "Any", "Base", "Abyss" }, function(index, value) end)
14101514
controls.jewelType.selIndex = self.lastJewelType or 1
@@ -1486,9 +1590,11 @@ function TradeQueryGeneratorClass:RequestQuery(slot, context, statWeights, callb
14861590
if controls.includeMirrored then
14871591
self.lastIncludeMirrored, options.includeMirrored = controls.includeMirrored.state, controls.includeMirrored.state
14881592
end
1489-
if controls.requireCurrentMods then
1490-
self.lastRequireCurrentMods, options.requireCurrentMods = controls.requireCurrentMods.state, controls.requireCurrentMods.state
1593+
if controls.tradeMode then
1594+
self.lastTradeMode = controls.tradeMode.selIndex
1595+
options.tradeMode = controls.tradeMode.selIndex
14911596
end
1597+
options.isUnique = context.slotTbl and context.slotTbl.unique == true
14921598
if controls.includeCorrupted then
14931599
self.lastIncludeCorrupted, options.includeCorrupted = controls.includeCorrupted.state, controls.includeCorrupted.state
14941600
end
@@ -1498,6 +1604,11 @@ function TradeQueryGeneratorClass:RequestQuery(slot, context, statWeights, callb
14981604
if controls.includeEldritch then
14991605
self.lastIncludeEldritch, options.includeEldritch = controls.includeEldritch.state, controls.includeEldritch.state
15001606
end
1607+
if controls.includeImplicits then
1608+
self.lastIncludeImplicits, options.includeImplicits = controls.includeImplicits.state, controls.includeImplicits.state
1609+
else
1610+
options.includeImplicits = true
1611+
end
15011612
if controls.includeScourge then
15021613
self.lastIncludeScourge, options.includeScourge = controls.includeScourge.state, controls.includeScourge.state
15031614
end

0 commit comments

Comments
 (0)