Files
coa-altoholic/Altoholic-Addon/Altoholic/Loots.lua
T
2023-11-25 15:21:53 +01:00

471 lines
14 KiB
Lua

local addonName, addonTable = ...
local addon = _G[addonName]
local L = LibStub("AceLocale-3.0"):GetLocale(addonName)
local BB = LibStub("LibBabble-Boss-3.0"):GetLookupTable()
local BZ = LibStub("LibBabble-Zone-3.0"):GetLookupTable()
local BI = LibStub("LibBabble-Inventory-3.0"):GetLookupTable()
local WHITE = "|cFFFFFFFF"
local GREEN = "|cFF00FF00"
local lootTable, lootTableRev_Single, lootTableRev_Multi
local LootSourceTooltipDB = {}
local LootSourceTooltip_VERSION = 2
local DataProviders
addon.Loots = {}
local ns = addon.Loots -- ns = namespace
-- Simplified loot table containing item ID's only, based on AtlasLoot v5.09.00
lootTable = addonTable.LootTableSetup.lootTable
function ns:setupLootTable()
LootSourceTooltipDB = Altoholic.db.global.LootSourceTooltip
if not LootSourceTooltipDB.version or LootSourceTooltipDB.version ~= LootSourceTooltip_VERSION then
local lootSecundary = { -- these items are only considered second after all other sources
L["Emblem of Frost"],
L["Emblem of Triumph"],
L["Emblem of Conquest"],
L["Emblems of Valor"],
L["Emblems of Heroism"],
L["Badge of Justice"],
BZ["Vault of Archavon"],
}
local lootSecundaryCheck = {}
for k,v in pairs(lootSecundary) do lootSecundaryCheck[v]= true; end
local lastZone = {}
local lootTable_names = {}
for zName, _ in pairs(lootTable) do
tinsert( lootTable_names, zName )
end
sort(lootTable_names)
local zTable
lootTableRev_Single = {}
lootTableRev_Multi = {}
-- PRIMARY
for _, zName in pairs(lootTable_names) do
if not lootSecundaryCheck[zName] then -- check primary sources first
zTable = lootTable[zName]
for encounterName, encounterTable in pairs(zTable) do
for _, item_id in pairs(encounterTable) do
if not lootTableRev_Single[item_id] then -- insert only once
lootTableRev_Single[item_id] = format("%s\1%s", zName, encounterName)
end
if not lootTableRev_Multi[item_id] then
lootTableRev_Multi[item_id] = format("%s, %s", zName, encounterName)
else
if lastZone[item_id] and lastZone[item_id] == zName then
lootTableRev_Multi[item_id] = format("%s/ %s", lootTableRev_Multi[item_id], encounterName)
else
lootTableRev_Multi[item_id] = format("%s; %s, %s", lootTableRev_Multi[item_id], zName, encounterName)
end
end
lastZone[item_id] = zName
end
end
end
end
-- SECUNDARY
for _, zName in pairs(lootTable_names) do
if lootSecundaryCheck[zName] then -- check secundary sources next
zTable = lootTable[zName]
for encounterName, encounterTable in pairs(zTable) do
for _, item_id in pairs(encounterTable) do
if not lootTableRev_Single[item_id] then -- insert only once
lootTableRev_Single[item_id] = format("%s\1%s", zName, encounterName)
end
if not lootTableRev_Multi[item_id] then
lootTableRev_Multi[item_id] = format("%s, %s", zName, encounterName)
else
if lastZone[item_id] and lastZone[item_id] == zName then
lootTableRev_Multi[item_id] = format("%s/ %s", lootTableRev_Multi[item_id], encounterName)
else
lootTableRev_Multi[item_id] = format("%s; %s, %s", lootTableRev_Multi[item_id], zName, encounterName)
end
end
lastZone[item_id] = zName
end
end
end
end
-- LootSourceTooltip = { version = 0, single={}, multi={}, },
wipe(LootSourceTooltipDB.multi)
wipe(LootSourceTooltipDB.single)
LootSourceTooltipDB.multi = lootTableRev_Multi
LootSourceTooltipDB.single = lootTableRev_Single
LootSourceTooltipDB.version = LootSourceTooltip_VERSION
wipe(lastZone)
wipe(lootTable_names)
else -- ################################################################################################
end
lootTable[ BZ["Vault of Archavon"] ] = nil -- we ignore this one, cuz its confusing
end
function ns:GetSource(searchedID)
DataProviders = DataProviders or { -- list of sources that have a :GetSource() method
DataStore_Reputations,
DataStore_Crafts,
DataStore_Inventory,
}
-- LootSourceTooltipDB.single
if LootSourceTooltipDB.single[searchedID] then
local txt,domain, subDomain
txt = LootSourceTooltipDB.single[searchedID]
domain, subDomain = strmatch(txt, "(.+)\1(.+)")
return domain, subDomain
end
local domain, subDomain
for _, provider in pairs(DataProviders) do
domain, subDomain = provider:GetSource(searchedID)
if domain and subDomain then
return domain, subDomain
end
end
return nil
end
function ns:GetSource_multi(searchedID)
-- LootSourceTooltipDB.multi
return LootSourceTooltipDB.multi[searchedID]
end
function ns:GetSource_OLD(searchedID) -- old version
DataProviders = DataProviders or { -- list of sources that have a :GetSource() method
DataStore_Reputations,
DataStore_Crafts,
DataStore_Inventory,
}
-- extremely fast: takes from 0.3 to 3 ms max, depends on the location of the item in the table (obviously longer if the item is at the end)
for Instance, BossList in pairs(lootTable) do
for Boss, LootList in pairs(BossList) do
for itemID, _ in pairs(LootList) do
if LootList[itemID] == searchedID then
return Instance, Boss
end
end
end
end
local domain, subDomain
for _, provider in pairs(DataProviders) do
domain, subDomain = provider:GetSource(searchedID)
if domain and subDomain then
return domain, subDomain
end
end
return nil
end
local filters = addon.ItemFilters
local function ParseAltoholicLoots(OnMatch, OnNoMatch)
assert(type(OnMatch) == "function")
local count = 0
for Instance, BossList in pairs(lootTable) do
for Boss, LootList in pairs(BossList) do
for _, itemID in pairs(LootList) do
count = count + 1
filters:SetSearchedItem(itemID)
if filters:ItemPassesFilters() then
OnMatch(Instance, Boss)
else
if OnNoMatch then
OnNoMatch()
end
end
end
end
end
filters:ClearSearchedItem()
return count
end
local function ParseLPTSet(set, OnMatch, OnNoMatch)
assert(type(OnMatch) == "function")
local PT = LibStub("LibPeriodicTable-3.1")
if not PT then return 0 end -- exit if LPT is not active
-- LPT stores certain sets twice, but does not offer the possibility to differentiate entries (instances that are part of hubs)
-- So keep track of the sets we've already parsed, to avoid returning items twice.
local doneSets = {}
local count = 0
for _, list in pairs(PT:GetSetTable(set)) do
local _, domain, subdomain = strsplit(".", list.set)
if not doneSets[list.set] then -- if this set hasn't been parsed yet, proceed..
for itemID, value in pairs(list) do
if tostring(itemID) ~= "set" then
count = count + 1
filters:SetSearchedItem(itemID)
if filters:ItemPassesFilters() then
OnMatch(domain, subdomain or value) -- pass the value in case "subdomain" is nil
else
if OnNoMatch then
OnNoMatch()
end
end
end
end
end
doneSets[list.set] = true
end
filters:ClearSearchedItem()
return count
end
local allowedQueries, unknownCount
local function OnMatch(domain, subdomain)
Altoholic.Search:AddResult( {
id = filters:GetSearchedItemInfo("itemID"),
iLvl = filters:GetSearchedItemInfo("itemLevel"),
dropLocation = domain,
bossName = subdomain,
} )
end
local function Currency_OnMatch(domain, subdomain)
Altoholic.Search:AddResult( {
id = filters:GetSearchedItemInfo("itemID"),
iLvl = filters:GetSearchedItemInfo("itemLevel"),
dropLocation = domain,
bossName = subdomain.."x",
} )
end
local function OnNoMatch()
-- if FilterByExistence() then return end -- if the item exists, do nothing
if filters:TryFilter("Existence") then return end -- if the item exists, do nothing
unknownCount = unknownCount + 1
if allowedQueries > 0 then
if Altoholic.Options:Get("SearchAutoQuery") == 1 then -- if autoquery is enabled
local itemID = filters:GetSearchedItemInfo("itemID")
if not addon:IsItemUnsafe(itemID) then -- if the item is not known to be unsafe
GameTooltip:SetHyperlink("item:"..itemID..":0:0:0:0:0:0:0") -- this line queries the server for an unknown id
GameTooltip:ClearLines(); -- don't leave residual info in the tooltip after the server query
-- save ALL tested id's, clean the list in OnEnable during the next session.
-- the unsafe list will be cleaned in OnEnable, by parsing all ids and testing if getiteminfo returns a nil or not, if so, it's a definite unsafe link
addon:SaveUnsafeItem(itemID) -- save id to unsafe list
end
end
allowedQueries = allowedQueries - 1
end
end
function ns:Find()
unknownCount = 0
allowedQueries = 5
local count = ParseAltoholicLoots(OnMatch, OnNoMatch)
count = count + ParseLPTSet("InstanceLoot", OnMatch, OnNoMatch)
count = count + ParseLPTSet("InstanceLootHeroic", OnMatch, OnNoMatch)
count = count + ParseLPTSet("CurrencyItems", Currency_OnMatch, OnNoMatch)
addon.Options:Set("TotalLoots", count)
addon.Options:Set("UnknownLoots", unknownCount)
end
function ns:FindUpgrade()
local function OnMatch(domain, subdomain)
addon.Search:AddResult( {
id = filters:GetSearchedItemInfo("itemID"),
iLvl = filters:GetSearchedItemInfo("itemLevel"),
dropLocation = domain,
bossName = subdomain,
} )
end
ParseAltoholicLoots(OnMatch)
ParseLPTSet("InstanceLoot", OnMatch)
ParseLPTSet("InstanceLootHeroic", OnMatch)
ParseLPTSet("CurrencyItems", Currency_OnMatch)
end
local tooltipLines -- cache containing the text lines of the tooltip "+15 stamina, etc.."
local rawItemStats -- contains the raw stats of the item currently being searched, placed here to avoid creating/deleting the table during the search
local currentItemStats -- contains the stats of the item for which we'll try to find upgrades
local classExcludedStats
local classBaseStats
local function AddCurrentlyEquippedItem(itemID, class)
AltoTooltip:SetOwner(AltoholicFrame, "ANCHOR_LEFT")
local _, itemLink, _, itemLevel = GetItemInfo(itemID)
AltoTooltip:SetHyperlink(itemLink)
local statLine = addon.Equipment.FormatStats[class]
local numLines = AltoTooltip:NumLines()
local j=1
for _, BaseStat in pairs(classBaseStats) do
for i = 4, numLines do
local tooltipText = _G[ "AltoTooltipTextLeft" .. i]:GetText()
if tooltipText then
if string.find(tooltipText, BaseStat) ~= nil then
currentItemStats[BaseStat] = tonumber(string.sub(tooltipText, string.find(tooltipText, "%d+")))
statLine = string.gsub(statLine, "-s", WHITE .. currentItemStats[BaseStat], 1)
rawItemStats[j] = currentItemStats[BaseStat] .. "|0"
break
end
end
end
if not currentItemStats[BaseStat] then
rawItemStats[j] = "0|0"
currentItemStats[BaseStat] = 0 -- Set the current stat to zero if it was not found on the item
statLine = string.gsub(statLine, "-s", WHITE .. "0", 1)
end
j = j + 1
end
AltoTooltip:ClearLines();
-- Save currently equipped item to the results table
addon.Search:AddResult( {
id = itemID,
iLvl = itemLevel,
dropLocation = "Currently equipped",
stat1 = rawItemStats[1],
stat2 = rawItemStats[2],
stat3 = rawItemStats[3],
stat4 = rawItemStats[4],
stat5 = rawItemStats[5],
stat6 = rawItemStats[6]
} )
end
local function MatchUpgradeByStats(itemID)
filters:SetSearchedItem(itemID)
if not filters:ItemPassesFilters() then
filters:ClearSearchedItem()
return
end
AltoTooltip:ClearLines();
AltoTooltip:SetOwner(AltoholicFrame, "ANCHOR_LEFT");
AltoTooltip:SetHyperlink(filters:GetSearchedItemInfo("itemLink"))
-- save some time by trying to find out if the item could be excluded
wipe(tooltipLines)
for i = 4, AltoTooltip:NumLines() do -- parse all tooltip lines, one by one, start at 4 since 1= item name, 2 = binds on.., 3 = type/slot/unique ..etc
-- in this first pass, save the lines into a cache, reused below
local tooltipLine = _G[ "AltoTooltipTextLeft" .. i]:GetText()
if tooltipLine then
if string.find(tooltipLine, L["Socket"]) == nil then
for _, v in pairs(classExcludedStats) do
--if string.find(tooltipLine, v, 1, true) ~= nil then return end
if string.find(tooltipLine, v) ~= nil then return end
end
tooltipLines[i] = tooltipLine
end
end
end
local statFound
local j=1
for _, BaseStat in pairs(classBaseStats) do
statFound = nil
for i, tooltipText in pairs(tooltipLines) do
--if string.find(tooltipText, BaseStat, 1, true) ~= nil then
if string.find(tooltipText, BaseStat) ~= nil then
--local stat = tonumber(string.sub(tooltipText, string.find(tooltipText, "%d+")))
local stat = tonumber(string.match(tooltipText, "%d+"))
rawItemStats[j] = stat .. "|" .. (stat - currentItemStats[BaseStat])
table.remove(tooltipLines, i) -- remove the current entry, so it won't be parsed in the next loop cycle
statFound = true
break
end
end
if not statFound then
rawItemStats[j] = "0|" .. (0 - currentItemStats[BaseStat])
end
j = j + 1
end
local iLvl = filters:GetSearchedItemInfo("itemLevel")
filters:ClearSearchedItem()
-- All conditions ok ? save it
return true, iLvl
end
-- modify this one after 3.2, to use GetItemStats
function ns:FindUpgradeByStats(currentID, class)
classExcludedStats = addon.Equipment.ExcludeStats[class]
classBaseStats = addon.Equipment.BaseStats[class]
rawItemStats = {}
currentItemStats = {}
tooltipLines = {}
AddCurrentlyEquippedItem(currentID, class)
for Instance, BossList in pairs(lootTable) do -- parse the loot table to find an upgrade
for Boss, LootList in pairs(BossList) do
for _, itemID in pairs(LootList) do
local matches, itemLevel = MatchUpgradeByStats(itemID)
if matches then
addon.Search:AddResult( {
id = itemID,
iLvl = itemLevel,
dropLocation = Instance .. ", " .. GREEN .. Boss,
stat1 = rawItemStats[1],
stat2 = rawItemStats[2],
stat3 = rawItemStats[3],
stat4 = rawItemStats[4],
stat5 = rawItemStats[5],
stat6 = rawItemStats[6]
} )
end
end
end
end
classExcludedStats = nil
classBaseStats = nil
currentItemStats = nil
tooltipLines = nil
rawItemStats = nil
end