-- CountDoom -- Author: Scrum -- based on work by Justin Milligan -- USEFUL STUFF -- isDemon = UnitCreatureType("target") == "Demon" -- isElemental = UnitCreatureType("target") == "Elemental" -- local targetName = UnitName("target"); -- TargetByName( targetName ) CD_DefaultConfig = {}; -- 0.3 config items CD_DefaultConfig.isLocked = true; CD_DefaultConfig.announceSpells = "never"; CD_DefaultConfig.playSounds = true; CD_DefaultConfig.flashSpells = true; CD_DefaultConfig.enable = true; CD_DefaultConfig.layout = "vertical"; CD_DefaultConfig.scale = "small"; CD_DefaultConfig.hseconds = false; CD_DefaultConfig.postExpireDelay = 0; CD_DefaultConfig.outofcombatDelay = 0; CD_DefaultConfig.announceChannel = nil; CountDoom = {}; CountDoom.debugVerbose = false; CountDoom.debugEvents = false; CountDoom.initialized = false; CountDoom.playerName = nil; CountDoom.realmName = nil; CountDoom.activeSpell = nil; CountDoom.soundPath = "Interface\\AddOns\\CountDoom\\"; CountDoom.targetID = 0; CountDoom.targetName = nil; CountDoom.activeSpell = nil; CountDoom.totalTime = 0; CountDoom.activeSpellWaitingForTarget = nil; CountDoom.petTargetName = nil; CountDoom.petTargeLevel = 0; CountDoom.petActiveSpell = nil; CountDoom.lastSpellID = -1; CountDoom.spellRank = nil; CountDoom.timeRemoveAllTimers = nil; CountDoom.event = {}; CountDoom.event.castSpellName = nil; CountDoom.event.castMode = nil; CountDoom.event.castTarget = nil; CountDoom.event.removedSpellName = nil; CountDoom.event.removedLastReason = nil; CountDoom.event.removedTarget = nil; CountDoom.prt = function( msg, r, g, b ) local msgOutput = DEFAULT_CHAT_FRAME; if( msgOutput ) then msgOutput:AddMessage( msg, r, g, b ); end end; CountDoom.CombatPrint = function( msg, r, g, b ) local msgOutput = ChatFrame2; if( msgOutput ) then msgOutput:AddMessage( msg, r, g, b ); end end; CountDoom.dpf = function(arg1) if (CountDoom.debugVerbose) then CountDoom.prt("CountDoom: " .. arg1); debuginfo( arg1 ); end end; CountDoom.ToStr = function(arg1) local argType = type( arg1 ); if argType == "nil" then return "nil"; elseif argType == "boolean" then if arg1 then return COUNTDOOM_TRUE; else return COUNTDOOM_FALSE; end else return "" .. arg1; end end; CountDoom.DisplaySettings = function() CountDoom.prt( "Locked: " .. CountDoom.ToStr( CountDoom.config.isLocked ) ); CountDoom.prt( "Announce Spells: " .. CountDoom.ToStr( CountDoom.config.announceSpells ) ); CountDoom.prt( "Announce channel: " .. CountDoom.ToStr( CountDoom.config.announceChannel ) ); CountDoom.prt( "Flash Spells: " .. CountDoom.ToStr( CountDoom.config.flashSpells ) ); CountDoom.prt( "Play sounds: " .. CountDoom.ToStr( CountDoom.config.playSounds ) ); CountDoom.prt( "Layout: " .. CountDoom.ToStr( CountDoom.config.layout ) ); CountDoom.prt( "Scale: " .. CountDoom.ToStr( CountDoom.config.scale ) ); CountDoom.prt( "Out of combat delay: " .. CountDoom.ToStr( CountDoom.config.outofcombatDelay ) ); CountDoom.prt( "Delay after expiration: " .. CountDoom.ToStr( CountDoom.config.postExpireDelay ) ); CountDoom.prt( "Display Hundredths: " .. CountDoom.ToStr( CountDoom.config.hseconds ) ); end; CountDoom.DisplayCommands = function() CountDoom.prt("CountDoom Usage:"); CountDoom.prt("/cd -- enable/disable CountDoom" ); CountDoom.prt("/cd -- lock/unlock timers" ); CountDoom.prt("/cd announce -- toggle per spell announce" ); CountDoom.prt("/cd play -- toggle per spell sounds" ); CountDoom.prt("/cd flash -- toggle warning flashes" ); CountDoom.prt("/cd layout -- cycle layout styles horiz/vert/text" ); CountDoom.prt("/cd scale -- cycle icon scale" ); CountDoom.prt("/cd -- dump spell information" ); CountDoom.prt("/cd start -- debug: start a spell" ); CountDoom.prt("/cd end -- debug: end a spell" ); CountDoom.prt("/cd toggle -- enable disable tracking a spell" ); CountDoom.prt("/cd ooc -- delay before removing all timers" ); CountDoom.prt("/cd expire -- delay before removing expired timers" ); CountDoom.prt("/cd hseconds -- display Hundredths of a second" ); CountDoom.prt("/cd debug verbose -- verbose debug msgs" ); end; CountDoom.RegisterWorldEvents = function() -- Need to detect changes to enslaved demon state this:RegisterEvent("LOCALPLAYER_PET_CHANGED"); this:RegisterEvent("UNIT_PET"); this:RegisterEvent("SPELLCAST_STOP"); this:RegisterEvent("SPELLCAST_FAILED"); this:RegisterEvent("SPELLCAST_INTERRUPTED"); -- Used for clearing timers after combat this:RegisterEvent("PLAYER_REGEN_ENABLED"); this:RegisterEvent("PLAYER_REGEN_DISABLED"); -- Used to determine the target ID of a timer. It counts how many times -- a player changes target (including back and forth) this:RegisterEvent("PLAYER_TARGET_CHANGED"); -- Used to detect when a spell was resisted or immune this:RegisterEvent("CHAT_MSG_SPELL_SELF_DAMAGE"); -- Used to detect when Succubus casts Seduction this:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_CREATURE_DAMAGE"); this:RegisterEvent("CHAT_MSG_SPELL_PET_DAMAGE"); -- Used to detect when the succubus Seduction fades this:RegisterEvent("CHAT_MSG_SPELL_AURA_GONE_OTHER"); -- Used to detect when someone dies. this:RegisterEvent("CHAT_MSG_COMBAT_HOSTILE_DEATH"); end; CountDoom.RegisterEvents = function() -- Used to know when a player zones in. this:RegisterEvent("PLAYER_ENTERING_WORLD"); this:RegisterEvent("PLAYER_LEAVING_WORLD"); CountDoom.RegisterWorldEvents(); end; CountDoom.UnregisterWorldEvents = function() -- Need to detect changes to enslaved demon state this:UnregisterEvent("LOCALPLAYER_PET_CHANGED"); this:UnregisterEvent("UNIT_PET"); this:UnregisterEvent("SPELLCAST_STOP"); this:UnregisterEvent("SPELLCAST_FAILED"); this:UnregisterEvent("SPELLCAST_INTERRUPTED"); -- Used for clearing timers after combat this:UnregisterEvent("PLAYER_REGEN_ENABLED"); this:UnregisterEvent("PLAYER_REGEN_DISABLED"); -- Used to determine the target ID of a timer. It counts how many times -- a player changes target (including back and forth) this:UnregisterEvent("PLAYER_TARGET_CHANGED"); -- Used to detect when a spell was resisted or immune this:UnregisterEvent("CHAT_MSG_SPELL_SELF_DAMAGE"); -- Used to detect when Succubus casts Seduction this:UnregisterEvent("CHAT_MSG_SPELL_PERIODIC_CREATURE_DAMAGE"); this:UnregisterEvent("CHAT_MSG_SPELL_PET_DAMAGE"); -- Used to detect when the succubus Seduction fades this:UnregisterEvent("CHAT_MSG_SPELL_AURA_GONE_OTHER"); -- Used to detect when someone dies. this:UnregisterEvent("CHAT_MSG_COMBAT_HOSTILE_DEATH"); end; CountDoom.UnregisterEvents = function() -- Used to know when a player zones in. this:UnregisterEvent("PLAYER_ENTERING_WORLD"); this:UnregisterEvent("PLAYER_LEAVING_WORLD"); CountDoom.UnregisterWorldEvents(); end; CountDoom.RegisterCommands = function() SLASH_COUNTDOOM1 = "/countdoom"; SLASH_COUNTDOOM2 = "/cd"; SlashCmdList["COUNTDOOM"] = function(msg) CountDoomSlash(msg); end end; -- Credit to Morbain for the logic CountDoom.UpdateDurations = function() local tab, talent; local numTalentTabs = GetNumTalentTabs(); CountDoom.dpf( "Talent Tabs: " .. CountDoom.ToStr( numTalentTabs ) ); for tab = 1, numTalentTabs do local numTalents = GetNumTalents(tab); CountDoom.dpf( "Talents: " .. CountDoom.ToStr( numTalents ) ); for talent=1, numTalents do local name, iconTexture, tier, column, rank, maxRank, isExceptional, meetsPrereq = GetTalentInfo(tab, talent); if name then CountDoom.dpf( "("..tab..","..talent .. "): " .. name .. "= " .. rank .. "(" .. maxRank .. ")" ); end end end local duration = CountDoomSpell[ "seduce" ].rankDuration[1]; -- Determine if the player has improved succubus which increases timer duration. local _, texture, _, _, rank, _, _, _ = GetTalentInfo(2, 7); if (texture) then duration = duration + (duration * 0.1 * rank) end CountDoomSpell[ "seduce" ].rankDuration[1] = duration; end; CountDoom.UpdateLayout = function() if CountDoom.config.layout == "horizontal" then CDTimers_LayoutHorizontal(); elseif CountDoom.config.layout == "textonly" then CDTimers_LayoutTextOnly(); else CDTimers_LayoutVertical(); end CDTimerSpell_UpdateSpellPrefixes(); end; CountDoom.CycleScale = function() if CountDoom.config.scale == "small" then CountDoom.config.scale = "medium"; elseif CountDoom.config.scale == "medium" then CountDoom.config.scale = "large"; else CountDoom.config.scale = "small"; end CountDoom.UpdateLayout(); CountDoom.prt( "Scale set to: " .. CountDoom.config.scale ); end; CountDoom.UpdateDragButtons = function() if CountDoom.config.isLocked ~= true then CountDoomFrame:SetBackdropColor( 1.0, 1.0, 1.0, 1.0 ); CountDoomFrame:SetBackdropBorderColor( 1.0, 1.0, 1.0, 1.0 ); CountDoomFrameBackground:SetAlpha(1.0); CountDoomFrame:Show(); getglobal("CountDoomFrameDragButton"):Show(); CountDoom.dpf("Showing drag buttons."); else CountDoomFrame:SetBackdropColor( 1.0, 1.0, 1.0, 0.0 ); CountDoomFrame:SetBackdropBorderColor( 1.0, 1.0, 1.0, 0.0 ); CountDoomFrameBackground:SetAlpha(0.0); getglobal("CountDoomFrameDragButton"):Hide(); CountDoom.dpf("Hiding drag buttons."); if CDTimer_numTimers == 0 then CountDoomFrame:Hide(); end end end; CountDoom.RotateLayout = function( frameName ) if frameName == "CountDoomFrame" then if CountDoom.config.layout == "vertical" then CountDoom.config.layout = "horizontal"; elseif CountDoom.config.layout == "horizontal" then CountDoom.config.layout = "textonly"; else CountDoom.config.layout = "vertical"; end CountDoom.UpdateLayout(); end CountDoom.prt( "Layout set to: " .. CountDoom.config.layout ); end; CountDoom.CreateConfig = function( playerName, realmName ) CountDoom.dpf("Initializing CountDoom.config"); if not CountDoomConfig then CountDoom.dpf("Allocating CountDoomConfig"); CountDoomConfig = {}; end if not CountDoomConfig[realmName] then CountDoomConfig[realmName] = {}; end if not CountDoomConfig[realmName][playerName] then CountDoomConfig[realmName][playerName] = {}; end if (not CountDoomConfig[realmName][playerName].enableSpell) then CountDoomConfig[realmName][playerName].enableSpell = {}; end -- set the defaults if the values weren't loaded by the SavedVariables.lua if( CountDoomConfig[realmName][playerName].isLocked == nil ) then CountDoomConfig[realmName][playerName].isLocked = CD_DefaultConfig.isLocked; end if( CountDoomConfig[realmName][playerName].warningSound == nil ) then CountDoomConfig[realmName][playerName].warningSound = {}; CountDoomConfig[realmName][playerName].warningSound[ "enslave" ] = "enslavewarning"; end if( CountDoomConfig[realmName][playerName].endSound == nil ) then CountDoomConfig[realmName][playerName].endSound = {}; CountDoomConfig[realmName][playerName].endSound[ "enslave" ] = "enslaveend"; end if( CountDoomConfig[realmName][playerName].announceSpells == nil ) then CountDoomConfig[realmName][playerName].announceSpells = CD_DefaultConfig.announceSpells; end if( CountDoomConfig[realmName][playerName].playSounds == nil ) then CountDoomConfig[realmName][playerName].playSounds = CD_DefaultConfig.playSounds; end if( CountDoomConfig[realmName][playerName].flashSpells == nil ) then CountDoomConfig[realmName][playerName].flashSpells = CD_DefaultConfig.flashSpells; end if( CountDoomConfig[realmName][playerName].enable == nil ) then CountDoomConfig[realmName][playerName].enable = CD_DefaultConfig.enable; end if( CountDoomConfig[realmName][playerName].layout == nil ) then CountDoomConfig[realmName][playerName].layout = CD_DefaultConfig.layout; end if( CountDoomConfig[realmName][playerName].scale == nil ) then CountDoomConfig[realmName][playerName].scale = CD_DefaultConfig.scale; end if( CountDoomConfig[realmName][playerName].hseconds == nil ) then CountDoomConfig[realmName][playerName].hseconds = CD_DefaultConfig.hseconds; end if( CountDoomConfig[realmName][playerName].outofcombatDelay == nil ) then CountDoomConfig[realmName][playerName].outofcombatDelay = CD_DefaultConfig.outofcombatDelay; end if( CountDoomConfig[realmName][playerName].postExpireDelay == nil ) then CountDoomConfig[realmName][playerName].postExpireDelay = CD_DefaultConfig.postExpireDelay; end if( CountDoomConfig[realmName][playerName].announceChannel == nil ) then CountDoomConfig[realmName][playerName].announceChannel = CD_DefaultConfig.announceChannel; end end; CountDoom.Initialize = function () if (CountDoom.initialized == true) then CountDoom.dpf("Initialize: already loaded. returning."); return end local playerName = UnitName("player"); local realmName = GetRealmName(); -- if CountDoom.playerName is "Unknown Entity" get out, need a real name if (playerName == nil or playerName == UNKNOWNOBJECT) then CountDoom.dpf("Initialize: invalid name. returning"); return end CountDoom.playerName = playerName; CountDoom.realmName = realmName; -- Initialize our configuration CountDoom.CreateConfig(playerName, realmName); -- Alias the config to shorten the name. Hopefully, this save settings otherwise we're toast. CountDoom.config = CountDoomConfig[realmName][playerName]; CountDoom.RegisterEvents(); CountDoom.RegisterCommands(); CountDoom.UpdateDurations(); CountDoom.UpdateLayout(); CountDoom.UpdateDragButtons(); CountDoom.GetSpellBookInfo(); -- print out a nice message letting the user know the addon loaded CountDoom.prt( COUNTDOOM_LOADED ); -- All variables are loaded now CountDoom.initialized = true; end; -- Sets the warning sound. (FUTURE USE) CountDoom.SetWarningSound = function(spellAbbreviation, msg) CountDoom.config.warningSound[ spellAbbreviation ] = msg; CountDoom.prt( string.format( COUNTDOOM_SETWARNINGSOUNDMSG, warningSound[ spellAbbreviation ] ) ); end; -- Sets the end sound. (FUTURE USE) CountDoom.SetEndSound = function(spellAbbreviation, msg) CountDoom.config.endSound[ spellAbbreviation ] = msg; CountDoom.prt( string.format( COUNTDOOM_SETENDSOUNDMSG, CountDoom.config.endSound[ spellAbbreviation ] ) ); end; CountDoom.ToggleAnnouncingSpells = function() if CountDoom.config.announceSpells == nil or CountDoom.config.announceSpells == "never" then CountDoom.config.announceSpells = "local"; elseif CountDoom.config.announceSpells == "local" then CountDoom.config.announceSpells = "party"; elseif CountDoom.config.announceSpells == "party" then CountDoom.config.announceSpells = "raid"; elseif CountDoom.config.announceSpells == "raid" then CountDoom.config.announceSpells = "channel"; elseif CountDoom.config.announceSpells == "channel" then CountDoom.config.announceSpells = "all"; else CountDoom.config.announceSpells = "never"; end CountDoom.prt( "Spells will be announced: " .. CountDoom.config.announceSpells ); end; CountDoom.SetEnable = function( enable ) CountDoom.config.enable = enable; if( CountDoom.config.enable == true ) then CountDoom.prt( COUNTDOOM_ENABLEDMSG ); else CountDoom.prt( COUNTDOOM_DISABLEDMSG ); end end; CountDoom.SetLocked = function( locked ) CountDoom.config.isLocked = locked; if( CountDoom.config.isLocked == true ) then CountDoom.prt( COUNTDOOM_LOCKED ); else CountDoom.prt( COUNTDOOM_UNLOCKED ); end CountDoom.UpdateDragButtons(); end; CountDoom.ToggleFlashingSpells = function () if( CountDoom.config.flashSpells == true ) then CountDoom.config.flashSpells = false; CountDoom.prt( COUNTDOOM_NOFLASHMSG ); else CountDoom.config.flashSpells = true; CountDoom.prt( COUNTDOOM_YESFLASHMSG ); end end; CountDoom.TogglePlayingSounds = function () if( CountDoom.config.playSounds == true ) then CountDoom.config.playSounds = false; CountDoom.prt( COUNTDOOM_NOSOUNDSMSG ); else CountDoom.config.playSounds = true; CountDoom.prt( COUNTDOOM_YESSOUNDSMSG ); end end; CountDoom.ToggleHseconds = function () if( CountDoom.config.hseconds == true ) then CountDoom.config.hseconds = false; CountDoom.prt( COUNTDOOM_HSECONDS_OFF ); else CountDoom.config.hseconds = true; CountDoom.prt( COUNTDOOM_HSECONDS_ON ); end end; CountDoom.SetOOCTime = function ( delayString ) if delayString ~= nil then local oocTime = tonumber( delayString ); if oocTime ~= nil then CountDoom.config.outofcombatDelay = oocTime; end end CountDoom.prt( "Out of combat delay is: " .. CountDoom.ToStr( CountDoom.config.outofcombatDelay ) ); end; CountDoom.SetPostExpireDelay = function ( delayString ) if delayString ~= nil then local expireDelayTime = tonumber( delayString ); if expireDelayTime ~= nil then CountDoom.config.postExpireDelay = expireDelayTime; end end CountDoom.prt( "Delay before removing expired timers: " .. CountDoom.ToStr( CountDoom.config.postExpireDelay ) ); end; CountDoom.SetAnnounceChannel = function( channelName ) if channelName ~= nil then CountDoom.config.announceChannel = channelName; end CountDoom.prt( "Announce channel: " .. CountDoom.ToStr( CountDoom.config.announceChannel ) ); end; CountDoom.DumpEvents = function () CountDoom.prt( "Last spell cast: " .. CountDoom.ToStr( CountDoom.event.castSpellName ) ); CountDoom.prt( "Mode: " .. CountDoom.ToStr( CountDoom.event.castMode ) ); CountDoom.prt( "Target: " .. CountDoom.ToStr( CountDoom.event.castTarget ) ); CountDoom.prt( "Last spell removed: " .. CountDoom.ToStr( CountDoom.event.removedSpellName ) ); CountDoom.prt( "Removal reason: " .. CountDoom.ToStr( CountDoom.event.removedLastReason ) ); CountDoom.prt( "Target: " .. CountDoom.ToStr( CountDoom.event.removedTarget ) ); end; CountDoom.FilterTimer = function( spellName ) CountDoom.dpf( "CD: CountDoom.FilterTimer " .. CountDoom.ToStr( spellName ) ); -- Destroy the last active spell CountDoom.activeSpell = nil; if (CountDoom.lastSpellID == nil or CountDoom.lastSpellID == -1 or CountDoom.replacedASpell) then return; end local spellIndex = CDTimerSpell_GetSpellIndex( CountDoom.lastSpellID ); local spellNameLast = nil; if spellIndex ~= -1 then spellNameLast = CDTimerSpell_GetSpellName( spellIndex ); end if spellNameLast == spellName then CDTimerSpell_DeleteIndex( spellIndex ); end CountDoom.lastSpellID = -1; end; function CountDoomSlash(msg) msg = string.lower(msg); local a, b, c, n = string.find (msg, "(%w+) ([_%w]+)"); if( c == nil and n == nil ) then a, b, c = string.find (msg, "(%w+)"); end if (c ~= nil) then CountDoom.dpf("c:"..c); else CountDoom.dpf("c: nil"); end if (n ~= nil) then CountDoom.dpf("n:"..n); else CountDoom.dpf("n: nil"); end if (c == nil) then CountDoom.DisplayCommands(); CountDoom.DisplaySettings(); return; end if( CountDoomSpell[ c ] ~= nil ) then if( n == "start" ) then local targetInfo = {}; targetInfo.targetName = UnitName( "target" ); targetInfo.targetLevel = UnitLevel( "target" ); targetInfo.id = CountDoom.targetID; CountDoom.lastSpellID, CountDoom.replacedASpell = CDTimerSpell_CreateBySpellAbbreviation( c, targetInfo, nil ); elseif( n == "end" ) then CDTimerSpell_DestroyBySpellAbbreviation( c ); CountDoom.event.removedSpellName = c; CountDoom.event.removedLastReason = "/cd " .. c .. " end"; CountDoom.event.removedTarget = nil; elseif( n == "toggle" ) then CountDoomSpell.ToggleEnabled( c ); elseif( n == "play" ) then CountDoomSpell.ToggleSound( c ); elseif( n == "announce" ) then CountDoomSpell.ToggleAnnounce( c ); elseif( n == nil ) then CountDoomSpell.Dump( c ); end elseif( c == "announce" ) then CountDoom.ToggleAnnouncingSpells(); elseif( c == "play" ) then CountDoom.TogglePlayingSounds(); elseif( c == "flash" ) then CountDoom.ToggleFlashingSpells(); elseif( c == "debug" ) then if( n == "verbose" ) then if( CountDoom.debugVerbose == true ) then CountDoom.debugVerbose = false; CountDoom.dpf( "debugVerbose is off" ); else CountDoom.debugVerbose = true; CountDoom.dpf( "debugVerbose is on" ); end elseif( n == "events" ) then if( CountDoom.debugEvents == true ) then CountDoom.debugEvents = false; CountDoom.prt( "debugEvents is off" ); else CountDoom.debugEvents = true; CountDoom.prt( "debugEvents is on" ); end elseif( n == "dump" ) then CountDoom.DumpEvents(); end elseif( c == "enable" ) then CountDoom.SetEnable( true ); elseif( c == "disable" ) then CountDoom.SetEnable( false ); elseif( c == "layout" ) then CountDoom.RotateLayout( "CountDoomFrame" ); elseif( c == "unlock" ) then CountDoom.SetLocked( false ); elseif( c == "lock" ) then CountDoom.SetLocked( true ); elseif( c == "scale" ) then CountDoom.CycleScale(); elseif( c == "hseconds" ) then CountDoom.ToggleHseconds(); elseif( c == "ooc" and n ~= nil ) then CountDoom.SetOOCTime( n ); elseif( c == "expire" and n ~= nil ) then CountDoom.SetPostExpireDelay( n ); elseif( c == "channel" ) then CountDoom.SetAnnounceChannel( n ); end end function CDFrame_OnUpdate(elapsed) CountDoom.totalTime = CountDoom.totalTime + elapsed; if CountDoom.timeRemoveAllTimers ~= nil then -- Dont' remove timers if we have a pending spell to cast if CountDoom.activeSpell ~= nil then CountDoom.timeRemoveAllTimers = nil; return; end if GetTime() >= CountDoom.timeRemoveAllTimers then CountDoom.event.removedSpellName = "All timers"; CountDoom.event.removedLastReason = event; CountDoom.event.removedTarget = nil; CDTimerSpell_RemoveCombatSpellTimers(); CountDoom.timeRemoveAllTimers = nil; end end end function CDFrame_OnLoad() -- Used for initialization of the mod this:RegisterEvent("VARIABLES_LOADED"); end function CDFrame_OnEvent(event) -- Keep track of the events if CountDoom.debugVerbose or CountDoom.debugEvents then local msg = "Event: " .. event .. ": " .. CountDoom.ToStr( arg1 ) .. " " .. CountDoom.ToStr( arg2 ); if CountDoom.debugEvents then CountDoom.CombatPrint(msg); end CountDoom.dpf(msg); end -- Initialize as soon as the player logs in if (event == "VARIABLES_LOADED") then CountDoom.Initialize(); return; end if CountDoom.initialized ~= true then return; end if CountDoom.config.enable ~= true then return; end if UnitClass("player") ~= COUNTDOOM_WARLOCK then return; end -- Pet status changed if (event == "UNIT_PET" and arg1 == "player") or (event == "LOCALPLAYER_PET_CHANGED") then if (UnitIsFriend("player", "pet")) then local iIterator = 1 while (UnitDebuff("pet", iIterator)) do local debuffString = UnitDebuff("pet", iIterator); CountDoom.dpf( "Debuff[" .. iIterator .. "] " .. debuffString); if (string.find(debuffString, COUNTDOOMDEBUFF_ENSLAVEDEMON)) then local targetInfo = {}; targetInfo.targetName = UnitName( "target" ); targetInfo.targetLevel = UnitLevel( "target" ); targetInfo.id = CountDoom.targetID; CountDoom.lastSpellID, CountDoom.replacedASpell = CDTimerSpell_CreateBySpellAbbreviation( "enslave", targetInfo, nil ); end iIterator = iIterator + 1 end else CDTimerSpell_DestroyBySpellAbbreviation( "enslave" ); CountDoom.event.removedSpellName = "enslave"; CountDoom.event.removedLastReason = event; CountDoom.event.removedTarget = nil; end -- Started casting a spell (DEBUG) elseif event == "SPELLCAST_START" then --We used to track the activeSpell name by monitoring this event but --unfortunately it doesn't track insta-cast spells -- Spell casting was interrupted elseif event == "SPELLCAST_INTERRUPTED" or event == "SPELLCAST_FAILED" then CountDoom.activeSpell = nil; CountDoom.targetName = nil; CountDoom.targetLevel = 0; CountDoom.activeSpellWaitingForTarget = nil; if CountDoom.lastSpellID ~= -1 and CountDoom.replacedASpell == false then CDTimerSpell_DeleteID( CountDoom.lastSpellID ); CountDoom.event.removedSpellName = "spellID: " .. CountDoom.lastSpellID; CountDoom.event.removedLastReason = event; if CDTimerSpells[ CountDoom.lastSpellID ] ~= nil then CountDoom.event.removedTarget = CDTimerSpells[ CountDoom.lastSpellID ].targetName; else CountDoom.event.removedTarget = nil; end end CountDoom.lastSpellID = -1; CountDoom.replacedASpell = false; -- User stopped casting a spell elseif event == "SPELLCAST_STOP" then if CountDoom.activeSpell ~= nil then local targetInfo = {}; targetInfo.targetName = CountDoom.targetName; targetInfo.targetLevel = CountDoom.targetLevel; targetInfo.id = CountDoom.targetID; -- We are likely to enter combat. Don't delete timers immediately. CountDoom.timeRemoveAllTimers = nil; CountDoom.lastSpellID, CountDoom.replacedASpell = CDTimerSpell_CreateBySpellName( CountDoom.activeSpell, targetInfo, CountDoom.spellRank ); CountDoom.activeSpell = nil; end -- Player has left combat elseif event == "PLAYER_REGEN_ENABLED" then CountDoom.timeRemoveAllTimers = GetTime() + CountDoom.config.outofcombatDelay; -- Player has entered combat elseif event == "PLAYER_REGEN_DISABLED" then CountDoom.timeRemoveAllTimers = nil; -- Pet has cast a spell (DEBUG) elseif(event == "CHAT_MSG_SPELL_PET_DAMAGE") then arg1 = string.gsub(arg1," %(.+%)","") -- strip trailing ()'s we don't use arg1 = string.gsub(arg1,"%.$","") -- strip trailing .'s local found,_,casterName,spellName,index3,index4,index5 = string.find(arg1, CD_SPELLCASTOTHERSTART); if found then CountDoom.dpf( "CD: " .. CountDoom.ToStr( casterName ) .. " begins to cast " .. CountDoom.ToStr( spellName ) ); CountDoom.petTargetName = UnitName( "pettarget" ); CountDoom.petTargeLevel = UnitLevel( "pettarget" ); CountDoom.petActiveSpell = spellName; end elseif(event == "CHAT_MSG_SPELL_SELF_DAMAGE") then arg1 = string.gsub(arg1," %(.+%)","") -- strip trailing ()'s we don't use arg1 = string.gsub(arg1,"%.$","") -- strip trailing .'s -- Determine if the target resisted local found,_,spellName,mobName,index3,index4,index5 = string.find(arg1, CD_SPELLRESISTSELFOTHER); if found then CountDoom.dpf( "CD: " .. CountDoom.ToStr( spellName ) .. " was resisted by " .. CountDoom.ToStr( mobName ) ); CountDoom.FilterTimer( spellName ); return; end -- Determine if the target was immune local found,_,spellName,mobName,index3,index4,index5 = string.find(arg1, CD_SPELLIMMUNESELFOTHER); if found then CountDoom.dpf( "CD: " .. CountDoom.ToStr( mobName ) .. " was immune to " .. CountDoom.ToStr( spellName ) ); CountDoom.FilterTimer( spellName ); return; end -- Determine if the target was evaded local found,_,spellName,mobName,index3,index4,index5 = string.find(arg1, CD_SPELLEVADEDSELFOTHER); if found then CountDoom.dpf( "CD: " .. CountDoom.ToStr( mobName ) .. " evaded your " .. CountDoom.ToStr( spellName ) ); CountDoom.FilterTimer( spellName ); return; end -- Creature received damage. See if Succubus seduced a mob. elseif(event == "CHAT_MSG_SPELL_PERIODIC_CREATURE_DAMAGE") then arg1 = string.gsub(arg1," %(.+%)","") -- strip trailing ()'s we don't use arg1 = string.gsub(arg1,"%.$","") -- strip trailing .'s local found,_,mobName,spellName,index3,index4,index5 = string.find(arg1, CD_AURAADDEDOTHERHARMFUL); if found and spellName ~= nil then CountDoom.dpf( "CD: " .. CountDoom.ToStr( mobName ) .. " is afflicted by " .. CountDoom.ToStr( spellName ) ); if (spellName == COUNTDOOMSPELL_SEDUCE and CountDoom.petActiveSpell == COUNTDOOMSPELL_SEDUCE) or (spellName == COUNTDOOMSPELL_SPELL_LOCK) then local targetInfo = {}; targetInfo.targetName = CountDoom.petTargetName; targetInfo.targetLevel = CountDoom.petTargeLevel; targetInfo.id = -1; -- Currently we assume pet casts max rank spell. local petRank = nil; CountDoom.lastSpellID, CountDoom.replacedASpell = CDTimerSpell_CreateBySpellName( spellName, targetInfo, petRank ); CountDoom.petTargetName = nil; CountDoom.petTargeLevel = 0; CountDoom.petActiveSpell = nil; end return; end -- Created lost a debuff. See if Succubus' seduction had faded. elseif(event == "CHAT_MSG_SPELL_AURA_GONE_OTHER") then arg1 = string.gsub(arg1," %(.+%)","") -- strip trailing ()'s we don't use arg1 = string.gsub(arg1,"%.$","") -- strip trailing .'s local found,_,spellName,mobName,index3,index4,index5 = string.find(arg1, CD_AURAREMOVEDOTHER); if found then CountDoom.dpf( "CD: " .. CountDoom.ToStr( spellName ) .. " fades from " .. CountDoom.ToStr( mobName ) ); if spellName == COUNTDOOMSPELL_SEDUCE or spellName == COUNTDOOMSPELL_SPELL_LOCK then CDTimerSpell_DestroyBySpellName( spellName ); CountDoom.event.removedSpellName = spellName; CountDoom.event.removedLastReason = CHAT_MSG_SPELL_AURA_GONE_OTHER; CountDoom.event.removedTarget = mobName; end return; end elseif event == "CHAT_MSG_COMBAT_HOSTILE_DEATH" then arg1 = string.gsub(arg1," %(.+%)","") -- strip trailing ()'s we don't use arg1 = string.gsub(arg1,"%.$","") -- strip trailing .'s local found,_,mobName,index3,index4,index5 = string.find(arg1, CD_UNITDIESOTHER); if found then CountDoom.dpf( "CD: " .. CountDoom.ToStr( mobName ) .. " dies." ); if UnitIsDead( "target" ) then local deletedOne; repeat deletedOne = CDTimerSpell_DeleteTarget( CountDoom.targetID ); until deletedOne == false; end; return; end -- Determine if the player has changed targets (FUTURE USE) elseif event == "PLAYER_TARGET_CHANGED" then -- Keep track of the current targetID CountDoom.targetID = CountDoom.targetID + 1; if CountDoom.targetID > 10000 then CountDoom.targetID = 0; end -- Player has changed zones elseif event == "PLAYER_ENTERING_WORLD" then CountDoom.timeRemoveAllTimers = GetTime() + CountDoom.config.outofcombatDelay; CountDoom.RegisterWorldEvents(); -- Player has changed zones elseif event == "PLAYER_LEAVING_WORLD" then CountDoom.UnregisterWorldEvents(); end end CountDoom.GetSpellBookInfo = function() CountDoom.dpf( "CountDoom.GetSpellBookInfo" ); local i; for i = 1, GetNumSpellTabs(), 1 do local name, texture, offset, numSpells = GetSpellTabInfo(i); local y; for y = 1, numSpells, 1 do local spellName, rankName = GetSpellName(offset+y, BOOKTYPE_SPELL); local _, _, rankStr = string.find(rankName, "(%d+)"); local rank = tonumber(rankStr); if rank then if not CountDoom.spellTable then CountDoom.spellTable = {}; end; if not CountDoom.spellTable[spellName] then CountDoom.spellTable[spellName] = {}; end if not CountDoom.spellTable[spellName].maxRank or rank > CountDoom.spellTable[spellName].maxRank then CountDoom.spellTable[spellName].maxRank = rank; end CountDoom.dpf( "Adding spell (" .. y+offset .. ") " .. spellName .. " rank " .. rank .. " to spellTable" ); CountDoom.spellTable[spellName][rank] = { ["tab"] = i, ["spell"] = y+offset }; end end end end; -- SPELLCAST_START event only sets the spell name for spells that are -- not insta-cast. To detect insta-cast spells, we need to hook into -- the CastSpell function. Once hooked, each spell is placed into a hidden -- tooltip and the name is extracted. Courtesy of CT_RaidAssist. CountDoom.oldCastSpell = CastSpell; CountDoom.OnCastSpell = function (spellId,spellbookTabNum) local spellName, spellRank = GetSpellName(spellId, spellbookTabNum); CountDoom.dpf( CountDoom.ToStr( spellName ) .. " " .. CountDoom.ToStr( spellRank ) ); if spellRank ~= nil then local _, _, spellRankString = string.find(spellRank, "(%d+)"); --local _, _, spellRankString = string.find( spellRank, "[^%d]*(%d+)"); rank = tonumber(spellRankString); CountDoom.dpf( "Rank: " .. CountDoom.ToStr( rank ) ); end; CountDoom.spellRank = rank; if SpellIsTargeting() then CountDoom.activeSpellWaitingForTarget = spellName; else CountDoom.activeSpell = spellName; CountDoom.targetName = UnitName( "target" ); CountDoom.targetLevel = UnitLevel( "target" ); CountDoom.lastSpellID = -1; CountDoom.replacedASpell = false; CountDoom.event.castSpellName = spellName; CountDoom.event.castMode = "CastSpell"; CountDoom.event.castTarget = CountDoom.targetName; end CountDoom.dpf( "Starting cast of: " .. CountDoom.ToStr( spellName ) .. " " .. spellId .. " " .. spellbookTabNum ); CountDoom.oldCastSpell( spellId, spellbookTabNum ); end; CastSpell = CountDoom.OnCastSpell; CountDoom.oldSpellTargetUnit = SpellTargetUnit; CountDoom.OnSpellTargetUnit = function (unit) if CountDoom.activeSpellWaitingForTarget then CountDoom.activeSpell = CountDoom.activeSpellWaitingForTarget; CountDoom.targetName = UnitName( unit ); CountDoom.targetLevel = UnitLevel( unit ); CountDoom.activeSpellWaitingForTarget = nil; CountDoom.lastSpellID = -1; end CountDoom.oldSpellTargetUnit(unit); end; SpellTargetUnit = CountDoom.OnSpellTargetUnit; CountDoom.oldTargetUnit = TargetUnit; CountDoom.OnTargetUnit = function (unit) if CountDoom.activeSpellWaitingForTarget then CountDoom.activeSpell = CountDoom.activeSpellWaitingForTarget; CountDoom.targetName = UnitName( unit ); CountDoom.targetLevel = UnitLevel( unit ); CountDoom.activeSpellWaitingForTarget = nil; CountDoom.lastSpellID = -1; end CountDoom.oldTargetUnit(unit); end; TargetUnit = CountDoom.OnTargetUnit; CountDoom.oldSpellStopTargetting = SpellStopTargetting; CountDoom.OnSpellStopTargetting = function() if CountDoom.activeSpellWaitingForTarget then CountDoom.activeSpell = nil; CountDoom.targetName = nil; CountDoom.targetLevel = 0; CountDoom.activeSpellWaitingForTarget = nil; end CountDoom.oldSpellStopTargetting(); end; SpellStopTargetting = CountDoom.OnSpellStopTargetting; CountDoom.oldUseAction = UseAction; CountDoom.OnUseAction = function (a1, a2, a3) -- Only process if it isn't a macro if GetActionText(a1) == nil then CD_SpellDetector:Hide(); CD_SpellDetector:SetOwner(CountDoomFrame,"ANCHOR_NONE"); CD_SpellDetector:SetAction(a1); local spellName = nil; if CD_SpellDetectorTextLeft1 ~= nil then spellName = CD_SpellDetectorTextLeft1:GetText(); end; local spellRank = nil; if CD_SpellDetectorTextRight1 ~= nil then spellRank = CD_SpellDetectorTextRight1:GetText(); end; local rank = nil; if spellRank ~= nil then local _, _, spellRankString = string.find( spellRank, "[^%d]*(%d+)"); rank = tonumber(spellRankString); end; CountDoom.spellRank = rank; if SpellIsTargeting() then CountDoom.activeSpellWaitingForTarget = spellName; else CountDoom.activeSpell = spellName; CountDoom.targetName = UnitName( "target" ); CountDoom.targetLevel = UnitLevel( "target" ); CountDoom.lastSpellID = -1; CountDoom.event.castSpellName = spellName; CountDoom.event.castMode = "UseAction"; CountDoom.event.castTarget = CountDoom.targetName; end if ( a3 and a3 == 1 ) then CountDoom.activeSpell = nil; end if CountDoom.debugEvents then local msg = "UseAction: " .. CountDoom.ToStr( a1 ) .. " " .. CountDoom.ToStr( a2 ) .. " " .. CountDoom.ToStr( a3 ); msg = msg .. " " .. CountDoom.ToStr( spellName ); CountDoom.CombatPrint(msg); end CountDoom.dpf( "UseAction cast of: " .. CountDoom.ToStr( spellName ) .. " Rank: " .. CountDoom.ToStr( rank ) ); end CountDoom.oldUseAction(a1, a2, a3); end; UseAction = CountDoom.OnUseAction; CountDoom.oldCastSpellByName = CastSpellByName; CountDoom.OnCastSpellByName = function(spellString) local startIndex, endIndex, spellName = string.find( spellString, "(.+)%(" ); local _, _, spellRankStr = string.find( spellString, "([%d]+)"); local rank = nil; CountDoom.dpf( spellString ); if spellName == nil then startIndex, endIndex, spellName = string.find( spellString, "(.+)" ); end if spellName == nil then CountDoom.prt( "CountDoom: Error: Unable to determine spell information from: '" .. spellString .. "'"); CountDoom.prt( "CountDoom: contact mod owner with string for help." ); end if spellRankStr ~= nil then CountDoom.dpf( spellRankStr ); rank = tonumber( spellRankStr ); end; CountDoom.spellRank = rank; if SpellIsTargeting() then CountDoom.activeSpellWaitingForTarget = spellName; else CountDoom.activeSpell = spellName; CountDoom.targetName = UnitName( "target" ); CountDoom.targetLevel = UnitLevel( "target" ); CountDoom.lastSpellID = -1; CountDoom.replacedASpell = false; CountDoom.event.castSpellName = spellName; CountDoom.event.castMode = "CastSpellByName"; CountDoom.event.castTarget = CountDoom.targetName; end CountDoom.dpf( "CastSpellByName cast of: " .. CountDoom.ToStr( spellName ) .. " Rank: " .. CountDoom.ToStr( rank ) ); return CountDoom.oldCastSpellByName(spellString); end; CastSpellByName = CountDoom.OnCastSpellByName; --[[ Can't hook this in 1.10 CountDoom.oldCameraOrSelectOrMoveStart = CameraOrSelectOrMoveStart; function CountDoom.OnCameraOrSelectOrMoveStart() -- If we're waiting to target local targetName = nil; local targetLevel = 0; if CountDoom.activeSpellWaitingForTarget and UnitName("mouseover") then targetName = UnitName("mouseover"); targetLevel = UnitLevel("mouseover"); end CountDoom.oldCameraOrSelectOrMoveStart(); if CountDoom.activeSpellWaitingForTarget then CountDoom.activeSpell = CountDoom.activeSpellWaitingForTarget; CountDoom.targetName = targetName; CountDoom.targetLevel = targetLevel; CountDoom.activeSpellWaitingForTarget = nil; CountDoom.lastSpellID = -1; CountDoom.replacedASpell = false; end end; CameraOrSelectOrMoveStart = CountDoom.OnCameraOrSelectOrMoveStart; --]]