IdleScape - Lootify Quickfix TH Zones

IdleScape Statistics Tracker

当前为 2021-10-08 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name IdleScape - Lootify Quickfix TH Zones
  3. // @namespace D4IS
  4. // @version 1.4.11
  5. // @description IdleScape Statistics Tracker
  6. // @author D4M4G3X
  7. // @match *://*.idlescape.com/*
  8. // @grant none
  9. // @require https://code.jquery.com/jquery-3.4.1.min.js
  10. // @run-at document-start
  11. // ==/UserScript==
  12. if( typeof WebSocket.prototype._send == "undefined" ){
  13. WebSocket.prototype._send = WebSocket.prototype.send;
  14. }
  15. WebSocket.prototype.send = function(data){
  16. this._send(data);
  17. if( typeof window.IdlescapeSocket == "undefined" ){
  18. window.IdlescapeSocket = this;
  19. this.send = this._send;
  20. }
  21. }
  22.  
  23. let setupSocket = setInterval(()=> {
  24. if( typeof window.IdlescapeSocket !== "undefined" ){
  25. clearInterval(setupSocket);
  26. window.IdlescapeSocket.addEventListener('message', (e) => socketMessageHandler(e));
  27. console.log('Lootify: Attached to socket!');
  28. }
  29. }, 250);
  30.  
  31. function socketMessageHandler(e) {
  32. let lib = window.D4IS;
  33. let getHandler = setInterval(()=> {
  34. if (lib) {
  35. clearInterval(getHandler);
  36. lib.app.messageHandler(e);
  37. }
  38. }, 250);
  39. }
  40.  
  41. (function() {
  42. window.D4IS = {
  43. init: function(name, ver) {
  44. let lib = this;
  45. if (!lib.app.getAllowedApps().includes(name)) { return false; }
  46. !lib.apps ? lib.apps = [] : 1;
  47. lib.apps.push(name);
  48. lib[name] = {};
  49. lib[name].name = name;
  50. lib[name].ver = ver;
  51. lib[name].status = false;
  52. lib[name].setup = false;
  53. lib[name].ready = false;
  54.  
  55. lib.app.getStatus(name, function(name, d) {
  56. if (d === 'true') {
  57. window.D4IS[name].status = true;
  58. } else {
  59. alert(window.D4IS.general.ucfirst(name) + ' has been temporarily disabled! Please try again later.');
  60. }
  61. });
  62.  
  63. lib.app.getData(function() {
  64. if (lib[name].status) {
  65. if (lib.app.data) {
  66. lib.app.runIntervals();
  67. lib[name].ready = true;
  68. }
  69. }
  70. });
  71.  
  72. if(!lib.game.marketPrices) {
  73. lib.game.getMarketPrices();
  74. }
  75.  
  76. lib[name].render = setInterval(() => {
  77. if( $('.status-action').length) {
  78. clearInterval(lib[name].render);
  79. if(!lib.game.getMenuItem('Mods', 'category')) {
  80. lib.game.setMenuItem({
  81. 'text': 'Mods',
  82. 'clone': 'Gathering',
  83. 'class': 'd4is-header',
  84. 'before': lib.game.getMenuItem('Misc', 'category'),
  85. }, 'category');
  86. }
  87. if (!lib.general.getStorage('Terms') || lib.general.getStorage('Terms') === 'false') {
  88.  
  89. let msg = "";
  90. msg += "By using D4IS mods you agree to allow us saving your username and ID in our database for statistical purposes.";
  91. msg += "These will be used for setting up personal statistics and highscores. (coming soon...) ";
  92. msg += "If you don't want your username to be on public highscore lists then you can opt-out in the settings";
  93. lib.game.dialog({
  94. 'title': 'Terms of service',
  95. 'text': msg,
  96. 'type': 'confirm',
  97. 'img': true,
  98. 'cbconfirm': function() {
  99. lib.general.setStorage('Terms', 'true');
  100. },
  101. });
  102. }
  103. if(!lib.game.getMenuItem(lib.general.ucfirst(name) +' Settings')) {
  104. lib.game.setMenuItem({
  105. 'icon': lib.app.APIUrl+name+'/img/'+name+'_logo.png',
  106. 'text': lib.general.ucfirst(name) +' Settings',
  107. 'class': 'd4is-menu-button',
  108. 'after': lib.game.getMenuItem('Mods', 'category'),
  109. 'click': function() {
  110. $('.'+name+'-settings').toggle();
  111. },
  112. });
  113. $('<div>', {
  114. 'class': name+'-settings'
  115. }).hide().insertAfter(lib.game.getMenuItem(lib.general.ucfirst(name) +' Settings'));
  116. }
  117.  
  118. if (!$('.d4is-logo').length) {
  119. $('.d4is-header').prepend($('<img>', {
  120. 'class': 'd4is-logo'
  121. }).css({
  122. 'width': '52px',
  123. 'margin-bottom': '3px'
  124. }).attr('src', lib.app.APIUrl+'/assets/img/D4IS_logo.png'));
  125. }
  126. }
  127.  
  128. }, 250);
  129.  
  130. return lib;
  131. },
  132.  
  133. /********************************
  134. * APP LIBRARY
  135. *
  136. *********************************/
  137. app: {
  138. APIUrl: 'https://digimol.net/idlescape/',
  139.  
  140. debug: {
  141. socket: false,
  142. interval: false,
  143. },
  144.  
  145. runIntervals: function() {
  146. let lib = window.D4IS;
  147. !lib.app.interval ? lib.app.interval = [] : 1;
  148. if(!lib.app.interval.includes('updater')) {
  149. lib.app.interval.push('updater');
  150. lib.app.updater = setInterval(() => {
  151. lib.app.getData();
  152. $.each(lib.apps, function(k, name) {
  153. if (lib[name]) {
  154. lib.app.checkUpdates(name, lib[name].updateLocation);
  155. }
  156. });
  157. }, 5 * 60 * 1000);
  158. }
  159. if(!lib.app.interval.includes('quick')) {
  160. lib.app.interval.push('quick');
  161. lib.app.quick = setInterval(() => {
  162. if( $('.status-action').length) {
  163. lib.user.updateStatus();
  164. }
  165. }, 250);
  166. }
  167. if(!lib.app.interval.includes('debug') && lib.app.debug.interval) {
  168. lib.app.interval.push('debug');
  169. lib.app.debugInterval = setInterval(() => {
  170. if( $('.status-action').length) {
  171.  
  172. }
  173. }, 3000);
  174. }
  175. },
  176.  
  177. checkUpdates: function(name, $place) {
  178. let lib = window.D4IS;
  179. let app = lib[name];
  180. this.getNewest(name, function(d) {
  181. app.newver = d ? d : 0;
  182. });
  183. if (app.newver && app.ver < app.newver) {
  184. if (!$('.'+name+'-update').length) {
  185. if(!~app.ver.toLowerCase().indexOf('error')) {
  186. $('<div>', {
  187. 'class': 'd4is-update '+name+'-update'
  188. }).text(lib.general.ucfirst(name)+' update: v'+app.newver+' available!').insertAfter($place);
  189. }
  190. }
  191. }
  192. },
  193. setUpdateLocation: function(app, $location) {
  194. let lib = window.D4IS;
  195. if (lib[app]) {
  196. lib[app].updateLocation = $location;
  197. }
  198. },
  199. setCommand: function(cmd, cb) {
  200. let lib = window.D4IS;
  201. !lib.app.commands ? lib.app.commands = {} : 1;
  202. lib.app.commands[cmd] = cb;
  203. },
  204. updateUser(app) {
  205. let lib = window.D4IS;
  206. lib.app.getRequest(app, lib.app.APIUrl+'/assets/api.php?a=updateuser&id='+lib.user.id+'&name='+lib.user.name);
  207. },
  208. getAllowedApps: function() {
  209. return ['lootify', 'chat.0'];
  210. },
  211. getNewest: function(app, cb) {
  212. let lib = window.D4IS;
  213. $.get(lib.app.APIUrl+'/assets/const.php?type=ver&app='+app).done(function(d) {
  214. cb(d);
  215. });
  216. },
  217. getStatus: function(app, cb) {
  218. let lib = window.D4IS;
  219. $.get(lib.app.APIUrl+'/assets/const.php?type=status&app='+app).done(function(d) {
  220. cb(app, d);
  221. });
  222. },
  223. getRequest: function(app, url, cb = function(){}) {
  224. let lib = window.D4IS;
  225. let ver = lib[app] ? lib[app].ver : '0';
  226. $.get(lib.app.APIUrl+'/assets/api.php?a=getauth').done(function(d1) {
  227. $.get(url+'&auth='+d1+'&app='+app+'&ver='+ver).done(function(d2) {
  228. cb(d2);
  229. });
  230. });
  231. },
  232. getData(cb = function(){}) {
  233. let lib = window.D4IS;
  234. lib.app.getRequest('', lib.app.APIUrl+'/assets/data.php?', function(d) {
  235. lib.app.data = d ? JSON.parse(d) : {};
  236. cb();
  237. });
  238. },
  239. messageHandler: function(e) {
  240. let lib = window.D4IS;
  241. let msg = e.data;
  242. msg = (msg.match(/^[0-9]+(\[.+)$/) || [])[1];
  243. if(msg && !~msg.indexOf('chat') && !~msg.indexOf('Essence') && !~msg.indexOf('"send message"')) {
  244. (msg && lib.app.debug.socket) ? console.log(JSON.parse(msg)) : 1;
  245. }
  246. if(msg && ~msg.indexOf('"update player"')) {
  247. let d = JSON.parse(msg.split('Socket'))[1];
  248. if(~msg.indexOf('activeEnchantments')) {
  249. if(~msg.indexOf('"portion":"all"')) {
  250. lib.user.enchantments = d.value.activeEnchantments;
  251. lib.user.name = d.value.username;
  252. lib.user.id = d.value.id;
  253. lib.user.stockpile = d.value.stockpile;
  254. if(lib.apps.includes('lootify')) {
  255. lib['lootify'].initExp(d);
  256. }
  257.  
  258. } else {
  259. lib.user.enchantments = d.value[0];
  260. }
  261. }
  262. }
  263. // GET MINING AND FORAGING ITEMS
  264. if (lib.user.isActiveSkill()) {
  265. if (msg && ~msg.indexOf('"update inventory"') && !~msg.indexOf('Essence')) {
  266. let d = JSON.parse(msg)[1];
  267. let allowed = true;
  268. let stockpileID = undefined;
  269. $.each(lib.user.stockpile, function(i, item) {
  270. if (item.name === d.item.name) {
  271. if (d.item.stackSize < item.stackSize) {
  272. allowed = false;
  273. }
  274. stockpileID = i;
  275. }
  276. });
  277. if (lib.user.lastAction === 'vault') {
  278. lib.user.lastAction = '';
  279. allowed = false;
  280. }
  281. if(lib.apps.includes('lootify') && allowed) {
  282. lib['lootify'].addLogMob(d);
  283. }
  284. if (lib.user.stockpile) {
  285. if (stockpileID) {
  286. lib.user.stockpile[stockpileID].stackSize = d.item.stackSize;
  287. } else {
  288. lib.user.stockpile.push(d.item);
  289. }
  290. }
  291. }
  292. }
  293. // GET COMBAT EXP
  294. if(msg && ~msg.indexOf('"update player"') && ~msg.indexOf('"skills"')) {
  295. if(lib.apps.includes('lootify')) {
  296. lib['lootify'].updateExp(msg);
  297. }
  298. }
  299. // UPDATE STATUS
  300. if(msg && ~msg.indexOf('"start animation"')) {
  301. let d = JSON.parse(msg)[1];
  302. lib.user.prevStatus = !lib.user.isStatus(d.action) ? lib.user.getStatus() : lib.user.prevStatus;
  303. lib.user.status = d.action;
  304. lib.user.location = d.location;
  305. }
  306. // CHECK IF PLAYER WENT TO MARKET
  307. if(msg && ~msg.indexOf('"get player marketplace items"')) {
  308. if(lib.apps.includes('lootify')) {
  309. lib['lootify'].disablePaster();
  310. }
  311. }
  312. // CHECK IF PLAYER USED THE FAULT
  313. if(msg && ~msg.indexOf('"update inventory"') && ~msg.indexOf('vault')) {
  314. lib.user.lastAction = 'vault';
  315. }
  316. // GET COMBAT EXP
  317. if(msg && ~msg.indexOf('"update player"') && ~msg.indexOf('"crafting"')) {
  318. lib.user.lastAction = 'crafting';
  319. }
  320. if(msg && ~msg.indexOf('"new monster"')) {
  321. let d = JSON.parse(msg)[1];
  322. lib.user.currentMob = d.name;
  323. }
  324. if(msg && ~msg.indexOf('"clear monster"')) {
  325. let d = JSON.parse(msg)[1];
  326. lib.user.currentMob = '';
  327. }
  328. }
  329. },
  330.  
  331. /********************************
  332. * GAME LIBRARY
  333. *
  334. *********************************/
  335. game: {
  336. isUniversalItem: function(txt) {
  337. let lib = window.D4IS;
  338. return lib.app.data.item ? lib.app.data.item.universal.includes(txt) : false;
  339. },
  340. isRareItem: function(txt) {
  341. let lib = window.D4IS;
  342. return lib.app.data.item ? lib.app.data.item.rare.includes(txt) : false;
  343. },
  344. isEventItem: function(txt) {
  345. let lib = window.D4IS;
  346. return lib.app.data.item ? lib.app.data.item.event.includes(txt) : false;
  347. },
  348. isEventMob: function(txt) {
  349. let lib = window.D4IS;
  350. return lib.app.data.mob ? lib.app.data.mob.event.includes(txt) : false;
  351. },
  352. isAllowedItem: function(skill, item) {
  353. let lib = window.D4IS;
  354. if (lib.user.isStatus('cooking')) { return false; }
  355. let allowed = false;
  356. if (lib.app.data.skill) {
  357. if (lib.app.data.skill[skill].allowed.includes(item)) {
  358. allowed = true;
  359. }
  360. }
  361. return allowed;
  362. },
  363. getSkillIcon: function(skill) {
  364. let icon = '';
  365. switch(skill) {
  366. case 'mining':
  367. icon = '/images/mining/iron_pickaxe.png';
  368. break;
  369. case 'foraging':
  370. icon = '/images/foraging/foraging_icon.png';
  371. break;
  372. case 'fishing':
  373. icon = '/images/fishing/fishing_logo.png';
  374. break;
  375. case 'smithing':
  376. icon = '/images/smithing/smithing_icon.png';
  377. break;
  378. case 'crafting':
  379. icon = '/images/ui/crafting_icon.png';
  380. break;
  381. case 'cooking':
  382. icon = '/images/cooking/cooking_icon.png';
  383. break;
  384. case 'constitution':
  385. icon = '/images/combat/constitution_icon.png';
  386. break;
  387. case 'attack':
  388. icon = '/images/combat/attack_icon.png';
  389. break;
  390. case 'strength':
  391. icon = '/images/combat/strength_icon.png';
  392. break;
  393. case 'defense':
  394. icon = '/images/combat/defense_icon.png';
  395. break;
  396. case 'runecrafting':
  397. icon = '/images/runecrafting/RuneCraftingIcon.png';
  398. break;
  399. case 'enchanting':
  400. icon = '/images/enchanting/enchanting_logo.png';
  401. break;
  402. case 'farming':
  403. icon = '/images/farming/farming_icon.png';
  404. break;
  405. }
  406. return icon;
  407. },
  408. getLeagueIcon: function(league) {
  409. let icon = '';
  410. switch(league) {
  411. case 'default':
  412. icon = '/images/leagues/default_league_icon.png';
  413. break;
  414. case 'ironman':
  415. icon = '/images/leagues/ironman_league_icon_v5.png';
  416. break;
  417. }
  418. return icon;
  419. },
  420. getMobIcon: function(name) {
  421. let lib = window.D4IS;
  422. return lib.app.data.icon ? lib.app.data.icon.mob[name] : '';
  423. },
  424. getZoneMobs: function(name) {
  425. let zone = {
  426. "Farm": [
  427. 'Cow',
  428. 'Chicken',
  429. 'Small Rat',
  430. 'Farm Goblin'
  431. ],
  432. "Caves": [
  433. 'Imp',
  434. 'Greater Imp',
  435. 'Cave Goblin'
  436. ],
  437. "City": [
  438. 'Guard',
  439. 'Black Knight'
  440. ],
  441. "Lava Maze": [
  442. 'Deadly Red Spider',
  443. 'Lesser Demon'
  444. ],
  445. "Corrupted Lands": [
  446. "Bone Giant",
  447. "Infected Naga",
  448. "Corrupted Tree"
  449. ],
  450. "Valley of Giants": [
  451. 'Fire Giant',
  452. 'Moss Giant',
  453. 'Ice Giant'
  454. ]
  455. };
  456. return zone[name];
  457. },
  458. getLocation: function(id = undefined) {
  459. let loc = {
  460. 10: {
  461. name: 'Clay Pit',
  462. icon: 'https://idlescape.com/images/mining/Place-ClayPits.png'
  463. },
  464. 11: {
  465. name: 'City Outskirts',
  466. icon: 'https://idlescape.com/images/mining/Place-CityOutskirts.png'
  467. },
  468. 14: {
  469. name: 'Village',
  470. icon: 'https://idlescape.com/images/mining/Place-Village.png'
  471. },
  472. 15: {
  473. name: 'Desert',
  474. icon: 'https://idlescape.com/images/mining/Place-Desert.png'
  475. },
  476. 17: {
  477. name: 'Underground',
  478. icon: 'https://idlescape.com/images/mining/Place-Underground.png'
  479. },
  480. 19: {
  481. name: 'Hidden Mine',
  482. icon: 'https://idlescape.com/images/mining/Place-HiddenLocation.png'
  483. },
  484. 13: {
  485. name: 'Volcano',
  486. icon: 'https://idlescape.com/images/mining/Place-Volcano.png'
  487. },
  488. 20: {
  489. name: 'Deep Pit',
  490. icon: 'https://idlescape.com/images/mining/Place-DeepMine.png'
  491. },
  492. 12: {
  493. name: 'Grasslands',
  494. icon: 'https://idlescape.com/images/foraging/grasslands.png'
  495. },
  496. 21: {
  497. name: 'Verdant Valley',
  498. icon: 'https://idlescape.com/images/foraging/verdant_valley.png'
  499. },
  500. 22: {
  501. name: 'Fungal Grotto',
  502. icon: 'https://idlescape.com/images/foraging/fungal_grotto.png'
  503. },
  504. 16: {
  505. name: 'The Tangle',
  506. icon: 'https://idlescape.com/images/foraging/the_tangle.png'
  507. },
  508. 24: {
  509. name: 'Misty Marsh',
  510. icon: 'https://idlescape.com/images/foraging/misty_marsh.png'
  511. },
  512. 18: {
  513. name: 'Frozen Tundra',
  514. icon: 'https://idlescape.com/images/foraging/frozen_tundra.png'
  515. },
  516. 25: {
  517. name: 'Haunted Woods',
  518. icon: 'https://idlescape.com/images/foraging/haunted_woods.png'
  519. },
  520. 26: {
  521. name: 'Living Forest',
  522. icon: 'https://idlescape.com/images/foraging/living_forest.png'
  523. },
  524. 50: {
  525. name: 'Shallow Pond',
  526. icon: 'https://idlescape.com/images/fishing/net_fishing.jpg'
  527. },
  528. 51: {
  529. name: 'Lazy River',
  530. icon: 'https://idlescape.com/images/fishing/fly_fishing.jpg'
  531. },
  532. 52: {
  533. name: 'Still Lake',
  534. icon: 'https://idlescape.com/images/fishing/cage_fishing.jpg'
  535. },
  536. 53: {
  537. name: 'Open Ocean',
  538. icon: 'https://idlescape.com/images/fishing/harpoon_fishing.jpg'
  539. },
  540. };
  541. return id ? loc[id] : loc;
  542. },
  543. getLocationID: function(name) {
  544. let lib = window.D4IS;
  545. let id = -1;
  546. let loc = lib.game.getLocation();
  547. if(loc) {
  548. $.each(loc, function(k, loc) {
  549. if (name === loc.name) {
  550. id = k;
  551. }
  552. });
  553. }
  554. return id;
  555. },
  556. getLocationName: function(id) {
  557. let lib = window.D4IS;
  558. let loc = lib.game.getLocation(id);
  559. return loc ? loc.name : undefined;
  560. },
  561. getLocationIcon: function(id) {
  562. let lib = window.D4IS;
  563. let loc = lib.game.getLocation(id);
  564. return loc ? loc.icon : undefined;
  565. },
  566. getMarketPrices: function() {
  567. let lib = window.D4IS;
  568. let get = function() {
  569. $.getJSON( '/api/market/manifest', function( d ) {
  570. if(d) {
  571. $.each(d.manifest, function(k, v) {
  572. !lib.game.marketPrices ? lib.game.marketPrices = {} : 1;
  573. lib.game.marketPrices[v.name] = v.minPrice;
  574. });
  575. }
  576. });
  577. };
  578. get();
  579.  
  580. setInterval(()=> {
  581. get();
  582. }, 5 * 60 * 1000);
  583. },
  584. getLevel: function(xp) {
  585. let lib = window.D4IS;
  586. let level = 0;
  587. for(i = 1; i <= 200; i++) {
  588. if(xp >= lib.game.getExperience(i) && xp < lib.game.getExperience(i+1)) {
  589. level = i;
  590. break;
  591. }
  592. }
  593. return parseInt(level);
  594. },
  595. getExperience: function(level) {
  596. let lib = window.D4IS;
  597. level -= 1;
  598. let xp = 0;
  599. let output = 0;
  600. let i;
  601. for(i = 1; i <= level; i++) {
  602. xp += Math.floor((i + (300 * (Math.pow(2, (i/7.0))))));
  603. }
  604. return Math.floor((xp/4));
  605. },
  606. setMenuItem: function(args, type = 'item') {
  607. let $item, $img, e;
  608. !args['text'] ? args['text'] = 'Menu Item' : 1;
  609. !args['clone'] ? args['clone'] = 'Shop' : 1;
  610.  
  611. if(!this.getMenuItem(args['clone'], type)) { return $('<div>'); }
  612. $item = this.getMenuItem(args['clone'], type).clone();
  613.  
  614. if(args['class']) {
  615. $item.addClass(args['class']);
  616. }
  617.  
  618. if(args['css']) {
  619. $item.css(args['css']);
  620. }
  621.  
  622. $img = $item.find('img').clone();
  623. $item.unbind().empty();
  624.  
  625. if(type === 'item') {
  626. $item.append($('<span/>').text(args['text']));
  627. } else if (type === 'category') {
  628. $item.append($('<b/>').text(args['text']));
  629. }
  630.  
  631. if (args['icon']) {
  632. $img.attr('src', args['icon']).prependTo($item);
  633. }
  634.  
  635. args['before'] ? args['before'].before($item) : 1;
  636. args['after'] ? args['after'].after($item) : 1;
  637.  
  638. if (typeof args['click'] === 'function') {
  639. $item.click(args['click']);
  640. }
  641.  
  642. return $item;
  643. },
  644. getMenuItem: function(label, type = 'item') {
  645. let $item;
  646. $.each($('.drawer-' + type), function() {
  647. if (~$(this).text().indexOf(label)) {
  648. $item = $(this);
  649. }
  650. });
  651. return $item;
  652. },
  653. editMenuItem: function(label) {
  654. this.getMenuItem(label).text('Hidden').hide();
  655. },
  656. removeMenuItem: function(label) {
  657. this.getMenuItem(label).remove();
  658. },
  659. addSetting: function(args) {
  660. let lib = window.D4IS;
  661. if(!args['app']) { return false; }
  662. if($('.'+args['app']+'-setting-'+args['name'].toLowerCase()).length) { return false; }
  663. args['text'] = args['text'] ? args['text'] : 'New setting';
  664. args['name'] = args['name'] ? args['name'] : args['text'].replace(' ', '-').toLowerCase();
  665. args['type'] = args['type'] ? args['type'] : 'text';
  666. args['default'] = args['default'] ? args['default'] : 0;
  667. let $setting = $('<div/>', {
  668. 'class': 'd4is-setting '+ args['app']+'-setting-'+args['name'].toLowerCase()
  669. }).append($('<span/>')).append($('<input/>').addClass('setting-'+args['type']));
  670. $setting.find('span').text(args['text']);
  671. $setting.find('input').addClass(args['name']).attr('type', args['type']);
  672. if (args['min']) {
  673. $setting.find('input').attr('min', args['min']);
  674. }
  675. if (args['max']) {
  676. $setting.find('input').attr('max', args['max']);
  677. }
  678. let val = lib.general.getStorage(args['name']);
  679. if (args['type'] === 'checkbox') {
  680. val = val ? val : args['default'];
  681. val = val == 'true' ? true : false;
  682. $setting.find('input').prop('checked', val);
  683. }
  684. if (val) {
  685. $setting.find('input').val(val)
  686. } else {
  687. $setting.find('input').val(args['default']);
  688. }
  689. if (args['change']) {
  690. $setting.find('input').change(args['change']);
  691. }
  692. $setting.appendTo($('.'+args['app']+'-settings'));
  693. return $setting;
  694. },
  695. tooltip: function($obj, msg) {
  696. let $tooltip;
  697. let pos = $obj.position();
  698. $obj.hover(function() {
  699. $tooltip = $('<div>', {
  700. 'class': 'ltf-tooltip'
  701. }).css({
  702. 'left': pos.left + 50,
  703. 'top': pos.top
  704. }).html('<span>'+msg+'</span>').appendTo($obj);
  705. }, function() {
  706. $tooltip.remove();
  707. });
  708. },
  709. dialog: function(args) {
  710. let lib = window.D4IS;
  711. args['class'] = args['class'] ? args['class'] : '';
  712. $('.d4is-dialog').remove();
  713. let $root = $('<div>', {
  714. 'class': 'd4is-dialog '+args['class']
  715. }).appendTo($('#root'));
  716.  
  717. if(args['class']) {
  718. $root.addClass(args['class']);
  719. }
  720.  
  721. let $backdrop = $('<div>', {
  722. 'class': 'd4is-dialog-backdrop'
  723. }).attr({
  724. 'aria-hidden': 'true'
  725. }).appendTo($root);
  726.  
  727. let $container = $('<div>', {
  728. 'class': 'd4is-dialog-container d4is-dialog-scrollPaper',
  729. }).css({
  730. 'opacity': '1',
  731. 'transition': 'opacity 225ms cubic-bezier(0.4, 0, 0.2, 1) 0ms'
  732. }).appendTo($root);
  733.  
  734. let $paper = $('<div>', {
  735. 'class': 'd4is-dialog-MuiPaper-root d4is-dialog-MuiDialog-paper d4is-dialog-paperScrollPaper d4is-dialog-paperWidthSm'
  736. }).css({
  737. 'z-index': '200'
  738. }).appendTo($container);
  739.  
  740. if (args['img']) {
  741. let $image = $('<img>', {
  742. 'class': 'ltf-dialog-image'
  743. }).attr({
  744. 'src': lib.app.APIUrl+'/lootify/img/lootify_logo.png'
  745. }).appendTo($paper);
  746. }
  747.  
  748. let $title = $('<div>', {
  749. 'class': 'd4is-dialog-Title-root'
  750. }).appendTo($paper);
  751.  
  752. $('<h2>', {
  753. 'class': 'd4is-dialogTypography-root d4is-dialogTypography-h6'
  754. }).html(args['title']).appendTo($title);
  755.  
  756. $('<p>', {
  757. 'class': 'd4is-dialogTypography-root'
  758. }).html(args['text']).appendTo($paper);
  759.  
  760. let $actions = $('<div>', {
  761. 'class': 'd4is-dialogDialogActions-root'
  762. }).appendTo($paper);
  763.  
  764. if(!args['cbyes']) {
  765. args['cbyes'] = function(){};
  766. }
  767. if(!args['cbno']) {
  768. args['cbno'] = function(){};
  769. }
  770. if(!args['cbconfirm']) {
  771. args['cbconfirm'] = function(){};
  772. }
  773.  
  774. let $button = [];
  775. args['type'] = args['type'] ? args['type'] : 'yesno';
  776. if (args['type'] === 'yesno') {
  777. $backdrop.click(function() {
  778. $root.remove();
  779. });
  780. $button[0] = $('<div>', {
  781. 'class': 'item-dialogue-button idlescape-button idlescape-button-green'
  782. }).attr({
  783. 'variant': 'contained',
  784. 'color': 'secondary'
  785. }).text('Yes').appendTo($actions);
  786. $button[0].click(function() {
  787. $root.remove();
  788. args['cbyes']();
  789. });
  790. $button[1] = $('<div>', {
  791. 'class': 'item-dialogue-button idlescape-button idlescape-button-red'
  792. }).attr({
  793. 'variant': 'contained',
  794. 'color': 'secondary'
  795. }).text('No').appendTo($actions);
  796. $button[1].click(function() {
  797. $root.remove();
  798. args['cbno']();
  799. });
  800. } else if(args['type'] === 'confirm') {
  801. $button[0] = $('<div>', {
  802. 'class': 'item-dialogue-button idlescape-button idlescape-button-blue'
  803. }).attr({
  804. 'variant': 'contained',
  805. 'color': 'primary'
  806. }).text('Accept').appendTo($actions);
  807. $button[0].click(function() {
  808. $root.remove();
  809. args['cbconfirm']();
  810. });
  811. } else if(args['type'] === 'close') {
  812. $button[0] = $('<div>', {
  813. 'class': 'item-dialogue-button idlescape-button idlescape-button-red'
  814. }).attr({
  815. 'variant': 'contained',
  816. 'color': 'primary'
  817. }).text('Close').appendTo($actions);
  818. $button[0].click(function() {
  819. $root.remove();
  820. });
  821. }
  822. },
  823. chat: function(args) {
  824. let lib = window.D4IS;
  825. if(lib.general.getStorage('ChatMessage') === 'false') {
  826. return false;
  827. }
  828. let e = setInterval(()=> {
  829. let $chat = $('.chat-message-container > .chat-message-list > div');
  830. if( $chat.length ) {
  831. clearInterval(e);
  832. $chat.each(function() {
  833. if (!$(this).find('.activity-log').length) {
  834. let $msg = $('<div/>', {
  835. 'class': 'chat-message msg-lootify'
  836. }).clone().appendTo($(this));
  837. if (args['date'] !== false) {
  838. let $date = $('<span/>', {
  839. 'class': 'message-time-stamp',
  840. }).text('['+lib.general.getDate(new Date)+']').appendTo($msg);
  841. }
  842. args['color'] = args['color'] ? args['color'] : '#00A0FD';
  843. args['glow'] = args['glow'] ? '0 0 3px '+args['glow'] : 'none';
  844. let $txt = $('<span/>', {
  845. 'class':'chat-message-system'
  846. }).css({
  847. 'font-size': '14px',
  848. 'color': args['color'],
  849. 'text-shadow': args['glow'],
  850. }).text(args['msg']).appendTo($msg);
  851.  
  852. args['ttl'] = args['ttl'] ? args['ttl'] : 5;
  853. setTimeout(()=> {
  854. $msg.remove();
  855. }, args['ttl'] * 60 * 1000);
  856. }
  857. });
  858. }
  859. }, 1000);
  860. },
  861. },
  862.  
  863. /********************************
  864. * USER LIBRARY
  865. *
  866. *********************************/
  867. user: {
  868. getName: function() {
  869. return $('.navbar1-box').text().split(' ')[1];
  870. },
  871. getStatus: function() {
  872. let lib = window.D4IS;
  873. return lib.user.status;
  874. },
  875. getStockpile: function(item = undefined) {
  876. let lib = window.D4IS;
  877. let stack = lib.user.stockpile;
  878. if (item) {
  879. $.each(lib.user.stockpile, function(i, v) {
  880. if (v.name.toLowerCase() === item.toLowerCase()) {
  881. stack = v;
  882. return false;
  883. }
  884. stack = undefined;
  885. });
  886. }
  887. return stack;
  888. },
  889. getEnchantment: function(id) {
  890. let lib = window.D4IS;
  891. let strength = 0;
  892. if(lib.user.enchantments) {
  893. $.each(lib.user.enchantments, function(k, v) {
  894. if(v.enchantmentID == id) {
  895. strength = v.enchantmentStrength;
  896. }
  897. });
  898. }
  899. return strength;
  900. },
  901. getTimeToLevel: function(level, currentExp, gainedExp, time) {
  902. let lib = window.D4IS;
  903. let currentLevel = lib.game.getLevel(currentExp);
  904. let nextExp = lib.game.getExperience(level);
  905. let diffExp = nextExp - currentExp;
  906. let ePerSec = (gainedExp/time);
  907. return lib.general.getTimeString(Math.floor((diffExp/ePerSec)*1000), true);
  908. },
  909. isIron: function() {
  910. return $('.header-league-icon').attr('src') === '/images/leagues/ironman_league_icon_v5.png';
  911. },
  912. isActiveSkill: function() {
  913. let lib = window.D4IS;
  914. let isActive = false;
  915. if (lib.app.data) {
  916. $.each(lib.app.data.active.skills, function(k, skill) {
  917. if(lib.user.isStatus(skill)) {
  918. isActive = true;
  919. return false;
  920. }
  921. });
  922. }
  923. return isActive;
  924. },
  925. isFighting: function() {
  926. let lib = window.D4IS;
  927. return lib.user.isStatus('fighting');
  928. },
  929. isStatus: function(txt) {
  930. let lib = window.D4IS;
  931. !lib.user.status ? lib.user.status = 'idling' : 1;
  932. return lib.user.status === txt;
  933. },
  934. isPrevStatus: function(txt) {
  935. let lib = window.D4IS;
  936. return lib.user.prevStatus === txt;
  937. },
  938. updateStatus: function() {
  939. let lib = window.D4IS;
  940. if ($('.status-action').length) {
  941. let list = ['Fighting', 'Idling', 'Mining', 'Foraging', 'Fishing', 'Cooking', 'Smithing'];
  942. $.each(list, function(k, v) {
  943. if(~$(document).attr('title').indexOf(v)) {
  944. lib.user.prevStatus = !lib.user.isStatus(v.toLowerCase()) ? lib.user.status : lib.user.prevStatus;
  945. lib.user.status = v.toLowerCase();
  946. }
  947. });
  948. }
  949. },
  950. },
  951.  
  952. /********************************
  953. * GENERAL LIBRARY
  954. *
  955. *********************************/
  956. general: {
  957. Timer: function(app, name) {
  958. let lib = window.D4IS;
  959. !lib[app].timers ? lib[app].timers = {} : 1;
  960. !lib[app].timers[name] ? lib[app].timers[name] = this : 1;
  961. let that = this;
  962.  
  963. that.running = false;
  964. that.status = name;
  965. that.starts = [];
  966. that.stops = [];
  967.  
  968. that.time = 0;
  969. that.interval = setInterval(()=> {
  970. if (that.running) {
  971. that.time = lib.general.getTime(that.starts, that.stops);
  972. if($('.time-stats').length) {
  973. $('.time-stats').find('span').text('Elapsed: ' + lib.general.getTimeString(that.time));
  974. }
  975. }
  976. if (lib.user.getStatus() === that.status) {
  977. !that.running ? that.start() : 1;
  978. } else {
  979. that.running ? that.stop() : 1;
  980. }
  981. }, 250);
  982.  
  983. that.start = function() {
  984. if (that.status == 'idling') {
  985. that.starts = [];
  986. that.stops = [];
  987. }
  988. that.running = true;
  989. that.starts.unshift(new Date());
  990. };
  991. that.stop = function() {
  992. that.running = false;
  993. that.stops.unshift(new Date());
  994. };
  995. that.reset = function() {
  996. clearInterval(that.interval);
  997. delete lib[app].timers[name];
  998. };
  999. },
  1000. getTimerTime: function(app, status) {
  1001. let lib = window.D4IS;
  1002. if (lib[app].timers) {
  1003. return lib[app].timers[lib.user.getStatus()] ? lib[app].timers[lib.user.getStatus()].time/1000 : 0;
  1004. }
  1005. },
  1006. setStorage: function(name, value) {
  1007. let lib = window.D4IS;
  1008. window.localStorage.setItem(lib.user.getName() + '-' + name, value);
  1009. },
  1010. getStorage: function(name) {
  1011. let lib = window.D4IS;
  1012. return window.localStorage.getItem(lib.user.getName() + '-' + name);
  1013. },
  1014. getTime: function(starts, stops) {
  1015. let count = 0;
  1016. $.each(starts, function(k, start) {
  1017. let stop = !stops[k] ? new Date() : stops[k];
  1018. count += stop - start;
  1019. });
  1020. return Math.round(count);
  1021. },
  1022. getTimeString: function(count, disableSec = false) {
  1023. let s = Math.floor((count / 1000)) % 60;
  1024. let m = Math.floor((count / 60000)) % 60;
  1025. let h = Math.floor((count / 3600000)) % 24;
  1026. let d = Math.floor((count / 86400000)) % 7;
  1027. let w = Math.floor((count / 604800000)) % 52;
  1028. let y = Math.floor((count / 31557600000));
  1029. let timeStr = '';
  1030. if(!isFinite(y)) {
  1031. return 'Infinity';
  1032. }
  1033. timeStr += (y>0) ? y+'Y ' : '';
  1034. timeStr += (w>0) ? w+'W ' : '';
  1035. timeStr += (d>0) ? d+'D ' : '';
  1036. timeStr += (h>0) ? h+'H ' : '';
  1037. if(y<1) {
  1038. timeStr += (m>0) ? m+'M ' : '';
  1039. }
  1040. if (!disableSec) {
  1041. timeStr += (s>0) ? s+'S ' : '';
  1042. }
  1043.  
  1044. return timeStr;
  1045. },
  1046. getDate: function(date) {
  1047. let h = date.getHours();
  1048. let m = date.getMinutes();
  1049. let s = date.getSeconds()
  1050. m = m < 10 ? '0'+m : m;
  1051. s = s < 10 ? '0'+s : s;
  1052. h = h < 10 ? '0'+h : h;
  1053. let strTime = h + ':' + m + ':' + s;
  1054. if(~$('.chat-message .message-time-stamp').text().indexOf(' AM]') || ~$('.chat-message .message-time-stamp').text().indexOf(' PM]')) {
  1055. let ampm = parseInt(h) >= 12 ? 'PM' : 'AM';
  1056. h = h % 12;
  1057. h = h ? h : 12;
  1058. strTime = h + ':' + m + ':' + s + ' ' + ampm
  1059. }
  1060. return strTime;
  1061. },
  1062. addCommas: function(nStr) {
  1063. nStr += '';
  1064. let x = nStr.split('.');
  1065. let x1 = x[0];
  1066. let x2 = x.length > 1 ? '.' + x[1] : '';
  1067. let rgx = /(\d+)(\d{3})/;
  1068. while (rgx.test(x1)) {
  1069. x1 = x1.replace(rgx, '$1' + ',' + '$2');
  1070. }
  1071. return x1 + x2;
  1072. },
  1073. ucfirst: function(string) {
  1074. return string.charAt(0).toUpperCase() + string.slice(1);
  1075. },
  1076. sortObject: function(o) {
  1077. var sorted = {},
  1078. key, a = [];
  1079. for (key in o) {
  1080. if (o.hasOwnProperty(key)) {
  1081. a.push(key);
  1082. }
  1083. }
  1084. a.sort();
  1085. for (key = 0; key < a.length; key++) {
  1086. sorted[a[key]] = o[a[key]];
  1087. }
  1088. return sorted;
  1089. }
  1090. },
  1091. }
  1092.  
  1093. /* INITIATE APPLICATION */
  1094. let lib = {};
  1095. let app = {};
  1096.  
  1097. let initInterval = setInterval(()=> {
  1098. if (window.D4IS && window.D4IS.init) {
  1099. clearInterval(initInterval);
  1100. lib = window.D4IS.init('lootify', '1.4.10');
  1101. app = lib['lootify'];
  1102. lib.app.setUpdateLocation(lib.game.getMenuItem('Lootify Settings'));
  1103. main();
  1104. }
  1105. }, 250);
  1106.  
  1107. function main() {
  1108. /* SET DEFAULT VALUES */
  1109. let runs = [{}];
  1110. let total = {};
  1111. let totalLoot = {};
  1112. let headers = {};
  1113.  
  1114. /* ############## INTERVALS ############## */
  1115. let mainInterval = setInterval(()=> {
  1116. if (app.ready && $('.status-action').length) {
  1117. if(is()) {
  1118. if(!app.setup) {
  1119. app.setup = true;
  1120. setupSettings();
  1121. lib.app.updateUser(app.name);
  1122. app.getKills();
  1123. setupLogs();
  1124. }
  1125. renderErrors();
  1126. renderStatus();
  1127. saveLogs();
  1128. mergeLogs();
  1129. renderLogs();
  1130. renderTimer();
  1131. renderKPH();
  1132. renderGPH();
  1133. renderXPH();
  1134. }
  1135. }
  1136. }, 1000);
  1137.  
  1138. let pasteInterval = setInterval(()=> {
  1139. if (app.ready && $('.status-action').length) {
  1140. if(lib.general.getStorage('AutoPaster') == 'true') {
  1141. reset('single');
  1142. }
  1143. }
  1144. }, getPasteInterval() * 60 * 1000);
  1145.  
  1146. let adInterval = setInterval(()=> {
  1147. if (app.ready && $('.status-action').length) {
  1148. lib.game.chat({
  1149. 'msg': 'Lootify - Please report bugs to D4M4G3X#6263 on Discord!',
  1150. 'color': '#00a0fd',
  1151. });
  1152. }
  1153. }, 20 * 60 * 1000);
  1154.  
  1155. let updateInterval = setInterval(()=> {
  1156. if (app.ready && $('.status-action').length) {
  1157. lib.app.getData();
  1158. lib.app.updateUser(app.name);
  1159. app.getKills();
  1160. }
  1161. }, 15 * 60 * 1000);
  1162.  
  1163. let quickInterval = setInterval(()=> {
  1164. if (app.ready && $('.status-action').length) {
  1165. addConfirmations();
  1166. checkRemovals();
  1167. checkDisabled();
  1168. checkChests();
  1169. }
  1170. }, 250);
  1171.  
  1172. function addConfirmations() {
  1173.  
  1174. // #### GATHERING CONFIRMATIONS #### //
  1175. $('.resource-container-button').each(function() {
  1176. if(lib.app.data) {
  1177. if (!lib.app.data.active.skills.includes($(this).parents('.play-area').attr('class').split('theme-')[1])) {
  1178. return false;
  1179. }
  1180.  
  1181. let $parent = $(this).parents('.resource-container');
  1182. if( !$(this).next('.resource-button-overlay').length && !~$(this).text().indexOf('Stop')) {
  1183. let $btn = $(this).find('.resource-button').hide();
  1184.  
  1185. let $btnClone = $('<div>', {
  1186. 'class': 'resource-button-overlay resource-button'
  1187. }).text($btn.text()).insertAfter($(this));
  1188.  
  1189. $btnClone.click(function() {
  1190. let includes = false;
  1191. // Switch Warnings Disabled
  1192. if (lib.general.getStorage('SwitchReset') === 'false') {
  1193. $parent.find('.resource-button:not(.resource-button-overlay)').click();
  1194. return false;
  1195. }
  1196.  
  1197. // Game Start
  1198. if ((lib.user.isStatus('idling') && !app.prevStatus) || isLogEmpty()) {
  1199. $parent.find('.resource-button:not(.resource-button-overlay)').click();
  1200. return false;
  1201. }
  1202.  
  1203. // Is Logged
  1204. if (isLogged('skill', $parent.find('.resource-container-title').text())) {
  1205. $parent.find('.resource-button:not(.resource-button-overlay)').click();
  1206. return false;
  1207. }
  1208.  
  1209. lib.game.dialog({
  1210. 'title': 'Lootify warning!',
  1211. 'text': 'Going to a different location will paste and reset your Lootify statistics, are you sure you want to do this?',
  1212. 'img': true,
  1213. 'cbyes': function() {
  1214. reset();
  1215. $parent.find('.resource-button:not(.resource-button-overlay)').click();
  1216. }
  1217. });
  1218. });
  1219. }
  1220. if (~$(this).text().indexOf('Stop')) {
  1221. $(this).next('.resource-button-overlay').remove();
  1222. $(this).find('.resource-button').show();
  1223. }
  1224. }
  1225. });
  1226.  
  1227. // #### COOKING CONFIRMATIONS #### //
  1228.  
  1229. if ($('.cooking-item').find('img').length) {
  1230. if(!$('.cooking-start-clone').length && !lib.user.isStatus('cooking')) {
  1231. let $cookBtn = $('.cooking-start-button').css('visibility', 'hidden');
  1232. let $cloneBtn = $('<div>', {
  1233. 'class': 'cooking-start-button cooking-start-clone'
  1234. }).text('Start Cooking').insertAfter($cookBtn);
  1235. $(window).resize(function() {
  1236. let pos = $cookBtn.position();
  1237. $cloneBtn.width($cookBtn.width());
  1238. $cloneBtn.css({
  1239. 'position': 'absolute',
  1240. 'z-index': '1000',
  1241. 'top': pos.top,
  1242. 'left': pos.left,
  1243. });
  1244. }).resize();
  1245. $cloneBtn.click(function() {
  1246. // Switch Warnings Disabled
  1247. if (lib.general.getStorage('SwitchReset') === 'false') {
  1248. $cookBtn.click();
  1249. $cookBtn.css('visibility', 'visible');
  1250. $cloneBtn.hide();
  1251. return false;
  1252. }
  1253.  
  1254. // Game Start
  1255. if ((lib.user.isStatus('idling') && !app.prevStatus) || isLogEmpty()) {
  1256. $cookBtn.click();
  1257. $cookBtn.css('visibility', 'visible');
  1258. $cloneBtn.hide();
  1259. return false;
  1260. }
  1261.  
  1262. // Is Previous Status
  1263. if (lib.user.isPrevStatus('cooking')) {
  1264. $cookBtn.click();
  1265. $cookBtn.css('visibility', 'visible');
  1266. $cloneBtn.hide();
  1267. return false;
  1268. }
  1269.  
  1270. lib.game.dialog({
  1271. 'title': 'Lootify warning!',
  1272. 'text': 'Cooking now will paste and reset your current Lootify statistics, are you sure you want to do this?',
  1273. 'img': true,
  1274. 'cbyes': function() {
  1275. reset();
  1276. $cloneBtn.hide();
  1277. $cookBtn.css('visibility', 'visible');
  1278. $cookBtn.click();
  1279. }
  1280. });
  1281. });
  1282. }
  1283. } else {
  1284. $('.cooking-start-button').css('visibility', 'visible');
  1285. $('.cooking-start-clone').remove();
  1286. }
  1287.  
  1288. // #### COMBAT CONFIRMATIONS #### //
  1289.  
  1290. if ($('.combat-zones').length && !$('.zone-button-overlay').length) {
  1291. let $overlay = $('<div>', {
  1292. 'class': 'zone-button-overlay'
  1293. }).appendTo($('.combat-zones'));
  1294.  
  1295. $('.combat-zone').each(function() {
  1296. let zoneName = $(this).find('.combat-zone-text').text();
  1297. let zoneTH = $(this).find('.combat-zone-elite-challenge').text();
  1298. let $zoneBtn = $('<div>', {'class':'combat-zone-clone', 'style':'font-size: 2vw;line-height:inherit;'}).appendTo($overlay);
  1299. $('<div>', {'class':'combat-zone-text'}).text(zoneName).appendTo($zoneBtn);
  1300. if (zoneTH.length > 0){
  1301. let thDiv = $('<div>', {'class':'combat-zone-elite-challenge'});
  1302. thDiv.append('<img src="/images/magic/buffs/treasurehunter_icon.png">');
  1303. thDiv.append(zoneTH);
  1304. thDiv.appendTo($zoneBtn);
  1305. }
  1306.  
  1307. $zoneBtn.click(function(e) {
  1308. let $parent = $(this).parents('.combat-zones');
  1309.  
  1310. // Switch Warnings Disabled
  1311. if (lib.general.getStorage('SwitchReset') === 'false') {
  1312. $parent.find('.combat-zone').each(function() {
  1313. if($(this).find('.combat-zone-text').text() === zoneName) {
  1314. $(this).click();
  1315. }
  1316. });
  1317. return false;
  1318. }
  1319.  
  1320. // Game Start
  1321. if ((lib.user.isStatus('idling') && !lib.user.prevStatus) || isLogEmpty()) {
  1322. $parent.find('.combat-zone').each(function() {
  1323. if($(this).find('.combat-zone-text').text() === zoneName) {
  1324. $(this).click();
  1325. }
  1326. });
  1327. return false;
  1328. }
  1329.  
  1330. // Is Logged
  1331. if (isLogged('combat', zoneName)) {
  1332. $parent.find('.combat-zone').each(function() {
  1333. if($(this).find('.combat-zone-text').text() === zoneName) {
  1334. $(this).click();
  1335. }
  1336. });
  1337. return false;
  1338. }
  1339.  
  1340. // Is an event mob
  1341. if (lib.game.isEventMob(lib.user.currentMob)) {
  1342. $parent.find('.combat-zone').each(function() {
  1343. if($(this).find('.combat-zone-text').text() === zoneName) {
  1344. $(this).click();
  1345. }
  1346. });
  1347. return false;
  1348. }
  1349.  
  1350. lib.game.dialog({
  1351. 'title': 'Lootify warning!',
  1352. 'text': 'Going to a different zone will paste and reset your Lootify statistics, are you sure you want to do this?',
  1353. 'img': true,
  1354. 'cbyes': function() {
  1355. $parent.find('.combat-zone').each(function() {
  1356. if($(this).find('.combat-zone-text').text() === zoneName) {
  1357. reset();
  1358. $(this).click();
  1359. }
  1360. });
  1361. },
  1362. });
  1363. });
  1364. });
  1365.  
  1366. $(window).resize(function() {
  1367. $overlay.width($('.combat-zones').width());
  1368. }).resize();
  1369. }
  1370. }
  1371. function checkRemovals() {
  1372. if(!lib.user.isStatus('foraging')) { return; }
  1373. if ($('.recipe-item').length) {
  1374. $('.recipe-item').each(function() {
  1375. if($(this).find('img').attr('src') === '/images/foraging/log.png') {
  1376. $(this).remove();
  1377. }
  1378. });
  1379. }
  1380. }
  1381. function checkDisabled() {
  1382. if(lib.user.getEnchantment('35') > 0 && lib.user.isStatus('foraging')) {
  1383. app.isNature = true;
  1384. }
  1385. if(lib.user.getEnchantment('7') > 0 && lib.user.isActiveSkill()) {
  1386. app.isWealth = true;
  1387. }
  1388. }
  1389. function checkChests() {
  1390. if ($('.chat-message .activity-log:not(.ltf-done)').length) {
  1391. $('.chat-message .activity-log:not(.ltf-done)').each(function(k, v) {
  1392. $(this).addClass('ltf-done');
  1393. let data = {};
  1394. data.chest = {};
  1395. data.loot = {};
  1396. if(~$(this).text().indexOf('geode')) {
  1397. data.chest.base = 'geode';
  1398. data.chest.name = 'Geode';
  1399. } else if(~$(this).text().indexOf('bird nest')) {
  1400. data.chest.base = 'bird nest';
  1401. data.chest.name = "Bird's Nest";
  1402. } else if(~$(this).text().indexOf('sunken treasure')) {
  1403. data.chest.base = 'sunken treasure';
  1404. data.chest.name = 'Sunken Treasure';
  1405. } else if(~$(this).text().indexOf('satchel')) {
  1406. data.chest.base = 'satchel';
  1407. data.chest.name = 'Satchel';
  1408. } else {
  1409. return false;
  1410. }
  1411. let count = $(this).text().split(data.chest.base)[0].match(/\d+/g);
  1412. data.chest.count = (count) ? parseInt(count) : 1;
  1413. let loot = $(this).text().split('found ')[1];
  1414. if(~loot.indexOf(',')) {
  1415. loot = loot.split(', ');
  1416. } else {
  1417. let item = loot.split(' as')[0];
  1418. loot = [];
  1419. loot.push(item);
  1420. }
  1421. $.each(loot, function(k, loot) {
  1422. if(~loot.indexOf(' as') && !~loot.indexOf(',')) {
  1423. loot = loot.split(' as')[0];
  1424. }
  1425. data.loot[loot.split(' x ')[0]] = parseInt(loot.split(' x ')[1]);
  1426. });
  1427. app.addLogChest(data);
  1428. });
  1429. }
  1430. }
  1431.  
  1432. // #### SETUP SETTINGS ####
  1433. function setupSettings() {
  1434. if(!lib.game.getMenuItem('Lootify', 'category')) {
  1435. lib.game.setMenuItem({
  1436. 'text': 'Lootify',
  1437. 'clone': 'Gathering',
  1438. 'class': 'hdr-lootify',
  1439. 'before': lib.game.getMenuItem('Gathering', 'category'),
  1440. }, 'category');
  1441. $('<i/>').text(' (v'+app.ver+')').appendTo(lib.game.getMenuItem('Lootify', 'category'));
  1442. }
  1443. // ### PRIVACY SETTINGS ###
  1444. lib.game.addSetting({
  1445. 'app': 'lootify',
  1446. 'text': 'Allow Lootify to publicly use your username for personal highscores and kill statistics',
  1447. 'name': 'UserPublic',
  1448. 'type': 'checkbox',
  1449. 'default': 'false',
  1450. 'change': function() {
  1451. setUserPublic($(this).prop('checked'));
  1452. lib.general.setStorage('UserPublic', $(this).prop('checked'));
  1453. }
  1454. });
  1455. // ### SETTING AUTO PASTER ###
  1456. lib.game.addSetting({
  1457. 'app': 'lootify',
  1458. 'text': 'Enable Auto Paster',
  1459. 'name': 'AutoPaster',
  1460. 'type': 'checkbox',
  1461. 'default': 'false',
  1462. 'change': function() {
  1463. lib.general.setStorage('AutoPaster', $(this).prop('checked'));
  1464. }
  1465. });
  1466. // ### SETTING PASTE INTERVAL ###
  1467. lib.game.addSetting({
  1468. 'app': 'lootify',
  1469. 'text': 'Paste Interval (minutes)',
  1470. 'name': 'AutoPasterInterval',
  1471. 'type': 'number',
  1472. 'min': 15,
  1473. 'max': 60,
  1474. 'default': 30,
  1475. 'change': function() {
  1476. if($(this).val() >= $(this).attr('min') && $(this).val() <= $(this).attr('max')) {
  1477. lib.general.setStorage('AutoPasterInterval', $(this).val());
  1478. }
  1479. }
  1480. });
  1481. // ### SETTING CHAT MESSAGES ###
  1482. lib.game.addSetting({
  1483. 'app': 'lootify',
  1484. 'text': 'Enable Chat Messages',
  1485. 'name': 'ChatMessage',
  1486. 'type': 'checkbox',
  1487. 'default': 'true',
  1488. 'change': function() {
  1489. lib.general.setStorage('ChatMessage', $(this).prop('checked'));
  1490. }
  1491. });
  1492. // ### RESET ON SWITCH ###
  1493. lib.game.addSetting({
  1494. 'app': 'lootify',
  1495. 'text': 'Reset on Switch',
  1496. 'name': 'SwitchReset',
  1497. 'type': 'checkbox',
  1498. 'default': 'true',
  1499. 'change': function() {
  1500. lib.general.setStorage('SwitchReset', $(this).prop('checked'));
  1501. }
  1502. });
  1503. }
  1504. function renderErrors() {
  1505. if (!app.disabled || !app.enabled) {
  1506. $('.lootify-error').remove();
  1507. }
  1508. if (!$('.lootify-error.error-paster').length && app.disabled) { // TODO: Create Error Message Function
  1509. let $error = $('<div/>', {
  1510. 'class': 'lootify-error error-paster'
  1511. }).css({
  1512. 'color': 'red',
  1513. 'width': '80%',
  1514. 'margin': '0 auto',
  1515. }).html('Paster is disabled!<br>Reset log to Enable again.').insertAfter(lib.game.getMenuItem('Lootify', 'category'));
  1516. lib.game.tooltip($error, 'Pasting is disabled on market usage, you can still use the counters.<br> This is done to prevent skewing the statistics.');
  1517. }
  1518. if (!$('.lootify-error.error-nature').length && app.isNature) { // TODO: Create Error Message Function
  1519. let $error = $('<div/>', {
  1520. 'class': 'lootify-error error-nature'
  1521. }).css({
  1522. 'color': 'red',
  1523. 'width': '80%',
  1524. 'margin': '0 auto',
  1525. }).html('Paster is disabled!<br>Unequip scroll of nature item and reset to Enable again.').insertAfter(lib.game.getMenuItem('Lootify', 'category'));
  1526. lib.game.tooltip($error, 'Pasting is disabled when scroll of nature is active, you can still use the counters.<br> This is done to prevent skewing the statistics.');
  1527. }
  1528. if (!$('.lootify-error.error-wealth').length && app.isWealth) { // TODO: Create Error Message Function
  1529. let $error = $('<div/>', {
  1530. 'class': 'lootify-error error-wealth'
  1531. }).css({
  1532. 'color': 'red',
  1533. 'width': '80%',
  1534. 'margin': '0 auto',
  1535. }).html('Paster is disabled!<br>Unequip scroll of wealth item and reset to Enable again.').insertAfter(lib.game.getMenuItem('Lootify', 'category'));
  1536. lib.game.tooltip($error, 'Pasting is disabled when scroll of wealth is active, you can still use the counters.<br> This is done to prevent skewing the statistics.');
  1537. }
  1538. if (!$('.lootify-error.error-disabled').length && (app.enabled && app.enabled === 'false')) { // TODO: Create Error Message Function
  1539. $('<div/>', {
  1540. 'class': 'lootify-error error-disabled'
  1541. }).css({
  1542. 'color': 'red',
  1543. 'width': '80%',
  1544. 'margin': '0 auto',
  1545. }).html('Paster is globally disabled!').insertAfter(lib.game.getMenuItem('Lootify', 'category'));
  1546. }
  1547. }
  1548. function renderStatus() {
  1549. if (!lib.user.isFighting() && !lib.user.isActiveSkill() && !lib.user.isStatus('idling')) {
  1550. $('.ltf-status').remove();
  1551. return false;
  1552. }
  1553. if (!lib.game.getMenuItem('Status:')) {
  1554. lib.game.setMenuItem({
  1555. 'class': 'ltf-status',
  1556. 'text': 'Status:',
  1557. 'clone': 'General Shop',
  1558. 'icon': '/images/combat/combat_level.png',
  1559. 'after': lib.game.getMenuItem('Lootify', 'category'),
  1560. 'click': function() {
  1561. $('[data-for="'+lib.user.getStatus()+'Header"]').click();
  1562. }
  1563. });
  1564. }
  1565. let status = lib.user.getStatus();
  1566. status += lib.user.isFighting() ? ' '+lib.user.currentMob : '';
  1567. $('.ltf-status').find('span').text('Status: ' + lib.general.ucfirst(status));
  1568. }
  1569. function setupLogs() {
  1570. if ($('.btn-loot-log').length) {
  1571. $('.btn-loot-log').remove();
  1572. }
  1573. let $logBtn = lib.game.getMenuItem('Loot Log');
  1574. if (($logBtn && $logBtn.text() === 'Loot Log') && !$logBtn.hasClass('btn-loot-log')) {
  1575. lib.game.editMenuItem('Loot Log');
  1576. }
  1577. if(!lib.game.getMenuItem('Lootify Log')) {
  1578. lib.game.setMenuItem({
  1579. 'icon': '/images/ui/inventory_icon.png',
  1580. 'text': 'Lootify Log',
  1581. 'clone': 'General Shop',
  1582. 'class': 'btn-loot-log',
  1583. 'before': lib.game.getMenuItem('Gathering', 'category'),
  1584. 'click': function() {
  1585. $('.item-log-clone').toggle();
  1586. },
  1587. });
  1588. }
  1589. let $logwrap = $('.item-log-window');
  1590. $logwrap.hide();
  1591. let $clone = $logwrap.clone();
  1592. $clone.addClass('item-log-clone').removeClass('hidden');
  1593. $clone.find('.item-log-timer').remove();
  1594. $clone.find('.item-log-info').remove();
  1595. $clone.find('.drawer-setting-large').addClass('clone').unbind().click(function() {
  1596. reset();
  1597. }).text('Paste and Reset');
  1598. if (app.disabled || lib.user.isStatus('fishing') || lib.user.isStatus('smithing') || lib.user.isStatus('cooking')) {
  1599. $clone.find('.drawer-setting-large').text('Reset');
  1600. }
  1601. lib.game.getMenuItem('Lootify Log').after($clone);
  1602. }
  1603. function renderTimer() {
  1604. if (!lib.user.isFighting() && !lib.user.isActiveSkill() && !lib.user.isStatus('idling')) {
  1605. $('.time-stats').remove();
  1606. return false;
  1607. }
  1608. if (!lib.game.getMenuItem('Elapsed:')) {
  1609. let $timeStats = lib.game.setMenuItem({
  1610. 'class': 'time-stats',
  1611. 'text': 'Elapsed: 0S',
  1612. 'clone': 'General Shop',
  1613. 'icon': '/images/clock.png',
  1614. 'after': $('.ltf-status')
  1615. });
  1616. }
  1617. setTimeout(()=> {
  1618. !lib['lootify'].timers ? lib['lootify'].timers = {} : 1;
  1619. if(!lib['lootify'].timers[lib.user.getStatus()]) {
  1620. new lib.general.Timer(app.name, lib.user.getStatus());
  1621. }
  1622. }, 100);
  1623. }
  1624. function renderKPH() {
  1625. if (!lib.user.isFighting()) {
  1626. $('.kph-stats').remove();
  1627. $('.kph-wrap').remove();
  1628. return false;
  1629. }
  1630. if (!lib.game.getMenuItem('Kills:')) {
  1631. let $kphStats = lib.game.setMenuItem({
  1632. 'class': 'kph-stats',
  1633. 'text': 'Kills: 0 p/h',
  1634. 'clone': 'General Shop',
  1635. 'icon': '/images/combat/combat_level.png',
  1636. 'after': $('.time-stats'),
  1637. 'click': function() {
  1638. $('.kph-wrap').toggle();
  1639. }
  1640. });
  1641. $kphStats.after($('<div/>', {
  1642. 'class': 'kph-wrap ltf-submenu'
  1643. }).hide());
  1644. }
  1645. let mobkills = {};
  1646. $.each(total, function(mob, mobinfo) {
  1647. if(!lib.game.isUniversalItem(mob)) {
  1648. mobkills[mob] = mobinfo.count;
  1649. }
  1650. });
  1651. let totalkills = function() {
  1652. let c = 0;
  1653. $.each(mobkills, function(mob, kills) {
  1654. c += kills;
  1655. });
  1656. return c;
  1657. };
  1658. let kph = 0;
  1659. let time = lib.general.getTimerTime('lootify', lib.user.getStatus());
  1660. kph = lib.general.addCommas(Math.floor((totalkills()/time)*3600));
  1661. $('.kph-stats').find('span').text('Kills: ' + kph + ' p/h');
  1662. $.each(lib.general.sortObject(mobkills), function(mob, count) {
  1663. let mobinfoclass = 'mob-info-'+sanitizeEntry(mob);
  1664. kph = lib.general.addCommas(Math.floor((count/time)*3600));
  1665. if(!$('.'+mobinfoclass).length) {
  1666. let $mobwrap = $('<div/>', {
  1667. 'class': 'ltf-header mob-header '+mobinfoclass
  1668. });
  1669. $mobwrap.prepend($('<span>').css('display','block').text(mob + ': ' + kph + ' p/h'));
  1670. $mobwrap.prepend($('<img>').attr('src', lib.game.getMobIcon(mob)));
  1671. $('.kph-wrap').append($mobwrap);
  1672. } else {
  1673. $('.'+mobinfoclass).find('span').text(mob + ': ' + kph + ' p/h');
  1674. }
  1675. });
  1676. }
  1677. function renderGPH() {
  1678. if((!lib.user.isActiveSkill() && !lib.user.isFighting()) || lib.user.isIron() || lib.user.isStatus('cooking')) {
  1679. $('.gph-stats').remove();
  1680. $('.gph-wrap').remove();
  1681. return false;
  1682. }
  1683. if (!lib.game.getMenuItem('Gold:')) {
  1684. let $gphStats = lib.game.setMenuItem({
  1685. 'class': 'gph-stats',
  1686. 'text': 'Gold: 0 p/h',
  1687. 'icon': '/images/ui/shop_icon.png',
  1688. 'clone': 'General Shop',
  1689. 'after': $('.time-stats'),
  1690. 'click': function() {
  1691. $('.gph-wrap').toggle();
  1692. }
  1693. });
  1694. $gphStats.after($('<div/>', {
  1695. 'class': 'gph-wrap ltf-submenu'
  1696. }).hide());
  1697. }
  1698.  
  1699. let totalGold = 0;
  1700. if (lib.game.marketPrices) {
  1701. $.each(totalLoot, function(item, count) {
  1702. if (item === 'Gold') {
  1703. totalGold += count;
  1704. } else {
  1705. if (lib.user.isStatus('smithing')) {
  1706. let ore = item.split(' ')[0] + ' Ore';
  1707. totalGold += (lib.game.marketPrices[item] - lib.game.marketPrices[ore])* count;
  1708. } else {
  1709. totalGold += lib.game.marketPrices[item] * count;
  1710. }
  1711. }
  1712. });
  1713. } else {
  1714. totalGold = 'n/a';
  1715. }
  1716.  
  1717. let time = lib.general.getTimerTime('lootify', lib.user.getStatus());
  1718. if (time) {
  1719. let gph = lib.general.addCommas(Math.floor((totalGold/time)*3600));
  1720. $('.gph-stats').find('span').text('Gold: ' + gph + ' p/h');
  1721. }
  1722. $('.gph-wrap').empty();
  1723. let $lootwrap = $('<div/>', {
  1724. 'class': 'ltf-header mob-header'
  1725. }).appendTo($('.gph-wrap'));
  1726.  
  1727. let lootval = lib.general.addCommas(Math.floor(totalGold));
  1728. let $lootInfo = $('<span/>').css('display','block').text('Loot value: ' + lootval);
  1729. $lootInfo.prepend($('<img>').attr('src', '/images/money_icon.png'));
  1730. $lootwrap.append($lootInfo);
  1731. }
  1732. function renderXPH() {
  1733. if(!lib.user.isActiveSkill() && !lib.user.isFighting()) {
  1734. $('.mxph-stats').remove();
  1735. $('.mxph-wrap').remove();
  1736. $('.xph-stats').remove();
  1737. $('.xph-wrap').remove();
  1738. return false;
  1739. }
  1740. let currentLevel = {};
  1741. let approxTime = {};
  1742. let totalExp = {};
  1743. let skillExp = {};
  1744. let combatSkills = ['attack', 'defense', 'strength', 'constitution'];
  1745. let expTypes = ['exp', 'mexp'];
  1746. let time = lib.general.getTimerTime('lootify', lib.user.getStatus());
  1747. !lib.user.levelGoal ? lib.user.levelGoal = {} : 1;
  1748. $.each(expTypes, function(k, type) {
  1749. if (lib.user[type]) {
  1750. !lib.user.levelGoal[type] ? lib.user.levelGoal[type] = {} : 1;
  1751. // CALCULATE EXP FOR COMBAT SKILLS
  1752. if (lib.user.isFighting()) {
  1753. $.each(combatSkills, function(k, skill) {
  1754. !skillExp[type] ? skillExp[type] = [] : 1;
  1755. !totalExp[type] ? totalExp[type] = 0 : 1;
  1756. !currentLevel[type] ? currentLevel[type] = [] : 1;
  1757. // GET THE CURRENT SKILL EXP GAINED
  1758. skillExp[type][skill] = lib.user[type][skill].current - lib.user[type][skill].init;
  1759. // GET TOTAL EXP GAINED OF ALL COMBAT SKILLS
  1760. totalExp[type] += (lib.user[type][skill].current - lib.user[type][skill].init);
  1761. currentLevel[type][skill] = lib.game.getLevel(lib.user[type][skill].current);
  1762. !lib.user.levelGoal[type][skill] ? lib.user.levelGoal[type][skill] = currentLevel[type][skill] + 1 : 1;
  1763. if(type === 'exp' && currentLevel[type] < 99) {
  1764. lib.user.levelGoal[type][skill] = (lib.user.levelGoal[type][skill] <= currentLevel[type]) ? lib.user.levelGoal[type][skill] = currentLevel[type] + 1 : lib.user.levelGoal[type][skill];
  1765. } else if (type ==='mexp' && currentLevel[type] >= 99) {
  1766. lib.user.levelGoal[type][skill] = (lib.user.levelGoal[type][skill] <= currentLevel[type]) ? lib.user.levelGoal[type][skill] = currentLevel[type] + 1 : lib.user.levelGoal[type][skill];
  1767. }
  1768. });
  1769. }
  1770. // CALCULATE EXP FOR GATHERING SKILLS
  1771. $.each(lib.app.data.active.skills, function(k, skill) {
  1772. if(lib.user.isStatus(skill)) {
  1773. totalExp[type] = lib.user[type][skill].current - lib.user[type][skill].init;
  1774. currentLevel[type] = lib.game.getLevel(lib.user[type][skill].current);
  1775. !lib.user.levelGoal[type][skill] ? lib.user.levelGoal[type][skill] = currentLevel[type] + 1 : 1;
  1776. if(type === 'exp' && currentLevel[type] < 99) {
  1777. lib.user.levelGoal[type][skill] = (lib.user.levelGoal[type][skill] <= currentLevel[type]) ? lib.user.levelGoal[type][skill] = currentLevel[type] + 1 : lib.user.levelGoal[type][skill];
  1778. } else if (type ==='mexp' && currentLevel[type] >= 99) {
  1779. lib.user.levelGoal[type][skill] = (lib.user.levelGoal[type][skill] <= currentLevel[type]) ? lib.user.levelGoal[type][skill] = currentLevel[type] + 1 : lib.user.levelGoal[type][skill];
  1780. }
  1781. }
  1782. });
  1783. }
  1784. });
  1785. // EXP COUNTERS
  1786. $.each(expTypes, function(k, type) {
  1787. let menuItem = (type === 'exp') ? 'Experience:' : 'Mastery:';
  1788. let menuClass = (type === 'exp') ? 'xph' : 'mxph';
  1789. let menuIcon = (type === 'exp') ? '/images/total_level.png' : '/images/total_level_mastery_icon.png';
  1790.  
  1791. if (!lib.game.getMenuItem(menuItem) && totalExp[type] > 1) {
  1792. let $xphStats = lib.game.setMenuItem({
  1793. 'class': menuClass+'-stats',
  1794. 'text': menuItem+' 0 p/h',
  1795. 'icon': menuIcon,
  1796. 'after': $('.time-stats'),
  1797. 'clone': 'General Shop',
  1798. 'click': function() {
  1799. $('.'+menuClass+'-wrap').toggle();
  1800. }
  1801. });
  1802. $xphStats.after($('<div/>', {
  1803. 'class': menuClass+'-wrap ltf-submenu'
  1804. }).hide());
  1805. }
  1806.  
  1807. let xph = lib.general.addCommas(Math.floor((totalExp[type]/time)*3600));
  1808. $('.'+menuClass+'-stats').find('span').text(menuItem + ' ' + xph + ' p/h');
  1809.  
  1810. // XP Earned since activity start
  1811. let xpearned = lib.general.addCommas(Math.floor(totalExp[type]));
  1812. let $expwrap;
  1813. if(!$('.'+menuClass+'-header').length) {
  1814. $expwrap = $('<div/>', {
  1815. 'class': 'ltf-header mob-header ' + menuClass + '-header'
  1816. })
  1817. $expwrap.prepend($('<span>').css('display','block').text('Exp earned: ' + xpearned));
  1818. $expwrap.prepend($('<img>').attr('src', 'https://digimol.net/idlescape/assets/img/plus_sign.png'));
  1819. $expwrap.appendTo($('.'+menuClass+'-wrap'));
  1820. } else {
  1821. $expwrap = $('.'+menuClass+'-wrap');
  1822. $('.'+menuClass+'-header > span').css('display','block').text('Exp earned: ' + xpearned);
  1823. }
  1824.  
  1825. // Time Left For Next Level
  1826. let $expTimeWrap, $expTimeInfo;
  1827. if (lib.user[type]) {
  1828. if(!lib.user.isFighting()) {
  1829. let skill = lib.user.getStatus();
  1830. let exptimeclass = 'exptime-'+type+'-'+skill;
  1831. approxTime = lib.user.getTimeToLevel(lib.user.levelGoal[type][skill], lib.user[type][skill].current, totalExp[type], time);
  1832. if(!$('.'+exptimeclass).length) {
  1833. $expTimeWrap = $('<div/>', {
  1834. 'class': 'ltf-header mob-header'
  1835. })
  1836. $expTimeInfo = $('<div>', {
  1837. 'class': exptimeclass
  1838. }).css('display','flex')
  1839. $expTimeInfo.append($('<img>').attr('src', lib.game.getSkillIcon(skill)));
  1840. $expTimeInfo.append($('<span>', {
  1841. 'class': exptimeclass+'-level'
  1842. }).text('to '+lib.user.levelGoal[type][skill]));
  1843. let $arrows = $('<div>', {
  1844. 'class': 'd4is-control-arrows'
  1845. }).appendTo($expTimeInfo);
  1846. $('<span>').text('▲').click(function() {
  1847. if(lib.user.levelGoal[type][skill] < 200) {
  1848. lib.user.levelGoal[type][skill]++;
  1849. $('.'+exptimeclass+'-level').text('to '+lib.user.levelGoal[type][skill]);
  1850. }
  1851. }).appendTo($arrows);
  1852. $('<span>').text('▼').click(function() {
  1853. if(lib.user.levelGoal[type][skill] > currentLevel[type]+1) {
  1854. lib.user.levelGoal[type][skill]--;
  1855. $('.'+exptimeclass+'-level').text('to '+lib.user.levelGoal[type][skill]);
  1856. }
  1857. }).appendTo($arrows);
  1858. $expTimeInfo.append($('<span>', {
  1859. 'class': exptimeclass+'-text'
  1860. }).text(approxTime));
  1861. $expTimeWrap.append($expTimeInfo);
  1862. $expTimeWrap.appendTo($('.'+menuClass+'-wrap'));
  1863. } else {
  1864. $('.'+exptimeclass+'-text').text(approxTime);
  1865. }
  1866. } else {
  1867. $.each(combatSkills, function(k, skill) {
  1868. if(skillExp[type][skill] > 0) {
  1869. let exptimeclass = 'exptime-'+type+'-'+skill;
  1870. approxTime = lib.user.getTimeToLevel(lib.user.levelGoal[type][skill], lib.user[type][skill].current, skillExp[type][skill], time);
  1871. if(!$('.'+exptimeclass).length) {
  1872. $expTimeWrap = $('<div/>', {
  1873. 'class': 'ltf-header mob-header'
  1874. })
  1875. $expTimeInfo = $('<div>', {
  1876. 'class': exptimeclass
  1877. }).css('display','flex')
  1878. $expTimeInfo.append($('<img>').attr('src', lib.game.getSkillIcon(skill)));
  1879. $expTimeInfo.append($('<span>', {
  1880. 'class': exptimeclass+'-level'
  1881. }).text('to '+lib.user.levelGoal[type][skill]));
  1882. let $arrows = $('<div>', {
  1883. 'class': 'd4is-control-arrows'
  1884. }).appendTo($expTimeInfo);
  1885. $('<span>').text('▲').click(function() {
  1886. if(lib.user.levelGoal[type][skill] < 200) {
  1887. lib.user.levelGoal[type][skill]++;
  1888. $('.'+exptimeclass+'-level').text('to '+lib.user.levelGoal[type][skill]);
  1889. }
  1890. }).appendTo($arrows);
  1891. $('<span>').text('▼').click(function() {
  1892. if(lib.user.levelGoal[type][skill] > currentLevel[type][skill]+1) {
  1893. lib.user.levelGoal[type][skill]--;
  1894. $('.'+exptimeclass+'-level').text('to '+lib.user.levelGoal[type][skill]);
  1895. }
  1896. }).appendTo($arrows);
  1897. $expTimeInfo.append($('<span>', {
  1898. 'class': exptimeclass+'-text'
  1899. }).text(approxTime));
  1900. $expTimeWrap.append($expTimeInfo);
  1901. $expTimeWrap.appendTo($('.'+menuClass+'-wrap'));
  1902. } else {
  1903. $('.'+exptimeclass+'-text').text(approxTime);
  1904. }
  1905. }
  1906.  
  1907. });
  1908. }
  1909. }
  1910. });
  1911. }
  1912. function saveLogs() {
  1913. if (!lib.user.isFighting()) { return false; }
  1914. let $logcats = $('.item-log-window:not(.item-log-clone)').find('.item-log-cateogry');
  1915. if ($logcats.length) {
  1916. $logcats.each(function() {
  1917. if( $(this).find('.item-log-category-closed').length ) {
  1918. $(this).find('.item-log-category-closed').click();
  1919. }
  1920. let mobs = $(this).find('.item-log-category-open').text().split(" x ");
  1921. !runs[0][mobs[0]] ? runs[0][mobs[0]] = {} : 1;
  1922. runs[0][mobs[0]].count = parseInt(mobs[1]);
  1923. $(this).find('.item-log-item').each(function() {
  1924. if ($(this).text() !== "None") {
  1925. let loot = $(this).text().split(" x ");
  1926. !runs[0][mobs[0]].loot ? runs[0][mobs[0]].loot = {} : 1;
  1927. !runs[0][mobs[0]].loot[loot[0]] ? runs[0][mobs[0]].loot[loot[0]] = {} : 1;
  1928. runs[0][mobs[0]].loot[loot[0]].count = parseInt(loot[1]);
  1929. }
  1930. });
  1931. });
  1932. }
  1933. }
  1934. function mergeLogs() {
  1935. if(runs) {
  1936. total = {};
  1937. totalLoot = {};
  1938. $.each(runs, function(num, run) {
  1939. $.each(run, function(mobname, mobinfo) {
  1940. !total[mobname] ? total[mobname] = {} : 1;
  1941. !total[mobname].count ? total[mobname].count = 0 : 1;
  1942. total[mobname].count += mobinfo.count;
  1943. $.each(mobinfo.loot, function(lootname, lootinfo) {
  1944. /* SET TOTAL LOOT PER MOB */
  1945. !total[mobname].loot ? total[mobname].loot = {} : 1;
  1946. !total[mobname].loot[lootname] ? total[mobname].loot[lootname] = {} : 1;
  1947. !total[mobname].loot[lootname].count ? total[mobname].loot[lootname].count = 0 : 1;
  1948. !total[mobname].loot[lootname].procs ? total[mobname].loot[lootname].procs = 0 : 1;
  1949. total[mobname].loot[lootname].count += lootinfo.count;
  1950. total[mobname].loot[lootname].procs += lootinfo.procs;
  1951. /* SET TOTAL LOOT OVERALL */
  1952. if (!lib.game.isUniversalItem(mobname)) {
  1953. let procs = lootinfo.procs ? lootinfo.procs : 0;
  1954. !totalLoot[lootname] ? totalLoot[lootname] = 0 : 1;
  1955. totalLoot[lootname] += lootinfo.count + procs;
  1956. }
  1957. });
  1958. });
  1959. });
  1960. delete total[''];
  1961. }
  1962. }
  1963. function renderLogs() {
  1964. if(!runs) { return false; }
  1965. let $logEntry, $logHeader, $lootWrap, $lootEntry;
  1966. $('.item-log-clone').find('.item-log-cateogry').remove();
  1967. $.each(lib.general.sortObject(total), function(mob, mobinfo) {
  1968. let icon = !lib.user.isFighting() ? lib.game.getLocationIcon(lib.game.getLocationID(mob)) : lib.game.getMobIcon(mob);
  1969. icon = lib.game.isUniversalItem(mob) ? getChestIcon(mob) : icon;
  1970. if (lib.user.isStatus('smithing')) {
  1971. $.each(mobinfo.loot, function(loot, info) {
  1972. if(~loot.indexOf('Bar')) {
  1973. mob = loot;
  1974. icon = '/images/smithing/'+ sanitizeEntry(mob) +'.png';
  1975. }
  1976. });
  1977. if (lib.user.getEnchantment('7') > 0) {
  1978. mob = 'Wealthing';
  1979. icon = '/images/money_icon.png';
  1980. }
  1981. }
  1982. if (!$('.log-entry-' + sanitizeEntry(mob)).length) {
  1983. // Log Wrapper
  1984. $logEntry = $('<div/>', {
  1985. 'class':'ltf-log-entry log-entry-' + sanitizeEntry(mob)
  1986. });
  1987. $logHeader = $('<div/>', {
  1988. 'class':'ltf-log-header noselect'
  1989. }).appendTo($logEntry);
  1990.  
  1991. $lootWrap = $('<div/>', {
  1992. 'class': 'ltf-loot-wrap'
  1993. }).appendTo($logEntry);
  1994.  
  1995. $logHeader.click(function() {
  1996. headers[mob] = headers[mob] == false ? true : false;
  1997. $lootWrap.toggle();
  1998. });
  1999. // HEADER
  2000. $logHeader.append($('<div/>').text(mob + ' x ' + mobinfo.count));
  2001. $logHeader.append($('<img/>').addClass('drawer-item-icon').attr('src', icon));
  2002. addLogEntry($logEntry, mob, mobinfo.loot);
  2003. $logEntry.sort(function (a, b) {
  2004. return $(a).find('.ltf-log-header').text() - $(b).find('.ltf-log-header').text();
  2005. }).each(function (_, container) {
  2006. $(container).parent().find('.drawer-setting-large.clone').before(container);
  2007. });
  2008. $('.drawer-setting-large.clone').before($logEntry);
  2009. } else {
  2010. $logEntry = $('.log-entry-' + sanitizeEntry(mob));
  2011. $logHeader = $logEntry.find('.ltf-log-header');
  2012. $logHeader.find('div').text(mob + ' x ' + mobinfo.count);
  2013. addLogEntry($logEntry, mob, mobinfo.loot);
  2014. }
  2015. });
  2016. }
  2017. function sanitizeEntry(text) {
  2018. text = text.toLowerCase();
  2019. text = text.split("'").join("");
  2020. text = text.split(" ").join("_");
  2021. return text;
  2022. }
  2023. function getChestIcon(chest) {
  2024. let icon = '';
  2025. switch(chest) {
  2026. case 'Geode':
  2027. icon = '/images/misc/geode.png';
  2028. break;
  2029. case 'Sunken Treasure':
  2030. icon = '/images/misc/sunken_treasure.png';
  2031. break;
  2032. case "Bird's Nest":
  2033. icon = '/images/misc/bird_nest.png';
  2034. break;
  2035. case 'Satchel':
  2036. icon = '/images/misc/satchel.png';
  2037. break;
  2038. }
  2039. return icon;
  2040. }
  2041. function addLogEntry($log, entry, info) {
  2042. if (!entry) { return false; }
  2043. $.each(lib.general.sortObject(info), function(loot, lootinfo) {
  2044. if (loot == '') { return false; }
  2045. let lootEntryClass = 'loot-entry-'+sanitizeEntry(entry)+'-'+sanitizeEntry(loot);
  2046. let $lootEntry = $('.'+lootEntryClass);
  2047. let lootText = loot + ' x ' + lootinfo.count;
  2048. lootText += (lootinfo.procs >= 1) ? ' (+'+lootinfo.procs+')' : '';
  2049. if(!$lootEntry.length) {
  2050. $lootEntry = $('<div/>', {
  2051. 'class': 'ltf-loot-entry '+lootEntryClass
  2052. }).text(lootText).appendTo($log.find('.ltf-loot-wrap'));
  2053. if(lib.game.isRareItem(loot)) {
  2054. $lootEntry.addClass('loot-rare');
  2055. } else if(lib.game.isEventItem(loot)) {
  2056. $lootEntry.addClass('loot-event');
  2057. } else if(lib.game.isUniversalItem(loot)) {
  2058. $lootEntry.addClass('loot-universal');
  2059. }
  2060. } else {
  2061. $lootEntry.text(lootText);
  2062. }
  2063. });
  2064. }
  2065.  
  2066. /* ############## ACTIONS ############## */
  2067. function getLogs() {
  2068. let logs = [];
  2069. let $logcats = $('.item-log-window:not(.item-log-clone)').find('.item-log-cateogry');
  2070. if ($logcats.length) {
  2071. $logcats.each(function(k,v) {
  2072. if( $(this).find('.item-log-category-closed').length ) {
  2073. $(this).find('.item-log-category-closed').click();
  2074. }
  2075. if( $(this).find('.item-log-category-open').length ) {
  2076. logs.push($(this).find('.item-log-category-open').text());
  2077. $(this).find('.item-log-item').each(function() {
  2078. if( $(this).text() !== "None" ) {
  2079. logs.push($(this).text());
  2080. }
  2081. });
  2082. }
  2083. });
  2084. }
  2085. return logs;
  2086. }
  2087. function clearLogs(m = 'all') {
  2088. if (m == 'single') {
  2089. runs.unshift({});
  2090. } else if (m == 'all') {
  2091. if (lib.user.exp && lib.user.mexp) {
  2092. $.each(lib.app.data.active.skills, function(k, skill) {
  2093. lib.user.exp[skill].init = lib.user.exp[skill].current;
  2094. lib.user.mexp[skill].init = lib.user.mexp[skill].current;
  2095. });
  2096. }
  2097. if (Object.keys(lib['lootify'].timers).length !== 0) {
  2098. $.each(lib['lootify'].timers, function(k, t) {
  2099. t.reset();
  2100. });
  2101. }
  2102. runs = [{}];
  2103. total = {};
  2104. app.disabled = false;
  2105. app.isNature = false;
  2106. app.setup = false;
  2107. $('.item-log-clone').remove();
  2108. }
  2109. $('.item-log-window').find('.drawer-setting-large.active:not(.clone)').click();
  2110. }
  2111. function reset(m = 'all') {
  2112. submitStats(getLogs(), lib.user.getEnchantment('32'));
  2113. clearLogs(m);
  2114. }
  2115. /* ############## GAME LIBRARY ############## */
  2116. function is() {
  2117. return lib.general.getStorage('Terms') === 'true';
  2118. }
  2119. function isLogged(type, txt) {
  2120. let includes = false;
  2121. if(getLogMobs()) {
  2122. $.each(getLogMobs(), function(key, mob) {
  2123. if (type === 'combat') {
  2124. if (lib.game.getZoneMobs(txt).includes(mob)) {
  2125. includes = true;
  2126. }
  2127. } else if (type === 'skill') {
  2128. if (txt == mob) {
  2129. includes = true;
  2130. }
  2131. }
  2132. });
  2133. }
  2134. return includes;
  2135. }
  2136. function isLogEmpty() {
  2137. return Object.keys(runs[0]).length === 0;
  2138. }
  2139. function getLogMobs() {
  2140. let mobs = [];
  2141. $.each(total, function(mob, info) {
  2142. mobs.push(mob);
  2143. });
  2144. return mobs;
  2145. }
  2146. function getPasteInterval() {
  2147. let interval = parseInt($('.AutoPasterInterval').val());
  2148. interval = interval ? interval : 30;
  2149. interval = interval < 15 ? 15 : interval;
  2150. interval = interval > 60 ? 60 : interval;
  2151. return interval;
  2152. }
  2153. /* ############## API INTERACTION ############## */
  2154. function setUserPublic(s) {
  2155. lib.app.getRequest(app.name, 'https://digimol.net/idlescape/assets/api.php?a=userpublic&id='+lib.user.id+'&state='+s);
  2156. }
  2157. function canSubmit(logs) {
  2158. let can = true;
  2159.  
  2160. if (lib.user.getEnchantment('7') > 0) {
  2161. can = false;
  2162. }
  2163. if (lib.user.getEnchantment('35') > 0 && lib.user.isStatus('foraging')) {
  2164. can = false;
  2165. }
  2166. if ((lib.user.isFighting() && logs.length < 1) || Object.keys(runs[0]).length === 0 || app.disabled) {
  2167. can = false;
  2168. }
  2169.  
  2170. return can;
  2171. }
  2172. function submitStats(logs, th) {
  2173. if (!canSubmit(logs)) {
  2174. return false;
  2175. }
  2176. lib.user.updateStatus();
  2177. let targetUrl = 'https://docs.google.com/forms/u/0/d/e/1FAIpQLSch3eG9Tqts0tIvnkk-C5JZeTwfbkWXhxkIpFnxyyaNO26h4Q/formResponse';
  2178. let logEntryId = 'entry.558332813';
  2179. let thEntryId = 'entry.22586929';
  2180. let noteEntryId = 'entry.1726819066';
  2181. let finalTH = (parseInt(th) > 0) ? 'TH '+th : 'None';
  2182. let fullUrl = targetUrl + '?'+ noteEntryId + '=Lootify-' + app.ver + '&' + thEntryId + '=' + encodeURIComponent(finalTH) + '&' + logEntryId + '=' + encodeURIComponent(logs.join('\n'));
  2183. if (!lib.user.isFighting()) {
  2184. th = 0;
  2185. } else {
  2186. $.get(fullUrl);
  2187. }
  2188. lib.app.getRequest(app.name, 'https://digimol.net/idlescape/assets/api.php?a=paste&th='+th+'&user='+lib.user.id+'&log='+encodeURIComponent(JSON.stringify(runs[0])));
  2189. lib.game.chat({
  2190. 'msg': 'Lootify - Loot log pasted!',
  2191. 'color': '#00a0fd',
  2192. });
  2193. }
  2194. app.getRuns = function(n = 0) {
  2195. return runs[n];
  2196. }
  2197.  
  2198. app.getKills = function() {
  2199. if (!lib.user.id || lib.user.id == 0) { return false; }
  2200. lib.app.getRequest(app.name, 'https://digimol.net/idlescape/assets/api.php?a=getkills&id='+lib.user.id, function(d) {
  2201. lib.user.kills = d;
  2202. });
  2203. }
  2204.  
  2205. /* ############## SOCKET RESPONSES ############## */
  2206. app.initExp = function(d) {
  2207. let e = setInterval(()=> {
  2208. if (app.ready) {
  2209. clearInterval(e);
  2210. $.each(lib.app.data.active.skills, function(k, skill) {
  2211. !lib.user.exp ? lib.user.exp = {} : 1;
  2212. !lib.user.mexp ? lib.user.mexp = {} : 1;
  2213. !lib.user.exp[skill] ? lib.user.exp[skill] = {} : 1;
  2214. lib.user.exp[skill].init = parseInt(d.value.skills[skill].experience);
  2215. lib.user.exp[skill].current = parseInt(d.value.skills[skill].experience);
  2216. !lib.user.mexp[skill] ? lib.user.mexp[skill] = {} : 1;
  2217. lib.user.mexp[skill].init = parseInt(d.value.skills[skill].masteryExperience);
  2218. lib.user.mexp[skill].current = parseInt(d.value.skills[skill].masteryExperience);
  2219. });
  2220. }
  2221. }, 250);
  2222. }
  2223.  
  2224. app.updateExp = function(msg) {
  2225. let d = JSON.parse(msg)[1];
  2226. if (app.ready) {
  2227. $.each(lib.app.data.active.skills, function(k, skill) {
  2228. !lib.user.exp ? lib.user.exp = {} : 1;
  2229. !lib.user.exp[skill] ? lib.user.exp[skill] = {} : 1;
  2230. !lib.user.mexp ? lib.user.mexp = {} : 1;
  2231. !lib.user.mexp[skill] ? lib.user.mexp[skill] = {} : 1;
  2232. if (~msg.indexOf('"'+skill+'"')) {
  2233. lib.user.exp[skill].current = parseInt(d.value.experience);
  2234. lib.user.mexp[skill].current = parseInt(d.value.masteryExperience);
  2235. }
  2236. });
  2237. }
  2238. }
  2239.  
  2240. /* USES
  2241. * d.item.name
  2242. * d.item.stackSize
  2243. */
  2244. app.addLogMob = function(d) {
  2245. if (lib.user.lastAction === 'craft') {
  2246. lib.user.lastAction = '';
  2247. return false;
  2248. }
  2249. if (lib.game.isAllowedItem(lib.user.getStatus(), d.item.name)) {
  2250. let loc = lib.game.getLocationName(lib.user.location);
  2251. let item = lib.user.getStockpile(d.item.name);
  2252. let diff = item ? d.item.stackSize - item.stackSize : d.item.stackSize;
  2253. if (diff >= 3 && d.item.name !== 'Gold') { return false; }
  2254. !runs[0] ? runs[0] = {} : 1;
  2255. !runs[0][loc] ? runs[0][loc] = {} : 1;
  2256. !runs[0][loc].count ? runs[0][loc].count = 0 : 1;
  2257. runs[0][loc].count++;
  2258. if(lib.user.isStatus('mining') && ~d.item.name.indexOf('Bar')) {
  2259. runs[0][loc].count--;
  2260. }
  2261. !runs[0][loc].loot ? runs[0][loc].loot = {} : 1;
  2262. !runs[0][loc].loot[d.item.name] ? runs[0][loc].loot[d.item.name] = {} : 1;
  2263. !runs[0][loc].loot[d.item.name].count ? runs[0][loc].loot[d.item.name].count = 0 : 1;
  2264. !runs[0][loc].loot[d.item.name].procs ? runs[0][loc].loot[d.item.name].procs = 0 : 1;
  2265. if(d.item.name == 'Gold' && lib.user.getEnchantment('7') > 0) {
  2266. runs[0][loc].loot[d.item.name].count += diff;
  2267. } else {
  2268. runs[0][loc].loot[d.item.name].count++;
  2269. diff >= 2 ? runs[0][loc].loot[d.item.name].procs++ : 1;
  2270. }
  2271. }
  2272. }
  2273.  
  2274. app.addLogChest = function(d) {
  2275. !runs[0] ? runs[0] = {} : 1;
  2276. !runs[0][d.chest.name] ? runs[0][d.chest.name] = {} : 1;
  2277. !runs[0][d.chest.name].count ? runs[0][d.chest.name].count = 0 : 1;
  2278. runs[0][d.chest.name].count += d.chest.count;
  2279. $.each(d.loot, function(loot, count) {
  2280. !runs[0][d.chest.name].loot ? runs[0][d.chest.name].loot = {} : 1;
  2281. !runs[0][d.chest.name].loot[loot] ? runs[0][d.chest.name].loot[loot] = {} : 1;
  2282. !runs[0][d.chest.name].loot[loot].count ? runs[0][d.chest.name].loot[loot].count = 0 : 1;
  2283. runs[0][d.chest.name].loot[loot].count += count;
  2284. });
  2285. }
  2286.  
  2287. app.disablePaster = function() {
  2288. if (!app.disabled) {
  2289. app.disabled = true;
  2290. $('.drawer-setting-large.clone').text('Reset Log');
  2291. lib.game.chat({
  2292. 'msg': "Lootify - Paster has been disabled to prevent skewed statistics. You can still use Lootify, but won't be able to paste your data to our server. Please reset your log to enable the paster again!",
  2293. 'color': 'red',
  2294. 'date': false
  2295. });
  2296. }
  2297. }
  2298.  
  2299. lib.app.setCommand('killcount', function() {
  2300. let e = setInterval(()=> {
  2301. if(lib.user.kills) {
  2302. clearInterval(e);
  2303. let msg = '';
  2304. let $html = $('<div>');
  2305. let $wrap = $('<div>',{
  2306. 'class': 'ltf-killcount-wrap'
  2307. }).appendTo($html);
  2308. $.each(lib.user.kills, function(mob, count) {
  2309. let $killcount = $('<div>', {
  2310. 'class': 'ltf-killcount'
  2311. }).appendTo($wrap);
  2312. let $icon = $('<img>', {
  2313. 'class': 'dialog-icon ltf-killcount-icon'
  2314. }).attr({
  2315. 'src': lib.game.getMobIcon(mob)
  2316. }).appendTo($killcount);
  2317. let $text = $('<span>', {
  2318. 'class': 'dialog-text-medium lft-killcount-text'
  2319. }).html(mob+': '+count).appendTo($killcount);
  2320. });
  2321. $('<div>', {
  2322. 'class': 'dialog-disclaimer'
  2323. }).text('These are statistics submitted to Lootify only (updates every 15 minutes)').appendTo($html);
  2324. msg += $html.html();
  2325.  
  2326. lib.game.dialog({
  2327. 'title': lib.user.name+' Kill Count',
  2328. 'text': msg,
  2329. 'class': 'ltf-killcount',
  2330. 'type': 'close',
  2331. });
  2332. }
  2333. setTimeout(()=> {
  2334. clearInterval(e);
  2335. }, 3000);
  2336. }, 250);
  2337. });
  2338. }
  2339.  
  2340. })();
  2341.  
  2342. function includeCSS(file) {
  2343. var style = document.createElement('link');
  2344. style.rel = 'stylesheet';
  2345. style.href = file;
  2346. style.type = 'text/css';
  2347.  
  2348. document.getElementsByTagName('head').item(0).appendChild(style);
  2349. }
  2350. includeCSS('https://digimol.net/idlescape/assets/css/game.css');