comparison Modules/Queue.lua @ 132:8460855e3d90

Rewrote queueing module to insert a GUI. Minor mover window changes.
author Zerotorescue
date Tue, 18 Jan 2011 00:30:15 +0100
parents 67bd5057ecb7
children 396c2960d54d
comparison
equal deleted inserted replaced
131:a27948591159 132:8460855e3d90
1 local addon = select(2, ...); 1 local addon = select(2, ...);
2 local mod = addon:NewModule("Queue", "AceEvent-3.0", "AceTimer-3.0"); 2 local mod = addon:NewModule("Queue", "AceEvent-3.0", "AceTimer-3.0");
3 3
4 local pairs = pairs; 4 local _G = _G;
5 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;
6
7 local queue, skipped = {}, {};
8
9 -- 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
10 local skipReasons = {
11 ["LOW_VALUE"] = { "|cffff6633Underpriced|r", "The recorded auction value of this item is below your price threshold." },
12 ["CAPPED"] = { "|cff66ff33Fully stocked|r", "The recorded item count is above or equal to your minimum global stock setting." },
13 ["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." },
14 ["NO_ITEMCOUNT_ADDON"] = { "|cffff0000No itemcount addon|r", "No compatible item count could be found." },
15 ["NOT_CRAFTABLE"] = { "|cff3d3d3dNot in profession|r", "This item is not part of this profession." },
16 };
17
18 local function OnQueueCancel()
19 twipe(queue);
20 twipe(skipped);
21
22 InventoriumQueuer:Hide();
23 end
24
25 local function OnQueueAccept()
26 -- Prepare a table with all possible tradeskill craftables
27 local craftables = mod:GetTradeskillCraftables();
28
29 for _, q in pairs(queue) do
30 if craftables[q.itemId] then
31 if mod:QueueWithAddon(craftables[q.itemId].no, ceil(q.amount / craftables[q.itemId].quantity), q.groupName) == -1 then
32 addon:Print("Couldn't queue, no supported crafting addon found.", addon.Colors.Red);
33
34 OnQueueCancel();
35 return;
36 end
37 else
38 addon:Debug("Lost %s", IdToItemLink(q.itemId));
39 end
40 end
41
42 twipe(queue);
43 twipe(skipped);
44
45 InventoriumQueuer:Hide();
46 end
47
48 local function MakeQueueWindow()
49 do
50 local frame = InventoriumQueuer; -- both for speed as code-consistency
51
52 -- Scrolling table with a list of items to be moved
53 local scrollTableWidth = ( frame.frmMeasureDummy:GetWidth() - 30 ); -- adjust width by the scrollbar size
54 local headers = {
55 {
56 ["name"] = "Item",
57 ["width"] = (scrollTableWidth * .60),
58 ["defaultsort"] = "asc",
59 ["comparesort"] = function(this, aRow, bRow, column)
60 local aName, _, aRarity = GetItemInfo(this:GetRow(aRow).rowData.itemId);
61 local bName, _, bRarity = GetItemInfo(this:GetRow(bRow).rowData.itemId);
62 local template = "%d%s";
63 aName = template:format((10 - (aRarity or 10)), (aName or ""):lower());
64 bName = template:format((10 - (bRarity or 10)), (bName or ""):lower());
65
66 if this.cols[column].sort == "dsc" then
67 return aName > bName;
68 else
69 return aName < bName;
70 end
71 end,
72 ["sort"] = "asc", -- when the data is set, use this column so sort the default data
73 ["tooltipTitle"] = (not addon.db.profile.defaults.hideHelp and "Item"),
74 ["tooltip"] = (not addon.db.profile.defaults.hideHelp and "Click to sort the list by item quality then item name."),
75 },
76 {
77 ["name"] = "Amount",
78 ["width"] = (scrollTableWidth * .20),
79 ["align"] = "RIGHT",
80 ["defaultsort"] = "dsc",
81 ["sortnext"] = 1,
82 ["tooltipTitle"] = (not addon.db.profile.defaults.hideHelp and "Amount"),
83 ["tooltip"] = (not addon.db.profile.defaults.hideHelp and "Click to sort the list by the amount of items to be queued."),
84 },
85 {
86 ["name"] = "Extra",
87 ["width"] = (scrollTableWidth * .20),
88 ["align"] = "RIGHT",
89 ["defaultsort"] = "dsc",
90 ["sortnext"] = 1,
91 ["tooltipTitle"] = (not addon.db.profile.defaults.hideHelp and "Extra"),
92 ["tooltip"] = (not addon.db.profile.defaults.hideHelp and "Click to sort the list by the amount of bonus items."),
93 },
94 };
95
96 local scrollTableWidth = ( InventoriumQueuerUnqueueables.frmMeasureDummy:GetWidth() - 30 ); -- adjust width by the scrollbar size
97 local unqueueablesHeaders = {
98 {
99 ["name"] = "Item",
100 ["width"] = (scrollTableWidth * .6),
101 ["defaultsort"] = "asc",
102 ["comparesort"] = function(this, aRow, bRow, column)
103 local aName, _, aRarity = GetItemInfo(this:GetRow(aRow).rowData.itemId);
104 local bName, _, bRarity = GetItemInfo(this:GetRow(bRow).rowData.itemId);
105 local template = "%d%s";
106 aName = template:format((10 - (aRarity or 10)), (aName or ""):lower());
107 bName = template:format((10 - (bRarity or 10)), (bName or ""):lower());
108
109 if this.cols[column].sort == "dsc" then
110 return aName > bName;
111 else
112 return aName < bName;
113 end
114 end,
115 ["sort"] = "asc", -- when the data is set, use this column so sort the default data
116 ["tooltipTitle"] = (not addon.db.profile.defaults.hideHelp and "Item"),
117 ["tooltip"] = (not addon.db.profile.defaults.hideHelp and "Click to sort the list by item quality then item name."),
118 },
119 {
120 ["name"] = "Reason",
121 ["width"] = (scrollTableWidth * .4),
122 ["defaultsort"] = "dsc",
123 ["sortnext"] = 1,
124 ["tooltipTitle"] = (not addon.db.profile.defaults.hideHelp and "Reason"),
125 ["tooltip"] = (not addon.db.profile.defaults.hideHelp and "Click to sort the list by the reason the items couldn't be queued."),
126 },
127 };
128
129 local proceedButton = {
130 text = "Queue",
131 tooltipTitle = (not addon.db.profile.defaults.hideHelp and "Queue"),
132 tooltip = (not addon.db.profile.defaults.hideHelp and "Add these items to the queue of your crafting addon."),
133 onClick = OnQueueAccept,
134 };
135 local cancelButton = {
136 text = "Cancel",
137 tooltipTitle = (not addon.db.profile.defaults.hideHelp and "Cancel"),
138 tooltip = (not addon.db.profile.defaults.hideHelp and "Do not queue anything and close the window."),
139 onClick = OnQueueCancel,
140 };
141
142 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);
143 end
144 end
145
146 local function DisplayQueue()
147 MakeQueueWindow();
148
149 -- This table is never copied, just referenced. It is the same for every row.
150 local queueablesColumns = {
151 {
152 ["value"] = function(data, cols, realrow, column, table)
153 return IdToItemLink(data[realrow].rowData.itemId);
154 end,
155 }, -- item
156 {
157 ["value"] = function(data, cols, realrow, column, table)
158 return data[realrow].rowData.amount;
159 end,
160 }, -- amount
161 {
162 ["value"] = function(data, cols, realrow, column, table)
163 return (data[realrow].rowData.bonus == 0 and 0) or "+" .. data[realrow].rowData.bonus;
164 end,
165 ["color"] = function(data, cols, realrow, column, table)
166 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 };
167 end,
168 }, -- extra
169 };
170
171 -- Store the list with rows in this
172 local queueables = {};
173
174 for _, q in pairs(queue) do
175 tinsert(queueables, {
176 ["rowData"] = q, -- this is not a key usually found in a row item and ignored by the library
177 ["cols"] = queueablesColumns,
178 });
179 end
180
181 -- This table is never copied, just referenced. It is the same for every row.
182 local unqueueablesColumns = {
183 {
184 ["value"] = function(data, cols, realrow, column, table)
185 return IdToItemLink(data[realrow].rowData.itemId);
186 end,
187 }, -- item
188 {
189 ["value"] = function(data, cols, realrow, column, table)
190 return data[realrow].rowData.reason[1];
191 end,
192 }, -- reason
193 };
194
195 -- Store the list with rows in this
196 local unqueueables = {};
197
198 for _, s in pairs(skipped) do
199 tinsert(unqueueables, {
200 ["rowData"] = s, -- this is not a key usually found in a row item and ignored by the library
201 ["cols"] = unqueueablesColumns,
202 });
203 end
204
205 addon:SetQueueFrameData(queueables, unqueueables);
206 end
5 207
6 function mod:OnEnable() 208 function mod:OnEnable()
7 -- Register our own slash commands 209 -- Register our own slash commands
8 -- /im queue 210 -- /im queue
9 addon:RegisterSlash(function() 211 addon:RegisterSlash(function()
10 mod:QueueAll(); 212 mod:QueueAll();
11 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."); 213 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.");
12 214
13 self:RegisterMessage("IM_QUEUE_ALL"); 215 self:RegisterMessage("IM_QUEUE_ALL");
14 self:RegisterMessage("IM_QUEUE_GROUP"); 216 self:RegisterMessage("IM_QUEUE_GROUP");
217
218 if not InventoriumQueuer then
219 addon:CreateQueueFrame(OnMoveAccept, OnMoveCancel);
220 end
15 end 221 end
16 222
17 function mod:IM_QUEUE_ALL() 223 function mod:IM_QUEUE_ALL()
18 self:QueueAll(); 224 self:QueueAll();
19 end 225 end
21 function mod:IM_QUEUE_GROUP(event, groupName) 227 function mod:IM_QUEUE_GROUP(event, groupName)
22 self:QueueGroup(groupName); 228 self:QueueGroup(groupName);
23 end 229 end
24 230
25 function mod:QueueAll() 231 function mod:QueueAll()
232 -- Prepare a table with all possible tradeskill craftables
233 local craftables = self:GetTradeskillCraftables();
234
235 -- Forget old queue
236 twipe(queue);
237 twipe(skipped);
238
26 local playerName = UnitName("player"); 239 local playerName = UnitName("player");
27 240
28 -- Go through all groups 241 -- Go through all groups
29 for groupName, values in pairs(addon.db.profile.groups) do 242 for groupName, values in pairs(addon.db.profile.groups) do
30 local trackAt = addon:GetOptionByKey(groupName, "trackAtCharacters"); 243 local trackAt = addon:GetOptionByKey(groupName, "trackAtCharacters");
31 244
32 if trackAt[playerName] then 245 if trackAt[playerName] then
33 self:QueueGroup(groupName); 246 self:QueueGroup(groupName, craftables);
34 end 247 end
35 end 248 end
36 end 249
37 250 DisplayQueue();
38 function mod:QueueGroup(groupName) 251 end
39 if not addon.db.profile.groups[groupName] then 252
40 addon:Print(("Tried to queue items from a group named \"%s\", but no such group exists."):format(groupName), addon.Colors.Red); 253 function mod:QueueGroup(groupName, craftables)
254 -- Prepare a table with all possible tradeskill craftables
255 if not craftables then
256 craftables = self:GetTradeskillCraftables(); -- nil when no tradeskill window is open
257 end
258
259 if not craftables then
260 addon:Print("No tradeskill window detected.", addon.Colors.Red);
41 return; 261 return;
42 end 262 elseif not addon.db.profile.groups[groupName] then
43 263 addon:Print(sformat("Tried to queue items from a group named \"%s\", but no such group exists.", groupName), addon.Colors.Red);
44 local temp = {}; 264 return;
45 265 elseif not addon.db.profile.groups[groupName].items then
46 local tradeskillName, currentLevel, maxLevel = GetTradeSkillLine(); 266 addon:Debug("This group (%s) has no items.", groupName);
47 267 return;
48 if tradeskillName ~= "UNKNOWN" then 268 end
49 -- Go through all trade skills for the profession 269
50 for i = 1, GetNumTradeSkills() do 270 -- Retrieve group settings
51 -- Process every single tradeskill 271 local restockTarget = addon:GetOptionByKey(groupName, "restockTarget");
52 self:ProcessTradeSkill(i, groupName, temp); 272 local bonusQueue = addon:GetOptionByKey(groupName, "bonusQueue");
53 end 273 local minCraftingQueue = floor( addon:GetOptionByKey(groupName, "minCraftingQueue") * restockTarget ); -- If the minCraftingQueue is 5% and restockTarget is 60, this will result in 3
54 end 274 local priceThreshold = addon:GetOptionByKey(groupName, "priceThreshold");
55 275
56 if addon.db.profile.groups[groupName].items then 276 for itemId in pairs(addon.db.profile.groups[groupName].items) do
57 for itemId, _ in pairs(addon.db.profile.groups[groupName].items) do 277 if craftables[itemId] then
58 if not temp[itemId] then
59 local itemLink = select(2, GetItemInfo(itemId));
60
61 addon:Print(("Couldn't queue %s (not part of this profession)"):format((itemLink or itemId or "???")), addon.Colors.Orange);
62 end
63 end
64 end
65 end
66
67 function mod:ProcessTradeSkill(i, groupName, temp)
68 -- Try to retrieve the itemlink, this will be nil if current item is a group instead
69 local itemLink = GetTradeSkillItemLink(i);
70
71 if itemLink then
72 local itemId = addon:GetItemId(itemLink);
73 if not itemId then
74 -- If this isn't an item, it can only be an enchant instead
75 itemId = tonumber(itemLink:match("|Henchant:([-0-9]+)|h"));
76
77 itemId = addon.scrollIds[itemId]; -- change enchantIds into scrollIds
78 end
79
80 if addon.db.profile.groups[groupName].items and addon.db.profile.groups[groupName].items[itemId] then
81 -- This item is in this group, queue it!
82
83 if temp then
84 -- Remember which items have been processed
85 temp[itemId] = true;
86 end
87
88 local currentStock = addon:GetItemCount(itemId, groupName); 278 local currentStock = addon:GetItemCount(itemId, groupName);
89 279
90 if currentStock >= 0 then 280 if currentStock >= 0 then
91 -- Current stock will be -1 when no itemcount addon was found 281 -- Current stock will be -1 when no itemcount addon was found
92 282
93 -- Retrieve group settings
94 local restockTarget = addon:GetOptionByKey(groupName, "restockTarget");
95 local bonusQueue = addon:GetOptionByKey(groupName, "bonusQueue");
96 local minCraftingQueue = floor( addon:GetOptionByKey(groupName, "minCraftingQueue") * restockTarget ); -- If the minCraftingQueue is 5% and restockTarget is 60, this will result in 3
97
98 -- Calculate the amount to be queued 283 -- Calculate the amount to be queued
99 local amount = ( restockTarget - currentStock ); 284 local amount = ( restockTarget - currentStock );
285 local bonus = 0;
100 286
101 if currentStock == 0 and bonusQueue > 0 then 287 if currentStock == 0 and bonusQueue > 0 then
102 -- If we have none left and the bonus queue is enabled, modify the amount to be queued 288 -- If we have none left and the bonus queue is enabled, modify the amount to be queued
103 289
104 amount = floor( ( amount * ( bonusQueue + 1 ) ) + .5 ); -- round 290 bonus = floor( ( amount * ( bonusQueue ) ) + .5 ); -- round
291
292 -- Update amount
293 amount = (amount + bonus);
105 end 294 end
106 295
107 if amount > 0 and amount >= minCraftingQueue then 296 if amount > 0 and amount >= minCraftingQueue then
108 -- If we are queueing at least one AND more than the minimum amount, then proceed 297 -- If we are queueing at least one AND more than the minimum amount, then proceed
109 298
110 -- Auction value settings 299 -- Auction value settings
111 local priceThreshold = addon:GetOptionByKey(groupName, "priceThreshold");
112 local value = (priceThreshold ~= 0 and addon:GetAuctionValue(itemLink, groupName)); 300 local value = (priceThreshold ~= 0 and addon:GetAuctionValue(itemLink, groupName));
113 301
114 if priceThreshold == 0 or value == -1 or value >= priceThreshold then 302 if priceThreshold == 0 or value == -1 or value >= priceThreshold then
115 -- If no price threshold is set or the auction value is equal to or larger than the price threshold, then proceed 303 -- If no price threshold is set or the auction value is equal to or larger than the price threshold, then proceed
116 304
117 self:Queue(i, amount, groupName); 305 self:Queue(itemId, amount, bonus, groupName);
118 306 else
119 addon:Print(("Queued %d of %s"):format(amount, itemLink)); 307 self:Skip(itemId, skipReasons.LOW_VALUE);
308 end
309 else
310 if amount <= 0 then
311 self:Skip(itemId, skipReasons.CAPPED);
312 else
313 self:Skip(itemId, skipReasons.MIN_CRAFTING_QUEUE);
120 end 314 end
121 end 315 end
122 else 316 else
317 self:Skip(itemId, skipReasons.NO_ITEMCOUNT_ADDON);
123 addon:Print("No usable itemcount addon found."); 318 addon:Print("No usable itemcount addon found.");
319 return;
124 end 320 end
125 end 321 else
126 end 322 self:Skip(itemId, skipReasons.NOT_CRAFTABLE);
127 end 323 end
128 324 end
129 function mod:Queue(tradeSkillIndex, amount, group) 325 end
326
327 function mod:Queue(itemId, amount, bonus, groupName)
328 tinsert(queue, {
329 ["itemId"] = itemId,
330 ["amount"] = amount,
331 ["bonus"] = bonus,
332 ["groupName"] = groupName,
333 });
334 end
335
336 function mod:Skip(itemId, reason)
337 tinsert(skipped, {
338 ["itemId"] = itemId,
339 ["reason"] = reason,
340 });
341 end
342
343 function mod:QueueWithAddon(tradeSkillIndex, amount, group)
344 -- Sanity check
130 tradeSkillIndex = tonumber(tradeSkillIndex); 345 tradeSkillIndex = tonumber(tradeSkillIndex);
131 amount = tonumber(amount); 346 amount = tonumber(amount);
132
133 if not tradeSkillIndex or not amount then return; end
134 347
135 local selectedExternalAddon = addon:GetOptionByKey(group, "craftingAddon"); 348 local selectedExternalAddon = addon:GetOptionByKey(group, "craftingAddon");
136 349
137 if addon.supportedAddons.crafting[selectedExternalAddon] and addon.supportedAddons.crafting[selectedExternalAddon].IsEnabled() then 350 if addon.supportedAddons.crafting[selectedExternalAddon] and addon.supportedAddons.crafting[selectedExternalAddon].IsEnabled() then
138 -- Try to use the default auction pricing addon 351 -- Try to use the default auction pricing addon
146 return value.Queue(tradeSkillIndex, amount); 359 return value.Queue(tradeSkillIndex, amount);
147 end 360 end
148 end 361 end
149 end 362 end
150 363
151 return -2; 364 return -1;
152 end 365 end
366
367 -- Expand all categories
368 local function ExpandSubClasses()
369 for i = GetNumTradeSkills(), 1, -1 do
370 local _, skillType, _, isExpanded = GetTradeSkillInfo(i);
371
372 if skillType == "header" and not isExpanded then
373 ExpandTradeSkillSubClass(i);
374 end
375 end
376 end
377
378 function mod:GetTradeskillCraftables()
379 local craftables = {};
380
381 if GetTradeSkillLine() ~= "UNKNOWN" then
382 ExpandSubClasses();
383
384 -- Cache all craftable items
385 for i = 1, GetNumTradeSkills() do
386 local itemLink = GetTradeSkillItemLink(i);
387
388 if itemLink then
389 local itemId = addon:GetItemId(itemLink);
390 if not itemId then
391 -- If this isn't an item, it can only be an enchant instead
392 itemId = tonumber(smatch(itemLink, "|Henchant:([-0-9]+)|h"));
393
394 itemId = addon.scrollIds[itemId]; -- change enchantIds into scrollIds
395 end
396
397 -- 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)
398 local minMade, maxMade = GetTradeSkillNumMade(i);
399 local average = ((minMade == maxMade) and minMade) or ((minMade + maxMade) / 2);
400
401 craftables[itemId] = {
402 ["no"] = i,
403 ["quantity"] = average,
404 };
405 end
406 end
407 else
408 return;
409 end
410
411 return craftables;
412 end