Allows searching a players various storage containers via a slash command, for: 1.) Items based on search criteria 2.) Duplicate items 3.) Items that are storable on storage slips
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

434 lines
17 KiB

  1. --[[
  2. * The MIT License (MIT)
  3. *
  4. * Copyright (c) 2014 MalRD
  5. *
  6. * Permission is hereby granted, free of charge, to any person obtaining a copy
  7. * of this software and associated documentation files (the "Software"), to
  8. * deal in the Software without restriction, including without limitation the
  9. * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  10. * sell copies of the Software, and to permit persons to whom the Software is
  11. * furnished to do so, subject to the following conditions:
  12. *
  13. * The above copyright notice and this permission notice shall be included in
  14. * all copies or substantial portions of the Software.
  15. *
  16. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  21. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  22. * DEALINGS IN THE SOFTWARE.
  23. ]]--
  24. _addon.author = 'MalRD, zombie343';
  25. _addon.name = 'Find';
  26. _addon.version = '3.1.0';
  27. local slips = require('slips');
  28. local STORAGES = {
  29. [1] = { id=0, name='Inventory' },
  30. [2] = { id=1, name='Safe' },
  31. [3] = { id=2, name='Storage' },
  32. [4] = { id=3, name='Temporary' },
  33. [5] = { id=4, name='Locker' },
  34. [6] = { id=5, name='Satchel' },
  35. [7] = { id=6, name='Sack' },
  36. [8] = { id=7, name='Case' },
  37. [9] = { id=8, name='Wardrobe' },
  38. [10]= { id=9, name='Safe 2' },
  39. [11]= { id=10, name='Wardrobe 2' },
  40. [12]= { id=11, name='Wardrobe 3' },
  41. [13]= { id=12, name='Wardrobe 4' }
  42. };
  43. local default_config =
  44. {
  45. language = 0
  46. };
  47. local config = default_config;
  48. local inventory = AshitaCore:GetDataManager():GetInventory();
  49. local resources = AshitaCore:GetResourceManager();
  50. local MINSLIP = 1;
  51. local MAXSLIP = #slips.ids;
  52. -------------------------------------------------------------------------------
  53. --Returns the real ID and name for the given inventory storage index. --
  54. -------------------------------------------------------------------------------
  55. local function getStorage(storageIndex)
  56. return STORAGES[storageIndex].id, STORAGES[storageIndex].name;
  57. end
  58. -------------------------------------------------------------------------------
  59. ashita.register_event('load', function()
  60. end );
  61. -------------------------------------------------------------------------------
  62. ashita.register_event('unload', function()
  63. end );
  64. -------------------------------------------------------------------------------
  65. -- func : printf
  66. -- desc : Because printing without formatting is for the birds.
  67. -------------------------------------------------------------------------------
  68. function printf(s,...)
  69. print(s:format(...));
  70. end;
  71. -------------------------------------------------------------------------------
  72. -- func: find
  73. -- desc: Attempts to match the supplied cleanString to the supplied item.
  74. -- args: item -> the item being matched against.
  75. -- cleanString -> the cleaned string being searched for.
  76. -- useDescription -> true if the item description should be searched.
  77. -- returns: true if a match is found, otherwise false.
  78. -------------------------------------------------------------------------------
  79. local function find(item, cleanString, useDescription)
  80. if (item == nil) then return false end;
  81. if (cleanString == nil) then return false end;
  82. if (string.lower(item.Name[config.language]):find(cleanString)) then
  83. return true;
  84. elseif (item.LogNameSingular[config.language] ~= nil and string.lower(item.LogNameSingular[config.language]):find(cleanString)) then
  85. return true;
  86. elseif (item.LogNamePlural[config.language] ~= nil and string.lower(item.LogNamePlural[config.language]):find(cleanString)) then
  87. return true;
  88. elseif (useDescription and item.Description ~= nil and item.Description[config.language] ~= nil) then
  89. return (string.lower(item.Description[config.language]):find(cleanString));
  90. end
  91. return false;
  92. end
  93. -------------------------------------------------------------------------------
  94. -- func: search
  95. -- desc: Searches the player's inventory for an item that matches the supplied
  96. -- string.
  97. -- args: searchString -> the string that is being searched for.
  98. -- useDescription -> true if the item description should be searched.
  99. -------------------------------------------------------------------------------
  100. local function search(searchString, useDescription)
  101. if (searchString == nil) then return; end
  102. local cleanString = ParseAutoTranslate(searchString, false);
  103. if (cleanString == nil) then return; end
  104. cleanString = string.lower(cleanString);
  105. printf("\30\08Finding \"%s\"...", cleanString);
  106. local inventory = AshitaCore:GetDataManager():GetInventory();
  107. local resources = AshitaCore:GetResourceManager();
  108. local found = { };
  109. local result = { };
  110. local storageSlips = { };
  111. for k,v in ipairs(STORAGES) do
  112. local foundCount = 1;
  113. for j = 0, inventory:GetContainerMax(v.id), 1 do
  114. local itemEntry = inventory:GetItem(v.id, j);
  115. if (itemEntry.Id ~= 0 and itemEntry.Id ~= 65535) then
  116. local item = resources:GetItemById(itemEntry.Id);
  117. if (item ~= nil) then
  118. if (find(item, cleanString, useDescription)) then
  119. quantity = 1;
  120. if (itemEntry.Count ~= nil and item.StackSize > 1) then
  121. quantity = itemEntry.Count;
  122. end
  123. if result[k] == nil then
  124. result[k] = { };
  125. found[k] = { };
  126. end
  127. if found[k][itemEntry.Id] == nil then
  128. found[k][itemEntry.Id] = foundCount;
  129. result[k][foundCount] = { name = item.Name[config.language], count = 0 };
  130. foundCount = foundCount + 1;
  131. end
  132. result[k][found[k][itemEntry.Id]].count = result[k][found[k][itemEntry.Id]].count + quantity;
  133. end
  134. if find(item, 'storage slip ', false) then
  135. storageSlips[#storageSlips + 1] = {item, itemEntry};
  136. end
  137. end
  138. end
  139. end
  140. end
  141. local total = 0;
  142. for k,v in ipairs(STORAGES) do
  143. if result[k] ~= nil then
  144. storageID, storageName = getStorage(k);
  145. for _,item in ipairs(result[k]) do
  146. quantity = '';
  147. if item.count > 1 then
  148. quantity = string.format('[%d]', item.count)
  149. end
  150. printf('%s: %s %s', storageName, item.name, quantity);
  151. total = total + item.count;
  152. end
  153. end
  154. end
  155. for k,v in ipairs(storageSlips) do
  156. local slip = resources:GetItemById(v[1].ItemId);
  157. local slipItems = slips.items[v[1].ItemId];
  158. local extra = v[2].Extra;
  159. for i,slipItemID in ipairs(slipItems) do
  160. local slipItem = resources:GetItemById(slipItemID);
  161. if (find(slipItem, cleanString, useDescription)) then
  162. local byte = extra[math.floor((i - 1) / 8)];
  163. if byte < 0 then
  164. byte = byte + 256;
  165. end
  166. if (hasBit(byte, bit((i - 1) % 8 + 1))) then
  167. printf('%s: %s', slip.Name[config.language], slipItem.Name[config.language]);
  168. total = total + 1;
  169. end
  170. end
  171. end
  172. end
  173. printf('\30\08Found %d matching items.', total);
  174. end
  175. -------------------------------------------------------------------------------
  176. function bit(p)
  177. return 2 ^ (p - 1);
  178. end
  179. -------------------------------------------------------------------------------
  180. function hasBit(x, p)
  181. return x % (p + p) >= p;
  182. end
  183. local function findinslip(searchslip, item)
  184. if (item == nil) then
  185. return nil,nil
  186. end;
  187. if searchslip == 0 then
  188. for k,v in pairs(slips.items) do
  189. local slip = resources:GetItemById(k);
  190. for x = 1, #v do
  191. if item.Id == v[x] then
  192. local slipItem = resources:GetItemById(item.Id);
  193. --printf('%s: %s', slip.Name[config.language], slipItem.Name[config.language]);
  194. return slip.Name[config.language], slipItem.Name[config.language];
  195. end
  196. end
  197. end
  198. elseif searchslip >= MINSLIP and searchslip <= MAXSLIP then
  199. local slip = resources:GetItemById(slips.ids[searchslip]);
  200. for x = 1, #slips.items[slips.ids[searchslip]] do
  201. if item.Id == slips.items[slips.ids[searchslip]][x] then
  202. local slipItem = resources:GetItemById(item.Id);
  203. --printf('%s: %s', slip.Name[config.language], slipItem.Name[config.language]);
  204. return slip.Name[config.language], slipItem.Name[config.language];
  205. end
  206. end
  207. else
  208. printf('\30\08Please enter a valid storage slip between %i and %i, inclusive.', MINSLIP, MAXSLIP);
  209. end
  210. return nil,nil;
  211. end
  212. -------------------------------------------------------------------------------
  213. local function getFindArgs(cmd)
  214. if (not cmd:find('/find', 1, true)) then return nil; end
  215. local indexOf = cmd:find(' ', 1, true);
  216. if (cmd:find('/findslips', 1, true) or cmd:find('/finddupes', 1, true)) and indexOf == nil then
  217. cmdTable = {
  218. [1] = cmd
  219. };
  220. return cmdTable;
  221. end
  222. --Specific /findxyz command inputs that require second argument but don't have one specified
  223. if indexOf == nil then
  224. return nil;
  225. end
  226. --All other inputs that have /find and a space " ", return both words:
  227. cmdTable = {
  228. [1] = cmd:sub(1,indexOf-1),
  229. [2] = cmd:sub(indexOf+1),
  230. };
  231. return cmdTable;
  232. end
  233. -------------------------------------------------------------------------------
  234. -- func: printslips
  235. -- desc: Searches the player's inventory for any items that can be stored in
  236. -- storage slips.
  237. --
  238. -- args: searchslip -> Indicates search all slips (0) OR specifies slip to
  239. -- search for (1-27 index into slip_data:slip.items[])
  240. -------------------------------------------------------------------------------
  241. local function printslips(searchslip)
  242. local found = { };
  243. local foundSlip, foundItem;
  244. local result = { };
  245. local keyset = {};
  246. if searchslip == 0 then
  247. printf('\30\08Searching for any items that can be stored on any storage slips...');
  248. elseif searchslip >= MINSLIP and searchslip <= MAXSLIP then
  249. printf('\30\08Searching for any items that can be stored on Storage Slip #%i...', searchslip);
  250. else
  251. printf('\30\08Please enter a valid storage slip between %i and %i, inclusive.', MINSLIP, MAXSLIP);
  252. return;
  253. end
  254. for k,v in ipairs(STORAGES) do
  255. for j = 0, inventory:GetContainerMax(v.id), 1 do
  256. local itemEntry = inventory:GetItem(v.id, j);
  257. if (itemEntry.Id ~= 0 and itemEntry.Id ~= 65535) then
  258. local item = resources:GetItemById(itemEntry.Id);
  259. if (item ~= nil) then
  260. --printf('%s: %s', item.Name[config.language], itemEntry.Id)
  261. foundSlip,foundItem = findinslip(searchslip, itemEntry)
  262. if (foundSlip ~= nil) then
  263. --keyset[#keyset+1] = foundSlip
  264. --printf('%s: %s', foundSlip, foundItem)
  265. --result[foundSlip] = foundItem;
  266. --table.insert(found, foundItem)
  267. if result[foundSlip] == nil then
  268. result[foundSlip] = {}
  269. table.insert(result[foundSlip], foundItem);
  270. keyset[#keyset+1] = foundSlip
  271. --result[foundSlip][itemEntry] = {};
  272. else
  273. table.insert(result[foundSlip], foundItem);
  274. --result[foundSlip].itemEntry = item.Name
  275. end
  276. end
  277. end
  278. end
  279. end
  280. end
  281. --table.sort()
  282. local keysize = #keyset;
  283. local resultsize = 0;
  284. if keysize > 0 then
  285. table.sort(keyset)
  286. for slipIndex=1, keysize, 1 do
  287. for _,item in pairs(result[keyset[slipIndex]]) do
  288. printf('%s: %s', keyset[slipIndex],item);
  289. resultsize = resultsize + 1;
  290. end
  291. end
  292. if searchslip == 0 then
  293. printf("\30\08Found %i occurrence(s) of storable items in %i slips.", resultsize, keysize);
  294. else
  295. printf("\30\08Found %i occurrence(s) of storable items in storage slip #%i.", resultsize, searchslip);
  296. end
  297. else
  298. printf('\30\08No slip-storable items found.');
  299. end
  300. end
  301. -------------------------------------------------------------------------------
  302. -- func: printdupes
  303. -- desc: Searches the player's inventory for items that occupy more than one
  304. -- inventory slot. (Note, stacks or single items will both count as 1.
  305. -- Therefore, 2 stacks of 99 HP-Bayld will have a count of 2.)
  306. --
  307. -- args: none
  308. --
  309. -------------------------------------------------------------------------------
  310. local function printdupes()
  311. local result = { };
  312. local dupes = {};
  313. local resultsize = 0;
  314. local dupesize = 0;
  315. printf('\30\08Searching for duplicate items...');
  316. for k,v in ipairs(STORAGES) do
  317. for j = 0, inventory:GetContainerMax(v.id), 1 do
  318. local itemEntry = inventory:GetItem(v.id, j);
  319. if (itemEntry.Id ~= 0 and itemEntry.Id ~= 65535) then
  320. local item = resources:GetItemById(itemEntry.Id);
  321. if (item ~= nil) then
  322. if result[item.ItemId] == nil then
  323. result[item.ItemId] = 1;
  324. else
  325. cnt = result[item.ItemId] + 1
  326. result[item.ItemId] = cnt;
  327. if cnt == 2 then
  328. resultsize = resultsize + 1;
  329. end
  330. end
  331. --printf('(itemName)=%s: (itemID):%s, (result[itemID]):%s', item.Name[config.language], item.ItemId, result[item.ItemId]);
  332. end
  333. end
  334. end
  335. end
  336. dupesize = 0;
  337. if (resultsize > 0) then
  338. for id,cnt in pairs(result) do
  339. if ( tonumber(cnt) > 1 ) then
  340. --printf('I %s: %d', id, cnt);
  341. local dupeitem = resources:GetItemById(id);
  342. dupes[id] = { name = dupeitem.Name[config.language], count=cnt };
  343. dupesize = dupesize + 1;
  344. end
  345. end
  346. end
  347. if (dupesize > 0) then
  348. for k,v in pairs(dupes) do
  349. printf('%s: %d', v.name, v.count);
  350. end
  351. printf("\30\08Found %i occurrence(s) of duplicate items.", dupesize);
  352. else
  353. printf('\30\08No duplicate items found.');
  354. return true;
  355. end
  356. end
  357. -------------------------------------------------------------------------------
  358. ashita.register_event('command', function(cmd, nType)
  359. local args = getFindArgs(cmd);
  360. if (args == nil) then return false; end
  361. if (args[1]:lower() == '/find' and #args <= 2) then
  362. search(args[2]:lower(), false);
  363. return true;
  364. elseif (args[1]:lower() == '/findmore' and #args <= 2) then
  365. search(args[2]:lower(), true);
  366. return true;
  367. elseif (args[1]:lower() == '/finddupes' and #args <= 1) then
  368. printdupes();
  369. return true;
  370. elseif (args[1]:lower() == '/findslips' and #args <= 2) then
  371. if #args >= 2 then
  372. searchslip = tonumber(args[2]:lower());
  373. if not searchslip then
  374. printf('\30\08Please enter a valid storage slip between %i and %i, inclusive.', MINSLIP, MAXSLIP);
  375. return false;
  376. else
  377. printslips(searchslip);
  378. return true;
  379. end
  380. else
  381. printslips(0);
  382. end
  383. end;
  384. return false;
  385. end );