-- ------------------------------------------------------------------------------ -- -- TradeSkillMaster_Auctioning -- -- http://www.curse.com/addons/wow/tradeskillmaster_auctioning -- -- -- -- A TradeSkillMaster Addon (http://tradeskillmaster.com) -- -- All Rights Reserved* - Detailed license information included with addon. -- -- ------------------------------------------------------------------------------ -- local TSM = select(2, ...) local Reset = TSM:NewModule("Reset", "AceEvent-3.0") local L = LibStub("AceLocale-3.0"):GetLocale("TradeSkillMaster_Auctioning") -- loads the localization table local resetData, summarySTCache, showCache, itemsReset, justBought = {}, {}, {}, {}, {} local isScanning, doneScanningText, currentItem, GUI local summaryST, auctionST, resetButtons local currentAuction local pendingBuyout function Reset:Show(frame) summaryST = summaryST or Reset:CreateSummaryST(frame.content) summaryST:Show() summaryST:SetData({}) auctionST = auctionST or Reset:CreateAuctionST(frame.content) auctionST:Hide() auctionST:SetData({}) resetButtons = resetButtons or Reset:CreateResetButtons(frame) resetButtons:Show() resetButtons.stop:Enable() resetButtons.stop.isDone = nil resetButtons.stop:SetText(L["Stop"]) resetButtons:Disable() end function Reset:Hide() if summaryST then summaryST:SetData({}) summaryST:Hide() auctionST:SetData({}) auctionST:Hide() resetButtons:Hide() Reset.isSearching = nil end end local function ColSortMethod(st, aRow, bRow, col) local a, b = st:GetCell(aRow, col), st:GetCell(bRow, col) local column = st.cols[col] local direction = column.sort or column.defaultsort or "dsc" local aValue, bValue = ((a.args or {})[1] or a.value), ((b.args or {})[1] or b.value) if direction == "asc" then return aValue < bValue else return aValue > bValue end end function Reset:CreateSummaryST(parent) local stCols = { { name = L["Item"], width = 0.31, }, { name = L["Operation"], width = 0.17, }, { name = L["Quantity (Yours)"], width = 0.13, align = "CENTER" }, { name = L["Total Cost"], width = 0.12, align = "RIGHT", }, { name = L["Target Price"], width = 0.12, align = "RIGHT", }, { name = L["Profit Per Item"], width = 0.12, align = "RIGHT", }, } local handlers = { OnEnter = function(_, data, self) if not data.operation then return end local prices = TSM.Util:GetItemPrices(data.operation, data.itemString, true) GameTooltip:SetOwner(self, "ANCHOR_NONE") GameTooltip:SetPoint("BOTTOMLEFT", self, "TOPLEFT") GameTooltip:AddLine(data.itemLink) GameTooltip:AddLine(L["Max Cost:"].." "..(TSMAPI:FormatTextMoney(prices.resetMaxCost, "|cffffffff") or "---")) GameTooltip:AddLine(L["Min Profit:"].." "..(TSMAPI:FormatTextMoney(prices.resetMinProfit, "|cffffffff") or "---")) GameTooltip:AddLine(L["Max Quantity:"].." "..(TSMAPI:FormatTextMoney(data.operation.resetMaxQuantity, "|cffffffff") or "---")) GameTooltip:AddLine(L["Max Price Per:"].." "..(TSMAPI:FormatTextMoney(data.operation.resetMaxPricePer, "|cffffffff") or "---")) if TSM.Reset:IsScanning() then GameTooltip:AddLine("\n"..L["Must wait for scan to finish before starting to reset."]) else GameTooltip:AddLine(TSMAPI.Design:GetInlineColor("link2").."\n"..L["Click to show auctions for this item."].."|r") GameTooltip:AddLine(TSMAPI.Design:GetInlineColor("link2")..L["Shift-Right-Click to show the options for this item's Auctioning group."].."|r") end GameTooltip:Show() end, OnLeave = function() GameTooltip:Hide() end, OnClick = function(_, data, _, button) if TSM.Reset:IsScanning() then return end if button == "LeftButton" then summaryST:Hide() auctionST:Show() resetButtons.summaryButton:Enable() currentItem = CopyTable(data) Reset:UpdateAuctionST() Reset:SelectAuctionRow(auctionST.rowData[1]) currentItem.isReset = true TSM.Manage:SetInfoText(currentItem) elseif button == "RightButton" then if IsShiftKeyDown() then TSMAPI:ShowOperationOptions("Auctioning", TSM.operationNameLookup[data.operation]) end end end, } local st = TSMAPI:CreateScrollingTable(parent, stCols, handlers) st:SetParent(parent) st:SetAllPoints() st:EnableSorting(true) return st end function Reset:CreateAuctionST(parent) local stCols = { { name = L["Seller"], width = 0.4, }, { name = L["Stack Size"], width = 0.2, align = "CENTER", }, { name = L["Auction Buyout"], width = 0.35, align = "RIGHT", }, } local handlers = { OnClick = function(_, data) Reset:SelectAuctionRow(data) end, } local st = TSMAPI:CreateScrollingTable(parent, stCols, handlers) st:SetParent(parent) st:SetAllPoints() st:EnableSorting(false) return st end function Reset:CreateResetButtons(parent) local height = 24 local frame = CreateFrame("Frame", nil, parent) frame:SetHeight(height) frame:SetWidth(210) frame:SetPoint("BOTTOMRIGHT", -92, 6) frame.Disable = function(self) self.buyout:Disable() self.cancel:Disable() self.summaryButton:Disable() end local function OnCancelClick(self) if self.auction then for i=GetNumAuctionItems("owner"), 1, -1 do if Reset:VerifyAuction(i, "owner", self.auction.record, self.auction.itemString) then CancelAuction(i) break end end end self.auction = nil self:Disable() Reset:RegisterMessage("TSM_AH_EVENTS", Reset.RemoveCurrentAuction) TSMAPI:WaitForAuctionEvents("Cancel") end local function OnStopClick(self) if self.isDone then Reset:Hide() TSM.GUI:ShowSelectionFrame() else TSM.Manage:OnGUIEvent("stop") GUI:Stopped(true) Reset:DoneScanning() end end local function ReturnToSummary() frame:Disable() auctionST:Hide() summaryST:Show() Reset:UpdateSummaryST() end local button = TSMAPI.GUI:CreateButton(frame, 22, "TSMAuctioningResetBuyoutButton") button:SetPoint("TOPLEFT", -5, 0) button:SetWidth(80) button:SetHeight(height) button:SetText(BUYOUT) button:SetScript("OnClick", Reset.BuyAuction) frame.buyout = button local button = TSMAPI.GUI:CreateButton(frame, 18, "TSMAuctioningResetCancelButton") button:SetPoint("TOPLEFT", frame.buyout, "TOPRIGHT", 5, 0) button:SetWidth(70) button:SetHeight(height) button:SetText(L["Cancel"]) button:SetScript("OnClick", OnCancelClick) frame.cancel = button local button = TSMAPI.GUI:CreateButton(frame, 18, "TSMAuctioningResetStopButton") button:SetPoint("TOPLEFT", frame.cancel, "TOPRIGHT", 5, 0) button:SetWidth(60) button:SetHeight(height) button:SetText(L["Stop"]) button:SetScript("OnClick", OnStopClick) button.isDone = nil frame.stop = button local summaryButton = TSMAPI.GUI:CreateButton(frame, 16) summaryButton:SetPoint("TOPRIGHT", parent, "TOPRIGHT", -10, -50) summaryButton:SetHeight(17) summaryButton:SetWidth(150) summaryButton:SetScript("OnClick", ReturnToSummary) summaryButton:SetText(L["Return to Summary"]) frame.summaryButton = summaryButton return frame end function Reset:GetTotalQuantity(itemString) local playerTotal, altTotal = TSMAPI:ModuleAPI("ItemTracker", "playertotal", itemString) if playerTotal and altTotal then local guildTotal = TSMAPI:ModuleAPI("ItemTracker", "guildtotal", itemString) or 0 local auctionTotal = TSMAPI:ModuleAPI("ItemTracker", "auctionstotal", itemString) or 0 return playerTotal + altTotal + guildTotal + auctionTotal else return GetItemCount(itemString, true) end end function Reset:ValidateOperation(itemString, operation) local itemLink = select(2, TSMAPI:GetSafeItemInfo(itemString)) or itemString local prices = TSM.Util:GetItemPrices(operation, itemString, true) -- don't reset this item if their settings are invalid if not prices.minPrice then if not TSM.db.global.disableInvalidMsg then TSM:Printf(L["Did not reset %s because your minimum price (%s) is invalid. Check your settings."], itemLink, operation.minPrice) end elseif not prices.maxPrice then if not TSM.db.global.disableInvalidMsg then TSM:Printf(L["Did not reset %s because your maximum price (%s) is invalid. Check your settings."], itemLink, operation.maxPrice) end elseif not prices.normalPrice then if not TSM.db.global.disableInvalidMsg then TSM:Printf(L["Did not reset %s because your normal price (%s) is invalid. Check your settings."], itemLink, operation.normalPrice) end elseif not prices.resetMaxCost then if not TSM.db.global.disableInvalidMsg then TSM:Printf(L["Did not reset %s because your reset max cost (%s) is invalid. Check your settings."], itemLink, operation.resetMaxCost) end elseif not prices.resetMinProfit then if not TSM.db.global.disableInvalidMsg then TSM:Printf(L["Did not reset %s because your reset min profit (%s) is invalid. Check your settings."], itemLink, operation.resetMinProfit) end elseif not prices.resetResolution then if not TSM.db.global.disableInvalidMsg then TSM:Printf(L["Did not reset %s because your reset resolution (%s) is invalid. Check your settings."], itemLink, operation.resetResolution) end elseif not prices.resetMaxItemCost then if not TSM.db.global.disableInvalidMsg then TSM:Printf(L["Did not reset %s because your reset max item cost (%s) is invalid. Check your settings."], itemLink, operation.resetMaxItemCost) end elseif not prices.undercut then if not TSM.db.global.disableInvalidMsg then TSM:Printf(L["Did not reset %s because your undercut (%s) is invalid. Check your settings."], itemLink or itemString, operation.undercut) end elseif prices.maxPrice < prices.minPrice then if not TSM.db.global.disableInvalidMsg then TSM:Printf(L["Did not reset %s because your maximum price (%s) is lower than your minimum price (%s). Check your settings."], itemLink, operation.maxPrice, operation.minPrice) end elseif prices.normalPrice < prices.minPrice then if not TSM.db.global.disableInvalidMsg then TSM:Printf(L["Did not reset %s because your normal price (%s) is lower than your minimum price (%s). Check your settings."], itemLink, operation.normalPrice, operation.minPrice) end elseif Reset:GetTotalQuantity(itemString) >= operation.resetMaxInventory then -- already have at least max inventory - do nothing here else return true end end function Reset:GetScanListAndSetup(GUIRef, options) local scanList, tempList, groupTemp = {}, {}, {} GUI = GUIRef doneScanningText = nil isScanning = true wipe(resetData) wipe(summarySTCache) wipe(showCache) wipe(itemsReset) wipe(TSM.operationLookup) local temp = {} for itemString, operations in pairs(options.itemOperations) do for _, operation in ipairs(operations) do if operation.resetEnabled and Reset:ValidateOperation(itemString, operation) then temp[itemString] = temp[itemString] or {} tinsert(temp[itemString], operation) end end end for itemString, operations in pairs(temp) do TSM.operationLookup[itemString] = operations tinsert(scanList, itemString) end return scanList end function Reset:ProcessItem(itemString) local operations = TSM.operationLookup[itemString] if not operations then return end for _, operation in ipairs(operations) do Reset:ProcessItemOperation(itemString, operation) end end function Reset:ProcessItemOperation(itemString, operation) local scanData = TSM.Scan.auctionData[itemString] if not scanData then return end local prices = TSM.Util:GetItemPrices(operation, itemString, true) local priceLevels = {} local addNormal, isFirstItem = true, true local currentPriceLevel = -math.huge for _, record in ipairs(scanData.compactRecords) do local itemBuyout = record:GetItemBuyout() if itemBuyout then if not isFirstItem and itemBuyout > prices.minPrice and itemBuyout < prices.maxPrice and itemBuyout > (currentPriceLevel + prices.resetResolution) then if itemBuyout >= prices.normalPrice then addNormal = false end currentPriceLevel = itemBuyout tinsert(priceLevels, itemBuyout) end isFirstItem = false end end if addNormal then tinsert(priceLevels, prices.normalPrice) end for _, targetPrice in ipairs(priceLevels) do local playerCost, cost, quantity, maxItemCost, playerQuantity = 0, 0, 0, 0, 0 for _, record in ipairs(scanData.compactRecords) do local itemBuyout = record:GetItemBuyout() if itemBuyout then if itemBuyout >= targetPrice then break end if itemBuyout > maxItemCost then maxItemCost = itemBuyout end if not record:IsPlayer() then cost = cost + (record:GetItemBuyout() * record.totalQuantity) else playerQuantity = playerQuantity + record.totalQuantity playerCost = playerCost + (record:GetItemBuyout() * record.totalQuantity) end quantity = quantity + record.totalQuantity end end local profit = (targetPrice * quantity - (cost + playerCost)) / quantity if profit > 0 then tinsert(resetData, {prices=prices, itemString=itemString, targetPrice=targetPrice, cost=cost, quantity=quantity, profit=profit, maxItemCost=maxItemCost, playerQuantity=playerQuantity, operation=operation}) end end Reset:UpdateSummaryST() end function Reset:ShouldShow(data) local result = {validCost=true, validQuantity=true, validProfit=true, isValid=true} if data.cost > data.prices.resetMaxCost or data.maxItemCost > data.prices.resetMaxItemCost then result.validCost = false end if data.quantity > data.operation.resetMaxQuantity or data.quantity > (data.operation.resetMaxInventory - Reset:GetTotalQuantity(data.itemString)) then result.validQuantity = false end if data.profit < data.prices.resetMinProfit then result.validProfit = false end return (result.validCost and result.validQuantity and result.validProfit), result end function Reset:GetSummarySTRow(data) local function GetQuantityText(quantity, playerQuantity, isValid) if isValid then if playerQuantity > 0 then return quantity..TSMAPI.Design:GetInlineColor("link2").."("..playerQuantity..")|r" else return quantity end end return "|cffff2222"..quantity.."|r" end local function GetPriceText(amount, isValid) local color if not isValid then color = "|cffff2222" end return TSMAPI:FormatTextMoney(amount, color, true) or "---" end local name, itemLink = TSMAPI:GetSafeItemInfo(data.itemString) local _, shouldShowDetails = Reset:ShouldShow(data) local row = { cols = { { value = itemLink, sortArg = name, }, { value = data.operation and TSM.operationNameLookup[data.operation] or "---", sortArg = data.operation and TSM.operationNameLookup[data.operation] or "---", }, { value = GetQuantityText(data.quantity, data.playerQuantity, shouldShowDetails.validQuantity), sortArg = data.quantity, }, { value = GetPriceText(data.cost or 0, shouldShowDetails.validCost), sortArg = data.cost or 0, }, { value = GetPriceText(data.targetPrice, true), sortArg = data.targetPrice, }, { value = GetPriceText(data.profit, shouldShowDetails.validProfit), sortArg = data.profit, }, }, itemString = data.itemString, targetPrice = data.targetPrice, num = data.quantity, profit = data.profit, operation = data.operation, } return row end function Reset:UpdateSummaryST() local rows = {} local num = 0 for _, data in ipairs(resetData) do if not itemsReset[data.itemString] then if showCache[data] == nil then showCache[data] = Reset:ShouldShow(data) or false end if showCache[data] then local key = data.itemString .. data.targetPrice if not summarySTCache[key] then num = num + 1 end summarySTCache[key] = summarySTCache[key] or Reset:GetSummarySTRow(data) tinsert(rows, summarySTCache[key]) end end end summaryST:SetData(rows) if doneScanningText then TSM.Manage:SetInfoText(doneScanningText) end end function Reset:GetAuctionSTRow(record, index) local function GetSellerText(name) if TSMAPI:IsPlayer(name) then return "|cff99ffff" .. name .. "|r" elseif TSM.db.realm.whitelist[strlower(name)] then return name .. " |cffff2222(" .. L["Whitelist"] .. ")|r" end return name end local row = { cols = { { value = GetSellerText(record.seller), sortArg = record.seller, }, { value = record.count, sortArg = record.count, }, { value = TSMAPI:FormatTextMoney(record.buyout, nil, true) or "---", sortArg = record.buyout, }, }, record = record, itemString = TSMAPI:GetBaseItemString(record.parent:GetItemString(), true), index = index, } return row end function Reset:UpdateAuctionST() local scanData = TSM.Scan.auctionData[currentItem.itemString] local rows = {} for i, record in ipairs(scanData.records) do local itemBuyout = record:GetItemBuyout() if itemBuyout and itemBuyout >= currentItem.targetPrice then break end tinsert(rows, Reset:GetAuctionSTRow(record, i)) end auctionST:SetData(rows) end function Reset:SelectAuctionRow(data) local function OnAuctionFound(index) local row = auctionST.rowData[auctionST:GetSelection()] resetButtons.summaryButton:Enable() resetButtons.buyout:Enable() resetButtons.buyout.auction = {index=index, row=row.itemString, record=row.record} end local row = data resetButtons.buyout:Disable() resetButtons.cancel:Disable() justBought = {} if row.record:IsPlayer() then resetButtons.summaryButton:Enable() resetButtons.cancel:Enable() resetButtons.cancel.auction = {record=row.record, itemString=row.itemString} else Reset:FindCurrentAuctionForBuyout(row.itemString, row.record.buyout, row.record.count) end end function Reset:RemoveCurrentAuction() Reset:UnregisterMessage("TSM_AH_EVENTS") if not currentItem then return end local scanData = TSM.Scan.auctionData[currentItem.itemString] if not scanData then return end local row = auctionST.rowData[auctionST:GetSelection()] if not row then return end scanData:RemoveRecord(row.index) itemsReset[row.itemString] = true if pendingBuyout then TSMAPI:FireEvent("TSM:AUCTIONCONTROL:ITEMBOUGHT", pendingBuyout) pendingBuyout = nil end Reset:UpdateAuctionST() if #auctionST.rowData == 0 then TSM.Scan.auctionData[row.itemString] = nil resetButtons.summaryButton:Enable() resetButtons.summaryButton:Click() Reset:UpdateSummaryST() else TSMAPI:CreateTimeDelay("resetBuyDelay", 0.2, function() Reset:SelectAuctionRow(auctionST.rowData[1]) end) end end function Reset:VerifyAuction(index, tab, record, itemString) local iString = TSMAPI:GetBaseItemString(GetAuctionItemLink(tab, index), true) local _, _, count, _, _, _, minBid, _, buyout, bid = GetAuctionItemInfo(tab, index) return (iString == itemString and bid == record.bid and minBid == record.minBid and buyout == record.buyout and count == record.count) end function Reset:GetStatus() return 0, 0, 100 end function Reset:DoAction() end function Reset:SkipItem() end function Reset:Stop() isScanning = false end function Reset:DoneScanning() local num, totalProfit = 0, 0 local temp = {} for _, data in ipairs(resetData) do if not temp[data.itemString] and Reset:ShouldShow(data) then temp[data.itemString] = true num = num + 1 totalProfit = totalProfit + data.profit * data.quantity end end resetButtons.stop:SetText(L["Restart"]) resetButtons.stop.isDone = true isScanning = false doneScanningText = format(L["Done Scanning!\n\nCould potentially reset %d items for %s profit."], num, TSMAPI:FormatTextMoneyIcon(totalProfit)) TSM.Manage:SetInfoText(doneScanningText) end function Reset:SetupForAction() end function Reset:GetCurrentItem() end function Reset:IsScanning() return isScanning end local function ValidateAuction(index, listType) local itemString = TSMAPI:GetBaseItemString(GetAuctionItemLink(listType, index), true) local _, _, count, _, _, _, _, _, buyout = GetAuctionItemInfo(listType, index) return count == currentAuction.count and buyout == currentAuction.buyout and itemString == currentAuction.itemString end function Reset:BuyAuction() local altIndex, mainIndex, foundAuction TSMAPI:CancelFrame("resetFindRepeat") Reset.isSearching = nil for i=GetNumAuctionItems("list"), 1, -1 do if ValidateAuction(i, "list") then if not justBought[i] then mainIndex = i break else altIndex = altIndex or i end end end if mainIndex or altIndex then PlaceAuctionBid("list", mainIndex or altIndex, currentAuction.buyout) foundAuction = true justBought[mainIndex or altIndex] = true pendingBuyout = { itemString = currentAuction.itemString, count = currentAuction.count, buyout = currentAuction.buyout, } end resetButtons.buyout:Disable() if foundAuction then -- wait for all the events that are triggered by this action Reset:RegisterMessage("TSM_AH_EVENTS", Reset.RemoveCurrentAuction) TSMAPI:WaitForAuctionEvents("Buyout") else TSM:Print(L["Auction not found. Skipped."]) Reset:RemoveCurrentAuction() end end function Reset:OnAuctionFound(index) if not Reset.isSearching then return end resetButtons.buyout:Enable() end function Reset:FindCurrentAuctionForBuyout(itemString, buyout, count) currentAuction = {itemString=itemString, buyout=buyout, count=count} TSMAPI:CancelFrame("resetFindRepeat") TSMAPI.AuctionScan:FindAuction(function(...) Reset:OnAuctionFound(...) end, currentAuction) TSMAPI:CreateTimeDelay("resetFindRepeat", 0.1, function(...) local isFindScanning = TSMAPI.AuctionScan:IsFindScanning() if TSMAPI:AHTabIsVisible("Auctioning") then if not isFindScanning then TSMAPI.AuctionScan:FindAuction(function(...) Reset:OnAuctionFound(...) end, currentAuction) else if isFindScanning ~= currentAuction.itemString then TSMAPI.AuctionScan:FindAuction(function(...) Reset:OnAuctionFound(...) end, currentAuction) end end end end, 0.1) Reset.isSearching = true end