201 lines
6.9 KiB
Lua
201 lines
6.9 KiB
Lua
local _G = ShaguTweaks.GetGlobalEnv()
|
|
local API = ShaguTweaks.API
|
|
local libtipscan = ShaguTweaks.libtipscan
|
|
|
|
-- Current ClassicAPI supplies spell metadata directly. Create the tooltip
|
|
-- scanner only if that surface is unavailable for a specific lookup.
|
|
local scanner
|
|
local function GetScanner()
|
|
if not scanner then
|
|
scanner = libtipscan:GetScanner("libspell")
|
|
end
|
|
return scanner
|
|
end
|
|
|
|
local libspell = {}
|
|
local spellmaxrank = {}
|
|
local spellindex = {}
|
|
local spellinfo = {}
|
|
|
|
local function ClearSpellCaches()
|
|
spellmaxrank = {}
|
|
spellindex = {}
|
|
spellinfo = {}
|
|
end
|
|
|
|
-- Learned ranks and talent/trainer changes can invalidate both successful and
|
|
-- negative spellbook lookups. Without clearing these caches, a spell queried
|
|
-- before it was learned can remain "missing" until the next UI reload.
|
|
local events = CreateFrame("Frame")
|
|
events:RegisterEvent("LEARNED_SPELL_IN_TAB")
|
|
if API.eventutils and _G.C_EventUtils
|
|
and _G.C_EventUtils.IsEventValid("SPELLS_CHANGED") then
|
|
events:RegisterEvent("SPELLS_CHANGED")
|
|
end
|
|
events:SetScript("OnEvent", ClearSpellCaches)
|
|
|
|
-- [ GetSpellMaxRank ]
|
|
-- Returns the maximum rank of a players spell.
|
|
-- 'name' [string] spellname to query
|
|
-- return: [string],[number] maximum rank in characters and the number
|
|
-- e.g "Rank 1" and "1"
|
|
function libspell.GetSpellMaxRank(name)
|
|
local cache = spellmaxrank[name]
|
|
if cache then return cache[1], cache[2] end
|
|
|
|
-- ClassicAPI resolves a spell name to the highest known rank in native code.
|
|
local _, apiRank = API.GetSpellInfo(name)
|
|
if apiRank and apiRank ~= "" then
|
|
local _, _, numRank = string.find(apiRank, " (%d+)$")
|
|
local rankNumber = tonumber(numRank) or 0
|
|
spellmaxrank[name] = { apiRank, rankNumber }
|
|
return apiRank, rankNumber
|
|
end
|
|
|
|
local rank = { 0, nil}
|
|
for i = 1, GetNumSpellTabs() do
|
|
local _, _, offset, num = GetSpellTabInfo(i)
|
|
local bookType = BOOKTYPE_SPELL
|
|
for id = offset + 1, offset + num do
|
|
local spellName, spellRank = GetSpellName(id, bookType)
|
|
if spellName == name then
|
|
if not rank[2] then rank[2] = spellRank end
|
|
|
|
local _, _, numRank = string.find(spellRank, " (%d+)$")
|
|
if numRank and tonumber(numRank) > rank[1] then
|
|
rank = { tonumber(numRank), spellRank}
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
spellmaxrank[name] = { rank[2], rank[1] }
|
|
return rank[2], rank[1]
|
|
end
|
|
|
|
-- [ GetSpellIndex ]
|
|
-- Returns the spellbook index and bookid of the given spell.
|
|
-- 'name' [string] spellname to query
|
|
-- 'rank' [string] rank to query (optional)
|
|
-- return: [number],[string] spell index and spellbook id
|
|
function libspell.GetSpellIndex(name, rank)
|
|
if not name then return end
|
|
local cacheKey = name..(rank or "")
|
|
local cache = spellindex[cacheKey]
|
|
if cache then return cache[1], cache[2] end
|
|
|
|
-- Resolve name/rank to a spellID in ClassicAPI, then use its native inverse
|
|
-- spellbook lookup. This avoids two nested Lua spellbook scans.
|
|
if type(_G.FindSpellBookSlotByID) == "function" then
|
|
local query = rank and (name .. "(" .. rank .. ")") or name
|
|
local _, _, _, _, _, _, _, _, _, spellID = API.GetSpellInfo(query)
|
|
if spellID then
|
|
local slot, bookType = _G.FindSpellBookSlotByID(spellID)
|
|
if slot then
|
|
spellindex[cacheKey] = { slot, bookType }
|
|
return slot, bookType
|
|
end
|
|
end
|
|
end
|
|
|
|
if not rank then rank = libspell.GetSpellMaxRank(name) end
|
|
|
|
for i = 1, GetNumSpellTabs() do
|
|
local _, _, offset, num = GetSpellTabInfo(i)
|
|
local bookType = BOOKTYPE_SPELL
|
|
for id = offset + 1, offset + num do
|
|
local spellName, spellRank = GetSpellName(id, bookType)
|
|
if rank and rank == spellRank and name == spellName then
|
|
spellindex[name..rank] = { id, bookType }
|
|
return id, bookType
|
|
elseif not rank and name == spellName then
|
|
spellindex[name] = { id, bookType }
|
|
return id, bookType
|
|
end
|
|
end
|
|
end
|
|
spellindex[cacheKey] = { nil }
|
|
return nil
|
|
end
|
|
|
|
-- [ GetSpellInfo ]
|
|
-- Returns several information about a spell.
|
|
-- 'index' [string/number] Spellname or Index of a spell in the spellbook
|
|
-- 'bookType' [string] Type of spellbook (optional)
|
|
-- return:
|
|
-- [string] Name of the spell
|
|
-- [string] Secondary text associated with the spell
|
|
-- (e.g."Rank 5", "Racial", etc.)
|
|
-- [string] Path to an icon texture for the spell
|
|
-- [number] Casting time of the spell in milliseconds
|
|
-- [number] Minimum range from the target required to cast the spell
|
|
-- [number] Maximum range from the target at which you can cast the spell
|
|
function libspell.GetSpellInfo(index, bookType)
|
|
local cacheKey = tostring(index) .. ":" .. tostring(bookType or "")
|
|
local cache = spellinfo[cacheKey]
|
|
if cache then return cache[1], cache[2], cache[3], cache[4], cache[5], cache[6] end
|
|
|
|
-- ClassicAPI resolves spell IDs, spellbook slots, spell names and explicit
|
|
-- ranks directly in native code. Use that first so normal casts never walk
|
|
-- the Lua spellbook or build a hidden tooltip.
|
|
local name, rank, icon, _, _, _, castingTime, minRange, maxRange =
|
|
API.GetSpellInfo(index, bookType)
|
|
|
|
if name then
|
|
spellinfo[cacheKey] = {
|
|
name,
|
|
rank or "",
|
|
icon or "",
|
|
castingTime or 0,
|
|
minRange or 0,
|
|
maxRange or 0,
|
|
}
|
|
return name, rank or "", icon or "", castingTime or 0,
|
|
minRange or 0, maxRange or 0
|
|
end
|
|
|
|
-- Defensive compatibility path for an older ClassicAPI build that does not
|
|
-- expose the direct GetSpellInfo resolver. This path stays cold on current
|
|
-- ClassicAPI and preserves the historical ShaguTweaks behavior.
|
|
local id
|
|
icon = ""
|
|
castingTime = 0
|
|
minRange = 0
|
|
maxRange = 0
|
|
|
|
if type(index) == "string" then
|
|
local _, _, sname, srank = string.find(index, '(.+)%((.+)%)')
|
|
name = sname or index
|
|
rank = srank or libspell.GetSpellMaxRank(name)
|
|
id, bookType = libspell.GetSpellIndex(name, rank)
|
|
else
|
|
name, rank = GetSpellName(index, bookType)
|
|
id, bookType = libspell.GetSpellIndex(name, rank)
|
|
end
|
|
|
|
if id then
|
|
icon = GetSpellTexture(id, bookType) or ""
|
|
local tip = GetScanner()
|
|
tip:SetSpell(id, bookType)
|
|
local _, sec = tip:Find(gsub(SPELL_CAST_TIME_SEC, "%%.3g", "%(.+%)"))
|
|
local _, min = tip:Find(gsub(SPELL_CAST_TIME_MIN, "%%.3g", "%(.+%)"))
|
|
local _, range = tip:Find(gsub(SPELL_RANGE, "%%s", "%(.+%)"))
|
|
castingTime = (tonumber(sec) or tonumber(min) or 0) * 1000
|
|
if range then
|
|
local _, _, low, high = string.find(range, "(.+)-(.+)")
|
|
if low and high then
|
|
minRange = tonumber(low)
|
|
maxRange = tonumber(high)
|
|
else
|
|
minRange = 0
|
|
maxRange = tonumber(range)
|
|
end
|
|
end
|
|
end
|
|
|
|
spellinfo[cacheKey] = { name, rank, icon, castingTime, minRange, maxRange }
|
|
return name, rank, icon, castingTime, minRange, maxRange
|
|
end
|
|
|
|
ShaguTweaks.libspell = libspell
|