@@ -34,6 +34,7 @@ local TradeQueryClass = newClass("TradeQuery", function(self, itemsTab)
3434 -- default set of trade item sort selection
3535 self .slotTables = { }
3636 self .pbItemSortSelectionIndex = 1
37+ self .hideResultsFailingAttributeRequirements = false
3738 self .pbCurrencyConversion = { }
3839 self .currencyConversionTradeMap = { }
3940 self .lastCurrencyConversionRequest = 0
@@ -368,6 +369,20 @@ Highest Weight - Displays the order retrieved from trade]]
368369 self .controls .itemSortSelection :SetSel (self .pbItemSortSelectionIndex , true )
369370 self .controls .itemSortSelectionLabel = new (" LabelControl" , {" TOPRIGHT" , self .controls .itemSortSelection , " TOPLEFT" }, {- 4 , 0 , 56 , 16 }, " ^7Sort By:" )
370371
372+ -- Hide fetched results that would leave unmet attribute requirements unless unchecked.
373+ local hideAttributeRequirementsLabel = " ^7Hide results failing attribute requirements"
374+ local hideAttributeRequirementsLabelWidth = DrawStringWidth (row_height - 4 , " VAR" , hideAttributeRequirementsLabel ) + 5
375+ local hideAttributeRequirementsRect = {24 + hideAttributeRequirementsLabelWidth , 0 , row_height , row_height }
376+ self .controls .hideAttributeRequirementsCheck = new (" CheckBoxControl" , {" LEFT" , self .controls .tradeTypeSelection , " RIGHT" }, hideAttributeRequirementsRect , hideAttributeRequirementsLabel , function (state )
377+ self .hideResultsFailingAttributeRequirements = state
378+ for row_idx , _ in pairs (self .resultTbl ) do
379+ self :UpdateControlsWithItems (row_idx )
380+ end
381+ end )
382+ self .controls .hideAttributeRequirementsCheck .tooltipText = " Hide fetched results when equipping the item would leave unmet Str/Dex/Int/Omniscience attribute requirements.\n Unchecked: show those results after fetching."
383+ self .hideResultsFailingAttributeRequirements = self .hideResultsFailingAttributeRequirements == true
384+ self .controls .hideAttributeRequirementsCheck .state = self .hideResultsFailingAttributeRequirements
385+
371386 -- Realm selection
372387 self .controls .realmLabel = new (" LabelControl" , {" LEFT" , self .controls .setSelect , " RIGHT" }, {18 , 0 , 20 , row_height - 4 }, " ^7Realm:" )
373388 self .controls .realm = new (" DropDownControl" , {" LEFT" , self .controls .realmLabel , " RIGHT" }, {6 , 0 , 150 , row_height }, self .realmDropList , function (index , value )
@@ -464,7 +479,9 @@ Highest Weight - Displays the order retrieved from trade]]
464479 t_insert (slotTables , { slotName = self .itemsTab .sockets [nodeId ].label , nodeId = nodeId })
465480 end
466481
467- self .controls .sectionAnchor = new (" LabelControl" , {" LEFT" , self .controls .tradeTypeSelection , " LEFT" }, {0 , row_vertical_padding + row_height , 0 , 0 }, " " )
482+ -- Base Y offset for sectionAnchor (used to preserve position when scrollbar shifts it)
483+ local sectionAnchorBaseY = row_vertical_padding + row_height
484+ self .controls .sectionAnchor = new (" LabelControl" , {" LEFT" , self .controls .tradeTypeSelection , " LEFT" }, {0 , sectionAnchorBaseY , 0 , 0 }, " " )
468485 top_pane_alignment_ref = {" TOPLEFT" , self .controls .sectionAnchor , " TOPLEFT" }
469486 local scrollBarShown = # slotTables > 21 -- clipping starts beyond this
470487 -- dynamically hide rows that are above or below the scrollBar
@@ -542,7 +559,7 @@ Highest Weight - Displays the order retrieved from trade]]
542559 local function scrollBarFunc ()
543560 self .controls .scrollBar .height = self .pane_height - 100
544561 self .controls .scrollBar :SetContentDimension (self .pane_height - 100 , self .effective_rows_height )
545- self .controls .sectionAnchor .y = - self .controls .scrollBar .offset
562+ self .controls .sectionAnchor .y = sectionAnchorBaseY - self .controls .scrollBar .offset
546563 end
547564 main :OpenPopup (pane_width , self .pane_height , " Trader" , self .controls , nil , nil , " close" , (scrollBarShown and scrollBarFunc or nil ))
548565end
@@ -626,7 +643,7 @@ function TradeQueryClass:SetStatWeights(previousSelectionList)
626643 for row_idx in pairs (self .resultTbl ) do
627644 self :UpdateControlsWithItems (row_idx )
628645 end
629- end )
646+ end )
630647 controls .cancel = new (" ButtonControl" , { " BOTTOM" , nil , " BOTTOM" }, { 0 , - 10 , 80 , 20 }, " Cancel" , function ()
631648 if previousSelectionList and # previousSelectionList > 0 then
632649 self .statSortSelectionList = copyTable (previousSelectionList , true )
@@ -719,6 +736,25 @@ function TradeQueryClass:ReduceOutput(output)
719736 return smallOutput
720737end
721738
739+ function TradeQueryClass :GetReplacementSlotName (row_idx )
740+ local slotTbl = self .slotTables [row_idx ]
741+ if not slotTbl then
742+ return nil
743+ end
744+ if slotTbl .nodeId then
745+ return " Jewel " .. tostring (slotTbl .nodeId )
746+ end
747+ if slotTbl .replacementSlotName then
748+ return slotTbl .replacementSlotName
749+ end
750+ if slotTbl .fullName then
751+ return slotTbl .fullName
752+ end
753+ if self .itemsTab .slots and self .itemsTab .slots [slotTbl .slotName ] then
754+ return slotTbl .slotName
755+ end
756+ end
757+
722758-- Method to evaluate a result by getting it's output and weight
723759function TradeQueryClass :GetResultEvaluation (row_idx , result_index , calcFunc , baseOutput )
724760 local result = self .resultTbl [row_idx ][result_index ]
@@ -738,7 +774,7 @@ function TradeQueryClass:GetResultEvaluation(row_idx, result_index, calcFunc, ba
738774 self .onlyWeightedBaseOutput [row_idx ][result_index ] = onlyWeightedBaseOutput
739775 self .lastComparedWeightList [row_idx ][result_index ] = self .statSortSelectionList
740776 end
741- local slotName = self . slotTables [ row_idx ]. nodeId and " Jewel " .. tostring ( self . slotTables [ row_idx ]. nodeId ) or self .slotTables [row_idx ].slotName
777+ local slotName = self : GetReplacementSlotName ( row_idx ) or self .slotTables [row_idx ].slotName
742778 if slotName == " Megalomaniac" then
743779 local addedNodes = {}
744780 for nodeName in (result .item_string .. " \r\n " ):gmatch (" 1 Added Passive Skill is (.-)\r ?\n " ) do
@@ -776,18 +812,34 @@ function TradeQueryClass:UpdateDropdownList(row_idx)
776812
777813 if not self .resultTbl [row_idx ] then return end
778814
779- for result_index = 1 , # self .resultTbl [row_idx ] do
780-
781- local pb_index = self .sortedResultTbl [row_idx ][result_index ].index
782- local result = self .resultTbl [row_idx ][pb_index ]
783- local price = string.format (" %s(%d %s)" , colorCodes [" CURRENCY" ], result .amount , result .currency )
784- local item = new (" Item" , result .item_string )
785- table.insert (dropdownLabels , colorCodes [item .rarity ] .. item .name .. price )
815+ -- Iterate the sorted (and potentially filtered) list so attribute-filtered rows are omitted from the dropdown
816+ for _ , sorted in ipairs (self .sortedResultTbl [row_idx ] or {}) do
817+ if sorted and sorted .index and self .resultTbl [row_idx ][sorted .index ] then
818+ local result = self .resultTbl [row_idx ][sorted .index ]
819+ local price = string.format (" %s(%d %s)" , colorCodes [" CURRENCY" ], result .amount , result .currency )
820+ local item = new (" Item" , result .item_string )
821+ table.insert (dropdownLabels , colorCodes [item .rarity ] .. item .name .. price )
822+ end
823+ end
824+ if self .controls [" resultDropdown" .. row_idx ] then
825+ self .controls [" resultDropdown" .. row_idx ].selIndex = 1
826+ self .controls [" resultDropdown" .. row_idx ]:SetList (dropdownLabels )
786827 end
787- self .controls [" resultDropdown" .. row_idx ].selIndex = 1
788- self .controls [" resultDropdown" .. row_idx ]:SetList (dropdownLabels )
789828end
790829function TradeQueryClass :UpdateControlsWithItems (row_idx )
830+ local results = self .resultTbl [row_idx ]
831+ if not results or # results == 0 then
832+ self .sortedResultTbl [row_idx ] = {}
833+ if self .controls [" resultDropdown" .. row_idx ] then
834+ self .controls [" resultDropdown" .. row_idx ]:SetList ({})
835+ self .controls [" resultDropdown" .. row_idx ].selIndex = 1
836+ end
837+ self .itemIndexTbl [row_idx ] = nil
838+ self .totalPrice [row_idx ] = nil
839+ self .controls .fullPrice .label = " Total Price: " .. self :GetTotalPriceString ()
840+ return
841+ end
842+
791843 local sortMode = self .itemSortSelectionList [self .pbItemSortSelectionIndex ]
792844 local sortedItems , errMsg = self :SortFetchResults (row_idx , sortMode )
793845 if errMsg == " MissingConversionRates" then
@@ -800,6 +852,18 @@ function TradeQueryClass:UpdateControlsWithItems(row_idx)
800852 else
801853 self :SetNotice (self .controls .pbNotice , " " )
802854 end
855+ if not sortedItems or # sortedItems == 0 then
856+ self :SetNotice (self .controls .pbNotice , " No usable results (attribute requirements)" )
857+ self .sortedResultTbl [row_idx ] = {}
858+ if self .controls [" resultDropdown" .. row_idx ] then
859+ self .controls [" resultDropdown" .. row_idx ]:SetList ({})
860+ self .controls [" resultDropdown" .. row_idx ].selIndex = 1
861+ end
862+ self .itemIndexTbl [row_idx ] = nil
863+ self .totalPrice [row_idx ] = nil
864+ self .controls .fullPrice .label = " Total Price: " .. self :GetTotalPriceString ()
865+ return
866+ end
803867
804868 self .sortedResultTbl [row_idx ] = sortedItems
805869 local pb_index = self .sortedResultTbl [row_idx ][1 ].index
827891-- Method to sort the fetched results
828892function TradeQueryClass :SortFetchResults (row_idx , mode )
829893 local calcFunc , baseOutput
894+ local attrReqCache = {}
895+ local slotName = self :GetReplacementSlotName (row_idx )
896+ local results = self .resultTbl [row_idx ]
897+ if not results or # results == 0 then
898+ return {}
899+ end
900+
901+ -- Returns true if the candidate item meets its attribute requirements when equipped
902+ local function meetsAttributeRequirements (result_index )
903+ if not self .hideResultsFailingAttributeRequirements or not slotName then
904+ return true
905+ end
906+ if attrReqCache [result_index ] ~= nil then
907+ return attrReqCache [result_index ]
908+ end
909+ if not calcFunc then
910+ calcFunc , baseOutput = self .itemsTab .build .calcsTab :GetMiscCalculator ()
911+ end
912+ local item = new (" Item" , self .resultTbl [row_idx ][result_index ].item_string )
913+ local output = calcFunc ({ repSlotName = slotName , repItem = item })
914+ local ok
915+ if output .ReqOmni then
916+ ok = (output .ReqOmni or 0 ) <= (output .Omni or 0 )
917+ else
918+ local function attrOk (reqKey , attrKey )
919+ return (output [reqKey ] or 0 ) <= (output [attrKey ] or 0 )
920+ end
921+ ok = attrOk (" ReqStr" , " Str" ) and attrOk (" ReqDex" , " Dex" ) and attrOk (" ReqInt" , " Int" )
922+ end
923+ attrReqCache [result_index ] = ok
924+ return ok
925+ end
926+
830927 local function getResultWeight (result_index )
831928 if not calcFunc then
832929 calcFunc , baseOutput = self .itemsTab .build .calcsTab :GetMiscCalculator ()
@@ -854,13 +951,17 @@ function TradeQueryClass:SortFetchResults(row_idx, mode)
854951 local newTbl = {}
855952 if mode == self .sortModes .Weight then
856953 for index , _ in pairs (self .resultTbl [row_idx ]) do
857- t_insert (newTbl , { outputAttr = index , index = index })
954+ if meetsAttributeRequirements (index ) then
955+ t_insert (newTbl , { outputAttr = index , index = index })
956+ end
858957 end
859958 return newTbl
860959 elseif mode == self .sortModes .StatValue then
861960 for result_index = 1 , # self .resultTbl [row_idx ] do
862961 -- ConPrintf("%.3f", getResultWeight(result_index))
863- t_insert (newTbl , { outputAttr = getResultWeight (result_index ), index = result_index })
962+ if meetsAttributeRequirements (result_index ) then
963+ t_insert (newTbl , { outputAttr = getResultWeight (result_index ), index = result_index })
964+ end
864965 end
865966 table.sort (newTbl , function (a ,b ) return a .outputAttr > b .outputAttr end )
866967 elseif mode == self .sortModes .StatValuePrice then
@@ -880,9 +981,11 @@ function TradeQueryClass:SortFetchResults(row_idx, mode)
880981
881982 -- scaling factor for price
882983 local k = 0.03
883- t_insert (newTbl ,
884- { outputAttr = getResultWeight (result_index ) - k * math.log (priceTable [result_index ], 10 ), index =
885- result_index })
984+ if meetsAttributeRequirements (result_index ) then
985+ t_insert (newTbl ,
986+ { outputAttr = getResultWeight (result_index ) - k * math.log (priceTable [result_index ], 10 ), index =
987+ result_index })
988+ end
886989 end
887990 table.sort (newTbl , function (a ,b ) return a .outputAttr > b .outputAttr end )
888991 elseif mode == self .sortModes .Price then
@@ -891,7 +994,9 @@ function TradeQueryClass:SortFetchResults(row_idx, mode)
891994 return nil , " MissingConversionRates"
892995 end
893996 for result_index , price in pairs (priceTable ) do
894- t_insert (newTbl , { outputAttr = price , index = result_index })
997+ if meetsAttributeRequirements (result_index ) then
998+ t_insert (newTbl , { outputAttr = price , index = result_index })
999+ end
8951000 end
8961001 table.sort (newTbl , function (a ,b ) return a .outputAttr < b .outputAttr end )
8971002 else
@@ -945,6 +1050,7 @@ function TradeQueryClass:PriceItemRowDisplay(row_idx, top_pane_alignment_ref, ro
9451050 slotTbl .slotName and (self .itemsTab .slots [slotTbl .slotName ] or
9461051 slotTbl .slotName == " Watcher's Eye" and self :findValidSlotForWatchersEye () or
9471052 slotTbl .fullName and self .itemsTab .slots [slotTbl .fullName ]) -- fullName for Abyssal Sockets
1053+ slotTbl .replacementSlotName = activeSlot and activeSlot .slotName or slotTbl .fullName or nil
9481054 local nameColor = slotTbl .unique and colorCodes .UNIQUE or " ^7"
9491055 controls [" name" .. row_idx ] = new (" LabelControl" , top_pane_alignment_ref , {0 , row_idx * (row_height + row_vertical_padding ), 100 , row_height - 4 }, nameColor .. slotTbl .slotName )
9501056 controls [" bestButton" .. row_idx ] = new (" ButtonControl" , { " LEFT" , controls [" name" .. row_idx ], " LEFT" }, {100 + 8 , 0 , 80 , row_height }, " Find best" , function ()
@@ -1076,8 +1182,10 @@ function TradeQueryClass:PriceItemRowDisplay(row_idx, top_pane_alignment_ref, ro
10761182 end )
10771183 controls [" changeButton" .. row_idx ].shown = function () return self .resultTbl [row_idx ] end
10781184 controls [" resultDropdown" .. row_idx ] = new (" DropDownControl" , { " TOPLEFT" , controls [" changeButton" .. row_idx ], " TOPRIGHT" }, {8 , 0 , 325 , row_height }, {}, function (index )
1079- self .itemIndexTbl [row_idx ] = self .sortedResultTbl [row_idx ][index ].index
1080- self :SetFetchResultReturn (row_idx , self .itemIndexTbl [row_idx ])
1185+ if self .sortedResultTbl [row_idx ] and self .sortedResultTbl [row_idx ][index ] then
1186+ self .itemIndexTbl [row_idx ] = self .sortedResultTbl [row_idx ][index ].index
1187+ self :SetFetchResultReturn (row_idx , self .itemIndexTbl [row_idx ])
1188+ end
10811189 end )
10821190 self :UpdateDropdownList (row_idx )
10831191 local function addMegalomaniacCompareToTooltipIfApplicable (tooltip , result_index )
@@ -1117,8 +1225,17 @@ function TradeQueryClass:PriceItemRowDisplay(row_idx, top_pane_alignment_ref, ro
11171225 tooltip :AddSeparator (10 )
11181226 tooltip :AddLine (16 , string.format (" ^7Price: %s %s" , result .amount , result .currency ))
11191227 end
1228+ local function getSelectedResult ()
1229+ local selected_result_index = self .itemIndexTbl [row_idx ]
1230+ local rowResults = self .resultTbl [row_idx ]
1231+ return selected_result_index and rowResults and rowResults [selected_result_index ], selected_result_index
1232+ end
11201233 controls [" importButton" .. row_idx ] = new (" ButtonControl" , { " TOPLEFT" , controls [" resultDropdown" .. row_idx ], " TOPRIGHT" }, {8 , 0 , 100 , row_height }, " Import Item" , function ()
1121- self .itemsTab :CreateDisplayItemFromRaw (self .resultTbl [row_idx ][self .itemIndexTbl [row_idx ]].item_string )
1234+ local itemResult = getSelectedResult ()
1235+ if not itemResult or not itemResult .item_string then
1236+ return
1237+ end
1238+ self .itemsTab :CreateDisplayItemFromRaw (itemResult .item_string )
11221239 local item = self .itemsTab .displayItem
11231240 -- pass "true" to not auto equip it as we will have our own logic
11241241 self .itemsTab :AddDisplayItem (true )
@@ -1133,8 +1250,8 @@ function TradeQueryClass:PriceItemRowDisplay(row_idx, top_pane_alignment_ref, ro
11331250 end )
11341251 controls [" importButton" .. row_idx ].tooltipFunc = function (tooltip )
11351252 tooltip :Clear ()
1136- local selected_result_index = self . itemIndexTbl [ row_idx ]
1137- local item_string = self . resultTbl [ row_idx ][ selected_result_index ] .item_string
1253+ local itemResult , selected_result_index = getSelectedResult ()
1254+ local item_string = itemResult and itemResult .item_string
11381255 if selected_result_index and item_string then
11391256 -- TODO: item parsing bug caught here.
11401257 -- item.baseName is nil and throws error in the following AddItemTooltip func
@@ -1150,12 +1267,13 @@ function TradeQueryClass:PriceItemRowDisplay(row_idx, top_pane_alignment_ref, ro
11501267 end
11511268 end
11521269 controls [" importButton" .. row_idx ].enabled = function ()
1153- return self .itemIndexTbl [row_idx ] and self .resultTbl [row_idx ][self .itemIndexTbl [row_idx ]].item_string ~= nil
1270+ local itemResult = getSelectedResult ()
1271+ return itemResult and itemResult .item_string ~= nil
11541272 end
11551273 -- Whisper so we can copy to clipboard
11561274 controls [" whisperButton" .. row_idx ] = new (" ButtonControl" ,
11571275 { " TOPLEFT" , controls [" importButton" .. row_idx ], " TOPRIGHT" }, { 8 , 0 , 170 , row_height }, function ()
1158- local itemResult = self . itemIndexTbl [ row_idx ] and self . resultTbl [ row_idx ][ self . itemIndexTbl [ row_idx ]]
1276+ local itemResult = getSelectedResult ()
11591277
11601278 if not itemResult then return " " end
11611279
@@ -1169,7 +1287,10 @@ function TradeQueryClass:PriceItemRowDisplay(row_idx, top_pane_alignment_ref, ro
11691287 end
11701288
11711289 end , function ()
1172- local itemResult = self .itemIndexTbl [row_idx ] and self .resultTbl [row_idx ][self .itemIndexTbl [row_idx ]]
1290+ local itemResult = getSelectedResult ()
1291+ if not itemResult then
1292+ return
1293+ end
11731294 if itemResult .whisper then
11741295 Copy (itemResult .whisper )
11751296 else
@@ -1199,7 +1320,10 @@ function TradeQueryClass:PriceItemRowDisplay(row_idx, top_pane_alignment_ref, ro
11991320 controls [" whisperButton" .. row_idx ].tooltipFunc = function (tooltip )
12001321 tooltip :Clear ()
12011322 tooltip .center = true
1202- local itemResult = self .itemIndexTbl [row_idx ] and self .resultTbl [row_idx ][self .itemIndexTbl [row_idx ]]
1323+ local itemResult = getSelectedResult ()
1324+ if not itemResult then
1325+ return
1326+ end
12031327 local text = itemResult .whisper and " Copies the item purchase whisper to the clipboard" or
12041328 " Opens the search page to show the item"
12051329 tooltip :AddLine (16 , text )
0 commit comments