Zerotorescue@17: local addon = select(2, ...); Zerotorescue@13: local mod = addon:NewModule("Queue", "AceEvent-3.0", "AceTimer-3.0"); Zerotorescue@13: Zerotorescue@132: local _G = _G; Zerotorescue@132: local tonumber, pairs, sformat, smatch, floor, ceil, tinsert, twipe = _G.tonumber, _G.pairs, _G.string.format, _G.string.match, _G.floor, _G.ceil, _G.table.insert, _G.table.wipe; Zerotorescue@132: Zerotorescue@132: local queue, skipped = {}, {}; Zerotorescue@132: Zerotorescue@132: -- strings are passed by reference, so it takes no additional memory if one string was used in a thousand tables compared to any other reference type Zerotorescue@132: local skipReasons = { Zerotorescue@132: ["LOW_VALUE"] = { "|cffff6633Underpriced|r", "The recorded auction value of this item is below your price threshold." }, Zerotorescue@132: ["CAPPED"] = { "|cff66ff33Fully stocked|r", "The recorded item count is above or equal to your minimum global stock setting." }, Zerotorescue@132: ["MIN_CRAFTING_QUEUE"] = { "|cffffff00Min crafting queue|r", "The amount of missing items is below or equal to your \"don't queue if I only miss\"-setting." }, Zerotorescue@132: ["NO_ITEMCOUNT_ADDON"] = { "|cffff0000No itemcount addon|r", "No compatible item count could be found." }, Zerotorescue@132: ["NOT_CRAFTABLE"] = { "|cff3d3d3dNot in profession|r", "This item is not part of this profession." }, Zerotorescue@132: }; Zerotorescue@132: Zerotorescue@132: local function OnQueueCancel() Zerotorescue@132: twipe(queue); Zerotorescue@132: twipe(skipped); Zerotorescue@132: Zerotorescue@132: InventoriumQueuer:Hide(); Zerotorescue@132: end Zerotorescue@132: Zerotorescue@132: local function OnQueueAccept() Zerotorescue@132: -- Prepare a table with all possible tradeskill craftables Zerotorescue@132: local craftables = mod:GetTradeskillCraftables(); Zerotorescue@132: Zerotorescue@132: for _, q in pairs(queue) do Zerotorescue@132: if craftables[q.itemId] then Zerotorescue@132: if mod:QueueWithAddon(craftables[q.itemId].no, ceil(q.amount / craftables[q.itemId].quantity), q.groupName) == -1 then Zerotorescue@132: addon:Print("Couldn't queue, no supported crafting addon found.", addon.Colors.Red); Zerotorescue@132: Zerotorescue@132: OnQueueCancel(); Zerotorescue@132: return; Zerotorescue@140: else Zerotorescue@140: -- Update the crafted-item count Zerotorescue@140: for groupName, values in pairs(addon.db.profile.groups) do Zerotorescue@140: if values.items and values.items[q.itemId] then Zerotorescue@140: values.items[q.itemId] = tonumber(values.items[q.itemId]) + 1; Zerotorescue@140: break; Zerotorescue@140: end Zerotorescue@140: end Zerotorescue@132: end Zerotorescue@132: else Zerotorescue@132: addon:Debug("Lost %s", IdToItemLink(q.itemId)); Zerotorescue@132: end Zerotorescue@132: end Zerotorescue@132: Zerotorescue@132: twipe(queue); Zerotorescue@132: twipe(skipped); Zerotorescue@132: Zerotorescue@132: InventoriumQueuer:Hide(); Zerotorescue@132: end Zerotorescue@132: Zerotorescue@132: local function MakeQueueWindow() Zerotorescue@132: do Zerotorescue@132: local frame = InventoriumQueuer; -- both for speed as code-consistency Zerotorescue@132: Zerotorescue@132: -- Scrolling table with a list of items to be moved Zerotorescue@132: local scrollTableWidth = ( frame.frmMeasureDummy:GetWidth() - 30 ); -- adjust width by the scrollbar size Zerotorescue@132: local headers = { Zerotorescue@132: { Zerotorescue@132: ["name"] = "Item", Zerotorescue@132: ["width"] = (scrollTableWidth * .60), Zerotorescue@132: ["defaultsort"] = "asc", Zerotorescue@132: ["comparesort"] = function(this, aRow, bRow, column) Zerotorescue@132: local aName, _, aRarity = GetItemInfo(this:GetRow(aRow).rowData.itemId); Zerotorescue@132: local bName, _, bRarity = GetItemInfo(this:GetRow(bRow).rowData.itemId); Zerotorescue@132: local template = "%d%s"; Zerotorescue@132: aName = template:format((10 - (aRarity or 10)), (aName or ""):lower()); Zerotorescue@132: bName = template:format((10 - (bRarity or 10)), (bName or ""):lower()); Zerotorescue@132: Zerotorescue@132: if this.cols[column].sort == "dsc" then Zerotorescue@132: return aName > bName; Zerotorescue@132: else Zerotorescue@132: return aName < bName; Zerotorescue@132: end Zerotorescue@132: end, Zerotorescue@132: ["sort"] = "asc", -- when the data is set, use this column so sort the default data Zerotorescue@132: ["tooltipTitle"] = (not addon.db.profile.defaults.hideHelp and "Item"), Zerotorescue@132: ["tooltip"] = (not addon.db.profile.defaults.hideHelp and "Click to sort the list by item quality then item name."), Zerotorescue@132: }, Zerotorescue@132: { Zerotorescue@132: ["name"] = "Amount", Zerotorescue@132: ["width"] = (scrollTableWidth * .20), Zerotorescue@132: ["align"] = "RIGHT", Zerotorescue@132: ["defaultsort"] = "dsc", Zerotorescue@132: ["sortnext"] = 1, Zerotorescue@132: ["tooltipTitle"] = (not addon.db.profile.defaults.hideHelp and "Amount"), Zerotorescue@132: ["tooltip"] = (not addon.db.profile.defaults.hideHelp and "Click to sort the list by the amount of items to be queued."), Zerotorescue@132: }, Zerotorescue@132: { Zerotorescue@132: ["name"] = "Extra", Zerotorescue@132: ["width"] = (scrollTableWidth * .20), Zerotorescue@132: ["align"] = "RIGHT", Zerotorescue@132: ["defaultsort"] = "dsc", Zerotorescue@132: ["sortnext"] = 1, Zerotorescue@132: ["tooltipTitle"] = (not addon.db.profile.defaults.hideHelp and "Extra"), Zerotorescue@132: ["tooltip"] = (not addon.db.profile.defaults.hideHelp and "Click to sort the list by the amount of bonus items."), Zerotorescue@132: }, Zerotorescue@132: }; Zerotorescue@132: Zerotorescue@132: local scrollTableWidth = ( InventoriumQueuerUnqueueables.frmMeasureDummy:GetWidth() - 30 ); -- adjust width by the scrollbar size Zerotorescue@132: local unqueueablesHeaders = { Zerotorescue@132: { Zerotorescue@132: ["name"] = "Item", Zerotorescue@132: ["width"] = (scrollTableWidth * .6), Zerotorescue@132: ["defaultsort"] = "asc", Zerotorescue@132: ["comparesort"] = function(this, aRow, bRow, column) Zerotorescue@132: local aName, _, aRarity = GetItemInfo(this:GetRow(aRow).rowData.itemId); Zerotorescue@132: local bName, _, bRarity = GetItemInfo(this:GetRow(bRow).rowData.itemId); Zerotorescue@132: local template = "%d%s"; Zerotorescue@132: aName = template:format((10 - (aRarity or 10)), (aName or ""):lower()); Zerotorescue@132: bName = template:format((10 - (bRarity or 10)), (bName or ""):lower()); Zerotorescue@132: Zerotorescue@132: if this.cols[column].sort == "dsc" then Zerotorescue@132: return aName > bName; Zerotorescue@132: else Zerotorescue@132: return aName < bName; Zerotorescue@132: end Zerotorescue@132: end, Zerotorescue@132: ["sort"] = "asc", -- when the data is set, use this column so sort the default data Zerotorescue@132: ["tooltipTitle"] = (not addon.db.profile.defaults.hideHelp and "Item"), Zerotorescue@132: ["tooltip"] = (not addon.db.profile.defaults.hideHelp and "Click to sort the list by item quality then item name."), Zerotorescue@132: }, Zerotorescue@132: { Zerotorescue@132: ["name"] = "Reason", Zerotorescue@132: ["width"] = (scrollTableWidth * .4), Zerotorescue@132: ["defaultsort"] = "dsc", Zerotorescue@132: ["sortnext"] = 1, Zerotorescue@132: ["tooltipTitle"] = (not addon.db.profile.defaults.hideHelp and "Reason"), Zerotorescue@132: ["tooltip"] = (not addon.db.profile.defaults.hideHelp and "Click to sort the list by the reason the items couldn't be queued."), Zerotorescue@132: }, Zerotorescue@132: }; Zerotorescue@132: Zerotorescue@132: local proceedButton = { Zerotorescue@132: text = "Queue", Zerotorescue@132: tooltipTitle = (not addon.db.profile.defaults.hideHelp and "Queue"), Zerotorescue@132: tooltip = (not addon.db.profile.defaults.hideHelp and "Add these items to the queue of your crafting addon."), Zerotorescue@132: onClick = OnQueueAccept, Zerotorescue@132: }; Zerotorescue@132: local cancelButton = { Zerotorescue@132: text = "Cancel", Zerotorescue@132: tooltipTitle = (not addon.db.profile.defaults.hideHelp and "Cancel"), Zerotorescue@132: tooltip = (not addon.db.profile.defaults.hideHelp and "Do not queue anything and close the window."), Zerotorescue@132: onClick = OnQueueCancel, Zerotorescue@132: }; Zerotorescue@132: Zerotorescue@132: 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); Zerotorescue@132: end Zerotorescue@132: end Zerotorescue@132: Zerotorescue@132: local function DisplayQueue() Zerotorescue@132: MakeQueueWindow(); Zerotorescue@132: Zerotorescue@132: -- This table is never copied, just referenced. It is the same for every row. Zerotorescue@132: local queueablesColumns = { Zerotorescue@132: { Zerotorescue@132: ["value"] = function(data, cols, realrow, column, table) Zerotorescue@132: return IdToItemLink(data[realrow].rowData.itemId); Zerotorescue@132: end, Zerotorescue@132: }, -- item Zerotorescue@132: { Zerotorescue@132: ["value"] = function(data, cols, realrow, column, table) Zerotorescue@132: return data[realrow].rowData.amount; Zerotorescue@132: end, Zerotorescue@132: }, -- amount Zerotorescue@132: { Zerotorescue@132: ["value"] = function(data, cols, realrow, column, table) Zerotorescue@132: return (data[realrow].rowData.bonus == 0 and 0) or "+" .. data[realrow].rowData.bonus; Zerotorescue@132: end, Zerotorescue@132: ["color"] = function(data, cols, realrow, column, table) Zerotorescue@132: 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 }; Zerotorescue@132: end, Zerotorescue@132: }, -- extra Zerotorescue@132: }; Zerotorescue@132: Zerotorescue@132: -- Store the list with rows in this Zerotorescue@132: local queueables = {}; Zerotorescue@132: Zerotorescue@132: for _, q in pairs(queue) do Zerotorescue@132: tinsert(queueables, { Zerotorescue@132: ["rowData"] = q, -- this is not a key usually found in a row item and ignored by the library Zerotorescue@132: ["cols"] = queueablesColumns, Zerotorescue@132: }); Zerotorescue@132: end Zerotorescue@132: Zerotorescue@132: -- This table is never copied, just referenced. It is the same for every row. Zerotorescue@132: local unqueueablesColumns = { Zerotorescue@132: { Zerotorescue@132: ["value"] = function(data, cols, realrow, column, table) Zerotorescue@132: return IdToItemLink(data[realrow].rowData.itemId); Zerotorescue@132: end, Zerotorescue@132: }, -- item Zerotorescue@132: { Zerotorescue@132: ["value"] = function(data, cols, realrow, column, table) Zerotorescue@132: return data[realrow].rowData.reason[1]; Zerotorescue@132: end, Zerotorescue@132: }, -- reason Zerotorescue@132: }; Zerotorescue@132: Zerotorescue@132: -- Store the list with rows in this Zerotorescue@132: local unqueueables = {}; Zerotorescue@132: Zerotorescue@132: for _, s in pairs(skipped) do Zerotorescue@132: tinsert(unqueueables, { Zerotorescue@132: ["rowData"] = s, -- this is not a key usually found in a row item and ignored by the library Zerotorescue@132: ["cols"] = unqueueablesColumns, Zerotorescue@132: }); Zerotorescue@132: end Zerotorescue@132: Zerotorescue@132: addon:SetQueueFrameData(queueables, unqueueables); Zerotorescue@132: end Zerotorescue@62: Zerotorescue@13: function mod:OnEnable() Zerotorescue@13: -- Register our own slash commands Zerotorescue@62: -- /im queue Zerotorescue@13: addon:RegisterSlash(function() Zerotorescue@36: mod:QueueAll(); Zerotorescue@36: 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."); Zerotorescue@14: Zerotorescue@14: self:RegisterMessage("IM_QUEUE_ALL"); Zerotorescue@14: self:RegisterMessage("IM_QUEUE_GROUP"); Zerotorescue@132: Zerotorescue@132: if not InventoriumQueuer then Zerotorescue@132: addon:CreateQueueFrame(OnMoveAccept, OnMoveCancel); Zerotorescue@132: end Zerotorescue@14: end Zerotorescue@14: Zerotorescue@14: function mod:IM_QUEUE_ALL() Zerotorescue@14: self:QueueAll(); Zerotorescue@14: end Zerotorescue@14: Zerotorescue@14: function mod:IM_QUEUE_GROUP(event, groupName) Zerotorescue@14: self:QueueGroup(groupName); Zerotorescue@13: end Zerotorescue@13: Zerotorescue@13: function mod:QueueAll() Zerotorescue@132: -- Prepare a table with all possible tradeskill craftables Zerotorescue@132: local craftables = self:GetTradeskillCraftables(); Zerotorescue@132: Zerotorescue@132: -- Forget old queue Zerotorescue@132: twipe(queue); Zerotorescue@132: twipe(skipped); Zerotorescue@132: Zerotorescue@14: local playerName = UnitName("player"); Zerotorescue@14: Zerotorescue@14: -- Go through all groups Zerotorescue@61: for groupName, values in pairs(addon.db.profile.groups) do Zerotorescue@62: local trackAt = addon:GetOptionByKey(groupName, "trackAtCharacters"); Zerotorescue@14: Zerotorescue@14: if trackAt[playerName] then Zerotorescue@132: self:QueueGroup(groupName, craftables); Zerotorescue@13: end Zerotorescue@13: end Zerotorescue@132: Zerotorescue@132: DisplayQueue(); Zerotorescue@13: end Zerotorescue@13: Zerotorescue@132: function mod:QueueGroup(groupName, craftables) Zerotorescue@132: -- Prepare a table with all possible tradeskill craftables Zerotorescue@132: if not craftables then Zerotorescue@132: craftables = self:GetTradeskillCraftables(); -- nil when no tradeskill window is open Zerotorescue@132: end Zerotorescue@132: Zerotorescue@132: if not craftables then Zerotorescue@132: addon:Print("No tradeskill window detected.", addon.Colors.Red); Zerotorescue@132: return; Zerotorescue@132: elseif not addon.db.profile.groups[groupName] then Zerotorescue@132: addon:Print(sformat("Tried to queue items from a group named \"%s\", but no such group exists.", groupName), addon.Colors.Red); Zerotorescue@132: return; Zerotorescue@132: elseif not addon.db.profile.groups[groupName].items then Zerotorescue@132: addon:Debug("This group (%s) has no items.", groupName); Zerotorescue@14: return; Zerotorescue@14: end Zerotorescue@14: Zerotorescue@132: -- Retrieve group settings Zerotorescue@132: local restockTarget = addon:GetOptionByKey(groupName, "restockTarget"); Zerotorescue@132: local bonusQueue = addon:GetOptionByKey(groupName, "bonusQueue"); Zerotorescue@132: local minCraftingQueue = floor( addon:GetOptionByKey(groupName, "minCraftingQueue") * restockTarget ); -- If the minCraftingQueue is 5% and restockTarget is 60, this will result in 3 Zerotorescue@132: local priceThreshold = addon:GetOptionByKey(groupName, "priceThreshold"); Zerotorescue@13: Zerotorescue@140: for itemId, count in pairs(addon.db.profile.groups[groupName].items) do Zerotorescue@132: if craftables[itemId] then Zerotorescue@23: local currentStock = addon:GetItemCount(itemId, groupName); Zerotorescue@31: Zerotorescue@14: if currentStock >= 0 then Zerotorescue@14: -- Current stock will be -1 when no itemcount addon was found Zerotorescue@31: Zerotorescue@31: -- Calculate the amount to be queued Zerotorescue@17: local amount = ( restockTarget - currentStock ); Zerotorescue@132: local bonus = 0; Zerotorescue@17: Zerotorescue@17: if currentStock == 0 and bonusQueue > 0 then Zerotorescue@31: -- If we have none left and the bonus queue is enabled, modify the amount to be queued Zerotorescue@31: Zerotorescue@132: bonus = floor( ( amount * ( bonusQueue ) ) + .5 ); -- round Zerotorescue@132: Zerotorescue@132: -- Update amount Zerotorescue@132: amount = (amount + bonus); Zerotorescue@17: end Zerotorescue@17: Zerotorescue@17: if amount > 0 and amount >= minCraftingQueue then Zerotorescue@136: -- If we are queuing at least one AND more than the minimum amount, then proceed Zerotorescue@14: Zerotorescue@31: -- Auction value settings Zerotorescue@92: local value = (priceThreshold ~= 0 and addon:GetAuctionValue(itemLink, groupName)); Zerotorescue@31: Zerotorescue@92: if priceThreshold == 0 or value == -1 or value >= priceThreshold then Zerotorescue@31: -- If no price threshold is set or the auction value is equal to or larger than the price threshold, then proceed Zerotorescue@31: Zerotorescue@132: self:Queue(itemId, amount, bonus, groupName); Zerotorescue@132: else Zerotorescue@132: self:Skip(itemId, skipReasons.LOW_VALUE); Zerotorescue@132: end Zerotorescue@132: else Zerotorescue@132: if amount <= 0 then Zerotorescue@132: self:Skip(itemId, skipReasons.CAPPED); Zerotorescue@132: else Zerotorescue@132: self:Skip(itemId, skipReasons.MIN_CRAFTING_QUEUE); Zerotorescue@31: end Zerotorescue@14: end Zerotorescue@14: else Zerotorescue@132: self:Skip(itemId, skipReasons.NO_ITEMCOUNT_ADDON); Zerotorescue@98: addon:Print("No usable itemcount addon found."); Zerotorescue@132: return; Zerotorescue@13: end Zerotorescue@132: else Zerotorescue@132: self:Skip(itemId, skipReasons.NOT_CRAFTABLE); Zerotorescue@13: end Zerotorescue@13: end Zerotorescue@13: end Zerotorescue@13: Zerotorescue@132: function mod:Queue(itemId, amount, bonus, groupName) Zerotorescue@132: tinsert(queue, { Zerotorescue@132: ["itemId"] = itemId, Zerotorescue@132: ["amount"] = amount, Zerotorescue@132: ["bonus"] = bonus, Zerotorescue@132: ["groupName"] = groupName, Zerotorescue@132: }); Zerotorescue@132: end Zerotorescue@132: Zerotorescue@132: function mod:Skip(itemId, reason) Zerotorescue@132: tinsert(skipped, { Zerotorescue@132: ["itemId"] = itemId, Zerotorescue@132: ["reason"] = reason, Zerotorescue@132: }); Zerotorescue@132: end Zerotorescue@132: Zerotorescue@132: function mod:QueueWithAddon(tradeSkillIndex, amount, group) Zerotorescue@132: -- Sanity check Zerotorescue@13: tradeSkillIndex = tonumber(tradeSkillIndex); Zerotorescue@13: amount = tonumber(amount); Zerotorescue@13: Zerotorescue@23: local selectedExternalAddon = addon:GetOptionByKey(group, "craftingAddon"); Zerotorescue@23: Zerotorescue@23: if addon.supportedAddons.crafting[selectedExternalAddon] and addon.supportedAddons.crafting[selectedExternalAddon].IsEnabled() then Zerotorescue@13: -- Try to use the default auction pricing addon Zerotorescue@13: Zerotorescue@23: return addon.supportedAddons.crafting[selectedExternalAddon].Queue(tradeSkillIndex, amount); Zerotorescue@13: else Zerotorescue@13: -- Default not available, get the first one then Zerotorescue@13: Zerotorescue@13: for name, value in pairs(addon.supportedAddons.crafting) do Zerotorescue@13: if value.IsEnabled() then Zerotorescue@13: return value.Queue(tradeSkillIndex, amount); Zerotorescue@13: end Zerotorescue@13: end Zerotorescue@13: end Zerotorescue@13: Zerotorescue@132: return -1; Zerotorescue@13: end Zerotorescue@132: Zerotorescue@132: -- Expand all categories Zerotorescue@132: local function ExpandSubClasses() Zerotorescue@132: for i = GetNumTradeSkills(), 1, -1 do Zerotorescue@132: local _, skillType, _, isExpanded = GetTradeSkillInfo(i); Zerotorescue@132: Zerotorescue@132: if skillType == "header" and not isExpanded then Zerotorescue@132: ExpandTradeSkillSubClass(i); Zerotorescue@132: end Zerotorescue@132: end Zerotorescue@132: end Zerotorescue@132: Zerotorescue@132: function mod:GetTradeskillCraftables() Zerotorescue@132: local craftables = {}; Zerotorescue@132: Zerotorescue@132: if GetTradeSkillLine() ~= "UNKNOWN" then Zerotorescue@132: ExpandSubClasses(); Zerotorescue@132: Zerotorescue@132: -- Cache all craftable items Zerotorescue@132: for i = 1, GetNumTradeSkills() do Zerotorescue@132: local itemLink = GetTradeSkillItemLink(i); Zerotorescue@132: Zerotorescue@132: if itemLink then Zerotorescue@132: local itemId = addon:GetItemId(itemLink); Zerotorescue@132: if not itemId then Zerotorescue@132: -- If this isn't an item, it can only be an enchant instead Zerotorescue@132: itemId = tonumber(smatch(itemLink, "|Henchant:([-0-9]+)|h")); Zerotorescue@132: Zerotorescue@132: itemId = addon.scrollIds[itemId]; -- change enchantIds into scrollIds Zerotorescue@132: end Zerotorescue@132: Zerotorescue@132: -- 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) Zerotorescue@132: local minMade, maxMade = GetTradeSkillNumMade(i); Zerotorescue@132: local average = ((minMade == maxMade) and minMade) or ((minMade + maxMade) / 2); Zerotorescue@132: Zerotorescue@132: craftables[itemId] = { Zerotorescue@132: ["no"] = i, Zerotorescue@132: ["quantity"] = average, Zerotorescue@132: }; Zerotorescue@132: end Zerotorescue@132: end Zerotorescue@132: else Zerotorescue@132: return; Zerotorescue@132: end Zerotorescue@132: Zerotorescue@132: return craftables; Zerotorescue@132: end