view Core.lua @ 62:fee06221176f

Seperated the config from Core.lua. Many other code cleaning up for readability. Added local references for much used globals. Moved widgets to a different file for readability. Re-added global function for slash command handling since we do need it for our chat-hyperlinks. Fixed queueing to properly use the track at property of virtual groups. Fixed queueing to display the item id instead of the item link if the item link could not be loaded. Speed slider at the summary now has an interval of 1% down from 5% and rounds rather than ceils it?s value so 101% will become 100% rather than 105%. Now using the right stock properties at the summary. Added a help group to the config.
author Zerotorescue
date Wed, 22 Dec 2010 19:56:55 +0100
parents d903b0a151d3
children ac1189599769
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");

--@debug@
local addonRevision = 1;
--@end-debug@
--[===[@non-debug@
local addonRevision = @project-revision@;
--@end-non-debug@]===]

local _G = _G;
local print, pairs, tonumber = _G.print, _G.pairs, _G.tonumber;

--  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 = {};

function addon:OnInitialize()
	self:Debug("OnInitialize");
	
	-- SAVED VARIABLES
	
	local defaults = {
		global = {
			version = nil,
		},
		profile = {
			defaults = {
				auctionPricingAddon = "Auctioneer",
				itemCountAddon = "Altoholic",
				craftingAddon = "AdvancedTradeSkillWindow",
				minLocalStock = 20,
				alertBelowLocalMinimum = true,
				minGlobalStock = 60,
				alertBelowGlobalMinimum = true,
				summaryThresholdShow = 10,
				restockTarget = 60,
				minCraftingQueue = 0.05,
				bonusQueue = 0.1,
				priceThreshold = 0,
				summaryHidePriceThreshold = false,
				trackAtCharacters = {
				},
				localItemData = {
					["Bag"] = true,
					["Auction House"] = true,
				},
				summary = {
					speed = 5,
					width = 650,
					height = 600,
				},
				colors = {
					red = 0,
					orange = 0.3,
					yellow = 0.6,
					green = 0.95,
				},
			},
			groups = {
			},
		},
		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 name:upper() == "DEBUG" then
				this.debugChannel = _G["ChatFrame" .. i];
			
				print("A debug channel already exists, used the old one. (" .. i .. ")");
				return;
			end
		end
		
		if not this.debugChannel then
			-- Create a new debug channel
			local chatFrame = FCF_OpenNewWindow('Debug');
			ChatFrame_RemoveAllMessageGroups(chatFrame);
			this.debugChannel = chatFrame;
			
			print("New debug channel created.");
		end
	end, { "d", "debug" });

	-- 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:PremadeGroupsCheck();
end

function addon:UpdateDatabase()
	if not self.db.global.version or self.db.global.version < addonRevision then
		-- Is our database outdated? Then patch it.
		
		if not self.db.global.version then
			-- Old version was before version was saved, many changes were done in that revision
			
			print("Updating Inventorium database from version " .. (self.db.global.version or "Unknown") .. " to version " .. addonRevision .. "...");
			
			if self.db.global and self.db.global.defaults then
				print("Moving all global data into your current profile...");
				
				-- All data mustn't be global but profile-based
				self.db.profile.defaults = CopyTable(self.db.global.defaults);
				self.db.profile.groups = CopyTable(self.db.global.groups);
				
				self.db.global.defaults = nil;
				self.db.global.groups = nil;
				
				self.CommandHandler = function()
					message("You must /reload once to finalize the Inventorium database updates. This will only be required once during the BETA.");
				end;
				self:CommandHandler();
			end
			
			if self.db.profile.defaults.minimumStock then
				print("Copying the minimum stock value into the minimum global stock...");
				
				-- We added another stock option and renamed the old to be more obvious about what it means
				self.db.profile.defaults.minGlobalStock = self.db.profile.defaults.minimumStock;
				self.db.profile.defaults.minimumStock = nil;
			end
			
			if self.db.profile.defaults.minimumLocalStock then
				print("Renaming the minimum local stock property...");
				
				-- We added another stock option and then renamed it
				self.db.profile.defaults.minLocalStock = self.db.profile.defaults.minimumLocalStock;
				self.db.profile.defaults.minimumLocalStock = nil;
			end
			
			if self.db.profile.defaults.alertBelowMinimum then
				print("Copying the alert below minimum value into the alert below global minimum value...");
				
				-- We added another stock option and then renamed it
				self.db.profile.defaults.alertBelowGlobalMinimum = self.db.profile.defaults.alertBelowMinimum;
				self.db.profile.defaults.alertBelowMinimum = nil;
			end
			
			-- Go through all groups to see if there's one with the above two renamed variables
			for groupName, values in pairs(self.db.profile.groups) do
				if values.minimumStock then
					values.minGlobalStock = values.minimumStock;
					values.minimumStock = nil;
				end
			end
		end
		
		-- Remember the version of our database
		self.db.global.version = addonRevision;
	end
end

function addon:PremadeGroupsCheck(updateGroupName, updateKey, accept)
	-- Compare the current premade groups with those used, notify about changes
	if addon.defaultGroups then
		for premadeGroupName, groupInfo in pairs(addon.defaultGroups) do
			-- Go through all default groups
			
			for groupName, values in pairs(addon.db.profile.groups) do
				-- Go through all groups to find those with this premade group
				
				if values.premadeGroups and values.premadeGroups[premadeGroupName] and values.premadeGroups[premadeGroupName] < groupInfo.version then
					-- Outdated group
					
					if updateGroupName and updateKey then
						-- This function was called after pressing yes or no in a confirm box
						
						if accept then
							-- Yes was clicked
							
							for itemId, version in pairs(groupInfo.items) do
								-- Go through all items in this premade group
								
								if version > values.premadeGroups[premadeGroupName] then
									-- This item was added in a more recent version than this group: Add item
									
									if self:InGroup(itemId) then
										print(("Skipping %s (#%d) as it is already in the group |cfffed000%s|r."):format(select(2, GetItemInfo(itemId)) or "Unknown", itemId, self:InGroup(itemId)));
									elseif self:AddItemToGroup(groupName, itemId) then
										print(("|cff00ff00Added|r %s (#%d) found in the premade group |cfffed000%s|r to the group |cfffed000%s|r."):format(select(2, GetItemInfo(itemId)) or "Unknown", itemId, premadeGroupName, self:InGroup(itemId)));
									end
								elseif ( version * -1 ) > values.premadeGroups[premadeGroupName] then
									if self:InGroup(itemId) == groupName then
										print(("|cffff0000Removed|r %s (#%d) from the group |cfffed000%s|r as it was removed from the premade group |cfffed000%s|r."):format(select(2, GetItemInfo(itemId)) or "Unknown", itemId, self:InGroup(itemId), premadeGroupName));
										self:RemoveItemFromGroup(groupName, itemId);
									else
										print(("Skipping the removal of %s (#%d) as it isn't in this group."):format(select(2, GetItemInfo(itemId)) or "Unknown", itemId, self:InGroup(itemId)));
									end
								end
							end
							
							-- Remember the new version
							values.premadeGroups[premadeGroupName] = groupInfo.version;
						else
							-- No was clicked
							
							-- Let user know what was not added
							for itemId, version in pairs(groupInfo.items) do
								-- Go through all items in this premade group
								
								if version > values.premadeGroups[premadeGroupName] then
									-- This item was added in a more recent version than this group: don't add (since we clicked no), but announce it
									
									print(("Skipping %s (#%d) found in the premade group |cfffed000%s|r."):format(select(2, GetItemInfo(itemId)) or "Unknown", itemId, self:InGroup(itemId)));
								end
							end
							
							-- Remember the new version
							values.premadeGroups[premadeGroupName] = groupInfo.version;
						end
					else
						StaticPopupDialogs["InventoriumConfirmUpdatePremadeGroup"] = {
							text = "The premade group |cfffed000%s|r used in the group |cfffed000%s|r has been changed. Do you wish to copy these changes?",
							button1 = YES,
							button2 = NO,
							OnAccept = function(self)
								addon:PremadeGroupsCheck(groupName, premadeGroupName, true);
							end,
							OnCancel = function(self, _, reason)
								if reason == "clicked" then
									addon:PremadeGroupsCheck(groupName, premadeGroupName, false);
								end
							end,
							timeout = 0,
							whileDead = 1,
							hideOnEscape = 1,
						};
						StaticPopup_Show("InventoriumConfirmUpdatePremadeGroup", premadeGroupName, groupName);
						
						return;
					end
				end
			end
		end
	end
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

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

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
		
		return self.supportedAddons.itemCount[selectedExternalAddon], selectedExternalAddon;
	else
		-- Default not available, get the first one then
		
		for name, value in pairs(self.supportedAddons.itemCount) do
			if value.IsEnabled() then
				return value, name;
			end
		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(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 = string.split(" ", (message or ""), 2);
	cmd = string.lower(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
		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

-- Group functions

function addon:InGroup(itemId)
	-- Go through all groups to see if this item is already somewhere
	for groupName, values in pairs(addon.db.profile.groups) do
		if values.items and values.items[itemId] then
			return groupName;
		end
	end
	
	return;
end

function addon:AddItemToGroup(groupName, itemId)
	if self:InGroup(itemId) then
		return false;
	end
	
	if not addon.db.profile.groups[groupName].items then
		addon.db.profile.groups[groupName].items = {};
	end
	
	-- Set this item
	addon.db.profile.groups[groupName].items[itemId] = true;
	
	if AceConfigRegistry then
		-- Now rebuild the list
		AceConfigRegistry:NotifyChange("InventoriumOptions");
	end
	
	return true;
end

function addon:RemoveItemFromGroup(groupName, itemId)
	if self:InGroup(itemId) ~= groupName then
		return false;
	end
	
	-- Unset this item
	addon.db.profile.groups[groupName].items[itemId] = nil;
	
	return true;
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 = goldText:format(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 = silverText:format(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 = copperText:format(text, copper);
			end
		end
	end
	
	
	return string.trim(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(string.match(value, "(%d+)|c[a-fA-F0-9]+g|r") or string.match(value, "(%d+)g"));
	local silver = tonumber(string.match(value, "(%d+)|c[a-fA-F0-9]+s|r") or string.match(value, "(%d+)s"));
	local copper = tonumber(string.match(value, "(%d+)|c[a-fA-F0-9]+c|r") or string.match(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(string.match(value, "(%d+)|c[a-fA-F0-9]+g|r") or string.match(value, "(%d+)g"));
	local silver = tonumber(string.match(value, "(%d+)|c[a-fA-F0-9]+s|r") or string.match(value, "(%d+)s"));
	local copper = tonumber(string.match(value, "(%d+)|c[a-fA-F0-9]+c|r") or string.match(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)
	addon.supportedAddons.itemCount[name] = {
		GetTotalCount = getTotal,
		GetCharacterCount = getCharacter,
		IsEnabled = enabled,
		OnSelect = onSelect,
	};
end

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

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



-- Debug

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() == "DEBUG" then
				self.debugChannel = _G["ChatFrame" .. i];
			end
		end
	end
	
	if self.debugChannel then
		self.debugChannel:AddMessage(t);
	end
end