annotate Modules/Crafting.lua @ 94:4ec8611d9466

Fixed Enchanting. I was not getting the ItemID correctly, so enchants could not be mapped to the scrolls they were to created Changed snatch to only add each item once and to only add a snatch for items you don't have API: Added haveMaterials to the item and need to the reagents that get passed to queue destinations. This is in preparation for building a shopping list module.
author Asa Ayers <Asa.Ayers@Gmail.com>
date Wed, 11 Aug 2010 23:48:23 -0700
parents 693f664aad2b
children 63823b6b5e28
rev   line source
Asa@63 1 local ItemAuditor = select(2, ...)
Asa@63 2 local Crafting = ItemAuditor:NewModule("Crafting")
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@59 13
Asa@67 14
Asa@68 15 local queueDestinations = {}
Asa@70 16 local displayCraftingDestinations = {}
Asa@68 17 function Crafting.RegisterQueueDestination(name, destination)
Asa@68 18 queueDestinations[name] = destination
Asa@70 19 displayCraftingDestinations[name] = name
Asa@70 20 end
Asa@70 21
Asa@70 22 function Crafting.UnRegisterQueueDestination(name)
Asa@70 23 queueDestinations[name] = nil
Asa@70 24 displayCraftingDestinations[name] = nil
Asa@70 25 end
Asa@70 26
Asa@70 27 function Crafting.GetQueueDestination()
Asa@70 28 local dest = ItemAuditor.db.profile.queue_destination
Asa@70 29 if dest and queueDestinations[dest] then
Asa@70 30 return queueDestinations[dest], dest
Asa@70 31 end
Asa@70 32 -- If there is none selected or the selected option has
Asa@70 33 -- dissapeared, choose the first one in the list
Asa@70 34 for name, func in pairs(queueDestinations) do
Asa@70 35 if dest then
Asa@70 36 ItemAuditor:Print("%s is no longer available as a queue destination. %s is the new default", dest, name)
Asa@70 37 end
Asa@70 38 ItemAuditor.db.profile.queue_destination = name
Asa@70 39 return func, name
Asa@70 40 end
Asa@70 41
Asa@70 42 error('Unable to determine queue destination.')
Asa@68 43 end
Asa@68 44
Asa@67 45 -- TODO: Convert this to a text field.
Asa@67 46 local craftingThresholds = {5000, 10000, 50000}
Asa@67 47 local craftingThresholdsDisplay = {}
Asa@67 48
Asa@67 49 for key, value in pairs(craftingThresholds) do
Asa@67 50 craftingThresholdsDisplay[key] = ItemAuditor:FormatMoney(value, '', true)
Asa@67 51 -- craftingThresholdsDisplay[key] = value
Asa@67 52 end
Asa@67 53
Asa@68 54 function ItemAuditor:GetCraftingThreshold()
Asa@68 55 local key = ItemAuditor.db.char.crafting_threshold
Asa@68 56 return craftingThresholds[key]
Asa@68 57 end
Asa@68 58
Asa@67 59 ItemAuditor.Options.args.crafting_options = {
Asa@70 60 name = "Crafting",
Asa@67 61 type = 'group',
Asa@67 62 args = {
Asa@67 63 crafting_threshold = {
Asa@67 64 type = "select",
Asa@67 65 name = "Crafting Threshold",
Asa@67 66 desc = "Don't create items that will make less than this amount of profit",
Asa@67 67 values = craftingThresholdsDisplay,
Asa@67 68 get = function() return ItemAuditor.db.char.crafting_threshold end,
Asa@67 69 set = function(info, value) ItemAuditor.db.char.crafting_threshold = value end,
Asa@72 70 order = 0,
Asa@67 71 },
Asa@70 72 queue_destination = {
Asa@70 73 type = "select",
Asa@70 74 name = "Queue Destination",
Asa@70 75 desc = "Select the addon who's queue you would like ItemAuditor to post to.",
Asa@70 76 values = displayCraftingDestinations,
Asa@70 77 get = function() return select(2, Crafting.GetQueueDestination()) end,
Asa@70 78 set = function(info, value) ItemAuditor.db.profile.queue_destination = value end,
Asa@72 79 order = 1,
Asa@70 80 },
Asa@72 81 deciders = {
Asa@72 82 type="header",
Asa@72 83 name="Crafting Deciders",
Asa@72 84 order = 10,
Asa@72 85 },
Asa@72 86
Asa@67 87 },
Asa@67 88 }
Asa@67 89
Asa@59 90 local function displayMoney(rowFrame, cellFrame, data, cols, row, realrow, column, fShow, table, ...)
Asa@59 91 if fShow == true then
Asa@59 92 local money = data[realrow][column]
Asa@59 93 if money then
Asa@59 94 cellFrame.text:SetText(ItemAuditor:FormatMoney(tonumber(money)))
Asa@59 95 else
Asa@59 96 cellFrame.text:SetText("")
Asa@59 97 end
Asa@59 98
Asa@59 99 end
Asa@59 100 end
Asa@59 101
Asa@59 102 local craftingCols = {
Asa@59 103 { name= "Item", width = 200, defaultsort = "desc",
Asa@59 104 ['DoCellUpdate'] = function(rowFrame, cellFrame, data, cols, row, realrow, column, fShow, table, ...)
Asa@59 105 if fShow == true then
Asa@59 106 local data = realData[realrow]
Asa@59 107 cellFrame.text:SetText(data.link)
Asa@59 108 end
Asa@59 109 end,
Asa@59 110 },
Asa@59 111 { name= "Cost Each", width = 100, align = "RIGHT",
Asa@59 112 ['DoCellUpdate'] = displayMoney,
Asa@59 113 },
Asa@59 114 { name= "Est Sale Each", width = 100, align = "RIGHT",
Asa@59 115 ['DoCellUpdate'] = displayMoney,
Asa@59 116 },
Asa@59 117 { name= "Decided By", width = 100, align = "RIGHT",
Asa@59 118
Asa@59 119 },
Asa@59 120 { name= "craft", width = 50, align = "RIGHT",
Asa@59 121
Asa@59 122 },
Asa@59 123 { name= "Total Profit", width = 100, align = "RIGHT",
Asa@59 124 ['DoCellUpdate'] = displayMoney,
Asa@59 125 },
Asa@59 126 }
Asa@59 127
Asa@68 128 function Crafting.ExportToSkillet(data)
Asa@68 129 local skillString = select(3, string.find(data.recipeLink, "^|%x+|H(.+)|h%[.+%]"))
Asa@68 130 local _, skillId = strsplit(":", skillString)
Asa@68 131
Asa@68 132 ItemAuditor:AddToQueue(skillId,tradeSkillIndex, data.queue)
Asa@68 133 end
Asa@68 134
Asa@68 135 Crafting.RegisterQueueDestination('Skillet', Crafting.ExportToSkillet)
Asa@68 136
Asa@68 137
Asa@68 138
Asa@68 139 function Crafting.Export(destination)
Asa@68 140 if type(destination) == 'function' then
Asa@68 141 -- do nothing
Asa@68 142 elseif destination == nil then
Asa@70 143 destination = Crafting.GetQueueDestination()
Asa@68 144 elseif type(destination) == 'string' then
Asa@68 145 destination = queueDestinations[destination]
Asa@68 146 else
Asa@68 147 error('destination must be a function or a string')
Asa@68 148 end
Asa@68 149
Asa@59 150 local index = 1
Asa@59 151 local data = ItemAuditor:GetCraftingRow(index)
Asa@59 152 while data do
Asa@68 153 if data.queue > 0 then
Asa@68 154 destination(data)
Asa@68 155 end
Asa@61 156 index = index + 1
Asa@61 157 data = ItemAuditor:GetCraftingRow(index)
Asa@59 158
Asa@59 159 end
Asa@59 160 end
Asa@59 161
Asa@74 162 -- ItemAuditor:GetModule('Crafting').filter_queued = false
Asa@74 163 Crafting.filter_queued = true
Asa@74 164 local function tableFilter(self, row, ...)
Asa@74 165 -- column 5 is how many should be crafted
Asa@74 166 if Crafting.filter_queued and row[5] <= 0 then
Asa@74 167 return false
Asa@74 168 end
Asa@74 169 return true
Asa@74 170 end
Asa@74 171
Asa@59 172 local craftingContent = false
Asa@59 173 local craftingTable = false
Asa@60 174 local btnProcess = false
Asa@59 175 local function ShowCrafting(container)
Asa@59 176 if craftingContent == false then
Asa@59 177 local window = container.frame
Asa@59 178 craftingContent = CreateFrame("Frame",nil,window)
Asa@59 179 craftingContent:SetBackdropColor(0, 0, 1, 0.5)
Asa@59 180 craftingContent:SetBackdropBorderColor(1, 0, 0, 1)
Asa@59 181
Asa@59 182 craftingContent:SetPoint("TOPLEFT", window, 10, -50)
Asa@59 183 craftingContent:SetPoint("BOTTOMRIGHT",window, -10, 10)
Asa@59 184
Asa@59 185 craftingTable = ScrollingTable:CreateST(craftingCols, 22, nil, nil, craftingContent )
Asa@59 186
Asa@59 187 IAcc = craftingContent
Asa@59 188 IAccWindow = window
Asa@59 189 craftingTable.frame:SetPoint("TOPLEFT",craftingContent, 0,0)
Asa@59 190 craftingTable.frame:SetPoint("BOTTOMRIGHT", craftingContent, 0, 30)
Asa@59 191
Asa@59 192 craftingTable:RegisterEvents({
Asa@59 193 ["OnEnter"] = function (rowFrame, cellFrame, data, cols, row, realrow, column, scrollingTable, ...)
Asa@59 194 if realrow then
Asa@59 195 local data = realData[realrow]
Asa@59 196
Asa@59 197 GameTooltip:SetOwner(rowFrame, "ANCHOR_CURSOR")
Asa@59 198 GameTooltip:SetHyperlink(data.link)
Asa@59 199 GameTooltip:Show()
Asa@59 200 end
Asa@59 201 end,
Asa@59 202 ["OnLeave"] = function (rowFrame, cellFrame, data, cols, row, realrow, column, scrollingTable, ...)
Asa@59 203 GameTooltip:Hide()
Asa@59 204 end,
Asa@59 205 });
Asa@59 206
Asa@59 207
Asa@59 208 btnProcess = CreateFrame("Button", nil, craftingContent, "UIPanelButtonTemplate")
Asa@59 209 btnProcess:SetText("Process")
Asa@59 210 btnProcess:SetSize(100, 25)
Asa@59 211 btnProcess:SetPoint("BOTTOMRIGHT", craftingContent, 0, 0)
Asa@59 212 btnProcess:RegisterForClicks("LeftButtonUp");
Asa@59 213
Asa@60 214 local function UpdateProcessTooltip(btn)
Asa@59 215 local data = ItemAuditor:GetCraftingRow(1)
Asa@59 216 if data then
Asa@59 217 GameTooltip:SetOwner(this, "ANCHOR_CURSOR")
Asa@59 218 GameTooltip:SetText(format('Create %sx%s', data.link, data.queue))
Asa@59 219 GameTooltip:Show()
Asa@59 220 end
Asa@60 221 end
Asa@60 222 btnProcess:SetScript("OnClick", function (self, button, down)
Asa@60 223 local data = ItemAuditor:GetCraftingRow(1)
Asa@60 224 if data then
Asa@60 225 ItemAuditor:Print('Crafting %sx%s', data.link, data.queue)
Asa@60 226 DoTradeSkill(data.tradeSkillIndex, data.queue)
Asa@60 227 data.queue = 0
Asa@60 228 ItemAuditor:RefreshCraftingTable()
Asa@60 229 UpdateProcessTooltip()
Asa@60 230 end
Asa@59 231 end)
Asa@59 232
Asa@60 233 btnProcess:SetScript("OnEnter", UpdateProcessTooltip)
Asa@60 234
Asa@59 235 btnProcess:SetScript("OnLeave", function()
Asa@59 236 GameTooltip:Hide()
Asa@59 237 end)
Asa@59 238
Asa@59 239 btnSkillet = CreateFrame("Button", nil, craftingContent, "UIPanelButtonTemplate")
Asa@70 240
Asa@70 241
Asa@59 242 btnSkillet:SetSize(125, 25)
Asa@59 243 btnSkillet:SetPoint("BOTTOMRIGHT", btnProcess, 'BOTTOMLEFT', 0, 0)
Asa@59 244 btnSkillet:RegisterForClicks("LeftButtonUp");
Asa@59 245 btnSkillet:SetScript("OnClick", function (self, button, down)
Asa@70 246 Crafting.Export()
Asa@59 247 end)
Asa@59 248
Asa@59 249 end
Asa@70 250 local destination = select(2, Crafting.GetQueueDestination())
Asa@70 251 btnSkillet:SetText("Export to "..destination)
Asa@70 252
Asa@59 253 craftingContent:Show()
Asa@59 254
Asa@59 255 if container.parent then
Asa@59 256 local width = 80
Asa@59 257 for i, data in pairs(craftingCols) do
Asa@59 258 width = width + data.width
Asa@59 259 end
Asa@59 260 container.parent:SetWidth(width);
Asa@59 261 end
Asa@59 262
Asa@59 263 ItemAuditor:RegisterEvent("TRADE_SKILL_SHOW", function()
Asa@59 264 if craftingContent and craftingContent:IsVisible() then
Asa@59 265 ItemAuditor:UpdateCraftingTable()
Asa@59 266 end
Asa@59 267 end)
Asa@59 268 ItemAuditor:UpdateCraftingTable()
Asa@59 269
Asa@59 270 return craftingContent
Asa@59 271 end
Asa@59 272
Asa@59 273
Asa@59 274
Asa@59 275 ItemAuditor:RegisterTab('Crafting', 'tab_crafting', ShowCrafting)
Asa@59 276 function ItemAuditor:DisplayCrafting()
Asa@59 277 self:CreateFrame('tab_crafting')
Asa@59 278 end
Asa@59 279
Asa@59 280 local craftingDeciders = {}
Asa@59 281
Asa@72 282 function Crafting.RegisterCraftingDecider(name, decider, options)
Asa@59 283 craftingDeciders[name] = decider
Asa@72 284
Asa@72 285 ItemAuditor.Options.args.crafting_options.args['chk'..name] = {
Asa@72 286 type = "toggle",
Asa@72 287 name = "Enable "..name,
Asa@72 288 get = function() return not ItemAuditor.db.profile.disabled_deciders[name] end,
Asa@72 289 set = function(info, value) ItemAuditor.db.profile.disabled_deciders[name] = not value end,
Asa@72 290 order = 11,
Asa@72 291 }
Asa@72 292
Asa@72 293 if options then
Asa@72 294 ItemAuditor.Options.args.crafting_options.args['decider_'..name] = {
Asa@72 295 handler = {},
Asa@72 296 name = name,
Asa@72 297 type = 'group',
Asa@72 298 args = options,
Asa@72 299 }
Asa@72 300 end
Asa@59 301 end
Asa@59 302
Asa@59 303 local lastWinnder = ""
Asa@59 304 local function Decide(data)
Asa@59 305 local newDecision = 0
Asa@74 306 local reason = ""
Asa@59 307 for name, decider in pairs(craftingDeciders) do
Asa@72 308 if not ItemAuditor.db.profile.disabled_deciders[name] and name ~= lastWinner then
Asa@74 309 newDecision, reason = decider(data)
Asa@74 310
Asa@59 311 if newDecision > data.queue then
Asa@59 312 data.queue = newDecision
Asa@74 313 lastWinner = (reason or name)
Asa@59 314 return Decide(data)
Asa@59 315 elseif newDecision < 0 then
Asa@59 316 lastWinner = ""
Asa@74 317 return 'VETO: '..(reason or name), -1
Asa@59 318 end
Asa@59 319 end
Asa@59 320 end
Asa@59 321
Asa@59 322 winner = lastWinner
Asa@59 323 lastWinner = ""
Asa@59 324
Asa@77 325 data.queue = ceil(data.queue / GetTradeSkillNumMade(data.tradeSkillIndex))
Asa@77 326
Asa@59 327 return winner, data.queue
Asa@59 328 end
Asa@59 329
Asa@59 330 local function isProfitable(data)
Asa@59 331 if data.profit > 0 and data.profit > ItemAuditor:GetCraftingThreshold() then
Asa@59 332 return 1
Asa@59 333 end
Asa@59 334 return -1
Asa@59 335 end
Asa@72 336
Asa@64 337 Crafting.RegisterCraftingDecider('Is Profitable', isProfitable)
Asa@59 338
Asa@74 339
Asa@59 340
Asa@59 341 local tableData = {}
Asa@59 342 function ItemAuditor:UpdateCraftingTable()
Asa@59 343 if LSW == nil then
Asa@59 344 self:Print("This feature requires LilSparky's Workshop.")
Asa@59 345 return
Asa@59 346 end
Asa@59 347 if GetAuctionBuyout ~= nil then
Asa@59 348 elseif AucAdvanced and AucAdvanced.Version then
Asa@59 349 else
Asa@59 350 self:Print("This feature requires Auctionator, Auctioneer, AuctionLite, or AuctionMaster.")
Asa@59 351 return
Asa@59 352 end
Asa@59 353 wipe(realData)
Asa@59 354 wipe(tableData)
Asa@59 355
Asa@59 356 local profitableItems = {}
Asa@59 357 local profitableIndex = 1
Asa@59 358 local numChecked = 0
Asa@59 359 local row = 1
Asa@59 360
Asa@59 361 for i = 1, GetNumTradeSkills() do
Asa@59 362 local itemLink = GetTradeSkillItemLink(i)
Asa@86 363 local itemId = Utils.GetItemID(itemLink)
Asa@59 364
Asa@59 365 --Figure out if its an enchant or not
Asa@59 366 _, _, _, _, altVerb = GetTradeSkillInfo(i)
Asa@59 367 if LSW.scrollData[itemId] ~= nil and altVerb == 'Enchant' then
Asa@59 368 -- Ask LSW for the correct scroll
Asa@59 369 itemId = LSW.scrollData[itemId]["scrollID"]
Asa@59 370 end
Asa@59 371
Asa@59 372 local recipeLink = GetTradeSkillRecipeLink(i)
Asa@59 373 local stackSize = 1
Asa@59 374 if recipeLink ~= nil and itemId ~= nil then
Asa@59 375 local skillName, skillType, numAvailable, isExpanded, altVerb = GetTradeSkillInfo(i)
Asa@59 376 local itemName, itemLink= GetItemInfo(itemId)
Asa@74 377
Asa@74 378 -- This check has to be here for things like Inscription Research that don't produce an item.
Asa@74 379 if itemLink then
Asa@82 380 local count = ItemAuditor:GetItemCount(itemId)
Asa@74 381 local reagents = {}
Asa@74 382 local totalCost = 0
Asa@74 383 for reagentId = 1, GetTradeSkillNumReagents(i) do
Asa@74 384 local reagentName, _, reagentCount = GetTradeSkillReagentInfo(i, reagentId);
Asa@74 385 local reagentLink = GetTradeSkillReagentItemLink(i, reagentId)
Asa@74 386
Asa@74 387 reagents[reagentId] = {
Asa@93 388 link = reagentLink,
Asa@74 389 name = reagentName,
Asa@74 390 count = reagentCount,
Asa@74 391 price = self:GetReagentCost(reagentLink, reagentCount),
Asa@94 392 need = 0, -- This will get populated after the decisions have been made. it can't
Asa@94 393 -- be done before that because highest profit items get priority on materials.
Asa@74 394 }
Asa@74 395 totalCost = totalCost + self:GetReagentCost(reagentLink, reagentCount)
Asa@74 396 end
Asa@74 397 local data = {
Asa@74 398 recipeLink = recipeLink,
Asa@74 399 link = itemLink,
Asa@74 400 name = itemName,
Asa@74 401 count = count,
Asa@74 402 price = (self:GetAuctionPrice(itemLink) or 0),
Asa@74 403 cost = totalCost,
Asa@74 404 profit = (self:GetAuctionPrice(itemLink) or 0) - totalCost,
Asa@74 405 reagents = reagents,
Asa@74 406 count = count,
Asa@74 407 tradeSkillIndex = i,
Asa@74 408 queue = 0,
Asa@74 409 winner = "",
Asa@74 410 }
Asa@59 411
Asa@74 412 data.winner, data.queue = Decide(data)
Asa@74 413 data.queue = data.queue - count
Asa@74 414
Asa@74 415 -- If a tradeskill makes 5 at a time and something asks for 9, we should only
Asa@74 416 -- craft twice to get 10.
Asa@74 417 data.queue = ceil(data.queue / GetTradeSkillNumMade(i))
Asa@74 418
Asa@74 419 realData[row] = data
Asa@74 420 row = row + 1
Asa@59 421 end
Asa@59 422 end
Asa@59 423 end
Asa@59 424 table.sort(realData, function(a, b) return a.profit*a.queue > b.profit*b.queue end)
Asa@94 425
Asa@94 426 local numOwned = {}
Asa@94 427 for key, data in pairs(realData) do
Asa@94 428 data.haveMaterials = true
Asa@94 429 for id, reagent in pairs(data.reagents) do
Asa@94 430 if not numOwned[reagent.link] then
Asa@94 431 numOwned[reagent.link] = ItemAuditor:GetItemCount(ItemAuditor:GetIDFromLink(reagent.link))
Asa@94 432 end
Asa@94 433 numOwned[reagent.link] = numOwned[reagent.link] - reagent.count
Asa@94 434
Asa@94 435 if numOwned[reagent.link] < 0 then
Asa@94 436 data.haveMaterials = false
Asa@94 437 reagent.need = min(reagent.count, abs(numOwned[reagent.link]))
Asa@94 438 end
Asa@94 439 end
Asa@94 440 end
Asa@94 441
Asa@68 442 if craftingTable then
Asa@68 443 craftingTable:SetFilter(tableFilter)
Asa@68 444 self:RefreshCraftingTable()
Asa@68 445 end
Asa@60 446 end
Asa@60 447
Asa@60 448 function ItemAuditor:RefreshCraftingTable()
Asa@59 449 for key, data in pairs(realData) do
Asa@59 450 tableData[key] = {
Asa@59 451 data.name,
Asa@59 452 data.cost,
Asa@59 453 data.price,
Asa@59 454 data.winner,
Asa@59 455 data.queue,
Asa@59 456 data.profit*data.queue,
Asa@59 457 }
Asa@59 458 end
Asa@60 459 craftingTable:SetData(tableData, true)
Asa@59 460
Asa@60 461 if self:GetCraftingRow(1) then
Asa@60 462 btnProcess:Enable()
Asa@60 463 else
Asa@60 464 btnProcess:Disable()
Asa@60 465 end
Asa@59 466 end
Asa@59 467
Asa@59 468 function ItemAuditor:GetCraftingRow(row)
Asa@59 469 if craftingTable then
Asa@59 470 for _, index in pairs(craftingTable.sorttable) do
Asa@59 471 local tableRow = tableData[index]
Asa@59 472 if tableFilter(nil, tableRow) then
Asa@59 473 row = row - 1
Asa@59 474 if row == 0 then
Asa@59 475 return realData[index]
Asa@59 476 end
Asa@59 477 end
Asa@59 478 end
Asa@59 479 elseif realData then
Asa@59 480 return realData[row]
Asa@59 481 end
Asa@59 482 return nil
Asa@59 483 end
Asa@67 484 ItemAuditor.Options.args.crafting = {
Asa@67 485 type = "execute",
Asa@67 486 name = "crafting",
Asa@67 487 desc = "This opens a window to configure a crafting queue.",
Asa@67 488 func = "DisplayCrafting",
Asa@67 489 guiHidden = false,
Asa@67 490 }