diff --git a/libs/libtotem.lua b/libs/libtotem.lua index 4f0eb6e8..9836a4b3 100644 --- a/libs/libtotem.lua +++ b/libs/libtotem.lua @@ -26,59 +26,119 @@ AIR_TOTEM_SLOT = AIR_TOTEM_SLOT or 4 local _, class = UnitClass("player") local libtotem -local queue = { ["slot"] = nil, ["name"] = nil, ["start"] = nil, ["duration"] = nil, ["icon"] = nil } local active = { [1] = {}, [2] = {}, [3] = {}, [4] = {} } + +-- SpellID -> { slot, duration } mapping +-- rank-specific durations are handled via spellId directly +local spellids = { + -- FIRE (slot 1) + [1535] = { slot = FIRE_TOTEM_SLOT, duration = 5 }, -- Fire Nova Totem R1 + [8498] = { slot = FIRE_TOTEM_SLOT, duration = 5 }, -- Fire Nova Totem R2 + [8499] = { slot = FIRE_TOTEM_SLOT, duration = 5 }, -- Fire Nova Totem R3 + [11314] = { slot = FIRE_TOTEM_SLOT, duration = 5 }, -- Fire Nova Totem R4 + [11315] = { slot = FIRE_TOTEM_SLOT, duration = 5 }, -- Fire Nova Totem R5 + [8227] = { slot = FIRE_TOTEM_SLOT, duration = 120 }, -- Flametongue Totem R1 + [8249] = { slot = FIRE_TOTEM_SLOT, duration = 120 }, -- Flametongue Totem R2 + [10526] = { slot = FIRE_TOTEM_SLOT, duration = 120 }, -- Flametongue Totem R3 + [16387] = { slot = FIRE_TOTEM_SLOT, duration = 120 }, -- Flametongue Totem R4 + [8184] = { slot = FIRE_TOTEM_SLOT, duration = 120 }, -- Frost Resistance Totem R1 + [10478] = { slot = FIRE_TOTEM_SLOT, duration = 120 }, -- Frost Resistance Totem R2 + [10479] = { slot = FIRE_TOTEM_SLOT, duration = 120 }, -- Frost Resistance Totem R3 + [8190] = { slot = FIRE_TOTEM_SLOT, duration = 20 }, -- Magma Totem R1 + [10585] = { slot = FIRE_TOTEM_SLOT, duration = 20 }, -- Magma Totem R2 + [10586] = { slot = FIRE_TOTEM_SLOT, duration = 20 }, -- Magma Totem R3 + [10587] = { slot = FIRE_TOTEM_SLOT, duration = 20 }, -- Magma Totem R4 + [3599] = { slot = FIRE_TOTEM_SLOT, duration = 30 }, -- Searing Totem R1 + [6363] = { slot = FIRE_TOTEM_SLOT, duration = 35 }, -- Searing Totem R2 + [6364] = { slot = FIRE_TOTEM_SLOT, duration = 40 }, -- Searing Totem R3 + [6365] = { slot = FIRE_TOTEM_SLOT, duration = 45 }, -- Searing Totem R4 + [10437] = { slot = FIRE_TOTEM_SLOT, duration = 50 }, -- Searing Totem R5 + [10438] = { slot = FIRE_TOTEM_SLOT, duration = 55 }, -- Searing Totem R6 + + -- EARTH (slot 2) + [2484] = { slot = EARTH_TOTEM_SLOT, duration = 45 }, -- Earthbind Totem + [5730] = { slot = EARTH_TOTEM_SLOT, duration = 15 }, -- Stoneclaw Totem R1 + [6390] = { slot = EARTH_TOTEM_SLOT, duration = 15 }, -- Stoneclaw Totem R2 + [6391] = { slot = EARTH_TOTEM_SLOT, duration = 15 }, -- Stoneclaw Totem R3 + [6392] = { slot = EARTH_TOTEM_SLOT, duration = 15 }, -- Stoneclaw Totem R4 + [10427] = { slot = EARTH_TOTEM_SLOT, duration = 15 }, -- Stoneclaw Totem R5 + [10428] = { slot = EARTH_TOTEM_SLOT, duration = 15 }, -- Stoneclaw Totem R6 + [8071] = { slot = EARTH_TOTEM_SLOT, duration = 120 }, -- Stoneskin Totem R1 + [8154] = { slot = EARTH_TOTEM_SLOT, duration = 120 }, -- Stoneskin Totem R2 + [8155] = { slot = EARTH_TOTEM_SLOT, duration = 120 }, -- Stoneskin Totem R3 + [10406] = { slot = EARTH_TOTEM_SLOT, duration = 120 }, -- Stoneskin Totem R4 + [10407] = { slot = EARTH_TOTEM_SLOT, duration = 120 }, -- Stoneskin Totem R5 + [10408] = { slot = EARTH_TOTEM_SLOT, duration = 120 }, -- Stoneskin Totem R6 + [8075] = { slot = EARTH_TOTEM_SLOT, duration = 120 }, -- Strength of Earth Totem R1 + [8160] = { slot = EARTH_TOTEM_SLOT, duration = 120 }, -- Strength of Earth Totem R2 + [8161] = { slot = EARTH_TOTEM_SLOT, duration = 120 }, -- Strength of Earth Totem R3 + [10442] = { slot = EARTH_TOTEM_SLOT, duration = 120 }, -- Strength of Earth Totem R4 + [25361] = { slot = EARTH_TOTEM_SLOT, duration = 120 }, -- Strength of Earth Totem R5 + [8143] = { slot = EARTH_TOTEM_SLOT, duration = 120 }, -- Tremor Totem + + -- WATER (slot 3) + [8170] = { slot = WATER_TOTEM_SLOT, duration = 120 }, -- Disease Cleansing Totem + [8185] = { slot = WATER_TOTEM_SLOT, duration = 120 }, -- Fire Resistance Totem R1 + [10537] = { slot = WATER_TOTEM_SLOT, duration = 120 }, -- Fire Resistance Totem R2 + [10538] = { slot = WATER_TOTEM_SLOT, duration = 120 }, -- Fire Resistance Totem R3 + [5394] = { slot = WATER_TOTEM_SLOT, duration = 60 }, -- Healing Stream Totem R1 + [6375] = { slot = WATER_TOTEM_SLOT, duration = 60 }, -- Healing Stream Totem R2 + [6377] = { slot = WATER_TOTEM_SLOT, duration = 60 }, -- Healing Stream Totem R3 + [10462] = { slot = WATER_TOTEM_SLOT, duration = 60 }, -- Healing Stream Totem R4 + [10463] = { slot = WATER_TOTEM_SLOT, duration = 60 }, -- Healing Stream Totem R5 + [5675] = { slot = WATER_TOTEM_SLOT, duration = 60 }, -- Mana Spring Totem R1 + [10495] = { slot = WATER_TOTEM_SLOT, duration = 60 }, -- Mana Spring Totem R2 + [10496] = { slot = WATER_TOTEM_SLOT, duration = 60 }, -- Mana Spring Totem R3 + [10497] = { slot = WATER_TOTEM_SLOT, duration = 60 }, -- Mana Spring Totem R4 + [16190] = { slot = WATER_TOTEM_SLOT, duration = 12 }, -- Mana Tide Totem + [8166] = { slot = WATER_TOTEM_SLOT, duration = 120 }, -- Poison Cleansing Totem + + -- AIR (slot 4) + [8835] = { slot = AIR_TOTEM_SLOT, duration = 120 }, -- Grace of Air Totem R1 + [10627] = { slot = AIR_TOTEM_SLOT, duration = 120 }, -- Grace of Air Totem R2 + [8177] = { slot = AIR_TOTEM_SLOT, duration = 45 }, -- Grounding Totem + [10595] = { slot = AIR_TOTEM_SLOT, duration = 120 }, -- Nature Resistance Totem R1 + [10600] = { slot = AIR_TOTEM_SLOT, duration = 120 }, -- Nature Resistance Totem R2 + [10601] = { slot = AIR_TOTEM_SLOT, duration = 120 }, -- Nature Resistance Totem R3 + [25359] = { slot = AIR_TOTEM_SLOT, duration = 120 }, -- Tranquil Air Totem + [8512] = { slot = AIR_TOTEM_SLOT, duration = 120 }, -- Windfury Totem R1 + [10613] = { slot = AIR_TOTEM_SLOT, duration = 120 }, -- Windfury Totem R2 + [10614] = { slot = AIR_TOTEM_SLOT, duration = 120 }, -- Windfury Totem R3 + [15107] = { slot = AIR_TOTEM_SLOT, duration = 120 }, -- Windwall Totem R1 + [15421] = { slot = AIR_TOTEM_SLOT, duration = 120 }, -- Windwall Totem R2 + [15422] = { slot = AIR_TOTEM_SLOT, duration = 120 }, -- Windwall Totem R3 +} + +-- icon-based fallback table (used by CastSpell/UseAction hooks that don't have spellId) local totems = { [FIRE_TOTEM_SLOT] = { - --Fire Nova Totem (Fire) ["Spell_Fire_SealOfFire"] = {[-1] = 5}, - --Flametongue Totem (Fire) ["Spell_Nature_GuardianWard"] = {[-1] = 120}, - --Frost Resistance Totem (Fire) ["Spell_FrostResistanceTotem_01"] = {[-1] = 120}, - --Magma Totem (Fire) ["Spell_Fire_SelfDestruct"] = {[-1] = 20}, - --Searing Totem (Fire) ["Spell_Fire_SearingTotem"] = {[-1] = 55,[1] = 30,[2] = 35,[3] = 40,[4] = 45,[5] = 50,[6] = 55}, }, [EARTH_TOTEM_SLOT] = { - --Earthbind Totem (Earth) ["Spell_Nature_StrengthOfEarthTotem02"] = {[-1] = 45}, - --Stoneclaw Totem (Earth) ["Spell_Nature_StoneClawTotem"] = {[-1] = 15}, - --Stoneskin Totem (Earth) ["Spell_Nature_StoneSkinTotem"] = {[-1] = 120}, - --Strength of Earth Totem (Earth) ["Spell_Nature_EarthBindTotem"] = {[-1] = 120}, - --Tremor Totem (Earth) ["Spell_Nature_TremorTotem"] = {[-1] = 120}, }, [WATER_TOTEM_SLOT] = { - -- Disease Cleansing Totem (Water) ["Spell_Nature_DiseaseCleansingTotem"] = {[-1] = 120}, - --Fire Resistance Totem (Water) ["Spell_FireResistanceTotem_01"] = {[-1] = 120}, - --Healing Stream Totem (Water) ["INV_Spear_04"] = {[-1] = 60}, - --Mana Spring Totem (Water) ["Spell_Nature_ManaRegenTotem"] = {[-1] = 60}, - --Mana Tide Totem (Water) ["Spell_Frost_SummonWaterElemental"] = {[-1] = 12}, - --Poison Cleansing Totem (Water) ["Spell_Nature_PoisonCleansingTotem"] = {[-1] = 120}, }, [AIR_TOTEM_SLOT] = { - --Grace of Air Totem (Air) ["Spell_Nature_InvisibilityTotem"] = {[-1] = 120}, - --Grounding Totem (Air) ["Spell_Nature_GroundingTotem"] = {[-1] = 45}, - --Nature Resistance Totem (Air) ["Spell_Nature_NatureResistanceTotem"] = {[-1] = 120}, - --Tranquil Air Totem (Air) ["Spell_Nature_Brilliance"] = {[-1] = 120}, - --Windfury Totem (Air) ["Spell_Nature_Windfury"] = {[-1] = 120}, - --Windwall Totem (Air) ["Spell_Nature_EarthBind"] = {[-1] = 120}, }, } @@ -89,30 +149,16 @@ GetTotemInfo = function(id) libtotem:Clean(id) return nil end - return 1, active[id].name, active[id].start, active[id].duration, active[id].icon end if class ~= "SHAMAN" then return end libtotem = CreateFrame("Frame") -libtotem:RegisterEvent("SPELLCAST_STOP") libtotem:RegisterEvent("PLAYER_DEAD") libtotem:SetScript("OnEvent", function() if event == "PLAYER_DEAD" then - for i = 1,4 do - libtotem:Clean(i) - end - elseif event == "SPELLCAST_STOP" then - if queue.slot and queue.name then - active[queue.slot].name = queue.name - active[queue.slot].duration = queue.duration - active[queue.slot].icon = queue.icon - active[queue.slot].start = GetTime() - end - - queue.slot = nil - queue.name = nil + for i = 1, 4 do libtotem:Clean(i) end end end) @@ -125,47 +171,93 @@ libtotem.Clean = function(self, slot) active[slot].icon = nil end -libtotem.CheckAddQueue = function(self, name, rank, icon) +-- Direct SpellID commit (Nampower SPELL_GO_SELF, most accurate) +libtotem.CommitBySpellId = function(spellId, icon) + local data = spellids[spellId] + if not data then return false end + local slot = data.slot + active[slot].name = active[slot].pending_name or active[slot].name + active[slot].duration = data.duration + active[slot].icon = icon or active[slot].pending_icon + active[slot].start = GetTime() + active[slot].pending_name = nil + active[slot].pending_icon = nil + return true +end + +-- Fallback: icon-based lookup (for CastSpell/UseAction without spellId) +libtotem.CheckAddQueue = function(self, name, rank, icon, spellId) + -- if we have a spellId, just store the name/icon as pending for SPELL_GO + if spellId and spellids[spellId] then + local slot = spellids[spellId].slot + active[slot].pending_name = name + active[slot].pending_icon = icon + return true + end + + -- icon-based fallback for slot = 1, 4 do for texture, data in pairs(totems[slot]) do if string.find(icon, texture, 1) then - if rank then -- try to obtain plain rank number - _, _, rank = string.find(rank,"%s(%d+)") + if rank then + _, _, rank = string.find(rank, "%s(%d+)") end - - queue.slot = slot - queue.name = name - queue.icon = icon + local duration if rank and tonumber(rank) and data[tonumber(rank)] then - queue.duration = data[tonumber(rank)] + duration = data[tonumber(rank)] else - queue.duration = data[-1] + duration = data[-1] end - + active[slot].pending_name = name + active[slot].pending_icon = icon + active[slot].pending_duration = duration return true end end end - return nil end -- assign library to global space pfUI.api.libtotem = libtotem --- Check for totem spell casts -hooksecurefunc("CastSpell", function(id, bookType) - local name, rank, icon = libspell.GetSpellInfo(id, bookType) - if not name then return end - if libtotem:CheckAddQueue(name, rank, icon) then return end -end) +-- SPELL_GO_SELF from libdebuff: commit directly by SpellID, no queue needed +pfUI.libdebuff_spell_go_hooks = pfUI.libdebuff_spell_go_hooks or {} +pfUI.libdebuff_spell_go_hooks["libtotem"] = function(spellId) + if not spellId then return end + local data = spellids[spellId] + if not data then return end + local slot = data.slot + -- use pending name/icon if available (set by CastSpellByName hook), else GetSpellInfo + local name = active[slot].pending_name + local icon = active[slot].pending_icon + if not name and GetSpellInfo then + name = GetSpellInfo(spellId) + end + active[slot].name = name + active[slot].duration = data.duration + active[slot].icon = icon + active[slot].start = GetTime() + active[slot].pending_name = nil + active[slot].pending_icon = nil + active[slot].pending_duration = nil +end +-- Hook CastSpellByName to store pending name/icon per slot hooksecurefunc("CastSpellByName", function(effect, target) - local name, rank, icon = libspell.GetSpellInfo(effect) + local name, rank, icon, _, _, _, spellId = libspell.GetSpellInfo(effect) if not name then return end - if libtotem:CheckAddQueue(name, rank, icon) then return end + libtotem:CheckAddQueue(name, rank, icon, spellId) end) +-- Hook CastSpell to store pending name/icon per slot +hooksecurefunc("CastSpell", function(id, bookType) + local name, rank, icon, _, _, _, spellId = libspell.GetSpellInfo(id, bookType) + if not name then return end + libtotem:CheckAddQueue(name, rank, icon, spellId) +end) + +-- Hook UseAction (no spellId available, icon-based fallback) local scanner = libtipscan:GetScanner("prediction") hooksecurefunc("UseAction", function(slot, target, selfcast) if GetActionText(slot) or not IsCurrentAction(slot) then return end @@ -173,5 +265,5 @@ hooksecurefunc("UseAction", function(slot, target, selfcast) local name, rank = scanner:Line(1) local icon = GetActionTexture(slot) if not name then return end - if libtotem:CheckAddQueue(name, rank, icon) then return end -end) + libtotem:CheckAddQueue(name, rank, icon, nil) +end) \ No newline at end of file