1185 lines
40 KiB
Lua
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
|
|
|