diff --git a/Bindings.xml b/Bindings.xml
new file mode 100644
index 0000000..69e5b35
--- /dev/null
+++ b/Bindings.xml
@@ -0,0 +1,141 @@
+
+
+
+
+
+ if( COE.Initialized ) then
+ COE:ToggleConfigFrame();
+ end
+
+
+
+
+ if( COE.Initialized ) then
+ COE_Heal:BestHeal();
+ end
+
+
+ if( COE.Initialized ) then
+ COE_Heal:BattleHeal();
+ end
+
+
+
+
+ if( COE.Initialized ) then
+ COE_Totem:ThrowAdvisedTotem();
+ end
+
+
+
+
+ if( COE.Initialized ) then
+ COE_Totem:ThrowAdvisedTotem();
+ end
+
+
+
+
+ if( COE.Initialized ) then
+ COE_Totem:SwitchToNextSet();
+ end
+
+
+ if( COE.Initialized ) then
+ COE_Totem:ThrowSet();
+ end
+
+
+ if( COE.Initialized ) then
+ COE_Totem:ResetSetCycle();
+ end
+
+
+
+
+ COE_Totem:ThrowTotem( "Earth", 1 );
+
+
+ COE_Totem:ThrowTotem( "Earth", 2 );
+
+
+ COE_Totem:ThrowTotem( "Earth", 3 );
+
+
+ COE_Totem:ThrowTotem( "Earth", 4 );
+
+
+ COE_Totem:ThrowTotem( "Earth", 5 );
+
+
+
+
+ COE_Totem:ThrowTotem( "Fire", 1 );
+
+
+ COE_Totem:ThrowTotem( "Fire", 2 );
+
+
+ COE_Totem:ThrowTotem( "Fire", 3 );
+
+
+ COE_Totem:ThrowTotem( "Fire", 4 );
+
+
+ COE_Totem:ThrowTotem( "Fire", 5 );
+
+
+
+
+ COE_Totem:ThrowTotem( "Water", 1 );
+
+
+ COE_Totem:ThrowTotem( "Water", 2 );
+
+
+ COE_Totem:ThrowTotem( "Water", 3 );
+
+
+ COE_Totem:ThrowTotem( "Water", 4 );
+
+
+ COE_Totem:ThrowTotem( "Water", 5 );
+
+
+ COE_Totem:ThrowTotem( "Water", 6 );
+
+
+
+
+ COE_Totem:ThrowTotem( "Air", 1 );
+
+
+ COE_Totem:ThrowTotem( "Air", 2 );
+
+
+ COE_Totem:ThrowTotem( "Air", 3 );
+
+
+ COE_Totem:ThrowTotem( "Air", 4 );
+
+
+ COE_Totem:ThrowTotem( "Air", 5 );
+
+
+ COE_Totem:ThrowTotem( "Air", 6 );
+
+
+ COE_Totem:ThrowTotem( "Air", 7 );
+
+
+
\ No newline at end of file
diff --git a/COE_Config.lua b/COE_Config.lua
new file mode 100644
index 0000000..b16389f
--- /dev/null
+++ b/COE_Config.lua
@@ -0,0 +1,1412 @@
+--[[
+
+ CALL OF ELEMENTS
+ The All-In-One Shaman Addon
+
+ by Wyverex (2006)
+
+
+ Configuration Dialog
+
+]]
+
+if( not COE_Config ) then
+ COE_Config = {};
+end
+
+
+--[[ ----------------------------------------------------------------
+ COE_Config.CurrentPanel and COE_Config.CurrentSubPanel contain
+ the button names for the currently active panel and subpanel
+ in the confiuration dialog
+-------------------------------------------------------------------]]
+COE_Config["CurrentPanel"] = "COE_ConfigTotemTab";
+COE_Config["CurrentSubPanel"] = "COE_ConfigTotemTotemBar";
+
+
+--[[ ----------------------------------------------------------------
+ COE_Config.Options contains all registered option defaults
+-------------------------------------------------------------------]]
+COE_Config["Options"] = {};
+COE_Config["ConfigureSetsMode"] = false;
+COE_Config["ConfigureBarMode"] = false;
+COE_Config["ConfigureOrderMode"] = false;
+COE_Config["CurrentOrderTotem"] = nil;
+
+COEOPT_ENABLETOTEMBAR = 1;
+COEOPT_HIDEBACKDROP = 2;
+COEOPT_ENABLETIMERS = 3;
+COEOPT_TIMERNOTIFICATIONS = 4;
+COEOPT_TTALIGNMENT = 5;
+COEOPT_DISPLAYMODE = 6;
+COEOPT_DISPLAYALIGN = 7;
+COEOPT_ADVISOR = 8;
+COEOPT_ENABLESETS = 9;
+COEOPT_ACTIVESET = 10;
+COEOPT_AUTOSWITCH = 11;
+COEOPT_FIXBAR = 12;
+COEOPT_VERSION = 13;
+COEOPT_OVERRIDESET = 14;
+COEOPT_CURRENTFRAME = 15;
+COEOPT_DIRECTION = 16;
+COEOPT_FRAMEMODE = 17;
+COEOPT_FLEXCOUNT = 18;
+COEOPT_SCALING = 19;
+COEOPT_TIMERFRAME = 20;
+COEOPT_OVERRIDERANK = 21;
+COEOPT_GROUPBARS = 22;
+COEOPT_FRAMETIMERSONLY = 23;
+
+COEMODE_ALLTOTEMS = 1;
+COEMODE_TIMERSONLY = 2;
+COEMODE_ACTIVESET = 3;
+
+COEMODE_BOX = 1;
+COEMODE_VERTICAL = 2;
+COEMODE_HORIZONTAL = 3;
+
+COEMODE_NOOVERRIDE = 1;
+COEMODE_OVERRIDESHIFT = 2;
+COEMODE_OVERRIDEALT = 3;
+COEMODE_OVERRIDECTRL = 4;
+
+--[[ ----------------------------------------------------------------
+ COE_Config.Defaults contains the default values of all
+ saved variables
+-------------------------------------------------------------------]]
+COE_Config.Defaults = {
+[COEOPT_ENABLETOTEMBAR] = 1;
+[COEOPT_HIDEBACKDROP] = 1;
+[COEOPT_ENABLETIMERS] = 1;
+[COEOPT_TIMERNOTIFICATIONS] = 1;
+[COEOPT_TTALIGNMENT] = 2;
+[COEOPT_DISPLAYMODE] = 1;
+[COEOPT_DISPLAYALIGN] = 1;
+[COEOPT_ADVISOR] = 1;
+[COEOPT_ENABLESETS] = 1;
+[COEOPT_ACTIVESET] = 10;
+[COEOPT_AUTOSWITCH] = 1;
+[COEOPT_FIXBAR] = 0;
+[COEOPT_OVERRIDESET] = 1;
+[COEOPT_CURRENTFRAME] = "Earth";
+[COEOPT_SCALING] = 1.0;
+[COEOPT_TIMERFRAME] = 0;
+[COEOPT_OVERRIDERANK] = 1;
+[COEOPT_GROUPBARS] = 0;
+[COEOPT_FRAMETIMERSONLY] = 0;
+}
+
+--[[ ----------------------------------------------------------------
+ COE_Config.Saved contains all saved variables
+-------------------------------------------------------------------]]
+COE_Saved = {
+[COEOPT_ENABLETOTEMBAR] = 1;
+[COEOPT_HIDEBACKDROP] = 1;
+[COEOPT_ENABLETIMERS] = 1;
+[COEOPT_TIMERNOTIFICATIONS] = 1;
+[COEOPT_TTALIGNMENT] = 2;
+[COEOPT_DISPLAYMODE] = 1;
+[COEOPT_DISPLAYALIGN] = 1;
+[COEOPT_ADVISOR] = 1;
+[COEOPT_ENABLESETS] = 1;
+[COEOPT_ACTIVESET] = 10;
+[COEOPT_AUTOSWITCH] = 1;
+[COEOPT_FIXBAR] = 0;
+[COEOPT_OVERRIDESET] = 1;
+[COEOPT_CURRENTFRAME] = "Earth";
+[COEOPT_SCALING] = 1.0;
+[COEOPT_TIMERFRAME] = 0;
+[COEOPT_OVERRIDERANK] = 1;
+[COEOPT_GROUPBARS] = 0;
+[COEOPT_FRAMETIMERSONLY] = 0;
+}
+
+
+--[[ ----------------------------------------------------------------
+ COE_SavedTotemSets holds the default set and the PVP sets
+ COE_CustomTotemSets holds the player-defined custom sets
+-------------------------------------------------------------------]]
+COE_SavedTotemSets = {
+{ Name = COEUI_PVPSETS[1], Earth = "", Fire = "", Water = "", Air = "",
+ CastOrder = { COESTR_ELEMENT_EARTH, COESTR_ELEMENT_FIRE, COESTR_ELEMENT_WATER, COESTR_ELEMENT_AIR } };
+{ Name = COEUI_PVPSETS[2], Earth = "", Fire = "", Water = "", Air = "",
+ CastOrder = { COESTR_ELEMENT_EARTH, COESTR_ELEMENT_FIRE, COESTR_ELEMENT_WATER, COESTR_ELEMENT_AIR } };
+{ Name = COEUI_PVPSETS[3], Earth = "", Fire = "", Water = "", Air = "",
+ CastOrder = { COESTR_ELEMENT_EARTH, COESTR_ELEMENT_FIRE, COESTR_ELEMENT_WATER, COESTR_ELEMENT_AIR } };
+{ Name = COEUI_PVPSETS[4], Earth = "", Fire = "", Water = "", Air = "",
+ CastOrder = { COESTR_ELEMENT_EARTH, COESTR_ELEMENT_FIRE, COESTR_ELEMENT_WATER, COESTR_ELEMENT_AIR } };
+{ Name = COEUI_PVPSETS[5], Earth = "", Fire = "", Water = "", Air = "",
+ CastOrder = { COESTR_ELEMENT_EARTH, COESTR_ELEMENT_FIRE, COESTR_ELEMENT_WATER, COESTR_ELEMENT_AIR } };
+{ Name = COEUI_PVPSETS[6], Earth = "", Fire = "", Water = "", Air = "",
+ CastOrder = { COESTR_ELEMENT_EARTH, COESTR_ELEMENT_FIRE, COESTR_ELEMENT_WATER, COESTR_ELEMENT_AIR } };
+{ Name = COEUI_PVPSETS[7], Earth = "", Fire = "", Water = "", Air = "",
+ CastOrder = { COESTR_ELEMENT_EARTH, COESTR_ELEMENT_FIRE, COESTR_ELEMENT_WATER, COESTR_ELEMENT_AIR } };
+{ Name = COEUI_PVPSETS[8], Earth = "", Fire = "", Water = "", Air = "",
+ CastOrder = { COESTR_ELEMENT_EARTH, COESTR_ELEMENT_FIRE, COESTR_ELEMENT_WATER, COESTR_ELEMENT_AIR } };
+{ Name = COEUI_PVPSETS[9], Earth = "", Fire = "", Water = "", Air = "",
+ CastOrder = { COESTR_ELEMENT_EARTH, COESTR_ELEMENT_FIRE, COESTR_ELEMENT_WATER, COESTR_ELEMENT_AIR } };
+{ Name = COEUI_DEFAULTSET, Earth = "", Fire = "", Water = "", Air = "",
+ CastOrder = { COESTR_ELEMENT_EARTH, COESTR_ELEMENT_FIRE, COESTR_ELEMENT_WATER, COESTR_ELEMENT_AIR } };
+};
+
+COE_CustomTotemSets = { n = 0 };
+
+COESET_DEFAULT = 10;
+
+
+--[[ ----------------------------------------------------------------
+ COE_DisplayedTotems stores the visibility option for each
+ totem when in "All Totems" display mode
+-------------------------------------------------------------------]]
+COE_DisplayedTotems = { };
+
+
+--[[ ----------------------------------------------------------------
+ COE_TotemBars stores the alignment options of the totem bars
+-------------------------------------------------------------------]]
+COE_TotemBars = {
+Earth = { Direction = "Left", Mode = "Flex", FlexCount = 1 },
+Fire = { Direction = "Right", Mode = "Flex", FlexCount = 1 },
+Water = { Direction = "Left", Mode = "Flex", FlexCount = 1 },
+Air = { Direction = "Right", Mode = "Flex", FlexCount = 1 },
+};
+
+COE_Element = { "Earth", "Fire", "Water", "Air" };
+COE_Direction = { "Up", "Down", "Left", "Right" };
+COE_FrameMode = { "Closed", "Open", "Flex", "Hidden" };
+
+
+--[[ =============================================================================================
+
+ I N T E R F A C E
+
+================================================================================================]]
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE_Config:OnFrameLoad
+
+ PURPOSE: Registers all options
+-------------------------------------------------------------------]]
+function COE_Config:OnFrameLoad()
+
+ if( not COE.Initialized ) then
+ return;
+ end
+
+ this:SetBackdropBorderColor( 0, 0, 0 );
+ this:SetBackdropColor( 0, 0, 0 );
+
+ -- make the dialog draggable
+ -- --------------------------
+ this:RegisterForDrag( "LeftButton" );
+
+ -- totem options
+ -- --------------
+ COE_Config:RegisterOption( COEOPT_ENABLETOTEMBAR, 'check', COEOptionEnableTotemBar, COE_Config:GetSaved( COEOPT_ENABLETOTEMBAR ) );
+ COE_Config:RegisterOption( COEOPT_ENABLETIMERS, 'check', COEOptionEnableTimers, COE_Config:GetSaved( COEOPT_ENABLETIMERS ) );
+ COE_Config:RegisterOption( COEOPT_TIMERNOTIFICATIONS, 'check', nil, COE_Config:GetSaved( COEOPT_TIMERNOTIFICATIONS ) );
+ COE_Config:RegisterOption( COEOPT_TTALIGNMENT, 'combo', nil, COE_Config:GetSaved( COEOPT_TTALIGNMENT ), COEOptionTTAlignmentInit );
+ COE_Config:RegisterOption( COEOPT_DISPLAYMODE, 'combo', nil, COE_Config:GetSaved( COEOPT_DISPLAYMODE ), COEOptionDisplayModeInit );
+ COE_Config:RegisterOption( COEOPT_ADVISOR, 'check', COEOptionEnableAdvisor, COE_Config:GetSaved( COEOPT_ADVISOR ) );
+ COE_Config:RegisterOption( COEOPT_ENABLESETS, 'check', COEOptionEnableSets, COE_Config:GetSaved( COEOPT_ENABLESETS ) );
+ COE_Config:RegisterOption( COEOPT_AUTOSWITCH, 'check', COEOptionEnableAutoSwitch, COE_Config:GetSaved( COEOPT_AUTOSWITCH ) );
+ COE_Config:RegisterOption( COEOPT_ACTIVESET, 'combo', nil, COE_Config:GetSaved( COEOPT_ACTIVESET ), COEOptionActiveSetInit );
+ COE_Config:RegisterOption( COEOPT_FIXBAR, 'check', nil, COE_Config:GetSaved( COEOPT_FIXBAR ) );
+ COE_Config:RegisterOption( COEOPT_CURRENTFRAME, 'combo', nil, COE_Config:GetSaved( COEOPT_CURRENTFRAME ), COEOptionCurrentFrameInit );
+ COE_Config:RegisterOption( COEOPT_DIRECTION, 'combo', nil, nil, COEOptionDirectionInit, true );
+ COE_Config:RegisterOption( COEOPT_FRAMEMODE, 'combo', nil, nil, COEOptionFrameModeInit, true );
+ COE_Config:RegisterOption( COEOPT_FLEXCOUNT, 'slider', COEOptionFlexCountChange, nil, COEOptionFlexCountShow, true );
+ COE_Config:RegisterOption( COEOPT_SCALING, 'slider', COEOptionScalingChange, nil, COEOptionScalingShow );
+ COE_Config:RegisterOption( COEOPT_TIMERFRAME, 'check', COEOptionEnableTimerFrame, COE_Config:GetSaved( COEOPT_TIMERFRAME ) );
+ COE_Config:RegisterOption( COEOPT_DISPLAYALIGN, 'combo', nil, COE_Config:GetSaved( COEOPT_DISPLAYALIGN ), COEOptionDisplayAlignInit );
+ COE_Config:RegisterOption( COEOPT_OVERRIDERANK, 'combo', nil, COE_Config:GetSaved( COEOPT_OVERRIDERANK ), COEOptionOverrideRankInit );
+ COE_Config:RegisterOption( COEOPT_GROUPBARS, 'check', COEOptionGroupBars, COE_Config:GetSaved( COEOPT_GROUPBARS ) );
+ COE_Config:RegisterOption( COEOPT_FRAMETIMERSONLY, 'check', nil, COE_Config:GetSaved( COEOPT_FRAMETIMERSONLY ) );
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE_Config:CloseDialog
+
+ PURPOSE: Ends totem set configuration and closes the dialog
+-------------------------------------------------------------------]]
+function COE_Config:CloseDialog()
+
+ COE_Config:ConfigureBar( false );
+ COE_Config:ConfigureOrder( false );
+ COE_Config:ConfigureSet( false );
+ COE_ConfigFrame:Hide();
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE_Config:OnTabButtonClick
+
+ PURPOSE: Shows the panel corresponding to the button
+-------------------------------------------------------------------]]
+function COE_Config:OnTabButtonClick()
+
+ -- do nothing if this is already
+ -- active panel
+ -- ------------------------------
+ if( COE_Config.CurrentPanel == this:GetName() ) then
+ this:SetChecked( true );
+ return;
+ end
+
+ -- first uncheck old button
+ -- and hide the panel
+ -- -------------------------
+ getglobal( COE_Config.CurrentPanel ):SetChecked( false );
+ getglobal( COE_Config.CurrentPanel .. "Panel" ):Hide();
+
+ -- then check this button
+ -- and show the panel
+ -- -----------------------
+ this:SetChecked( true );
+ getglobal( this:GetName() .. "Panel" ):Show();
+
+ COE_Config.CurrentPanel = this:GetName();
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE_Config:OnConfigElementLoad
+
+ PURPOSE: Sets the localized element text
+-------------------------------------------------------------------]]
+function COE_Config:OnConfigElementLoad( element )
+
+ if( not COE.Initialized ) then
+ return;
+ end
+
+ if( COEUI_STRINGS[this:GetName()] ) then
+ element:SetText( COEUI_STRINGS[this:GetName()] );
+ else
+ element:SetText( COESTR_UI_NOTASSIGNED );
+ end
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE_Config:OnConfigElementEnter
+
+ PURPOSE: Shows the tooltip for the config element
+-------------------------------------------------------------------]]
+function COE_Config:OnConfigElementEnter()
+
+ if( COEUI_TOOLTIPS[this:GetName()] ) then
+ GameTooltip:SetOwner( this, "ANCHOR_RIGHT" );
+ GameTooltip:SetText( COEUI_TOOLTIPS[this:GetName()], 1.0, 1.0, 1.0 );
+ end
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE_Config:OnConfigElementLeave
+
+ PURPOSE: Hides the tooltip for the config element
+-------------------------------------------------------------------]]
+function COE_Config:OnConfigElementLeave()
+
+ GameTooltip:Hide();
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE_Config:OnSubTabButtonClick
+
+ PURPOSE: Shows the sub panel corresponding to the button
+-------------------------------------------------------------------]]
+function COE_Config:OnSubTabButtonClick( button )
+
+ -- do nothing if this is already
+ -- active panel
+ -- ------------------------------
+ if( COE_Config.CurrentSubPanel == button:GetName() ) then
+ return;
+ end
+
+ -- first hide the old panel
+ -- -------------------------
+ getglobal( COE_Config.CurrentSubPanel .. "Panel" ):Hide();
+
+ -- then show the new panel
+ -- ------------------------
+ getglobal( button:GetName() .. "Panel" ):Show();
+
+ if( button:GetName() == "COE_ConfigTotemTotemSets" ) then
+ COE_Config:ActivateSet( COE_Config:GetSaved( COEOPT_ACTIVESET ) );
+ end
+
+ COE_Config.CurrentSubPanel = button:GetName();
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE_Config:OnCheckBoxShow
+
+ PURPOSE: Sets the checked state depending on the current
+ option value and call the associated function to update
+ the state
+-------------------------------------------------------------------]]
+function COE_Config:OnCheckBoxShow()
+
+ -- set check state
+ -- ----------------
+ this:SetChecked( COE_Config:GetSaved( this:GetID() ) );
+
+ -- call function
+ -- --------------
+ if( COE_Config.Options[this:GetID()].Func ) then
+ COE_Config.Options[this:GetID()].Func();
+ end
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE_Config:OnCheckBoxClick
+
+ PURPOSE: Toggles the associated option
+-------------------------------------------------------------------]]
+function COE_Config:OnCheckBoxClick()
+
+ if( COE_Config.Options[this:GetID()] ) then
+ local option = COE_Config.Options[this:GetID()];
+
+ -- execute function and store new value
+ -- -------------------------------------
+ if( COE_Config:GetSaved( this:GetID() ) == 1 ) then
+ COE_Config:SetOption( this:GetID(), 0 );
+ else
+ COE_Config:SetOption( this:GetID(), 1 );
+ end
+
+ if( option.Func ) then
+ option.Func();
+ end
+ else
+ COE:DebugMessage( "No ID assigned" );
+ end
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE_Config:EnableCheckBox
+
+ PURPOSE: Enables a check box and sets the text color to the
+ normal text color
+-------------------------------------------------------------------]]
+function COE_Config:EnableCheckBox( element )
+
+ element:Enable();
+ getglobal( element:GetName() .. "Text" ):SetTextColor( NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b );
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE_Config:DisableCheckBox
+
+ PURPOSE: Disables a check box and sets the text color to gray
+-------------------------------------------------------------------]]
+function COE_Config:DisableCheckBox( element )
+
+ element:Disable();
+ getglobal( element:GetName() .. "Text" ):SetTextColor( GRAY_FONT_COLOR.r, GRAY_FONT_COLOR.g, GRAY_FONT_COLOR.b );
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE_Config:OnComboBoxShow
+
+ PURPOSE: Initializes the combo box content
+-------------------------------------------------------------------]]
+function COE_Config:OnComboBoxShow()
+
+ if( COE_Config.Options[this:GetID()] ) then
+ UIDropDownMenu_Initialize( this, COE_Config.Options[this:GetID()].InitFunc );
+ end
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE_Config:EnableComboBox
+
+ PURPOSE: Enables a combo box and sets the text color to the
+ normal text color
+-------------------------------------------------------------------]]
+function COE_Config:EnableComboBox( element )
+
+ getglobal( element:GetName() .. "CB" ):Show();
+ getglobal( element:GetName() .. "LeftText" ):SetTextColor( NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b );
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE_Config:DisableComboBox
+
+ PURPOSE: Disables a combo box and sets the text color to gray
+-------------------------------------------------------------------]]
+function COE_Config:DisableComboBox( element )
+
+ getglobal( element:GetName() .. "CB" ):Hide();
+ getglobal( element:GetName() .. "LeftText" ):SetTextColor( GRAY_FONT_COLOR.r, GRAY_FONT_COLOR.g, GRAY_FONT_COLOR.b );
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE_Config:OnSliderShow
+
+ PURPOSE: Sets the slider texts and values
+-------------------------------------------------------------------]]
+function COE_Config:OnSliderShow()
+
+ if( COE_Config.Options[this:GetID()] ) then
+ COE_Config.Options[this:GetID()].InitFunc();
+ end
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE_Config:OnSliderChange
+
+ PURPOSE: Sets the new value
+-------------------------------------------------------------------]]
+function COE_Config:OnSliderChange()
+
+ if( COE_Config.Options[this:GetID()] ) then
+ COE_Config.Options[this:GetID()].Func();
+ end
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE_Config:ConfigureBar
+
+ PURPOSE: Switches between set bar configuration modes
+-------------------------------------------------------------------]]
+function COE_Config:ConfigureBar( mode )
+
+ if( mode == nil ) then
+ mode = not COE.ConfigureBarMode;
+ end
+
+ if( mode ) then
+ COE_Config:ConfigureOrder( false );
+ COE_Config:ConfigureSet( false );
+ COE.ConfigureBarMode = true;
+ COE_OptionConfigureBar:SetText( COEUI_STRINGS["COE_OptionStopConfigureSet"] );
+ else
+ COE.ConfigureBarMode = false;
+ COE_OptionConfigureBar:SetText( COEUI_STRINGS["COE_OptionConfigureBar"] );
+ end
+
+ COE_Totem:Invalidate( COEEarthFrame, true, true, true );
+ COE_Totem:Invalidate( COEFireFrame, true, true, true );
+ COE_Totem:Invalidate( COEWaterFrame, true, true, true );
+ COE_Totem:Invalidate( COEAirFrame, true, true, true );
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE_Config:ConfigureOrder
+
+ PURPOSE: Switches between set bar configuration modes
+-------------------------------------------------------------------]]
+function COE_Config:ConfigureOrder( mode )
+
+ if( mode == nil ) then
+ mode = not COE.ConfigureOrderMode;
+ end
+
+ if( mode ) then
+ COE_Config:ConfigureBar( false );
+ COE_Config:ConfigureSet( false );
+ COE.ConfigureOrderMode = true;
+ COE_OptionConfigureOrder:SetText( COEUI_STRINGS["COE_OptionStopConfigureSet"] );
+ else
+ COE.ConfigureOrderMode = false;
+ COE_Config.ConfigureOrderTotem = nil;
+ COE_OptionConfigureOrder:SetText( COEUI_STRINGS["COE_OptionConfigureOrder"] );
+ end
+
+ COE_Totem:Invalidate( COEEarthFrame, true, true, true );
+ COE_Totem:Invalidate( COEFireFrame, true, true, true );
+ COE_Totem:Invalidate( COEWaterFrame, true, true, true );
+ COE_Totem:Invalidate( COEAirFrame, true, true, true );
+end
+
+
+--[[ =============================================================================================
+
+ O P T I O N S
+
+================================================================================================]]
+
+--[[ ----------------------------------------------------------------
+ METHOD: COEOptionEnableTotemBar
+
+ PURPOSE: Update the totem bar frame to reflect the change and
+ set availability of dependent options
+-------------------------------------------------------------------]]
+function COEOptionEnableTotemBar()
+
+ -- this call is needed if the bar was invisible
+ -- ---------------------------------------------
+ COE_Totem:UpdateAllFrames();
+
+ if( COE_Config:GetSaved( COEOPT_ENABLETOTEMBAR ) == 1 ) then
+ COE_ConfigTotemTotemBar:Enable();
+ COE_ConfigTotemTotemOptions:Enable();
+ COE_ConfigTotemTotemSets:Enable();
+
+ COE_Config:EnableCheckBox( COE_OptionFixBar );
+ COE_Config:EnableCheckBox( COE_OptionGroupBars );
+ COE_Config:EnableComboBox( COE_OptionTTAlignment );
+ COE_Config:EnableComboBox( COE_OptionDisplayMode );
+ COE_OptionConfigureBar:Enable();
+ COE_OptionConfigureOrder:Enable();
+ COE_Config:EnableComboBox( COE_OptionCurrentFrame );
+ COE_Config:EnableComboBox( COE_OptionDirection );
+ COE_Config:EnableComboBox( COE_OptionFrameMode );
+ COE_OptionFlexCount:Show();
+ else
+ COE_ConfigTotemTotemBar:Disable();
+ COE_ConfigTotemTotemOptions:Disable();
+ COE_ConfigTotemTotemSets:Disable();
+
+ COE_Config:OnSubTabButtonClick( COE_ConfigTotemTotemBar );
+
+ COE_Config:DisableCheckBox( COE_OptionFixBar );
+ COE_Config:DisableCheckBox( COE_OptionGroupBars );
+ COE_Config:DisableComboBox( COE_OptionTTAlignment );
+ COE_Config:DisableComboBox( COE_OptionDisplayMode );
+ COE_Config:ConfigureSet( false );
+ COE_Config:ConfigureBar( false );
+ COE_Config:ConfigureOrder( false );
+ COE_OptionConfigureBar:Disable();
+ COE_OptionConfigureOrder:Disable();
+ COE_Config:DisableComboBox( COE_OptionCurrentFrame );
+ COE_Config:DisableComboBox( COE_OptionDirection );
+ COE_Config:DisableComboBox( COE_OptionFrameMode );
+ COE_OptionFlexCount:Hide();
+ end
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COEOptionEnableTimers
+
+ PURPOSE: Disable dependent elements
+-------------------------------------------------------------------]]
+function COEOptionEnableTimers()
+
+ if( COE_Config:GetSaved( COEOPT_ENABLETIMERS ) == 0 ) then
+ -- disable dependent elements
+ -- ---------------------------
+ COE_Config:DisableCheckBox( COE_OptionEnableTimerNotifications );
+ COE_Config:DisableCheckBox( COE_OptionEnableTimerFrame );
+ COE_Config:DisableComboBox( COE_OptionDisplayAlignment );
+ COE_Config:DisableCheckBox( COE_OptionFrameTimersOnly );
+ COETimerFrame:Hide();
+ else
+ COE_Config:EnableCheckBox( COE_OptionEnableTimerNotifications );
+ COE_Config:EnableCheckBox( COE_OptionEnableTimerFrame );
+ COE_Config:EnableComboBox( COE_OptionDisplayAlignment );
+ COE_Config:EnableCheckBox( COE_OptionFrameTimersOnly );
+
+ if( COE_Config:GetSaved( COEOPT_TIMERFRAME ) == 1 ) then
+ COETimerFrame:Show();
+ end
+ end
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COEOptionTTAlignmentInit
+
+ PURPOSE: Fills the Tooltip Alignment combo box
+-------------------------------------------------------------------]]
+function COEOptionTTAlignmentInit()
+
+ local i;
+ for i = 1, table.getn( COEUI_TTALIGN ) do
+ UIDropDownMenu_AddButton( { text = COEUI_TTALIGN[i][2]; func = COEOptionTTAlignmentClick } );
+ end
+ UIDropDownMenu_SetSelectedID( COE_OptionTTAlignmentCB, COE_Config:GetSaved( COEOPT_TTALIGNMENT ) );
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COEOptionTTAlignmentClick()
+
+ PURPOSE: Selects the clicked button and stores the option
+-------------------------------------------------------------------]]
+function COEOptionTTAlignmentClick()
+
+ UIDropDownMenu_SetSelectedID( COE_OptionTTAlignmentCB, this:GetID() );
+ COE_Config:SetOption( COEOPT_TTALIGNMENT, this:GetID() );
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COEOptionDisplayModeInit
+
+ PURPOSE: Fills the Display Mode combo box
+-------------------------------------------------------------------]]
+function COEOptionDisplayModeInit()
+
+ local i;
+ for i = 1, table.getn( COEUI_DISPLAYMODE ) do
+ UIDropDownMenu_AddButton( { text = COEUI_DISPLAYMODE[i]; func = COEOptionDisplayModeClick } );
+ end
+ UIDropDownMenu_SetSelectedID( COE_OptionDisplayModeCB, COE_Config:GetSaved( COEOPT_DISPLAYMODE ) );
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COEOptionDisplayModeClick()
+
+ PURPOSE: Selects the clicked button and stores the option
+-------------------------------------------------------------------]]
+function COEOptionDisplayModeClick()
+
+ UIDropDownMenu_SetSelectedID( COE_OptionDisplayModeCB, this:GetID() );
+ COE_Config:SetOption( COEOPT_DISPLAYMODE, this:GetID() );
+
+ -- reconfigure totems
+ -- -------------------
+ COETotemFrame.Reconfigure = true;
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COEOptionEnableSets()
+
+ PURPOSE: Set availability of the set buttons
+-------------------------------------------------------------------]]
+function COEOptionEnableSets()
+
+ if( COE_Config:GetSaved( COEOPT_ENABLESETS ) == 1 ) then
+ COE_Config:EnableCheckBox( COE_OptionEnableAutoSwitch );
+ COE_Config:EnableComboBox( COE_OptionActiveSet );
+ COE_OptionConfigureSet:Enable();
+ COE_OptionSetName:Show();
+ else
+ COE_Config:DisableCheckBox( COE_OptionEnableAutoSwitch );
+ COE_Config:DisableComboBox( COE_OptionActiveSet );
+ COE_OptionConfigureSet:Disable();
+ COE_OptionSetName:Hide();
+
+ COE_Config:ActivateSet( COESET_DEFAULT );
+ end
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COEOptionEnableAdvisor()
+
+ PURPOSE: Schedules the advisor check if enabled
+-------------------------------------------------------------------]]
+function COEOptionEnableAdvisor()
+
+ if( COE_Config:GetSaved( COEOPT_ADVISOR ) == 1 ) then
+ -- schedule only if not already scheduled to prevent
+ -- rescheduling on config dialog display
+ -- --------------------------------------------------
+ if( not Chronos.isScheduledByName( "COEAdvise" ) ) then
+ Chronos.scheduleByName( "COEAdvise", COE.AdvisorInterval, COESched_RunAdvisor );
+ end
+ else
+ Chronos.unscheduleByName( "COEAdvise" );
+ COE.CleansingTotems.Tremor.Warn = false;
+ COE.CleansingTotems.Disease.Warn = false;
+ COE.CleansingTotems.Poison.Warn = false;
+ end
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COEOptionEnableAutoSwitch()
+
+ PURPOSE: Refill the Active Set combo box
+-------------------------------------------------------------------]]
+function COEOptionEnableAutoSwitch()
+
+ if( COE_Config:GetSaved( COEOPT_AUTOSWITCH ) == 0 and
+ COE_Config:GetSaved( COEOPT_ACTIVESET ) <= COESET_DEFAULT ) then
+
+ -- switch to default set
+ -- ----------------------
+ COE_Config:ActivateSet( COESET_DEFAULT );
+ end
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COEOptionActiveSetInit
+
+ PURPOSE: Fills the Active Set combo box
+-------------------------------------------------------------------]]
+function COEOptionActiveSetInit()
+
+ local i;
+
+ -- pvp sets
+ -- ---------
+ if( COE_Config:GetSaved( COEOPT_AUTOSWITCH ) == 1 ) then
+ for i = 1, table.getn( COEUI_PVPSETS ) do
+ UIDropDownMenu_AddButton( { text = COEUI_PVPSETS[i]; func = COEOptionActiveSetClick; value = i } );
+ end
+ end
+
+ -- default set
+ -- ------------
+ UIDropDownMenu_AddButton( { text = COEUI_DEFAULTSET; func = COEOptionActiveSetClick; value = COESET_DEFAULT } );
+
+ -- custom sets
+ -- ------------
+ for i = 1, table.getn( COE_CustomTotemSets ) do
+ UIDropDownMenu_AddButton( { text = COE_CustomTotemSets[i].Name; func = COEOptionActiveSetClick; value = COESET_DEFAULT + i } );
+ end
+
+ -- select the activated set
+ -- -------------------------
+ UIDropDownMenu_SetSelectedValue( COE_OptionActiveSetCB, COE_Config:GetSaved( COEOPT_ACTIVESET ) );
+
+ COE_Config:SetSetButtonStates();
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COEOptionActiveSetClick
+
+ PURPOSE: Activates the chosen totem set
+-------------------------------------------------------------------]]
+function COEOptionActiveSetClick()
+
+ COE_Config:ActivateSet( this.value );
+
+ -- reset set cycle
+ -- ----------------
+ COE_Totem:ResetSetCycle();
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COEOptionCurrentFrameInit
+
+ PURPOSE: Fills the Configure Bar combo box
+-------------------------------------------------------------------]]
+function COEOptionCurrentFrameInit()
+
+ local i;
+ for i = 1, table.getn( COEUI_CURRENTFRAME ) do
+ UIDropDownMenu_AddButton( { text = COEUI_CURRENTFRAME[i]; func = COEOptionCurrentFrameClick; value = COE_Element[i] } );
+ end
+ UIDropDownMenu_SetSelectedValue( COE_OptionCurrentFrameCB, COE_Config:GetSaved( COEOPT_CURRENTFRAME ) );
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COEOptionCurrentFrameClick()
+
+ PURPOSE: Selects the clicked button and stores the option
+-------------------------------------------------------------------]]
+function COEOptionCurrentFrameClick()
+
+ UIDropDownMenu_SetSelectedID( COE_OptionCurrentFrameCB, this:GetID() );
+ COE_Config:SetOption( COEOPT_CURRENTFRAME, this.value );
+
+ -- show associated options
+ -- ------------------------
+ UIDropDownMenu_Initialize( COE_OptionDirectionCB, COEOptionDirectionInit );
+ UIDropDownMenu_Initialize( COE_OptionFrameModeCB, COEOptionFrameModeInit );
+ COEOptionFlexCountShow();
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COEOptionDirectionInit
+
+ PURPOSE: Fills the Configure Bar combo box
+-------------------------------------------------------------------]]
+function COEOptionDirectionInit()
+
+ local i;
+ for i = 1, table.getn( COEUI_DIRECTION ) do
+ UIDropDownMenu_AddButton( { text = COEUI_DIRECTION[i]; func = COEOptionDirectionClick; value = COE_Direction[i]} );
+ end
+ local dir = COE_TotemBars[COE_Config:GetSaved( COEOPT_CURRENTFRAME )].Direction;
+ UIDropDownMenu_SetSelectedValue( COE_OptionDirectionCB, dir );
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COEOptionDirectionClick()
+
+ PURPOSE: Selects the clicked button and stores the option
+-------------------------------------------------------------------]]
+function COEOptionDirectionClick()
+
+ UIDropDownMenu_SetSelectedID( COE_OptionDirectionCB, this:GetID() );
+ COE_TotemBars[COE_Config:GetSaved( COEOPT_CURRENTFRAME )].Direction = this.value;
+
+ -- set the frame parameters
+ -- -------------------------
+ local frame = getglobal( "COE" .. COE_Config:GetSaved( COEOPT_CURRENTFRAME ) .. "Frame" );
+ COE_Totem:SetFrameDirection( frame, this.value );
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COEOptionFrameModeInit
+
+ PURPOSE: Fills the Frame mode combo box
+-------------------------------------------------------------------]]
+function COEOptionFrameModeInit()
+
+ local i;
+ for i = 1, table.getn( COEUI_FRAMEMODE ) do
+ UIDropDownMenu_AddButton( { text = COEUI_FRAMEMODE[i]; func = COEOptionFrameModeClick; value = COE_FrameMode[i]} );
+ end
+ local mode = COE_TotemBars[COE_Config:GetSaved( COEOPT_CURRENTFRAME )].Mode;
+ UIDropDownMenu_SetSelectedValue( COE_OptionFrameModeCB, mode );
+
+ -- show the slider if appropriate
+ -- -------------------------------
+ if( mode == "Flex" ) then
+ COE_OptionFlexCount:Show();
+ else
+ COE_OptionFlexCount:Hide();
+ end
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COEOptionFrameModeClick()
+
+ PURPOSE: Selects the clicked button and stores the option
+-------------------------------------------------------------------]]
+function COEOptionFrameModeClick()
+
+ UIDropDownMenu_SetSelectedID( COE_OptionFrameModeCB, this:GetID() );
+ COE_TotemBars[COE_Config:GetSaved( COEOPT_CURRENTFRAME )].Mode = this.value;
+
+ -- set the frame parameters
+ -- -------------------------
+ local frame = getglobal( "COE" .. COE_Config:GetSaved( COEOPT_CURRENTFRAME ) .. "Frame" );
+ frame.Mode = this.value;
+
+ -- show the slider if appropriate
+ -- -------------------------------
+ if( this.value == "Flex" ) then
+ COE_OptionFlexCount:Show();
+ else
+ COE_OptionFlexCount:Hide();
+ end
+
+ -- force frame update
+ -- -------------------
+ COE_Totem:UpdateAllFrames();
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COEOptionFlexCountShow()
+
+ PURPOSE: Sets the slider texts and values
+-------------------------------------------------------------------]]
+function COEOptionFlexCountShow()
+
+ local element = COE_Config:GetSaved( COEOPT_CURRENTFRAME );
+
+ -- set values
+ -- -----------
+ COE_OptionFlexCount:SetMinMaxValues( 1, COE.MaxTotems[element] );
+ COE_OptionFlexCount:SetValue( COE_TotemBars[element].FlexCount );
+ COE_OptionFlexCount:SetValueStep( 1 );
+
+ -- set texts
+ -- ----------
+ getglobal( COE_OptionFlexCount:GetName() .. "Low" ):SetText( 1 );
+ getglobal( COE_OptionFlexCount:GetName() .. "High" ):SetText( COE.MaxTotems[element] );
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COEOptionFlexCountChange()
+
+ PURPOSE: Stores the option and updates the display
+-------------------------------------------------------------------]]
+function COEOptionFlexCountChange()
+
+ local element = COE_Config:GetSaved( COEOPT_CURRENTFRAME );
+
+ COE_TotemBars[element].FlexCount = COE_OptionFlexCount:GetValue();
+
+ -- set the frame parameters
+ -- -------------------------
+ local frame = getglobal( "COE" .. element .. "Frame" );
+ frame.FlexCount = COE_TotemBars[element].FlexCount;
+ COE_Totem:Invalidate( frame, false, true, true );
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COEOptionScalingShow()
+
+ PURPOSE: Sets the slider texts and values
+-------------------------------------------------------------------]]
+function COEOptionScalingShow()
+
+ -- set values
+ -- -----------
+ COE_OptionScaling:SetMinMaxValues( 0.64, 1.2 );
+ COE_OptionScaling:SetValue( COE_Config:GetSaved( COEOPT_SCALING ) );
+ COE_OptionScaling:SetValueStep( 0.01 );
+
+ -- set texts
+ -- ----------
+ getglobal( COE_OptionScaling:GetName() .. "Low" ):SetText( 0.64 );
+ getglobal( COE_OptionScaling:GetName() .. "High" ):SetText( 1.2 );
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COEOptionScalingChange()
+
+ PURPOSE: Stores the option and updates the display
+-------------------------------------------------------------------]]
+function COEOptionScalingChange()
+
+ COE_Config:SetOption( COEOPT_SCALING, COE_OptionScaling:GetValue() );
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COEOptionEnableTimerFrame()
+
+ PURPOSE: Sets the visibility of the timer frame
+-------------------------------------------------------------------]]
+function COEOptionEnableTimerFrame()
+
+ if( COE_Config:GetSaved( COEOPT_TIMERFRAME ) == 1 ) then
+ COETimerFrame:Show();
+ else
+ COETimerFrame:Hide();
+ end
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COEOptionDisplayAlignInit
+
+ PURPOSE: Fills the Display Alignment combo box
+-------------------------------------------------------------------]]
+function COEOptionDisplayAlignInit()
+
+ local i;
+ for i = 1, table.getn( COEUI_DISPLAYALIGN ) do
+ UIDropDownMenu_AddButton( { text = COEUI_DISPLAYALIGN[i]; func = COEOptionDisplayAlignClick } );
+ end
+ UIDropDownMenu_SetSelectedID( COE_OptionDisplayAlignmentCB, COE_Config:GetSaved( COEOPT_DISPLAYALIGN ) );
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COEOptionDisplayAlignClick()
+
+ PURPOSE: Selects the clicked button and stores the option
+-------------------------------------------------------------------]]
+function COEOptionDisplayAlignClick()
+
+ UIDropDownMenu_SetSelectedID( COE_OptionDisplayAlignmentCB, this:GetID() );
+ COE_Config:SetOption( COEOPT_DISPLAYALIGN, this:GetID() );
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COEOptionOverrideRankInit
+
+ PURPOSE: Fills the Rank 1 modifier combo box
+-------------------------------------------------------------------]]
+function COEOptionOverrideRankInit()
+
+ local i;
+ for i = 1, table.getn( COEUI_OVERRIDERANK ) do
+ UIDropDownMenu_AddButton( { text = COEUI_OVERRIDERANK[i]; func = COEOptionOverrideRankClick } );
+ end
+ UIDropDownMenu_SetSelectedID( COE_OptionOverrideRankCB, COE_Config:GetSaved( COEOPT_OVERRIDERANK ) );
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COEOptionOverrideRankClick()
+
+ PURPOSE: Selects the clicked button and stores the option
+-------------------------------------------------------------------]]
+function COEOptionOverrideRankClick()
+
+ UIDropDownMenu_SetSelectedID( COE_OptionOverrideRankCB, this:GetID() );
+ COE_Config:SetOption( COEOPT_OVERRIDERANK, this:GetID() );
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COEOptionGroupBars()
+
+ PURPOSE: Stores the relative frame positions when enabled
+-------------------------------------------------------------------]]
+function COEOptionGroupBars()
+
+ if( COE_Config:GetSaved( COEOPT_GROUPBARS ) == 1 ) then
+ COE_Totem:UpdateFrameCoordinates();
+ end
+
+end
+
+
+--[[ =============================================================================================
+
+ T O T E M S E T S
+
+================================================================================================]]
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE_Config:ActivateSet
+
+ PURPOSE: Activates the specified set and shows its options
+-------------------------------------------------------------------]]
+function COE_Config:ActivateSet( index )
+
+ -- save setting
+ -- -------------
+ COE_Config:SetOption( COEOPT_ACTIVESET, index );
+
+ -- update drop down
+ -- -----------------
+ UIDropDownMenu_Initialize( COE_OptionActiveSetCB, COE_Config.Options[COEOPT_ACTIVESET].InitFunc );
+
+ -- update cast order
+ -- ------------------
+ if( index <= COESET_DEFAULT ) then
+ set = COE_SavedTotemSets[index];
+ else
+ set = COE_CustomTotemSets[index - COESET_DEFAULT];
+ end
+
+ local i;
+ for i = 1, 4 do
+ getglobal( "COE_OptionCastOrder" .. i .. "Text" ):SetText( set.CastOrder[i] );
+ end
+
+ -- reconfigure totem bar
+ -- ----------------------
+ COE_Totem:Invalidate( COEEarthFrame, true, true, true );
+ COE_Totem:Invalidate( COEFireFrame, true, true, true );
+ COE_Totem:Invalidate( COEWaterFrame, true, true, true );
+ COE_Totem:Invalidate( COEAirFrame, true, true, true );
+ COETotemFrame.Reconfigure = true;
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE_Config:SetSetButtonStates
+
+ PURPOSE: Sets the availablitity of the options in the totem
+ sets sup panel
+-------------------------------------------------------------------]]
+function COE_Config:SetSetButtonStates()
+
+ -- set button states
+ -- ------------------
+ if( UIDropDownMenu_GetSelectedValue( COE_OptionActiveSetCB ) <= COESET_DEFAULT ) then
+
+ -- set not removable
+ -- ------------------
+ COE_OptionDeleteSet:Disable();
+ else
+
+ -- set is removable
+ -- -----------------
+ COE_OptionDeleteSet:Enable();
+ end
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE_Config:NewSetTextChanged
+
+ PURPOSE: Enabled the New Set button only if text is entered
+-------------------------------------------------------------------]]
+function COE_Config:NewSetTextChanged()
+
+ if( this:GetText() ~= "" ) then
+ COE_OptionNewSet:Enable();
+ else
+ COE_OptionNewSet:Disable();
+ end
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE_Config:NewTotemSet
+
+ PURPOSE: Creates a new totem set with the specified name
+-------------------------------------------------------------------]]
+function COE_Config:NewTotemSet()
+
+ COE_OptionSetName:ClearFocus();
+
+ local name = COE_OptionSetName:GetText();
+ if( name and name ~= "" ) then
+ local setcount = table.getn( COE_CustomTotemSets );
+
+ -- create new set
+ -- ---------------
+ table.insert( COE_CustomTotemSets, { Name = name, Earth = "", Fire = "", Water = "", Air = "",
+ CastOrder = { COESTR_ELEMENT_EARTH, COESTR_ELEMENT_FIRE, COESTR_ELEMENT_WATER, COESTR_ELEMENT_AIR } } );
+
+ -- activate set
+ -- -------------
+ COE_Config:ActivateSet( COESET_DEFAULT + setcount + 1 );
+
+ -- rescan totem sets
+ -- ------------------
+ COE:InitTotemSets();
+
+ -- clear the edit box
+ -- -------------------
+ COE_OptionSetName:SetText( "" );
+ end
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE_Config:DeleteTotemSet
+
+ PURPOSE: Deletes the current custom set
+-------------------------------------------------------------------]]
+function COE_Config:DeleteTotemSet()
+
+ local set = UIDropDownMenu_GetSelectedValue( COE_OptionActiveSetCB );
+
+ if( set <= COESET_DEFAULT ) then
+ return;
+ end
+
+ set = set - COESET_DEFAULT;
+
+ -- delete set
+ -- -----------
+ table.remove( COE_CustomTotemSets, set );
+
+ -- set default set as active
+ -- --------------------------
+ COE_Config:ActivateSet( COESET_DEFAULT );
+
+ -- rescan totem sets
+ -- ------------------
+ COE:InitTotemSets();
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE_Config:ConfigureSet
+
+ PURPOSE: Switches between set configuration modes
+-------------------------------------------------------------------]]
+function COE_Config:ConfigureSet( mode )
+
+ if( mode == nil ) then
+ mode = not COE.ConfigureSetsMode;
+ end
+
+ if( mode ) then
+ COE_Config:ConfigureBar( false );
+ COE_Config:ConfigureOrder( false );
+ COE.ConfigureSetsMode = true;
+ COE_OptionConfigureSet:SetText( COEUI_STRINGS["COE_OptionStopConfigureSet"] );
+ else
+ COE.ConfigureSetsMode = false;
+ COE_OptionConfigureSet:SetText( COEUI_STRINGS["COE_OptionConfigureSet"] );
+ end
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE_Config:DoCastOrder
+
+ PURPOSE: Increases or decreases the cast order of the
+ associated element in this set depending on the
+ direction parameter (up/down)
+-------------------------------------------------------------------]]
+function COE_Config:DoCastOrder( up )
+
+ -- get id first
+ -- -------------
+ local id = this:GetParent():GetID();
+
+ -- already the first/last element?
+ -- --------------------------------
+ if( (up and id == 1) or (not up and id == 4) ) then
+ return;
+ end
+
+ -- get the other element id
+ -- -------------------------
+ local otherid;
+ if( up ) then
+ otherid = id - 1;
+ else
+ otherid = id + 1;
+ end
+
+ --=======================================
+ -- swap element with the next one
+ --=======================================
+
+ local activeset = COE_Config:GetSaved( COEOPT_ACTIVESET );
+ local swap;
+
+ -- swap in internal set
+ -- since the cast order is stored by reference
+ -- in the saved set, we only need to swap it here
+ -- -----------------------------------------------
+ swap = COE.TotemSets[activeset].CastOrder[otherid];
+ COE.TotemSets[activeset].CastOrder[otherid] = COE.TotemSets[activeset].CastOrder[id];
+ COE.TotemSets[activeset].CastOrder[id] = swap;
+
+ -- swap labels
+ -- ------------
+ local thistext = getglobal( "COE_OptionCastOrder" .. id .. "Text" );
+ local othertext = getglobal( "COE_OptionCastOrder" .. otherid .. "Text" );
+
+ swap = othertext:GetText();
+ othertext:SetText( thistext:GetText() );
+ thistext:SetText( swap );
+
+end
+
+
+--[[ =============================================================================================
+
+ L O G I C
+
+================================================================================================]]
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE_Config:RegisterOption
+
+ PURPOSE: Registers a checkbox or combobox for the configuration
+ dialog along with function to be executed and saved
+ variables
+
+ PARAMS: id - id of the frame (the unique option id)
+ type - 'check', 'combo' or 'slider'
+ func - function pointer for OnClick or OnChange
+ value - initial value (saved variable)
+ initfunc - function pointer for combobox initialization
+ / slider show
+ skip - if true, the option is not saved
+-------------------------------------------------------------------]]
+function COE_Config:RegisterOption( id, type, func, value, initfunc, skip )
+
+ -- check if the type is valid
+ -- ---------------------------
+ if( type ~= 'check' and type ~= 'combo' and type ~= 'slider' ) then
+ return;
+ end
+
+ -- store option
+ -- -------------
+ COE_Config.Options[id] = { Type = type, Func = func, InitFunc = initfunc };
+
+ -- store saved variable
+ -- ---------------------
+ if( not skip ) then
+ COE_Saved[id] = value;
+ end
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE_Config:SetOption
+
+ PURPOSE: Stores the new value in the options and saved
+ variables table
+-------------------------------------------------------------------]]
+function COE_Config:SetOption( id, value )
+
+ COE_Saved[id] = value;
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE_Config:GetSaved
+
+ PURPOSE: Retrieves the value from the saved variables or uses
+ the default value if it cannot find it there
+-------------------------------------------------------------------]]
+function COE_Config:GetSaved( id )
+
+ if( COE_Saved[id] ) then
+ return COE_Saved[id];
+ elseif( COE_Config.Defaults[id] ) then
+ return COE_Config.Defaults[id];
+ else
+ return nil;
+ end
+
+end
\ No newline at end of file
diff --git a/COE_Config.xml b/COE_Config.xml
new file mode 100644
index 0000000..e62e2a3
--- /dev/null
+++ b/COE_Config.xml
@@ -0,0 +1,565 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ COE_Config:OnFrameLoad();
+
+
+ this:StartMoving();
+
+
+ this:StopMovingOrSizing();
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ if( this.tooltipShown ) then
+ local value = math.floor( COE_Config:GetSaved( COEOPT_SCALING ) * 100 ) / 100;
+ GameTooltip:SetText( value );
+ end
+
+
+ GameTooltip:SetOwner( this, "TOPRIGHT" );
+ local value = math.floor( COE_Config:GetSaved( COEOPT_SCALING ) * 100 ) / 100;
+ GameTooltip:SetText( value );
+ this.tooltipShown = true;
+
+
+ GameTooltip:Hide();
+ this.tooltipShown = false;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ COE_Config:NewSetTextChanged();
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ COE_Config:OnConfigElementLoad( getglobal( this:GetName() .. "Text" ) );
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/COE_ConfigTemplates.xml b/COE_ConfigTemplates.xml
new file mode 100644
index 0000000..980941c
--- /dev/null
+++ b/COE_ConfigTemplates.xml
@@ -0,0 +1,340 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ this:SetBackdropBorderColor( 1, 1, 1 );
+ this:SetBackdropColor( 0.1, 0.1, 0.1 );
+ COE_Config:OnConfigElementLoad( getglobal( this:GetName().."TitleBoxText" ) );
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ this:SetBackdropBorderColor( 0.5, 0.5, 0.5 );
+ this:SetBackdropColor( 0.1, 0.1, 0.1 );
+
+
+
+
+
+
+
+
+
+
+
+ COE_Config:OnTabButtonClick();
+
+
+ COE_Config:OnConfigElementEnter();
+
+
+ COE_Config:OnConfigElementLeave();
+
+
+
+
+
+
+
+
+
+
+
+ COE_Config:OnConfigElementLoad( this );
+
+
+ COE_Config:OnSubTabButtonClick( this );
+
+
+ COE_Config:OnConfigElementEnter();
+
+
+ COE_Config:OnConfigElementLeave();
+
+
+
+
+
+
+
+
+
+
+
+ COE_Config:OnConfigElementLoad( getglobal( this:GetName() .. "Text" ) );
+
+
+ COE_Config:OnCheckBoxShow();
+
+
+ COE_Config:OnCheckBoxClick();
+
+
+ COE_Config:OnConfigElementEnter();
+
+
+ COE_Config:OnConfigElementLeave();
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ COE_Config:OnConfigElementLoad( getglobal( this:GetName() .. "LeftText" ) );
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ COE_Config:OnConfigElementLoad( this );
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ COE_Config:OnConfigElementLoad( getglobal( this:GetName() .. "Text" ) );
+
+
+ COE_Config:OnSliderShow();
+
+
+ COE_Config:OnSliderChange();
+
+
+
+
+
\ No newline at end of file
diff --git a/COE_Heal.lua b/COE_Heal.lua
new file mode 100644
index 0000000..19c441b
--- /dev/null
+++ b/COE_Heal.lua
@@ -0,0 +1,356 @@
+--[[
+
+ CALL OF ELEMENTS
+ The All-In-One Shaman Addon
+
+ by Wyverex (2006)
+
+ Healing Module
+]]
+
+if( not COE_Heal ) then
+ COE_Heal = {};
+end
+
+
+--[[ ----------------------------------------------------------------
+ COE_Heal.Thresholds stores different healing thresholds
+
+ HealingNeeded - A target is only healed if it has less than
+ this health ratio
+
+ OverrideTarget - If a friendly target is active and a heal is
+ attempted, this target is only healed if no party/raid
+ member is below this health ration threshold
+-------------------------------------------------------------------]]
+COE_Heal["Thresholds"] = { HealingNeeded = 0.85, OverrideTarget = 0.5 };
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE_Heal:Init
+
+ PURPOSE: Registers events
+-------------------------------------------------------------------]]
+function COE_Heal:Init()
+
+ -- addon loaded?
+ -- --------------
+ if( not COE.Initialized ) then
+ return;
+ end
+
+ this:RegisterEvent( "PLAYER_ENTERING_WORLD" );
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE_Heal:OnEvent
+
+ PURPOSE: Handles In-Game events
+-------------------------------------------------------------------]]
+function COE_Heal:OnEvent( event )
+
+ if( event == "PLAYER_ENTERING_WORLD" ) then
+ -- load healing spells
+ -- --------------------
+ COE:ScanHealingSpells();
+ end
+
+end
+
+--[[ =============================================================================================
+
+ P U B L I C H E A L I N G F U N C T I O N S
+
+================================================================================================]]
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE_Heal:BestHeal
+
+ PURPOSE: Determines the party/raid member with the lowest
+ health ratio and attempts to heal him with the best
+ possible heal spell but still tries to be as mana
+ efficient as possible
+-------------------------------------------------------------------]]
+function COE_Heal:BestHeal()
+
+ COE_Heal:Heal( "best" );
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE_Heal:BattleHeal
+
+ PURPOSE: Determines the party/raid member with the lowest
+ health ratio and attempts to heal him with the heal spell
+ that has the shortest possible cast time but still heals
+ him a good amount.
+ If Nature's Swiftness is active, it uses BestHeal instead
+-------------------------------------------------------------------]]
+function COE_Heal:BattleHeal()
+
+ COE_Heal:Heal( "battle" );
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE_Heal:Heal
+
+ PURPOSE: Performs the healing logic
+-------------------------------------------------------------------]]
+function COE_Heal:Heal( type )
+
+ -- first determine the target to be healed
+ -- ----------------------------------------
+ local target = COE_Heal:DetermineHealTarget();
+
+ if( target.ratio > COE_Heal.Thresholds.HealingNeeded ) then
+ -- no healing needed
+ -- -------------------
+ COE:Message( COESTR_NOHEALING );
+ return;
+ end
+
+ COE:DebugMessage( "Attempting to heal " .. target.target .. " with health ratio: " .. target.ratio ..
+ " (missing: " .. target.max - target.current .. ")" );
+
+ -- then determine the right spell and rank to use
+ -- -----------------------------------------------
+ local id, spell, rank;
+ if( type == "best" ) then
+ id, spell, rank = COE_Heal:DetermineBestSpell( COE.HealData.Sorted, target.max - target.current );
+ else
+ id, spell, rank = COE_Heal:DetermineBattleSpell( target.max - target.current );
+ end
+
+ if( not id ) then
+ return;
+ end
+
+ COE:DebugMessage( "Using " .. spell .. " (Rank " .. rank .. ")" );
+
+ -- is the spell usable?
+ -- ---------------------
+ local start, duration = GetSpellCooldown( id, BOOKTYPE_SPELL );
+ if( start > 0 or duration > 0 ) then
+ COE:Message( spell .. COESTR_HEALCOOLDOWN );
+ return;
+ end
+
+ -- is the target in range?
+ -- ------------------------
+
+ -- cast spell
+ -- -----------
+ COE:Message( string.format( COESTR_HEALING, UnitName( target.target ), spell, rank ) );
+ CastSpell( id, BOOKTYPE_SPELL );
+ SpellTargetUnit( target.target );
+
+
+end
+
+
+--[[ =============================================================================================
+
+ H E A L I N G L O G I C
+
+================================================================================================]]
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE_Heal:DetermineHealTarget
+
+ PURPOSE: Determines the party/raid member with
+ the lowest health ratio which can also be the player himself
+ If a friendly non-party/non-raid target is active,
+ it will return this target instead, if no party/raid
+ member falls below the OverrideTarget threshold
+-------------------------------------------------------------------]]
+function COE_Heal:DetermineHealTarget()
+
+ local toBeHealed = { target = nil, ratio = 1.0, current = 0, max = 0 };
+
+ -- first check the player
+ -- -----------------------
+ local curHealth = UnitHealth( "player" );
+ local maxHealth = UnitHealthMax( "player" )
+ local curRatio = curHealth / maxHealth;
+
+ toBeHealed.target = "player";
+ toBeHealed.ratio = curRatio;
+ toBeHealed.current = curHealth;
+ toBeHealed.max = maxHealth;
+
+ -- then check the party
+ -- ---------------------
+ local i;
+ for i = 1, GetNumPartyMembers() do
+ curHealth = UnitHealth( "party" .. i );
+ maxHealth = UnitHealthMax( "party" .. i )
+ curRatio = curHealth / maxHealth;
+
+ if( curRatio < toBeHealed.ratio and curHealth ~= 0 ) then
+ toBeHealed.target = "party" .. i;
+ toBeHealed.ratio = curRatio;
+ toBeHealed.current = curHealth;
+ toBeHealed.max = maxHealth;
+ end
+ end
+
+ -- finally check the raid
+ -- -----------------------
+ for i = 1, GetNumRaidMembers() do
+ curHealth = UnitHealth( "raid" .. i );
+ maxHealth = UnitHealthMax( "raid" .. i )
+ curRatio = curHealth / maxHealth;
+
+ if( curRatio < toBeHealed.ratio and curHealth ~= 0 ) then
+ toBeHealed.target = "raid" .. i;
+ toBeHealed.ratio = curRatio;
+ toBeHealed.current = curHealth;
+ toBeHealed.max = maxHealth;
+ end
+ end
+
+ -- do we have a friendly target?
+ -- ------------------------------
+ if( UnitExists( "target" ) and UnitIsFriend( "player", "target" ) ) then
+ local _,class = UnitClass( "target" );
+ local scale = ( COE.DefClassHealth[class].L60 - COE.DefClassHealth[class].L1 ) / 59;
+
+ if( UnitInParty( "target" ) or UnitInRaid( "target" ) ) then
+ curHealth = UnitHealth( "target" );
+ maxHealth = UnitHealthMax( "target" );
+ else
+ -- estimate the target's real health
+ -- ----------------------------------
+ maxHealth = scale * UnitLevel( "target" ) + COE.DefClassHealth[class].L1;
+ curHealth = maxHealth * UnitHealth( "target" ) / 100;
+ COE:DebugMessage( curHealth .. " / " .. maxHealth );
+ end
+ curRatio = curHealth / maxHealth;
+
+ if( curRatio < toBeHealed.ratio and curHealth ~= 0 ) then
+ -- if target is party or raid member heal it
+ -- and don't care about the override threshold.
+ -- the player supposedly knows what he's doing
+ -- ---------------------------------------------
+ if( UnitInParty( "target" ) or UnitInRaid( "target" ) ) then
+ toBeHealed.target = "target";
+ toBeHealed.ratio = curRatio;
+ toBeHealed.current = curHealth;
+ toBeHealed.max = maxHealth;
+
+ else
+ -- only heal the target if no party or raid member
+ -- is below the override threshold
+ -- ------------------------------------------------
+ if( toBeHealed.ratio > COE_Heal.Thresholds.OverrideTarget ) then
+ COE:DebugMessage( curHealth .. "/" .. maxHealth );
+ toBeHealed.target = "target";
+ toBeHealed.ratio = curRatio;
+ toBeHealed.current = curHealth;
+ toBeHealed.max = maxHealth;
+ else
+ COE:DebugMessage( "Overriding target due to OverrideTarget threshold" );
+ end
+ end
+
+ end
+
+ end
+
+ -- round ratio to 2 digits
+ -- ------------------------
+ toBeHealed.ratio = math.floor( toBeHealed.ratio * 100 ) / 100;
+
+ return toBeHealed;
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE_Heal:DetermineBestSpell
+
+ PURPOSE: Determines the the spell to be used to heal possibly
+ healmount of health but uses least possible mana
+-------------------------------------------------------------------]]
+function COE_Heal:DetermineBestSpell( spelltable, healamount )
+
+ local lowerRank = false;
+ local i, spell;
+ for i = table.getn( spelltable ), 1, -1 do
+ spell = spelltable[i];
+ local avg = spell.AvgAmount;
+
+ if( avg <= healamount ) then
+ -- found a suitable spell
+ -- do we have enough mana?
+ -- ------------------------
+ if( UnitMana( "player" ) < spell.Mana ) then
+ lowerRank = true;
+ else
+ if( lowerRank ) then
+ COE:Message( string.format( COESTR_HEALLOWERRANK, spell.Rank ) );
+ end
+ return spell.SpellID, spell.Type, spell.Rank;
+ end
+
+ end
+
+ end
+
+ if( lowerRank ) then
+ -- out of mana
+ -- ------------
+ COE:Message( COESTR_HEALOOM );
+ return nil;
+ end
+
+ -- if we got here, the target is below threshold but misses
+ -- less health than our rank 1 spell can heal at minimum
+ -- use rank 1 explicitly, we want to heal after all...
+ -- ---------------------------------------------------------
+ spell = spelltable[1];
+
+ if( UnitMana( "player" ) < spell.Mana ) then
+ -- out of mana
+ -- ------------
+ COE:Message( COESTR_HEALOOM );
+ return nil;
+ else
+ return spell.SpellID, spell.Type, 1;
+ end
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE_Heal:DetermineBattleSpell
+
+ PURPOSE: Determines the the spell to be used to heal possibly
+ healmount of health but uses the least casting time
+ possible. That is, we use Lesser Healing Wave whenever
+ possible
+ If Nature's Swiftness is active, switch to
+ DetermineBestSpell instead to receive most out of our heal
+-------------------------------------------------------------------]]
+function COE_Heal:DetermineBattleSpell( healamount )
+
+ -- do we possess the lesser wave?
+ -- -------------------------------
+ if( table.getn( COE.HealData.Lesser ) == 0 ) then
+ return COE_Heal:DetermineBestSpell( COE.HealData.Sorted, healamount );
+ end
+
+ -- check for nature's swiftness
+ -- -----------------------------
+
+ -- use lesser healing wave
+ -- ------------------------
+ return COE_Heal:DetermineBestSpell( COE.HealData.Lesser, healamount );
+
+end
+
diff --git a/COE_Heal.xml b/COE_Heal.xml
new file mode 100644
index 0000000..0b686c7
--- /dev/null
+++ b/COE_Heal.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+ COE_Heal:Init();
+
+
+ COE_Heal:OnEvent( event );
+
+
+
+
+
+
\ No newline at end of file
diff --git a/COE_HealData.lua b/COE_HealData.lua
new file mode 100644
index 0000000..081fcac
--- /dev/null
+++ b/COE_HealData.lua
@@ -0,0 +1,171 @@
+--[[
+
+ CALL OF ELEMENTS
+ The All-In-One Shaman Addon
+
+ by Wyverex (2006)
+
+ Healing Module Data
+]]
+
+
+--[[ ----------------------------------------------------------------
+ COE.HealData contains a list of all heal spells that the
+ player possesses
+-------------------------------------------------------------------]]
+COE["HealData"] = {};
+COE.HealData["Wave"] = { };
+COE.HealData["Lesser"] = { };
+COE.HealData["Sorted"] = {};
+
+
+--[[ ----------------------------------------------------------------
+ COE.DefClassHealth contains a an estimation of average player
+ health by class. Stolen directly from PaladinAssistant :)
+ Shaman added with a first estimation
+-------------------------------------------------------------------]]
+COE["DefClassHealth"] = {
+ ["WARRIOR"] = { L1=30, L60=4100 },
+ ["SHAMAN"] = { L1=30, L60=3500 },
+ ["ROGUE"] = { L1=30, L60=3100 },
+ ["HUNTER"] = { L1=30, L60=3100 },
+ ["DRUID"] = { L1=30, L60=3100 },
+ ["WARLOCK"] = { L1=30, L60=2300 },
+ ["MAGE"] = { L1=30, L60=2200 },
+ ["PRIEST"] = { L1=30, L60=2100 }
+ };
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE:CreateHealingSpell
+
+ PURPOSE: Creates a new healing spell object with default values
+-------------------------------------------------------------------]]
+function COE:CreateHealingSpell()
+
+ return { SpellID = 0, Type = "", Rank = 0, Mana = 0, MinAmount = 0,
+ MaxAmount = 0, AvgAmount = 0 };
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE:LocalizedSpell
+
+ PURPOSE: Returns the english name for a healing spell
+-------------------------------------------------------------------]]
+function COE:LocalizedSpell( spellname )
+
+ if( spellname == COESTR_HEALINGWAVE ) then
+ return "Wave";
+ elseif( spellname == COESTR_LESSERWAVE ) then
+ return "Lesser";
+ else
+ return spellname;
+ end
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COEAvgHealComparator
+
+ PURPOSE: Comparator for sorting the healing spells for BestHeal
+ Spells are sorted by average amount of healing
+-------------------------------------------------------------------]]
+function COEAvgHealComparator( x, y )
+
+ return x.AvgAmount < y.AvgAmount;
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE:ScanHealingSpells
+
+ PURPOSE: Scans the spellbook for available healing spells and
+ populates the COE.HealData list
+-------------------------------------------------------------------]]
+function COE:ScanHealingSpells()
+
+ COE:DebugMessage( "Scanning Healing Spells..." );
+
+ -- delete existing healing spells
+ -- -------------------------------
+ COE.HealData = {};
+ COE.HealData.Wave = { };
+ COE.HealData.Lesser = { };
+ COE.HealData.Sorted = {};
+
+ -- iterate over all spells in the spellbook
+ -- -----------------------------------------
+ local i = 1;
+ while true do
+
+ local SpellName, SpellRank = GetSpellName( i, BOOKTYPE_SPELL );
+ if not SpellName then
+ do break end;
+ end
+
+ -- is this a healing spell?
+ -- -------------------------
+ if( string.find( SpellName, COESTR_HEALINGWAVE ) ~= nil or
+ string.find( SpellName, COESTR_LESSERWAVE ) ~= nil ) then
+
+ local valid = true;
+ local heal = COE:CreateHealingSpell();
+ heal.SpellID = i;
+ heal.Type = SpellName;
+
+ -- extract mana cost and amount of healing from the tooltip
+ -- ---------------------------------------------------------
+ COETotemTT:SetSpell( i, BOOKTYPE_SPELL );
+
+ local text = COETotemTTTextLeft2:GetText();
+ local _,_,value = string.find( text, COESTR_TOTEMMANA );
+ if( value ) then
+ heal.Mana = tonumber( value );
+ else
+ valid = false;
+ end
+
+ text = COETotemTTTextLeft4:GetText();
+ _,_,value = string.find( text, COESTR_MINAMOUNT );
+ if( value ) then
+ heal.MinAmount = tonumber( value );
+ else
+ valid = false;
+ end
+
+ _,_,value = string.find( text, COESTR_MAXAMOUNT );
+ if( value ) then
+ heal.MaxAmount = tonumber( value );
+ else
+ valid = false;
+ end
+
+ -- valid spell?
+ -- -------------
+ if( valid ) then
+ heal.AvgAmount = heal.MinAmount + (heal.MaxAmount - heal.MinAmount) / 2;
+
+ local spell = COE:LocalizedSpell( SpellName );
+ table.insert( COE.HealData[spell], heal );
+ table.insert( COE.HealData.Sorted, heal );
+
+ heal.Rank = table.getn( COE.HealData[spell] );
+
+ COE:DebugMessage( "Found healing spell: " .. SpellName .. " (Rank " .. table.getn( COE.HealData[spell] ) .. ")" );
+ end
+
+ end
+
+ i = i + 1;
+
+ end
+
+ -- sort the list for BestHeal
+ -- ---------------------------
+ table.sort( COE.HealData.Sorted, COEAvgHealComparator );
+
+end
diff --git a/COE_Totem.xml b/COE_Totem.xml
new file mode 100644
index 0000000..f16fb9a
--- /dev/null
+++ b/COE_Totem.xml
@@ -0,0 +1,283 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ COE_Totem:OnTotemButtonLoad();
+
+
+ COE_Totem:OnTotemButtonEvent( event );
+
+
+ COE_Totem:OnTotemButtonClick();
+
+
+ COE_Totem:OnEnterTotemButton();
+
+
+ COE_Totem:OnLeaveTotemButton();
+
+
+ COE_Totem:UpdateTotemButton( arg1 );
+
+
+ COE_Totem:ButtonStartDrag();
+
+
+ COE_Totem:ButtonStopDrag();
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ COE_Totem:InitFrame();
+ this.Element = "Earth";
+
+
+ COE_Totem:UpdateFrame( arg1 );
+
+
+ COE_Totem:OnFrameEvent( event );
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ COE_Totem:InitFrame();
+ this.Element = "Fire";
+
+
+ COE_Totem:UpdateFrame( arg1 );
+
+
+ COE_Totem:OnFrameEvent( event );
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ COE_Totem:InitFrame();
+ this.Element = "Water";
+
+
+ COE_Totem:UpdateFrame( arg1 );
+
+
+ COE_Totem:OnFrameEvent( event );
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ COE_Totem:InitFrame();
+ this.Element = "Air";
+
+
+ COE_Totem:UpdateFrame( arg1 );
+
+
+ COE_Totem:OnFrameEvent( event );
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ COE_Totem:InitMainFrame();
+
+
+ COE_Totem:OnMainFrameEvent( event );
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ COE_Totem:InitTimerFrame();
+
+
+ COE_Totem:UpdateTimerFrame( arg1 );
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/COE_TotemData.lua b/COE_TotemData.lua
new file mode 100644
index 0000000..51bbf83
--- /dev/null
+++ b/COE_TotemData.lua
@@ -0,0 +1,827 @@
+--[[
+
+ CALL OF ELEMENTS
+ The All-In-One Shaman Addon
+
+ by Wyverex (2006)
+
+
+ Totem Module Data
+
+]]
+
+--[[ ----------------------------------------------------------------
+ COE.TotemData contains a list of totem classes that are
+ returned by COE:CreateTotem
+ For every available totem the player has, one object is
+ added to this list
+-------------------------------------------------------------------]]
+COE["TotemData"] = {};
+COE["TotemCount"] = 0;
+
+
+--[[ ----------------------------------------------------------------
+ COE.MaxTotems stores the maximum number of totems per element
+ This is taken right from the current game content
+-------------------------------------------------------------------]]
+COE["MaxTotems"] = { Earth = 5, Fire = 5, Water = 7, Air = 7 };
+
+
+--[[ ----------------------------------------------------------------
+ COE.TotemsAvailable contains the number of totems available
+ of each element
+-------------------------------------------------------------------]]
+COE["TotemsAvailable"] = {};
+COE.TotemsAvailable["Earth"] = 0;
+COE.TotemsAvailable["Fire"] = 0;
+COE.TotemsAvailable["Water"] = 0;
+COE.TotemsAvailable["Air"] = 0;
+
+
+--[[ ----------------------------------------------------------------
+ COE.ActiveTotems contains a pointer to the active totem of
+ each element
+
+ COE.TotemPending contains a pointer to the last totem spell
+ that was initiated but not yet completed. This lets COE check
+ whether something went wrong while casting (sitting etc.) by
+ activating the timer first when SPELLCAST_STOP is fired
+ After the timeout a pending totem timer is automatically removed
+ to prevent actions that trigger SPELLCAST_STOP and are not
+ hooked from activating the timer accidentally
+-------------------------------------------------------------------]]
+COE["ActiveTotems"] = { Earth = nil, Fire = nil, Water = nil, Air = nil };
+COE["TotemPending"] = { Totem = nil, UseRank = 0, Timeout = 0.75 };
+
+
+--[[ ----------------------------------------------------------------
+ COE.CleansingTotems stores pointers to the buttons and totems
+ that are able to cleanse poisons, diseases and sleep, charm
+ or fear effects
+-------------------------------------------------------------------]]
+COE["CleansingTotems"] = {
+Poison = { Totem = nil, Button = nil, Warn = false },
+Disease = { Totem = nil, Button = nil, Warn = false },
+Tremor = { Totem = nil, Button = nil, Warn = false } };
+
+
+--[[ ----------------------------------------------------------------
+ COE.TotemSets contains the totem pointers for each set and
+ element
+-------------------------------------------------------------------]]
+COE["TotemSetCount"] = 0;
+COE["TotemSets"] = { }
+
+
+--[[ ----------------------------------------------------------------
+ COE.SetCycle stores which totem of the active set have
+ already been thrown
+-------------------------------------------------------------------]]
+COE["SetCycle"] = { Earth = false, Fire = false, Water = false, Air = false };
+
+
+--[[ ----------------------------------------------------------------
+ COE.NoTotem is a placeholder for an empty anchor button
+-------------------------------------------------------------------]]
+COE["NoTotem"] = { SpellName = "", Element = "", Texture = "Interface\\Icons\\INV_Misc_Idol_03.blp",
+ ToolPresent = false, Ranks = { SpellID = 0, Mana = 0, Duration = 0, Health = 0, Cooldown = 0 },
+ MaxRank = 1, isActive = false, CurDuration = 0, CurHealth = 0, CurCooldown = 0 };
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE:CreateTotem
+
+ PURPOSE: Returns the totem class for a new totem
+-------------------------------------------------------------------]]
+function COE:CreateTotem()
+ return { SpellName = "", Element = "", Texture = "",
+ ToolPresent = false, Ranks = {}, MaxRank = 0, isActive = false,
+ CurDuration = 0, CurHealth = 0, CurCooldown = 0,
+ isTrinket = false, TrinketSlot = nil };
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE:CreateTotemRank
+
+ PURPOSE: Returns the class for a new totem rank
+-------------------------------------------------------------------]]
+function COE:CreateTotemRank()
+ return { SpellID = 0, Mana = 0, Duration = 0, Health = 0, Cooldown = 0 };
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE:ElementFromTool
+
+ PURPOSE: Returns the element corresponding to a totem tool.
+ This is needed for the french version to work. In the
+ english and german versions it just returns the input
+-------------------------------------------------------------------]]
+function COE:ElementFromTool( element )
+
+ if element == COESTR_TOTEMTOOLS_EARTH then
+ return COESTR_ELEMENT_EARTH;
+ elseif element == COESTR_TOTEMTOOLS_FIRE then
+ return COESTR_ELEMENT_FIRE;
+ elseif element == COESTR_TOTEMTOOLS_WATER then
+ return COESTR_ELEMENT_WATER;
+ elseif element == COESTR_TOTEMTOOLS_AIR then
+ return COESTR_ELEMENT_AIR;
+ end
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE:LocalizedElement
+
+ PURPOSE: Translates a localized element name into english
+-------------------------------------------------------------------]]
+function COE:LocalizedElement( element )
+
+ if( element == COESTR_ELEMENT_EARTH ) then
+ return "Earth";
+ elseif( element == COESTR_ELEMENT_FIRE ) then
+ return "Fire";
+ elseif( element == COESTR_ELEMENT_WATER ) then
+ return "Water";
+ elseif( element == COESTR_ELEMENT_AIR ) then
+ return "Air";
+ end
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE:ScanTotems
+
+ PURPOSE: Scans the spellbook for available totems and
+ populates the COE.TotemData list
+-------------------------------------------------------------------]]
+function COE:ScanTotems()
+
+ COE:DebugMessage( "Scanning Totems..." );
+
+ -- delete existing totem objects
+ -- ------------------------------
+ COE.TotemData = {};
+ COE.TotemCount = 0;
+ COE.TotemsAvailable.Earth = 0;
+ COE.TotemsAvailable.Fire = 0;
+ COE.TotemsAvailable.Water = 0;
+ COE.TotemsAvailable.Air = 0;
+
+ -- iterate over all spells in the spellbook
+ -- -----------------------------------------
+ local i, k = 1;
+ while true do
+
+ local SpellName, SpellRank = GetSpellName( i, BOOKTYPE_SPELL );
+ if not SpellName or SpellName == "Totemic Recall" then
+ do break end;
+ end
+
+ -- is this a totem?
+ -- -----------------
+ if( string.find( SpellName, COESTR_SCANTOTEMS ) ~= nil ) then
+
+ local newtotem = true;
+ local totem = nil;
+
+ -- get the rank value
+ -- -------------------
+ local _,_,rank = string.find( SpellRank, COESTR_TOTEMRANK );
+ if( not rank ) then
+ rank = 1;
+ else
+ rank = tonumber( rank );
+ end
+
+ -- =======================================================================
+ -- check if totem already exists with another rank in the list
+ -- =======================================================================
+ for k = 1, COE.TotemCount do
+ if( SpellName == COE.TotemData[k].SpellName ) then
+ -- use existing totem object
+ -- --------------------------
+ totem = COE.TotemData[k];
+ newtotem = false;
+ break;
+ end
+ end
+
+ COETotemTT:SetSpell( i, BOOKTYPE_SPELL );
+
+ -- =======================================================================
+ -- create new totem object if not found
+ -- =======================================================================
+ if( not totem ) then
+ totem = COE:CreateTotem();
+
+ -- set totem spell name
+ -- ---------------------
+ totem.SpellName = SpellName;
+
+ -- get totem school from tooltip
+ -- ------------------------------
+ local text = COETotemTTTextLeft4;
+ if( text and text:GetText() ) then
+
+ local _,_, element = string.find( text:GetText(), COESTR_TOTEMTOOLS );
+ if( element ) then
+
+ -- if element starts with a |, the player does
+ -- not possess the needed totem for this spell
+ -- extract the color code before using the element
+ -- ------------------------------------------------
+ if( string.sub( element, 1, 1 ) == "|" ) then
+ element = string.sub( element, 11 );
+ end
+
+ -- get element from tool
+ -- ----------------------
+ element = COE:ElementFromTool( element );
+
+ -- translate element to english
+ -- -----------------------------
+ element = COE:LocalizedElement( element );
+ -- valid element?
+ -- ---------------
+ if( COE.TotemsAvailable[element] ~= nil ) then
+ totem.Element = element;
+ COE.TotemsAvailable[element] = COE.TotemsAvailable[element] + 1;
+ else
+ COE:Message( COESTR_INVALIDELEMENT .. SpellName );
+ end
+ end
+ end
+
+ -- get totem texture
+ -- ------------------
+ totem.Texture = GetSpellTexture( i, BOOKTYPE_SPELL );
+
+ -- get tool presence
+ -- ------------------
+ totem.ToolPresent = COE:IsToolPresent( i );
+ end
+
+ -- =======================================================================
+ -- create new totem rank
+ -- =======================================================================
+ local totemrank = COE:CreateTotemRank();
+
+ -- set totem spell id
+ -- -------------------
+ totemrank.SpellID = i;
+
+ -- get totem mana cost from tooltip
+ -- ---------------------------------
+ text = COETotemTTTextLeft2;
+ if( text and text:GetText() ) then
+
+ local _,_, mana = string.find( text:GetText(), COESTR_TOTEMMANA );
+ if( mana ) then
+ totemrank.Mana = tonumber( mana );
+ end
+ end
+
+ -- get totem duration and health
+ -- ------------------------------
+ totemrank.Duration, totemrank.Health, totemrank.Cooldown = COE:GetTotemDurationAndHealth( i );
+
+ -- store rank in totem
+ -- --------------------
+ totem.Ranks[rank] = totemrank;
+ if( rank > totem.MaxRank ) then
+ totem.MaxRank = rank;
+ end
+
+ -- is this a new totem?
+ -- ---------------------
+ if( newtotem ) then
+ -- store totem
+ -- ------------
+ COE.TotemCount = COE.TotemCount + 1;
+ COE.TotemData[COE.TotemCount] = totem;
+
+
+ -- check if it's a cleansing totem
+ -- --------------------------------
+ if( SpellName == COESTR_TOTEMPOISON ) then
+ COE.CleansingTotems.Poison.Totem = totem;
+ elseif( SpellName == COESTR_TOTEMDISEASE ) then
+ COE.CleansingTotems.Disease.Totem = totem;
+ elseif( SpellName == COESTR_TOTEMTREMOR ) then
+ COE.CleansingTotems.Tremor.Totem = totem;
+ end
+
+ COE:DebugMessage( "Found totem: " .. SpellName );
+ end
+
+ -- =======================================================================
+ -- check visibility options
+ -- =======================================================================
+
+ if( COE_DisplayedTotems[SpellName] == nil ) then
+ -- perhaps a new totem. set it to default visible
+ -- and reorder it when the element count is known
+ -- -----------------------------------------------
+ COE_DisplayedTotems[SpellName] = { Order = 0, Element = totem.Element, Visible = true };
+ else
+ -- update old saved variables versions by adding the element
+ -- ----------------------------------------------------------
+ COE_DisplayedTotems[SpellName]["Element"] = totem.Element;
+ end
+ end
+
+ i = i + 1;
+ end
+
+ -- ====================================
+ -- Trinket support
+ -- ====================================
+
+ -- create the trinket totem even if the player doesn't have it.
+ -- this makes handling it much simpler and the player can choose
+ -- to make it invisible anyway
+ -- the player needs to have at least one water totem though
+ -- --------------------------------------------------------------
+ if( COE.TotemsAvailable.Water > 0 ) then
+ local trinket = COE:CreateTotem();
+ trinket.SpellName = "Trinket";
+ trinket.Element = "Water";
+ trinket.Texture = "Interface\\Icons\\INV_Wand_01";
+ trinket.isTrinket = true;
+ trinket.ToolPresent, trinket.TrinketSlot = COE:IsTrinketPresent();
+ trinket.Ranks[0] = COE:CreateTotemRank();
+ trinket.Ranks[0].Duration = 24;
+ trinket.Ranks[0].Health = 5;
+ trinket.Ranks[0].Cooldown = 180;
+
+ -- store totem
+ -- ------------
+ COE.TotemsAvailable.Water = COE.TotemsAvailable.Water + 1;
+ COE.TotemCount = COE.TotemCount + 1;
+ COE.TotemData[COE.TotemCount] = trinket;
+
+ if( COE_DisplayedTotems[trinket.SpellName] == nil ) then
+ -- perhaps a new totem. set it to default visible
+ -- and reorder it when the element count is known
+ -- -----------------------------------------------
+ COE_DisplayedTotems[trinket.SpellName] = { Order = 0, Visible = true };
+ end
+ end
+
+ -- ===================================
+ -- Finish
+ -- ===================================
+
+ COE:DebugMessage( "Found " .. COE.TotemCount .. " totems in spellbook" ..
+ "(" .. COE.TotemsAvailable.Earth .. " Earth, " ..
+ COE.TotemsAvailable.Fire .. " Fire, " .. COE.TotemsAvailable.Water .. " Water, " ..
+ COE.TotemsAvailable.Air .. " Air)" );
+
+ -- reorder new totems
+ -- -------------------
+ COE:ReorderNewTotems();
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE:GetTotemDurationAndHealth
+
+ PURPOSE: Extracts the duration of the totem out of the tooltip
+ When there is more than one time specification present,
+ the one with the longer duration is used since this is the
+ totem duration then
+ Also the health and the cooldown of the totem are returned
+-------------------------------------------------------------------]]
+function COE:GetTotemDurationAndHealth( spellid )
+
+ COETotemTTTextRight3:SetText( nil );
+ COETotemTT:SetSpell( spellid, BOOKTYPE_SPELL );
+ text = COETotemTTTextLeft5:GetText();
+
+ if( not text ) then
+ COE:DebugMessage( "nil text with id: " .. spellid );
+ return 0, 0;
+ end
+
+ local duration = 0;
+ local health = 0;
+ local cooldown = 0;
+
+ -- ===============================================================
+ -- Duration
+ -- ===============================================================
+
+ -- first search for a minute specification
+ -- if we find one it is surely the totem duration
+ -- -----------------------------------------------
+ local _,_,minutetext = string.find( text, COESTR_MINUTEDURATION );
+ if( minutetext ) then
+ -- calculate the duration in seconds
+ -- ----------------------------------
+ local min = tonumber( string.sub( minutetext, 1, 1 ) );
+ local sec = tonumber( string.sub( minutetext, 3, 4 ) ) / 100 * 60;
+
+ duration = min * 60 + sec;
+ else
+ _,_,minutetext = string.find( text, COESTR_MINUTEDURATION_INT );
+ if( minutetext ) then
+ duration = tonumber( minutetext ) * 60;
+ else
+ -- now test for a duration in seconds
+ -- -----------------------------------
+ local _,b,sectext1 = string.find( text, COESTR_SECDURATION );
+ if( sectext1 ) then
+
+ -- look if there are two second specifications
+ -- if so, take the greate one
+ -- --------------------------------------------
+ local _,_,sectext2 = string.find( string.sub( text, b ), COESTR_SECDURATION );
+
+ if( sectext2 ) then
+ duration = math.max( tonumber( sectext1 ), tonumber( sectext2 ) );
+ else
+ duration = tonumber( sectext1 );
+ end
+ end
+ end
+ end
+
+ -- ===============================================================
+ -- Health
+ -- ===============================================================
+
+ for num, regex in COESTR_TOTEMHEALTH do
+ local match = { string.gfind( text, regex )() };
+ if ( table.getn(match) >= 1 ) then
+
+ health = tonumber( match[1] );
+ break;
+ end
+ end
+
+ -- ===============================================================
+ -- Cooldown
+ -- ===============================================================
+
+ text = COETotemTTTextRight3:GetText();
+
+ if( not text ) then
+ cooldown = 0;
+ else
+ _,_,cooldown = string.find( text, COESTR_SECDURATION );
+ if( cooldown ) then
+ cooldown = tonumber( cooldown );
+ else
+ _,_,cooldown = string.find( text, COESTR_MINUTEDURATION_INT );
+ if( cooldown ) then
+ cooldown = tonumber( cooldown ) * 60;
+ else
+ cooldown = 0;
+ end
+ end
+ end
+
+ return duration, health, cooldown;
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE:IsToolPresent
+
+ PURPOSE: Checks if the player has the totem of the specified
+ element in his inventory
+
+ This is done by testing the color of the "Tools:" section
+ in the totem tooltip
+-------------------------------------------------------------------]]
+function COE:IsToolPresent( spellid )
+
+ -- get totem tooltip
+ -- ------------------
+ COETotemTT:SetSpell( spellid, BOOKTYPE_SPELL );
+
+ -- test for presence of the totem tool
+ -- ------------------------------------
+ local text = COETotemTTTextLeft4;
+ if( text and text:GetText() ) then
+
+ local _,_, element = string.find( text:GetText(), COESTR_TOTEMTOOLS );
+ if( element ) then
+
+ -- if element doesn't start with a |, the player
+ -- possesses the needed totem for this spell
+ -- ------------------------------------------------
+ if( string.sub( element, 1, 1 ) ~= "|" ) then
+ return true;
+ end
+ end
+ end
+
+ return false;
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE:IsTrinketPresent
+
+ PURPOSE: Checks if the player has the enamored water spirit
+ trinket equipped and returns the trinket slot
+
+ RETURNS: equipped, slot
+-------------------------------------------------------------------]]
+function COE:IsTrinketPresent()
+
+ for i = 0, 1 do
+ local slot = GetInventorySlotInfo( "Trinket" .. i .. "Slot" );
+ local item = GetInventoryItemLink( "player", slot );
+
+ if( item ) then
+ local itemname = string.find( item, COESTR_TRINKET );
+ if( itemname ) then
+ -- trinket is equipped
+ -- --------------------
+ return true, slot;
+ end
+ end
+ end
+
+ return false, nil;
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE:ReorderNewTotems
+
+ PURPOSE: Assigns each COE_DisplayedTotems entry with a zero
+ order a valid order depending on the number of available
+ totems in this element
+-------------------------------------------------------------------]]
+function COE:ReorderNewTotems()
+
+ local nextslot = { Earth = COE.TotemsAvailable.Earth, Fire = COE.TotemsAvailable.Fire,
+ Water = COE.TotemsAvailable.Water, Air = COE.TotemsAvailable.Air };
+
+ local used = { Earth = {}, Fire = {}, Water = {}, Air = {} };
+ local bError = false;
+
+ local k;
+ for k = 1, COE.TotemCount do
+
+ local totem = COE.TotemData[k];
+ if( COE_DisplayedTotems[totem.SpellName] ~= nil ) then
+ if( COE_DisplayedTotems[totem.SpellName].Order == 0 ) then
+
+ -- this totem has just been added
+ -- assign the currently free slot
+ -- -------------------------------
+ COE_DisplayedTotems[totem.SpellName].Order = nextslot[totem.Element];
+ nextslot[totem.Element] = nextslot[totem.Element] - 1;
+ end
+
+ -- register that this slot of the element is now in use
+ -- mark as error if already in use
+ -- -----------------------------------------------------
+ if( used[totem.Element][COE_DisplayedTotems[totem.SpellName].Order] == nil ) then
+ used[totem.Element][COE_DisplayedTotems[totem.SpellName].Order] = true;
+ else
+ bError = true;
+ end
+ end
+ end
+
+ -- are there multiple entries for one slot?
+ -- -----------------------------------------
+ if( bError ) then
+ -- there is something wrong with the saved variables
+ -- reset all ordering and reassign it
+ -- --------------------------------------------------
+ for k = 1, COE.TotemCount do
+
+ local totem = COE.TotemData[k];
+ if( COE_DisplayedTotems[totem.SpellName] ~= nil ) then
+ COE_DisplayedTotems[totem.SpellName].Order = 0;
+ end
+ end
+
+ COE:ReorderNewTotems();
+
+ end
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE:InitTotemSets
+
+ PURPOSE: Fills the COE.TotemSets list with the totem objects
+ corresponding to the spell names saved in COEOPT_TOTEMSETS
+-------------------------------------------------------------------]]
+function COE:InitTotemSets()
+
+ local indices = { "Earth", "Fire", "Water", "Air" };
+
+ COE.TotemSets = {};
+
+ -- for each standard set
+ -- ----------------------
+ local set;
+ for set = 1, table.getn( COE_SavedTotemSets ) do
+
+ COE.TotemSets[set] = { Earth = nil, Fire = nil, Water = nil, Air = nil,
+ CastOrder = COE_SavedTotemSets[set].CastOrder };
+
+ -- for each element
+ -- -----------------
+ local k, totem;
+ for k = 1,4 do
+
+ if( COE_SavedTotemSets[set][indices[k]] ~= "" ) then
+
+ -- iterate over all totems
+ -- ------------------------
+ for totem in COE.TotemData do
+
+ if( COE.TotemData[totem].SpellName == COE_SavedTotemSets[set][indices[k]] ) then
+ COE.TotemSets[set][indices[k]] = COE.TotemData[totem];
+ end
+ end
+ end
+ end
+ end
+
+ -- for each custom set
+ -- --------------------
+ for set = 1, table.getn( COE_CustomTotemSets ) do
+
+ COE.TotemSets[COESET_DEFAULT + set] = { Earth = nil, Fire = nil, Water = nil, Air = nil,
+ CastOrder = COE_CustomTotemSets[set].CastOrder };
+
+ -- for each element
+ -- -----------------
+ local k, totem;
+ for k = 1,4 do
+
+ if( COE_CustomTotemSets[set][indices[k]] ~= "" ) then
+
+ -- iterate over all totems
+ -- ------------------------
+ for totem in COE.TotemData do
+
+ if( COE.TotemData[totem].SpellName == COE_CustomTotemSets[set][indices[k]] ) then
+ COE.TotemSets[COESET_DEFAULT + set][indices[k]] = COE.TotemData[totem];
+ end
+ end
+ end
+ end
+ end
+
+ COE.TotemSetCount = table.getn( COE.TotemSets );
+
+end
+
+
+--[[ =============================================================================================
+
+ F I X E S
+
+================================================================================================]]
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE:Fix_CastOrderLocalization
+
+ PURPOSE: Correctly localizes the element names in the
+ cast order in COE_SavedTotemSets
+-------------------------------------------------------------------]]
+function COE:Fix_CastOrderLocalization()
+
+ -- for each standard set
+ -- ----------------------
+ local set, k;
+ for set = 1, table.getn( COE_SavedTotemSets ) do
+
+ for k = 1, 4 do
+ if( COE_SavedTotemSets[set].CastOrder[k] == "Earth" ) then
+ COE_SavedTotemSets[set].CastOrder[k] = COESTR_ELEMENT_EARTH;
+
+ elseif( COE_SavedTotemSets[set].CastOrder[k] == "Fire" ) then
+ COE_SavedTotemSets[set].CastOrder[k] = COESTR_ELEMENT_FIRE;
+
+ elseif( COE_SavedTotemSets[set].CastOrder[k] == "Water" ) then
+ COE_SavedTotemSets[set].CastOrder[k] = COESTR_ELEMENT_WATER;
+
+ elseif( COE_SavedTotemSets[set].CastOrder[k] == "Air" ) then
+ COE_SavedTotemSets[set].CastOrder[k] = COESTR_ELEMENT_AIR;
+ end
+ end
+ end
+
+ -- notify user
+ -- ------------
+ COE:Message( COESTR_FIXEDSETS );
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE:Fix_DisplayedTotems
+
+ PURPOSE: Fixes errors in the COE_DisplayedTotems array
+-------------------------------------------------------------------]]
+function COE:Fix_DisplayedTotems()
+
+ local i;
+ local fixed = false;
+ foreach( COE_DisplayedTotems, function( index, value )
+
+ -- is this an old-style entry?
+ -- ----------------------------
+ if( type( value ) == "boolean" ) then
+ COE_DisplayedTotems[index] = { Order = 0, Visible = value };
+ fixed = true;
+
+ elseif( type( value ) == "table" ) then
+ if( COE_DisplayedTotems[index].Visible == nil ) then
+ COE_DisplayedTotems[index].Visible = true;
+ fixed = true;
+ end
+
+ if( COE_DisplayedTotems[index].Order == nil ) then
+ COE_DisplayedTotems[index].Order = 0;
+ fixed = true;
+ end
+
+ else
+ COE_DisplayedTotems[index] = { Order = 0, Visible = value };
+ fixed = true;
+ end
+
+ end );
+
+ -- notify user
+ -- ------------
+ if( fixed ) then
+ COE:Message( COESTR_FIXEDDISPLAY );
+ end
+
+end
+
+
+--[[ ----------------------------------------------------------------
+ METHOD: COE:Fix_CastOrderLocalization2
+
+ PURPOSE: Correctly localizes the element names in the
+ cast order in COE_SavedTotemSets
+-------------------------------------------------------------------]]
+function COE:Fix_CastOrderLocalization2()
+
+ -- for each standard set
+ -- ----------------------
+ local set, k;
+ for set = 1, table.getn( COE_SavedTotemSets ) do
+
+ for k = 1, 4 do
+ if( COE_SavedTotemSets[set].CastOrder[k] == COESTR_TOTEMTOOLS_EARTH ) then
+ COE_SavedTotemSets[set].CastOrder[k] = COESTR_ELEMENT_EARTH;
+
+ elseif( COE_SavedTotemSets[set].CastOrder[k] == COESTR_TOTEMTOOLS_FIRE ) then
+ COE_SavedTotemSets[set].CastOrder[k] = COESTR_ELEMENT_FIRE;
+
+ elseif( COE_SavedTotemSets[set].CastOrder[k] == COESTR_TOTEMTOOLS_WATER ) then
+ COE_SavedTotemSets[set].CastOrder[k] = COESTR_ELEMENT_WATER;
+
+ elseif( COE_SavedTotemSets[set].CastOrder[k] == COESTR_TOTEMTOOLS_AIR ) then
+ COE_SavedTotemSets[set].CastOrder[k] = COESTR_ELEMENT_AIR;
+ end
+ end
+ end
+
+ -- for each custom set
+ -- --------------------
+ for set = 1, table.getn( COE_CustomTotemSets ) do
+
+ for k = 1, 4 do
+ if( COE_CustomTotemSets[set].CastOrder[k] == COESTR_TOTEMTOOLS_EARTH ) then
+ COE_CustomTotemSets[set].CastOrder[k] = COESTR_ELEMENT_EARTH;
+
+ elseif( COE_CustomTotemSets[set].CastOrder[k] == COESTR_TOTEMTOOLS_FIRE ) then
+ COE_CustomTotemSets[set].CastOrder[k] = COESTR_ELEMENT_FIRE;
+
+ elseif( COE_CustomTotemSets[set].CastOrder[k] == COESTR_TOTEMTOOLS_WATER ) then
+ COE_CustomTotemSets[set].CastOrder[k] = COESTR_ELEMENT_WATER;
+
+ elseif( COE_CustomTotemSets[set].CastOrder[k] == COESTR_TOTEMTOOLS_AIR ) then
+ COE_CustomTotemSets[set].CastOrder[k] = COESTR_ELEMENT_AIR;
+ end
+ end
+ end
+
+end
+
diff --git a/COE_TotemLogic.lua b/COE_TotemLogic.lua
new file mode 100644
index 0000000..40b4db7
--- /dev/null
+++ b/COE_TotemLogic.lua
@@ -0,0 +1,1200 @@
+--[[
+
+ 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
+
+
diff --git a/COE_TotemVisual.lua b/COE_TotemVisual.lua
new file mode 100644
index 0000000..02e893a
--- /dev/null
+++ b/COE_TotemVisual.lua
@@ -0,0 +1,1740 @@
+--[[
+
+ CALL OF ELEMENTS
+ The All-In-One Shaman Addon
+
+ by Wyverex (2006)
+
+
+ Totem Module / Visual
+
+]]
+
+if( not COE_Totem ) then
+ COE_Totem = {};
+end
+
+COEUI_BUTTONGAP = 4;
+
+COE_Totem.FlexTime = 0.3;
+
+COEFramePositions = { Earth = { x = 0, y = 0 }, Fire = { x = 0, y = 0 },
+ Water = { x = 0, y = 0 }, Air = { x = 0, y = 0 } }
+
+COEDynamic = { n = 7, nil, nil, nil, nil, nil, nil, nil };
+
+--[[ =============================================================================================
+
+ M A I N F R A M E
+
+================================================================================================]]
+
+--[[ ---------------------------------------------------------------------------------------------
+
+ Each element frame has the following properties:
+
+ - AdvisorTime :