Compare commits

..

No commits in common. 'dev' and 'main' have entirely different histories.
dev ... main

  1. 130
      .gitignore
  2. 22
      AGENTS.md
  3. 135
      README.md
  4. 100
      TradeSkillMaster/Auction/AuctionControl.lua
  5. 1
      TradeSkillMaster/Auction/AuctionFrame.lua
  6. 49
      TradeSkillMaster/Auction/AuctionScanning.lua
  7. 90
      TradeSkillMaster/Core/Prices.lua
  8. 40
      TradeSkillMaster/GUI/BuildPage.lua
  9. 7
      TradeSkillMaster/GUI/TSMWidgets/TSMSlider.lua
  10. 2
      TradeSkillMaster/Libs/LibAuctionScan-1.0/LibAuctionScan-1.0.lua
  11. 6
      TradeSkillMaster/TradeSkillMaster.lua
  12. 2
      TradeSkillMaster/TradeSkillMaster.toc
  13. 16
      TradeSkillMaster_Accounting/Locale/enUS.lua
  14. 7
      TradeSkillMaster_Accounting/Modules/data.lua
  15. 200
      TradeSkillMaster_Accounting/Modules/gui.lua
  16. 111
      TradeSkillMaster_Accounting/TradeSkillMaster_Accounting.lua
  17. 6
      TradeSkillMaster_AuctionDB/Locale/deDE.lua
  18. 37
      TradeSkillMaster_AuctionDB/Locale/enUS.lua
  19. 6
      TradeSkillMaster_AuctionDB/Locale/esES.lua
  20. 6
      TradeSkillMaster_AuctionDB/Locale/esMX.lua
  21. 6
      TradeSkillMaster_AuctionDB/Locale/frFR.lua
  22. 6
      TradeSkillMaster_AuctionDB/Locale/koKR.lua
  23. 6
      TradeSkillMaster_AuctionDB/Locale/ptBR.lua
  24. 6
      TradeSkillMaster_AuctionDB/Locale/ruRU.lua
  25. 6
      TradeSkillMaster_AuctionDB/Locale/zhCN.lua
  26. 6
      TradeSkillMaster_AuctionDB/Locale/zhTW.lua
  27. 471
      TradeSkillMaster_AuctionDB/Modules/ChannelSync.lua
  28. 293
      TradeSkillMaster_AuctionDB/Modules/GUI.lua
  29. 305
      TradeSkillMaster_AuctionDB/Modules/Scanning.lua
  30. 61
      TradeSkillMaster_AuctionDB/Modules/config.lua
  31. 73
      TradeSkillMaster_AuctionDB/Modules/data.lua
  32. 34
      TradeSkillMaster_AuctionDB/TradeSkillMaster_AuctionDB.lua
  33. 5
      TradeSkillMaster_AuctionDB/TradeSkillMaster_AuctionDB.toc
  34. 1
      TradeSkillMaster_Auctioning/TradeSkillMaster_Auctioning.lua
  35. 6
      TradeSkillMaster_Auctioning/locale/enUS.lua
  36. 51
      TradeSkillMaster_Auctioning/modules/GUI.lua
  37. 12
      TradeSkillMaster_Auctioning/modules/Options.lua
  38. 36
      TradeSkillMaster_Auctioning/modules/PostScan.lua
  39. 10
      TradeSkillMaster_Auctioning/modules/ResetScan.lua
  40. 18
      TradeSkillMaster_Auctioning/modules/ScanUtil.lua
  41. 21
      TradeSkillMaster_Auctioning/modules/manage.lua
  42. 3
      TradeSkillMaster_Crafting/Locale/enUS.lua
  43. 489
      TradeSkillMaster_Crafting/Modules/CraftingGUI.lua
  44. 34
      TradeSkillMaster_Crafting/Modules/Gather.lua
  45. 34
      TradeSkillMaster_Crafting/Modules/Inventory.lua
  46. 24
      TradeSkillMaster_Crafting/Modules/Queue.lua
  47. 6
      TradeSkillMaster_Crafting/TradeSkillMaster_Crafting.lua
  48. 140
      TradeSkillMaster_Mailing/Modules/Inbox.lua
  49. 1
      TradeSkillMaster_Mailing/TradeSkillMaster_Mailing.lua
  50. 2
      TradeSkillMaster_Shopping/Locale/enUS.lua
  51. 35
      TradeSkillMaster_Shopping/TradeSkillMaster_Shopping.lua
  52. 2
      TradeSkillMaster_Shopping/modules/Destroying.lua
  53. 13
      TradeSkillMaster_Shopping/modules/Options.lua
  54. 7
      TradeSkillMaster_Shopping/modules/Search.lua
  55. 29
      TradeSkillMaster_Shopping/modules/Util.lua
  56. 169
      TradeSkillMaster_Shopping/sidebar/Groups.lua
  57. 50
      TradeSkillMaster_Shopping/sidebar/Other.lua
  58. 5
      TradeSkillMaster_Warehousing/Locale/enUS.lua
  59. 13
      TradeSkillMaster_Warehousing/Modules/bankui.lua
  60. 75
      TradeSkillMaster_Warehousing/Modules/move.lua
  61. 5
      TradeSkillMaster_Warehousing/TradeSkillMaster_Warehousing.lua
  62. 40
      bin/link_addons.sh
  63. 78
      scripts/link_and_go.sh

130
.gitignore vendored

@ -1,135 +1,7 @@
*~
.env
.DS_Store
.release
.install
.lua/*
.vscode
.idea.
archive-*/
02a7e8db-fd8d-4213-8d9c-ab66009a487d.zip
408e9dd4-e03b-4a89-b1f1-a488136d6283.zip
4a37aaf8-9aa6-4246-8c0d-1cafc6b994a1.zip
4dfc20a9-9140-4893-b179-be02e48463be.zip
90bb9009-f7ef-46ff-8a2e-38192b1823a9.zip
9bc9a709-ad9d-4536-9841-f0c5f04b6822.zip
a1464a44-7ec6-4cec-896d-d24131e2ac4a.zip
AI_VoiceOver
AI_VoiceOverData_Vanilla
AllStats
AtlasLoot
AtlasLoot_BurningCrusade
AtlasLoot_Cache
AtlasLoot_Crafting_OriginalWoW
AtlasLoot_Crafting_TBC
AtlasLoot_Crafting_Wrath
AtlasLoot_OriginalWoW
AtlasLoot_Vanity
AtlasLoot_WorldEvents
AtlasLoot_WrathoftheLichKing
AutoQuest
b823d87f-b909-4698-9ac1-a6d9934e618e.zip
Bagnon
Bagnon_Config
Bagnon_Forever
Bagnon_GuildBank
Bagnon_Tooltips
Blizzard_AchievementUI
Blizzard_ArenaUI
Blizzard_AuctionUI
Blizzard_BarbershopUI
Blizzard_BattlefieldMinimap
Blizzard_BindingUI
Blizzard_Calendar
Blizzard_CombatLog
Blizzard_CombatText
Blizzard_DebugTools
Blizzard_GlyphUI
Blizzard_GMChatUI
Blizzard_GMSurveyUI
Blizzard_GuildBankUI
Blizzard_InspectUI
Blizzard_ItemSocketingUI
Blizzard_MacroUI
Blizzard_RaidUI
Blizzard_TalentUI
Blizzard_TimeManager
Blizzard_TokenUI
Blizzard_TradeSkillUI
Blizzard_TrainerUI.old
c81d84c4-62a1-4839-b245-b48123ba3973.zip
Chatter
d5d9dbc2-4e0f-4472-9301-ee99107f0039.zip
d613bf63-a1bb-4311-b82a-4cbe7c2fd272.zip
DBM-AQ20
DBM-AQ40
DBM-BlackTemple
DBM-BurningCrusade
DBM-BWL
DBM-Core
DBM-GruulsLair
DBM-GUI
DBM-Hyjal
DBM-Karazhan
DBM-MagtheridonsLair
DBM-MC
DBM-Naxx
DBM-Onyxia
DBM-Outlands
DBM-Party-BC
DBM-Party-Vanilla
DBM-PvP
DBM-Serpentshrine
DBM-SpellTimers
DBM-Sunwell
DBM-TheEye
DBM-WorldEvents
DBM-ZG
DBM-ZulAman
dcfbd3bc-59dd-4f48-b9c0-4b7db6beb0ec.zip
Decursive
Details
Details_Compare2
Details_DataStorage
Details_EncounterDetails
Details_RaidCheck
Details_Streamer
Details_TargetCaller
Details_TinyThreat
Details_Vanguard
df1a0fb8-4b69-4468-99ad-6fdb2ef72b78.zip
e0910ca2-c063-47de-a651-8705d6d70be8.zip
ElvUI
ElvUI_AddOnSkins
ElvUI_Enhanced
ElvUI_EnhancedFriendsList
ElvUI_ExtraActionBars
ElvUI_OptionsUI
ESN_Rare
f024ba61-e2a4-47a5-86c7-3d1d5542cc5d.zip
f33cd75c-e6a9-4330-ad26-f94483d0f8a7.zip
fa9e16f2-0fbb-4f77-b4b5-af33af8987e8.zip
GatherMate2
.git
.github
LootCollector
New Folder
Omen
OmniCC
OmniCC_Config
Outfitter
Passloot
pfQuest
pfQuest-ascension
README.md
SilverDragon
SilverDragon_Data
tmp
TomTom
TrinketMenu
WeakAuras
WeakAurasArchive
WeakAurasModelPaths
WeakAurasOptions
WTF/
omanfred/
.idea

22
AGENTS.md

@ -1,22 +0,0 @@
# Repository Guidelines
## Project Structure & Module Organization
TradeSkillMaster is a World of Warcraft addon composed of a core module and several feature modules. Each module lives in its own top-level folder (for example `TradeSkillMaster/`, `TradeSkillMaster_Auctioning/`) and contains a `.toc` manifest plus one or more `.lua` files. Most modules also include `ChangeLog.txt` and `LICENSE.txt`. The root contains repository metadata like `README.md` and `.github/` templates.
## Build, Test, and Development Commands
There is no build system in this repository. To run locally, copy the desired module folders into your WoW AddOns directory (for example `World of Warcraft/Interface/AddOns/TradeSkillMaster_Auctioning`) and use `/reload` in-game after changes. Manual in-game verification is the default test loop.
## Coding Style & Naming Conventions
Lua files use tab indentation in existing code; follow that style when editing. Keep module folder names and primary files aligned (`TradeSkillMaster_Mailing/TradeSkillMaster_Mailing.lua`, `TradeSkillMaster_Mailing.toc`). Use clear, descriptive function and local variable names, and avoid introducing new globals unless required by the addon API.
## Testing Guidelines
No automated test framework is present. Validate changes by enabling the addon in-game and exercising the affected feature paths. When changes are module-specific, test both module initialization (login/reload) and the primary UI workflows.
## Commit & Pull Request Guidelines
Recent commit messages are short and action-focused (for example “add group scan”, “skip wipe of data when searching”). Keep messages concise and imperative when possible. For pull requests, follow the template in `.github/PULL_REQUEST_TEPMLATE.md`: include a clear description, reference issues with `Fixes #<id>`, select a change type, describe test steps, and complete the self-review checklist.
## Agent Notes
When adding or renaming modules, update the `.toc` and matching `.lua` filenames together, and ensure any new files are listed in the `.toc` manifest. Keep changes scoped to the relevant module folder to avoid cross-module regressions. Increment numeric `Version`/`X-Curse-Packaged-Version` in `TradeSkillMaster_AuctionDB/TradeSkillMaster_AuctionDB.toc` and `Version` in `TradeSkillMaster/TradeSkillMaster.toc` on each commit.
Local Ascension client paths used in this workspace:
- AddOns: `/home/zalox/Games/ascension-wow/drive_c/Program Files/Ascension Launcher/resources/client/Interface/AddOns`
- SavedVariables: `/home/zalox/Games/ascension-wow/drive_c/Program Files/Ascension Launcher/resources/client/WTF/Account/omanfred/SavedVariables`

135
README.md

@ -1,134 +1,3 @@
# TradeSkillMaster (Ascension.gg)
# TradeSkillMaster
TradeSkillMaster (TSM) is a World of Warcraft addon suite. This fork is tailored for Ascension.gg and includes the core addon plus feature modules.
## Modules
Each module lives in its own top-level folder and is loaded by its `.toc` manifest:
- `TradeSkillMaster` (core)
- `TradeSkillMaster_Accounting`
- `TradeSkillMaster_AuctionDB`
- `TradeSkillMaster_Auctioning`
- `TradeSkillMaster_Crafting`
- `TradeSkillMaster_Destroying`
- `TradeSkillMaster_ItemTracker`
- `TradeSkillMaster_Mailing`
- `TradeSkillMaster_Shopping`
- `TradeSkillMaster_Warehousing`
## Install (Local)
Copy the desired module folders into your AddOns directory and reload the game:
- AddOns: `/home/zalox/Games/ascension-wow/drive_c/Program Files/Ascension Launcher/resources/client/Interface/AddOns`
- SavedVariables: `/home/zalox/Games/ascension-wow/drive_c/Program Files/Ascension Launcher/resources/client/WTF/Account/omanfred/SavedVariables`
In-game, use `/reload` after changes.
## Development Notes
- Lua files use tab indentation.
- Keep `.toc` and primary `.lua` filenames aligned with the module name.
- If adding files, list them in the module `.toc`.
- Versions are tracked in `TradeSkillMaster/TradeSkillMaster.toc` and `TradeSkillMaster_AuctionDB/TradeSkillMaster_AuctionDB.toc`.
## Custom Price Sources (Full Reference)
TSM price strings can reference built-in data sources and combine them with math. Source names are case-insensitive.
### Core sources
- `VendorBuy`: Buy from vendor.
- `VendorSell`: Sell to vendor.
- `Disenchant`: Disenchant value (if available).
### AuctionDB sources
- `DBMarket`: AuctionDB market value (scan/app historical average).
- `DBMinBuyout`: AuctionDB minimum buyout from the most recent scan data.
### Accounting sources
- `avgSell`: Average sell price.
- `avgBuy`: Average buy price.
- `smartAvgBuy`: Smart average buy price (uses current holdings to weight recent buys).
- `lastBuy`: Last buy price (most recent purchase; also shown under Last Purchased in Accounting tooltips).
- `maxSell`: Maximum sell price.
- `maxBuy`: Maximum buy price.
- `minSell`: Minimum sell price.
- `minBuy`: Minimum buy price.
### Crafting sources
- `Crafting`: Crafting cost.
- `matPrice`: Crafting material cost.
### Optional addon sources
Available only if the addon is installed and enabled.
- `AucAppraiser`: Auctioneer - Appraiser.
- `AucMinBuyout`: Auctioneer - Minimum Buyout.
- `AucMarket`: Auctioneer - Market Value.
- `AtrValue`: Auctionator - Auction Value.
### Functions and syntax
- Math operators: `+`, `-`, `*`, `/`, `^`
- Money literals: `100g`, `50s`, `25c`, or combined `12g 34s 56c`
- Convert another item: `convert(priceSource, itemLink)` or `convert(priceSource, item:12345)`
### Function: min(...)
Returns the smallest of the provided values. Ignores invalid values.
Example: `min(dbmarket, 250g)`
### Function: max(...)
Returns the largest of the provided values. Ignores invalid values.
Example: `max(dbminbuyout, 100g)`
### Function: avg(...)
Returns the average (rounded) of the provided values. Ignores invalid values.
Example: `avg(dbmarket, dbminbuyout)`
### Function: first(...)
Returns the first valid value from left to right.
Example: `first(dbminbuyout, dbmarket, 100g)`
### Function: check(checkValue, ifValue, elseValue)
Returns `ifValue` when `checkValue` is greater than 0, otherwise `elseValue`.
Example: `check(dbminbuyout, dbminbuyout, dbmarket)`
### Function: ifgt(a, b, ifValue, elseValue)
Returns `ifValue` when `a` is greater than `b`, otherwise `elseValue`.
Example: `ifgt(dbminbuyout, 200g, dbminbuyout, dbmarket)`
### Function: ifgte(a, b, ifValue, elseValue)
Returns `ifValue` when `a` is greater than or equal to `b`, otherwise `elseValue`.
Example: `ifgte(dbmarket, 500g, 500g, dbmarket)`
### Function: iflt(a, b, ifValue, elseValue)
Returns `ifValue` when `a` is less than `b`, otherwise `elseValue`.
Example: `iflt(dbminbuyout, 50g, 50g, dbminbuyout)`
### Function: iflte(a, b, ifValue, elseValue)
Returns `ifValue` when `a` is less than or equal to `b`, otherwise `elseValue`.
Example: `iflte(dbmarket, 20g, dbmarket, 20g)`
### Function: ifeq(a, b, ifValue, elseValue)
Returns `ifValue` when `a` equals `b`, otherwise `elseValue`.
Example: `ifeq(dbminbuyout, dbmarket, dbminbuyout, dbmarket)`
### Function: round(value[, step])
Rounds to the nearest `step` (default `1`).
Example: `round(dbmarket, 1g)`
### Function: roundup(value[, step])
Rounds up to the nearest `step` (default `1`).
Example: `roundup(dbmarket, 5g)`
### Function: rounddown(value[, step])
Rounds down to the nearest `step` (default `1`).
Example: `rounddown(dbmarket, 5g)`
### Examples
- `dbmarket * 0.9` (90% of market value)
- `max(dbminbuyout, 100g)` (at least 100g)
- `min(dbmarket, 250g)` (cap at 250g)
- `ifgt(dbminbuyout, 200g, dbminbuyout, dbmarket)` (prefer min buyout if above 200g)
Notes:
- Sources return values only when their module has data (for example, no AuctionDB scan means empty values).
- Use `DBMarket` for stable pricing and `DBMinBuyout` for recent lows.
## Testing
There is no automated test framework. Validate changes in-game by:
1. Logging in / reloading to verify module initialization.
2. Exercising the relevant UI workflows.
This is the repository for Trade Skill Master (TSM). Modified for Ascension.gg.

100
TradeSkillMaster/Auction/AuctionControl.lua

@ -15,9 +15,6 @@ TSMAPI:RegisterForTracing(private, "TradeSkillMaster.AuctionControl_private")
LibStub("AceEvent-3.0"):Embed(private)
private.matchList = {}
private.currentPage = {}
private.shoppingPurchasedCounts = {}
private.shoppingTotalCache = {}
private.shoppingOperationSettings = {}
local function GetNumInBags(baseItemString)
@ -46,72 +43,6 @@ local function ValidateAuction(index, list)
return count == private.currentAuction.count and buyout == private.currentAuction.buyout and itemString == private.currentAuction.itemString, data
end
local function GetShoppingTotalQuantity(itemString)
itemString = TSMAPI:GetBaseItemString(itemString, true) or itemString
local cached = private.shoppingTotalCache[itemString]
if cached ~= nil then
return cached
end
local player, alts = TSMAPI:ModuleAPI("ItemTracker", "playertotal", itemString)
if not player then
alts = nil
end
player = player or 0
alts = alts or 0
local guild = TSMAPI:ModuleAPI("ItemTracker", "guildtotal", itemString) or 0
local auctions = TSMAPI:ModuleAPI("ItemTracker", "auctionstotal", itemString) or 0
local bought = private.shoppingPurchasedCounts[itemString] or 0
local total = player + alts + guild + auctions + bought
private.shoppingTotalCache[itemString] = total
return total
end
local function GetOperationMaxPrice(opSettings, itemString)
if not opSettings or opSettings.maxPrice == nil then return end
if type(opSettings.maxPrice) == "number" then
return opSettings.maxPrice
elseif type(opSettings.maxPrice) == "string" then
local func = TSMAPI:ParseCustomPrice(opSettings.maxPrice)
return func and func(itemString) or nil
end
end
local function GetShoppingOperationSettings(opName)
local settings = private.shoppingOperationSettings[opName]
if settings == nil then
TSMAPI:UpdateOperation("Shopping", opName)
settings = TSM.operations["Shopping"] and TSM.operations["Shopping"][opName] or nil
private.shoppingOperationSettings[opName] = settings or false
end
return settings ~= false and settings or nil
end
local function ShoppingBuyoutAllowed(itemString, count, perItemBuyout)
local operations = TSMAPI:GetItemOperation(itemString, "Shopping")
if not operations or #operations == 0 then return true end
itemString = TSMAPI:GetBaseItemString(itemString, true) or itemString
local totalQty = GetShoppingTotalQuantity(itemString)
for _, opName in ipairs(operations) do
local opSettings = GetShoppingOperationSettings(opName)
if opSettings then
local maxRestock = tonumber(opSettings.maxRestock) or 0
if maxRestock > 0 and (totalQty + count) > maxRestock then
-- over restock for this operation
elseif opSettings.evenStacks and count % 5 ~= 0 then
-- not an even stack for this operation
elseif opSettings.showAboveMaxPrice then
return true
else
local opMaxPrice = GetOperationMaxPrice(opSettings, itemString)
if opMaxPrice and perItemBuyout and perItemBuyout > 0 and perItemBuyout <= opMaxPrice then
return true
end
end
end
end
return false
end
local diffFrame = CreateFrame("Frame")
diffFrame:Hide()
diffFrame.num = 0
@ -215,15 +146,6 @@ end
function private:DoBuyout()
if private.isSearching or not private.currentAuction or not private.confirmationFrame:IsVisible() then return end
local perItemBuyout = private.currentAuction.buyout and private.currentAuction.count and (private.currentAuction.buyout / private.currentAuction.count)
if TSM.groupBuyoutCheck and (not perItemBuyout or not TSM.groupBuyoutCheck(private.currentAuction.itemString, private.currentAuction.count, perItemBuyout)) then
TSM:Print("Max restock quantity reached for this item.")
return
end
if private.module == "Shopping" and TSM.shoppingGroupSearchActive and not ShoppingBuyoutAllowed(private.currentAuction.itemString, private.currentAuction.count, perItemBuyout) then
TSM:Print("Max restock quantity reached for this item.")
return
end
for i=#private.matchList, 1, -1 do
local aucIndex = private.matchList[i]
@ -353,14 +275,8 @@ function private:AUCTION_ITEM_LIST_UPDATE()
TSMAPI.AuctionScan:CacheRemove(prevAuction.itemString, private.currentCacheIndex)
private.currentCacheIndex = nil
end
if private.module == "Shopping" and prevAuction.itemString and prevAuction.count then
local baseItemString = TSMAPI:GetBaseItemString(prevAuction.itemString, true)
private.shoppingPurchasedCounts[baseItemString] = (private.shoppingPurchasedCounts[baseItemString] or 0) + prevAuction.count
private.shoppingTotalCache[baseItemString] = nil
end
TSM:AuctionControlCallback("OnBuyout", prevAuction)
TSMAPI:FireEvent("TSM:AUCTIONCONTROL:ITEMBOUGHT", prevAuction)
end
end
@ -395,11 +311,6 @@ function TSMAPI.AuctionControl:ShowControlButtons(parent, rt, callback, module,
private.rt = rt
private.callback = callback
private.module = module
if module == "Shopping" then
wipe(private.shoppingPurchasedCounts)
wipe(private.shoppingTotalCache)
wipe(private.shoppingOperationSettings)
end
private.postBidPercent = postBidPercent
private.postUndercut = postUndercut
return private.controlButtons
@ -436,17 +347,6 @@ function private:ShowConfirmationWindow()
end
private:SetCurrentAuction(private.rt:GetSelectedAuction())
if not private.currentAuction then return end
if private.confirmationMode == "Buyout" then
local perItemBuyout = private.currentAuction.buyout and private.currentAuction.count and (private.currentAuction.buyout / private.currentAuction.count)
if TSM.groupBuyoutCheck and (not perItemBuyout or not TSM.groupBuyoutCheck(private.currentAuction.itemString, private.currentAuction.count, perItemBuyout)) then
TSM:Print("Max restock quantity reached for this item.")
return
end
if private.module == "Shopping" and TSM.shoppingGroupSearchActive and not ShoppingBuyoutAllowed(private.currentAuction.itemString, private.currentAuction.count, perItemBuyout) then
TSM:Print("Max restock quantity reached for this item.")
return
end
end
private:RegisterEvent("AUCTION_ITEM_LIST_UPDATE")
diffFrame.num = 0

1
TradeSkillMaster/Auction/AuctionFrame.lua

@ -43,7 +43,6 @@ function private:CreateTSMAHTab(moduleName, callbackShow, callbackHide)
tab:SetText(TSMAPI.Design:GetInlineColor("link2")..moduleName.."|r")
tab:SetNormalFontObject(GameFontHighlightSmall)
tab.isTSMTab = moduleName
_G["TSM"..moduleName.."TabButton"] = tab
tab:SetPoint("LEFT", _G["AuctionFrameTab"..n-1], "RIGHT", -8, 0)
tab:Show()
PanelTemplates_SetNumTabs(AuctionFrame, n)

49
TradeSkillMaster/Auction/AuctionScanning.lua

@ -14,7 +14,7 @@ TSMAPI.AuctionScan = {}
local RETRY_DELAY = 2
local MAX_RETRIES = 4
local BASE_DELAY = 0.10 -- time to delay for before trying to scan a page again when it isn't fully loaded
local private = { callbackHandler = nil, query = {}, options = {}, data = {}, isScanning = nil, isPaused = nil, pauseReason = nil }
local private = { callbackHandler = nil, query = {}, options = {}, data = {}, isScanning = nil }
TSMAPI:RegisterForTracing(private, "TradeSkillMaster.AuctionScanning_private")
local scanCache = {}
@ -34,7 +34,7 @@ local function eventHandler(event)
-- auction house was closed, make sure all scanning is stopped
AuctionScanning:UnregisterEvent("AUCTION_ITEM_LIST_UPDATE")
private.auctionHouseShown = false
DoCallback("INTERRUPTED", private.data)
DoCallback("INTERRUPTED")
private:StopScanning()
elseif event == "AUCTION_ITEM_LIST_UPDATE" then
-- gets called whenever the AH window is updated (something is shown in the results section)
@ -147,7 +147,6 @@ function TSMAPI.AuctionScan:RunQuery(query, callbackHandler, resolveSellers, max
-- setup other stuff
wipe(private.data)
private.isScanning = true
private.isPaused = nil
private.callbackHandler = callbackHandler
private.resolveSellers = resolveSellers
private.scanType = "query"
@ -180,7 +179,6 @@ function TSMAPI.AuctionScan:ScanLastPage(callbackHandler)
-- setup other stuff
wipe(private.data)
private.isScanning = true
private.isPaused = nil
private.callbackHandler = callbackHandler
private.scanType = "lastPage"
@ -191,7 +189,7 @@ end
-- sends a query to the AH frame once it is ready to be queried (uses frame as a delay)
function private:SendQuery()
if not private.isScanning or private.isPaused then return end
if not private.isScanning then return end
if CanSendAuctionQuery() then
-- stop delay timer
@ -210,7 +208,7 @@ end
--scans the currently shown page of auctions and collects all the data
function private:ScanAuctions()
if not private.isScanning or private.isPaused then return end
if not private.isScanning then return end
local shown, total = GetNumAuctionItems("list")
local totalPages = ceil(total / NUM_AUCTION_ITEMS_PER_PAGE)
@ -305,9 +303,6 @@ function private:ScanAuctions()
end
-- query the next page and continue scanning
if private.isPaused then
return
end
private:SendQuery()
end
@ -368,47 +363,14 @@ function private:StopScanning()
TSMAPI:CancelFrame("updateDelay")
AuctionScanning:UnregisterEvent("AUCTION_ITEM_LIST_UPDATE")
private.isScanning = nil
private.isPaused = nil
private.pauseReason = nil
private.pageTemp = nil
end
-- API for stopping the scan
-- returns true/false if we were/weren't actually scanning
function TSMAPI.AuctionScan:StopScan(force)
if force == nil then
force = true
end
if private.isPaused and private.pauseReason == "AuctionDB" and not force then return false end
function TSMAPI.AuctionScan:StopScan()
private:StopScanning()
TSM:StopGeneratingQueries()
return true
end
function TSMAPI.AuctionScan:PauseScan(reason)
if not private.isScanning or private.isPaused then return false end
private.isPaused = true
private.pauseReason = reason
TSMAPI:CancelFrame("queryDelay")
TSMAPI:CancelFrame("updateDelay")
AuctionScanning:UnregisterEvent("AUCTION_ITEM_LIST_UPDATE")
return true
end
function TSMAPI.AuctionScan:ResumeScan()
if not private.isScanning or not private.isPaused then return false end
private.isPaused = nil
private.pauseReason = nil
private:SendQuery()
return true
end
function TSMAPI.AuctionScan:IsPaused()
return private.isPaused and true or false
end
function TSMAPI.AuctionScan:IsScanning()
return private.isScanning and true or false
end
@ -460,7 +422,6 @@ function TSMAPI.AuctionScan:GetNumPages(query, callbackHandler)
-- setup other stuff
wipe(private.data)
private.isScanning = true
private.isPaused = nil
private.callbackHandler = callbackHandler
private.scanType = "numPages"

90
TradeSkillMaster/Core/Prices.lua

@ -28,14 +28,6 @@ local MATH_FUNCTIONS = {
["max"] = "_max",
["first"] = "_first",
["check"] = "_check",
["ifgt"] = "_ifgt",
["ifgte"] = "_ifgte",
["iflt"] = "_iflt",
["iflte"] = "_iflte",
["ifeq"] = "_ifeq",
["round"] = "_round",
["roundup"] = "_roundup",
["rounddown"] = "_rounddown",
}
function TSMAPI:GetPriceSources()
@ -76,7 +68,7 @@ end
-- validates a price string that was passed into TSMAPI:ParseCustomPrice
local supportedOperators = { "+", "-", "*", "/", "^" }
local supportedOperators = { "+", "-", "*", "/" }
local function ParsePriceString(str, badPriceSource)
if tonumber(str) then
return function() return tonumber(str) end
@ -324,7 +316,7 @@ local function ParsePriceString(str, badPriceSource)
-- replace math functions special custom function names
for word, funcName in pairs(MATH_FUNCTIONS) do
str = gsub(str, "([^%w_])"..word.."([^%w_])", "%1"..funcName.."%2")
str = gsub(str, word, funcName)
end
-- remove any unused values
@ -350,10 +342,6 @@ local function ParsePriceString(str, badPriceSource)
TSM_PRICE_TEMP.lastPrint = time()
TSM_PRICE_TEMP.loopError(origStr)
end
TSM_PRICE_TEMP.num = TSM_PRICE_TEMP.num - 1
if isTop then
TSM_PRICE_TEMP.num = nil
end
return
end
local function isNAN(num)
@ -408,67 +396,6 @@ local function ParsePriceString(str, badPriceSource)
elseValue = elseValue or NAN
return check > 0 and ifValue or elseValue
end
local function _ifgt(...)
local a, b, c, d = ...
if isNAN(a) or isNAN(b) then return NAN end
if a > b then
return (c and not isNAN(c)) and c or NAN
end
return (d and not isNAN(d)) and d or NAN
end
local function _ifgte(...)
local a, b, c, d = ...
if isNAN(a) or isNAN(b) then return NAN end
if a >= b then
return (c and not isNAN(c)) and c or NAN
end
return (d and not isNAN(d)) and d or NAN
end
local function _iflt(...)
local a, b, c, d = ...
if isNAN(a) or isNAN(b) then return NAN end
if a < b then
return (c and not isNAN(c)) and c or NAN
end
return (d and not isNAN(d)) and d or NAN
end
local function _iflte(...)
local a, b, c, d = ...
if isNAN(a) or isNAN(b) then return NAN end
if a <= b then
return (c and not isNAN(c)) and c or NAN
end
return (d and not isNAN(d)) and d or NAN
end
local function _ifeq(...)
local a, b, c, d = ...
if isNAN(a) or isNAN(b) then return NAN end
if a == b then
return (c and not isNAN(c)) and c or NAN
end
return (d and not isNAN(d)) and d or NAN
end
local function _round(...)
local a, b = ...
if isNAN(a) then return NAN end
b = b or 1
if isNAN(b) or b == 0 then return NAN end
return floor(a / b + 0.5) * b
end
local function _roundup(...)
local a, b = ...
if isNAN(a) then return NAN end
b = b or 1
if isNAN(b) or b == 0 then return NAN end
return ceil(a / b) * b
end
local function _rounddown(...)
local a, b = ...
if isNAN(a) then return NAN end
b = b or 1
if isNAN(b) or b == 0 then return NAN end
return floor(a / b) * b
end
local values = {}
for i, params in ipairs({%s}) do
local itemString, key, extraParam = unpack(params)
@ -518,17 +445,8 @@ function TSMAPI:ParseCustomPrice(priceString, badPriceSource)
return nil, err
end
local function safeFunc(itemString)
local prevNum = TSM_PRICE_TEMP.num
local success, value = pcall(func, itemString)
TSM_PRICE_TEMP.num = prevNum
if success then
return value
end
end
customPriceCache[priceString] = safeFunc
return safeFunc
customPriceCache[priceString] = func
return func
end
function TSMAPI:GetCustomPriceSourceValue(itemString, key)

40
TradeSkillMaster/GUI/BuildPage.lua

@ -74,31 +74,11 @@ local function CreateCustomPriceFrame()
text = TSMAPI.Design:GetInlineColor("category")..L["More Advanced Methods"].."|r",
relativeWidth = 1,
},
{
type = "Label",
text = "Combine sources, math, and functions to build rules. Sources are case-insensitive.",
relativeWidth = 1,
},
{
type = "Label",
text = format("See %s for more info.", TSMAPI.Design:GetInlineColor("link").."https://tinyurl.com/tsm2Ascension|r"),
relativeWidth = 1,
},
{
type = "Label",
text = "Functions: min, max, avg, first, check, ifgt, ifgte, iflt, iflte, ifeq, round, roundup, rounddown",
relativeWidth = 1,
},
{
type = "Label",
text = "Convert other items: convert(dbmarket, item:12345) or convert(dbmarket, |cffffffff|Hitem:12345|h[Item]|h|r)",
relativeWidth = 1,
},
{
type = "Label",
text = "Test strings: /tsm price <ItemLink> <Price String>",
relativeWidth = 1,
},
{
type = "HeadingLine",
relativeWidth = 1,
@ -133,26 +113,6 @@ local function CreateCustomPriceFrame()
text = "max(vendor, 120% crafting)",
relativeWidth = 1,
},
{
type = "Label",
text = "dbmarket * 0.9",
relativeWidth = 1,
},
{
type = "Label",
text = "ifgt(dbminbuyout, 200g, dbminbuyout, dbmarket)",
relativeWidth = 1,
},
{
type = "Label",
text = "round(dbmarket, 1g)",
relativeWidth = 1,
},
{
type = "Label",
text = "convert(dbmarket, item:36901) * 1.05",
relativeWidth = 1,
},
}
TSMAPI:BuildPage(container, page)

7
TradeSkillMaster/GUI/TSMWidgets/TSMSlider.lua

@ -158,13 +158,6 @@ local methods = {
end,
["SetValue"] = function(self, value)
if value == nil then
value = self.min or 0
end
value = tonumber(value)
if not value then
return
end
self.slider.setup = true
self.slider:SetValue(value)
self.value = value

2
TradeSkillMaster/Libs/LibAuctionScan-1.0/LibAuctionScan-1.0.lua

@ -522,7 +522,7 @@ do
if interrupted then
-- fires if the scan was interrupted
DoCallback("SCAN_INTERRUPTED", status.data)
DoCallback("SCAN_INTERRUPTED")
else
-- fires if the scan completed sucessfully
DoCallback("SCAN_COMPLETE", status.data)

6
TradeSkillMaster/TradeSkillMaster.lua

@ -735,12 +735,6 @@ function TSMAPI:GetItemPrices(itemLink)
-- Use TSM:GetCustomPrice to get avgBuy price
prices.avgBuy = TSM:GetCustomPrice("avgBuy", itemString) or 0
-- Use TSM:GetCustomPrice to get smartAvgBuy price
prices.smartAvgBuy = TSM:GetCustomPrice("smartAvgBuy", itemString) or 0
-- Use TSM:GetCustomPrice to get lastBuy price
prices.lastBuy = TSM:GetCustomPrice("lastBuy", itemString) or 0
-- Use TSM:GetCustomPrice to get avgSell price
prices.avgSell = TSM:GetCustomPrice("avgSell", itemString) or 0

2
TradeSkillMaster/TradeSkillMaster.toc

@ -2,7 +2,7 @@
## Title: |cff00fe00TradeSkillMaster: Revived|r
## Notes: Core addon for the TradeSkillMaster suite, revived for Wrath of the Lich King. Does nothing without modules installed.
## Author: Sapu94, Bart39, Gnomezilla [Warmane-Icecrown(A)], BlueAo [Warmane], andrew6180, Yoshiyuka, DimaSheiko, and other contributors...
## Version: 2.3.46
## Version: Rev668
## SavedVariables: TradeSkillMasterAppDB, AscensionTSMDB
## OptionalDeps: AccurateTime, Ace3, LibDataBroker-1.1, LibDBIcon-1.0, LibExtraTip, TipHelper, LibParse, LibCompress, LibGraph-2.0, SharedMedia, TheUndermineJournal, TheUndermineJournalGE
## X-Embeds: AccurateTime, Ace3, LibDataBroker-1.1, LibDBIcon-1.0, LibExtraTip, TipHelper, LibParse, LibCompress, LibGraph-2.0

16
TradeSkillMaster_Accounting/Locale/enUS.lua

@ -15,7 +15,7 @@ if not L then return end
L["|cffff0000IMPORTANT:|r When TSM_Accounting last saved data for this realm, it was too big for WoW to handle, so old data was automatically trimmed in order to avoid corruption of the saved variables. The last %s of %s data has been preserved."] = true
L["%s ago"] = true
L["Accounting has not yet collected enough information for this tab. This is likely due to not having recorded enough data points or not seeing any significant fluctuations in your gold on hand."] = true
L["Accounting has not yet collected enough information for this tab. This is likely due to not having recorded enough data points or not seeing any significant fluctuations (over 1k gold) in your gold on hand."] = true
L["Activity Type"] = true
L["All"] = true
L["Amount"] = true
@ -24,13 +24,9 @@ L["Average Prices:"] = true
L["Avg Buy Price"] = true
L["Avg Resale Profit"] = true
L["Avg Sell Price"] = true
L["Last Buy Price"] = true
L["Last Buy Price:"] = true
L["Smart Avg Buy Price:"] = true
L["Smart Avg Buy Price"] = true
L["Back to Previous Page"] = true
L["Balance"] = true
L["Below is a graph of the your character's gold on hand over time.\n\nThe x-axis is time and goes from %s to %s\nThe y-axis is gold."] = true
L["Below is a graph of the your character's gold on hand over time.\n\nThe x-axis is time and goes from %s to %s\nThe y-axis is thousands of gold."] = true
L["Bought"] = true
L["Buyer/Seller"] = true
L["Cancelled Since Last Sale:"] = true
@ -85,8 +81,6 @@ L["Market Value Source"] = true
L["Market Value"] = true
L["Max Buy Price"] = true
L["Max Sell Price"] = true
L["Min Buy Price"] = true
L["Min Sell Price"] = true
L["None"] = true
L["Options"] = true
L["Other Income"] = true
@ -149,12 +143,6 @@ L["Total Sale Price"] = true
L["Total Spent:"] = true
L["Total Value"] = true
L["Total:"] = true
L["Player Wealth"] = true
L["Not Available"] = true
L["Bags:"] = true
L["Bank:"] = true
L["Auctions:"] = true
L["Coin on Hand"] = true
L["Track sales/purchases via trade"] = true
L["Type"] = true
L["Use smart average for purchase price"] = true

7
TradeSkillMaster_Accounting/Modules/data.lua

@ -1251,12 +1251,7 @@ function Data:LogGold()
TSM.db.realm.goldLog[player] = TSM.db.realm.goldLog[player] or {}
local goldLog = TSM.db.realm.goldLog[player]
local currentMoney = GetMoney()
local resolution = COPPER_PER_GOLD
if currentMoney >= (COPPER_PER_GOLD * 1000) then
resolution = COPPER_PER_GOLD * 1000
end
local currentGold = TSM:Round(currentMoney, resolution)
local currentGold = TSM:Round(GetMoney(), COPPER_PER_GOLD * 1000)
if #goldLog > 0 and currentGold == goldLog[#goldLog].copper then
goldLog[#goldLog].endMinute = currentMinute
else

200
TradeSkillMaster_Accounting/Modules/gui.lua

@ -809,7 +809,7 @@ function private:GetGoldGraphPoints(goldLog)
local data = {}
for _, info in ipairs(goldLog) do
local x1, x2 = info.startMinute, info.endMinute
local y = info.copper / COPPER_PER_GOLD
local y = info.copper / COPPER_PER_GOLD / 1000
minX = min(minX, x1)
maxX = max(maxX, x2)
minY = min(minY, floor(y))
@ -831,7 +831,7 @@ function private:GetGoldGraphSumData()
if i > 1 then
data[i].startMinute = data[i-1].endMinute+1
end
data[i].copper = TSM:Round(data[i].copper, COPPER_PER_GOLD)
data[i].copper = TSM:Round(data[i].copper, COPPER_PER_GOLD*1000)
end
tinsert(players, data)
tinsert(starts, data[1].startMinute)
@ -884,100 +884,22 @@ function private:GetGoldGraphSumData()
return private:GetGoldGraphPoints(temp)
end
function private:GetWealthPrice(itemString)
local avgSell = TSM:GetAvgSellPrice(itemString)
local marketValue = TSMAPI:GetItemValue(itemString, "DBMarket")
if avgSell and marketValue then
return (avgSell + marketValue) / 2
end
return avgSell or marketValue
end
function private:GetWealthTotals(player)
local totals = {
bags = 0,
bank = 0,
auctions = 0,
}
local hasData = false
local function AddTotals(itemData, key)
if not itemData then return end
hasData = true
for itemString, quantity in pairs(itemData) do
if quantity > 0 then
local price = private:GetWealthPrice(itemString)
if price then
totals[key] = totals[key] + (price * quantity)
end
end
end
end
if player == "<ALL>" then
local players = TSMAPI:ModuleAPI("ItemTracker", "playerlist") or {}
for _, playerName in ipairs(players) do
AddTotals(TSMAPI:ModuleAPI("ItemTracker", "playerbags", playerName), "bags")
AddTotals(TSMAPI:ModuleAPI("ItemTracker", "playerbank", playerName), "bank")
AddTotals(TSMAPI:ModuleAPI("ItemTracker", "playerauctions", playerName), "auctions")
end
else
AddTotals(TSMAPI:ModuleAPI("ItemTracker", "playerbags", player), "bags")
AddTotals(TSMAPI:ModuleAPI("ItemTracker", "playerbank", player), "bank")
AddTotals(TSMAPI:ModuleAPI("ItemTracker", "playerauctions", player), "auctions")
end
if not hasData then return end
return totals
end
function private:GetLatestGold(player)
local function GetLogGold(log)
if not log or #log == 0 then return end
return log[#log].copper
end
if player == "<ALL>" then
local total = 0
local hasData = false
for name, log in pairs(TSM.db.realm.goldLog) do
local gold = GetLogGold(log)
if gold then
total = total + gold
hasData = true
end
end
if not hasData then return end
return total
end
return GetLogGold(TSM.db.realm.goldLog[player])
end
function GUI:DrawGoldGraph(container)
TSM.db.realm.goldGraphCharacter = TSM.db.realm.goldGraphCharacter or UnitName("player")
local player = TSM.db.realm.goldGraphCharacter
local data, minX, maxX, minY, maxY
if player == "<ALL>" then
data, minX, maxX, minY, maxY = private:GetGoldGraphSumData()
else
data, minX, maxX, minY, maxY = private:GetGoldGraphPoints(TSM.db.realm.goldLog[player])
end
local dropdownList = {["<ALL>"]="Sum of All Characters"}
for player in pairs(TSM.db.realm.goldLog) do
dropdownList[player] = player
end
local wealthTotals = private:GetWealthTotals(player)
local wealthTotalText = L["Not Available"]
local wealthBagsText = L["Not Available"]
local wealthBankText = L["Not Available"]
local wealthAuctionsText = L["Not Available"]
if wealthTotals then
wealthTotalText = TSMAPI:FormatTextMoney(wealthTotals.bags + wealthTotals.bank + wealthTotals.auctions) or "---"
wealthBagsText = TSMAPI:FormatTextMoney(wealthTotals.bags) or "---"
wealthBankText = TSMAPI:FormatTextMoney(wealthTotals.bank) or "---"
wealthAuctionsText = TSMAPI:FormatTextMoney(wealthTotals.auctions) or "---"
end
local coinTotal = private:GetLatestGold(player)
local coinTotalText = coinTotal and TSMAPI:FormatTextMoney(coinTotal) or L["Not Available"]
if not data then
local page = {
{
type = "SimpleGroup",
@ -985,66 +907,104 @@ function GUI:DrawGoldGraph(container)
children = {
{
type = "Label",
text = L["Player Gold"],
text = L["Accounting has not yet collected enough information for this tab. This is likely due to not having recorded enough data points or not seeing any significant fluctuations (over 1k gold) in your gold on hand."],
relativeWidth = 1,
},
-- {
-- type = "Spacer",
-- },
{
type = "Spacer",
},
{
type = "Dropdown",
label = L["Character to Graph"],
label = "Character to Graph",
settingInfo = {TSM.db.realm, "goldGraphCharacter"},
relativeWidth = 0.5,
list = dropdownList,
callback = function() container:ReloadTab() end,
tooltip = "",
},
},
},
}
TSMAPI:BuildPage(container, page)
return
end
local startDate, endDate
if TSM.db.realm.timeFormat == "eudate" then
startDate = date("%d/%m/%y %H:%M", minX * 60)
endDate = date("%d/%m/%y %H:%M", maxX * 60)
elseif TSM.db.realm.timeFormat == "aidate" then
startDate = date("%y/%m/%d %H:%M", minX * 60)
endDate = date("%y/%m/%d %H:%M", maxX * 60)
else
startDate = date("%m/%d/%y %H:%M", minX * 60)
endDate = date("%m/%d/%y %H:%M", maxX * 60)
end
local page = {
{
type = "InlineGroup",
type = "SimpleGroup",
layout = "Flow",
title = L["Player Wealth"],
backdrop = true,
children = {
{
type = "Label",
text = L["Total:"] .. " " .. wealthTotalText,
--text = format(L["Below is a graph of the your character's gold on hand over time.\n\nThe x-axis is time and goes from %s to %s\nThe y-axis is thousands of gold."], startDate, endDate),
text = format("Below is a graph of the your character's gold on hand over time.\nThe x-axis is time and goes from %s to %s. The y-axis is thousands of gold.", startDate, endDate),
relativeWidth = 1,
},
-- {
-- type = "Spacer",
-- },
{
type = "Label",
text = L["Bags:"] .. " " .. wealthBagsText,
relativeWidth = 0.33,
},
{
type = "Label",
text = L["Bank:"] .. " " .. wealthBankText,
relativeWidth = 0.33,
type = "Dropdown",
label = L["Character to Graph"],
settingInfo = {TSM.db.realm, "goldGraphCharacter"},
relativeWidth = 0.5,
list = dropdownList,
callback = function() container:ReloadTab() end,
},
-- {
-- type = "HeadingLine"
-- },
{
type = "Label",
text = L["Auctions:"] .. " " .. wealthAuctionsText,
relativeWidth = 0.33,
},
},
type = "Spacer",
},
{
type = "InlineGroup",
layout = "Flow",
title = L["Coin on Hand"],
backdrop = true,
children = {
{
type = "Label",
text = L["Total:"] .. " " .. coinTotalText,
relativeWidth = 1,
},
},
type = "ScrollFrame",
fullHeight = false, --true
layout = "flow"
},
},
},
}
TSMAPI:BuildPage(container, page)
local parent = container.children[1].children[#container.children[1].children].frame
parent:SetWidth(container.frame:GetWidth() - 80)
parent:SetHeight(container.frame:GetHeight() - 140)
--if not GUI.lineGraph then
local graph = LibStub("LibGraph-2.0"):CreateGraphLine(nil, parent, "CENTER", nil, nil, nil, parent:GetWidth(), parent:GetHeight())
graph:SetGridColor({ 0.8, 0.8, 0.8, 0.6 })
graph:SetYLabels(true)
GUI.lineGraph = graph
--end
GUI.lineGraph:Show()
GUI.lineGraph:SetParent(parent)
GUI.lineGraph:ClearAllPoints()
GUI.lineGraph:SetAllPoints(parent)
GUI.lineGraph:ResetData()
local ySpacing = max(ceil((maxY - minY) / 20), 0.5)
GUI.lineGraph:SetGridSpacing(nil, ySpacing)
local xBuffer = (maxX-minX)*0.05
local yBuffer = (maxY-minY)*0.03
GUI.lineGraph:SetXAxis(minX-xBuffer, maxX)
GUI.lineGraph:SetYAxis(minY-yBuffer, maxY+yBuffer)
GUI.lineGraph:AddDataSeries(data, {1, 0.83, 0, 1})
GUI.lineGraph:RefreshGraph()
end
function GUI:DrawOptions(container)

111
TradeSkillMaster_Accounting/TradeSkillMaster_Accounting.lua

@ -90,8 +90,8 @@ function TSM:OnInitialize()
if data.startMinute == data.endMinute and data.copper == 0 then
tremove(playerData, i)
else
-- round to nearest gold
data.copper = TSM:Round(data.copper, COPPER_PER_GOLD)
-- round to nearest 1k gold
data.copper = TSM:Round(data.copper, COPPER_PER_GOLD*1000)
end
end
if #playerData >= 2 then
@ -125,12 +125,8 @@ function TSM:RegisterModule()
TSM.priceSources = {
{ key = "avgSell", label = L["Avg Sell Price"], callback = "GetAvgSellPrice" },
{ key = "avgBuy", label = L["Avg Buy Price"], callback = "GetAvgBuyPrice" },
{ key = "smartAvgBuy", label = L["Smart Avg Buy Price"], callback = "GetSmartAvgBuyPrice" },
{ key = "lastBuy", label = L["Last Buy Price"], callback = "GetLastBuyPrice" },
{ key = "maxSell", label = L["Max Sell Price"], callback = "GetMaxSellPrice" },
{ key = "maxBuy", label = L["Max Buy Price"], callback = "GetMaxBuyPrice" },
{ key = "minSell", label = L["Min Sell Price"], callback = "GetMinSellPrice" },
{ key = "minBuy", label = L["Min Buy Price"], callback = "GetMinBuyPrice" },
}
TSM.tooltipOptions = { callback = "GUI:LoadTooltipOptions" }
@ -189,7 +185,6 @@ function TSM:GetTooltip(itemString)
if TSM.db.realm.tooltip.purchase and TSM.items[itemString] and #TSM.items[itemString].buys > 0 then
local lastPurchased = TSM.items[itemString].buys[#TSM.items[itemString].buys].time
local lastBuyPrice = TSM:GetLastBuyPrice(itemString)
local totalPrice, totalNum = 0, 0
for _, record in ipairs(TSM.items[itemString].buys) do
totalNum = totalNum + record.quantity
@ -205,30 +200,15 @@ function TSM:GetTooltip(itemString)
else
local avgPrice = TSM:GetAvgBuyPrice(itemString)
local maxPrice = TSM:GetMaxBuyPrice(itemString)
local smartAvgPrice = TSM:GetSmartAvgBuyPrice(itemString)
if moneyCoinsTooltip then
tinsert(text, { left = " " .. L["Purchased (Avg/Max Price):"], right = format("%s (%s / %s)", "|cffffffff" .. totalNum .. "|r", (TSMAPI:FormatTextMoneyIcon(avgPrice, "|cffffffff", true) or ("|cffffffff" .. "?")), (TSMAPI:FormatTextMoneyIcon(maxPrice, "|cffffffff", true) or ("|cffffffff" .. "?"))) })
else
tinsert(text, { left = " " .. L["Purchased (Avg/Max Price):"], right = format("%s (%s / %s)", "|cffffffff" .. totalNum .. "|r", (TSMAPI:FormatTextMoney(avgPrice, "|cffffffff", true) or ("|cffffffff" .. "?")), (TSMAPI:FormatTextMoney(maxPrice, "|cffffffff", true) or ("|cffffffff" .. "?"))) })
end
if smartAvgPrice then
if moneyCoinsTooltip then
tinsert(text, { left = " " .. L["Smart Avg Buy Price:"], right = (TSMAPI:FormatTextMoneyIcon(smartAvgPrice, "|cffffffff", true) or ("|cffffffff" .. "?")) })
else
tinsert(text, { left = " " .. L["Smart Avg Buy Price:"], right = (TSMAPI:FormatTextMoney(smartAvgPrice, "|cffffffff", true) or ("|cffffffff" .. "?")) })
end
end
end
if lastPurchased then
local timeDiff = SecondsToTime(time() - lastPurchased)
tinsert(text, { left = " " .. L["Last Purchased:"], right = "|cffffffff" .. format(L["%s ago"], timeDiff) })
if lastBuyPrice then
if moneyCoinsTooltip then
tinsert(text, { left = " " .. L["Last Buy Price:"], right = (TSMAPI:FormatTextMoneyIcon(lastBuyPrice, "|cffffffff", true) or ("|cffffffff" .. "?")) })
else
tinsert(text, { left = " " .. L["Last Buy Price:"], right = (TSMAPI:FormatTextMoney(lastBuyPrice, "|cffffffff", true) or ("|cffffffff" .. "?")) })
end
end
end
end
@ -442,24 +422,12 @@ function TSM:GetAvgSellPrice(itemString)
return TSM.cache[itemString].avgSellPrice, TSM.cache[itemString].avgSellNum
end
local function GetSmartBuyItemCount(itemString)
local player, alts = TSMAPI:ModuleAPI("ItemTracker", "playertotal", itemString)
if not player then
alts = nil
end
player = player or 0
alts = alts or 0
local guild = TSMAPI:ModuleAPI("ItemTracker", "guildtotal", itemString) or 0
local auctions = TSMAPI:ModuleAPI("ItemTracker", "auctionstotal", itemString) or 0
return player + alts + guild + auctions
end
local function GetAvgerageBuyPrice(itemString, noBaseItem, useSmart)
local function GetAvgerageBuyPrice(itemString, noBaseItem)
if not noBaseItem and itemString and baseItemLookup[itemString] then
local totalPrice, totalNum = 0, 0
for _, item in ipairs(baseItemLookup[itemString]) do
if not baseItemLookup[item] then
local price, num = GetAvgerageBuyPrice(item, true, useSmart)
local price, num = GetAvgerageBuyPrice(item, true)
if price and num and num > 0 then
totalPrice = totalPrice + price
totalNum = totalNum + num
@ -473,11 +441,16 @@ local function GetAvgerageBuyPrice(itemString, noBaseItem, useSmart)
if not (TSM.items[itemString] and #TSM.items[itemString].buys > 0) then return end
local itemCount = 0
if useSmart or TSM.db.realm.smartBuyPrice then
itemCount = GetSmartBuyItemCount(itemString)
if useSmart and itemCount <= 0 then
return
if TSM.db.realm.smartBuyPrice then
local player, alts = TSMAPI:ModuleAPI("ItemTracker", "playertotal", itemString)
if not player then
alts = nil
end
player = player or 0
alts = alts or 0
local guild = TSMAPI:ModuleAPI("ItemTracker", "guildtotal", itemString) or 0
local auctions = TSMAPI:ModuleAPI("ItemTracker", "auctionstotal", itemString) or 0
itemCount = player + alts + guild + auctions
end
local num, totalPrice = 0, 0
@ -507,19 +480,6 @@ function TSM:GetAvgBuyPrice(itemString)
return TSM.cache[itemString].avgBuyPrice, TSM.cache[itemString].avgBuyNum
end
function TSM:GetSmartAvgBuyPrice(itemString)
itemString = TSMAPI:GetItemString(select(2, TSMAPI:GetSafeItemInfo(itemString)))
if not itemString then return end
TSM.cache[itemString] = TSM.cache[itemString] or {}
TSM:UpdateBaseItemLookup()
if not TSM.cache[itemString].smartAvgBuyPrice then
local price, num = GetAvgerageBuyPrice(itemString, nil, true)
TSM.cache[itemString].smartAvgBuyPrice = price
TSM.cache[itemString].smartAvgBuyNum = num
end
return TSM.cache[itemString].smartAvgBuyPrice, TSM.cache[itemString].smartAvgBuyNum
end
function TSM:GetMaxSellPrice(itemString)
itemString = TSMAPI:GetItemString(select(2, TSMAPI:GetSafeItemInfo(itemString)))
if not (itemString and TSM.items[itemString] and #TSM.items[itemString].sales > 0) then return end
@ -536,22 +496,6 @@ function TSM:GetMaxSellPrice(itemString)
return TSM.cache[itemString].maxSellPrice
end
function TSM:GetMinSellPrice(itemString)
itemString = TSMAPI:GetItemString(select(2, TSMAPI:GetSafeItemInfo(itemString)))
if not (itemString and TSM.items[itemString] and #TSM.items[itemString].sales > 0) then return end
TSM.cache[itemString] = TSM.cache[itemString] or {}
if not TSM.cache[itemString].minSellPrice then
local minPrice = math.huge
for _, record in ipairs(TSM.items[itemString].sales) do
minPrice = min(minPrice, record.copper)
end
TSM.cache[itemString].minSellPrice = minPrice
end
return TSM.cache[itemString].minSellPrice
end
function TSM:GetMaxBuyPrice(itemString)
itemString = TSMAPI:GetItemString(select(2, TSMAPI:GetSafeItemInfo(itemString)))
if not (itemString and TSM.items[itemString] and #TSM.items[itemString].buys > 0) then return end
@ -568,35 +512,6 @@ function TSM:GetMaxBuyPrice(itemString)
return TSM.cache[itemString].maxBuyPrice
end
function TSM:GetMinBuyPrice(itemString)
itemString = TSMAPI:GetItemString(select(2, TSMAPI:GetSafeItemInfo(itemString)))
if not (itemString and TSM.items[itemString] and #TSM.items[itemString].buys > 0) then return end
TSM.cache[itemString] = TSM.cache[itemString] or {}
if not TSM.cache[itemString].minBuyPrice then
local minPrice = math.huge
for _, record in ipairs(TSM.items[itemString].buys) do
minPrice = min(minPrice, record.copper)
end
TSM.cache[itemString].minBuyPrice = minPrice
end
return TSM.cache[itemString].minBuyPrice
end
function TSM:GetLastBuyPrice(itemString)
itemString = TSMAPI:GetItemString(select(2, TSMAPI:GetSafeItemInfo(itemString)))
if not (itemString and TSM.items[itemString] and #TSM.items[itemString].buys > 0) then return end
TSM.cache[itemString] = TSM.cache[itemString] or {}
if not TSM.cache[itemString].lastBuyPrice then
local lastRecord = TSM.items[itemString].buys[#TSM.items[itemString].buys]
TSM.cache[itemString].lastBuyPrice = lastRecord and lastRecord.copper or nil
end
return TSM.cache[itemString].lastBuyPrice
end
function TSM:Round(value, sig)
sig = sig or 1
local gold = value / sig

6
TradeSkillMaster_AuctionDB/Locale/deDE.lua

@ -13,11 +13,14 @@
local L = LibStub("AceLocale-3.0"):NewLocale("TradeSkillMaster_AuctionDB", "deDE")
if not L then return end
L["A full auction house scan will scan every item on the auction house but is far slower than a GetAll scan. Expect this scan to take several minutes or longer."] = "Ein voller Auktionshausscan wird jedes einzelne Item im Auktionshaus scannen, ist aber sehr viel langsamer als der GetAll-Scan. Erwarte, dass es mehrere Minuten dauert oder länger."
L["A GetAll scan is the fastest in-game method for scanning every item on the auction house. However, there are many possible bugs on Blizzard's end with it including the chance for it to disconnect you from the game. Also, it has a 15 minute cooldown. You can disable the GetAll button via TSM's AuctionDB options if this feature doesn't work well on your server."] = "Ein GetAll-Scan ist die schnellste Methode, um in-game alle Gegenstände im Auktionshaus zu scannen. Allerdings gibt es viele mögliche Bugs seitens Blizzard die auftreten können, inklusive der Möglichkeit, dass deine Verbindung zum Spiel getrennt wird. Außerdem gibt es einen 15-Minuten-Cooldown. You can disable the GetAll button via TSM's AuctionDB options if this feature doesn't work well on your server." -- Needs review
L["Any items in the AuctionDB database that contain the search phrase in their names will be displayed."] = "Es werden alle Gegenstände in der \"AuctionDB\" Datenbank angezeigt, deren Namen mit der Sucheingabe übereinstimmen."
L["Are you sure you want to clear your AuctionDB data?"] = "Sind Sie sicher, dass Sie die \"AuctionDB\" Daten löschen wollen?"
L["Ascending"] = "Aufsteigend"
L["AuctionDB - Market Value"] = "AuctionDB - Marktwert"
L["AuctionDB - Minimum Buyout"] = "AuctionDB - Mindestpreis"
L["Can't run a GetAll scan right now."] = "Kann im Moment keinen GetAll Scan durchführen."
L["Descending"] = "Absteigend"
L["Display lowest buyout value seen in the last scan in tooltip."] = "Zeige den niedrigsten Sofortkauf-Betrag aus dem letzten Scan im Tooltip."
L["Display market value in tooltip."] = "Zeige Marktwert im Tooltip."
@ -26,6 +29,7 @@ L["Display total number of items ever seen in tooltip."] = "Zeige Gesamtzahl der
L["Done Scanning"] = "Scannen beendet"
L["Download the FREE TSM desktop application which will automatically update your TSM_AuctionDB prices using Blizzard's online APIs (and does MUCH more). Visit %s for more info and never scan the AH again! This is the best way to update your AuctionDB prices."] = "Lade die KOSTENLOSE TSM Desktopsoftware herunter die automatisch deine TSM_AuctionDB mit den Preisen aus Blizzard's online API aktualisiert (und noch VIEL mehr kann). Besuche %s für weitere Informationen und scanne das AH nie wieder! Dies ist die beste Art deine AuctionDB Preise aktuell zu halten."
L["Enable display of AuctionDB data in tooltip."] = "Aktiviere die Anzeige der AuctionDB-Daten im Tooltip."
L["GetAll scan did not run successfully due to issues on Blizzard's end. Using the TSM application for your scans is recommended."] = "GetAll scan nicht erfolgreich aufgrund von Problemen bei Blizzard. Nutzung der TSM Software für deinen Scan wird empfohlen."
L["Hide poor quality items"] = "Verstecke Gegenstände schlechter Qualität"
L["If checked, poor quality items won't be shown in the search results."] = "Wenn markiert, tauchen Gegenstände schlechter Qualität nicht in den Suchergebnissen auf."
L["If checked, the lowest buyout value seen in the last scan of the item will be displayed."] = "Wenn ausgewählt, wird der niedrigste Sofortkaufpreis der im letzten Scan gefunden wurde angezeigt."
@ -40,6 +44,7 @@ L["Items per page"] = "Gegenstände pro Seite"
L["Items %s - %s (%s total)"] = "Gegenstände %s - %s (%s gesamt)"
L["Item SubType Filter"] = "Gegenstands-Unterkategorie-Filter"
L["Item Type Filter"] = "Gegenstands-Kategorie-Filter"
L["It is strongly recommended that you reload your ui (type '/reload') after running a GetAll scan. Otherwise, any other scans (Post/Cancel/Search/etc) will be much slower than normal."] = "Es wird sehr empfohlen, dass du die UI neu lädst (tippe \"/reload\"), nachdem du einen GetAll-Scan gemacht hast. Sonst werden andere Scans (Posten/Abbrechen/Suchen/etc) sehr viel langsamer als Normal laufen."
L["Last Scanned"] = "Zuletzt gescannt"
L["Last updated from in-game scan %s ago."] = "Zuletzt aktualisiert durch in-game Scan vor %s"
L["Last updated from the TSM Application %s ago."] = "Zuletzt aktualisiert durch TSM-Applikation vor %s"
@ -66,6 +71,7 @@ L["Reset Data"] = "Daten zurücksetzen"
L["Resets AuctionDB's scan data"] = "Setzt die Scandaten der \"AuctionDB\" zurück"
L["Result Order:"] = "Reihenfolge der Ergebnisse:"
L["Run Full Scan"] = "Starte einen vollen Scan"
L["Run GetAll Scan"] = "Starte Komplettscan"
L["Running query..."] = "Abfrage läuft..."
L["%s ago"] = "Vor %s"
L["Scanning page %s/%s"] = "Scanne Seite %s/%s"

37
TradeSkillMaster_AuctionDB/Locale/enUS.lua

@ -14,34 +14,31 @@ local L = LibStub("AceLocale-3.0"):NewLocale("TradeSkillMaster_AuctionDB", "enUS
if not L then return end
L["%s ago"] = true
L["A full auction house scan will scan every item on the auction house. Expect this scan to take several minutes or longer."] = true
L["A GetAll scan is the fastest in-game method for scanning every item on the auction house. However, there are many possible bugs on Blizzard's end with it including the chance for it to disconnect you from the game. Also, it has a 15 minute cooldown. You can disable the GetAll button via TSM's AuctionDB options if this feature doesn't work well on your server."] = true
L["A full auction house scan will scan every item on the auction house but is far slower than a GetAll scan. Expect this scan to take several minutes or longer."] = true
L["Any items in the AuctionDB database that contain the search phrase in their names will be displayed."] = true
L["A newer version of AuctionDB (%s) was received from %s. You are running %s."] = true
L["Are you sure you want to clear your AuctionDB data?"] = true
L["Ascending"] = true
L["AuctionDB - Market Value"] = true
L["AuctionDB - Minimum Buyout"] = true
L["Can't run a GetAll scan right now."] = true
L["Descending"] = true
L["Disable \"GetAll\" Auction Scans"] = true
L["Display lowest buyout value seen in the last scan in tooltip."] = true
L["Channel Sync"] = true
L["Channel sync name"] = true
L["Display market value in tooltip."] = true
L["Enable channel sync"] = true
L["Done Scanning"] = true
L["Download the FREE TSM desktop application which will automatically update your TSM_AuctionDB prices using Blizzard's online APIs (and does MUCH more). Visit %s for more info and never scan the AH again! This is the best way to update your AuctionDB prices."] = true
L["Enable channel sync debug messages"] = true
L["Enable display of AuctionDB data in tooltip."] = true
L["The channel name used to share AuctionDB data."] = true
L["GetAll scan did not run successfully due to issues on Blizzard's end. Using the TSM application for your scans is recommended."] = true
L["Hide poor quality items"] = true
L["If checked, AuctionDB will add a tab to the AH to allow for in-game scans. If you are using the TSM app exclusively for your scans, you may want to hide it by unchecking this option. This option requires a reload to take effect."] = true
L["If checked, AuctionDB will print channel sync debug messages to chat."] = true
L["If checked, AuctionDB will sync scan data over the configured channel."] = true
L["If checked, AuctionDB will only receive channel sync data and will not broadcast scan data."] = true
L["If checked, AuctionDB will not perform \"GetAll\" scans. This is useful if your server doesn't return all auctions in its \"GetAll\" results, which means that you'll get incorrect market value calculations for all items. If you're playing on such servers, it's best to disable the \"GetAll\" feature to avoid accidentally polluting your price database with incorrect data. This option takes effect immediately, but requires a reload to completely hide the \"Run GetAll Scan\" button."] = true
L["If checked, poor quality items won't be shown in the search results."] = true
L["If checked, the lowest buyout value seen in the last scan of the item will be displayed."] = true
L["If checked, the market value of the item will be displayed"] = true
L["Imported %s scans worth of new auction data!"] = true
L["Invalid value entered. You must enter a number between 5 and 500 inclusive."] = true
L["It is strongly recommended that you reload your ui (type '/reload') after running a GetAll scan. Otherwise, any other scans (Post/Cancel/Search/etc) will be much slower than normal."] = true
L["Item Link"] = true
L["Item MinLevel"] = true
L["Item SubType Filter"] = true
@ -63,7 +60,6 @@ L["No scans found."] = true
L["Not Ready"] = true
L["Not Scanned"] = true
L["Options"] = true
L["Pause"] = true
L["Preparing Filter %d / %d"] = true
L["Preparing Filters..."] = true
L["Previous Page"] = true
@ -74,20 +70,11 @@ L["Refreshes the current search results."] = true
L["Removed %s from AuctionDB."] = true
L["Reset Data"] = true
L["Resets AuctionDB's scan data"] = true
L["Reset"] = true
L["Resets the channel name to the default."] = true
L["Receive-only mode"] = true
L["Resume"] = true
L["Resuming Scan..."] = true
L["Result Order:"] = true
L["Run Full Scan"] = true
L["Run GetAll Scan"] = true
L["Running query..."] = true
L["Running query... Server not responding due to throttling? Try again later..."] = true
L["Scan Bags"] = true
L["Scan Bank"] = true
L["Cancel Scan"] = true
L["Scan Inventory"] = true
L["Scan Paused"] = true
L["Scan Selected Groups"] = true
L["Scanning %d / %d (Page 1 / ?)"] = true
L["Scanning %d / %d (Page %d / %d)"] = true
@ -100,16 +87,10 @@ L["Select whether to sort search results in ascending or descending order."] = t
L["Shift-Right-Click to clear all data for this item from AuctionDB."] = true
L["Show AuctionDB AH Tab (Requires Reload)"] = true
L["Sort items by"] = true
L["Scan bag items."] = true
L["Scan bank items."] = true
L["Scans bags and bank for all characters."] = true
L["This determines how many items are shown per page in results area of the \"Search\" tab of the AuctionDB page in the main TSM window. You may enter a number between 5 and 500 inclusive. If the page lags, you may want to decrease this number."] = true
L["This will do a slow auction house scan of every item in the selected groups and update their AuctionDB prices. This may take several minutes."] = true
L["Use the search box and category filters above to search the AuctionDB data."] = true
L["You can filter the results by item subtype by using this dropdown. For example, if you want to search for all herbs, you would select \"Trade Goods\" in the item type dropdown and \"Herbs\" in this dropdown."] = true
L["You can filter the results by item type by using this dropdown. For example, if you want to search for all herbs, you would select \"Trade Goods\" in this dropdown and \"Herbs\" as the subtype filter."] = true
L["You can use this page to lookup an item or group of items in the AuctionDB database. Note that this does not perform a live search of the AH."] = true
L["No bag items found."] = true
L["No inventory items found."] = true
L["No bank items found."] = true
L["ItemTracker data unavailable."] = true
L["You have disabled GetAll scans via AuctionDB's options."] = true

6
TradeSkillMaster_AuctionDB/Locale/esES.lua

@ -13,11 +13,14 @@
local L = LibStub("AceLocale-3.0"):NewLocale("TradeSkillMaster_AuctionDB", "esES")
if not L then return end
-- L["A full auction house scan will scan every item on the auction house but is far slower than a GetAll scan. Expect this scan to take several minutes or longer."] = ""
-- L["A GetAll scan is the fastest in-game method for scanning every item on the auction house. However, there are many possible bugs on Blizzard's end with it including the chance for it to disconnect you from the game. Also, it has a 15 minute cooldown. You can disable the GetAll button via TSM's AuctionDB options if this feature doesn't work well on your server."] = ""
L["Any items in the AuctionDB database that contain the search phrase in their names will be displayed."] = "Todos los artículos en la base de datos de AuctionDB que contienen la frase de búsqueda en su nombre en la pantalla."
L["Are you sure you want to clear your AuctionDB data?"] = "¿Está seguro que desea borrar los datos AuctionDB?"
L["Ascending"] = "Ascendente"
-- L["AuctionDB - Market Value"] = ""
-- L["AuctionDB - Minimum Buyout"] = ""
-- L["Can't run a GetAll scan right now."] = ""
L["Descending"] = "Descendiendo"
-- L["Display lowest buyout value seen in the last scan in tooltip."] = ""
-- L["Display market value in tooltip."] = ""
@ -26,6 +29,7 @@ L["Descending"] = "Descendiendo"
-- L["Done Scanning"] = ""
-- L["Download the FREE TSM desktop application which will automatically update your TSM_AuctionDB prices using Blizzard's online APIs (and does MUCH more). Visit %s for more info and never scan the AH again! This is the best way to update your AuctionDB prices."] = ""
L["Enable display of AuctionDB data in tooltip."] = "Permitir la visualización de los datos AuctionDB en la descripción."
-- L["GetAll scan did not run successfully due to issues on Blizzard's end. Using the TSM application for your scans is recommended."] = ""
L["Hide poor quality items"] = "Esconder objetos de calidad pobre"
L["If checked, poor quality items won't be shown in the search results."] = "Si se marca, los artículos de calidad pobre no se mostrará en los resultados de búsqueda."
-- L["If checked, the lowest buyout value seen in the last scan of the item will be displayed."] = ""
@ -40,6 +44,7 @@ L["Item MinLevel"] = "NivelMin Objeto"
-- L["Items %s - %s (%s total)"] = ""
-- L["Item SubType Filter"] = ""
-- L["Item Type Filter"] = ""
-- L["It is strongly recommended that you reload your ui (type '/reload') after running a GetAll scan. Otherwise, any other scans (Post/Cancel/Search/etc) will be much slower than normal."] = ""
-- L["Last Scanned"] = ""
-- L["Last updated from in-game scan %s ago."] = ""
-- L["Last updated from the TSM Application %s ago."] = ""
@ -66,6 +71,7 @@ L["Ready in %s min and %s sec"] = "Listo en %s min y %s sec."
-- L["Resets AuctionDB's scan data"] = ""
-- L["Result Order:"] = ""
-- L["Run Full Scan"] = ""
L["Run GetAll Scan"] = "Hacer un GetAll Scan"
-- L["Running query..."] = ""
L["%s ago"] = "hace %s"
-- L["Scanning page %s/%s"] = ""

6
TradeSkillMaster_AuctionDB/Locale/esMX.lua

@ -13,11 +13,14 @@
local L = LibStub("AceLocale-3.0"):NewLocale("TradeSkillMaster_AuctionDB", "esMX")
if not L then return end
-- L["A full auction house scan will scan every item on the auction house but is far slower than a GetAll scan. Expect this scan to take several minutes or longer."] = ""
-- L["A GetAll scan is the fastest in-game method for scanning every item on the auction house. However, there are many possible bugs on Blizzard's end with it including the chance for it to disconnect you from the game. Also, it has a 15 minute cooldown. You can disable the GetAll button via TSM's AuctionDB options if this feature doesn't work well on your server."] = ""
-- L["Any items in the AuctionDB database that contain the search phrase in their names will be displayed."] = ""
-- L["Are you sure you want to clear your AuctionDB data?"] = ""
-- L["Ascending"] = ""
-- L["AuctionDB - Market Value"] = ""
-- L["AuctionDB - Minimum Buyout"] = ""
-- L["Can't run a GetAll scan right now."] = ""
-- L["Descending"] = ""
-- L["Display lowest buyout value seen in the last scan in tooltip."] = ""
-- L["Display market value in tooltip."] = ""
@ -26,6 +29,7 @@ if not L then return end
-- L["Done Scanning"] = ""
-- L["Download the FREE TSM desktop application which will automatically update your TSM_AuctionDB prices using Blizzard's online APIs (and does MUCH more). Visit %s for more info and never scan the AH again! This is the best way to update your AuctionDB prices."] = ""
-- L["Enable display of AuctionDB data in tooltip."] = ""
-- L["GetAll scan did not run successfully due to issues on Blizzard's end. Using the TSM application for your scans is recommended."] = ""
-- L["Hide poor quality items"] = ""
-- L["If checked, poor quality items won't be shown in the search results."] = ""
-- L["If checked, the lowest buyout value seen in the last scan of the item will be displayed."] = ""
@ -40,6 +44,7 @@ if not L then return end
-- L["Items %s - %s (%s total)"] = ""
-- L["Item SubType Filter"] = ""
-- L["Item Type Filter"] = ""
-- L["It is strongly recommended that you reload your ui (type '/reload') after running a GetAll scan. Otherwise, any other scans (Post/Cancel/Search/etc) will be much slower than normal."] = ""
-- L["Last Scanned"] = ""
-- L["Last updated from in-game scan %s ago."] = ""
-- L["Last updated from the TSM Application %s ago."] = ""
@ -66,6 +71,7 @@ if not L then return end
-- L["Resets AuctionDB's scan data"] = ""
-- L["Result Order:"] = ""
-- L["Run Full Scan"] = ""
-- L["Run GetAll Scan"] = ""
-- L["Running query..."] = ""
-- L["%s ago"] = ""
-- L["Scanning page %s/%s"] = ""

6
TradeSkillMaster_AuctionDB/Locale/frFR.lua

@ -13,11 +13,14 @@
local L = LibStub("AceLocale-3.0"):NewLocale("TradeSkillMaster_AuctionDB", "frFR")
if not L then return end
L["A full auction house scan will scan every item on the auction house but is far slower than a GetAll scan. Expect this scan to take several minutes or longer."] = "Une analyse complète de l'Hôtel des ventes examinera tous les objets de l'hôtel des ventes mais est beaucoup plus lente que la méthode du GetAll. Attendez-vous à ce que cette analyse dure de nombreuses minutes."
-- L["A GetAll scan is the fastest in-game method for scanning every item on the auction house. However, there are many possible bugs on Blizzard's end with it including the chance for it to disconnect you from the game. Also, it has a 15 minute cooldown. You can disable the GetAll button via TSM's AuctionDB options if this feature doesn't work well on your server."] = ""
L["Any items in the AuctionDB database that contain the search phrase in their names will be displayed."] = "Tout objets présent dans la base de donnée d'AuctionDB contenant la phrase recherchée dans son nom sera affiché."
L["Are you sure you want to clear your AuctionDB data?"] = "Êtes-vous sûr de vouloir vider les données d'AuctionDB?"
L["Ascending"] = "Croissant"
L["AuctionDB - Market Value"] = "AuctionDB - Valeur du Marché"
L["AuctionDB - Minimum Buyout"] = "AuctionDB - Achat minimum"
-- L["Can't run a GetAll scan right now."] = ""
L["Descending"] = "Décroissant"
-- L["Display lowest buyout value seen in the last scan in tooltip."] = ""
-- L["Display market value in tooltip."] = ""
@ -26,6 +29,7 @@ L["Descending"] = "Décroissant"
L["Done Scanning"] = "Analyse terminée"
-- L["Download the FREE TSM desktop application which will automatically update your TSM_AuctionDB prices using Blizzard's online APIs (and does MUCH more). Visit %s for more info and never scan the AH again! This is the best way to update your AuctionDB prices."] = ""
L["Enable display of AuctionDB data in tooltip."] = "Activer l'affichage des données d'AuctionDB dans les info-bulles."
-- L["GetAll scan did not run successfully due to issues on Blizzard's end. Using the TSM application for your scans is recommended."] = ""
L["Hide poor quality items"] = "Masquer les objets gris"
L["If checked, poor quality items won't be shown in the search results."] = "Si coché, les objets gris ne seront pas affiché lors de la recherche."
-- L["If checked, the lowest buyout value seen in the last scan of the item will be displayed."] = ""
@ -40,6 +44,7 @@ L["Items per page"] = "Objets par page"
L["Items %s - %s (%s total)"] = "Objets %s - %s (%s au total)"
L["Item SubType Filter"] = "Sous-catégorie de l'objet"
L["Item Type Filter"] = "Catégorie de l'objet"
L["It is strongly recommended that you reload your ui (type '/reload') after running a GetAll scan. Otherwise, any other scans (Post/Cancel/Search/etc) will be much slower than normal."] = "Il est fortement recommandé que vous rechargez votre interface (tapez '/reload') après une analyse GetAll. Sinon, toutes les autres analyses (Poster/Annuler/Rechercher/etc) vont être plus longue que d'habitude."
L["Last Scanned"] = "Dernière fois analysé"
-- L["Last updated from in-game scan %s ago."] = ""
-- L["Last updated from the TSM Application %s ago."] = ""
@ -66,6 +71,7 @@ L["Reset Data"] = "Réinitialiser les données"
L["Resets AuctionDB's scan data"] = "Réinitialise les données d'AuctionDB"
-- L["Result Order:"] = ""
L["Run Full Scan"] = "Analyse complète"
L["Run GetAll Scan"] = "Analyse GetAll"
-- L["Running query..."] = ""
L["%s ago"] = "Il y a %s"
L["Scanning page %s/%s"] = "Scan de la page %s/%s" -- Needs review

6
TradeSkillMaster_AuctionDB/Locale/koKR.lua

@ -13,11 +13,14 @@
local L = LibStub("AceLocale-3.0"):NewLocale("TradeSkillMaster_AuctionDB", "koKR")
if not L then return end
L["A full auction house scan will scan every item on the auction house but is far slower than a GetAll scan. Expect this scan to take several minutes or longer."] = "전체 검색은 경매장 내의 모든 아이템을 검색하지만 GetAll 검색보다는 훨씬 느립니다. 이 검색은 몇 분 정도 또는 그 이상의 시간이 소요됩니다."
L["A GetAll scan is the fastest in-game method for scanning every item on the auction house. However, there are many possible bugs on Blizzard's end with it including the chance for it to disconnect you from the game. Also, it has a 15 minute cooldown. You can disable the GetAll button via TSM's AuctionDB options if this feature doesn't work well on your server."] = "GetAll 검색은 게임 내에서 경매장의 모든 아이템을 검색하기 위한 가장 빠른 검색 방법입니다. 하지만 블리자드 쪽에 많은 버그가 존재하며 게임의 접속이 끊길 가능성도 있습니다. 또한, 15분의 쿨다운이 존재합니다. You can disable the GetAll button via TSM's AuctionDB options if this feature doesn't work well on your server." -- Needs review
L["Any items in the AuctionDB database that contain the search phrase in their names will be displayed."] = "이름에 검색 구문을 포함하는 AuctionDB 데이터베이스 내의 모든 아이템이 표시됩니다."
L["Are you sure you want to clear your AuctionDB data?"] = "모든 AuctionDB 데이터를 삭제 하시겠습니까?"
L["Ascending"] = "오름차순"
L["AuctionDB - Market Value"] = "AuctionDB - 시장 가격"
L["AuctionDB - Minimum Buyout"] = "AuctionDB - 최소 구매가격"
L["Can't run a GetAll scan right now."] = "지금은 GetAll 검색을 실행할 수 없습니다." -- Needs review
L["Descending"] = "내림차순"
L["Display lowest buyout value seen in the last scan in tooltip."] = "최근 검색 시 본 최소 구매가를 툴팁에 표시합니다." -- Needs review
L["Display market value in tooltip."] = "시장 가격을 툴팁에 표시합니다." -- Needs review
@ -26,6 +29,7 @@ L["Display total number of items ever seen in tooltip."] = "아이템의 전체
L["Done Scanning"] = "검색 완료"
L["Download the FREE TSM desktop application which will automatically update your TSM_AuctionDB prices using Blizzard's online APIs (and does MUCH more). Visit %s for more info and never scan the AH again! This is the best way to update your AuctionDB prices."] = "무료 TSM 데스크톱 애플리케이션을 다운로드하면 TSM_AuctionDB 가격을 블리자드의 온라인 API를 이용해 자동으로 업데이트합니다. %s을 방문하여 더 많은 정보를 얻고 더 이상은 경매장 검색을 하지 마세요! 이것은 AuctionDB 가격을 업데이트하는 최고의 방법입니다." -- Needs review
L["Enable display of AuctionDB data in tooltip."] = "AuctionDB 데이터를 툴팁에 표시" -- Needs review
L["GetAll scan did not run successfully due to issues on Blizzard's end. Using the TSM application for your scans is recommended."] = "블리자드의 문제로 GetAll 검색이 성공적으로 수행되지 못했습니다. TSM 애플리케이션의 사용한 검색을 추천합니다." -- Needs review
L["Hide poor quality items"] = "저급 품질 아이템 숨기기"
L["If checked, poor quality items won't be shown in the search results."] = "선택하면, 저급 품질 아이템은 검색 결과에 표시하지 않습니다."
L["If checked, the lowest buyout value seen in the last scan of the item will be displayed."] = "선택하면, 최근 검색에서 본 아이템의 최소 구매 가격을 표시합니다." -- Needs review
@ -40,6 +44,7 @@ L["Items per page"] = "페이지당 아이템 개수"
L["Items %s - %s (%s total)"] = "아이템 %s - %s (전체 %s)"
L["Item SubType Filter"] = "아이템 하위 유형 필터" -- Needs review
L["Item Type Filter"] = "아이템 유형 필터"
L["It is strongly recommended that you reload your ui (type '/reload') after running a GetAll scan. Otherwise, any other scans (Post/Cancel/Search/etc) will be much slower than normal."] = "GetAll 검색을 실행한 후 UI를 다시 로드('/reload' 입력)해 주시길 바랍니다. 그렇게 하지 않으면, 다른 검색(등록/취소/검색/기타)은 정상보다 훨씬 느려지게 됩니다."
L["Last Scanned"] = "최근 검색"
L["Last updated from in-game scan %s ago."] = "게임 내 검색의 최근 업데이트 %s 전." -- Needs review
L["Last updated from the TSM Application %s ago."] = "TSM 애플리케이션의 최근 업데이트 %s 전." -- Needs review
@ -66,6 +71,7 @@ L["Reset Data"] = "데이터 리셋"
L["Resets AuctionDB's scan data"] = "AuctionDB의 검색 데이터 리셋"
L["Result Order:"] = "결과 정렬:" -- Needs review
L["Run Full Scan"] = "전체 검색"
L["Run GetAll Scan"] = "GetAll 검색"
L["Running query..."] = "쿼리 실행 중..." -- Needs review
L["%s ago"] = "%s 전"
L["Scanning page %s/%s"] = "페이지 검색 %s/%s" -- Needs review

6
TradeSkillMaster_AuctionDB/Locale/ptBR.lua

@ -13,11 +13,14 @@
local L = LibStub("AceLocale-3.0"):NewLocale("TradeSkillMaster_AuctionDB", "ptBR")
if not L then return end
L["A full auction house scan will scan every item on the auction house but is far slower than a GetAll scan. Expect this scan to take several minutes or longer."] = "Um escaneamento completo da casa de leilões irá escanear todos os itens da casa de leilões, porém é bem mais lento que um escaneamento PegaTudo. Espere que este escaneamento demore vários minutos ou mais."
-- L["A GetAll scan is the fastest in-game method for scanning every item on the auction house. However, there are many possible bugs on Blizzard's end with it including the chance for it to disconnect you from the game. Also, it has a 15 minute cooldown. You can disable the GetAll button via TSM's AuctionDB options if this feature doesn't work well on your server."] = ""
L["Any items in the AuctionDB database that contain the search phrase in their names will be displayed."] = "Qualquer item no bando de dados do AuctionDB que contém a frase procurada em seus nomes serão exibidos."
L["Are you sure you want to clear your AuctionDB data?"] = "Você tem certeza de que quer limpar os dados do seu AuctionDB?"
L["Ascending"] = "Crescente"
L["AuctionDB - Market Value"] = "AuctionDB - Valor de Mercado"
L["AuctionDB - Minimum Buyout"] = "AuctionDB - Arremate Mínimo"
-- L["Can't run a GetAll scan right now."] = ""
L["Descending"] = "Decrescente"
-- L["Display lowest buyout value seen in the last scan in tooltip."] = ""
-- L["Display market value in tooltip."] = ""
@ -26,6 +29,7 @@ L["Descending"] = "Decrescente"
L["Done Scanning"] = "Escaneamento Completo"
-- L["Download the FREE TSM desktop application which will automatically update your TSM_AuctionDB prices using Blizzard's online APIs (and does MUCH more). Visit %s for more info and never scan the AH again! This is the best way to update your AuctionDB prices."] = ""
L["Enable display of AuctionDB data in tooltip."] = "Habilita a exibição de dados do AuctionDB nas dicas de interface."
-- L["GetAll scan did not run successfully due to issues on Blizzard's end. Using the TSM application for your scans is recommended."] = ""
L["Hide poor quality items"] = "Esconder itens de qualidade inferior"
L["If checked, poor quality items won't be shown in the search results."] = "Se marcado, itens de qualidade inferior não serão exibidos nos resultados das buscas."
-- L["If checked, the lowest buyout value seen in the last scan of the item will be displayed."] = ""
@ -40,6 +44,7 @@ L["Items per page"] = "Itens por página"
L["Items %s - %s (%s total)"] = "Itens %s - %s (%s no total)"
L["Item SubType Filter"] = "Filtro de SubTipo de Item"
L["Item Type Filter"] = "Filtro de Tipo de Item"
L["It is strongly recommended that you reload your ui (type '/reload') after running a GetAll scan. Otherwise, any other scans (Post/Cancel/Search/etc) will be much slower than normal."] = "É altamente recomendado que você recarregue sua IU (digite '/reload') após rodar um escaneamento PegaTudo. De outra forma, qualquer outro escaneamento (Postagem/Cancelamento/Busca/etc) será muito mais lento que o normal."
L["Last Scanned"] = "Escaneado pela última vez"
-- L["Last updated from in-game scan %s ago."] = ""
-- L["Last updated from the TSM Application %s ago."] = ""
@ -66,6 +71,7 @@ L["Reset Data"] = "Redefinir Dados"
L["Resets AuctionDB's scan data"] = "Redefine os dados de escaneamento do AuctionDB"
L["Result Order:"] = "Order de Resultado"
L["Run Full Scan"] = "Escaneamento Completo" -- Needs review
L["Run GetAll Scan"] = "PegaTudo" -- Needs review
-- L["Running query..."] = ""
L["%s ago"] = "%s atrás"
L["Scanning page %s/%s"] = "Escaneando página %s/%s" -- Needs review

6
TradeSkillMaster_AuctionDB/Locale/ruRU.lua

@ -13,11 +13,14 @@
local L = LibStub("AceLocale-3.0"):NewLocale("TradeSkillMaster_AuctionDB", "ruRU")
if not L then return end
L["A full auction house scan will scan every item on the auction house but is far slower than a GetAll scan. Expect this scan to take several minutes or longer."] = "Полный скан Аукциона просканирует каждый товар, но намного дольше, чем GetAll скан. Ждите, это займёт несколько минут или более."
L["A GetAll scan is the fastest in-game method for scanning every item on the auction house. However, there are many possible bugs on Blizzard's end with it including the chance for it to disconnect you from the game. Also, it has a 15 minute cooldown. You can disable the GetAll button via TSM's AuctionDB options if this feature doesn't work well on your server."] = "GetAll скан - самый быстрый внутриигровой способ сканирования. Однако, из-за из-за ошибок со стороны Blizzard's, существует вероятность отключения от сервера. Кроме того, он имеет 15-минутный перерыв. You can disable the GetAll button via TSM's AuctionDB options if this feature doesn't work well on your server." -- Needs review
L["Any items in the AuctionDB database that contain the search phrase in their names will be displayed."] = "Будут отображены все товары из базы данных AuctionDB, содержащие искомую фразу в названии."
L["Are you sure you want to clear your AuctionDB data?"] = "Вы действительно хотите очистить базу AuctionDB?"
L["Ascending"] = "Возрастание"
L["AuctionDB - Market Value"] = "AuctionDB - Рыночная стоимость"
L["AuctionDB - Minimum Buyout"] = "AuctionDB - Минимальный выкуп"
L["Can't run a GetAll scan right now."] = "Сейчас невозможно запустить GetAll скан."
L["Descending"] = "Убывание"
-- L["Display lowest buyout value seen in the last scan in tooltip."] = ""
L["Display market value in tooltip."] = "Показывать рыночную цену в подсказке."
@ -26,6 +29,7 @@ L["Display market value in tooltip."] = "Показывать рыночную
L["Done Scanning"] = "Сканирование завершено"
-- L["Download the FREE TSM desktop application which will automatically update your TSM_AuctionDB prices using Blizzard's online APIs (and does MUCH more). Visit %s for more info and never scan the AH again! This is the best way to update your AuctionDB prices."] = ""
L["Enable display of AuctionDB data in tooltip."] = "Показывать данные AuctionDB в подсказке."
L["GetAll scan did not run successfully due to issues on Blizzard's end. Using the TSM application for your scans is recommended."] = "GetAll скан завершился неудачно из-за проблем со стороны Blizzard. Рекомендуется использовать для сканирования TSM."
L["Hide poor quality items"] = "Скрыть товары низкого качества"
L["If checked, poor quality items won't be shown in the search results."] = "Не показывать товары низкого качества в результатах поиска."
L["If checked, the lowest buyout value seen in the last scan of the item will be displayed."] = "Если выбрано, будет показана минимальная цена выкупа товара с последнего скана"
@ -40,6 +44,7 @@ L["Items per page"] = "Товаров на страницу"
L["Items %s - %s (%s total)"] = "Товаров %s - %s (%s всего)"
L["Item SubType Filter"] = "Фильтр по подтипу товара"
L["Item Type Filter"] = "Фильтр по типу товара"
L["It is strongly recommended that you reload your ui (type '/reload') after running a GetAll scan. Otherwise, any other scans (Post/Cancel/Search/etc) will be much slower than normal."] = "Настоятельно рекомендуем перезагрузить ваш интерфейс (наберите в чате '/reload') после выполнения GetAll скана. Иначе, любые другие сканы (Выставить/Отменить/Поиск/т.д.) будут намного медленнее, чем обычно."
L["Last Scanned"] = "Последний скан"
-- L["Last updated from in-game scan %s ago."] = ""
-- L["Last updated from the TSM Application %s ago."] = ""
@ -66,6 +71,7 @@ L["Reset Data"] = "Сбросить данные"
L["Resets AuctionDB's scan data"] = "Сбрасывает данные сканирования модуля AuctionDB"
-- L["Result Order:"] = ""
L["Run Full Scan"] = "Полный скан"
L["Run GetAll Scan"] = "GetAll скан"
L["Running query..."] = "Осуществляется запрос..."
L["%s ago"] = "%s назад"
L["Scanning page %s/%s"] = "Сканирование страницы %s/%s"

6
TradeSkillMaster_AuctionDB/Locale/zhCN.lua

@ -13,11 +13,14 @@
local L = LibStub("AceLocale-3.0"):NewLocale("TradeSkillMaster_AuctionDB", "zhCN")
if not L then return end
L["A full auction house scan will scan every item on the auction house but is far slower than a GetAll scan. Expect this scan to take several minutes or longer."] = "完整扫描拍卖行内的所有物品,此方式远慢于快速扫描,预计费时几分钟甚至更久。"
L["A GetAll scan is the fastest in-game method for scanning every item on the auction house. However, there are many possible bugs on Blizzard's end with it including the chance for it to disconnect you from the game. Also, it has a 15 minute cooldown. You can disable the GetAll button via TSM's AuctionDB options if this feature doesn't work well on your server."] = "快速扫描时扫描拍卖行中每件物品最快的方式。然而,在服务器端有着可能的BUG会使您掉线,所以每15分钟才能执行一次。 You can disable the GetAll button via TSM's AuctionDB options if this feature doesn't work well on your server." -- Needs review
L["Any items in the AuctionDB database that contain the search phrase in their names will be displayed."] = "任何包含搜索短语的AuctionDB数据库中的物品都将显示。"
L["Are you sure you want to clear your AuctionDB data?"] = "您确定要清除AuctionDB数据吗?"
L["Ascending"] = "升序"
L["AuctionDB - Market Value"] = "AuctionDB - 市场价"
L["AuctionDB - Minimum Buyout"] = "AuctionDB - 最低一口价"
L["Can't run a GetAll scan right now."] = "现在还不能执行快速扫描。"
L["Descending"] = "降序"
L["Display lowest buyout value seen in the last scan in tooltip."] = "在鼠标提示中显示上次扫描的最低一口价。"
L["Display market value in tooltip."] = "在鼠标提示中显示市场价。"
@ -26,6 +29,7 @@ L["Display total number of items ever seen in tooltip."] = "在鼠标提示中
L["Done Scanning"] = "完成扫描"
L["Download the FREE TSM desktop application which will automatically update your TSM_AuctionDB prices using Blizzard's online APIs (and does MUCH more). Visit %s for more info and never scan the AH again! This is the best way to update your AuctionDB prices."] = "下载完全免费的 TSM APP (TSM应用程序) 来更新你的 AuctionDB数据库中的物品价格 (利用到暴雪提供的在线APIs)。访问 %s 来获取更多信息。以后将不用在游戏里扫描拍卖行物价了,这将是更新拍卖行物价好最好的方法。"
L["Enable display of AuctionDB data in tooltip."] = "在鼠标提示中显示AuctionDB数据"
L["GetAll scan did not run successfully due to issues on Blizzard's end. Using the TSM application for your scans is recommended."] = "快速扫描由于服务器端的争议而不能成功的运行,因此强烈推荐使用 TSM APP 进行物价扫描。"
L["Hide poor quality items"] = "隐藏灰色物品"
L["If checked, poor quality items won't be shown in the search results."] = "如果勾选,灰色物品将不会出现在扫描结果中。"
L["If checked, the lowest buyout value seen in the last scan of the item will be displayed."] = "如果勾选,将显示上次扫描的物品最低一口价。"
@ -40,6 +44,7 @@ L["Items per page"] = "每页显示的物品"
L["Items %s - %s (%s total)"] = "物品 %s - %s (总数 %s) "
L["Item SubType Filter"] = "物品子类型筛选"
L["Item Type Filter"] = "物品类型筛选"
L["It is strongly recommended that you reload your ui (type '/reload') after running a GetAll scan. Otherwise, any other scans (Post/Cancel/Search/etc) will be much slower than normal."] = "强烈推荐您在运行快速扫描后重载界面(输入'/reload'),否则任何其他的扫描(上架/下架/搜索等)都较平时要慢。"
L["Last Scanned"] = "上一次扫描"
L["Last updated from in-game scan %s ago."] = "距离上次游戏内的数据扫描 %s 。"
L["Last updated from the TSM Application %s ago."] = "距离上次游戏外TSM APP的数据扫描 %s 。"
@ -66,6 +71,7 @@ L["Reset Data"] = "重置数据"
L["Resets AuctionDB's scan data"] = "重置AuctionDB扫描数据"
L["Result Order:"] = "结果顺序:"
L["Run Full Scan"] = "执行完整扫描"
L["Run GetAll Scan"] = "进行快速扫描"
L["Running query..."] = "运行查询…"
L["%s ago"] = "%s之前"
L["Scanning page %s/%s"] = "扫描页面 %s/%s"

6
TradeSkillMaster_AuctionDB/Locale/zhTW.lua

@ -13,11 +13,14 @@
local L = LibStub("AceLocale-3.0"):NewLocale("TradeSkillMaster_AuctionDB", "zhTW")
if not L then return end
L["A full auction house scan will scan every item on the auction house but is far slower than a GetAll scan. Expect this scan to take several minutes or longer."] = "完整的拍賣行掃描會掃描每件在拍賣行的物品但是比GetAll掃描還緩慢。預期掃描會花費幾分鐘或是更久。"
-- L["A GetAll scan is the fastest in-game method for scanning every item on the auction house. However, there are many possible bugs on Blizzard's end with it including the chance for it to disconnect you from the game. Also, it has a 15 minute cooldown. You can disable the GetAll button via TSM's AuctionDB options if this feature doesn't work well on your server."] = ""
L["Any items in the AuctionDB database that contain the search phrase in their names will be displayed."] = "任何在AuctionDB資料庫中符合搜尋條件的物品,都會顯示出來。"
L["Are you sure you want to clear your AuctionDB data?"] = "你確定要清除AuctionDB資料?"
L["Ascending"] = "遞增"
L["AuctionDB - Market Value"] = "AuctionDB - 市場價格"
L["AuctionDB - Minimum Buyout"] = "AuctionDB - 最小直購價"
-- L["Can't run a GetAll scan right now."] = ""
L["Descending"] = "遞減"
-- L["Display lowest buyout value seen in the last scan in tooltip."] = ""
-- L["Display market value in tooltip."] = ""
@ -26,6 +29,7 @@ L["Descending"] = "遞減"
L["Done Scanning"] = "完成掃描"
-- L["Download the FREE TSM desktop application which will automatically update your TSM_AuctionDB prices using Blizzard's online APIs (and does MUCH more). Visit %s for more info and never scan the AH again! This is the best way to update your AuctionDB prices."] = ""
L["Enable display of AuctionDB data in tooltip."] = "啟用在提示顯示AuctionDB資料。"
-- L["GetAll scan did not run successfully due to issues on Blizzard's end. Using the TSM application for your scans is recommended."] = ""
L["Hide poor quality items"] = "隱藏低品質物品"
L["If checked, poor quality items won't be shown in the search results."] = "勾選,低品質物㗊將不會顯示在搜尋記錄。"
-- L["If checked, the lowest buyout value seen in the last scan of the item will be displayed."] = ""
@ -40,6 +44,7 @@ L["Items per page"] = "一頁幾項物品"
L["Items %s - %s (%s total)"] = "物品%s - %s (總共:%s)"
L["Item SubType Filter"] = "物品次要類型過濾"
L["Item Type Filter"] = "物品類型過濾"
L["It is strongly recommended that you reload your ui (type '/reload') after running a GetAll scan. Otherwise, any other scans (Post/Cancel/Search/etc) will be much slower than normal."] = "強烈建議你在跑了GetAll掃描後重新載入你的UI(輸入\"/reload\")。否則,任何其它掃描(發佈/取消/搜尋/等等)都會比平常更慢。"
L["Last Scanned"] = "最後掃描"
-- L["Last updated from in-game scan %s ago."] = ""
-- L["Last updated from the TSM Application %s ago."] = ""
@ -66,6 +71,7 @@ L["Reset Data"] = "重置資料"
L["Resets AuctionDB's scan data"] = "重置AuctionDB的掃描資料"
L["Result Order:"] = "結果順序:"
L["Run Full Scan"] = "執行完整掃描"
L["Run GetAll Scan"] = "執行GetAll掃描"
-- L["Running query..."] = ""
L["%s ago"] = "%s以前"
-- L["Scanning page %s/%s"] = ""

471
TradeSkillMaster_AuctionDB/Modules/ChannelSync.lua

@ -1,471 +0,0 @@
-- ------------------------------------------------------------------------------ --
-- TradeSkillMaster_AuctionDB --
-- http://www.curse.com/addons/wow/tradeskillmaster_auctiondb --
-- --
-- A TradeSkillMaster Addon (http://tradeskillmaster.com) --
-- All Rights Reserved* - Detailed license information included with addon. --
-- ------------------------------------------------------------------------------ --
-- Channel sync for sharing AuctionDB scan data between players.
local TSM = select(2, ...)
local ChannelSync = TSM:NewModule("ChannelSync", "AceEvent-3.0")
local L = LibStub("AceLocale-3.0"):GetLocale("TradeSkillMaster_AuctionDB")
local DEFAULT_CHANNEL_NAME = "TSM_AuctionDB"
local function GetChannelConfigName()
if TSM.db and TSM.db.profile and TSM.db.profile.channelSyncName and TSM.db.profile.channelSyncName ~= "" then
return TSM.db.profile.channelSyncName
end
return DEFAULT_CHANNEL_NAME
end
local COMM_PREFIX = "TSMADB1"
local CHUNK_SIZE = 220
local BUNDLE_TIMEOUT = 45
local SEND_INTERVAL = 0.08
local MAX_TOTAL_CHUNKS = 800
local MAX_ITEMS_PER_PAYLOAD = 250
local private = {
channelId = nil,
channelName = nil,
lastBroadcastHash = nil,
incoming = {},
sendQueue = {},
isSending = false,
notifiedNewer = {},
cachedFullScan = {time=nil, itemIDs=nil},
}
local strbyte = string.byte
local libS = LibStub:GetLibrary("AceSerializer-3.0")
local libD = LibStub("LibDeflate", true)
local warnedNoDeflate
local function GetAddonVersion()
local version = GetAddOnMetadata("TradeSkillMaster_AuctionDB", "X-Curse-Packaged-Version")
or GetAddOnMetadata("TradeSkillMaster_AuctionDB", "Version")
if not version or version == "" then
return "unknown"
end
return version
end
local function VersionKey(version)
local key = 0
for part in tostring(version or ""):gmatch("%d+") do
key = key * 1000 + tonumber(part)
end
return key
end
private.addonVersion = GetAddonVersion()
private.addonVersionKey = VersionKey(private.addonVersion)
local function DebugPrint(msg)
if TSM.db and TSM.db.profile and TSM.db.profile.channelSyncDebug then
TSM:Print("ChannelSync: " .. msg)
end
end
local function CanEncode()
if libD then return true end
if not warnedNoDeflate then
warnedNoDeflate = true
TSM:Print("AuctionDB channel sync requires LibDeflate. Install or enable an addon that provides it.")
end
return false
end
local function HashString(str)
local hash = 0
for i = 1, #str do
hash = (hash * 31 + strbyte(str, i)) % 4294967296
end
return hash
end
local function EnsureChannel()
if private.channelId and private.channelName and private.channelName ~= GetChannelConfigName() then
LeaveChannelByName(private.channelName)
private.channelId = nil
private.channelName = nil
end
if not TSM.db or not TSM.db.profile or not TSM.db.profile.channelSyncEnabled then
private.channelId = nil
private.channelName = nil
return
end
local channelName = GetChannelConfigName()
local channelId = GetChannelName(channelName)
if channelId == 0 then
JoinChannelByName(channelName)
channelId = GetChannelName(channelName)
end
private.channelId = channelId > 0 and channelId or nil
private.channelName = private.channelId and channelName or nil
end
local function GetChannelDisplayName(channelName, channelString, channelNumber, channelBaseName)
if channelBaseName and channelBaseName ~= "" and not tonumber(channelBaseName) then
return channelBaseName
end
if channelName and channelName ~= "" and not tonumber(channelName) then
return channelName
end
if channelString and channelString ~= "" then
local parsed = channelString:match("^%d+%.%s*(.+)$")
if parsed then return parsed end
local numeric = tonumber(channelString)
if numeric then return select(2, GetChannelName(numeric)) end
return channelString
end
if channelNumber then
return select(2, GetChannelName(channelNumber))
end
end
local function ChatFilter(_, _, msg, _, channelString, _, channelNumber, channelName, channelBaseName)
local channelNameKey = GetChannelConfigName()
local displayName = GetChannelDisplayName(channelName, channelString, channelNumber, channelBaseName)
if displayName ~= channelNameKey then return end
if strsub(msg, 1, #COMM_PREFIX) == COMM_PREFIX then
return true
end
end
function ChannelSync:OnEnable()
self:RegisterEvent("PLAYER_ENTERING_WORLD", "OnPlayerEnteringWorld")
self:RegisterEvent("CHAT_MSG_CHANNEL_NOTICE", "OnChannelNotice")
self:RegisterEvent("CHAT_MSG_CHANNEL", "OnChannelMessage")
if ChatFrame_AddMessageEventFilter then
ChatFrame_AddMessageEventFilter("CHAT_MSG_CHANNEL", ChatFilter)
end
EnsureChannel()
end
function ChannelSync:OnPlayerEnteringWorld()
EnsureChannel()
end
function ChannelSync:OnChannelNotice()
EnsureChannel()
end
function ChannelSync:ReloadConfig()
EnsureChannel()
end
local function EncodePayload(payload)
if not CanEncode() then return end
local serialized = libS:Serialize(payload)
if not serialized then
DebugPrint("Serialize failed.")
return
end
local compressed = libD:CompressDeflate(serialized, { level = 9 })
if not compressed then
DebugPrint("CompressDeflate failed.")
return
end
return libD:EncodeForPrint(compressed)
end
local function DecodePayload(encoded)
if not CanEncode() then return end
local decoded = libD:DecodeForPrint(encoded)
if not decoded then
DebugPrint("DecodeForPrint failed.")
return
end
local decompressed = libD:DecompressDeflate(decoded)
if not decompressed then
DebugPrint("DecompressDeflate failed.")
return
end
local ok, payload = libS:Deserialize(decompressed)
if not ok then
DebugPrint("Deserialize failed.")
return
end
return payload
end
local function MaybeNotifyNewerVersion(remoteVersion, sender)
local remoteKey = VersionKey(remoteVersion)
local localKey = private.addonVersionKey
if remoteKey == 0 or localKey == 0 or remoteKey <= localKey then return end
local noticeKey = tostring(sender or "unknown") .. ":" .. tostring(remoteVersion)
if private.notifiedNewer[noticeKey] then return end
private.notifiedNewer[noticeKey] = true
TSM:Printf(L["A newer version of AuctionDB (%s) was received from %s. You are running %s."], remoteVersion, sender or "unknown", private.addonVersion)
end
function ChannelSync:BroadcastScanData(scanType, items)
if scanType ~= "Full" and scanType ~= "Group" and scanType ~= "Search" then
return
end
if TSM.processingData then
TSMAPI:CreateTimeDelay("auctionDBChannelSyncBroadcast", 0.5, function()
ChannelSync:BroadcastScanData(scanType, items)
end)
return
end
EnsureChannel()
if not TSM.db or not TSM.db.profile or not TSM.db.profile.channelSyncEnabled then return end
if TSM.db.profile.channelSyncReceiveOnly then return end
if not private.channelName or not private.channelId then return end
local itemIDs = ChannelSync:CollectItemIDs(items)
if not itemIDs or #itemIDs == 0 then return end
local function QueueEncoded(encoded)
local hash = HashString(encoded)
if hash == private.lastBroadcastHash then return end
private.lastBroadcastHash = hash
local total = ceil(#encoded / CHUNK_SIZE)
if total > MAX_TOTAL_CHUNKS then return false end
tinsert(private.sendQueue, {hash = hash, encoded = encoded, total = total})
if not private.isSending then
private.isSending = true
TSMAPI.Threading:Start(ChannelSync.SendQueueThread, 0.3)
end
return true
end
local function QueueBatch(startIndex, endIndex)
local batch = {}
for i = startIndex, endIndex do
tinsert(batch, itemIDs[i])
end
local payload = ChannelSync:BuildPayloadFromItemIDs(scanType, batch)
if not payload then return end
local encoded = EncodePayload(payload)
if not encoded then return end
local total = ceil(#encoded / CHUNK_SIZE)
if total > MAX_TOTAL_CHUNKS and #batch > 1 then
local mid = floor((startIndex + endIndex) / 2)
QueueBatch(startIndex, mid)
QueueBatch(mid + 1, endIndex)
return
end
if total > MAX_TOTAL_CHUNKS then
if TSM.db and TSM.db.profile and TSM.db.profile.channelSyncDebug then
TSM:Print("AuctionDB channel sync payload too large; skipping batch.")
end
return
end
QueueEncoded(encoded)
end
for i = 1, #itemIDs, MAX_ITEMS_PER_PAYLOAD do
QueueBatch(i, min(i + MAX_ITEMS_PER_PAYLOAD - 1, #itemIDs))
end
end
function ChannelSync:CollectItemIDs(items)
local itemIDs, list = {}, {}
if items then
if #items > 0 then
for _, itemString in ipairs(items) do
local itemID = TSMAPI:GetItemID(itemString)
if itemID and not itemIDs[itemID] then
itemIDs[itemID] = true
tinsert(list, itemID)
end
end
else
for itemString in pairs(items) do
local itemID = TSMAPI:GetItemID(itemString)
if itemID and not itemIDs[itemID] then
itemIDs[itemID] = true
tinsert(list, itemID)
end
end
end
else
local scanTime = TSM.db.realm.lastCompleteScan
if private.cachedFullScan.time == scanTime and private.cachedFullScan.itemIDs then
return private.cachedFullScan.itemIDs
end
for itemID in pairs(TSM.data) do
TSM:DecodeItemData(itemID)
if TSM.data[itemID].lastScan == scanTime then
tinsert(list, itemID)
end
end
private.cachedFullScan.time = scanTime
private.cachedFullScan.itemIDs = list
end
return list
end
function ChannelSync:BuildPayloadFromItemIDs(scanType, itemIDList)
local payloadItems = {}
for _, itemID in ipairs(itemIDList) do
local data = TSM.data[itemID]
if data then
TSM:EncodeItemData(itemID)
if data.encoded then
payloadItems[itemID] = data.encoded
end
end
end
if not next(payloadItems) then return end
return {
v = 1,
scanType = scanType,
scanTime = TSM.db.realm.lastCompleteScan,
version = private.addonVersion,
items = payloadItems,
}
end
local function DecodeIncomingItem(itemID, encoded)
local tmp = {}
tmp[itemID] = {encoded = encoded}
TSM:DecodeItemData(itemID, tmp)
return tmp[itemID]
end
local function MergeIncomingData(payload, sender)
if type(payload) ~= "table" then return end
local incoming = {}
if payload.items then
incoming = payload.items
elseif type(payload.scanData) == "string" then
TSM:Deserialize(payload.scanData, incoming, true)
else
return
end
local updated = 0
local lastItemID
for itemID, data in pairs(incoming) do
local incomingData = data
if payload.items then
incomingData = DecodeIncomingItem(itemID, data)
end
if incomingData then
local existing = TSM.data[itemID]
if existing then
TSM:DecodeItemData(itemID)
if incomingData.lastScan and (not existing.lastScan or incomingData.lastScan > existing.lastScan) then
TSM.data[itemID] = incomingData
updated = updated + 1
lastItemID = itemID
end
else
TSM.data[itemID] = incomingData
updated = updated + 1
lastItemID = itemID
end
end
end
if payload.scanTime and (not TSM.db.realm.lastCompleteScan or payload.scanTime > TSM.db.realm.lastCompleteScan) then
TSM.db.realm.lastCompleteScan = payload.scanTime
end
TSM:Serialize()
if updated == 0 and sender then
DebugPrint("No items updated from " .. sender .. ".")
elseif updated > 0 and sender then
if TSM.db and TSM.db.profile and TSM.db.profile.channelSyncDebug then
if updated == 1 and lastItemID then
local link = select(2, GetItemInfo(lastItemID)) or ("item:" .. tostring(lastItemID))
TSM:Printf("AuctionDB updated %s from %s.", link, sender)
else
TSM:Printf("AuctionDB updated %d items from %s.", updated, sender)
end
end
end
end
function ChannelSync:OnChannelMessage(_, msg, source, _, channelString, _, channelNumber, channelName, channelBaseName)
local channelNameKey = GetChannelConfigName()
local displayName = GetChannelDisplayName(channelName, channelString, channelNumber, channelBaseName)
if displayName ~= channelNameKey then
if strsub(msg or "", 1, #COMM_PREFIX) == COMM_PREFIX then
DebugPrint("Prefix received; displayName=" .. tostring(displayName) .. " channelNumber=" .. tostring(channelNumber) .. " channelString=" .. tostring(channelString) .. " channelName=" .. tostring(channelName) .. " channelBaseName=" .. tostring(channelBaseName))
end
return
end
if strsub(msg or "", 1, #COMM_PREFIX) ~= COMM_PREFIX then
DebugPrint("Message on channel without prefix: '" .. strsub(msg or "", 1, #COMM_PREFIX) .. "'.")
return
end
source = ("-"):split(source or "")
if strlower(source or "") == strlower(UnitName("player") or "") then return end
local headerLen = #COMM_PREFIX + 16
if #msg <= headerLen then return end
local meta = strsub(msg, #COMM_PREFIX + 1, headerLen)
local chunk = strsub(msg, headerLen + 1)
local hashHex = strsub(meta, 1, 8)
local seqHex = strsub(meta, 9, 12)
local totalHex = strsub(meta, 13, 16)
local hash = tonumber(hashHex, 16)
local seq = tonumber(seqHex, 16)
local total = tonumber(totalHex, 16)
if not hash or not seq or not total then return end
local bundle = private.incoming[hash]
if not bundle or (time() - bundle.time) > BUNDLE_TIMEOUT then
if bundle then
DebugPrint("Bundle timeout " .. format("%08x", hash) .. " (" .. bundle.received .. "/" .. bundle.total .. ").")
end
bundle = {time = time(), total = total, chunks = {}, received = 0}
private.incoming[hash] = bundle
end
if bundle.total ~= total then
DebugPrint("Bundle total mismatch for " .. format("%08x", hash) .. ". Resetting.")
private.incoming[hash] = {time = time(), total = total, chunks = {}, received = 0}
bundle = private.incoming[hash]
end
if not bundle.chunks[seq] then
bundle.chunks[seq] = chunk
bundle.received = bundle.received + 1
end
if bundle.received < bundle.total then return end
local parts = {}
for i = 1, bundle.total do
if not bundle.chunks[i] then return end
tinsert(parts, bundle.chunks[i])
end
private.incoming[hash] = nil
local payload = DecodePayload(table.concat(parts))
if not payload then
DebugPrint("Payload decode failed for bundle " .. format("%08x", hash))
return
end
if payload.version then
MaybeNotifyNewerVersion(payload.version, source)
end
MergeIncomingData(payload, source)
end
function ChannelSync:SendQueueThread()
while #private.sendQueue > 0 do
local job = private.sendQueue[1]
for i = 1, job.total do
local chunk = strsub(job.encoded, (i - 1) * CHUNK_SIZE + 1, i * CHUNK_SIZE)
local msg = COMM_PREFIX .. string.format("%08x%04x%04x", job.hash, i, job.total) .. chunk
SendChatMessage(msg, "CHANNEL", nil, private.channelId)
self:Sleep(SEND_INTERVAL)
end
DebugPrint("Sent bundle " .. format("%08x", job.hash) .. " (" .. job.total .. " chunks).")
tremove(private.sendQueue, 1)
end
private.isSending = false
end

293
TradeSkillMaster_AuctionDB/Modules/GUI.lua

@ -20,33 +20,14 @@ function GUI:Show(frame)
private.startScanContent = private.startScanContent or private:CreateStartScanContent(frame)
private.startScanContent:Show()
if TSM.Scan.isScanning and not TSMAPI.AuctionScan:IsScanning() then
TSM.Scan:DoneScanning()
end
if TSM.Scan.isScanning then
GUI:SetPauseEnabled(true)
GUI:SetPaused(TSMAPI.AuctionScan:IsPaused())
else
GUI:SetPauseEnabled(false)
GUI:SetPaused(false)
end
end
function GUI:Hide()
private.statusBar:Hide()
private.startScanContent:Hide()
if TSM.Scan.isScanning and not TSMAPI.AuctionScan:IsScanning() then
TSM.Scan:DoneScanning()
end
if TSM.Scan.isScanning then
TSM.Scan:PauseScan()
else
TSM.Scan:DoneScanning()
TSMAPI.AuctionScan:StopScan()
GUI:SetPauseEnabled(false)
GUI:SetPaused(false)
end
end
function GUI:UpdateStatus(text, major, minor)
@ -58,20 +39,6 @@ function GUI:UpdateStatus(text, major, minor)
end
end
function GUI:SetPauseEnabled(enabled)
if not private.startScanContent or not private.startScanContent.pauseFullScanButton then return end
if enabled then
private.startScanContent.pauseFullScanButton:Enable()
else
private.startScanContent.pauseFullScanButton:Disable()
end
end
function GUI:SetPaused(isPaused)
if not private.startScanContent or not private.startScanContent.pauseFullScanButton then return end
private.startScanContent.pauseFullScanButton:SetText(isPaused and L["Resume"] or L["Pause"])
end
function private:CreateStatusBar(parent)
local frame = TSMAPI.GUI:CreateStatusBar(parent, "TSMAuctionDBStatusBar")
TSMAPI.GUI:CreateHorizontalLine(frame, -30, parent)
@ -84,29 +51,59 @@ function private:CreateStartScanContent(parent)
frame:SetAllPoints(parent)
frame:Hide()
-- Don't create or handle the GetAll button if player has disabled GetAll.
-- NOTE: This GUI creation is only done once per game reload, so this choice
-- won't change until the user does a UI "/reload" or logs out of the game.
local includeGetAll = not TSM.db.profile.disableGetAll
local function UpdateGetAllButton()
if not frame.startGetAllButton then
return -- Do nothing if the GetAll feature is disabled.
end
if TSM.Scan.isScanning then
frame:Disable()
elseif not select(2, CanSendAuctionQuery()) then
-- Server says that GetAll isn't ready. Check our stored cooldown value.
local previous = TSM.db.profile.lastGetAll or time()
if previous > (time() - 15*60) then -- 15 minute enforced cooldown between GetAll scans...
local diff = previous + 15*60 - time()
local diffMin = math.floor(diff/60)
local diffSec = diff - diffMin*60
frame.getAllStatusText:SetText("|cff990000"..format(L["Ready in %s min and %s sec"], diffMin, diffSec))
else
frame.getAllStatusText:SetText("|cff990000"..L["Not Ready"])
end
frame:Enable()
frame.startGetAllButton:Disable()
else
-- Server says that GetAll is ready.
frame:Enable()
frame.getAllStatusText:SetText("|cff009900"..L["Ready"])
frame.startGetAllButton:Enable()
end
end
if includeGetAll then
frame:SetScript("OnShow", function(self)
TSMAPI:CreateTimeDelay("auctionDBGetAllStatus", 0, UpdateGetAllButton, 0.2)
end)
frame:SetScript("OnHide", function(self)
TSMAPI:CancelFrame("auctionDBGetAllStatus")
end)
end
frame.Enable = function(self)
if self.startGetAllButton then self.startGetAllButton:Enable() end
self.startFullScanButton:Enable()
self.pauseFullScanButton:Disable()
self.cancelScanButton:Disable()
self.startGroupScanButton:Enable()
self.startInventoryScanButton:Enable()
self.startBagScanButton:Enable()
self.startBankScanButton:Enable()
end
frame.Disable = function(self)
if self.startGetAllButton then self.startGetAllButton:Disable() end
self.startFullScanButton:Disable()
if TSM.Scan.isScanning then
self.pauseFullScanButton:Enable()
self.cancelScanButton:Enable()
else
self.pauseFullScanButton:Disable()
self.cancelScanButton:Disable()
end
self.startGroupScanButton:Disable()
self.startInventoryScanButton:Disable()
self.startBagScanButton:Disable()
self.startBankScanButton:Disable()
end
-- Top row: Auto updater.
@ -144,34 +141,43 @@ function private:CreateStartScanContent(parent)
buttonFrame:SetPoint("TOPLEFT", content, "TOPRIGHT", -200, 0)
buttonFrame:SetPoint("BOTTOMRIGHT")
-- Row: Full Scan.
-- Row: GetAll Scan.
-- NOTE: We hide this button if the player has disabled GetAll scans.
local yOffset = -50
-- Row: Full Scan.
if includeGetAll then
local btn = TSMAPI.GUI:CreateButton(buttonFrame, 18)
btn:SetPoint("TOPLEFT", 6, yOffset)
btn:SetPoint("TOPRIGHT", -6, yOffset)
btn:SetHeight(22)
btn:SetScript("OnClick", TSM.Scan.StartFullScan)
btn:SetText(L["Run Full Scan"])
btn.tooltip = L["A full auction house scan will scan every item on the auction house. Expect this scan to take several minutes or longer."]
frame.startFullScanButton = btn
btn:SetScript("OnClick", TSM.Scan.StartGetAllScan)
btn:SetText(L["Run GetAll Scan"])
btn.tooltip = L["A GetAll scan is the fastest in-game method for scanning every item on the auction house. However, there are many possible bugs on Blizzard's end with it including the chance for it to disconnect you from the game. Also, it has a 15 minute cooldown. You can disable the GetAll button via TSM's AuctionDB options if this feature doesn't work well on your server."]
frame.startGetAllButton = btn
local text = TSMAPI.GUI:CreateLabel(buttonFrame)
text:SetPoint("TOPLEFT", btn, "BOTTOMLEFT", 0, -3)
text:SetPoint("TOPRIGHT", btn, "BOTTOMRIGHT", 0, -3)
text:SetHeight(16)
text:SetJustifyH("CENTER")
text:SetJustifyV("CENTER")
frame.getAllStatusText = text
yOffset = yOffset - 40
yOffset = yOffset - 50
TSMAPI.GUI:CreateHorizontalLine(buttonFrame, yOffset)
yOffset = yOffset - 20
end
-- Row: Group Scan.
-- Row: Full Scan.
local btn = TSMAPI.GUI:CreateButton(buttonFrame, 18)
btn:SetPoint("TOPLEFT", 6, yOffset)
btn:SetPoint("TOPRIGHT", -6, yOffset)
btn:SetHeight(22)
btn:SetScript("OnClick", GUI.StartGroupScan)
btn:SetText(L["Scan Selected Groups"])
btn.tooltip = L["This will do a slow auction house scan of every item in the selected groups and update their AuctionDB prices. This may take several minutes."]
frame.startGroupScanButton = btn
btn:SetScript("OnClick", TSM.Scan.StartFullScan)
btn:SetText(L["Run Full Scan"])
btn.tooltip = L["A full auction house scan will scan every item on the auction house but is far slower than a GetAll scan. Expect this scan to take several minutes or longer."]
frame.startFullScanButton = btn
yOffset = yOffset - 40
@ -179,65 +185,15 @@ function private:CreateStartScanContent(parent)
yOffset = yOffset - 20
-- Row: Inventory Scan.
local btn = TSMAPI.GUI:CreateButton(buttonFrame, 18)
btn:SetPoint("TOPLEFT", 6, yOffset)
btn:SetPoint("TOPRIGHT", -6, yOffset)
btn:SetHeight(22)
btn:SetScript("OnClick", GUI.StartInventoryScan)
btn:SetText(L["Scan Inventory"])
btn.tooltip = L["Scans bags and bank for all characters."]
frame.startInventoryScanButton = btn
yOffset = yOffset - 30
-- Row: Bag Scan.
local btn = TSMAPI.GUI:CreateButton(buttonFrame, 18)
btn:SetPoint("TOPLEFT", 6, yOffset)
btn:SetPoint("TOPRIGHT", -6, yOffset)
btn:SetHeight(22)
btn:SetScript("OnClick", GUI.StartBagScan)
btn:SetText(L["Scan Bags"])
btn.tooltip = L["Scan bag items."]
frame.startBagScanButton = btn
yOffset = yOffset - 30
-- Row: Bank Scan.
-- Row: Group Scan.
local btn = TSMAPI.GUI:CreateButton(buttonFrame, 18)
btn:SetPoint("TOPLEFT", 6, yOffset)
btn:SetPoint("TOPRIGHT", -6, yOffset)
btn:SetHeight(22)
btn:SetScript("OnClick", GUI.StartBankScan)
btn:SetText(L["Scan Bank"])
btn.tooltip = L["Scan bank items."]
frame.startBankScanButton = btn
yOffset = yOffset - 40
TSMAPI.GUI:CreateHorizontalLine(buttonFrame, yOffset)
yOffset = yOffset - 20
local pauseBtn = TSMAPI.GUI:CreateButton(buttonFrame, 16)
pauseBtn:SetPoint("TOPLEFT", 6, yOffset)
pauseBtn:SetPoint("TOPRIGHT", -6, yOffset)
pauseBtn:SetHeight(18)
pauseBtn:SetScript("OnClick", TSM.Scan.TogglePause)
pauseBtn:SetText(L["Pause"])
pauseBtn:Disable()
frame.pauseFullScanButton = pauseBtn
yOffset = yOffset - 24
local cancelBtn = TSMAPI.GUI:CreateButton(buttonFrame, 16)
cancelBtn:SetPoint("TOPLEFT", 6, yOffset)
cancelBtn:SetPoint("TOPRIGHT", -6, yOffset)
cancelBtn:SetHeight(18)
cancelBtn:SetScript("OnClick", GUI.CancelScan)
cancelBtn:SetText(L["Cancel Scan"])
cancelBtn:Disable()
frame.cancelScanButton = cancelBtn
btn:SetScript("OnClick", GUI.StartGroupScan)
btn:SetText(L["Scan Selected Groups"])
btn.tooltip = L["This will do a slow auction house scan of every item in the selected groups and update their AuctionDB prices. This may take several minutes."]
frame.startGroupScanButton = btn
return frame
end
@ -252,108 +208,3 @@ function GUI:StartGroupScan()
end
TSM.Scan:StartGroupScan(items)
end
function GUI:StartInventoryScan()
local players = TSMAPI:ModuleAPI("ItemTracker", "playerlist")
if not players then
TSM:Print(L["ItemTracker data unavailable."])
return
end
local specificItems, baseCandidates, hasSpecific = {}, {}, {}
for _, player in ipairs(players) do
local bags = TSMAPI:ModuleAPI("ItemTracker", "playerbags", player)
local bank = TSMAPI:ModuleAPI("ItemTracker", "playerbank", player)
if bags then
private:CollectItemStrings(bags, specificItems, baseCandidates, hasSpecific)
end
if bank then
private:CollectItemStrings(bank, specificItems, baseCandidates, hasSpecific)
end
end
for baseItemString in pairs(baseCandidates) do
if not hasSpecific[baseItemString] then
specificItems[baseItemString] = true
end
end
local items = {}
for itemString in pairs(specificItems) do
tinsert(items, itemString)
end
if #items == 0 then
TSM:Print(L["No inventory items found."])
return
end
TSM.Scan:StartGroupScan(items)
end
function private:CollectItemStrings(data, specificItems, baseCandidates, hasSpecific)
for itemString in pairs(data) do
if type(itemString) == "string" then
local baseItemString = TSMAPI:GetBaseItemString(itemString) or itemString
if itemString ~= baseItemString then
specificItems[itemString] = true
hasSpecific[baseItemString] = true
else
baseCandidates[baseItemString] = true
end
end
end
end
function private:GetItemTrackerItems(key)
local data = TSMAPI:ModuleAPI("ItemTracker", key)
if not data then
TSM:Print(L["ItemTracker data unavailable."])
return
end
local specificItems, baseCandidates, hasSpecific = {}, {}, {}
private:CollectItemStrings(data, specificItems, baseCandidates, hasSpecific)
for baseItemString in pairs(baseCandidates) do
if not hasSpecific[baseItemString] then
specificItems[baseItemString] = true
end
end
local items = {}
for itemString in pairs(specificItems) do
tinsert(items, itemString)
end
return items
end
function GUI:StartBagScan()
local items = private:GetItemTrackerItems("playerbags")
if not items then
return
end
if #items == 0 then
TSM:Print(L["No bag items found."])
return
end
TSM.Scan:StartGroupScan(items)
end
function GUI:StartBankScan()
local items = private:GetItemTrackerItems("playerbank")
if not items then
return
end
if #items == 0 then
TSM:Print(L["No bank items found."])
return
end
TSM.Scan:StartGroupScan(items)
end
function GUI:CancelScan()
TSMAPI.AuctionScan:StopScan(true)
TSM.Scan:DoneScanning()
end

305
TradeSkillMaster_AuctionDB/Modules/Scanning.lua

@ -27,8 +27,10 @@ local function FullScanCallback(event, ...)
-- We're running a "Full Scan" and have received an auction page.
-- NOTE: These normal per-page scans receive 50 items per page, and will
-- successfully download ALL auctions on private servers, thanks to pagination.
-- For example, a full scan retrieves all pages of 50 items each,
-- meaning that it covers the entire auction list.
-- For example, while Warmane's "GetAll" only returns 55000 of 126559 auctions,
-- the regular "Full Scan" mode retrieves all 2532 pages of 50 items each,
-- meaning that it covers 126600 auctions (and therefore grabs them all)
-- in this example. Users should always prefer "Full Scan" when "GetAll" fails.
local page, total = ...
-- Calculate the current page progress and the remainder as floating-point values.
@ -159,12 +161,240 @@ local function FullScanCallback(event, ...)
-- NOTE: "SCAN_INTERRUPTED" is from LibAuctionScan-1.0, which isn't used
-- by TSM anymore, and "INTERRUPTED" is from "TSM/Auction/AuctionScanning.lua",
-- which is what this scanner uses nowadays.
local data = ...
Scan:ProcessScanData(data)
Scan:DoneScanning()
end
end
function Scan.ProcessGetAllScan(self)
-- Await the "AUCTION_ITEM_LIST_UPDATE" event with the results of the "get all" scan.
-- NOTE: This event won't be received if the server ignored/throttled the "get all" scan.
local time_start = time()
local progress_bar = 0
while true do
-- NOTE: The fake progress bar takes 20 seconds to fill to 100% with this interval.
progress_bar = min(progress_bar + 1, 100) -- Fake progress bar from 0-100%.
self:Sleep(0.2) -- Update progress bar with this interval while waiting for server data.
if not Scan.isScanning then return end -- Failed or aborted scan.
if Scan.getAllLoaded then -- We have received the list of auctions via "AUCTION_ITEM_LIST_UPDATE" event!
break
end
-- NOTE: If it has taken more than 30 seconds, we can assume that the server
-- didn't send any data and won't be replying, most likely due to hidden
-- throttling (happens on many private servers, if scanning too soon after another).
local time_elapsed = time() - time_start
if time_elapsed >= 30 then
-- If this happens, the user has most likely been throttled and should try again later.
TSM.GUI:UpdateStatus(L["Running query... Server not responding due to throttling? Try again later..."], nil, 100)
else
TSM.GUI:UpdateStatus(L["Running query..."], nil, progress_bar)
end
end
-- IMPORTANT: As explained in the "Scan:AUCTION_ITEM_LIST_UPDATE()" code
-- comments, we're allowing GetAll scans even when the server DOESN'T return
-- all auctions. For example, Warmane has chosen to limit GetAll results to
-- only 55k auctions (even though they usually have 120k+ auctions). This
-- means that the market data calculations will be missing data. We should
-- therefore warn the user and tell them to use "Full Scan" instead, when
-- they're playing on such "limited GetAll" servers. But we'll still allow
-- use of "GetAll", with a warning message for people who INSIST on using it,
-- since even when it only looks at half the auctions, it can still give a
-- "pretty decent" idea of market values.
-- NOTE: It WOULD be "best" to always throw away (ignore) incomplete "GetAll"
-- data, but most casual players will prefer to have this feature even if those
-- incomplete scans are much less accurate than a proper "Full Scan".
-- NOTE: The inaccuracy of an incomplete "GetAll" scan completely depends on
-- how the server implements "GetAll". If the server sorts the cheapest
-- auctions first, then it's somewhat acceptable for popular items which
-- have many stacks (and are therefore likely to contain enough data points
-- in the "GetAll" result), and those items will "just" tend to be undervalued
-- by 2-10% less than their real market value (since their percentile-based
-- scans will look at less auctions than the real amount). But for very rare
-- or unpopular items, the partial "GetAll" scan is very dangerous, since
-- you might only receive the massively overpriced auctions of a certain
-- item, and thereby calculate an insanely high market value for it. These
-- dangers are increased if the server sends the auctions in a totally
-- random order, which means an even greater risk of market price pollution
-- by only seeing overpriced auctions for many of the items.
-- NOTE: Furthermore, receiving incomplete "GetAll" results means that TSM
-- will wipe all of its "cheapest, current buyout price" data for all items,
-- and then fills them with incorrect data (or nothing at all if an item
-- wasn't seen in the latest fetch), thus hindering your ability to look up
-- the correct "best current buyout prices" too.
-- NOTE: In summary, you'll have to use partial "GetAll" results at your
-- own risk! A "Full Scan" is ALWAYS much better when your server's "GetAll"
-- doesn't provide all auction data!
local shown, total = GetNumAuctionItems("list")
if total ~= Scan.getAllLoaded then -- getAllLoaded = Same as "shown", but cached via our GetAll event.
-- NOTE: Message is not localized, because who the hell is gonna provide
-- translations to this project? It'd be a waste of time to translate it,
-- especially since we dynamically insert a word based on the result.
TSM:Print(format(
"WARNING: Your server has %d auctions, but it%s sent %d auctions to us. Please use the normal \"Full Scan\" instead, if you want to accurately calculate the real market values of items. Your server's \"GetAll\" scan doesn't fetch all auctions!",
total, ((total > shown) and " only" or ""), shown
))
else
TSM:Print(format("All auctions received from server (%d auctions)...", Scan.getAllLoaded))
end
-- Collect relevant data about the auctions that we've received.
-- NOTE: "Scan.getAllLoaded" is the count of auctions we've received from
-- the server. One per listing. We keep this cached value to constantly verify
-- that we're still looking at the same auction-list while we're processing.
local data = {}
progress_bar = 0
for auction_idx=1, Scan.getAllLoaded do
if (auction_idx == 1) or (auction_idx == Scan.getAllLoaded) or ((auction_idx % 100) == 0) then
-- Update progress bar for the 1st, last, and every 100th auction.
progress_bar = min(100*(auction_idx/Scan.getAllLoaded), 100) -- Calculate progress bar from 0-100%.
TSM.GUI:UpdateStatus(format(L["Scanning page %s/%s"], 1, 1), progress_bar)
-- Yield the CPU every 100th "auction listing", to prevent freezing the game.
self:Yield()
-- Verify the currently visible auction count, to ensure that we're
-- still analyzing the same list of "GetAll" auction items.
if GetNumAuctionItems("list") ~= Scan.getAllLoaded then
-- This can happen due to server/game issues, or if the user
-- closes the auction GUI while we're processing. If that
-- happens, we'll abort the scan without processing the data.
--TSM:Print(L["GetAll scan did not run successfully due to issues on Blizzard's end. Using the TSM application for your scans is recommended."])
TSM:Print("GetAll: Scan failed or aborted by user.")
Scan:DoneScanning() -- Sets isScanning and getAllLoaded to nil.
return
end
end
-- Retrieve information about this auction.
-- * itemID = The numeric ID of the item (such as 52021, which would represent "Iceblade Arrow" for example).
-- * stack_size = How many are in this auction's exact "stack" (such as 940, if they're selling a stack of 940).
-- * buyout = The buyout price of the auction (to get the per-item price, we need to divide "buyout / stack_size").
local itemID = TSMAPI:GetItemID(GetAuctionItemLink("list", auction_idx))
--local _, _, stack_size, _, _, _, _, _, _, buyout = GetAuctionItemInfo("list", auction_idx)
local _, _, stack_size, _, _, _, _, _, buyout = GetAuctionItemInfo("list", auction_idx)
-- Only process this auction if we saw a valid buyout price (ignore bid-only auctions, etc).
if itemID and buyout and (buyout > 0) then
-- Calculate the price per item, always rounded downwards.
-- NOTE: TSM's "buyout per item" calculations are actually a freaking
-- mess. They basically use "floor(buyout/count)" everywhere in the
-- code, EXCEPT in SOME places where they use "floor(buyout/count + 0.5)"
-- which means that those round to the nearest number instead, which
-- is definitely more correct. In fact, it would probably be even more
-- correct to enforce a minimum value of "1", otherwise a stack of
-- 100 items for a total buyout price of 10 copper would end up as
-- "0 copper per item" by the basic flooring algorithm (floor(0.1) = 0).
-- But whatever, it's extremely inconsistently used everywhere in TSM's
-- codebase, and most places use the plain "floor downwards", so we'll
-- do that too. It would be way too much effort to rewrite the HUNDREDS of
-- other code locations that deal with money (all via differently named
-- variables), just to handle smarter rounding, and it would require
-- a lot of effort to ensure that TSM's algorithms still work afterwards.
-- But yeah, just realize this: TSM's per-item calculations aren't great,
-- however it really DOESN'T MATTER MUCH since it only affects items
-- whose prices are less than 1 copper per item, which is probably
-- why TSM's author never noticed any issues with the basic "floor()".
local buyout_per_item = floor(buyout / stack_size)
-- Append to the existing "data to process" for this item ID if exists, else create new item.
data[itemID] = data[itemID]
if not data[itemID] then
data[itemID] = {records={}, minBuyout=math.huge, quantity=0}
end
-- Calculate the lowest "per-item buyout price" we're seeing for this item.
data[itemID].minBuyout = min(data[itemID].minBuyout, buyout_per_item)
-- Count the total amount of this item that exists on the auction house (adds together all stacks).
-- NOTE: This is super useful when we're calculating the market value in data.lua,
-- since it tells us immediately what the total count (quantity) of all new records is.
data[itemID].quantity = data[itemID].quantity + stack_size
-- BRAINDEAD OLD TSM CODE WHICH ADDS 1 RECORD PER ITEM IN THE STACK,
-- MEANING 500 STACKS OF 1000 ARROWS WOULD BE HALF A MILLION TABLE
-- ROWS AND WOULD LEAD TO "OUT OF MEMORY" ERRORS. DON'T DO THIS!
-- for j=1, stack_size do
-- tinsert(data[itemID].records, buyout_per_item)
-- end
-- Rewritten, intelligent code which adds 1 record per "stack" (auction) instead.
-- NOTE: We avoid using hash-keys, saving memory by using a numeric array instead.
-- NOTE: We'll store 1 table per unique itemID, and 1 record per "auction"
-- which passed our filters (has a buyout price), meaning that it's usually
-- a bit less than the total auction count on the server, and will never
-- be too many rows for WoW's Lua memory system to handle.
-- NOTE: If there is ever a server with so many auctions that even this
-- would reach WoW's Lua memory limits, then we could possibly store
-- the records as a concatenated, semicolon-separated string instead,
-- which would be slower but would handle millions of auctions with ease.
tinsert(data[itemID].records, {stack_size, buyout_per_item})
end
end
-- Process the collected "GetAll" auction data as a new "complete scan" with today's date.
TSM.db.realm.lastCompleteScan = time()
TSM.Data:ProcessData(data, nil, verifyNewAlgorithm)
-- Show GUI progress while we're waiting for the processing.
-- NOTE: The status text will be set to "complete" elsewhere, automatically.
TSM.GUI:UpdateStatus(L["Processing data..."])
while TSM.processingData do
self:Sleep(0.2)
end
-- Processing is complete, so warn the user that they should reload the UI now.
TSM:Print(L["It is strongly recommended that you reload your ui (type '/reload') after running a GetAll scan. Otherwise, any other scans (Post/Cancel/Search/etc) will be much slower than normal."])
end
function Scan:AUCTION_ITEM_LIST_UPDATE()
Scan:UnregisterEvent("AUCTION_ITEM_LIST_UPDATE")
-- shown = How many auctions we received in the current data batch.
-- total = The total number of auction items.
local shown, total = GetNumAuctionItems("list")
-- OLD TSM CODE: It assumes that "GetAll" returns ALL auctions on official
-- Blizzard servers, so it also checks if "shown equals the total".
--if shown ~= total or shown == 0 then
-- WORKAROUND: Because places like Warmane with huge auction houses DON'T
-- return all items even via "GetAll" scans, we must ignore shown-vs-total
-- mismatches. The server won't let us query the subsequent pages since
-- there isn't any pagination in the "GetAll" API, and wouldn't work anyway
-- due to the 15-30 minute cooldown for "GetAll" calls, so we can't fetch
-- all auctions if the server is too popular. For example, typical
-- Warmane-Icecrown "GetAll" results will be: shown=55000, total=122523,
-- meaning that "GetAll" only receives about half of the total auctions.
if shown <= 0 then
--TSM:Print(L["GetAll scan did not run successfully due to issues on Blizzard's end. Using the TSM application for your scans is recommended."])
TSM:Print("GetAll: Scan failed due to server issues.")
Scan:DoneScanning() -- Sets isScanning and getAllLoaded to nil.
return
end
-- Cache the amount of auctions we received on the current "page/batch", so that
-- we can validate that we're looking at this "page" while processing later.
Scan.getAllLoaded = shown
end
function Scan:GetAllScanQuery()
-- NOTE: This API doesn't work properly on some servers. For example, on Warmane,
-- it always claims that you can do a "GetAll" scan after you log in, even
-- if you're on cooldown and the server will actually be ignoring your request,
-- which is why we had to implement timeout detection in "ProcessGetAllScan()".
local canScan, canGetAll = CanSendAuctionQuery()
if not canGetAll then return TSM:Print(L["Can't run a GetAll scan right now."]) end
if not canScan then return TSMAPI:CreateTimeDelay(0.5, Scan.GetAllScanQuery) end
Scan:RegisterEvent("AUCTION_ITEM_LIST_UPDATE")
QueryAuctionItems("", nil, nil, 0, 0, 0, 0, 0, 0, true)
TSMAPI.Threading:Start(Scan.ProcessGetAllScan, 1, function()
-- Pass through the cached "full scan complete elapsed" value, which ONLY
-- contains a value if the latest full scan was successfully completed.
-- NOTE: This callback runs when the thread is finished, no matter what
-- reason. That's why we must preserve the "time elapsed" value for display.
Scan:DoneScanning(Scan.fullScanCompleteElapsed)
end)
end
local function GroupScanCallback(event, ...)
if event == "QUERY_COMPLETE" then
@ -232,13 +462,12 @@ end
function Scan:StartGroupScan(items)
Scan.isScanning = "Group"
Scan.isBuggedGetAll = nil
Scan.groupItems = items
wipe(Scan.filterList)
wipe(Scan.groupScanData)
Scan.numFilters = 0
TSMAPI.AuctionScan:StopScan()
TSM.GUI:SetPauseEnabled(true)
TSM.GUI:SetPaused(false)
Scan.groupScanStartTime = time() -- Keep track of when we started the "Group Scan".
TSMAPI:GenerateQueries(items, GroupScanCallback)
TSM.GUI:UpdateStatus(L["Preparing Filters..."])
@ -247,16 +476,34 @@ end
function Scan:StartFullScan()
Scan.isScanning = "Full"
TSM.GUI:UpdateStatus(L["Running query..."])
Scan.isBuggedGetAll = nil
Scan.groupItems = nil
TSMAPI.AuctionScan:StopScan()
TSM.GUI:SetPauseEnabled(true)
TSM.GUI:SetPaused(false)
Scan.fullScanStartTime = time() -- Keep track of when we started the "Full Scan".
Scan.fullScanSecondsPerPage = -1 -- Reset the page-speed timer.
Scan.fullScanCompleteElapsed = nil -- Reset the "full scan completed" information.
TSMAPI.AuctionScan:RunQuery({name=""}, FullScanCallback)
end
function Scan:StartGetAllScan()
-- Refuse to perform "GetAll" if we're called while "GetAll" is disabled.
-- NOTE: Only happens if the player has visited the auction house and looked
-- at the AuctionDB GUI, and THEN gone into TSM's options to disable "GetAll".
-- The "Run GetAll Scan" button remains until /reload, so we must block it.
if TSM.db.profile.disableGetAll then
TSM:Print(L["You have disabled GetAll scans via AuctionDB's options."])
return
end
-- Begin the "GetAll" scan.
TSM.db.profile.lastGetAll = time()
Scan.isScanning = "GetAll"
Scan.isBuggedGetAll = nil
Scan.groupItems = nil
TSMAPI.AuctionScan:StopScan()
Scan:GetAllScanQuery()
end
function Scan:DoneScanning(seconds_elapsed)
if seconds_elapsed then
-- If given the "time elapsed", display it as "Done Scanning (1:35:27)".
@ -266,40 +513,12 @@ function Scan:DoneScanning(seconds_elapsed)
TSM.GUI:UpdateStatus(L["Done Scanning"], 100)
end
Scan.isScanning = nil
TSM.GUI:SetPauseEnabled(false)
TSM.GUI:SetPaused(false)
end
function Scan:PauseScan()
if not Scan.isScanning then return end
if TSMAPI.AuctionScan:PauseScan("AuctionDB") then
TSM.GUI:SetPaused(true)
TSM.GUI:UpdateStatus(L["Scan Paused"])
end
end
function Scan:ResumeScan()
if not Scan.isScanning then return end
if not TSMAPI.AuctionScan:IsScanning() then
Scan:DoneScanning()
return
end
if TSMAPI.AuctionScan:ResumeScan() then
TSM.GUI:SetPaused(false)
TSM.GUI:UpdateStatus(L["Resuming Scan..."])
end
end
function Scan:TogglePause()
if TSMAPI.AuctionScan:IsPaused() then
Scan:ResumeScan()
else
Scan:PauseScan()
end
Scan.getAllLoaded = nil
end
function Scan:ProcessScanData(scanData)
-- Handle scans performed via "Full Scan" and "Group Scan".
-- Handle scans performed via "Full Scan" and "Group Scan", but not "GetAll".
-- NOTE: See "Scan.ProcessGetAllScan()" for full explanation of this algorithm.
local data = {}
for itemString, obj in pairs(scanData) do
@ -337,7 +556,8 @@ function Scan:ProcessScanData(scanData)
-- Add this item to "data to process" even if there's zero records,
-- which can happen if they're all bid-only auctions.
-- NOTE: Empty records are fine; ProcessData ignores items without buyout prices.
-- NOTE: This differs from the behavior of "ProcessGetAllScan", which
-- only adds items that have at least 1 record with a buyout value.
-- NOTE: Empty records are totally fine either way, since "ProcessData"
-- simply ignores items that don't contain any buyout prices.
-- NOTE: If no buyout records were found, the "minBuyout" and "quantity"
@ -348,15 +568,12 @@ function Scan:ProcessScanData(scanData)
-- Mark the collected auction data as a new "complete scan" with today's date,
-- but only if this was a normal "Full Scan" (not just a "TSM item group" scan).
if Scan.isScanning ~= "Group" then
if Scan.isScanning ~= "group" then
TSM.db.realm.lastCompleteScan = time()
end
-- Process the collected auction data.
TSM.Data:ProcessData(data, Scan.groupItems, verifyNewAlgorithm)
if TSM.ChannelSync then
TSM.ChannelSync:BroadcastScanData(Scan.isScanning, Scan.groupItems)
end
end
function Scan:ProcessImportedData(auctionData)

61
TradeSkillMaster_AuctionDB/Modules/config.lua

@ -390,67 +390,12 @@ function Config:LoadOptions(container)
relativeWidth = 0.5,
tooltip = L["If checked, AuctionDB will add a tab to the AH to allow for in-game scans. If you are using the TSM app exclusively for your scans, you may want to hide it by unchecking this option. This option requires a reload to take effect."],
},
},
},
{
type = "InlineGroup",
title = L["Channel Sync"],
layout = "Flow",
children = {
{
type = "CheckBox",
label = L["Enable channel sync"],
settingInfo = { TSM.db.profile, "channelSyncEnabled" },
relativeWidth = 1,
callback = function() TSM.ChannelSync:ReloadConfig() container:ReloadTab() end,
tooltip = L["If checked, AuctionDB will sync scan data over the configured channel."],
},
{
type = "EditBox",
label = L["Channel sync name"],
settingInfo = { TSM.db.profile, "channelSyncName" },
label = L["Disable \"GetAll\" Auction Scans"],
settingInfo = { TSM.db.profile, "disableGetAll" },
relativeWidth = 0.5,
disabled = not TSM.db.profile.channelSyncEnabled,
callback = function(_, _, value)
value = strtrim(value or "")
if value == "" then
TSM.db.profile.channelSyncName = "TSM_AuctionDB"
else
TSM.db.profile.channelSyncName = value
end
TSM.ChannelSync:ReloadConfig()
container:ReloadTab()
end,
tooltip = L["The channel name used to share AuctionDB data."],
},
{
type = "Button",
text = L["Reset"],
relativeWidth = 0.2,
disabled = not TSM.db.profile.channelSyncEnabled,
callback = function()
TSM.db.profile.channelSyncName = "TSM_AuctionDB"
TSM.ChannelSync:ReloadConfig()
container:ReloadTab()
end,
tooltip = L["Resets the channel name to the default."],
},
{
type = "CheckBox",
label = L["Receive-only mode"],
settingInfo = { TSM.db.profile, "channelSyncReceiveOnly" },
relativeWidth = 1,
disabled = not TSM.db.profile.channelSyncEnabled,
callback = function() TSM.ChannelSync:ReloadConfig() end,
tooltip = L["If checked, AuctionDB will only receive channel sync data and will not broadcast scan data."],
},
{
type = "CheckBox",
label = L["Enable channel sync debug messages"],
settingInfo = { TSM.db.profile, "channelSyncDebug" },
relativeWidth = 1,
disabled = not TSM.db.profile.channelSyncEnabled,
tooltip = L["If checked, AuctionDB will print channel sync debug messages to chat."],
tooltip = L["If checked, AuctionDB will not perform \"GetAll\" scans. This is useful if your server doesn't return all auctions in its \"GetAll\" results, which means that you'll get incorrect market value calculations for all items. If you're playing on such servers, it's best to disable the \"GetAll\" feature to avoid accidentally polluting your price database with incorrect data. This option takes effect immediately, but requires a reload to completely hide the \"Run GetAll Scan\" button."],
},
},
},

73
TradeSkillMaster_AuctionDB/Modules/data.lua

@ -130,79 +130,31 @@ function Data:GetMarketValue(scans)
return totalWeight > 0 and floor(totalAmount / totalWeight + 0.5) or 0
end
function Data:ProcessExternalScanData(scanData, groupItems, scanTime)
if type(scanData) ~= "table" then return end
local data = {}
local groupSet = {}
if type(groupItems) == "table" then
if #groupItems > 0 then
for _, itemString in ipairs(groupItems) do
groupSet[itemString] = true
end
else
for itemString in pairs(groupItems) do
groupSet[itemString] = true
end
end
end
for itemString, obj in pairs(scanData) do
if TSMAPI:GetBaseItemString(itemString) == itemString then
local itemID = obj:GetItemID()
local quantity, minBuyout = 0, 0
local records = {}
for _, record in ipairs(obj.records) do
if record.buyout and record.buyout > 0 then
local itemBuyout = record:GetItemBuyout()
if itemBuyout then
if (itemBuyout < minBuyout or minBuyout == 0) then
minBuyout = itemBuyout
end
quantity = quantity + record.count
tinsert(records, {record.count, itemBuyout})
end
end
end
data[itemID] = {records=records, minBuyout=minBuyout, quantity=quantity}
groupSet[itemString] = true
end
end
if not next(data) then return end
Data:ProcessData(data, groupSet, nil, scanTime or time(), true)
if TSM.ChannelSync then
TSM.ChannelSync:BroadcastScanData("Search", groupSet)
end
end
--- Process a table of new market scan data.
-- @param scanData The market scan data.
-- @param[opt] groupItems Affects how the minBuyout data is wiped. Use nil for regular behavior.
-- @param[opt] verifyNewAlgorithm Boolean 'true' if you want to benchmark and verify the new market value algorithm.
-- @param[opt] scanTime Unix time to use for lastScan values.
-- @param[opt] skipMinBuyoutWipe If true, do not wipe minBuyout data before processing.
function Data:ProcessData(scanData, groupItems, verifyNewAlgorithm, scanTime, skipMinBuyoutWipe)
function Data:ProcessData(scanData, groupItems, verifyNewAlgorithm)
-- If we're currently processing data, retry in 0.2 seconds.
-- NOTE: This will retry itself over and over until it's able to process.
if TSM.processingData then
return TSMAPI:CreateTimeDelay(0.2, function() Data:ProcessData(scanData, groupItems, verifyNewAlgorithm, scanTime, skipMinBuyoutWipe) end)
return TSMAPI:CreateTimeDelay(0.2, function() Data:ProcessData(scanData, groupItems, verifyNewAlgorithm) end)
end
-- Wipe all of our existing "minBuyout" data for the items included in the
-- new, incoming scan data in case of "Item Group scan", or for all items
-- in memory for full scans.
-- new, incoming scan data in case of "Item Group scan", or for ALL currently
-- cached items in memory in other cases (such as "Full" and "GetAll" scans).
-- NOTE: It's no problem if we leave some items empty with "nil" minBuyout
-- values. That's how TSM is supposed to work, with items having an empty "minBuyout"
-- if there wasn't any "minBuyout" data for that item in the newest data batch.
if not skipMinBuyoutWipe then
if groupItems then
-- For group scans, only wipe items that are present in the incoming
-- payload. This avoids clearing data for timed out / missing queries.
for itemID in pairs(scanData) do
if TSM.data[itemID] then
-- A list of items ("group scan") was provided. Wipe data for those items.
for itemString in pairs(groupItems) do
local itemID = TSMAPI:GetItemID(itemString)
if TSM.data[itemID] then -- If we have existing data for this item.
TSM:DecodeItemData(itemID)
TSM.data[itemID].minBuyout = nil
TSM.data[itemID].minBuyout = nil -- Erase its stored minBuyout value.
TSM:EncodeItemData(itemID)
end
end
@ -215,7 +167,6 @@ function Data:ProcessData(scanData, groupItems, verifyNewAlgorithm, scanTime, sk
TSM:EncodeItemData(itemID)
end
end
end
-- Convert the incoming "scanData" hashmap to a numerically indexed table,
@ -233,7 +184,6 @@ function Data:ProcessData(scanData, groupItems, verifyNewAlgorithm, scanTime, sk
-- pausing between each chunk to allow the game client to avoid freezing.
local index = 1
local day = Data:GetDay()
local scanTimestamp = scanTime or TSM.db.realm.lastCompleteScan
local function DoDataProcessing()
for i = 1, 500 do
-- Abort if we've reached the end of the processing queue.
@ -323,9 +273,12 @@ function Data:ProcessData(scanData, groupItems, verifyNewAlgorithm, scanTime, sk
-- NOTE: This can happen if the scan data only contained "bid without
-- buyout" items, meaning they didn't have any per-item buyout data,
-- which can ONLY happen via "Scanning.lua:ProcessScanData()" when
-- doing a normal "Full Scan" or "Group Scan" (not "GetAll"). If
-- there aren't any buyout prices for the item, it still gets added
-- without any "records". This differs from "GetAll" which only adds
-- items to the queue if they had at least one "buyout price" auction.
-- NOTE: We're skipping the empty/indeterminable items to ensure that
-- we have identical behavior for both "GetAll" and all other scan
-- types, so that we NEVER add "empty/missing" market values for items!
-- NOTE: We allow a market value of "0", since it means there was
-- valid data in the calculations. However, "0" is extremely unlikely
@ -363,7 +316,7 @@ function Data:ProcessData(scanData, groupItems, verifyNewAlgorithm, scanTime, sk
-- item contains a "greater than 0" buyout value. That was mostly
-- necessary in the past, when TSM sloppily included bid-only items
-- in the data, but should no longer be able to happen with our new code!
TSM.data[itemID].lastScan = scanTimestamp
TSM.data[itemID].lastScan = TSM.db.realm.lastCompleteScan
TSM.data[itemID].minBuyout = data.minBuyout > 0 and data.minBuyout or nil
TSM.data[itemID].quantity = data.quantity -- Counts all items of all stacks.
Data:UpdateMarketValue(TSM.data[itemID])

34
TradeSkillMaster_AuctionDB/TradeSkillMaster_AuctionDB.lua

@ -15,8 +15,6 @@ local L = LibStub("AceLocale-3.0"):GetLocale("TradeSkillMaster_AuctionDB") -- lo
TSM.MAX_AVG_DAY = 1
local SECONDS_PER_DAY = 60 * 60 * 24
local eventObj
local private = {}
local savedDBDefaults = {
realm = {
@ -36,10 +34,7 @@ local savedDBDefaults = {
marketValueTooltip = true,
minBuyoutTooltip = true,
showAHTab = true,
channelSyncEnabled = true,
channelSyncName = "TSM_AuctionDB",
channelSyncReceiveOnly = false,
channelSyncDebug = false,
disableGetAll = false,
},
}
@ -80,7 +75,6 @@ function TSM:RegisterModule()
{ key = "lastCompleteScan", callback = TSM.GetLastCompleteScan },
{ key = "lastCompleteScanTime", callback = TSM.GetLastCompleteScanTime },
{ key = "adbScans", callback = TSM.GetScans },
{ key = "processScanData", callback = "Data:ProcessExternalScanData" },
--{ key = "adbOppositeFaction", callback = TSM.GetOppositeFactionData },
}
TSM.tooltipOptions = {callback = "Config:LoadTooltipOptions"}
@ -229,32 +223,6 @@ function TSM:OnEnable()
end
TSM:LoadAuctionData()
eventObj = eventObj or TSMAPI:GetEventObject()
eventObj:SetCallback("TSM:AUCTIONCONTROL:ITEMBOUGHT", private.OnItemBought)
end
function private.OnItemBought(_, data)
if type(data) ~= "table" or not data.itemString then return end
local itemID = TSMAPI:GetItemID(data.itemString)
if not itemID or not TSM.data[itemID] then return end
TSM:DecodeItemData(itemID)
local link = data.link or select(2, TSMAPI:GetSafeItemInfo(data.itemString)) or data.itemString
local buyoutText = data.buyout and (TSMAPI:FormatTextMoney(data.buyout, "|cffffffff", true) or "---") or "---"
local total = 0
local baseItemString = TSMAPI:GetBaseItemString(data.itemString)
for _, _, itemString, quantity in TSMAPI:GetBagIterator() do
if TSMAPI:GetBaseItemString(itemString) == baseItemString then
total = total + quantity
end
end
local mailData = TSMAPI:ModuleAPI("ItemTracker", "playermail")
if type(mailData) == "table" then
total = total + (mailData[baseItemString] or 0)
end
local marketValue = TSM:GetMarketValue(itemID)
local count = data.count or 1
local marketText = marketValue and (TSMAPI:FormatTextMoney(marketValue * count, "|cffffffff", true) or "---") or "---"
TSM:Printf("Bought %s for %s (x%d). bags=%s dbmarket=%s (x%d)", link, buyoutText, count, tostring(total), marketText, count)
end
function TSM:OnTSMDBShutdown()

5
TradeSkillMaster_AuctionDB/TradeSkillMaster_AuctionDB.toc

@ -2,10 +2,10 @@
## Title: |cff00ff00TradeSkillMaster_AuctionDB|r
## Notes: Stores auction house data and calculates market prices.
## Author: Sapu94, Bart39
## Version: 2.3.45
## Version: v2.3.10
## SavedVariables: AscensionTSM_AuctionDB
## Dependency: TradeSkillMaster
## X-Curse-Packaged-Version: 2.3.45
## X-Curse-Packaged-Version: v2.3.10
## X-Curse-Project-Name: TradeSkillMaster_AuctionDB
## X-Curse-Project-ID: tradeskillmaster_auctiondb
## X-Curse-Repository-ID: wow/tradeskillmaster_auctiondb/mainline
@ -25,7 +25,6 @@ Locale\ptBR.lua
TradeSkillMaster_AuctionDB.lua
AppData.lua
Modules\data.lua
Modules\ChannelSync.lua
Modules\Scanning.lua
Modules\GUI.lua
Modules\config.lua

1
TradeSkillMaster_Auctioning/TradeSkillMaster_Auctioning.lua

@ -22,7 +22,6 @@ local savedDBDefaults = {
global = {
optionsTreeStatus = {},
scanCompleteSound = 1,
flashClientOnComplete = false,
cancelWithBid = false,
matchWhitelist = true,
roundNormalPrice = false,

6
TradeSkillMaster_Auctioning/locale/enUS.lua

@ -100,7 +100,6 @@ L["Duration"] = true
L["Edit Post Price"] = true
L["Enable Reset Scan"] = true
L["Enable Sounds"] = true
L["Flash Client Icon"] = true
L["Error creating operation. Operation with name '%s' already exists."] = true
L["General Operation Options"] = true
L["General Options"] = true
@ -122,7 +121,6 @@ L["If checked, TSM will not print out a chat message when you have an invalid pr
L["If checked, a cancel scan will cancel any auctions which can be reposted for a higher price."] = true
L["If checked, a cancel scan will cancel any auctions which have been undercut and are still above your minimum price."] = true
L["If checked, groups which the opperation applies to will be included in a reset scan."] = true
L["If checked, the game client icon will flash when a scan is complete."] = true
L["If checked, the minimum, normal and maximum prices of the first operation for the item will be shown in tooltips."] = true
L["If checked, whenever you post an item at its normal price, the buyout will be rounded up to the nearest gold."] = true
L["If enabled, instead of not posting when a whitelisted player has an auction posted, Auctioning will match their price."] = true
@ -186,7 +184,6 @@ L["Operation"] = true
L["Operations"] = true
L["Options"] = true
L["Other Auctioning Searches"] = true
L["Pause"] = true
L["Percentage of the buyout as bid, if you set this to 90% then a 100g buyout will have a 90g bid."] = true
L["Player name"] = true
L["Play the selected sound when a post / cancel scan is complete and items are ready to be posted / canceled (the gray bar is all the way across).Select None to disable sounds"] = true
@ -220,16 +217,13 @@ L["Reset Scan Finished"] = true
L["Reset Settings"] = true
L["Reset"] = true
L["Resetting enabled."] = true
L["Resume"] = true
L["Restart"] = true
L["Return to Summary"] = true
L["Right-Click to add %s to your friends list."] = true
L["Round Normal Price"] = true
L["Resuming Scan..."] = true
L["Running Scan..."] = true
L["Save New Price"] = true
L["Scan Complete!"] = true
L["Scan Paused"] = true
L["Scanning %d / %d (Page 1 / ?)"] = true
L["Scanning %d / %d (Page %d / %d)"] = true
L["Scanning %d / %d"] = true

51
TradeSkillMaster_Auctioning/modules/GUI.lua

@ -12,22 +12,11 @@ local GUI = TSM:NewModule("GUI", "AceEvent-3.0", "AceHook-3.0")
local AceGUI = LibStub("AceGUI-3.0")
local private = {}
function private:SetSelectionButtonsEnabled(enabled)
if not private.selectionFrame then return end
local action = enabled and "Enable" or "Disable"
if private.selectionFrame.postBtn then
private.selectionFrame.postBtn[action](private.selectionFrame.postBtn)
end
if private.selectionFrame.cancelBtn then
private.selectionFrame.cancelBtn[action](private.selectionFrame.cancelBtn)
end
end
function private:CreateButtons(parent)
local height = 24
local frame = CreateFrame("Frame", nil, parent)
frame:SetHeight(height)
frame:SetWidth(265)
frame:SetWidth(210)
frame:SetPoint("BOTTOMRIGHT", -92, 5)
frame.Enable = function(self)
@ -37,7 +26,6 @@ function private:CreateButtons(parent)
self.cancel:Enable()
end
self.skip:Enable()
self.pause:Disable()
self.stop:Enable()
end
@ -48,7 +36,6 @@ function private:CreateButtons(parent)
self.cancel:Disable()
end
self.skip:Disable()
self.pause:Enable()
end
frame.UpdateMode = function(self)
@ -64,16 +51,10 @@ function private:CreateButtons(parent)
self.stop:Enable()
end
frame.SetPaused = function(self, isPaused)
self.pause:SetText(isPaused and L["Resume"] or L["Pause"])
self.pause.isPaused = isPaused
end
local function OnClick(self)
if self.which == "stop" and self.isDone then
GUI:HideSelectionFrame()
private.selectionFrame:Show()
private:SetSelectionButtonsEnabled(true)
elseif frame:IsVisible() and private.OnAction then
private:OnAction(self.which)
end
@ -98,28 +79,18 @@ function private:CreateButtons(parent)
button:SetScript("OnClick", OnClick)
frame.cancel = button
local button = TSMAPI.GUI:CreateButton(frame, 18, "TSMAuctioningSkipButton")
local button = TSMAPI.GUI:CreateButton(frame, 18)
button:SetPoint("TOPLEFT", frame.post, "TOPRIGHT", 5, 0)
button:SetWidth(60)
button:SetHeight(height)
button:SetText(L["Skip"])
button.which = "skip"
button:SetScript("OnClick", OnClick)
_G["TSMAuctioningCancelSkipButton"] = button
frame.skip = button
local button = TSMAPI.GUI:CreateButton(frame, 18)
button:SetPoint("TOPLEFT", frame.skip, "TOPRIGHT", 5, 0)
button:SetWidth(60)
button:SetHeight(height)
button:SetText(L["Pause"])
button.which = "pause"
button:SetScript("OnClick", OnClick)
frame.pause = button
local button = TSMAPI.GUI:CreateButton(frame, 18)
button:SetPoint("TOPLEFT", frame.pause, "TOPRIGHT", 5, 0)
button:SetWidth(60)
button:SetWidth(70)
button:SetHeight(height)
button:SetText(L["Stop"])
button.which = "stop"
@ -710,7 +681,6 @@ function private:Stopped(notDone)
private.buttons:Disable(true)
private.statusBar:UpdateStatus(100, 100)
private.contentButtons.currAuctionsButton:Hide()
private.buttons.pause:Disable()
if private.mode == "Post" then
TSMAPI:CreateTimeDelay(0.5, SetGoldText)
@ -727,7 +697,6 @@ function private:Stopped(notDone)
end
private.buttons.stop:SetText(L["Restart"])
private.buttons.stop.isDone = true
private.buttons:SetPaused(false)
end
@ -765,7 +734,7 @@ function GUI:CreateSelectionFrame(parent)
frame.helpText = helpText
local btnWidth = floor((stContainer:GetWidth() - 10)/3)
local postBtn = TSMAPI.GUI:CreateButton(frame, 16, "TSMAuctioningPostScanButton")
local postBtn = TSMAPI.GUI:CreateButton(frame, 16)
postBtn:SetPoint("BOTTOMLEFT", 5, 5)
postBtn:SetHeight(20)
postBtn:SetWidth(btnWidth)
@ -777,7 +746,7 @@ function GUI:CreateSelectionFrame(parent)
end)
frame.postBtn = postBtn
local cancelBtn = TSMAPI.GUI:CreateButton(frame, 16, "TSMAuctioningCancelScanButton")
local cancelBtn = TSMAPI.GUI:CreateButton(frame, 16)
cancelBtn:SetPoint("BOTTOMLEFT", postBtn, "BOTTOMRIGHT", 5, 0)
cancelBtn:SetHeight(20)
cancelBtn:SetWidth(btnWidth)
@ -902,7 +871,7 @@ function GUI:CreateScanFrame(parent)
local statusBarFrame = CreateFrame("Frame", nil, frame.content)
statusBarFrame:SetPoint("TOPLEFT", frame.content, "BOTTOMLEFT", 165, -2)
statusBarFrame:SetWidth(300)
statusBarFrame:SetWidth(355)
statusBarFrame:SetHeight(30)
private.statusBar = TSMAPI.GUI:CreateStatusBar(statusBarFrame, "TSMAuctioningStatusBar")
@ -921,7 +890,6 @@ function GUI:CreateScanFrame(parent)
end
function GUI:StartScan(frame)
private:SetSelectionButtonsEnabled(false)
private.selectionFrame:Hide()
private.scanFrame = private.scanFrame or GUI:CreateScanFrame(frame)
private.scanFrame:Show()
@ -929,8 +897,6 @@ function GUI:StartScan(frame)
private.buttons:Show()
private.buttons:UpdateMode()
private.buttons:Disable()
private.buttons:SetPaused(false)
private.buttons.pause:Enable()
private.buttons.stop.isDone = nil
private.buttons.stop:SetText(L["Stop"])
private.contentButtons:Show()
@ -981,13 +947,12 @@ function GUI:ShowSelectionFrame(frame)
if private.scanFrame then private.scanFrame:Hide() end
private.selectionFrame = private.selectionFrame or GUI:CreateSelectionFrame(frame)
private.selectionFrame:Show()
private:SetSelectionButtonsEnabled(true)
TSMAPI.AuctionScan:StopScan(false)
TSMAPI.AuctionScan:StopScan()
end
function GUI:HideSelectionFrame()
private.selectionFrame:Hide()
if private.scanFrame then private.scanFrame:Hide() end
TSMAPI.AuctionScan:StopScan(false)
TSMAPI.AuctionScan:StopScan()
TSM.Reset:Hide()
end

12
TradeSkillMaster_Auctioning/modules/Options.lua

@ -107,12 +107,6 @@ function Options:DrawGeneralSettings(container)
settingInfo = { TSM.db.global, "scanCompleteSound" },
tooltip = L["Play the selected sound when a post / cancel scan is complete and items are ready to be posted / canceled (the gray bar is all the way across).Select None to disable sounds"],
},
{
type = "CheckBox",
label = L["Flash Client Icon"],
settingInfo = { TSM.db.global, "flashClientOnComplete" },
tooltip = L["If checked, the game client icon will flash when a scan is complete."],
},
{
type = "Button",
text = L["Test Selected Sound"],
@ -548,7 +542,7 @@ function Options:DrawOperationPost(container, operationName)
type = "EditBox",
label = L["Minimum Price"],
settingInfo = { operation, "minPrice" },
relativeWidth = 1,
relativeWidth = 0.49,
acceptCustom = true,
disabled = operation.relationships.minPrice,
tooltip = L["The lowest price you want an item to be posted for. Auctioning will not undercut auctions below this price."],
@ -566,7 +560,7 @@ function Options:DrawOperationPost(container, operationName)
type = "EditBox",
label = L["Maximum Price"],
settingInfo = { operation, "maxPrice" },
relativeWidth = 1,
relativeWidth = 0.49,
acceptCustom = true,
disabled = operation.relationships.maxPrice,
tooltip = L["The maximum price you want an item to be posted for. Auctioning will not undercut auctions above this price."],
@ -584,7 +578,7 @@ function Options:DrawOperationPost(container, operationName)
type = "EditBox",
label = L["Normal Price"],
settingInfo = { operation, "normalPrice" },
relativeWidth = 1,
relativeWidth = 0.49,
acceptCustom = true,
disabled = operation.relationships.normalPrice,
tooltip = L["Price to post at if there are none of an item currently on the AH."],

36
TradeSkillMaster_Auctioning/modules/PostScan.lua

@ -13,11 +13,8 @@ local L = LibStub("AceLocale-3.0"):GetLocale("TradeSkillMaster_Auctioning") -- l
local bagInfo, bagState = {}, {}
local bagInfoUpdate = 0
local postQueue, currentItem, itemLocations = {}, {}, {}
local scanItems
local totalToPost, totalPosted, count = 0, 0, 0
local isScanning, GUI
local lastProcessedAuctionDataHash
local lastProcessedAuctionDataCount
function Post:ValidateOperation(itemString, operation)
local itemLink, salePrice = TSMAPI:Select({2, 11}, TSMAPI:GetSafeItemInfo(itemString))
@ -132,10 +129,6 @@ function Post:GetScanListAndSetup(GUIRef, options)
tinsert(scanList, itemString)
end
end
scanItems = {}
for _, itemString in ipairs(scanList) do
tinsert(scanItems, itemString)
end
TSMAPI:FireEvent("AUCTIONING:POST:START", {numItems=#scanList, isGroup=true})
return scanList
@ -156,11 +149,8 @@ function Post:ProcessItem(itemString)
if toPost then
local bid
bid, buyout, reason = Post:GetPostPrice(itemString, operation)
if not bid or not buyout then
toPost = nil
reason = reason or "invalid"
else
local postTime = (operation.duration == 48 and 3) or (operation.duration == 24 and 2) or 1
for i = 1, #toPost do
local stackSize, numStacks = unpack(toPost[i])
@ -170,7 +160,6 @@ function Post:ProcessItem(itemString)
tinsert(data, { numStacks = numStacks, stackSize = stackSize, buyout = buyout, postTime = postTime })
end
end
end
TSM.Log:AddLogRecord(itemString, "post", (toPost and L["Post"] or L["Skip"]), reason, operation, buyout)
if #postQueue > 0 and not currentItem.bag then
@ -207,9 +196,6 @@ function Post:ShouldPost(itemString, operation, numInBags)
end
local prices = TSM.Util:GetItemPrices(operation, itemString)
if not prices.minPrice or not prices.maxPrice or not prices.normalPrice or not prices.undercut then
return nil, "invalid", numInBags
end
if buyout and buyout <= prices.minPrice then
-- lowest is below min price
if not prices.resetPrice then
@ -260,14 +246,11 @@ function Post:GetPostPrice(itemString, operation)
local lowestBuyout, lowestBid, lowestOwner, isWhitelist, isPlayer = TSM.Scan:GetLowestAuction(itemString, operation)
local bid, buyout, info
local prices = TSM.Util:GetItemPrices(operation, itemString)
if not prices.minPrice or not prices.maxPrice or not prices.normalPrice or not prices.undercut then
return nil, nil, "invalid"
end
if not lowestOwner then
-- No other auctions up, default to normalPrice
info = "postingNormal"
buyout = prices.normalPrice
elseif prices.resetPrice and lowestBuyout and lowestBuyout <= prices.minPrice then
elseif prices.resetPrice and lowestBuyout <= prices.minPrice then
-- item is below min price and a priceReset is set
if operation.priceReset == "minPrice" then
info = "postingResetMin"
@ -280,14 +263,10 @@ function Post:GetPostPrice(itemString, operation)
error("Unknown 'below minimum' price setting.")
end
buyout = prices.resetPrice
elseif isPlayer or (isWhitelist and lowestBuyout and lowestBuyout - prices.undercut <= prices.maxPrice) then
elseif isPlayer or (isWhitelist and lowestBuyout - prices.undercut <= prices.maxPrice) then
-- Either we already have one up or someone on the whitelist does
bid, buyout = min(lowestBid, lowestBuyout), lowestBuyout
info = isPlayer and "postingPlayer" or "postingWhitelist"
elseif not lowestBuyout then
-- We have an owner record but no valid buyout; treat as no reliable floor.
info = "postingNormal"
buyout = prices.normalPrice
else
-- we've been undercut and we are going to undercut back
buyout = lowestBuyout - prices.undercut
@ -544,14 +523,5 @@ end
function Post:DoneScanning()
isScanning = false
if scanItems and next(TSM.Scan.auctionData) then
local currentHash = TSM.Scan.auctionDataHash
local currentCount = TSM.Scan.auctionDataCount
if currentHash and currentCount and (currentHash ~= lastProcessedAuctionDataHash or currentCount ~= lastProcessedAuctionDataCount) then
lastProcessedAuctionDataHash = currentHash
lastProcessedAuctionDataCount = currentCount
TSMAPI:ModuleAPI("AuctionDB", "processScanData", TSM.Scan.auctionData, scanItems, time())
end
end
return totalToPost
end

10
TradeSkillMaster_Auctioning/modules/ResetScan.lua

@ -14,7 +14,6 @@ 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)
@ -605,10 +604,6 @@ function Reset:RemoveCurrentAuction()
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
@ -702,11 +697,6 @@ function Reset:BuyAuction()
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()

18
TradeSkillMaster_Auctioning/modules/ScanUtil.lua

@ -12,21 +12,6 @@ local L = LibStub("AceLocale-3.0"):GetLocale("TradeSkillMaster_Auctioning") -- l
Scan.auctionData = {}
Scan.skipped = {}
Scan.auctionDataHash = nil
Scan.auctionDataCount = 0
local function HashAuctionData(scanData)
local hash = 0
local count = 0
for itemString, auctionItem in pairs(scanData) do
count = count + 1
hash = (hash * 31 + (auctionItem and #auctionItem.records or 0)) % 4294967296
hash = (hash * 31 + (auctionItem and #auctionItem.compactRecords or 0)) % 4294967296
local itemId = TSMAPI:GetItemID(itemString) or 0
hash = (hash * 31 + itemId) % 4294967296
end
return hash, count
end
local function CallbackHandler(event, ...)
@ -77,8 +62,6 @@ end
function Scan:StartItemScan(itemList)
wipe(Scan.auctionData)
wipe(Scan.skipped)
Scan.auctionDataHash = nil
Scan.auctionDataCount = 0
TSMAPI:GenerateQueries(itemList, CallbackHandler)
TSM.Manage:UpdateStatus("query", 0, -1)
end
@ -96,7 +79,6 @@ function Scan:ScanNextFilter()
-- NOTE: Our scan progress counter below starts counting from 0 as the first item.
if #Scan.filterList == 0 then
TSM.Manage:UpdateStatus("scan", Scan.numFilters, Scan.numFilters)
Scan.auctionDataHash, Scan.auctionDataCount = HashAuctionData(Scan.auctionData)
return TSM.Manage:ScanComplete()
end
TSM.Manage:UpdateStatus("scan", Scan.numFilters-#Scan.filterList, Scan.numFilters)

21
TradeSkillMaster_Auctioning/modules/manage.lua

@ -72,25 +72,9 @@ function Manage:OnGUIEvent(event)
end
elseif event == "skip" then
Util:SkipItem()
elseif event == "pause" then
if TSMAPI.AuctionScan:IsPaused() then
if TSMAPI.AuctionScan:ResumeScan() then
GUI.buttons:SetPaused(false)
GUI.statusBar:SetStatusText(L["Resuming Scan..."])
GUI.infoText:SetInfo(L["Running Scan..."])
end
else
if TSMAPI.AuctionScan:PauseScan() then
GUI.buttons:SetPaused(true)
GUI.statusBar:SetStatusText(L["Scan Paused"])
GUI.infoText:SetInfo(L["Scan Paused"])
end
end
elseif event == "stop" then
TSMAPI:CancelFrame("auctioningNoScanProcessing")
TSMAPI.AuctionScan:StopScan()
GUI.buttons:SetPaused(false)
GUI.buttons.pause:Disable()
Util:Stop()
end
TSMAPI:CreateTimeDelay("aucManageSTUpdate", 0.01, GUI.UpdateAuctionsSTData)
@ -104,8 +88,6 @@ function Manage:ProcessScannedItem(itemString, noUpdate)
end
function Manage:ScanComplete(interrupted)
GUI.buttons:SetPaused(false)
GUI.buttons.pause:Disable()
if interrupted then
-- If our scan has been interrupted by the Auction House closing,
-- simply act as if the user clicked "Stop", but with an extra flag
@ -119,11 +101,8 @@ function Manage:ScanComplete(interrupted)
elseif TSM.db.global.scanCompleteSound ~= 1 then
PlaySound(TSM.Options:GetScanCompleteSound(TSM.db.global.scanCompleteSound), "Master")
end
if TSM.db.global.flashClientOnComplete then
FlashClientIcon()
end
end
end
-- these functions help display the status text which goes inside the statusbar
local function IsStepStarted(step)

3
TradeSkillMaster_Crafting/Locale/enUS.lua

@ -40,9 +40,6 @@ L["Craft Value Method"] = true
L["Craft Queue"] = true
L["Crafter"] = true
L["Crafts"] = true
L["All Professions"] = true
L["Available"] = true
L["Cost"] = true
L["Crafting Cost"] = true
L["Crafting Material Cost"] = true
L["Crafting operations contain settings for restocking the items in a group. Type the name of the new operation into the box below and hit 'enter' to create a new Crafting operation."] = true

489
TradeSkillMaster_Crafting/Modules/CraftingGUI.lua

@ -15,18 +15,6 @@ local priceTextCache = { lastClear = 0 }
local private = {}
private.gather = {}
private.shown = {}
private.eventObj = nil
local function IsCrafterPlayer(crafter)
if not crafter then return false end
local player = UnitName("player")
if not player then return false end
if strlower(crafter) == strlower(player) then
return true
end
local baseName = strmatch(crafter, "^[^-]+")
return baseName and strlower(baseName) == strlower(player) or false
end
-- list of profession skills that do not have crafting. used by UpdateTradeSkills
local invalidTrade = {
@ -75,7 +63,6 @@ function GUI:OnEnable()
GUI:RegisterEvent("UPDATE_PENDING_MAIL", "GatheringEventHandler")
GUI:RegisterEvent("AUCTION_HOUSE_SHOW", "GatheringEventHandler")
GUI:RegisterEvent("AUCTION_HOUSE_CLOSED", "GatheringEventHandler")
GUI:RegisterEvent("MAIL_INBOX_UPDATE", "QueueUpdateHandler")
TSMAPI:CreateTimeDelay("craftingUpdateTradeSkill", 1, function() GUI:UpdateSelectedTradeSkill() end, 0.1)
TSMAPI:RegisterForBagChange(function()
@ -88,9 +75,6 @@ function GUI:OnEnable()
TSMAPI:CreateTimeDelay("gatheringUpdateThrottle", 0.3, GUI.UpdateGathering)
end)
private.eventObj = private.eventObj or TSMAPI:GetEventObject()
private.eventObj:SetCallback("TSM:AUCTIONCONTROL:ITEMBOUGHT", private.OnAuctionBought)
GUI:UpdateTradeSkills()
GUI.gatheringFrame = GUI:CreateGatheringFrame()
if next(TSM.db.realm.gathering.neededMats) then
@ -98,18 +82,6 @@ function GUI:OnEnable()
end
end
function private.OnAuctionBought()
if not GUI.frame or not GUI.frame:IsVisible() then return end
TSMAPI:CreateTimeDelay("craftingQueueUpdateThrottle", 0.1, GUI.UpdateQueue)
TSMAPI:CreateTimeDelay("craftingProfessionUpdateThrottle", 0.2, GUI.UpdateProfessionsTabST)
end
function GUI:QueueUpdateHandler()
if not GUI.frame or not GUI.frame:IsVisible() then return end
TSMAPI:CreateTimeDelay("craftingQueueUpdateThrottle", 0.1, GUI.UpdateQueue)
TSMAPI:CreateTimeDelay("craftingProfessionUpdateThrottle", 0.2, GUI.UpdateProfessionsTabST)
end
function GUI:ShowGatheringFrame()
if GUI.gatheringFrame then
GUI.gatheringFrame:Show()
@ -151,12 +123,7 @@ function GUI:ShowProfessionWindow()
GUI:UpdateTradeSkills(GetTradeSkillLine())
TradeSkillFrame:SetScript("OnHide", nil)
HideUIPanel(TradeSkillFrame)
TradeSkillFrame:SetScript("OnHide", function()
if not GUI.noClose and not GUI.switchingProfession then
GUI.switchBtn:Hide()
CloseTradeSkill()
end
end)
TradeSkillFrame:SetScript("OnHide", function() if not GUI.noClose then GUI.switchBtn:Hide() CloseTradeSkill() end end)
priceTextCache.lastClear = 0
GUI.switchBtn:Update()
@ -218,7 +185,6 @@ function GUI:EventHandler(event, ...)
local unittest = ...
if unittest == "player" or unittest==nil then --Changing tradeskill frames and stuff has "nil" unit, when other players cast this also triggers with nil
if event == "TRADE_SKILL_CLOSE" then
if GUI.switchingProfession then return end
GUI.frame:Hide()
elseif event == "TRADE_SKILL_UPDATE" or event == "TRADE_SKILL_FILTER_UPDATE" then
if GetTradeSkillLine() ~= GUI.currentTradeSkill or select(2, IsTradeSkillLinked()) ~= GUI.currentLinkedPlayer then
@ -492,13 +458,13 @@ function GUI:CreateGUI()
local frameDefaults = {
x = 100,
y = 300,
width = 520,
width = 450,
height = 500,
scale = 1,
}
local frame = TSMAPI:CreateMovableFrame("TSMCraftingTradeSkillFrame", frameDefaults)
frame:SetResizable(true)
frame:SetMinResize(520, 400)
frame:SetMinResize(450, 400)
TSMAPI.Design:SetFrameBackdropColor(frame)
frame:Show()
frame:SetScript("OnHide", function() if not GUI.noClose then GUI.switchBtn:Hide() TradeSkillFrame:Show() CloseTradeSkill() end end)
@ -579,14 +545,11 @@ function GUI:CreateQueueFrame(parent)
GameTooltip:AddLine(TSM.db.realm.crafts[data.spellID].name .. " (x" .. data.numQueued .. ")")
local cost = TSM.Cost:GetCraftPrices(data.spellID)
local profitPercText, profitPercTextM = "---", "---"
if data.profit and cost and cost > 0 then
if data.profit then
local profitPercent = data.profit / cost * 100
profitPercText = format("%s%.0f%%|r", color, profitPercent)
local profitPercText = format("%s%.0f%%|r", color, profitPercent)
local profitPercentM = data.profit / cost * data.numQueued * 100
profitPercTextM = format("%s%.0f%%|r", color, profitPercentM)
end
if data.profit then
local profitPercTextM = format("%s%.0f%%|r", color, profitPercentM)
if data.profit>0 then
if moneyCoinsTooltip then
GameTooltip:AddLine("Profit: " .. (TSMAPI:FormatTextMoneyIcon(data.profit, color) or "---") .. " (" .. (profitPercText or "---") .. ")")
@ -920,52 +883,17 @@ function GUI:CreateProfessionsTab(parent)
local currentSelection
local player = UnitName("player")
local function SetNoClose()
GUI.noClose = true
GUI.switchingProfession = true
TSMAPI:CreateTimeDelay("craftingNoCloseClear", 0.6, function() GUI.noClose = nil end)
TSMAPI:CreateTimeDelay("craftingSwitchingClear", 0.8, function() GUI.switchingProfession = nil end)
end
local function SelectSpellInProfession(profession, spellID)
if GetTradeSkillLine() ~= profession then
local link = TSM.db.realm.tradeSkills[player] and TSM.db.realm.tradeSkills[player][profession] and TSM.db.realm.tradeSkills[player][profession].link
if not link then return end
local tradeString = strsub(select(3, ("|"):split(link)), 2)
SetNoClose()
SetItemRef(tradeString, link, "LeftButton", ChatFrame1)
return
end
for i = 1, GetNumTradeSkills() do
if TSM.Util:GetSpellID(i) == spellID then
TradeSkillFrame_SetSelection(i)
TradeSkillFrame_Update()
GUI:UpdateSelectedTradeSkill(true)
return
end
end
end
local HaveMatsCheckBox
local function UpdateProfession(self)
local list = {}
list["ALL"] = L["All Professions"]
for playerName, professionData in pairs(TSM.db.realm.tradeSkills) do
for name, data in pairs(professionData) do
if playerName == player then -- only display current player profs until blizz fix it
if not data.isSecondary and playerName == player then -- only display current player profs until blizz fix it
list[playerName .. "~" .. name] = format("%s %d/%d - %s", name, data.level or "?", data.maxLevel or "?", playerName)
end
end
end
self.dropdown:SetList(list)
if TSM.db.global.showAllProfessions then
self.dropdown:SetValue("ALL")
self.dropdown:SetText(L["All Professions"])
return
end
local playerName = select(2, IsTradeSkillLinked()) or player
local professionName, level, maxLevel = GetTradeSkillLine()
local professionString = format("%s %d/%d - %s", professionName, level, maxLevel, playerName)
@ -980,36 +908,13 @@ function GUI:CreateProfessionsTab(parent)
frame:SetScript("OnShow", UpdateProfession)
local function OnValueChanged(_, _, index)
if index == "ALL" then
TSM.db.global.showAllProfessions = true
frame:UpdateProfession()
GUI:UpdateProfessionsTabST()
return
end
if TSM.db.global.showAllProfessions then
SetNoClose()
TSM.db.global.showAllProfessions = false
end
local playerName, profession = ("~"):split(index)
local currentPlayer = select(2, IsTradeSkillLinked()) or player
local currentProfession = GetTradeSkillLine()
if playerName == currentPlayer and profession == currentProfession then
frame:UpdateProfession()
GUI:ShowProfessionsTab()
currentSelection = index
return
end
if playerName == player then
local link = TSM.db.realm.tradeSkills[playerName] and TSM.db.realm.tradeSkills[playerName][profession] and TSM.db.realm.tradeSkills[playerName][profession].link
if not link then
TSM:Printf(L["Profession data not found for %s on %s. Logging into this player and opening the profession may solve this issue."], profession, playerName)
return OnValueChanged(_, _, currentSelection)
if profession == "Mining" then
CastSpellByName("Smelting")
else
CastSpellByName(profession)
end
local tradeString = strsub(select(3, ("|"):split(link)), 2)
SetNoClose()
SetItemRef(tradeString, link, "LeftButton", ChatFrame1)
else
local link = TSM.db.realm.tradeSkills[playerName][profession].link
if not link then
@ -1017,8 +922,7 @@ function GUI:CreateProfessionsTab(parent)
return OnValueChanged(_, _, currentSelection)
end
local tradeString = strsub(select(3, ("|"):split(link)), 2)
SetNoClose()
SetItemRef(tradeString, link, "LeftButton", ChatFrame1)
SetItemRef(tradeString, link)
end
currentSelection = index
end
@ -1030,19 +934,15 @@ function GUI:CreateProfessionsTab(parent)
dd:SetCallback("OnValueChanged", OnValueChanged)
frame.dropdown = dd
HaveMatsCheckBox = TSMAPI.GUI:CreateCheckBox(frame)
local HaveMatsCheckBox = TSMAPI.GUI:CreateCheckBox(frame)
HaveMatsCheckBox:SetLabel(" Have Mats")
HaveMatsCheckBox:SetPoint("TOPLEFT", 252, -4)
HaveMatsCheckBox:SetWidth(90)
HaveMatsCheckBox:SetHeight(26)
frame.HaveMatsCheckBox = HaveMatsCheckBox
HaveMatsCheckBox:SetCallback("OnValueChanged", function(_, _, value)
if TSM.db.global.showAllProfessions then
GUI:UpdateProfessionsTabST()
else
TradeSkillFrameAvailableFilterCheckButton:SetChecked(value)
TradeSkillOnlyShowMakeable(value)
end
end)
local RestockBtn = TSMAPI.GUI:CreateButton(frame, 14)
@ -1054,7 +954,6 @@ function GUI:CreateProfessionsTab(parent)
QuickRestock = true
GUI.ShowGroupsTab()
end)
frame.restockBtn = RestockBtn
local linkBtn = TSMAPI.GUI:CreateButton(frame, 14)
linkBtn:SetPoint("TOPRIGHT", -5, -4)
@ -1113,9 +1012,7 @@ function GUI:CreateProfessionsTab(parent)
end)
searchBar:SetScript("OnTextChanged", function(self)
local text = self:GetText()
if not TSM.db.global.showAllProfessions then
SetTradeSkillItemNameFilter(text == SEARCH and "" or text)
end
GUI:UpdateProfessionsTabST()
end)
searchBar:SetScript("OnEnterPressed", searchBar.ClearFocus)
@ -1156,17 +1053,7 @@ function GUI:CreateProfessionsTab(parent)
TSMAPI.GUI:CreateHorizontalLine(frame, -64)
local function OnSTRowClick(_, data, _, button)
if TSM.db.global.showAllProfessions and data.spellID then
if IsModifiedClick() then
local itemID = TSM.db.realm.crafts[data.spellID] and TSM.db.realm.crafts[data.spellID].itemID
if itemID then
HandleModifiedItemClick(itemID)
end
else
SelectSpellInProfession(data.profession, data.spellID)
end
return
elseif data.isCollapseAll then
if data.isCollapseAll then
TradeSkillCollapseAllButton:Click()
GUI:UpdateProfessionsTabST()
elseif button == "LeftButton" then
@ -1180,27 +1067,24 @@ function GUI:CreateProfessionsTab(parent)
end
end
local function OnSTColumnClick(self)
if TSM.db.global.showAllProfessions then
local sortKey
if self.colNum == 1 then
sortKey = "available"
elseif self.colNum == 3 then
sortKey = "cost"
elseif self.colNum == 4 then
sortKey = "profit"
elseif self.colNum == 5 then
sortKey = "profitPercent"
end
if sortKey then
if TSM.db.global.showAllProfessionsSortKey ~= sortKey then
TSM.db.global.showAllProfessionsSortKey = sortKey
TSM.db.global.showAllProfessionsSortDescending = true
else
TSM.db.global.showAllProfessionsSortDescending = not TSM.db.global.showAllProfessionsSortDescending
local function GetPriceColumnText()
if TSM.db.global.priceColumn == 1 then
return L["Crafting Cost"]
elseif TSM.db.global.priceColumn == 2 then
return L["Item Value"]
elseif TSM.db.global.priceColumn == 3 then
return L["Profit"]
end
GUI:UpdateProfessionsTabST()
end
local function OnSTColumnClick(self)
if self.colNum == 2 then
TSM.db.global.priceColumn = TSM.db.global.priceColumn + 1
TSM.db.global.priceColumn = TSM.db.global.priceColumn > 3 and 1 or TSM.db.global.priceColumn
self:SetText(GetPriceColumnText())
wipe(priceTextCache)
priceTextCache.lastClear = time()
GUI:UpdateProfessionsTabST()
end
end
@ -1211,11 +1095,11 @@ function GUI:CreateProfessionsTab(parent)
TSMAPI.Design:SetFrameColor(stContainer)
local stCols = {
{ name = "#", width = 0.08, align = "LEFT", headAlign = "LEFT" },
{ name = L["Name"], width = 0.40, align = "LEFT", headAlign = "LEFT" },
{ name = L["Cost"], width = 0.16, align = "LEFT", headAlign = "LEFT" },
{ name = L["Profit"], width = 0.18, align = "LEFT", headAlign = "LEFT" },
{ name = "%", width = 0.08, align = "LEFT", headAlign = "LEFT" }
-- { name = L["Name"], width = 0.725, align = "LEFT" },
-- { name = GetPriceColumnText(), width = 0.275, align = "LEFT" },
{ name = L["Name"], width = 0.7, align = "LEFT" },
{ name = GetPriceColumnText(), width = 0.2, align = "LEFT" },
{ name = "P. %", width = 0.1, align = "LEFT" }
}
frame.st = TSMAPI:CreateScrollingTable(stContainer, stCols, { OnClick = OnSTRowClick, OnColumnClick = OnSTColumnClick })
@ -1609,8 +1493,6 @@ function GUI:UpdateProfessionsTabST()
local stData = {}
TSM:UpdateCraftReverseLookup()
local showAll = TSM.db.global.showAllProfessions
local player = UnitName("player")
local function RGBPercToHex(tbl)
local r = tbl.r
@ -1627,206 +1509,25 @@ function GUI:UpdateProfessionsTabST()
priceTextCache.lastClear = time()
end
local function GetInventoryTotals()
local totals = {}
local bags = TSMAPI:ModuleAPI("ItemTracker", "playerbags", player)
local bank = TSMAPI:ModuleAPI("ItemTracker", "playerbank", player)
if bags or bank then
for itemString, quantity in pairs(bags or {}) do
totals[itemString] = (totals[itemString] or 0) + quantity
end
for itemString, quantity in pairs(bank or {}) do
totals[itemString] = (totals[itemString] or 0) + quantity
end
return totals
end
return select(4, TSM.Inventory:GetTotals())
end
local ts = ""
local numAvailableAllCache = {}
local inventoryTotals = GetInventoryTotals()
if showAll then
local searchText = GUI.frame and GUI.frame.content and GUI.frame.content.professionsTab and GUI.frame.content.professionsTab.searchBar and GUI.frame.content.professionsTab.searchBar:GetText() or ""
if searchText == SEARCH then
searchText = ""
end
searchText = strlower(searchText or "")
local filterHaveMats = GUI.frame and GUI.frame.content and GUI.frame.content.professionsTab and GUI.frame.content.professionsTab.HaveMatsCheckBox and GUI.frame.content.professionsTab.HaveMatsCheckBox:GetValue()
local craftList = {}
for spellID, data in pairs(TSM.db.realm.crafts) do
if data.players and data.players[UnitName("player")] then
tinsert(craftList, {
spellID = spellID,
name = data.name or "",
profession = data.profession or UNKNOWN,
numResult = data.numResult or 1,
})
end
end
for _, craft in ipairs(craftList) do
craft.cost, craft.buyout, craft.profit = TSM.Cost:GetCraftPrices(craft.spellID)
if craft.profit and craft.cost and craft.cost > 0 then
craft.profitPercent = (craft.profit / craft.cost) * 100
else
craft.profitPercent = nil
end
local available = math.huge
if TSM.db.realm.crafts[craft.spellID] then
for itemString, quantity in pairs(TSM.db.realm.crafts[craft.spellID].mats) do
available = min(available, floor((inventoryTotals[itemString] or 0) / quantity))
end
end
craft.available = available ~= math.huge and available or 0
end
if searchText ~= "" or filterHaveMats then
local filtered = {}
for _, craft in ipairs(craftList) do
if (searchText == "" or strfind(strlower(craft.name or ""), searchText, 1, true)) and (not filterHaveMats or craft.available > 0) then
tinsert(filtered, craft)
end
end
craftList = filtered
end
if TSM.db.global.showAllProfessionsSortKey == "cost" then
sort(craftList, function(a, b)
local aValue = tonumber(a.cost) or math.huge
local bValue = tonumber(b.cost) or math.huge
if aValue == bValue then
return a.name < b.name
end
if TSM.db.global.showAllProfessionsSortDescending then
return aValue > bValue
end
return aValue < bValue
end)
elseif TSM.db.global.showAllProfessionsSortKey == "profit" then
sort(craftList, function(a, b)
local aValue = tonumber(a.profit) or -math.huge
local bValue = tonumber(b.profit) or -math.huge
if aValue == bValue then
return a.name < b.name
end
if TSM.db.global.showAllProfessionsSortDescending then
return aValue > bValue
end
return aValue < bValue
end)
elseif TSM.db.global.showAllProfessionsSortKey == "available" then
sort(craftList, function(a, b)
local aValue = tonumber(a.available) or -math.huge
local bValue = tonumber(b.available) or -math.huge
if aValue == bValue then
return a.name < b.name
end
if TSM.db.global.showAllProfessionsSortDescending then
return aValue > bValue
end
return aValue < bValue
end)
elseif TSM.db.global.showAllProfessionsSortKey == "profitPercent" then
sort(craftList, function(a, b)
local aValue = tonumber(a.profitPercent) or -math.huge
local bValue = tonumber(b.profitPercent) or -math.huge
if aValue == bValue then
return a.name < b.name
end
if TSM.db.global.showAllProfessionsSortDescending then
return aValue > bValue
end
return aValue < bValue
end)
else
sort(craftList, function(a, b)
if TSM.db.global.showAllProfessionsSortDescending then
return a.name > b.name
end
return a.name < b.name
end)
end
for _, craft in ipairs(craftList) do
local spellID = craft.spellID
local craftName = craft.name
if not numAvailableAllCache[spellID] then
local numAvailableAll = math.huge
if spellID and TSM.db.realm.crafts[spellID] then
for itemString, quantity in pairs(TSM.db.realm.crafts[spellID].mats) do
numAvailableAll = min(numAvailableAll, floor((inventoryTotals[itemString] or 0) / quantity))
end
end
if numAvailableAll ~= math.huge then
numAvailableAllCache[spellID] = numAvailableAll
end
end
local available = craft.available or 0
local displayName = craftName
local costText
local profitText
local cost, _, profit = craft.cost, craft.buyout, craft.profit
if cost and cost > 0 then
costText = TSMAPI:FormatTextMoney(cost, TSMAPI.Design:GetInlineColor("link"))
else
costText = "---"
end
if profit then
if profit < 0 then
profitText = "|cffff0000-|r" .. TSMAPI:FormatTextMoney(-profit, "|cffff0000")
else
profitText = TSMAPI:FormatTextMoney(profit, "|cff00ff00")
end
else
profitText = "---"
end
local profitPercent = "---"
if profit and cost and cost > 0 then
profitPercent = craft.profitPercent
if profit < 0 then
profitPercent = format("%s%.0f%%|r", "|cffff0000", profitPercent)
else
profitPercent = format("%s%.0f%%|r", "|cff00ff00", profitPercent)
end
end
tinsert(stData, {
cols = {
{ value = available },
{ value = displayName },
{ value = costText or "" },
{ value = profitText or "" },
{ value = profitPercent or "" },
},
spellID = spellID,
profession = craft.profession,
})
end
local frame = GUI.frame.content.professionsTab
frame.st:SetData(stData)
return
end
local collapseAllRow = {
cols = {
{ value = "" },
{
value = "|cff" .. RGBPercToHex(TradeSkillTypeColor.header) .. ALL .. " [" .. (TradeSkillCollapseAllButton.collapsed and "+" or "-") .. "]|r",
},
{ value = "" },
{ value = "" },
{ value = "" },
{
value = "",
},
{
value = "",
},
},
isCollapseAll = true,
}
tinsert(stData, collapseAllRow)
local ts = ""
local numAvailableAllCache = {}
local inventoryTotals = select(4, TSM.Inventory:GetTotals())
for i = 1, GetNumTradeSkills() do
-- local skillName, skillType, numAvailable, isExpanded, _, numSkillUps, _, showProgressBar, currentRank, maxRank = GetTradeSkillInfo(i)
local skillName, skillType, numAvailable, isExpanded, _ = GetTradeSkillInfo(i)
@ -1862,29 +1563,50 @@ function GUI:UpdateProfessionsTabST()
end
end
local available = numAvailableAllCache[spellID] or 0
if numAvailable > 0 or (numAvailableAllCache[spellID] and numAvailableAllCache[spellID] > 0) then
local availableText = numAvailable .. " (" .. (numAvailableAllCache[spellID] or 0) .. ")"
skillName = ts .. "|cff" .. RGBPercToHex(TradeSkillTypeColor[skillType]) .. skillName .. " [" .. availableText .. "]|r"
else
skillName = ts .. "|cff" .. RGBPercToHex(TradeSkillTypeColor[skillType]) .. skillName .. "|r"
end
local costText
local profitText
local cost, _, profit = TSM.Cost:GetCraftPrices(spellID)
local priceText = priceTextCache[spellID]
local cost, buyout, profit = TSM.Cost:GetCraftPrices(spellID)
if spellID and not priceText then
--local cost, buyout, profit = TSM.Cost:GetCraftPrices(spellID)
if TSM.db.global.priceColumn == 1 then -- Crafting Cost
if cost and cost > 0 then
costText = TSMAPI:FormatTextMoney(cost, TSMAPI.Design:GetInlineColor("link"))
else
costText = "---"
priceText = TSMAPI:FormatTextMoney(cost, TSMAPI.Design:GetInlineColor("link"))
end
elseif TSM.db.global.priceColumn == 2 then -- Item Value
if buyout and buyout > 0 then
priceText = TSMAPI:FormatTextMoney(buyout, TSMAPI.Design:GetInlineColor("link"))
end
elseif TSM.db.global.priceColumn == 3 then -- Profit
if profit then
-- if profit < 0 then
-- priceText = "|cffff0000-|r" .. TSMAPI:FormatTextMoney(-profit, "|cffff0000") .. format(" (%s%.0f%%|r)", "|cffff0000", profitPercent)
-- else
-- priceText = TSMAPI:FormatTextMoney(profit, "|cff00ff00") .. format(" (%s%.0f%%|r)", "|cff00ff00", profitPercent)
-- end
if profit < 0 then
profitText = "|cffff0000-|r" .. TSMAPI:FormatTextMoney(-profit, "|cffff0000")
priceText = "|cffff0000-|r" .. TSMAPI:FormatTextMoney(-profit, "|cffff0000")
else
profitText = TSMAPI:FormatTextMoney(profit, "|cff00ff00")
priceText = TSMAPI:FormatTextMoney(profit, "|cff00ff00")
end
end
end
if priceText then
priceTextCache[spellID] = priceText
else
profitText = "---"
priceText = "---"
end
end
local profitPercent = "---"
if profit and cost and cost > 0 then
if profit then
profitPercent = profit / cost * 100
if profit < 0 then
profitPercent = format("%s%.0f%%|r", "|cffff0000", profitPercent)
@ -1895,17 +1617,11 @@ function GUI:UpdateProfessionsTabST()
local row = {
cols = {
{
value = spellID and available or "",
},
{
value = skillName,
},
{
value = spellID and costText or "",
},
{
value = spellID and profitText or "",
value = spellID and priceText or "",
},
{
value = spellID and profitPercent or "",
@ -1932,12 +1648,7 @@ function GUI:UpdateSelectedTradeSkill(forceUpdate)
local frame = GUI.frame.content.professionsTab
TradeSkillFrame.selectedSkill = TradeSkillFrame.selectedSkill or 1
if TSM.db.global.showAllProfessions then
frame.craftInfoFrame:SetTradeSkillIndex(TradeSkillFrame.selectedSkill)
return
end
local selection = frame.st:GetSelection() or 0
if forceUpdate or selection - 1 ~= TradeSkillFrame.selectedSkill then
if forceUpdate or frame.st:GetSelection() - 1 ~= TradeSkillFrame.selectedSkill then
frame.st:SetSelection(TradeSkillFrame.selectedSkill + 1)
frame.craftInfoFrame:SetTradeSkillIndex(TradeSkillFrame.selectedSkill)
end
@ -2641,28 +2352,6 @@ function GUI:UpdateGathering()
if not GUI.gatheringFrame or not GUI.gatheringFrame:IsVisible() then return end
if not TSM.db.realm.gathering.crafter or not next(TSM.db.realm.gathering.neededMats) then return end
local nameCache = {}
local merchantCache = {}
local function GetItemName(itemString)
local name = nameCache[itemString]
if name == nil then
name = TSMAPI:GetSafeItemInfo(itemString)
if not name then
name = GetItemInfo(itemString)
end
nameCache[itemString] = name or false
end
return name ~= false and name or nil
end
local function MerchantSells(itemString)
local sells = merchantCache[itemString]
if sells == nil then
sells = TSM.Gather:MerchantSells(itemString) and true or false
merchantCache[itemString] = sells
end
return sells
end
-- recheck the craft queue and update neededMats
local _, queuedMats = TSM.Queue:GetQueue()
local neededMats = {}
@ -2814,7 +2503,7 @@ function GUI:UpdateGathering()
else
needQty = neededMats[itemString] - (crafterBags[itemString] or 0)
end
local name = GetItemName(itemString) or itemString
local name = TSMAPI:GetSafeItemInfo(itemString) or itemString
local row
-- if task.taskType == L["Search for Mats"] or task.taskType == L["Visit Vendor"] or task.taskType == L["Collect Mail"] or task.taskType == L["Mail Items"] then
@ -2852,7 +2541,7 @@ function GUI:UpdateGathering()
local crafterMail = TSMAPI:ModuleAPI("ItemTracker", "playermail", crafter) or {}
for itemString, quantity in pairs(neededMats) do
local need = quantity - (crafterBags[itemString] or 0)
if not IsCrafterPlayer(crafter) then
if UnitName("player") ~= crafter then
need = need - (crafterMail[itemString] or 0)
end
if need > 0 then
@ -2870,15 +2559,18 @@ function GUI:UpdateGathering()
if task.taskType == L["Search for Mats"] then
availQty = taskQuantity
end
if task.taskType == L["Mail Items"] or task.taskType == L["Collect Mail"] then
if task.taskType == L["Mail Items"] then
availQty = min(need, taskQuantity)
elseif not IsCrafterPlayer(crafter) then
elseif not crafter == UnitName("player") then
availQty = min(need, taskQuantity) - (playerBags[itemString] or 0)
else
availQty = min(need, taskQuantity) -- (crafterBags[itemString] or 0)
end
availableMats[itemString] = availQty
local name = GetItemName(itemString)
local name = select(1, TSMAPI:GetSafeItemInfo(itemString))
if not name then
name = GetItemInfo(itemString)
end
local color
if need == availQty then
color = "|cff00ff00"
@ -2917,8 +2609,6 @@ function GUI:UpdateGathering()
GUI.gatheringFrame.gatherButton:SetText(L["Buy Vendor Items"])
elseif private.currentTask == L["Mail Items"] then
GUI.gatheringFrame.gatherButton:SetText(L["Mail Items"])
elseif private.currentTask == L["Collect Mail"] then
GUI.gatheringFrame.gatherButton:SetText(L["Collect Mail"])
end
else
GUI.gatheringFrame.gatherButton:Disable()
@ -2954,12 +2644,7 @@ function GUI:GatheringEventHandler(event)
GUI.gatheringFrame.gatherButton:Disable()
elseif event == "MAIL_SHOW" then
private.currentSource = UnitName("player")
local crafter = TSM.db.realm.gathering.crafter
if IsCrafterPlayer(crafter) then
private.currentTask = L["Collect Mail"]
else
private.currentTask = L["Mail Items"]
end
elseif event == "MAIL_CLOSED" then
private.currentSource = nil
private.currentTask = nil

34
TradeSkillMaster_Crafting/Modules/Gather.lua

@ -14,32 +14,15 @@ local L = LibStub("AceLocale-3.0"):GetLocale("TradeSkillMaster_Crafting") -- loa
local next = next
local private = { shoppingItems = {} }
local function IsCrafterPlayer(crafter)
if not crafter then return false end
local player = UnitName("player")
if not player then return false end
if strlower(crafter) == strlower(player) then
return true
end
local baseName = strmatch(crafter, "^[^-]+")
return baseName and strlower(baseName) == strlower(player) or false
end
function Gather:BuyFromMerchant(neededMats)
for i = 1, GetMerchantNumItems() do
local itemString = TSMAPI:GetItemString(GetMerchantItemLink(i))
if neededMats[itemString] then
local _, _, _, quantity = GetMerchantItemInfo(i)
local maxStack = GetMerchantItemMaxStack(i)
quantity = max(quantity or 1, 1)
local toBuy = neededMats[itemString]
while toBuy > 0 do
local buyCount = math.ceil(toBuy / quantity)
if maxStack and maxStack > 0 then
buyCount = math.min(buyCount, maxStack)
end
BuyMerchantItem(i, buyCount)
toBuy = toBuy - (buyCount * quantity)
BuyMerchantItem(i, math.min(toBuy, maxStack))
toBuy = toBuy - maxStack
TSM.db.realm.gathering.gatheredMats = true
end
end
@ -48,14 +31,11 @@ end
function Gather:gatherItems(source, task)
local items = TSM.db.realm.gathering.availableMats
local crafter = TSM.db.realm.gathering.crafter
if source == L["Vendor"] then
Gather:BuyFromMerchant(items)
elseif source == UnitName("player") and (task == L["Visit Bank"] or task == L["Visit Guild Bank"]) then
Gather:GatherBank(items)
elseif source == UnitName("player") and (task == L["Collect Mail"] or (task == L["Mail Items"] and IsCrafterPlayer(crafter))) then
TSMAPI:ModuleAPI("Mailing", "collectItems", items, Gather.PrintMsg)
elseif source == UnitName("player") and task == L["Mail Items"] then
Gather:MailItems(items)
elseif source == L["Auction House"] then
@ -186,23 +166,23 @@ function Gather:ShoppingSearch(itemString, need, ignoreMaxQty)
TSMAPI:ModuleAPI("Shopping", "runDestroySearch", TSMAPI:GetSafeItemInfo(itemString) .. "/x" .. need, ShoppingCallback)
end
else
TSMAPI:ModuleAPI("Shopping", "runSearch", TSMAPI:GetSafeItemInfo(itemString) .. "/exact/x" .. need, ShoppingCallback, true)
TSMAPI:ModuleAPI("Shopping", "runSearch", TSMAPI:GetSafeItemInfo(itemString) .. "/exact/x" .. need, ShoppingCallback)
end
else
TSM.Inventory.gatherItem = nil
if ignoreMaxQty then
TSMAPI:ModuleAPI("Shopping", "runSearch", TSMAPI:GetSafeItemInfo(itemString) .. "/exact", ShoppingCallback, true)
TSMAPI:ModuleAPI("Shopping", "runSearch", TSMAPI:GetSafeItemInfo(itemString) .. "/exact", ShoppingCallback)
else
TSMAPI:ModuleAPI("Shopping", "runSearch", TSMAPI:GetSafeItemInfo(itemString) .. "/exact/x" .. need, ShoppingCallback, true)
TSMAPI:ModuleAPI("Shopping", "runSearch", TSMAPI:GetSafeItemInfo(itemString) .. "/exact/x" .. need, ShoppingCallback)
end
end
else
TSM.Inventory.gatherItem = nil
if ignoreMaxQty then
TSMAPI:ModuleAPI("Shopping", "runSearch", TSMAPI:GetSafeItemInfo(itemString) .. "/exact", ShoppingCallback, true)
TSMAPI:ModuleAPI("Shopping", "runSearch", TSMAPI:GetSafeItemInfo(itemString) .. "/exact", ShoppingCallback)
else
TSMAPI:ModuleAPI("Shopping", "runSearch", TSMAPI:GetSafeItemInfo(itemString) .. "/exact/x" .. need, ShoppingCallback, true)
TSMAPI:ModuleAPI("Shopping", "runSearch", TSMAPI:GetSafeItemInfo(itemString) .. "/exact/x" .. need, ShoppingCallback)
end
end
end

34
TradeSkillMaster_Crafting/Modules/Inventory.lua

@ -111,15 +111,6 @@
return count
end
local function IsSamePlayer(a, b)
if not a or not b then return false end
local function Normalize(name)
local base = strmatch(name, "^[^-]+") or name
return strlower(base)
end
return Normalize(a) == Normalize(b)
end
function Inventory:GetItemSources(crafter, neededMats)
if not neededMats then return end
local sources = {}
@ -170,7 +161,7 @@
-- add bags/bank/mail "tasks" for needed items of all non-ignored characters (always include crafter)
for _, player in pairs(TSMAPI:ModuleAPI("ItemTracker", "playerlist") or {}) do
if IsSamePlayer(player, crafter) or not TSM.db.global.ignoreCharacters[player] then
if player == crafter or not TSM.db.global.ignoreCharacters[player] then
local task = {}
local bags = TSMAPI:ModuleAPI("ItemTracker", "playerbags", player) or {}
local bank = TSMAPI:ModuleAPI("ItemTracker", "playerbank", player) or {}
@ -191,21 +182,24 @@
soulboundBagCount = GetItemCount(itemString)
soulboundBankCount = GetItemCount(itemString, true) - soulboundBagCount
end
local remainingNeed = shortItems[itemString] and shortItems[itemString] - (crafterMail[itemString] or 0) or 0
if remainingNeed > 0 then
if (bank[itemString] or (soulboundBankCount and soulboundBankCount > 0)) then
bankItems[itemString] = min(bank[itemString] or soulboundBankCount, remainingNeed)
if (bank[itemString] or (soulboundBankCount and soulboundBankCount > 0)) and shortItems[itemString] then
if shortItems[itemString] - (crafterMail[itemString] or 0) - (player ~= crafter and bags[itemString] or 0) > 0 then
bankItems[itemString] = bank[itemString] or soulboundBankCount
end
if gbank[itemString] then
gbankItems[itemString] = min(gbank[itemString], remainingNeed)
end
if gbank[itemString] and shortItems[itemString] then
if shortItems[itemString] - (crafterMail[itemString] or 0) - (player ~= crafter and bags[itemString] or 0) > 0 then
gbankItems[itemString] = gbank[itemString]
end
end
if mail[itemString] and shortItems[itemString] then
mailItems[itemString] = mail[itemString]
end
if bags[itemString] and shortItems[itemString] and not IsSamePlayer(player, crafter) then
if remainingNeed > 0 then
bagItems[itemString] = min(bags[itemString], remainingNeed)
if bags[itemString] and shortItems[itemString] then
if player ~= crafter then
if shortItems[itemString] - (crafterMail[itemString] or 0) > 0 then
bagItems[itemString] = bags[itemString]
end
end
end
end
@ -229,7 +223,7 @@
tinsert(task, { taskType = L["Mail Items"], items = bagItems })
end
if next(task) then
tinsert(sources, { sourceName = player, isCrafter = IsSamePlayer(player, crafter), isVendor = false, isAH = false, tasks = task, isCurrent = (player == UnitName("player")) })
tinsert(sources, { sourceName = player, isCrafter = player == crafter, isVendor = false, isAH = false, tasks = task, isCurrent = (player == UnitName("player")) })
end
end
end

24
TradeSkillMaster_Crafting/Modules/Queue.lua

@ -33,9 +33,6 @@ end
function Queue:CreateRestockQueue(groupInfo)
TSM:UpdateCraftReverseLookup()
local numItems = 0
local bestQueued = {}
local bestSpellID = {}
local seenItems = {}
for _, data in pairs(groupInfo) do
for _, opName in ipairs(data.operations) do
@ -45,7 +42,6 @@ function Queue:CreateRestockQueue(groupInfo)
-- it's a valid operation
for itemString in pairs(data.items) do
itemString = TSMAPI:GetItemString(itemString)
seenItems[itemString] = true
local spellID = TSM.craftReverseLookup[itemString] and TSM.craftReverseLookup[itemString][1]
if spellID and TSM.db.realm.crafts[spellID] then
local maxQueueCount = max(opSettings.maxRestock - TSM.Inventory:GetTotalQuantity(itemString), 0)
@ -65,25 +61,13 @@ function Queue:CreateRestockQueue(groupInfo)
end
local craft = TSM.db.realm.crafts[spellID]
local queued = floor(numToQueue / craft.numResult)
queued = queued >= opSettings.minRestock and queued or 0
if queued > 0 and queued > (bestQueued[itemString] or 0) then
bestQueued[itemString] = queued
bestSpellID[itemString] = spellID
end
end
end
craft.queued = floor(numToQueue / craft.numResult)
craft.queued = craft.queued >= opSettings.minRestock and craft.queued or 0
if craft.queued > 0 then
numItems = numItems + 1
end
end
end
for itemString in pairs(seenItems) do
local queued = bestQueued[itemString] or 0
local spellID = bestSpellID[itemString] or (TSM.craftReverseLookup[itemString] and TSM.craftReverseLookup[itemString][1])
if spellID and TSM.db.realm.crafts[spellID] then
TSM.db.realm.crafts[spellID].queued = queued
if queued > 0 then
numItems = numItems + 1
end
end
end

6
TradeSkillMaster_Crafting/TradeSkillMaster_Crafting.lua

@ -29,16 +29,12 @@ local savedDBDefaults = {
frameQueueOpen = nil,
showingDefaultFrame = nil,
matsInTooltip = true,
showAllProfessions = false,
showAllProfessionsSortKey = "name",
showAllProfessionsSortDescending = false,
},
realm = {
tradeSkills = {},
crafts = {},
mats = {},
queueStatus = { collapsed = {} },
professionsTabCollapsed = {},
sourceStatus = { collapsed = {} },
gathering = { crafter = nil, professions = {}, neededMats = {}, availableMats = {}, gatheredMats = false, gatherAll = false, destroyingMats = {}, destroyDisable = false, evenStacks = true },
craftingCostCache = {}
@ -105,7 +101,7 @@ end
-- registers this module with TSM by first setting all fields and then calling TSMAPI:NewModule().
function TSM:RegisterModule()
TSM.icons = { { side = "module", desc = "Crafting", slashCommand = "crafting", callback = "Options:LoadCrafting", icon = "Interface\\Icons\\INV_Misc_Gear_08" } }
TSM.operations = { maxOperations = 5, callbackOptions = "Options:Load", callbackInfo = "GetOperationInfo" }
TSM.operations = { maxOperations = 1, callbackOptions = "Options:Load", callbackInfo = "GetOperationInfo" }
TSM.priceSources = {
{ key = "Crafting", label = L["Crafting Cost"], callback = "GetCraftingCost" },
{ key = "matPrice", label = L["Crafting Material Cost"], callback = "GetCraftingMatCost" },

140
TradeSkillMaster_Mailing/Modules/Inbox.lua

@ -74,7 +74,7 @@ function Inbox:CreateTab(parent)
st:SetData({})
frame.st = st
local btn = TSMAPI.GUI:CreateButton(frame, 18, "TSMMailingOpenAllButton")
local btn = TSMAPI.GUI:CreateButton(frame, 18)
btn:SetPoint("BOTTOMLEFT", 5, 30)
btn:SetPoint("BOTTOMRIGHT", -5, 30)
btn:SetHeight(20)
@ -91,7 +91,7 @@ function Inbox:CreateTab(parent)
local btnWidth = (frame:GetWidth() - label:GetWidth() - 25) / 5
local btn = TSMAPI.GUI:CreateButton(frame, 18, "TSMMailingSalesButton")
local btn = TSMAPI.GUI:CreateButton(frame, 18)
btn:SetPoint("BOTTOMLEFT", label, "BOTTOMRIGHT", 5, 0)
btn:SetWidth(btnWidth)
btn:SetHeight(20)
@ -99,7 +99,7 @@ function Inbox:CreateTab(parent)
btn:SetScript("OnClick", function() private:StartAutoLooting("sales") end)
frame.salesBtn = btn
local btn = TSMAPI.GUI:CreateButton(frame, 18, "TSMMailingBuysButton")
local btn = TSMAPI.GUI:CreateButton(frame, 18)
btn:SetPoint("BOTTOMLEFT", frame.salesBtn, "BOTTOMRIGHT", 5, 0)
btn:SetWidth(btnWidth)
btn:SetHeight(20)
@ -107,7 +107,7 @@ function Inbox:CreateTab(parent)
btn:SetScript("OnClick", function() private:StartAutoLooting("buys") end)
frame.buysBtn = btn
local btn = TSMAPI.GUI:CreateButton(frame, 18, "TSMMailingCancelsButton")
local btn = TSMAPI.GUI:CreateButton(frame, 18)
btn:SetPoint("BOTTOMLEFT", frame.buysBtn, "BOTTOMRIGHT", 5, 0)
btn:SetWidth(btnWidth)
btn:SetHeight(20)
@ -115,7 +115,7 @@ function Inbox:CreateTab(parent)
btn:SetScript("OnClick", function() private:StartAutoLooting("cancels") end)
frame.cancelsBtn = btn
local btn = TSMAPI.GUI:CreateButton(frame, 18, "TSMMailingExpiresButton")
local btn = TSMAPI.GUI:CreateButton(frame, 18)
btn:SetPoint("BOTTOMLEFT", frame.cancelsBtn, "BOTTOMRIGHT", 5, 0)
btn:SetWidth(btnWidth)
btn:SetHeight(20)
@ -247,7 +247,7 @@ function private:UpdateTopLabel()
end
function private:InboxUpdate()
if not private.frame or (not private.frame:IsVisible() and private.frame.buttonsEnabled) then return end
if not private.frame or not private.frame:IsVisible() then return end
TSMAPI:CancelFrame("inboxLootTextDelay")
local numMail, totalMail = GetInboxNumItems()
@ -409,7 +409,6 @@ end
-- Deals with auto looting of mail!
function private:StartAutoLooting(mode)
if not MailFrame or not MailFrame:IsShown() then return end
private.mode = mode
local canCollectMail
if private.mode == "all" then
@ -437,130 +436,8 @@ function private:StartAutoLooting(mode)
private:AutoLoot()
end
local function CanLootItem(itemString, quantity)
if not itemString or not quantity then return false end
local maxStack = select(8, TSMAPI:GetSafeItemInfo(itemString))
local hasPartial = false
local hasEmpty = false
local totalFreeSlots = 0
for bag = 0, NUM_BAG_SLOTS do
if TSMAPI:ItemWillGoInBag(itemString, bag) then
totalFreeSlots = totalFreeSlots + (GetContainerNumFreeSlots(bag) or 0)
for slot = 1, GetContainerNumSlots(bag) do
local iLink = GetContainerItemLink(bag, slot)
if iLink then
if TSMAPI:GetItemString(iLink) == itemString and maxStack then
local stackSize = select(2, GetContainerItemInfo(bag, slot))
if stackSize and (maxStack - stackSize) >= quantity then
hasPartial = true
end
end
else
hasEmpty = true
end
end
end
end
if hasPartial then
return true
end
if not hasEmpty then
return false
end
if TSM.db.global.keepMailSpace and TSM.db.global.keepMailSpace > 0 then
return (totalFreeSlots - 1) >= TSM.db.global.keepMailSpace
end
return true
end
local function HasFittableStack(itemString, needed)
if not itemString or not needed or needed <= 0 then return false end
for i = 1, GetInboxNumItems() do
local _, _, _, _, _, cod, _, hasItem = GetInboxHeaderInfo(i)
if hasItem and hasItem > 0 and (not cod or cod == 0) then
for j = 1, hasItem do
local itemLink = GetInboxItemLink(i, j)
if itemLink and TSMAPI:GetItemString(itemLink) == itemString then
local quantity = select(3, GetInboxItem(i, j)) or 0
if quantity > 0 and quantity <= needed then
return true
end
end
end
end
end
return false
end
function Inbox:CollectItems(items, callback)
if not items or not next(items) then return end
if not MailFrame or not MailFrame:IsShown() then return end
local remaining = CopyTable(items)
local collected = false
local candidates = {}
for i = GetInboxNumItems(), 1, -1 do
local _, _, _, _, _, cod, _, hasItem = GetInboxHeaderInfo(i)
if hasItem and hasItem > 0 and (not cod or cod == 0) then
for j = hasItem, 1, -1 do
local itemLink = GetInboxItemLink(i, j)
local itemString = itemLink and TSMAPI:GetItemString(itemLink)
local need = itemString and remaining[itemString]
if need and need > 0 then
local quantity = select(3, GetInboxItem(i, j)) or 0
if quantity > 0 then
if not candidates[itemString] then
candidates[itemString] = {}
end
tinsert(candidates[itemString], { index = i, itemIndex = j, quantity = quantity })
end
end
end
end
end
for itemString, entries in pairs(candidates) do
table.sort(entries, function(a, b)
if a.quantity ~= b.quantity then
return a.quantity < b.quantity
end
return a.index > b.index
end)
for _, entry in ipairs(entries) do
local need = remaining[itemString]
if not need or need <= 0 then
break
end
if entry.quantity > 0 and (entry.quantity <= need or not HasFittableStack(itemString, need)) then
if CanLootItem(itemString, entry.quantity) then
TakeInboxItem(entry.index, entry.itemIndex)
remaining[itemString] = max(need - entry.quantity, 0)
collected = true
elseif callback then
callback(L["Cannot finish auto looting, inventory is full or too many unique items."])
return
end
end
end
end
-- Keep looting as new mail indexes shift, while preserving 1 free slot.
if collected then
TSMAPI:CreateTimeDelay("craftingGatherMailLoop", 0.4, function()
Inbox:CollectItems(remaining, callback)
end)
end
end
function private:AutoLoot()
TSMAPI:CancelFrame("mailSkipDelay")
if not MailFrame or not MailFrame:IsShown() then
private:StopAutoLooting()
return
end
-- Already looted everything after the invalid indexes we had, so fail it
if private.lootIndex > 1 and private.lootIndex > GetInboxNumItems() then
@ -612,10 +489,6 @@ function private:AutoLoot()
end
function private:LootMailItem(index)
if not MailFrame or not MailFrame:IsShown() then
private:StopAutoLooting()
return
end
if TSM.db.global.inboxMessages then
--local _, _, sender, subject, money, cod, _, hasItem = GetInboxHeaderInfo(index)
local _, _, sender, subject, money, cod, daysLeft, hasItem, _, _, _, _, _, itemQuantity = GetInboxHeaderInfo(index)
@ -792,7 +665,6 @@ function private:StopAutoLooting(failed)
private.autoLootTotal = nil
if not private.frame then return end
private.frame:EnableButtons()
TSMAPI:CancelFrame("mailWaitDelay")
--Tell user how much money has been collected if they don't have it turned off in TradeSkillMaster_Mailing options
if private.moneyCollected and private.moneyCollected > 0 and TSM.db.global.displayMoneyCollected then

1
TradeSkillMaster_Mailing/TradeSkillMaster_Mailing.lua

@ -62,7 +62,6 @@ function TSM:RegisterModule()
TSM.operations = {maxOperations=12, callbackOptions="Options:Load", callbackInfo="GetOperationInfo"}
TSM.moduleAPIs = {
{key="mailItems", callback="AutoMail:SendItems"},
{key="collectItems", callback="Inbox:CollectItems"},
}
TSMAPI:NewModule(TSM)

2
TradeSkillMaster_Shopping/Locale/enUS.lua

@ -81,8 +81,6 @@ L["Max Disenchant Level"] = true
L["Max Disenchant Search Percent"] = true
L["Max Shopping Price:"] = true
L["Maximum Auction Price (per item)"] = true
L["Max Restock Quantity"] = true
L["If set above 0, Shopping will only buy up to this total quantity for items in groups using this operation."] = true
L["Maximum Quantity to Buy:"] = true
L["Maximum quantity purchased for %s."] = true
L["Maximum quantity purchased for destroy search."] = true

35
TradeSkillMaster_Shopping/TradeSkillMaster_Shopping.lua

@ -53,7 +53,7 @@ end
-- registers this module with TSM by first setting all fields and then calling TSMAPI:NewModule().
function TSM:RegisterModule()
TSM.operations = { maxOperations = 5, callbackOptions = "Options:Load", callbackInfo = "GetOperationInfo" }
TSM.operations = { maxOperations = 1, callbackOptions = "Options:Load", callbackInfo = "GetOperationInfo" }
TSM.auctionTab = { callbackShow = "Search:Show", callbackHide = "Search:Hide" }
TSM.tooltipOptions = { callback = "Options:LoadTooltipOptions" }
TSM.moduleAPIs = {
@ -68,7 +68,6 @@ end
TSM.operationDefaults = {
maxPrice = 1,
maxRestock = 0,
evenStacks = nil,
showAboveMaxPrice = nil,
ignorePlayer = {},
@ -95,7 +94,6 @@ end
function TSM:StartFilterSearch(searchQuery, callback)
if not TSMAPI:AHTabIsVisible("Shopping") then return end
TSM.Search:StartFilterSearch(searchQuery, nil, true)
TSM.shoppingGroupSearchActive = nil
TSM.moduleAPICallback = callback
end
@ -127,19 +125,6 @@ function TSM:GetMaxPrice(operationPrice, itemString)
return price ~= 0 and price or nil, err
end
function TSM:GetTotalQuantity(itemString)
itemString = TSMAPI:GetBaseItemString(itemString, true) or itemString
local player, alts = TSMAPI:ModuleAPI("ItemTracker", "playertotal", itemString)
if not player then
alts = nil
end
player = player or 0
alts = alts or 0
local guild = TSMAPI:ModuleAPI("ItemTracker", "guildtotal", itemString) or 0
local auctions = TSMAPI:ModuleAPI("ItemTracker", "auctionstotal", itemString) or 0
return player + alts + guild + auctions
end
function TSM:AddSidebarFeature(...)
TSM.modules.Sidebar:AddSidebarFeature(...)
end
@ -151,24 +136,11 @@ function TSM:GetTooltip(itemString)
itemString = TSMAPI:GetBaseItemString(itemString, true)
local operations = TSMAPI:GetItemOperation(itemString, "Shopping")
if not operations then return end
local maxPrice
local totalQty = TSM:GetTotalQuantity(itemString) or 0
for _, operationName in ipairs(operations) do
local operationName = operations[1]
TSMAPI:UpdateOperation("Shopping", operationName)
local operation = TSM.operations[operationName]
if operation then
local maxRestock = tonumber(operation.maxRestock) or 0
if maxRestock > 0 and totalQty >= maxRestock then
operation = nil
end
end
if operation then
local price = TSM:GetMaxPrice(operation.maxPrice, itemString)
if price then
maxPrice = maxPrice and max(maxPrice, price) or price
end
end
end
local maxPrice = TSM:GetMaxPrice(operation.maxPrice, itemString)
if maxPrice then
local priceText
if moneyCoinsTooltip then
@ -178,6 +150,7 @@ function TSM:GetTooltip(itemString)
end
tinsert(text, { left = " " .. L["Max Shopping Price:"], right = format("%s", priceText) })
end
end
if #text > 0 then
tinsert(text, 1, "|cffffff00" .. "TSM Shopping:")
return text

2
TradeSkillMaster_Shopping/modules/Destroying.lua

@ -62,9 +62,7 @@ function Destroying:StartDestroyingSearch(target, filter, isCrafting)
if not private.sources[target] then return TSM:Printf(L["Invalid destroy target: '%s'"], target) end
TSM.isCrafting = isCrafting
TSM.shoppingGroupSearchActive = nil
Destroying.maxQuantity = filter.maxQuantity
filter.maxQuantity = nil
filter.maxPrice = nil
if private.sources[target] == "mill" then
private:TryStarting(private.StartMillingSearch, target, filter)

13
TradeSkillMaster_Shopping/modules/Options.lua

@ -305,18 +305,6 @@ function Options:DrawOperationGeneral(container, operationName)
disabled = operation.relationships.maxprice,
tooltip = L["The highest price per item you will pay for items in affected by this operation."],
},
{
type = "Slider",
settingInfo = { operation, "maxRestock" },
label = L["Max Restock Quantity"],
disabled = operation.relationships.maxRestock,
isPercent = false,
min = 0,
max = 5000,
step = 1,
relativeWidth = 0.49,
tooltip = L["If set above 0, Shopping will only buy up to this total quantity for items in groups using this operation."],
},
{
type = "CheckBox",
label = L["Show Auctions Above Max Price"],
@ -345,7 +333,6 @@ function Options:DrawOperationRelationships(container, operationName)
{
label = L["General Settings"],
{ key = "maxPrice", label = L["Maximum Auction Price (per item)"] },
{ key = "maxRestock", label = L["Max Restock Quantity"] },
{ key = "showAboveMaxPrice", label = L["Show Auctions Above Max Price"] },
{ key = "evenStacks", label = L["Even (5/10/15/20) Stacks Only"] },
},

7
TradeSkillMaster_Shopping/modules/Search.lua

@ -39,9 +39,9 @@ end
function Search:Hide()
if not private.searchBar then return end
private.searchBar:Hide()
TSM.Util:HideSearchFrame(false)
TSM.Util:HideSearchFrame()
TSMAPI.AuctionControl:HideControlButtons()
TSMAPI.AuctionScan:StopScan(false)
TSMAPI.AuctionScan:StopScan()
TSM.Sidebar:Hide()
end
@ -325,7 +325,6 @@ end
function Search:StartFilterSearch(filter, callback, isCrafting)
TSM.isCrafting = isCrafting
TSM.searchCallback = callback
TSM.shoppingGroupSearchActive = nil
if strfind(filter, "item:([0-9]+):?([0-9]*):?([0-9]*):?([0-9]*):?([0-9]*):?([0-9]*):?%-?([0-9]*)$") then --or strfind(filter, "battlepet:([0-9]+):?([0-9]*):?([0-9]*):?([0-9]*):?([0-9]*):?([0-9]*):?([0-9]*)$") then
filter = TSMAPI:GetSafeItemInfo(filter) or filter
end
@ -525,7 +524,7 @@ local function GetSearchFilterOptions(searchTerm)
minILevel = oldMaxILevel
end
return true, queryString or "", class or 0, subClass or 0, minLevel or 0, maxLevel or 0, minILevel or 0, maxILevel or 0, rarity or -1, usableOnly or 0, exactOnly or nil, evenOnly or nil, maxQuantity, maxPrice
return true, queryString or "", class or 0, subClass or 0, minLevel or 0, maxLevel or 0, minILevel or 0, maxILevel or 0, rarity or -1, usableOnly or 0, exactOnly or nil, evenOnly or nil, maxQuantity or 0, maxPrice
--return true, queryString or "", class or 0, subClass or 0, minLevel or 0, maxLevel or 0, minILevel or 0, maxILevel or 0, rarity or 0, usableOnly or 0, exactOnly or nil, evenOnly or nil, maxQuantity or 0, maxPrice
end

29
TradeSkillMaster_Shopping/modules/Util.lua

@ -91,12 +91,6 @@ function private:CreateSearchFrame()
private.controlButtons.cancel:Disable()
private.controlButtons.post:Disable()
end
if private.controlButtons.buyout:IsEnabled() and TSM.groupBuyoutCheck then
local perItemBuyout = data.auctionRecord.GetItemBuyout and data.auctionRecord:GetItemBuyout() or data.auctionRecord.buyout
if not TSM.groupBuyoutCheck(data.itemString, data.auctionRecord.count, perItemBuyout) then
private.controlButtons.buyout:Disable()
end
end
if private:HasInBags(TSMAPI:GetBaseItemString(data.itemString)) then
private.controlButtons.post:Enable()
end
@ -121,7 +115,7 @@ function Util:SetParent(parent)
private.parent = parent
end
function Util:ShowSearchFrame(isDestroying, pctColName, clearRT, forceStop)
function Util:ShowSearchFrame(isDestroying, pctColName, clearRT)
if private.searchFrame and private.searchFrame:IsVisible() then
Util:HideSearchFrame()
end
@ -136,7 +130,7 @@ function Util:ShowSearchFrame(isDestroying, pctColName, clearRT, forceStop)
private.controlButtons.buyout:Disable()
private.controlButtons.cancel:Disable()
private.controlButtons.post:Disable()
TSMAPI.AuctionScan:StopScan(forceStop ~= false)
TSMAPI.AuctionScan:StopScan()
TSMAPI.AuctionScan:ClearCache()
private.searchFrame.statusBar:SetStatusText("")
private.searchFrame.statusBar:UpdateStatus(0, 0)
@ -144,14 +138,11 @@ function Util:ShowSearchFrame(isDestroying, pctColName, clearRT, forceStop)
TSM.Search:SetMode(private.mode)
end
function Util:HideSearchFrame(forceStop)
function Util:HideSearchFrame()
private.searchFrame:Hide()
TSMAPI.AuctionControl:HideControlButtons()
TSMAPI.AuctionScan:StopScan(forceStop ~= false)
TSMAPI.AuctionScan:StopScan()
TSMAPI.AuctionScan:ClearCache()
TSM.searchCallback = nil
TSM.groupBuyoutCheck = nil
TSM.shoppingGroupSearchActive = nil
end
@ -200,9 +191,6 @@ function private:PrepareForScan(callback, isLastPageScan)
private.isLastPageScan = isLastPageScan
private.callback = callback
wipe(private.auctions)
if not isLastPageScan and not TSM.shoppingGroupSearchActive then
TSM.groupBuyoutCheck = nil
end
if private.isLastPageScan then
private.searchFrame.statusBar:SetStatusText("Scanning last page...")
else
@ -243,10 +231,6 @@ function private.ScanCallback(event, ...)
elseif event == "SCAN_COMPLETE" then
if not private.filterList or not private.filterList[1] then return end -- protect against sniper scan starts causing issues
local data = ...
local groupItems = private.filterList[1].items
if not private.isLastPageScan then
TSMAPI:ModuleAPI("AuctionDB", "processScanData", data, groupItems, time())
end
if private.filterList[1].items then
for _, itemString in ipairs(private.filterList[1].items) do
if data[itemString] then
@ -470,7 +454,7 @@ function private:RemoveAuction(auction, event, itemString)
-- handle max quantities on queries
local query = private.auctions[itemString].query
if event == "OnBuyout" and query then
if private.mode == "normal" and query.maxQuantity and query.maxQuantity > 0 then
if private.mode == "normal" and (query.maxQuantity or 0) > 0 then
query.maxQuantity = query.maxQuantity - auction.count
if TSM.moduleAPICallback then TSM.moduleAPICallback(max(query.maxQuantity, 0), itemString, auction.count) end
for item, auctionItem in pairs(private.auctions) do
@ -513,9 +497,6 @@ function private:RemoveAuction(auction, event, itemString)
private.searchFrame.rt:SetSelectedAuction(selected.parent:GetItemString())
end
end
if #private.searchFrame.rt.auctionData == 0 and TSM.moduleAPICallback then
TSM.moduleAPICallback()
end
end
function private:AddPostedAuction(postInfo)

169
TradeSkillMaster_Shopping/sidebar/Groups.lua

@ -1,69 +1,7 @@
local TSM = select(2, ...)
local L = LibStub("AceLocale-3.0"):GetLocale("TradeSkillMaster_Shopping") -- loads the localization table
local private = {itemOperations={}, purchasedCounts={}, itemQuantities={}, operationSettings={}, itemOperationSettings={}, itemOperationMaxPrices={}}
local function GetCachedItemQuantity(itemString)
local quantity = private.itemQuantities[itemString]
if quantity == nil then
local totalQty = TSM:GetTotalQuantity(itemString) or 0
totalQty = totalQty + (private.purchasedCounts[itemString] or 0)
private.itemQuantities[itemString] = totalQty
quantity = totalQty
end
return quantity
end
local function GetOperationSettings(opName)
local settings = private.operationSettings[opName]
if settings == nil then
TSMAPI:UpdateOperation("Shopping", opName)
settings = TSM.operations[opName]
private.operationSettings[opName] = settings or false
end
return settings ~= false and settings or nil
end
local function GetOperationMaxPrice(itemString, opName, opSettings)
private.itemOperationMaxPrices[itemString] = private.itemOperationMaxPrices[itemString] or {}
local cached = private.itemOperationMaxPrices[itemString][opName]
if cached ~= nil then
return cached ~= false and cached or nil
end
local operationPrice = TSM:GetMaxPrice(opSettings.maxPrice, itemString)
private.itemOperationMaxPrices[itemString][opName] = operationPrice or false
return operationPrice
end
local function GetItemOperationSettings(itemString, operations)
local settingsList = private.itemOperationSettings[itemString]
if settingsList then return settingsList end
settingsList = {}
for _, opName in ipairs(operations or {}) do
local opSettings = GetOperationSettings(opName)
if opSettings then
tinsert(settingsList, {name=opName, settings=opSettings})
end
end
private.itemOperationSettings[itemString] = settingsList
return settingsList
end
local function GetActiveOperations(itemString, operations)
if not operations or #operations == 0 then return end
local totalQty = GetCachedItemQuantity(itemString)
local active = {}
for _, opEntry in ipairs(GetItemOperationSettings(itemString, operations)) do
local opSettings = opEntry.settings
if opSettings then
local maxRestock = tonumber(opSettings.maxRestock) or 0
if maxRestock <= 0 or totalQty < maxRestock then
tinsert(active, opEntry)
end
end
end
return active
end
local private = {itemOperations={}}
function private.Create(parent)
local frame = CreateFrame("Frame", nil, parent)
@ -101,63 +39,33 @@ function private.ScanCallback(event, ...)
local filter = ...
local maxPrice
for _, itemString in ipairs(filter.items) do
local operations = GetActiveOperations(itemString, private.itemOperations[itemString])
if not operations or #operations == 0 then return end
local itemMaxPrice
for _, opEntry in ipairs(operations) do
local opSettings = opEntry.settings
if opSettings.showAboveMaxPrice then
itemMaxPrice = nil
break
end
local operationPrice = GetOperationMaxPrice(itemString, opEntry.name, opSettings)
if operationPrice then
itemMaxPrice = itemMaxPrice and max(itemMaxPrice, operationPrice) or operationPrice
end
end
if itemMaxPrice == nil then
local operation = private.itemOperations[itemString]
local operationPrice = TSM:GetMaxPrice(operation.maxPrice, itemString)
if not operationPrice then return end
if operation.showAboveMaxPrice then
maxPrice = nil
break
end
if not itemMaxPrice then return end
maxPrice = maxPrice and max(maxPrice, itemMaxPrice) or itemMaxPrice
maxPrice = maxPrice and max(maxPrice, operationPrice) or operationPrice
end
return maxPrice
elseif event == "process" then
local itemString, auctionItem = ...
-- filter out auctions according to operation settings
itemString = TSMAPI:GetBaseItemString(itemString, true)
local operations = GetActiveOperations(itemString, private.itemOperations[itemString])
if not operations or #operations == 0 then return end
local totalQty = GetCachedItemQuantity(itemString)
local operation = private.itemOperations[itemString]
if not operation then return end
local operationPrice = TSM:GetMaxPrice(operation.maxPrice, itemString)
if not operationPrice then return end
auctionItem:FilterRecords(function(record)
local buyout = record:GetItemBuyout() or 0
for _, opEntry in ipairs(operations) do
local opSettings = opEntry.settings
local maxRestock = tonumber(opSettings.maxRestock) or 0
if maxRestock > 0 and (totalQty + record.count) > maxRestock then
-- this operation can't accept more of this item
elseif opSettings.evenStacks and record.count % 5 ~= 0 then
-- not an even stack for this operation
elseif opSettings.showAboveMaxPrice then
return false
else
local operationPrice = GetOperationMaxPrice(itemString, opEntry.name, opSettings)
if operationPrice and buyout <= operationPrice then
return false
end
end
end
if operation.evenStacks and record.count % 5 ~= 0 then
return true
end)
local marketValue
for _, opEntry in ipairs(operations) do
local operationPrice = GetOperationMaxPrice(itemString, opEntry.name, opEntry.settings)
if operationPrice then
marketValue = marketValue and max(marketValue, operationPrice) or operationPrice
end
if not operation.showAboveMaxPrice then
return (record:GetItemBuyout() or 0) > operationPrice
end
auctionItem:SetMarketValue(marketValue)
end)
auctionItem:SetMarketValue(operationPrice)
return auctionItem
elseif event == "done" then
TSM.Search:SetSearchBarDisabled(false)
@ -167,46 +75,7 @@ end
function private.StartScan()
TSMAPI:FireEvent("SHOPPING:GROUPS:STARTSCAN")
TSM.shoppingGroupSearchActive = true
wipe(private.itemOperations)
wipe(private.purchasedCounts)
private.itemQuantities = {}
private.operationSettings = {}
private.itemOperationSettings = {}
private.itemOperationMaxPrices = {}
TSM.searchCallback = function(event, auction)
if event == "OnBuyout" and auction then
local linkItemString = auction.link and TSMAPI:GetItemString(auction.link)
if linkItemString then
local itemString = TSMAPI:GetBaseItemString(linkItemString, true)
private.purchasedCounts[itemString] = (private.purchasedCounts[itemString] or 0) + (auction.count or 0)
private.itemQuantities[itemString] = nil
end
end
end
TSM.groupBuyoutCheck = function(itemString, count, buyoutPerItem)
itemString = TSMAPI:GetBaseItemString(itemString, true)
local operations = GetActiveOperations(itemString, private.itemOperations[itemString])
if not operations or #operations == 0 then return false end
local totalQty = GetCachedItemQuantity(itemString)
for _, opEntry in ipairs(operations) do
local opSettings = opEntry.settings
local maxRestock = tonumber(opSettings.maxRestock) or 0
if maxRestock > 0 and (totalQty + count) > maxRestock then
-- doesn't fit this operation
elseif opSettings.evenStacks and count % 5 ~= 0 then
-- not an even stack for this operation
elseif opSettings.showAboveMaxPrice then
return true
else
local operationPrice = GetOperationMaxPrice(itemString, opEntry.name, opSettings)
if operationPrice and buyoutPerItem and buyoutPerItem > 0 and buyoutPerItem <= operationPrice then
return true
end
end
end
return false
end
for groupName, data in pairs(private.groupTree:GetSelectedGroupInfo()) do
groupName = TSMAPI:FormatGroupPath(groupName, true)
for _, opName in ipairs(data.operations) do
@ -218,17 +87,11 @@ function private.StartScan()
else
-- it's a valid operation
for itemString in pairs(data.items) do
local baseItemString = TSMAPI:GetBaseItemString(itemString, true) or itemString
local _, err = TSM:GetMaxPrice(opSettings.maxPrice, itemString)
if err then
TSM:Printf(L["Invalid custom price source for %s. %s"], TSMAPI:GetSafeItemInfo(itemString) or itemString, err)
else
local totalQty = GetCachedItemQuantity(itemString)
local maxRestock = tonumber(opSettings.maxRestock) or 0
if maxRestock <= 0 or totalQty < maxRestock then
private.itemOperations[baseItemString] = private.itemOperations[baseItemString] or {}
tinsert(private.itemOperations[baseItemString], opName)
end
private.itemOperations[itemString] = opSettings
end
end
end

50
TradeSkillMaster_Shopping/sidebar/Other.lua

@ -1,37 +1,7 @@
local TSM = select(2, ...)
local L = LibStub("AceLocale-3.0"):GetLocale("TradeSkillMaster_Shopping") -- loads the localization table
local private = {sniperItemQuantities={}, sniperOperationSettings={}, sniperItemMaxPrices={}}
local function GetSniperItemQuantity(itemString)
local quantity = private.sniperItemQuantities[itemString]
if quantity == nil then
quantity = TSM:GetTotalQuantity(itemString) or 0
private.sniperItemQuantities[itemString] = quantity
end
return quantity
end
local function GetSniperOperationSettings(opName)
local settings = private.sniperOperationSettings[opName]
if settings == nil then
TSMAPI:UpdateOperation("Shopping", opName)
settings = TSM.operations[opName]
private.sniperOperationSettings[opName] = settings or false
end
return settings ~= false and settings or nil
end
local function GetSniperOperationMaxPrice(itemString, opName, opSettings)
private.sniperItemMaxPrices[itemString] = private.sniperItemMaxPrices[itemString] or {}
local cached = private.sniperItemMaxPrices[itemString][opName]
if cached ~= nil then
return cached ~= false and cached or nil
end
local opPrice = TSM:GetMaxPrice(opSettings.maxPrice, itemString)
private.sniperItemMaxPrices[itemString][opName] = opPrice or false
return opPrice
end
local private = {}
function private.Create(parent)
local frame = CreateFrame("Frame", nil, parent)
@ -240,9 +210,6 @@ end
function private:StartSniperSearch()
private.sniperItemQuantities = {}
private.sniperOperationSettings = {}
private.sniperItemMaxPrices = {}
TSM.Util:ShowSearchFrame(nil, L["% Market Value"])
TSM.Search:SetSearchBarDisabled(true)
TSM.Util:StartLastPageScan(private.SniperScanCallback)
@ -263,20 +230,9 @@ function private.SniperScanCallback(event, itemString, auctionItem)
vendorPrice = vendor
end
local operations = TSMAPI:GetItemOperation(itemString, "Shopping")
local totalQty = GetSniperItemQuantity(itemString)
for _, opName in ipairs(operations or {}) do
local opSettings = GetSniperOperationSettings(opName)
local opSettings = operations and operations[1] and TSM.operations[operations[1]]
if opSettings and opSettings.maxPrice then
local maxRestock = tonumber(opSettings.maxRestock) or 0
if maxRestock > 0 and totalQty >= maxRestock then
-- skip this operation if we're already restocked
else
local opPrice = GetSniperOperationMaxPrice(itemString, opName, opSettings)
if opPrice then
maxPrice = maxPrice and max(maxPrice, opPrice) or opPrice
end
end
end
maxPrice = TSM:GetMaxPrice(opSettings.maxPrice, itemString)
end
customPrice = TSM:GetMaxPrice(TSM.db.global.sniperCustomPrice, itemString)
end

5
TradeSkillMaster_Warehousing/Locale/enUS.lua

@ -55,23 +55,18 @@ L["Nothing to Move"] = true
L["Nothing to Move"] = true
L["Nothing to Move"] = true
L["Nothing to Restock"] = true
L["No Auctions found for %s"] = true
L["Operation Name"] = true
L["Operations"] = true
L["Preparing to Move"] = true
L["Preparing to Move"] = true
L["Preparing to Move"] = true
L["Please switch to the Shopping tab to restock from the auction house."] = true
L["Puts items matching the itemstring, itemID or partial text entered into the bank or guild bank."] = true
L["Relationships"] = true
L["Restock Bags"] = true
L["Restock from AH"] = true
L["Restocks items from the AH based on your Warehousing group selections."] = true
L["Restock Quantity"] = true
L["Restock Settings"] = true
L["Restock Settings"] = true
L["Restocking"] = true
L["No groups selected."] = true
L["Restore Bags"] = true
L["Set Keep in Bags Quantity"] = true
L["Set Keep in Bank Quantity"] = true

13
TradeSkillMaster_Warehousing/Modules/bankui.lua

@ -74,16 +74,13 @@ function bankui:createTab(parent)
buttonFrame:SetPoint("BOTTOMRIGHT")
buttonFrame.btnToBags = createButton(L["Move Group To Bags"], buttonFrame, nil)
buttonFrame.btnToBags:SetPoint("BOTTOM", buttonFrame, "BOTTOM", 0, 85)
buttonFrame.btnToBags:SetPoint("BOTTOM", buttonFrame, "BOTTOM", 0, 75)
buttonFrame.btnToBank = createButton(L["Move Group To Bank"], buttonFrame, nil)
buttonFrame.btnToBank:SetPoint("BOTTOM", buttonFrame, "BOTTOM", 0, 105)
buttonFrame.btnToBank:SetPoint("BOTTOM", buttonFrame, "BOTTOM", 0, 95)
buttonFrame.btnRestock = createButton(L["Restock Bags"], buttonFrame, nil)
buttonFrame.btnRestock:SetPoint("BOTTOM", buttonFrame, "BOTTOM", 0, 65)
buttonFrame.btnRestockAH = createButton(L["Restock from AH"], buttonFrame, nil)
buttonFrame.btnRestockAH:SetPoint("BOTTOM", buttonFrame, "BOTTOM", 0, 45)
buttonFrame.btnRestock:SetPoint("BOTTOM", buttonFrame, "BOTTOM", 0, 45)
buttonFrame.btnEmpty = createButton(L["Empty Bags"], buttonFrame, nil)
buttonFrame.btnEmpty:SetPoint("BOTTOM", buttonFrame, "BOTTOM", 0, 25)
@ -127,8 +124,4 @@ function bankui:updateButtons()
TSM.move:restockGroup(groupTree:GetSelectedGroupInfo())
end)
buttonFrame.btnRestockAH:SetScript("OnClick", function(self)
TSM.move:restockGroupAuction(groupTree:GetSelectedGroupInfo())
end)
end

75
TradeSkillMaster_Warehousing/Modules/move.lua

@ -13,50 +13,6 @@ local L = LibStub("AceLocale-3.0"):GetLocale("TradeSkillMaster_Warehousing")
local TSM = select(2, ...)
local move = TSM:NewModule("move", "AceEvent-3.0")
local AceGUI = LibStub("AceGUI-3.0") -- load the AceGUI libraries
local private = { shoppingItems = {} }
local function GetSelectedGroupInfoFallback()
local selection = TSM.db.profile.groupTreeSelectedGroupStatus and TSM.db.profile.groupTreeSelectedGroupStatus["Warehousing_Bank"]
if not selection then return end
local groupInfo = {}
for groupPath, isSelected in pairs(selection) do
if isSelected then
local operations = TSM:GetGroupOperations(groupPath, "Warehousing")
if operations then
groupInfo[groupPath] = { operations = operations, items = TSM:GetGroupItems(groupPath) }
end
end
end
if next(groupInfo) == nil then return end
return groupInfo
end
local function ShoppingNextSearch()
if next(private.shoppingItems) then
move:ShoppingSearch(private.shoppingItems[1].itemString, private.shoppingItems[1].quantity)
end
end
local function ShoppingCallback(remainingQty, boughtItem)
if not boughtItem then
if next(private.shoppingItems) then
local name = TSMAPI:GetSafeItemInfo(private.shoppingItems[1].itemString)
TSM:Print(format(L["No Auctions found for %s"], name or private.shoppingItems[1].itemString))
tremove(private.shoppingItems, 1)
TSMAPI:CreateTimeDelay("warehousingShoppingSearchThrottle", 0.5, ShoppingNextSearch)
end
elseif math.max(remainingQty or 0, 0) == 0 and next(private.shoppingItems) then
tremove(private.shoppingItems, 1)
TSMAPI:CreateTimeDelay("warehousingShoppingSearchThrottle", 0.5, ShoppingNextSearch)
end
end
function move:ShoppingSearch(itemString, need)
if not itemString or not need or need <= 0 then return end
local name = TSMAPI:GetSafeItemInfo(itemString)
if not name then return end
TSMAPI:ModuleAPI("Shopping", "runSearch", name .. "/exact/x" .. need, ShoppingCallback)
end
function move:restockGroup(grpInfo)
local restockItems, next = TSM.data:unIndexRestockGroupTree(grpInfo), next
@ -68,37 +24,6 @@ function move:restockGroup(grpInfo)
end
end
function move:restockGroupAuction(grpInfo)
if not TSMAPI:AHTabIsVisible("Shopping") then
TSM:Print(L["Please switch to the Shopping tab to restock from the auction house."])
return
end
if not grpInfo then
grpInfo = GetSelectedGroupInfoFallback()
end
if not grpInfo or next(grpInfo) == nil then
TSM:Print(L["No groups selected."])
return
end
local restockItems = TSM.data:unIndexRestockGroupTree(grpInfo)
private.shoppingItems = {}
for itemString, quantity in pairs(restockItems) do
if quantity > 0 then
tinsert(private.shoppingItems, { itemString = itemString, quantity = quantity })
end
end
if next(private.shoppingItems) == nil then
TSM:Print(L["Nothing to Restock"])
return
end
TSM:Print(L["Restocking"])
move:ShoppingSearch(private.shoppingItems[1].itemString, private.shoppingItems[1].quantity)
end
function move:groupTree(grpInfo, src)
local moveItems, next = TSM.data:unIndexMoveGroupTree(grpInfo, src), next
if next(moveItems) == nil then

5
TradeSkillMaster_Warehousing/TradeSkillMaster_Warehousing.lua

@ -89,7 +89,6 @@ function TSM:RegisterModule()
{ key = "movedata", label = L["Displays realtime move data."], callback = "SetLogFlag" },
{ key = "get", label = L["Gets items from the bank or guild bank matching the itemstring, itemID or partial text entered."], callback = "GetItem" },
{ key = "put", label = L["Puts items matching the itemstring, itemID or partial text entered into the bank or guild bank."], callback = "PutItem" },
{ key = "restockah", label = L["Restocks items from the AH based on your Warehousing group selections."], callback = "RestockFromAH" },
}
TSM.operations = { maxOperations = 1, callbackOptions = "Options:Load", callbackInfo = "GetOperationInfo" }
TSM.bankUiButton = { callback = "bankui:createTab" }
@ -239,7 +238,3 @@ function TSM:PutItem(args)
TSM:Print(L["Invalid criteria entered."])
end
end
function TSM:RestockFromAH()
TSM.move:restockGroupAuction()
end

40
bin/link_addons.sh

@ -1,40 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
TARGET_DIR="../Games/ascension-wow/drive_c/Program Files/Ascension Launcher/resources/client/Interface/AddOns"
ADDONS=(
"TradeSkillMaster"
"TradeSkillMaster_Accounting"
"TradeSkillMaster_AuctionDB"
"TradeSkillMaster_Auctioning"
"TradeSkillMaster_Crafting"
"TradeSkillMaster_Destroying"
"TradeSkillMaster_ItemTracker"
"TradeSkillMaster_Mailing"
"TradeSkillMaster_Shopping"
"TradeSkillMaster_Warehousing"
)
if [[ ! -d "$TARGET_DIR" ]]; then
echo "Target AddOns directory not found: $TARGET_DIR" >&2
exit 1
fi
for addon in "${ADDONS[@]}"; do
live_path="${TARGET_DIR}/${addon}"
src_path="${ROOT_DIR}/${addon}"
if [[ ! -d "$src_path" ]]; then
echo "Source addon folder missing: $src_path" >&2
continue
fi
if [[ -L "$live_path" || -d "$live_path" ]]; then
rm -rf "$live_path"
fi
ln -s "$src_path" "$live_path"
echo "Linked ${addon}"
done

78
scripts/link_and_go.sh

@ -1,78 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
MODE="${MODE:-local}" # local or ssh
if [[ "${1:-}" == "--ssh" ]]; then
MODE="ssh"
elif [[ "${1:-}" == "--local" ]]; then
MODE="local"
fi
ADDONS_DIR="${ADDONS_DIR:-../../ascension-wow/drive_c/Program Files/Ascension Launcher/resources/client/Interface/AddOns}"
MOUNT_DIR="${MOUNT_DIR:-/home/TradeSkillMaster}"
LOCAL_BASE="${LOCAL_BASE:-$ROOT_DIR}"
SSH_USER="${SSH_USER:-h}"
SSH_HOST="${SSH_HOST:-sarvern.no}"
SSH_PORT="${SSH_PORT:-36}"
SSH_REMOTE_BASE="${SSH_REMOTE_BASE:-/home/TradeSkillMaster}"
ADDONS=(
"TradeSkillMaster"
"TradeSkillMaster_Accounting"
"TradeSkillMaster_AuctionDB"
"TradeSkillMaster_Auctioning"
"TradeSkillMaster_Crafting"
"TradeSkillMaster_Destroying"
"TradeSkillMaster_ItemTracker"
"TradeSkillMaster_Mailing"
"TradeSkillMaster_Shopping"
"TradeSkillMaster_Warehousing"
)
MOUNT_ITEMS=("scripts" "${ADDONS[@]}")
if [[ "$MODE" == "ssh" ]]; then
mkdir -p "$MOUNT_DIR"
for item in "${MOUNT_ITEMS[@]}"; do
target_path="${MOUNT_DIR}/${item}"
mkdir -p "$target_path"
if command -v mountpoint >/dev/null && mountpoint -q "$target_path"; then
echo "Already mounted: $target_path"
continue
fi
sshfs -p "$SSH_PORT" "$SSH_USER@$SSH_HOST:$SSH_REMOTE_BASE/$item" "$target_path"
echo "Mounted $item"
done
SOURCE_BASE="$MOUNT_DIR"
else
SOURCE_BASE="$LOCAL_BASE"
fi
if [[ ! -d "$ADDONS_DIR" ]]; then
echo "AddOns directory not found: $ADDONS_DIR" >&2
exit 1
fi
for addon in "${ADDONS[@]}"; do
live_path="${ADDONS_DIR}/${addon}"
src_path="${SOURCE_BASE}/${addon}"
if [[ ! -d "$src_path" ]]; then
echo "Source addon folder missing: $src_path" >&2
continue
fi
if [[ -L "$live_path" || -d "$live_path" ]]; then
rm -rf "$live_path"
fi
ln -s "$src_path" "$live_path"
echo "Linked ${addon}"
done
Loading…
Cancel
Save