-- ------------------------------------------------------------------------------ -- -- TradeSkillMaster -- -- https://tradeskillmaster.com -- -- All Rights Reserved - Detailed license information included with addon. -- -- ------------------------------------------------------------------------------ -- local _, TSM = ... local ItemFilter = TSM.Init("Service.ItemFilter") local LibTSMClass = TSM.Include("LibTSMClass") local L = TSM.Include("Locale").GetTable() local Filter = LibTSMClass.DefineClass("ItemFilter") local ItemClass = TSM.Include("Data.ItemClass") local Money = TSM.Include("Util.Money") local String = TSM.Include("Util.String") local Vararg = TSM.Include("Util.Vararg") local Log = TSM.Include("Util.Log") local ItemInfo = TSM.Include("Service.ItemInfo") -- ============================================================================ -- Module Functions -- ============================================================================ function ItemFilter.New() return Filter() end -- ============================================================================ -- Filter Class -- ============================================================================ function Filter.__init(self) self._isValid = nil self._str = nil self._escapedStr = nil self._class = nil self._subClass = nil self._invSlotId = nil self._minQuality = nil self._maxQuality = nil self._minLevel = nil self._maxLevel = nil self._minItemLevel = nil self._maxItemLevel = nil self._minPrice = nil self._maxPrice = nil self._maxQuantity = nil self._uncollected = nil self._usable = nil self._upgrades = nil self._unlearned = nil self._canlearn = nil self._exactOnly = nil self._crafting = nil self._disenchant = nil self._item = nil self:_Reset() end function Filter._Reset(self) self._isValid = nil self._str = "" self._escapedStr = "" self._class = nil self._subClass = nil self._invSlotId = nil self._minQuality = -math.huge self._maxQuality = math.huge self._minLevel = 0 self._maxLevel = math.huge self._minItemLevel = 0 self._maxItemLevel = math.huge self._minPrice = 0 self._maxPrice = math.huge self._maxQuantity = math.huge self._uncollected = nil self._usable = nil self._upgrades = nil self._unlearned = nil self._canlearn = nil self._exactOnly = nil self._crafting = nil self._disenchant = nil self._item = nil end function Filter._ItemQualityToIndex(self, str) for i = 0, 7 do local text = _G["ITEM_QUALITY"..i.."_DESC"] if strlower(str) == strlower(text) then return i end end end function Filter.ParseStr(self, str) self:_Reset() local errMsg = nil local numLevelParts, numItemLevelParts, numPriceParts, numQualityParts = 0, 0, 0, 0 self._isValid = nil local hasNonCraftingPart = false for i, part in Vararg.Iterator(strsplit("/", strtrim(str))) do part = strtrim(part) if self._isValid ~= nil then -- already done iterating, but can't break / return out of a VarargIterator if strmatch(part, "^[ip]:[0-9]+") then -- request item info in case we failed due to not having it (for next time) ItemInfo.FetchInfo(part) end elseif i == 1 then -- first part must be a filter string or an item if strmatch(part, "^[ip]:[0-9]+") then local name = ItemInfo.GetName(part) local level = ItemInfo.GetMinLevel(part) local quality = ItemInfo.GetQuality(part) if not name or not level or not quality then errMsg = L["The specified item was not found."] self._isValid = false else self._exactOnly = true self._item = part self._str = strlower(name) self._escapedStr = String.Escape(self._str) self._minQuality = quality self._maxQuality = quality self._minLevel = level self._maxLevel = level self._class = ItemInfo.GetClassId(self._item) or 0 self._subClass = ItemInfo.GetSubClassId(self._item) or 0 end else self._str = strlower(part) self._escapedStr = String.Escape(self._str) end elseif part == "" then -- ignore an empty part elseif tonumber(part) then if numLevelParts == 0 then self._minLevel = tonumber(part) elseif numLevelParts == 1 then self._maxLevel = tonumber(part) else -- already have min / max level errMsg = L["The same filter was specified multiple times."] self._isValid = false end numLevelParts = numLevelParts + 1 hasNonCraftingPart = true elseif tonumber(strmatch(part, "^i(%d+)$")) then if numItemLevelParts == 0 then self._minItemLevel = tonumber(strmatch(part, "^i(%d+)$")) elseif numItemLevelParts == 1 then self._maxItemLevel = tonumber(strmatch(part, "^i(%d+)$")) else -- already have min / max item level errMsg = L["The same filter was specified multiple times."] self._isValid = false end numItemLevelParts = numItemLevelParts + 1 hasNonCraftingPart = true elseif ItemClass.GetClassIdFromClassString(part) then self._class = ItemClass.GetClassIdFromClassString(part) hasNonCraftingPart = true elseif self._class and ItemClass.GetSubClassIdFromSubClassString(part, self._class) then self._subClass = ItemClass.GetSubClassIdFromSubClassString(part, self._class) hasNonCraftingPart = true elseif ItemClass.GetInventorySlotIdFromInventorySlotString(part) then self._invSlotId = ItemClass.GetInventorySlotIdFromInventorySlotString(part) hasNonCraftingPart = true elseif self:_ItemQualityToIndex(part) then if numQualityParts == 0 then self._minQuality = self:_ItemQualityToIndex(part) elseif numQualityParts == 1 then self._maxQuality = self:_ItemQualityToIndex(part) else -- already have min / max quality errMsg = L["The same filter was specified multiple times."] self._isValid = false end numQualityParts = numQualityParts + 1 hasNonCraftingPart = true elseif Money.FromString(part) then if numPriceParts == 0 then self._maxPrice = Money.FromString(part) elseif numPriceParts == 1 then self._minPrice = self._maxPrice self._maxPrice = Money.FromString(part) else -- already have min / max price errMsg = L["The same filter was specified multiple times."] self._isValid = false end numPriceParts = numPriceParts + 1 hasNonCraftingPart = true elseif not TSM.IsWowClassic() and strlower(part) == "uncollected" then if self._uncollected then errMsg = L["The same filter was specified multiple times."] self._isValid = false end self._uncollected = true hasNonCraftingPart = true elseif strlower(part) == "usable" then if self._usable then errMsg = L["The same filter was specified multiple times."] self._isValid = false end self._usable = true hasNonCraftingPart = true elseif not TSM.IsWowClassic() and strlower(part) == "upgrades" then if self._upgrades then errMsg = L["The same filter was specified multiple times."] self._isValid = false end self._upgrades = true hasNonCraftingPart = true elseif strlower(part) == "unlearned" then if self._unlearned then errMsg = L["The same filter was specified multiple times."] self._isValid = false end if CanIMogIt and CanIMogIt.PlayerKnowsTransmog then self._unlearned = true else Log.PrintUser(L["The unlearned filter was ignored because the CanIMogIt addon was not found."]) end hasNonCraftingPart = true elseif strlower(part) == "canlearn" then if self._canlearn then errMsg = L["The same filter was specified multiple times."] self._isValid = false end if CanIMogIt and CanIMogIt.CharacterCanLearnTransmog then self._canlearn = true else Log.PrintUser(L["The canlearn filter was ignored because the CanIMogIt addon was not found."]) end hasNonCraftingPart = true elseif strlower(part) == "exact" then if self._exactOnly then errMsg = L["The same filter was specified multiple times."] self._isValid = false end self._exactOnly = true hasNonCraftingPart = true elseif tonumber(strmatch(part, "^x(%d+)$")) then self._maxQuantity = tonumber(strmatch(part, "^x(%d+)$")) if self._maxQuantity == 0 then errMsg = L["The max quantity cannot be zero."] self._isValid = false end elseif strlower(part) == "crafting" then if self._crafting or self._disenchant then errMsg = L["The same filter was specified multiple times."] self._isValid = false end self._crafting = true elseif strlower(part) == "disenchant" then if self._disenchant or self._crafting then errMsg = L["The same filter was specified multiple times."] self._isValid = false end self._disenchant = true else -- invalid part errMsg = format(L["Unknown word (%s)."], part) self._isValid = false end end if (self._crafting or self._disenchant) and hasNonCraftingPart then errMsg = L["Cannot use additional filters with /crafting or /disenchant."] self._isValid = false end if self._isValid == nil then self._isValid = true end return self._isValid, errMsg end function Filter.GetStr(self) return self._str ~= "" and self._str or nil end function Filter.GetItem(self) return self._item end function Filter.GetMinQuality(self) return self._minQuality ~= -math.huge and self._minQuality or nil end function Filter.GetMaxQuality(self) return self._maxQuality ~= math.huge and self._maxQuality or nil end function Filter.GetClass(self) return self._class end function Filter.GetSubClass(self) return self._subClass end function Filter.GetInvSlotId(self) return self._invSlotId end function Filter.GetMinLevel(self) return self._minLevel ~= 0 and self._minLevel or nil end function Filter.GetMaxLevel(self) return self._maxLevel ~= math.huge and self._maxLevel or nil end function Filter.GetMinItemLevel(self) return self._minItemLevel ~= 0 and self._minItemLevel or nil end function Filter.GetMaxItemLevel(self) return self._maxItemLevel ~= math.huge and self._maxItemLevel or nil end function Filter.GetUncollected(self) return self._uncollected end function Filter.GetUsableOnly(self) return self._usable end function Filter.GetUpgrades(self) return self._upgrades end function Filter.GetUnlearned(self) return self._unlearned end function Filter.GetCanLearn(self) return self._canlearn end function Filter.GetExactOnly(self) return self._exactOnly end function Filter.GetMaxQuantity(self) return self._maxQuantity ~= math.huge and self._maxQuantity or nil end function Filter.GetMinPrice(self) return self._minPrice ~= 0 and self._minPrice or nil end function Filter.GetMaxPrice(self) return self._maxPrice ~= math.huge and self._maxPrice or nil end function Filter.GetCrafting(self) return self._crafting end function Filter.GetDisenchant(self) return self._disenchant end function Filter.Matches(self, item, price) if not self._isValid then return false end -- check the name local name = ItemInfo.GetName(item) name = name and strlower(name) if not name or not strfind(name, self._escapedStr) or (self._exactOnly and name ~= self._str) then return false end -- check the quality local quality = ItemInfo.GetQuality(item) if not quality or quality < self._minQuality or quality > self._maxQuality then return false end -- check the item level local itemLevel = ItemInfo.GetItemLevel(item) if not itemLevel or itemLevel < self._minItemLevel or itemLevel > self._maxItemLevel then return false end -- check the required level local level = ItemInfo.GetMinLevel(item) if not level or level < self._minLevel or level > self._maxLevel then return false end -- check the item class if self._class and ItemInfo.GetClassId(item) ~= self._class then return false end -- check the item subclass if self._subClass and ItemInfo.GetSubClassId(item) ~= self._subClass then return false end -- check the inventory slot if self._invSlotId and ItemInfo.GetInvSlotId(item) ~= self._invSlotId then return false end -- check unlearned if self._unlearned and CanIMogIt:PlayerKnowsTransmog(ItemInfo.GetLink(item)) then return false end -- check canlearn if self._canlearn and CanIMogIt:CharacterCanLearnTransmog(ItemInfo.GetLink(item)) then return false end -- check the price price = price or 0 if price < self._minPrice or price > self._maxPrice then return false end -- it passed! return true end