You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

234 lines
6.0 KiB

if not WeakAuras.IsCorrectVersion() then return end
local AddonName, OptionsPrivate = ...
-- Lua APIs
local pairs, error, coroutine = pairs, error, coroutine
-- WoW APIs
local GetSpellInfo, IsSpellKnown = GetSpellInfo, IsSpellKnown
local WeakAuras = WeakAuras
local spellCache = {}
WeakAuras.spellCache = spellCache
local cache
local metaData
local bestIcon = {}
-- Builds a cache of name/icon pairs from existing spell data
-- This is a rather slow operation, so it's only done once, and the result is subsequently saved
function spellCache.Build()
if not cache then
error("spellCache has not been loaded. Call WeakAuras.spellCache.Load(...) first.")
end
if not metaData.needsRebuild then
return
end
wipe(cache)
local co = coroutine.create(function()
local id = 0
local misses = 0
while misses < 400 do
id = id + 1
local name, _, icon = GetSpellInfo(id)
if(icon == 136243) then -- 136243 is the a gear icon, we can ignore those spells
misses = 0;
elseif name and name ~= "" then
cache[name] = cache[name] or {}
cache[name].spells = cache[name].spells or {}
cache[name].spells[id] = icon
misses = 0
else
misses = misses + 1
end
coroutine.yield()
end
if WeakAuras.IsRetail() then
for _, category in pairs(GetCategoryList()) do
local total = GetCategoryNumAchievements(category, true)
for i = 1, total do
local id,name,_,_,_,_,_,_,_,iconID = GetAchievementInfo(category, i)
if name and iconID then
cache[name] = cache[name] or {}
cache[name].achievements = cache[name].achievements or {}
cache[name].achievements[id] = iconID
end
end
coroutine.yield()
end
end
-- Updates the icon cache with whatever icons WeakAuras core has actually used.
-- This helps keep name<->icon matches relevant.
for name, icons in pairs(WeakAurasSaved.dynamicIconCache) do
if WeakAurasSaved.dynamicIconCache[name] then
for spellId, icon in pairs(WeakAurasSaved.dynamicIconCache[name]) do
spellCache.AddIcon(name, spellId, icon)
end
end
end
metaData.needsRebuild = false
end)
OptionsPrivate.Private.dynFrame:AddAction("spellCache", co)
end
function spellCache.GetIcon(name)
if (name == nil) then
return nil;
end
if cache then
if (bestIcon[name]) then
return bestIcon[name]
end
local icons = cache[name]
local bestMatch = nil
if (icons) then
if (icons.spells) then
for spellId, icon in pairs(icons.spells) do
if not bestMatch or (type(spellId) == "number" and IsSpellKnown(spellId)) then
bestMatch = spellId
end
end
end
end
bestIcon[name] = bestMatch and icons.spells[bestMatch];
return bestIcon[name];
else
error("spellCache has not been loaded. Call WeakAuras.spellCache.Load(...) first.")
end
end
function spellCache.GetSpellsMatching(name)
if cache[name] then
return cache[name].spells
end
end
function spellCache.AddIcon(name, id, icon)
if cache then
if name then
cache[name] = cache[name] or {}
cache[name].spells = cache[name].spells or {}
if id and icon then
cache[name].spells[id] = icon
end
end
else
error("spellCache has not been loaded. Call WeakAuras.spellCache.Load(...) first.")
end
end
function spellCache.Get()
if cache then
return cache
else
error("spellCache has not been loaded. Call WeakAuras.spellCache.Load(...) first.")
end
end
function spellCache.Load(data)
metaData = data
cache = metaData.spellCache
local _, build = GetBuildInfo();
local locale = GetLocale();
local version = WeakAuras.versionString
local num = 0;
for i,v in pairs(cache) do
num = num + 1;
end
if(num < 39000 or metaData.locale ~= locale or metaData.build ~= build or metaData.version ~= version or not metaData.spellCacheAchivements) then
metaData.build = build;
metaData.locale = locale;
metaData.version = version;
metaData.spellCacheAchivements = true
metaData.needsRebuild = true
wipe(cache)
end
end
-- This function computes the Levenshtein distance between two strings
-- It is used in this program to match spell icon textures with "good" spell names; i.e.,
-- spell names that are very similar to the name of the texture
local function Lev(str1, str2)
local matrix = {};
for i=0, str1:len() do
matrix[i] = {[0] = i};
end
for j=0, str2:len() do
matrix[0][j] = j;
end
for j=1, str2:len() do
for i =1, str1:len() do
if(str1:sub(i, i) == str2:sub(j, j)) then
matrix[i][j] = matrix[i-1][j-1];
else
matrix[i][j] = math.min(matrix[i-1][j], matrix[i][j-1], matrix[i-1][j-1]) + 1;
end
end
end
return matrix[str1:len()][str2:len()];
end
function spellCache.BestKeyMatch(nearkey)
local bestKey = "";
local bestDistance = math.huge;
local partialMatches = {};
if cache[nearkey] then
return nearkey
end
for key, value in pairs(cache) do
if key:lower() == nearkey:lower() then
return key
end
if(key:lower():find(nearkey:lower(), 1, true)) then
partialMatches[key] = value;
end
end
for key, value in pairs(partialMatches) do
local distance = Lev(nearkey, key);
if(distance < bestDistance) then
bestKey = key;
bestDistance = distance;
end
end
return bestKey;
end
function spellCache.CorrectAuraName(input)
if (not cache) then
error("spellCache has not been loaded. Call WeakAuras.spellCache.Load(...) first.")
end
local spellId = WeakAuras.SafeToNumber(input);
if(spellId) then
local name, _, icon = GetSpellInfo(spellId);
if(name) then
spellCache.AddIcon(name, spellId, icon)
return name, spellId;
else
return "Invalid Spell ID";
end
else
local ret = spellCache.BestKeyMatch(input);
if(ret == "") then
return "No Match Found", nil;
else
return ret, nil;
end
end
end