64df5f1f90
* update to release 7.3.1 * minor tweaks - tweaked the drop location text to display a bit more cleanly - added boss sigils to bc raid tables - added right click context menu to boss name list so you can open db to that boss (only has raids atm) - the All dungeon items list now wont precache past the normal loot * fixs and text display change - updated old locations to have new display - frame now sits on top when selected - fixed some incorrect ids * favorites button change/loot table updates - Changed how favorites buttons are saved you now alt right click them to save - Added missing new t0 sets to classic dungeon sets - Added missing new trash loot weapons to Black temple * minor changes/fixs - fixed t3 incorrect itemids - added heroic bloodforged * merg from main repo * bug fix and item removals - some quest tooltip was showing up incorrect - removed depreciated high risk patterns * swapped to using ItemButtonTemplate for popup window buttons * added all the new heirloom items * all the changes from main git up to 7.3.9 * update to version 7.3.15 * Revert "Merge branch 'main' into anch-main" This reverts commit 8db63dc268f910dcc1df31d50072735263eeda58, reversing changes made to f536a382a8fce401e0d8dcfaae8eb0a2c61f1e08.
832 lines
27 KiB
Lua
832 lines
27 KiB
Lua
local RED = "|cffff0000"
|
|
local WHITE = "|cffFFFFFF"
|
|
local GREEN = "|cff1eff00"
|
|
local ORANGE = "|cffFF8400"
|
|
local YELLOW = "|cffFFd200"
|
|
|
|
local AL = LibStub("AceLocale-3.0"):GetLocale("AtlasLoot")
|
|
|
|
-- Supported Operators
|
|
local OP_AND = "&"
|
|
-- multi-character patterns must come before single-character patterns
|
|
local RELATIONAL_OPERATORS = {">=", "<=", "<>", "<", ">", "="}
|
|
|
|
local searchCategories = {
|
|
{
|
|
Name = "Classic",
|
|
{"Dungeon", "ClassicDungeonExt", "AtlasLoot_OriginalWoW"},
|
|
{"Raid", "ClassicRaid", "AtlasLoot_OriginalWoW"},
|
|
{"Crafting", "ClassicCrafting", "AtlasLoot_Crafting_OriginalWoW"},
|
|
},
|
|
{
|
|
Name = "BurningCrusade",
|
|
{"Dungeon", "BCDungeon", "AtlasLoot_BurningCrusade"},
|
|
{"Raid", "BCRaid", "AtlasLoot_BurningCrusade"},
|
|
{"Crafting", "BCCrafting", "AtlasLoot_Crafting_TBC"},
|
|
},
|
|
{
|
|
Name = "Wrath",
|
|
{"Dungeon", "WrathDungeon", "AtlasLoot_WrathoftheLichKing"},
|
|
{"Raid", "WrathRaid", "AtlasLoot_WrathoftheLichKing"},
|
|
{"Crafting", "WrathCrafting", "AtlasLoot_Crafting_Wrath"},
|
|
}
|
|
}
|
|
|
|
-- 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",
|
|
|
|
["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
|
|
|
|
function AtlasLoot:NameMatches(name, searchText)
|
|
if self.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},
|
|
|
|
["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},
|
|
|
|
}
|
|
|
|
function AtlasLoot: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 self:NameMatches(itemDetails[1], term.name)
|
|
end
|
|
end
|
|
|
|
function AtlasLoot:ItemMatchesAllTerms(searchTerms, itemDetails)
|
|
for _, term in ipairs(searchTerms) do
|
|
if not self:ItemMatchesTerm(term, itemDetails) then
|
|
return false
|
|
end
|
|
end
|
|
|
|
return true
|
|
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
|
|
|
|
function AtlasLoot:GetItemDetails(itemId)
|
|
-- 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 = self:GetItemInfo(itemId)
|
|
return itemName, itemQuality, itemLvl, minLvl, itemEquipLoc, itemSubType, GetItemStats("item:" .. itemId)
|
|
end
|
|
|
|
local count = 0
|
|
local tablenum = 1
|
|
|
|
function AtlasLoot:AddItemToSearchResult(item, dataSource, dataID, tableNum)
|
|
AtlasLootCharDB["SearchResult"].SearchIDs = AtlasLootCharDB["SearchResult"].SearchIDs or {}
|
|
local tableCopy = self:CloneTable(item)
|
|
local inResults = AtlasLootCharDB["SearchResult"].SearchIDs[item.itemID]
|
|
if inResults then
|
|
AtlasLootCharDB["SearchResult"][inResults[1]][inResults[2]] = tableCopy
|
|
AtlasLootCharDB["SearchResult"][inResults[1]][inResults[2]].lootTable = {{dataID, dataSource, tableNum}, "Source"}
|
|
AtlasLootCharDB["SearchResult"][inResults[1]][inResults[2]][1] = inResults[3]
|
|
else
|
|
if AtlasLootCharDB["SearchResult"][tablenum] == nil then
|
|
AtlasLootCharDB["SearchResult"][tablenum] = {Name = "Page "..tablenum}
|
|
end
|
|
tinsert(AtlasLootCharDB["SearchResult"][tablenum], tableCopy)
|
|
local tNum = #AtlasLootCharDB["SearchResult"][tablenum]
|
|
AtlasLootCharDB["SearchResult"][tablenum][tNum].lootTable = {{dataID, dataSource, tableNum}, "Source"}
|
|
AtlasLootCharDB["SearchResult"][tablenum][tNum][1] = (count % 30) + 1
|
|
if item.itemID then AtlasLootCharDB["SearchResult"].SearchIDs[item.itemID] = {tablenum, tNum, AtlasLootCharDB["SearchResult"][tablenum][tNum][1]} end
|
|
count = count + 1
|
|
if (count) % 30 == 0 then
|
|
tablenum = tablenum + 1
|
|
end
|
|
end
|
|
end
|
|
|
|
local showSearch
|
|
function AtlasLoot:ProcessItem(data)
|
|
if not data then return end
|
|
local itemData, dataID, tableNum, searchTerms, searchText = unpack(data)
|
|
if type(itemData) == "table" then
|
|
local itemID = itemData.itemID
|
|
local spellID = itemData.spellID
|
|
if spellID then
|
|
self:ItemsLoading(-1)
|
|
local spellName = GetSpellInfo(spellID)
|
|
if self:NameMatches(spellName, searchText) then
|
|
self:AddItemToSearchResult(itemData, "AtlasLoot_Data", dataID, tableNum)
|
|
if not showSearch then
|
|
self:ShowSearchResult()
|
|
showSearch = true
|
|
end
|
|
self:ItemFrameRefresh()
|
|
end
|
|
elseif itemID then
|
|
local function nextItem(item)
|
|
self:ItemsLoading(-1)
|
|
local itemDetails = {self:GetItemDetails(itemID)}
|
|
itemDetails[1] = item:GetName()
|
|
if not itemDetails[1] then return end
|
|
if self:ItemMatchesAllTerms(searchTerms, itemDetails) then
|
|
self:AddItemToSearchResult(itemData, "AtlasLoot_Data", dataID, tableNum)
|
|
if not showSearch then
|
|
self:ShowSearchResult()
|
|
showSearch = true
|
|
else
|
|
self:ItemFrameRefresh()
|
|
end
|
|
end
|
|
end
|
|
local item = Item:CreateFromID(itemID)
|
|
if item then
|
|
if searchTerms.relational and not item:GetInfo() then
|
|
item:ContinueOnLoad(function(item)
|
|
nextItem(item)
|
|
end)
|
|
nextItem(item)
|
|
else
|
|
nextItem(item)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local itemList = {}
|
|
|
|
function AtlasLoot:DoSearch(searchText)
|
|
AtlasLootCharDB["SearchResult"] = {Name = "Search Result" , Type = "Search"}
|
|
count = 0
|
|
tablenum = 1
|
|
showSearch = false
|
|
|
|
wipe(itemList)
|
|
|
|
local searchTerms = ParseQuery(searchText)
|
|
for dataID, data in pairs(AtlasLoot_Data) do
|
|
if self.db.profile.SearchOn[data.Type] and self.db.profile.SearchOn[data.Type][1] or (self.db.profile.SearchAscensionVanity and data.Module == "AtlasLoot_Ascension_Vanity") then
|
|
for tableNum, t in ipairs(data) do
|
|
for _, itemData in pairs(t) do
|
|
if type(itemData) == "table" then
|
|
if itemData.itemID or itemData.spellID then
|
|
if data.Type then
|
|
itemData.Type = data.Type
|
|
end
|
|
if self.db.profile.showdropLocationOnSearch then
|
|
itemData.dropLoc = {data.DisplayName or data.Name, t.Name}
|
|
end
|
|
tinsert(itemList, {{itemData, dataID, tableNum, searchTerms, searchText}})
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
-- rate limit tied to half the current frame rate
|
|
self:ItemsLoading(#itemList)
|
|
local maxDuration = 500/GetFramerate()
|
|
local startTime = debugprofilestop()
|
|
local function continue()
|
|
startTime = debugprofilestop()
|
|
local task = tremove(itemList)
|
|
while (task) do
|
|
self:ProcessItem(task[1])
|
|
if (debugprofilestop() - startTime > maxDuration) then
|
|
Timer.After(0, continue)
|
|
return
|
|
end
|
|
task = tremove(itemList)
|
|
end
|
|
end
|
|
|
|
return continue()
|
|
end
|
|
|
|
function AtlasLoot:ShowSearchResult()
|
|
self:ShowItemsFrame("SearchResult", "AtlasLootCharDB", 1)
|
|
end
|
|
|
|
function AtlasLoot:Search(text)
|
|
if not text then
|
|
return
|
|
end
|
|
text = strtrim(text)
|
|
if text == "" then
|
|
return
|
|
end
|
|
|
|
if self.db.profile.SearchAscensionDB then
|
|
return OpenAscensionDBURL("?search="..text)
|
|
end
|
|
|
|
-- Decide if we need load all modules or just specified ones
|
|
local allDisabled = true
|
|
if allDisabled then
|
|
for _, module in pairs(self.db.profile.SearchOn) do
|
|
if module and module[1] or self.db.profile.SearchAscensionVanity 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. Right click search to select modules the more selected the longer it will take to search"])
|
|
return
|
|
end
|
|
|
|
for _, cat in pairs(self.db.profile.SearchOn) do
|
|
if type(cat) == "table" and cat[1] and not IsAddOnLoaded(cat[2]) then
|
|
LoadAddOn(cat[2])
|
|
end
|
|
end
|
|
|
|
self:DoSearch(text)
|
|
end
|
|
|
|
function AtlasLoot:ShowSearchOptions(button, point)
|
|
if self.Dewdrop:IsOpen(button) then
|
|
self.Dewdrop:Close(1)
|
|
else
|
|
local setOptions = function()
|
|
self.Dewdrop:AddLine("text", WHITE.."Search Categories", "isTitle", true, "notCheckable", true)
|
|
for expac, cat in pairs(searchCategories) do
|
|
self.Dewdrop:AddLine( "text", YELLOW..cat.Name, "isTitle", true, "notCheckable", true)
|
|
for _, data in ipairs(cat) do
|
|
self.db.profile.SearchOn[data[2]] = self.db.profile.SearchOn[data[2]] or {false, data[3]}
|
|
self.Dewdrop:AddLine(
|
|
"text", data[1],
|
|
"checked", self.db.profile.SearchOn[data[2]] and self.db.profile.SearchOn[data[2]][1],
|
|
"func", function()
|
|
self.db.profile.SearchOn[data[2]][1] = not self.db.profile.SearchOn[data[2]][1]
|
|
end)
|
|
end
|
|
end
|
|
self.Dewdrop:AddLine("text", AL["Search options"], "isTitle", true, "notCheckable", true)
|
|
self.Dewdrop:AddLine("text", AL["Ascension Vanity Collection"], "checked", self.db.profile.SearchAscensionVanity, "tooltipTitle", AL["Ascension Vanity Collection"], "tooltipText",
|
|
AL["If checked, AtlasLoot will search Ascension Vanity Collection"], "func", function()
|
|
self.db.profile.SearchAscensionVanity = not self.db.profile.SearchAscensionVanity
|
|
end)
|
|
self.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)
|
|
self.Dewdrop:AddLine("text", AL["Search AscensionDB"], "checked", self.db.profile.SearchAscensionDB, "tooltipTitle", AL["Partial matching"], "tooltipText",
|
|
AL["If checked, AtlasLoot will open a browser window and search AscensionDB"], "func", function()
|
|
self.db.profile.SearchAscensionDB = not self.db.profile.SearchAscensionDB
|
|
end)
|
|
end
|
|
self.Dewdrop:Open(button, 'point', function(parent)
|
|
if point then
|
|
return point[1] , point[2]
|
|
else
|
|
return "BOTTOMLEFT", "BOTTOMRIGHT"
|
|
end
|
|
end, "children", setOptions)
|
|
end
|
|
end |