Advertisement
lilig

Dupefind 0.5.1

Dec 3rd, 2018
820
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 7.74 KB | None | 0 0
  1. --[[
  2. Copyright © Lili, 2019
  3. All rights reserved.
  4.  
  5. Redistribution and use in source and binary forms, with or without
  6. modification, are permitted provided that the following conditions are met:
  7.  
  8.     * Redistributions of source code must retain the above copyright
  9.       notice, this list of conditions and the following disclaimer.
  10.     * Redistributions in binary form must reproduce the above copyright
  11.       notice, this list of conditions and the following disclaimer in the
  12.       documentation and/or other materials provided with the distribution.
  13.     * Neither the name of dumperino nor the
  14.       names of its contributors may be used to endorse or promote products
  15.       derived from this software without specific prior written permission.
  16.  
  17. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  18. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  19. WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  20. DISCLAIMED. IN NO EVENT SHALL <your name> BE LIABLE FOR ANY
  21. DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  22. (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  23. LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  24. ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  25. (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  26. SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27. ]]
  28.  
  29. --[[ --------------------------------------------------------------------------
  30. ----------------------------------- README ------------------------------------
  31. -------------------------------------------------------------------------------
  32. dupefind: finds duplicates on current char/across all characters
  33.  
  34. usage:
  35. //dupefind flag1 flag2 ... flagN
  36.  
  37. shorthands: //dupe, //df
  38.  
  39. dupefind by default does not display:
  40. - items with the RARE or EXclusive tags
  41. - items that cannot be stacked (with a stack size of 1)
  42. - items that cannot be sent to another character on the same account
  43. and will only disply items on the currently logged in character.
  44.  
  45. flags:
  46. - rare: includes rare items
  47. - ex: includes exclusive items as long as they can be sent via POL
  48. - nostack: includes items with stack size of 1
  49. - findall: searchs every available character instead of just current
  50. to use the findall flag, you need to have the addon findAll installed, and have
  51. run it at least once on all characters
  52.  
  53. example:
  54. //dupe nostack findall - will find all duplicate items that are not rare/ex,
  55.                          across every character
  56. //dupe ex nostack      - will find all duplicate items, including Ex and items
  57.                          that do not stack, but excluding Rare
  58.  
  59. Changelog
  60.  
  61. 0.5.1 - Cleanup and better help text.
  62. 0.4 - Some more cleanup.
  63. 0.3 - First release. Many thanks to Arcon for the feedback about the code.
  64. 0.2 - Cleanup. Multiple character search, toggles for stackable, rare, ex, CanSendPol.
  65. 0.1 - Initial version. Single character search and stackable toggle.
  66.  
  67. Thanks to Zohno, this addon contains code taken from his addon findAll
  68. Thanks to Arcon, he took the time to read my original iterations and letting me know all the dumb crap I was doing
  69. ]]
  70.  
  71. _addon.name = 'dupefind'
  72. _addon.author = 'Lili'
  73. _addon.version = '0.5.1'
  74. _addon.commands = {'dupefind','dupe', 'df',}
  75.  
  76. require('logger')
  77. require('tables')
  78. require('strings')
  79. --require('config')
  80. res_items = require('resources').items
  81. lang = windower.ffxi.get_info().language:lower()
  82.  
  83. function preferences()
  84.     ignore_items = S{ 'linkshell', 'linkpearl' }
  85.     ignore_players = S{ 'Yourmule','Andyourothermule', }
  86.  
  87.     ignore_rare = true
  88.     ignore_ex = true
  89.     ignore_nostack = true
  90.    
  91.     player_only = true
  92.     filter_by_player = true
  93. end
  94.  
  95. bags = S{'safe','safe2','storage','locker','inventory','satchel','sack','case','wardrobe','wardrobe2','wardrobe3','wardrobe4'}
  96.  
  97. -------------------------------------------------------------------------------------------------------------
  98. preferences()
  99.  
  100. ignore_items = ignore_items:map(string.lower)
  101. ignore_ids = res_items:filter(function(item)
  102.         return ignore_items:contains(item.name:lower()) or ignore_items:contains(item.name_log:lower())
  103.     end):keyset()
  104.  
  105. local get_flag = function(args, flag, default)
  106.     for _, arg in ipairs(args) do
  107.         if arg == flag then
  108.             return false
  109.         end
  110.     end
  111.     return default
  112. end
  113.  
  114. function CanSendPol(id) return S(res_items[id].flags):contains('Can Send POL') end
  115. function IsRare(id) return S(res_items[id].flags):contains('Rare') end
  116. function IsExclusive(id) return S(res_items[id].flags):contains('Exclusive') or S(res_items[id].flags):contains('No PC Trade') end
  117. function IsStackable(id) return res_items[id].stack > 1 end
  118.  
  119. function work(...)
  120.     args = {...}    
  121.    
  122.     local ignore_rare = get_flag(args, 'rare', ignore_rare) -- where `settings` is the global settings table
  123.     local ignore_ex = get_flag(args, 'ex', ignore_ex)
  124.     local ignore_nostack = get_flag(args, 'nostack', ignore_nostack)
  125.     local player_only = get_flag(args, 'findall', player_only)
  126.     local filter_by_player = get_flag(args, 'nofilter', filter_by_player)
  127.    
  128.     local player = windower.ffxi.get_player().name
  129.     local inventory = windower.ffxi.get_items()
  130.     local storages = {}
  131.    
  132.     storages[player] = {}
  133.    
  134.     local haystack = {}
  135.     local results = 0
  136.  
  137.     -- flatten inventory
  138.     --Shamelessly stolen from findAll. Many thanks to Zohno.   
  139.     for bag,_ in pairs(bags:keyset()) do
  140.         storages[player][bag] = T{}
  141.         for i = 1, inventory[bag].max do
  142.             data = inventory[bag][i]
  143.             if data.id ~= 0 then
  144.                 local id = data.id
  145.                 storages[player][bag][id] = (storages[player][bag][id] or 0) + data.count
  146.             end
  147.         end
  148.     end
  149.    
  150.     -- get offline storages from findAll if available. This code is also lifted almost verbatim from findAll.
  151.     if not player_only then
  152.         local findall_data = windower.get_dir(windower.addon_path..'..\\findall\\data')
  153.         if findall_data then
  154.             for _,f in pairs(findall_data) do
  155.                 if f:sub(-4) == '.lua' and f:sub(1,-5) ~= player then
  156.                     local success,result = pcall(dofile,windower.addon_path..'..\\findall\\data\\'..f)
  157.                     if success then
  158.                         storages[f:sub(1,-5)] = result
  159.                     else
  160.                         warning('Unable to retrieve updated item storage for %s.':format(f:sub(1,-5)))
  161.                     end
  162.                 end
  163.             end
  164.         end
  165.     end
  166.    
  167.     for character,inventory in pairs(storages) do
  168.         if not ignore_players:contains(character) then
  169.             for bag,items in pairs(inventory) do
  170.                 if bags:contains(bag) then
  171.                     for id, count in pairs(items) do
  172.                         id = tonumber(id)
  173.                         --if item is valid, stackable, not ignored, not rare, not Exclusive
  174.                         if res_items[id] and (not ignore_ids:contains(id))
  175.                             and (IsStackable(id) or not ignore_nostack)
  176.                             and (not IsRare(id) or not ignore_rare)
  177.                             and ((not IsExclusive(id) or CanSendPol(id)) or not ignore_ex)
  178.                         then
  179.                             --player str, bag str, id int, count int
  180.                             location = (player_only and bag or character..': '..bag)
  181.                             if not haystack[id] then haystack[id] = {} end
  182.                             haystack[id][location] = count
  183.                         end
  184.                     end
  185.                 end
  186.             end
  187.         end
  188.     end
  189.  
  190.     --print duplicates
  191.     for id,locations in pairs(haystack) do
  192.         if table.length(locations) > 1 then
  193.             results = results +1
  194.             log(res_items[id].name,'found in:')
  195.             for location,count in pairs(locations) do
  196.                 log('\t',location,count)
  197.             end
  198.         end
  199.     end
  200.    
  201.     if results >= 1 then
  202.         log(results,'found.')
  203.     else
  204.         log('No duplicates found. Congratulations!')
  205.     end
  206.    
  207.     preferences()
  208.    
  209. end
  210.  
  211. function handle_commands(...)
  212.     args = {...}
  213.     if args[1] == 'r' then -- shorthand for easy reloading
  214.         windower.send_command('lua r '.._addon.name)
  215.     else
  216.         work(...)
  217.     end
  218. end
  219.  
  220. windower.register_event('addon command',handle_commands)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement