Files
CallOfElements/COE_TotemLogic.lua
T
2024-12-14 13:38:00 -05:00

1185 lines
40 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:ResetTimers();
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)
COE:DebugMessage(
"Method Executing: ActivatePendingTotem with totem parameter " ..
totem.SpellName);
-- deactivate still active totem of the same element
-- --------------------------------------------------
COE:DebugMessage(
"Method Executing: ActivatePendingTotem - identifying active totems of type" ..
COE.TotemPending.Totem.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)
COE:DebugMessage(
"Method Executing: COESched_ExpirationWarning with totem parameter " ..
totem.SpellName);
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()
COE:DebugMessage("Method Executing: 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()
COE:DebugMessage("Method Executing: 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
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