From 51a5aeaf0c1b2086da09f592bd015fedb698d8d1 Mon Sep 17 00:00:00 2001 From: praxis <889+praxis@noreply.octowow.st> Date: Sun, 13 Sep 2026 09:50:42 +0000 Subject: [PATCH] Add libs/libspell.lua --- libs/libspell.lua | 153 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 libs/libspell.lua diff --git a/libs/libspell.lua b/libs/libspell.lua new file mode 100644 index 0000000..e99de82 --- /dev/null +++ b/libs/libspell.lua @@ -0,0 +1,153 @@ +-- load pfUI environment +setfenv(1, pfUI:GetEnvironment()) + +-- return instantly when another libspell is already active +if pfUI.api.libspell then return end + +local scanner = libtipscan:GetScanner("libspell") +local libspell = {} + +-- [ 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" +local spellmaxrank = {} +function libspell.GetSpellMaxRank(name) + local cache = spellmaxrank[name] + if cache then return cache[1], cache[2] end + local name = string.lower(name) + + 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 name == string.lower(spellName) 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 +local spellindex = {} +function libspell.GetSpellIndex(name, rank) + if not name then return end + name = string.lower(name) + local cache = spellindex[name..(rank and ("("..rank..")") or "")] + if cache then return cache[1], cache[2] 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 == string.lower(spellName) then + spellindex[name.."("..rank..")"] = { id, bookType } + return id, bookType + elseif not rank and name == string.lower(spellName) then + spellindex[name] = { id, bookType } + return id, bookType + end + end + end + + spellindex[name..(rank and ("("..rank..")") or "")] = { 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 +-- [number] The numeric spell-id of the spell +-- [number] The type of the spellbook that the spell is in +local spellinfo = {} +function libspell.GetSpellInfo(index, bookType) + local cache = spellinfo[index] + if cache then return cache[1], cache[2], cache[3], cache[4], cache[5], cache[6], cache[7], cache[8] end + + local name, rank, id + local icon = "" + local castingTime = 0 + local minRange = 0 + local 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) + + -- correct name in case of wrong upper/lower cases + if id and bookType then + name = GetSpellName(id, bookType) + end + else + if not bookType or (bookType ~= BOOKTYPE_SPELL and bookType ~= BOOKTYPE_PET) then + return nil + end + name, rank = GetSpellName(index, bookType) + id, bookType = libspell.GetSpellIndex(name, rank) + end + + if name and id then + icon = GetSpellTexture(id, bookType) + end + + if id then + scanner:SetSpell(id, bookType) + local _, sec = scanner:Find(gsub(SPELL_CAST_TIME_SEC, "%%.3g", "%(.+%)"), false) + local _, min = scanner:Find(gsub(SPELL_CAST_TIME_MIN, "%%.3g", "%(.+%)"), false) + local _, range = scanner:Find(gsub(SPELL_RANGE, "%%s", "%(.+%)"), false) + + castingTime = (tonumber(sec) or tonumber(min) or 0) * 1000 + if range then + local _, _, min, max = string.find(range, "(.+)-(.+)") + if min and max then + minRange = tonumber(min) + maxRange = tonumber(max) + else + minRange = 0 + maxRange = tonumber(range) + end + end + end + + spellinfo[index] = { name, rank, icon, castingTime, minRange, maxRange, id, bookType } + return name, rank, icon, castingTime, minRange, maxRange, id, bookType +end + +-- Reset all spell caches whenever new spells are learned/unlearned +local resetcache = CreateFrame("Frame") +resetcache:RegisterEvent("LEARNED_SPELL_IN_TAB") +resetcache:SetScript("OnEvent", function() + spellmaxrank, spellindex, spellinfo = {}, {}, {} +end) + +-- add libspell to pfUI API +pfUI.api.libspell = libspell