vanilla-wow-addons – Rev 1
?pathlinks?
-- 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<DEBUG>: " .. 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> -- enable/disable CountDoom" );
CountDoom.prt("/cd <lock|unlock> -- 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 <spell> -- dump spell information" );
CountDoom.prt("/cd <spell> start -- debug: start a spell" );
CountDoom.prt("/cd <spell> end -- debug: end a spell" );
CountDoom.prt("/cd <spell> toggle -- enable disable tracking a spell" );
CountDoom.prt("/cd ooc <seconds> -- delay before removing all timers" );
CountDoom.prt("/cd expire <seconds> -- 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;
--]]