Files
CallOfElements/COE_TotemLogic.lua
T
MDGitHubRepo 233acdbaaa Add files via upload
Updated totemdata.lua to ignore spellname "Totemic Recall" when building the totems.
2024-11-29 13:25:01 -05:00

1201 lines
36 KiB
Lua

--[[
CALL OF ELEMENTS
The All-In-One Shaman Addon
by Wyverex (2006)
Totem Module / Logical
]]
if( not COE_Totem ) then
COE_Totem = {};
end
--[[ ----------------------------------------------------------------
stores the original functions hooked by SetupTimerHooks()
-------------------------------------------------------------------]]
COE_Totem["TimerHooks"] = {};
--[[ =============================================================================================
L O G I C
================================================================================================]]
--[[ ----------------------------------------------------------------
METHOD: COE_Totem:Rescan
PURPOSE: Scans the totems and totem sets
-------------------------------------------------------------------]]
function COE_Totem:Rescan()
COE:ScanTotems();
COE:InitTotemSets();
-- Reset all active totems
-- ------------------------
COE_Totem:DeactivateTimer( COE.ActiveTotems.Earth );
COE_Totem:DeactivateTimer( COE.ActiveTotems.Fire );
COE_Totem:DeactivateTimer( COE.ActiveTotems.Water );
COE_Totem:DeactivateTimer( COE.ActiveTotems.Air );
COE.ActiveTotems.Earth = nil;
COE.ActiveTotems.Fire = nil;
COE.ActiveTotems.Water = nil;
COE.ActiveTotems.Air = nil;
COE_Totem:UpdateAllFrames();
COETotemFrame.Reconfigure = true;
end
--[[ ----------------------------------------------------------------
METHOD: COE_Totem:ThrowTotem
PURPOSE: Throws the totem with the specified named id
-------------------------------------------------------------------]]
function COE_Totem:ThrowTotem( element, id )
if( not COE.Initialized ) then
return;
end
-- get associated button
-- ----------------------
local button = getglobal( "COETotem" .. element .. id );
if( button == nil ) then
COE:Message( "Invalid Totem" );
return;
end
local totem = button.totem;
if( totem == nil ) then
COE:Message( COESTR_NOTOTEM );
return;
end
-- throw totem
-- ------------
CastSpellByName( totem.SpellName );
end
--[[ ----------------------------------------------------------------
METHOD: COE_Totem:RankModifierDown
PURPOSE: Returns if the rank 1 modifier is currently pressed
-------------------------------------------------------------------]]
function COE_Totem:RankModifierDown()
local overridekey = COE_Config:GetSaved( COEOPT_OVERRIDERANK );
-- check if the override key is down
-- ----------------------------------
if( overridekey == COEMODE_OVERRIDESHIFT and IsShiftKeyDown() ) then
return true;
elseif( overridekey == COEMODE_OVERRIDEALT and IsAltKeyDown() ) then
return true;
elseif( overridekey == COEMODE_OVERRIDECTRL and IsControlKeyDown() ) then
return true;
end
return false;
end
--[[ ----------------------------------------------------------------
METHOD: COESched_AdviseTotem
PURPOSE: If the totem warning is still active, a notification
is displayed and the function rescheduled
-------------------------------------------------------------------]]
function COESched_AdviseTotem( totem, msg )
if( COE_Config:GetSaved( COEOPT_ADVISOR ) == 1 and totem.Warn ) then
-- issue warning
-- --------------
COE:Notification( string.format( COESTR_CLEANSINGTOTEM, msg ), COECOL_TOTEMCLEANSING );
-- reschedule
-- -----------
Chronos.scheduleByName( "COEAdvise" .. msg, COE.AdvisorWarningInterval, COESched_AdviseTotem, totem, msg );
end
end
--[[ ----------------------------------------------------------------
METHOD: COESched_RunAdvisor
PURPOSE: Scans the party/raid for debuffs that are curable by
totems and shows a notification if it finds such
-------------------------------------------------------------------]]
function COESched_RunAdvisor()
-- advisor still activated?
-- -------------------------
if( COE_Config:GetSaved( COEOPT_ADVISOR ) == 0 ) then
return;
end
local warnPoison, warnDisease, warnTremor = false, false, false;
local poison, disease, tremor;
-- scan player first
-- ------------------
poison, disease, tremor = COE_Totem:ScanTargetForDebuff( "player" );
warnPoison = warnPoison or poison;
warnDisease = warnDisease or disease;
warnTremor = warnTremor or tremor;
-- scan party
-- -----------
local i;
for i = 1, GetNumPartyMembers() do
if( warnPoison and warnDisease and warnTremor ) then
break;
end
-- scan party member
-- ------------------
poison, disease, tremor = COE_Totem:ScanTargetForDebuff( "party" .. i );
warnPoison = warnPoison or poison;
warnDisease = warnDisease or disease;
warnTremor = warnTremor or tremor;
-- scan party member's pet
-- ------------------------
if( UnitExists( "partypet" .. i ) ) then
poison, disease, tremor = COE_Totem:ScanTargetForDebuff( "partypet" .. i );
warnPoison = warnPoison or poison;
warnDisease = warnDisease or disease;
warnTremor = warnTremor or tremor;
end
end
-- issue warnings if necessary
-- ----------------------------
if( warnTremor and COE.CleansingTotems.Tremor.Totem and
(COE.ActiveTotems["Earth"] ~= COE.CleansingTotems.Tremor.Totem or
not COE_Totem:IsTimerActive( COE.ActiveTotems["Earth"] )) ) then
if( not COE.CleansingTotems.Tremor.Warn ) then
COE.CleansingTotems.Tremor.Warn = true;
COESched_AdviseTotem( COE.CleansingTotems.Tremor, COESTR_TOTEMTREMOR );
end
else
COE.CleansingTotems.Tremor.Warn = false;
end
if( warnDisease and COE.CleansingTotems.Disease.Totem and
(COE.ActiveTotems["Water"] ~= COE.CleansingTotems.Disease.Totem or
not COE_Totem:IsTimerActive( COE.ActiveTotems["Water"] )) ) then
if( not COE.CleansingTotems.Disease.Warn ) then
COE.CleansingTotems.Disease.Warn = true;
COESched_AdviseTotem( COE.CleansingTotems.Disease, COESTR_TOTEMDISEASE );
end
else
COE.CleansingTotems.Disease.Warn = false;
end
if( warnPoison and COE.CleansingTotems.Poison.Totem and
(COE.ActiveTotems["Water"] ~= COE.CleansingTotems.Poison.Totem or
not COE_Totem:IsTimerActive( COE.ActiveTotems["Water"] )) ) then
if( not COE.CleansingTotems.Poison.Warn ) then
COE.CleansingTotems.Poison.Warn = true;
COESched_AdviseTotem( COE.CleansingTotems.Poison, COESTR_TOTEMPOISON );
end
else
COE.CleansingTotems.Poison.Warn = false;
end
-- reschedule
-- -----------
Chronos.scheduleByName( "COEAdvise", COE.AdvisorInterval, COESched_RunAdvisor );
end
--[[ ----------------------------------------------------------------
METHOD: COE_Totem:ScanTargetForDebuff
PURPOSE: Scans the specified target for debuffs that are
curable by totems
-------------------------------------------------------------------]]
function COE_Totem:ScanTargetForDebuff( target )
local slot = 1;
local poison, disease, tremor = false, false, false;
-- iterate over all possible 16 debuff slots
-- ------------------------------------------
COETotemTTTextLeft1:SetText( "" );
COETotemTTTextRight1:SetText( "" );
COETotemTT:SetUnitDebuff( target, slot );
local debufftype = COETotemTTTextRight1:GetText();
local debuffname = COETotemTTTextLeft1:GetText();
while( debuffname and slot <= 16 and not(poison and disease and tremor) ) do
if( debufftype ) then
if( debufftype == COESTR_POISON ) then
-- it's a poison debuff
-- ---------------------
poison = true;
elseif( debufftype == COESTR_DISEASE ) then
-- it's a disease debuff
-- ---------------------
disease = true;
else
-- check if it's a debuff curable
-- by the tremor totem
-- -------------------------------
local debuff;
for debuff in COESTR_TREMOR do
if( debuffname == COESTR_TREMOR[debuff] ) then
tremor = true;
break
end
end
end
end
slot = slot + 1;
COETotemTTTextLeft1:SetText( "" );
COETotemTTTextRight1:SetText( "" );
COETotemTT:SetUnitDebuff( target, slot );
debufftype = COETotemTTTextRight1:GetText();
debuffname = COETotemTTTextLeft1:GetText();
end
return poison, disease, tremor;
end
--[[ ----------------------------------------------------------------
METHOD: COE_Totem:ThrowAdvisedTotem
PURPOSE: If there is an advised totem waiting to be thrown
is is done here
-------------------------------------------------------------------]]
function COE_Totem:ThrowAdvisedTotem()
if( COE.CleansingTotems.Tremor.Warn and
(COE.ActiveTotems.Earth ~= COE.CleansingTotems.Tremor.Totem or
not COE_Totem:IsTimerActive( COE.ActiveTotems["Earth"] )) ) then
CastSpellByName( COE.CleansingTotems.Tremor.Totem.SpellName );
elseif( COE.CleansingTotems.Disease.Warn and
(COE.ActiveTotems.Water ~= COE.CleansingTotems.Disease.Totem or
not COE_Totem:IsTimerActive( COE.ActiveTotems["Water"] )) ) then
CastSpellByName( COE.CleansingTotems.Disease.Totem.SpellName );
elseif( COE.CleansingTotems.Poison.Warn and
(COE.ActiveTotems.Water ~= COE.CleansingTotems.Poison.Totem or
not COE_Totem:IsTimerActive( COE.ActiveTotems["Water"] )) ) then
CastSpellByName( COE.CleansingTotems.Poison.Totem.SpellName );
end
end
--[[ ----------------------------------------------------------------
METHOD: COE_Totem:IsAdvisedTotem
PURPOSE: Returns if the specified totem is an advised totem
-------------------------------------------------------------------]]
function COE_Totem:IsAdvisedTotem( totem )
if( (COE.CleansingTotems.Tremor.Warn and totem == COE.CleansingTotems.Tremor.Totem) or
(COE.CleansingTotems.Disease.Warn and totem == COE.CleansingTotems.Disease.Totem) or
(COE.CleansingTotems.Poison.Warn and totem == COE.CleansingTotems.Poison.Totem) ) then
return true;
else
return false;
end
end
--[[ =============================================================================================
T O T E M - S E T S
================================================================================================]]
--[[ ----------------------------------------------------------------
METHOD: COE_Totem:SwitchNamedSet
PURPOSE: If there exists a custom set with the name of the
playertarget and the target is hostile, switch to this set
-------------------------------------------------------------------]]
function COE_Totem:SwitchNamedSet()
if( COE_Config:GetSaved( COEOPT_ENABLESETS ) == 1 ) then
if( UnitIsEnemy( "player", "target" ) ) then
-- iterate over all custom sets
-- -----------------------------
local i;
for i = 1, table.getn( COE_CustomTotemSets ) do
if( COE_CustomTotemSets[i].Name == UnitName( "target" ) ) then
COE_Totem:SwitchToSet( COE_CustomTotemSets[i].Name );
return true;
end
end
end
end
return false;
end
--[[ ----------------------------------------------------------------
METHOD: COE_Totem:SwitchPVPSet
PURPOSE: If the playertarget is a hostile player, activate the
class totem set, else switch back to the last active set
-------------------------------------------------------------------]]
function COE_Totem:SwitchPVPSet()
if( COE_Config:GetSaved( COEOPT_ENABLESETS ) == 1 and
COE_Config:GetSaved( COEOPT_AUTOSWITCH ) == 1 ) then
if( UnitIsEnemy( "player", "target" ) ) then
if( UnitIsPlayer( "target" ) ) then
-- switch to pvp set
-- ------------------
local class = UnitClass( "target" );
COE_Totem:SwitchToSet( "[PVP] " .. class );
elseif( UnitExists( "target" ) ) then
-- targeted a mob. switch back to the last set
-- --------------------------------------------
if( COE.LastActiveSet ~= nil ) then
COE_Totem:SwitchToSet( COE.LastActiveSet );
else
COE_Totem:SwitchToSet( COEUI_DEFAULTSET );
end;
end
end
end
end
--[[ ----------------------------------------------------------------
METHOD: COE_Totem:SwitchToNextSet
PURPOSE: If a pvp set is activated, the default set is activated
Otherwise the next custom set is activated
-------------------------------------------------------------------]]
function COE_Totem:SwitchToNextSet()
if( COE_Config:GetSaved( COEOPT_ENABLESETS ) == 1 ) then
local activeset = COE_Config:GetSaved( COEOPT_ACTIVESET );
if( activeset < COESET_DEFAULT ) then
-- pvp set is active
-- switch to default set
-- ----------------------
COE_Totem:SwitchToSet( COEUI_DEFAULTSET );
elseif( activeset == COE.TotemSetCount ) then
-- last custom set is active or none available
-- switch to default set
-- --------------------------------------------
COE_Totem:SwitchToSet( COEUI_DEFAULTSET );
elseif( activeset == COESET_DEFAULT ) then
-- default set is active
-- switch to first custom set
-- ---------------------------
COE_Totem:SwitchToSet( COE_CustomTotemSets[1].Name );
else
-- switch to next custom set
-- --------------------------
COE_Totem:SwitchToSet( COE_CustomTotemSets[activeset - COESET_DEFAULT + 1].Name );
end
end
end
--[[ ----------------------------------------------------------------
METHOD: COE_Totem:SwitchToPriorSet
PURPOSE: If a pvp set is activated, the default set is activated
Otherwise the next custom set is activated
-------------------------------------------------------------------]]
function COE_Totem:SwitchToPriorSet()
if( COE_Config:GetSaved( COEOPT_ENABLESETS ) == 1 ) then
local activeset = COE_Config:GetSaved( COEOPT_ACTIVESET );
if( activeset < COESET_DEFAULT ) then
-- pvp set is active
-- switch to default set
-- ----------------------
COE_Totem:SwitchToSet( COEUI_DEFAULTSET );
elseif( activeset == COESET_DEFAULT ) then
-- default set is active
-- switch to last custom set
-- ---------------------------
COE_Totem:SwitchToSet( COE_CustomTotemSets[COE.TotemSetCount - COESET_DEFAULT].Name );
elseif( activeset == COESET_DEFAULT + 1 ) then
-- first custom set is active
-- switch to default set
-- ---------------------------
COE_Totem:SwitchToSet( COEUI_DEFAULTSET );
else
-- switch to prior custom set
-- ---------------------------
COE_Totem:SwitchToSet( COE_CustomTotemSets[activeset - COESET_DEFAULT - 1].Name );
end
end
end
--[[ ----------------------------------------------------------------
METHOD: COE_Totem:SwitchToSet
PURPOSE: Activates the specified totem set
-------------------------------------------------------------------]]
function COE_Totem:SwitchToSet( name )
if( COE_Config:GetSaved( COEOPT_ENABLESETS ) == 0 ) then
return;
end
local pvpset = false;
-- check for pvp sets
-- -------------------
local i, set;
for i = 1, table.getn( COE_SavedTotemSets ) do
if( COE_SavedTotemSets[i].Name == name ) then
-- set found
-- ----------
set = i;
pvpset = i ~= COESET_DEFAULT;
break;
end
end
-- check for custom sets
-- ----------------------
if( not set ) then
for i = 1, table.getn( COE_CustomTotemSets ) do
if( COE_CustomTotemSets[i].Name == name ) then
-- set found
-- ----------
set = COESET_DEFAULT + i;
break;
end
end
end
if( not set ) then
return;
end
-- activate set if not already active
-- chech this to stop notification spamming in a duel
-- ---------------------------------------------------
if( COE_Config:GetSaved( COEOPT_ACTIVESET ) ~= set ) then
COE_Config:ActivateSet( set );
if( not pvpset ) then
COE.LastActiveSet = name;
end
-- reset set cycle
-- ----------------
COE_Totem:ResetSetCycle();
-- notify player
-- --------------
COE:Message( string.format( COESTR_SWITCHTOSET, name ) );
end
end
--[[ ----------------------------------------------------------------
METHOD: COE_Totem:ThrowSet
PURPOSE: Throws the next totem in the active set that is not
yet thrown
-------------------------------------------------------------------]]
function COE_Totem:ThrowSet()
if( COE_Config:GetSaved( COEOPT_ENABLESETS ) == 1 ) then
-- =======================================================================
-- check which totem to throw
-- =======================================================================
local activeset = COE_Config:GetSaved( COEOPT_ACTIVESET );
local k;
for k = 1,4 do
local element = COE:LocalizedElement( COE.TotemSets[activeset].CastOrder[k] );
local totem = COE.TotemSets[activeset][element];
if( COE.SetCycle[element] == false and totem ) then
if( totem.isTrinket ) then
if( totem.TrinketSlot ) then
-- first check if the trinket is already usable
-- ---------------------------------------------
local start, duration = GetInventoryItemCooldown( "player", totem.TrinketSlot );
if( start == 0 and duration == 0 ) then
UseInventoryItem( totem.TrinketSlot );
return;
end
end
else
-- first check if the totem is already usable
-- -------------------------------------------
local start, duration = GetSpellCooldown( totem.Ranks[totem.MaxRank].SpellID, BOOKTYPE_SPELL );
if( start == 0 and duration == 0 ) then
CastSpellByName( COE.TotemSets[activeset][element].SpellName );
return;
end
end
end
end
end
end
--[[ ----------------------------------------------------------------
METHOD: COE_Totem:ResetSetCycle
PURPOSE: Marks all totems of the current set as not yet thrown
-------------------------------------------------------------------]]
function COE_Totem:ResetSetCycle()
COE:Message( COESTR_RESTARTINGSET );
COE.SetCycle = { Earth = false, Fire = false, Water = false, Air = false };
end
--[[ =============================================================================================
T I M E R - L O G I C
================================================================================================]]
--[[ ----------------------------------------------------------------
METHOD: COE_Totem:SetPendingTotem
PURPOSE: Sets up or clears a pending totem
The pending totem's timer is activated on the next
successful SPELLCAST_STOP or removed if it times out first
-------------------------------------------------------------------]]
function COE_Totem:SetPendingTotem( totem, rank )
if( totem ) then
COE:DebugMessage( "Setting pending totem: " .. totem.SpellName .. " with rank: " .. rank );
COE.TotemPending.Totem = totem;
COE.TotemPending.UseRank = rank;
-- get the current lag
-- --------------------
local _,_,lag = GetNetStats();
local timeout = lag / 1000 + COE.TotemPending.Timeout;
-- use lag + 0.5 seconds as pending timeout
-- -----------------------------------------
COE:DebugMessage( "Setting pending timeout to: " .. timeout .. " msec" );
Chronos.scheduleByName( "COE_Pending", timeout, COESched_CheckPendingTotem );
else
COE.TotemPending.Totem = nil;
COE.TotemPending.UseRank = 0;
end
end
--[[ ----------------------------------------------------------------
METHOD: COESched_CheckPendingTotem
PURPOSE: Clears the pending totem if it is still active
-------------------------------------------------------------------]]
function COESched_CheckPendingTotem()
-- if there is still a pending totem it has timed out
-- ---------------------------------------------------
if( COE.TotemPending.Totem ) then
COE:DebugMessage( "Pending totem has timed out: " .. COE.TotemPending.Totem.SpellName );
COE_Totem:SetPendingTotem( nil );
end
end
--[[ ----------------------------------------------------------------
METHOD: COE_Totem:ActivatePendingTotem
PURPOSE: Activates the pending totem timer and deactivates a
still active timer of the same element
-------------------------------------------------------------------]]
function COE_Totem:ActivatePendingTotem( totem )
-- deactivate still active totem of the same element
-- --------------------------------------------------
local active = COE.ActiveTotems[COE.TotemPending.Totem.Element];
if( active ) then
COE_Totem:DeactivateTimer( active );
end
-- activate timer
-- ---------------
Chronos.startTimer( "COE" .. totem.SpellName );
totem.CurDuration = COE.TotemPending.Totem.Ranks[COE.TotemPending.UseRank].Duration;
totem.CurHealth = COE.TotemPending.Totem.Ranks[COE.TotemPending.UseRank].Health;
-- activate cooldown timer if this totem has a cooldown
-- -----------------------------------------------------
if( COE.TotemPending.Totem.Ranks[COE.TotemPending.UseRank].Cooldown > 0 ) then
totem.CurCooldown = COE.TotemPending.Totem.Ranks[COE.TotemPending.UseRank].Cooldown;
Chronos.startTimer( "COECooldown" .. totem.SpellName );
Chronos.scheduleByName( "COECooldownSwitch" .. totem.SpellName, totem.CurCooldown, COESched_CooldownEnd, totem );
end
-- activate warning timers
-- ------------------------
if( totem.CurDuration >= 10 ) then
Chronos.scheduleByName( "COEWarn10" .. totem.SpellName, totem.CurDuration - 10, COESched_ExpirationWarning, 10, totem );
end
Chronos.scheduleByName( "COEWarn5" .. totem.SpellName, totem.CurDuration - 5, COESched_ExpirationWarning, 5, totem );
Chronos.scheduleByName( "COEWarn0" .. totem.SpellName, totem.CurDuration, COESched_ExpirationWarning, 0, totem );
-- mark totem as active
-- ---------------------
COE.ActiveTotems[COE.TotemPending.Totem.Element] = totem;
totem.isActive = true;
-- set totem in timer frame
-- -------------------------
if( COE_Config:GetSaved( COEOPT_TIMERFRAME ) == 1 ) then
getglobal( "COETimer" .. COE.TotemPending.Totem.Element ).totem = totem;
COETimerFrame:Show();
end
-- mark totem as thrown if in current set
-- ---------------------------------------
local activeset = COE_Config:GetSaved( COEOPT_ACTIVESET );
if( COE.TotemSets[activeset][totem.Element] and
COE.TotemSets[activeset][totem.Element].SpellName == totem.SpellName ) then
COE:DebugMessage( "Element " .. totem.Element .. " in current cycle: SET" );
COE.SetCycle[totem.Element] = true;
end
-- clear pending totem
-- --------------------
COE_Totem:SetPendingTotem( nil );
-- if in timers only mode, reconfigure the totem bar
-- --------------------------------------------------
if( COE_Config:GetSaved( COEOPT_DISPLAYMODE ) == COEMODE_TIMERSONLY ) then
COE_Totem:Invalidate( getglobal( "COE" .. totem.Element .. "Frame" ), true, true, true );
COETotemFrame.Reconfigure = true;
else
-- otherwise just invalidate the dynamic buttons
-- ----------------------------------------------
COE_Totem:Invalidate( getglobal( "COE" .. totem.Element .. "Frame" ), false, false, true );
end
end
--[[ ----------------------------------------------------------------
METHOD: COE_Totem:DeactivateTimer
PURPOSE: Deactivates an active totem timer
-------------------------------------------------------------------]]
function COE_Totem:DeactivateTimer( totem )
if( totem and totem.isActive ) then
COE:DebugMessage( "Deactivating totem: " .. totem.SpellName );
-- end timers
-- -----------
Chronos.endTimer( "COE" .. totem.SpellName );
Chronos.unscheduleByName( "COEWarn10" ..totem.SpellName );
Chronos.unscheduleByName( "COEWarn5" ..totem.SpellName );
Chronos.unscheduleByName( "COEWarn0" ..totem.SpellName );
-- reset totem data
-- ------------------
totem.CurDuration = 0;
totem.CurHealth = 0;
totem.isActive = false;
-- unset totem in timerframe
-- --------------------------
local timer = getglobal( "COETimer" .. totem.Element );
timer.totem = nil;
COETimerFrame:StopMovingOrSizing();
-- mark totem as unthrown if in current set
-- -----------------------------------------
local activeset = COE_Config:GetSaved( COEOPT_ACTIVESET );
if( COE.TotemSets[activeset][totem.Element] and
COE.TotemSets[activeset][totem.Element].SpellName == totem.SpellName ) then
COE:DebugMessage( "Element " .. totem.Element .. " in current cycle: UNSET" );
COE.SetCycle[totem.Element] = false;
end
-- invalidate dynamic buttons
-- ---------------------------
COE_Totem:Invalidate( getglobal( "COE" .. totem.Element .. "Frame" ), false, false, true );
end
end
--[[ ----------------------------------------------------------------
METHOD: COE_Totem:IsTimerActive
PURPOSE: Returns if the specified totem timer is active
-------------------------------------------------------------------]]
function COE_Totem:IsTimerActive( totem )
if( not totem ) then
return false;
end
return (totem.CurDuration > 0) and
(totem.CurDuration - Chronos.getTimer( "COE" .. totem.SpellName ) > 0);
end
--[[ ----------------------------------------------------------------
METHOD: COE_Totem:GetTimeLeft
PURPOSE: Returns the amount of seconds left on the totem timer
-------------------------------------------------------------------]]
function COE_Totem:GetTimeLeft( totem )
if( totem.CurDuration == 0 ) then
return 0;
else
return totem.CurDuration - Chronos.getTimer( "COE" .. totem.SpellName );
end
end
--[[ ----------------------------------------------------------------
METHOD: COE_Totem:GetCooldownLeft
PURPOSE: Returns the amount of seconds left on the totem cooldown
-------------------------------------------------------------------]]
function COE_Totem:GetCooldownLeft( totem )
if( totem.CurCooldown == 0 ) then
return 0;
else
return totem.CurCooldown - Chronos.getTimer( "COECooldown" .. totem.SpellName );
end
end
--[[ ----------------------------------------------------------------
METHOD: COESched_ExpirationWarning
PURPOSE: Issues an expiration warning for the specified totem
-------------------------------------------------------------------]]
function COESched_ExpirationWarning( timemark, totem )
if( COE_Config:GetSaved( COEOPT_ENABLETOTEMBAR ) == 1 and
COE_Config:GetSaved( COEOPT_ENABLETIMERS ) == 1 and
COE_Config:GetSaved( COEOPT_TIMERNOTIFICATIONS ) == 1 ) then
local text;
if( totem.isTrinket ) then
text = COESTR_TRINKET_TOTEM;
else
text = totem.SpellName;
end
if( timemark > 0 ) then
COE:Notification( string.format( COESTR_TOTEMWARNING, text, timemark ), COECOL_TOTEMWARNING );
else
COE:Notification( string.format( COESTR_TOTEMEXPIRED, text ), COECOL_TOTEMDESTROYED );
COE_Totem:DeactivateTimer( totem );
end
end
end
--[[ ----------------------------------------------------------------
METHOD: COESched_CooldownEnd
PURPOSE: Stops the cooldown timer of the totem when the
cooldown has ended
-------------------------------------------------------------------]]
function COESched_CooldownEnd( totem )
Chronos.endTimer( "COECooldown" .. totem.SpellName );
totem.CurCooldown = 0;
end
--[[ ----------------------------------------------------------------
METHOD: COE_Totem:TotemDamage
PURPOSE: Assigns damage to the totem being hit and triggers
a notification if the totem is destroyed
-------------------------------------------------------------------]]
function COE_Totem:TotemDamage()
local num, regex, i;
for num, regex in COESTR_TOTEMDAMAGE do
local match = { string.gfind( arg1, regex )() };
if ( table.getn(match) >= 1 ) then
-- find totem
-- -----------
for i = 1, COE.TotemCount do
if( COE.TotemData[i].SpellName == match[1] ) then
-- subtract damage
-- ----------------
COE.TotemData[i].CurHealth = COE.TotemData[i].CurHealth - tonumber( match[2] );
-- totem destroyed?
-- -----------------
if( COE_Config:GetSaved( COEOPT_ENABLETIMERS ) == 1 ) then
if( COE.TotemData[i].CurHealth <= 0 ) then
COE:DebugMessage( match[1] .. " destroyed" );
if( COE_Config:GetSaved( COEOPT_TIMERNOTIFICATIONS ) == 1 ) then
COE:Notification( string.format( COESTR_TOTEMDESTROYED, COE.TotemData[i].SpellName ),
COECOL_TOTEMDESTROYED );
end
COE_Totem:DeactivateTimer( COE.TotemData[i] );
else
COE:DebugMessage( match[1] .. " took " .. match[2] .. " damage. " ..
COE.TotemData[i].CurHealth .. " health left" );
end
end
break;
end
end
break;
end
end
end
--[[ ----------------------------------------------------------------
METHOD: COE_Totem:ResetTimers
PURPOSE: Resets all active timers to zero
-------------------------------------------------------------------]]
function COE_Totem:ResetTimers()
local k;
for k = 1, 4 do
if( COE.ActiveTotems[COE_Element[k]] ) then
COE_Totem:DeactivateTimer( COE.ActiveTotems[COE_Element[k]] );
end
end
COE:Message( COESTR_RESET );
end
--[[ =============================================================================================
T I M E R - H O O K S
================================================================================================]]
--[[ ----------------------------------------------------------------
METHOD: COE_Totem:SetupTimerHooks
PURPOSE: Stores the CastSpell, CastSpellByName, UseAction
and UseInventoryItem functions and replaces them with
a call to the corresponding hook functions and the
original function afterwards
-------------------------------------------------------------------]]
function COE_Totem:SetupTimerHooks()
COE_Totem.TimerHooks["UseAction"] = UseAction;
UseAction = function( id, book, onself )
COE_Totem:HookUseAction( id, book );
COE_Totem.TimerHooks["UseAction"]( id, book, onself );
end;
COE_Totem.TimerHooks["CastSpell"] = CastSpell;
CastSpell = function( id, book )
COE_Totem:HookCastSpell( id, book );
COE_Totem.TimerHooks["CastSpell"]( id, book );
end;
COE_Totem.TimerHooks["CastSpellByName"] = CastSpellByName;
CastSpellByName = function( SpellName )
COE_Totem:HookCastSpellByName( SpellName );
COE_Totem.TimerHooks["CastSpellByName"]( SpellName );
end;
COE_Totem.TimerHooks["UseInventoryItem"] = UseInventoryItem;
UseInventoryItem = function( slotid )
COE_Totem:HookUseInventoryItem( slotid );
COE_Totem.TimerHooks["UseInventoryItem"]( slotid );
end;
end
--[[ ----------------------------------------------------------------
METHOD: COE_Totem:HookUseAction
PURPOSE: Setup the totem timer if the action triggered
a totem
-------------------------------------------------------------------]]
function COE_Totem:HookUseAction( id, book )
-- use only when timers are enabled
-- ---------------------------------
if( COE_Config:GetSaved( COEOPT_ENABLETIMERS ) == 0 ) then
return;
end
-- get action tooltip
-- -------------------
COETotemTT:SetAction( id );
-- check if this triggered a totem
-- --------------------------------
local text = COETotemTTTextLeft1:GetText();
local i, rank, ranktext, trinket;
-- check if this triggered the trinket
-- ------------------------------------
trinket = text == COESTR_TRINKET_TOOLTIP;
for i = 1, COE.TotemCount do
if( (trinket and COE.TotemData[i].isTrinket) or COE.TotemData[i].SpellName == text ) then
if( COE.TotemData[i].isTrinket ) then
rank = 0;
else
-- get rank information
-- Since WOW doesn't seem to clear the rank information
-- when setting a tooltip for an action that doesn't
-- have such, we need to test if the rank info is
-- actually shown first
-- -----------------------------------------------------
if( COETotemTTTextRight1:IsShown() ) then
ranktext = COETotemTTTextRight1:GetText();
end
if( ranktext ) then
_,_,rank = string.find( ranktext, COESTR_TOTEMRANK );
rank = tonumber( rank );
else
rank = COE.TotemData[i].MaxRank;
end
end
-- set pending totem
-- ------------------
COE_Totem:SetPendingTotem( COE.TotemData[i], rank );
return;
end
end
-- no totem spell. remove pending totem if set
-- --------------------------------------------
COE_Totem:SetPendingTotem( nil );
end
--[[ ----------------------------------------------------------------
METHOD: COE_Totem:HookCastSpell
PURPOSE: Setup the totem timer if the spell triggered
a totem
-------------------------------------------------------------------]]
function COE_Totem:HookCastSpell( id, book )
-- use only when timers are enabled
-- ---------------------------------
if( COE_Config:GetSaved( COEOPT_ENABLETIMERS ) == 0 ) then
return;
end
-- get the associated totem object
-- --------------------------------
local i, k, totem;
for i = 1, COE.TotemCount do
for k = 1, COE.TotemData[i].MaxRank do
if( COE.TotemData[i].Ranks[k].SpellID == id ) then
-- totem found. mark it as pending
-- --------------------------------
COE_Totem:SetPendingTotem( COE.TotemData[i], k );
return;
end
end
end
-- no totem spell. remove pending totem if set
-- --------------------------------------------
COE_Totem:SetPendingTotem( nil );
end
--[[ ----------------------------------------------------------------
METHOD: COE_Totem:HookCastSpellByName
PURPOSE: Setup the totem timer if the spell triggered
a totem
-------------------------------------------------------------------]]
function COE_Totem:HookCastSpellByName( SpellName )
-- use only when timers are enabled
-- ---------------------------------
if( COE_Config:GetSaved( COEOPT_ENABLETIMERS ) == 0 ) then
return;
end
-- extract rank information
-- -------------------------
local _,_,rank = string.find( SpellName, COESTR_CASTBYNAME );
local spell = string.gsub( SpellName, COESTR_CASTBYNAME, "" );
-- get the associated totem object
-- --------------------------------
local i, k, totem;
for i = 1, COE.TotemCount do
if( COE.TotemData[i].SpellName == spell ) then
if( rank ) then
rank = tonumber( rank );
else
rank = COE.TotemData[i].MaxRank;
end
-- totem found. mark it as pending
-- --------------------------------
COE_Totem:SetPendingTotem( COE.TotemData[i], rank );
return;
end
end
-- no totem spell. remove pending totem if set
-- --------------------------------------------
COE_Totem:SetPendingTotem( nil );
end
--[[ ----------------------------------------------------------------
METHOD: COE_Totem:HookUseInventoryItem
PURPOSE: Setup the totem timer if the item triggered
a totem
-------------------------------------------------------------------]]
function COE_Totem:HookUseInventoryItem( slotid )
-- use only when timers are enabled
-- ---------------------------------
if( COE_Config:GetSaved( COEOPT_ENABLETIMERS ) == 0 ) then
return;
end
-- only use for trinket slots
-- ---------------------------
if( slotid ~= GetInventorySlotInfo( "Trinket0Slot" ) and slotid ~= GetInventorySlotInfo( "Trinket1Slot" ) ) then
return;
end
-- iterate through out totems and find the trinket totem
-- ------------------------------------------------------
local i;
for i = 1, COE.TotemCount do
if( COE.TotemData[i].isTrinket ) then
-- does the totem have the same slot id?
-- --------------------------------------
if( COE.TotemData[i].TrinketSlot == slotid ) then
-- our trinket totem was cast
-- ---------------------------
COE_Totem:SetPendingTotem( COE.TotemData[i], 0 );
return;
end
end
end
-- no totem spell. remove pending totem if set
-- --------------------------------------------
COE_Totem:SetPendingTotem( nil );
end