view Core.lua @ 225:2e4e52a589e5

Added onQueueStart and onQueueEnd events to crafting addon registering. GnomeWorks queue frame should automatically be closed before adding items to the queue and opened afterwards to speed this process up.
author Zerotorescue
date Mon, 07 Feb 2011 15:06:41 +0100
parents c04257b42b03
children
line wrap: on
line source
-- You can access this addon's object through: LibStub("AceAddon-3.0"):GetAddon("Inventorium")
local addon = select(2, ...);
addon = LibStub("AceAddon-3.0"):NewAddon(addon, "Inventorium", "AceEvent-3.0");

local _G = _G;
local sformat, ssplit, slower, strim, smatch = _G.string.format, _G.string.split, _G.string.lower, _G.string.trim, _G.string.match;
local floor, print, pairs, tonumber = _G.floor, _G.print, _G.pairs, _G.tonumber;

--@debug@
local addonRevision = 1; -- used to update the database whenever required
--@end-debug@
--[===[@non-debug@
local addonRevision = @project-revision@;
--@end-non-debug@]===]

--  All modules must be able to retrieve our supported addons database, thus keep it a part of the addon object rather than local
addon.supportedAddons = {};
addon.supportedAddons.auctionPricing = {};
addon.supportedAddons.itemCount = {};
addon.supportedAddons.crafting = {};

addon.Locations = {
	["Bag"] = "Bag",
	["Bank"] = "Bank",
	["Guild"] = "Guild",
	["Mailbox"] = "Mailbox",
	["Merchant"] = "Merchant",
};

function addon:OnInitialize()
	-- SAVED VARIABLES
	
	local defaults = {
		global = {
			version = nil,
		},
		profile = {
			defaults = {
				-- General
				auctionPricingAddon = "Auctioneer",
				itemCountAddon = "DataStore (with guilds)",
				craftingAddon = "AdvancedTradeSkillWindow",
				trackAtCharacters = { },
				dontAlertAtCharacters = { },
				localItemData = {
					["Bag"] = true,
					["Auction House"] = true,
				},
				
				-- Minimumm stock
				minLocalStock = 20,
				alertBelowLocalMinimum = true,
				autoRefill = true,
				autoRefillSkipConfirm = false,
				minGlobalStock = 40,
				alertBelowGlobalMinimum = true,
				
				-- Queueing
				restockTarget = 40,
				minCraftingQueue = 0.05,
				bonusQueue = 0.1,
				priceThreshold = 0,
				
				-- Summary
				summaryThresholdShow = 100, -- 10.000%
				summaryHidePriceThreshold = false,
				
				-- Global (can't be overridden)
				minimapIcon = true,
				hideHelp = false,
				scanInterval = "0.1", -- string because the associated select requires it to be 
				summary = {
					speed = 20,
					width = 700,
					height = 600,
				},
				colors = {
					red = 0,
					orange = 0.3,
					yellow = 0.6,
					green = 0.95,
				},
				itemCountGuildsExcluded = { },
			},
			groups = {
				-- items = {},
				-- isVirtual = nil,
			},
		},
		factionrealm = {
			characters = {
			},
		},
	};
	
	-- Register our saved variables database
	self.db = LibStub("AceDB-3.0"):New("InventoriumDB", defaults, true);
	
	-- SLASH COMMANDS
	
	-- Disable the AddonLoader slash commands
	SLASH_INVENTORIUM1 = nil;
	SLASH_IM1 = nil;
	
	-- Register our own slash commands
	SLASH_INVENTORIUM1 = "/inventorium";
	SLASH_INVENTORIUM2 = "/im";
	SlashCmdList["INVENTORIUM"] = function(msg)
		addon:CommandHandler(msg);
	end;
	
	-- Debug command handling
	self:RegisterSlash(function(this)
		this.debugChannel = false;
		for i = 1, NUM_CHAT_WINDOWS do
			local name = GetChatWindowInfo(i);
			
			if string.upper(name) == "IMDEBUG" then
				addon:Print("A debug channel already exists, removing the old one... (" .. i .. ")");
				FCF_Close(_G["ChatFrame" .. i]);
			end
		end
		
		if not this.debugChannel then
			-- Create a new debug channel
			local chatFrame = FCF_OpenNewWindow('IMDebug');
			ChatFrame_RemoveAllMessageGroups(chatFrame);
			this.debugChannel = chatFrame;
			
			addon:Print("New debug channel created.");
		end
	end, { "d", "debug", "imdebug" });

	-- Remember this character is on this account
	local playerName = UnitName("player");
	if not self.db.factionrealm.characters[playerName] then
		self.db.factionrealm.characters[playerName] = true;
		
		-- Default to tracking on all chars, untracking is a convenience, not tracking by default would probably get multiple issue reports.
		self.db.profile.defaults.trackAtCharacters[playerName] = true;
	end
	
	self:UpdateDatabase();
end





-- Database patching after new revisions

function addon:UpdateDatabase()
	if not self.db.global.version or self.db.global.version < addonRevision then
		-- Is our database outdated? Then patch it.
		
		--[[if self.db.global.version < 1337 then
		end]]
		
		-- Remember the version of our database
		self.db.global.version = addonRevision;
	end
end

function addon:GetOptionByKey(groupName, optionName, noDefault)
	if groupName and addon.db.profile.groups[groupName] and addon.db.profile.groups[groupName][optionName] ~= nil then
		-- If this option exists within the settings of this group
		
		return addon.db.profile.groups[groupName][optionName];
	elseif groupName and addon.db.profile.groups[groupName] and addon.db.profile.groups[groupName].virtualGroup ~= "" and not noDefault then
		-- If a virtual group was selected
		
		return self:GetOptionByKey(addon.db.profile.groups[groupName].virtualGroup, optionName, noDefault);
	elseif addon.db.profile.defaults[optionName] and not noDefault then
		return addon.db.profile.defaults[optionName];
	else
		return nil;
	end
end

local autoSelectedItemCountAddon;
function addon:GetItemCountAddon(group)
	local selectedExternalAddon = self:GetOptionByKey(group, "itemCountAddon");
	
	if self.supportedAddons.itemCount[selectedExternalAddon] and self.supportedAddons.itemCount[selectedExternalAddon].IsEnabled() then
		-- Try to use the default item count addon
		
		if self.supportedAddons.itemCount[selectedExternalAddon].SetGuildState then
			self.supportedAddons.itemCount[selectedExternalAddon].SetGuildState(self.db.profile.defaults.itemCountGuildsExcluded);
		end
		
		return self.supportedAddons.itemCount[selectedExternalAddon], selectedExternalAddon;
	elseif self.supportedAddons.itemCount[autoSelectedItemCountAddon] and self.supportedAddons.itemCount[autoSelectedItemCountAddon].IsEnabled() then
		-- Use previously automatically selected addon
		
		if self.supportedAddons.itemCount[autoSelectedItemCountAddon].SetGuildState then
			self.supportedAddons.itemCount[autoSelectedItemCountAddon].SetGuildState(self.db.profile.defaults.itemCountGuildsExcluded);
		end
		
		return self.supportedAddons.itemCount[autoSelectedItemCountAddon], autoSelectedItemCountAddon;
	else
		-- Default not available, get the first one then
		
		-- We are finding the best match, quality is used to compare everything
		local altName, altValue, altQuality;
		
		for name, value in pairs(self.supportedAddons.itemCount) do
			if value.IsEnabled() then
				-- Quality is based on functionality supported; TotalCount, LocalCount & GuildSelect = 3; TotalCount & LocalCount = 2, TotalCount = 1
				local quality = ((value.GetTotalCount and value.GetCharacterCount and value.SetGuildState and 3) or (value.GetTotalCount and value.GetCharacterCount and 2) or (value.GetTotalCount and 1) or 0);
				
				if quality == 3 then
					-- Best quality means instant return
					
					-- Remember this was auto selected so we don't loop again 
					autoSelectedItemCountAddon = name;
					
					return value, name;
				elseif not altQuality or quality > altQuality then
					-- Compare quality; improvement? = overwrite
					altName = name;
					altValue = value;
					altQuality = quality;
				end
			end
		end
		
		if altName and altValue and altQuality then
			-- Remember this was auto selected so we don't loop again 
			autoSelectedItemCountAddon = altName;
			
			return altValue, altName;
		end
	end
	
	return;
end

function addon:GetItemCount(itemId, group)
	itemId = tonumber(itemId);
	
	if not itemId then return; end
	
	local itemCountAddon = self:GetItemCountAddon(group);
	
	return (itemCountAddon and itemCountAddon.GetTotalCount and itemCountAddon.GetTotalCount(itemId)) or -1;
end

function addon:GetLocalItemCount(itemId, group)
	itemId = tonumber(itemId);
	
	if not itemId then return; end
	
	local itemCountAddon = self:GetItemCountAddon(group);
	
	local currentItemCount;
	
	if itemCountAddon and itemCountAddon.GetCharacterCount then
		local bag, bank, auctionHouse, mail = itemCountAddon.GetCharacterCount(itemId);
		
		local selectedLocalItemCountSources = self:GetOptionByKey(group, "localItemData");
		
		currentItemCount = 0;
		if selectedLocalItemCountSources["Bag"] then
			currentItemCount = currentItemCount + bag;
		end
		if selectedLocalItemCountSources["Bank"] then
			currentItemCount = currentItemCount + bank;
		end
		if selectedLocalItemCountSources["Auction House"] then
			currentItemCount = currentItemCount + auctionHouse;
		end
		if selectedLocalItemCountSources["Mailbox"] then
			currentItemCount = currentItemCount + mail;
		end
	end
	
	return currentItemCount or -1;
end

function addon:GetAuctionValue(itemLink, group)
	if not itemLink then return -5; end
	
	local selectedExternalAddon = self:GetOptionByKey(group, "auctionPricingAddon");
	
	if self.supportedAddons.auctionPricing[selectedExternalAddon] and self.supportedAddons.auctionPricing[selectedExternalAddon].IsEnabled() then
		-- Try to use the default auction pricing addon
		
		return self.supportedAddons.auctionPricing[selectedExternalAddon].GetValue(itemLink);
	else
		-- Default not available, get the first one then
		
		for name, value in pairs(self.supportedAddons.auctionPricing) do
			if value.IsEnabled() then
				return value.GetValue(itemLink);
			end
		end
	end
	
	return -2;
end





-- Slash commands

local slashArgs = {};
local slashError = "Wrong argument, the following arguments are available:";

function addon:CommandHandler(message)
	local cmd, arg = ssplit(" ", (message or ""), 2);
	cmd = slower(cmd);
	
	if slashArgs[cmd] then
		-- Pass a reference to the addon (to be used as "self") and the provided arg
		slashArgs[cmd](addon, arg);
	else
		addon:Print(slashError);
	end
end

function addon:RegisterSlash(func, args, description)
	for _, arg in pairs(args) do
		slashArgs[arg] = func;
	end
	
	if description then
		slashError = slashError .. "\n" .. description;
	end
end





function addon:ColorCode(num, required)
	local percentage = ( num / required );
	
	if percentage >= addon.db.profile.defaults.colors.green then
		return sformat("|cff00ff00%d|r", num);
	elseif percentage >= addon.db.profile.defaults.colors.yellow then
		return sformat("|cffffff00%d|r", num);
	elseif percentage >= addon.db.profile.defaults.colors.orange then
		return sformat("|cffff9933%d|r", num);
	elseif percentage >= addon.db.profile.defaults.colors.red then
		return sformat("|cffff0000%d|r", num);
	else
		return num;
	end
end

function addon:DisplayItemCount(value, minimumStock)
	if value == -1 then
		return "|cffffff00Unknown|r";
	elseif value == -2 then
		return "|cffffff00-|r";
	elseif value == -3 then
		return "|cffffff00Unknown|r";
	else
		return sformat("%s / %d", self:ColorCode(value, minimumStock), minimumStock);
	end
end

-- Readable money

local goldText = "%s%d|cffffd700g|r ";
local silverText = "%s%d|cffc7c7cfs|r ";
local copperText = "%s%d|cffeda55fc|r";

function addon:ReadableMoney(copper, clean)
	local text = "";
	
	local gold = floor( copper / COPPER_PER_GOLD );
	if gold > 0 then
		text = sformat(goldText, text, gold);
	end
	
	if not clean or (not gold or gold < 10) then
		local silver = floor( ( copper % COPPER_PER_GOLD ) / COPPER_PER_SILVER );
		if silver > 0 then
			text = sformat(silverText, text, silver);
		end
		
		if not clean or (not gold or gold < 1) then
			local copper = floor( copper % COPPER_PER_SILVER );
			if copper > 0 or text == "" then
				text = sformat(copperText, text, copper);
			end
		end
	end
	
	
	return strim(text);
end

function addon:ReadableMoneyToCopper(value)
	-- If a player enters a value it will be filled without color codes
	-- If it is retrieved from the database, it will be colored coded
	-- Thus we look for both
	local gold = tonumber(smatch(value, "(%d+)|c[a-fA-F0-9]+g|r") or smatch(value, "(%d+)g"));
	local silver = tonumber(smatch(value, "(%d+)|c[a-fA-F0-9]+s|r") or smatch(value, "(%d+)s"));
	local copper = tonumber(smatch(value, "(%d+)|c[a-fA-F0-9]+c|r") or smatch(value, "(%d+)c"));
		
	return ( (gold or 0) * COPPER_PER_GOLD ) + ( (silver or 0) * COPPER_PER_SILVER ) + (copper or 0);
end

function addon:ValidateReadableMoney(info, value)
	-- If a player enters a value it will be filled without color codes
	-- If it is retrieved from the database, it will be colored coded
	-- Thus we look for both
	local gold = tonumber(smatch(value, "(%d+)|c[a-fA-F0-9]+g|r") or smatch(value, "(%d+)g"));
	local silver = tonumber(smatch(value, "(%d+)|c[a-fA-F0-9]+s|r") or smatch(value, "(%d+)s"));
	local copper = tonumber(smatch(value, "(%d+)|c[a-fA-F0-9]+c|r") or smatch(value, "(%d+)c"));
	
	if not gold and not silver and not copper then
		return "The provided amount of money is invalid. Please provide the amount of money as #g#s#c, e.g. 591617g24s43c.";
	else
		return true;
	end
end





-- Public

function IMRegisterPricingAddon(name, get, enabled, onSelect)
	addon.supportedAddons.auctionPricing[name] = {
		["GetValue"] = get,
		["IsEnabled"] = enabled,
		["OnSelect"] = onSelect,
	};
end

function IMRegisterItemCountAddon(name, getTotal, getCharacter, enabled, onSelect, getGuildNames, setGuildState)
	addon.supportedAddons.itemCount[name] = {
		["GetTotalCount"] = getTotal,
		["GetCharacterCount"] = getCharacter,
		["IsEnabled"] = enabled,
		["OnSelect"] = onSelect,
		["GetGuildNames"] = getGuildNames,
		["SetGuildState"] = setGuildState,
	};
end

function IMRegisterCraftingAddon(name, queue, enabled, onSelect, onQueueStart, onQueueEnd)
	addon.supportedAddons.crafting[name] = {
		["Queue"] = queue,
		["IsEnabled"] = enabled,
		["OnSelect"] = onSelect,
		["OnQueueStart"] = onQueueStart,
		["OnQueueEnd"] = onQueueEnd,
	};
end

-- We need a global command handler for our chat-links
function InventoriumCommandHandler(msg)
	addon:CommandHandler(msg);
end





-- General

addon.Colors = {
	["Red"] = { 1, 0, 0 },
	["Orange"] = { 1, .46, .1 },
	["Green"] = { 0, 1, 0 },
	["Blue"] = { 0, 0, 1 },
	["Yellow"] = { 1, 1, 0 },
	["Cyan"] = { 0, 1, 1 },
}; -- easy to extend if more colors are needed
function addon:Print(text, color)
	local red, green, blue;
	
	if color then
		red, green, blue = color[1], color[2], color[3];
	end
	
	DEFAULT_CHAT_FRAME:AddMessage(text or "", red, green, blue, nil, 5);
end

function addon:GetItemId(itemLink)
	itemLink = itemLink and itemLink:match("|Hitem:([-0-9]+):"); -- if itemLink is nil, it won't execute the second part
	itemLink = itemLink and tonumber(itemLink);
	
	return itemLink;
end

-- Debug

local function ReadableTable(t, includeKeys, jumps)
	local tabs = "";
	for i = 1, (jumps or 0) do
		tabs = tabs .. "  ";
	end

	local temp = "{\n";

	for i, v in pairs(t) do
		if type(v) == "table" then
			if includeKeys then
				local key = (type(i) == "number" and tostring(i)) or sformat("\"%s\"", tostring(i));
				
				temp = sformat("%s%s  [%s] => %s,\n", temp, tabs, key, ReadableTable(v, includeKeys, (jumps or 0) + 1));
			else
				temp = sformat("%s%s  %s,\n", temp, tabs, ReadableTable(v, includeKeys, (jumps or 0) + 1));
			end
		else
			if includeKeys then
				local key = (type(i) == "number" and tostring(i)) or sformat("\"%s\"", tostring(i));
				local value = (type(v) == "number" and tostring(v)) or sformat("\"%s\"", tostring(v));
				
				temp = sformat("%s%s  [%s] => %s,\n", temp, tabs, key, value);
			else
				local value = (type(v) == "number" and tostring(v)) or sformat("\"%s\"", tostring(v));
				
				temp = sformat("%s%s  %s,\n", temp, tabs, value);
			end
		end
	end
	temp = temp .. tabs .. "}";

	return temp;
end

function addon:Debug(t, ...)
	if not self.debugChannel and self.debugChannel ~= false then
		-- We want to check just once, so if you add a debug channel later just do a /reload (registering an event for this is wasted resources)
		self.debugChannel = false;
		
		for i = 1, NUM_CHAT_WINDOWS do
			local name = GetChatWindowInfo(i);
			
			if name:upper() == "IMDEBUG" then
				self.debugChannel = _G["ChatFrame" .. i];
			end
		end
	end
	
	if self.debugChannel then
		if type(t) == "table" then
			t = ReadableTable(t, true);
		end
		
		self.debugChannel:AddMessage("|cffffff00Inventorium|r:" .. sformat(t, ...));
	end
end