Files
coa-atlasloot/AtlasLoot/Core/Search.lua
T
2022-07-08 08:04:15 -04:00

846 lines
28 KiB
Lua

local GREY = "|cff999999";
local RED = "|cffff0000";
local WHITE = "|cffFFFFFF";
local GREEN = "|cff1eff00";
local PURPLE = "|cff9F3FFF";
local BLUE = "|cff0070dd";
local ORANGE = "|cffFF8400";
local AL = LibStub("AceLocale-3.0"):GetLocale("AtlasLoot");
local modules = {"AtlasLoot_BurningCrusade", "AtlasLoot_Crafting", "AtlasLoot_OriginalWoW", "AtlasLoot_WorldEvents", "AtlasLoot_WrathoftheLichKing"};
local currentPage = 1;
local SearchResult = nil;
-- Supported Operators
local OP_AND = "&"
local BINARYOPERATORS = {OP_AND};
-- multi-character patterns must come before single-character patterns
local RELATIONAL_OPERATORS = {">=", "<=", "<>", "<", ">", "="};
-- Supported Stat Filters
local STAT_FILTERS = {
-- Base Stats
["stamina"] = "ITEM_MOD_STAMINA_SHORT",
["stam"] = "ITEM_MOD_STAMINA_SHORT",
["sta"] = "ITEM_MOD_STAMINA_SHORT",
["strength"] = "ITEM_MOD_STRENGTH_SHORT",
["str"] = "ITEM_MOD_STRENGTH_SHORT",
["agility"] = "ITEM_MOD_AGILITY_SHORT",
["agi"] = "ITEM_MOD_AGILITY_SHORT",
["intellect"] = "ITEM_MOD_INTELLECT_SHORT",
["int"] = "ITEM_MOD_INTELLECT_SHORT",
["spirit"] = "ITEM_MOD_SPIRIT_SHORT",
["spir"] = "ITEM_MOD_SPIRIT_SHORT",
["spi"] = "ITEM_MOD_SPIRIT_SHORT",
["health"] = "ITEM_MOD_HEALTH_SHORT",
["mana"] = "ITEM_MOD_MANA_SHORT",
["mp5"] = "ITEM_MOD_POWER_REGEN0_SHORT",
["mpr"] = "ITEM_MOD_POWER_REGEN0_SHORT",
["hp5"] = "ITEM_MOD_HEALTH_REGEN_SHORT",
["hpr"] = "ITEM_MOD_HEALTH_REGEN_SHORT",
-- Sockets
["socketblue"] = "EMPTY_SOCKET_BLUE",
["socketred"] = "EMPTY_SOCKET_RED",
["socketyellow"] = "EMPTY_SOCKET_YELLOW",
["socketnocolor"] = "EMPTY_SOCKET_NO_COLOR",
["socketwhite"] = "EMPTY_SOCKET_NO_COLOR",
["socketmeta"] = "EMPTY_SOCKET_META",
["meta"] = "EMPTY_SOCKET_META",
-- Secondary Stats
["attackpowerferal"] = "ITEM_MOD_FERAL_ATTACK_POWER_SHORT",
["attackpowferal"] = "ITEM_MOD_FERAL_ATTACK_POWER_SHORT",
["apferal"] = "ITEM_MOD_FERAL_ATTACK_POWER_SHORT",
["attackpower"] = "ITEM_MOD_ATTACK_POWER_SHORT",
["attackpow"] = "ITEM_MOD_ATTACK_POWER_SHORT",
["ap"] = "ITEM_MOD_ATTACK_POWER_SHORT",
["spellpower"] = "ITEM_MOD_SPELL_POWER_SHORT",
["spellpow"] = "ITEM_MOD_SPELL_POWER_SHORT",
["sp"] = "ITEM_MOD_SPELL_POWER_SHORT",
["spellpenetration"] = "ITEM_MOD_SPELL_PENETRATION_SHORT",
["spellpen"] = "ITEM_MOD_SPELL_PENETRATION_SHORT",
["spp"] = "ITEM_MOD_SPELL_PENETRATION_SHORT",
["crit"] = "ITEM_MOD_CRIT_RATING_SHORT",
["haste"] = "ITEM_MOD_HASTE_RATING_SHORT",
["hit"] = "ITEM_MOD_HIT_RATING_SHORT",
["armorpenetration"] = "ITEM_MOD_ARMOR_PENETRATION_RATING_SHORT",
["armourpenetration"] = "ITEM_MOD_ARMOR_PENETRATION_RATING_SHORT",
["armorpen"] = "ITEM_MOD_ARMOR_PENETRATION_RATING_SHORT",
["armourpen"] = "ITEM_MOD_ARMOR_PENETRATION_RATING_SHORT",
["arp"] = "ITEM_MOD_ARMOR_PENETRATION_RATING_SHORT",
["expertise"] = "ITEM_MOD_EXPERTISE_RATING_SHORT",
["exp"] = "ITEM_MOD_EXPERTISE_RATING_SHORT",
["dps"] = "ITEM_MOD_DAMAGE_PER_SECOND_SHORT",
["resilience"] = "ITEM_MOD_RESILIENCE_RATING",
["resil"] = "ITEM_MOD_RESILIENCE_RATING",
["res"] = "ITEM_MOD_RESILIENCE_RATING",
["defense"] = "ITEM_MOD_DEFENSE_SKILL_RATING_SHORT",
["def"] = "ITEM_MOD_DEFENSE_SKILL_RATING_SHORT",
["dodge"] = "ITEM_MOD_DODGE_RATING_SHORT",
["dod"] = "ITEM_MOD_DODGE_RATING_SHORT",
["block"] = "ITEM_MOD_BLOCK_RATING_SHORT",
["blockvalue"] = "ITEM_MOD_BLOCK_VALUE_SHORT",
["blockval"] = "ITEM_MOD_BLOCK_VALUE_SHORT",
["bv"] = "ITEM_MOD_BLOCK_VALUE_SHORT",
["parry"] = "ITEM_MOD_PARRY_RATING_SHORT",
-- Resistances
["armor"] = "RESISTANCE0_NAME",
["armour"] = "RESISTANCE0_NAME",
["arm"] = "RESISTANCE0_NAME",
["resistancephysical"] = "RESISTANCE0_NAME",
["resistancephys"] = "RESISTANCE0_NAME",
["resphys"] = "RESISTANCE0_NAME",
["resistanceholy"] = "RESISTANCE1_NAME",
["resholy"] = "RESISTANCE1_NAME",
["resistancefire"] = "RESISTANCE2_NAME",
["resfire"] = "RESISTANCE2_NAME",
["resistancenature"] = "RESISTANCE3_NAME",
["resnature"] = "RESISTANCE3_NAME",
["resnat"] = "RESISTANCE3_NAME",
["resistanceforst"] = "RESISTANCE4_NAME",
["resfrost"] = "RESISTANCE4_NAME",
["resistanceshadow"] = "RESISTANCE5_NAME",
["resshadow"] = "RESISTANCE5_NAME",
["resshad"] = "RESISTANCE5_NAME",
["resistancearcane"] = "RESISTANCE6_NAME",
["resarcane"] = "RESISTANCE6_NAME",
["resarc"] = "RESISTANCE6_NAME"
};
local SOCKET_FILTERS = {
["socket"] = true,
["sockets"] = true,
["gem"] = true,
["gems"] = true
};
local SOCKET_TYPES = {"EMPTY_SOCKET_BLUE", "EMPTY_SOCKET_RED", "EMPTY_SOCKET_YELLOW", "EMPTY_SOCKET_META", "EMPTY_SOCKET_NO_COLOR"};
local INFO_FILTERS = {
["ilvl"] = true,
["minlvl"] = true
-- ["type"] = true,
-- ["subtype"] = true,
};
local QUALITY_FILTERS = {
["poor"] = 0,
["common"] = 1,
["uncommon"] = 2,
["rare"] = 3,
["epic"] = 4,
["legendary"] = 5,
["artifact"] = 6,
["heirloom"] = 7
};
local SLOT_FILTERS = {
["none"] = "INVTYPE_NON_EQUIP",
["head"] = "INVTYPE_HEAD",
["neck"] = "INVTYPE_NECK",
["shoulder"] = "INVTYPE_SHOULDER",
["body"] = "INVTYPE_BODY",
["chest"] = "INVTYPE_CHEST",
["waist"] = "INVTYPE_WAIST",
["legs"] = "INVTYPE_LEGS",
["feet"] = "INVTYPE_FEET",
["wrist"] = "INVTYPE_WRIST",
["hand"] = "INVTYPE_HAND",
["finger"] = "INVTYPE_FINGER",
["trinket"] = "INVTYPE_TRINKET",
["weapon"] = "INVTYPE_WEAPON",
["shield"] = "INVTYPE_SHIELD",
["ranged"] = "INVTYPE_RANGED",
["cloak"] = "INVTYPE_CLOAK",
["2hweapon"] = "INVTYPE_2HWEAPON",
["2h"] = "INVTYPE_2HWEAPON",
["bag"] = "INVTYPE_BAG",
["tabard"] = "INVTYPE_TABARD",
["robe"] = "INVTYPE_ROBE",
["weaponmainhand"] = "INVTYPE_WEAPONMAINHAND",
["mainhand"] = "INVTYPE_WEAPONMAINHAND",
["weaponoffhand"] = "INVTYPE_WEAPONOFFHAND",
["offhand"] = "INVTYPE_WEAPONOFFHAND",
["holdable"] = "INVTYPE_HOLDABLE",
["ammo"] = "INVTYPE_AMMO",
["thrown"] = "INVTYPE_THROWN",
["rangedright"] = "INVTYPE_RANGEDRIGHT",
["quiver"] = "INVTYPE_QUIVER",
["relic"] = "INVTYPE_RELIC"
};
local TYPE_FILTERS = {
["cloth"] = "Cloth",
["leather"] = "Leather",
["mail"] = "Mail",
["plate"] = "Plate",
["shield"] = "Shields",
["libram"] = "Librams",
["idol"] = "Idols",
["totem"] = "Totems",
["sigil"] = "Sigils",
["axe1h"] = "One-Handed Axes",
["axe2h"] = "Two-Handed Axes",
["bows"] = "Bows",
["bow"] = "Bows",
["guns"] = "Guns",
["gun"] = "Guns",
["mace1h"] = "One-Handed Maces",
["mace2h"] = "Two-Handed Maces",
["polearm"] = "Polearms",
["sword1h"] = "One-Handed Swords",
["sword2h"] = "Two-Handed Swords",
["staff"] = "Staves",
["fist"] = "Fist Weapons",
["generic"] = "",
["dagger"] = "Daggers",
["thrown"] = "Thrown",
["crossbows"] = "Crossbows",
["crossbow"] = "Crossbows",
["wand"] = "Wands",
["fishingpole"] = "Fishing Poles"
}
local NON_EQUIPABLE_SLOTS = {
["INVTYPE_NON_EQUIP"] = true,
["INVTYPE_BODY"] = true,
["INVTYPE_BAG"] = true,
["INVTYPE_AMMO"] = true,
["INVTYPE_QUIVER"] = true
};
SLASH_ATLASLOOTHELP1 = "/atlasloothelp";
SlashCmdList["ATLASLOOTHELP"] = function(msg, editBox)
local function show(caption, t)
local keys = {}
for key in pairs(t) do
table.insert(keys, key)
end
table.sort(keys, function(a, b)
return a < b
end)
print(caption .. ": " .. table.concat(keys, ", "))
end
show("stat", STAT_FILTERS)
show("socket", SOCKET_FILTERS)
show("slot", SLOT_FILTERS)
show("quality", QUALITY_FILTERS)
show("info", INFO_FILTERS)
print("\nExamples:")
print("gladiator")
print("str>40")
print("gladiator&str>40")
print("str>40&ilvl>=120&ilvl<140&int>0&socket>2");
print("sp>20&quality>=rare&quality<legendary&slot=finger");
print("stam>20&minlvl<=50")
end
SLASH_ATLASLOOTSEARCH1 = "/atlaslootsearch";
SLASH_ATLASLOOTSEARCH2 = "/als";
SlashCmdList["ATLASLOOTSEARCH"] = function(search, editBox)
AtlasLootDefaultFrame:Show()
if search and search ~= '' then
AtlasLootDefaultFrameSearchBox:SetText(search)
AtlasLootDefaultFrameSearchButton:Click()
end
end
-- split one string on another (delimiter can be more than one character)
local function SplitString(str, delimiter)
local result = {}
local start = 1
local len = #str
while start <= len do
local delimPos = str:find(delimiter, start, true)
if delimPos then
result[#result + 1] = str:sub(start, delimPos - 1) -- grab up to the delimeter
start = delimPos + #delimiter -- advance past the delimiter
else
result[#result + 1] = str:sub(start) -- grab the rest of the string
break
end
end
return result
end
local function CompareNumbersByOperator(operator, left, right)
return left and right and
((operator == "<>" and left ~= right) or (operator == "<=" and left <= right) or (operator == ">=" and left >= right) or (operator == "=" and left == right) or
(operator == "<" and left < right) or (operator == ">" and left > right))
end
local function ThrowQueryError(...)
error("Error: " .. string.format(...))
end
local function IsItemStatMatch(term, stats)
local filterKey = STAT_FILTERS[term.left]
if not filterKey then
return
end
local statValue = tonumber(stats[filterKey])
if not statValue then
return
end
local searchedValue = tonumber(term.right)
if not searchedValue then
ThrowQueryError("'%s' requires a numeric argument", term.left)
end
return CompareNumbersByOperator(term.relational, statValue, searchedValue)
end
local function IsItemLevelFilterMatch(term, itemLvl)
if term.left ~= "ilvl" then
return
end
local searchedValue = tonumber(term.right);
if not searchedValue then
ThrowQueryError("ilvl search requires a numeric argument")
end
return itemLvl ~= nil and itemLvl > 0 and CompareNumbersByOperator(term.relational, itemLvl, searchedValue)
end
local function IsItemQualityMatch(term, itemQuality)
if term.left ~= "quality" then
return
end
local searchedValue = QUALITY_FILTERS[term.right]
if not searchedValue then
ThrowQueryError("unrecognized quality value \"%s\"", term.right)
end
return CompareNumbersByOperator(term.relational, itemQuality, searchedValue)
end
local function IsItemSocketMatch(term, stats)
if not SOCKET_FILTERS[term.left] then
return
end
local searchedValue = tonumber(term.right)
if not searchedValue then
ThrowQueryError("'%s' requires a numeric argument", term.left)
end
local socketCount = 0;
for _, socketType in pairs(SOCKET_TYPES) do
local statValue = tonumber(stats[socketType]);
if statValue then
socketCount = socketCount + statValue;
end
end
return CompareNumbersByOperator(term.relational, socketCount, searchedValue)
end
local function IsMinLevelFilterMatch(term, minLvl)
if term.left ~= "minlvl" then
return
end
local searchedValue = tonumber(term.right);
if not searchedValue then
ThrowQueryError("minlvl search requires a numeric argument")
end
return minLvl ~= nil and minLvl > 0 and CompareNumbersByOperator(term.relational, minLvl, searchedValue)
end
local function IsItemSlotMatch(term, itemEquipLoc)
if term.left ~= "slot" then
return
end
if term.relational ~= "=" then
ThrowQueryError("slot searches should be in the form \"slot=[slotname]\"")
end
local slot = SLOT_FILTERS[term.right]
if not slot then
ThrowQueryError("unrecognized slot name: \"%s\"", term.right)
end
return slot == itemEquipLoc
end
local function IsItemTypeMatch(term, itemEquipType)
if term.left ~= "type" then
return
end
if term.relational ~= "=" then
ThrowQueryError("type searches should be in the form \"type=[typename]\"")
end
local type = TYPE_FILTERS[term.right]
if not type then
ThrowQueryError("unrecognized type name: \"%s\"", term.right)
end
return type == itemEquipType;
end
local function IsItemDifficulty(term, itemName)
if term.left ~= "dif" then
return
end
if term.relational ~= "=" then
ThrowQueryError("difficulty searches should be in the form \"dif=[difficulty]\"")
end
if tonumber(term.right) > (#AtlasLoot_Difficulty.MythicPlus + 4) then
ThrowQueryError("difficulty should be a number: 1 - %i", #AtlasLoot_Difficulty.MythicPlus + 4)
end
local _, hasDifficulty = AL_FindId(itemName, tonumber(term.right));
return hasDifficulty;
end
local function nameMatches(name, searchText)
if AtlasLoot.db.profile.PartialMatching then
return string.find(string.lower(name), string.lower(searchText));
else
return string.lower(name) == string.lower(searchText);
end
end
local RelationalFunctions = {
["ilvl"] = {IsItemLevelFilterMatch, 3},
["minlvl"] = {IsMinLevelFilterMatch, 4},
["type"] = {IsItemTypeMatch, 6},
["slot"] = {IsItemSlotMatch, 5},
["quality"] = {IsItemQualityMatch, 2},
["gem"] = {IsItemSocketMatch, 7},
["gems"] = {IsItemSocketMatch, 7},
["socket"] = {IsItemSocketMatch, 7},
["sockets"] = {IsItemSocketMatch, 7},
-- Base Stats
["stamina"] = {IsItemStatMatch, 7},
["stam"] = {IsItemStatMatch, 7},
["sta"] = {IsItemStatMatch, 7},
["strength"] = {IsItemStatMatch, 7},
["str"] = {IsItemStatMatch, 7},
["agility"] = {IsItemStatMatch, 7},
["agi"] = {IsItemStatMatch, 7},
["intellect"] = {IsItemStatMatch, 7},
["int"] = {IsItemStatMatch, 7},
["spirit"] = {IsItemStatMatch, 7},
["spir"] = {IsItemStatMatch, 7},
["spi"] = {IsItemStatMatch, 7},
["health"] = {IsItemStatMatch, 7},
["mana"] = {IsItemStatMatch, 7},
["mp5"] = {IsItemStatMatch, 7},
["mpr"] = {IsItemStatMatch, 7},
["hp5"] = {IsItemStatMatch, 7},
["hpr"] = {IsItemStatMatch, 7},
-- Sockets
["socketblue"] = {IsItemStatMatch, 7},
["socketred"] = {IsItemStatMatch, 7},
["socketyellow"] = {IsItemStatMatch, 7},
["socketnocolor"] = {IsItemStatMatch, 7},
["socketwhite"] = {IsItemStatMatch, 7},
["socketmeta"] = {IsItemStatMatch, 7},
["meta"] = {IsItemStatMatch, 7},
-- Secondary Stats
["attackpowerferal"] = {IsItemStatMatch, 7},
["attackpowferal"] = {IsItemStatMatch, 7},
["apferal"] = {IsItemStatMatch, 7},
["attackpower"] = {IsItemStatMatch, 7},
["attackpow"] = {IsItemStatMatch, 7},
["ap"] = {IsItemStatMatch, 7},
["spellpower"] = {IsItemStatMatch, 7},
["spellpow"] = {IsItemStatMatch, 7},
["sp"] = {IsItemStatMatch, 7},
["spellpenetration"] = {IsItemStatMatch, 7},
["spellpen"] = {IsItemStatMatch, 7},
["spp"] = {IsItemStatMatch, 7},
["crit"] = {IsItemStatMatch, 7},
["haste"] = {IsItemStatMatch, 7},
["hit"] = {IsItemStatMatch, 7},
["armorpenetration"] = {IsItemStatMatch, 7},
["armourpenetration"] = {IsItemStatMatch, 7},
["armorpen"] = {IsItemStatMatch, 7},
["armourpen"] = {IsItemStatMatch, 7},
["arp"] = {IsItemStatMatch, 7},
["expertise"] = {IsItemStatMatch, 7},
["exp"] = {IsItemStatMatch, 7},
["dps"] = {IsItemStatMatch, 7},
["resilience"] = {IsItemStatMatch, 7},
["resil"] = {IsItemStatMatch, 7},
["res"] = {IsItemStatMatch, 7},
["defense"] = {IsItemStatMatch, 7},
["def"] = {IsItemStatMatch, 7},
["dodge"] = {IsItemStatMatch, 7},
["dod"] = {IsItemStatMatch, 7},
["block"] = {IsItemStatMatch, 7},
["blockvalue"] = {IsItemStatMatch, 7},
["blockval"] = {IsItemStatMatch, 7},
["bv"] = {IsItemStatMatch, 7},
["parry"] = {IsItemStatMatch, 7},
-- Resistances
["armor"] = {IsItemStatMatch, 7},
["armour"] = {IsItemStatMatch, 7},
["arm"] = {IsItemStatMatch, 7},
["resistancephysical"] = {IsItemStatMatch, 7},
["resistancephys"] = {IsItemStatMatch, 7},
["resphys"] = {IsItemStatMatch, 7},
["resistanceholy"] = {IsItemStatMatch, 7},
["resholy"] = {IsItemStatMatch, 7},
["resistancefire"] = {IsItemStatMatch, 7},
["resfire"] = {IsItemStatMatch, 7},
["resistancenature"] = {IsItemStatMatch, 7},
["resnature"] = {IsItemStatMatch, 7},
["resnat"] = {IsItemStatMatch, 7},
["resistanceforst"] = {IsItemStatMatch, 7},
["resfrost"] = {IsItemStatMatch, 7},
["resistanceshadow"] = {IsItemStatMatch, 7},
["resshadow"] = {IsItemStatMatch, 7},
["resshad"] = {IsItemStatMatch, 7},
["resistancearcane"] = {IsItemStatMatch, 7},
["resarcane"] = {IsItemStatMatch, 7},
["resarc"] = {IsItemStatMatch, 7},
["dif"] = {IsItemDifficulty, 8},
}
local function GetItemDetails(itemId, atlasName)
-- Name, Link, Quality(num), iLvl(num), minLvl(num), itemType(localized string), itemSubType(localized string), stackCount(num), itemEquipLoc(enum), texture(link to a local file), displayId(num)
local itemName, _, itemQuality, itemLvl, minLvl, _, itemSubType, _, itemEquipLoc = GetItemInfo(itemId);
if not itemName then
itemName = gsub(atlasName, "=q%d=", "")
end
return itemName, itemQuality, itemLvl, minLvl, itemEquipLoc, itemSubType, GetItemStats("item:" .. itemId)
end
local function ItemMatchesTerm(term, itemDetails)
if term.relational then
local func, arg = unpack(RelationalFunctions[term.left]);
if func then
return func(term, itemDetails[arg]);
end
return false;
else
return nameMatches(itemDetails[1], term.name);
end
end
local function ItemMatchesAllTerms(searchTerms, itemDetails)
local function IsItemEquipableMatch(itemEquipLoc)
return not AtlasLoot.db.profile.EquipableFilter or ((itemEquipLoc and itemEquipLoc ~= '' and not NON_EQUIPABLE_SLOTS[itemEquipLoc]))
end
for _, term in ipairs(searchTerms) do
if not ItemMatchesTerm(term, itemDetails) then
return false
end
end
return true and IsItemEquipableMatch(itemDetails[5])
end
local function TermsContainDifficulty(searchTerms)
for _, term in ipairs(searchTerms) do
if term.relational then
if term.left == "dif" then
return tonumber(term.right)
end
end
end
return AtlasLoot_Difficulty.Normal;
end
local function ParseTerm(termText)
for _, relational in ipairs(RELATIONAL_OPERATORS) do
local operands = SplitString(termText, relational)
if #operands == 2 then
return {
left = operands[1],
right = operands[2],
relational = relational
}
end
end
return {
name = termText
}
end
-- Parse search text into '&'-delimited search terms,
-- then parse each term on its relational operator, if present.
local function ParseQuery(searchText)
local terms = {}
for _, term in pairs(SplitString(searchText, OP_AND)) do
table.insert(terms, ParseTerm(term));
end
return terms
end
local function GetSpellName(itemId, atlasName)
local spellName = GetSpellInfo(string.sub(itemId, 2));
if spellName then
return spellName
elseif (string.sub(atlasName, 1, 2) == "=d") then
return gsub(atlasName, "=ds=", "");
else
return gsub(atlasName, "=q%d=", "");
end
end
local function DoSearch(searchText)
AtlasLootCharDB["SearchResult"] = {};
AtlasLootCharDB.LastSearchedText = searchText;
local function AddItemToSearchResult(itemId, itemType, itemName, dataID)
local lootPage = AtlasLoot_TableNames[dataID] and AtlasLoot_TableNames[dataID][1] or "Argh!";
table.insert(AtlasLootCharDB["SearchResult"], {0, itemId, itemType, itemName, lootPage, "", "", dataID .. "|" .. "\"\""});
end
local searchTerms = ParseQuery(searchText);
local equipableFilterOn = AtlasLoot.db.profile.EquipableFilter;
local difficulty = TermsContainDifficulty(searchTerms);
for dataID, data in pairs(AtlasLoot_Data) do
for _, v in ipairs(data) do
local _, itemId, itemType, atlasName = unpack(v)
if type(itemId) == "number" and itemId > 0 then
if difficulty ~= 2 then
itemId = AL_FindId(itemId, difficulty) or itemId;
end
local itemDetails = {GetItemDetails(itemId, atlasName)};
itemDetails[8] = difficulty;
if #searchTerms == 1 and searchTerms[1].name then
if nameMatches(atlasName, searchTerms[1].name) then
AddItemToSearchResult(itemId, itemType, atlasName, dataID);
end
elseif ItemMatchesAllTerms(searchTerms, itemDetails) then
AddItemToSearchResult(itemId, itemType, atlasName, dataID);
end
elseif not equipableFilterOn and itemId and itemId ~= "" and string.sub(itemId, 1, 1) == "s" then
local spellName = GetSpellName(itemId, atlasName)
if nameMatches(spellName, searchText) then
spellName = string.sub(atlasName, 1, 4) .. spellName;
AddItemToSearchResult(itemId, itemType, spellName, dataID)
end
end
end
end
end
function AtlasLoot:ShowSearchResult()
AtlasLoot_ShowItemsFrame("SearchResult", "SearchResultPage" .. currentPage, (AL["Search Result: %s"]):format(AtlasLootCharDB.LastSearchedText or ""), pFrame);
end
function AtlasLoot:Search(Text)
if not Text then
return
end
Text = strtrim(Text);
if Text == "" then
return
end
-- Decide if we need load all modules or just specified ones
local allDisabled = not self.db.profile.SearchOn.All;
if allDisabled then
for _, module in ipairs(modules) do
if self.db.profile.SearchOn[module] == true then
allDisabled = false;
break
end
end
end
if allDisabled then
DEFAULT_CHAT_FRAME:AddMessage(RED .. AL["AtlasLoot"] .. ": " .. WHITE .. AL["You don't have any module selected to search on."]);
return;
end
if self.db.profile.SearchOn.All then
AtlasLoot_LoadAllModules();
else
for k, v in pairs(self.db.profile.SearchOn) do
if k ~= "All" and v == true and not IsAddOnLoaded(k) and LoadAddOn(k) and self.db.profile.LoDNotify then
DEFAULT_CHAT_FRAME:AddMessage(GREEN .. AL["AtlasLoot"] .. ": " .. ORANGE .. k .. WHITE .. " " .. AL["sucessfully loaded."]);
end
end
end
local success, message = pcall(DoSearch, Text)
if not success then
message = message:match("[^:]+: (.*)") or message -- strip stack location
DEFAULT_CHAT_FRAME:AddMessage(RED .. AL["AtlasLoot"] .. ": " .. WHITE .. message);
elseif #AtlasLootCharDB["SearchResult"] == 0 then
local itemFilterErrorMessage = "";
if operator then
itemFilterErrorMessage = [[
Please check if you have a typo in the filter.
For help, type "/atlasloothelp".
You might also have to query the server for item informations to load them into your client's Cache.]];
end
DEFAULT_CHAT_FRAME:AddMessage(RED .. AL["AtlasLoot"] .. ": " .. WHITE .. AL["No match found for"] .. " \"" .. Text .. "\"." .. itemFilterErrorMessage);
else
currentPage = 1;
SearchResult = AtlasLoot_CategorizeWishList(AtlasLootCharDB["SearchResult"]);
AtlasLoot_ShowItemsFrame("SearchResult", "SearchResultPage1", (AL["Search Result: %s"]):format(AtlasLootCharDB.LastSearchedText or ""), pFrame);
end
end
function AtlasLoot:ShowSearchOptions(button)
local dewdrop = AceLibrary("Dewdrop-2.0");
if dewdrop:IsOpen(button) then
dewdrop:Close(1);
else
local setOptions = function()
dewdrop:AddLine("text", AL["Search on"], "isTitle", true, "notCheckable", true);
dewdrop:AddLine("text", AL["All modules"], "checked", self.db.profile.SearchOn.All, "tooltipTitle", AL["All modules"], "tooltipText",
AL["If checked, AtlasLoot will load and search across all the modules."], "func", function()
self.db.profile.SearchOn.All = not self.db.profile.SearchOn.All;
end);
for _, module in ipairs(modules) do
if IsAddOnLoadOnDemand(module) then
local title = GetAddOnMetadata(module, "title");
local notes = GetAddOnMetadata(module, "notes");
dewdrop:AddLine("text", title, "checked", self.db.profile.SearchOn.All or self.db.profile.SearchOn[module], "disabled", self.db.profile.SearchOn.All, "tooltipTitle", title,
"tooltipText", notes, "func", function()
if self.db.profile.SearchOn[module] == nil then
self.db.profile.SearchOn[module] = true;
else
self.db.profile.SearchOn[module] = nil;
end
end);
end
end
dewdrop:AddLine("text", AL["Search options"], "isTitle", true, "notCheckable", true);
dewdrop:AddLine("text", AL["Partial matching"], "checked", self.db.profile.PartialMatching, "tooltipTitle", AL["Partial matching"], "tooltipText",
AL["If checked, AtlasLoot search item names for a partial match."], "func", function()
self.db.profile.PartialMatching = not self.db.profile.PartialMatching
end);
dewdrop:AddLine("text", "Only search equipable", -- TODO: put in AL
"checked", self.db.profile.EquipableFilter, "tooltipTitle", "Equipable filter", -- TODO: put in AL
"tooltipText", "If checked, AtlasLoot only includes items that are euipable.", -- TODO: put in AL
"func", function()
self.db.profile.EquipableFilter = not self.db.profile.EquipableFilter
end);
end;
dewdrop:Open(button, 'point', function(parent)
return "BOTTOMLEFT", "BOTTOMRIGHT";
end, "children", setOptions);
end
end
function AtlasLoot:GetOriginalDataFromSearchResult(itemID)
for i, v in ipairs(AtlasLootCharDB["SearchResult"]) do
if v[2] == itemID then
AtlasLoot_ShowWishListDropDown(v[2], v[3], v[4], v[5], v[8], this, nil, v[AtlasLoot_Difficulty.DIF_SEARCH]);
end
end
end
-- Copied and modified from AtlasLoot_GetWishListPage
function AtlasLoot:GetSearchResultPage(page)
if not SearchResult then
SearchResult = AtlasLoot_CategorizeWishList(AtlasLootCharDB["SearchResult"])
end
-- Calc for maximal pages
local pageMax = math.ceil(#SearchResult / 30);
if page < 1 then
page = 1
end
if page > pageMax then
page = pageMax
end
currentPage = page;
-- Table copy
local k = 1;
local result = {};
local start = (page - 1) * 30 + 1;
for i = start, start + 29 do
if not SearchResult[i] then
break
end
SearchResult[i][1] = k;
table.insert(result, SearchResult[i]);
k = k + 1;
end
return result, pageMax;
end