Quantcast

v2.0 - fluid action bar support

Pawel [11-23-16 - 05:20]
v2.0 - fluid action bar support
Filename
Libs/BigLibTimer/BigLibTimer.lua
Modules/module.lua.template
TDButtons.lua
TDDps.lua
TDDps.toc
TDSettings.lua
diff --git a/Libs/BigLibTimer/BigLibTimer.lua b/Libs/BigLibTimer/BigLibTimer.lua
new file mode 100644
index 0000000..3727a09
--- /dev/null
+++ b/Libs/BigLibTimer/BigLibTimer.lua
@@ -0,0 +1,253 @@
+
+local MajorVersion = "BigLibTimer6"
+local BigLibTimer = LibStub:NewLibrary(MajorVersion, tonumber("20150826224730") or tonumber(date("%Y%m%d%H%M%S")))
+if not BigLibTimer then return end
+
+BigLibTimer.API = BigLibTimer.API or {}
+
+function BigLibTimer:Register(handler)
+	if type(handler) ~= "table" then
+		handler = {}
+	elseif handler[MajorVersion] then
+		return
+	end
+	handler[MajorVersion] = {}
+	handler[MajorVersion].RECYCLE_TABLES = setmetatable({}, {__mode = "k"})
+	handler[MajorVersion].TimerFrame = CreateFrame("Frame")
+	handler[MajorVersion].TimerFrame:Hide()
+	handler[MajorVersion].TIMER = {}
+	handler[MajorVersion].OnUpdate = function() BigLibTimer.OnUpdate(handler) end
+	handler[MajorVersion].TimerFrame:SetScript("OnUpdate", handler[MajorVersion].OnUpdate)
+	for key in pairs(BigLibTimer.API) do
+		handler[key] = function(...) return BigLibTimer.API[key](...) end
+	end
+	return handler
+end
+
+function BigLibTimer.OnUpdate(handler)
+	local TIMER = handler[MajorVersion].TIMER
+	if next(TIMER) then
+		if not handler[MajorVersion].Running then
+			handler[MajorVersion].Running = 1
+			for Name in pairs(TIMER) do
+				if TIMER and TIMER[Name] and not TIMER[Name].Running and TIMER[Name].Seconds <= GetTime() then
+					if TIMER[Name].Function then
+						TIMER[Name].Function(unpack(TIMER[Name].Args))
+						if TIMER and TIMER[Name] and TIMER[Name].Seconds <= GetTime() then
+							if TIMER[Name].RepeatSeconds > 0 then
+								TIMER[Name].Seconds = GetTime() + TIMER[Name].RepeatSeconds
+							else
+								TIMER[Name].Args = handler:RecycleTable(TIMER[Name].Args)
+								TIMER[Name] = handler:RecycleTable(TIMER[Name])
+							end
+						end
+					elseif TIMER[Name].RepeatSeconds > 0 then
+						TIMER[Name].Seconds = GetTime() + TIMER[Name].RepeatSeconds
+					else
+						TIMER[Name].Args = handler:RecycleTable(TIMER[Name].Args)
+						TIMER[Name] = handler:RecycleTable(TIMER[Name])
+					end
+				end
+			end
+			if not next(TIMER) then
+				handler[MajorVersion].TimerFrame:Hide()
+			end
+			handler[MajorVersion].Running = nil
+		end
+	elseif not handler[MajorVersion].Running then
+		handler[MajorVersion].TimerFrame:Hide()
+	end
+end
+
+function BigLibTimer.API:SetTimer(Name, Seconds, RepeatSeconds, Function, ...)
+	local TIMER = self[MajorVersion].TIMER
+	if type(Name) == "string" and TIMER then
+		if TIMER[Name] then
+			TIMER[Name].Args = self:RecycleTable(TIMER[Name].Args)
+		end
+		TIMER[Name] = self:CreateTable(TIMER[Name])
+		TIMER[Name].Running = 1
+		if type(Seconds) == "number" and Seconds > 0 then
+			TIMER[Name].Seconds = GetTime() + Seconds
+		else
+			TIMER[Name].Seconds = 0
+		end
+		if type(RepeatSeconds) == "number" and RepeatSeconds > 0 then
+			TIMER[Name].RepeatSeconds = RepeatSeconds
+		else
+			TIMER[Name].RepeatSeconds = 0
+		end
+		if type(Function) == "function" then
+			TIMER[Name].Function = Function
+			TIMER[Name].Args = self:CreateTable(TIMER[Name].Args)
+			local n = select("#", ...)
+			if n > 0 then
+				for i = 1, n do
+					TIMER[Name].Args[i] = select(i, ...)
+				end
+			end
+		end
+		if TIMER[Name].Seconds == 0 and TIMER[Name].Function then
+			Function(...)
+			if TIMER and TIMER[Name] and TIMER[Name].Seconds <= GetTime() then
+				if TIMER[Name].RepeatSeconds > 0 then
+					TIMER[Name].Seconds = GetTime() + TIMER[Name].RepeatSeconds
+				else
+					TIMER[Name].Args = self:RecycleTable(TIMER[Name].Args)
+					TIMER[Name] = self:RecycleTable(TIMER[Name])
+				end
+			end
+		end
+		if TIMER and TIMER[Name] then
+			TIMER[Name].Running = nil
+			self[MajorVersion].TimerFrame:Show()
+		end
+	end
+end
+
+function BigLibTimer.API:ReplaceTimer(Name, Seconds, RepeatSeconds, Function, ...)
+	local TIMER = self[MajorVersion].TIMER
+	if type(Name) == "string" and TIMER[Name] then
+		if type(Seconds) == "number" and Seconds > 0 then
+			TIMER[Name].Seconds = GetTime() + Seconds
+		elseif Seconds ~= nil then
+			TIMER[Name].Seconds = 0
+		end
+		if type(RepeatSeconds) == "number" and RepeatSeconds > 0 then
+			TIMER[Name].RepeatSeconds = RepeatSeconds
+		elseif RepeatSeconds ~= nil then
+			TIMER[Name].RepeatSeconds = 0
+		end
+		if type(Function) == "function" then
+			TIMER[Name].Function = Function
+			TIMER[Name].Args = self:CreateTable(TIMER[Name].Args)
+			local n = select("#", ...)
+			if n > 0 then
+				for i = 1, n do
+					TIMER[Name].Args[i] = select(i, ...)
+				end
+			end
+		elseif Function ~= nil then
+			TIMER[Name].Function = nil
+		end
+		return true
+	end
+	return false
+end
+
+function BigLibTimer.API:ClearTimer(Name, Search)
+	local TIMER = self[MajorVersion].TIMER
+	local found = nil
+	if type(Name) == "string" then
+		if Search then
+			for key in pairs(TIMER) do
+				if key:match(Name) and ( TIMER[key].RepeatSeconds > 0 or TIMER[key].Seconds - GetTime() > 0 ) then
+					TIMER[key].Args = self:RecycleTable(TIMER[key].Args)
+					TIMER[key] = self:RecycleTable(TIMER[key])
+					found = true
+				end
+			end
+		elseif TIMER[Name] and ( TIMER[Name].RepeatSeconds > 0 or TIMER[Name].Seconds - GetTime() > 0 ) then
+			TIMER[Name].Args = self:RecycleTable(TIMER[Name].Args)
+			TIMER[Name] = self:RecycleTable(TIMER[Name])
+			return true
+		end
+	end
+	return found
+end
+
+function BigLibTimer.API:ClearAllTimers()
+	wipe(self[MajorVersion].TIMER)
+end
+
+function BigLibTimer.API:IsTimer(Name, Search)
+	local TIMER = self[MajorVersion].TIMER
+	if type(Name) == "string" then
+		if Search then
+			for key in pairs(TIMER) do
+				if key:match(Name) and ( TIMER[key].RepeatSeconds > 0 or TIMER[key].Seconds - GetTime() > 0 ) then
+					return true
+				end
+			end
+		elseif TIMER[Name] and ( TIMER[Name].RepeatSeconds > 0 or TIMER[Name].Seconds - GetTime() > 0 ) then
+			return true
+		end
+	end
+	return false
+end
+
+function BigLibTimer.API:IsRepeatTimer(Name, Search)
+	local TIMER = self[MajorVersion].TIMER
+	if type(Name) == "string" then
+		if Search then
+			for key in pairs(TIMER) do
+				if key:match(Name) and TIMER[key].RepeatSeconds > 0 then
+					return true
+				end
+			end
+		elseif TIMER[Name] and TIMER[Name].RepeatSeconds > 0 then
+			return true
+		end
+	end
+	return false
+end
+
+function BigLibTimer.API:GetTimer(Name)
+	local TIMER = self[MajorVersion].TIMER
+	if type(Name) == "string" and TIMER[Name] then
+		local TimeRemaining = TIMER[Name].Seconds - GetTime()
+		if TimeRemaining > 0 then
+			return TimeRemaining
+		end
+	end
+	return 0
+end
+
+function BigLibTimer.API:CreateTable(Table, All)
+	if type(Table) == "table" and type(Table[0]) ~= "userdata" then
+		if All then
+			self:RecycleTable(Table, All)
+		else
+			wipe(Table)
+			return Table
+		end
+	end
+	local t = next(self[MajorVersion].RECYCLE_TABLES)
+	if t then
+		self[MajorVersion].RECYCLE_TABLES[t] = nil
+		if next(t) then
+			return self:CreateTable()
+		end
+		return t
+	end
+	return {}
+end
+
+function BigLibTimer.RecycleAllTables(self, Table, CompareList)
+	if not CompareList[Table] then
+		CompareList[Table] = 1
+		for k, v in pairs(Table) do
+			if type(v) == "table" and type(v[0]) ~= "userdata" then
+				BigLibTimer.RecycleAllTables(self, v, CompareList)
+			end
+			if type(k) == "table" and type(k[0]) ~= "userdata" then
+				BigLibTimer.RecycleAllTables(self, k, CompareList)
+			end
+		end
+		self:RecycleTable(Table)
+	end
+end
+
+function BigLibTimer.API:RecycleTable(Table, All)
+	if type(Table) == "table" and type(Table[0]) ~= "userdata" then
+		if All then
+			local CompareList = self:CreateTable()
+			BigLibTimer.RecycleAllTables(self, Table, CompareList)
+			self:RecycleTable(CompareList)
+		else
+			wipe(Table)
+			self[MajorVersion].RECYCLE_TABLES[Table] = 1
+		end
+	end
+	return nil
+end
diff --git a/Modules/module.lua.template b/Modules/module.lua.template
index fa5ac09..60ebc96 100644
--- a/Modules/module.lua.template
+++ b/Modules/module.lua.template
@@ -23,18 +23,18 @@ end
 ----------------------------------------------
 function TDDps_[Class]_EnableAddon(mode)
 	mode = mode or 1;
-	_TD["DPS_Description"] = "TD [Class] DPS supports: [Spec1], [Spec2], [Spec3]";
-	_TD["DPS_OnEnable"] = TDDps_[Class]_CheckTalents;
+	TDDps.Description = "TD [Class] DPS supports: [Spec1], [Spec2], [Spec3]";
+	TDDps.OnEnable = TDDps_[Class]_CheckTalents;
 	if mode == 1 then
-		_TD["DPS_NextSpell"] = TDDps_[Class]_[Spec1];
+		TDDps.NextSpell = TDDps_[Class]_[Spec1];
 	end;
 	if mode == 2 then
-		_TD["DPS_NextSpell"] = TDDps_[Class]_[Spec2];
+		TDDps.NextSpell = TDDps_[Class]_[Spec2];
 	end;
 	if mode == 3 then
-		_TD["DPS_NextSpell"] = TDDps_[Class]_[Spec3];
+		TDDps.NextSpell = TDDps_[Class]_[Spec3];
 	end;
-	TDDps_EnableAddon();
+	TDDps:EnableAddon();
 end

 ----------------------------------------------
diff --git a/TDButtons.lua b/TDButtons.lua
index a3f77c3..3b6775b 100644
--- a/TDButtons.lua
+++ b/TDButtons.lua
@@ -1,12 +1,12 @@
-
-local TDButton_Spells = {};
-local TDButton_Flags = {};
-local TDButton_SpellsGlowing = {};
-TDButton_FramePool = {};
-TDButton_Frames = {};
-
-function TDButton_CreateOverlay(parent, id, texture, r, g, b)
-	local frame = tremove(TDButton_FramePool);
+TDButton = {};
+TDButton.Spells = {};
+TDButton.Flags = {};
+TDButton.SpellsGlowing = {};
+TDButton.FramePool = {};
+TDButton.Frames = {};
+
+function TDButton.CreateOverlay(parent, id, texture, r, g, b)
+	local frame = tremove(TDButton.FramePool);
 	if not frame then
 		frame = CreateFrame('Frame', 'TDButton_Overlay_' .. id, parent);
 	else
@@ -35,13 +35,13 @@ function TDButton_CreateOverlay(parent, id, texture, r, g, b)
 		TDDps_Options.highlightColor.a
 	);

-	tinsert(TDButton_Frames, frame);
+	tinsert(TDButton.Frames, frame);
 	return frame;
 end

-function TDButton_DestroyAllOverlays()
+function TDButton.DestroyAllOverlays()
 	local frame;
-	for key, frame in pairs(TDButton_Frames) do
+	for key, frame in pairs(TDButton.Frames) do
 		frame:GetParent().tdOverlays = nil;
 		frame:ClearAllPoints();
 		frame:Hide();
@@ -49,13 +49,13 @@ function TDButton_DestroyAllOverlays()
 		frame.width = nil;
 		frame.height = nil;
 	end
-	for key, frame in pairs(TDButton_Frames) do
-		tinsert(TDButton_FramePool, frame);
-		TDButton_Frames[key] = nil;
+	for key, frame in pairs(TDButton.Frames) do
+		tinsert(TDButton.FramePool, frame);
+		TDButton.Frames[key] = nil;
 	end
 end

-function TDButton_UpdateButtonGlow()
+function TDButton.UpdateButtonGlow()
 	local LAB;
 	local LBG;
 	local origShow;
@@ -93,7 +93,7 @@ end
 ----------------------------------------------
 -- Show Overlay on button
 ----------------------------------------------
-function TDButton_Glow(button, id, r, g, b, texture)
+function TDButton.Glow(button, id, r, g, b, texture)
 	if button.tdOverlays and button.tdOverlays[id] then
 		button.tdOverlays[id]:Show();
 	else
@@ -101,7 +101,7 @@ function TDButton_Glow(button, id, r, g, b, texture)
 			button.tdOverlays = {};
 		end

-		button.tdOverlays[id] = TDButton_CreateOverlay(button, id, texture, r, g, b);
+		button.tdOverlays[id] = TDButton.CreateOverlay(button, id, texture, r, g, b);
 		button.tdOverlays[id]:Show();
 	end
 end
@@ -109,7 +109,7 @@ end
 ----------------------------------------------
 -- Hide Overlay on button
 ----------------------------------------------
-function TDButton_HideGlow(button, id)
+function TDButton.HideGlow(button, id)
 	if button.tdOverlays and button.tdOverlays[id] then
 		button.tdOverlays[id]:Hide();
 	end
@@ -118,37 +118,46 @@ end
 ----------------------------------------------
 -- Fetch button spells
 ----------------------------------------------
-function TDButton_Fetch()
-	TDButton_GlowClear();
-	TDButton_Spells = {};
-	TDButton_Flags = {};
-	TDButton_SpellsGlowing = {};
+function TDButton.Fetch()
+	local origEna = TDDps.rotationEnabled;
+	TDDps.rotationEnabled = false;
+	TDDps.Spell = nil;
+
+	TDButton.GlowClear();
+	TDButton.Spells = {};
+	TDButton.Flags = {};
+	TDButton.SpellsGlowing = {};
 	local isBartender = IsAddOnLoaded('Bartender4');
 	local isElv = IsAddOnLoaded('ElvUI');
 	local isSv = IsAddOnLoaded('SVUI_ActionBars');

 	if (isBartender) then
-		TDButton_FetchBartender4();
+		TDButton.FetchBartender4();
 	elseif (isElv) then
-		TDButton_FetchElvUI();
+		TDButton.FetchElvUI();
 	elseif (isSv) then
-		TDButton_FetchSuperVillain();
+		TDButton.FetchSuperVillain();
 	else
-		TDButton_FetchBlizzard();
+		TDButton.FetchBlizzard();
 	end

 	-- It does not alter original button frames so it needs to be fetched too
 	if IsAddOnLoaded('ButtonForge') then
-		TDButton_FetchButtonForge();
+		TDButton.FetchButtonForge();
 	end

-	TDDps_Print(_tdInfo, 'Fetched action bars!');
+	TDDps.rotationEnabled = origEna;
+	TDDps:Print(_tdInfo, 'Fetched action bars!');
+	-- after fetching invoke spell check
+	if TDDps.rotationEnabled then
+		TDDps:InvokeNextSpell();
+	end
 end

 ----------------------------------------------
 -- Button spells on original blizzard UI
 ----------------------------------------------
-function TDButton_FetchBlizzard()
+function TDButton.FetchBlizzard()
 	local TDActionBarsBlizzard = {'Action', 'MultiBarBottomLeft', 'MultiBarBottomRight', 'MultiBarRight', 'MultiBarLeft'};
 	for _, barName in pairs(TDActionBarsBlizzard) do
 		for i = 1, 12 do
@@ -164,11 +173,11 @@ function TDButton_FetchBlizzard()
 					actionName = GetSpellInfo(id);
 				end
 				if actionName then
-					if TDButton_Spells[actionName] == nil then
-						TDButton_Spells[actionName] = {};
+					if TDButton.Spells[actionName] == nil then
+						TDButton.Spells[actionName] = {};
 					end

-					tinsert(TDButton_Spells[actionName], button);
+					tinsert(TDButton.Spells[actionName], button);
 				end
 			end
 		end
@@ -178,7 +187,7 @@ end
 ----------------------------------------------
 -- Button spells on original button forge
 ----------------------------------------------
-function TDButton_FetchButtonForge()
+function TDButton.FetchButtonForge()
 	local i = 1;
 	while true do
 		local button = _G['ButtonForge' .. i];
@@ -203,11 +212,11 @@ function TDButton_FetchButtonForge()
 				actionName = GetSpellInfo(actionType);
 			end
 			if actionName then
-				if TDButton_Spells[actionName] == nil then
-					TDButton_Spells[actionName] = {};
+				if TDButton.Spells[actionName] == nil then
+					TDButton.Spells[actionName] = {};
 				end

-				tinsert(TDButton_Spells[actionName], button);
+				tinsert(TDButton.Spells[actionName], button);
 			end
 		end
 	end
@@ -216,16 +225,8 @@ end
 ----------------------------------------------
 -- Button spells on ElvUI
 ----------------------------------------------
-function TDButton_FetchElvUI()
+function TDButton.FetchElvUI()
 	local ret = false;
---	local slotID = rememberedActionSlot[spellName];
---	local bonusOffset = ((NUM_ACTIONBAR_PAGES + GetBonusBarOffset() - 1) * NUM_ACTIONBAR_BUTTONS);
---	slotID = slotID - bonusOffset;
---	local bar = math.floor(slotID / 10) + 1;
---	local btn = slotID % 10;
---
---	local button = _G['ElvUI_Bar' .. bar .. 'Button' .. btn];
-
 	for x = 1, 10 do
 		for i = 1, 12 do
 			local button = _G['ElvUI_Bar' .. x .. 'Button' .. i];
@@ -234,11 +235,11 @@ function TDButton_FetchElvUI()
 				if spellId then
 					local actionName, _ = GetSpellInfo(spellId);
 					if actionName then
-						if TDButton_Spells[actionName] == nil then
-							TDButton_Spells[actionName] = {};
+						if TDButton.Spells[actionName] == nil then
+							TDButton.Spells[actionName] = {};
 						end
 						ret = true;
-						tinsert(TDButton_Spells[actionName], button);
+						tinsert(TDButton.Spells[actionName], button);
 					end
 				end
 			end
@@ -250,7 +251,7 @@ end
 ----------------------------------------------
 -- Button spells on SuperVillain
 ----------------------------------------------
-function TDButton_FetchSuperVillain()
+function TDButton.FetchSuperVillain()
 	local ret = false;
 	for x = 1, 10 do
 		for i = 1, 12 do
@@ -260,11 +261,11 @@ function TDButton_FetchSuperVillain()
 				if spellId then
 					local actionName, _ = GetSpellInfo(spellId);
 					if actionName then
-						if TDButton_Spells[actionName] == nil then
-							TDButton_Spells[actionName] = {};
+						if TDButton.Spells[actionName] == nil then
+							TDButton.Spells[actionName] = {};
 						end
 						ret = true;
-						tinsert(TDButton_Spells[actionName], button);
+						tinsert(TDButton.Spells[actionName], button);
 					end
 				end
 			end
@@ -276,7 +277,7 @@ end
 ----------------------------------------------
 -- Button spells on Bartender4
 ----------------------------------------------
-function TDButton_FetchBartender4()
+function TDButton.FetchBartender4()
 	local ret = false;
 	for i = 1, 120 do
 		local button = _G['BT4Button' .. i];
@@ -285,11 +286,11 @@ function TDButton_FetchBartender4()
 			if spellId then
 				local actionName, _ = GetSpellInfo(spellId);
 				if actionName then
-					if TDButton_Spells[actionName] == nil then
-						TDButton_Spells[actionName] = {};
+					if TDButton.Spells[actionName] == nil then
+						TDButton.Spells[actionName] = {};
 					end
 					ret = true;
-					tinsert(TDButton_Spells[actionName], button);
+					tinsert(TDButton.Spells[actionName], button);
 				end
 			end
 		end
@@ -300,20 +301,30 @@ end
 ----------------------------------------------
 -- Dump spells for debug
 ----------------------------------------------
-function TDButton_Dump()
-	for k, button in pairs(TDButton_Spells) do
-		print(k, button);
+function TDButton.Dump()
+	local s = '';
+	for k, v in pairs(TDButton.Spells) do
+		s = s .. ', ' .. k;
 	end
+	print(s);
+end
+
+----------------------------------------------
+-- Find button on action bars
+----------------------------------------------
+function TDButton.FindSpell(spellName)
+	local name = GetSpellInfo(spellName) or spellName;
+	return TDButton.Spells[name];
 end

 ----------------------------------------------
 -- Glow independent button by spell name
 ----------------------------------------------
-function TDButton_GlowIndependent(spellName, id, r, g, b, texture)
+function TDButton.GlowIndependent(spellName, id, r, g, b, texture)
 	local name = GetSpellInfo(spellName) or spellName;
-	if TDButton_Spells[name] ~= nil then
-		for k, button in pairs(TDButton_Spells[name]) do
-			TDButton_Glow(button, id, r, g, b, texture);
+	if TDButton.Spells[name] ~= nil then
+		for k, button in pairs(TDButton.Spells[name]) do
+			TDButton.Glow(button, id, r, g, b, texture);
 		end
 	end
 end
@@ -321,11 +332,11 @@ end
 ----------------------------------------------
 -- Clear glow independent button by spell name
 ----------------------------------------------
-function TDButton_ClearGlowIndependent(spellName, id)
+function TDButton.ClearGlowIndependent(spellName, id)
 	local name = GetSpellInfo(spellName) or spellName;
-	if TDButton_Spells[name] ~= nil then
-		for k, button in pairs(TDButton_Spells[name]) do
-			TDButton_HideGlow(button, id);
+	if TDButton.Spells[name] ~= nil then
+		for k, button in pairs(TDButton.Spells[name]) do
+			TDButton.HideGlow(button, id);
 		end
 	end
 end
@@ -333,69 +344,72 @@ end
 ----------------------------------------------
 -- Glow cooldown
 ----------------------------------------------
-function TDButton_GlowCooldown(spell, condition)
-	if TDButton_Flags[spell] == nil then
-		TDButton_Flags[spell] = false;
+function TDButton.GlowCooldown(spell, condition)
+	if TDButton.Flags[spell] == nil then
+		TDButton.Flags[spell] = false;
 	end
-	if condition and not TDButton_Flags[spell] then
-		TDButton_Flags[spell] = true;
-		TDButton_GlowIndependent(spell, spell, 0, 1, 0);
+	if condition and not TDButton.Flags[spell] then
+		TDButton.Flags[spell] = true;
+		TDButton.GlowIndependent(spell, spell, 0, 1, 0);
 	end
-	if not condition and TDButton_Flags[spell] then
-		TDButton_Flags[spell] = false;
-		TDButton_ClearGlowIndependent(spell, spell);
+	if not condition and TDButton.Flags[spell] then
+		TDButton.Flags[spell] = false;
+		TDButton.ClearGlowIndependent(spell, spell);
 	end
 end

+function TDButton_GlowCooldown(spell, condition)
+	TDButton.GlowCooldown(spell, condition);
+end
 ----------------------------------------------
 -- Glow spell by name
 ----------------------------------------------
-function TDButton_GlowSpell(spellName)
-	if TDButton_Spells[spellName] ~= nil then
-		for k, button in pairs(TDButton_Spells[spellName]) do
-			TDButton_Glow(button, 'next');
+function TDButton.GlowSpell(spellName)
+	if TDButton.Spells[spellName] ~= nil then
+		for k, button in pairs(TDButton.Spells[spellName]) do
+			TDButton.Glow(button, 'next');
 		end
-		TDButton_SpellsGlowing[spellName] = 1;
+		TDButton.SpellsGlowing[spellName] = 1;
 	else
-		TDDps_Print(_tdError, 'Spell not found on action bars: ' .. spellName);
+		TDDps:Print(_tdError, 'Spell not found on action bars: ' .. spellName);
 	end
 end

 ----------------------------------------------
 -- Glow spell by id
 ----------------------------------------------
-function TDButton_GlowSpellId(spellId)
+function TDButton.GlowSpellId(spellId)
 	local name = GetSpellInfo(spellId);
-	TDButton_GlowSpell(name);
+	TDButton.GlowSpell(name);
 end

 ----------------------------------------------
 -- Glow next spell by name
 ----------------------------------------------
-function TDButton_GlowNextSpell(spellName)
-	TDButton_GlowClear();
-	TDButton_GlowSpell(spellName);
+function TDButton.GlowNextSpell(spellName)
+	TDButton.GlowClear();
+	TDButton.GlowSpell(spellName);
 end

 ----------------------------------------------
 -- Glow next spell by id
 ----------------------------------------------
-function TDButton_GlowNextSpellId(spellId)
+function TDButton.GlowNextSpellId(spellId)
 	local spellName = GetSpellInfo(spellId);
-	TDButton_GlowClear();
-	TDButton_GlowSpell(spellName);
+	TDButton.GlowClear();
+	TDButton.GlowSpell(spellName);
 end

 ----------------------------------------------
 -- Clear next spell glows
 ----------------------------------------------
-function TDButton_GlowClear()
-	for spellName, v in pairs(TDButton_SpellsGlowing) do
+function TDButton.GlowClear()
+	for spellName, v in pairs(TDButton.SpellsGlowing) do
 		if v == 1 then
-			for k, button in pairs(TDButton_Spells[spellName]) do
-				TDButton_HideGlow(button, 'next');
+			for k, button in pairs(TDButton.Spells[spellName]) do
+				TDButton.HideGlow(button, 'next');
 			end
-			TDButton_SpellsGlowing[spellName] = 0;
+			TDButton.SpellsGlowing[spellName] = 0;
 		end
 	end
 end
\ No newline at end of file
diff --git a/TDDps.lua b/TDDps.lua
index 6a9ed0a..dda2cef 100644
--- a/TDDps.lua
+++ b/TDDps.lua
@@ -1,25 +1,17 @@
-_TD = _TD or {};
+_TD = _TD or {}; -- depreciated

-_TD['DPS_Enabled'] 	= 0;
-_TD['DPS_OnEnable'] = nil;
-_TD['DPS_NextSpell'] = nil;
-_TD['DPS_Description'] = '';
-_TD['DPS_Mode'] = 1;
+local timer = LibStub:GetLibrary("BigLibTimer6"):Register(timer);

-DPS_Skill = nil;
+local TDDps = CreateFrame('Frame', 'TDDps');
+TDDps.AddonEnabled = false;
+TDDps.rotationEnabled = false;
+TDDps.ModuleOnEnable = nil;
+TDDps.NextSpell = nil;
+TDDps.Spell = nil;
+TDDps.Description = nil;
+TDDps.Time = 0;

--- Name and colors
-TDDpsName = 'TDDPS';
-_tdInfo = '|cFF1394CC';
-_tdError = '|cFFF0563D';
-_tdSuccess = '|cFFBCCF02';
-
-local _DPS_time = 0;
--- Globals for time to die
-TDDps_TargetGuid = nil;
-TD_Hp0, TD_T0, TD_Hpm, TD_Tm = nil, nil, nil, nil;
-
-local Classes = {
+TDDps.Classes = {
 	[1] = 'Warrior',
 	[2] = 'Paladin',
 	[3] = 'Hunter',
@@ -33,11 +25,19 @@ local Classes = {
 	[11] = 'Druid',
 	[12] = 'DemonHunter',
 }
-local TDDps_Frame = CreateFrame('Frame', 'TDDps_Frame');
-TDDps_Frame.rotationEnabled = false;

-function TDDps_Print(color, message)
-	if TDDps_Options.disabledInfo then
+-- Name and colors
+TDDpsName = 'TDDPS';
+_tdInfo = '|cFF1394CC';
+_tdError = '|cFFF0563D';
+_tdSuccess = '|cFFBCCF02';
+
+-- Globals for time to die
+TDDps_TargetGuid = nil;
+TD_Hp0, TD_T0, TD_Hpm, TD_Tm = nil, nil, nil, nil;
+
+function TDDps:Print(color, message, force)
+	if (TDDps_Options.disabledInfo and not TDDps_Options.debugMode) or force then
 		return;
 	end

@@ -47,91 +47,111 @@ end
 ----------------------------------------------
 -- Disable dps addon functionality
 ----------------------------------------------
-function TDDps_DisableAddon()
-	if _TD['DPS_Enabled'] == 0 then
+function TDDps:DisableAddon()
+	if not TDDps.AddonEnabled then
 		return;
 	end
-	TDButton_DestroyAllOverlays();
-	TDDps_Print(_tdInfo, 'Disabling');
-	TDDps_Frame:SetScript('OnUpdate', nil);
-	DPS_Skill = nil;
-	TDDps_Frame.rotationEnabled = false;
-	_TD['DPS_Enabled'] = 0;
+
+	TDButton.DestroyAllOverlays();
+	TDDps:Print(_tdInfo, 'Disabling', true);
+	TDDps:SetScript('OnUpdate', nil);
+	TDDps.Spell = nil;
+	TDDps.rotationEnabled = false;
+	TDDps.AddonEnabled = false;
 end

 ----------------------------------------------
 -- Initialize dps addon functionality
 ----------------------------------------------
-function TDDps_InitAddon()
-	TDDps_Frame:Show();
-
-	TDDps_Frame:RegisterEvent('PLAYER_TARGET_CHANGED');
-	TDDps_Frame:RegisterEvent('PLAYER_TALENT_UPDATE');
-	TDDps_Frame:RegisterEvent('ACTIONBAR_SLOT_CHANGED');
-	TDDps_Frame:RegisterEvent('PLAYER_REGEN_DISABLED');
-	TDDps_Frame:RegisterEvent('PLAYER_ENTERING_WORLD');
---	TDDps_Frame:RegisterEvent('PLAYER_REGEN_ENABLED');
-
-	TDDps_Frame:SetScript('OnEvent', TDDps_OnEvent);
-
-	TDDps_Print(_tdInfo, 'Initialized');
+function TDDps:InitAddon()
+	TDDps:Show();
+
+	TDDps:RegisterEvent('PLAYER_TARGET_CHANGED');
+	TDDps:RegisterEvent('PLAYER_TALENT_UPDATE');
+	TDDps:RegisterEvent('ACTIONBAR_SLOT_CHANGED');
+	TDDps:RegisterEvent('PLAYER_REGEN_DISABLED');
+	TDDps:RegisterEvent('PLAYER_ENTERING_WORLD');
+
+	TDDps:RegisterEvent('ACTIONBAR_HIDEGRID');
+	TDDps:RegisterEvent('ACTIONBAR_PAGE_CHANGED');
+	TDDps:RegisterEvent('LEARNED_SPELL_IN_TAB');
+	TDDps:RegisterEvent('CHARACTER_POINTS_CHANGED');
+	TDDps:RegisterEvent('ACTIVE_TALENT_GROUP_CHANGED');
+	TDDps:RegisterEvent('PLAYER_SPECIALIZATION_CHANGED');
+	TDDps:RegisterEvent('UPDATE_MACROS');
+	TDDps:RegisterEvent('VEHICLE_UPDATE');
+--	TDDps:RegisterEvent('PLAYER_REGEN_ENABLED');
+
+	TDDps:SetScript('OnEvent', self.OnEvent);
+
+	TDDps:Print(_tdInfo, 'Initialized');
 end

 ----------------------------------------------
 -- Enable dps addon functionality
 ----------------------------------------------
-function TDDps_EnableAddon(mode)
-	TDDps_Print(_tdInfo, 'Enabling');
+function TDDps:EnableAddon()
+	TDDps:Print(_tdInfo, 'Enabling');

-	if _TD['DPS_NextSpell'] == nil then
+	if TDDps.NextSpell == nil or TDDps.AddonEnabled then
+		TDDps:Print(_tdError, 'Failed to enable addon!', true);
 		return;
 	end
+	TDDps:Print(_tdInfo, 'Fetching');
+	TDButton.Fetch();

-	if _TD['DPS_Enabled'] == 1 then
-		return;
+	if TDDps.ModuleOnEnable then
+		TDDps.ModuleOnEnable();
 	end

-	_TD['DPS_Mode'] = mode;
-
-	TDButton_Fetch();
+	TDDps:SetScript('OnUpdate', TDDps.OnUpdate);

-	if _TD['DPS_OnEnable'] then
-		_TD['DPS_OnEnable']();
-	end
-
-	TDDps_Frame:SetScript('OnUpdate', TDDps_OnUpdate);
+	TDDps.AddonEnabled = true;
+	TDDps:Print(_tdSuccess, 'Enabled', true);
+end

-	_TD['DPS_Enabled'] = 1;
-	TDDps_Print(_tdSuccess, 'Enabled');
+function TDDps_EnableAddon()
+	-- backwards compatibility, don't load it until we say so
 end

 ----------------------------------------------
 -- Event Script, Target Change, Specializaton Change
 ----------------------------------------------
-function TDDps_InvokeNextSpell()
+function TDDps:InvokeNextSpell()
 	-- invoke spell check
-	local oldSkill = DPS_Skill;
+	local oldSkill = TDDps.Spell;

-	DPS_Skill = _TD['DPS_NextSpell']();
+	TDDps.Spell = TDDps.NextSpell();

-	if (oldSkill ~= DPS_Skill or oldSkill == nil) and DPS_Skill ~= nil then
-		TDButton_GlowNextSpellId(DPS_Skill);
+	if (oldSkill ~= TDDps.Spell or oldSkill == nil) and TDDps.Spell ~= nil then
+		TDButton.GlowNextSpellId(TDDps.Spell);
 	end
-	if DPS_Skill == nill and oldSkill ~= nil then
-		TDButton_GlowClear();
+	if TDDps.Spell == nil and oldSkill ~= nil then
+		TDButton.GlowClear();
 	end
 end

 ----------------------------------------------
 -- Event Script, Target Change, Specializaton Change
 ----------------------------------------------
-function TDDps_OnEvent(self, event)
+function TDDps.OnEvent(self, event)
 	if event == 'PLAYER_TALENT_UPDATE' then
-		TDDps_DisableAddon();
-	elseif event == 'ACTIONBAR_SLOT_CHANGED' then
-		--TDDps_DisableAddon();
+		TDDps:DisableAddon();
 	elseif event == 'PLAYER_ENTERING_WORLD' then
-		TDButton_UpdateButtonGlow();
+		TDButton.UpdateButtonGlow();
+	elseif event == 'ACTIONBAR_SLOT_CHANGED' or
+			event == 'ACTIONBAR_HIDEGRID' or
+			event == 'ACTIONBAR_PAGE_CHANGED' or
+			event == 'LEARNED_SPELL_IN_TAB' or
+			event == 'CHARACTER_POINTS_CHANGED' or
+			event == 'ACTIVE_TALENT_GROUP_CHANGED' or
+			event == 'PLAYER_SPECIALIZATION_CHANGED' or
+			event == 'UPDATE_MACROS' or
+			event == 'VEHICLE_UPDATE' then
+			if TDDps.rotationEnabled then
+				timer:SetTimer("TDButton_Fetch", 0.5, 0, TDButton.Fetch);
+			end
+		return;
 	end
 	if event == 'PLAYER_TARGET_CHANGED' then
 		TD_Hp0, TD_T0, TD_Hpm, TD_Tm = nil, nil, nil, nil;
@@ -142,58 +162,59 @@ function TDDps_OnEvent(self, event)
 			TDDps_TargetGuid = nil;
 		end
 	end
-	if TDDps_Frame.rotationEnabled then
+	if TDDps.rotationEnabled then
 		if event == 'PLAYER_TARGET_CHANGED' then
 			if (UnitIsFriend('player', 'target')) then
 				return;
 			else
-				TDDps_InvokeNextSpell();
+				TDDps:InvokeNextSpell();
 			end
 		end
 	end
-	if event == 'PLAYER_REGEN_DISABLED' and TDDps_Options.onCombatEnter and not TDDps_Frame.rotationEnabled then
-		TDDps_Print(_tdSuccess, 'Auto enable on combat!');
-		TDDps_Frame.rotationEnabled = true;
-		TDDps_LoadModule();
+	if event == 'PLAYER_REGEN_DISABLED' and TDDps_Options.onCombatEnter and not TDDps.rotationEnabled then
+		TDDps:Print(_tdSuccess, 'Auto enable on combat!');
+		TDDps.rotationEnabled = true;
+		TDDps:LoadModule();
 	end
 --	if event == 'PLAYER_REGEN_ENABLED' then
---		TDDps_Print(_tdSuccess, 'Auto disable on combat!');
---		TDDps_Frame.rotationEnabled = false;
---		TDDps_DisableAddon();
+--		TDDps:Print(_tdSuccess, 'Auto disable on combat!');
+--		TDDps.rotationEnabled = false;
+--		TDDps:DisableAddon();
 --	end
 end

 ----------------------------------------------
 -- Update script (timer)
 ----------------------------------------------
-function TDDps_OnUpdate(self, elapsed)
-	_DPS_time = _DPS_time + elapsed;
-	if _DPS_time >= TDDps_Options.interval then
-		_DPS_time = 0;
-		TDDps_InvokeNextSpell();
+function TDDps.OnUpdate(self, elapsed)
+	TDDps.Time = TDDps.Time + elapsed;
+	if TDDps.Time >= TDDps_Options.interval then
+		TDDps.Time = 0;
+		TDDps:InvokeNextSpell();
 	end
 end

 ----------------------------------------------
 -- Load appropriate addon for class
 ----------------------------------------------
-function TDDps_LoadModule()
-	TDDps_Frame.rotationEnabled = true;
+function TDDps:LoadModule()
+	TDDps.rotationEnabled = true;

+	TDDps:Print(_tdInfo, 'Loading class module');
 	local _, _, classId = UnitClass('player');
-	if Classes[classId] == nil then
-		TDDps_Print(_tdError, 'Invalid player class, please contact author of addon.');
+	if TDDps.Classes[classId] == nil then
+		TDDps:Print(_tdError, 'Invalid player class, please contact author of addon.', true);
 		return;
 	end

-	local module = 'TDDps_' .. Classes[classId];
+	local module = 'TDDps_' .. TDDps.Classes[classId];

 	if not IsAddOnLoaded(module) then
 		LoadAddOn(module);
 	end

 	if not IsAddOnLoaded(module) then
-		TDDps_Print(_tdError, 'Could not find class module.');
+		TDDps:Print(_tdError, 'Could not find class module.', true);
 		return;
 	end

@@ -202,10 +223,21 @@ function TDDps_LoadModule()

 	_G[init](mode);

-	if _TD['DPS_NextSpell'] == nil then
-		TDDps_Frame.rotationEnabled = false;
-		TDDps_Print(_tdError, 'Specialization is not supported.');
+	-- backward compatiblity
+	if _TD['DPS_NextSpell'] ~= nil then
+		TDDps:Print(_tdInfo, 'Backward compatibility mode');
+		TDDps.NextSpell = _TD['DPS_NextSpell'];
+		TDDps.ModuleOnEnable = _TD['DPS_OnEnable'];
+		TDDps.Description = _TD['DPS_Description'];
+	end
+
+	TDDps:EnableAddon();
+
+	if TDDps.NextSpell == nil then
+		TDDps.rotationEnabled = false;
+		TDDps:Print(_tdError, 'Specialization is not supported.', true);
 	end
+	TDDps:Print(_tdSuccess, 'Finished Loading class module');
 end

-TDDps_InitAddon();
\ No newline at end of file
+TDDps:InitAddon();
\ No newline at end of file
diff --git a/TDDps.toc b/TDDps.toc
index b022090..0ead109 100644
--- a/TDDps.toc
+++ b/TDDps.toc
@@ -12,6 +12,7 @@ Libs\LibSharedMedia-3.0\lib.xml
 Libs\AceGUI-3.0\AceGUI-3.0.xml
 Libs\AceGUI-3.0-SharedMediaWidgets\widget.xml
 Libs\AceConfig-3.0\AceConfig-3.0.xml
+Libs\BigLibTimer\BigLibTimer.lua

 TDSettings.lua
 TDButtons.lua
diff --git a/TDSettings.lua b/TDSettings.lua
index 667b788..6835a44 100644
--- a/TDSettings.lua
+++ b/TDSettings.lua
@@ -11,6 +11,7 @@ TDDps_textures = {
 TDDps_Options = {
 	enabled = true,
 	disabledInfo = false,
+	debugMode = false,
 	disableButtonGlow = false,
 	onCombatEnter = true,
 	texture = '',
@@ -62,6 +63,16 @@ local options = {
 			end,
 			get = function(info) return TDDps_Options.disabledInfo end
 		},
+		debugMode = {
+			name = 'Enable debug mode',
+			desc = 'Enables spammy chat messages (use this when addon does not work for you)',
+			type = 'toggle',
+			width = 'full',
+			set = function(info, val)
+				TDDps_Options.debugMode = val;
+			end,
+			get = function(info) return TDDps_Options.debugMode end
+		},
 		disableButtonGlow = {
 			name = 'Dissable blizzard button glow (experimental)',
 			desc = 'Disables original blizzard button glow',