• Dear Guest,

    You're browsing our forum as a Guest meaning you can only see a portion of the forum in read-only mode.
    To view all forum nodes and be able to create threads/posts please register or log-in with your existing account.

    TwinStar team

OmniCC refactored, programming example.

Malkaviano

New Member
Joined
Jul 9, 2015
I was trainning and learning some LUA/Addon programming.

I choose one simple and well written one, OmniCC.

As a trainning i remade the logic, if you wanna try it out, compare it to the original.

Comments and critics are welcome.

Major points:
- Refactor code;
- Functional over Imperative;
- Avoiding getting "elses" where not needed. So using a master IF and a sub IF when needed contrary to old AND clause (logic enhancement);
- DRY - removal of repetitive code;
- Reworked the math;
- Less work on render loop during cooldown (performance maybe?);
- Better support for commands, new command "current" to show some values;
- Very easy to increment the work;
- Number formating was wrong in my version, fixed;

Not fully tested, nothing new was added, yet.

Code:
--[[
	Omni Cooldown Count
		A universal cooldown count, based on Gello's spec
--]]


--[[
	Saved Variables
		These values are reloaded from saved variables if the user already has saved settings.
		So, the settings always exist
--]]


OmniCC = GetOmniCC;


--returns the formatted time, scaling to use, color, and the time until the next update is needed
local function GetFormattedTime(time)
	--day
	if (time > 86400) then
		return format( DAY_ONELETTER_ABBR, math.ceil (time / 86400) ), 0.6, OmniCC.long.r, OmniCC.long.g, OmniCC.long.b;
	--hour
	elseif (time > 3600) then
		return format( HOUR_ONELETTER_ABBR, math.ceil (time / 3600) ), 0.6, OmniCC.long.r, OmniCC.long.g, OmniCC.long.b;
	--minute
	elseif (time > 60) then
		return format( MINUTE_ONELETTER_ABBR, math.ceil (time / 60) ), 0.8, OmniCC.long.r, OmniCC.long.g, OmniCC.long.b;
	--second, more than 5 seconds left
	elseif (time > 5) then
		return math.ceil (time), 1.0, OmniCC.medium.r, OmniCC.medium.g, OmniCC.medium.b;
	end
	
	return math.ceil (time), 1.3, OmniCC.short.r, OmniCC.short.g, OmniCC.short.b;
end


--[[
	Text cooldown constructor
		Its a separate frame to prevent some rendering issues.
--]]
local function CreateCooldownCount(cooldown)
	
	cooldown.textFrame = CreateFrame("Frame", nil, cooldown:GetParent());
	cooldown.textFrame:SetAllPoints(cooldown:GetParent());
	cooldown.textFrame:SetFrameLevel(cooldown.textFrame:GetFrameLevel() + 1);
	
	cooldown.textFrame.text = cooldown.textFrame:CreateFontString(nil, "OVERLAY");
	cooldown.textFrame.text:SetPoint("CENTER", cooldown.textFrame, "CENTER", 0, 1);
	
	cooldown.textFrame:SetAlpha(cooldown:GetParent():GetAlpha());
	
	--[[
		OmniCC hides the text cooldown if the icon the button is hidden or not.
		This makes it a bit more dependent on other mods as far as their icon format goes.
		Its the only way I can think of to absolutely make sure that the text cooldown is hidden properly.
	--]]
	cooldown.textFrame.icon = 
		--standard action button icon, $parentIcon
		getglobal(cooldown:GetParent():GetName() .. "Icon") or 
		--standard item button icon,  $parentIconTexture
		getglobal(cooldown:GetParent():GetName() .. "IconTexture") or 
		--discord action button, $parent_Icon
		getglobal(cooldown:GetParent():GetName() .. "_Icon");
	
	--cooldown.textFrame:Hide();
end


--[[
	OnX functions
		It now uses a separate OnX function, but it no longer blinks.
--]]


function OmniCC_OnUpdate()
	if( this.timeToNextUpdate >= 1 ) then
		local remain = this.duration - (GetTime() - this.start);


		if( remain >= 0 and this.icon:IsVisible() ) then
			local time, scale, r, g, b = GetFormattedTime( remain );
			this.text:SetFont(OmniCC.font , OmniCC.size * scale, "OUTLINE");
			this.text:SetText( time );
			this.text:SetTextColor(r, g, b);
			this.timeToNextUpdate = 0;
		else
			this:Hide();
		end		
	end
	
	this.timeToNextUpdate = this.timeToNextUpdate + arg1;
end


--[[ 
	Function Overrides
--]]


function CooldownFrame_SetTimer(this, start, duration, enable)	
	if ( start > 0 and duration > 0 and enable > 0) then
		if( duration > OmniCC.min ) then
			if( not this.textFrame ) then
				CreateCooldownCount(this);
			end
			
			if (this.textFrame.icon) then
				this.textFrame:SetScript("OnUpdate", OmniCC_OnUpdate);
				
				this.textFrame.start = start;
				this.textFrame.duration = duration;
				
				this.textFrame.timeToNextUpdate = 1;
				this.textFrame:Show();
			end
			
		elseif( this.textFrame ) then
			this.textFrame:Hide();
		end
		
		this.start = start;
		this.duration = duration;
		this.stopping = 0;
		this:SetSequence(0);
		
		if(OmniCC.hideModel) then
			this:Hide();
		else
			this:Show();
		end
	else
		this:Hide();
		if(this.textFrame) then
			this.textFrame:Hide();
		end	
	end
end


--[[
	Slash Command Handler
--]]
SlashCmdList["OmniCCCOMMAND"] = function(msg)
	if(not msg or msg == "" or msg == "help" or msg == "?") then
		--print help messages
		ShowHelpMsg();
		
		return;
	end
	
	local args = {};
	
	local word;
	for word in string.gfind(msg, "[^%s]+") do
		table.insert(args, word );
	end


	cmd = string.lower(args[1]);
	
	--/omnicc size <size>
	if(cmd == "size") then
		local size = tonumber(args[2]);
		local msg;
		
		if(size and size > 0) then
			OmniCC.size = size;
			msg = "Size set to " .. size .. ".";
		else
			msg = "Invalid font size.";
		end
		
		PrintMessage(msg);
	--/omnicc font <font>
	elseif(cmd == "font") then
		local msg;
		if args[2] and CreateFont("OmniCCFont"):SetFont(args[2], OmniCC.size) then 
			OmniCC.font = args[2];
			msg = "Set font to " .. OmniCC.font .. ".";
		else
			msg = (args[2] or "<no value>") .. " is an invalid font.";
		end
		
		PrintMessage(msg);
	--/omnicc min <size>
	elseif(cmd == "min") then
		OmniCC.min  = tonumber(args[2]) or OmniCC.min;
		
		PrintMessage("Min set to " .. OmniCC.min);
	elseif(cmd == "hidemodel") then
		OmniCC.hideModel = 1;
		
		PrintMessage("Model is disabled.");
	elseif(cmd == "showmodel") then
		OmniCC.hideModel = nil;
		
		PrintMessage("Model is enabled.");
	elseif(cmd == "color" and args[2] and tonumber(args[3]) and tonumber(args[4]) and tonumber(args[5]) ) then
		local index = string.lower(args[2]);
		if(index == "long" or index == "short" or index == "medium") then
			OmniCC[index] = {r = tonumber(args[3]), g = tonumber(args[4]), b = tonumber(args[5])};
		end
	elseif(cmd == "reset") then
		OmniCC = DefaultOmniCC();
	elseif (cmd == "current") then
		ShowCurrent();
	end
end
SLASH_OmniCCCOMMAND1 = "/omnicc";


--[[
	Helpers
--]]


function ShowHelpMsg()
	local omnicc = DefaultOmniCC();
	
	PrintMessage("OmniCC Commands:");
	PrintMessage("/omnicc size <value> - Set font size. " .. omnicc.size .. " is the default.");
	PrintMessage("/omnicc font <value> - Set the font to use.  " .. omnicc.font .. " is the default.");
	PrintMessage("/omnicc color <duration> <red> <green> <blue> - Set the color to use for cooldowns of <duration>.  Duration can be long, medium or short.");
	PrintMessage("/omnicc min <value> - Set the minimum duration (seconds) a cooldown should be to show text.  Default value of " .. omnicc.min .. ".");
	PrintMessage("/omnicc hidemodel - Hide the cooldown model");
	PrintMessage("/omnicc showmodel - Show the cooldown model (default behavior)");
	PrintMessage("/omnicc reset - Go back to default settings.");
end


function ShowCurrent()
	PrintMessage("Current settings:");
	PrintMessage("omnicc size: " .. OmniCC.size);
	PrintMessage("omnicc font: " .. OmniCC.font);
	PrintMessage("omnicc min: " .. OmniCC.min);
end


function PrintMessage(msg)
	DEFAULT_CHAT_FRAME:AddMessage(msg);
end


function GetOmniCC()
	return OmniCC or DefaultOmniCC();
end


function DefaultOmniCC()
	return {
		min = 3, 								--minimum duration to show text
		font = STANDARD_TEXT_FONT, 				--cooldown text font; ClearFont loads before OmniCC and will be used if its there.
		size = 20,								--cooldown text size
		long = {r = 0.8, g = 0.8, b = 0.9}, 	--long duration color
		medium = {r = 1, g = 1, b = 0.4}, 		--medium duration color
		short = {r = 1, g = 0, b = 0},			--short duration color
	}
end
 
Top Bottom