annotate Modules/Crafting.lua @ 77:a8fc802b42ba

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