--------------------------------------------------------------------------------------- -- CREDITS: -- Thanks to TotemTracker for all the Basecode -- And Just the wonderful Idea with Bufftime --------------------------------------------------------------------------------------- -- The "ttr" table acts as a "namespace" of sorts to help prevent conflicts with other addons --------------------------------------------------------------------------------------- -- Local function references (for efficiency) -- ttr_UpdateLocalFuncRefs() can be called by other addons to force TotemTracker -- to update its local copies (in case those functions were hooked). local GetSpellName; local BuffFrame_UpdateDuration; local GetSpellIDByName; local GetSpellTexture; function ttr_UpdateLocalFuncRefs() GetSpellName = getfenv(0).GetSpellName; BuffFrame_UpdateDuration = getfenv(0).BuffFrame_UpdateDuration; GetSpellIDByName = getfenv(0).GetSpellIDByName; GetSpellTexture = getfenv(0).GetSpellTexture; end -- Localize the update function and call it to initialize local references local ttr_UpdateLocalFuncRefs = ttr_UpdateLocalFuncRefs; ttr_UpdateLocalFuncRefs(); --------------------------------------------------------------------------------------- -- Header _ttr = { localUpdateHooks = { }; }; local ttr = _ttr; ttr.UpdateLocalReferences = function() local i,v; for i,v in ipairs(ttr.localUpdateHooks) do if type(v)=="function" then v(); end end end --Data ------------------------------------------------------------------------------- -- GLOBAL DECLARATIONS -- Duration of the totem, in seconds ttr.totemTotalTime = { ["Disease Cleansing Totem"] = 120; ["Earthbind Totem"] = 45; ["Earth Elemental Totem(Rank 1)"] = 120; ["Fire Elemental Totem(Rank 1)"] = 120; ["Fire Nova Totem(Rank 1)"] = 4; ["Fire Nova Totem(Rank 2)"] = 4; ["Fire Nova Totem(Rank 3)"] = 4; ["Fire Nova Totem(Rank 4)"] = 4; ["Fire Nova Totem(Rank 5)"] = 4; ["Fire Nova Totem(Rank 6)"] = 4; ["Fire Nova Totem(Rank 7)"] = 4; ["Fire Resistance Totem(Rank 1)"] = 120; ["Fire Resistance Totem(Rank 2)"] = 120; ["Fire Resistance Totem(Rank 3)"] = 120; ["Fire Resistance Totem(Rank 4)"] = 120; ["Flametongue Totem(Rank 1)"] = 120; ["Flametongue Totem(Rank 2)"] = 120; ["Flametongue Totem(Rank 3)"] = 120; ["Flametongue Totem(Rank 4)"] = 120; ["Flametongue Totem(Rank 5)"] = 120; ["Frost Resistance Totem(Rank 1)"] = 120; ["Frost Resistance Totem(Rank 2)"] = 120; ["Frost Resistance Totem(Rank 3)"] = 120; ["Frost Resistance Totem(Rank 4)"] = 120; ["Grace of Air Totem(Rank 1)"] = 120; ["Grace of Air Totem(Rank 2)"] = 120; ["Grace of Air Totem(Rank 3)"] = 120; ["Grounding Totem"] = 45; ["Healing Stream Totem(Rank 1)"] = 120; ["Healing Stream Totem(Rank 2)"] = 120; ["Healing Stream Totem(Rank 3)"] = 120; ["Healing Stream Totem(Rank 4)"] = 120; ["Healing Stream Totem(Rank 5)"] = 120; ["Healing Stream Totem(Rank 6)"] = 120; ["Magma Totem(Rank 1)"] = 20; ["Magma Totem(Rank 2)"] = 20; ["Magma Totem(Rank 3)"] = 20; ["Magma Totem(Rank 4)"] = 20; ["Magma Totem(Rank 5)"] = 20; ["Mana Spring Totem"] = 24; -- Enamored Water Spirit trinket ["Mana Spring Totem(Rank 1)"] = 120; ["Mana Spring Totem(Rank 2)"] = 120; ["Mana Spring Totem(Rank 3)"] = 120; ["Mana Spring Totem(Rank 4)"] = 120; ["Mana Spring Totem(Rank 5)"] = 120; ["Mana Tide Totem"] = 12; ["Nature Resistance Totem(Rank 1)"] = 120; ["Nature Resistance Totem(Rank 2)"] = 120; ["Nature Resistance Totem(Rank 3)"] = 120; ["Nature Resistance Totem(Rank 4)"] = 120; ["Poison Cleansing Totem"] = 120; ["Searing Totem(Rank 1)"] = 30; ["Searing Totem(Rank 2)"] = 35; ["Searing Totem(Rank 3)"] = 40; ["Searing Totem(Rank 4)"] = 45; ["Searing Totem(Rank 5)"] = 50; ["Searing Totem(Rank 6)"] = 55; ["Searing Totem(Rank 7)"] = 60; ["Sentry Totem"] = 300; ["Stoneclaw Totem(Rank 1)"] = 15; ["Stoneclaw Totem(Rank 2)"] = 15; ["Stoneclaw Totem(Rank 3)"] = 15; ["Stoneclaw Totem(Rank 4)"] = 15; ["Stoneclaw Totem(Rank 5)"] = 15; ["Stoneclaw Totem(Rank 6)"] = 15; ["Stoneclaw Totem(Rank 7)"] = 15; ["Stoneskin Totem(Rank 1)"] = 120; ["Stoneskin Totem(Rank 2)"] = 120; ["Stoneskin Totem(Rank 3)"] = 120; ["Stoneskin Totem(Rank 4)"] = 120; ["Stoneskin Totem(Rank 5)"] = 120; ["Stoneskin Totem(Rank 6)"] = 120; ["Stoneskin Totem(Rank 7)"] = 120; ["Stoneskin Totem(Rank 8)"] = 120; ["Strength of Earth Totem(Rank 1)"] = 120; ["Strength of Earth Totem(Rank 2)"] = 120; ["Strength of Earth Totem(Rank 3)"] = 120; ["Strength of Earth Totem(Rank 4)"] = 120; ["Strength of Earth Totem(Rank 5)"] = 120; ["Strength of Earth Totem(Rank 6)"] = 120; ["Totem of Wrath(Rank 1)"] = 120; ["Tranquil Air Totem"] = 120; ["Tremor Totem"] = 120; ["Windfury Totem(Rank 1)"] = 120; ["Windfury Totem(Rank 2)"] = 120; ["Windfury Totem(Rank 3)"] = 120; ["Windfury Totem(Rank 4)"] = 120; ["Windfury Totem(Rank 5)"] = 120; ["Windwall Totem(Rank 1)"] = 120; ["Windwall Totem(Rank 2)"] = 120; ["Windwall Totem(Rank 3)"] = 120; ["Windwall Totem(Rank 4)"] = 120; ["Wrath of Air Totem(Rank 1)"] = 120; }; -- Totem type ttr.totemType = { ["Disease Cleansing Totem"] = "Water"; ["Earthbind Totem"] = "Earth"; ["Earth Elemental Totem"] = "Earth"; ["Fire Elemental Totem"] = "Fire"; ["Fire Nova Totem"] = "Fire"; ["Fire Resistance Totem"] = "Water"; ["Flametongue Totem"] = "Fire"; ["Frost Resistance Totem"] = "Fire"; ["Grace of Air Totem"] = "Air"; ["Grounding Totem"] = "Air"; ["Healing Stream Totem"] = "Water"; ["Magma Totem"] = "Fire"; ["Mana Spring Totem"] = "Water"; ["Mana Tide Totem"] = "Water"; ["Nature Resistance Totem"] = "Air"; ["Poison Cleansing Totem"] = "Water"; ["Searing Totem"] = "Fire"; ["Sentry Totem"] = "Air"; ["Stoneclaw Totem"] = "Earth"; ["Stoneskin Totem"] = "Earth"; ["Strength of Earth Totem"] = "Earth"; ["Totem of Wrath"] = "Fire"; ["Tranquil Air Totem"] = "Air"; ["Tremor Totem"] = "Earth"; ["Windfury Totem"] = "Air"; ["Windwall Totem"] = "Air"; ["Wrath of Air Totem"] = "Air"; }; -- Starting health of the totem ttr.totemHealth = { ["default"] = 5; ["Stoneclaw Totem(Rank 1)"] = 50; ["Stoneclaw Totem(Rank 2)"] = 150; ["Stoneclaw Totem(Rank 3)"] = 220; ["Stoneclaw Totem(Rank 4)"] = 280; ["Stoneclaw Totem(Rank 5)"] = 390; ["Stoneclaw Totem(Rank 6)"] = 480; ["Stoneclaw Totem(Rank 7)"] = 1315; }; -- Maps buff names to the totem that grants the buff ttr.buff2Totem = { ["Grounding Totem Effect"] = "Grounding Totem"; ["Strength of Earth"] = "Strength of Earth Totem"; ["Grace of Air"] = "Grace of Air Totem"; ["Stoneskin"] = "Stoneskin Totem"; ["Windwall"] = "Windwall Totem"; ["Healing Stream"] = "Healing Stream Totem"; ["Mana Spring"] = "Mana Spring Totem"; ["Frost Resistance"] = "Frost Resistance Totem"; ["Fire Resistance"] = "Fire Resistance Totem"; ["Nature Resistance"] = "Nature Resistance Totem"; ["Mana Tide"] = "Mana Tide Totem"; ["Tranquil Air"] = "Tranquil Air Totem"; ["Wrath of Air Totem"] = "Wrath of Air Totem"; ["Totem of Wrath"] = "Totem of Wrath"; }; --Table provided to prevent indexing errors before values are initialized ttr.totemInfo = { player = { Earth = { }; Fire = { }; Water = { }; Air = { }; }; party1 = { Earth = { }; Fire = { }; Water = { }; Air = { }; }; party2 = { Earth = { }; Fire = { }; Water = { }; Air = { }; }; party3 = { Earth = { }; Fire = { }; Water = { }; Air = { }; }; party4 = { Earth = { }; Fire = { }; Water = { }; Air = { }; }; }; -- Config --------------------------------------------------------------------------------------- -- SEMI-GLOBAL FUNCTION DEFINITIONS ttr.initConfig = function() if type(ttr_config)=="nil" then ttr_config = { }; end if type(ttr_config.buffMode) == "nil" then ttr_config.buffMode = false; end end -- Util ------------------------------------------------------------------------------- -- LOCAL REFERENCE DECLARATIONS local localUpdateHook = function() end ttr.localUpdateHooks[(#(ttr.localUpdateHooks))+1] = localUpdateHook; --------------------------------------------------------------------------------------- -- LOCAL FUNCTION DECLARATIONS local extractNum; local GetSpellIDByName; local getTotemTimeRemainingByType; local getTotemTimeRemainingByName; local clearTotemType; local clearAllTotemTypes; local enumTotemDuration; --------------------------------------------------------------------------------------- -- SEMI-GLOBAL FUNCTION DEFINITIONS ttr.extractNum = function(str,si) local index=si; local sindex,num,goodnum,found; if type(str)=="string" then if type(index) ~= "number" then index = 1; end sindex = index; while index <= strlen(str) do num = tonumber(strsub(str,sindex,index)); if type(num) == "nil" then if found then return goodnum,index; end sindex = index + 1; else goodnum = num; found = true; end index = index + 1; end end return goodnum,nil; end extractNum = ttr.extractNum; ttr.GetSpellIDByName = function(name,booktype) local i,curname,currank; for i=1,1000 do -- Don't want it to loop endlessly by accident... curname, currank = GetSpellName(i,booktype); if curname then if currank and currank~="" then curname = curname.."("..currank..")"; end if name == curname then return i; end end end return nil; end GetSpellIDByName = ttr.GetSpellIDByName; ttr.getTotemTimeRemainingByType = function(typeref) local t; if type(typeref)=="table" and type(typeref.timeStart)=="number" and type(typeref.timeTotal)=="number" then local tcur = GetTime(); t = typeref.timeStart + typeref.timeTotal - tcur; if (t < 0) then t=nil; end end return t; end getTotemTimeRemainingByType = ttr.getTotemTimeRemainingByType; ttr.getTotemTimeRemainingByName = function(playerref,tname) local t; if type(playerref)=="table" then local ttype = totemType[tname]; if ttype and type(playerref[ttype])=="table" then if (playerref[ttype].totemName == tname) then t = getTotemTimeRemainingByType(playerref[ttype]); end end end return t; end getTotemTimeRemainingByName = ttr.getTotemTimeRemainingByName; ttr.clearTotemType = function(typeref) local k,v; for k,v in pairs(typeref) do typeref[k] = nil; end end clearTotemType = ttr.clearTotemType; ttr.clearAllTotemTypes = function(playerref) if type(playerref)=="table" then local k,v; for k,v in pairs(playerref) do clearTotemType(v); end end end clearAllTotemTypes = ttr.clearAllTotemTypes; ttr.enumTotemDuration = function() for k,v in pairs(totemDuration) do DEFAULT_CHAT_FRAME:AddMessage(tostring(k)..": "..tostring(v)); end end enumTotemDuration = ttr.enumTotemDuration; -- Core ------------------------------------------------------------------------------- -- LOCAL REFERENCE DECLARATIONS local totemInfo; local totemTotalTime; local totemType; local totemHealth; local clearTotemType; local clearAllTotemTypes; local GetSpellIDByName; local buff_OnUpdate; local localUpdateHook = function() totemInfo = ttr.totemInfo; totemTotalTime = ttr.totemTotalTime; totemType = ttr.totemType; totemHealth = ttr.totemHealth; clearTotemType = ttr.clearTotemType; clearAllTotemTypes = ttr.clearAllTotemTypes; GetSpellIDByName = ttr.GetSpellIDByName; buff_OnUpdate = ttr.buff_OnUpdate; end ttr.localUpdateHooks[(#(ttr.localUpdateHooks))+1] = localUpdateHook; ------------------------------------------------------------------------------- -- CORE EVENT GLOBAL FUNCTIONS -- Q) Will this work as local function? Might need this global to work with XML... -- A) Needs to be global, but can declare it as local and link the environment -- reference to global afterwards... core_OnLoad = function() -- IMPORTANT! ttr.UpdateLocalReferences(); this:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED"); this:RegisterEvent("PLAYER_DEAD"); this:RegisterEvent("PLAYER_ENTERING_WORLD"); this:RegisterEvent("ADDON_LOADED"); this:RegisterEvent("CHAT_MSG_COMBAT_CREATURE_VS_SELF_HITS"); this:RegisterEvent("CHAT_MSG_SPELL_CREATURE_VS_SELF_DAMAGE"); end core_OnEvent = function(frame,event,arg1,arg2,arg3,arg4) if event == "ADDON_LOADED" then if arg1 == "TotemTracker" then ttr.setBuffMode(ttr_config.buffMode); end elseif event == "UNIT_SPELLCAST_SUCCEEDED" then local spell,rank = arg2,arg3; if type(spell)=="string" and type(rank)=="string" then local name=spell; -- Spell cast... if rank ~= "" then name = name.."("..rank..")"; end local ttt = totemTotalTime[name]; local ttype = totemType[spell]; if type(ttt)=="number" and type(ttype)=="string" then -- TOTEM CAST!!! totemInfo.player[ttype].timeStart = GetTime(); totemInfo.player[ttype].timeTotal = ttt; totemInfo.player[ttype].totemName = spell; totemInfo.player[ttype].totemHealth = totemHealth[name]; if totemHealth[name]==nil then totemInfo.player[ttype].totemHealth = totemHealth["default"]; end local spellID = GetSpellIDByName(name,BOOKTYPE_SPELL); if type(spellID)=="number" then local texture = GetSpellTexture(spellID, BOOKTYPE_SPELL); end end -- Clear totems on Totemic Call -- if spell=="Totemic Call" then -- clearAllTotemTypes(totemInfo.player); -- end end elseif event == "CHAT_MSG_COMBAT_CREATURE_VS_SELF_HITS" or event == "CHAT_MSG_SPELL_CREATURE_VS_SELF_DAMAGE" then local k,v; local msg = arg1; for k,v in pairs(totemInfo.player) do if v.totemName and strfind(msg,v.totemName) then local strstart,strend = strfind(msg,"for "); if strend then local num = extractNum(msg,strend); if (type(num)=="number") then local newhealth = v.totemHealth - num; v.totemHealth = newhealth; end end --if v.totemName ~= "Stoneclaw Totem" then if v.totemHealth <= 0 then clearTotemType(v); end end end end end --local playerOnTaxi = false; --local first = true; core_OnUpdate = function() -- if first==true then -- first=false; -- end -- Update totem buff durations buff_OnUpdate(); end -- Buff ------------------------------------------------------------------------------- -- LOCAL REFERENCE DECLARATIONS -- Data local totemInfo; local buff2Totem; -- Functions local clearTotemType; local getTotemTimeRemainingByType; local localUpdateHook = function() totemInfo = ttr.totemInfo; buff2Totem = ttr.buff2Totem; clearTotemType = ttr.clearTotemType; getTotemTimeRemainingByType = ttr.getTotemTimeRemainingByType; end ttr.localUpdateHooks[(#(ttr.localUpdateHooks))+1] = localUpdateHook; ------------------------------------------------------------------------------- -- LOCAL VARIABLE DECLARATIONS local buffTimeRemaining = { }; local totemDuration = { }; local setBuffMode; --------------------------------------------------------------------------------------- -- FUNCTION HOOKS -- First, a local variable for the old function is created. Then, a local function -- hook is created. Finally, the global function is updated using the global environment -- to point to the local function. Addons that create local references to the hooked -- function must create the local reference AFTER this code executes. List TotemTracker -- under OptionalDeps in your .toc file to ensure this happens! local GetPlayerBuffTimeLeft_old = GetPlayerBuffTimeLeft; ttr.GetPlayerBuffTimeLeft_new = function(buffIndex) local rval = GetPlayerBuffTimeLeft_old(buffIndex); if (type(rval) == "nil" or rval <= 0) then local buffname = GetPlayerBuffName(buffIndex); if buffname then local t = buffTimeRemaining[buffname]; if t and t>0 then rval = t; end end end return rval; end GetPlayerBuffTimeLeft = ttr.GetPlayerBuffTimeLeft_new; local BuffButton_OnUpdate_old = BuffButton_OnUpdate; ttr.BuffButton_OnUpdate_new = function() local oldUntilCancelled = this.untilCancelled; local buffname = GetPlayerBuffName(this:GetID()); if buffname and buffTimeRemaining[buffname] then this.untilCancelled = 0; end BuffButton_OnUpdate_old(); this.untilCancelled = oldUntilCancelled; end BuffButton_OnUpdate = ttr.BuffButton_OnUpdate_new; ttr.setBuffMode = function(mode) if mode==true then BuffButton_OnUpdate = ttr.BuffButton_OnUpdate_new; GetPlayerBuffTimeLeft = ttr.GetPlayerBuffTimeLeft_new; else BuffButton_OnUpdate = BuffButton_OnUpdate_old; GetPlayerBuffTimeLeft = GetPlayerBuffTimeLeft_old; end end setBuffMode = ttr.setBuffMode; ------------------------------------------------------------------------------- -- BUFF EVENT GLOBAL FUNCTIONS ttr.buff_OnUpdate = function() local k,v,k2,v2,tCur,tMin; for k,v in pairs(totemDuration) do totemDuration[k] = nil; end for k,v in pairs(totemInfo) do for k2,v2 in pairs(v) do tCur = getTotemTimeRemainingByType(v2); if tCur and tCur > 0 then tMin = totemDuration[v2.totemName]; if tMin and tMin > 0 then tMin = min(tMin,tCur); else tMin = tCur; end totemDuration[v2.totemName] = tMin; else clearTotemType(v2); end end end for k,v in pairs(buff2Totem) do if totemDuration[v] then buffTimeRemaining[k] = totemDuration[v]; else buffTimeRemaining[k] = nil; end end end