Compare commits

...

68 Commits
main ... dev

Author SHA1 Message Date
Jørgen Lien Sellæg 2e4092c0af fixes rart 3 months ago
Jørgen Lien Sellæg 1764cdc6e3 Add frame:IsVisible check to certain mailbox functions 3 months ago
Jørgen Lien Sellæg efff9329fa Add AuctionDB channel sync options and controls. 3 months ago
Jørgen Lien Sellæg df429912a0 Ignore archive directories in gitignore. 3 months ago
Jørgen Lien Sellæg e433f9b530 prioritize small/older mail stacks in gather 3 months ago
Jørgen Lien Sellæg 1732439a5c Fix roundup custom price parsing. 3 months ago
Jørgen Lien Sellæg 510a2bd018 guard custom price eval and slider value 3 months ago
h 0f16a83f10 optimize scans (hangs still pending) 3 months ago
h 5e805a6f72 advance gather shopping on empty results 4 months ago
Jørgen Lien Sellæg 1435baa1d2 refine shopping group search handling 4 months ago
Jørgen Lien Sellæg 639d6d8744 fix gather mail tasks 4 months ago
Jørgen Lien Sellæg 9f9d5b2c63 fix vendor gather overbuying 4 months ago
Jørgen Lien Sellæg f2fd4df956 widen auctioning price inputs 4 months ago
Jørgen Lien Sellæg d41968acd6 add click targets and scan guards 4 months ago
Jørgen Lien Sellæg 3fc255bfbe add shopping max restock 4 months ago
Jørgen Lien Sellæg 5a102f600f allow multiple shopping and crafting ops 4 months ago
Jørgen Lien Sellæg acaa3a5150 add click frame names 4 months ago
Jørgen Lien Sellæg c658f4838d fix gather mailbox collection 4 months ago
Jørgen Lien Sellæg 269852a19a show last buy price in tooltips 4 months ago
Jørgen Lien Sellæg db1a384c5b document smart avg buy price 4 months ago
Jørgen Lien Sellæg 227f65bb26 add smart avg buy price 4 months ago
h 817d2b6dd1 delete unused mount script 4 months ago
h aaf6e1b55b Changes link_and_go defaults 4 months ago
Jørgen Lien Sellæg 51a389d780 add ah restock command 4 months ago
Jørgen Lien Sellæg 7952573bab update crafting and warehousing 4 months ago
Jørgen Lien Sellæg 6d7a9e90b9 ignore local wtf and omanfred 4 months ago
Jørgen Lien Sellæg 4d760e9b79 update player gold tab 4 months ago
Jørgen Lien Sellæg 697be43d7a link and go 4 months ago
Jørgen Lien Sellæg c14a5f8ca7 add scan complete flash toggle 4 months ago
Jørgen Lien Sellæg e938497873 refresh crafting queue on buyout 4 months ago
Jørgen Lien Sellæg 01371c7c2d refine auctiondb buyout log 4 months ago
Jørgen Lien Sellæg 33e6d6718a log bag count on buyout 4 months ago
Jørgen Lien Sellæg 94590f30ed tweak auctiondb buyout log 4 months ago
Jørgen Lien Sellæg 96b210e65d log auctiondb info on buyout 4 months ago
Jørgen Lien Sellæg 800e7a17c3 add more tips 4 months ago
Jørgen Lien Sellæg 0e24db1153 expand custom price function docs 4 months ago
Jørgen Lien Sellæg 9f2205850e document custom price sources 4 months ago
Jørgen Lien Sellæg 472f8d2be6 update auctiondb sync and accounting graph 4 months ago
Jørgen Lien Sellæg 0fbfb60c9a fix channel name parsing 4 months ago
Jørgen Lien Sellæg 0f922cf180 fix channel name resolution 4 months ago
Jørgen Lien Sellæg 8b9106c975 shrink auctioning status bar 4 months ago
Jørgen Lien Sellæg 469e692eb5 remove getall and add inventory scan 4 months ago
Jørgen Lien Sellæg 1cfbdc4c4a fix channel name parsing 4 months ago
Jørgen Lien Sellæg f2e48bb0cd log channel sync prefix mismatch 4 months ago
Jørgen Lien Sellæg 1ff65947fe add channel sync debug logging 4 months ago
Jørgen Lien Sellæg 6157f813bf remove getall and add inventory buttons 4 months ago
Jørgen Lien Sellæg d6f0b2ee21 batch auctiondb channel sync 4 months ago
Jørgen Lien Sellæg 7483aa3ad3 pause auctiondb scan on tab change 4 months ago
Jørgen Lien Sellæg b181ef7bc6 add scan pause resume 4 months ago
Jørgen Lien Sellæg 4ae12602b0 add meta stuff 4 months ago
Jørgen Lien Sellæg caf152df3a update scan 4 months ago
Jørgen Lien Sellæg 4081ece2e9 add messages in chat 4 months ago
Jørgen Lien Sellæg c443d40d2f async send of messages to avoid fps drop 4 months ago
Jørgen Lien Sellæg 505f2304d4 fix encode 4 months ago
Jørgen Lien Sellæg 10047e31ab codec 4 months ago
Jørgen Lien Sellæg 95f5bec324 update to constraints 4 months ago
Jørgen Lien Sellæg 36e7bc0be5 change api 4 months ago
Jørgen Lien Sellæg c6820172a9 channel sync? 4 months ago
Jørgen Lien Sellæg 8ec1d3fd08 add more custom price options 4 months ago
Jørgen Lien Sellæg e9177f4a98 bag scan and bank scan seperate 4 months ago
Jørgen Lien Sellæg 4d78f056ff Merge branch 'no-attributes-dev' into dev 4 months ago
Jørgen Sverre Lien Sellæg d26abcc6ae add group scan 4 months ago
Jørgen Lien Sellæg a5fb892a13 Revert "add attributes to keep windows endings in files" 4 months ago
Jørgen Sverre Lien Sellæg 8b77bede68 skip wipe of data when searching 4 months ago
Jørgen Sverre Lien Sellæg 1a80d3f148 attempt to update scan data on searches 4 months ago
Jørgen Lien Sellæg 2f5db70887 add attributes to keep windows endings in files 5 months ago
Jørgen Lien Sellæg e722482be5 stores auction data that has been scanned up to the point of interruption 5 months ago
Jørgen Lien Sellæg 8c661ab24f add stuff 5 months ago
  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,7 +1,135 @@
*~
.env .env
.DS_Store .DS_Store
.release .release
.install .install
.lua/* .lua/*
.vscode .vscode
.idea .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/

22
AGENTS.md

@ -0,0 +1,22 @@
# 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,3 +1,134 @@
# TradeSkillMaster # TradeSkillMaster (Ascension.gg)
This is the repository for Trade Skill Master (TSM). Modified for Ascension.gg. 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.

100
TradeSkillMaster/Auction/AuctionControl.lua

@ -15,6 +15,9 @@ TSMAPI:RegisterForTracing(private, "TradeSkillMaster.AuctionControl_private")
LibStub("AceEvent-3.0"):Embed(private) LibStub("AceEvent-3.0"):Embed(private)
private.matchList = {} private.matchList = {}
private.currentPage = {} private.currentPage = {}
private.shoppingPurchasedCounts = {}
private.shoppingTotalCache = {}
private.shoppingOperationSettings = {}
local function GetNumInBags(baseItemString) local function GetNumInBags(baseItemString)
@ -43,6 +46,72 @@ local function ValidateAuction(index, list)
return count == private.currentAuction.count and buyout == private.currentAuction.buyout and itemString == private.currentAuction.itemString, data return count == private.currentAuction.count and buyout == private.currentAuction.buyout and itemString == private.currentAuction.itemString, data
end 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") local diffFrame = CreateFrame("Frame")
diffFrame:Hide() diffFrame:Hide()
diffFrame.num = 0 diffFrame.num = 0
@ -146,6 +215,15 @@ end
function private:DoBuyout() function private:DoBuyout()
if private.isSearching or not private.currentAuction or not private.confirmationFrame:IsVisible() then return end 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 for i=#private.matchList, 1, -1 do
local aucIndex = private.matchList[i] local aucIndex = private.matchList[i]
@ -275,8 +353,14 @@ function private:AUCTION_ITEM_LIST_UPDATE()
TSMAPI.AuctionScan:CacheRemove(prevAuction.itemString, private.currentCacheIndex) TSMAPI.AuctionScan:CacheRemove(prevAuction.itemString, private.currentCacheIndex)
private.currentCacheIndex = nil private.currentCacheIndex = nil
end 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) TSM:AuctionControlCallback("OnBuyout", prevAuction)
TSMAPI:FireEvent("TSM:AUCTIONCONTROL:ITEMBOUGHT", prevAuction)
end end
end end
@ -311,6 +395,11 @@ function TSMAPI.AuctionControl:ShowControlButtons(parent, rt, callback, module,
private.rt = rt private.rt = rt
private.callback = callback private.callback = callback
private.module = module private.module = module
if module == "Shopping" then
wipe(private.shoppingPurchasedCounts)
wipe(private.shoppingTotalCache)
wipe(private.shoppingOperationSettings)
end
private.postBidPercent = postBidPercent private.postBidPercent = postBidPercent
private.postUndercut = postUndercut private.postUndercut = postUndercut
return private.controlButtons return private.controlButtons
@ -347,6 +436,17 @@ function private:ShowConfirmationWindow()
end end
private:SetCurrentAuction(private.rt:GetSelectedAuction()) private:SetCurrentAuction(private.rt:GetSelectedAuction())
if not private.currentAuction then return end 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") private:RegisterEvent("AUCTION_ITEM_LIST_UPDATE")
diffFrame.num = 0 diffFrame.num = 0

1
TradeSkillMaster/Auction/AuctionFrame.lua

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

49
TradeSkillMaster/Auction/AuctionScanning.lua

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

90
TradeSkillMaster/Core/Prices.lua

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

40
TradeSkillMaster/GUI/BuildPage.lua

@ -74,11 +74,31 @@ local function CreateCustomPriceFrame()
text = TSMAPI.Design:GetInlineColor("category")..L["More Advanced Methods"].."|r", text = TSMAPI.Design:GetInlineColor("category")..L["More Advanced Methods"].."|r",
relativeWidth = 1, relativeWidth = 1,
}, },
{
type = "Label",
text = "Combine sources, math, and functions to build rules. Sources are case-insensitive.",
relativeWidth = 1,
},
{ {
type = "Label", type = "Label",
text = format("See %s for more info.", TSMAPI.Design:GetInlineColor("link").."https://tinyurl.com/tsm2Ascension|r"), text = format("See %s for more info.", TSMAPI.Design:GetInlineColor("link").."https://tinyurl.com/tsm2Ascension|r"),
relativeWidth = 1, 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", type = "HeadingLine",
relativeWidth = 1, relativeWidth = 1,
@ -113,6 +133,26 @@ local function CreateCustomPriceFrame()
text = "max(vendor, 120% crafting)", text = "max(vendor, 120% crafting)",
relativeWidth = 1, 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) TSMAPI:BuildPage(container, page)

7
TradeSkillMaster/GUI/TSMWidgets/TSMSlider.lua

@ -158,6 +158,13 @@ local methods = {
end, end,
["SetValue"] = function(self, value) ["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.setup = true
self.slider:SetValue(value) self.slider:SetValue(value)
self.value = value self.value = value

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

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

6
TradeSkillMaster/TradeSkillMaster.lua

@ -735,6 +735,12 @@ function TSMAPI:GetItemPrices(itemLink)
-- Use TSM:GetCustomPrice to get avgBuy price -- Use TSM:GetCustomPrice to get avgBuy price
prices.avgBuy = TSM:GetCustomPrice("avgBuy", itemString) or 0 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 -- Use TSM:GetCustomPrice to get avgSell price
prices.avgSell = TSM:GetCustomPrice("avgSell", itemString) or 0 prices.avgSell = TSM:GetCustomPrice("avgSell", itemString) or 0

2
TradeSkillMaster/TradeSkillMaster.toc

@ -2,7 +2,7 @@
## Title: |cff00fe00TradeSkillMaster: Revived|r ## Title: |cff00fe00TradeSkillMaster: Revived|r
## Notes: Core addon for the TradeSkillMaster suite, revived for Wrath of the Lich King. Does nothing without modules installed. ## 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... ## Author: Sapu94, Bart39, Gnomezilla [Warmane-Icecrown(A)], BlueAo [Warmane], andrew6180, Yoshiyuka, DimaSheiko, and other contributors...
## Version: Rev668 ## Version: 2.3.46
## SavedVariables: TradeSkillMasterAppDB, AscensionTSMDB ## SavedVariables: TradeSkillMasterAppDB, AscensionTSMDB
## OptionalDeps: AccurateTime, Ace3, LibDataBroker-1.1, LibDBIcon-1.0, LibExtraTip, TipHelper, LibParse, LibCompress, LibGraph-2.0, SharedMedia, TheUndermineJournal, TheUndermineJournalGE ## 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 ## 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["|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["%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 (over 1k gold) 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 in your gold on hand."] = true
L["Activity Type"] = true L["Activity Type"] = true
L["All"] = true L["All"] = true
L["Amount"] = true L["Amount"] = true
@ -24,9 +24,13 @@ L["Average Prices:"] = true
L["Avg Buy Price"] = true L["Avg Buy Price"] = true
L["Avg Resale Profit"] = true L["Avg Resale Profit"] = true
L["Avg Sell Price"] = 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["Back to Previous Page"] = true
L["Balance"] = 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 thousands of 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 gold."] = true
L["Bought"] = true L["Bought"] = true
L["Buyer/Seller"] = true L["Buyer/Seller"] = true
L["Cancelled Since Last Sale:"] = true L["Cancelled Since Last Sale:"] = true
@ -81,6 +85,8 @@ L["Market Value Source"] = true
L["Market Value"] = true L["Market Value"] = true
L["Max Buy Price"] = true L["Max Buy Price"] = true
L["Max Sell Price"] = true L["Max Sell Price"] = true
L["Min Buy Price"] = true
L["Min Sell Price"] = true
L["None"] = true L["None"] = true
L["Options"] = true L["Options"] = true
L["Other Income"] = true L["Other Income"] = true
@ -143,6 +149,12 @@ L["Total Sale Price"] = true
L["Total Spent:"] = true L["Total Spent:"] = true
L["Total Value"] = true L["Total Value"] = true
L["Total:"] = 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["Track sales/purchases via trade"] = true
L["Type"] = true L["Type"] = true
L["Use smart average for purchase price"] = true L["Use smart average for purchase price"] = true

7
TradeSkillMaster_Accounting/Modules/data.lua

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

200
TradeSkillMaster_Accounting/Modules/gui.lua

@ -809,7 +809,7 @@ function private:GetGoldGraphPoints(goldLog)
local data = {} local data = {}
for _, info in ipairs(goldLog) do for _, info in ipairs(goldLog) do
local x1, x2 = info.startMinute, info.endMinute local x1, x2 = info.startMinute, info.endMinute
local y = info.copper / COPPER_PER_GOLD / 1000 local y = info.copper / COPPER_PER_GOLD
minX = min(minX, x1) minX = min(minX, x1)
maxX = max(maxX, x2) maxX = max(maxX, x2)
minY = min(minY, floor(y)) minY = min(minY, floor(y))
@ -831,7 +831,7 @@ function private:GetGoldGraphSumData()
if i > 1 then if i > 1 then
data[i].startMinute = data[i-1].endMinute+1 data[i].startMinute = data[i-1].endMinute+1
end end
data[i].copper = TSM:Round(data[i].copper, COPPER_PER_GOLD*1000) data[i].copper = TSM:Round(data[i].copper, COPPER_PER_GOLD)
end end
tinsert(players, data) tinsert(players, data)
tinsert(starts, data[1].startMinute) tinsert(starts, data[1].startMinute)
@ -884,22 +884,100 @@ function private:GetGoldGraphSumData()
return private:GetGoldGraphPoints(temp) return private:GetGoldGraphPoints(temp)
end end
function GUI:DrawGoldGraph(container) function private:GetWealthPrice(itemString)
TSM.db.realm.goldGraphCharacter = TSM.db.realm.goldGraphCharacter or UnitName("player") local avgSell = TSM:GetAvgSellPrice(itemString)
local player = TSM.db.realm.goldGraphCharacter local marketValue = TSMAPI:GetItemValue(itemString, "DBMarket")
local data, minX, maxX, minY, maxY 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 if player == "<ALL>" then
data, minX, maxX, minY, maxY = private:GetGoldGraphSumData() 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 else
data, minX, maxX, minY, maxY = private:GetGoldGraphPoints(TSM.db.realm.goldLog[player]) 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 end
function GUI:DrawGoldGraph(container)
TSM.db.realm.goldGraphCharacter = TSM.db.realm.goldGraphCharacter or UnitName("player")
local player = TSM.db.realm.goldGraphCharacter
local dropdownList = {["<ALL>"]="Sum of All Characters"} local dropdownList = {["<ALL>"]="Sum of All Characters"}
for player in pairs(TSM.db.realm.goldLog) do for player in pairs(TSM.db.realm.goldLog) do
dropdownList[player] = player dropdownList[player] = player
end end
if not data then 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"]
local page = { local page = {
{ {
type = "SimpleGroup", type = "SimpleGroup",
@ -907,104 +985,66 @@ function GUI:DrawGoldGraph(container)
children = { children = {
{ {
type = "Label", type = "Label",
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."], text = L["Player Gold"],
relativeWidth = 1, relativeWidth = 1,
}, },
{ -- {
type = "Spacer", -- type = "Spacer",
}, -- },
{ {
type = "Dropdown", type = "Dropdown",
label = "Character to Graph", label = L["Character to Graph"],
settingInfo = {TSM.db.realm, "goldGraphCharacter"}, settingInfo = {TSM.db.realm, "goldGraphCharacter"},
relativeWidth = 0.5, relativeWidth = 0.5,
list = dropdownList, list = dropdownList,
callback = function() container:ReloadTab() end, 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 = "SimpleGroup", type = "InlineGroup",
layout = "Flow", layout = "Flow",
title = L["Player Wealth"],
backdrop = true,
children = { children = {
{ {
type = "Label", type = "Label",
--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 = L["Total:"] .. " " .. wealthTotalText,
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, relativeWidth = 1,
}, },
-- {
-- type = "Spacer",
-- },
{ {
type = "Dropdown", type = "Label",
label = L["Character to Graph"], text = L["Bags:"] .. " " .. wealthBagsText,
settingInfo = {TSM.db.realm, "goldGraphCharacter"}, relativeWidth = 0.33,
relativeWidth = 0.5,
list = dropdownList,
callback = function() container:ReloadTab() end,
}, },
-- {
-- type = "HeadingLine"
-- },
{ {
type = "Spacer", type = "Label",
text = L["Bank:"] .. " " .. wealthBankText,
relativeWidth = 0.33,
}, },
{ {
type = "ScrollFrame", type = "Label",
fullHeight = false, --true text = L["Auctions:"] .. " " .. wealthAuctionsText,
layout = "flow" relativeWidth = 0.33,
},
},
},
{
type = "InlineGroup",
layout = "Flow",
title = L["Coin on Hand"],
backdrop = true,
children = {
{
type = "Label",
text = L["Total:"] .. " " .. coinTotalText,
relativeWidth = 1,
},
},
}, },
}, },
}, },
} }
TSMAPI:BuildPage(container, page) 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 end
function GUI:DrawOptions(container) 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 if data.startMinute == data.endMinute and data.copper == 0 then
tremove(playerData, i) tremove(playerData, i)
else else
-- round to nearest 1k gold -- round to nearest gold
data.copper = TSM:Round(data.copper, COPPER_PER_GOLD*1000) data.copper = TSM:Round(data.copper, COPPER_PER_GOLD)
end end
end end
if #playerData >= 2 then if #playerData >= 2 then
@ -125,8 +125,12 @@ function TSM:RegisterModule()
TSM.priceSources = { TSM.priceSources = {
{ key = "avgSell", label = L["Avg Sell Price"], callback = "GetAvgSellPrice" }, { key = "avgSell", label = L["Avg Sell Price"], callback = "GetAvgSellPrice" },
{ key = "avgBuy", label = L["Avg Buy Price"], callback = "GetAvgBuyPrice" }, { 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 = "maxSell", label = L["Max Sell Price"], callback = "GetMaxSellPrice" },
{ key = "maxBuy", label = L["Max Buy Price"], callback = "GetMaxBuyPrice" }, { 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" } TSM.tooltipOptions = { callback = "GUI:LoadTooltipOptions" }
@ -185,6 +189,7 @@ function TSM:GetTooltip(itemString)
if TSM.db.realm.tooltip.purchase and TSM.items[itemString] and #TSM.items[itemString].buys > 0 then 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 lastPurchased = TSM.items[itemString].buys[#TSM.items[itemString].buys].time
local lastBuyPrice = TSM:GetLastBuyPrice(itemString)
local totalPrice, totalNum = 0, 0 local totalPrice, totalNum = 0, 0
for _, record in ipairs(TSM.items[itemString].buys) do for _, record in ipairs(TSM.items[itemString].buys) do
totalNum = totalNum + record.quantity totalNum = totalNum + record.quantity
@ -200,15 +205,30 @@ function TSM:GetTooltip(itemString)
else else
local avgPrice = TSM:GetAvgBuyPrice(itemString) local avgPrice = TSM:GetAvgBuyPrice(itemString)
local maxPrice = TSM:GetMaxBuyPrice(itemString) local maxPrice = TSM:GetMaxBuyPrice(itemString)
local smartAvgPrice = TSM:GetSmartAvgBuyPrice(itemString)
if moneyCoinsTooltip then 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" .. "?"))) }) 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 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" .. "?"))) }) 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 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 end
if lastPurchased then if lastPurchased then
local timeDiff = SecondsToTime(time() - lastPurchased) local timeDiff = SecondsToTime(time() - lastPurchased)
tinsert(text, { left = " " .. L["Last Purchased:"], right = "|cffffffff" .. format(L["%s ago"], timeDiff) }) 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
end end
@ -422,12 +442,24 @@ function TSM:GetAvgSellPrice(itemString)
return TSM.cache[itemString].avgSellPrice, TSM.cache[itemString].avgSellNum return TSM.cache[itemString].avgSellPrice, TSM.cache[itemString].avgSellNum
end end
local function GetAvgerageBuyPrice(itemString, noBaseItem) 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)
if not noBaseItem and itemString and baseItemLookup[itemString] then if not noBaseItem and itemString and baseItemLookup[itemString] then
local totalPrice, totalNum = 0, 0 local totalPrice, totalNum = 0, 0
for _, item in ipairs(baseItemLookup[itemString]) do for _, item in ipairs(baseItemLookup[itemString]) do
if not baseItemLookup[item] then if not baseItemLookup[item] then
local price, num = GetAvgerageBuyPrice(item, true) local price, num = GetAvgerageBuyPrice(item, true, useSmart)
if price and num and num > 0 then if price and num and num > 0 then
totalPrice = totalPrice + price totalPrice = totalPrice + price
totalNum = totalNum + num totalNum = totalNum + num
@ -441,16 +473,11 @@ local function GetAvgerageBuyPrice(itemString, noBaseItem)
if not (TSM.items[itemString] and #TSM.items[itemString].buys > 0) then return end if not (TSM.items[itemString] and #TSM.items[itemString].buys > 0) then return end
local itemCount = 0 local itemCount = 0
if TSM.db.realm.smartBuyPrice then if useSmart or TSM.db.realm.smartBuyPrice then
local player, alts = TSMAPI:ModuleAPI("ItemTracker", "playertotal", itemString) itemCount = GetSmartBuyItemCount(itemString)
if not player then if useSmart and itemCount <= 0 then
alts = nil return
end 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 end
local num, totalPrice = 0, 0 local num, totalPrice = 0, 0
@ -480,6 +507,19 @@ function TSM:GetAvgBuyPrice(itemString)
return TSM.cache[itemString].avgBuyPrice, TSM.cache[itemString].avgBuyNum return TSM.cache[itemString].avgBuyPrice, TSM.cache[itemString].avgBuyNum
end 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) function TSM:GetMaxSellPrice(itemString)
itemString = TSMAPI:GetItemString(select(2, TSMAPI:GetSafeItemInfo(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 if not (itemString and TSM.items[itemString] and #TSM.items[itemString].sales > 0) then return end
@ -496,6 +536,22 @@ function TSM:GetMaxSellPrice(itemString)
return TSM.cache[itemString].maxSellPrice return TSM.cache[itemString].maxSellPrice
end 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) function TSM:GetMaxBuyPrice(itemString)
itemString = TSMAPI:GetItemString(select(2, TSMAPI:GetSafeItemInfo(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 if not (itemString and TSM.items[itemString] and #TSM.items[itemString].buys > 0) then return end
@ -512,6 +568,35 @@ function TSM:GetMaxBuyPrice(itemString)
return TSM.cache[itemString].maxBuyPrice return TSM.cache[itemString].maxBuyPrice
end 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) function TSM:Round(value, sig)
sig = sig or 1 sig = sig or 1
local gold = value / sig local gold = value / sig

6
TradeSkillMaster_AuctionDB/Locale/deDE.lua

@ -13,14 +13,11 @@
local L = LibStub("AceLocale-3.0"):NewLocale("TradeSkillMaster_AuctionDB", "deDE") local L = LibStub("AceLocale-3.0"):NewLocale("TradeSkillMaster_AuctionDB", "deDE")
if not L then return end 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["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["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["Ascending"] = "Aufsteigend"
L["AuctionDB - Market Value"] = "AuctionDB - Marktwert" L["AuctionDB - Market Value"] = "AuctionDB - Marktwert"
L["AuctionDB - Minimum Buyout"] = "AuctionDB - Mindestpreis" 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["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 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." L["Display market value in tooltip."] = "Zeige Marktwert im Tooltip."
@ -29,7 +26,6 @@ L["Display total number of items ever seen in tooltip."] = "Zeige Gesamtzahl der
L["Done Scanning"] = "Scannen beendet" 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["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["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["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, 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." 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."
@ -44,7 +40,6 @@ L["Items per page"] = "Gegenstände pro Seite"
L["Items %s - %s (%s total)"] = "Gegenstände %s - %s (%s gesamt)" L["Items %s - %s (%s total)"] = "Gegenstände %s - %s (%s gesamt)"
L["Item SubType Filter"] = "Gegenstands-Unterkategorie-Filter" L["Item SubType Filter"] = "Gegenstands-Unterkategorie-Filter"
L["Item Type Filter"] = "Gegenstands-Kategorie-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 Scanned"] = "Zuletzt gescannt"
L["Last updated from in-game scan %s ago."] = "Zuletzt aktualisiert durch in-game Scan vor %s" 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" L["Last updated from the TSM Application %s ago."] = "Zuletzt aktualisiert durch TSM-Applikation vor %s"
@ -71,7 +66,6 @@ L["Reset Data"] = "Daten zurücksetzen"
L["Resets AuctionDB's scan data"] = "Setzt die Scandaten der \"AuctionDB\" zurück" L["Resets AuctionDB's scan data"] = "Setzt die Scandaten der \"AuctionDB\" zurück"
L["Result Order:"] = "Reihenfolge der Ergebnisse:" L["Result Order:"] = "Reihenfolge der Ergebnisse:"
L["Run Full Scan"] = "Starte einen vollen Scan" L["Run Full Scan"] = "Starte einen vollen Scan"
L["Run GetAll Scan"] = "Starte Komplettscan"
L["Running query..."] = "Abfrage läuft..." L["Running query..."] = "Abfrage läuft..."
L["%s ago"] = "Vor %s" L["%s ago"] = "Vor %s"
L["Scanning page %s/%s"] = "Scanne Seite %s/%s" L["Scanning page %s/%s"] = "Scanne Seite %s/%s"

37
TradeSkillMaster_AuctionDB/Locale/enUS.lua

@ -14,31 +14,34 @@ local L = LibStub("AceLocale-3.0"):NewLocale("TradeSkillMaster_AuctionDB", "enUS
if not L then return end if not L then return end
L["%s ago"] = true L["%s ago"] = 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. Expect this scan to take several minutes or longer."] = 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["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["Are you sure you want to clear your AuctionDB data?"] = true
L["Ascending"] = true L["Ascending"] = true
L["AuctionDB - Market Value"] = true L["AuctionDB - Market Value"] = true
L["AuctionDB - Minimum Buyout"] = true L["AuctionDB - Minimum Buyout"] = true
L["Can't run a GetAll scan right now."] = true
L["Descending"] = true L["Descending"] = true
L["Disable \"GetAll\" Auction Scans"] = true
L["Display lowest buyout value seen in the last scan in tooltip."] = 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["Display market value in tooltip."] = true
L["Enable channel sync"] = true
L["Done Scanning"] = 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["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["Enable display of AuctionDB data in tooltip."] = 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["The channel name used to share AuctionDB data."] = true
L["Hide poor quality items"] = 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 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 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, 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, poor quality items won't be shown in the search results."] = 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 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["If checked, the market value of the item will be displayed"] = true
L["Imported %s scans worth of new auction data!"] = 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["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 Link"] = true
L["Item MinLevel"] = true L["Item MinLevel"] = true
L["Item SubType Filter"] = true L["Item SubType Filter"] = true
@ -60,6 +63,7 @@ L["No scans found."] = true
L["Not Ready"] = true L["Not Ready"] = true
L["Not Scanned"] = true L["Not Scanned"] = true
L["Options"] = true L["Options"] = true
L["Pause"] = true
L["Preparing Filter %d / %d"] = true L["Preparing Filter %d / %d"] = true
L["Preparing Filters..."] = true L["Preparing Filters..."] = true
L["Previous Page"] = true L["Previous Page"] = true
@ -70,11 +74,20 @@ L["Refreshes the current search results."] = true
L["Removed %s from AuctionDB."] = true L["Removed %s from AuctionDB."] = true
L["Reset Data"] = true L["Reset Data"] = true
L["Resets AuctionDB's scan 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["Result Order:"] = true
L["Run Full Scan"] = true L["Run Full Scan"] = true
L["Run GetAll Scan"] = true
L["Running query..."] = true L["Running query..."] = true
L["Running query... Server not responding due to throttling? Try again later..."] = 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["Scan Selected Groups"] = true
L["Scanning %d / %d (Page 1 / ?)"] = true L["Scanning %d / %d (Page 1 / ?)"] = true
L["Scanning %d / %d (Page %d / %d)"] = true L["Scanning %d / %d (Page %d / %d)"] = true
@ -87,10 +100,16 @@ 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["Shift-Right-Click to clear all data for this item from AuctionDB."] = true
L["Show AuctionDB AH Tab (Requires Reload)"] = true L["Show AuctionDB AH Tab (Requires Reload)"] = true
L["Sort items by"] = 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 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["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["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 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 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["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["You have disabled GetAll scans via AuctionDB's options."] = true L["No bag items found."] = true
L["No inventory items found."] = true
L["No bank items found."] = true
L["ItemTracker data unavailable."] = true

6
TradeSkillMaster_AuctionDB/Locale/esES.lua

@ -13,14 +13,11 @@
local L = LibStub("AceLocale-3.0"):NewLocale("TradeSkillMaster_AuctionDB", "esES") local L = LibStub("AceLocale-3.0"):NewLocale("TradeSkillMaster_AuctionDB", "esES")
if not L then return end 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["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["Are you sure you want to clear your AuctionDB data?"] = "¿Está seguro que desea borrar los datos AuctionDB?"
L["Ascending"] = "Ascendente" L["Ascending"] = "Ascendente"
-- L["AuctionDB - Market Value"] = "" -- L["AuctionDB - Market Value"] = ""
-- L["AuctionDB - Minimum Buyout"] = "" -- L["AuctionDB - Minimum Buyout"] = ""
-- L["Can't run a GetAll scan right now."] = ""
L["Descending"] = "Descendiendo" L["Descending"] = "Descendiendo"
-- L["Display lowest buyout value seen in the last scan in tooltip."] = "" -- L["Display lowest buyout value seen in the last scan in tooltip."] = ""
-- L["Display market value in tooltip."] = "" -- L["Display market value in tooltip."] = ""
@ -29,7 +26,6 @@ L["Descending"] = "Descendiendo"
-- L["Done Scanning"] = "" -- 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["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["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["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, 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."] = "" -- L["If checked, the lowest buyout value seen in the last scan of the item will be displayed."] = ""
@ -44,7 +40,6 @@ L["Item MinLevel"] = "NivelMin Objeto"
-- L["Items %s - %s (%s total)"] = "" -- L["Items %s - %s (%s total)"] = ""
-- L["Item SubType Filter"] = "" -- L["Item SubType Filter"] = ""
-- L["Item Type 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 Scanned"] = ""
-- L["Last updated from in-game scan %s ago."] = "" -- L["Last updated from in-game scan %s ago."] = ""
-- L["Last updated from the TSM Application %s ago."] = "" -- L["Last updated from the TSM Application %s ago."] = ""
@ -71,7 +66,6 @@ L["Ready in %s min and %s sec"] = "Listo en %s min y %s sec."
-- L["Resets AuctionDB's scan data"] = "" -- L["Resets AuctionDB's scan data"] = ""
-- L["Result Order:"] = "" -- L["Result Order:"] = ""
-- L["Run Full Scan"] = "" -- L["Run Full Scan"] = ""
L["Run GetAll Scan"] = "Hacer un GetAll Scan"
-- L["Running query..."] = "" -- L["Running query..."] = ""
L["%s ago"] = "hace %s" L["%s ago"] = "hace %s"
-- L["Scanning page %s/%s"] = "" -- L["Scanning page %s/%s"] = ""

6
TradeSkillMaster_AuctionDB/Locale/esMX.lua

@ -13,14 +13,11 @@
local L = LibStub("AceLocale-3.0"):NewLocale("TradeSkillMaster_AuctionDB", "esMX") local L = LibStub("AceLocale-3.0"):NewLocale("TradeSkillMaster_AuctionDB", "esMX")
if not L then return end 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["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["Are you sure you want to clear your AuctionDB data?"] = ""
-- L["Ascending"] = "" -- L["Ascending"] = ""
-- L["AuctionDB - Market Value"] = "" -- L["AuctionDB - Market Value"] = ""
-- L["AuctionDB - Minimum Buyout"] = "" -- L["AuctionDB - Minimum Buyout"] = ""
-- L["Can't run a GetAll scan right now."] = ""
-- L["Descending"] = "" -- L["Descending"] = ""
-- L["Display lowest buyout value seen in the last scan in tooltip."] = "" -- L["Display lowest buyout value seen in the last scan in tooltip."] = ""
-- L["Display market value in tooltip."] = "" -- L["Display market value in tooltip."] = ""
@ -29,7 +26,6 @@ if not L then return end
-- L["Done Scanning"] = "" -- 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["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["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["Hide poor quality items"] = ""
-- L["If checked, poor quality items won't be shown in the search results."] = "" -- 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."] = "" -- L["If checked, the lowest buyout value seen in the last scan of the item will be displayed."] = ""
@ -44,7 +40,6 @@ if not L then return end
-- L["Items %s - %s (%s total)"] = "" -- L["Items %s - %s (%s total)"] = ""
-- L["Item SubType Filter"] = "" -- L["Item SubType Filter"] = ""
-- L["Item Type 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 Scanned"] = ""
-- L["Last updated from in-game scan %s ago."] = "" -- L["Last updated from in-game scan %s ago."] = ""
-- L["Last updated from the TSM Application %s ago."] = "" -- L["Last updated from the TSM Application %s ago."] = ""
@ -71,7 +66,6 @@ if not L then return end
-- L["Resets AuctionDB's scan data"] = "" -- L["Resets AuctionDB's scan data"] = ""
-- L["Result Order:"] = "" -- L["Result Order:"] = ""
-- L["Run Full Scan"] = "" -- L["Run Full Scan"] = ""
-- L["Run GetAll Scan"] = ""
-- L["Running query..."] = "" -- L["Running query..."] = ""
-- L["%s ago"] = "" -- L["%s ago"] = ""
-- L["Scanning page %s/%s"] = "" -- L["Scanning page %s/%s"] = ""

6
TradeSkillMaster_AuctionDB/Locale/frFR.lua

@ -13,14 +13,11 @@
local L = LibStub("AceLocale-3.0"):NewLocale("TradeSkillMaster_AuctionDB", "frFR") local L = LibStub("AceLocale-3.0"):NewLocale("TradeSkillMaster_AuctionDB", "frFR")
if not L then return end 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["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["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["Ascending"] = "Croissant"
L["AuctionDB - Market Value"] = "AuctionDB - Valeur du Marché" L["AuctionDB - Market Value"] = "AuctionDB - Valeur du Marché"
L["AuctionDB - Minimum Buyout"] = "AuctionDB - Achat minimum" L["AuctionDB - Minimum Buyout"] = "AuctionDB - Achat minimum"
-- L["Can't run a GetAll scan right now."] = ""
L["Descending"] = "Décroissant" L["Descending"] = "Décroissant"
-- L["Display lowest buyout value seen in the last scan in tooltip."] = "" -- L["Display lowest buyout value seen in the last scan in tooltip."] = ""
-- L["Display market value in tooltip."] = "" -- L["Display market value in tooltip."] = ""
@ -29,7 +26,6 @@ L["Descending"] = "Décroissant"
L["Done Scanning"] = "Analyse terminée" 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["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["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["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, 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."] = "" -- L["If checked, the lowest buyout value seen in the last scan of the item will be displayed."] = ""
@ -44,7 +40,6 @@ L["Items per page"] = "Objets par page"
L["Items %s - %s (%s total)"] = "Objets %s - %s (%s au total)" L["Items %s - %s (%s total)"] = "Objets %s - %s (%s au total)"
L["Item SubType Filter"] = "Sous-catégorie de l'objet" L["Item SubType Filter"] = "Sous-catégorie de l'objet"
L["Item Type Filter"] = "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 Scanned"] = "Dernière fois analysé"
-- L["Last updated from in-game scan %s ago."] = "" -- L["Last updated from in-game scan %s ago."] = ""
-- L["Last updated from the TSM Application %s ago."] = "" -- L["Last updated from the TSM Application %s ago."] = ""
@ -71,7 +66,6 @@ L["Reset Data"] = "Réinitialiser les données"
L["Resets AuctionDB's scan data"] = "Réinitialise les données d'AuctionDB" L["Resets AuctionDB's scan data"] = "Réinitialise les données d'AuctionDB"
-- L["Result Order:"] = "" -- L["Result Order:"] = ""
L["Run Full Scan"] = "Analyse complète" L["Run Full Scan"] = "Analyse complète"
L["Run GetAll Scan"] = "Analyse GetAll"
-- L["Running query..."] = "" -- L["Running query..."] = ""
L["%s ago"] = "Il y a %s" L["%s ago"] = "Il y a %s"
L["Scanning page %s/%s"] = "Scan de la page %s/%s" -- Needs review L["Scanning page %s/%s"] = "Scan de la page %s/%s" -- Needs review

6
TradeSkillMaster_AuctionDB/Locale/koKR.lua

@ -13,14 +13,11 @@
local L = LibStub("AceLocale-3.0"):NewLocale("TradeSkillMaster_AuctionDB", "koKR") local L = LibStub("AceLocale-3.0"):NewLocale("TradeSkillMaster_AuctionDB", "koKR")
if not L then return end 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["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["Are you sure you want to clear your AuctionDB data?"] = "모든 AuctionDB 데이터를 삭제 하시겠습니까?"
L["Ascending"] = "오름차순" L["Ascending"] = "오름차순"
L["AuctionDB - Market Value"] = "AuctionDB - 시장 가격" L["AuctionDB - Market Value"] = "AuctionDB - 시장 가격"
L["AuctionDB - Minimum Buyout"] = "AuctionDB - 최소 구매가격" L["AuctionDB - Minimum Buyout"] = "AuctionDB - 최소 구매가격"
L["Can't run a GetAll scan right now."] = "지금은 GetAll 검색을 실행할 수 없습니다." -- Needs review
L["Descending"] = "내림차순" L["Descending"] = "내림차순"
L["Display lowest buyout value seen in the last scan in tooltip."] = "최근 검색 시 본 최소 구매가를 툴팁에 표시합니다." -- Needs review L["Display lowest buyout value seen in the last scan in tooltip."] = "최근 검색 시 본 최소 구매가를 툴팁에 표시합니다." -- Needs review
L["Display market value in tooltip."] = "시장 가격을 툴팁에 표시합니다." -- Needs review L["Display market value in tooltip."] = "시장 가격을 툴팁에 표시합니다." -- Needs review
@ -29,7 +26,6 @@ L["Display total number of items ever seen in tooltip."] = "아이템의 전체
L["Done Scanning"] = "검색 완료" 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["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["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["Hide poor quality items"] = "저급 품질 아이템 숨기기"
L["If checked, poor quality items won't be shown in the search results."] = "선택하면, 저급 품질 아이템은 검색 결과에 표시하지 않습니다." 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 L["If checked, the lowest buyout value seen in the last scan of the item will be displayed."] = "선택하면, 최근 검색에서 본 아이템의 최소 구매 가격을 표시합니다." -- Needs review
@ -44,7 +40,6 @@ L["Items per page"] = "페이지당 아이템 개수"
L["Items %s - %s (%s total)"] = "아이템 %s - %s (전체 %s)" L["Items %s - %s (%s total)"] = "아이템 %s - %s (전체 %s)"
L["Item SubType Filter"] = "아이템 하위 유형 필터" -- Needs review L["Item SubType Filter"] = "아이템 하위 유형 필터" -- Needs review
L["Item Type 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 Scanned"] = "최근 검색"
L["Last updated from in-game scan %s ago."] = "게임 내 검색의 최근 업데이트 %s 전." -- Needs review 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 L["Last updated from the TSM Application %s ago."] = "TSM 애플리케이션의 최근 업데이트 %s 전." -- Needs review
@ -71,7 +66,6 @@ L["Reset Data"] = "데이터 리셋"
L["Resets AuctionDB's scan data"] = "AuctionDB의 검색 데이터 리셋" L["Resets AuctionDB's scan data"] = "AuctionDB의 검색 데이터 리셋"
L["Result Order:"] = "결과 정렬:" -- Needs review L["Result Order:"] = "결과 정렬:" -- Needs review
L["Run Full Scan"] = "전체 검색" L["Run Full Scan"] = "전체 검색"
L["Run GetAll Scan"] = "GetAll 검색"
L["Running query..."] = "쿼리 실행 중..." -- Needs review L["Running query..."] = "쿼리 실행 중..." -- Needs review
L["%s ago"] = "%s 전" L["%s ago"] = "%s 전"
L["Scanning page %s/%s"] = "페이지 검색 %s/%s" -- Needs review L["Scanning page %s/%s"] = "페이지 검색 %s/%s" -- Needs review

6
TradeSkillMaster_AuctionDB/Locale/ptBR.lua

@ -13,14 +13,11 @@
local L = LibStub("AceLocale-3.0"):NewLocale("TradeSkillMaster_AuctionDB", "ptBR") local L = LibStub("AceLocale-3.0"):NewLocale("TradeSkillMaster_AuctionDB", "ptBR")
if not L then return end 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["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["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["Ascending"] = "Crescente"
L["AuctionDB - Market Value"] = "AuctionDB - Valor de Mercado" L["AuctionDB - Market Value"] = "AuctionDB - Valor de Mercado"
L["AuctionDB - Minimum Buyout"] = "AuctionDB - Arremate Mínimo" L["AuctionDB - Minimum Buyout"] = "AuctionDB - Arremate Mínimo"
-- L["Can't run a GetAll scan right now."] = ""
L["Descending"] = "Decrescente" L["Descending"] = "Decrescente"
-- L["Display lowest buyout value seen in the last scan in tooltip."] = "" -- L["Display lowest buyout value seen in the last scan in tooltip."] = ""
-- L["Display market value in tooltip."] = "" -- L["Display market value in tooltip."] = ""
@ -29,7 +26,6 @@ L["Descending"] = "Decrescente"
L["Done Scanning"] = "Escaneamento Completo" 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["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["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["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, 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."] = "" -- L["If checked, the lowest buyout value seen in the last scan of the item will be displayed."] = ""
@ -44,7 +40,6 @@ L["Items per page"] = "Itens por página"
L["Items %s - %s (%s total)"] = "Itens %s - %s (%s no total)" L["Items %s - %s (%s total)"] = "Itens %s - %s (%s no total)"
L["Item SubType Filter"] = "Filtro de SubTipo de Item" L["Item SubType Filter"] = "Filtro de SubTipo de Item"
L["Item Type Filter"] = "Filtro de Tipo 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 Scanned"] = "Escaneado pela última vez"
-- L["Last updated from in-game scan %s ago."] = "" -- L["Last updated from in-game scan %s ago."] = ""
-- L["Last updated from the TSM Application %s ago."] = "" -- L["Last updated from the TSM Application %s ago."] = ""
@ -71,7 +66,6 @@ L["Reset Data"] = "Redefinir Dados"
L["Resets AuctionDB's scan data"] = "Redefine os dados de escaneamento do AuctionDB" L["Resets AuctionDB's scan data"] = "Redefine os dados de escaneamento do AuctionDB"
L["Result Order:"] = "Order de Resultado" L["Result Order:"] = "Order de Resultado"
L["Run Full Scan"] = "Escaneamento Completo" -- Needs review L["Run Full Scan"] = "Escaneamento Completo" -- Needs review
L["Run GetAll Scan"] = "PegaTudo" -- Needs review
-- L["Running query..."] = "" -- L["Running query..."] = ""
L["%s ago"] = "%s atrás" L["%s ago"] = "%s atrás"
L["Scanning page %s/%s"] = "Escaneando página %s/%s" -- Needs review L["Scanning page %s/%s"] = "Escaneando página %s/%s" -- Needs review

6
TradeSkillMaster_AuctionDB/Locale/ruRU.lua

@ -13,14 +13,11 @@
local L = LibStub("AceLocale-3.0"):NewLocale("TradeSkillMaster_AuctionDB", "ruRU") local L = LibStub("AceLocale-3.0"):NewLocale("TradeSkillMaster_AuctionDB", "ruRU")
if not L then return end 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["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["Are you sure you want to clear your AuctionDB data?"] = "Вы действительно хотите очистить базу AuctionDB?"
L["Ascending"] = "Возрастание" L["Ascending"] = "Возрастание"
L["AuctionDB - Market Value"] = "AuctionDB - Рыночная стоимость" L["AuctionDB - Market Value"] = "AuctionDB - Рыночная стоимость"
L["AuctionDB - Minimum Buyout"] = "AuctionDB - Минимальный выкуп" L["AuctionDB - Minimum Buyout"] = "AuctionDB - Минимальный выкуп"
L["Can't run a GetAll scan right now."] = "Сейчас невозможно запустить GetAll скан."
L["Descending"] = "Убывание" L["Descending"] = "Убывание"
-- L["Display lowest buyout value seen in the last scan in tooltip."] = "" -- L["Display lowest buyout value seen in the last scan in tooltip."] = ""
L["Display market value in tooltip."] = "Показывать рыночную цену в подсказке." L["Display market value in tooltip."] = "Показывать рыночную цену в подсказке."
@ -29,7 +26,6 @@ L["Display market value in tooltip."] = "Показывать рыночную
L["Done Scanning"] = "Сканирование завершено" 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["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["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["Hide poor quality items"] = "Скрыть товары низкого качества"
L["If checked, poor quality items won't be shown in the search results."] = "Не показывать товары низкого качества в результатах поиска." 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."] = "Если выбрано, будет показана минимальная цена выкупа товара с последнего скана" L["If checked, the lowest buyout value seen in the last scan of the item will be displayed."] = "Если выбрано, будет показана минимальная цена выкупа товара с последнего скана"
@ -44,7 +40,6 @@ L["Items per page"] = "Товаров на страницу"
L["Items %s - %s (%s total)"] = "Товаров %s - %s (%s всего)" L["Items %s - %s (%s total)"] = "Товаров %s - %s (%s всего)"
L["Item SubType Filter"] = "Фильтр по подтипу товара" L["Item SubType Filter"] = "Фильтр по подтипу товара"
L["Item Type 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 Scanned"] = "Последний скан"
-- L["Last updated from in-game scan %s ago."] = "" -- L["Last updated from in-game scan %s ago."] = ""
-- L["Last updated from the TSM Application %s ago."] = "" -- L["Last updated from the TSM Application %s ago."] = ""
@ -71,7 +66,6 @@ L["Reset Data"] = "Сбросить данные"
L["Resets AuctionDB's scan data"] = "Сбрасывает данные сканирования модуля AuctionDB" L["Resets AuctionDB's scan data"] = "Сбрасывает данные сканирования модуля AuctionDB"
-- L["Result Order:"] = "" -- L["Result Order:"] = ""
L["Run Full Scan"] = "Полный скан" L["Run Full Scan"] = "Полный скан"
L["Run GetAll Scan"] = "GetAll скан"
L["Running query..."] = "Осуществляется запрос..." L["Running query..."] = "Осуществляется запрос..."
L["%s ago"] = "%s назад" L["%s ago"] = "%s назад"
L["Scanning page %s/%s"] = "Сканирование страницы %s/%s" L["Scanning page %s/%s"] = "Сканирование страницы %s/%s"

6
TradeSkillMaster_AuctionDB/Locale/zhCN.lua

@ -13,14 +13,11 @@
local L = LibStub("AceLocale-3.0"):NewLocale("TradeSkillMaster_AuctionDB", "zhCN") local L = LibStub("AceLocale-3.0"):NewLocale("TradeSkillMaster_AuctionDB", "zhCN")
if not L then return end 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["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["Are you sure you want to clear your AuctionDB data?"] = "您确定要清除AuctionDB数据吗?"
L["Ascending"] = "升序" L["Ascending"] = "升序"
L["AuctionDB - Market Value"] = "AuctionDB - 市场价" L["AuctionDB - Market Value"] = "AuctionDB - 市场价"
L["AuctionDB - Minimum Buyout"] = "AuctionDB - 最低一口价" L["AuctionDB - Minimum Buyout"] = "AuctionDB - 最低一口价"
L["Can't run a GetAll scan right now."] = "现在还不能执行快速扫描。"
L["Descending"] = "降序" L["Descending"] = "降序"
L["Display lowest buyout value seen in the last scan in tooltip."] = "在鼠标提示中显示上次扫描的最低一口价。" L["Display lowest buyout value seen in the last scan in tooltip."] = "在鼠标提示中显示上次扫描的最低一口价。"
L["Display market value in tooltip."] = "在鼠标提示中显示市场价。" L["Display market value in tooltip."] = "在鼠标提示中显示市场价。"
@ -29,7 +26,6 @@ L["Display total number of items ever seen in tooltip."] = "在鼠标提示中
L["Done Scanning"] = "完成扫描" 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["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["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["Hide poor quality items"] = "隐藏灰色物品"
L["If checked, poor quality items won't be shown in the search results."] = "如果勾选,灰色物品将不会出现在扫描结果中。" 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."] = "如果勾选,将显示上次扫描的物品最低一口价。" L["If checked, the lowest buyout value seen in the last scan of the item will be displayed."] = "如果勾选,将显示上次扫描的物品最低一口价。"
@ -44,7 +40,6 @@ L["Items per page"] = "每页显示的物品"
L["Items %s - %s (%s total)"] = "物品 %s - %s (总数 %s) " L["Items %s - %s (%s total)"] = "物品 %s - %s (总数 %s) "
L["Item SubType Filter"] = "物品子类型筛选" L["Item SubType Filter"] = "物品子类型筛选"
L["Item Type 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 Scanned"] = "上一次扫描"
L["Last updated from in-game scan %s ago."] = "距离上次游戏内的数据扫描 %s 。" L["Last updated from in-game scan %s ago."] = "距离上次游戏内的数据扫描 %s 。"
L["Last updated from the TSM Application %s ago."] = "距离上次游戏外TSM APP的数据扫描 %s 。" L["Last updated from the TSM Application %s ago."] = "距离上次游戏外TSM APP的数据扫描 %s 。"
@ -71,7 +66,6 @@ L["Reset Data"] = "重置数据"
L["Resets AuctionDB's scan data"] = "重置AuctionDB扫描数据" L["Resets AuctionDB's scan data"] = "重置AuctionDB扫描数据"
L["Result Order:"] = "结果顺序:" L["Result Order:"] = "结果顺序:"
L["Run Full Scan"] = "执行完整扫描" L["Run Full Scan"] = "执行完整扫描"
L["Run GetAll Scan"] = "进行快速扫描"
L["Running query..."] = "运行查询…" L["Running query..."] = "运行查询…"
L["%s ago"] = "%s之前" L["%s ago"] = "%s之前"
L["Scanning page %s/%s"] = "扫描页面 %s/%s" L["Scanning page %s/%s"] = "扫描页面 %s/%s"

6
TradeSkillMaster_AuctionDB/Locale/zhTW.lua

@ -13,14 +13,11 @@
local L = LibStub("AceLocale-3.0"):NewLocale("TradeSkillMaster_AuctionDB", "zhTW") local L = LibStub("AceLocale-3.0"):NewLocale("TradeSkillMaster_AuctionDB", "zhTW")
if not L then return end 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["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["Are you sure you want to clear your AuctionDB data?"] = "你確定要清除AuctionDB資料?"
L["Ascending"] = "遞增" L["Ascending"] = "遞增"
L["AuctionDB - Market Value"] = "AuctionDB - 市場價格" L["AuctionDB - Market Value"] = "AuctionDB - 市場價格"
L["AuctionDB - Minimum Buyout"] = "AuctionDB - 最小直購價" L["AuctionDB - Minimum Buyout"] = "AuctionDB - 最小直購價"
-- L["Can't run a GetAll scan right now."] = ""
L["Descending"] = "遞減" L["Descending"] = "遞減"
-- L["Display lowest buyout value seen in the last scan in tooltip."] = "" -- L["Display lowest buyout value seen in the last scan in tooltip."] = ""
-- L["Display market value in tooltip."] = "" -- L["Display market value in tooltip."] = ""
@ -29,7 +26,6 @@ L["Descending"] = "遞減"
L["Done Scanning"] = "完成掃描" 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["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["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["Hide poor quality items"] = "隱藏低品質物品"
L["If checked, poor quality items won't be shown in the search results."] = "勾選,低品質物㗊將不會顯示在搜尋記錄。" 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."] = "" -- L["If checked, the lowest buyout value seen in the last scan of the item will be displayed."] = ""
@ -44,7 +40,6 @@ L["Items per page"] = "一頁幾項物品"
L["Items %s - %s (%s total)"] = "物品%s - %s (總共:%s)" L["Items %s - %s (%s total)"] = "物品%s - %s (總共:%s)"
L["Item SubType Filter"] = "物品次要類型過濾" L["Item SubType Filter"] = "物品次要類型過濾"
L["Item Type 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 Scanned"] = "最後掃描"
-- L["Last updated from in-game scan %s ago."] = "" -- L["Last updated from in-game scan %s ago."] = ""
-- L["Last updated from the TSM Application %s ago."] = "" -- L["Last updated from the TSM Application %s ago."] = ""
@ -71,7 +66,6 @@ L["Reset Data"] = "重置資料"
L["Resets AuctionDB's scan data"] = "重置AuctionDB的掃描資料" L["Resets AuctionDB's scan data"] = "重置AuctionDB的掃描資料"
L["Result Order:"] = "結果順序:" L["Result Order:"] = "結果順序:"
L["Run Full Scan"] = "執行完整掃描" L["Run Full Scan"] = "執行完整掃描"
L["Run GetAll Scan"] = "執行GetAll掃描"
-- L["Running query..."] = "" -- L["Running query..."] = ""
L["%s ago"] = "%s以前" L["%s ago"] = "%s以前"
-- L["Scanning page %s/%s"] = "" -- L["Scanning page %s/%s"] = ""

471
TradeSkillMaster_AuctionDB/Modules/ChannelSync.lua

@ -0,0 +1,471 @@
-- ------------------------------------------------------------------------------ --
-- 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,14 +20,33 @@ function GUI:Show(frame)
private.startScanContent = private.startScanContent or private:CreateStartScanContent(frame) private.startScanContent = private.startScanContent or private:CreateStartScanContent(frame)
private.startScanContent:Show() 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 end
function GUI:Hide() function GUI:Hide()
private.statusBar:Hide() private.statusBar:Hide()
private.startScanContent: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() TSM.Scan:DoneScanning()
TSMAPI.AuctionScan:StopScan() TSMAPI.AuctionScan:StopScan()
GUI:SetPauseEnabled(false)
GUI:SetPaused(false)
end
end end
function GUI:UpdateStatus(text, major, minor) function GUI:UpdateStatus(text, major, minor)
@ -39,6 +58,20 @@ function GUI:UpdateStatus(text, major, minor)
end end
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) function private:CreateStatusBar(parent)
local frame = TSMAPI.GUI:CreateStatusBar(parent, "TSMAuctionDBStatusBar") local frame = TSMAPI.GUI:CreateStatusBar(parent, "TSMAuctionDBStatusBar")
TSMAPI.GUI:CreateHorizontalLine(frame, -30, parent) TSMAPI.GUI:CreateHorizontalLine(frame, -30, parent)
@ -51,59 +84,29 @@ function private:CreateStartScanContent(parent)
frame:SetAllPoints(parent) frame:SetAllPoints(parent)
frame:Hide() 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) frame.Enable = function(self)
if self.startGetAllButton then self.startGetAllButton:Enable() end
self.startFullScanButton:Enable() self.startFullScanButton:Enable()
self.pauseFullScanButton:Disable()
self.cancelScanButton:Disable()
self.startGroupScanButton:Enable() self.startGroupScanButton:Enable()
self.startInventoryScanButton:Enable()
self.startBagScanButton:Enable()
self.startBankScanButton:Enable()
end end
frame.Disable = function(self) frame.Disable = function(self)
if self.startGetAllButton then self.startGetAllButton:Disable() end
self.startFullScanButton:Disable() 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.startGroupScanButton:Disable()
self.startInventoryScanButton:Disable()
self.startBagScanButton:Disable()
self.startBankScanButton:Disable()
end end
-- Top row: Auto updater. -- Top row: Auto updater.
@ -141,33 +144,8 @@ function private:CreateStartScanContent(parent)
buttonFrame:SetPoint("TOPLEFT", content, "TOPRIGHT", -200, 0) buttonFrame:SetPoint("TOPLEFT", content, "TOPRIGHT", -200, 0)
buttonFrame:SetPoint("BOTTOMRIGHT") buttonFrame:SetPoint("BOTTOMRIGHT")
-- Row: GetAll Scan. -- Row: Full Scan.
-- NOTE: We hide this button if the player has disabled GetAll scans.
local yOffset = -50 local yOffset = -50
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.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 - 50
TSMAPI.GUI:CreateHorizontalLine(buttonFrame, yOffset)
yOffset = yOffset - 20
end
-- Row: Full Scan. -- Row: Full Scan.
local btn = TSMAPI.GUI:CreateButton(buttonFrame, 18) local btn = TSMAPI.GUI:CreateButton(buttonFrame, 18)
@ -176,7 +154,7 @@ function private:CreateStartScanContent(parent)
btn:SetHeight(22) btn:SetHeight(22)
btn:SetScript("OnClick", TSM.Scan.StartFullScan) btn:SetScript("OnClick", TSM.Scan.StartFullScan)
btn:SetText(L["Run Full Scan"]) 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."] 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 frame.startFullScanButton = btn
yOffset = yOffset - 40 yOffset = yOffset - 40
@ -195,6 +173,72 @@ function private:CreateStartScanContent(parent)
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."] 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 frame.startGroupScanButton = btn
yOffset = yOffset - 40
TSMAPI.GUI:CreateHorizontalLine(buttonFrame, yOffset)
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.
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
return frame return frame
end end
@ -208,3 +252,108 @@ function GUI:StartGroupScan()
end end
TSM.Scan:StartGroupScan(items) TSM.Scan:StartGroupScan(items)
end 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,10 +27,8 @@ local function FullScanCallback(event, ...)
-- We're running a "Full Scan" and have received an auction page. -- 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 -- NOTE: These normal per-page scans receive 50 items per page, and will
-- successfully download ALL auctions on private servers, thanks to pagination. -- successfully download ALL auctions on private servers, thanks to pagination.
-- For example, while Warmane's "GetAll" only returns 55000 of 126559 auctions, -- For example, a full scan retrieves all pages of 50 items each,
-- the regular "Full Scan" mode retrieves all 2532 pages of 50 items each, -- meaning that it covers the entire auction list.
-- 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 = ... local page, total = ...
-- Calculate the current page progress and the remainder as floating-point values. -- Calculate the current page progress and the remainder as floating-point values.
@ -161,240 +159,12 @@ local function FullScanCallback(event, ...)
-- NOTE: "SCAN_INTERRUPTED" is from LibAuctionScan-1.0, which isn't used -- NOTE: "SCAN_INTERRUPTED" is from LibAuctionScan-1.0, which isn't used
-- by TSM anymore, and "INTERRUPTED" is from "TSM/Auction/AuctionScanning.lua", -- by TSM anymore, and "INTERRUPTED" is from "TSM/Auction/AuctionScanning.lua",
-- which is what this scanner uses nowadays. -- which is what this scanner uses nowadays.
local data = ...
Scan:ProcessScanData(data)
Scan:DoneScanning() Scan:DoneScanning()
end end
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, ...) local function GroupScanCallback(event, ...)
if event == "QUERY_COMPLETE" then if event == "QUERY_COMPLETE" then
@ -462,12 +232,13 @@ end
function Scan:StartGroupScan(items) function Scan:StartGroupScan(items)
Scan.isScanning = "Group" Scan.isScanning = "Group"
Scan.isBuggedGetAll = nil
Scan.groupItems = items Scan.groupItems = items
wipe(Scan.filterList) wipe(Scan.filterList)
wipe(Scan.groupScanData) wipe(Scan.groupScanData)
Scan.numFilters = 0 Scan.numFilters = 0
TSMAPI.AuctionScan:StopScan() TSMAPI.AuctionScan:StopScan()
TSM.GUI:SetPauseEnabled(true)
TSM.GUI:SetPaused(false)
Scan.groupScanStartTime = time() -- Keep track of when we started the "Group Scan". Scan.groupScanStartTime = time() -- Keep track of when we started the "Group Scan".
TSMAPI:GenerateQueries(items, GroupScanCallback) TSMAPI:GenerateQueries(items, GroupScanCallback)
TSM.GUI:UpdateStatus(L["Preparing Filters..."]) TSM.GUI:UpdateStatus(L["Preparing Filters..."])
@ -476,34 +247,16 @@ end
function Scan:StartFullScan() function Scan:StartFullScan()
Scan.isScanning = "Full" Scan.isScanning = "Full"
TSM.GUI:UpdateStatus(L["Running query..."]) TSM.GUI:UpdateStatus(L["Running query..."])
Scan.isBuggedGetAll = nil
Scan.groupItems = nil Scan.groupItems = nil
TSMAPI.AuctionScan:StopScan() TSMAPI.AuctionScan:StopScan()
TSM.GUI:SetPauseEnabled(true)
TSM.GUI:SetPaused(false)
Scan.fullScanStartTime = time() -- Keep track of when we started the "Full Scan". Scan.fullScanStartTime = time() -- Keep track of when we started the "Full Scan".
Scan.fullScanSecondsPerPage = -1 -- Reset the page-speed timer. Scan.fullScanSecondsPerPage = -1 -- Reset the page-speed timer.
Scan.fullScanCompleteElapsed = nil -- Reset the "full scan completed" information. Scan.fullScanCompleteElapsed = nil -- Reset the "full scan completed" information.
TSMAPI.AuctionScan:RunQuery({name=""}, FullScanCallback) TSMAPI.AuctionScan:RunQuery({name=""}, FullScanCallback)
end 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) function Scan:DoneScanning(seconds_elapsed)
if seconds_elapsed then if seconds_elapsed then
-- If given the "time elapsed", display it as "Done Scanning (1:35:27)". -- If given the "time elapsed", display it as "Done Scanning (1:35:27)".
@ -513,12 +266,40 @@ function Scan:DoneScanning(seconds_elapsed)
TSM.GUI:UpdateStatus(L["Done Scanning"], 100) TSM.GUI:UpdateStatus(L["Done Scanning"], 100)
end end
Scan.isScanning = nil Scan.isScanning = nil
Scan.getAllLoaded = 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
end end
function Scan:ProcessScanData(scanData) function Scan:ProcessScanData(scanData)
-- Handle scans performed via "Full Scan" and "Group Scan", but not "GetAll". -- Handle scans performed via "Full Scan" and "Group Scan".
-- NOTE: See "Scan.ProcessGetAllScan()" for full explanation of this algorithm.
local data = {} local data = {}
for itemString, obj in pairs(scanData) do for itemString, obj in pairs(scanData) do
@ -556,8 +337,7 @@ function Scan:ProcessScanData(scanData)
-- Add this item to "data to process" even if there's zero records, -- Add this item to "data to process" even if there's zero records,
-- which can happen if they're all bid-only auctions. -- which can happen if they're all bid-only auctions.
-- NOTE: This differs from the behavior of "ProcessGetAllScan", which -- NOTE: Empty records are fine; ProcessData ignores items without buyout prices.
-- only adds items that have at least 1 record with a buyout value.
-- NOTE: Empty records are totally fine either way, since "ProcessData" -- NOTE: Empty records are totally fine either way, since "ProcessData"
-- simply ignores items that don't contain any buyout prices. -- simply ignores items that don't contain any buyout prices.
-- NOTE: If no buyout records were found, the "minBuyout" and "quantity" -- NOTE: If no buyout records were found, the "minBuyout" and "quantity"
@ -568,12 +348,15 @@ function Scan:ProcessScanData(scanData)
-- Mark the collected auction data as a new "complete scan" with today's date, -- 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). -- 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() TSM.db.realm.lastCompleteScan = time()
end end
-- Process the collected auction data. -- Process the collected auction data.
TSM.Data:ProcessData(data, Scan.groupItems, verifyNewAlgorithm) TSM.Data:ProcessData(data, Scan.groupItems, verifyNewAlgorithm)
if TSM.ChannelSync then
TSM.ChannelSync:BroadcastScanData(Scan.isScanning, Scan.groupItems)
end
end end
function Scan:ProcessImportedData(auctionData) function Scan:ProcessImportedData(auctionData)

61
TradeSkillMaster_AuctionDB/Modules/config.lua

@ -390,12 +390,67 @@ function Config:LoadOptions(container)
relativeWidth = 0.5, 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."], 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", type = "CheckBox",
label = L["Disable \"GetAll\" Auction Scans"], label = L["Enable channel sync"],
settingInfo = { TSM.db.profile, "disableGetAll" }, 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" },
relativeWidth = 0.5, relativeWidth = 0.5,
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."], 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."],
}, },
}, },
}, },

73
TradeSkillMaster_AuctionDB/Modules/data.lua

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

34
TradeSkillMaster_AuctionDB/TradeSkillMaster_AuctionDB.lua

@ -15,6 +15,8 @@ local L = LibStub("AceLocale-3.0"):GetLocale("TradeSkillMaster_AuctionDB") -- lo
TSM.MAX_AVG_DAY = 1 TSM.MAX_AVG_DAY = 1
local SECONDS_PER_DAY = 60 * 60 * 24 local SECONDS_PER_DAY = 60 * 60 * 24
local eventObj
local private = {}
local savedDBDefaults = { local savedDBDefaults = {
realm = { realm = {
@ -34,7 +36,10 @@ local savedDBDefaults = {
marketValueTooltip = true, marketValueTooltip = true,
minBuyoutTooltip = true, minBuyoutTooltip = true,
showAHTab = true, showAHTab = true,
disableGetAll = false, channelSyncEnabled = true,
channelSyncName = "TSM_AuctionDB",
channelSyncReceiveOnly = false,
channelSyncDebug = false,
}, },
} }
@ -75,6 +80,7 @@ function TSM:RegisterModule()
{ key = "lastCompleteScan", callback = TSM.GetLastCompleteScan }, { key = "lastCompleteScan", callback = TSM.GetLastCompleteScan },
{ key = "lastCompleteScanTime", callback = TSM.GetLastCompleteScanTime }, { key = "lastCompleteScanTime", callback = TSM.GetLastCompleteScanTime },
{ key = "adbScans", callback = TSM.GetScans }, { key = "adbScans", callback = TSM.GetScans },
{ key = "processScanData", callback = "Data:ProcessExternalScanData" },
--{ key = "adbOppositeFaction", callback = TSM.GetOppositeFactionData }, --{ key = "adbOppositeFaction", callback = TSM.GetOppositeFactionData },
} }
TSM.tooltipOptions = {callback = "Config:LoadTooltipOptions"} TSM.tooltipOptions = {callback = "Config:LoadTooltipOptions"}
@ -223,6 +229,32 @@ function TSM:OnEnable()
end end
TSM:LoadAuctionData() 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 end
function TSM:OnTSMDBShutdown() function TSM:OnTSMDBShutdown()

5
TradeSkillMaster_AuctionDB/TradeSkillMaster_AuctionDB.toc

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

1
TradeSkillMaster_Auctioning/TradeSkillMaster_Auctioning.lua

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

6
TradeSkillMaster_Auctioning/locale/enUS.lua

@ -100,6 +100,7 @@ L["Duration"] = true
L["Edit Post Price"] = true L["Edit Post Price"] = true
L["Enable Reset Scan"] = true L["Enable Reset Scan"] = true
L["Enable Sounds"] = true L["Enable Sounds"] = true
L["Flash Client Icon"] = true
L["Error creating operation. Operation with name '%s' already exists."] = true L["Error creating operation. Operation with name '%s' already exists."] = true
L["General Operation Options"] = true L["General Operation Options"] = true
L["General Options"] = true L["General Options"] = true
@ -121,6 +122,7 @@ 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 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, 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, 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, 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 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 L["If enabled, instead of not posting when a whitelisted player has an auction posted, Auctioning will match their price."] = true
@ -184,6 +186,7 @@ L["Operation"] = true
L["Operations"] = true L["Operations"] = true
L["Options"] = true L["Options"] = true
L["Other Auctioning Searches"] = 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["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["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 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
@ -217,13 +220,16 @@ L["Reset Scan Finished"] = true
L["Reset Settings"] = true L["Reset Settings"] = true
L["Reset"] = true L["Reset"] = true
L["Resetting enabled."] = true L["Resetting enabled."] = true
L["Resume"] = true
L["Restart"] = true L["Restart"] = true
L["Return to Summary"] = true L["Return to Summary"] = true
L["Right-Click to add %s to your friends list."] = true L["Right-Click to add %s to your friends list."] = true
L["Round Normal Price"] = true L["Round Normal Price"] = true
L["Resuming Scan..."] = true
L["Running Scan..."] = true L["Running Scan..."] = true
L["Save New Price"] = true L["Save New Price"] = true
L["Scan Complete!"] = true L["Scan Complete!"] = true
L["Scan Paused"] = true
L["Scanning %d / %d (Page 1 / ?)"] = true L["Scanning %d / %d (Page 1 / ?)"] = true
L["Scanning %d / %d (Page %d / %d)"] = true L["Scanning %d / %d (Page %d / %d)"] = true
L["Scanning %d / %d"] = true L["Scanning %d / %d"] = true

51
TradeSkillMaster_Auctioning/modules/GUI.lua

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

12
TradeSkillMaster_Auctioning/modules/Options.lua

@ -107,6 +107,12 @@ function Options:DrawGeneralSettings(container)
settingInfo = { TSM.db.global, "scanCompleteSound" }, 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"], 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", type = "Button",
text = L["Test Selected Sound"], text = L["Test Selected Sound"],
@ -542,7 +548,7 @@ function Options:DrawOperationPost(container, operationName)
type = "EditBox", type = "EditBox",
label = L["Minimum Price"], label = L["Minimum Price"],
settingInfo = { operation, "minPrice" }, settingInfo = { operation, "minPrice" },
relativeWidth = 0.49, relativeWidth = 1,
acceptCustom = true, acceptCustom = true,
disabled = operation.relationships.minPrice, 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."], tooltip = L["The lowest price you want an item to be posted for. Auctioning will not undercut auctions below this price."],
@ -560,7 +566,7 @@ function Options:DrawOperationPost(container, operationName)
type = "EditBox", type = "EditBox",
label = L["Maximum Price"], label = L["Maximum Price"],
settingInfo = { operation, "maxPrice" }, settingInfo = { operation, "maxPrice" },
relativeWidth = 0.49, relativeWidth = 1,
acceptCustom = true, acceptCustom = true,
disabled = operation.relationships.maxPrice, 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."], tooltip = L["The maximum price you want an item to be posted for. Auctioning will not undercut auctions above this price."],
@ -578,7 +584,7 @@ function Options:DrawOperationPost(container, operationName)
type = "EditBox", type = "EditBox",
label = L["Normal Price"], label = L["Normal Price"],
settingInfo = { operation, "normalPrice" }, settingInfo = { operation, "normalPrice" },
relativeWidth = 0.49, relativeWidth = 1,
acceptCustom = true, acceptCustom = true,
disabled = operation.relationships.normalPrice, disabled = operation.relationships.normalPrice,
tooltip = L["Price to post at if there are none of an item currently on the AH."], tooltip = L["Price to post at if there are none of an item currently on the AH."],

36
TradeSkillMaster_Auctioning/modules/PostScan.lua

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

10
TradeSkillMaster_Auctioning/modules/ResetScan.lua

@ -14,6 +14,7 @@ local resetData, summarySTCache, showCache, itemsReset, justBought = {}, {}, {},
local isScanning, doneScanningText, currentItem, GUI local isScanning, doneScanningText, currentItem, GUI
local summaryST, auctionST, resetButtons local summaryST, auctionST, resetButtons
local currentAuction local currentAuction
local pendingBuyout
function Reset:Show(frame) function Reset:Show(frame)
summaryST = summaryST or Reset:CreateSummaryST(frame.content) summaryST = summaryST or Reset:CreateSummaryST(frame.content)
@ -604,6 +605,10 @@ function Reset:RemoveCurrentAuction()
scanData:RemoveRecord(row.index) scanData:RemoveRecord(row.index)
itemsReset[row.itemString] = true itemsReset[row.itemString] = true
if pendingBuyout then
TSMAPI:FireEvent("TSM:AUCTIONCONTROL:ITEMBOUGHT", pendingBuyout)
pendingBuyout = nil
end
Reset:UpdateAuctionST() Reset:UpdateAuctionST()
if #auctionST.rowData == 0 then if #auctionST.rowData == 0 then
@ -697,6 +702,11 @@ function Reset:BuyAuction()
PlaceAuctionBid("list", mainIndex or altIndex, currentAuction.buyout) PlaceAuctionBid("list", mainIndex or altIndex, currentAuction.buyout)
foundAuction = true foundAuction = true
justBought[mainIndex or altIndex] = true justBought[mainIndex or altIndex] = true
pendingBuyout = {
itemString = currentAuction.itemString,
count = currentAuction.count,
buyout = currentAuction.buyout,
}
end end
resetButtons.buyout:Disable() resetButtons.buyout:Disable()

18
TradeSkillMaster_Auctioning/modules/ScanUtil.lua

@ -12,6 +12,21 @@ local L = LibStub("AceLocale-3.0"):GetLocale("TradeSkillMaster_Auctioning") -- l
Scan.auctionData = {} Scan.auctionData = {}
Scan.skipped = {} 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, ...) local function CallbackHandler(event, ...)
@ -62,6 +77,8 @@ end
function Scan:StartItemScan(itemList) function Scan:StartItemScan(itemList)
wipe(Scan.auctionData) wipe(Scan.auctionData)
wipe(Scan.skipped) wipe(Scan.skipped)
Scan.auctionDataHash = nil
Scan.auctionDataCount = 0
TSMAPI:GenerateQueries(itemList, CallbackHandler) TSMAPI:GenerateQueries(itemList, CallbackHandler)
TSM.Manage:UpdateStatus("query", 0, -1) TSM.Manage:UpdateStatus("query", 0, -1)
end end
@ -79,6 +96,7 @@ function Scan:ScanNextFilter()
-- NOTE: Our scan progress counter below starts counting from 0 as the first item. -- NOTE: Our scan progress counter below starts counting from 0 as the first item.
if #Scan.filterList == 0 then if #Scan.filterList == 0 then
TSM.Manage:UpdateStatus("scan", Scan.numFilters, Scan.numFilters) TSM.Manage:UpdateStatus("scan", Scan.numFilters, Scan.numFilters)
Scan.auctionDataHash, Scan.auctionDataCount = HashAuctionData(Scan.auctionData)
return TSM.Manage:ScanComplete() return TSM.Manage:ScanComplete()
end end
TSM.Manage:UpdateStatus("scan", Scan.numFilters-#Scan.filterList, Scan.numFilters) TSM.Manage:UpdateStatus("scan", Scan.numFilters-#Scan.filterList, Scan.numFilters)

21
TradeSkillMaster_Auctioning/modules/manage.lua

@ -72,9 +72,25 @@ function Manage:OnGUIEvent(event)
end end
elseif event == "skip" then elseif event == "skip" then
Util:SkipItem() 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 elseif event == "stop" then
TSMAPI:CancelFrame("auctioningNoScanProcessing") TSMAPI:CancelFrame("auctioningNoScanProcessing")
TSMAPI.AuctionScan:StopScan() TSMAPI.AuctionScan:StopScan()
GUI.buttons:SetPaused(false)
GUI.buttons.pause:Disable()
Util:Stop() Util:Stop()
end end
TSMAPI:CreateTimeDelay("aucManageSTUpdate", 0.01, GUI.UpdateAuctionsSTData) TSMAPI:CreateTimeDelay("aucManageSTUpdate", 0.01, GUI.UpdateAuctionsSTData)
@ -88,6 +104,8 @@ function Manage:ProcessScannedItem(itemString, noUpdate)
end end
function Manage:ScanComplete(interrupted) function Manage:ScanComplete(interrupted)
GUI.buttons:SetPaused(false)
GUI.buttons.pause:Disable()
if interrupted then if interrupted then
-- If our scan has been interrupted by the Auction House closing, -- If our scan has been interrupted by the Auction House closing,
-- simply act as if the user clicked "Stop", but with an extra flag -- simply act as if the user clicked "Stop", but with an extra flag
@ -101,6 +119,9 @@ function Manage:ScanComplete(interrupted)
elseif TSM.db.global.scanCompleteSound ~= 1 then elseif TSM.db.global.scanCompleteSound ~= 1 then
PlaySound(TSM.Options:GetScanCompleteSound(TSM.db.global.scanCompleteSound), "Master") PlaySound(TSM.Options:GetScanCompleteSound(TSM.db.global.scanCompleteSound), "Master")
end end
if TSM.db.global.flashClientOnComplete then
FlashClientIcon()
end
end end
end end

3
TradeSkillMaster_Crafting/Locale/enUS.lua

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

34
TradeSkillMaster_Crafting/Modules/Gather.lua

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

34
TradeSkillMaster_Crafting/Modules/Inventory.lua

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

24
TradeSkillMaster_Crafting/Modules/Queue.lua

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

6
TradeSkillMaster_Crafting/TradeSkillMaster_Crafting.lua

@ -29,12 +29,16 @@ local savedDBDefaults = {
frameQueueOpen = nil, frameQueueOpen = nil,
showingDefaultFrame = nil, showingDefaultFrame = nil,
matsInTooltip = true, matsInTooltip = true,
showAllProfessions = false,
showAllProfessionsSortKey = "name",
showAllProfessionsSortDescending = false,
}, },
realm = { realm = {
tradeSkills = {}, tradeSkills = {},
crafts = {}, crafts = {},
mats = {}, mats = {},
queueStatus = { collapsed = {} }, queueStatus = { collapsed = {} },
professionsTabCollapsed = {},
sourceStatus = { collapsed = {} }, sourceStatus = { collapsed = {} },
gathering = { crafter = nil, professions = {}, neededMats = {}, availableMats = {}, gatheredMats = false, gatherAll = false, destroyingMats = {}, destroyDisable = false, evenStacks = true }, gathering = { crafter = nil, professions = {}, neededMats = {}, availableMats = {}, gatheredMats = false, gatherAll = false, destroyingMats = {}, destroyDisable = false, evenStacks = true },
craftingCostCache = {} craftingCostCache = {}
@ -101,7 +105,7 @@ end
-- registers this module with TSM by first setting all fields and then calling TSMAPI:NewModule(). -- registers this module with TSM by first setting all fields and then calling TSMAPI:NewModule().
function TSM:RegisterModule() function TSM:RegisterModule()
TSM.icons = { { side = "module", desc = "Crafting", slashCommand = "crafting", callback = "Options:LoadCrafting", icon = "Interface\\Icons\\INV_Misc_Gear_08" } } TSM.icons = { { side = "module", desc = "Crafting", slashCommand = "crafting", callback = "Options:LoadCrafting", icon = "Interface\\Icons\\INV_Misc_Gear_08" } }
TSM.operations = { maxOperations = 1, callbackOptions = "Options:Load", callbackInfo = "GetOperationInfo" } TSM.operations = { maxOperations = 5, callbackOptions = "Options:Load", callbackInfo = "GetOperationInfo" }
TSM.priceSources = { TSM.priceSources = {
{ key = "Crafting", label = L["Crafting Cost"], callback = "GetCraftingCost" }, { key = "Crafting", label = L["Crafting Cost"], callback = "GetCraftingCost" },
{ key = "matPrice", label = L["Crafting Material Cost"], callback = "GetCraftingMatCost" }, { 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({}) st:SetData({})
frame.st = st frame.st = st
local btn = TSMAPI.GUI:CreateButton(frame, 18) local btn = TSMAPI.GUI:CreateButton(frame, 18, "TSMMailingOpenAllButton")
btn:SetPoint("BOTTOMLEFT", 5, 30) btn:SetPoint("BOTTOMLEFT", 5, 30)
btn:SetPoint("BOTTOMRIGHT", -5, 30) btn:SetPoint("BOTTOMRIGHT", -5, 30)
btn:SetHeight(20) btn:SetHeight(20)
@ -91,7 +91,7 @@ function Inbox:CreateTab(parent)
local btnWidth = (frame:GetWidth() - label:GetWidth() - 25) / 5 local btnWidth = (frame:GetWidth() - label:GetWidth() - 25) / 5
local btn = TSMAPI.GUI:CreateButton(frame, 18) local btn = TSMAPI.GUI:CreateButton(frame, 18, "TSMMailingSalesButton")
btn:SetPoint("BOTTOMLEFT", label, "BOTTOMRIGHT", 5, 0) btn:SetPoint("BOTTOMLEFT", label, "BOTTOMRIGHT", 5, 0)
btn:SetWidth(btnWidth) btn:SetWidth(btnWidth)
btn:SetHeight(20) btn:SetHeight(20)
@ -99,7 +99,7 @@ function Inbox:CreateTab(parent)
btn:SetScript("OnClick", function() private:StartAutoLooting("sales") end) btn:SetScript("OnClick", function() private:StartAutoLooting("sales") end)
frame.salesBtn = btn frame.salesBtn = btn
local btn = TSMAPI.GUI:CreateButton(frame, 18) local btn = TSMAPI.GUI:CreateButton(frame, 18, "TSMMailingBuysButton")
btn:SetPoint("BOTTOMLEFT", frame.salesBtn, "BOTTOMRIGHT", 5, 0) btn:SetPoint("BOTTOMLEFT", frame.salesBtn, "BOTTOMRIGHT", 5, 0)
btn:SetWidth(btnWidth) btn:SetWidth(btnWidth)
btn:SetHeight(20) btn:SetHeight(20)
@ -107,7 +107,7 @@ function Inbox:CreateTab(parent)
btn:SetScript("OnClick", function() private:StartAutoLooting("buys") end) btn:SetScript("OnClick", function() private:StartAutoLooting("buys") end)
frame.buysBtn = btn frame.buysBtn = btn
local btn = TSMAPI.GUI:CreateButton(frame, 18) local btn = TSMAPI.GUI:CreateButton(frame, 18, "TSMMailingCancelsButton")
btn:SetPoint("BOTTOMLEFT", frame.buysBtn, "BOTTOMRIGHT", 5, 0) btn:SetPoint("BOTTOMLEFT", frame.buysBtn, "BOTTOMRIGHT", 5, 0)
btn:SetWidth(btnWidth) btn:SetWidth(btnWidth)
btn:SetHeight(20) btn:SetHeight(20)
@ -115,7 +115,7 @@ function Inbox:CreateTab(parent)
btn:SetScript("OnClick", function() private:StartAutoLooting("cancels") end) btn:SetScript("OnClick", function() private:StartAutoLooting("cancels") end)
frame.cancelsBtn = btn frame.cancelsBtn = btn
local btn = TSMAPI.GUI:CreateButton(frame, 18) local btn = TSMAPI.GUI:CreateButton(frame, 18, "TSMMailingExpiresButton")
btn:SetPoint("BOTTOMLEFT", frame.cancelsBtn, "BOTTOMRIGHT", 5, 0) btn:SetPoint("BOTTOMLEFT", frame.cancelsBtn, "BOTTOMRIGHT", 5, 0)
btn:SetWidth(btnWidth) btn:SetWidth(btnWidth)
btn:SetHeight(20) btn:SetHeight(20)
@ -247,7 +247,7 @@ function private:UpdateTopLabel()
end end
function private:InboxUpdate() function private:InboxUpdate()
if not private.frame or not private.frame:IsVisible() then return end if not private.frame or (not private.frame:IsVisible() and private.frame.buttonsEnabled) then return end
TSMAPI:CancelFrame("inboxLootTextDelay") TSMAPI:CancelFrame("inboxLootTextDelay")
local numMail, totalMail = GetInboxNumItems() local numMail, totalMail = GetInboxNumItems()
@ -409,6 +409,7 @@ end
-- Deals with auto looting of mail! -- Deals with auto looting of mail!
function private:StartAutoLooting(mode) function private:StartAutoLooting(mode)
if not MailFrame or not MailFrame:IsShown() then return end
private.mode = mode private.mode = mode
local canCollectMail local canCollectMail
if private.mode == "all" then if private.mode == "all" then
@ -436,8 +437,130 @@ function private:StartAutoLooting(mode)
private:AutoLoot() private:AutoLoot()
end 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() function private:AutoLoot()
TSMAPI:CancelFrame("mailSkipDelay") 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 -- Already looted everything after the invalid indexes we had, so fail it
if private.lootIndex > 1 and private.lootIndex > GetInboxNumItems() then if private.lootIndex > 1 and private.lootIndex > GetInboxNumItems() then
@ -489,6 +612,10 @@ function private:AutoLoot()
end end
function private:LootMailItem(index) function private:LootMailItem(index)
if not MailFrame or not MailFrame:IsShown() then
private:StopAutoLooting()
return
end
if TSM.db.global.inboxMessages then if TSM.db.global.inboxMessages then
--local _, _, sender, subject, money, cod, _, hasItem = GetInboxHeaderInfo(index) --local _, _, sender, subject, money, cod, _, hasItem = GetInboxHeaderInfo(index)
local _, _, sender, subject, money, cod, daysLeft, hasItem, _, _, _, _, _, itemQuantity = GetInboxHeaderInfo(index) local _, _, sender, subject, money, cod, daysLeft, hasItem, _, _, _, _, _, itemQuantity = GetInboxHeaderInfo(index)
@ -665,6 +792,7 @@ function private:StopAutoLooting(failed)
private.autoLootTotal = nil private.autoLootTotal = nil
if not private.frame then return end if not private.frame then return end
private.frame:EnableButtons() 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 --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 if private.moneyCollected and private.moneyCollected > 0 and TSM.db.global.displayMoneyCollected then

1
TradeSkillMaster_Mailing/TradeSkillMaster_Mailing.lua

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

2
TradeSkillMaster_Shopping/Locale/enUS.lua

@ -81,6 +81,8 @@ L["Max Disenchant Level"] = true
L["Max Disenchant Search Percent"] = true L["Max Disenchant Search Percent"] = true
L["Max Shopping Price:"] = true L["Max Shopping Price:"] = true
L["Maximum Auction Price (per item)"] = 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 to Buy:"] = true
L["Maximum quantity purchased for %s."] = true L["Maximum quantity purchased for %s."] = true
L["Maximum quantity purchased for destroy search."] = 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(). -- registers this module with TSM by first setting all fields and then calling TSMAPI:NewModule().
function TSM:RegisterModule() function TSM:RegisterModule()
TSM.operations = { maxOperations = 1, callbackOptions = "Options:Load", callbackInfo = "GetOperationInfo" } TSM.operations = { maxOperations = 5, callbackOptions = "Options:Load", callbackInfo = "GetOperationInfo" }
TSM.auctionTab = { callbackShow = "Search:Show", callbackHide = "Search:Hide" } TSM.auctionTab = { callbackShow = "Search:Show", callbackHide = "Search:Hide" }
TSM.tooltipOptions = { callback = "Options:LoadTooltipOptions" } TSM.tooltipOptions = { callback = "Options:LoadTooltipOptions" }
TSM.moduleAPIs = { TSM.moduleAPIs = {
@ -68,6 +68,7 @@ end
TSM.operationDefaults = { TSM.operationDefaults = {
maxPrice = 1, maxPrice = 1,
maxRestock = 0,
evenStacks = nil, evenStacks = nil,
showAboveMaxPrice = nil, showAboveMaxPrice = nil,
ignorePlayer = {}, ignorePlayer = {},
@ -94,6 +95,7 @@ end
function TSM:StartFilterSearch(searchQuery, callback) function TSM:StartFilterSearch(searchQuery, callback)
if not TSMAPI:AHTabIsVisible("Shopping") then return end if not TSMAPI:AHTabIsVisible("Shopping") then return end
TSM.Search:StartFilterSearch(searchQuery, nil, true) TSM.Search:StartFilterSearch(searchQuery, nil, true)
TSM.shoppingGroupSearchActive = nil
TSM.moduleAPICallback = callback TSM.moduleAPICallback = callback
end end
@ -125,6 +127,19 @@ function TSM:GetMaxPrice(operationPrice, itemString)
return price ~= 0 and price or nil, err return price ~= 0 and price or nil, err
end 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(...) function TSM:AddSidebarFeature(...)
TSM.modules.Sidebar:AddSidebarFeature(...) TSM.modules.Sidebar:AddSidebarFeature(...)
end end
@ -136,11 +151,24 @@ function TSM:GetTooltip(itemString)
itemString = TSMAPI:GetBaseItemString(itemString, true) itemString = TSMAPI:GetBaseItemString(itemString, true)
local operations = TSMAPI:GetItemOperation(itemString, "Shopping") local operations = TSMAPI:GetItemOperation(itemString, "Shopping")
if not operations then return end if not operations then return end
local operationName = operations[1] local maxPrice
local totalQty = TSM:GetTotalQuantity(itemString) or 0
for _, operationName in ipairs(operations) do
TSMAPI:UpdateOperation("Shopping", operationName) TSMAPI:UpdateOperation("Shopping", operationName)
local operation = TSM.operations[operationName] local operation = TSM.operations[operationName]
if operation then if operation then
local maxPrice = TSM:GetMaxPrice(operation.maxPrice, itemString) 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
if maxPrice then if maxPrice then
local priceText local priceText
if moneyCoinsTooltip then if moneyCoinsTooltip then
@ -150,7 +178,6 @@ function TSM:GetTooltip(itemString)
end end
tinsert(text, { left = " " .. L["Max Shopping Price:"], right = format("%s", priceText) }) tinsert(text, { left = " " .. L["Max Shopping Price:"], right = format("%s", priceText) })
end end
end
if #text > 0 then if #text > 0 then
tinsert(text, 1, "|cffffff00" .. "TSM Shopping:") tinsert(text, 1, "|cffffff00" .. "TSM Shopping:")
return text return text

2
TradeSkillMaster_Shopping/modules/Destroying.lua

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

13
TradeSkillMaster_Shopping/modules/Options.lua

@ -305,6 +305,18 @@ function Options:DrawOperationGeneral(container, operationName)
disabled = operation.relationships.maxprice, disabled = operation.relationships.maxprice,
tooltip = L["The highest price per item you will pay for items in affected by this operation."], 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", type = "CheckBox",
label = L["Show Auctions Above Max Price"], label = L["Show Auctions Above Max Price"],
@ -333,6 +345,7 @@ function Options:DrawOperationRelationships(container, operationName)
{ {
label = L["General Settings"], label = L["General Settings"],
{ key = "maxPrice", label = L["Maximum Auction Price (per item)"] }, { 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 = "showAboveMaxPrice", label = L["Show Auctions Above Max Price"] },
{ key = "evenStacks", label = L["Even (5/10/15/20) Stacks Only"] }, { 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() function Search:Hide()
if not private.searchBar then return end if not private.searchBar then return end
private.searchBar:Hide() private.searchBar:Hide()
TSM.Util:HideSearchFrame() TSM.Util:HideSearchFrame(false)
TSMAPI.AuctionControl:HideControlButtons() TSMAPI.AuctionControl:HideControlButtons()
TSMAPI.AuctionScan:StopScan() TSMAPI.AuctionScan:StopScan(false)
TSM.Sidebar:Hide() TSM.Sidebar:Hide()
end end
@ -325,6 +325,7 @@ end
function Search:StartFilterSearch(filter, callback, isCrafting) function Search:StartFilterSearch(filter, callback, isCrafting)
TSM.isCrafting = isCrafting TSM.isCrafting = isCrafting
TSM.searchCallback = callback 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 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 filter = TSMAPI:GetSafeItemInfo(filter) or filter
end end
@ -524,7 +525,7 @@ local function GetSearchFilterOptions(searchTerm)
minILevel = oldMaxILevel minILevel = oldMaxILevel
end 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 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 -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 0, 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 end

29
TradeSkillMaster_Shopping/modules/Util.lua

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

169
TradeSkillMaster_Shopping/sidebar/Groups.lua

@ -1,7 +1,69 @@
local TSM = select(2, ...) local TSM = select(2, ...)
local L = LibStub("AceLocale-3.0"):GetLocale("TradeSkillMaster_Shopping") -- loads the localization table local L = LibStub("AceLocale-3.0"):GetLocale("TradeSkillMaster_Shopping") -- loads the localization table
local private = {itemOperations={}} 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
function private.Create(parent) function private.Create(parent)
local frame = CreateFrame("Frame", nil, parent) local frame = CreateFrame("Frame", nil, parent)
@ -39,33 +101,63 @@ function private.ScanCallback(event, ...)
local filter = ... local filter = ...
local maxPrice local maxPrice
for _, itemString in ipairs(filter.items) do for _, itemString in ipairs(filter.items) do
local operation = private.itemOperations[itemString] local operations = GetActiveOperations(itemString, private.itemOperations[itemString])
local operationPrice = TSM:GetMaxPrice(operation.maxPrice, itemString) if not operations or #operations == 0 then return end
if not operationPrice then return end local itemMaxPrice
if operation.showAboveMaxPrice then 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
maxPrice = nil maxPrice = nil
break break
end end
maxPrice = maxPrice and max(maxPrice, operationPrice) or operationPrice if not itemMaxPrice then return end
maxPrice = maxPrice and max(maxPrice, itemMaxPrice) or itemMaxPrice
end end
return maxPrice return maxPrice
elseif event == "process" then elseif event == "process" then
local itemString, auctionItem = ... local itemString, auctionItem = ...
-- filter out auctions according to operation settings -- filter out auctions according to operation settings
itemString = TSMAPI:GetBaseItemString(itemString, true) itemString = TSMAPI:GetBaseItemString(itemString, true)
local operation = private.itemOperations[itemString] local operations = GetActiveOperations(itemString, private.itemOperations[itemString])
if not operation then return end if not operations or #operations == 0 then return end
local operationPrice = TSM:GetMaxPrice(operation.maxPrice, itemString) local totalQty = GetCachedItemQuantity(itemString)
if not operationPrice then return end
auctionItem:FilterRecords(function(record) auctionItem:FilterRecords(function(record)
if operation.evenStacks and record.count % 5 ~= 0 then local buyout = record:GetItemBuyout() or 0
return true 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 not operation.showAboveMaxPrice then
return (record:GetItemBuyout() or 0) > operationPrice
end end
return true
end) end)
auctionItem:SetMarketValue(operationPrice) 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
end
auctionItem:SetMarketValue(marketValue)
return auctionItem return auctionItem
elseif event == "done" then elseif event == "done" then
TSM.Search:SetSearchBarDisabled(false) TSM.Search:SetSearchBarDisabled(false)
@ -75,7 +167,46 @@ end
function private.StartScan() function private.StartScan()
TSMAPI:FireEvent("SHOPPING:GROUPS:STARTSCAN") TSMAPI:FireEvent("SHOPPING:GROUPS:STARTSCAN")
TSM.shoppingGroupSearchActive = true
wipe(private.itemOperations) 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 for groupName, data in pairs(private.groupTree:GetSelectedGroupInfo()) do
groupName = TSMAPI:FormatGroupPath(groupName, true) groupName = TSMAPI:FormatGroupPath(groupName, true)
for _, opName in ipairs(data.operations) do for _, opName in ipairs(data.operations) do
@ -87,11 +218,17 @@ function private.StartScan()
else else
-- it's a valid operation -- it's a valid operation
for itemString in pairs(data.items) do for itemString in pairs(data.items) do
local baseItemString = TSMAPI:GetBaseItemString(itemString, true) or itemString
local _, err = TSM:GetMaxPrice(opSettings.maxPrice, itemString) local _, err = TSM:GetMaxPrice(opSettings.maxPrice, itemString)
if err then if err then
TSM:Printf(L["Invalid custom price source for %s. %s"], TSMAPI:GetSafeItemInfo(itemString) or itemString, err) TSM:Printf(L["Invalid custom price source for %s. %s"], TSMAPI:GetSafeItemInfo(itemString) or itemString, err)
else else
private.itemOperations[itemString] = opSettings 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
end end
end end
end end

50
TradeSkillMaster_Shopping/sidebar/Other.lua

@ -1,7 +1,37 @@
local TSM = select(2, ...) local TSM = select(2, ...)
local L = LibStub("AceLocale-3.0"):GetLocale("TradeSkillMaster_Shopping") -- loads the localization table local L = LibStub("AceLocale-3.0"):GetLocale("TradeSkillMaster_Shopping") -- loads the localization table
local private = {} 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
function private.Create(parent) function private.Create(parent)
local frame = CreateFrame("Frame", nil, parent) local frame = CreateFrame("Frame", nil, parent)
@ -210,6 +240,9 @@ end
function private:StartSniperSearch() function private:StartSniperSearch()
private.sniperItemQuantities = {}
private.sniperOperationSettings = {}
private.sniperItemMaxPrices = {}
TSM.Util:ShowSearchFrame(nil, L["% Market Value"]) TSM.Util:ShowSearchFrame(nil, L["% Market Value"])
TSM.Search:SetSearchBarDisabled(true) TSM.Search:SetSearchBarDisabled(true)
TSM.Util:StartLastPageScan(private.SniperScanCallback) TSM.Util:StartLastPageScan(private.SniperScanCallback)
@ -230,9 +263,20 @@ function private.SniperScanCallback(event, itemString, auctionItem)
vendorPrice = vendor vendorPrice = vendor
end end
local operations = TSMAPI:GetItemOperation(itemString, "Shopping") local operations = TSMAPI:GetItemOperation(itemString, "Shopping")
local opSettings = operations and operations[1] and TSM.operations[operations[1]] local totalQty = GetSniperItemQuantity(itemString)
for _, opName in ipairs(operations or {}) do
local opSettings = GetSniperOperationSettings(opName)
if opSettings and opSettings.maxPrice then if opSettings and opSettings.maxPrice then
maxPrice = TSM:GetMaxPrice(opSettings.maxPrice, itemString) 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
end end
customPrice = TSM:GetMaxPrice(TSM.db.global.sniperCustomPrice, itemString) customPrice = TSM:GetMaxPrice(TSM.db.global.sniperCustomPrice, itemString)
end end

5
TradeSkillMaster_Warehousing/Locale/enUS.lua

@ -55,18 +55,23 @@ L["Nothing to Move"] = true
L["Nothing to Move"] = true L["Nothing to Move"] = true
L["Nothing to Move"] = true L["Nothing to Move"] = true
L["Nothing to Restock"] = true L["Nothing to Restock"] = true
L["No Auctions found for %s"] = true
L["Operation Name"] = true L["Operation Name"] = true
L["Operations"] = true L["Operations"] = true
L["Preparing to Move"] = true L["Preparing to Move"] = true
L["Preparing to Move"] = 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["Puts items matching the itemstring, itemID or partial text entered into the bank or guild bank."] = true
L["Relationships"] = true L["Relationships"] = true
L["Restock Bags"] = 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 Quantity"] = true
L["Restock Settings"] = true L["Restock Settings"] = true
L["Restock Settings"] = true L["Restock Settings"] = true
L["Restocking"] = true L["Restocking"] = true
L["No groups selected."] = true
L["Restore Bags"] = true L["Restore Bags"] = true
L["Set Keep in Bags Quantity"] = true L["Set Keep in Bags Quantity"] = true
L["Set Keep in Bank Quantity"] = true L["Set Keep in Bank Quantity"] = true

13
TradeSkillMaster_Warehousing/Modules/bankui.lua

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

75
TradeSkillMaster_Warehousing/Modules/move.lua

@ -13,6 +13,50 @@ local L = LibStub("AceLocale-3.0"):GetLocale("TradeSkillMaster_Warehousing")
local TSM = select(2, ...) local TSM = select(2, ...)
local move = TSM:NewModule("move", "AceEvent-3.0") local move = TSM:NewModule("move", "AceEvent-3.0")
local AceGUI = LibStub("AceGUI-3.0") -- load the AceGUI libraries 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) function move:restockGroup(grpInfo)
local restockItems, next = TSM.data:unIndexRestockGroupTree(grpInfo), next local restockItems, next = TSM.data:unIndexRestockGroupTree(grpInfo), next
@ -24,6 +68,37 @@ function move:restockGroup(grpInfo)
end end
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) function move:groupTree(grpInfo, src)
local moveItems, next = TSM.data:unIndexMoveGroupTree(grpInfo, src), next local moveItems, next = TSM.data:unIndexMoveGroupTree(grpInfo, src), next
if next(moveItems) == nil then if next(moveItems) == nil then

5
TradeSkillMaster_Warehousing/TradeSkillMaster_Warehousing.lua

@ -89,6 +89,7 @@ function TSM:RegisterModule()
{ key = "movedata", label = L["Displays realtime move data."], callback = "SetLogFlag" }, { 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 = "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 = "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.operations = { maxOperations = 1, callbackOptions = "Options:Load", callbackInfo = "GetOperationInfo" }
TSM.bankUiButton = { callback = "bankui:createTab" } TSM.bankUiButton = { callback = "bankui:createTab" }
@ -238,3 +239,7 @@ function TSM:PutItem(args)
TSM:Print(L["Invalid criteria entered."]) TSM:Print(L["Invalid criteria entered."])
end end
end end
function TSM:RestockFromAH()
TSM.move:restockGroupAuction()
end

40
bin/link_addons.sh

@ -0,0 +1,40 @@
#!/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

@ -0,0 +1,78 @@
#!/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