diff Modules/Queue.lua @ 144:12a8ea5af671

Added a ?remove? button to the crafting queue. When removing an item from the queue or when it is finished casting (when using the Inventorium queue processer), items are moved to the ?unqueuables? window. Fixed auction price checking. Now resetting filters before scanning the tradeskill recipes.
author Zerotorescue
date Wed, 19 Jan 2011 23:21:16 +0100
parents 8eb0f5b5a885
children 6a52272a0e5a
line wrap: on
line diff
--- a/Modules/Queue.lua	Tue Jan 18 23:48:16 2011 +0100
+++ b/Modules/Queue.lua	Wed Jan 19 23:21:16 2011 +0100
@@ -2,7 +2,7 @@
 local mod = addon:NewModule("Queue", "AceEvent-3.0", "AceTimer-3.0");
 
 local _G = _G;
-local tonumber, pairs, sformat, smatch, slower, floor, ceil, tinsert, twipe = _G.tonumber, _G.pairs, _G.string.format, _G.string.match, _G.string.lower, _G.floor, _G.ceil, _G.table.insert, _G.table.wipe;
+local tonumber, tostring, pairs, sformat, smatch, slower, floor, ceil, tinsert, twipe = _G.tonumber, _G.tostring, _G.pairs, _G.string.format, _G.string.match, _G.string.lower, _G.floor, _G.ceil, _G.table.insert, _G.table.wipe;
 
 local queue, skipped = {}, {};
 
@@ -33,122 +33,18 @@
 		"No compatible item count could be found.",
 		20,
 	},
+	["REMOVED"] = { -- because this is updated realtime, it is most useful around the top of the list
+		"|cffff0000Removed|r", -- red
+		"You manually removed this item from the queue.",
+		45,
+	},
+	["FINISHED"] = { -- because this is updated realtime, it is most useful on the top of the list
+		"|cff00ff00Just finished|r", -- green
+		"Just finished restocking this item.",
+		50,
+	},
 };
 
-local function OnQueueCancel()
-	twipe(queue);
-	twipe(skipped);
-	
-	if InventoriumQueuer then
-		InventoriumQueuer:Hide();
-	end
-end
-
-local function OnQueueAccept()
-	-- Prepare a table with all possible tradeskill craftables
-	local craftables = mod:GetTradeskillCraftables();
-	
-	for _, q in pairs(queue) do
-		if craftables[q.itemId] then
-			if mod:QueueWithAddon(craftables[q.itemId].no, ceil(q.amount / craftables[q.itemId].quantity), q.groupName) == -1 then
-				addon:Print("Couldn't queue, no supported crafting addon found.", addon.Colors.Red);
-				
-				OnQueueCancel();
-				return;
-			else
-				-- Update the crafted-item count
-				for groupName, values in pairs(addon.db.profile.groups) do
-					if values.items and values.items[q.itemId] then
-						values.items[q.itemId] = (tonumber(values.items[q.itemId]) or 0) + 1;
-						break;
-					end
-				end
-			end
-		else
-			addon:Debug("Lost %s", IdToItemLink(q.itemId));
-		end
-	end
-	
-	twipe(queue);
-	twipe(skipped);
-	
-	if InventoriumQueuer then
-		InventoriumQueuer:Hide();
-	end
-end
-
-
---[[Allowing the queue window to actually initiate crafts is going to be a likely feature.
-do
-	-- Start crafting the selected skill (or the first in line)
-	local function StartSelectedCraft()
-		local craftables = mod:GetTradeskillCraftables();
-		
-		for _, q in pairs(queue) do
-			if craftables[q.itemId] then
-				DoTradeSkill(craftables[q.itemId].no, ceil(q.amount / craftables[q.itemId].quantity));
-				return;
-			end
-		end
-	end
-	
-	-- Remove from queue and if it was selected, auto-select the next
-	local function CraftFinished()
-		
-	end
-	
-	-- Refresh the item count for this item
-	local function RefreshItem()
-		q.amount = mod:GetRestockAmount(q.itemId, q.groupName);
-		
-		if q.amount < 1 then
-			table.remove(crafts, i);
-		end
-		
-		DisplayQueue();
-	end
-	
-	local function OnSpellFinished(event, unit, spellName, _, _, spellId)
-		if unit == "player" then
-			for i, q in pairs(queue) do
-				if q.craft.spellId == spellId then
-					-- We just finished this spell
-					
-					print("pass");
-					
-					
-					return;
-				end
-			end
-		end
-	end
-	function Test() OnSpellFinished("SomeSpell", "player", "Delicate Inferno Ruby", nil, nil, 55400); end
-	
-	tinsert(queue, {
-		["itemId"] = itemId, -- needed to display the queued item in the queue window
-		["amount"] = amount, -- the amount missing
-		["bonus"] = bonus, -- the amount queued by the bonus queue
-		["craft"] = craft, -- (craftable) - needed to find the proper element of this parent array when crafting has finished (spellId), and to update the numCrafts (quantity)
-		["groupName"] = groupName, -- related group, needed to find the selected crafting addon
-	});
-	[1] = “player”;
-	[2] = “Delicate Inferno Ruby”;
-	[3] = “”;
-	[4] = 19;
-	[5] = 73336;
-	arg1 
-		Unit casting the spell - "player"
-	arg2 
-		Spell name - "“Delicate Inferno Ruby”"
-	arg3 
-		Spell rank (deprecated in 4.0)  - ""
-	arg4 
-		Spell lineID counter - 19
-	arg5 
-		Spell ID (added in 4.0) - 73336
-	
-end]]
-
 local function MakeQueueWindow()
 	if not InventoriumQueuer then
 		addon:CreateQueueFrame();
@@ -160,7 +56,7 @@
 		local headers = {
 			{
 				["name"] = "Item",
-				["width"] = (scrollTableWidth * .60),
+				["width"] = (scrollTableWidth * .65),
 				["defaultsort"] = "asc",
 				["comparesort"] = function(this, aRow, bRow, column)
 					local aName, _, aRarity = GetItemInfo(this:GetRow(aRow).rowData.itemId);
@@ -176,12 +72,12 @@
 					end
 				end,
 				["sort"] = "asc", -- when the data is set, use this column so sort the default data
-				["tooltipTitle"] = (not addon.db.profile.defaults.hideHelp and "Item"),
-				["tooltip"] = (not addon.db.profile.defaults.hideHelp and "Click to sort the list by item quality then item name."),
+				["tooltipTitle"] = "Item",
+				["tooltip"] = "Click to sort the list by item quality then item name.",
 			},
 			{
 				["name"] = "Amount",
-				["width"] = (scrollTableWidth * .20),
+				["width"] = (scrollTableWidth * .15),
 				["align"] = "RIGHT",
 				["defaultsort"] = "dsc",
 				["comparesort"] = function(this, aRow, bRow, column)
@@ -192,12 +88,12 @@
 					end
 				end,
 				["sortnext"] = 1,
-				["tooltipTitle"] = (not addon.db.profile.defaults.hideHelp and "Amount needed"),
-				["tooltip"] = (not addon.db.profile.defaults.hideHelp and "Click to sort the list by the amount of items needed to reach the restock target."),
+				["tooltipTitle"] = "Amount needed",
+				["tooltip"] = "Click to sort the list by the amount of items needed to reach the restock target.",
 			},
 			{
 				["name"] = "Extra",
-				["width"] = (scrollTableWidth * .20),
+				["width"] = (scrollTableWidth * .15),
 				["align"] = "RIGHT",
 				["defaultsort"] = "dsc",
 				["comparesort"] = function(this, aRow, bRow, column)
@@ -208,8 +104,30 @@
 					end
 				end,
 				["sortnext"] = 1,
-				["tooltipTitle"] = (not addon.db.profile.defaults.hideHelp and "Extra items"),
-				["tooltip"] = (not addon.db.profile.defaults.hideHelp and "Click to sort the list by the amount of bonus items."),
+				["tooltipTitle"] = "Extra items",
+				["tooltip"] = "Click to sort the list by the amount of bonus items.",
+			},
+			{
+				["name"] = "X",
+				["width"] = (scrollTableWidth * .05),
+				["align"] = "CENTER",
+				["sortnext"] = 1,
+				["tooltipTitle"] = "Remove",
+				["tooltip"] = "Click any of the fields in this column to remove this item from the queue.",
+				["onClick"] = function(rowData)
+					-- Remove this element from the queue
+					for index, q in pairs(queue) do
+						if q == rowData then
+							table.remove(queue, index);
+							mod:Skip(q.itemId, skipReasons.REMOVED);
+							break;
+						end
+					end
+					
+					-- Rebuild our scrolltable (records were removed and added)
+					mod:BuildQueue();
+				end,
+				["color"] = { ["r"] = 1.0, ["g"] = 0.0, ["b"] = 0.0, ["a"] = 1, },
 			},
 		};
 		
@@ -232,47 +150,45 @@
 						return aName < bName;
 					end
 				end,
-				["tooltipTitle"] = (not addon.db.profile.defaults.hideHelp and "Item"),
-				["tooltip"] = (not addon.db.profile.defaults.hideHelp and "Click to sort the list by item quality then item name."),
+				["tooltipTitle"] = "Item",
+				["tooltip"] = "Click to sort the list by item quality then item name.",
 			},
 			{
 				["name"] = "Reason",
 				["width"] = (scrollTableWidth * .4),
 				["defaultsort"] = "dsc",
 				["comparesort"] = function(this, aRow, bRow, column)
-					local result = (this:GetRow(aRow).rowData.reason[3] > this:GetRow(bRow).rowData.reason[3]);
-					
 					if this.cols[column].sort == "dsc" then
-						return result;
+						return this:GetRow(aRow).rowData.reason[3] > this:GetRow(bRow).rowData.reason[3];
 					else
-						return (not result);
+						return this:GetRow(aRow).rowData.reason[3] < this:GetRow(bRow).rowData.reason[3];
 					end
 				end,
 				["sort"] = "dsc", -- when the data is set, use this column to sort the default data
 				["sortnext"] = 1,
-				["tooltipTitle"] = (not addon.db.profile.defaults.hideHelp and "Reason"),
-				["tooltip"] = (not addon.db.profile.defaults.hideHelp and "Click to sort the list by the reason the items couldn't be queued."),
+				["tooltipTitle"] = "Reason",
+				["tooltip"] = "Click to sort the list by the reason the items couldn't be queued.",
 			},
 		};
 		
 		local proceedButton = {
 			text = "Queue",
-			tooltipTitle = (not addon.db.profile.defaults.hideHelp and "Queue"),
-			tooltip = (not addon.db.profile.defaults.hideHelp and "Add these items to the queue of your crafting addon."),
-			onClick = OnQueueAccept,
+			tooltipTitle = "Queue",
+			tooltip = "Add these items to the queue of your crafting addon.",
+			onClick = function() mod:QueueProcess(); end,
 		};
 		local cancelButton = {
 			text = "Cancel",
-			tooltipTitle = (not addon.db.profile.defaults.hideHelp and "Cancel"),
-			tooltip = (not addon.db.profile.defaults.hideHelp and "Do not queue anything and close the window."),
-			onClick = OnQueueCancel,
+			tooltipTitle = "Cancel",
+			tooltip = "Do not queue anything and close the window.",
+			onClick = function() mod:QueueAbort(); end,
 		};
 		
 		addon:SetQueueFrameSettings("Inventorium Queue", "The following items can be added to the queue of your crafting addon. Do you wish to proceed?", proceedButton, cancelButton, headers, unqueueablesHeaders);
 	end
 end
 
-local function DisplayQueue()
+function mod:BuildQueue()
 	MakeQueueWindow();
 	
 	-- This table is never copied, just referenced. It is the same for every row.
@@ -295,6 +211,9 @@
 				return ((data[realrow].rowData.bonus == 0) and { r = 1, g = 1, b = 1, a = 0.5 }) or { r = 0, g = 1, b = 0, a = 1 };
 			end,
 		}, -- extra
+		{
+			["value"] = "X",
+		},
 	};
 	
 	-- Store the list with rows in this
@@ -334,28 +253,150 @@
 	addon:SetQueueFrameData(queueables, unqueueables);
 end
 
-function mod:OnEnable()
-	-- Register our own slash commands
-	-- /im queue
-	addon:RegisterSlash(function()
-		mod:QueueAll();
-	end, { "q", "que", "queue" }, "|Hfunction:InventoriumCommandHandler:queue|h|cff00fff7/im queue|r|h (or /im q) - Queue all items found in the currently opened profession that are within the groups tracked at this current character.");
-	
-	self:RegisterMessage("IM_QUEUE_ALL");
-	self:RegisterMessage("IM_QUEUE_GROUP");
-	
-	self:RegisterEvent("TRADE_SKILL_CLOSE", OnQueueCancel);
-	
-	--self:RegisterEvent("UNIT_SPELLCAST_STOP", OnSpellFinished);
-	--self:RegisterEvent("UNIT_SPELLCAST_CHANNEL_STOP", OnSpellFinished);
+local function RefreshQueue()
+	InventoriumQueuer.scrollTable:Refresh();
 end
 
-function mod:IM_QUEUE_ALL()
-	self:QueueAll();
+do -- Crafting region
+	-- We are keeping these events within the module object to allow for easier testing and overriding
+	-- To test: LibStub("AceAddon-3.0"):GetAddon("Inventorium"):GetModule("Queue"):FUNCTION_NAME(param1, param2, ...);
+	
+	-- Start crafting the selected skill (or the first in line)
+	local currentQueueItem;
+	function mod:StartCrafting(test)
+		local frame = InventoriumQueuer; -- both for speed as code-consistency
+		
+		local selectedIndex = frame.scrollTable:GetSelection(); -- gets realrow index
+		
+		addon:Debug("%d was selected.", tostring(selectedIndex));
+		
+		if not selectedIndex then
+			-- Select the top most element (scrolltable with index of 1 will contain a index of the related realrow of the data table)
+			selectedIndex = ((frame.scrollTable.sorttable and frame.scrollTable.sorttable[1]) or 1);
+			
+			addon:Debug("%d should be the top record.", tostring(selectedIndex));
+		end
+		
+		local nextQueue = frame.scrollTable.data[selectedIndex] or frame.scrollTable.data[1]; -- if the selected index still fails, try to get the first record
+		
+		if nextQueue then
+			if not test then
+				-- Initiate spell (test will be used while debugging to fake crafts)
+				DoTradeSkill(nextQueue.craft.no, ceil(nextQueue.amount / nextQueue.craft.quantity));
+			end
+			
+			-- Remember what we're crafting (saves many loops and/or table storing)
+			currentQueueItem = nextQueue;
+			
+			return;
+		else
+			addon:Print("Nothing is available in the craft queue.", addon.Colors.Red);
+		end
+	end
+	
+	function mod:SpellCastComplete(_, unit, _, _, _, spellId)
+		-- Sadly the item isn't put in our inventory yet so we don't know how many were made.
+		-- Because of that we assume the average amount was made, this isn't really the best solution, but it's pretty-est - for now.
+		
+		if unit == "player" and currentQueueItem and spellId == currentQueueItem.craft.spellId then
+			-- Make sure the old amount is accurate, this won't be updated by the spell we just finished, that item hasn't been received yet
+			currentQueueItem.amount = mod:GetRestockAmount(currentQueueItem.itemId, currentQueueItem.groupName);
+			
+			-- Decrease amount remaining by one quantity
+			currentQueueItem.amount = ( currentQueueItem.amount - currentQueueItem.craft.quantity );
+			
+			if currentQueueItem.amount < 1 then
+				-- We finished crafting this item
+				
+				-- Remove this element from the queue
+				for index, q in pairs(queue) do
+					if q == currentQueueItem then
+						table.remove(queue, index);
+						break;
+					end
+				end
+				
+				-- Add this queue item to the "Unqueueables" frame - we finished it so it is no longer queueable and the user may become interested
+				self:Skip(currentQueueItem.itemId, skipReasons.FINISHED);
+				
+				-- We are no longer crafting anything
+				currentQueueItem = nil;
+				
+				-- Rebuild our scrolltable (records were removed and added)
+				mod:BuildQueue();
+			else
+				-- Refresh the scrolltable (update item counts)
+				RefreshQueue();
+			end
+		end
+	end
+	
+	function mod:SpellCastStart(_, unit, _, _, _, spellId)
+		if unit == "player" and currentQueueItem and spellId == currentQueueItem.craft.spellId then
+			self.isProcessing = true;
+		end
+	end
+
+	function mod:SpellCastStop(_, unit, _, _, _, spellId)
+		if unit == "player" and currentQueueItem and spellId == currentQueueItem.craft.spellId then
+			self.isProcessing = nil;
+		end
+	end
+
+	function mod:SpellCastFailed(_, unit, _, _, _, spellId)
+		if unit == "player" and currentQueueItem and spellId == currentQueueItem.craft.spellId then
+			currentQueueItem = nil;
+			self.isProcessing = nil;
+		end
+	end
+	
+	--@debug@
+	function TestCraft()
+		mod:StartCrafting(true);
+		mod:SpellCastComplete("UNIT_SPELLCAST_SUCCEEDED", "player", "Relentless Earthsiege Diamond", nil, nil, 55400);
+	end
+	--@end-debug@
 end
 
-function mod:IM_QUEUE_GROUP(event, groupName)
-	self:QueueGroup(groupName);
+function mod:QueueProcess()
+	-- Prepare a table with all possible tradeskill craftables
+	local craftables = self:GetTradeskillCraftables();
+	
+	for _, q in pairs(queue) do
+		if craftables[q.itemId] then
+			if self:QueueWithAddon(craftables[q.itemId].no, ceil(q.amount / craftables[q.itemId].quantity), q.groupName) == -1 then
+				addon:Print("Couldn't queue, no supported crafting addon found.", addon.Colors.Red);
+				
+				self:QueueAbort();
+				return;
+			else
+				-- Update the crafted-item count
+				for groupName, values in pairs(addon.db.profile.groups) do
+					if values.items and values.items[q.itemId] then
+						values.items[q.itemId] = (tonumber(values.items[q.itemId]) or 0) + 1;
+						break;
+					end
+				end
+			end
+		else
+			addon:Debug("Lost %s", IdToItemLink(q.itemId));
+		end
+	end
+	
+	self:QueueHide();
+end
+
+function mod:QueueAbort()
+	self:QueueHide();
+end
+
+function mod:QueueHide()
+	twipe(queue);
+	twipe(skipped);
+	
+	if InventoriumQueuer then
+		InventoriumQueuer:Hide();
+	end
 end
 
 function mod:QueueAll()
@@ -377,7 +418,7 @@
 		end
 	end
 	
-	DisplayQueue();
+	mod:BuildQueue();
 end
 
 function mod:QueueGroup(groupName, craftables)
@@ -409,7 +450,7 @@
 				-- If we are queuing at least one AND more than the minimum amount, then proceed
 				
 				-- Auction value settings
-				local value = (priceThreshold ~= 0 and addon:GetAuctionValue(itemLink, groupName));
+				local value = (priceThreshold ~= 0 and addon:GetAuctionValue(IdToItemLink(itemId), groupName));
 				
 				if priceThreshold == 0 or value == -1 or value >= priceThreshold then
 					-- If no price threshold is set or the auction value is equal to or larger than the price threshold, then proceed
@@ -417,6 +458,7 @@
 					self:Queue(itemId, amount, bonus, craftables[itemId], groupName);
 				else
 					self:Skip(itemId, skipReasons.LOW_VALUE);
+					--addon:Debug("%s is valued at %s while %s is needed", IdToItemLink(itemId), tostring(value), tostring(priceThreshold));
 				end
 			else
 				if not amount then
@@ -507,53 +549,104 @@
 	return -1;
 end
 
--- Expand all categories
-local function ExpandSubClasses()
-	for i = GetNumTradeSkills(), 1, -1 do
-		local _, skillType, _, isExpanded = GetTradeSkillInfo(i);
+function mod:OnEnable()
+	-- Register our own slash commands
+	-- /im queue
+	addon:RegisterSlash(function()
+		mod:QueueAll();
+	end, { "q", "que", "queue" }, "|Hfunction:InventoriumCommandHandler:queue|h|cff00fff7/im queue|r|h (or /im q) - Queue all items found in the currently opened profession that are within the groups tracked at this current character.");
+	
+	self:RegisterMessage("IM_QUEUE_ALL");
+	self:RegisterMessage("IM_QUEUE_GROUP");
+	
+	-- When closing the tradeskill window also hide the queue screen.
+	-- We scan the recipes right before queueing not when the tradeskill is opened because we really don't need it at any other time.
+	self:RegisterEvent("TRADE_SKILL_CLOSE", "QueueAbort");
+	
+	-- Crafting events
+	self:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED", "SpellCastComplete");
+	
+	-- Button en-/disabling
+	self:RegisterEvent("UNIT_SPELLCAST_START", "SpellCastStart");
+	self:RegisterEvent("UNIT_SPELLCAST_STOP", "SpellCastStop");
+	
+	self:RegisterEvent("UNIT_SPELLCAST_FAILED", "SpellCastFailed");
+	self:RegisterEvent("UNIT_SPELLCAST_INTERRUPTED", "SpellCastFailed");
+end
 
-		if skillType == "header" and not isExpanded then
-			ExpandTradeSkillSubClass(i);
-		end
+do -- Addon messages (Ace3) region
+	function mod:IM_QUEUE_ALL()
+		self:QueueAll();
+	end
+
+	function mod:IM_QUEUE_GROUP(event, groupName)
+		self:QueueGroup(groupName);
 	end
 end
 
-function mod:GetTradeskillCraftables()
-	local craftables = {};
-	
-	if GetTradeSkillLine() ~= "UNKNOWN" then
-		ExpandSubClasses();
-		
-		-- Cache all craftable items
-		for i = 1, GetNumTradeSkills() do
-			local itemLink = GetTradeSkillItemLink(i);
-			
-			if itemLink then
-				local itemId = addon:GetItemId(itemLink);
-				if not itemId then
-					-- If this isn't an item, it can only be an enchant instead
-					itemId = tonumber(smatch(itemLink, "|Henchant:([-0-9]+)|h"));
-					
-					itemId = addon.scrollIds[itemId]; -- change enchantIds into scrollIds
-				end
-				
-				-- Remember the average amount of items created per craft (doesn't need to be a round number, since we multiply this by the amount of items to be queued we're better off rounding at that time)
-				local minMade, maxMade = GetTradeSkillNumMade(i);
-				local average = ((minMade == maxMade) and minMade) or ((minMade + maxMade) / 2);
-				
-				local recipeLink = GetTradeSkillRecipeLink(i);
-				local spellId = tonumber(smatch(recipeLink, "|Henchant:([-0-9]+)|h"));
-				
-				craftables[itemId] = {
-					["no"] = i, -- needed to start crafting at the end of the entire cycle
-					["spellId"] = spellId, -- needed to detect creation of this item was finished
-					["quantity"] = average, -- needed to calculate the amount of crafts
-				};
+do -- Trade skill recipes region
+	-- Expand all categories so no crafts are hidden
+	local function ExpandSubClasses()
+		for i = GetNumTradeSkills(), 1, -1 do
+			local _, skillType, _, isExpanded = GetTradeSkillInfo(i);
+
+			if skillType == "header" and not isExpanded then
+				ExpandTradeSkillSubClass(i);
 			end
 		end
-	else
-		return;
 	end
-	
-	return craftables;
+
+	-- Reset all filters so no crafts are hidden
+	local function ResetFilters()
+		SetTradeSkillSubClassFilter(0, 1, 1);
+		SetTradeSkillItemNameFilter("");
+		SetTradeSkillItemLevelFilter(0, 0);
+		TradeSkillOnlyShowSkillUps(false);
+		TradeSkillOnlyShowMakeable(false);
+	end
+
+	-- Get all craftable items into a table. Each record contains "no", "spellId" and "quantity". The last is the average amount made per craft.
+	function mod:GetTradeskillCraftables()
+		local craftables = {};
+		
+		if GetTradeSkillLine() ~= "UNKNOWN" then
+			ExpandSubClasses();
+			ResetFilters();
+			
+			-- Cache all craftable items
+			for i = 1, GetNumTradeSkills() do
+				local itemLink = GetTradeSkillItemLink(i);
+				
+				if itemLink then
+					local itemId = addon:GetItemId(itemLink);
+					if not itemId then
+						-- If this isn't an item, it can only be an enchant instead
+						itemId = tonumber(smatch(itemLink, "|Henchant:([-0-9]+)|h"));
+						
+						if itemId and addon.scrollIds[itemId] then
+							-- Only if this scroll id actually exists
+							itemId = addon.scrollIds[itemId]; -- change enchantIds into scrollIds
+						end
+					end
+					
+					-- Remember the average amount of items created per craft (doesn't need to be a round number, since we multiply this by the amount of items to be queued we're better off rounding at that time)
+					local minMade, maxMade = GetTradeSkillNumMade(i);
+					local average = ((minMade == maxMade) and minMade) or ((minMade + maxMade) / 2);
+					
+					local recipeLink = GetTradeSkillRecipeLink(i);
+					local spellId = tonumber(smatch(recipeLink, "|Henchant:([-0-9]+)|h"));
+					
+					craftables[itemId] = {
+						["no"] = i, -- needed to start crafting at the end of the entire cycle
+						["spellId"] = spellId, -- needed to detect creation of this item was finished
+						["quantity"] = average, -- needed to calculate the amount of crafts
+					};
+				end
+			end
+		else
+			return;
+		end
+		
+		return craftables;
+	end
 end