@@ -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
10411046end
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+
10431094function 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