Asa@63
|
1 local ItemAuditor = select(2, ...)
|
Asa@124
|
2 local Crafting = ItemAuditor:NewModule("Crafting", 'AceEvent-3.0')
|
Asa@59
|
3
|
Asa@86
|
4 local Utils = ItemAuditor:GetModule("Utils")
|
Asa@86
|
5
|
Asa@59
|
6 local AceGUI = LibStub("AceGUI-3.0")
|
Asa@59
|
7 local ScrollingTable = LibStub("ScrollingTable")
|
Asa@59
|
8
|
Asa@59
|
9 local validateMoney = ItemAuditor.validateMoney
|
Asa@59
|
10 local parseMoney = ItemAuditor.parseMoney
|
Asa@59
|
11
|
Asa@59
|
12 local realData = {}
|
Asa@128
|
13 local nameMap = nil
|
Asa@59
|
14
|
Asa@128
|
15 function Crafting:OnInitialize()
|
Asa@128
|
16 self:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED")
|
Asa@159
|
17 self:RegisterEvent("UNIT_SPELLCAST_FAILED")
|
Asa@128
|
18 end
|
Asa@128
|
19
|
Asa@128
|
20 local function getQueueLocation(name)
|
Asa@159
|
21 -- this is supposed to cache, but it isn't working. its easier to just
|
Asa@159
|
22 -- disable the cache for now.
|
Asa@159
|
23 nameMap = nil
|
Asa@128
|
24 if not nameMap then
|
Asa@128
|
25 nameMap = {}
|
Asa@128
|
26 for key, data in pairs(realData) do
|
Asa@159
|
27 -- TODO: Fix the cache and remove this.
|
Asa@159
|
28 if data.skillName == name then
|
Asa@159
|
29 return key
|
Asa@159
|
30 end
|
Asa@128
|
31 nameMap[data.skillName] = key
|
Asa@128
|
32 end
|
Asa@128
|
33 end
|
Asa@128
|
34 return nameMap[name]
|
Asa@128
|
35 end
|
Asa@128
|
36
|
Asa@128
|
37 --@debug@
|
Asa@128
|
38 Crafting.getQueueLocation = getQueueLocation
|
Asa@128
|
39 function Crafting.getNameMap()
|
Asa@128
|
40 return nameMap
|
Asa@128
|
41 end
|
Asa@128
|
42
|
Asa@128
|
43 function Crafting.getRealData()
|
Asa@128
|
44 return realData
|
Asa@128
|
45 end
|
Asa@128
|
46 --@end-debug@
|
Asa@128
|
47
|
Asa@128
|
48 function Crafting:UNIT_SPELLCAST_SUCCEEDED(event, unit, spell)
|
Asa@128
|
49 if unit == "player" and getQueueLocation(spell) then
|
Asa@128
|
50 local data = realData[getQueueLocation(spell)]
|
Asa@128
|
51 data.queue = data.queue - 1
|
Asa@128
|
52 ItemAuditor:RefreshCraftingTable()
|
Asa@128
|
53 end
|
Asa@128
|
54 end
|
Asa@128
|
55
|
Asa@159
|
56 --[[
|
Asa@159
|
57 If the craft failed, probably because you're missing an item, remove it and move on.
|
Asa@159
|
58 ]]
|
Asa@159
|
59 function Crafting:UNIT_SPELLCAST_FAILED(event, unit, spell)
|
Asa@159
|
60 if unit == "player" and getQueueLocation(spell) then
|
Asa@159
|
61 local data = realData[getQueueLocation(spell)]
|
Asa@159
|
62 data.queue = 0
|
Asa@159
|
63 ItemAuditor:RefreshCraftingTable()
|
Asa@159
|
64 end
|
Asa@159
|
65 end
|
Asa@159
|
66
|
Asa@68
|
67 local queueDestinations = {}
|
Asa@70
|
68 local displayCraftingDestinations = {}
|
Asa@68
|
69 function Crafting.RegisterQueueDestination(name, destination)
|
Asa@68
|
70 queueDestinations[name] = destination
|
Asa@70
|
71 displayCraftingDestinations[name] = name
|
Asa@70
|
72 end
|
Asa@70
|
73
|
Asa@70
|
74 function Crafting.UnRegisterQueueDestination(name)
|
Asa@70
|
75 queueDestinations[name] = nil
|
Asa@70
|
76 displayCraftingDestinations[name] = nil
|
Asa@70
|
77 end
|
Asa@70
|
78
|
Asa@70
|
79 function Crafting.GetQueueDestination()
|
Asa@70
|
80 local dest = ItemAuditor.db.profile.queue_destination
|
Asa@70
|
81 if dest and queueDestinations[dest] then
|
Asa@70
|
82 return queueDestinations[dest], dest
|
Asa@70
|
83 end
|
Asa@133
|
84 -- If there is none selected or the selected option has
|
Asa@70
|
85 -- dissapeared, choose the first one in the list
|
Asa@70
|
86 for name, func in pairs(queueDestinations) do
|
Asa@70
|
87 if dest then
|
Asa@70
|
88 ItemAuditor:Print("%s is no longer available as a queue destination. %s is the new default", dest, name)
|
Asa@70
|
89 end
|
Asa@70
|
90 ItemAuditor.db.profile.queue_destination = name
|
Asa@70
|
91 return func, name
|
Asa@70
|
92 end
|
Asa@133
|
93
|
Asa@70
|
94 error('Unable to determine queue destination.')
|
Asa@68
|
95 end
|
Asa@68
|
96
|
Asa@68
|
97 function ItemAuditor:GetCraftingThreshold()
|
Asa@101
|
98 return self.db.char.profitable_threshold
|
Asa@68
|
99 end
|
Asa@68
|
100
|
Asa@67
|
101 ItemAuditor.Options.args.crafting_options = {
|
Asa@70
|
102 name = "Crafting",
|
Asa@67
|
103 type = 'group',
|
Asa@67
|
104 args = {
|
Asa@70
|
105 queue_destination = {
|
Asa@70
|
106 type = "select",
|
Asa@70
|
107 name = "Queue Destination",
|
Asa@70
|
108 desc = "Select the addon who's queue you would like ItemAuditor to post to.",
|
Asa@70
|
109 values = displayCraftingDestinations,
|
Asa@70
|
110 get = function() return select(2, Crafting.GetQueueDestination()) end,
|
Asa@70
|
111 set = function(info, value) ItemAuditor.db.profile.queue_destination = value end,
|
Asa@72
|
112 order = 1,
|
Asa@70
|
113 },
|
Asa@72
|
114 deciders = {
|
Asa@72
|
115 type="header",
|
Asa@72
|
116 name="Crafting Deciders",
|
Asa@72
|
117 order = 10,
|
Asa@72
|
118 },
|
Asa@67
|
119 },
|
Asa@67
|
120 }
|
Asa@67
|
121
|
Asa@59
|
122 local function displayMoney(rowFrame, cellFrame, data, cols, row, realrow, column, fShow, table, ...)
|
Asa@59
|
123 if fShow == true then
|
Asa@59
|
124 local money = data[realrow][column]
|
Asa@59
|
125 if money then
|
Asa@59
|
126 cellFrame.text:SetText(ItemAuditor:FormatMoney(tonumber(money)))
|
Asa@59
|
127 else
|
Asa@59
|
128 cellFrame.text:SetText("")
|
Asa@59
|
129 end
|
Asa@133
|
130
|
Asa@59
|
131 end
|
Asa@59
|
132 end
|
Asa@59
|
133
|
Asa@59
|
134 local craftingCols = {
|
Asa@59
|
135 { name= "Item", width = 200, defaultsort = "desc",
|
Asa@59
|
136 ['DoCellUpdate'] = function(rowFrame, cellFrame, data, cols, row, realrow, column, fShow, table, ...)
|
Asa@59
|
137 if fShow == true then
|
Asa@59
|
138 local data = realData[realrow]
|
Asa@59
|
139 cellFrame.text:SetText(data.link)
|
Asa@59
|
140 end
|
Asa@59
|
141 end,
|
Asa@59
|
142 },
|
Asa@133
|
143 { name= "Cost Each", width = 100, align = "RIGHT",
|
Asa@59
|
144 ['DoCellUpdate'] = displayMoney,
|
Asa@59
|
145 },
|
Asa@133
|
146 { name= "Est Sale Each", width = 100, align = "RIGHT",
|
Asa@59
|
147 ['DoCellUpdate'] = displayMoney,
|
Asa@59
|
148 },
|
Asa@99
|
149 { name= "Decided By", width = 125, align = "RIGHT",
|
Asa@133
|
150
|
Asa@59
|
151 },
|
Asa@133
|
152 { name= "craft", width = 50, align = "RIGHT",
|
Asa@133
|
153
|
Asa@59
|
154 },
|
Asa@133
|
155 { name= "Have Mats", width = 60, align = "RIGHT",
|
Asa@133
|
156
|
Asa@99
|
157 },
|
Asa@123
|
158 { name= "Profit Each", width = 100, align = "RIGHT",
|
Asa@59
|
159 ['DoCellUpdate'] = displayMoney,
|
Asa@59
|
160 },
|
Asa@59
|
161 }
|
Asa@59
|
162
|
Asa@68
|
163 function Crafting.ExportToSkillet(data)
|
Asa@68
|
164 local skillString = select(3, string.find(data.recipeLink, "^|%x+|H(.+)|h%[.+%]"))
|
Asa@68
|
165 local _, skillId = strsplit(":", skillString)
|
Asa@133
|
166
|
Asa@68
|
167 ItemAuditor:AddToQueue(skillId,tradeSkillIndex, data.queue)
|
Asa@68
|
168 end
|
Asa@68
|
169
|
Asa@68
|
170 Crafting.RegisterQueueDestination('Skillet', Crafting.ExportToSkillet)
|
Asa@68
|
171
|
Asa@68
|
172
|
Asa@68
|
173
|
Asa@68
|
174 function Crafting.Export(destination)
|
Asa@68
|
175 if type(destination) == 'function' then
|
Asa@68
|
176 -- do nothing
|
Asa@68
|
177 elseif destination == nil then
|
Asa@70
|
178 destination = Crafting.GetQueueDestination()
|
Asa@68
|
179 elseif type(destination) == 'string' then
|
Asa@68
|
180 destination = queueDestinations[destination]
|
Asa@68
|
181 else
|
Asa@68
|
182 error('destination must be a function or a string')
|
Asa@68
|
183 end
|
Asa@133
|
184
|
Asa@59
|
185 local index = 1
|
Asa@59
|
186 local data = ItemAuditor:GetCraftingRow(index)
|
Asa@59
|
187 while data do
|
Asa@68
|
188 if data.queue > 0 then
|
Asa@68
|
189 destination(data)
|
Asa@68
|
190 end
|
Asa@61
|
191 index = index + 1
|
Asa@61
|
192 data = ItemAuditor:GetCraftingRow(index)
|
Asa@133
|
193
|
Asa@59
|
194 end
|
Asa@59
|
195 end
|
Asa@59
|
196
|
Asa@74
|
197 -- ItemAuditor:GetModule('Crafting').filter_queued = false
|
Asa@99
|
198 Crafting.filter_have_mats = false
|
Asa@99
|
199 Crafting.filter_show_all = false
|
Asa@74
|
200 local function tableFilter(self, row, ...)
|
Asa@128
|
201 if Crafting.nameFilter then
|
Asa@128
|
202 return string.find(row[1], Crafting.nameFilter) ~= nil
|
Asa@128
|
203 end
|
Asa@128
|
204
|
Asa@99
|
205 if Crafting.filter_show_all then
|
Asa@99
|
206 return true
|
Asa@99
|
207 end
|
Asa@99
|
208
|
Asa@74
|
209 -- column 5 is how many should be crafted
|
Asa@126
|
210 if Crafting.filter_have_mats and row[6] == 0 then
|
Asa@99
|
211 return false
|
Asa@99
|
212 end
|
Asa@153
|
213 if (row[4] and strfind(row[4], 'VETO: .*')) or row[5] == 0 then
|
Asa@74
|
214 return false
|
Asa@74
|
215 end
|
Asa@74
|
216 return true
|
Asa@74
|
217 end
|
Asa@74
|
218
|
Asa@59
|
219 local craftingContent = false
|
Asa@59
|
220 local craftingTable = false
|
Asa@60
|
221 local btnProcess = false
|
Asa@59
|
222 local function ShowCrafting(container)
|
Asa@59
|
223 if craftingContent == false then
|
Asa@59
|
224 local window = container.frame
|
Asa@59
|
225 craftingContent = CreateFrame("Frame",nil,window)
|
Asa@133
|
226 craftingContent:SetBackdropColor(0, 0, 1, 0.5)
|
Asa@59
|
227 craftingContent:SetBackdropBorderColor(1, 0, 0, 1)
|
Asa@133
|
228
|
Asa@59
|
229 craftingContent:SetPoint("TOPLEFT", window, 10, -50)
|
Asa@59
|
230 craftingContent:SetPoint("BOTTOMRIGHT",window, -10, 10)
|
Asa@133
|
231
|
Asa@59
|
232 craftingTable = ScrollingTable:CreateST(craftingCols, 22, nil, nil, craftingContent )
|
Asa@133
|
233
|
Asa@133
|
234 IAcc = craftingContent
|
Asa@59
|
235 IAccWindow = window
|
Asa@59
|
236 craftingTable.frame:SetPoint("TOPLEFT",craftingContent, 0,0)
|
Asa@59
|
237 craftingTable.frame:SetPoint("BOTTOMRIGHT", craftingContent, 0, 30)
|
Asa@133
|
238
|
Asa@59
|
239 craftingTable:RegisterEvents({
|
Asa@59
|
240 ["OnEnter"] = function (rowFrame, cellFrame, data, cols, row, realrow, column, scrollingTable, ...)
|
Asa@59
|
241 if realrow then
|
Asa@134
|
242 local data = realData[realrow]
|
Asa@134
|
243
|
Asa@134
|
244 GameTooltip:SetOwner(rowFrame, "ANCHOR_CURSOR")
|
Asa@134
|
245 GameTooltip:SetHyperlink(data.link)
|
Asa@134
|
246 GameTooltip:Show()
|
Asa@59
|
247 end
|
Asa@59
|
248 end,
|
Asa@59
|
249 ["OnLeave"] = function (rowFrame, cellFrame, data, cols, row, realrow, column, scrollingTable, ...)
|
Asa@59
|
250 GameTooltip:Hide()
|
Asa@59
|
251 end,
|
Asa@59
|
252 });
|
Asa@133
|
253
|
Asa@99
|
254 local craftingView = CreateFrame("Button", nil, craftingContent, "UIPanelButtonTemplate")
|
Asa@99
|
255 craftingView:SetText("View")
|
Asa@99
|
256 craftingView:SetSize(50, 25)
|
Asa@99
|
257 craftingView:SetPoint("BOTTOMLEFT", craftingContent, 0, 0)
|
Asa@99
|
258
|
Asa@99
|
259 local menu = {
|
Asa@99
|
260 { text = "View", isTitle = true},
|
Asa@99
|
261 { text = "To be crafted", func = function()
|
Asa@99
|
262 Crafting.filter_have_mats = false
|
Asa@99
|
263 Crafting.filter_show_all = false
|
Asa@99
|
264 ItemAuditor:RefreshCraftingTable()
|
Asa@99
|
265 end },
|
Asa@99
|
266 { text = "Have Mats", func = function()
|
Asa@99
|
267 Crafting.filter_have_mats = true
|
Asa@99
|
268 Crafting.filter_show_all = false
|
Asa@99
|
269 ItemAuditor:RefreshCraftingTable()
|
Asa@99
|
270 end },
|
Asa@99
|
271 { text = "All", func = function()
|
Asa@99
|
272 Crafting.filter_have_mats = false
|
Asa@99
|
273 Crafting.filter_show_all = true
|
Asa@99
|
274 ItemAuditor:RefreshCraftingTable()
|
Asa@99
|
275 end },
|
Asa@99
|
276 }
|
Asa@99
|
277 local menuFrame = CreateFrame("Frame", "ExampleMenuFrame", UIParent, "UIDropDownMenuTemplate")
|
Asa@99
|
278 craftingView:SetScript("OnClick", function (self, button, down)
|
Asa@99
|
279 EasyMenu(menu, menuFrame, "cursor", 0 , 0, "MENU");
|
Asa@99
|
280 end)
|
Asa@99
|
281
|
Asa@99
|
282
|
Asa@59
|
283 btnProcess = CreateFrame("Button", nil, craftingContent, "UIPanelButtonTemplate")
|
Asa@59
|
284 btnProcess:SetText("Process")
|
Asa@133
|
285 btnProcess:SetSize(100, 25)
|
Asa@59
|
286 btnProcess:SetPoint("BOTTOMRIGHT", craftingContent, 0, 0)
|
Asa@59
|
287 btnProcess:RegisterForClicks("LeftButtonUp");
|
Asa@133
|
288
|
Asa@148
|
289 local function UpdateProcessTooltip()
|
Asa@59
|
290 local data = ItemAuditor:GetCraftingRow(1)
|
Asa@59
|
291 if data then
|
Asa@148
|
292 GameTooltip:SetOwner(btnProcess, "ANCHOR_CURSOR")
|
Asa@59
|
293 GameTooltip:SetText(format('Create %sx%s', data.link, data.queue))
|
Asa@59
|
294 GameTooltip:Show()
|
Asa@59
|
295 end
|
Asa@60
|
296 end
|
Asa@124
|
297
|
Asa@124
|
298 --[[
|
Asa@124
|
299 When enchanting UseContainerItem seems to be protected, so the enchants
|
Asa@124
|
300 have to be done one at a time.
|
Asa@124
|
301 ]]
|
Asa@156
|
302 local function useVellum(vellumID)
|
Asa@124
|
303 for bagID = 0, NUM_BAG_SLOTS do
|
Asa@124
|
304 for slotID = 0, GetContainerNumSlots(bagID) do
|
Asa@124
|
305 local link = GetContainerItemLink(bagID, slotID)
|
Asa@124
|
306 local id = Utils.GetItemID(link);
|
Asa@124
|
307 if id == vellumID then
|
Asa@124
|
308 UseContainerItem(bagID, slotID)
|
Asa@124
|
309 return
|
Asa@124
|
310 end
|
Asa@124
|
311 end
|
Asa@124
|
312 end
|
Asa@124
|
313 end
|
Asa@133
|
314
|
Asa@60
|
315 btnProcess:SetScript("OnClick", function (self, button, down)
|
Asa@60
|
316 local data = ItemAuditor:GetCraftingRow(1)
|
Asa@60
|
317 if data then
|
Asa@128
|
318 -- This will make sure the correct tradeskill window is open.
|
Asa@128
|
319 local tradeskillName = GetTradeSkillLine()
|
Asa@128
|
320 if data.tradeskillName ~= tradeskillName then
|
Asa@128
|
321 CastSpellByName(data.tradeskillName)
|
Asa@128
|
322 end
|
Asa@133
|
323
|
Asa@124
|
324 local queue = data.queue
|
Asa@124
|
325 local vellumID = nil
|
Asa@156
|
326 local altVerb = select(5, GetTradeSkillInfo(data.tradeSkillIndex))
|
Asa@156
|
327 if altVerb == 'Enchant' then
|
Asa@156
|
328 vellumID = 38682
|
Asa@124
|
329 queue = 1
|
Asa@124
|
330 end
|
Asa@124
|
331 ItemAuditor:Print('Crafting %sx%s', data.link, queue)
|
Asa@124
|
332 DoTradeSkill(data.tradeSkillIndex, queue)
|
Asa@124
|
333 if vellumID then
|
Asa@124
|
334 useVellum(vellumID)
|
Asa@124
|
335 end
|
Asa@124
|
336
|
Asa@60
|
337 UpdateProcessTooltip()
|
Asa@60
|
338 end
|
Asa@59
|
339 end)
|
Asa@59
|
340
|
Asa@60
|
341 btnProcess:SetScript("OnEnter", UpdateProcessTooltip)
|
Asa@60
|
342
|
Asa@59
|
343 btnProcess:SetScript("OnLeave", function()
|
Asa@59
|
344 GameTooltip:Hide()
|
Asa@59
|
345 end)
|
Asa@133
|
346
|
Asa@59
|
347 btnSkillet = CreateFrame("Button", nil, craftingContent, "UIPanelButtonTemplate")
|
Asa@99
|
348
|
Asa@133
|
349 btnSkillet:SetSize(125, 25)
|
Asa@59
|
350 btnSkillet:SetPoint("BOTTOMRIGHT", btnProcess, 'BOTTOMLEFT', 0, 0)
|
Asa@59
|
351 btnSkillet:RegisterForClicks("LeftButtonUp");
|
Asa@59
|
352 btnSkillet:SetScript("OnClick", function (self, button, down)
|
Asa@70
|
353 Crafting.Export()
|
Asa@59
|
354 end)
|
Asa@133
|
355
|
Asa@59
|
356 end
|
Asa@70
|
357 local destination = select(2, Crafting.GetQueueDestination())
|
Asa@70
|
358 btnSkillet:SetText("Export to "..destination)
|
Asa@133
|
359
|
Asa@59
|
360 craftingContent:Show()
|
Asa@133
|
361
|
Asa@59
|
362 if container.parent then
|
Asa@59
|
363 local width = 80
|
Asa@133
|
364 for i, data in pairs(craftingCols) do
|
Asa@59
|
365 width = width + data.width
|
Asa@59
|
366 end
|
Asa@59
|
367 container.parent:SetWidth(width);
|
Asa@59
|
368 end
|
Asa@133
|
369
|
Asa@59
|
370 ItemAuditor:RegisterEvent("TRADE_SKILL_SHOW", function()
|
Asa@59
|
371 if craftingContent and craftingContent:IsVisible() then
|
Asa@59
|
372 ItemAuditor:UpdateCraftingTable()
|
Asa@59
|
373 end
|
Asa@59
|
374 end)
|
Asa@59
|
375 ItemAuditor:UpdateCraftingTable()
|
Asa@133
|
376
|
Asa@59
|
377 return craftingContent
|
Asa@59
|
378 end
|
Asa@59
|
379
|
Asa@59
|
380 ItemAuditor:RegisterTab('Crafting', 'tab_crafting', ShowCrafting)
|
Asa@59
|
381 function ItemAuditor:DisplayCrafting()
|
Asa@59
|
382 self:CreateFrame('tab_crafting')
|
Asa@59
|
383 end
|
Asa@59
|
384
|
Asa@59
|
385 local craftingDeciders = {}
|
Asa@59
|
386
|
Asa@72
|
387 function Crafting.RegisterCraftingDecider(name, decider, options)
|
Asa@59
|
388 craftingDeciders[name] = decider
|
Asa@133
|
389
|
Asa@72
|
390 ItemAuditor.Options.args.crafting_options.args['chk'..name] = {
|
Asa@72
|
391 type = "toggle",
|
Asa@72
|
392 name = "Enable "..name,
|
Asa@72
|
393 get = function() return not ItemAuditor.db.profile.disabled_deciders[name] end,
|
Asa@72
|
394 set = function(info, value) ItemAuditor.db.profile.disabled_deciders[name] = not value end,
|
Asa@72
|
395 order = 11,
|
Asa@72
|
396 }
|
Asa@133
|
397
|
Asa@72
|
398 if options then
|
Asa@72
|
399 ItemAuditor.Options.args.crafting_options.args['decider_'..name] = {
|
Asa@72
|
400 handler = {},
|
Asa@72
|
401 name = name,
|
Asa@72
|
402 type = 'group',
|
Asa@72
|
403 args = options,
|
Asa@72
|
404 }
|
Asa@72
|
405 end
|
Asa@59
|
406 end
|
Asa@59
|
407
|
Asa@59
|
408 local lastWinnder = ""
|
Asa@59
|
409 local function Decide(data)
|
Asa@59
|
410 local newDecision = 0
|
Asa@74
|
411 local reason = ""
|
Asa@59
|
412 for name, decider in pairs(craftingDeciders) do
|
Asa@72
|
413 if not ItemAuditor.db.profile.disabled_deciders[name] and name ~= lastWinner then
|
Asa@74
|
414 newDecision, reason = decider(data)
|
Asa@133
|
415
|
Asa@59
|
416 if newDecision > data.queue then
|
Asa@59
|
417 data.queue = newDecision
|
Asa@74
|
418 lastWinner = (reason or name)
|
Asa@59
|
419 return Decide(data)
|
Asa@59
|
420 elseif newDecision < 0 then
|
Asa@59
|
421 lastWinner = ""
|
Asa@74
|
422 return 'VETO: '..(reason or name), -1
|
Asa@59
|
423 end
|
Asa@59
|
424 end
|
Asa@59
|
425 end
|
Asa@133
|
426
|
Asa@59
|
427 winner = lastWinner
|
Asa@59
|
428 lastWinner = ""
|
Asa@133
|
429
|
Asa@77
|
430 data.queue = ceil(data.queue / GetTradeSkillNumMade(data.tradeSkillIndex))
|
Asa@133
|
431
|
Asa@59
|
432 return winner, data.queue
|
Asa@59
|
433 end
|
Asa@59
|
434
|
Asa@59
|
435 local function isProfitable(data)
|
Asa@59
|
436 if data.profit > 0 and data.profit > ItemAuditor:GetCraftingThreshold() then
|
Asa@59
|
437 return 1
|
Asa@59
|
438 end
|
Asa@99
|
439 return -1, 'Not Profitable'
|
Asa@59
|
440 end
|
Asa@72
|
441
|
Asa@101
|
442 local isProfitableOptions = {
|
Asa@101
|
443 profitable_threshold = {
|
Asa@101
|
444 type = "input",
|
Asa@101
|
445 name = "Crafting Threshold",
|
Asa@101
|
446 desc = "Don't create items that will make less than this amount of profit",
|
Asa@101
|
447 get = function() return
|
Asa@101
|
448 Utils.FormatMoney(ItemAuditor:GetCraftingThreshold(), '', true)
|
Asa@101
|
449 end,
|
Asa@101
|
450 validate = function(info, value)
|
Asa@101
|
451 if not Utils.validateMoney(value) then
|
Asa@101
|
452 return "Invalid money format"
|
Asa@101
|
453 end
|
Asa@101
|
454 return true
|
Asa@101
|
455 end,
|
Asa@101
|
456 set = function(info, value)
|
Asa@101
|
457 ItemAuditor.db.char.profitable_threshold = Utils.parseMoney(value)
|
Asa@101
|
458 end,
|
Asa@101
|
459 usage = "###g ##s ##c",
|
Asa@101
|
460 order = 0,
|
Asa@101
|
461 },
|
Asa@101
|
462 }
|
Asa@101
|
463
|
Asa@101
|
464 Crafting.RegisterCraftingDecider('Is Profitable', isProfitable, isProfitableOptions)
|
Asa@59
|
465
|
Asa@74
|
466
|
Asa@59
|
467
|
Asa@59
|
468 local tableData = {}
|
Asa@59
|
469 function ItemAuditor:UpdateCraftingTable()
|
Asa@59
|
470 if GetAuctionBuyout ~= nil then
|
Asa@59
|
471 elseif AucAdvanced and AucAdvanced.Version then
|
Asa@59
|
472 else
|
Asa@59
|
473 self:Print("This feature requires Auctionator, Auctioneer, AuctionLite, or AuctionMaster.")
|
Asa@59
|
474 return
|
Asa@59
|
475 end
|
Asa@128
|
476 local tradeskillName = GetTradeSkillLine()
|
Asa@133
|
477
|
Asa@59
|
478 wipe(tableData)
|
Asa@133
|
479
|
Asa@59
|
480 local profitableItems = {}
|
Asa@59
|
481 local profitableIndex = 1
|
Asa@59
|
482 local numChecked = 0
|
Asa@128
|
483 local numTradeSkills = GetNumTradeSkills()
|
Asa@128
|
484 if tradeskillName == 'UNKNOWN' then
|
Asa@128
|
485 numTradeSkills = 0
|
Asa@128
|
486 end
|
Asa@133
|
487
|
Asa@134
|
488 local row = 1
|
Asa@148
|
489 getQueueLocation()
|
Asa@59
|
490
|
Asa@128
|
491 for i = 1, numTradeSkills do
|
Asa@59
|
492 local itemLink = GetTradeSkillItemLink(i)
|
Asa@86
|
493 local itemId = Utils.GetItemID(itemLink)
|
Asa@124
|
494 local vellumID = nil
|
Asa@59
|
495
|
Asa@59
|
496 --Figure out if its an enchant or not
|
Asa@156
|
497 local altVerb = select(5, GetTradeSkillInfo(i))
|
Asa@156
|
498 if LSW and LSW.scrollData[itemId] ~= nil and altVerb == 'Enchant' then
|
Asa@59
|
499 -- Ask LSW for the correct scroll
|
Asa@156
|
500 itemId = LSW.scrollData[itemId]
|
Asa@156
|
501 vellumID = 38682
|
Asa@59
|
502 end
|
Asa@59
|
503
|
Asa@59
|
504 local recipeLink = GetTradeSkillRecipeLink(i)
|
Asa@59
|
505 local stackSize = 1
|
Asa@59
|
506 if recipeLink ~= nil and itemId ~= nil then
|
Asa@59
|
507 local skillName, skillType, numAvailable, isExpanded, altVerb = GetTradeSkillInfo(i)
|
Asa@59
|
508 local itemName, itemLink= GetItemInfo(itemId)
|
Asa@133
|
509
|
Asa@74
|
510 -- This check has to be here for things like Inscription Research that don't produce an item.
|
Asa@74
|
511 if itemLink then
|
Asa@82
|
512 local count = ItemAuditor:GetItemCount(itemId)
|
Asa@74
|
513 local reagents = {}
|
Asa@74
|
514 local totalCost = 0
|
Asa@74
|
515 for reagentId = 1, GetTradeSkillNumReagents(i) do
|
Asa@74
|
516 local reagentName, _, reagentCount = GetTradeSkillReagentInfo(i, reagentId);
|
Asa@74
|
517 local reagentLink = GetTradeSkillReagentItemLink(i, reagentId)
|
Asa@102
|
518 local reagentTotalCost = self:GetReagentCost(reagentLink, reagentCount)
|
Asa@159
|
519 if not reagentLink then
|
Asa@159
|
520 -- we can't continue without a link, but sometimes blizzard just fails to return one.z
|
Asa@159
|
521 self:Print("GetItemInfo failed to return an item link for %s. Retrying in 1 second.", tostring(reagentName))
|
Asa@159
|
522 return self:ScheduleTimer("UpdateCraftingTable", 1)
|
Asa@159
|
523 end
|
Asa@159
|
524
|
Asa@159
|
525 assert(reagentLink, format("GetItemInfo failed to return an item link for %s (skill: %s) (ReagentID: %s)", tostring(reagentName), tostring(i), tostring(reagentId)))
|
Asa@133
|
526
|
Asa@74
|
527 reagents[reagentId] = {
|
Asa@93
|
528 link = reagentLink,
|
Asa@124
|
529 itemID = Utils.GetItemID(reagentLink),
|
Asa@74
|
530 name = reagentName,
|
Asa@74
|
531 count = reagentCount,
|
Asa@102
|
532 price = reagentTotalCost / reagentCount,
|
Asa@94
|
533 need = 0, -- This will get populated after the decisions have been made. it can't
|
Asa@94
|
534 -- be done before that because highest profit items get priority on materials.
|
Asa@74
|
535 }
|
Asa@102
|
536 totalCost = totalCost + reagentTotalCost
|
Asa@74
|
537 end
|
Asa@124
|
538 if vellumID then
|
Asa@124
|
539 reagentId = GetTradeSkillNumReagents(i) + 1
|
Asa@124
|
540 local reagentName, reagentLink = GetItemInfo(vellumID)
|
Asa@159
|
541 assert(reagentLink, format("GetItemInfo failed to return an item link %s:%s", tostring(vellumID), tostring(reagentName)))
|
Asa@124
|
542 reagents[reagentId] = {
|
Asa@124
|
543 link = reagentLink,
|
Asa@124
|
544 itemID = vellumID,
|
Asa@124
|
545 name = reagentName,
|
Asa@124
|
546 count = 1,
|
Asa@124
|
547 price = self:GetReagentCost(reagentLink, 1),
|
Asa@124
|
548 need = 0, -- This will get populated after the decisions have been made. it can't
|
Asa@124
|
549 -- be done before that because highest profit items get priority on materials.
|
Asa@124
|
550 }
|
Asa@124
|
551 totalCost = totalCost + self:GetReagentCost(reagentLink, 1)
|
Asa@124
|
552 end
|
Asa@116
|
553
|
Asa@116
|
554 local price = (self:GetAuctionPrice(itemLink) or 0)
|
Asa@116
|
555 totalCost = totalCost + (price * ItemAuditor:GetAHCut())
|
Asa@74
|
556 local data = {
|
Asa@74
|
557 recipeLink = recipeLink,
|
Asa@95
|
558 recipeID = Utils.GetItemID(recipeLink),
|
Asa@74
|
559 link = itemLink,
|
Asa@74
|
560 name = itemName,
|
Asa@128
|
561 skillName = skillName,
|
Asa@74
|
562 count = count,
|
Asa@110
|
563 price = price,
|
Asa@74
|
564 cost = totalCost,
|
Asa@110
|
565 profit = price - totalCost,
|
Asa@74
|
566 reagents = reagents,
|
Asa@74
|
567 count = count,
|
Asa@74
|
568 tradeSkillIndex = i,
|
Asa@74
|
569 queue = 0,
|
Asa@74
|
570 winner = "",
|
Asa@128
|
571 tradeskillName = tradeskillName,
|
Asa@74
|
572 }
|
Asa@133
|
573
|
Asa@74
|
574 data.winner, data.queue = Decide(data)
|
Asa@99
|
575 --[[
|
Asa@99
|
576 If it wasn't vetoed we need to reduce the number by how many are owned
|
Asa@99
|
577 but this should not go below 0
|
Asa@99
|
578 ]]
|
Asa@99
|
579 if data.queue > 0 then
|
Asa@99
|
580 data.queue = max(0, data.queue - count)
|
Asa@99
|
581 end
|
Asa@133
|
582
|
Asa@133
|
583 -- If a tradeskill makes 5 at a time and something asks for 9, we should only
|
Asa@74
|
584 -- craft twice to get 10.
|
Asa@74
|
585 data.queue = ceil(data.queue / GetTradeSkillNumMade(i))
|
Asa@128
|
586
|
Asa@74
|
587 realData[row] = data
|
Asa@128
|
588 nameMap[skillName] = row
|
Asa@74
|
589 row = row + 1
|
Asa@59
|
590 end
|
Asa@59
|
591 end
|
Asa@59
|
592 end
|
Asa@99
|
593 table.sort(realData, function(a, b) return a.profit*max(1, a.queue) > b.profit*max(1, b.queue) end)
|
Asa@94
|
594
|
Asa@94
|
595 local numOwned = {}
|
Asa@124
|
596
|
Asa@94
|
597 for key, data in pairs(realData) do
|
Asa@126
|
598 data.haveMaterials = data.queue
|
Asa@94
|
599 for id, reagent in pairs(data.reagents) do
|
Asa@126
|
600 local needEach = reagent.count
|
Asa@122
|
601 reagent.count = reagent.count * data.queue
|
Asa@122
|
602
|
Asa@94
|
603 if not numOwned[reagent.link] then
|
Asa@94
|
604 numOwned[reagent.link] = ItemAuditor:GetItemCount(ItemAuditor:GetIDFromLink(reagent.link))
|
Asa@94
|
605 end
|
Asa@126
|
606 data.haveMaterials = min(data.haveMaterials, floor(numOwned[reagent.link] / needEach))
|
Asa@94
|
607 numOwned[reagent.link] = numOwned[reagent.link] - reagent.count
|
Asa@94
|
608
|
Asa@156
|
609 if numOwned[reagent.link] < 0 then
|
Asa@94
|
610 reagent.need = min(reagent.count, abs(numOwned[reagent.link]))
|
Asa@94
|
611 end
|
Asa@94
|
612 end
|
Asa@126
|
613 data.haveMaterials = max(0, data.haveMaterials)
|
Asa@94
|
614 end
|
Asa@94
|
615
|
Asa@68
|
616 if craftingTable then
|
Asa@68
|
617 craftingTable:SetFilter(tableFilter)
|
Asa@68
|
618 self:RefreshCraftingTable()
|
Asa@68
|
619 end
|
Asa@60
|
620 end
|
Asa@60
|
621
|
Asa@60
|
622 function ItemAuditor:RefreshCraftingTable()
|
Asa@131
|
623 -- If the crafting table hasn't been created/displayed, there is no
|
Asa@131
|
624 -- reason to try to update it.
|
Asa@131
|
625 if not craftingTable then
|
Asa@131
|
626 return
|
Asa@131
|
627 end
|
Asa@128
|
628 tableData = {}
|
Asa@128
|
629 nameMap = {}
|
Asa@59
|
630 for key, data in pairs(realData) do
|
Asa@128
|
631 nameMap[data.name] = key
|
Asa@128
|
632
|
Asa@59
|
633 tableData[key] = {
|
Asa@59
|
634 data.name,
|
Asa@59
|
635 data.cost,
|
Asa@59
|
636 data.price,
|
Asa@59
|
637 data.winner,
|
Asa@99
|
638 abs(data.queue),
|
Asa@126
|
639 data.haveMaterials,
|
Asa@123
|
640 data.profit,
|
Asa@59
|
641 }
|
Asa@59
|
642 end
|
Asa@60
|
643 craftingTable:SetData(tableData, true)
|
Asa@133
|
644
|
Asa@60
|
645 if self:GetCraftingRow(1) then
|
Asa@60
|
646 btnProcess:Enable()
|
Asa@60
|
647 else
|
Asa@60
|
648 btnProcess:Disable()
|
Asa@60
|
649 end
|
Asa@59
|
650 end
|
Asa@59
|
651
|
Asa@59
|
652 function ItemAuditor:GetCraftingRow(row)
|
Asa@59
|
653 if craftingTable then
|
Asa@59
|
654 for _, index in pairs(craftingTable.sorttable) do
|
Asa@59
|
655 local tableRow = tableData[index]
|
Asa@59
|
656 if tableFilter(nil, tableRow) then
|
Asa@59
|
657 row = row - 1
|
Asa@59
|
658 if row == 0 then
|
Asa@59
|
659 return realData[index]
|
Asa@59
|
660 end
|
Asa@59
|
661 end
|
Asa@59
|
662 end
|
Asa@59
|
663 elseif realData then
|
Asa@59
|
664 return realData[row]
|
Asa@59
|
665 end
|
Asa@59
|
666 return nil
|
Asa@59
|
667 end
|
Asa@67
|
668 ItemAuditor.Options.args.crafting = {
|
Asa@67
|
669 type = "execute",
|
Asa@67
|
670 name = "crafting",
|
Asa@67
|
671 desc = "This opens a window to configure a crafting queue.",
|
Asa@67
|
672 func = "DisplayCrafting",
|
Asa@67
|
673 guiHidden = false,
|
Asa@67
|
674 }
|