Asa@63: local ItemAuditor = select(2, ...) Asa@63: local Crafting = ItemAuditor:NewModule("Crafting") Asa@59: Asa@86: local Utils = ItemAuditor:GetModule("Utils") Asa@86: Asa@59: local AceGUI = LibStub("AceGUI-3.0") Asa@59: local ScrollingTable = LibStub("ScrollingTable") Asa@59: Asa@59: local validateMoney = ItemAuditor.validateMoney Asa@59: local parseMoney = ItemAuditor.parseMoney Asa@59: Asa@59: local realData = {} Asa@59: Asa@67: Asa@68: local queueDestinations = {} Asa@70: local displayCraftingDestinations = {} Asa@68: function Crafting.RegisterQueueDestination(name, destination) Asa@68: queueDestinations[name] = destination Asa@70: displayCraftingDestinations[name] = name Asa@70: end Asa@70: Asa@70: function Crafting.UnRegisterQueueDestination(name) Asa@70: queueDestinations[name] = nil Asa@70: displayCraftingDestinations[name] = nil Asa@70: end Asa@70: Asa@70: function Crafting.GetQueueDestination() Asa@70: local dest = ItemAuditor.db.profile.queue_destination Asa@70: if dest and queueDestinations[dest] then Asa@70: return queueDestinations[dest], dest Asa@70: end Asa@70: -- If there is none selected or the selected option has Asa@70: -- dissapeared, choose the first one in the list Asa@70: for name, func in pairs(queueDestinations) do Asa@70: if dest then Asa@70: ItemAuditor:Print("%s is no longer available as a queue destination. %s is the new default", dest, name) Asa@70: end Asa@70: ItemAuditor.db.profile.queue_destination = name Asa@70: return func, name Asa@70: end Asa@70: Asa@70: error('Unable to determine queue destination.') Asa@68: end Asa@68: Asa@68: function ItemAuditor:GetCraftingThreshold() Asa@101: return self.db.char.profitable_threshold Asa@68: end Asa@68: Asa@67: ItemAuditor.Options.args.crafting_options = { Asa@70: name = "Crafting", Asa@67: type = 'group', Asa@67: args = { Asa@70: queue_destination = { Asa@70: type = "select", Asa@70: name = "Queue Destination", Asa@70: desc = "Select the addon who's queue you would like ItemAuditor to post to.", Asa@70: values = displayCraftingDestinations, Asa@70: get = function() return select(2, Crafting.GetQueueDestination()) end, Asa@70: set = function(info, value) ItemAuditor.db.profile.queue_destination = value end, Asa@72: order = 1, Asa@70: }, Asa@72: deciders = { Asa@72: type="header", Asa@72: name="Crafting Deciders", Asa@72: order = 10, Asa@72: }, Asa@67: }, Asa@67: } Asa@67: Asa@59: local function displayMoney(rowFrame, cellFrame, data, cols, row, realrow, column, fShow, table, ...) Asa@59: if fShow == true then Asa@59: local money = data[realrow][column] Asa@59: if money then Asa@59: cellFrame.text:SetText(ItemAuditor:FormatMoney(tonumber(money))) Asa@59: else Asa@59: cellFrame.text:SetText("") Asa@59: end Asa@59: Asa@59: end Asa@59: end Asa@59: Asa@59: local craftingCols = { Asa@59: { name= "Item", width = 200, defaultsort = "desc", Asa@59: ['DoCellUpdate'] = function(rowFrame, cellFrame, data, cols, row, realrow, column, fShow, table, ...) Asa@59: if fShow == true then Asa@59: local data = realData[realrow] Asa@59: cellFrame.text:SetText(data.link) Asa@59: end Asa@59: end, Asa@59: }, Asa@59: { name= "Cost Each", width = 100, align = "RIGHT", Asa@59: ['DoCellUpdate'] = displayMoney, Asa@59: }, Asa@59: { name= "Est Sale Each", width = 100, align = "RIGHT", Asa@59: ['DoCellUpdate'] = displayMoney, Asa@59: }, Asa@99: { name= "Decided By", width = 125, align = "RIGHT", Asa@59: Asa@59: }, Asa@59: { name= "craft", width = 50, align = "RIGHT", Asa@59: Asa@59: }, Asa@99: { name= "Have Mats", width = 60, align = "RIGHT", Asa@99: Asa@99: }, Asa@59: { name= "Total Profit", width = 100, align = "RIGHT", Asa@59: ['DoCellUpdate'] = displayMoney, Asa@59: }, Asa@59: } Asa@59: Asa@68: function Crafting.ExportToSkillet(data) Asa@68: local skillString = select(3, string.find(data.recipeLink, "^|%x+|H(.+)|h%[.+%]")) Asa@68: local _, skillId = strsplit(":", skillString) Asa@68: Asa@68: ItemAuditor:AddToQueue(skillId,tradeSkillIndex, data.queue) Asa@68: end Asa@68: Asa@68: Crafting.RegisterQueueDestination('Skillet', Crafting.ExportToSkillet) Asa@68: Asa@68: Asa@68: Asa@68: function Crafting.Export(destination) Asa@68: if type(destination) == 'function' then Asa@68: -- do nothing Asa@68: elseif destination == nil then Asa@70: destination = Crafting.GetQueueDestination() Asa@68: elseif type(destination) == 'string' then Asa@68: destination = queueDestinations[destination] Asa@68: else Asa@68: error('destination must be a function or a string') Asa@68: end Asa@68: Asa@59: local index = 1 Asa@59: local data = ItemAuditor:GetCraftingRow(index) Asa@59: while data do Asa@68: if data.queue > 0 then Asa@68: destination(data) Asa@68: end Asa@61: index = index + 1 Asa@61: data = ItemAuditor:GetCraftingRow(index) Asa@59: Asa@59: end Asa@59: end Asa@59: Asa@74: -- ItemAuditor:GetModule('Crafting').filter_queued = false Asa@99: Crafting.filter_have_mats = false Asa@99: Crafting.filter_show_all = false Asa@74: local function tableFilter(self, row, ...) Asa@99: if Crafting.filter_show_all then Asa@99: return true Asa@99: end Asa@99: Asa@74: -- column 5 is how many should be crafted Asa@99: if Crafting.filter_have_mats and row[6] == 'n' then Asa@99: return false Asa@99: end Asa@99: if strfind(row[4], 'VETO: .*') or row[5] == 0 then Asa@74: return false Asa@74: end Asa@74: return true Asa@74: end Asa@74: Asa@59: local craftingContent = false Asa@59: local craftingTable = false Asa@60: local btnProcess = false Asa@59: local function ShowCrafting(container) Asa@59: if craftingContent == false then Asa@59: local window = container.frame Asa@59: craftingContent = CreateFrame("Frame",nil,window) Asa@59: craftingContent:SetBackdropColor(0, 0, 1, 0.5) Asa@59: craftingContent:SetBackdropBorderColor(1, 0, 0, 1) Asa@59: Asa@59: craftingContent:SetPoint("TOPLEFT", window, 10, -50) Asa@59: craftingContent:SetPoint("BOTTOMRIGHT",window, -10, 10) Asa@59: Asa@59: craftingTable = ScrollingTable:CreateST(craftingCols, 22, nil, nil, craftingContent ) Asa@59: Asa@59: IAcc = craftingContent Asa@59: IAccWindow = window Asa@59: craftingTable.frame:SetPoint("TOPLEFT",craftingContent, 0,0) Asa@59: craftingTable.frame:SetPoint("BOTTOMRIGHT", craftingContent, 0, 30) Asa@59: Asa@59: craftingTable:RegisterEvents({ Asa@59: ["OnEnter"] = function (rowFrame, cellFrame, data, cols, row, realrow, column, scrollingTable, ...) Asa@59: if realrow then Asa@59: local data = realData[realrow] Asa@59: Asa@59: GameTooltip:SetOwner(rowFrame, "ANCHOR_CURSOR") Asa@59: GameTooltip:SetHyperlink(data.link) Asa@59: GameTooltip:Show() Asa@59: end Asa@59: end, Asa@59: ["OnLeave"] = function (rowFrame, cellFrame, data, cols, row, realrow, column, scrollingTable, ...) Asa@59: GameTooltip:Hide() Asa@59: end, Asa@59: }); Asa@59: Asa@99: local craftingView = CreateFrame("Button", nil, craftingContent, "UIPanelButtonTemplate") Asa@99: craftingView:SetText("View") Asa@99: craftingView:SetSize(50, 25) Asa@99: craftingView:SetPoint("BOTTOMLEFT", craftingContent, 0, 0) Asa@99: Asa@99: local menu = { Asa@99: { text = "View", isTitle = true}, Asa@99: { text = "To be crafted", func = function() Asa@99: Crafting.filter_have_mats = false Asa@99: Crafting.filter_show_all = false Asa@99: ItemAuditor:RefreshCraftingTable() Asa@99: end }, Asa@99: { text = "Have Mats", func = function() Asa@99: Crafting.filter_have_mats = true Asa@99: Crafting.filter_show_all = false Asa@99: ItemAuditor:RefreshCraftingTable() Asa@99: end }, Asa@99: { text = "All", func = function() Asa@99: Crafting.filter_have_mats = false Asa@99: Crafting.filter_show_all = true Asa@99: ItemAuditor:RefreshCraftingTable() Asa@99: end }, Asa@99: } Asa@99: local menuFrame = CreateFrame("Frame", "ExampleMenuFrame", UIParent, "UIDropDownMenuTemplate") Asa@99: craftingView:SetScript("OnClick", function (self, button, down) Asa@99: EasyMenu(menu, menuFrame, "cursor", 0 , 0, "MENU"); Asa@99: end) Asa@99: Asa@99: Asa@59: btnProcess = CreateFrame("Button", nil, craftingContent, "UIPanelButtonTemplate") Asa@59: btnProcess:SetText("Process") Asa@59: btnProcess:SetSize(100, 25) Asa@59: btnProcess:SetPoint("BOTTOMRIGHT", craftingContent, 0, 0) Asa@59: btnProcess:RegisterForClicks("LeftButtonUp"); Asa@59: Asa@60: local function UpdateProcessTooltip(btn) Asa@59: local data = ItemAuditor:GetCraftingRow(1) Asa@59: if data then Asa@59: GameTooltip:SetOwner(this, "ANCHOR_CURSOR") Asa@59: GameTooltip:SetText(format('Create %sx%s', data.link, data.queue)) Asa@59: GameTooltip:Show() Asa@59: end Asa@60: end Asa@60: btnProcess:SetScript("OnClick", function (self, button, down) Asa@60: local data = ItemAuditor:GetCraftingRow(1) Asa@60: if data then Asa@60: ItemAuditor:Print('Crafting %sx%s', data.link, data.queue) Asa@60: DoTradeSkill(data.tradeSkillIndex, data.queue) Asa@60: data.queue = 0 Asa@60: ItemAuditor:RefreshCraftingTable() Asa@60: UpdateProcessTooltip() Asa@60: end Asa@59: end) Asa@59: Asa@60: btnProcess:SetScript("OnEnter", UpdateProcessTooltip) Asa@60: Asa@59: btnProcess:SetScript("OnLeave", function() Asa@59: GameTooltip:Hide() Asa@59: end) Asa@59: Asa@59: btnSkillet = CreateFrame("Button", nil, craftingContent, "UIPanelButtonTemplate") Asa@99: Asa@59: btnSkillet:SetSize(125, 25) Asa@59: btnSkillet:SetPoint("BOTTOMRIGHT", btnProcess, 'BOTTOMLEFT', 0, 0) Asa@59: btnSkillet:RegisterForClicks("LeftButtonUp"); Asa@59: btnSkillet:SetScript("OnClick", function (self, button, down) Asa@70: Crafting.Export() Asa@59: end) Asa@59: Asa@59: end Asa@70: local destination = select(2, Crafting.GetQueueDestination()) Asa@70: btnSkillet:SetText("Export to "..destination) Asa@70: Asa@59: craftingContent:Show() Asa@59: Asa@59: if container.parent then Asa@59: local width = 80 Asa@59: for i, data in pairs(craftingCols) do Asa@59: width = width + data.width Asa@59: end Asa@59: container.parent:SetWidth(width); Asa@59: end Asa@59: Asa@59: ItemAuditor:RegisterEvent("TRADE_SKILL_SHOW", function() Asa@59: if craftingContent and craftingContent:IsVisible() then Asa@59: ItemAuditor:UpdateCraftingTable() Asa@59: end Asa@59: end) Asa@59: ItemAuditor:UpdateCraftingTable() Asa@59: Asa@59: return craftingContent Asa@59: end Asa@59: Asa@59: Asa@59: Asa@59: ItemAuditor:RegisterTab('Crafting', 'tab_crafting', ShowCrafting) Asa@59: function ItemAuditor:DisplayCrafting() Asa@59: self:CreateFrame('tab_crafting') Asa@59: end Asa@59: Asa@59: local craftingDeciders = {} Asa@59: Asa@72: function Crafting.RegisterCraftingDecider(name, decider, options) Asa@59: craftingDeciders[name] = decider Asa@72: Asa@72: ItemAuditor.Options.args.crafting_options.args['chk'..name] = { Asa@72: type = "toggle", Asa@72: name = "Enable "..name, Asa@72: get = function() return not ItemAuditor.db.profile.disabled_deciders[name] end, Asa@72: set = function(info, value) ItemAuditor.db.profile.disabled_deciders[name] = not value end, Asa@72: order = 11, Asa@72: } Asa@72: Asa@72: if options then Asa@72: ItemAuditor.Options.args.crafting_options.args['decider_'..name] = { Asa@72: handler = {}, Asa@72: name = name, Asa@72: type = 'group', Asa@72: args = options, Asa@72: } Asa@72: end Asa@59: end Asa@59: Asa@59: local lastWinnder = "" Asa@59: local function Decide(data) Asa@59: local newDecision = 0 Asa@74: local reason = "" Asa@59: for name, decider in pairs(craftingDeciders) do Asa@72: if not ItemAuditor.db.profile.disabled_deciders[name] and name ~= lastWinner then Asa@74: newDecision, reason = decider(data) Asa@74: Asa@59: if newDecision > data.queue then Asa@59: data.queue = newDecision Asa@74: lastWinner = (reason or name) Asa@59: return Decide(data) Asa@59: elseif newDecision < 0 then Asa@59: lastWinner = "" Asa@74: return 'VETO: '..(reason or name), -1 Asa@59: end Asa@59: end Asa@59: end Asa@59: Asa@59: winner = lastWinner Asa@59: lastWinner = "" Asa@59: Asa@77: data.queue = ceil(data.queue / GetTradeSkillNumMade(data.tradeSkillIndex)) Asa@77: Asa@59: return winner, data.queue Asa@59: end Asa@59: Asa@59: local function isProfitable(data) Asa@59: if data.profit > 0 and data.profit > ItemAuditor:GetCraftingThreshold() then Asa@59: return 1 Asa@59: end Asa@99: return -1, 'Not Profitable' Asa@59: end Asa@72: Asa@101: local isProfitableOptions = { Asa@101: profitable_threshold = { Asa@101: type = "input", Asa@101: name = "Crafting Threshold", Asa@101: desc = "Don't create items that will make less than this amount of profit", Asa@101: get = function() return Asa@101: Utils.FormatMoney(ItemAuditor:GetCraftingThreshold(), '', true) Asa@101: end, Asa@101: validate = function(info, value) Asa@101: if not Utils.validateMoney(value) then Asa@101: return "Invalid money format" Asa@101: end Asa@101: return true Asa@101: end, Asa@101: set = function(info, value) Asa@101: ItemAuditor.db.char.profitable_threshold = Utils.parseMoney(value) Asa@101: end, Asa@101: usage = "###g ##s ##c", Asa@101: order = 0, Asa@101: }, Asa@101: } Asa@101: Asa@101: Crafting.RegisterCraftingDecider('Is Profitable', isProfitable, isProfitableOptions) Asa@59: Asa@74: Asa@59: Asa@59: local tableData = {} Asa@59: function ItemAuditor:UpdateCraftingTable() Asa@59: if LSW == nil then Asa@59: self:Print("This feature requires LilSparky's Workshop.") Asa@59: return Asa@59: end Asa@59: if GetAuctionBuyout ~= nil then Asa@59: elseif AucAdvanced and AucAdvanced.Version then Asa@59: else Asa@59: self:Print("This feature requires Auctionator, Auctioneer, AuctionLite, or AuctionMaster.") Asa@59: return Asa@59: end Asa@59: wipe(realData) Asa@59: wipe(tableData) Asa@59: Asa@59: local profitableItems = {} Asa@59: local profitableIndex = 1 Asa@59: local numChecked = 0 Asa@59: local row = 1 Asa@59: Asa@59: for i = 1, GetNumTradeSkills() do Asa@59: local itemLink = GetTradeSkillItemLink(i) Asa@86: local itemId = Utils.GetItemID(itemLink) Asa@59: Asa@59: --Figure out if its an enchant or not Asa@59: _, _, _, _, altVerb = GetTradeSkillInfo(i) Asa@59: if LSW.scrollData[itemId] ~= nil and altVerb == 'Enchant' then Asa@59: -- Ask LSW for the correct scroll Asa@59: itemId = LSW.scrollData[itemId]["scrollID"] Asa@59: end Asa@59: Asa@59: local recipeLink = GetTradeSkillRecipeLink(i) Asa@59: local stackSize = 1 Asa@59: if recipeLink ~= nil and itemId ~= nil then Asa@59: local skillName, skillType, numAvailable, isExpanded, altVerb = GetTradeSkillInfo(i) Asa@59: local itemName, itemLink= GetItemInfo(itemId) Asa@74: Asa@74: -- This check has to be here for things like Inscription Research that don't produce an item. Asa@74: if itemLink then Asa@82: local count = ItemAuditor:GetItemCount(itemId) Asa@74: local reagents = {} Asa@74: local totalCost = 0 Asa@74: for reagentId = 1, GetTradeSkillNumReagents(i) do Asa@74: local reagentName, _, reagentCount = GetTradeSkillReagentInfo(i, reagentId); Asa@74: local reagentLink = GetTradeSkillReagentItemLink(i, reagentId) Asa@74: Asa@74: reagents[reagentId] = { Asa@93: link = reagentLink, Asa@74: name = reagentName, Asa@74: count = reagentCount, Asa@74: price = self:GetReagentCost(reagentLink, reagentCount), Asa@94: need = 0, -- This will get populated after the decisions have been made. it can't Asa@94: -- be done before that because highest profit items get priority on materials. Asa@74: } Asa@74: totalCost = totalCost + self:GetReagentCost(reagentLink, reagentCount) Asa@74: end Asa@74: local data = { Asa@74: recipeLink = recipeLink, Asa@95: recipeID = Utils.GetItemID(recipeLink), Asa@74: link = itemLink, Asa@74: name = itemName, Asa@74: count = count, Asa@74: price = (self:GetAuctionPrice(itemLink) or 0), Asa@74: cost = totalCost, Asa@74: profit = (self:GetAuctionPrice(itemLink) or 0) - totalCost, Asa@74: reagents = reagents, Asa@74: count = count, Asa@74: tradeSkillIndex = i, Asa@74: queue = 0, Asa@74: winner = "", Asa@74: } Asa@59: Asa@74: data.winner, data.queue = Decide(data) Asa@99: --[[ Asa@99: If it wasn't vetoed we need to reduce the number by how many are owned Asa@99: but this should not go below 0 Asa@99: ]] Asa@99: if data.queue > 0 then Asa@99: data.queue = max(0, data.queue - count) Asa@99: end Asa@74: Asa@74: -- If a tradeskill makes 5 at a time and something asks for 9, we should only Asa@74: -- craft twice to get 10. Asa@74: data.queue = ceil(data.queue / GetTradeSkillNumMade(i)) Asa@74: Asa@74: realData[row] = data Asa@74: row = row + 1 Asa@59: end Asa@59: end Asa@59: end Asa@99: table.sort(realData, function(a, b) return a.profit*max(1, a.queue) > b.profit*max(1, b.queue) end) Asa@94: Asa@94: local numOwned = {} Asa@94: for key, data in pairs(realData) do Asa@94: data.haveMaterials = true Asa@94: for id, reagent in pairs(data.reagents) do Asa@94: if not numOwned[reagent.link] then Asa@94: numOwned[reagent.link] = ItemAuditor:GetItemCount(ItemAuditor:GetIDFromLink(reagent.link)) Asa@94: end Asa@94: numOwned[reagent.link] = numOwned[reagent.link] - reagent.count Asa@94: Asa@94: if numOwned[reagent.link] < 0 then Asa@94: data.haveMaterials = false Asa@94: reagent.need = min(reagent.count, abs(numOwned[reagent.link])) Asa@94: end Asa@94: end Asa@94: end Asa@94: Asa@68: if craftingTable then Asa@68: craftingTable:SetFilter(tableFilter) Asa@68: self:RefreshCraftingTable() Asa@68: end Asa@60: end Asa@60: Asa@60: function ItemAuditor:RefreshCraftingTable() Asa@99: local displayMaterials Asa@59: for key, data in pairs(realData) do Asa@99: displayMaterials = 'n' Asa@99: if data.haveMaterials then Asa@99: displayMaterials = 'y' Asa@99: end Asa@59: tableData[key] = { Asa@59: data.name, Asa@59: data.cost, Asa@59: data.price, Asa@59: data.winner, Asa@99: abs(data.queue), Asa@99: displayMaterials, Asa@99: data.profit*abs(data.queue), Asa@59: } Asa@59: end Asa@60: craftingTable:SetData(tableData, true) Asa@59: Asa@60: if self:GetCraftingRow(1) then Asa@60: btnProcess:Enable() Asa@60: else Asa@60: btnProcess:Disable() Asa@60: end Asa@59: end Asa@59: Asa@59: function ItemAuditor:GetCraftingRow(row) Asa@59: if craftingTable then Asa@59: for _, index in pairs(craftingTable.sorttable) do Asa@59: local tableRow = tableData[index] Asa@59: if tableFilter(nil, tableRow) then Asa@59: row = row - 1 Asa@59: if row == 0 then Asa@59: return realData[index] Asa@59: end Asa@59: end Asa@59: end Asa@59: elseif realData then Asa@59: return realData[row] Asa@59: end Asa@59: return nil Asa@59: end Asa@67: ItemAuditor.Options.args.crafting = { Asa@67: type = "execute", Asa@67: name = "crafting", Asa@67: desc = "This opens a window to configure a crafting queue.", Asa@67: func = "DisplayCrafting", Asa@67: guiHidden = false, Asa@67: }