From d6aaac97afa9eac238f4e95a1fe058c70a91a207 Mon Sep 17 00:00:00 2001 From: Andrew6810 <16847730+andrew6180@users.noreply.github.com> Date: Thu, 30 Mar 2023 12:32:45 -0700 Subject: [PATCH] Sync with https://github.com/andrew6180/TradeSkillMaster --- TradeSkillMaster/Auction/AuctionItem.lua | 1 + TradeSkillMaster/ChangeLog.txt | 213 +++ TradeSkillMaster/Core/Options.lua | 71 +- TradeSkillMaster/GUI/Design.lua | 41 +- TradeSkillMaster/GUI/MainFrame.lua | 6 +- .../LibSharedMedia-3.0/LibSharedMedia-3.0.lua | 245 ++++ TradeSkillMaster/Locale/deDE.lua | 2 +- TradeSkillMaster/Locale/enUS.lua | 9 +- TradeSkillMaster/Locale/esES.lua | 2 +- TradeSkillMaster/Locale/esMX.lua | 2 +- TradeSkillMaster/Locale/frFR.lua | 2 +- TradeSkillMaster/Locale/koKR.lua | 2 +- TradeSkillMaster/Locale/ptBR.lua | 2 +- TradeSkillMaster/Locale/ruRU.lua | 2 +- TradeSkillMaster/Locale/zhCN.lua | 2 +- TradeSkillMaster/Locale/zhTW.lua | 2 +- TradeSkillMaster/TradeSkillMaster.lua | 39 +- TradeSkillMaster/TradeSkillMaster.toc | 13 +- TradeSkillMaster/Util/Util.lua | 40 + TradeSkillMaster_AuctionDB/Locale/deDE.lua | 2 +- TradeSkillMaster_AuctionDB/Locale/enUS.lua | 9 +- TradeSkillMaster_AuctionDB/Locale/esES.lua | 2 +- TradeSkillMaster_AuctionDB/Locale/esMX.lua | 2 +- TradeSkillMaster_AuctionDB/Locale/frFR.lua | 2 +- TradeSkillMaster_AuctionDB/Locale/koKR.lua | 2 +- TradeSkillMaster_AuctionDB/Locale/ptBR.lua | 2 +- TradeSkillMaster_AuctionDB/Locale/ruRU.lua | 2 +- TradeSkillMaster_AuctionDB/Locale/zhCN.lua | 2 +- TradeSkillMaster_AuctionDB/Locale/zhTW.lua | 2 +- TradeSkillMaster_AuctionDB/Modules/GUI.lua | 117 +- .../Modules/Scanning.lua | 506 ++++++- TradeSkillMaster_AuctionDB/Modules/config.lua | 7 + TradeSkillMaster_AuctionDB/Modules/data.lua | 580 +++++++- .../TradeSkillMaster_AuctionDB.lua | 70 +- TradeSkillMaster_Auctioning/locale/enUS.lua | 1 + TradeSkillMaster_Auctioning/locale/frFR.lua | 1 + TradeSkillMaster_Auctioning/locale/koKR.lua | 1 + TradeSkillMaster_Auctioning/locale/ruRU.lua | 1 + TradeSkillMaster_Auctioning/locale/zhCN.lua | 1 + .../modules/ScanUtil.lua | 27 +- .../modules/manage.lua | 35 +- TradeSkillMaster_Crafting/Modules/Cost.lua | 12 +- .../Modules/CraftingGUI.lua | 46 +- .../Modules/EnchantingInfo.lua | 1194 ++++++++--------- .../Modules/SpellNames2IDs.lua | 79 +- TradeSkillMaster_Crafting/Modules/Util.lua | 49 + .../Modules/VellumInfo.lua | 567 ++++---- .../TradeSkillMaster_Crafting.lua | 107 +- TradeSkillMaster_Shopping/Locale/enUS.lua | 1 + TradeSkillMaster_Shopping/Locale/koKR.lua | 1 + TradeSkillMaster_Shopping/Locale/ruRU.lua | 3 +- TradeSkillMaster_Shopping/Locale/zhCN.lua | 1 + TradeSkillMaster_Shopping/modules/Util.lua | 105 +- 53 files changed, 2929 insertions(+), 1306 deletions(-) create mode 100644 TradeSkillMaster/Libs/LibSharedMedia-3.0/LibSharedMedia-3.0.lua diff --git a/TradeSkillMaster/Auction/AuctionItem.lua b/TradeSkillMaster/Auction/AuctionItem.lua index 4533461..a60c030 100644 --- a/TradeSkillMaster/Auction/AuctionItem.lua +++ b/TradeSkillMaster/Auction/AuctionItem.lua @@ -61,6 +61,7 @@ local AuctionRecord = { GetItemBuyout = function(self) if not self.buyout or self.buyout == 0 then return end + -- Calculate the price per item, always rounded downwards. return floor(self.buyout / self.count) end, diff --git a/TradeSkillMaster/ChangeLog.txt b/TradeSkillMaster/ChangeLog.txt index 813a068..c2baac9 100644 --- a/TradeSkillMaster/ChangeLog.txt +++ b/TradeSkillMaster/ChangeLog.txt @@ -1,3 +1,216 @@ +# v2.8.3.668 + +TradeSkillMaster: Revived + +- Release Date: March 23rd, 2023 +- Author: Gnomezilla on Warmane-Icecrown [https://github.com/Bananaman]. + + +## Total Time Elapsed Shown After "Full Scan" and "Group Scan" Completion + +The "Full Scan" and "Group Scan" features now display the "total time elapsed" in the status bar after completion, such as "Done Scanning (1:35:27)". + +This helps players get a better feel for how long each scan takes, and gives people something nice to look at after each scan. + + +## Improved Formatting of the "Full Scan" Time Estimate + +We now display the time as `elapsed time / ~estimated total time`, such as `0:15:37 / ~1:41:27`. + +The total time estimate is continuously updated during the scan, based on our intelligent, weighted "seconds per page" metric and the total number of pages (which can dynamically change during a scan, when other people post or buy auctions). This "total time estimate" therefore follows the server performance beautifully and is very accurate yet responsive to server performance changes. + +It's easier to understand than our previous display of "estimated time remaining", since the server might rapidly send you 10 pages in just a few seconds (which then counted down the "estimated time remaining" very quickly), before suddenly stalling for 30 seconds without any progress. It was still exactly as accurate as the new display method, but the new method is nicer to look at and easier to understand. + + + +--- + +# v2.8.3.667 + +TradeSkillMaster: Revived + +- Release Date: March 20th, 2023 +- Author: Gnomezilla on Warmane-Icecrown [https://github.com/Bananaman]. + + +## AuctionDB's "GetAll" Scans Can Now Be Disabled + +You can now completely disable "GetAll" scans via the AuctionDB settings. 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. + +Go into TSM's main options, and then the "AuctionDB" section at the top. Click on the "Options" tab, and use the "Disable GetAll Auction Scans" checkbox. Disabling the "GetAll" feature will also hide its button from the auction scanning GUI. + +This protection feature is disabled by default, since most casual players probably won't care that "GetAll" generates incorrect market values. However, it's an incredibly good feature for professional gold makers, who want to avoid all risks of calculating incorrect item prices. + +Regardless of whether you use this feature or not, the AuctionDB scan GUI has also been cleaned up now, with much cleaner and more uniform spacing between the various scan buttons. + + + +--- + +# v2.8.3.666 + +TradeSkillMaster: Revived + +- Release Date: March 19th, 2023 +- Author: Gnomezilla on Warmane-Icecrown [https://github.com/Bananaman]. + + +## The Market Value Algorithm Actually Works Now + +The new market value algorithm now actually calculates the market value. + +Yes, seriously. For some reason, the previous algorithm author didn't think about sorting the auction scan data by "buyout price" before parsing it, which meant that all previous TSM calculations were completely incorrect. All previous versions of TSM generate absolutely batshit insane market prices, such as "1 Wool = 500 Gold", simply because the old algorithm was completely broken. + +Therefore, TSM's core feature, the "dbmarket" value which is used in its automatic pricing algorithms, was completely broken and useless. + +It has now been fixed. All market value calculations are now correct. + +In fact, all of your old AuctionDB databases should be wiped and restarted from zero data with this new patch, so that you get rid of all old, corrupt market price data. + + +## Completely Rewritten Auction Scanning Algorithm + +Rewritten, cleaned up and much faster auction scanning algorithm, which uses almost zero memory and NEVER causes any memory overflow crashes, unlike the old algorithm. + +This new algorithm is 1.3x-27.3x faster depending on input data size, and on average 5x faster for most data. It uses less than 5% of the memory of the old algorithm. :) + +If you want to see for yourself that the new algorithm is working correctly, you simply have to set `verifyNewAlgorithm = true` at the top of `TradeSkillMaster_AuctionDB/Modules/Scanning.lua`, and then run a "GetAll" scan to quickly fetch a huge amount of auctions. It will then process all of the data via both the old and new algorithms, and will display the speed improvements of the new algorithm. If there are any differences at all between the calculations of the two algorithms, it would tell you about that via an "error" message. But the algorithms behave completely identically, so you don't have to worry about that and won't see any errors. This verification feature was simply added because the algorithm rewrite is a massive change, and I am sure that some people will want to verify the complex new algorithm with their own eyes. + + +## Fixed Database Decoding Crash + +Fixed a database decoding bug which caused TSM to crash anytime you attempted to decode data for a "corrupted" item in the database. + +This bug usually manifested itself as people failing to perform auction house scans, since every "Full Scan" and "GetAll Scan" (but not "Group Scan") ends by decoding EVERY existing item in your current database (to reset their last-seen, cheapest "minBuyout" value to nil before processing the new data). This meant that the decode failures were usually triggered when people reached the end of their auction scans (but would also happen any other time when something attempts to decode data for that item, such as attempting to view the price-info tooltip of an item that has corrupt data in your database). + +The cause of the random database corruption is pretty much impossible to find, but it's extremely rare (and it might even have been caused by people re-using databases from older versions of TSM). The fix simply ignores the corrupt "market day / scan" data for items that cannot be correctly decoded, thus purging those corrupt "days / scans" from the database automatically, while still rescuing their remaining non-corrupt data. + + +## Added Support for SharedMedia and Custom GUI Fonts + +For some insane reason, TradeSkillMaster uses "Arial" as its default font, which is one of the ugliest fonts in the Universe. + +Therefore, I've implemented LibSharedMedia support in TSM, which means that you now have access to a huge variety of fonts. By default, TSM will only list the game's own fonts, but you can install the [SharedMedia](https://github.com/bkader/SharedMedia#install) addon for WOTLK to add over 100 extra fonts that are now usable in TSM! Other addons you've installed may also be adding extra fonts to the LibSharedMedia collection! + +To change TSM's font, simply type "/tsm", then go into the main tab, and then "Options". Scroll down to the bottom, until you see the new "Normal Font" and "Header Font" options. Have fun! + +Note: TSM is unable to use SharedMedia fonts that are registered by addons that load AFTER TSM, since TSM accesses the fonts and creates its own GUI immediately when it loads, even before the user opens TSM's GUI. To use fonts from such an addon, you have to add that addon to "OptionalDeps" in "TradeSkillMaster.toc", which tells the game that you want TSM to load AFTER the addon that provides your extra fonts. This step isn't necessary for SharedMedia fonts, since they're already marked as an optional TSM dependency now. + + +## Made the "Group Scan" (AuctionDB) Progress Bar More Logical + +The old progress bar for "Group Scans" used absolutely insane numbering and progress messages. It previously went: "Preparing Filter 1 / 2", "Preparing Filter 2 / 2", "Scanning 0 / 2 (Page 1 / 1)", "Scanning 0 / 2 (Page 2 / 16)", ... "Scanning 0 / 2 (Page 16 / 16)", "Scanning 1 / 2 (Page 1 / 1)", "Scanning 1 / 2 (Page 2 / 6)", ... "Scanning 1 / 2 (Page 6 / 6)", "Done Scanning". + +It made absolutely zero sense, and was inconsistent with the behavior of other "counting" progress labels in TSM. There were two issues with it: The item scanner counted from 0, so item 0 was actually item 1, and item 1 was actually item 2, etc. The other issue is that it always said "Page 1 / 1" while requesting the first page, which was a very confusing and misleading display. + +Both issues have now been fixed, and the exact same scan would now look as follows: "Preparing Filter 1 / 2", "Preparing Filter 2 / 2", "Scanning 1 / 2 (Page 1 / ?)", "Scanning 1 / 2 (Page 2 / 16)", ... "Scanning 1 / 2 (Page 16 / 16)", "Scanning 2 / 2 (Page 1 / ?)", "Scanning 2 / 2 (Page 2 / 6)", ... "Scanning 2 / 2 (Page 6 / 6)", "Done Scanning". + +In other words, we now properly count the items we're processing, and we display a temporary question mark while we're fetching page 1 (while we don't know how many other pages exist), which then changes into the actual page count as the scan proceeds. + + +## Made the "Auctioning" Module's Progress Bar More Logical + +This is similar to the "Group Scan" issues described above. + +The "Auctioning" module's features (Post Scan / Cancel Scan / Reset Scan) had several issues. + +They were counting items from 0, so the progres bar said "Scanning 0 / 2" for the first item, and "Scanning 1 / 2" for the final item. That has now been fixed and now counts properly (as "1/2" and "2/2"). + +They also had a completely broken page counter, which didn't properly reset between items, which meant that you'd see complete nonsense while scanning, such as "Scanning 3 / 5 (Page 5 / 5)" followed by "Scanning 4 / 5 (Page 5 / 5)" (keeping the previous page counter), and then the proper counter would finally show up as "Scanning 4 / 5 (Page 1 / 12)" after a few seconds (whenever the server finally replied to the query). It was broken and confusing as hell. That's now been fixed too, so that it properly resets the page counter before every new item it's scanning. + +Furthermore, the confusing "Page 1 / 1 ... Page 2 / 18" issue has been fixed here too, and now says "Page 1 / ?" while fetching the first page of each item. + +Lastly, their page counter was inconsistent with the behavior of regular "AuctionDB: Group Scan" and the "Shopping" progress bars. The page counter in "Auctioning" was zero-indexed, meaning that "Scanning Page 1/8" meant that you were actually scanning page 2/8. That math has now been fixed here, for consistency with the AuctionDB and Shopping scanner's math, because that's a much more logical way to display the pages. + + +## Made the "Shopping" Module's Progress Bar More Logical + +The "Shopping" module's scanner had multiple issues. + +The bar background was supposed to gradually "fill in" to indicate progress for both the "current/total item progress" and the "page progress of the current item". But the math was completely broken. It rendered the progress bar incorrectly, since it ended up counting the progress as "2 items below the item it's currently working on" (due to a mistaken "-1" in the old math). As a result, if you were currently scanning item 3 of 3, you SHOULD have seen a 66% full bar (since 2 of 3 items are done, and the 3rd is being worked on). But you instead only saw a 33% full bar, which was completely wrong. This was especially noticeable if you performed a multi-item search, such as "copper ore; copper bar; gold ore; gold bar". + +They were also counting items from 0, so the progress bar said "Scanning 0 / 2" for the first item, and "Scanning 1 / 2" for the final item. That has now been fixed and now counts properly (as "1/2" and "2/2"), just like with the other modules above. + +Lastly, the confusing "Page 1 / 1 ... Page 2 / 18" issue has been fixed here too, and now says "Page 1 / ?" while fetching the first page of each item. + + +## Accurate Time Estimate for the "Full Scan" Progress Bar + +Implemented an accurate "time remaining" estimate for the "Full Scan" algorithm. The progress bar now shows how much time remains until the scan is complete, such as `Scanning page 293/2399 (~1:04:34)`, meaning that roughly one hour remains. This is very useful for people who want to use the comprehensive "Full Scan" scanning method, but who hate the feeling of blindly scanning without being able to see the remaining time. + +The first time you run a scan on a server, you'll have a very unpredictable estimate, since we use a learning algorithm. The estimate is based on how fast the server is feeding us the "auction pages" on average, which is impossible to know on the first run. Most servers will gradually throttle the player's requests and use random load throttling, so you'll see a growing estimate until it finally settles on an accurate estimate after around 80% of the pages have been received. The slowdown curve depends on the server's throttling implementation, and cannot be predicted ahead of time. + +Think of the first run as the "learning run". After you've completed a "Full Scan", our algorithm learns from it and will produce excellent estimates for all future scans, using a blending algorithm which takes into account both the active run's speed and previous speeds, to predict the server's behavior. The statistics are stored per-server and per-faction, so if you're playing both Alliance and Horde, you'll have to do one full scan per faction to teach it about both factions. + +Note regarding the other scan types: The "GetAll Scan" algorithm only takes a few seconds, so it doesn't need any time estimate. And the "Group Scan" algorithm can't implement any estimate, since there's no way to know how many "auction pages" the remaining items in the group will have. + + +## Improved "GetAll" Algorithm Reliability + +Most private servers don't implement WoW's "can we perform a GetAll Query?" API, and incorrectly answer that the user is allowed to run a query, even though you may still be throttled after a recent "GetAll" scan. Therefore, TSM's own "don't start a GetAll if we're on cooldown" check doesn't work on private servers. The user may therefore click on "Run GetAll Scan" and will then just blindly sit there like an idiot while the request has actually been secretly ignored by the server. + +This situation has now been fixed, so that the "GetAll" scan UI tells the user when the server has silently throttled / ignored their "GetAll" request, so they don't sit there and wait for hours without any progress. Basically, if it's taken longer than 30 seconds, we're assuming that the request has been silently ignored by the server, and will now tell the user via the TSM GUI's progress bar message. It first says `Running query...` as usual, which then changes to `Running query... Server not responding due to throttling? Try again later...` if there hasn't been a server response in more than 30 seconds. + +The "GetAll" algorithm has also been improved to automatically detect when your server is only sending limited / truncated data. Extremely large private servers such as Warmane simply have too many auctions, and their "GetAll" scan won't retrieve all of them. We now warn the user via a chat-message whenever we detect truncated auction data, and we then tell them to use "Full Scan" instead. + +It's unfortunately impossible to calculate accurate market values if your server is sending truncated "GetAll" responses, but people may still prefer the speed of the "GetAll" scans, so we're therefore still allowing their "truncated" auction data to be processed. The users will have to read the warning message and decide for themselves which scans they want to use. + +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. + +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. + +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! + + +## Improved All Auction Data and Market Value Processing + +There were several bugs everywhere in the old code. For example, only the "GetAll" scan had correctly implemented filtering of non-buyout items (auctions that ONLY have a bid price, without any buyout price), which meant that TSM's other scan methods ("Full Scan", "Group Scan", etc) were injecting invalid data into the player's database, such as items with empty "buyout prices". They were being treated as items with a buyout price of ZERO (as if they were totally free items, at no cost!). This serious bug happened every time when there were ANY bid-based auctions for an item in the latest scan. + +All of the auction scanning and processing algorithms have been rewritten for correctness, and will now filter out those useless "bid-only" data entries. We will only analyze auctions that have buyout prices. + +Several bugs have also been fixed related to items being incorrectly marked as "seen and scanned now" even when there was no new data for that item in the latest data batch (this was yet again related to the "incoming data only has bid-based auctions" bug). We now only update the scan-statistics for items when we encounter new data for them with valid buyout prices. + +There were also bugs in the market value calculation algorithm, which would crash with "division by zero" if given empty input data (yet again related to only having bid-based auctions in the input data, but this crash only happened if there were only bid-based auctions for an item, without any buyout-based auctions whatsoever for that item, so it was rare to encounter this crash). That issue has also been fixed now, by adding perfect protection against empty inputs in the market value calculation algorithm. + +We also no longer update the market value for an item if we were unable to calculate a new market value (the old code simply wrote invalid market values to the database without any sanity checks). + +All of these improvements mean that TSM's market price estimates are now infinitely more reliable than old TSM versions. + + +## Fixed Missing "Interruption" Events in Auction Scanners + +This is a great example of what a total mess the TSM codebase is... + +All auction scanners (AuctionDB, Auctioning and Shopping) were missing their "scan interrupted" event, which is triggered when the Auction House frame closes while a scan is ongoing. + +The problem was caused by the fact that TSM previously used the "LibAuctionScan-1.0" library, which emits the "SCAN_INTERRUPTED" event. All TSM scanners were still written to react to that old event name. But TSM nowadays uses their own scanning library instead, which sends the "INTERRUPTED" event instead. They literally forgot to update the event handlers when they switched the library, and they didn't even bother deleting the old library either. + +All three scanning methods have now been fixed, to react to the proper "INTERRUPTED" event, to be able to detect when your scans are interrupted by the auction window being closed. + +Furthermore, the "Shopping" module hadn't implemented interruption handling whatsoever. There literally wasn't any event handler for it. It has now been properly implemented, so that the "Shopping" scanner works properly and correctly resets itself after interruptions. + + +## IMPORTANT! + +When upgrading to this improved version, it's an extremely good idea to delete your old-school, CORRUPT auction database, since your old data has been generated by the extremely buggy algorithms of the old TSM. For example, some of those old "auction data corrupting" bugs include the fact that it was calculating completely incorrect market values when doing "GetAll" scans, since it was previously unable to handle per-item prices inside item stacks, and another bug is the fact that all other scan methods generated wrong market values too, thanks to TSM's previously completely broken market value algorithm (those problems are described in the other changelog entries above). + +There is NO GOOD REASON to keep your old auction database. It's all corrupt thanks to all the old TSM bugs, 100% guaranteed. You need to start fresh. + +If you're ready to start fresh, simply delete these files (where `YOUR_ACCOUNT_NAME` is your own account name): + +``` +./WoW/WTF/Account/YOUR_ACCOUNT_NAME/SavedVariables/TradeSkillMaster_AuctionDB.lua.bak +./WoW/WTF/Account/YOUR_ACCOUNT_NAME/SavedVariables/TradeSkillMaster_AuctionDB.lua +``` + +Then do a "Full Scan" in the game, and you'll be sure that all data from now on is processed using more intelligent algorithms. + +All serious bugs are fixed now. You can finally relax and enjoy TSM. + + + +--- + v2.8.3 *Made TSMAPI:IsSoulbound() MUCH more efficient which will GREATLY reduce the interface log of all modules.. diff --git a/TradeSkillMaster/Core/Options.lua b/TradeSkillMaster/Core/Options.lua index 1b53c72..8f9430e 100644 --- a/TradeSkillMaster/Core/Options.lua +++ b/TradeSkillMaster/Core/Options.lua @@ -11,6 +11,7 @@ local TSM = select(2, ...) local L = LibStub("AceLocale-3.0"):GetLocale("TradeSkillMaster") -- loads the localization table local AceGUI = LibStub("AceGUI-3.0") -- load the AceGUI libraries +local LSM = LibStub("LibSharedMedia-3.0") -- load the SharedMedia library local lib = TSMAPI local private = {} @@ -132,6 +133,11 @@ function private:LoadHelpPage(parent) text = TSMAPI.Design:ColorText(L["Co-Founder:"], "link") .. " Cente [US-Illidan(H)]", relativeWidth = 1, }, + { + type = "Label", + text = TSMAPI.Design:ColorText(L["Wrath of the Lich King Revival:"], "link") .. " Gnomezilla [Warmane-Icecrown(A)], BlueAo [Warmane], andrew6180, Yoshiyuka, DimaSheiko, and other contributors...", + relativeWidth = 1, + }, }, }, }, @@ -224,12 +230,23 @@ end local function DecodeAppearanceData(encodedData) if not encodedData then return end - encodedData = gsub(encodedData, " ", "") + -- Decode the appearance-data string into a table. + encodedData = gsub(encodedData, " ", "") local result = StringToTable(encodedData, 1) if not result then return TSM:Print(L["Invalid appearance data."]) end + + -- Preserve the user's current font choices and switch the theme. + -- NOTE: The "design" table won't exist on first TSM launch, while we're + -- loading the default design, so we also check the "design" table's existence. + local preservedFonts = TSM.db.profile.design and TSM.db.profile.design.fonts or nil TSM.db.profile.design = result + TSM.db.profile.design.fonts = preservedFonts -- Is allowed to be nil. + + -- Use default values for any missing fields (such as fonts, if missing). TSM:SetDesignDefaults(TSM.designDefaults, TSM.db.profile.design) + + -- Apply the new design. TSMAPI:UpdateDesign() end @@ -762,7 +779,59 @@ function private:LoadOptionsPage(parent) tinsert(page[1].children[4].children, { type = "HeadingLine" }) + -- Figure out which fonts to automatically select in the dropdown. + -- NOTE: We visually fall back to our defaults if the stored names are + -- invalid, to avoid confusion if user-fonts are temporarily unavailable. + local availableFontTitlesList = LSM:List("font") + local selectedContentFontIndex = -1 + local selectedBoldFontIndex = -1 + local arialNarrowFontIndex = 1 + local tsmDroidBoldFontIndex = 1 + for i,v in ipairs(availableFontTitlesList) do + if v == "Arial Narrow" then + arialNarrowFontIndex = i + elseif v == "TSM Droid Sans Bold" then + tsmDroidBoldFontIndex = i + end + + if v == TSM.db.profile.design.fonts.content then + selectedContentFontIndex = i + elseif v == TSM.db.profile.design.fonts.bold then + selectedBoldFontIndex = i + end + end + if selectedContentFontIndex == -1 then + selectedContentFontIndex = arialNarrowFontIndex + end + if selectedBoldFontIndex == -1 then + selectedBoldFontIndex = tsmDroidBoldFontIndex + end + local miscWidgets = { + { + type = "Dropdown", + relativeWidth = 0.5, + label = L["Normal Font (Requires Reload)"], + list = availableFontTitlesList, + value = selectedContentFontIndex, + callback = function(_, _, index) + local fontTitle = availableFontTitlesList[index] + TSM.db.profile.design.fonts.content = fontTitle + end, + tooltip = format(L["Default: \"%s\"."], "Arial Narrow"), + }, + { + type = "Dropdown", + relativeWidth = 0.5, + label = L["Header Font (Requires Reload)"], + list = availableFontTitlesList, + value = selectedBoldFontIndex, + callback = function(_, _, index) + local fontTitle = availableFontTitlesList[index] + TSM.db.profile.design.fonts.bold = fontTitle + end, + tooltip = format(L["Default: \"%s\"."], "TSM Droid Sans Bold"), + }, { type = "Slider", relativeWidth = 0.5, diff --git a/TradeSkillMaster/GUI/Design.lua b/TradeSkillMaster/GUI/Design.lua index 98a7cfb..97f5b59 100644 --- a/TradeSkillMaster/GUI/Design.lua +++ b/TradeSkillMaster/GUI/Design.lua @@ -10,11 +10,15 @@ local TSM = select(2, ...) local lib = TSMAPI +local LSM = LibStub("LibSharedMedia-3.0") -- load the SharedMedia library + TSMAPI.Design = {} local Design = TSMAPI.Design local coloredFrames = {} local coloredTexts = {} +local LSM_Fonts = LSM:HashTable("font") -- Persistent hashtable of all registered LSM fonts. + --[[----------------------------------------------------------------------------- Support functions @@ -88,15 +92,44 @@ function Design:SetTitleTextColor(obj) return SetTextColor(obj, "title") end +--- Primary content font. function Design:GetContentFont(size) + -- Retrieve the user's desired font size, if the "size" specifier is valid. size = size or "normal" - TSM.db.profile.design.fontSizes[size] = TSM.db.profile.design.fontSizes[size] or TSM.designDefaults.fontSizes[size] - assert(TSM.db.profile.design.fontSizes[size], format("Invalid font size '%s", tostring(size))) - return TSM.db.profile.design.fonts.content, TSM.db.profile.design.fontSizes[size] + local size_number = TSM.db.profile.design.fontSizes[size] + if not size_number then + if TSM.designDefaults.fontSizes[size] then + -- Save the default size to user's settings, for faster lookups. + TSM.db.profile.design.fontSizes[size] = TSM.designDefaults.fontSizes[size] + size_number = TSM.db.profile.design.fontSizes[size] + end + assert(size_number, format("Invalid font size '%s", tostring(size))) + end + + -- Verify existence of user's chosen font, and do a "soft revert" to our defaults + -- if missing, which can happen if previous SharedMedia fonts have been uninstalled. + -- NOTE: We don't overwrite the database value, in case the user re-enables that font. + local content_font = LSM_Fonts[TSM.db.profile.design.fonts.content] + if not content_font then + content_font = LSM_Fonts["Arial Narrow"] + end + + -- Return the path to the font, and the desired (user-configurable) size. + return content_font, size_number end +--- Header/section title font. function Design:GetBoldFont() - return TSM.db.profile.design.fonts.bold + -- Verify existence of user's chosen font, and do a "soft revert" to our defaults + -- if missing, which can happen if previous SharedMedia fonts have been uninstalled. + -- NOTE: We don't overwrite the database value, in case the user re-enables that font. + local bold_font = LSM_Fonts[TSM.db.profile.design.fonts.bold] + if not bold_font then + bold_font = LSM_Fonts["TSM Droid Sans Bold"] + end + + -- Return the path to the font. + return bold_font end function Design:GetInlineColor(key) diff --git a/TradeSkillMaster/GUI/MainFrame.lua b/TradeSkillMaster/GUI/MainFrame.lua index b2b87e0..987cb64 100644 --- a/TradeSkillMaster/GUI/MainFrame.lua +++ b/TradeSkillMaster/GUI/MainFrame.lua @@ -29,10 +29,10 @@ function TSMAPI:OpenFrame() if TSM.db.global.infoMessage < 1001 then TSM.db.global.infoMessage = 1001 StaticPopupDialogs["TSM_INFO_MESSAGE"] = StaticPopupDialogs["TSM_INFO_MESSAGE"] or { - text = format(L["More advanced options are now designated by %sred text|r. Beginners are encourages to come back to these once they have a solid understanding of the basics."], TSMAPI.Design:GetInlineColor("advanced")), + text = format(L["More advanced options are now designated by %sred text|r. Beginners are encouraged to come back to these once they have a solid understanding of the basics."], TSMAPI.Design:GetInlineColor("advanced")), button1 = OKAY, OnAccept = function() - TSM:Printf(L["More advanced options are now designated by %sred text|r. Beginners are encourages to come back to these once they have a solid understanding of the basics."], TSMAPI.Design:GetInlineColor("advanced")) + TSM:Printf(L["More advanced options are now designated by %sred text|r. Beginners are encouraged to come back to these once they have a solid understanding of the basics."], TSMAPI.Design:GetInlineColor("advanced")) end, timeout = 0, preferredIndex = 3, @@ -137,4 +137,4 @@ function TSM:GetTSMFrameSelectionPath() tinsert(path, {type="Icon", value=TSM.Frame.selected.info.name}) private:FramePathHelper(TSM.Frame, path) return path -end \ No newline at end of file +end diff --git a/TradeSkillMaster/Libs/LibSharedMedia-3.0/LibSharedMedia-3.0.lua b/TradeSkillMaster/Libs/LibSharedMedia-3.0/LibSharedMedia-3.0.lua new file mode 100644 index 0000000..1b24822 --- /dev/null +++ b/TradeSkillMaster/Libs/LibSharedMedia-3.0/LibSharedMedia-3.0.lua @@ -0,0 +1,245 @@ +--[[ +Name: LibSharedMedia-3.0 +Revision: $Revision: 62 $ +Author: Elkano (elkano@gmx.de) +Inspired By: SurfaceLib by Haste/Otravi (troeks@gmail.com) +Website: http://www.wowace.com/projects/libsharedmedia-3-0/ +Description: Shared handling of media data (fonts, sounds, textures, ...) between addons. +Dependencies: LibStub, CallbackHandler-1.0 +License: LGPL v2.1 +]] + +local MAJOR, MINOR = "LibSharedMedia-3.0", 3030001 -- 3.3.5 / increase manually on changes +local lib = LibStub:NewLibrary(MAJOR, MINOR) + +if not lib then return end + +local _G = getfenv(0) + +local pairs = _G.pairs +local type = _G.type + +local band = _G.bit.band +local table_sort = _G.table.sort + +local locale = GetLocale() +local locale_is_western +local LOCALE_MASK = 0 +lib.LOCALE_BIT_koKR = 1 +lib.LOCALE_BIT_ruRU = 2 +lib.LOCALE_BIT_zhCN = 4 +lib.LOCALE_BIT_zhTW = 8 +lib.LOCALE_BIT_western = 128 + +local CallbackHandler = LibStub:GetLibrary("CallbackHandler-1.0") + +lib.callbacks = lib.callbacks or CallbackHandler:New(lib) + +lib.DefaultMedia = lib.DefaultMedia or {} +lib.MediaList = lib.MediaList or {} +lib.MediaTable = lib.MediaTable or {} +lib.MediaType = lib.MediaType or {} +lib.OverrideMedia = lib.OverrideMedia or {} + +local defaultMedia = lib.DefaultMedia +local mediaList = lib.MediaList +local mediaTable = lib.MediaTable +local overrideMedia = lib.OverrideMedia + + +-- create mediatype constants +lib.MediaType.BACKGROUND = "background" -- background textures +lib.MediaType.BORDER = "border" -- border textures +lib.MediaType.FONT = "font" -- fonts +lib.MediaType.STATUSBAR = "statusbar" -- statusbar textures +lib.MediaType.SOUND = "sound" -- sound files + +-- populate lib with default Blizzard data +-- BACKGROUND +if not lib.MediaTable.background then lib.MediaTable.background = {} end +lib.MediaTable.background["None"] = [[]] +lib.MediaTable.background["Blizzard Dialog Background"] = [[Interface\DialogFrame\UI-DialogBox-Background]] +lib.MediaTable.background["Blizzard Dialog Background Dark"] = [[Interface\DialogFrame\UI-DialogBox-Background-Dark]] +lib.MediaTable.background["Blizzard Dialog Background Gold"] = [[Interface\DialogFrame\UI-DialogBox-Gold-Background]] +lib.MediaTable.background["Blizzard Low Health"] = [[Interface\FullScreenTextures\LowHealth]] +lib.MediaTable.background["Blizzard Marble"] = [[Interface\FrameGeneral\UI-Background-Marble]] +lib.MediaTable.background["Blizzard Out of Control"] = [[Interface\FullScreenTextures\OutOfControl]] +lib.MediaTable.background["Blizzard Parchment"] = [[Interface\AchievementFrame\UI-Achievement-Parchment-Horizontal]] +lib.MediaTable.background["Blizzard Parchment 2"] = [[Interface\AchievementFrame\UI-GuildAchievement-Parchment-Horizontal]] +lib.MediaTable.background["Blizzard Rock"] = [[Interface\FrameGeneral\UI-Background-Rock]] +lib.MediaTable.background["Blizzard Tabard Background"] = [[Interface\TabardFrame\TabardFrameBackground]] +lib.MediaTable.background["Blizzard Tooltip"] = [[Interface\Tooltips\UI-Tooltip-Background]] +lib.MediaTable.background["Solid"] = [[Interface\Buttons\WHITE8X8]] +lib.DefaultMedia.background = "None" + +-- BORDER +if not lib.MediaTable.border then lib.MediaTable.border = {} end +lib.MediaTable.border["None"] = [[]] +lib.MediaTable.border["Blizzard Achievement Wood"] = [[Interface\AchievementFrame\UI-Achievement-WoodBorder]] +lib.MediaTable.border["Blizzard Chat Bubble"] = [[Interface\Tooltips\ChatBubble-Backdrop]] +lib.MediaTable.border["Blizzard Dialog"] = [[Interface\DialogFrame\UI-DialogBox-Border]] +lib.MediaTable.border["Blizzard Dialog Gold"] = [[Interface\DialogFrame\UI-DialogBox-Gold-Border]] +lib.MediaTable.border["Blizzard Party"] = [[Interface\CHARACTERFRAME\UI-Party-Border]] +lib.MediaTable.border["Blizzard Tooltip"] = [[Interface\Tooltips\UI-Tooltip-Border]] +lib.DefaultMedia.border = "None" + +-- FONT +if not lib.MediaTable.font then lib.MediaTable.font = {} end +local SML_MT_font = lib.MediaTable.font +if locale == "koKR" then + LOCALE_MASK = lib.LOCALE_BIT_koKR +-- + SML_MT_font["굵은 글꼴"] = [[Fonts\2002B.TTF]] + SML_MT_font["기본 글꼴"] = [[Fonts\2002.TTF]] + SML_MT_font["데미지 글꼴"] = [[Fonts\K_Damage.TTF]] + SML_MT_font["퀘스트 글꼴"] = [[Fonts\K_Pagetext.TTF]] +-- + lib.DefaultMedia["font"] = "기본 글꼴" -- someone from koKR please adjust if needed +-- +elseif locale == "zhCN" then + LOCALE_MASK = lib.LOCALE_BIT_zhCN +-- + SML_MT_font["伤害数字"] = [[Fonts\ZYKai_C.ttf]] + SML_MT_font["默认"] = [[Fonts\ZYKai_T.ttf]] + SML_MT_font["聊天"] = [[Fonts\ZYHei.ttf]] +-- + lib.DefaultMedia["font"] = "默认" -- someone from zhCN please adjust if needed +-- +elseif locale == "zhTW" then + LOCALE_MASK = lib.LOCALE_BIT_zhTW +-- + SML_MT_font["提示訊息"] = [[Fonts\bHEI00M.ttf]] + SML_MT_font["聊天"] = [[Fonts\bHEI01B.ttf]] + SML_MT_font["傷害數字"] = [[Fonts\bKAI00M.ttf]] + SML_MT_font["預設"] = [[Fonts\bLEI00D.ttf]] +-- + lib.DefaultMedia["font"] = "預設" -- someone from zhTW please adjust if needed + +elseif locale == "ruRU" then + LOCALE_MASK = lib.LOCALE_BIT_ruRU +-- + SML_MT_font["Arial Narrow"] = [[Fonts\ARIALN.TTF]] + SML_MT_font["Friz Quadrata TT"] = [[Fonts\FRIZQT__.TTF]] + SML_MT_font["Morpheus"] = [[Fonts\MORPHEUS.TTF]] + SML_MT_font["Nimrod MT"] = [[Fonts\NIM_____.ttf]] + SML_MT_font["Skurri"] = [[Fonts\SKURRI.TTF]] +-- + lib.DefaultMedia.font = "Arial Narrow" +-- +else + LOCALE_MASK = lib.LOCALE_BIT_western + locale_is_western = true +-- + SML_MT_font["Arial Narrow"] = [[Fonts\ARIALN.TTF]] + SML_MT_font["Friz Quadrata TT"] = [[Fonts\FRIZQT__.TTF]] + SML_MT_font["Morpheus"] = [[Fonts\MORPHEUS.TTF]] + SML_MT_font["Skurri"] = [[Fonts\SKURRI.TTF]] +-- + lib.DefaultMedia.font = "Friz Quadrata TT" +-- +end + +-- STATUSBAR +if not lib.MediaTable.statusbar then lib.MediaTable.statusbar = {} end +lib.MediaTable.statusbar["Blizzard"] = [[Interface\TargetingFrame\UI-StatusBar]] +lib.MediaTable.statusbar["Blizzard Character Skills Bar"] = [[Interface\PaperDollInfoFrame\UI-Character-Skills-Bar]] +lib.DefaultMedia.statusbar = "Blizzard" + +-- SOUND +if not lib.MediaTable.sound then lib.MediaTable.sound = {} end +lib.MediaTable.sound["None"] = [[Interface\Quiet.ogg]] -- Relies on the fact that PlaySound[File] doesn't error on these values. +lib.DefaultMedia.sound = "None" + +local function rebuildMediaList(mediatype) + local mtable = mediaTable[mediatype] + if not mtable then return end + if not mediaList[mediatype] then mediaList[mediatype] = {} end + local mlist = mediaList[mediatype] + -- list can only get larger, so simply overwrite it + local i = 0 + for k in pairs(mtable) do + i = i + 1 + mlist[i] = k + end + table_sort(mlist) +end + +function lib:Register(mediatype, key, data, langmask) + if type(mediatype) ~= "string" then + error(MAJOR..":Register(mediatype, key, data, langmask) - mediatype must be string, got "..type(mediatype)) + end + if type(key) ~= "string" then + error(MAJOR..":Register(mediatype, key, data, langmask) - key must be string, got "..type(key)) + end + mediatype = mediatype:lower() + if mediatype == lib.MediaType.FONT and ((langmask and band(langmask, LOCALE_MASK) == 0) or not (langmask or locale_is_western)) then + -- ignore fonts that aren't flagged as supporting local glyphs on non-western clients + return false + end + if mediatype == lib.MediaType.SOUND and type(data) == "string" then + local path = data:lower() + if not path:find(".ogg", nil, true) and not path:find(".mp3", nil, true) and not path:find(".wav", nil, true) then + -- Only wav, ogg and mp3 are valid sounds. + return false + end + end + if not mediaTable[mediatype] then mediaTable[mediatype] = {} end + local mtable = mediaTable[mediatype] + if mtable[key] then return false end + + mtable[key] = data + rebuildMediaList(mediatype) + self.callbacks:Fire("LibSharedMedia_Registered", mediatype, key) + return true +end + +function lib:Fetch(mediatype, key, noDefault) + local mtt = mediaTable[mediatype] + local overridekey = overrideMedia[mediatype] + local result = mtt and ((overridekey and mtt[overridekey] or mtt[key]) or (not noDefault and defaultMedia[mediatype] and mtt[defaultMedia[mediatype]])) or nil + return result ~= "" and result or nil +end + +function lib:IsValid(mediatype, key) + return mediaTable[mediatype] and (not key or mediaTable[mediatype][key]) and true or false +end + +function lib:HashTable(mediatype) + return mediaTable[mediatype] +end + +function lib:List(mediatype) + if not mediaTable[mediatype] then + return nil + end + if not mediaList[mediatype] then + rebuildMediaList(mediatype) + end + return mediaList[mediatype] +end + +function lib:GetGlobal(mediatype) + return overrideMedia[mediatype] +end + +function lib:SetGlobal(mediatype, key) + if not mediaTable[mediatype] then + return false + end + overrideMedia[mediatype] = (key and mediaTable[mediatype][key]) and key or nil + self.callbacks:Fire("LibSharedMedia_SetGlobal", mediatype, overrideMedia[mediatype]) + return true +end + +function lib:GetDefault(mediatype) + return defaultMedia[mediatype] +end + +function lib:SetDefault(mediatype, key) + if mediaTable[mediatype] and mediaTable[mediatype][key] and not defaultMedia[mediatype] then + defaultMedia[mediatype] = key + return true + else + return false + end +end diff --git a/TradeSkillMaster/Locale/deDE.lua b/TradeSkillMaster/Locale/deDE.lua index 3dfe89b..2961675 100644 --- a/TradeSkillMaster/Locale/deDE.lua +++ b/TradeSkillMaster/Locale/deDE.lua @@ -317,7 +317,7 @@ L["Module Information:"] = "Modulinformation:" L["Module Operations / Options"] = "Modul Operationen / Optionen" -- L["Modules"] = "" L["More Advanced Methods"] = "Fortgeschrittene Optionen" --- L["More advanced options are now designated by %sred text|r. Beginners are encourages to come back to these once they have a solid understanding of the basics."] = "" +-- L["More advanced options are now designated by %sred text|r. Beginners are encouraged to come back to these once they have a solid understanding of the basics."] = "" L["Move Already Grouped Items"] = "Verschiebe bereits gruppierte Gegenstände" L["Moved %s to %s."] = "Verschiebe %s zu %s" L["Move Group"] = "Verschiebe Gruppe" diff --git a/TradeSkillMaster/Locale/enUS.lua b/TradeSkillMaster/Locale/enUS.lua index 830638c..646b290 100644 --- a/TradeSkillMaster/Locale/enUS.lua +++ b/TradeSkillMaster/Locale/enUS.lua @@ -155,6 +155,7 @@ L["Default BankUI Tab"] = true L["Default Group Tab"] = true L["Default Tab (Open Auction House to Enable)"] = true L["Default Tab"] = true +L["Default: \"%s\"."] = true L["Default"] = true L["Delete a Profile"] = true L["Delete Custom Price Source"] = true @@ -235,6 +236,7 @@ L["Group names cannot contain %s characters."] = true L["Group:"] = true L["Group(Base Item):"] = true L["Groups"] = true +L["Header Font (Requires Reload)"] = true L["Help"] = true L["Here you can setup relationships between the settings of this operation and other operations for this module. For example, if you have a relationship set to OperationA for the stack size setting below, this operation's stack size setting will always be equal to OperationA's stack size setting."] = true L["Hide Minimap Icon"] = true @@ -335,7 +337,7 @@ L["Module Information:"] = true L["Module Operations / Options"] = true L["Modules"] = true L["More Advanced Methods"] = true -L["More advanced options are now designated by %sred text|r. Beginners are encourages to come back to these once they have a solid understanding of the basics."] = true +L["More advanced options are now designated by %sred text|r. Beginners are encouraged to come back to these once they have a solid understanding of the basics."] = true L["Move Already Grouped Items"] = true L["Move Group"] = true L["Move to Top Level"] = true @@ -351,6 +353,7 @@ L["New Subgroup Name"] = true L["New"] = true L["No Assistant guides available for the modules which you have installed."] = true L["None of your groups have %s operations assigned. Type '/tsm' and click on the 'TradeSkillMaster Groups' button to assign operations to your TSM groups."] = true +L["Normal Font (Requires Reload)"] = true L["Normal Text Size (Requires Reload)"] = true L["Now that the scan is finished, you can look through the results shown in the log, and for each item, decide what action you want to take.\n\nOnce you're done, click on the button below."] = true L["Number of Auction Result Rows (Requires Reload)"] = true @@ -580,10 +583,10 @@ L["We will add items to this group through its 'Items' tab. Click on that tab no L["We will import items into this group using the import list you have."] = true L["Web Master:"] = true L["What do you want to do?"] = true -L["What do you want to do?"] = true L["When checked, random enchants will be ignored for ungrouped items.\n\nNB: This will not affect parent group items that were already added with random enchants\n\nIf you have this checked when adding an ungrouped randomly enchanted item, it will act as all possible random enchants of that item."] = true L["When clicked, makes this group a top-level group with no parent."] = true L["Would you like to add this new operation to %s?"] = true +L["Wrath of the Lich King Revival:"] = true L["Wrong number of item links."] = true L["You appear to be attempting to import an operation from a different module."] = true L["You can change the active database profile, so you can have different settings for every character."] = true @@ -598,4 +601,4 @@ L["You cannot create a profile with an empty name."] = true L["You cannot use %s as part of this custom price."] = true L["You currently don't have any groups setup. Type '/tsm' and click on the 'TradeSkillMaster Groups' button to setup TSM groups."] = true L["You have closed the bankui. Use '/tsm bankui' to view again."] = true -L["You have successfully completed this guide. If you require further assistance, visit out our website:"] = true \ No newline at end of file +L["You have successfully completed this guide. If you require further assistance, visit out our website:"] = true diff --git a/TradeSkillMaster/Locale/esES.lua b/TradeSkillMaster/Locale/esES.lua index dfec8a4..c61eb67 100644 --- a/TradeSkillMaster/Locale/esES.lua +++ b/TradeSkillMaster/Locale/esES.lua @@ -321,7 +321,7 @@ L["Module Information:"] = "Información de Módulo:" L["Module Operations / Options"] = "Módulo Operaciones / Opciones" L["Modules"] = "Módulos" -- Needs review L["More Advanced Methods"] = "Mas Metodos Avanzados" --- L["More advanced options are now designated by %sred text|r. Beginners are encourages to come back to these once they have a solid understanding of the basics."] = "" +-- L["More advanced options are now designated by %sred text|r. Beginners are encouraged to come back to these once they have a solid understanding of the basics."] = "" L["Move Already Grouped Items"] = "Mover los objetos ya en grupo" L["Moved %s to %s."] = "Movido %s a %s" L["Move Group"] = "Mover Grupo" diff --git a/TradeSkillMaster/Locale/esMX.lua b/TradeSkillMaster/Locale/esMX.lua index 2f68c30..243b06e 100644 --- a/TradeSkillMaster/Locale/esMX.lua +++ b/TradeSkillMaster/Locale/esMX.lua @@ -315,7 +315,7 @@ Once you're done, click the button below.]=] ] = "" ]==] -- L["Module Operations / Options"] = "" -- L["Modules"] = "" -- L["More Advanced Methods"] = "" --- L["More advanced options are now designated by %sred text|r. Beginners are encourages to come back to these once they have a solid understanding of the basics."] = "" +-- L["More advanced options are now designated by %sred text|r. Beginners are encouraged to come back to these once they have a solid understanding of the basics."] = "" -- L["Move Already Grouped Items"] = "" -- L["Moved %s to %s."] = "" -- L["Move Group"] = "" diff --git a/TradeSkillMaster/Locale/frFR.lua b/TradeSkillMaster/Locale/frFR.lua index 518265b..b884a86 100644 --- a/TradeSkillMaster/Locale/frFR.lua +++ b/TradeSkillMaster/Locale/frFR.lua @@ -315,7 +315,7 @@ L["Module Information:"] = "Informations sur le module:" -- Needs review L["Module Operations / Options"] = "Opérations / Options du module" -- Needs review -- L["Modules"] = "" L["More Advanced Methods"] = "Méthodes plus avancées" -- Needs review --- L["More advanced options are now designated by %sred text|r. Beginners are encourages to come back to these once they have a solid understanding of the basics."] = "" +-- L["More advanced options are now designated by %sred text|r. Beginners are encouraged to come back to these once they have a solid understanding of the basics."] = "" -- L["Move Already Grouped Items"] = "" -- L["Moved %s to %s."] = "" L["Move Group"] = "Déplacer le groupe" -- Needs review diff --git a/TradeSkillMaster/Locale/koKR.lua b/TradeSkillMaster/Locale/koKR.lua index e6fa956..649b2b7 100644 --- a/TradeSkillMaster/Locale/koKR.lua +++ b/TradeSkillMaster/Locale/koKR.lua @@ -315,7 +315,7 @@ L["Module Information:"] = "모듈 정보:" -- Needs review L["Module Operations / Options"] = "모듈 작업 / 옵션" -- Needs review -- L["Modules"] = "" L["More Advanced Methods"] = "좀더 고급 방식" -- Needs review --- L["More advanced options are now designated by %sred text|r. Beginners are encourages to come back to these once they have a solid understanding of the basics."] = "" +-- L["More advanced options are now designated by %sred text|r. Beginners are encouraged to come back to these once they have a solid understanding of the basics."] = "" L["Move Already Grouped Items"] = "이미 그룹화된 아이템 이동" -- Needs review L["Moved %s to %s."] = "%s을(를) %s으로 이동했습니다." -- Needs review L["Move Group"] = "그룹 이동" -- Needs review diff --git a/TradeSkillMaster/Locale/ptBR.lua b/TradeSkillMaster/Locale/ptBR.lua index 0e21039..a3b4564 100644 --- a/TradeSkillMaster/Locale/ptBR.lua +++ b/TradeSkillMaster/Locale/ptBR.lua @@ -318,7 +318,7 @@ L["Module Information:"] = "Informações do Módulo:" -- Needs review L["Module Operations / Options"] = "Módulo de operações/opções" -- Needs review -- L["Modules"] = "" L["More Advanced Methods"] = "Métodos mais avançados" --- L["More advanced options are now designated by %sred text|r. Beginners are encourages to come back to these once they have a solid understanding of the basics."] = "" +-- L["More advanced options are now designated by %sred text|r. Beginners are encouraged to come back to these once they have a solid understanding of the basics."] = "" L["Move Already Grouped Items"] = "Mover itens já agrupados" L["Moved %s to %s."] = "Movido %s para %s." L["Move Group"] = "Mover grupo" -- Needs review diff --git a/TradeSkillMaster/Locale/ruRU.lua b/TradeSkillMaster/Locale/ruRU.lua index 0ae6cef..b5c5325 100644 --- a/TradeSkillMaster/Locale/ruRU.lua +++ b/TradeSkillMaster/Locale/ruRU.lua @@ -317,7 +317,7 @@ L["Module Information:"] = "Информация о модуле:" L["Module Operations / Options"] = "Операции/Настройки модуля" -- L["Modules"] = "" L["More Advanced Methods"] = "Более совершенных методов" --- L["More advanced options are now designated by %sred text|r. Beginners are encourages to come back to these once they have a solid understanding of the basics."] = "" +-- L["More advanced options are now designated by %sred text|r. Beginners are encouraged to come back to these once they have a solid understanding of the basics."] = "" L["Move Already Grouped Items"] = "Перемещать уже Группированные предметы" L["Moved %s to %s."] = "Переместить %s к %s." L["Move Group"] = "Переместить Группу" diff --git a/TradeSkillMaster/Locale/zhCN.lua b/TradeSkillMaster/Locale/zhCN.lua index 77100e4..dd77643 100644 --- a/TradeSkillMaster/Locale/zhCN.lua +++ b/TradeSkillMaster/Locale/zhCN.lua @@ -317,7 +317,7 @@ L["Module Information:"] = "模块信息:" L["Module Operations / Options"] = "模块操作/选项" L["Modules"] = "模块" L["More Advanced Methods"] = "更多高级模式" -L["More advanced options are now designated by %sred text|r. Beginners are encourages to come back to these once they have a solid understanding of the basics."] = "更多高级选项指定%sred文本|r。不建议初学者使用。" +L["More advanced options are now designated by %sred text|r. Beginners are encouraged to come back to these once they have a solid understanding of the basics."] = "更多高级选项指定%sred文本|r。不建议初学者使用。" L["Move Already Grouped Items"] = "移动已存在分组内物品" L["Moved %s to %s."] = "移动 %s 到 %s 。" L["Move Group"] = "移动分组" diff --git a/TradeSkillMaster/Locale/zhTW.lua b/TradeSkillMaster/Locale/zhTW.lua index c5a26fe..141f507 100644 --- a/TradeSkillMaster/Locale/zhTW.lua +++ b/TradeSkillMaster/Locale/zhTW.lua @@ -315,7 +315,7 @@ L["Module Information:"] = "模組資訊:" L["Module Operations / Options"] = "模組操作/選項" -- L["Modules"] = "" L["More Advanced Methods"] = "更多高級模式" --- L["More advanced options are now designated by %sred text|r. Beginners are encourages to come back to these once they have a solid understanding of the basics."] = "" +-- L["More advanced options are now designated by %sred text|r. Beginners are encouraged to come back to these once they have a solid understanding of the basics."] = "" L["Move Already Grouped Items"] = "移動已存在分組內物品" L["Moved %s to %s."] = "移動 %s 到 %s 。" L["Move Group"] = "移動分組" diff --git a/TradeSkillMaster/TradeSkillMaster.lua b/TradeSkillMaster/TradeSkillMaster.lua index bedbcc1..e0bd736 100644 --- a/TradeSkillMaster/TradeSkillMaster.lua +++ b/TradeSkillMaster/TradeSkillMaster.lua @@ -16,9 +16,19 @@ TSM.moduleNames = {} local L = LibStub("AceLocale-3.0"):GetLocale("TradeSkillMaster") -- loads the localization table TSM._version = GetAddOnMetadata("TradeSkillMaster", "Version") -- current version of the addon +local LSM = LibStub("LibSharedMedia-3.0") -- load the SharedMedia library TSMAPI = {} +-- We must register all custom fonts we want here, since we're loaded before the other TSM modules. +local LSM_Fonts = LSM:HashTable("font") -- Persistent hashtable of all registered LSM fonts. +if not LSM_Fonts["Arial Narrow"] then + -- LSM only registers it for western locales, so enforce its registration for all locales. + LSM:Register("font", "Arial Narrow", [[Fonts\ARIALN.TTF]]) +end +LSM:Register("font", "TSM Droid Sans Bold", [[Interface\Addons\TradeSkillMaster\Media\DroidSans-Bold.ttf]]) -- Our own custom font. + +-- Default TSM design settings. TSM.designDefaults = { frameColors = { frameBG = { backdrop = { 24, 24, 24, .93 }, border = { 30, 30, 30, 1 } }, @@ -42,8 +52,8 @@ TSM.designDefaults = { }, edgeSize = 1.5, fonts = { - content = "Fonts\\ARIALN.TTF", - bold = "Interface\\Addons\\TradeSkillMaster\\Media\\DroidSans-Bold.ttf", + content = "Arial Narrow", + bold = "TSM Droid Sans Bold", }, fontSizes = { normal = 15, @@ -155,11 +165,36 @@ function TSM:OnInitialize() -- add this character to the list of characters on this realm TSM.db.factionrealm.characters[UnitName("player")] = true + -- Initialize default design, and apply defaults for any missing DB values. + -- NOTE: We allow missing fonts (such as uninstalled SharedMedia fonts), and + -- simply do a soft fallback to our defaults in our font lookup functions + -- later (in "GUI/Design.lua"), to support the scenario where the user has + -- selected fonts which are registered by a SharedMedia-based addon which + -- may be temporarily disabled. + -- NOTE: TSM is unable to use SharedMedia fonts that are registered by addons + -- that load AFTER TSM, since TSM accesses the fonts and creates its own GUI + -- immediately when it loads, even before the user opens TSM's GUI. To use + -- fonts from such an addon, you have to add that addon to "OptionalDeps" + -- in "TradeSkillMaster.toc", which tells the game that you want TSM to load + -- AFTER the addon that provides your extra fonts. This step isn't necessary + -- for SharedMedia fonts, since they're already marked as an optional TSM + -- dependency now. if not TSM.db.profile.design then TSM:LoadDefaultDesign() end TSM:SetDesignDefaults(TSM.designDefaults, TSM.db.profile.design) + -- If the user is migrating from an old TSM version, convert their static + -- font paths into our modern font name representations instead. + -- NOTE: Our font system can handle fallbacks for these "missing fonts", but + -- this method cleans up their old database and modernizes it, which is nicer. + if TSM.db.profile.design.fonts.content == "Fonts\\ARIALN.TTF" then + TSM.db.profile.design.fonts.content = "Arial Narrow" + end + if TSM.db.profile.design.fonts.bold == "Interface\\Addons\\TradeSkillMaster\\Media\\DroidSans-Bold.ttf" then + TSM.db.profile.design.fonts.bold = "TSM Droid Sans Bold" + end + -- create / register the minimap button TSM.LDBIcon = LibStub("LibDataBroker-1.1", true) and LibStub("LibDBIcon-1.0", true) local TradeSkillMasterLauncher = LibStub("LibDataBroker-1.1", true):NewDataObject("TradeSkillMasterMinimapIcon", { diff --git a/TradeSkillMaster/TradeSkillMaster.toc b/TradeSkillMaster/TradeSkillMaster.toc index f646907..437f4fa 100644 --- a/TradeSkillMaster/TradeSkillMaster.toc +++ b/TradeSkillMaster/TradeSkillMaster.toc @@ -1,12 +1,14 @@ ## Interface: 30300 -## Title: |cff00fe00TradeSkillMaster|r -## Notes: Core addon for the TradeSkillMaster suite. Does nothing without modules installed. -## Author: Sapu94, Bart39 -## Version: v2.8.3 +## Title: |cff00fe00TradeSkillMaster: Revived|r +## Notes: Core addon for the TradeSkillMaster suite, revived for Wrath of the Lich King. Does nothing without modules installed. +## Author: Sapu94, Bart39, Gnomezilla [Warmane-Icecrown(A)], BlueAo [Warmane], andrew6180, Yoshiyuka, DimaSheiko, and other contributors... +## Version: Rev668 ## SavedVariables: TradeSkillMasterAppDB, TradeSkillMasterDB -## OptionalDeps: AccurateTime, Ace3, LibDataBroker-1.1, LibDBIcon-1.0, LibExtraTip, TipHelper, LibParse, LibCompress, LibGraph-2.0, 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 +# TSM Revived is based on TSM v2.8.3. + #@no-lib-strip@ Libs\LibStub\LibStub.lua Libs\CallbackHandler-1.0\CallbackHandler-1.0.xml @@ -32,6 +34,7 @@ Libs\LibChatAnims\LibChatAnims.xml ## Embeded libraries not on CurseForge Libs\LibExtraTip\Load.xml +Libs\LibSharedMedia-3.0\LibSharedMedia-3.0.lua Locale\enUS.lua Locale\deDE.lua diff --git a/TradeSkillMaster/Util/Util.lua b/TradeSkillMaster/Util/Util.lua index de1f6cf..d0eeecf 100644 --- a/TradeSkillMaster/Util/Util.lua +++ b/TradeSkillMaster/Util/Util.lua @@ -13,6 +13,25 @@ local private = {} TSMAPI:RegisterForTracing(private, "TradeSkillMaster.Util_private") +-- Locals to speed up function access. +local CopyTable = CopyTable +local error = error +local floor = floor +local format = format +local GetRealmName = GetRealmName +local gsub = gsub +local ipairs = ipairs +local select = select +local StaticPopup_Show = StaticPopup_Show +local strfind = strfind +local strlower = strlower +local strsub = strsub +local tinsert = tinsert +local tremove = tremove +local type = type +local UnitName = UnitName + + --- Shows a popup dialog with the given name and ensures it's visible over the TSM frame by setting the frame strata to TOOLTIP. -- @param name The name of the static popup dialog to be shown. function TSMAPI:ShowStaticPopupDialog(name) @@ -97,4 +116,25 @@ end function TSMAPI:IsPlayer(target) return strlower(target) == strlower(UnitName("player")) or (strfind(target, "-") and strlower(target) == strlower(UnitName("player").."-"..GetRealmName())) +end + +--- Converts seconds into hours, minutes and seconds. +-- @param seconds Number of seconds. Can be a float (will automatically be rounded down to whole seconds). +-- NOTE: The return values of this function can be wrapped directly by a call to "TSMAPI:FormatHMS()". +function TSMAPI:SecondsToHMS(seconds) + -- NOTE: This code can be shorter, but was written this way for efficiency. + local hours = floor(seconds / 3600) + seconds = seconds - (hours * 3600) + local minutes = floor(seconds / 60) + seconds = floor(seconds - (minutes * 60)) + + return hours, minutes, seconds +end + +--- Converts hours, minutes and seconds into a human-readable string. +-- @param hours Hours. Must be a whole number without decimals. +-- @param minutes Minutes. Must be a whole number without decimals. +-- @param seconds Seconds. Must be a whole number without decimals. +function TSMAPI:FormatHMS(hours, minutes, seconds) + return format("%d:%02d:%02d", hours, minutes, seconds) end \ No newline at end of file diff --git a/TradeSkillMaster_AuctionDB/Locale/deDE.lua b/TradeSkillMaster_AuctionDB/Locale/deDE.lua index 3807e0d..3164bf9 100644 --- a/TradeSkillMaster_AuctionDB/Locale/deDE.lua +++ b/TradeSkillMaster_AuctionDB/Locale/deDE.lua @@ -14,7 +14,7 @@ local L = LibStub("AceLocale-3.0"):NewLocale("TradeSkillMaster_AuctionDB", "deDE if not L then return end L["A full auction house scan will scan every item on the auction house but is far slower than a GetAll scan. Expect this scan to take several minutes or longer."] = "Ein voller Auktionshausscan wird jedes einzelne Item im Auktionshaus scannen, ist aber sehr viel langsamer als der GetAll-Scan. Erwarte, dass es mehrere Minuten dauert oder länger." -L["A GetAll scan is the fastest in-game method for scanning every item on the auction house. However, there are many possible bugs on Blizzard's end with it including the chance for it to disconnect you from the game. Also, it has a 15 minute cooldown."] = "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." +L["A GetAll scan is the fastest in-game method for scanning every item on the auction house. However, there are many possible bugs on Blizzard's end with it including the chance for it to disconnect you from the game. Also, it has a 15 minute cooldown. You can disable the GetAll button via TSM's AuctionDB options if this feature doesn't work well on your server."] = "Ein GetAll-Scan ist die schnellste Methode, um in-game alle Gegenstände im Auktionshaus zu scannen. Allerdings gibt es viele mögliche Bugs seitens Blizzard die auftreten können, inklusive der Möglichkeit, dass deine Verbindung zum Spiel getrennt wird. Außerdem gibt es einen 15-Minuten-Cooldown. You can disable the GetAll button via TSM's AuctionDB options if this feature doesn't work well on your server." -- Needs review L["Any items in the AuctionDB database that contain the search phrase in their names will be displayed."] = "Es werden alle Gegenstände in der \"AuctionDB\" Datenbank angezeigt, deren Namen mit der Sucheingabe übereinstimmen." L["Are you sure you want to clear your AuctionDB data?"] = "Sind Sie sicher, dass Sie die \"AuctionDB\" Daten löschen wollen?" L["Ascending"] = "Aufsteigend" diff --git a/TradeSkillMaster_AuctionDB/Locale/enUS.lua b/TradeSkillMaster_AuctionDB/Locale/enUS.lua index 0e41bea..70e8b6e 100644 --- a/TradeSkillMaster_AuctionDB/Locale/enUS.lua +++ b/TradeSkillMaster_AuctionDB/Locale/enUS.lua @@ -14,7 +14,7 @@ local L = LibStub("AceLocale-3.0"):NewLocale("TradeSkillMaster_AuctionDB", "enUS if not L then return end 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."] = true +L["A GetAll scan is the fastest in-game method for scanning every item on the auction house. However, there are many possible bugs on Blizzard's end with it including the chance for it to disconnect you from the game. Also, it has a 15 minute cooldown. You can disable the GetAll button via TSM's AuctionDB options if this feature doesn't work well on your server."] = true L["A full auction house scan will scan every item on the auction house but is far slower than a GetAll scan. Expect this scan to take several minutes or longer."] = true L["Any items in the AuctionDB database that contain the search phrase in their names will be displayed."] = true L["Are you sure you want to clear your AuctionDB data?"] = true @@ -23,6 +23,7 @@ L["AuctionDB - Market Value"] = true L["AuctionDB - Minimum Buyout"] = true L["Can't run a GetAll scan right now."] = true L["Descending"] = true +L["Disable \"GetAll\" Auction Scans"] = true L["Display lowest buyout value seen in the last scan in tooltip."] = true L["Display market value in tooltip."] = true L["Done Scanning"] = true @@ -31,6 +32,7 @@ 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["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 not perform \"GetAll\" scans. This is useful if your server doesn't return all auctions in its \"GetAll\" results, which means that you'll get incorrect market value calculations for all items. If you're playing on such servers, it's best to disable the \"GetAll\" feature to avoid accidentally polluting your price database with incorrect data. This option takes effect immediately, but requires a reload to completely hide the \"Run GetAll Scan\" button."] = true L["If checked, poor quality items won't be shown in the search results."] = true L["If checked, the lowest buyout value seen in the last scan of the item will be displayed."] = true L["If checked, the market value of the item will be displayed"] = true @@ -72,7 +74,9 @@ L["Result Order:"] = true L["Run Full Scan"] = true L["Run GetAll Scan"] = true L["Running query..."] = true +L["Running query... Server not responding due to throttling? Try again later..."] = true L["Scan Selected Groups"] = true +L["Scanning %d / %d (Page 1 / ?)"] = true L["Scanning %d / %d (Page %d / %d)"] = true L["Scanning page %s/%s"] = true L["Scanning the auction house in game is no longer necessary!"] = true @@ -88,4 +92,5 @@ L["This will do a slow auction house scan of every item in the selected groups a L["Use the search box and category filters above to search the AuctionDB data."] = true L["You can filter the results by item subtype by using this dropdown. For example, if you want to search for all herbs, you would select \"Trade Goods\" in the item type dropdown and \"Herbs\" in this dropdown."] = true L["You can filter the results by item type by using this dropdown. For example, if you want to search for all herbs, you would select \"Trade Goods\" in this dropdown and \"Herbs\" as the subtype filter."] = true -L["You can use this page to lookup an item or group of items in the AuctionDB database. Note that this does not perform a live search of the AH."] = true \ No newline at end of file +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 \ No newline at end of file diff --git a/TradeSkillMaster_AuctionDB/Locale/esES.lua b/TradeSkillMaster_AuctionDB/Locale/esES.lua index 5d8f7d6..c7e189b 100644 --- a/TradeSkillMaster_AuctionDB/Locale/esES.lua +++ b/TradeSkillMaster_AuctionDB/Locale/esES.lua @@ -14,7 +14,7 @@ local L = LibStub("AceLocale-3.0"):NewLocale("TradeSkillMaster_AuctionDB", "esES if not L then return end -- L["A full auction house scan will scan every item on the auction house but is far slower than a GetAll scan. Expect this scan to take several minutes or longer."] = "" --- L["A GetAll scan is the fastest in-game method for scanning every item on the auction house. However, there are many possible bugs on Blizzard's end with it including the chance for it to disconnect you from the game. Also, it has a 15 minute cooldown."] = "" +-- L["A GetAll scan is the fastest in-game method for scanning every item on the auction house. However, there are many possible bugs on Blizzard's end with it including the chance for it to disconnect you from the game. Also, it has a 15 minute cooldown. You can disable the GetAll button via TSM's AuctionDB options if this feature doesn't work well on your server."] = "" L["Any items in the AuctionDB database that contain the search phrase in their names will be displayed."] = "Todos los artículos en la base de datos de AuctionDB que contienen la frase de búsqueda en su nombre en la pantalla." L["Are you sure you want to clear your AuctionDB data?"] = "¿Está seguro que desea borrar los datos AuctionDB?" L["Ascending"] = "Ascendente" diff --git a/TradeSkillMaster_AuctionDB/Locale/esMX.lua b/TradeSkillMaster_AuctionDB/Locale/esMX.lua index a1ed13d..48d6a91 100644 --- a/TradeSkillMaster_AuctionDB/Locale/esMX.lua +++ b/TradeSkillMaster_AuctionDB/Locale/esMX.lua @@ -14,7 +14,7 @@ local L = LibStub("AceLocale-3.0"):NewLocale("TradeSkillMaster_AuctionDB", "esMX if not L then return end -- L["A full auction house scan will scan every item on the auction house but is far slower than a GetAll scan. Expect this scan to take several minutes or longer."] = "" --- L["A GetAll scan is the fastest in-game method for scanning every item on the auction house. However, there are many possible bugs on Blizzard's end with it including the chance for it to disconnect you from the game. Also, it has a 15 minute cooldown."] = "" +-- L["A GetAll scan is the fastest in-game method for scanning every item on the auction house. However, there are many possible bugs on Blizzard's end with it including the chance for it to disconnect you from the game. Also, it has a 15 minute cooldown. You can disable the GetAll button via TSM's AuctionDB options if this feature doesn't work well on your server."] = "" -- L["Any items in the AuctionDB database that contain the search phrase in their names will be displayed."] = "" -- L["Are you sure you want to clear your AuctionDB data?"] = "" -- L["Ascending"] = "" diff --git a/TradeSkillMaster_AuctionDB/Locale/frFR.lua b/TradeSkillMaster_AuctionDB/Locale/frFR.lua index 131b684..ad2f5b4 100644 --- a/TradeSkillMaster_AuctionDB/Locale/frFR.lua +++ b/TradeSkillMaster_AuctionDB/Locale/frFR.lua @@ -14,7 +14,7 @@ local L = LibStub("AceLocale-3.0"):NewLocale("TradeSkillMaster_AuctionDB", "frFR if not L then return end L["A full auction house scan will scan every item on the auction house but is far slower than a GetAll scan. Expect this scan to take several minutes or longer."] = "Une analyse complète de l'Hôtel des ventes examinera tous les objets de l'hôtel des ventes mais est beaucoup plus lente que la méthode du GetAll. Attendez-vous à ce que cette analyse dure de nombreuses minutes." --- L["A GetAll scan is the fastest in-game method for scanning every item on the auction house. However, there are many possible bugs on Blizzard's end with it including the chance for it to disconnect you from the game. Also, it has a 15 minute cooldown."] = "" +-- L["A GetAll scan is the fastest in-game method for scanning every item on the auction house. However, there are many possible bugs on Blizzard's end with it including the chance for it to disconnect you from the game. Also, it has a 15 minute cooldown. You can disable the GetAll button via TSM's AuctionDB options if this feature doesn't work well on your server."] = "" L["Any items in the AuctionDB database that contain the search phrase in their names will be displayed."] = "Tout objets présent dans la base de donnée d'AuctionDB contenant la phrase recherchée dans son nom sera affiché." L["Are you sure you want to clear your AuctionDB data?"] = "Êtes-vous sûr de vouloir vider les données d'AuctionDB?" L["Ascending"] = "Croissant" diff --git a/TradeSkillMaster_AuctionDB/Locale/koKR.lua b/TradeSkillMaster_AuctionDB/Locale/koKR.lua index b180fea..ecd7939 100644 --- a/TradeSkillMaster_AuctionDB/Locale/koKR.lua +++ b/TradeSkillMaster_AuctionDB/Locale/koKR.lua @@ -14,7 +14,7 @@ local L = LibStub("AceLocale-3.0"):NewLocale("TradeSkillMaster_AuctionDB", "koKR if not L then return end L["A full auction house scan will scan every item on the auction house but is far slower than a GetAll scan. Expect this scan to take several minutes or longer."] = "전체 검색은 경매장 내의 모든 아이템을 검색하지만 GetAll 검색보다는 훨씬 느립니다. 이 검색은 몇 분 정도 또는 그 이상의 시간이 소요됩니다." -L["A GetAll scan is the fastest in-game method for scanning every item on the auction house. However, there are many possible bugs on Blizzard's end with it including the chance for it to disconnect you from the game. Also, it has a 15 minute cooldown."] = "GetAll 검색은 게임 내에서 경매장의 모든 아이템을 검색하기 위한 가장 빠른 검색 방법입니다. 하지만 블리자드 쪽에 많은 버그가 존재하며 게임의 접속이 끊길 가능성도 있습니다. 또한, 15분의 쿨다운이 존재합니다." -- Needs review +L["A GetAll scan is the fastest in-game method for scanning every item on the auction house. However, there are many possible bugs on Blizzard's end with it including the chance for it to disconnect you from the game. Also, it has a 15 minute cooldown. You can disable the GetAll button via TSM's AuctionDB options if this feature doesn't work well on your server."] = "GetAll 검색은 게임 내에서 경매장의 모든 아이템을 검색하기 위한 가장 빠른 검색 방법입니다. 하지만 블리자드 쪽에 많은 버그가 존재하며 게임의 접속이 끊길 가능성도 있습니다. 또한, 15분의 쿨다운이 존재합니다. You can disable the GetAll button via TSM's AuctionDB options if this feature doesn't work well on your server." -- Needs review L["Any items in the AuctionDB database that contain the search phrase in their names will be displayed."] = "이름에 검색 구문을 포함하는 AuctionDB 데이터베이스 내의 모든 아이템이 표시됩니다." L["Are you sure you want to clear your AuctionDB data?"] = "모든 AuctionDB 데이터를 삭제 하시겠습니까?" L["Ascending"] = "오름차순" diff --git a/TradeSkillMaster_AuctionDB/Locale/ptBR.lua b/TradeSkillMaster_AuctionDB/Locale/ptBR.lua index f57cf70..30dc101 100644 --- a/TradeSkillMaster_AuctionDB/Locale/ptBR.lua +++ b/TradeSkillMaster_AuctionDB/Locale/ptBR.lua @@ -14,7 +14,7 @@ local L = LibStub("AceLocale-3.0"):NewLocale("TradeSkillMaster_AuctionDB", "ptBR if not L then return end L["A full auction house scan will scan every item on the auction house but is far slower than a GetAll scan. Expect this scan to take several minutes or longer."] = "Um escaneamento completo da casa de leilões irá escanear todos os itens da casa de leilões, porém é bem mais lento que um escaneamento PegaTudo. Espere que este escaneamento demore vários minutos ou mais." --- L["A GetAll scan is the fastest in-game method for scanning every item on the auction house. However, there are many possible bugs on Blizzard's end with it including the chance for it to disconnect you from the game. Also, it has a 15 minute cooldown."] = "" +-- L["A GetAll scan is the fastest in-game method for scanning every item on the auction house. However, there are many possible bugs on Blizzard's end with it including the chance for it to disconnect you from the game. Also, it has a 15 minute cooldown. You can disable the GetAll button via TSM's AuctionDB options if this feature doesn't work well on your server."] = "" L["Any items in the AuctionDB database that contain the search phrase in their names will be displayed."] = "Qualquer item no bando de dados do AuctionDB que contém a frase procurada em seus nomes serão exibidos." L["Are you sure you want to clear your AuctionDB data?"] = "Você tem certeza de que quer limpar os dados do seu AuctionDB?" L["Ascending"] = "Crescente" diff --git a/TradeSkillMaster_AuctionDB/Locale/ruRU.lua b/TradeSkillMaster_AuctionDB/Locale/ruRU.lua index c75d243..8f77b36 100644 --- a/TradeSkillMaster_AuctionDB/Locale/ruRU.lua +++ b/TradeSkillMaster_AuctionDB/Locale/ruRU.lua @@ -14,7 +14,7 @@ local L = LibStub("AceLocale-3.0"):NewLocale("TradeSkillMaster_AuctionDB", "ruRU if not L then return end L["A full auction house scan will scan every item on the auction house but is far slower than a GetAll scan. Expect this scan to take several minutes or longer."] = "Полный скан Аукциона просканирует каждый товар, но намного дольше, чем GetAll скан. Ждите, это займёт несколько минут или более." -L["A GetAll scan is the fastest in-game method for scanning every item on the auction house. However, there are many possible bugs on Blizzard's end with it including the chance for it to disconnect you from the game. Also, it has a 15 minute cooldown."] = "GetAll скан - самый быстрый внутриигровой способ сканирования. Однако, из-за из-за ошибок со стороны Blizzard's, существует вероятность отключения от сервера. Кроме того, он имеет 15-минутный перерыв." +L["A GetAll scan is the fastest in-game method for scanning every item on the auction house. However, there are many possible bugs on Blizzard's end with it including the chance for it to disconnect you from the game. Also, it has a 15 minute cooldown. You can disable the GetAll button via TSM's AuctionDB options if this feature doesn't work well on your server."] = "GetAll скан - самый быстрый внутриигровой способ сканирования. Однако, из-за из-за ошибок со стороны Blizzard's, существует вероятность отключения от сервера. Кроме того, он имеет 15-минутный перерыв. You can disable the GetAll button via TSM's AuctionDB options if this feature doesn't work well on your server." -- Needs review L["Any items in the AuctionDB database that contain the search phrase in their names will be displayed."] = "Будут отображены все товары из базы данных AuctionDB, содержащие искомую фразу в названии." L["Are you sure you want to clear your AuctionDB data?"] = "Вы действительно хотите очистить базу AuctionDB?" L["Ascending"] = "Возрастание" diff --git a/TradeSkillMaster_AuctionDB/Locale/zhCN.lua b/TradeSkillMaster_AuctionDB/Locale/zhCN.lua index 3fce487..883fb26 100644 --- a/TradeSkillMaster_AuctionDB/Locale/zhCN.lua +++ b/TradeSkillMaster_AuctionDB/Locale/zhCN.lua @@ -14,7 +14,7 @@ local L = LibStub("AceLocale-3.0"):NewLocale("TradeSkillMaster_AuctionDB", "zhCN if not L then return end L["A full auction house scan will scan every item on the auction house but is far slower than a GetAll scan. Expect this scan to take several minutes or longer."] = "完整扫描拍卖行内的所有物品,此方式远慢于快速扫描,预计费时几分钟甚至更久。" -L["A GetAll scan is the fastest in-game method for scanning every item on the auction house. However, there are many possible bugs on Blizzard's end with it including the chance for it to disconnect you from the game. Also, it has a 15 minute cooldown."] = "快速扫描时扫描拍卖行中每件物品最快的方式。然而,在服务器端有着可能的BUG会使您掉线,所以每15分钟才能执行一次。" +L["A GetAll scan is the fastest in-game method for scanning every item on the auction house. However, there are many possible bugs on Blizzard's end with it including the chance for it to disconnect you from the game. Also, it has a 15 minute cooldown. You can disable the GetAll button via TSM's AuctionDB options if this feature doesn't work well on your server."] = "快速扫描时扫描拍卖行中每件物品最快的方式。然而,在服务器端有着可能的BUG会使您掉线,所以每15分钟才能执行一次。 You can disable the GetAll button via TSM's AuctionDB options if this feature doesn't work well on your server." -- Needs review L["Any items in the AuctionDB database that contain the search phrase in their names will be displayed."] = "任何包含搜索短语的AuctionDB数据库中的物品都将显示。" L["Are you sure you want to clear your AuctionDB data?"] = "您确定要清除AuctionDB数据吗?" L["Ascending"] = "升序" diff --git a/TradeSkillMaster_AuctionDB/Locale/zhTW.lua b/TradeSkillMaster_AuctionDB/Locale/zhTW.lua index 2431f76..4bf32ee 100644 --- a/TradeSkillMaster_AuctionDB/Locale/zhTW.lua +++ b/TradeSkillMaster_AuctionDB/Locale/zhTW.lua @@ -14,7 +14,7 @@ local L = LibStub("AceLocale-3.0"):NewLocale("TradeSkillMaster_AuctionDB", "zhTW if not L then return end L["A full auction house scan will scan every item on the auction house but is far slower than a GetAll scan. Expect this scan to take several minutes or longer."] = "完整的拍賣行掃描會掃描每件在拍賣行的物品但是比GetAll掃描還緩慢。預期掃描會花費幾分鐘或是更久。" --- L["A GetAll scan is the fastest in-game method for scanning every item on the auction house. However, there are many possible bugs on Blizzard's end with it including the chance for it to disconnect you from the game. Also, it has a 15 minute cooldown."] = "" +-- L["A GetAll scan is the fastest in-game method for scanning every item on the auction house. However, there are many possible bugs on Blizzard's end with it including the chance for it to disconnect you from the game. Also, it has a 15 minute cooldown. You can disable the GetAll button via TSM's AuctionDB options if this feature doesn't work well on your server."] = "" L["Any items in the AuctionDB database that contain the search phrase in their names will be displayed."] = "任何在AuctionDB資料庫中符合搜尋條件的物品,都會顯示出來。" L["Are you sure you want to clear your AuctionDB data?"] = "你確定要清除AuctionDB資料?" L["Ascending"] = "遞增" diff --git a/TradeSkillMaster_AuctionDB/Modules/GUI.lua b/TradeSkillMaster_AuctionDB/Modules/GUI.lua index 9a1b69d..f34d7f1 100644 --- a/TradeSkillMaster_AuctionDB/Modules/GUI.lua +++ b/TradeSkillMaster_AuctionDB/Modules/GUI.lua @@ -51,12 +51,22 @@ function private:CreateStartScanContent(parent) frame:SetAllPoints(parent) frame:Hide() + -- Don't create or handle the GetAll button if player has disabled GetAll. + -- NOTE: This GUI creation is only done once per game reload, so this choice + -- won't change until the user does a UI "/reload" or logs out of the game. + local includeGetAll = not TSM.db.profile.disableGetAll + local function UpdateGetAllButton() + if not frame.startGetAllButton then + return -- Do nothing if the GetAll feature is disabled. + end + if TSM.Scan.isScanning then frame:Disable() elseif not select(2, CanSendAuctionQuery()) then + -- Server says that GetAll isn't ready. Check our stored cooldown value. local previous = TSM.db.profile.lastGetAll or time() - if previous > (time() - 15*60) then + 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 @@ -67,33 +77,36 @@ function private:CreateStartScanContent(parent) 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 - - frame:SetScript("OnShow", function(self) + + if includeGetAll then + frame:SetScript("OnShow", function(self) TSMAPI:CreateTimeDelay("auctionDBGetAllStatus", 0, UpdateGetAllButton, 0.2) end) - - frame:SetScript("OnHide", function(self) + + frame:SetScript("OnHide", function(self) TSMAPI:CancelFrame("auctionDBGetAllStatus") end) - + end + frame.Enable = function(self) - self.startGetAllButton:Enable() + if self.startGetAllButton then self.startGetAllButton:Enable() end self.startFullScanButton:Enable() self.startGroupScanButton:Enable() end frame.Disable = function(self) - self.startGetAllButton:Disable() + if self.startGetAllButton then self.startGetAllButton:Disable() end self.startFullScanButton:Disable() self.startGroupScanButton:Disable() end - - -- top row (auto updater) + + -- Top row: Auto updater. local text = TSMAPI.GUI:CreateLabel(frame) text:SetFont(TSMAPI.Design:GetContentFont(), 24) text:SetPoint("TOP", 0, -24) @@ -107,69 +120,81 @@ function private:CreateStartScanContent(parent) a1:SetDuration(.5) ag:SetLooping("BOUNCE") ag:Play() - + local content = CreateFrame("Frame", nil, frame) content:SetAllPoints(parent.content) TSMAPI.Design:SetFrameBackdropColor(content) - - -- group tree + + -- Group tree. local container = CreateFrame("Frame", nil, content) container:SetPoint("TOPLEFT", 5, -35) container:SetPoint("BOTTOMRIGHT", -205, 5) TSMAPI.Design:SetFrameColor(container) frame.groupTree = TSMAPI:CreateGroupTree(container, nil, "AuctionDB") - + local bar = TSMAPI.GUI:CreateVerticalLine(content, 0) bar:ClearAllPoints() bar:SetPoint("TOPRIGHT", -200, -30) bar:SetPoint("BOTTOMRIGHT", -200, 0) - + local buttonFrame = CreateFrame("Frame", nil, content) buttonFrame:SetPoint("TOPLEFT", content, "TOPRIGHT", -200, 0) buttonFrame:SetPoint("BOTTOMRIGHT") - - -- first row (getall scan) - local btn = TSMAPI.GUI:CreateButton(buttonFrame, 18) - btn:SetPoint("TOPLEFT", 6, -50) - btn:SetPoint("TOPRIGHT", -6, -50) - 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."] - 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 - - TSMAPI.GUI:CreateHorizontalLine(buttonFrame, -110) - - -- second row (full scan) + + -- Row: GetAll Scan. + -- NOTE: We hide this button if the player has disabled GetAll scans. + 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. local btn = TSMAPI.GUI:CreateButton(buttonFrame, 18) - btn:SetPoint("TOPLEFT", 6, -150) - btn:SetPoint("TOPRIGHT", -6, -150) + btn:SetPoint("TOPLEFT", 6, yOffset) + btn:SetPoint("TOPRIGHT", -6, yOffset) btn:SetHeight(22) btn:SetScript("OnClick", TSM.Scan.StartFullScan) btn:SetText(L["Run Full Scan"]) btn.tooltip = L["A full auction house scan will scan every item on the auction house but is far slower than a GetAll scan. Expect this scan to take several minutes or longer."] frame.startFullScanButton = btn - - TSMAPI.GUI:CreateHorizontalLine(buttonFrame, -200) - - -- third row (group scan) + + yOffset = yOffset - 40 + + TSMAPI.GUI:CreateHorizontalLine(buttonFrame, yOffset) + + yOffset = yOffset - 20 + + -- Row: Group Scan. local btn = TSMAPI.GUI:CreateButton(buttonFrame, 18) - btn:SetPoint("TOPLEFT", 6, -225) - btn:SetPoint("TOPRIGHT", -6, -225) + btn:SetPoint("TOPLEFT", 6, yOffset) + btn:SetPoint("TOPRIGHT", -6, yOffset) btn:SetHeight(22) btn:SetScript("OnClick", GUI.StartGroupScan) btn:SetText(L["Scan Selected Groups"]) btn.tooltip = L["This will do a slow auction house scan of every item in the selected groups and update their AuctionDB prices. This may take several minutes."] frame.startGroupScanButton = btn - + return frame end diff --git a/TradeSkillMaster_AuctionDB/Modules/Scanning.lua b/TradeSkillMaster_AuctionDB/Modules/Scanning.lua index 6cb257e..d2ec6b1 100644 --- a/TradeSkillMaster_AuctionDB/Modules/Scanning.lua +++ b/TradeSkillMaster_AuctionDB/Modules/Scanning.lua @@ -11,94 +11,389 @@ local TSM = select(2, ...) local Scan = TSM:NewModule("Scan", "AceEvent-3.0") local L = LibStub("AceLocale-3.0"):GetLocale("TradeSkillMaster_AuctionDB") -- loads the localization table +Scan.groupScanStartTime = 0 Scan.groupScanData = {} Scan.filterList = {} Scan.numFilters = 0 +Scan.fullScanStartTime = 0 +Scan.fullScanSecondsPerPage = -1 +Scan.fullScanCompleteElapsed = nil +local verifyNewAlgorithm = false -- DEVELOPERS: Set to "true" to validate and benchmark the new market data algorithm! -local function ScanCallback(event, ...) + +local function FullScanCallback(event, ...) if event == "SCAN_PAGE_UPDATE" then + -- We're running a "Full Scan" and have received an auction page. + -- NOTE: These normal per-page scans receive 50 items per page, and will + -- successfully download ALL auctions on private servers, thanks to pagination. + -- For example, while Warmane's "GetAll" only returns 55000 of 126559 auctions, + -- the regular "Full Scan" mode retrieves all 2532 pages of 50 items each, + -- meaning that it covers 126600 auctions (and therefore grabs them all) + -- in this example. Users should always prefer "Full Scan" when "GetAll" fails. local page, total = ... - TSM.GUI:UpdateStatus(format(L["Scanning page %s/%s"], page, total), page*100/total) + + -- Calculate the current page progress and the remainder as floating-point values. + local progress_float = page / total + local remaining_float = 1.0 - progress_float + + -- Estimate the total scan time, based on a MIX of the average per-page so far, + -- and the previous scan's averages stored in the database (if available). + -- NOTE: This callback triggers after we RECEIVED "page", so we count "page" too. + -- NOTE: We don't do any "live" updates of the progress bar text. We only + -- update the text labels when we receive a page, which is very CPU-efficient. + local time_estimate_str = "" + if (page >= 1) and (total > page) then + -- Calculate how many seconds have elapsed per page-request so far. + -- NOTE: We time it via the less-precise "time()" function, which + -- bluntly returns whole seconds. The alternative would be to use + -- "debugprofilestop()", which has millisecond-precision, but breaks + -- if another addon calls "debugprofilestart()" (which resets that + -- timer back to zero). It doesn't really matter, since our "seconds + -- per page" is constantly re-calculated based on the latest "total + -- amount of whole seconds elapsed", so it doesn't accumulate any + -- rounding errors and gets more precise the more pages have been + -- downloaded (after ~10 pages, it's practically as accurate as the + -- debug-timer). We have to use this technique for safety! + -- NOTE: Most servers will gradually slow down the page requests + -- across the first 300 requests or so, which will become slower + -- and slower, which means that the initial time estimate will + -- grow until it settles on the correct time remainder. There's + -- nothing we can do to predict those gradual slowdowns / throttling, + -- which is why we're also storing the last scan's "final average" + -- in the database and using that for our subsequent scan estimates. + local seconds_elapsed = abs(time() - Scan.fullScanStartTime) + local seconds_per_page = seconds_elapsed / page + -- local pages_remaining = total - page -- Not used for anything. + + -- Remember our "real", unweighted value, for later DB storage. + Scan.fullScanSecondsPerPage = seconds_per_page + + -- Calculate a smoothly weighted "seconds per page" value based on + -- a linear mix between the current "seconds per page" and the + -- stored "final seconds per page value" from our previous scan. + -- As we reach 100%, we'll use 100% of the current "real seconds + -- per page". But at 0%, we'll use the stored value instead. + -- Between that, we linearly fade the values so that we react + -- smoothly to changes in speed. This solves the issue that all + -- servers face, which is their gradual slowdown of page fetches, + -- where they start out very fast (such as 1.1 seconds per page), + -- but will have slowed down when you're at the end (such as 2.5 per + -- page). Typical server slowdown in speed is roughly linear, which + -- is why our linear blend between "current estimate" and "finished + -- estimate from previous scan" creates the most accurate results + -- we're able to get, given the server behavior. It should also + -- work perfectly on servers which don't follow this pattern, such + -- as if they have a perfectly linear time between all pages without + -- any throttling at all, in which case both estimates will basically + -- agree anyway (both the current and the saved value). This is the + -- best we can do with the facts of the game. A totally accurate + -- estimate is impossible, but we're as accurate as we can be. + -- NOTE: This estimate cannot be improved, since practically all + -- servers apply random throttling, have various loads and slowdowns + -- throughout the day, etc. This is the best we can do since the + -- actual speed depends on the server and is pretty unpredictable. + -- It would be like trying to predict "the total download-time of a + -- file that keeps fluctuating between fast and slow speeds". The + -- best we can do is estimate based on current and previous speeds. + local last_scan_seconds_per_page = TSM.db.factionrealm.lastScanSecondsPerPage + if last_scan_seconds_per_page and last_scan_seconds_per_page > 0 then + -- TSM:Print(format("Read from DB: %f (Our unweighted estimate: %f)", last_scan_seconds_per_page, seconds_per_page)) -- DEBUG + seconds_per_page = (seconds_per_page * progress_float) + (last_scan_seconds_per_page * remaining_float) + -- TSM:Print(format("New, weighted estimate: %f (Progress: %f / Remaining: %f)", seconds_per_page, progress_float, remaining_float)) -- DEBUG + else + -- TSM:Print(format("Nothing in DB yet (Our unweighted estimate: %f)", seconds_per_page)) -- DEBUG + end + + -- Estimate the "total time" requirement for ALL pages, rounded to + -- the nearest whole second, at least 1 second. + -- NOTE: We calculate the total estimate instead of the "remaining + -- time", because servers tend to fluctuate constantly between slowly + -- and then quickly sending the pages, which means a "pages_remaining" + -- timer is hard to understand in terms of real time remaining, since + -- we might get 10 pages within a few seconds and count down their + -- "seconds per page" amounts much faster than natural time, and + -- then suddenly stall for 30 seconds without getting any pages. + -- So a "remaining time" estimate would not move naturally. Instead, + -- we use a constantly updating "total time estimate" which follows + -- the server performance beautifully and is easy to understand. + -- NOTE: Thanks to the linear blend between historical and current + -- server performance, our estimate is very accurate yet responsive. + -- NOTE: The total page count is able to change during AH scan, when + -- more auctions are added or removed, which further contributes to + -- the confusion if we would use a "time remaining" display instead, + -- but since we use a "total time" estimate the user instead smoothly + -- sees the total estimate change when the page count changes. + local seconds_total_estimate = max(1, floor((total * seconds_per_page) + 0.5)) + + -- Convert the "elapsed / estimated" seconds into hours, minutes and seconds. + time_estimate_str = format(" (%s / ~%s)", TSMAPI:FormatHMS(TSMAPI:SecondsToHMS(seconds_elapsed)), TSMAPI:FormatHMS(TSMAPI:SecondsToHMS(seconds_total_estimate))) + end + + -- Calculate progress bar from 0-100%. + local progress_bar = min(100 * progress_float, 100) + + -- Display the progress bar with the time estimate. + TSM.GUI:UpdateStatus(format(L["Scanning page %s/%s"], page, total) .. time_estimate_str, progress_bar) elseif event == "SCAN_COMPLETE" then + -- The whole scan is complete, and wasn't interrupted by the player. + + -- Store the final "seconds elapsed per page request" into the database. + -- NOTE: We only update it here after complete scans, to avoid poisoning + -- with incorrect, partial-scan estimates, since most servers heavily + -- slow down their page requests over time. The completed scan is the truth. + if Scan.fullScanSecondsPerPage > 0 then + TSM.db.factionrealm.lastScanSecondsPerPage = Scan.fullScanSecondsPerPage + end + + -- Calculate how many seconds the completed "Full Scan" took. + -- NOTE: We must cache it in this external variable, because "Full Scans" + -- use a threading callback which calls "DoneScanning()" one more time, + -- so we preserve the value to still display it via that callback too. + Scan.fullScanCompleteElapsed = abs(time() - Scan.fullScanStartTime) + + -- Now process all of the fetched auctions, and display the total time elapsed. local data = ... Scan:ProcessScanData(data) - Scan:DoneScanning() - elseif event == "INTERRUPTED" then + Scan:DoneScanning(Scan.fullScanCompleteElapsed) + elseif event == "SCAN_INTERRUPTED" or event == "INTERRUPTED" then + -- We've been interrupted by the Auction House closing. + -- NOTE: "SCAN_INTERRUPTED" is from LibAuctionScan-1.0, which isn't used + -- by TSM anymore, and "INTERRUPTED" is from "TSM/Auction/AuctionScanning.lua", + -- which is what this scanner uses nowadays. Scan:DoneScanning() end end function Scan.ProcessGetAllScan(self) - local temp = 0 + -- 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 - temp = min(temp + 1, 100) - self:Sleep(0.2) - if not Scan.isScanning then return end - if Scan.getAllLoaded then + -- 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 - TSM.GUI:UpdateStatus(L["Running query..."], nil, temp) + + -- 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 = {} - for i=1, Scan.getAllLoaded do - TSM.GUI:UpdateStatus(format(L["Scanning page %s/%s"], 1, 1), i*100/Scan.getAllLoaded) - if i % 100 == 0 then + 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 did not run successfully.") - Scan:DoneScanning() + TSM:Print("GetAll: Scan failed or aborted by user.") + Scan:DoneScanning() -- Sets isScanning and getAllLoaded to nil. return end end - local itemID = TSMAPI:GetItemID(GetAuctionItemLink("list", i)) - --local _, _, count, _, _, _, _, _, _, buyout = GetAuctionItemInfo("list", i) - local _, _, count, _, _, _, _, _, buyout = GetAuctionItemInfo("list", i) - if itemID and buyout and buyout > 0 then - data[itemID] = data[itemID] or {records={}, minBuyout=math.huge, quantity=0} - data[itemID].minBuyout = min(data[itemID].minBuyout, floor(buyout/count)) - data[itemID].quantity = data[itemID].quantity + count - for j=1, count do - tinsert(data[itemID].records, floor(buyout/count)) + -- 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.factionrealm.lastCompleteScan = time() - TSM.Data:ProcessData(data) + 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") - local num, total = GetNumAuctionItems("list") - --if num ~= total or num == 0 then - if num == 0 then + -- 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 did not run successfully.") - Scan:DoneScanning() + TSM:Print("GetAll: Scan failed due to server issues.") + Scan:DoneScanning() -- Sets isScanning and getAllLoaded to nil. return end - Scan.getAllLoaded = num + + -- 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 - QueryAuctionItems("", nil, nil, nil, nil, nil, nil, nil, nil, true) Scan:RegisterEvent("AUCTION_ITEM_LIST_UPDATE") - TSMAPI.Threading:Start(Scan.ProcessGetAllScan, 1, function() Scan:DoneScanning() end) + QueryAuctionItems("", nil, nil, nil, nil, nil, nil, nil, nil, 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, ...) @@ -114,14 +409,23 @@ local function GroupScanCallback(event, ...) elseif event == "QUERY_UPDATE" then local current, total = ... TSM.GUI:UpdateStatus(format(L["Preparing Filter %d / %d"], current, total)) - elseif event == "SCAN_INTERRUPTED" then + elseif event == "SCAN_INTERRUPTED" or event == "INTERRUPTED" then + -- We've been interrupted by the Auction House closing. + -- NOTE: "SCAN_INTERRUPTED" is from LibAuctionScan-1.0, which isn't used + -- by TSM anymore, and "INTERRUPTED" is from "TSM/Auction/AuctionScanning.lua", + -- which is what this scanner uses nowadays. Scan:DoneScanning() elseif event == "SCAN_TIMEOUT" then tremove(Scan.filterList, 1) Scan:ScanNextGroupFilter() elseif event == "SCAN_PAGE_UPDATE" then local page, total = ... - TSM.GUI:UpdateStatus(format(L["Scanning %d / %d (Page %d / %d)"], Scan.numFilters-#Scan.filterList, Scan.numFilters, page+1, total), nil, page*100/total) + -- We have now received at least 1 page for this item. Show how many pages remain. + -- NOTE: We can't provide any time estimate here, since the other group sizes are unknown. + -- NOTE: We use this particular item's page-progress as the progress bar. + -- NOTE: We add "+1" to the page counter, to indicate that we've received that page and are working on the next page. + local progress_bar = min(100*(page/total), 100) -- Calculate progress bar from 0-100%. + TSM.GUI:UpdateStatus(format(L["Scanning %d / %d (Page %d / %d)"], ((Scan.numFilters-#Scan.filterList) + 1), Scan.numFilters, min(page + 1, total), total), nil, progress_bar) elseif event == "SCAN_COMPLETE" then local data = ... for _, itemString in ipairs(Scan.filterList[1].items) do @@ -136,11 +440,23 @@ end function Scan:ScanNextGroupFilter(data) if #Scan.filterList == 0 then + -- Calculate how many seconds the completed "Group Scan" took. + local seconds_elapsed = abs(time() - Scan.groupScanStartTime) + + -- Now process all of the fetched auctions, and display the total time elapsed. Scan:ProcessScanData(Scan.groupScanData) - Scan:DoneScanning() + Scan:DoneScanning(seconds_elapsed) return end - TSM.GUI:UpdateStatus(format(L["Scanning %d / %d (Page %d / %d)"], Scan.numFilters-#Scan.filterList, Scan.numFilters, 1, 1), (Scan.numFilters-#Scan.filterList)*100/Scan.numFilters) + + -- Apply the temporary label for when we've requested the item's 1st page, + -- but we don't yet know how many results or pages there are for this item. + -- NOTE: We can't provide any time estimate here, since the other group sizes are unknown. + -- NOTE: In the label, we count the items starting at 1, to say "Scanning 1 / 2" + -- (instead of "Scanning 0 / 2"), but for the progress bar we count starting + -- from 0, so that it fills up properly by only proceeding after an item is done. + local progress_bar = min(100*((Scan.numFilters-#Scan.filterList)/Scan.numFilters), 100) -- Calculate progress bar from 0-100%. + TSM.GUI:UpdateStatus(format(L["Scanning %d / %d (Page 1 / ?)"], ((Scan.numFilters-#Scan.filterList) + 1), Scan.numFilters), progress_bar) TSMAPI.AuctionScan:RunQuery(Scan.filterList[1], GroupScanCallback) end @@ -152,6 +468,7 @@ function Scan:StartGroupScan(items) wipe(Scan.groupScanData) Scan.numFilters = 0 TSMAPI.AuctionScan:StopScan() + Scan.groupScanStartTime = time() -- Keep track of when we started the "Group Scan". TSMAPI:GenerateQueries(items, GroupScanCallback) TSM.GUI:UpdateStatus(L["Preparing Filters..."]) end @@ -162,10 +479,23 @@ function Scan:StartFullScan() Scan.isBuggedGetAll = nil Scan.groupItems = nil TSMAPI.AuctionScan:StopScan() - TSMAPI.AuctionScan:RunQuery({name=""}, ScanCallback) + Scan.fullScanStartTime = time() -- Keep track of when we started the "Full Scan". + Scan.fullScanSecondsPerPage = -1 -- Reset the page-speed timer. + Scan.fullScanCompleteElapsed = nil -- Reset the "full scan completed" information. + TSMAPI.AuctionScan:RunQuery({name=""}, FullScanCallback) end function Scan:StartGetAllScan() + -- Refuse to perform "GetAll" if we're called while "GetAll" is disabled. + -- NOTE: Only happens if the player has visited the auction house and looked + -- at the AuctionDB GUI, and THEN gone into TSM's options to disable "GetAll". + -- The "Run GetAll Scan" button remains until /reload, so we must block it. + if TSM.db.profile.disableGetAll then + TSM:Print(L["You have disabled GetAll scans via AuctionDB's options."]) + return + end + + -- Begin the "GetAll" scan. TSM.db.profile.lastGetAll = time() Scan.isScanning = "GetAll" Scan.isBuggedGetAll = nil @@ -174,13 +504,21 @@ function Scan:StartGetAllScan() Scan:GetAllScanQuery() end -function Scan:DoneScanning() - TSM.GUI:UpdateStatus(L["Done Scanning"], 100) +function Scan:DoneScanning(seconds_elapsed) + if seconds_elapsed then + -- If given the "time elapsed", display it as "Done Scanning (1:35:27)". + TSM.GUI:UpdateStatus(format("%s (%s)", L["Done Scanning"], TSMAPI:FormatHMS(TSMAPI:SecondsToHMS(seconds_elapsed))), 100) + else + -- Used when we don't care about showing time (such as scan failures). + TSM.GUI:UpdateStatus(L["Done Scanning"], 100) + end Scan.isScanning = nil Scan.getAllLoaded = nil end function Scan:ProcessScanData(scanData) + -- Handle scans performed via "Full Scan" and "Group Scan", but not "GetAll". + -- NOTE: See "Scan.ProcessGetAllScan()" for full explanation of this algorithm. local data = {} for itemString, obj in pairs(scanData) do @@ -189,41 +527,97 @@ function Scan:ProcessScanData(scanData) local quantity, minBuyout = 0, 0 local records = {} for _, record in ipairs(obj.records) do - local itemBuyout = record:GetItemBuyout() - if itemBuyout and (itemBuyout < minBuyout or minBuyout == 0) then - minBuyout = itemBuyout - end - quantity = quantity + record.count - for i=1, record.count do - tinsert(records, itemBuyout) + -- Only process this auction if we saw a valid buyout price (ignore bid-only auctions, etc). + if record.buyout and record.buyout > 0 then + -- Calculate the price per item, always rounded downwards. + -- NOTE: "GetItemBuyout" returns nil if no buyout or if buyout is "0". + local itemBuyout = record:GetItemBuyout() + if itemBuyout then + -- Calculate the lowest "per-item buyout price" we're seeing for this item. + if (itemBuyout < minBuyout or minBuyout == 0) then + minBuyout = itemBuyout + end + + -- Count the total amount of this item that exists on the auction house (adds together all stacks). + quantity = quantity + record.count + + -- 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 i=1, record.count do + -- tinsert(records, itemBuyout) + -- end + + -- Rewritten, intelligent code which adds 1 record per "stack" (auction) instead. + tinsert(records, {record.count, itemBuyout}) + end end end + + -- Add this item to "data to process" even if there's zero records, + -- which can happen if they're all bid-only auctions. + -- NOTE: This differs from the behavior of "ProcessGetAllScan", which + -- only adds items that have at least 1 record with a buyout value. + -- NOTE: Empty records are totally fine either way, since "ProcessData" + -- simply ignores items that don't contain any buyout prices. + -- NOTE: If no buyout records were found, the "minBuyout" and "quantity" + -- fields below both have the default value of "0" (initialized above). data[itemID] = {records=records, minBuyout=minBuyout, quantity=quantity} end end + -- Mark the collected auction data as a new "complete scan" with today's date, + -- but only if this was a normal "Full Scan" (not just a "TSM item group" scan). if Scan.isScanning ~= "group" then TSM.db.factionrealm.lastCompleteScan = time() end - TSM.Data:ProcessData(data, Scan.groupItems) + + -- Process the collected auction data. + TSM.Data:ProcessData(data, Scan.groupItems, verifyNewAlgorithm) end function Scan:ProcessImportedData(auctionData) + -- Handle manually imported auction scan data. + -- NOTE: This function is deprecated? Nothing seems to call it, unless they're + -- somehow calling it via another non-named technique, or perhaps it's internal + -- for developer-use only (basically just a quick way to emulate a full scan). local data = {} + for itemID, auctions in pairs(auctionData) do - local quantity, minBuyout, records = 0, 0, {} + -- Process all imported auction records for this item. + local quantity, minBuyout = 0, 0 + local records = {} for _, auction in ipairs(auctions) do + -- Fetch the "price per item" and "item-count in this stack" from the auction's data. + -- NOTE: We only import auctions with per-item buyout values (ignore bid-only auctions, etc). local itemBuyout, count = unpack(auction) - if itemBuyout and (itemBuyout < minBuyout or minBuyout == 0) then - minBuyout = itemBuyout - end - quantity = quantity + count - for i=1, count do - tinsert(records, itemBuyout) + if itemBuyout then + -- Calculate the lowest "per-item buyout price" we're seeing for this item. + if (itemBuyout < minBuyout or minBuyout == 0) then + minBuyout = itemBuyout + end + + -- Count the total amount of this item that exists on the auction house (adds together all stacks). + quantity = quantity + count + + -- 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 i=1, count do + -- tinsert(records, itemBuyout) + -- end + + -- Rewritten, intelligent code which adds 1 record per "stack" (auction) instead. + tinsert(records, {count, itemBuyout}) end end + + -- Add this item to "data to process" even if there's zero records, + -- which can happen if they're all bid-only auctions. data[itemID] = {records=records, minBuyout=minBuyout, quantity=quantity} end + + -- Process the imported auction data as a new "complete scan" with today's date. TSM.db.factionrealm.lastCompleteScan = time() - TSM.Data:ProcessData(data) + TSM.Data:ProcessData(data, nil, verifyNewAlgorithm) end diff --git a/TradeSkillMaster_AuctionDB/Modules/config.lua b/TradeSkillMaster_AuctionDB/Modules/config.lua index 5c2843f..38f94a6 100644 --- a/TradeSkillMaster_AuctionDB/Modules/config.lua +++ b/TradeSkillMaster_AuctionDB/Modules/config.lua @@ -390,6 +390,13 @@ function Config:LoadOptions(container) relativeWidth = 0.5, tooltip = L["If checked, AuctionDB will add a tab to the AH to allow for in-game scans. If you are using the TSM app exclusively for your scans, you may want to hide it by unchecking this option. This option requires a reload to take effect."], }, + { + type = "CheckBox", + label = L["Disable \"GetAll\" Auction Scans"], + settingInfo = { TSM.db.profile, "disableGetAll" }, + 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."], + }, }, }, { diff --git a/TradeSkillMaster_AuctionDB/Modules/data.lua b/TradeSkillMaster_AuctionDB/Modules/data.lua index 032ac47..f035581 100644 --- a/TradeSkillMaster_AuctionDB/Modules/data.lua +++ b/TradeSkillMaster_AuctionDB/Modules/data.lua @@ -10,6 +10,21 @@ local TSM = select(2, ...) local Data = TSM:NewModule("Data") +-- locals to speed up function access +local abs = abs +local CopyTable = CopyTable +local debugprofilestop = debugprofilestop +local floor = floor +local format = format +local ipairs = ipairs +local pairs = pairs +local sqrt = sqrt +local time = time +local tinsert = tinsert +local tsort = table.sort +local type = type +local unpack = unpack + -- weight for the market value from X days ago (where X is the index of the table) local WEIGHTS = {[0] = 132, [1] = 125, [2] = 100, [3] = 75, [4] = 45, [5] = 34, [6] = 33, [7] = 38, [8] = 28, [9] = 21, [10] = 15, [11] = 10, [12] = 7, [13] = 5, [14] = 4} @@ -115,111 +130,540 @@ function Data:GetMarketValue(scans) return totalWeight > 0 and floor(totalAmount / totalWeight + 0.5) or 0 end -function Data:ProcessData(scanData, groupItems) - if TSM.processingData then return TSMAPI:CreateTimeDelay(0.2, function() Data:ProcessData(scanData, groupItems) end) end - +--- Process a table of new market scan data. +-- @param scanData The market scan data. +-- @param[opt] groupItems Affects how the minBuyout data is wiped. Use nil for regular behavior. +-- @param[opt] verifyNewAlgorithm Boolean 'true' if you want to benchmark and verify the new market value algorithm. +function Data:ProcessData(scanData, groupItems, verifyNewAlgorithm) + -- If we're currently processing data, retry in 0.2 seconds. + -- NOTE: This will retry itself over and over until it's able to process. + if TSM.processingData then + return TSMAPI:CreateTimeDelay(0.2, function() Data:ProcessData(scanData, groupItems, verifyNewAlgorithm) end) + end + - -- wipe all the minBuyout data + -- 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 + -- cached items in memory in other cases (such as "Full" and "GetAll" scans). + -- NOTE: It's no problem if we leave some items empty with "nil" minBuyout + -- values. That's how TSM is supposed to work, with items having an empty "minBuyout" + -- if there wasn't any "minBuyout" data for that item in the newest data batch. if groupItems then + -- A list of items ("group scan") was provided. Wipe data for those items. for itemString in pairs(groupItems) do local itemID = TSMAPI:GetItemID(itemString) - if TSM.data[itemID] then + if TSM.data[itemID] then -- If we have existing data for this item. TSM:DecodeItemData(itemID) - TSM.data[itemID].minBuyout = nil + TSM.data[itemID].minBuyout = nil -- Erase its stored minBuyout value. TSM:EncodeItemData(itemID) end end else + -- Wipe data for all items in memory, regardless of whether they're actually + -- included in the incoming scan data or not... for itemID, data in pairs(TSM.data) do TSM:DecodeItemData(itemID) - data.minBuyout = nil + data.minBuyout = nil -- Directly updates TSM.data[itemID] via reference. TSM:EncodeItemData(itemID) end end - + + + -- Convert the incoming "scanData" hashmap to a numerically indexed table, + -- to allow us to perform batched processing (since "pairs()" wouldn't know + -- how to resume at the current spot between our batch processing callbacks). + -- NOTE: Doesn't use much memory, since we're re-using "data" table refs. local scanDataList = {} for itemID, data in pairs(scanData) do - tinsert(scanDataList, {itemID, data}) + scanDataList[#scanDataList + 1] = {itemID, data} end - - -- go through each item and figure out the market value / update the data table + + + -- Go through each item and figure out their market value / update the data table. + -- NOTE: This processes the data in batched chunks of 500 items at a time, + -- pausing between each chunk to allow the game client to avoid freezing. local index = 1 local day = Data:GetDay() local function DoDataProcessing() for i = 1, 500 do + -- Abort if we've reached the end of the processing queue. if index > #scanDataList then TSMAPI:CancelFrame("adbProcessDelay") TSM.processingData = nil break end - - local itemID, data = unpack(scanDataList[index]) - TSM:DecodeItemData(itemID) - TSM.data[itemID] = TSM.data[itemID] or {scans={}, lastScan = 0} - local marketValue = Data:CalculateMarketValue(data.records) - - local scanData = TSM.data[itemID].scans - scanData[day] = scanData[day] or {avg=0, count=0} - if type(scanData[day]) == "number" then - -- this should never happen... - scanData[day] = {scanData[day]} + + -- Detect which Item ID we're processing, and read its new data (new auction records). + local itemID, data = unpack(scanDataList[index]) + + -- Calculate the market value, and optionally perform benchmarks and + -- validation of the "new, compact algorithm" to prove correctness. + -- NOTE: We refuse to verify data with over 200 000 "item rows", since + -- that would risk bloating RAM and leading to a script crash. This + -- has been VERIFIED to still WORK with 169 000 rows, but fail for + -- an item with 686 900 table rows (script stops executing), so 200k + -- should be a safe cutoff to prevent memory overflows during benchmark. + local marketValue = -1 + if (not verifyNewAlgorithm) or (data.quantity >= 200000) then + -- NOTE: Returns -1 if there aren't enough records to calculate. + marketValue = Data:CalculateMarketValue(data, verifyNewAlgorithm) + else + -- Verification and benchmark requested, and item is safe to check. + + -- Perform the two calculations and benchmark the algorithms. + -- NOTE: Debug profiling is counted in milliseconds. For the old + -- algorithm, we're also including the time it takes to create + -- the "bloated table", since that's how the old TSM algorithm + -- created the table too (adds 1 row per "individual stack item"). + -- SEE: https://wowpedia.fandom.com/wiki/API_debugprofilestop + local time_start_ms = debugprofilestop() + + -- Generate an old-school data table, where we insert one row + -- per "item" per stack, so 5 stacks of 1000 items would mean + -- a total of 5000 rows, each with the individual item's price. + local data_new_records = data.records + local data_old_table = {records={}, minBuyout=data.minBuyout, quantity=data.quantity} + local data_old_records = data_old_table.records + for stack_idx=1, #data_new_records do + local stack_size, buyout_per_item = unpack(data_new_records[stack_idx]) + for this_stack_item_idx=1, stack_size do + -- NOTE: The old algorithm used this exact tinsert() + -- method instead of the faster "tbl[#tbl + 1]" technique, + -- so that's why we're keeping that slow method here. + tinsert(data_old_records, buyout_per_item) + end + end + + -- Verify that the "old-school table" contains ALL "expanded" items. + if #data_old_records ~= data.quantity then + TSM:Print(format("TABLE CREATION ERROR: item=%d, expected quantity=%d, created quantity=%d", itemID, data.quantity, #data_old_records)) + end + + -- Generate the old algorithm's value and finish its benchmark. + local old_marketValue = Data:CalculateMarketValue(data_old_table, verifyNewAlgorithm) + local time_elapsed_old_ms = (debugprofilestop() - time_start_ms) + + -- Now generate the new algorithm's value and benchmark it too. + -- NOTE: This new algorithm is 1.3x-27.3x faster depending on + -- input data size, and on average 5x faster for most data. :) + time_start_ms = debugprofilestop() + marketValue = Data:CalculateMarketValue(data, verifyNewAlgorithm) + local time_elapsed_new_ms = (debugprofilestop() - time_start_ms) + + -- Verify the calculations to ensure the algorithms are equal. + -- NOTE: Yes, the algorithms are perfectly equal for all input data. + if old_marketValue ~= marketValue then + TSM:Print(format("! ALGORITHM ERROR: item=%d, old=%.1f, new=%.1f", itemID, old_marketValue, marketValue)) + end + + -- Output benchmark results, but only for items with at least 500 entries. + -- NOTE: Comment this out if you're only interested in errors + -- above, or feel free to raise cutoff quantity to reduce logging. + if data.quantity >= 500 then + TSM:Print(format("+ ALGORITHM SPEED: item=%d, quantity=%d, match=%s, old=%.1f, new=%.1f, old_speed=%f ms, new_speed=%f ms, speedup=%.2f x", + itemID, data.quantity, old_marketValue == marketValue and "YES" or "NO", old_marketValue, marketValue, time_elapsed_old_ms, time_elapsed_new_ms, time_elapsed_old_ms > 0 and (time_elapsed_old_ms / time_elapsed_new_ms) or math.huge)) -- Prevents division by zero. + end end - scanData[day].avg = scanData[day].avg or 0 - scanData[day].count = scanData[day].count or 0 - if #scanData[day] > 0 then - scanData[day] = Data:ConvertScansToAvg(scanData[day]) + + -- Detect whether it was POSSIBLE to calculate a market value, and + -- ONLY proceed with the item updates if we were able to calculate + -- a new market value. Otherwise, skip the item as if it "didn't even + -- exist in this scan", since it basically "doesn't exist" if there + -- were no buyout prices to calculate a new market value from! + -- NOTE: This can happen if the scan data only contained "bid without + -- buyout" items, meaning they didn't have any per-item buyout data, + -- which can ONLY happen via "Scanning.lua:ProcessScanData()" when + -- doing a normal "Full Scan" or "Group Scan" (not "GetAll"). If + -- there aren't any buyout prices for the item, it still gets added + -- without any "records". This differs from "GetAll" which only adds + -- items to the queue if they had at least one "buyout price" auction. + -- NOTE: We're skipping the empty/indeterminable items to ensure that + -- we have identical behavior for both "GetAll" and all other scan + -- types, so that we NEVER add "empty/missing" market values for items! + -- NOTE: We allow a market value of "0", since it means there was + -- valid data in the calculations. However, "0" is extremely unlikely + -- since it would require a single, huge stack of items for a price + -- of 1 copper or so, to make the per-item market value end up at + -- just "0" for that item. Basically, it's never gonna happen! + if marketValue and (marketValue >= 0) then + -- Fetch our archived data (if we have any) for this itemID. + TSM:DecodeItemData(itemID) + TSM.data[itemID] = TSM.data[itemID] or {scans={}, lastScan = 0} + + -- Update market scan statistics for this item. + local scanData = TSM.data[itemID].scans + scanData[day] = scanData[day] or {avg=0, count=0} + if type(scanData[day]) == "number" then + -- Original code comment here: "This should never happen..." + -- NOTE: WTF was TSM's original author doing here? They're + -- converting "scanData[day]" into an array with 1 numeric + -- value, and mixing that array data with hashmap keys below, + -- so it seems like they're storing some data with numeric + -- keys and others with hashmap keys, all in the same table... + scanData[day] = {scanData[day]} + end + scanData[day].avg = scanData[day].avg or 0 + scanData[day].count = scanData[day].count or 0 + if #scanData[day] > 0 then + scanData[day] = Data:ConvertScansToAvg(scanData[day]) + end + scanData[day].avg = floor((scanData[day].avg * scanData[day].count + marketValue) / (scanData[day].count + 1) + 0.5) + scanData[day].count = scanData[day].count + 1 + + -- Remember the item's scan date, cheapest buyout price on AH right now, + -- and how many items in total exist on AH (adds together all stacks). + -- NOTE: We only update "minBuyout" if the scanned data for that + -- item contains a "greater than 0" buyout value. That was mostly + -- necessary in the past, when TSM sloppily included bid-only items + -- in the data, but should no longer be able to happen with our new code! + TSM.data[itemID].lastScan = TSM.db.factionrealm.lastCompleteScan + TSM.data[itemID].minBuyout = data.minBuyout > 0 and data.minBuyout or nil + TSM.data[itemID].quantity = data.quantity -- Counts all items of all stacks. + Data:UpdateMarketValue(TSM.data[itemID]) + + -- Update our archived, encoded representation of this item's data. + TSM:EncodeItemData(itemID) end - scanData[day].avg = floor((scanData[day].avg * scanData[day].count + marketValue) / (scanData[day].count + 1) + 0.5) - scanData[day].count = scanData[day].count + 1 - - TSM.data[itemID].lastScan = TSM.db.factionrealm.lastCompleteScan - TSM.data[itemID].minBuyout = data.minBuyout > 0 and data.minBuyout or nil - TSM.data[itemID].quantity = data.quantity - Data:UpdateMarketValue(TSM.data[itemID]) - TSM:EncodeItemData(itemID) - + + -- Update our processing-index to point at the next item. index = index + 1 end end - + TSM.processingData = true TSMAPI:CreateTimeDelay("adbProcessDelay", 0, DoDataProcessing, 0.1) end -function Data:CalculateMarketValue(records) - local totalNum, totalBuyout = 0, 0 - local numRecords = #records - - for i=1, numRecords do - totalNum = i - 1 - if i ~= 1 and i > numRecords*MIN_PERCENTILE and (i > numRecords*MAX_PERCENTILE or records[i] >= MAX_JUMP*records[i-1]) then - break +--- Calculate the current market value of an item, from the given scan data. +-- @param data The market scan data. Beware that we will automatically mutate the "data.records" table to sort the incoming data! +-- @param[opt] hide_oldschool_warning Boolean 'true' to suppress the warning if you're using the old-school algorithm. This is only useful when benchmarking! +function Data:CalculateMarketValue(data, hide_oldschool_warning) + -- All auctions/stacks for this item (contains their price per item, and each stack's item count). + -- NOTE: The old-school algorithm instead uses bloated records (see description further down). + local records = data.records + + -- How many of this item currently exists in total on the auction house (combines all stacks). + -- NOTE: This is the sum of the per-stack counts of all "records", and can be trusted completely. + local total_quantity = data.quantity + + -- If we've been given zero records, return a market value of -1 to signal the issue. + -- NOTE: If we don't do this filtering, we would end up with "division by zero" below. + if (type(records) ~= "table") or (#records <= 0) then + return -1 + end + + + -- Determine which algorithm to use; either old-school or the smart, "compact" algorithm. + if type(records[1]) ~= "table" then + -- USE THE OLD, BRAINDEAD ALGORITHM IF WE'VE BEEN GIVEN OLD-SCHOOL "BLOATED" RECORDS. + -- NOTE: This old TSM algorithm relies on tables with millions of entries, + -- which often leads to out-of-memory crashes and is also extremely slow. + if not hide_oldschool_warning then + -- Warn if we've been called with old-school data and we haven't + -- been told to suppress this warning (benchmarks will suppress it). + TSM:Print("Warning: Calculating old-school market value. The calling code needs to be rewritten to use the new method!") + end + + local totalNum, totalBuyout = 0, 0 + local numRecords = #records + + -- See "STEP 1" of new algorithm for explanation about why we MUST sort. + tsort(records, function(a_buyout_per_item, b_buyout_per_item) + -- Sort by "per-item buyout" in ascending order. + return a_buyout_per_item < b_buyout_per_item + end) + + for i=1, numRecords do + totalNum = i - 1 + if i ~= 1 and i > numRecords*MIN_PERCENTILE and (i > numRecords*MAX_PERCENTILE or records[i] >= MAX_JUMP*records[i-1]) then + break + end + + totalBuyout = totalBuyout + records[i] + if i == numRecords then + totalNum = i + end end - - totalBuyout = totalBuyout + records[i] - if i == numRecords then - totalNum = i + + local uncorrectedMean = totalBuyout / totalNum + local variance = 0 + + for i=1, totalNum do + variance = variance + (records[i]-uncorrectedMean)^2 end - end - - local uncorrectedMean = totalBuyout / totalNum - local varience = 0 - - for i=1, totalNum do - varience = varience + (records[i]-uncorrectedMean)^2 - end - - local stdDev = sqrt(varience/totalNum) - local correctedTotalNum, correctedTotalBuyout = 1, uncorrectedMean - - for i=1, totalNum do - if abs(uncorrectedMean - records[i]) < 1.5*stdDev then - correctedTotalNum = correctedTotalNum + 1 - correctedTotalBuyout = correctedTotalBuyout + records[i] + + local stdDev = sqrt(variance/totalNum) + local correctedTotalNum, correctedTotalBuyout = 1, uncorrectedMean + + for i=1, totalNum do + if abs(uncorrectedMean - records[i]) < 1.5*stdDev then + correctedTotalNum = correctedTotalNum + 1 + correctedTotalBuyout = correctedTotalBuyout + records[i] + end + end + + local correctedMean = floor(correctedTotalBuyout / correctedTotalNum + 0.5) + + return correctedMean + else + -- Rewritten, cleaned up and faster algorithm, which uses almost zero memory + -- and NEVER causes any memory overflow crashes, unlike the old algorithm. + -- AUTHOR: Gnomezilla on Warmane-Icecrown [https://github.com/Bananaman]. + -- NOTE: This new algorithm is 1.3x-27.3x faster depending on input data + -- size, and on average 5x faster for most data. :) + -- NOTE: All code is heavily commented, to help other programmers understand + -- the complex algorith, and to avoid future breakages due to misunderstandings. + -- NOTE: TSM's intended algorithm is also documented online, but they + -- describe a slightly altered (more modern) algorithm than what we're using: + -- https://support.tradeskillmaster.com/en_US/custom-strings/how-is-auctiondb-market-value-calculated + -- Archived in case TSM deletes the page: https://archive.ph/LhSOI + + + -- How many of the cheapest items to consider (default: at least + -- 15%, at most 30% of the cheapest items). All items which are more + -- expensive than them are ignored. + -- NOTE: This is considered as the total of all items (combined + -- quantity of all items in all stacks). So if the first (cheapest) + -- stack is massive, and subsequent stacks are small, then we'll + -- only be calculating the value of the items of the 1st stack. + local idx_min_percentile = total_quantity * MIN_PERCENTILE + local idx_max_percentile = total_quantity * MAX_PERCENTILE + + -- Keep track of how many items we've processed and their combined buyout. + local processed_quantity, processed_total_buyout = 0, 0 + + -- Cutoff value for how much the "next auction" is allowed to cost, + -- so that we can ignore all overpriced items. Default: Any price + -- increase higher than 120% will discard all subsequent auctions. + -- NOTE: We only need to update this when we switch to another "stack", + -- and we're initializing it to a special "infinity" value (math.huge) + -- to ensure that we don't use this value until we've calculated it. + local max_jump_buyout_per_item = math.huge + + -- Keep track of the total "item index" we're at while we're traversing + -- through our compact "stacks". This emulates the classic way TSM + -- keeps track of the item/record counter. + local item_idx = 0 + + -- Used for signaling that we want to abort processing the remaining records. + local skip_remaining_records = false + + + -- STEP 1 (EXTREMELY IMPORTANT): We CANNOT trust the input. Our market + -- value algorithm ONLY WORKS if the prices are SORTED in ASCENDING ORDER, + -- but the auctions themselves are in RANDOM ORDER by default, in most + -- cases. So we MUST forcibly sort them now, otherwise we'll randomly + -- end up with very expensive items at the start of the list, which then + -- becomes extremely INCORRECT market values in our database, such as + -- thinking that "Wool Cloth" could be worth crazy amounts like "50 gold + -- per 1 wool cloth" instead of its real market value, thus breaking TSM! + tsort(records, function(a, b) + -- NOTE: Direct table refs instead of unpack() speeds up the sorting + -- by 3x, due to all the calls/comparisons involved when sorting. + local a_stack_size, a_buyout_per_item = a[1], a[2] + local b_stack_size, b_buyout_per_item = b[1], b[2] + + -- Sort by "per-item buyout" in ascending order, and use "stack size" + -- in ascending order as a fallback if two stacks have identical prices. + if a_buyout_per_item ~= b_buyout_per_item then + return a_buyout_per_item < b_buyout_per_item + else + return a_stack_size < b_stack_size + end + end) + + + -- STEP 2: Calculate the total buyout of all items we'll be processing. + + -- Process all of the compact "stack" records. + -- NOTE: Every record is 1 auction/stack, with a size and buyout-per-item. + for stack_idx=1, #records do + -- Fetch the stack size and buyout-per-item of this stack. + local stack_size, buyout_per_item = unpack(records[stack_idx]) + + -- Run TSM's algorithm for EVERY individual "item" in the stack's size, + -- so that we perform the same per-"item" work as their braindead algo. + -- NOTE: Does nothing if "stack_size" is <= 0. + -- NOTE: Technically, it would be possible to further optimize this + -- algorithm to "skip past entire stacks" all at once, instead of + -- processing every individual "virtual item from the stack", but + -- the algorithm is already so fast that adding extra complexity + -- is pointless and would just make it much harder to maintain! + for this_stack_item_idx=1, stack_size do + -- Increment the "total item index" that we're at now (across all stacks). + -- NOTE: Emulates TSM's counting of every "item" as individual records. + item_idx = item_idx + 1 + + -- Calculate the max allowed "buyout-per-item" price jump of the + -- current item. + -- NOTE: Yeah this leads to weirdness if we ended up including items + -- that were overpriced but were required by the "must process 15%+ + -- of all items no matter what" TSM requirement, but TSM fixes + -- that problem during later filtering "steps" of the algorithm. + -- NOTE: We're only recalculating the value when the "previous + -- item" changes, meaning that unlike the old-school TSM algo, + -- we don't waste time re-calculating it thousands of times in + -- a row for large stacks of identical "per-item" prices. + -- NOTE: This optimization has been verified to generate the EXACT + -- same behavior/result as the old-schoold TSM algorithm. + -- NOTE: Yes, item 1 of stack 1 keeps the pre-initialized value + -- of "math.huge (infinity)" (above), since we don't need it yet. + -- NOTE: In effect, we're ALWAYS comparing the CURRENT item's + -- buyout price to a "max buyout" calculated from the "PREVIOUS + -- item". This "stack magic" is just an optimization to reduce + -- the CPU usage of the algorithm! + if this_stack_item_idx == 1 and stack_idx >= 2 then + -- We're on the FIRST item of a NEW stack (not stack 1). + -- Re-calculate "max buyout price" based on PREVIOUS stack's price. + -- NOTE: We could cache the "previous" data to avoid the lookup + -- here, but we wouldn't really save any meaningful CPU usage + -- and it would be harder for other programmers to understand. + local previous_stack_size, previous_buyout_per_item = unpack(records[stack_idx - 1]) + max_jump_buyout_per_item = MAX_JUMP * previous_buyout_per_item + elseif this_stack_item_idx == 2 then + -- We're on the SECOND item of SAME stack (any stack, even stack 1). + -- Re-calculate "max buyout price" based on THIS stack's price. + max_jump_buyout_per_item = MAX_JUMP * buyout_per_item + end + + -- If we're on "total item index" 2 or higher, and we've processed + -- at least "min percentile" amount of records, AND we've -EITHER- + -- reached the "maximum percentile" too -OR- we've reached a severely + -- overpriced stack before we could reach the "maximum percentile", + -- and if so, we'll skip all other records (remaining stacks/items). + if item_idx >= 2 + and item_idx > idx_min_percentile + and (item_idx > idx_max_percentile or buyout_per_item >= max_jump_buyout_per_item) then + -- Signal that we want to skip the remaining records. + skip_remaining_records = true + break + end + + -- Keep track of the total buyout value of all processed items. + -- NOTE: This is the main purpose of this particular loop. + processed_total_buyout = processed_total_buyout + buyout_per_item + + -- Calculate "how many items we have processed", which is simply + -- the same as the current item index. + -- NOTE: TSM's classic algo does this as "item_idx - 1" because + -- they update this counter BEFORE they've decided whether to + -- actually process the current item. We instead only update this + -- after we've processed and incremented "total buyout" above, + -- which has the same end result but is much easier to understand. + -- NOTE: TSM's classic algo also has a weird chunk after this line, + -- which does "if item_idx == total_quantity then + -- processed_quantity = item_idx; end", which is complete nonsense + -- and was only needed due to their braindead sequence of updating + -- the "processed_quantity" variable, since they clearly just tried + -- to avoid having "processed_quantity = 0" when "total_quantity = 1", + -- but that was an extremely idiotic "fix" for their bad code. :) + processed_quantity = item_idx + end + + -- Exit from the nested loop if we've been told to stop processing records. + if skip_remaining_records then break end + end + + -- TSM:Print(format("processed_quantity: %d, processed_total_buyout: %d", processed_quantity, processed_total_buyout)) -- DEBUG + + + -- STEP 3: Calculate the mean (simple average) of all processed items. + + local uncorrected_mean = processed_total_buyout / processed_quantity + + + -- STEP 4: Calculate the standard deviation of all processed items. + + local variance = 0 + + -- Process all of the compact "stack" records again, but stop when we hit the limit. + -- NOTE: To understand this looping algorithm, look at STEP 2's comments above. + item_idx = 0 + skip_remaining_records = false + for stack_idx=1, #records do + local stack_size, buyout_per_item = unpack(records[stack_idx]) + for this_stack_item_idx=1, stack_size do + item_idx = item_idx + 1 + + -- Process up to and including "processed_quantity", but not higher. + if item_idx > processed_quantity then + skip_remaining_records = true + break + end + + -- Calculate the updated variance. + variance = variance + ((buyout_per_item - uncorrected_mean)^2) + end + + if skip_remaining_records then break end + end + + local std_dev = sqrt(variance / processed_quantity) + + -- TSM:Print(format("std_dev: %f, variance: %f", std_dev, variance)) -- DEBUG + + + -- STEP 5: Ignore all data points that are more than 1.5x std_dev away from the average. + + -- Initialize the "corrected" quantity and buyout with 1 "fake" item + -- that has the same value as the uncorrected mean. + -- NOTE: We're replicating TSM 2.8's classic algorithm here... even + -- though their algorithm might not be perfect. + local corrected_processed_quantity, corrected_processed_total_buyout = 1, uncorrected_mean + + -- Calculate the standard deviation cutoff. Anything further away will be ignored. + local std_dev_cutoff = 1.5 * std_dev + + -- Process all of the compact "stack" records again, but stop when we hit the limit. + -- NOTE: To understand this looping algorithm, look at STEP 2's comments above. + item_idx = 0 + skip_remaining_records = false + for stack_idx=1, #records do + local stack_size, buyout_per_item = unpack(records[stack_idx]) + + -- Speedup: Since the "buyout_per_item" only changes when we switch + -- to a different stack (all items in a stack have the same per-item + -- prices), we can therefore pre-calculate their deviation from the + -- average (mean) here, to avoid having to do it per-item below. + local abs_deviation_from_mean = abs(uncorrected_mean - buyout_per_item) + + -- Speedup: We can also pre-calculate whether the items of this "stack" + -- all fit the rule of "they're less than std_dev cutoff away from mean". + local stack_include_items = abs_deviation_from_mean < std_dev_cutoff + + -- Loop through all virtual "items" of the current "stack". + for this_stack_item_idx=1, stack_size do + item_idx = item_idx + 1 + + -- Process up to and including "processed_quantity", but not higher. + if item_idx > processed_quantity then + skip_remaining_records = true + break + end + + -- Calculate the filtered "quantity" and "total buyout", which + -- ignores anything that's more than "std_dev_cutoff" away from avg. + if stack_include_items then + corrected_processed_quantity = corrected_processed_quantity + 1 + corrected_processed_total_buyout = corrected_processed_total_buyout + buyout_per_item + end + end + + if skip_remaining_records then break end end + + -- TSM:Print(format("corrected_processed_quantity: %d, corrected_processed_total_buyout: %d", corrected_processed_quantity, corrected_processed_total_buyout)) -- DEBUG + + + -- STEP 6: Calculate our current market value by simply taking the + -- average of the remaining (filtered) data points. + -- NOTE: This method ensures that no poisoning of our market value can + -- take place by those who post high volume items at astronomical prices. + -- It also gets rid of more subtle outliers to determine the average. + + local corrected_mean = floor((corrected_processed_total_buyout / corrected_processed_quantity) + 0.5) + + + return corrected_mean end - - local correctedMean = floor(correctedTotalBuyout / correctedTotalNum + 0.5) - - return correctedMean end \ No newline at end of file diff --git a/TradeSkillMaster_AuctionDB/TradeSkillMaster_AuctionDB.lua b/TradeSkillMaster_AuctionDB/TradeSkillMaster_AuctionDB.lua index df7a908..82b1956 100644 --- a/TradeSkillMaster_AuctionDB/TradeSkillMaster_AuctionDB.lua +++ b/TradeSkillMaster_AuctionDB/TradeSkillMaster_AuctionDB.lua @@ -22,6 +22,7 @@ local savedDBDefaults = { scanData = "", time = 0, lastCompleteScan = 0, + lastScanSecondsPerPage = -1, appDataUpdate = 0, }, profile = { @@ -33,6 +34,7 @@ local savedDBDefaults = { marketValueTooltip = true, minBuyoutTooltip = true, showAHTab = true, + disableGetAll = false, }, } @@ -372,9 +374,10 @@ local function encodeScans(scans) local tbl, tbl2 = {}, {} for day, data in pairs(scans) do if type(data) == "table" and data.count and data.avg then + -- New method of encoding scans. data = encode(data.avg).."@"..encode(data.count) elseif type(data) == "table" then - -- Old method of encoding scans + -- Old method of encoding scans. for i = 1, #data do tbl2[i] = encode(data[i]) end @@ -394,37 +397,48 @@ local function decodeScans(rope) local currentDay = TSM.Data:GetDay() for _, data in ipairs(days) do local day, marketValueData = (":"):split(data) - day = decode(day) - scans[day] = {} - - --bug fix? ...SkillMaster_AuctionDB\TradeSkillMaster_AuctionDB.lua:398: bad argument #1 to 'strfind' (string expected, got nil) - if marketValueData ~= nil then - - if strfind(marketValueData, "@") then - local avg, count = ("@"):split(marketValueData) - avg = decode(avg) - count = decode(count) - if avg ~= "~" and count ~= "~" then - if abs(currentDay - day) <= TSM.MAX_AVG_DAY then - scans[day].avg = avg - scans[day].count = count - else - scans[day] = avg + -- BUG FIXED: Guard against incorrectly encoded "day" or "marketValueData", + -- which can happen extremely rarely due to some very rare, random bug + -- somewhere else in TSM (or perhaps due to mixing different versions + -- of TSM data). The cause of the rare corruption hasn't been found. + -- NOTE: We simply skip any "days/market values" that cannot be decoded, + -- which thereby ensures that we get a cleaned-up "decode" of the data, + -- so that TSM will then write the fixed data when it next "re-encodes" + -- the "decoded in-memory representation" of this item's data! + if day ~= nil and day ~= "" and marketValueData ~= nil and marketValueData ~= "" then + day = decode(day) + -- BUG FIXED: Verify yet again that the day itself was properly decoded, + -- but this time only check for "nil" which indicates "decode()" failure. + if day ~= nil then + -- Create a "scans" table entry for the decoded day. + scans[day] = {} + + if strfind(marketValueData, "@") then + -- New method of decoding scans. + local avg, count = ("@"):split(marketValueData) + avg = decode(avg) + count = decode(count) + if avg ~= "~" and count ~= "~" then + if abs(currentDay - day) <= TSM.MAX_AVG_DAY then + scans[day].avg = avg + scans[day].count = count + else + scans[day] = avg + end end - end - else - -- Old method of decoding scans - for _, value in ipairs({(";"):split(marketValueData)}) do - local decodedValue = decode(value) - if decodedValue ~= "~" then - tinsert(scans[day], tonumber(decodedValue)) + else + -- Old method of decoding scans. + for _, value in ipairs({(";"):split(marketValueData)}) do + local decodedValue = decode(value) + if decodedValue ~= "~" then + tinsert(scans[day], tonumber(decodedValue)) + end + end + if day ~= currentDay then + scans[day] = TSM.Data:GetAverage(scans[day]) end - end - if day ~= currentDay then - scans[day] = TSM.Data:GetAverage(scans[day]) end end - end end diff --git a/TradeSkillMaster_Auctioning/locale/enUS.lua b/TradeSkillMaster_Auctioning/locale/enUS.lua index da2f411..cb4fa71 100644 --- a/TradeSkillMaster_Auctioning/locale/enUS.lua +++ b/TradeSkillMaster_Auctioning/locale/enUS.lua @@ -222,6 +222,7 @@ L["Round Normal Price"] = true L["Running Scan..."] = true L["Save New Price"] = true L["Scan Complete!"] = true +L["Scanning %d / %d (Page 1 / ?)"] = true L["Scanning %d / %d (Page %d / %d)"] = true L["Scanning %d / %d"] = true L["ScrollWheel Direction (both recommended):"] = true diff --git a/TradeSkillMaster_Auctioning/locale/frFR.lua b/TradeSkillMaster_Auctioning/locale/frFR.lua index e578524..7f1c512 100644 --- a/TradeSkillMaster_Auctioning/locale/frFR.lua +++ b/TradeSkillMaster_Auctioning/locale/frFR.lua @@ -210,6 +210,7 @@ L["Right-Click to add %s to your friends list."] = "Clic-droit pour ajouter %s -- L["Save New Price"] = "" L["Scan Complete!"] = "Scan terminé !" -- Needs review L["Scanning %d / %d"] = "Scan %d / %d" -- Needs review +L["Scanning %d / %d (Page 1 / ?)"] = "Scan %d / %d (Page 1 / ?)" -- Needs review L["Scanning %d / %d (Page %d / %d)"] = "Scan %d / %d (Page %d / %d)" -- Needs review L["ScrollWheel Direction (both recommended):"] = "Direction de la molette (les deux sont recommandées)" -- L["Select a duration in this dropdown and click on the button below to cancel all auctions at or below this duration."] = "" diff --git a/TradeSkillMaster_Auctioning/locale/koKR.lua b/TradeSkillMaster_Auctioning/locale/koKR.lua index 9604102..2c7009c 100644 --- a/TradeSkillMaster_Auctioning/locale/koKR.lua +++ b/TradeSkillMaster_Auctioning/locale/koKR.lua @@ -210,6 +210,7 @@ L["Running Scan..."] = "검색 진행 중..." -- Needs review L["Save New Price"] = "새 가격 저장" -- Needs review L["Scan Complete!"] = "검색 완료!" -- Needs review L["Scanning %d / %d"] = "검색 중 %d / %d" -- Needs review +L["Scanning %d / %d (Page 1 / ?)"] = "검색 중 %d / %d (페이지 1 / ?)" -- Needs review L["Scanning %d / %d (Page %d / %d)"] = "검색 중 %d / %d (페이지 %d / %d)" -- Needs review L["ScrollWheel Direction (both recommended):"] = "스크롤휠 방향 (둘다 체크 추천)" L["Select a duration in this dropdown and click on the button below to cancel all auctions at or below this duration."] = "이 드롭다운에서 기간을 선택하고 아래의 버튼을 클릭하면 지정한 기간 또는 그 이하의 모든 경매는 취소됩니다." -- Needs review diff --git a/TradeSkillMaster_Auctioning/locale/ruRU.lua b/TradeSkillMaster_Auctioning/locale/ruRU.lua index 0eefd1f..0191b46 100644 --- a/TradeSkillMaster_Auctioning/locale/ruRU.lua +++ b/TradeSkillMaster_Auctioning/locale/ruRU.lua @@ -215,6 +215,7 @@ L["Running Scan..."] = "Запуск скана..." L["Save New Price"] = "Сохр.новую цену" L["Scan Complete!"] = "Скан завершен!" L["Scanning %d / %d"] = "Скан %d / %d" +L["Scanning %d / %d (Page 1 / ?)"] = "Скан %d / %d (стр 1 / ?)" L["Scanning %d / %d (Page %d / %d)"] = "Скан %d / %d (стр %d / %d)" L["ScrollWheel Direction (both recommended):"] = "Направление прокрутки колесика (рекомендованы оба):" L["Select a duration in this dropdown and click on the button below to cancel all auctions at or below this duration."] = "Выберите длительность из списка и кликните на кнопку ниже, чтобы отменить все акционы ниже этой длительности." diff --git a/TradeSkillMaster_Auctioning/locale/zhCN.lua b/TradeSkillMaster_Auctioning/locale/zhCN.lua index 95363f6..7fb326d 100644 --- a/TradeSkillMaster_Auctioning/locale/zhCN.lua +++ b/TradeSkillMaster_Auctioning/locale/zhCN.lua @@ -216,6 +216,7 @@ L["Running Scan..."] = "扫描中..." L["Save New Price"] = "保存新价格" L["Scan Complete!"] = "扫描完成!" L["Scanning %d / %d"] = "扫描中 %d/%d" +L["Scanning %d / %d (Page 1 / ?)"] = "扫描中 %d / %d (第1/?页)" L["Scanning %d / %d (Page %d / %d)"] = "扫描中 %d / %d (第%d/%d页)" L["ScrollWheel Direction (both recommended):"] = "滚轮方向 (建议都选):" L["Select a duration in this dropdown and click on the button below to cancel all auctions at or below this duration."] = "在本下拉列表中选择一个期限,然后点击下面的按钮来取消低于这个时间的拍卖。" diff --git a/TradeSkillMaster_Auctioning/modules/ScanUtil.lua b/TradeSkillMaster_Auctioning/modules/ScanUtil.lua index e6634ec..ecae6e1 100644 --- a/TradeSkillMaster_Auctioning/modules/ScanUtil.lua +++ b/TradeSkillMaster_Auctioning/modules/ScanUtil.lua @@ -17,10 +17,6 @@ Scan.skipped = {} local function CallbackHandler(event, ...) if event == "QUERY_COMPLETE" then local filterList = ... - local numItems = 0 - for _, v in ipairs(filterList) do - numItems = numItems + #v.items - end Scan.filterList = filterList Scan.numFilters = #filterList Scan:ScanNextFilter() @@ -32,8 +28,19 @@ local function CallbackHandler(event, ...) tinsert(Scan.skipped, itemString) end elseif event == "SCAN_PAGE_UPDATE" then + -- Simply forward the "currently received" and "total" page counts + -- for the current item we're scanning. + -- NOTE: Private servers sometimes give totally insane "total page counts" + -- on the first response, such as "page 1/142", but those server errors + -- always correct themselves to "page 2/REAL" when the scan has reached + -- page 2. There's nothing we can do to avoid that rare page-count issue + -- (which happens on many popular private servers, such as Warmane). TSM.Manage:UpdateStatus("page", ...) - elseif event == "SCAN_INTERRUPTED" then + elseif event == "SCAN_INTERRUPTED" or event == "INTERRUPTED" then + -- We've been interrupted by the Auction House closing. + -- NOTE: "SCAN_INTERRUPTED" is from LibAuctionScan-1.0, which isn't used + -- by TSM anymore, and "INTERRUPTED" is from "TSM/Auction/AuctionScanning.lua", + -- which is what this scanner uses nowadays. TSM.Manage:ScanComplete(true) elseif event == "SCAN_TIMEOUT" then tremove(Scan.filterList, 1) @@ -60,6 +67,16 @@ function Scan:StartItemScan(itemList) end function Scan:ScanNextFilter() + -- We must reset the page counter, otherwise the next scan will keep the + -- page count of the previous item until we receive "SCAN_PAGE_UPDATE". + -- NOTE: The "nil" signals that we don't know the item's page count yet. + -- NOTE: The recipient may want to ignore "page" events that have nil values + -- and not update their status bars based on those, since we send these empty + -- page events before we start each new scan! + TSM.Manage:UpdateStatus("page", nil, nil) + + -- Now update the scan counter. + -- NOTE: Our scan progress counter below starts counting from 0 as the first item. if #Scan.filterList == 0 then TSM.Manage:UpdateStatus("scan", Scan.numFilters, Scan.numFilters) return TSM.Manage:ScanComplete() diff --git a/TradeSkillMaster_Auctioning/modules/manage.lua b/TradeSkillMaster_Auctioning/modules/manage.lua index f211229..7541db3 100644 --- a/TradeSkillMaster_Auctioning/modules/manage.lua +++ b/TradeSkillMaster_Auctioning/modules/manage.lua @@ -85,6 +85,9 @@ end function Manage:ScanComplete(interrupted) if interrupted then + -- If our scan has been interrupted by the Auction House closing, + -- simply act as if the user clicked "Stop", but with an extra flag + -- to also clarify that this was interrupted. Util:Stop(true) else local numToManage = Util:DoneScanning() @@ -106,24 +109,40 @@ local function IsStepDone(step) end -- update the statusbar function Manage:UpdateStatus(statusType, current, total) + -- The ScanUtil.lua events trigger us with the "statusType" and the arguments + -- such as what page we're on. We then update our current scan status based on that. scanStatus[statusType] = {current, total} + + -- Ignore the empty "reset page count" events that happen before every new + -- scan. We've reset the page counts, which is the only thing that matters. + if statusType == "page" and current == nil and total == nil then + return + end + + -- Handle the progress event by updating our status bar. if statusType == "query" then if total >= 0 then GUI.statusBar:SetStatusText(format(L["Preparing Filter %d / %d"], current, total)) else GUI.statusBar:SetStatusText(format(L["Preparing Filters..."], current, total)) end - elseif IsStepDone("scan") and IsStepDone("manage") and IsStepDone("confirm") then -- scan complete + elseif IsStepDone("scan") and IsStepDone("manage") and IsStepDone("confirm") then + -- The entire scan of all items is complete. GUI.statusBar:SetStatusText(L["Scan Complete!"]) else local parts = {} if IsStepDone("scan") then tinsert(parts, L["Done Scanning"]) elseif IsStepStarted("scan") then + -- NOTE: In the label, we count the items starting at 1, to say + -- "Scanning 1 / 2" (instead of "Scanning 0 / 2"). if IsStepStarted("page") then - tinsert(parts, format(L["Scanning %d / %d (Page %d / %d)"], scanStatus.scan[1], scanStatus.scan[2], scanStatus.page[1], scanStatus.page[2])) + -- We have received the page counter ("current page / total pages") for the current item. + -- NOTE: We add "+1" to the page counter, to indicate that we've received that page and are working on the next page. + tinsert(parts, format(L["Scanning %d / %d (Page %d / %d)"], scanStatus.scan[1] + 1, scanStatus.scan[2], min(scanStatus.page[1] + 1, scanStatus.page[2]), scanStatus.page[2])) else - tinsert(parts, format(L["Scanning %d / %d"], scanStatus.scan[1], scanStatus.scan[2])) + -- We have started a new item scan but haven't received the page count yet. + tinsert(parts, format(L["Scanning %d / %d (Page 1 / ?)"], scanStatus.scan[1] + 1, scanStatus.scan[2])) end end if IsStepDone("manage") then @@ -133,25 +152,25 @@ function Manage:UpdateStatus(statusType, current, total) tinsert(parts, L["Done Canceling"]) end if IsStepStarted("confirm") then - tinsert(parts, format(L["Confirming %d / %d"], scanStatus.confirm[1]+1, scanStatus.confirm[2])) + tinsert(parts, format(L["Confirming %d / %d"], scanStatus.confirm[1] + 1, scanStatus.confirm[2])) else tinsert(parts, format(L["Confirming %d / %d"], 1, scanStatus.manage[2])) end elseif IsStepDone("scan") and IsStepStarted("manage") then if mode == "Post" then - tinsert(parts, format(L["Posting %d / %d"], scanStatus.manage[1]+1, scanStatus.manage[2])) + tinsert(parts, format(L["Posting %d / %d"], scanStatus.manage[1] + 1, scanStatus.manage[2])) elseif mode == "Cancel" then - tinsert(parts, format(L["Canceling %d / %d"], scanStatus.manage[1]+1, scanStatus.manage[2])) + tinsert(parts, format(L["Canceling %d / %d"], scanStatus.manage[1] + 1, scanStatus.manage[2])) end if IsStepStarted("confirm") then - tinsert(parts, format(L["Confirming %d / %d"], scanStatus.confirm[1]+1, scanStatus.confirm[2])) + tinsert(parts, format(L["Confirming %d / %d"], scanStatus.confirm[1] + 1, scanStatus.confirm[2])) else tinsert(parts, format(L["Confirming %d / %d"], 1, scanStatus.manage[2])) end end GUI.statusBar:SetStatusText(table.concat(parts, " - ")) end - + if IsStepDone("query") then local scanCurrent = scanStatus.scan and scanStatus.scan[1] or 0 local scanTotal = scanStatus.scan and scanStatus.scan[2] or 1 diff --git a/TradeSkillMaster_Crafting/Modules/Cost.lua b/TradeSkillMaster_Crafting/Modules/Cost.lua index 574962f..0cf0971 100644 --- a/TradeSkillMaster_Crafting/Modules/Cost.lua +++ b/TradeSkillMaster_Crafting/Modules/Cost.lua @@ -72,12 +72,12 @@ function Cost:GetCraftCost(itemID) for matID, matQuantity in pairs(craft.mats) do local MatName = GetItemInfo(matID) - -- if MatName ~= nil and strfind(MatName, "Vellum") then - -- local NewItemString = CheapestVellum(matID) - -- if matID ~= NewItemString then - -- matID = NewItemString - -- end - -- end + if MatName ~= nil and strfind(MatName, "Vellum") then + local NewItemString = CheapestVellum(matID) + if matID ~= NewItemString then + matID = NewItemString + end + end local matCost = Cost:GetMatCost(matID) if not matCost or matCost == 0 then costIsValid = false diff --git a/TradeSkillMaster_Crafting/Modules/CraftingGUI.lua b/TradeSkillMaster_Crafting/Modules/CraftingGUI.lua index 7778129..5c439b9 100644 --- a/TradeSkillMaster_Crafting/Modules/CraftingGUI.lua +++ b/TradeSkillMaster_Crafting/Modules/CraftingGUI.lua @@ -675,13 +675,19 @@ function GUI:CreateQueueFrame(parent) if strfind(name, "Vellum") then velName = name end - if (velName ~= nil) then - if strfind(velName, "Weapon") then - itemIDx = "item:52511:0:0:0:0:0:0" - name = TSMAPI:GetSafeItemInfo(itemIDx) + if (velName ~= nil) and (not strfind(velName, "III")) then + local VellumReplacePrice = TSM.Cost:GetMatCost(itemIDx) + + if strfind(velName, "Weapon Vellum") then + if VellumReplacePrice > TSM.Cost:GetMatCost("item:43146:0:0:0:0:0:0") then + itemIDx = "item:43146:0:0:0:0:0:0" + name = TSMAPI:GetSafeItemInfo(itemIDx) + end else - itemIDx = "item:52510:0:0:0:0:0:0" - name = TSMAPI:GetSafeItemInfo(itemIDx) + if VellumReplacePrice > TSM.Cost:GetMatCost("item:43145:0:0:0:0:0:0") then + itemIDx = "item:43145:0:0:0:0:0:0" + name = TSMAPI:GetSafeItemInfo(itemIDx) + end end end @@ -1449,7 +1455,7 @@ function GUI:CreateCraftInfoFrame(parent) -- Enable display of items created local lNum, hNum = GetTradeSkillNumMade(skillIndex) local numMade = floor(((lNum or 1) + (hNum or 1)) / 2) - if altVerb ~= nil and strfind(name,"Enchant ") then + if altVerb == ENSCRIBE then numMade = 1 end if numMade > 1 then @@ -1486,14 +1492,15 @@ function GUI:CreateCraftInfoFrame(parent) end end - -- if altVerb == ENSCRIBE then - if altVerb ~= nil and strfind(name,"Enchant ") then + if altVerb == ENSCRIBE then createAllBtn:SetText(L["Enchant Vellum"]) + -- createAllBtn.vellum = TSMAPI:GetSafeItemInfo("item:38682:0:0:0:0:0:0") if strfind(name, "Weapon") or strfind(name, "Staff") then - createAllBtn.vellum = TSMAPI:GetSafeItemInfo("item:52511:0:0:0:0:0:0") -- Weapon Vellum + createAllBtn.vellum = TSMAPI:GetSafeItemInfo("item:43146:0:0:0:0:0:0") -- Weapon Vellum III else - createAllBtn.vellum = TSMAPI:GetSafeItemInfo("item:52510:0:0:0:0:0:0") -- Armor Vellum + createAllBtn.vellum = TSMAPI:GetSafeItemInfo("item:43145:0:0:0:0:0:0") -- Armor Vellum III end + else createAllBtn:SetText(CREATE_ALL) createAllBtn.vellum = nil @@ -1817,6 +1824,12 @@ function GUI:UpdateQueue() canCraft = min(canCraft, floor(numHave / quantity)) end + -- local velName + -- local VELLUM_ID = "item:38682:0:0:0:0:0:0" + -- if TSM.db.factionrealm.crafts[spellID].mats[VELLUM_ID] then + -- velName = GetItemInfo(VELLUM_ID) or TSM.db.factionrealm.mats[VELLUM_ID].name + -- end + local color local craftIndex = skillIndexLookup[spellID] if canCraft >= numQueued then @@ -2740,7 +2753,7 @@ end function CheapestVellum(itemPassed) - -- Return one of the two vellum available + -- Get Cheapest vellum, lower vellum types can be replaced by III local MatName = GetItemInfo(itemPassed) -- MatName is sometimes nil ??? if MatName ~= nil then @@ -2748,11 +2761,12 @@ function CheapestVellum(itemPassed) if strfind(MatName, "Vellum") then velName = MatName end - if (velName ~= nil) then - if strfind(velName, "Weapon") then - itemPassed = "item:52511:0:0:0:0:0:0" + if (velName ~= nil) and (not strfind(velName, "III")) then + local VellumReplacePrice = TSM.Cost:GetMatCost(itemPassed) or 0 + if strfind(velName, "Weapon Vellum") then + if VellumReplacePrice > (TSM.Cost:GetMatCost("item:43146:0:0:0:0:0:0") or 0) then itemPassed = "item:43146:0:0:0:0:0:0" end else - itemPassed = "item:52510:0:0:0:0:0:0" + if VellumReplacePrice > (TSM.Cost:GetMatCost("item:43145:0:0:0:0:0:0") or 0) then itemPassed = "item:43145:0:0:0:0:0:0" end end end end diff --git a/TradeSkillMaster_Crafting/Modules/EnchantingInfo.lua b/TradeSkillMaster_Crafting/Modules/EnchantingInfo.lua index d3f8e75..fa4c5da 100644 --- a/TradeSkillMaster_Crafting/Modules/EnchantingInfo.lua +++ b/TradeSkillMaster_Crafting/Modules/EnchantingInfo.lua @@ -14,612 +14,590 @@ TSM.enchantingName = GetSpellInfo(7411) -- looks up the itemID of the scroll that the enchant makes -- index = spellID of the enchant -- value = itemID of scroll --- Enchant List: - -- WRATH - -- [44506] = 38960, -- Enchant Gloves - Gatherer - -- [44484] = 38951, -- Enchant Gloves - Expertise - -- [44488] = 38953, -- Enchant Gloves - Precision - -- [44489] = 38954, -- Enchant Shield - Dodge - -- [44492] = 38955, -- Enchant Chest - Mighty Health - -- [44500] = 38959, -- Enchant Cloak - Superior Agility - -- [44508] = 38961, -- Enchant Boots - Greater Spirit - -- [44509] = 38962, -- Enchant Chest - Greater Mana Restoration - -- [44513] = 38964, -- Enchant Gloves - Greater Assault - -- [44528] = 38966, -- Enchant Boots - Greater Fortitude - -- [44529] = 38967, -- Enchant Gloves - Major Agility - -- [44555] = 38968, -- Enchant Bracer - Exceptional Intellect - -- [44582] = 38973, -- Enchant Cloak - Minor Power - -- [44584] = 38974, -- Enchant Boots - Greater Vitality - -- [44588] = 38975, -- Enchant Chest - Exceptional Resilience - -- [44589] = 38976, -- Enchant Boots - Superior Agility - -- [44591] = 38978, -- Enchant Cloak - Superior Dodge - -- [44592] = 38979, -- Enchant Gloves - Exceptional Spellpower - -- [44593] = 38980, -- Enchant Bracer - Major Spirit - -- [44598] = 38984, -- Enchant Bracer - Expertise - -- [44616] = 38987, -- Enchant Bracer - Greater Stats - -- [44623] = 38989, -- Enchant Chest - Super Stats - -- [44625] = 38990, -- Enchant Gloves - Armsman - -- [44631] = 38993, -- Enchant Cloak - Shadow Armor - -- [44635] = 38997, -- Enchant Bracer - Greater Spellpower - -- [44575] = 44815, -- Enchant Bracer - Greater Assault - -- [44510] = 38963, -- Enchant Weapon - Exceptional Spirit - -- [44524] = 38965, -- Enchant Weapon - Icebreaker - -- [44576] = 38972, -- Enchant Weapon - Lifeward - -- [44595] = 38981, -- Enchant 2H Weapon - Scourgebane - -- [44621] = 38988, -- Enchant Weapon - Giant Slayer - -- [44629] = 38991, -- Enchant Weapon - Exceptional Spellpower - -- [44630] = 38992, -- Enchant 2H Weapon - Greater Savagery - -- [44633] = 38995, -- Enchant Weapon - Exceptional Agility - -- [44383] = 38949, -- Enchant Shield - Resilience - -- [46594] = 38999, -- Enchant Chest - Dodge - -- [46578] = 38998, -- Enchant Weapon - Deathfrost - -- [47051] = 39000, -- Enchant Cloak - Greater Dodge - -- [47672] = 39001, -- Enchant Cloak - Mighty Stamina - -- [47766] = 39002, -- Enchant Chest - Greater Dodge - -- [47898] = 39003, -- Enchant Cloak - Greater Speed - -- [47899] = 39004, -- Enchant Cloak - Wisdom - -- [47900] = 39005, -- Enchant Chest - Super Health - -- [47901] = 39006, -- Enchant Boots - Tuskarr's Vitality - -- [59625] = 43987, -- Enchant Weapon - Black Magic - -- [59621] = 44493, -- Enchant Weapon - Berserking - -- [59619] = 44497, -- Enchant Weapon - Accuracy - -- [60616] = 38971, -- Enchant Bracer - Assault - -- [60623] = 38986, -- Enchant Boots - Icewalker - -- [60606] = 44449, -- Enchant Boots - Assault - -- [60609] = 44456, -- Enchant Cloak - Speed - -- [60663] = 44457, -- Enchant Cloak - Major Agility - -- [60668] = 44458, -- Enchant Gloves - Crusher - -- [60692] = 44465, -- Enchant Chest - Powerful Stats - -- [60763] = 44469, -- Enchant Boots - Greater Assault - -- [60767] = 44470, -- Enchant Bracer - Superior Spellpower - -- [60621] = 44453, -- Enchant Weapon - Greater Potency - -- [60691] = 44463, -- Enchant 2H Weapon - Massacre - -- [60707] = 44466, -- Enchant Weapon - Superior Potency - -- [60714] = 44467, -- Enchant Weapon - Mighty Spellpower - -- [62256] = 44947, -- Enchant Bracer - Major Stamina - -- [62948] = 45056, -- Enchant Staff - Greater Spellpower - -- [62959] = 45060, -- Enchant Staff - Spellpower - -- [63746] = 45628, -- Enchant Boots - Lesser Accuracy - -- [64441] = 46026, -- Enchant Weapon - Blade Ward - -- [64579] = 46098, -- Enchant Weapon - Blood Draining - -- [71692] = 50816, -- Enchant Gloves - Angler - - -- TBC - -- [33990] = 38928, -- Enchant Chest - Major Spirit - -- [33991] = 38929, -- Enchant Chest - Restore Mana Prime - -- [33992] = 38930, -- Enchant Chest - Major Resilience - -- [33993] = 38931, -- Enchant Gloves - Blasting - -- [33994] = 38932, -- Enchant Gloves - Precise Strikes - -- [33995] = 38933, -- Enchant Gloves - Major Strength - -- [33996] = 38934, -- Enchant Gloves - Assault - -- [33997] = 38935, -- Enchant Gloves - Major Spellpower - -- [33999] = 38936, -- Enchant Gloves - Major Healing - -- [34001] = 38937, -- Enchant Bracer - Major Intellect - -- [34002] = 38938, -- Enchant Bracer - Lesser Assault - -- [34003] = 38939, -- Enchant Cloak - Spell Penetration - -- [34004] = 38940, -- Enchant Cloak - Greater Agility - -- [34007] = 38943, -- Enchant Boots - Cat's Swiftness - -- [34008] = 38944, -- Enchant Boots - Boar's Speed - -- [34009] = 38945, -- Enchant Shield - Major Stamina - -- [34010] = 38946, -- Enchant Weapon - Major Healing - -- [42974] = 38948, -- Enchant Weapon - Executioner - -- [42620] = 38947, -- Enchant Weapon - Greater Agility - - -- VANILLA - -- [7745] = 38772, -- Enchant 2H Weapon - Minor Impact - -- [7786] = 38779, -- Enchant Weapon - Minor Beastslayer - -- [7788] = 38780, -- Enchant Weapon - Minor Striking - -- [7793] = 38781, -- Enchant 2H Weapon - Lesser Intellect - -- [7418] = 38679, -- Enchant Bracer - Minor Health - -- [7420] = 38766, -- Enchant Chest - Minor Health - -- [7426] = 38767, -- Enchant Chest - Minor Absorption - -- [7428] = 38768, -- Enchant Bracer - Minor Dodge - -- [7443] = 38769, -- Enchant Chest - Minor Mana - -- [7457] = 38771, -- Enchant Bracer - Minor Stamina - -- [7748] = 38773, -- Enchant Chest - Lesser Health - -- [7766] = 38774, -- Enchant Bracer - Minor Spirit - -- [7771] = 38775, -- Enchant Cloak - Minor Protection - -- [7776] = 38776, -- Enchant Chest - Lesser Mana - -- [7779] = 38777, -- Enchant Bracer - Minor Agility - -- [7782] = 38778, -- Enchant Bracer - Minor Strength - -- [7857] = 38782, -- Enchant Chest - Health - -- [7859] = 38783, -- Enchant Bracer - Lesser Spirit - -- [7863] = 38785, -- Enchant Boots - Minor Stamina - -- [7867] = 38786, -- Enchant Boots - Minor Agility - -- [13378] = 38787, -- Enchant Shield - Minor Stamina - -- [13419] = 38789, -- Enchant Cloak - Minor Agility - -- [13421] = 38790, -- Enchant Cloak - Lesser Protection - -- [13464] = 38791, -- Enchant Shield - Lesser Protection - -- [13485] = 38792, -- Enchant Shield - Lesser Spirit - -- [13501] = 38793, -- Enchant Bracer - Lesser Stamina - -- [13536] = 38797, -- Enchant Bracer - Lesser Strength - -- [13538] = 38798, -- Enchant Chest - Lesser Absorption - -- [13607] = 38799, -- Enchant Chest - Mana - -- [13612] = 38800, -- Enchant Gloves - Mining - -- [13617] = 38801, -- Enchant Gloves - Herbalism - -- [13620] = 38802, -- Enchant Gloves - Fishing - -- [13622] = 38803, -- Enchant Bracer - Lesser Intellect - -- [13626] = 38804, -- Enchant Chest - Minor Stats - -- [13631] = 38805, -- Enchant Shield - Lesser Stamina - -- [13635] = 38806, -- Enchant Cloak - Defense - -- [13637] = 38807, -- Enchant Boots - Lesser Agility - -- [13640] = 38808, -- Enchant Chest - Greater Health - -- [13642] = 38809, -- Enchant Bracer - Spirit - -- [13644] = 38810, -- Enchant Boots - Lesser Stamina - -- [13646] = 38811, -- Enchant Bracer - Lesser Dodge - -- [13648] = 38812, -- Enchant Bracer - Stamina - -- [13659] = 38816, -- Enchant Shield - Spirit - -- [13661] = 38817, -- Enchant Bracer - Strength - -- [13663] = 38818, -- Enchant Chest - Greater Mana - -- [13687] = 38819, -- Enchant Boots - Lesser Spirit - -- [13689] = 38820, -- Enchant Shield - Lesser Parry - -- [13698] = 38823, -- Enchant Gloves - Skinning - -- [13700] = 38824, -- Enchant Chest - Lesser Stats - -- [13746] = 38825, -- Enchant Cloak - Greater Defense - -- [13815] = 38827, -- Enchant Gloves - Agility - -- [13817] = 38828, -- Enchant Shield - Stamina - -- [13822] = 38829, -- Enchant Bracer - Intellect - -- [13836] = 38830, -- Enchant Boots - Stamina - -- [13841] = 38831, -- Enchant Gloves - Advanced Mining - -- [13846] = 38832, -- Enchant Bracer - Greater Spirit - -- [13858] = 38833, -- Enchant Chest - Superior Health - -- [13868] = 38834, -- Enchant Gloves - Advanced Herbalism - -- [13882] = 38835, -- Enchant Cloak - Lesser Agility - -- [13887] = 38836, -- Enchant Gloves - Strength - -- [13890] = 38837, -- Enchant Boots - Minor Speed - -- [13905] = 38839, -- Enchant Shield - Greater Spirit - -- [13917] = 38841, -- Enchant Chest - Superior Mana - -- [13931] = 38842, -- Enchant Bracer - Dodge - -- [13935] = 38844, -- Enchant Boots - Agility - -- [13939] = 38846, -- Enchant Bracer - Greater Strength - -- [13941] = 38847, -- Enchant Chest - Stats - -- [13945] = 38849, -- Enchant Bracer - Greater Stamina - -- [13947] = 38850, -- Enchant Gloves - Riding Skill - -- [13948] = 38851, -- Enchant Gloves - Minor Haste - -- [13380] = 38788, -- Enchant 2H Weapon - Lesser Spirit - -- [13503] = 38794, -- Enchant Weapon - Lesser Striking - -- [13529] = 38796, -- Enchant 2H Weapon - Lesser Impact - -- [13653] = 38813, -- Enchant Weapon - Lesser Beastslayer - -- [13655] = 38814, -- Enchant Weapon - Lesser Elemental Slayer - -- [13693] = 38821, -- Enchant Weapon - Striking - -- [13695] = 38822, -- Enchant 2H Weapon - Impact - -- [13898] = 38838, -- Enchant Weapon - Fiery Weapon - -- [13915] = 38840, -- Enchant Weapon - Demonslaying - -- [13937] = 38845, -- Enchant 2H Weapon - Greater Impact - -- [13943] = 38848, -- Enchant Weapon - Greater Striking - -- [20008] = 38852, -- Enchant Bracer - Greater Intellect - -- [20009] = 38853, -- Enchant Bracer - Superior Spirit - -- [20010] = 38854, -- Enchant Bracer - Superior Strength - -- [20011] = 38855, -- Enchant Bracer - Superior Stamina - -- [20012] = 38856, -- Enchant Gloves - Greater Agility - -- [20013] = 38857, -- Enchant Gloves - Greater Strength - -- [20015] = 38859, -- Enchant Cloak - Superior Defense - -- [20016] = 38860, -- Enchant Shield - Vitality - -- [20017] = 38861, -- Enchant Shield - Greater Stamina - -- [20020] = 38862, -- Enchant Boots - Greater Stamina - -- [20023] = 38863, -- Enchant Boots - Greater Agility - -- [20024] = 38864, -- Enchant Boots - Spirit - -- [20025] = 38865, -- Enchant Chest - Greater Stats - -- [20026] = 38866, -- Enchant Chest - Major Health - -- [20028] = 38867, -- Enchant Chest - Major Mana - -- [20029] = 38868, -- Enchant Weapon - Icy Chill - -- [20030] = 38869, -- Enchant 2H Weapon - Superior Impact - -- [20031] = 38870, -- Enchant Weapon - Superior Striking - -- [20032] = 38871, -- Enchant Weapon - Lifestealing - -- [20033] = 38872, -- Enchant Weapon - Unholy Weapon - -- [20034] = 38873, -- Enchant Weapon - Crusader - -- [20035] = 38874, -- Enchant 2H Weapon - Major Spirit - -- [20036] = 38875, -- Enchant 2H Weapon - Major Intellect - -- [21931] = 38876, -- Enchant Weapon - Winter's Might - -- [22749] = 38877, -- Enchant Weapon - Spellpower - -- [22750] = 38878, -- Enchant Weapon - Healing Power - -- [23799] = 38879, -- Enchant Weapon - Strength - -- [23800] = 38880, -- Enchant Weapon - Agility - -- [23803] = 38883, -- Enchant Weapon - Mighty Spirit - -- [23804] = 38884, -- Enchant Weapon - Mighty Intellect - -- [23801] = 38881, -- Enchant Bracer - Mana Regeneration - -- [23802] = 38882, -- Enchant Bracer - Healing Power - -- [25072] = 38885, -- Enchant Gloves - Threat - -- [25073] = 38886, -- Enchant Gloves - Shadow Power - -- [25074] = 38887, -- Enchant Gloves - Frost Power - -- [25078] = 38888, -- Enchant Gloves - Fire Power - -- [25079] = 38889, -- Enchant Gloves - Healing Power - -- [25080] = 38890, -- Enchant Gloves - Superior Agility - -- [25083] = 38893, -- Enchant Cloak - Stealth - -- [25084] = 38894, -- Enchant Cloak - Subtlety - -- [25086] = 38895, -- Enchant Cloak - Dodge - -- [27837] = 38896, -- Enchant 2H Weapon - Agility - -- [27951] = 37603, -- Enchant Boots - Dexterity - -- [27958] = 38912, -- Enchant Chest - Exceptional Mana - -- [27899] = 38897, -- Enchant Bracer - Brawn - -- [27905] = 38898, -- Enchant Bracer - Stats - -- [27906] = 38899, -- Enchant Bracer - Greater Dodge - -- [27911] = 38900, -- Enchant Bracer - Superior Healing - -- [27913] = 38901, -- Enchant Bracer - Restore Mana Prime - -- [27914] = 38902, -- Enchant Bracer - Fortitude - -- [27917] = 38903, -- Enchant Bracer - Spellpower - -- [27944] = 38904, -- Enchant Shield - Lesser Dodge - -- [27945] = 38905, -- Enchant Shield - Intellect - -- [27946] = 38906, -- Enchant Shield - Parry - -- [27948] = 38908, -- Enchant Boots - Vitality - -- [27950] = 38909, -- Enchant Boots - Fortitude - -- [27954] = 38910, -- Enchant Boots - Surefooted - -- [27957] = 38911, -- Enchant Chest - Exceptional Health - -- [27960] = 38913, -- Enchant Chest - Exceptional Stats - -- [27961] = 38914, -- Enchant Cloak - Major Armor - -- [27967] = 38917, -- Enchant Weapon - Major Striking - -- [27968] = 38918, -- Enchant Weapon - Major Intellect - -- [27971] = 38919, -- Enchant 2H Weapon - Savagery - -- [27972] = 38920, -- Enchant Weapon - Potency - -- [27975] = 38921, -- Enchant Weapon - Major Spellpower - -- [27977] = 38922, -- Enchant 2H Weapon - Major Agility - -- [27981] = 38923, -- Enchant Weapon - Sunfire - -- [27982] = 38924, -- Enchant Weapon - Soulfrost - -- [27984] = 38925, -- Enchant Weapon - Mongoose - -- [28003] = 38926, -- Enchant Weapon - Spellsurge - -- [28004] = 38927, -- Enchant Weapon - Battlemaster - - -- High Risk Ascension Content - -- [968676] = 967760, -- Enchant Weapon - Unstoppable Assault I - -- [968677] = 967761, -- Enchant Weapon - Unstoppable Assault II - -- [968678] = 967762, -- Enchant Weapon - Unstoppable Assault III - -- [968679] = 967763, -- Enchant Weapon - Lucid Assault I - -- [968680] = 967764, -- Enchant Weapon - Lucid Assault II - -- [968681] = 967765, -- Enchant Weapon - Lucid Assault III - -- [968682] = 967766, -- Enchant Weapon - Spellbinder's Rage I - -- [968683] = 967767, -- Enchant Weapon - Spellbinder's Rage II - -- [968684] = 967768, -- Enchant Weapon - Spellbinder's Rage III - -- [968685] = 967769, -- Enchant Weapon - Ninja's Focus I - -- [968686] = 967770, -- Enchant Weapon - Ninja's Focus II - -- [968687] = 967771, -- Enchant Weapon - Ninja's Focus III - -- [968688] = 967772, -- Enchant Weapon - Grovewarden's Blessing I - -- [968689] = 967773, -- Enchant Weapon - Grovewarden's Blessing II - -- [968690] = 967774, -- Enchant Weapon - Grovewarden's Blessing III - -- [968691] = 967775, -- Enchant Weapon - Viscious Assault I - -- [968692] = 967776, -- Enchant Weapon - Viscious Assault II - -- [968693] = 967777, -- Enchant Weapon - Viscious Assault III - -- [968694] = 967778, -- Enchant Weapon - Arcane Dexterity I - -- [968695] = 967779, -- Enchant Weapon - Arcane Dexterity II - -- [968696] = 967780, -- Enchant Weapon - Arcane Dexterity III - -- [968697] = 967781, -- Enchant Weapon - Arcane Artillery I - -- [968698] = 967782, -- Enchant Weapon - Arcane Artillery II - -- [968699] = 967783, -- Enchant Weapon - Arcane Artillery III - -- [968700] = 967784, -- Enchant Weapon - Arcane Precision I - -- [968701] = 967785, -- Enchant Weapon - Arcane Precision II - -- [968702] = 967786, -- Enchant Weapon - Arcane Precision III - -- [968770] = 967787, -- Enchant Weapon - Crusader II - -- [968771] = 967788, -- Enchant Weapon - Crusader III - -- [1968677] = 1204125, -- Enchant Weapon - Void Assault - -- [1968678] = 1204126, -- Enchant Weapon - Overpowering Void Assault - -- [1968680] = 1204127, -- Enchant Weapon - Dread Assault - -- [1968681] = 1204128, -- Enchant Weapon - Overpowering Dread Assault - -- [1968683] = 1204129, -- Enchant Weapon - Twisted Evoker - -- [1968684] = 1204130, -- Enchant Weapon - Overpowering Twisted Evoker - -- [1968686] = 1204131, -- Enchant Weapon - Twisted Assault - -- [1968687] = 1204132, -- Enchant Weapon - Overpowering Twisted Assault - -- [1968689] = 1204133, -- Enchant Weapon - Twisted Channeler - -- [1968690] = 1204134, -- Enchant Weapon - Overpowering Twisted Channeler - -- [1968692] = 1204135, -- Enchant Weapon - Dread Omen Strikes - -- [1968693] = 1204136, -- Enchant Weapon - Overpowering Dread Omen Strikes - -- [1968695] = 1204137, -- Enchant Weapon - Void Flows - -- [1968696] = 1204138, -- Enchant Weapon - Overpowering Void Flows - -- [1968698] = 1204139, -- Enchant Weapon - Void Blasting - -- [1968699] = 1204140, -- Enchant Weapon - Overpowering Void Blasting - -- [1968701] = 1204141, -- Enchant Weapon - Dread Precision - -- [1968702] = 1204142, -- Enchant Weapon - Overpowering Dread Precision - -- [1968770] = 1204143, -- Enchant Weapon - Twisted Crusader - -- [1968771] = 1204144, -- Enchant Weapon - Overpowering Twisted Crusader --- - - TSM.enchantingItemIDs = { - [7418] = 38679, -- Scroll of Enchant Bracer - Minor Health - [7420] = 38766, -- Scroll of Enchant Chest - Minor Health - [7426] = 38767, -- Scroll of Enchant Chest - Minor Absorption - [7428] = 38768, -- Scroll of Enchant Bracer - Minor Deflection - [7443] = 38769, -- Scroll of Enchant Chest - Minor Mana - [7454] = 38770, -- Scroll of Enchant Cloak - Minor Resistance - [7457] = 38771, -- Scroll of Enchant Bracer - Minor Stamina - [7745] = 38772, -- Scroll of Enchant 2H Weapon - Minor Impact - [7748] = 38773, -- Scroll of Enchant Chest - Lesser Health - [7766] = 38774, -- Scroll of Enchant Bracer - Minor Spirit - [7771] = 38775, -- Scroll of Enchant Cloak - Minor Protection - [7776] = 38776, -- Scroll of Enchant Chest - Lesser Mana - [7779] = 38777, -- Scroll of Enchant Bracer - Minor Agility - [7782] = 38778, -- Scroll of Enchant Bracer - Minor Strength - [7786] = 38779, -- Scroll of Enchant Weapon - Minor Beastslayer - [7788] = 38780, -- Scroll of Enchant Weapon - Minor Striking - [7793] = 38781, -- Scroll of Enchant 2H Weapon - Lesser Intellect - [7857] = 38782, -- Scroll of Enchant Chest - Health - [7859] = 38783, -- Scroll of Enchant Bracer - Lesser Spirit - [7861] = 38784, -- Scroll of Enchant Cloak - Lesser Fire Resistance - [7863] = 38785, -- Scroll of Enchant Boots - Minor Stamina - [7867] = 38786, -- Scroll of Enchant Boots - Minor Agility - [13378] = 38787, -- Scroll of Enchant Shield - Minor Stamina - [13380] = 38788, -- Scroll of Enchant 2H Weapon - Lesser Spirit - [13419] = 38789, -- Scroll of Enchant Cloak - Minor Agility - [13421] = 38790, -- Scroll of Enchant Cloak - Lesser Protection - [13464] = 38791, -- Scroll of Enchant Shield - Lesser Protection - [13485] = 38792, -- Scroll of Enchant Shield - Lesser Spirit - [13501] = 38793, -- Scroll of Enchant Bracer - Lesser Stamina - [13503] = 38794, -- Scroll of Enchant Weapon - Lesser Striking - [13522] = 38795, -- Scroll of Enchant Cloak - Lesser Shadow Resistance - [13529] = 38796, -- Scroll of Enchant 2H Weapon - Lesser Impact - [13536] = 38797, -- Scroll of Enchant Bracer - Lesser Strength - [13538] = 38798, -- Scroll of Enchant Chest - Lesser Absorption - [13607] = 38799, -- Scroll of Enchant Chest - Mana - [13612] = 38800, -- Scroll of Enchant Gloves - Mining - [13617] = 38801, -- Scroll of Enchant Gloves - Herbalism - [13620] = 38802, -- Scroll of Enchant Gloves - Fishing - [13622] = 38803, -- Scroll of Enchant Bracer - Lesser Intellect - [13626] = 38804, -- Scroll of Enchant Chest - Minor Stats - [13631] = 38805, -- Scroll of Enchant Shield - Lesser Stamina - [13635] = 38806, -- Scroll of Enchant Cloak - Defense - [13637] = 38807, -- Scroll of Enchant Boots - Lesser Agility - [13640] = 38808, -- Scroll of Enchant Chest - Greater Health - [13642] = 38809, -- Scroll of Enchant Bracer - Spirit - [13644] = 38810, -- Scroll of Enchant Boots - Lesser Stamina - [13646] = 38811, -- Scroll of Enchant Bracer - Lesser Deflection - [13648] = 38812, -- Scroll of Enchant Bracer - Stamina - [13653] = 38813, -- Scroll of Enchant Weapon - Lesser Beastslayer - [13655] = 38814, -- Scroll of Enchant Weapon - Lesser Elemental Slayer - [13657] = 38815, -- Scroll of Enchant Cloak - Fire Resistance - [13659] = 38816, -- Scroll of Enchant Shield - Spirit - [13661] = 38817, -- Scroll of Enchant Bracer - Strength - [13663] = 38818, -- Scroll of Enchant Chest - Greater Mana - [13687] = 38819, -- Scroll of Enchant Boots - Lesser Spirit - [13689] = 38820, -- Scroll of Enchant Shield - Lesser Block - [13693] = 38821, -- Scroll of Enchant Weapon - Striking - [13695] = 38822, -- Scroll of Enchant 2H Weapon - Impact - [13698] = 38823, -- Scroll of Enchant Gloves - Skinning - [13700] = 38824, -- Scroll of Enchant Chest - Lesser Stats - [13746] = 38825, -- Scroll of Enchant Cloak - Greater Defense - [13794] = 38826, -- Scroll of Enchant Cloak - Resistance - [13815] = 38827, -- Scroll of Enchant Gloves - Agility - [13817] = 38828, -- Scroll of Enchant Shield - Stamina - [13822] = 38829, -- Scroll of Enchant Bracer - Intellect - [13836] = 38830, -- Scroll of Enchant Boots - Stamina - [13841] = 38831, -- Scroll of Enchant Gloves - Advanced Mining - [13846] = 38832, -- Scroll of Enchant Bracer - Greater Spirit - [13858] = 38833, -- Scroll of Enchant Chest - Superior Health - [13868] = 38834, -- Scroll of Enchant Gloves - Advanced Herbalism - [13882] = 38835, -- Scroll of Enchant Cloak - Lesser Agility - [13887] = 38836, -- Scroll of Enchant Gloves - Strength - [13890] = 38837, -- Scroll of Enchant Boots - Minor Speed - [13898] = 38838, -- Scroll of Enchant Weapon - Fiery Weapon - [13905] = 38839, -- Scroll of Enchant Shield - Greater Spirit - [13915] = 38840, -- Scroll of Enchant Weapon - Demonslaying - [13917] = 38841, -- Scroll of Enchant Chest - Superior Mana - [13931] = 38842, -- Scroll of Enchant Bracer - Deflection - [13933] = 38843, -- Scroll of Enchant Shield - Frost Resistance - [13935] = 38844, -- Scroll of Enchant Boots - Agility - [13937] = 38845, -- Scroll of Enchant 2H Weapon - Greater Impact - [13939] = 38846, -- Scroll of Enchant Bracer - Greater Strength - [13941] = 38847, -- Scroll of Enchant Chest - Stats - [13943] = 38848, -- Scroll of Enchant Weapon - Greater Striking - [13945] = 38849, -- Scroll of Enchant Bracer - Greater Stamina - [13947] = 38850, -- Scroll of Enchant Gloves - Riding Skill - [13948] = 38851, -- Scroll of Enchant Gloves - Minor Haste - [20008] = 38852, -- Scroll of Enchant Bracer - Greater Intellect - [20009] = 38853, -- Scroll of Enchant Bracer - Superior Spirit - [20010] = 38854, -- Scroll of Enchant Bracer - Superior Strength - [20011] = 38855, -- Scroll of Enchant Bracer - Superior Stamina - [20012] = 38856, -- Scroll of Enchant Gloves - Greater Agility - [20013] = 38857, -- Scroll of Enchant Gloves - Greater Strength - [20014] = 38858, -- Scroll of Enchant Cloak - Greater Resistance - [20015] = 38859, -- Scroll of Enchant Cloak - Superior Defense - [20016] = 38860, -- Scroll of Enchant Shield - Vitality - [20017] = 38861, -- Scroll of Enchant Shield - Greater Stamina - [20020] = 38862, -- Scroll of Enchant Boots - Greater Stamina - [20023] = 38863, -- Scroll of Enchant Boots - Greater Agility - [20024] = 38864, -- Scroll of Enchant Boots - Spirit - [20025] = 38865, -- Scroll of Enchant Chest - Greater Stats - [20026] = 38866, -- Scroll of Enchant Chest - Major Health - [20028] = 38867, -- Scroll of Enchant Chest - Major Mana - [20029] = 38868, -- Scroll of Enchant Weapon - Icy Chill - [20030] = 38869, -- Scroll of Enchant 2H Weapon - Superior Impact - [20031] = 38870, -- Scroll of Enchant Weapon - Superior Striking - [20032] = 38871, -- Scroll of Enchant Weapon - Lifestealing - [20033] = 38872, -- Scroll of Enchant Weapon - Unholy Weapon - [20034] = 38873, -- Scroll of Enchant Weapon - Crusader - [20035] = 38874, -- Scroll of Enchant 2H Weapon - Major Spirit - [20036] = 38875, -- Scroll of Enchant 2H Weapon - Major Intellect - [21931] = 38876, -- Scroll of Enchant Weapon - Winter's Might - [22749] = 38877, -- Scroll of Enchant Weapon - Spellpower - [22750] = 38878, -- Scroll of Enchant Weapon - Healing Power - [23799] = 38879, -- Scroll of Enchant Weapon - Strength - [23800] = 38880, -- Scroll of Enchant Weapon - Agility - [23801] = 38881, -- Scroll of Enchant Bracer - Mana Regeneration - [23802] = 38882, -- Scroll of Enchant Bracer - Healing Power - [23803] = 38883, -- Scroll of Enchant Weapon - Mighty Spirit - [23804] = 38884, -- Scroll of Enchant Weapon - Mighty Intellect - [25072] = 38885, -- Scroll of Enchant Gloves - Threat - [25073] = 38886, -- Scroll of Enchant Gloves - Shadow Power - [25074] = 38887, -- Scroll of Enchant Gloves - Frost Power - [25078] = 38888, -- Scroll of Enchant Gloves - Fire Power - [25079] = 38889, -- Scroll of Enchant Gloves - Healing Power - [25080] = 38890, -- Scroll of Enchant Gloves - Superior Agility - [25081] = 38891, -- Scroll of Enchant Cloak - Greater Fire Resistance - [25082] = 38892, -- Scroll of Enchant Cloak - Greater Nature Resistance - [25083] = 38893, -- Scroll of Enchant Cloak - Stealth - [25084] = 38894, -- Scroll of Enchant Cloak - Subtlety - [25086] = 38895, -- Scroll of Enchant Cloak - Dodge - [27837] = 38896, -- Scroll of Enchant 2H Weapon - Agility - [27899] = 38897, -- Scroll of Enchant Bracer - Brawn - [27905] = 38898, -- Scroll of Enchant Bracer - Stats - [27906] = 38899, -- Scroll of Enchant Bracer - Major Defense - [27911] = 38900, -- Scroll of Enchant Bracer - Superior Healing - [27913] = 38901, -- Scroll of Enchant Bracer - Restore Mana Prime - [27914] = 38902, -- Scroll of Enchant Bracer - Fortitude - [27917] = 38903, -- Scroll of Enchant Bracer - Spellpower - [27944] = 38904, -- Scroll of Enchant Shield - Tough Shield - [27945] = 38905, -- Scroll of Enchant Shield - Intellect - [27946] = 38906, -- Scroll of Enchant Shield - Shield Block - [27947] = 38907, -- Scroll of Enchant Shield - Resistance - [27948] = 38908, -- Scroll of Enchant Boots - Vitality - [27950] = 38909, -- Scroll of Enchant Boots - Fortitude - [27951] = 37603, -- Scroll of Enchant Boots - Dexterity - [27954] = 38910, -- Scroll of Enchant Boots - Surefooted - [27957] = 38911, -- Scroll of Enchant Chest - Exceptional Health - [27958] = 38912, -- Scroll of Enchant Chest - Exceptional Mana - [27960] = 38913, -- Scroll of Enchant Chest - Exceptional Stats - [27961] = 38914, -- Scroll of Enchant Cloak - Major Armor - [27962] = 38915, -- Scroll of Enchant Cloak - Major Resistance - [27967] = 38917, -- Scroll of Enchant Weapon - Major Striking - [27968] = 38918, -- Scroll of Enchant Weapon - Major Intellect - [27971] = 38919, -- Scroll of Enchant 2H Weapon - Savagery - [27972] = 38920, -- Scroll of Enchant Weapon - Potency - [27975] = 38921, -- Scroll of Enchant Weapon - Major Spellpower - [27977] = 38922, -- Scroll of Enchant 2H Weapon - Major Agility - [27981] = 38923, -- Scroll of Enchant Weapon - Sunfire - [27982] = 38924, -- Scroll of Enchant Weapon - Soulfrost - [27984] = 38925, -- Scroll of Enchant Weapon - Mongoose - [28003] = 38926, -- Scroll of Enchant Weapon - Spellsurge - [28004] = 38927, -- Scroll of Enchant Weapon - Battlemaster - [33990] = 38928, -- Scroll of Enchant Chest - Major Spirit - [33991] = 38929, -- Scroll of Enchant Chest - Restore Mana Prime - [33992] = 38930, -- Scroll of Enchant Chest - Major Resilience - [33993] = 38931, -- Scroll of Enchant Gloves - Blasting - [33994] = 38932, -- Scroll of Enchant Gloves - Precise Strikes - [33995] = 38933, -- Scroll of Enchant Gloves - Major Strength - [33996] = 38934, -- Scroll of Enchant Gloves - Assault - [33997] = 38935, -- Scroll of Enchant Gloves - Major Spellpower - [33999] = 38936, -- Scroll of Enchant Gloves - Major Healing - [34001] = 38937, -- Scroll of Enchant Bracer - Major Intellect - [34002] = 38938, -- Scroll of Enchant Bracer - Assault - [34003] = 38939, -- Scroll of Enchant Cloak - Spell Penetration - [34004] = 38940, -- Scroll of Enchant Cloak - Greater Agility - [34005] = 38941, -- Scroll of Enchant Cloak - Greater Arcane Resistance - [34006] = 38942, -- Scroll of Enchant Cloak - Greater Shadow Resistance - [34007] = 38943, -- Scroll of Enchant Boots - Cat's Swiftness - [34008] = 38944, -- Scroll of Enchant Boots - Boar's Speed - [34009] = 38945, -- Scroll of Enchant Shield - Major Stamina - [34010] = 38946, -- Scroll of Enchant Weapon - Major Healing - [42620] = 38947, -- Scroll of Enchant Weapon - Greater Agility - [42974] = 38948, -- Scroll of Enchant Weapon - Executioner - [44383] = 38949, -- Scroll of Enchant Shield - Resilience - [44483] = 38950, -- Scroll of Enchant Cloak - Superior Frost Resistance - [44484] = 38951, -- Scroll of Enchant Gloves - Expertise - [44488] = 38953, -- Scroll of Enchant Gloves - Precision - [44489] = 38954, -- Scroll of Enchant Shield - Defense - [44492] = 38955, -- Scroll of Enchant Chest - Mighty Health - [44494] = 38956, -- Scroll of Enchant Cloak - Superior Nature Resistance - [44500] = 38959, -- Scroll of Enchant Cloak - Superior Agility - [44506] = 38960, -- Scroll of Enchant Gloves - Gatherer - [44508] = 38961, -- Scroll of Enchant Boots - Greater Spirit - [44509] = 38962, -- Scroll of Enchant Chest - Greater Mana Restoration - [44510] = 38963, -- Scroll of Enchant Weapon - Exceptional Spirit - [44513] = 38964, -- Scroll of Enchant Gloves - Greater Assault - [44524] = 38965, -- Scroll of Enchant Weapon - Icebreaker - [44528] = 38966, -- Scroll of Enchant Boots - Greater Fortitude - [44529] = 38967, -- Scroll of Enchant Gloves - Major Agility - [44555] = 38968, -- Scroll of Enchant Bracers - Exceptional Intellect - [44556] = 38969, -- Scroll of Enchant Cloak - Superior Fire Resistance - [44575] = 44815, -- Scroll of Enchant Bracers - Greater Assault - [44576] = 38972, -- Scroll of Enchant Weapon - Lifeward - [44582] = 38973, -- Scroll of Enchant Cloak - Spell Piercing - [44584] = 38974, -- Scroll of Enchant Boots - Greater Vitality - [44588] = 38975, -- Scroll of Enchant Chest - Exceptional Resilience - [44589] = 38976, -- Scroll of Enchant Boots - Superior Agility - [44590] = 38977, -- Scroll of Enchant Cloak - Superior Shadow Resistance - [44591] = 38978, -- Scroll of Enchant Cloak - Titanweave - [44592] = 38979, -- Scroll of Enchant Gloves - Exceptional Spellpower - [44593] = 38980, -- Scroll of Enchant Bracers - Major Spirit - [44595] = 38981, -- Scroll of Enchant 2H Weapon - Scourgebane - [44596] = 38982, -- Scroll of Enchant Cloak - Superior Arcane Resistance - [44598] = 38984, -- Scroll of Enchant Bracer - Expertise - [44612] = 38985, -- Scroll of Enchant Gloves - Greater Blasting - [44616] = 38987, -- Scroll of Enchant Bracers - Greater Stats - [44621] = 38988, -- Scroll of Enchant Weapon - Giant Slayer - [44623] = 38989, -- Scroll of Enchant Chest - Super Stats - [44625] = 38990, -- Scroll of Enchant Gloves - Armsman - [44629] = 38991, -- Scroll of Enchant Weapon - Exceptional Spellpower - [44630] = 38992, -- Scroll of Enchant 2H Weapon - Greater Savagery - [44631] = 38993, -- Scroll of Enchant Cloak - Shadow Armor - [44633] = 38995, -- Scroll of Enchant Weapon - Exceptional Agility - [44635] = 38997, -- Scroll of Enchant Bracers - Greater Spellpower - [46578] = 38998, -- Scroll of Enchant Weapon - Deathfrost - [46594] = 38999, -- Scroll of Enchant Chest - Defense - [47051] = 39000, -- Scroll of Enchant Cloak - Steelweave - [47672] = 39001, -- Scroll of Enchant Cloak - Mighty Armor - [47766] = 39002, -- Scroll of Enchant Chest - Greater Defense - [47898] = 39003, -- Scroll of Enchant Cloak - Greater Speed - [47899] = 39004, -- Scroll of Enchant Cloak - Wisdom - [47900] = 39005, -- Scroll of Enchant Chest - Super Health - [47901] = 39006, -- Scroll of Enchant Boots - Tuskarr's Vitality - [59619] = 44497, -- Scroll of Enchant Weapon - Accuracy - [59621] = 44493, -- Scroll of Enchant Weapon - Berserking - [59625] = 43987, -- Scroll of Enchant Weapon - Black Magic - [60606] = 44449, -- Scroll of Enchant Boots - Assault - [60609] = 44456, -- Scroll of Enchant Cloak - Speed - [60616] = 38971, -- Scroll of Enchant Bracers - Striking - [60621] = 44453, -- Scroll of Enchant Weapon - Greater Potency - [60623] = 38986, -- Scroll of Enchant Boots - Icewalker - [60653] = 44455, -- Scroll of Enchant Shield - Greater Intellect - [60663] = 44457, -- Scroll of Enchant Cloak - Major Agility - [60668] = 44458, -- Scroll of Enchant Gloves - Crusher - [60691] = 44463, -- Scroll of Enchant 2H Weapon - Massacre - [60692] = 44465, -- Scroll of Enchant Chest - Powerful Stats - [60707] = 44466, -- Scroll of Enchant Weapon - Superior Potency - [60714] = 44467, -- Scroll of Enchant Weapon - Mighty Spellpower - [60763] = 44469, -- Scroll of Enchant Boots - Greater Assault - [60767] = 44470, -- Scroll of Enchant Bracer - Superior Spellpower - [62256] = 44947, -- Scroll of Enchant Bracer - Major Stamina - [62257] = 44946, -- Scroll of Enchant Weapon - Titanguard - [62948] = 45056, -- Scroll of Enchant Staff - Greater Spellpower - [62959] = 45060, -- Scroll of Enchant Staff - Spellpower - [63746] = 45628, -- Scroll of Enchant Boots - Lesser Accuracy - [64441] = 46026, -- Scroll of Enchant Weapon - Blade Ward - [64579] = 46098, -- Scroll of Enchant Weapon - Blood Draining - [71692] = 50816, -- Scroll of Enchant Gloves - Angler - [968676] = 967760, -- Scroll of Enchant Weapon - Unstoppable Assault I - [968677] = 967761, -- Scroll of Enchant Weapon - Unstoppable Assault II - [968678] = 967762, -- Scroll of Enchant Weapon - Unstoppable Assault III - [968679] = 967763, -- Scroll of Enchant Weapon - Lucid Assault I - [968680] = 967764, -- Scroll of Enchant Weapon - Lucid Assault II - [968681] = 967765, -- Scroll of Enchant Weapon - Lucid Assault III - [968682] = 967766, -- Scroll of Enchant Weapon - Spellbinder's Rage I - [968683] = 967767, -- Scroll of Enchant Weapon - Spellbinder's Rage II - [968684] = 967768, -- Scroll of Enchant Weapon - Spellbinder's Rage III - [968685] = 967769, -- Scroll of Enchant Weapon - Ninja's Focus I - [968686] = 967770, -- Scroll of Enchant Weapon - Ninja's Focus II - [968687] = 967771, -- Scroll of Enchant Weapon - Ninja's Focus III - [968688] = 967772, -- Scroll of Enchant Weapon - Grovewarden's Blessing I - [968689] = 967773, -- Scroll of Enchant Weapon - Grovewarden's Blessing II - [968690] = 967774, -- Scroll of Enchant Weapon - Grovewarden's Blessing III - [968691] = 967775, -- Scroll of Enchant Weapon - Viscious Assault I - [968692] = 967776, -- Scroll of Enchant Weapon - Viscious Assault II - [968693] = 967777, -- Scroll of Enchant Weapon - Viscious Assault III - [968694] = 967778, -- Scroll of Enchant Weapon - Arcane Dexterity I - [968695] = 967779, -- Scroll of Enchant Weapon - Arcane Dexterity II - [968696] = 967780, -- Scroll of Enchant Weapon - Arcane Dexterity III - [968697] = 967781, -- Scroll of Enchant Weapon - Arcane Artillery I - [968698] = 967782, -- Scroll of Enchant Weapon - Arcane Artillery II - [968699] = 967783, -- Scroll of Enchant Weapon - Arcane Artillery III - [968700] = 967784, -- Scroll of Enchant Weapon - Arcane Precision I - [968701] = 967785, -- Scroll of Enchant Weapon - Arcane Precision II - [968702] = 967786, -- Scroll of Enchant Weapon - Arcane Precision III - [968770] = 967787, -- Scroll of Enchant Weapon - Crusader II - [968771] = 967788, -- Scroll of Enchant Weapon - Crusader III - [1968677] = 1204125, -- Scroll of Enchant Weapon - Void Assault - [1968678] = 1204126, -- Scroll of Enchant Weapon - Overpowering Void Assault - [1968680] = 1204127, -- Scroll of Enchant Weapon - Dread Assault - [1968681] = 1204128, -- Scroll of Enchant Weapon - Overpowering Dread Assault - [1968683] = 1204129, -- Scroll of Enchant Weapon - Twisted Evoker - [1968684] = 1204130, -- Scroll of Enchant Weapon - Overpowering Twisted Evoker - [1968686] = 1204131, -- Scroll of Enchant Weapon - Twisted Assault - [1968687] = 1204132, -- Scroll of Enchant Weapon - Overpowering Twisted Assault - [1968689] = 1204133, -- Scroll of Enchant Weapon - Twisted Channeler - [1968690] = 1204134, -- Scroll of Enchant Weapon - Overpowering Twisted Channeler - [1968692] = 1204135, -- Scroll of Enchant Weapon - Dread Omen Strikes - [1968693] = 1204136, -- Scroll of Enchant Weapon - Overpowering Dread Omen Strikes - [1968695] = 1204137, -- Scroll of Enchant Weapon - Void Flows - [1968696] = 1204138, -- Scroll of Enchant Weapon - Overpowering Void Flows - [1968698] = 1204139, -- Scroll of Enchant Weapon - Void Blasting - [1968699] = 1204140, -- Scroll of Enchant Weapon - Overpowering Void Blasting - [1968701] = 1204141, -- Scroll of Enchant Weapon - Dread Precision - [1968702] = 1204142, -- Scroll of Enchant Weapon - Overpowering Dread Precision - [1968770] = 1204143, -- Scroll of Enchant Weapon - Twisted Crusader - [1968771] = 1204144, -- Scroll of Enchant Weapon - Overpowering Twisted Crusader -} - + -- -- MOP + -- [110764] = 79061, -- Enchant Weapon - Pandamonium + -- [104338] = 74700, -- Enchant Bracer - Mastery + -- [104385] = 74701, -- Enchant Bracer - Major Dodge + -- [104389] = 74703, -- Enchant Bracer - Super Intellect + -- [104390] = 74704, -- Enchant Bracer - Exceptional Strength + -- [104391] = 74705, -- Enchant Bracer - Greater Agility + -- [104392] = 74706, -- Enchant Chest - Super Resilience + -- [104393] = 74707, -- Enchant Chest - Mighty Spirit + -- [104395] = 74708, -- Enchant Chest - Glorious Stats + -- [104397] = 74709, -- Enchant Chest - Superior Stamina + -- [104398] = 74710, -- Enchant Cloak - Accuracy + -- [104401] = 74711, -- Enchant Cloak - Greater Protection + -- [104403] = 74712, -- Enchant Cloak - Superior Intellect + -- [104404] = 74713, -- Enchant Cloak - Superior Critical Strike + -- [104407] = 74715, -- Enchant Boots - Greater Haste + -- [104408] = 74716, -- Enchant Boots - Greater Precision + -- [104409] = 74717, -- Enchant Boots - Blurred Speed + -- [104414] = 74718, -- Enchant Boots - Pandaren's Step + -- [104416] = 74719, -- Enchant Gloves - Greater Haste + -- [104417] = 74720, -- Enchant Gloves - Superior Expertise + -- [104419] = 74721, -- Enchant Gloves - Super Strength + -- [104420] = 74722, -- Enchant Gloves - Superior Mastery + -- [104445] = 74729, -- Enchant Off-Hand - Major Intellect + -- [130758] = 89737, -- Enchant Shield - Greater Parry + -- [104425] = 74723, -- Enchant Weapon - Windsong + -- [104427] = 74724, -- Enchant Weapon - Jade Spirit + -- [104430] = 74725, -- Enchant Weapon - Elemental Force + -- [104434] = 74726, -- Enchant Weapon - Dancing Steel + -- [104440] = 74727, -- Enchant Weapon - Colossus + -- [104442] = 74728, -- Enchant Weapon - River's Song + + -- -- CATA + -- [74132] = 52687, -- Enchant Gloves - Mastery + -- [74189] = 52743, -- Enchant Boots - Earthen Vitality + -- [74191] = 52744, -- Enchant Chest - Mighty Stats + -- [74192] = 52745, -- Enchant Cloak - Lesser Power + -- [74193] = 52746, -- Enchant Bracer - Speed + -- [74197] = 52748, -- Enchant Weapon - Avalanche + -- [74198] = 52749, -- Enchant Gloves - Haste + -- [74199] = 52750, -- Enchant Boots - Haste + -- [74200] = 52751, -- Enchant Chest - Stamina + -- [74201] = 52752, -- Enchant Bracer - Critical Strike + -- [74202] = 52753, -- Enchant Cloak - Intellect + -- [74207] = 52754, -- Enchant Shield - Protection + -- [74211] = 52755, -- Enchant Weapon - Elemental Slayer + -- [74212] = 52756, -- Enchant Gloves - Exceptional Strength + -- [74213] = 52757, -- Enchant Boots - Major Agility + -- [74214] = 52758, -- Enchant Chest - Mighty Resilience + -- [74220] = 52759, -- Enchant Gloves - Greater Expertise + -- [74223] = 52760, -- Enchant Weapon - Hurricane + -- [74225] = 52761, -- Enchant Weapon - Heartsong + -- [74226] = 52762, -- Enchant Shield - Mastery + -- [74229] = 52763, -- Enchant Bracer - Superior Dodge + -- [74230] = 52764, -- Enchant Cloak - Critical Strike + -- [74231] = 52765, -- Enchant Chest - Exceptional Spirit + -- [74232] = 52766, -- Enchant Bracer - Precision + -- [74234] = 52767, -- Enchant Cloak - Protection + -- [74235] = 52768, -- Enchant Off-Hand - Superior Intellect + -- [74236] = 52769, -- Enchant Boots - Precision + -- [74237] = 52770, -- Enchant Bracer - Exceptional Spirit + -- [74238] = 52771, -- Enchant Boots - Mastery + -- [74239] = 52772, -- Enchant Bracer - Greater Expertise + -- [74240] = 52773, -- Enchant Cloak - Greater Intellect + -- [74242] = 52774, -- Enchant Weapon - Power Torrent + -- [74244] = 52775, -- Enchant Weapon - Windwalk + -- [74246] = 52776, -- Enchant Weapon - Landslide + -- [74247] = 52777, -- Enchant Cloak - Greater Critical Strike + -- [74248] = 52778, -- Enchant Bracer - Greater Critical Strike + -- [74250] = 52779, -- Enchant Chest - Peerless Stats + -- [74251] = 52780, -- Enchant Chest - Greater Stamina + -- [74252] = 52781, -- Enchant Boots - Assassin's Step + -- [74253] = 52782, -- Enchant Boots - Lavawalker + -- [74254] = 52783, -- Enchant Gloves - Mighty Strength + -- [74255] = 52784, -- Enchant Gloves - Greater Mastery + -- [74256] = 52785, -- Enchant Bracer - Greater Speed + -- [74195] = 52747, -- Enchant Weapon - Mending + -- [96264] = 68784, -- Enchant Bracer - Agility + -- [96261] = 68785, -- Enchant Bracer - Major Strength + -- [96262] = 68786, -- Enchant Bracer - Mighty Intellect + -- [95471] = 68134, -- Enchant 2H Weapon - Mighty Agility + + -- WRATH + -- [44506] = 38960, -- Enchant Gloves - Gatherer + -- [44484] = 38951, -- Enchant Gloves - Expertise + -- [44488] = 38953, -- Enchant Gloves - Precision + -- [44489] = 38954, -- Enchant Shield - Dodge + -- [44492] = 38955, -- Enchant Chest - Mighty Health + -- [44500] = 38959, -- Enchant Cloak - Superior Agility + -- [44508] = 38961, -- Enchant Boots - Greater Spirit + -- [44509] = 38962, -- Enchant Chest - Greater Mana Restoration + -- [44513] = 38964, -- Enchant Gloves - Greater Assault + -- [44528] = 38966, -- Enchant Boots - Greater Fortitude + -- [44529] = 38967, -- Enchant Gloves - Major Agility + -- [44555] = 38968, -- Enchant Bracer - Exceptional Intellect + -- [44582] = 38973, -- Enchant Cloak - Minor Power + -- [44584] = 38974, -- Enchant Boots - Greater Vitality + -- [44588] = 38975, -- Enchant Chest - Exceptional Resilience + -- [44589] = 38976, -- Enchant Boots - Superior Agility + -- [44591] = 38978, -- Enchant Cloak - Superior Dodge + -- [44592] = 38979, -- Enchant Gloves - Exceptional Spellpower + -- [44593] = 38980, -- Enchant Bracer - Major Spirit + -- [44598] = 38984, -- Enchant Bracer - Expertise + -- [44616] = 38987, -- Enchant Bracer - Greater Stats + -- [44623] = 38989, -- Enchant Chest - Super Stats + -- [44625] = 38990, -- Enchant Gloves - Armsman + -- [44631] = 38993, -- Enchant Cloak - Shadow Armor + -- [44635] = 38997, -- Enchant Bracer - Greater Spellpower + -- [44575] = 44815, -- Enchant Bracer - Greater Assault + -- [44510] = 38963, -- Enchant Weapon - Exceptional Spirit + -- [44524] = 38965, -- Enchant Weapon - Icebreaker + -- [44576] = 38972, -- Enchant Weapon - Lifeward + -- [44595] = 38981, -- Enchant 2H Weapon - Scourgebane + -- [44621] = 38988, -- Enchant Weapon - Giant Slayer + -- [44629] = 38991, -- Enchant Weapon - Exceptional Spellpower + -- [44630] = 38992, -- Enchant 2H Weapon - Greater Savagery + -- [44633] = 38995, -- Enchant Weapon - Exceptional Agility + -- [44383] = 38949, -- Enchant Shield - Resilience + -- [46594] = 38999, -- Enchant Chest - Dodge + -- [46578] = 38998, -- Enchant Weapon - Deathfrost + -- [47051] = 39000, -- Enchant Cloak - Greater Dodge + -- [47672] = 39001, -- Enchant Cloak - Mighty Stamina + -- [47766] = 39002, -- Enchant Chest - Greater Dodge + -- [47898] = 39003, -- Enchant Cloak - Greater Speed + -- [47899] = 39004, -- Enchant Cloak - Wisdom + -- [47900] = 39005, -- Enchant Chest - Super Health + -- [47901] = 39006, -- Enchant Boots - Tuskarr's Vitality + -- [59625] = 43987, -- Enchant Weapon - Black Magic + -- [59621] = 44493, -- Enchant Weapon - Berserking + -- [59619] = 44497, -- Enchant Weapon - Accuracy + -- [60616] = 38971, -- Enchant Bracer - Assault + -- [60623] = 38986, -- Enchant Boots - Icewalker + -- [60606] = 44449, -- Enchant Boots - Assault + -- [60609] = 44456, -- Enchant Cloak - Speed + -- [60663] = 44457, -- Enchant Cloak - Major Agility + -- [60668] = 44458, -- Enchant Gloves - Crusher + -- [60692] = 44465, -- Enchant Chest - Powerful Stats + -- [60763] = 44469, -- Enchant Boots - Greater Assault + -- [60767] = 44470, -- Enchant Bracer - Superior Spellpower + -- [60621] = 44453, -- Enchant Weapon - Greater Potency + -- [60691] = 44463, -- Enchant 2H Weapon - Massacre + -- [60707] = 44466, -- Enchant Weapon - Superior Potency + -- [60714] = 44467, -- Enchant Weapon - Mighty Spellpower + -- [62256] = 44947, -- Enchant Bracer - Major Stamina + -- [62948] = 45056, -- Enchant Staff - Greater Spellpower + -- [62959] = 45060, -- Enchant Staff - Spellpower + -- [63746] = 45628, -- Enchant Boots - Lesser Accuracy + -- [64441] = 46026, -- Enchant Weapon - Blade Ward + -- [64579] = 46098, -- Enchant Weapon - Blood Draining + -- [71692] = 50816, -- Enchant Gloves - Angler + + -- -- TBC + -- [33990] = 38928, -- Enchant Chest - Major Spirit + -- [33991] = 38929, -- Enchant Chest - Restore Mana Prime + -- [33992] = 38930, -- Enchant Chest - Major Resilience + -- [33993] = 38931, -- Enchant Gloves - Blasting + -- [33994] = 38932, -- Enchant Gloves - Precise Strikes + -- [33995] = 38933, -- Enchant Gloves - Major Strength + -- [33996] = 38934, -- Enchant Gloves - Assault + -- [33997] = 38935, -- Enchant Gloves - Major Spellpower + -- [33999] = 38936, -- Enchant Gloves - Major Healing + -- [34001] = 38937, -- Enchant Bracer - Major Intellect + -- [34002] = 38938, -- Enchant Bracer - Lesser Assault + -- [34003] = 38939, -- Enchant Cloak - Spell Penetration + -- [34004] = 38940, -- Enchant Cloak - Greater Agility + -- [34007] = 38943, -- Enchant Boots - Cat's Swiftness + -- [34008] = 38944, -- Enchant Boots - Boar's Speed + -- [34009] = 38945, -- Enchant Shield - Major Stamina + -- [34010] = 38946, -- Enchant Weapon - Major Healing + -- [42974] = 38948, -- Enchant Weapon - Executioner + -- [42620] = 38947, -- Enchant Weapon - Greater Agility + + -- -- VANILLA + -- [7745] = 38772, -- Enchant 2H Weapon - Minor Impact + -- [7786] = 38779, -- Enchant Weapon - Minor Beastslayer + -- [7788] = 38780, -- Enchant Weapon - Minor Striking + -- [7793] = 38781, -- Enchant 2H Weapon - Lesser Intellect + -- [7418] = 38679, -- Enchant Bracer - Minor Health + -- [7420] = 38766, -- Enchant Chest - Minor Health + -- [7426] = 38767, -- Enchant Chest - Minor Absorption + -- [7428] = 38768, -- Enchant Bracer - Minor Dodge + -- [7443] = 38769, -- Enchant Chest - Minor Mana + -- [7457] = 38771, -- Enchant Bracer - Minor Stamina + -- [7748] = 38773, -- Enchant Chest - Lesser Health + -- [7766] = 38774, -- Enchant Bracer - Minor Spirit + -- [7771] = 38775, -- Enchant Cloak - Minor Protection + -- [7776] = 38776, -- Enchant Chest - Lesser Mana + -- [7779] = 38777, -- Enchant Bracer - Minor Agility + -- [7782] = 38778, -- Enchant Bracer - Minor Strength + -- [7857] = 38782, -- Enchant Chest - Health + -- [7859] = 38783, -- Enchant Bracer - Lesser Spirit + -- [7863] = 38785, -- Enchant Boots - Minor Stamina + -- [7867] = 38786, -- Enchant Boots - Minor Agility + -- [13378] = 38787, -- Enchant Shield - Minor Stamina + -- [13419] = 38789, -- Enchant Cloak - Minor Agility + -- [13421] = 38790, -- Enchant Cloak - Lesser Protection + -- [13464] = 38791, -- Enchant Shield - Lesser Protection + -- [13485] = 38792, -- Enchant Shield - Lesser Spirit + -- [13501] = 38793, -- Enchant Bracer - Lesser Stamina + -- [13536] = 38797, -- Enchant Bracer - Lesser Strength + -- [13538] = 38798, -- Enchant Chest - Lesser Absorption + -- [13607] = 38799, -- Enchant Chest - Mana + -- [13612] = 38800, -- Enchant Gloves - Mining + -- [13617] = 38801, -- Enchant Gloves - Herbalism + -- [13620] = 38802, -- Enchant Gloves - Fishing + -- [13622] = 38803, -- Enchant Bracer - Lesser Intellect + -- [13626] = 38804, -- Enchant Chest - Minor Stats + -- [13631] = 38805, -- Enchant Shield - Lesser Stamina + -- [13635] = 38806, -- Enchant Cloak - Defense + -- [13637] = 38807, -- Enchant Boots - Lesser Agility + -- [13640] = 38808, -- Enchant Chest - Greater Health + -- [13642] = 38809, -- Enchant Bracer - Spirit + -- [13644] = 38810, -- Enchant Boots - Lesser Stamina + -- [13646] = 38811, -- Enchant Bracer - Lesser Dodge + -- [13648] = 38812, -- Enchant Bracer - Stamina + -- [13659] = 38816, -- Enchant Shield - Spirit + -- [13661] = 38817, -- Enchant Bracer - Strength + -- [13663] = 38818, -- Enchant Chest - Greater Mana + -- [13687] = 38819, -- Enchant Boots - Lesser Spirit + -- [13689] = 38820, -- Enchant Shield - Lesser Parry + -- [13698] = 38823, -- Enchant Gloves - Skinning + -- [13700] = 38824, -- Enchant Chest - Lesser Stats + -- [13746] = 38825, -- Enchant Cloak - Greater Defense + -- [13815] = 38827, -- Enchant Gloves - Agility + -- [13817] = 38828, -- Enchant Shield - Stamina + -- [13822] = 38829, -- Enchant Bracer - Intellect + -- [13836] = 38830, -- Enchant Boots - Stamina + -- [13841] = 38831, -- Enchant Gloves - Advanced Mining + -- [13846] = 38832, -- Enchant Bracer - Greater Spirit + -- [13858] = 38833, -- Enchant Chest - Superior Health + -- [13868] = 38834, -- Enchant Gloves - Advanced Herbalism + -- [13882] = 38835, -- Enchant Cloak - Lesser Agility + -- [13887] = 38836, -- Enchant Gloves - Strength + -- [13890] = 38837, -- Enchant Boots - Minor Speed + -- [13905] = 38839, -- Enchant Shield - Greater Spirit + -- [13917] = 38841, -- Enchant Chest - Superior Mana + -- [13931] = 38842, -- Enchant Bracer - Dodge + -- [13935] = 38844, -- Enchant Boots - Agility + -- [13939] = 38846, -- Enchant Bracer - Greater Strength + -- [13941] = 38847, -- Enchant Chest - Stats + -- [13945] = 38849, -- Enchant Bracer - Greater Stamina + -- [13947] = 38850, -- Enchant Gloves - Riding Skill + -- [13948] = 38851, -- Enchant Gloves - Minor Haste + -- [13380] = 38788, -- Enchant 2H Weapon - Lesser Spirit + -- [13503] = 38794, -- Enchant Weapon - Lesser Striking + -- [13529] = 38796, -- Enchant 2H Weapon - Lesser Impact + -- [13653] = 38813, -- Enchant Weapon - Lesser Beastslayer + -- [13655] = 38814, -- Enchant Weapon - Lesser Elemental Slayer + -- [13693] = 38821, -- Enchant Weapon - Striking + -- [13695] = 38822, -- Enchant 2H Weapon - Impact + -- [13898] = 38838, -- Enchant Weapon - Fiery Weapon + -- [13915] = 38840, -- Enchant Weapon - Demonslaying + -- [13937] = 38845, -- Enchant 2H Weapon - Greater Impact + -- [13943] = 38848, -- Enchant Weapon - Greater Striking + -- [20008] = 38852, -- Enchant Bracer - Greater Intellect + -- [20009] = 38853, -- Enchant Bracer - Superior Spirit + -- [20010] = 38854, -- Enchant Bracer - Superior Strength + -- [20011] = 38855, -- Enchant Bracer - Superior Stamina + -- [20012] = 38856, -- Enchant Gloves - Greater Agility + -- [20013] = 38857, -- Enchant Gloves - Greater Strength + -- [20015] = 38859, -- Enchant Cloak - Superior Defense + -- [20016] = 38860, -- Enchant Shield - Vitality + -- [20017] = 38861, -- Enchant Shield - Greater Stamina + -- [20020] = 38862, -- Enchant Boots - Greater Stamina + -- [20023] = 38863, -- Enchant Boots - Greater Agility + -- [20024] = 38864, -- Enchant Boots - Spirit + -- [20025] = 38865, -- Enchant Chest - Greater Stats + -- [20026] = 38866, -- Enchant Chest - Major Health + -- [20028] = 38867, -- Enchant Chest - Major Mana + -- [20029] = 38868, -- Enchant Weapon - Icy Chill + -- [20030] = 38869, -- Enchant 2H Weapon - Superior Impact + -- [20031] = 38870, -- Enchant Weapon - Superior Striking + -- [20032] = 38871, -- Enchant Weapon - Lifestealing + -- [20033] = 38872, -- Enchant Weapon - Unholy Weapon + -- [20034] = 38873, -- Enchant Weapon - Crusader + -- [20035] = 38874, -- Enchant 2H Weapon - Major Spirit + -- [20036] = 38875, -- Enchant 2H Weapon - Major Intellect + -- [21931] = 38876, -- Enchant Weapon - Winter's Might + -- [22749] = 38877, -- Enchant Weapon - Spellpower + -- [22750] = 38878, -- Enchant Weapon - Healing Power + -- [23799] = 38879, -- Enchant Weapon - Strength + -- [23800] = 38880, -- Enchant Weapon - Agility + -- [23803] = 38883, -- Enchant Weapon - Mighty Spirit + -- [23804] = 38884, -- Enchant Weapon - Mighty Intellect + -- [23801] = 38881, -- Enchant Bracer - Mana Regeneration + -- [23802] = 38882, -- Enchant Bracer - Healing Power + -- [25072] = 38885, -- Enchant Gloves - Threat + -- [25073] = 38886, -- Enchant Gloves - Shadow Power + -- [25074] = 38887, -- Enchant Gloves - Frost Power + -- [25078] = 38888, -- Enchant Gloves - Fire Power + -- [25079] = 38889, -- Enchant Gloves - Healing Power + -- [25080] = 38890, -- Enchant Gloves - Superior Agility + -- [25083] = 38893, -- Enchant Cloak - Stealth + -- [25084] = 38894, -- Enchant Cloak - Subtlety + -- [25086] = 38895, -- Enchant Cloak - Dodge + -- [27837] = 38896, -- Enchant 2H Weapon - Agility + -- [27951] = 37603, -- Enchant Boots - Dexterity + -- [27958] = 38912, -- Enchant Chest - Exceptional Mana + -- [27899] = 38897, -- Enchant Bracer - Brawn + -- [27905] = 38898, -- Enchant Bracer - Stats + -- [27906] = 38899, -- Enchant Bracer - Greater Dodge + -- [27911] = 38900, -- Enchant Bracer - Superior Healing + -- [27913] = 38901, -- Enchant Bracer - Restore Mana Prime + -- [27914] = 38902, -- Enchant Bracer - Fortitude + -- [27917] = 38903, -- Enchant Bracer - Spellpower + -- [27944] = 38904, -- Enchant Shield - Lesser Dodge + -- [27945] = 38905, -- Enchant Shield - Intellect + -- [27946] = 38906, -- Enchant Shield - Parry + -- [27948] = 38908, -- Enchant Boots - Vitality + -- [27950] = 38909, -- Enchant Boots - Fortitude + -- [27954] = 38910, -- Enchant Boots - Surefooted + -- [27957] = 38911, -- Enchant Chest - Exceptional Health + -- [27960] = 38913, -- Enchant Chest - Exceptional Stats + -- [27961] = 38914, -- Enchant Cloak - Major Armor + -- [27967] = 38917, -- Enchant Weapon - Major Striking + -- [27968] = 38918, -- Enchant Weapon - Major Intellect + -- [27971] = 38919, -- Enchant 2H Weapon - Savagery + -- [27972] = 38920, -- Enchant Weapon - Potency + -- [27975] = 38921, -- Enchant Weapon - Major Spellpower + -- [27977] = 38922, -- Enchant 2H Weapon - Major Agility + -- [27981] = 38923, -- Enchant Weapon - Sunfire + -- [27982] = 38924, -- Enchant Weapon - Soulfrost + -- [27984] = 38925, -- Enchant Weapon - Mongoose + -- [28003] = 38926, -- Enchant Weapon - Spellsurge + -- [28004] = 38927, -- Enchant Weapon - Battlemaster + + [7418] = 38679, -- Scroll of Enchant Bracer - Minor Health + [7420] = 38766, -- Scroll of Enchant Chest - Minor Health + [7426] = 38767, -- Scroll of Enchant Chest - Minor Absorption + [7428] = 38768, -- Scroll of Enchant Bracer - Minor Deflection + [7443] = 38769, -- Scroll of Enchant Chest - Minor Mana + [7454] = 38770, -- Scroll of Enchant Cloak - Minor Resistance + [7457] = 38771, -- Scroll of Enchant Bracer - Minor Stamina + [7745] = 38772, -- Scroll of Enchant 2H Weapon - Minor Impact + [7748] = 38773, -- Scroll of Enchant Chest - Lesser Health + [7766] = 38774, -- Scroll of Enchant Bracer - Minor Spirit + [7771] = 38775, -- Scroll of Enchant Cloak - Minor Protection + [7776] = 38776, -- Scroll of Enchant Chest - Lesser Mana + [7779] = 38777, -- Scroll of Enchant Bracer - Minor Agility + [7782] = 38778, -- Scroll of Enchant Bracer - Minor Strength + [7786] = 38779, -- Scroll of Enchant Weapon - Minor Beastslayer + [7788] = 38780, -- Scroll of Enchant Weapon - Minor Striking + [7793] = 38781, -- Scroll of Enchant 2H Weapon - Lesser Intellect + [7857] = 38782, -- Scroll of Enchant Chest - Health + [7859] = 38783, -- Scroll of Enchant Bracer - Lesser Spirit + [7861] = 38784, -- Scroll of Enchant Cloak - Lesser Fire Resistance + [7863] = 38785, -- Scroll of Enchant Boots - Minor Stamina + [7867] = 38786, -- Scroll of Enchant Boots - Minor Agility + [13378] = 38787, -- Scroll of Enchant Shield - Minor Stamina + [13380] = 38788, -- Scroll of Enchant 2H Weapon - Lesser Spirit + [13419] = 38789, -- Scroll of Enchant Cloak - Minor Agility + [13421] = 38790, -- Scroll of Enchant Cloak - Lesser Protection + [13464] = 38791, -- Scroll of Enchant Shield - Lesser Protection + [13485] = 38792, -- Scroll of Enchant Shield - Lesser Spirit + [13501] = 38793, -- Scroll of Enchant Bracer - Lesser Stamina + [13503] = 38794, -- Scroll of Enchant Weapon - Lesser Striking + [13522] = 38795, -- Scroll of Enchant Cloak - Lesser Shadow Resistance + [13529] = 38796, -- Scroll of Enchant 2H Weapon - Lesser Impact + [13536] = 38797, -- Scroll of Enchant Bracer - Lesser Strength + [13538] = 38798, -- Scroll of Enchant Chest - Lesser Absorption + [13607] = 38799, -- Scroll of Enchant Chest - Mana + [13612] = 38800, -- Scroll of Enchant Gloves - Mining + [13617] = 38801, -- Scroll of Enchant Gloves - Herbalism + [13620] = 38802, -- Scroll of Enchant Gloves - Fishing + [13622] = 38803, -- Scroll of Enchant Bracer - Lesser Intellect + [13626] = 38804, -- Scroll of Enchant Chest - Minor Stats + [13631] = 38805, -- Scroll of Enchant Shield - Lesser Stamina + [13635] = 38806, -- Scroll of Enchant Cloak - Defense + [13637] = 38807, -- Scroll of Enchant Boots - Lesser Agility + [13640] = 38808, -- Scroll of Enchant Chest - Greater Health + [13642] = 38809, -- Scroll of Enchant Bracer - Spirit + [13644] = 38810, -- Scroll of Enchant Boots - Lesser Stamina + [13646] = 38811, -- Scroll of Enchant Bracer - Lesser Deflection + [13648] = 38812, -- Scroll of Enchant Bracer - Stamina + [13653] = 38813, -- Scroll of Enchant Weapon - Lesser Beastslayer + [13655] = 38814, -- Scroll of Enchant Weapon - Lesser Elemental Slayer + [13657] = 38815, -- Scroll of Enchant Cloak - Fire Resistance + [13659] = 38816, -- Scroll of Enchant Shield - Spirit + [13661] = 38817, -- Scroll of Enchant Bracer - Strength + [13663] = 38818, -- Scroll of Enchant Chest - Greater Mana + [13687] = 38819, -- Scroll of Enchant Boots - Lesser Spirit + [13689] = 38820, -- Scroll of Enchant Shield - Lesser Block + [13693] = 38821, -- Scroll of Enchant Weapon - Striking + [13695] = 38822, -- Scroll of Enchant 2H Weapon - Impact + [13698] = 38823, -- Scroll of Enchant Gloves - Skinning + [13700] = 38824, -- Scroll of Enchant Chest - Lesser Stats + [13746] = 38825, -- Scroll of Enchant Cloak - Greater Defense + [13794] = 38826, -- Scroll of Enchant Cloak - Resistance + [13815] = 38827, -- Scroll of Enchant Gloves - Agility + [13817] = 38828, -- Scroll of Enchant Shield - Stamina + [13822] = 38829, -- Scroll of Enchant Bracer - Intellect + [13836] = 38830, -- Scroll of Enchant Boots - Stamina + [13841] = 38831, -- Scroll of Enchant Gloves - Advanced Mining + [13846] = 38832, -- Scroll of Enchant Bracer - Greater Spirit + [13858] = 38833, -- Scroll of Enchant Chest - Superior Health + [13868] = 38834, -- Scroll of Enchant Gloves - Advanced Herbalism + [13882] = 38835, -- Scroll of Enchant Cloak - Lesser Agility + [13887] = 38836, -- Scroll of Enchant Gloves - Strength + [13890] = 38837, -- Scroll of Enchant Boots - Minor Speed + [13898] = 38838, -- Scroll of Enchant Weapon - Fiery Weapon + [13905] = 38839, -- Scroll of Enchant Shield - Greater Spirit + [13915] = 38840, -- Scroll of Enchant Weapon - Demonslaying + [13917] = 38841, -- Scroll of Enchant Chest - Superior Mana + [13931] = 38842, -- Scroll of Enchant Bracer - Deflection + [13933] = 38843, -- Scroll of Enchant Shield - Frost Resistance + [13935] = 38844, -- Scroll of Enchant Boots - Agility + [13937] = 38845, -- Scroll of Enchant 2H Weapon - Greater Impact + [13939] = 38846, -- Scroll of Enchant Bracer - Greater Strength + [13941] = 38847, -- Scroll of Enchant Chest - Stats + [13943] = 38848, -- Scroll of Enchant Weapon - Greater Striking + [13945] = 38849, -- Scroll of Enchant Bracer - Greater Stamina + [13947] = 38850, -- Scroll of Enchant Gloves - Riding Skill + [13948] = 38851, -- Scroll of Enchant Gloves - Minor Haste + [20008] = 38852, -- Scroll of Enchant Bracer - Greater Intellect + [20009] = 38853, -- Scroll of Enchant Bracer - Superior Spirit + [20010] = 38854, -- Scroll of Enchant Bracer - Superior Strength + [20011] = 38855, -- Scroll of Enchant Bracer - Superior Stamina + [20012] = 38856, -- Scroll of Enchant Gloves - Greater Agility + [20013] = 38857, -- Scroll of Enchant Gloves - Greater Strength + [20014] = 38858, -- Scroll of Enchant Cloak - Greater Resistance + [20015] = 38859, -- Scroll of Enchant Cloak - Superior Defense + [20016] = 38860, -- Scroll of Enchant Shield - Vitality + [20017] = 38861, -- Scroll of Enchant Shield - Greater Stamina + [20020] = 38862, -- Scroll of Enchant Boots - Greater Stamina + [20023] = 38863, -- Scroll of Enchant Boots - Greater Agility + [20024] = 38864, -- Scroll of Enchant Boots - Spirit + [20025] = 38865, -- Scroll of Enchant Chest - Greater Stats + [20026] = 38866, -- Scroll of Enchant Chest - Major Health + [20028] = 38867, -- Scroll of Enchant Chest - Major Mana + [20029] = 38868, -- Scroll of Enchant Weapon - Icy Chill + [20030] = 38869, -- Scroll of Enchant 2H Weapon - Superior Impact + [20031] = 38870, -- Scroll of Enchant Weapon - Superior Striking + [20032] = 38871, -- Scroll of Enchant Weapon - Lifestealing + [20033] = 38872, -- Scroll of Enchant Weapon - Unholy Weapon + [20034] = 38873, -- Scroll of Enchant Weapon - Crusader + [20035] = 38874, -- Scroll of Enchant 2H Weapon - Major Spirit + [20036] = 38875, -- Scroll of Enchant 2H Weapon - Major Intellect + [21931] = 38876, -- Scroll of Enchant Weapon - Winter's Might + [22749] = 38877, -- Scroll of Enchant Weapon - Spellpower + [22750] = 38878, -- Scroll of Enchant Weapon - Healing Power + [23799] = 38879, -- Scroll of Enchant Weapon - Strength + [23800] = 38880, -- Scroll of Enchant Weapon - Agility + [23801] = 38881, -- Scroll of Enchant Bracer - Mana Regeneration + [23802] = 38882, -- Scroll of Enchant Bracer - Healing Power + [23803] = 38883, -- Scroll of Enchant Weapon - Mighty Spirit + [23804] = 38884, -- Scroll of Enchant Weapon - Mighty Intellect + [25072] = 38885, -- Scroll of Enchant Gloves - Threat + [25073] = 38886, -- Scroll of Enchant Gloves - Shadow Power + [25074] = 38887, -- Scroll of Enchant Gloves - Frost Power + [25078] = 38888, -- Scroll of Enchant Gloves - Fire Power + [25079] = 38889, -- Scroll of Enchant Gloves - Healing Power + [25080] = 38890, -- Scroll of Enchant Gloves - Superior Agility + [25081] = 38891, -- Scroll of Enchant Cloak - Greater Fire Resistance + [25082] = 38892, -- Scroll of Enchant Cloak - Greater Nature Resistance + [25083] = 38893, -- Scroll of Enchant Cloak - Stealth + [25084] = 38894, -- Scroll of Enchant Cloak - Subtlety + [25086] = 38895, -- Scroll of Enchant Cloak - Dodge + [27837] = 38896, -- Scroll of Enchant 2H Weapon - Agility + [27899] = 38897, -- Scroll of Enchant Bracer - Brawn + [27905] = 38898, -- Scroll of Enchant Bracer - Stats + [27906] = 38899, -- Scroll of Enchant Bracer - Major Defense + [27911] = 38900, -- Scroll of Enchant Bracer - Superior Healing + [27913] = 38901, -- Scroll of Enchant Bracer - Restore Mana Prime + [27914] = 38902, -- Scroll of Enchant Bracer - Fortitude + [27917] = 38903, -- Scroll of Enchant Bracer - Spellpower + [27944] = 38904, -- Scroll of Enchant Shield - Tough Shield + [27945] = 38905, -- Scroll of Enchant Shield - Intellect + [27946] = 38906, -- Scroll of Enchant Shield - Shield Block + [27947] = 38907, -- Scroll of Enchant Shield - Resistance + [27948] = 38908, -- Scroll of Enchant Boots - Vitality + [27950] = 38909, -- Scroll of Enchant Boots - Fortitude + [27951] = 37603, -- Scroll of Enchant Boots - Dexterity + [27954] = 38910, -- Scroll of Enchant Boots - Surefooted + [27957] = 38911, -- Scroll of Enchant Chest - Exceptional Health + [27958] = 38912, -- Scroll of Enchant Chest - Exceptional Mana + [27960] = 38913, -- Scroll of Enchant Chest - Exceptional Stats + [27961] = 38914, -- Scroll of Enchant Cloak - Major Armor + [27962] = 38915, -- Scroll of Enchant Cloak - Major Resistance + [27967] = 38917, -- Scroll of Enchant Weapon - Major Striking + [27968] = 38918, -- Scroll of Enchant Weapon - Major Intellect + [27971] = 38919, -- Scroll of Enchant 2H Weapon - Savagery + [27972] = 38920, -- Scroll of Enchant Weapon - Potency + [27975] = 38921, -- Scroll of Enchant Weapon - Major Spellpower + [27977] = 38922, -- Scroll of Enchant 2H Weapon - Major Agility + [27981] = 38923, -- Scroll of Enchant Weapon - Sunfire + [27982] = 38924, -- Scroll of Enchant Weapon - Soulfrost + [27984] = 38925, -- Scroll of Enchant Weapon - Mongoose + [28003] = 38926, -- Scroll of Enchant Weapon - Spellsurge + [28004] = 38927, -- Scroll of Enchant Weapon - Battlemaster + [33990] = 38928, -- Scroll of Enchant Chest - Major Spirit + [33991] = 38929, -- Scroll of Enchant Chest - Restore Mana Prime + [33992] = 38930, -- Scroll of Enchant Chest - Major Resilience + [33993] = 38931, -- Scroll of Enchant Gloves - Blasting + [33994] = 38932, -- Scroll of Enchant Gloves - Precise Strikes + [33995] = 38933, -- Scroll of Enchant Gloves - Major Strength + [33996] = 38934, -- Scroll of Enchant Gloves - Assault + [33997] = 38935, -- Scroll of Enchant Gloves - Major Spellpower + [33999] = 38936, -- Scroll of Enchant Gloves - Major Healing + [34001] = 38937, -- Scroll of Enchant Bracer - Major Intellect + [34002] = 38938, -- Scroll of Enchant Bracer - Assault + [34003] = 38939, -- Scroll of Enchant Cloak - Spell Penetration + [34004] = 38940, -- Scroll of Enchant Cloak - Greater Agility + [34005] = 38941, -- Scroll of Enchant Cloak - Greater Arcane Resistance + [34006] = 38942, -- Scroll of Enchant Cloak - Greater Shadow Resistance + [34007] = 38943, -- Scroll of Enchant Boots - Cat's Swiftness + [34008] = 38944, -- Scroll of Enchant Boots - Boar's Speed + [34009] = 38945, -- Scroll of Enchant Shield - Major Stamina + [34010] = 38946, -- Scroll of Enchant Weapon - Major Healing + [42620] = 38947, -- Scroll of Enchant Weapon - Greater Agility + [42974] = 38948, -- Scroll of Enchant Weapon - Executioner + [44383] = 38949, -- Scroll of Enchant Shield - Resilience + [44483] = 38950, -- Scroll of Enchant Cloak - Superior Frost Resistance + [44484] = 38951, -- Scroll of Enchant Gloves - Expertise + [44488] = 38953, -- Scroll of Enchant Gloves - Precision + [44489] = 38954, -- Scroll of Enchant Shield - Defense + [44492] = 38955, -- Scroll of Enchant Chest - Mighty Health + [44494] = 38956, -- Scroll of Enchant Cloak - Superior Nature Resistance + [44500] = 38959, -- Scroll of Enchant Cloak - Superior Agility + [44506] = 38960, -- Scroll of Enchant Gloves - Gatherer + [44508] = 38961, -- Scroll of Enchant Boots - Greater Spirit + [44509] = 38962, -- Scroll of Enchant Chest - Greater Mana Restoration + [44510] = 38963, -- Scroll of Enchant Weapon - Exceptional Spirit + [44513] = 38964, -- Scroll of Enchant Gloves - Greater Assault + [44524] = 38965, -- Scroll of Enchant Weapon - Icebreaker + [44528] = 38966, -- Scroll of Enchant Boots - Greater Fortitude + [44529] = 38967, -- Scroll of Enchant Gloves - Major Agility + [44555] = 38968, -- Scroll of Enchant Bracers - Exceptional Intellect + [44556] = 38969, -- Scroll of Enchant Cloak - Superior Fire Resistance + [44575] = 44815, -- Scroll of Enchant Bracers - Greater Assault + [44576] = 38972, -- Scroll of Enchant Weapon - Lifeward + [44582] = 38973, -- Scroll of Enchant Cloak - Spell Piercing + [44584] = 38974, -- Scroll of Enchant Boots - Greater Vitality + [44588] = 38975, -- Scroll of Enchant Chest - Exceptional Resilience + [44589] = 38976, -- Scroll of Enchant Boots - Superior Agility + [44590] = 38977, -- Scroll of Enchant Cloak - Superior Shadow Resistance + [44591] = 38978, -- Scroll of Enchant Cloak - Titanweave + [44592] = 38979, -- Scroll of Enchant Gloves - Exceptional Spellpower + [44593] = 38980, -- Scroll of Enchant Bracers - Major Spirit + [44595] = 38981, -- Scroll of Enchant 2H Weapon - Scourgebane + [44596] = 38982, -- Scroll of Enchant Cloak - Superior Arcane Resistance + [44598] = 38984, -- Scroll of Enchant Bracer - Expertise + [44612] = 38985, -- Scroll of Enchant Gloves - Greater Blasting + [44616] = 38987, -- Scroll of Enchant Bracers - Greater Stats + [44621] = 38988, -- Scroll of Enchant Weapon - Giant Slayer + [44623] = 38989, -- Scroll of Enchant Chest - Super Stats + [44625] = 38990, -- Scroll of Enchant Gloves - Armsman + [44629] = 38991, -- Scroll of Enchant Weapon - Exceptional Spellpower + [44630] = 38992, -- Scroll of Enchant 2H Weapon - Greater Savagery + [44631] = 38993, -- Scroll of Enchant Cloak - Shadow Armor + [44633] = 38995, -- Scroll of Enchant Weapon - Exceptional Agility + [44635] = 38997, -- Scroll of Enchant Bracers - Greater Spellpower + [46578] = 38998, -- Scroll of Enchant Weapon - Deathfrost + [46594] = 38999, -- Scroll of Enchant Chest - Defense + [47051] = 39000, -- Scroll of Enchant Cloak - Steelweave + [47672] = 39001, -- Scroll of Enchant Cloak - Mighty Armor + [47766] = 39002, -- Scroll of Enchant Chest - Greater Defense + [47898] = 39003, -- Scroll of Enchant Cloak - Greater Speed + [47899] = 39004, -- Scroll of Enchant Cloak - Wisdom + [47900] = 39005, -- Scroll of Enchant Chest - Super Health + [47901] = 39006, -- Scroll of Enchant Boots - Tuskarr's Vitality + [59619] = 44497, -- Scroll of Enchant Weapon - Accuracy + [59621] = 44493, -- Scroll of Enchant Weapon - Berserking + [59625] = 43987, -- Scroll of Enchant Weapon - Black Magic + [60606] = 44449, -- Scroll of Enchant Boots - Assault + [60609] = 44456, -- Scroll of Enchant Cloak - Speed + [60616] = 38971, -- Scroll of Enchant Bracers - Striking + [60621] = 44453, -- Scroll of Enchant Weapon - Greater Potency + [60623] = 38986, -- Scroll of Enchant Boots - Icewalker + [60653] = 44455, -- Scroll of Enchant Shield - Greater Intellect + [60663] = 44457, -- Scroll of Enchant Cloak - Major Agility + [60668] = 44458, -- Scroll of Enchant Gloves - Crusher + [60691] = 44463, -- Scroll of Enchant 2H Weapon - Massacre + [60692] = 44465, -- Scroll of Enchant Chest - Powerful Stats + [60707] = 44466, -- Scroll of Enchant Weapon - Superior Potency + [60714] = 44467, -- Scroll of Enchant Weapon - Mighty Spellpower + [60763] = 44469, -- Scroll of Enchant Boots - Greater Assault + [60767] = 44470, -- Scroll of Enchant Bracer - Superior Spellpower + [62256] = 44947, -- Scroll of Enchant Bracer - Major Stamina + [62257] = 44946, -- Scroll of Enchant Weapon - Titanguard + [62948] = 45056, -- Scroll of Enchant Staff - Greater Spellpower + [62959] = 45060, -- Scroll of Enchant Staff - Spellpower + [63746] = 45628, -- Scroll of Enchant Boots - Lesser Accuracy + [64441] = 46026, -- Scroll of Enchant Weapon - Blade Ward + [64579] = 46098, -- Scroll of Enchant Weapon - Blood Draining + [71692] = 50816, -- Scroll of Enchant Gloves - Angler +} \ No newline at end of file diff --git a/TradeSkillMaster_Crafting/Modules/SpellNames2IDs.lua b/TradeSkillMaster_Crafting/Modules/SpellNames2IDs.lua index b0923b9..acfad38 100644 --- a/TradeSkillMaster_Crafting/Modules/SpellNames2IDs.lua +++ b/TradeSkillMaster_Crafting/Modules/SpellNames2IDs.lua @@ -764,12 +764,6 @@ TSM.SpellName2ID = { ["Treads of Destiny"] = 63190, ["Indestructible Plate Girdle"] = 63191, ["Spiked Deathdealers"] = 63192, - ["Breastplate of the White Knight"] = 67091, - ["Saronite Swordbreakers"] = 67092, - ["Titanium Razorplate"] = 67093, - ["Titanium Spikeguards"] = 67094, - ["Sunforged Breastplate"] = 67095, - ["Sunforged Bracers"] = 67096, ["Breastplate of the White Knight"] = 67130, ["Saronite Swordbreakers"] = 67131, ["Titanium Razorplate"] = 67132, @@ -883,7 +877,6 @@ TSM.SpellName2ID = { ["Lesser Mystic Wand"] = 14809, ["Greater Mystic Wand"] = 14810, ["Smoking Heart of the Mountain"] = 15596, - ["Enchanted Thorium"] = 17180, ["Enchanted Leather"] = 17181, ["Enchant Bracer - Greater Intellect"] = 20008, ["Enchant Bracer - Superior Spirit"] = 20009, @@ -1086,55 +1079,7 @@ TSM.SpellName2ID = { ["Enchant Weapon - Blood Draining"] = 64579, ["Abyssal Shatter"] = 69412, ["Enchant Gloves - Angler"] = 71692, - ["Enchant Weapon - Unstoppable Assault I"] = 968676, - ["Enchant Weapon - Unstoppable Assault II"] = 968677, - ["Enchant Weapon - Unstoppable Assault III"] = 968678, - ["Enchant Weapon - Lucid Assault I"] = 968679, - ["Enchant Weapon - Lucid Assault II"] = 968680, - ["Enchant Weapon - Lucid Assault III"] = 968681, - ["Enchant Weapon - Spellbinder's Rage I"] = 968682, - ["Enchant Weapon - Spellbinder's Rage II"] = 968683, - ["Enchant Weapon - Spellbinder's Rage III"] = 968684, - ["Enchant Weapon - Ninja's Focus I"] = 968685, - ["Enchant Weapon - Ninja's Focus II"] = 968686, - ["Enchant Weapon - Ninja's Focus III"] = 968687, - ["Enchant Weapon - Grovewarden's Blessing I"] = 968688, - ["Enchant Weapon - Grovewarden's Blessing II"] = 968689, - ["Enchant Weapon - Grovewarden's Blessing III"] = 968690, - ["Enchant Weapon - Viscious Assault I"] = 968691, - ["Enchant Weapon - Viscious Assault II"] = 968692, - ["Enchant Weapon - Viscious Assault III"] = 968693, - ["Enchant Weapon - Arcane Dexterity I"] = 968694, - ["Enchant Weapon - Arcane Dexterity II"] = 968695, - ["Enchant Weapon - Arcane Dexterity III"] = 968696, - ["Enchant Weapon - Arcane Artillery I"] = 968697, - ["Enchant Weapon - Arcane Artillery II"] = 968698, - ["Enchant Weapon - Arcane Artillery III"] = 968699, - ["Enchant Weapon - Arcane Precision I"] = 968700, - ["Enchant Weapon - Arcane Precision II"] = 968701, - ["Enchant Weapon - Arcane Precision III"] = 968702, - ["Enchant Weapon - Crusader II"] = 968770, - ["Enchant Weapon - Crusader III"] = 968771, - ["Enchant Weapon - Void Assault"] = 1968677, - ["Enchant Weapon - Overpowering Void Assault"] = 1968678, - ["Enchant Weapon - Dread Assault"] = 1968680, - ["Enchant Weapon - Overpowering Dread Assault"] = 1968681, - ["Enchant Weapon - Twisted Evoker"] = 1968683, - ["Enchant Weapon - Overpowering Twisted Evoker"] = 1968684, - ["Enchant Weapon - Twisted Assault"] = 1968686, - ["Enchant Weapon - Overpowering Twisted Assault"] = 1968687, - ["Enchant Weapon - Twisted Channeler"] = 1968689, - ["Enchant Weapon - Overpowering Twisted ChannelerI"] = 1968690, - ["Enchant Weapon - Dread Omen Strikes"] = 1968692, - ["Enchant Weapon - Overpowering Dread Omen Strikes"] = 1968693, - ["Enchant Weapon - Void Flows"] = 1968695, - ["Enchant Weapon - Overpowering Void Flows"] = 1968696, - ["Enchant Weapon - Void Blasting"] = 1968698, - ["Enchant Weapon - Overpowering Void Blasting"] = 1968699, - ["Enchant Weapon - Dread Precision"] = 1968701, - ["Enchant Weapon - Overpowering Dread Precision"] = 1968702, - ["Enchant Weapon - Twisted Crusader"] = 1968770, - ["Enchant Weapon - Overpowering Twisted Crusader"] = 1968771, + -- Engineering @@ -1398,7 +1343,6 @@ TSM.SpellName2ID = { ["Gnomish Army Knife"] = 56462, ["Explosive Decoy"] = 56463, ["Overcharged Capacitor"] = 56464, - ["Mechanized Snow Goggles"] = 56465, ["Sonic Booster"] = 56466, ["Noise Machine"] = 56467, ["Box of Bombs"] = 56468, @@ -1425,8 +1369,6 @@ TSM.SpellName2ID = { ["Mekgineer's Chopper"] = 60867, ["Nesingwary 4000"] = 60874, ["Diamond-cut Refractor Scope"] = 61471, - ["Mechanized Snow Goggles"] = 61481, - ["Mechanized Snow Goggles"] = 61482, ["Mechanized Snow Goggles"] = 61483, ["Unbreakable Healing Amplifiers"] = 62271, ["High-powered Flashlight"] = 63750, @@ -2646,7 +2588,6 @@ TSM.SpellName2ID = { ["Rugged Leather"] = 22331, ["Shadowskin Gloves"] = 22711, ["Core Armor Kit"] = 22727, - ["Gordok Ogre Suit"] = 22815, ["Girdle of Insight"] = 22921, ["Mongoose Boots"] = 22922, ["Swift Flight Bracers"] = 22923, @@ -2966,14 +2907,6 @@ TSM.SpellName2ID = { ["Belt of Arctic Life"] = 63200, ["Boots of Wintry Endurance"] = 63201, ["Borean Leather"] = 64661, - ["Ensorcelled Nerubian Breastplate"] = 67080, - ["Black Chitin Bracers"] = 67081, - ["Crusader's Dragonscale Breastplate"] = 67082, - ["Crusader's Dragonscale Bracers"] = 67083, - ["Lunar Eclipse Robes"] = 67084, - ["Moonshadow Armguards"] = 67085, - ["Knightbane Carapace"] = 67086, - ["Bracers of Swift Death"] = 67087, ["Ensorcelled Nerubian Breastplate"] = 67136, ["Black Chitin Bracers"] = 67137, ["Crusader's Dragonscale Breastplate"] = 67138, @@ -3348,7 +3281,6 @@ TSM.SpellName2ID = { ["Duskweave Robe"] = 55921, ["Duskweave Gloves"] = 55922, ["Duskweave Shoulders"] = 55923, - ["Duskweave Boots"] = 55924, ["Black Duskweave Leggings"] = 55925, ["Black Duskweave Robe"] = 55941, ["Black Duskweave Wristwraps"] = 55943, @@ -3413,10 +3345,6 @@ TSM.SpellName2ID = { ["Emerald Bag"] = 63924, ["Frostguard Drape"] = 64729, ["Cloak of Crimson Snow"] = 64730, - ["Royal Moonshroud Robe"] = 67064, - ["Royal Moonshroud Bracers"] = 67065, - ["Merlin's Robe"] = 67066, - ["Bejeweled Wizard's Bracers"] = 67079, ["Royal Moonshroud Robe"] = 67144, ["Bejeweled Wizard's Bracers"] = 67145, ["Merlin's Robe"] = 67146, @@ -3621,11 +3549,6 @@ TSM.SpellName2ID = { ["Haunted Herring"] = 58525, ["Gigantic Feast"] = 58527, ["Small Feast"] = 58528, - ["Pumpkin Pie"] = 62044, - ["Slow-Roasted Turkey"] = 62045, - ["Cranberry Chutney"] = 62049, - ["Spice Bread Stuffing"] = 62050, - ["Candied Sweet Potato"] = 62051, ["Worg Tartare"] = 62350, ["Clamlette Magnifique"] = 64054, ["Black Jelly"] = 64358, diff --git a/TradeSkillMaster_Crafting/Modules/Util.lua b/TradeSkillMaster_Crafting/Modules/Util.lua index d3e5c38..340cbd4 100644 --- a/TradeSkillMaster_Crafting/Modules/Util.lua +++ b/TradeSkillMaster_Crafting/Modules/Util.lua @@ -105,7 +105,31 @@ function Util:ScanCurrentProfession() local hasCD = select(2, GetTradeSkillCooldown(index)) and true or nil local mats = {} if currentTradeSkill == TSM.enchantingName and strfind(itemLink, "enchant:") then + -- mats[VELLUM_ID] = 1 + -- local name = TSMAPI:GetSafeItemInfo(VELLUM_ID) or (GetLocale() == "enUS" and "Enchanting Vellum") or nil + -- TSM.db.factionrealm.mats[VELLUM_ID] = TSM.db.factionrealm.mats[VELLUM_ID] or {} + -- TSM.db.factionrealm.mats[VELLUM_ID].name = TSM.db.factionrealm.mats[VELLUM_ID].name or name local VellumString = "item:"..TSM.VellumInfo[spellID]..":0:0:0:0:0:0" + + + + -- -- Get Cheapest vellum, lower vellum types can be replaced by III + -- local velName + -- if TSM.VellumInfo[spellID] then + -- velName = GetItemInfo(TSM.VellumInfo[spellID]) + -- end + -- if (velName ~= nil) and (not strfind(velName, "III")) then + -- local VellumReplacePrice = TSM.Cost:GetMatCost(VellumString) + + -- if strfind(GetSpellInfo(spellID), "Weapon") or strfind(GetSpellInfo(spellID), "Staff")then + -- if VellumReplacePrice > TSM.Cost:GetMatCost("item:43146:0:0:0:0:0:0") then VellumString = "item:43146:0:0:0:0:0:0" end + -- else + -- if VellumReplacePrice > TSM.Cost:GetMatCost("item:43145:0:0:0:0:0:0") then VellumString = "item:4314:0:0:0:0:0:0" end + -- end + -- end + + + mats[VellumString] = 1 local name = TSMAPI:GetSafeItemInfo(VellumString) or nil TSM.db.factionrealm.mats[VellumString] = TSM.db.factionrealm.mats[VellumString] or {} @@ -230,8 +254,33 @@ function Util.ScanSyncedProfessionThread(self) local hasCD = select(2, GetTradeSkillCooldown(index)) and true or nil local mats = {} if currentTradeSkill == TSM.enchantingName and strfind(itemLink, "enchant:") then + -- mats[VELLUM_ID] = 1 + -- local name = TSMAPI:GetSafeItemInfo(VELLUM_ID) or (GetLocale() == "enUS" and "Enchanting Vellum") or nil + -- TSM.db.factionrealm.mats[VELLUM_ID] = TSM.db.factionrealm.mats[VELLUM_ID] or {} + -- TSM.db.factionrealm.mats[VELLUM_ID].name = TSM.db.factionrealm.mats[VELLUM_ID].name or name + local VellumString = "item:"..TSM.VellumInfo[spellID]..":0:0:0:0:0:0" + + + + -- -- Get Cheapest vellum, lower vellum types can be replaced by III + -- local velName + -- if TSM.VellumInfo[spellID] then + -- velName = GetItemInfo(TSM.VellumInfo[spellID]) + -- end + -- if (velName ~= nil) and (not strfind(velName, "III")) then + -- local VellumReplacePrice = TSM.Cost:GetMatCost(VellumString) + -- if strfind(GetSpellInfo(spellID), "Weapon") or strfind(GetSpellInfo(spellID), "Staff")then + -- if VellumReplacePrice > TSM.Cost:GetMatCost("item:43146:0:0:0:0:0:0") then VellumString = "item:43146:0:0:0:0:0:0" end + -- else + -- if VellumReplacePrice > TSM.Cost:GetMatCost("item:43145:0:0:0:0:0:0") then VellumString = "item:4314:0:0:0:0:0:0" end + -- end + -- end + + + + mats[VellumString] = 1 local name = TSMAPI:GetSafeItemInfo(VellumString) or nil TSM.db.factionrealm.mats[VellumString] = TSM.db.factionrealm.mats[VellumString] or {} diff --git a/TradeSkillMaster_Crafting/Modules/VellumInfo.lua b/TradeSkillMaster_Crafting/Modules/VellumInfo.lua index 681bc92..19dc6a2 100644 --- a/TradeSkillMaster_Crafting/Modules/VellumInfo.lua +++ b/TradeSkillMaster_Crafting/Modules/VellumInfo.lua @@ -2,311 +2,262 @@ local TSM = select(2, ...) TSM.VellumInfo = { -[7418] = 52510, -[7420] = 52510, -[7426] = 52510, -[7428] = 52510, -[7443] = 52510, -[7454] = 52510, -[7457] = 52510, -[7745] = 52511, -[7748] = 52510, -[7766] = 52510, -[7771] = 52510, -[7776] = 52510, -[7779] = 52510, -[7782] = 52510, -[7786] = 52511, -[7788] = 52511, -[7793] = 52511, -[7857] = 52510, -[7859] = 52510, -[7861] = 52510, -[7863] = 52510, -[7867] = 52510, -[13378] = 52510, -[13380] = 52511, -[13419] = 52510, -[13421] = 52510, -[13464] = 52510, -[13485] = 52510, -[13501] = 52510, -[13503] = 52511, -[13522] = 52510, -[13529] = 52511, -[13536] = 52510, -[13538] = 52510, -[13607] = 52510, -[13612] = 52510, -[13617] = 52510, -[13620] = 52510, -[13622] = 52510, -[13626] = 52510, -[13631] = 52510, -[13635] = 52510, -[13637] = 52510, -[13640] = 52510, -[13642] = 52510, -[13644] = 52510, -[13646] = 52510, -[13648] = 52510, -[13653] = 52511, -[13655] = 52511, -[13657] = 52510, -[13659] = 52510, -[13661] = 52510, -[13663] = 52510, -[13687] = 52510, -[13689] = 52510, -[13693] = 52511, -[13695] = 52511, -[13698] = 52510, -[13700] = 52510, -[13746] = 52510, -[13794] = 52510, -[13815] = 52510, -[13817] = 52510, -[13822] = 52510, -[13836] = 52510, -[13841] = 52510, -[13846] = 52510, -[13858] = 52510, -[13868] = 52510, -[13882] = 52510, -[13887] = 52510, -[13890] = 52510, -[13898] = 52511, -[13905] = 52510, -[13915] = 52511, -[13917] = 52510, -[13931] = 52510, -[13933] = 52510, -[13935] = 52510, -[13937] = 52511, -[13939] = 52510, -[13941] = 52510, -[13943] = 52511, -[13945] = 52510, -[13947] = 52510, -[13948] = 52510, -[20008] = 52510, -[20009] = 52510, -[20010] = 52510, -[20011] = 52510, -[20012] = 52510, -[20013] = 52510, -[20014] = 52510, -[20015] = 52510, -[20016] = 52510, -[20017] = 52510, -[20020] = 52510, -[20023] = 52510, -[20024] = 52510, -[20025] = 52510, -[20026] = 52510, -[20028] = 52510, -[20029] = 52511, -[20030] = 52511, -[20031] = 52511, -[20032] = 52511, -[20033] = 52511, -[20034] = 52511, -[20035] = 52511, -[20036] = 52511, -[21931] = 52511, -[22749] = 52511, -[22750] = 52511, -[23799] = 52511, -[23800] = 52511, -[23801] = 52510, -[23802] = 52510, -[23803] = 52511, -[23804] = 52511, -[25072] = 52510, -[25073] = 52510, -[25074] = 52510, -[25078] = 52510, -[25079] = 52510, -[25080] = 52510, -[25081] = 52510, -[25082] = 52510, -[25083] = 52510, -[25084] = 52510, -[25086] = 52510, -[27837] = 52511, -[27899] = 52510, -[27905] = 52510, -[27906] = 52510, -[27911] = 52510, -[27913] = 52510, -[27914] = 52510, -[27917] = 52510, -[27944] = 52510, -[27945] = 52510, -[27946] = 52510, -[27947] = 52510, -[27948] = 52510, -[27950] = 52510, -[27951] = 52510, -[27954] = 52510, -[27957] = 52510, -[27958] = 52510, -[27960] = 52510, -[27961] = 52510, -[27962] = 52510, -[27967] = 52511, -[27968] = 52511, -[27971] = 52511, -[27972] = 52511, -[27975] = 52511, -[27977] = 52511, -[27981] = 52511, -[27982] = 52511, -[27984] = 52511, -[28003] = 52511, -[28004] = 52511, -[33990] = 52510, -[33991] = 52510, -[33992] = 52510, -[33993] = 52510, -[33994] = 52510, -[33995] = 52510, -[33996] = 52510, -[33997] = 52510, -[33999] = 52510, -[34001] = 52510, -[34002] = 52510, -[34003] = 52510, -[34004] = 52510, -[34005] = 52510, -[34006] = 52510, -[34007] = 52510, -[34008] = 52510, -[34009] = 52510, -[34010] = 52511, -[42620] = 52511, -[42974] = 52511, -[44383] = 52510, -[44483] = 52510, -[44484] = 52510, -[44488] = 52510, -[44489] = 52510, -[44492] = 52510, -[44494] = 52510, -[44500] = 52510, -[44506] = 52510, -[44508] = 52510, -[44509] = 52510, -[44510] = 52511, -[44513] = 52510, -[44524] = 52511, -[44528] = 52510, -[44529] = 52510, -[44555] = 52510, -[44556] = 52510, -[44575] = 52510, -[44576] = 52511, -[44582] = 52510, -[44584] = 52510, -[44588] = 52510, -[44589] = 52510, -[44590] = 52510, -[44591] = 52510, -[44592] = 52510, -[44593] = 52510, -[44595] = 52511, -[44596] = 52510, -[44598] = 52510, -[44612] = 52510, -[44616] = 52510, -[44621] = 52511, -[44623] = 52510, -[44625] = 52510, -[44629] = 52511, -[44630] = 52511, -[44631] = 52510, -[44633] = 52511, -[44635] = 52510, -[46578] = 52511, -[46594] = 52510, -[47051] = 52510, -[47672] = 52510, -[47766] = 52510, -[47898] = 52510, -[47899] = 52510, -[47900] = 52510, -[47901] = 52510, -[59619] = 52511, -[59621] = 52511, -[59625] = 52511, -[60606] = 52510, -[60609] = 52510, -[60616] = 52510, -[60621] = 52511, -[60623] = 52510, -[60653] = 52510, -[60663] = 52510, -[60668] = 52510, -[60691] = 52511, -[60692] = 52510, -[60707] = 52511, -[60714] = 52511, -[60763] = 52510, -[60767] = 52510, -[62256] = 52510, -[62257] = 52511, -[62948] = 52511, -[62959] = 52511, -[63746] = 52510, -[64441] = 52511, -[64579] = 52511, -[71692] = 52510, -[968676] = 52511, -[968677] = 52511, -[968678] = 52511, -[968679] = 52511, -[968680] = 52511, -[968681] = 52511, -[968682] = 52511, -[968683] = 52511, -[968684] = 52511, -[968685] = 52511, -[968686] = 52511, -[968687] = 52511, -[968688] = 52511, -[968689] = 52511, -[968690] = 52511, -[968691] = 52511, -[968692] = 52511, -[968693] = 52511, -[968694] = 52511, -[968695] = 52511, -[968696] = 52511, -[968697] = 52511, -[968698] = 52511, -[968699] = 52511, -[968700] = 52511, -[968701] = 52511, -[968702] = 52511, -[968770] = 52511, -[968771] = 52511, -[1968677] = 52511, -[1968678] = 52511, -[1968680] = 52511, -[1968681] = 52511, -[1968683] = 52511, -[1968684] = 52511, -[1968686] = 52511, -[1968687] = 52511, -[1968689] = 52511, -[1968690] = 52511, -[1968692] = 52511, -[1968693] = 52511, -[1968695] = 52511, -[1968696] = 52511, -[1968698] = 52511, -[1968699] = 52511, -[1968701] = 52511, -[1968702] = 52511, -[1968770] = 52511, -[1968771] = 52511} +[7418] = 38682, +[7420] = 38682, +[7426] = 38682, +[7428] = 38682, +[7443] = 38682, +[7454] = 38682, +[7457] = 38682, +[7745] = 39349, +[7748] = 38682, +[7766] = 38682, +[7771] = 38682, +[7776] = 38682, +[7779] = 38682, +[7782] = 38682, +[7786] = 39349, +[7788] = 39349, +[7793] = 39349, +[7857] = 38682, +[7859] = 38682, +[7861] = 38682, +[7863] = 38682, +[7867] = 38682, +[13378] = 38682, +[13380] = 39349, +[13419] = 38682, +[13421] = 38682, +[13464] = 38682, +[13485] = 38682, +[13501] = 38682, +[13503] = 39349, +[13522] = 38682, +[13529] = 39349, +[13536] = 38682, +[13538] = 38682, +[13607] = 38682, +[13612] = 38682, +[13617] = 38682, +[13620] = 38682, +[13622] = 38682, +[13626] = 38682, +[13631] = 38682, +[13635] = 38682, +[13637] = 38682, +[13640] = 38682, +[13642] = 38682, +[13644] = 38682, +[13646] = 38682, +[13648] = 38682, +[13653] = 39349, +[13655] = 39349, +[13657] = 38682, +[13659] = 38682, +[13661] = 38682, +[13663] = 38682, +[13687] = 38682, +[13689] = 38682, +[13693] = 39349, +[13695] = 39349, +[13698] = 38682, +[13700] = 38682, +[13746] = 38682, +[13794] = 38682, +[13815] = 38682, +[13817] = 38682, +[13822] = 38682, +[13836] = 38682, +[13841] = 38682, +[13846] = 38682, +[13858] = 38682, +[13868] = 38682, +[13882] = 38682, +[13887] = 38682, +[13890] = 38682, +[13898] = 39349, +[13905] = 38682, +[13915] = 39349, +[13917] = 38682, +[13931] = 38682, +[13933] = 38682, +[13935] = 38682, +[13937] = 39349, +[13939] = 38682, +[13941] = 38682, +[13943] = 39349, +[13945] = 38682, +[13947] = 38682, +[13948] = 38682, +[20008] = 38682, +[20009] = 38682, +[20010] = 38682, +[20011] = 38682, +[20012] = 38682, +[20013] = 38682, +[20014] = 38682, +[20015] = 38682, +[20016] = 38682, +[20017] = 38682, +[20020] = 38682, +[20023] = 38682, +[20024] = 38682, +[20025] = 38682, +[20026] = 38682, +[20028] = 38682, +[20029] = 39349, +[20030] = 39349, +[20031] = 39349, +[20032] = 39349, +[20033] = 39349, +[20034] = 39349, +[20035] = 39349, +[20036] = 39349, +[21931] = 39349, +[22749] = 39349, +[22750] = 39349, +[23799] = 39349, +[23800] = 39349, +[23801] = 38682, +[23802] = 38682, +[23803] = 39349, +[23804] = 39349, +[25072] = 38682, +[25073] = 38682, +[25074] = 38682, +[25078] = 38682, +[25079] = 38682, +[25080] = 38682, +[25081] = 38682, +[25082] = 38682, +[25083] = 38682, +[25084] = 38682, +[25086] = 37602, +[27837] = 39349, +[27899] = 37602, +[27905] = 37602, +[27906] = 37602, +[27911] = 37602, +[27913] = 37602, +[27914] = 37602, +[27917] = 37602, +[27944] = 37602, +[27945] = 37602, +[27946] = 37602, +[27947] = 37602, +[27948] = 37602, +[27950] = 37602, +[27951] = 37602, +[27954] = 37602, +[27957] = 37602, +[27958] = 43145, +[27960] = 37602, +[27961] = 37602, +[27962] = 37602, +[27967] = 39350, +[27968] = 39350, +[27971] = 39350, +[27972] = 39350, +[27975] = 39350, +[27977] = 39350, +[27981] = 39350, +[27982] = 39350, +[27984] = 39350, +[28003] = 39350, +[28004] = 39350, +[33990] = 37602, +[33991] = 37602, +[33992] = 37602, +[33993] = 37602, +[33994] = 37602, +[33995] = 37602, +[33996] = 37602, +[33997] = 37602, +[33999] = 37602, +[34001] = 37602, +[34002] = 37602, +[34003] = 37602, +[34004] = 37602, +[34005] = 37602, +[34006] = 37602, +[34007] = 37602, +[34008] = 37602, +[34009] = 37602, +[34010] = 39350, +[42620] = 39350, +[42974] = 43146, +[44383] = 37602, +[44483] = 43145, +[44484] = 43145, +[44488] = 43145, +[44489] = 43145, +[44492] = 43145, +[44494] = 43145, +[44500] = 43145, +[44506] = 43145, +[44508] = 43145, +[44509] = 43145, +[44510] = 43146, +[44513] = 43145, +[44524] = 43146, +[44528] = 43145, +[44529] = 43145, +[44555] = 43145, +[44556] = 43145, +[44575] = 43145, +[44576] = 43146, +[44582] = 43145, +[44584] = 43145, +[44588] = 43145, +[44589] = 43145, +[44590] = 43145, +[44591] = 43145, +[44592] = 43145, +[44593] = 43145, +[44595] = 43146, +[44596] = 43145, +[44598] = 43145, +[44612] = 43145, +[44616] = 43145, +[44621] = 43146, +[44623] = 43145, +[44625] = 43145, +[44629] = 43146, +[44630] = 43146, +[44631] = 43145, +[44633] = 43146, +[44635] = 43145, +[46578] = 43146, +[46594] = 37602, +[47051] = 37602, +[47672] = 43145, +[47766] = 43145, +[47898] = 43145, +[47899] = 43145, +[47900] = 43145, +[47901] = 43145, +[59619] = 43146, +[59621] = 43146, +[59625] = 43146, +[60606] = 43145, +[60609] = 43145, +[60616] = 43145, +[60621] = 43146, +[60623] = 43145, +[60653] = 43145, +[60663] = 43145, +[60668] = 43145, +[60691] = 43146, +[60692] = 43145, +[60707] = 43146, +[60714] = 43146, +[60763] = 43145, +[60767] = 43145, +[62256] = 43145, +[62257] = 43146, +[62948] = 43146, +[62959] = 43146, +[63746] = 38682, +[64441] = 39349, +[64579] = 39349, +[71692] = 38682} \ No newline at end of file diff --git a/TradeSkillMaster_Crafting/TradeSkillMaster_Crafting.lua b/TradeSkillMaster_Crafting/TradeSkillMaster_Crafting.lua index e72f53f..3051775 100644 --- a/TradeSkillMaster_Crafting/TradeSkillMaster_Crafting.lua +++ b/TradeSkillMaster_Crafting/TradeSkillMaster_Crafting.lua @@ -58,26 +58,76 @@ function TSM:OnEnable() -- fix vellum issue for spellid, data in pairs(TSM.db.factionrealm.crafts) do for itemString in pairs(data.mats) do - if itemString == "item:52510" then - TSM.db.factionrealm.crafts[spellid].mats["item:52510:0:0:0:0:0:0"] = 1 + -- if itemString == "item:38682" then + -- TSM.db.factionrealm.crafts[spellid].mats["item:38682:0:0:0:0:0:0"] = 1 + if itemString == "item:43146" then + TSM.db.factionrealm.crafts[spellid].mats["item:43146:0:0:0:0:0:0"] = 1 TSM.db.factionrealm.crafts[spellid].mats[itemString] = nil - elseif itemString == "item:52511" then - TSM.db.factionrealm.crafts[spellid].mats["item:52511:0:0:0:0:0:0"] = 1 + elseif itemString == "item:43145" then + TSM.db.factionrealm.crafts[spellid].mats["item:43145:0:0:0:0:0:0"] = 1 + TSM.db.factionrealm.crafts[spellid].mats[itemString] = nil + elseif itemString == "item:39350" then + TSM.db.factionrealm.crafts[spellid].mats["item:39350:0:0:0:0:0:0"] = 1 + TSM.db.factionrealm.crafts[spellid].mats[itemString] = nil + elseif itemString == "item:37602" then + TSM.db.factionrealm.crafts[spellid].mats["item:37602:0:0:0:0:0:0"] = 1 + TSM.db.factionrealm.crafts[spellid].mats[itemString] = nil + elseif itemString == "item:39349" then + TSM.db.factionrealm.crafts[spellid].mats["item:39349:0:0:0:0:0:0"] = 1 + TSM.db.factionrealm.crafts[spellid].mats[itemString] = nil + elseif itemString == "item:38682" then + TSM.db.factionrealm.crafts[spellid].mats["item:38682:0:0:0:0:0:0"] = 1 TSM.db.factionrealm.crafts[spellid].mats[itemString] = nil end end end - if TSM.db.factionrealm.mats["item:52510"] then - local name = TSMAPI:GetSafeItemInfo("item:52510:0:0:0:0:0:0") or nil - TSM.db.factionrealm.mats["item:52510:0:0:0:0:0:0"] = {} - TSM.db.factionrealm.mats["item:52510:0:0:0:0:0:0"].name = name - TSM.db.factionrealm.mats["item:52510"] = nil + -- if TSM.db.factionrealm.mats["item:38682"] then + -- local name = TSMAPI:GetSafeItemInfo("item:38682:0:0:0:0:0:0") or (GetLocale() == "enUS" and "Enchanting Vellum") or nil + -- TSM.db.factionrealm.mats["item:38682:0:0:0:0:0:0"] = {} + -- TSM.db.factionrealm.mats["item:38682:0:0:0:0:0:0"].name = name + -- TSM.db.factionrealm.mats["item:38682"] = nil + -- end + if TSM.db.factionrealm.mats["item:43146"] then + local name = TSMAPI:GetSafeItemInfo("item:43146:0:0:0:0:0:0") or nil + TSM.db.factionrealm.mats["item:43146:0:0:0:0:0:0"] = {} + TSM.db.factionrealm.mats["item:43146:0:0:0:0:0:0"].name = name + TSM.db.factionrealm.mats["item:43146"] = nil + end + if TSM.db.factionrealm.mats["item:43145"] then + local name = TSMAPI:GetSafeItemInfo("item:43145:0:0:0:0:0:0") or nil + TSM.db.factionrealm.mats["item:43145:0:0:0:0:0:0"] = {} + TSM.db.factionrealm.mats["item:43145:0:0:0:0:0:0"].name = name + TSM.db.factionrealm.mats["item:43145"] = nil + end + if TSM.db.factionrealm.mats["item:39350"] then + local name = TSMAPI:GetSafeItemInfo("item:39350:0:0:0:0:0:0") or nil + TSM.db.factionrealm.mats["item:39350:0:0:0:0:0:0"] = {} + TSM.db.factionrealm.mats["item:39350:0:0:0:0:0:0"].name = name + TSM.db.factionrealm.mats["item:39350"] = nil + end + if TSM.db.factionrealm.mats["item:39350"] then + local name = TSMAPI:GetSafeItemInfo("item:39350:0:0:0:0:0:0") or nil + TSM.db.factionrealm.mats["item:39350:0:0:0:0:0:0"] = {} + TSM.db.factionrealm.mats["item:39350:0:0:0:0:0:0"].name = name + TSM.db.factionrealm.mats["item:39350"] = nil end - if TSM.db.factionrealm.mats["item:52511"] then - local name = TSMAPI:GetSafeItemInfo("item:52511:0:0:0:0:0:0") or nil - TSM.db.factionrealm.mats["item:52511:0:0:0:0:0:0"] = {} - TSM.db.factionrealm.mats["item:52511:0:0:0:0:0:0"].name = name - TSM.db.factionrealm.mats["item:52511"] = nil + if TSM.db.factionrealm.mats["item:37602"] then + local name = TSMAPI:GetSafeItemInfo("item:37602:0:0:0:0:0:0") or nil + TSM.db.factionrealm.mats["item:37602:0:0:0:0:0:0"] = {} + TSM.db.factionrealm.mats["item:37602:0:0:0:0:0:0"].name = name + TSM.db.factionrealm.mats["item:37602"] = nil + end + if TSM.db.factionrealm.mats["item:39349"] then + local name = TSMAPI:GetSafeItemInfo("item:39349:0:0:0:0:0:0") or nil + TSM.db.factionrealm.mats["item:39349:0:0:0:0:0:0"] = {} + TSM.db.factionrealm.mats["item:39349:0:0:0:0:0:0"].name = name + TSM.db.factionrealm.mats["item:39349"] = nil + end + if TSM.db.factionrealm.mats["item:38682"] then + local name = TSMAPI:GetSafeItemInfo("item:38682:0:0:0:0:0:0") or nil + TSM.db.factionrealm.mats["item:38682:0:0:0:0:0:0"] = {} + TSM.db.factionrealm.mats["item:38682:0:0:0:0:0:0"].name = name + TSM.db.factionrealm.mats["item:38682"] = nil end local func, err = TSMAPI:ParseCustomPrice(TSM.db.global.defaultCraftPriceMethod, "crafting") @@ -188,6 +238,33 @@ function TSM:GetTooltip(itemString) local name, _, quality = TSMAPI:GetSafeItemInfo(matItemString) if name then local mat = TSM.db.factionrealm.mats[matItemString] + + + + -- Get Cheapest vellum, lower vellum types can be replaced by III + local velName + if strfind(name, "Vellum") then + velName = name + end + if (velName ~= nil) and (not strfind(velName, "III")) then + local VellumReplacePrice = TSM.Cost:GetMatCost(matItemString) + + if strfind(velName, "Weapon Vellum") then + if VellumReplacePrice > TSM.Cost:GetMatCost("item:43146:0:0:0:0:0:0") then + matItemString = "item:43146:0:0:0:0:0:0" + name = TSMAPI:GetSafeItemInfo(matItemString) + end + else + if VellumReplacePrice > TSM.Cost:GetMatCost("item:43145:0:0:0:0:0:0") then + matItemString = "item:43145:0:0:0:0:0:0" + name = TSMAPI:GetSafeItemInfo(matItemString) + end + end + end + + + + if mat then local cost = TSM:GetCustomPrice(mat.customValue or TSM.db.global.defaultMatCostMethod, matItemString) if cost then @@ -358,4 +435,4 @@ function TSM:RestockHelp(link) end print("This item will be added to the queue when you restock its group. If this isn't happening, make a post on the TSM forums with a screenshot of the item's tooltip, operation settings, and your general TSM_Crafting options.") -end +end \ No newline at end of file diff --git a/TradeSkillMaster_Shopping/Locale/enUS.lua b/TradeSkillMaster_Shopping/Locale/enUS.lua index 0e4571b..0a5d54e 100644 --- a/TradeSkillMaster_Shopping/Locale/enUS.lua +++ b/TradeSkillMaster_Shopping/Locale/enUS.lua @@ -115,6 +115,7 @@ L["Reset Filters"] = true L["Right-Click to favorite this recent search."] = true L["Right-Click to remove from favorite searches."] = true L["Saved Searches"] = true +L["Scanning %d / %d (Page 1 / ?)"] = true L["Scanning %d / %d (Page %d / %d)"] = true L["Search Filter:"] = true L["Select the groups which you would like to include in the search."] = true diff --git a/TradeSkillMaster_Shopping/Locale/koKR.lua b/TradeSkillMaster_Shopping/Locale/koKR.lua index fb81da9..bbe2464 100644 --- a/TradeSkillMaster_Shopping/Locale/koKR.lua +++ b/TradeSkillMaster_Shopping/Locale/koKR.lua @@ -110,6 +110,7 @@ L["Reset Filters"] = "필터 리셋" -- Needs review L["Right-Click to favorite this recent search."] = "Right-Click으로 이 검색을 즐겨찾기에 등록합니다." -- Needs review L["Right-Click to remove from favorite searches."] = "Right-Click으로 츨겨찾기 검색에서 제거합니다." -- Needs review L["Saved Searches"] = "저장된 검색" +L["Scanning %d / %d (Page 1 / ?)"] = "검색 중 %d / %d (페이지 1 / ?)" -- Needs review L["Scanning %d / %d (Page %d / %d)"] = "검색 중 %d / %d (페이지 %d / %d)" -- Needs review L["Search Filter:"] = "검색 필터:" -- Needs review L["Select the groups which you would like to include in the search."] = "검색에 포함할 그룹을 선택하세요." -- Needs review diff --git a/TradeSkillMaster_Shopping/Locale/ruRU.lua b/TradeSkillMaster_Shopping/Locale/ruRU.lua index bc4075f..9671352 100644 --- a/TradeSkillMaster_Shopping/Locale/ruRU.lua +++ b/TradeSkillMaster_Shopping/Locale/ruRU.lua @@ -111,7 +111,8 @@ L["Recent Searches"] = "Недавние поиски" -- Needs review -- L["Right-Click to favorite this recent search."] = "" -- L["Right-Click to remove from favorite searches."] = "" L["Saved Searches"] = "Сохраненные поиски" -- Needs review --- L["Scanning %d / %d (Page %d / %d)"] = "" +L["Scanning %d / %d (Page 1 / ?)"] = "Скан %d / %d (стр 1 / ?)" +L["Scanning %d / %d (Page %d / %d)"] = "Скан %d / %d (стр %d / %d)" -- L["Search Filter:"] = "" -- L["Select the groups which you would like to include in the search."] = "" L["'%s' has a Shopping operation of '%s' which no longer exists. Shopping will ignore this group until this is fixed."] = "\"% s \" имеет Торговые операции '%s', которая больше не существует. Торговые будете игнорировать эту группу до тех пор, пока это будет Исправлено." -- Needs review diff --git a/TradeSkillMaster_Shopping/Locale/zhCN.lua b/TradeSkillMaster_Shopping/Locale/zhCN.lua index f292f9c..ab337cb 100644 --- a/TradeSkillMaster_Shopping/Locale/zhCN.lua +++ b/TradeSkillMaster_Shopping/Locale/zhCN.lua @@ -112,6 +112,7 @@ L["Reset Filters"] = "重置筛选器" L["Right-Click to favorite this recent search."] = "右键点击 保存到最喜欢的搜索" L["Right-Click to remove from favorite searches."] = "右键点击 从最喜欢的搜索里移除" L["Saved Searches"] = "保存搜索" +L["Scanning %d / %d (Page 1 / ?)"] = "扫描 %d / %d(页面 1 / ?)" L["Scanning %d / %d (Page %d / %d)"] = "扫描 %d / %d(页面 %d / %d)" L["Search Filter:"] = "搜索筛选器:" L["Select the groups which you would like to include in the search."] = "选择你想搜索的分组" diff --git a/TradeSkillMaster_Shopping/modules/Util.lua b/TradeSkillMaster_Shopping/modules/Util.lua index e16908f..680dc66 100644 --- a/TradeSkillMaster_Shopping/modules/Util.lua +++ b/TradeSkillMaster_Shopping/modules/Util.lua @@ -202,17 +202,29 @@ function private:PrepareForScan(callback, isLastPageScan) TSM.moduleAPICallback = nil end -local scanStatus, pageStatus function private.ScanCallback(event, ...) if event == "QUERY_COMPLETE" then private.filterList = ... private.numFilters = #private.filterList private:ScanNextFilter() elseif event == "QUERY_UPDATE" then - local arg1, arg2 = ... - private:UpdateStatus("query", arg1, arg2) + local current, total, skipped = ... + private:UpdateStatus("query", current, total) elseif event == "SCAN_PAGE_UPDATE" then + -- Simply forward the "currently received" and "total" page counts + -- for the current item we're scanning. + -- NOTE: Private servers sometimes give totally insane "total page counts" + -- on the first response, such as "page 1/142", but those server errors + -- always correct themselves to "page 2/REAL" when the scan has reached + -- page 2. There's nothing we can do to avoid that rare page-count issue + -- (which happens on many popular private servers, such as Warmane). private:UpdateStatus("page", ...) + elseif event == "SCAN_INTERRUPTED" or event == "INTERRUPTED" then + -- We've been interrupted by the Auction House closing. + -- NOTE: "SCAN_INTERRUPTED" is from LibAuctionScan-1.0, which isn't used + -- by TSM anymore, and "INTERRUPTED" is from "TSM/Auction/AuctionScanning.lua", + -- which is what this scanner uses nowadays. + private:ScanComplete(true) elseif event == "SCAN_TIMEOUT" then tremove(private.filterList, 1) private:ScanNextFilter() @@ -288,14 +300,25 @@ function private.ScanCallback(event, ...) end function private:ScanNextFilter() + -- We must reset the page counter, otherwise the next scan will keep the + -- page count of the previous item until we receive "SCAN_PAGE_UPDATE". + -- NOTE: The "nil" signals that we don't know the item's page count yet. + -- NOTE: The recipient may want to ignore "page" events that have nil values + -- and not update their status bars based on those, since we send these empty + -- page events before we start each new scan! + private:UpdateStatus("page", nil, nil) + + -- Now update the scan counter. + -- NOTE: Our scan progress counter below starts counting from 0 as the first item. if #private.filterList == 0 then + private:UpdateStatus("scan", private.numFilters, private.numFilters) return private:ScanComplete() end - pageStatus = {0, 1} - private:UpdateStatus("scan", private.numFilters-#private.filterList+1, private.numFilters) + private:UpdateStatus("scan", private.numFilters-#private.filterList, private.numFilters) TSMAPI.AuctionScan:RunQuery(private.filterList[1], private.ScanCallback, true, private.callback("filter", private.filterList[1]), true) end +local scanStatus, pageStatus function private:UpdateStatus(statusType, ...) if statusType == "query" then private.searchFrame.statusBar:SetStatusText(format(L["Preparing Filter %d / %d"], ...)) @@ -305,31 +328,63 @@ function private:UpdateStatus(statusType, ...) scanStatus = {...} elseif statusType == "page" then pageStatus = {...} + -- Ignore the empty "reset page count" events that happen before every new + -- scan. We've reset the page counts, which is the only thing that matters. + if pageStatus[1] == nil and pageStatus[2] == nil then + return + end + end + + -- NOTE: We don't do +1 for the item-scan or page-status counters below, + -- since we want our progress bar to show FULLY RECEIVED/FINISHED items + -- and pages, and not fill up until we've fully received each item/page. + local progress_bar_items = min(100*(scanStatus[1]/scanStatus[2]), 100) -- Calculate "total items" progress bar from 0-100%. + local progress_bar_pages = 0 + -- NOTE: In the status text label, we count the items starting at 1, + -- to say "Scanning 1 / 2" (instead of "Scanning 0 / 2"). For pages, + -- we show which page we're waiting for (so if we have received 2/4 pages, + -- the label will say 3/4, meaning "we're waiting for page 3..."). + if pageStatus[1] ~= nil and pageStatus[2] ~= nil then + -- We have received the page counter ("current page / total pages") for the current item. + -- NOTE: We add "+1" to the page counter, to indicate that we've received that page and are working on the next page. + private.searchFrame.statusBar:SetStatusText(format(L["Scanning %d / %d (Page %d / %d)"], scanStatus[1] + 1, scanStatus[2], min(pageStatus[1] + 1, pageStatus[2]), pageStatus[2])) + progress_bar_pages = min(100*(pageStatus[1]/pageStatus[2]), 100) -- Calculate "total pages of current item" progress bar from 0-100%. + else + -- We have started a new item scan but haven't received the page count yet. + -- NOTE: If we don't initialize the page-progress to 0%, we'd get a "jumpy" + -- progress bar that goes from 100% at page 1/? to "real%" after page 1. + private.searchFrame.statusBar:SetStatusText(format(L["Scanning %d / %d (Page 1 / ?)"], scanStatus[1] + 1, scanStatus[2])) + progress_bar_pages = 0 -- Begin at 0% for the gray page-counter bar of the current item. end - private.searchFrame.statusBar:SetStatusText(format(L["Scanning %d / %d (Page %d / %d)"], scanStatus[1], scanStatus[2], pageStatus[1]+1, pageStatus[2])) - private.searchFrame.statusBar:UpdateStatus(100*(scanStatus[1]-1)/scanStatus[2], 100*pageStatus[1]/pageStatus[2]) + private.searchFrame.statusBar:UpdateStatus(progress_bar_items, progress_bar_pages) end end -function private:ScanComplete() - if not private.callback then return end - private.searchFrame.statusBar:SetStatusText(L["Done Scanning"]) - private.searchFrame.statusBar:UpdateStatus(100, 100) - private.searchFrame.rt:SetDisabled(false) - if #private.searchFrame.rt.auctionData == 1 then - private.searchFrame.rt:SetExpanded(private.searchFrame.rt.auctionData[1]:GetItemString(), true) - private.searchFrame.rt.rows[1].cols[1]:Click() - elseif #private.searchFrame.rt.auctionData == 0 and private.searchItem and private:HasInBags(TSMAPI:GetBaseItemString(private.searchItem)) then - private.controlButtons.post:Enable() - local postPrice = TSM:GetMaxPrice(TSM.db.global.normalPostPrice, private.searchItem) or 0 - TSMAPI.AuctionControl:SetNoResultItem(private.searchItem, postPrice) - end - - if #private.searchFrame.rt.auctionData == 0 and TSM.moduleAPICallback then - TSM.moduleAPICallback() +function private:ScanComplete(interrupted) + if interrupted then + -- If our scan has been interrupted by the Auction House closing, + -- simply act as if the user clicked "Stop". + Util:StopScan() + else + if not private.callback then return end + private.searchFrame.statusBar:SetStatusText(L["Done Scanning"]) + private.searchFrame.statusBar:UpdateStatus(100, 100) + private.searchFrame.rt:SetDisabled(false) + if #private.searchFrame.rt.auctionData == 1 then + private.searchFrame.rt:SetExpanded(private.searchFrame.rt.auctionData[1]:GetItemString(), true) + private.searchFrame.rt.rows[1].cols[1]:Click() + elseif #private.searchFrame.rt.auctionData == 0 and private.searchItem and private:HasInBags(TSMAPI:GetBaseItemString(private.searchItem)) then + private.controlButtons.post:Enable() + local postPrice = TSM:GetMaxPrice(TSM.db.global.normalPostPrice, private.searchItem) or 0 + TSMAPI.AuctionControl:SetNoResultItem(private.searchItem, postPrice) + end + + if #private.searchFrame.rt.auctionData == 0 and TSM.moduleAPICallback then + TSM.moduleAPICallback() + end + private.callback("done", private.auctions) + TSMAPI:FireEvent("SHOPPING:SEARCH:SCANDONE", #private.searchFrame.rt.auctionData) end - private.callback("done", private.auctions) - TSMAPI:FireEvent("SHOPPING:SEARCH:SCANDONE", #private.searchFrame.rt.auctionData) end -- processes scan data for a specific item