------------------------------------------------------------------------------- -- Elementarist 2.0.2 -- -- Shows the advised spell for an elemental shaman for optimal DPS output. ------------------------------------------------------------------------------- Elementarist = {Locals = {}} local L = Elementarist.Locals Elementarist.versionNumber = '2.0.2' Elementarist.playerName = UnitName("player") Elementarist.playerGUID = UnitGUID("player") Elementarist.targetGUID = nil Elementarist.spellHaste = GetCombatRatingBonus(20) Elementarist.timeSinceLastUpdate = 0 Elementarist.debuffTrackerUpdate = 0 Elementarist.shieldTrackerUpdate = 0 Elementarist.spellPower = GetSpellBonusDamage(4); -- nature spell bonus Elementarist.lastBaseGCD = 1.5 Elementarist.lastShockCD = 5 Elementarist.person = { ["foeCount"] = 0, ["friendCount"] = 0, ["friend"] = {}, ["foe"] = {} } Elementarist.debuffCooldowns = { } Elementarist.lastPersonTablePurged = 0.0; Elementarist.configPanel = nil Elementarist.prevDB = {} Elementarist.DPSTable = {} Elementarist.DebugMode = false Elementarist.DebugChat = DEFAULT_CHAT_FRAME Elementarist.inParty = 0 Elementarist.OmniCC = _G['OmniCC'] Elementarist.SpellFlash = _G['SpellFlashAddon'] Elementarist.SFHistory = { ["spell"] = nil, ["misc"] = nil, ["int"] = nil } Elementarist.isEle = false Elementarist.talentUnsure = true Elementarist.lastSpell = nil Elementarist.CustomIDs = { ["Flask of Enhancement Item"] = 58149, ["Flask of Enhancement Spell"] = 79640, ["Moonkin Aura"] = 24907, ["Mind Quickening"] = 49868 } Elementarist.SpellList = { ["Flame Shock"] = GetSpellInfo(8050), ["Lightning Bolt"] = GetSpellInfo(403), ["Lava Burst"] = GetSpellInfo(51505), ["Chain Lightning"] = GetSpellInfo(421), ["Thunderstorm"] = GetSpellInfo(59159), ["Purge"] = GetSpellInfo(8012), ["Wind Shear"] = GetSpellInfo(57994), ["Water Shield"] = GetSpellInfo(52127), ["Flametongue Weapon"] = GetSpellInfo(8024), ["Wrath of Air Totem"] = GetSpellInfo(3738), ["Mana Spring Totem"] = GetSpellInfo(567), ["Elemental Mastery"] = GetSpellInfo(16166), ["Fire Nova"] = GetSpellInfo(1535), ["Earth Shock"] = GetSpellInfo(8042), ["Searing Totem"] = GetSpellInfo(3599), ["Lightning Shield"] = GetSpellInfo(324), ["Unleash Elements"] = GetSpellInfo(73680), ["Earthquake"] = GetSpellInfo(61882), -- racials ["Berserking"] = GetSpellInfo(26297), -- Troll racial ["Blood Fury"] = GetSpellInfo(33697), -- Orc racial -- debuffs ["Totem of Wrath Debuff"] = GetSpellInfo(30708), ["Heart of the Crusader"] = GetSpellInfo(54499), -- other buffs ["Demonic Pact"] = GetSpellInfo(48090), ["Flask of Enhancement"] = GetSpellInfo(Elementarist.CustomIDs["Flask of Enhancement Spell"]), ["Flask of the Draconic Mind"] = GetSpellInfo(79470), ["Lifeblood"] = GetSpellInfo(55503) } Elementarist.debuffCooldownFrame = { ["main"] = nil, ["mini_1"] = nil, ["mini_2"] = nil, ["mini_3"] = nil, ["mini_4"] = nil } Elementarist.textureList = { ["next"] = nil, ["next1"] = nil, ["next2"] = nil, ["misc"] = nil, ["int"] = nil, ["debuff"] = nil, ["debuff_1"] = nil, ["debuff_2"] = nil, ["debuff_3"] = nil, ["debuff_4"] = nil, ["shield"] = nil } Elementarist.textList = { ["dps"] = nil, ["debuff"] = nil, ["shield"] = nil } Elementarist.Behaviours = { } Elementarist.CLBehaviours = { } Elementarist.HostileFilter = { ["_DAMAGE"] = true, ["_LEECH"] = true, ["_DRAIN"] = true, ["_STOLEN"] = true, ["_INSTAKILL"] = true, ["_INTERRUPT"] = true, ["_MISSED"] = true } -- Our sneaky frame to watch for events ... checks Elementarist.events[] for the function. Passes all args. Elementarist.eventFrame = CreateFrame("Frame") Elementarist.eventFrame:SetScript("OnEvent", function(this, event, ...) Elementarist.events[event](...) end) Elementarist.eventFrame:RegisterEvent("ADDON_LOADED") Elementarist.eventFrame:RegisterEvent("PLAYER_LOGIN") Elementarist.eventFrame:RegisterEvent("PLAYER_ALIVE") -- Define our Event Handlers here Elementarist.events = {} function Elementarist:Debug(statictxt,msg) if (Elementarist.DebugMode) and (Elementarist.DebugChat) then if (msg) then Elementarist.DebugChat:AddMessage( statictxt .. " : " .. msg) else Elementarist.DebugChat:AddMessage( statictxt .. " : " .. "<nil>") end end end function Elementarist:GetDebugFrame() for i=1,NUM_CHAT_WINDOWS do local windowName = GetChatWindowInfo(i); if windowName == "EleDBG" then return getglobal("ChatFrame" .. i) end end end function Elementarist.events.PLAYER_TALENT_UPDATE() Elementarist:detectTalent() Elementarist:ApplySettings() end function Elementarist.events.PARTY_MEMBERS_CHANGED() Elementarist.inParty = Elementarist:PlayerInParty() end function Elementarist.events.PLAYER_ALIVE() -- check anything Elementarist:detectTalent() Elementarist:ApplySettings() -- Elementarist.eventFrame:UnregisterEvent("PLAYER_ALIVE") end function Elementarist.events.PLAYER_ENTERING_WORLD() Elementarist:detectTalent() end function Elementarist.events.PLAYER_LOGIN() Elementarist.playerName = UnitName("player"); Elementarist.spellHaste = GetCombatRatingBonus(20) Elementarist.spellPower = GetSpellBonusDamage(4); end function Elementarist.events.ADDON_LOADED(addon) if addon == "OmniCC" then Elementarist.OmniCC = _G['OmniCC'] end if addon == "SpellFlash" then Elementarist.SpellFlash = _G['SpellFlashAddon'] end if addon ~= "Elementarist" then return end local _,playerClass = UnitClass("player") if playerClass ~= "SHAMAN" then Elementarist.eventFrame:UnregisterEvent("PLAYER_ALIVE") return end -- load defaults, if first start Elementarist:InitSettings() -- add slash command SlashCmdList["Elementarist"] = Elementarist.Options SLASH_Elementarist1 = "/Elementarist" SLASH_Elementarist2 = "/ele" -- check if talent is elemental Elementarist:detectTalent() Elementarist.playerLevel = UnitLevel("player") -- Setup behaviours Elementarist.Behaviours["1"] = L.BEHAVIOUR_KEEP_FS_UP Elementarist.Behaviours["2"] = L.BEHAVIOUR_FS_BEFORE_LVB Elementarist.CLBehaviours["0"] = L.CLSTBEHAVIOUR_NONE Elementarist.CLBehaviours["1"] = L.CLSTBEHAVIOUR_CL_AFTER_LVB Elementarist.CLBehaviours["2"] = L.CLSTBEHAVIOUR_CL_ON_CD -- Create GUI Elementarist:CreateGUI() Elementarist.displayFrame:SetScale(ElementaristDB.scale) Elementarist.OmniCC = _G['OmniCC'] Elementarist.SpellFlash = _G['SpellFlashAddon'] -- Create config page Elementarist:CreateConfig() -- Register for Function Events Elementarist.eventFrame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED") Elementarist.eventFrame:RegisterEvent("COMBAT_RATING_UPDATE") -- Monitor the all-mighty haste Elementarist.eventFrame:RegisterEvent("PLAYER_TARGET_CHANGED") Elementarist.eventFrame:RegisterEvent("PLAYER_REGEN_ENABLED") -- Left combat, clean up all enemy GUIDs Elementarist.eventFrame:RegisterEvent("PLAYER_TALENT_UPDATE") Elementarist.eventFrame:RegisterEvent("PARTY_MEMBERS_CHANGED") -- get debug frame Elementarist.DebugChat = Elementarist:GetDebugFrame() end function Elementarist:InitSettings() if not ElementaristDB then ElementaristDB = {} -- fresh start end if not ElementaristDB.scale then ElementaristDB.scale = 1 end if not ElementaristDB.debuffscale then ElementaristDB.debuffscale = 1 end if not ElementaristDB.shieldscale then ElementaristDB.shieldscale = 1 end if ElementaristDB.locked == nil then ElementaristDB.locked = false end if ElementaristDB.enabled == nil then ElementaristDB.enabled = true end if ElementaristDB.disableIfNotEle == nil then ElementaristDB.disableIfNotEle = true end if ElementaristDB.debuffdisabled == nil then ElementaristDB.debuffdisabled = false end if ElementaristDB.shielddisabled == nil then ElementaristDB.shielddisabled = false end if ElementaristDB.alpha == nil then ElementaristDB.alpha = 0.8 end if ElementaristDB.debuffalpha == nil then ElementaristDB.debuffalpha = 1 end if ElementaristDB.shieldalpha == nil then ElementaristDB.shieldalpha = 1 end if ElementaristDB.FireNova == nil then ElementaristDB.FireNova = true end if ElementaristDB.Behaviour == nil then ElementaristDB.Behaviour = Elementarist.Behaviours["1"] end if ElementaristDB.CLBehaviour == nil then ElementaristDB.CLBehaviour = Elementarist.CLBehaviours["0"] end if ElementaristDB.EnableUE == nil then ElementaristDB.EnableUE = false end if ElementaristDB.ThreatWarning == nil then ElementaristDB.ThreatWarning = true end if ElementaristDB.EnableEQ == nil then ElementaristDB.EnableEQ = false end if not ElementaristDB.x then ElementaristDB.x = -200 end if not ElementaristDB.y then ElementaristDB.y = -200 end if not ElementaristDB.relativePoint then ElementaristDB.relativePoint = "CENTER" end if not ElementaristDB.debuffx then ElementaristDB.debuffx = -200 end if not ElementaristDB.debuffy then ElementaristDB.debuffy = -100 end if not ElementaristDB.debuffrelativePoint then ElementaristDB.debuffrelativePoint = "CENTER" end if not ElementaristDB.shieldrelativePoint then ElementaristDB.shieldrelativePoint = "CENTER" end end function Elementarist:detectTalent() local _,_,_,_,rest,_ = GetTalentTabInfo(3) local _,_,_,_,enha,_ = GetTalentTabInfo(2) local _,_,_,_,elem,_ = GetTalentTabInfo(1) if (elem+rest+enha>0) then Elementarist.isEle = ((elem>=enha) and (elem>=rest)) end if (elem + enha + rest == 0) then Elementarist.talentUnsure = true else Elementarist.talentUnsure = false end end function Elementarist:PlayerInParty() if (GetNumRaidMembers()>0) then return 2 elseif (GetNumPartyMembers()>0) then return 1 else return 0 end end function Elementarist:RemoveFromTables(guid) if (Elementarist.person["friend"][guid]) and (Elementarist.person["friend"][guid] ~= 0) then Elementarist.person["friend"][guid] = 0 Elementarist.person["friendCount"] = Elementarist.person["friendCount"] - 1 end if (Elementarist.person["foe"][guid]) and (Elementarist.person["foe"][guid] ~= 0) then Elementarist.person["foe"][guid] = 0 Elementarist.person["foeCount"] = Elementarist.person["foeCount"] - 1 Elementarist:Debug('Enemy died:', Elementarist.person["foeCount"] .. " " .. guid) end if (Elementarist.debuffCooldowns[guid]) then table.remove(Elementarist.debuffCooldowns, guid) Elementarist:UpdateDebuffTracker() end end function Elementarist:PurgeDebuffTable() Elementarist.debuffCooldowns = {} Elementarist:UpdateDebuffTracker() end function Elementarist:PurgePersonTable() for i,v in pairs(Elementarist.person["foe"]) do if ( ( GetTime() - Elementarist.person["foe"][i] ) > 3) then -- no activity from that unit in last 2 seconds, remove it if ( Elementarist.person["foe"][i] ~= 0) then Elementarist.person["foe"][i] = 0 -- mark as inactive Elementarist.person["foeCount"] = Elementarist.person["foeCount"] - 1 Elementarist:Debug('Enemy removed:', Elementarist.person["foeCount"]) end end end for i,v in pairs(Elementarist.person["friend"]) do if ( ( GetTime() - Elementarist.person["friend"][i] ) > 3) then -- no activity from that unit in last 2 seconds, remove it if ( Elementarist.person["friend"][i] ~= 0 ) then Elementarist.person["friend"][i] = 0 -- mark as inactive Elementarist.person["friendCount"] = Elementarist.person["friendCount"] - 1 Elementarist:Debug('Friend removed:', Elementarist.person["friendCount"]) end end end Elementarist.lastPersonTablePurged = GetTime() end function Elementarist:CountPerson(time, event, sguid, sname, sflags, dguid, dname, dflags) local suffix = event:match(".+(_.-)$") local stype = (tonumber(sguid:sub(5,5), 16)) % 8 local dtype = (tonumber(dguid:sub(5,5), 16)) % 8 if Elementarist.HostileFilter[suffix] then if (bit.band(dflags, COMBATLOG_OBJECT_REACTION_HOSTILE) == COMBATLOG_OBJECT_REACTION_HOSTILE) and (bit.band(dflags, COMBATLOG_OBJECT_AFFILIATION_OUTSIDER) == COMBATLOG_OBJECT_AFFILIATION_OUTSIDER) and ((dtype==0) or (dtype==3)) then if ((not Elementarist.person["foe"][dguid]) or (Elementarist.person["foe"][dguid]==0)) then Elementarist.person["foeCount"] = Elementarist.person["foeCount"] + 1 Elementarist:Debug('Enemy added', Elementarist.person["foeCount"] .. " " .. dguid .. " " .. dflags) end Elementarist.person["foe"][dguid] = GetTime() elseif (bit.band(sflags, COMBATLOG_OBJECT_REACTION_HOSTILE) == COMBATLOG_OBJECT_REACTION_HOSTILE) and (bit.band(sflags, COMBATLOG_OBJECT_AFFILIATION_OUTSIDER) == COMBATLOG_OBJECT_AFFILIATION_OUTSIDER) and ((stype==0) or (stype==3)) then if ((not Elementarist.person["foe"][sguid]) or (Elementarist.person["foe"][sguid]==0)) then Elementarist.person["foeCount"] = Elementarist.person["foeCount"] + 1 Elementarist:Debug('Enemy added', Elementarist.person["foeCount"] .. " " .. sguid) end Elementarist.person["foe"][sguid] = GetTime() end if (bit.band(dflags, COMBATLOG_OBJECT_REACTION_FRIENDLY) == COMBATLOG_OBJECT_REACTION_FRIENDLY) and ((dtype==0) or (dtype==3)) then if ((not Elementarist.person["friend"][dguid]) or (Elementarist.person["friend"][dguid]==0)) then Elementarist.person["friendCount"] = Elementarist.person["friendCount"] + 1 end Elementarist.person["friend"][dguid] = GetTime() elseif (bit.band(sflags, COMBATLOG_OBJECT_REACTION_FRIENDLY) == COMBATLOG_OBJECT_REACTION_FRIENDLY) and ((stype==0) or (stype==3)) then if ((not Elementarist.person["friend"][sguid]) or (Elementarist.person["friend"][sguid]==0)) then Elementarist.person["friendCount"] = Elementarist.person["friendCount"] + 1 end Elementarist.person["friend"][sguid] = GetTime() end end if (Elementarist.lastPersonTablePurged < (GetTime() - 3)) and (Elementarist.person["foeCount"]>0) then Elementarist:PurgePersonTable() end -- -- Elementarist:Debug('Enemy count:', Elementarist.person["foeCount"]) -- Elementarist:Debug('Friend count:', Elementarist.person["friendCount"]) end function Elementarist.HighDMGFormat(dmg_amount) if (dmg_amount >= 10000) then return(format('%.1f',dmg_amount/1000) .. "K") else return(format('%.f',dmg_amount)) end end -- for 4.2: -- function Elementarist.events.COMBAT_LOG_EVENT_UNFILTERED(timestamp, event, hideCaster, srcGUID, srcName, srcFlags, srcRaidFlags, dstGUID, dstName, dstFlags, dstRaidFlags, spellId, spellName, spellSchool, damage, ...) function Elementarist.events.COMBAT_LOG_EVENT_UNFILTERED(timestamp, event, hideCaster, srcGUID, srcName, srcFlags, dstGUID, dstName, dstFlags, spellId, spellName, spellSchool, damage, ...) if Elementarist.isEnabled() then if srcName == Elementarist.playerName then if (event=="SPELL_PERIODIC_DAMAGE") and (spellName==Elementarist.SpellList["Flame Shock"]) and (Elementarist.debuffCooldowns[dstGUID]) then Elementarist.debuffCooldowns[dstGUID]["action"] = GetTime() end if (event=="SPELL_CAST_START") then Elementarist.SFHistory.spell = nil Elementarist.SFHistory.misc = nil Elementarist.SFHistory.int = nil end Elementarist:DecideSpells() -- calculate DPS if (event=="SPELL_CAST_SUCCESS") then Elementarist.lastSpell = spellName end if ((event=="SPELL_DAMAGE") or (event=="SPELL_PERIODIC_DAMAGE")) then if (not Elementarist.DPSTable[dstGUID]) then Elementarist.DPSTable[dstGUID] = { ["time"] = GetTime(), ["amount"] = 0, } end Elementarist.DPSTable[dstGUID]["amount"] = Elementarist.DPSTable[dstGUID]["amount"] + damage local dps_txt = "" if (dstGUID == Elementarist.targetGUID) and (Elementarist.DPSTable[Elementarist.targetGUID]) then local dps_sec = GetTime() - Elementarist.DPSTable[dstGUID]["time"] if (dps_sec > 5) then dps_txt = format('%.f',(Elementarist.DPSTable[dstGUID]["amount"] / dps_sec)) end local threat_txt = "" local _, status, threatpct, _, _ = UnitDetailedThreatSituation("player", "target") if (status) then if (threatpct<80) then threat_txt = format("%.f",threatpct) .. " %" Elementarist.cooldownFrame:SetReverse(false) if (ElementaristDB.ThreatWarning) and (Elementarist.inParty>0) and (threatpct>70) then RaidNotice_AddMessage(RaidBossEmoteFrame, L.THREAT_WARNING_PREFIX .. format("%.f",threatpct) .. L.THREAT_WARNING_SUFFIX, ChatTypeInfo["RAID_WARNING"]) end else threat_txt = "|cffff0000" .. format("%.f",threatpct) .. " %|r" if (ElementaristDB.ThreatWarning) and (Elementarist.inParty>0) then RaidNotice_AddMessage(RaidBossEmoteFrame, "|cffff0000" .. L.THREAT_WARNING_PREFIX .. format("%.f",threatpct) .. L.THREAT_WARNING_SUFFIX .. "|r", ChatTypeInfo["RAID_WARNING"]) end Elementarist.cooldownFrame:SetReverse((Elementarist.person["friendCount"]>1) and (Elementarist.inParty>0)) end end dps_txt = dps_txt .. "|n" .. threat_txt Elementarist.textList["dps"]:SetText(dps_txt) end end else -- if unit died, remove if from friend and foe tables if (event=="UNIT_DIED") or (event=="UNIT_DESTROYED") then Elementarist:RemoveFromTables(dstGUID); end -- count enemies if player in combat if (UnitAffectingCombat("player")) then -- enemy count for CL advise and multiple player in combat (for aggro warning) Elementarist:CountPerson(timestamp, event, srcGUID, srcName, srcFlags, dstGUID, dstName, dstFlags) end end end end function Elementarist.events.COMBAT_RATING_UPDATE(unit) if unit == "player" then Elementarist.spellHaste = GetCombatRatingBonus(20) -- update spell haste Elementarist.spellPower = GetSpellBonusDamage(4); end end function Elementarist.events.PLAYER_TARGET_CHANGED(...) Elementarist.targetGUID = UnitGUID("target") Elementarist.inParty = Elementarist:PlayerInParty() if (ElementaristDB.disableIfNotEle) and (not Elementarist:isEnabled()) then Elementarist:detectTalent() if Elementarist:isEnabled() then Elementarist:ApplySettings() end end if (not Elementarist.targetGUID) then local threat_txt = "" local _, status, threatpct, _, _ = UnitDetailedThreatSituation("player", "target") if (status) then if (threatpct<80) then threat_txt = format("%.f",threatpct) .. " %" Elementarist.cooldownFrame:SetReverse(false) else threat_txt = "|cffff0000" .. format("%.f",threatpct) .. " %|r" Elementarist.cooldownFrame:SetReverse((Elementarist.person["friendCount"]>1) and (Elementarist.inParty>0)) end end Elementarist.textList["dps"]:SetText(threat_txt) end Elementarist:DecideSpells() end function Elementarist.events.PLAYER_REGEN_ENABLED(...) -- left combat Elementarist.person["friend"] = {} Elementarist.person["friendCount"] = 0 Elementarist.person["foe"] = {} Elementarist.person["foeCount"] = 0 Elementarist.DPSTable = {} Elementarist.textList["dps"]:SetText("") Elementarist.textList["debuff"]:SetText("") Elementarist.cooldownFrame:SetReverse(false) Elementarist:PurgePersonTable() Elementarist:PurgeDebuffTable() end function Elementarist:isEnabled() if (Elementarist.talentUnsure) then Elementarist:detectTalent() end return ( ElementaristDB.enabled and ( (not ElementaristDB.disableIfNotEle) or (Elementarist.isEle) ) ) end function Elementarist:UpdateShieldTracker() local name, _, icon, count, _, d, e = Elementarist:hasBuff("player",Elementarist.SpellList["Lightning Shield"]) Elementarist.shieldTrackerUpdate = GetTime() if (name) then Elementarist.textureList["shield"]:SetTexture(icon) Elementarist.textList["shield"]:SetText(format('%.0f', count)) Elementarist.shieldCooldownFrame:SetCooldown( e-d, d) else Elementarist.textureList["shield"]:SetTexture("") Elementarist.textList["shield"]:SetText("") Elementarist.shieldCooldownFrame:SetCooldown(0, 0) end end function Elementarist:UpdateDebuffTracker() local name, _, icon, _, _, d, e = Elementarist:hasDeBuff("target",Elementarist.SpellList["Flame Shock"],"player") local tguid = UnitGUID("target") Elementarist.debuffTrackerUpdate = GetTime() if (name) then if (tguid) and (not Elementarist.debuffCooldowns[tguid]) then Elementarist.debuffCooldowns[tguid] = {} end Elementarist.debuffCooldowns[tguid]["start"] = e-d Elementarist.debuffCooldowns[tguid]["duration"] = d Elementarist.debuffCooldowns[tguid]["action"] = GetTime() else _, _, icon = GetSpellInfo(Elementarist.SpellList["Flame Shock"]) end -- update mini frames local m = 1 for i,v in pairs(Elementarist.debuffCooldowns) do if ( (v["start"] + v["duration"]) > GetTime() ) and (i ~= tguid) and (m <= 4) and (v["action"]>GetTime() - 4) then Elementarist.textureList["debuff_" .. tostring(m)]:SetTexture(icon) Elementarist.debuffCooldownFrame["mini_" .. tostring(m)]:SetCooldown( v["start"], v["duration"]) m = m + 1 end end for i=m,4,1 do Elementarist.textureList["debuff_" .. tostring(m)]:SetTexture("") Elementarist.debuffCooldownFrame["mini_" .. tostring(m)]:SetCooldown( 0, 0) end -- update main frame if (name) then Elementarist.textureList["debuff"]:SetTexture(icon) if (not Elementarist.OmniCC) then Elementarist.textList["debuff"]:SetText(format('%.1f', (e - GetTime()))) end Elementarist.debuffCooldownFrame["main"]:SetCooldown( e-d, d) else Elementarist.textureList["debuff"]:SetTexture("") Elementarist.textList["debuff"]:SetText("") Elementarist.debuffCooldownFrame["main"]:SetCooldown(0, 0) end end function Elementarist:GetSpellCooldownRemaining(spell) local s, d, _ = GetSpellCooldown(spell) if (d) and (d>0) then d = s - GetTime() + d end return d end function Elementarist:hasDeBuff(unit, spellName, casterUnit) local i = 1; while true do local name, rank, icon, count, debuffType, duration, expirationTime, unitCaster, isStealable = UnitDebuff(unit, i); if not name then break; end if (name) and (spellName) then if string.match(name, spellName) and ((unitCaster == casterUnit) or (casterUnit == nil)) then return name, rank, icon, count, debuffType, duration, expirationTime, unitCaster, isStealable; end end i = i + 1; end end function Elementarist:hasBuff(unit, spellName, stealableOnly, getByID) local i = 1; while true do local name, rank, icon, count, buffType, duration, expirationTime, source, isStealable, _, spellId = UnitBuff(unit, i); if not name then break; end if (not getByID) and (name) and (spellName) then if string.match(name, spellName) then if (not stealableOnly) or (isStealable) then return name, rank, icon, count, buffType, duration, expirationTime, unitCaster, isStealable; end end else if (getByID == spellId) then return name, rank, icon, count, buffType, duration, expirationTime, unitCaster, isStealable; end end i = i + 1; end end function Elementarist:hasTotem(unit, spellName) local i = 1; while true do local name, rank, icon, count, buffType, duration, expirationTime, source, isStealable = UnitBuff(unit, i); if not name then break; end if (string.match(name, spellName) or (string.match(icon, spellName))) and (expirationTime==0) then return name, rank, icon, count, buffType, duration, expirationTime, unitCaster, isStealable; end i = i + 1; end end function Elementarist:SpellAvailable(spell) if (not spell) then return false end if (IsUsableSpell(spell)) then return true else return false end end function Elementarist:NextSpell(timeshift,exspell1,exspell2) local guid = UnitGUID("target") local currentTime = GetTime() local s,d,e local name, fsExpiration, unitCaster local lastSpell if (exspell1) then if (exspell2) then lastSpell = exspell2 else lastSpell = exspell1 end else lastSpell = Elementarist.lastSpell end Elementarist.lastBaseGCD = 1.5 - (1.5 * Elementarist.spellHaste * .01) local flameshockavail = false local LvBct = 2 - (2 * Elementarist.spellHaste * .01) -- check Shock CD local s, d = GetSpellCooldown(Elementarist.SpellList["Flame Shock"]) if (d) and (d>0) then Elementarist.lastShockCD = d end local s, d = GetSpellCooldown(Elementarist.SpellList["Earth Shock"]) if (d) and (d>0) then Elementarist.lastShockCD = d end -- if target is dead, return "" if (UnitHealth("target")<=0) then return "" end if (not timeshift) then timeshift = 0 end -- check current spell local spellInCast, _, _, _, sICstartTime, sICendTime = UnitCastingInfo("player") if (sepllInCast) then if ( (sICendTime - sICstartTime) / 1000 ) < Elementarist.lastBaseGCD then sICendTime = sICstartTime + (Elementarist.lastBaseGCD * 1000) end timeshift = timeshift + (sICendTime / 1000) - GetTime() else -- no spell in cast, check global cd via Flametongue Weapon if (Elementarist.SpellList["Flametongue Weapon"]) then local ftcd = Elementarist:GetSpellCooldownRemaining(Elementarist.SpellList["Flametongue Weapon"]) if (ftcd) then timeshift = timeshift + Elementarist:GetSpellCooldownRemaining(Elementarist.SpellList["Flametongue Weapon"]) else timeshift = timeshift + Elementarist.lastBaseGCD end else timeshift = timeshift + Elementarist.lastBaseGCD end end -- check if Flame shock applied on target first name, _, _, _, _, _, fsExpiration, unitCaster = Elementarist:hasDeBuff("target", Elementarist.SpellList["Flame Shock"], "player"); if (exspell1 ~= Elementarist.SpellList["Flame Shock"]) and (exspell2 ~= Elementarist.SpellList["Flame Shock"]) then if IsSpellInRange(Elementarist.SpellList["Flame Shock"], "target") == 1 then if (not fsExpiration) then fsExpiration = 0 end d = Elementarist:GetSpellCooldownRemaining(Elementarist.SpellList["Flame Shock"]) if ((d - timeshift) <= 0) then flameshockavail = true local doFS = false if (unitCaster ~= "player") then -- fs debuff is not casted by the player name = false fsExpiration = 0 end if (not name) then -- no fs debuff on target fsExpiration = 0 end if (ElementaristDB.Behaviour == Elementarist.Behaviours["1"]) then doFS = true else -- if fs before lvb, check lvb cd d = Elementarist:GetSpellCooldownRemaining(Elementarist.SpellList["Lava Burst"]) if (d <= (timeshift + Elementarist.lastBaseGCD)) then doFS = true end end if (doFS) and ((fsExpiration - GetTime() - timeshift) < 0) then return Elementarist.SpellList["Flame Shock"] end end end end -- Unleash Elements if LvB will be available after it, and target has FS debuff, and it will not expire before UE, and no LvB cast before UE -- Weapon enchant has to be Flamtounge Weapon, and Unleash Elements are not on CD if (ElementaristDB.EnableUE) then if (exspell1 ~= Elementarist.SpellList["Unleash Elements"]) and (exspell2 ~= Elementarist.SpellList["Unleash Elements"]) then if ( (IsSpellInRange(Elementarist.SpellList["Unleash Elements"], "target") == 1) and ( ( (Elementarist.SpellList["Lava Burst"]) ~= spellInCast) and ( (Elementarist.SpellList["Lava Burst"]) ~= exspell1) and ( (Elementarist.SpellList["Lava Burst"]) ~= exspell2) ) and ( (exspell1 == Elementarist.SpellList["Flame Shock"] ) or (exspell2 == Elementarist.SpellList["Flame Shock"] ) or (fsExpiration > timeshift + (2 * Elementarist.lastBaseGCD ) ) ) ) then local hasMainHandEnchant, _, _, _, _, _ = GetWeaponEnchantInfo() if (hasMainHandEnchant) then d = Elementarist:GetSpellCooldownRemaining(Elementarist.SpellList["Lava Burst"]) e = Elementarist:GetSpellCooldownRemaining(Elementarist.SpellList["Unleash Elements"]) if (d <= (timeshift + Elementarist.lastBaseGCD)) and ((e-timeshift) <= 0) then return Elementarist.SpellList["Unleash Elements"] end end end end end if (not fsExpiration) then fsExpiration = 0 end if (exspell1 ~= Elementarist.SpellList["Lava Burst"]) and (exspell2 ~= Elementarist.SpellList["Lava Burst"]) then if (IsSpellInRange(Elementarist.SpellList["Lava Burst"], "target") == 1) and ( ((fsExpiration~=0) and ((fsExpiration-GetTime()-timeshift) > LvBct)) or (exspell1 == Elementarist.SpellList["Flame Shock"]) or (exspell2 == Elementarist.SpellList["Flame Shock"]) ) then if ((Elementarist.SpellList["Lava Burst"]) ~= spellInCast) then d = Elementarist:GetSpellCooldownRemaining(Elementarist.SpellList["Lava Burst"]) if ((d-timeshift) <= 0) then return Elementarist.SpellList["Lava Burst"] end end end end -- if >=4 foes are available, and Earthquake not on cd if (ElementaristDB.EnableEQ) then if (exspell1 ~= Elementarist.SpellList["Earthquake"]) and (exspell2 ~= Elementarist.SpellList["Earthquake"]) then if (Elementarist.person["foeCount"]>=4) then d = Elementarist:GetSpellCooldownRemaining(Elementarist.SpellList["Earthquake"]) if ((d-timeshift) <= 0) then return Elementarist.SpellList["Earthquake"] end end end end -- if >=4 foes are available, and Fire Nova enabled in settings, and not in cd (and has Flame Shock applied on target) if (exspell1 ~= Elementarist.SpellList["Fire Nova"]) and (exspell2 ~= Elementarist.SpellList["Fire Nova"]) then if (ElementaristDB.FireNova) and (Elementarist.person["foeCount"]>=4) then -- is Flame Shock on target local f = Elementarist:hasDeBuff("target",Elementarist.SpellList["Flame Shock"],"player") if f then d = Elementarist:GetSpellCooldownRemaining(Elementarist.SpellList["Fire Nova"]) if ((d-timeshift) <= 0) then return Elementarist.SpellList["Fire Nova"] end end end end -- Earth shock if Lightning Shield count >=9 and not on cd or FS debuff remaining between FS and FS cd + 2sec and LS count>=7 if ( (exspell1 ~= Elementarist.SpellList["Earth Shock"]) and (exspell2 ~= Elementarist.SpellList["Earth Shock"]) ) then d = Elementarist:GetSpellCooldownRemaining(Elementarist.SpellList["Earth Shock"]) local _, _, _, lscount = Elementarist:hasBuff("player",Elementarist.SpellList["Lightning Shield"]) if ( ( (d) and ((d-timeshift) <= 0) and (lscount) ) and ( (lscount>=9) or ( (lscount>=7) and ( (fsExpiration - GetTime() - timeshift) > Elementarist.lastShockCD ) and ( (fsExpiration - GetTime() - timeshift) < Elementarist.lastShockCD + 2 ) ) ) ) then return Elementarist.SpellList["Earth Shock"] end end -- CL if there are multiple targets, (count in a dirty way from combat log, not to accurate!!!) if (exspell1 ~= Elementarist.SpellList["Chain Lightning"]) and (exspell2 ~= Elementarist.SpellList["Chain Lightning"]) then if (Elementarist.person["foeCount"]>2) then if IsSpellInRange(Elementarist.SpellList["Chain Lightning"], "target") == 1 then if ((Elementarist.SpellList["Chain Lightning"]) ~= spellInCast) then d = Elementarist:GetSpellCooldownRemaining(Elementarist.SpellList["Chain Lightning"]) if ((d-timeshift) <= 0) then return Elementarist.SpellList["Chain Lightning"] end end end end end -- otherwise lightning bolt if IsSpellInRange(Elementarist.SpellList["Lightning Bolt"], "target") == 1 then return Elementarist.SpellList["Lightning Bolt"] end -- if nothing works, try flameshock again if flameshockavail then return Elementarist.SpellList["Flame Shock"] end return "" end function Elementarist:MiscSpell() -- Miscelaneous spell order: -- Flametongoue weapon -- Lightning Shield -- Searing Totem -- Totem of Wrath -- Wrath of Air totem -- Elemental Mastery -- Berserking troll racial (if available) -- Blood Fury orc racial -- Lifeblood Herbalism spell local d, e local name, expirationTime -- Alchemy Flask of Enhancement if GetItemCount(Elementarist.CustomIDs["Flask of Enhancement Item"]) ~= 0 then name = Elementarist:hasBuff("player", Elementarist.SpellList["Flask of the Draconic Mind"]); if (name == nil) then name, _, _, _, _, _, expirationTime = Elementarist:hasBuff("player", Elementarist.SpellList["Flask of Enhancement"], false, Elementarist.CustomIDs["Flask of Enhancement Spell"]); if (name == nil) or (expirationTime < 2) then if (icon == nil) then icon = GetItemIcon(Elementarist.CustomIDs["Flask of Enhancement Item"]) end return nil,icon end end end -- Flametongue weapon if Elementarist:SpellAvailable(Elementarist.SpellList["Flametongue Weapon"]) then local hasMainHandEnchant, mainHandExpiration, _, _, _, _ = GetWeaponEnchantInfo() if (hasMainHandEnchant == nil) or ((mainHandExpiration / 60000) < 1) then return Elementarist.SpellList["Flametongue Weapon"] end end -- Lightning Shield if Elementarist:SpellAvailable(Elementarist.SpellList["Lightning Shield"]) then name, _, _, _, _, _, expirationTime = Elementarist:hasBuff("player", Elementarist.SpellList["Lightning Shield"]); if (name == nil) or (expirationTime < 1) then return Elementarist.SpellList["Lightning Shield"] end end -- Searing Totem if Elementarist:SpellAvailable(Elementarist.SpellList["Searing Totem"]) then local haveFireTotem,fireTotemName,_,_ = GetTotemInfo(1) if (fireTotemName == "") then -- no fire totem return Elementarist.SpellList["Searing Totem"] end end -- Wrath of Air totem if Elementarist:SpellAvailable(Elementarist.SpellList["Wrath of Air Totem"]) then if ( (Elementarist:hasTotem("player", Elementarist.SpellList["Wrath of Air Totem"]) == nil) and (Elementarist:hasBuff("player", GetSpellInfo(Elementarist.CustomIDs["Moonkin Aura"]), false, Elementarist.CustomIDs["Moonkin Aura"]) == nil) and (Elementarist:hasBuff("player", GetSpellInfo(Elementarist.CustomIDs["Mind Quickening"]), false, Elementarist.CustomIDs["Mind Quickening"]) == nil) ) then return Elementarist.SpellList["Wrath of Air Totem"] end end -- Elemental Mastery if Elementarist:SpellAvailable(Elementarist.SpellList["Elemental Mastery"]) then d = Elementarist:GetSpellCooldownRemaining(Elementarist.SpellList["Elemental Mastery"]) if d <= 0.5 then return Elementarist.SpellList["Elemental Mastery"] end end -- Berserking if Elementarist:SpellAvailable(Elementarist.SpellList["Berserking"]) then d = Elementarist:GetSpellCooldownRemaining(Elementarist.SpellList["Berserking"]) if d <= 0.5 then return Elementarist.SpellList["Berserking"] end end -- Blood Fury if Elementarist:SpellAvailable(Elementarist.SpellList["Blood Fury"]) then d = Elementarist:GetSpellCooldownRemaining(Elementarist.SpellList["Blood Fury"]) if d <= 0.5 then return Elementarist.SpellList["Blood Fury"] end end -- Lifeblood if Elementarist:SpellAvailable(Elementarist.SpellList["Lifeblood"]) then d = Elementarist:GetSpellCooldownRemaining(Elementarist.SpellList["Lifeblood"]) if d <= 0.5 then return Elementarist.SpellList["Lifeblood"] end end return "" end function Elementarist:IntSpell() -- interruptions, mana recharge wia thunderstorm, and purge on target if Elementarist:SpellAvailable(Elementarist.SpellList["Wind Shear"]) then if IsSpellInRange(Elementarist.SpellList["Wind Shear"], "target") == 1 then local _, status, threatpct, _, _ = UnitDetailedThreatSituation("player", "target") if ((UnitCastingInfo("target")) or (UnitChannelInfo("target"))) or ((status) and (threatpct>80) and (Elementarist.person["friendCount"]>1) and (Elementarist.inParty>0)) then local d = Elementarist:GetSpellCooldownRemaining(Elementarist.SpellList["Wind Shear"]) if (d) and (d<0.5) and (IsSpellInRange(Elementarist.SpellList["Wind Shear"], "target") ) then return Elementarist.SpellList["Wind Shear"] end end if (UnitChannelInfo("target")) then local d = Elementarist:GetSpellCooldownRemaining(Elementarist.SpellList["Wind Shear"]) if (d<0.5) and (interruptable) and (IsSpellInRange(Elementarist.SpellList["Wind Shear"], "target") ) then return Elementarist.SpellList["Wind Shear"] end end end end -- check if mana is below 70% and Thunderstorm available if Elementarist:SpellAvailable(Elementarist.SpellList["Thunderstorm"]) then local d = GetSpellCooldown(Elementarist.SpellList["Thunderstorm"]) if (d) and ((UnitPower("player",0) / UnitPowerMax("player",0)) < 0.7) and (d < 0.5) then return Elementarist.SpellList["Thunderstorm"] end end -- check if purgeable buff is on target (not sure if this is ok) if Elementarist:SpellAvailable(Elementarist.SpellList["Purge"]) then if IsSpellInRange(Elementarist.SpellList["Purge"], "target") == 1 then if (Elementarist:hasBuff("target", ".", 1)) then local d = Elementarist:GetSpellCooldownRemaining(Elementarist.SpellList["Purge"]) if (d<0.5) then return Elementarist.SpellList["Purge"] end end end end return "" end function Elementarist:FlashSpell(spell,spelltype) local color = "White" if (Elementarist.SpellFlash) and (Elementarist.SFHistory[spelltype] ~= spell) and (Elementarist.SpellFlash.Flashable(spell)) then if (spelltype == "int") then color = "Aqua" end if (spelltype == "misc") then color = "Green" end Elementarist.SpellFlash.Flash(spell,color) Elementarist.SFHistory.spell=spell end end function Elementarist:DecideSpells() Elementarist.timeSinceLastUpdate = 0; local currentTime = GetTime() local guid = UnitGUID("target") local guid = UnitGUID("target") if UnitName("target") == nil or UnitIsFriend("player","target") ~= nil or UnitHealth("target") == 0 then guid = nil end if UnitInVehicle("player") then -- player is in a "vehicle" don't suggest spell Elementarist.textureList["next"]:SetTexture("") Elementarist.textureList["next1"]:SetTexture("") Elementarist.textureList["next2"]:SetTexture("") Elementarist.textureList["misc"]:SetTexture("") Elementarist.textureList["int"]:SetTexture("") return end if guid == nil then Elementarist.textureList["next"]:SetTexture("") Elementarist.textureList["next1"]:SetTexture("") Elementarist.textureList["next2"]:SetTexture("") Elementarist.textureList["misc"]:SetTexture("") Elementarist.textureList["int"]:SetTexture("") return end if (UnitHealth("target") == 0) then Elementarist.textureList["next"]:SetTexture("") Elementarist.textureList["next1"]:SetTexture("") Elementarist.textureList["next2"]:SetTexture("") return end local spell = "" spell = Elementarist:NextSpell() Elementarist:FlashSpell(spell,"spell") local d = Elementarist:GetSpellCooldownRemaining(spell) if (d) and (d>0) then local cdStart = currentTime - Elementarist.lastBaseGCD + d -- should be less then the base gcd if we are suggesting it if (cdStart) and (Elementarist.lastBaseGCD) then Elementarist.cooldownFrame:SetCooldown(cdStart, Elementarist.lastBaseGCD) end end Elementarist.textureList["next"]:SetTexture(GetSpellTexture(spell)) local _,_,_,_,_,_,ct1=GetSpellInfo(spell) if (not ct1) then ct1 = 0 else ct1 = (ct1 / 1000) end local spell1 = Elementarist:NextSpell(ct1,spell) Elementarist.textureList["next1"]:SetTexture(GetSpellTexture(spell1)) local _,_,_,_,_,_,ct2=GetSpellInfo(spell1) if (not ct2) then ct2 = 0 else ct2 = (ct2 / 1000) end if (not ct2) or (ct2 < Elementarist.lastBaseGCD) then ct2 = Elementarist.lastBaseGCD end local spell2 = Elementarist:NextSpell(ct1+ct2,spell,spell1) Elementarist.textureList["next2"]:SetTexture(GetSpellTexture(spell2)) local icon spell,icon = Elementarist:MiscSpell() Elementarist:FlashSpell(spell,"misc") if (icon) then Elementarist.textureList["misc"]:SetTexture(icon) else if (spell) then Elementarist.textureList["misc"]:SetTexture(GetSpellTexture(spell)) end end spell = Elementarist:IntSpell() Elementarist:FlashSpell(spell,"int") Elementarist.textureList["int"]:SetTexture(GetSpellTexture(spell)) end function Elementarist:OnUpdate(elapsed) if (Elementarist:isEnabled()) then Elementarist.timeSinceLastUpdate = Elementarist.timeSinceLastUpdate + elapsed if (Elementarist.timeSinceLastUpdate > (1.5 - (1.5 * Elementarist.spellHaste * .01)) * 0.3) then Elementarist:DecideSpells() end if (not ElementaristDB.debuffdisabled) then Elementarist.debuffTrackerUpdate = Elementarist.debuffTrackerUpdate + elapsed if ( ((Elementarist.OmniCC) and (Elementarist.debuffTrackerUpdate >= 1)) or ((not Elementarist.OmniCC) and (Elementarist.debuffTrackerUpdate >= 1)) ) then Elementarist:UpdateDebuffTracker() end end if (not ElementaristDB.shielddisabled) then Elementarist.shieldTrackerUpdate = Elementarist.shieldTrackerUpdate + elapsed if ( ((Elementarist.OmniCC) and (Elementarist.shieldTrackerUpdate >= 1)) or ((not Elementarist.OmniCC) and (Elementarist.shieldTrackerUpdate >= 1)) ) then Elementarist:UpdateShieldTracker() end end end end