Melvor Snippets

Collection of various snippets

  1. // ==UserScript==
  2. // @name Melvor Snippets
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.0.18
  5. // @description Collection of various snippets
  6. // @grant none
  7. // @author GMiclotte
  8. // @include https://melvoridle.com/*
  9. // @include https://*.melvoridle.com/*
  10. // @exclude https://melvoridle.com/index.php
  11. // @exclude https://*.melvoridle.com/index.php
  12. // @exclude https://wiki.melvoridle.com/*
  13. // @exclude https://*.wiki.melvoridle.com/*
  14. // @inject-into page
  15. // @noframes
  16. // @grant none
  17. // ==/UserScript==
  18.  
  19. ((main) => {
  20. const script = document.createElement('script');
  21. script.textContent = `try { (${main})(); } catch (e) { console.log(e); }`;
  22. document.body.appendChild(script).parentNode.removeChild(script);
  23. })(() => {
  24.  
  25. function startSnippets() {
  26.  
  27. window.snippet = {
  28. name: '',
  29. log: (...args) => console.log('Snippets:', ...args),
  30. start: () => snippet.log(`Loading ${snippet.name}.`),
  31. end: () => snippet.log(`Loaded ${snippet.name}.`),
  32. };
  33.  
  34. // header end
  35.  
  36. /////////////////////////////////////
  37. //AgilityObstacleBuildsRemaining.js//
  38. /////////////////////////////////////
  39. snippet.name = 'AgilityObstacleBuildsRemaining.js';
  40. snippet.start();
  41. // show agility obstacles that have been built less than 10 times
  42. window.listObstaclesWithFewerThanTenBuilds = () => {
  43. agilityObstacleBuildCount.map((_, i) => i)
  44. .filter(i => agilityObstacleBuildCount[i] < 10)
  45. .map(i => agilityObstacles[i])
  46. .map(x => [x.category + 1, x.name]);
  47. }
  48. snippet.end();
  49.  
  50. ///////////////////
  51. //BankedHealth.js//
  52. ///////////////////
  53. snippet.name = 'BankedHealth.js';
  54. snippet.start();
  55. // return total healing in bank
  56. window.bankedHealth = () => {
  57. return items.filter(x => x.healsFor)
  58. .map(x => player.getFoodHealing(x) * combatManager.bank.getQty(x.id))
  59. .reduce((a, b) => a + b, 0);
  60. }
  61. snippet.end();
  62.  
  63. //////////////////
  64. //DefensePure.js//
  65. //////////////////
  66. snippet.name = 'DefensePure.js';
  67. snippet.start();
  68. // Various Defense Pure Calculations
  69. window.defensePure = {};
  70.  
  71. defensePure.defLvlToHPLvl = def => {
  72. const hpXP = exp.level_to_xp(10) + 1;
  73. const minDefXP = exp.level_to_xp(def) + 1;
  74. const maxDefXP = exp.level_to_xp(def + 1);
  75. const minHpXP = hpXP + minDefXP / 3;
  76. const maxHpXP = hpXP + maxDefXP / 3;
  77. const minHp = exp.xp_to_level(minHpXP) - 1;
  78. const maxHp = exp.xp_to_level(maxHpXP) - 1;
  79. return {min: minHp, max: maxHp};
  80. }
  81.  
  82. defensePure.defLvlToCbLvl = def => {
  83. const hp = defensePure.defLvlToHPLvl(def);
  84. const att = 1, str = 1, ran = 1, mag = 1, pray = 1;
  85. const minBase = (def + hp.min + Math.floor(pray / 2)) / 4;
  86. const maxBase = (def + hp.max + Math.floor(pray / 2)) / 4;
  87. const melee = (att + str) * 1.3 / 8;
  88. const ranged = Math.floor(1.5 * ran) * 1.3 / 8;
  89. const magic = Math.floor(1.5 * mag) * 1.3 / 8;
  90. const best = Math.max(melee, ranged, magic);
  91. return {min: minBase + best, max: maxBase + best};
  92. }
  93.  
  94. defensePure.lastHitOnly = (skillID, maxLevel = 1) => {
  95. if (skillXP[skillID] >= exp.level_to_xp(maxLevel + 1) - 1) {
  96. combatManager.stopCombat();
  97. return;
  98. }
  99. // swap weapon based on hp left
  100. let itemID;
  101. if (combatManager.enemy.hitpoints > 1) {
  102. if (skillID === Skills.Magic) {
  103. itemID = Items.Normal_Shortbow;
  104. } else {
  105. // melee or ranged
  106. itemID = Items.Staff_of_Air;
  107. }
  108. } else {
  109. if (skillID === Skills.Ranged) {
  110. itemID = Items.Iron_Throwing_Knife;
  111. } else if (skillID === Skills.Magic) {
  112. itemID = Items.Staff_of_Air;
  113. } else {
  114. // melee
  115. itemID = -1;
  116. }
  117. }
  118. if (player.equipment.slots.Weapon.item.id !== itemID) {
  119. if (itemID === -1) {
  120. player.unequipItem(0, 'Weapon');
  121. } else {
  122. player.equipItem(itemID, 0);
  123. }
  124. }
  125. // loop
  126. setTimeout(() => defensePure.lastHitOnly(skillID, maxLevel), 1000);
  127. }
  128. snippet.end();
  129.  
  130. /////////////////////////
  131. //GetLocalisationKey.js//
  132. /////////////////////////
  133. snippet.name = 'GetLocalisationKey.js';
  134. snippet.start();
  135. // Get Localisation Key for a given string
  136. window.getLocalisationKey = (text) => {
  137. const list = []
  138. for (const key in loadedLangJson) {
  139. for (const identifier in loadedLangJson[key]) {
  140. if (loadedLangJson[key][identifier] === text) {
  141. list.push({key: key, identifier: identifier});
  142. }
  143. }
  144. }
  145. return list;
  146. }
  147. snippet.end();
  148.  
  149. //////////////////////
  150. //ListRaidUnlocks.js//
  151. //////////////////////
  152. snippet.name = 'ListRaidUnlocks.js';
  153. snippet.start();
  154. // list unlocked raid items
  155. window.listCrateItems = (unlocked = true) =>
  156. RaidManager.crateItemWeights.filter(x =>
  157. unlocked === game.golbinRaid.ownedCrateItems.has(x.itemID)
  158. ).forEach(x =>
  159. snippet.log(items[x.itemID].name)
  160. );
  161. // to list the ones you have unlocked:
  162. // listCrateItems()
  163. // to list the ones you haven't unlocked:
  164. // listCrateItems(false)
  165. snippet.end();
  166.  
  167. ////////////////
  168. //LootDrops.js//
  169. ////////////////
  170. snippet.name = 'LootDrops.js';
  171. snippet.start();
  172. // Loot Drops
  173. window.lootDrops = () => {
  174. const loot = combatManager.loot;
  175. // only loot when the loot table is full
  176. if (loot.drops.length < loot.maxLoot) {
  177. return;
  178. }
  179. // when the bank is full, update the bank cache
  180. const bankFull = bank.length === getMaxBankSpace();
  181. if (bankFull) {
  182. for (let i = 0; i < bank.length; i++) {
  183. bankCache[bank[i].id] = i;
  184. }
  185. }
  186. loot.drops = loot.drops.filter(drop => {
  187. const itemID = drop.item.id;
  188. if (bankFull) {
  189. // reject all items that aren't in the bank cache
  190. if (bankCache[itemID] === undefined) {
  191. return false;
  192. }
  193. }
  194. if (addItemToBank(itemID, drop.qty))
  195. game.stats.Combat.add(CombatStats.ItemsLooted, drop.qty);
  196. return false;
  197. });
  198. }
  199.  
  200. // hook to player.rewardGPForKill, this runs on player death and is a relatively small method
  201. eval(player.rewardGPForKill.toString().replaceAll(
  202. 'this',
  203. 'player',
  204. ).replace(
  205. 'rewardGPForKill(){',
  206. 'window.rewardGPForKill = () => {window.lootDrops();',
  207. ));
  208.  
  209. window.hookLootDrops = () => {
  210. if (player) {
  211. player.rewardGPForKill = window.rewardGPForKill;
  212. } else {
  213. setTimeout(window.hookLootDrops, 50);
  214. }
  215. }
  216.  
  217. // window.hookLootDrops();
  218. snippet.end();
  219.  
  220. //////////////////
  221. //MasteryBars.js//
  222. //////////////////
  223. snippet.name = 'MasteryBars.js';
  224. snippet.start();
  225. // Add Mastery Bars
  226. setInterval(() => {
  227. for (const id in SKILLS) {
  228. if (SKILLS[id].hasMastery) {
  229. if ($(`#skill-nav-mastery-${id} .progress-bar`)[0]) {
  230. $(`#skill-nav-mastery-${id} .progress-bar`)[0].style.width =
  231. (MASTERY[id].pool / getMasteryPoolTotalXP(id)) * 100 + '%';
  232. if (MASTERY[id].pool < getMasteryPoolTotalXP(id)) {
  233. $(`#skill-nav-mastery-${id}`)[0].style.setProperty('background', 'rgb(76,80,84)', 'important');
  234. $(`#skill-nav-mastery-${id} .progress-bar`)[0].className = 'progress-bar bg-warning';
  235. } else {
  236. $(`#skill-nav-mastery-${id}`)[0].style.setProperty('background', 'rgb(48,199,141)', 'success');
  237. $(`#skill-nav-mastery-${id} .progress-bar`)[0].className = 'progress-bar bg-success';
  238. }
  239. const tip = $(`#skill-nav-mastery-${id}`)[0]._tippy;
  240. tip.setContent((Math.min(1, MASTERY[id].pool / getMasteryPoolTotalXP(id)) * 100).toFixed(2) + '%');
  241. } else {
  242. const skillItem = $(`#skill-nav-name-${id}`)[0].parentNode;
  243. skillItem.style.flexWrap = 'wrap';
  244. skillItem.style.setProperty('padding-top', '.25rem', 'important');
  245. const progress = document.createElement('div');
  246. const progressBar = document.createElement('div');
  247. progress.id = `skill-nav-mastery-${id}`;
  248. progress.className = 'progress active pointer-enabled';
  249. progress.style.height = '6px';
  250. progress.style.width = '100%';
  251. progress.style.margin = '.25rem 0rem';
  252. if (MASTERY[id].pool < getMasteryPoolTotalXP(id)) {
  253. progress.style.setProperty('background', 'rgb(76,80,84)', 'important');
  254. progressBar.className = 'progress-bar bg-warning';
  255. } else {
  256. progress.style.setProperty('background', 'rgb(48,199,141)', 'success');
  257. progressBar.className = 'progress-bar bg-success';
  258. }
  259. progressBar.style.width = (MASTERY[id].pool / getMasteryPoolTotalXP(id)) * 100 + '%';
  260. progress.appendChild(progressBar);
  261. skillItem.appendChild(progress);
  262. tippy($(`#skill-nav-mastery-${id}`)[0], {
  263. placement: 'right',
  264. content: ((MASTERY[id].pool / getMasteryPoolTotalXP(id)) * 100).toFixed(2) + '%',
  265. });
  266. }
  267. }
  268. }
  269. }, 5000);
  270. snippet.end();
  271.  
  272. ///////////////////
  273. //MasteryBuyer.js//
  274. ///////////////////
  275. snippet.name = 'MasteryBuyer.js';
  276. snippet.start();
  277. // methods to buy base mastery levels
  278. window.masteryBuyer = {
  279. poolXpPerItem: 500000,
  280. };
  281.  
  282. masteryBuyer.availXp = (skillID, minPercent = 95) => {
  283. let minPool = MASTERY[skillID].xp.length * masteryBuyer.poolXpPerItem * minPercent / 100;
  284. return MASTERY[skillID].pool - minPool;
  285. }
  286.  
  287. masteryBuyer.currentBase = (skillID) => {
  288. return Math.min(...MASTERY[skillID].xp.map((_, masteryID) => getMasteryLevel(skillID, masteryID)));
  289. }
  290.  
  291. masteryBuyer.maxAffordableBase = (skillID, minPercent = 95) => {
  292. let xp = masteryBuyer.availXp(skillID, minPercent);
  293. // make bins with mastery levels
  294. let bins = [];
  295. for (let i = 0; i < 100; i++) {
  296. bins[i] = [];
  297. }
  298. MASTERY[skillID].xp.forEach((_, masteryID) => {
  299. let level = getMasteryLevel(skillID, masteryID);
  300. bins[level].push(masteryID);
  301. });
  302. // level one at a time
  303. let maxBase = 0;
  304. bins.forEach((x, i) => {
  305. if (i >= 99) {
  306. return;
  307. }
  308. if (x.length === 0) {
  309. return;
  310. }
  311. let xpRequired = (exp.level_to_xp(i + 1) - exp.level_to_xp(i)) * x.length;
  312. xp -= xpRequired;
  313. if (xp >= 0) {
  314. maxBase = i + 1;
  315. x.forEach(y => bins[i + 1].push(y));
  316. }
  317. });
  318. maxBase = maxBase > 99 ? 99 : maxBase;
  319. return maxBase;
  320. }
  321.  
  322. masteryBuyer.increaseBase = (skillID, minPercent = 95, levelCap = 99) => {
  323. // buy until goal
  324. let goal = masteryBuyer.maxAffordableBase(skillID, minPercent);
  325. if (goal === 0) {
  326. goal = masteryBuyer.currentBase(skillID);
  327. }
  328. if (goal > levelCap) {
  329. goal = levelCap;
  330. }
  331. MASTERY[skillID].xp.forEach((_, masteryID) => {
  332. let level = getMasteryLevel(skillID, masteryID);
  333. if (level >= goal) {
  334. return;
  335. }
  336. masteryPoolLevelUp = goal - level;
  337. levelUpMasteryWithPool(skillID, masteryID);
  338. });
  339. // spend remainder on goal + 1
  340. const xpRequired = exp.level_to_xp(goal + 1) - exp.level_to_xp(goal);
  341. let count = Math.floor(masteryBuyer.availXp(skillID, minPercent) / xpRequired);
  342. masteryPoolLevelUp = 1;
  343. MASTERY[skillID].xp.forEach((_, masteryID) => {
  344. if (count === 0) {
  345. return;
  346. }
  347. let level = getMasteryLevel(skillID, masteryID);
  348. if (level > goal || level >= levelCap) {
  349. return;
  350. }
  351. count--;
  352. levelUpMasteryWithPool(skillID, masteryID);
  353. });
  354. // update total mastery
  355. updateTotalMastery(skillID);
  356. }
  357.  
  358. masteryBuyer.overview = (minPercent = 95) => {
  359. Object.getOwnPropertyNames(SKILLS).forEach(skillID => {
  360. const skill = SKILLS[skillID];
  361. if (!skill.hasMastery) {
  362. return;
  363. }
  364. const maxBase = masteryBuyer.maxAffordableBase(skillID, minPercent);
  365. if (maxBase === 0) {
  366. return;
  367. }
  368. const currentBase = masteryBuyer.currentBase(skillID);
  369. snippet.log(`${skill.name}: ${currentBase} -> ${maxBase}`);
  370. });
  371. }
  372.  
  373. masteryBuyer.remaining = (skillID, target = 99) => {
  374. let xp = 0;
  375. let xpTarget = exp.level_to_xp(target);
  376. MASTERY[skillID].xp.forEach(masteryXp => {
  377. xp += Math.max(0, xpTarget - masteryXp);
  378. });
  379. xp = Math.round(xp)
  380. snippet.log(formatNumber(xp))
  381. return xp
  382. }
  383. snippet.end();
  384.  
  385. ///////////////////////
  386. //PrintSynergyList.js//
  387. ///////////////////////
  388. snippet.name = 'PrintSynergyList.js';
  389. snippet.start();
  390. // functions to print synergies per category (cb vs non-cb)
  391. window.printSynergy = (x, y) => snippet.log('- [ ]',
  392. x.summoningID,
  393. parseInt(y),
  394. items[x.itemID].name,
  395. items[summoningItems[y].itemID].name,
  396. SUMMONING.Synergies[x.summoningID][y].description,
  397. SUMMONING.Synergies[x.summoningID][y].modifiers
  398. );
  399.  
  400. window.printCombatSynergyList = () => {
  401. // get combat synergies
  402. summoningItems.filter(x => items[x.itemID].summoningMaxHit).map(x => {
  403. for (y in SUMMONING.Synergies[x.summoningID]) {
  404. printSynergy(x, y);
  405. }
  406. });
  407. }
  408.  
  409. window.printNonCombatSynergyList = () => {
  410. // get non-combat synergies
  411. summoningItems.filter(x => !items[x.itemID].summoningMaxHit).map(x => {
  412. for (y in SUMMONING.Synergies[x.summoningID]) {
  413. printSynergy(x, y);
  414. }
  415. });
  416. }
  417. snippet.end();
  418.  
  419. /////////////////////
  420. //QuickEquipCape.js//
  421. /////////////////////
  422. snippet.name = 'QuickEquipCape.js';
  423. snippet.start();
  424. // Quick Equip Max/Comp Cape
  425. window.quickEquipSkillcape = (skill) => {
  426. const capes = [
  427. Items.Cape_of_Completion,
  428. Items.Max_Skillcape,
  429. skillcapeItems[skill],
  430. ];
  431. for (let i = 0; i < capes.length; i++) {
  432. const capeId = capes[i];
  433. if (player.equipment.checkForItemID(capeId)) {
  434. notifyPlayer(skill, `${items[capeId].name} is already equipped.`, "info");
  435. return;
  436. }
  437. const bankId = getBankId(capeId);
  438. if (bankId === -1) {
  439. continue;
  440. }
  441. if (!player.equipItem(capeId, player.selectedEquipmentSet)) {
  442. continue;
  443. }
  444. notifyPlayer(skill, `${items[capeId].name} Equipped.`, "success");
  445. if (skill === 0) {
  446. updateWCRates();
  447. }
  448. return;
  449. }
  450. notifyPlayer(skill, "There's no " + setToUppercase(Skills[skill]) + " Skillcape in your bank *shrug*", "danger");
  451. }
  452. snippet.end();
  453.  
  454. ////////////////////
  455. //ReclaimTokens.js//
  456. ////////////////////
  457. snippet.name = 'ReclaimTokens.js';
  458. snippet.start();
  459. // reclaim tokens
  460. window.reclaimMasteryTokens = () => {
  461. skillXP.forEach((_, s) => {
  462. if (MASTERY[s] === undefined) {
  463. return;
  464. }
  465. const id = Items['Mastery_Token_' + Skills[s]];
  466. const p = Math.floor((MASTERY[s].pool - getMasteryPoolTotalXP(s) ) / Math.floor(getMasteryPoolTotalXP(s)*0.001));
  467. const m = game.stats.Items.statsMap.get(id).stats.get(ItemStats.TimesFound);
  468. const o = getBankQty(id);
  469. const a = Math.min(p, m - o);
  470. const b = getBankId(id);
  471. if (a > 0 && b >= 0) {
  472. bank[b].qty += a;
  473. MASTERY[s].pool -= a * Math.floor(getMasteryPoolTotalXP(s)*0.001);
  474. snippet.log('reclaimed', a, Skills[s], 'tokens');
  475. }
  476. });
  477. }
  478.  
  479. snippet.end();
  480.  
  481. /////////////////////
  482. //RemoveElements.js//
  483. /////////////////////
  484. snippet.name = 'RemoveElements.js';
  485. snippet.start();
  486. // remove various elements
  487. // combat
  488. document.getElementById('offline-combat-alert').remove();
  489.  
  490. // summoning marks
  491. // green
  492. document.getElementById('summoning-category-0').children[0].children[0].children[2].remove();
  493. // orange and red
  494. document.getElementById('summoning-category-0').children[0].children[0].children[1].remove();
  495.  
  496. // summoning tablets
  497. document.getElementById('summoning-category-1').children[0].children[0].children[0].remove()
  498.  
  499. // alt. magic
  500. document.getElementById('magic-container').children[0].children[1].remove();
  501.  
  502. // cloud saving
  503. document.getElementById('header-cloud-save-time').remove();
  504. document.getElementById('header-cloud-save-btn-connected').remove();
  505. snippet.end();
  506.  
  507. /////////////////////////
  508. //RerollJuniorFarmer.js//
  509. /////////////////////////
  510. snippet.name = 'RerollJuniorFarmer.js';
  511. snippet.start();
  512. // automate rerolling and attacking of Junior Farmer
  513. window.rerollJuniorFarmer = () => {
  514. // rewardGPForKill loots drops and rerolls slayer task
  515. eval(player.rewardGPForKill.toString().replaceAll(
  516. 'this',
  517. 'player',
  518. ).replace(
  519. 'rewardGPForKill(){',
  520. 'window.rewardGPForKill = () => {' +
  521. 'window.lootDrops();' +
  522. 'window.rerollSlayerTaskFast([Monsters.JuniorFarmer], 0, false);',
  523. ));
  524. player.rewardGPForKill = window.rewardGPForKill;
  525.  
  526. // process death restarts fight
  527. let checkDeath = combatManager.checkDeath.toString().slice(0,-1); // remove closing curly brace
  528. checkDeath += 'if (playerDied) {' +
  529. 'combatManager.selectMonster(Monsters.JuniorFarmer, getMonsterArea(Monsters.JuniorFarmer));' +
  530. 'snippet.log("player death: new fight initiated");' +
  531. '}';
  532. checkDeath += '}'; // add closing curly brace
  533. eval(checkDeath.replaceAll(
  534. 'this',
  535. 'combatManager',
  536. ).replace(
  537. 'checkDeath(){',
  538. 'window.checkDeath = () => {',
  539. ));
  540. combatManager.checkDeath = window.checkDeath;
  541. }
  542.  
  543. // window.rerollJuniorFarmer();
  544. snippet.end();
  545.  
  546. ///////////////////
  547. //RerollSlayer.js//
  548. ///////////////////
  549. snippet.name = 'RerollSlayer.js';
  550. snippet.start();
  551. //reroll slayer task until desired task is met
  552. window.rerollSlayerTask = (monsterIDs, tier, extend = true, loop = true) => {
  553. if (window.stopRerolling) {
  554. return;
  555. }
  556. const task = combatManager.slayerTask;
  557. const taskID = task.monster.id;
  558. const taskName = MONSTERS[taskID].name;
  559. if (!combatManager.slayerTask.taskTimer.active) {
  560. // only do something if slayer task timer is not running
  561. if (!combatManager.slayerTask.active || !monsterIDs.includes(taskID)) {
  562. // roll task if we don't have one, or if it has the wrong monster
  563. snippet.log(`rerolling ${taskName} for tier ${tier} task ${monsterIDs.map(monsterID => MONSTERS[monsterID].name).join(', ')}`);
  564. combatManager.slayerTask.selectTask(tier, true, true, false);
  565. } else if (extend && !task.extended) {
  566. // extend task if it is the right monster
  567. snippet.log(`extending ${taskName}`);
  568. combatManager.slayerTask.extendTask();
  569. }
  570. }
  571. if (loop) {
  572. setTimeout(() => rerollSlayerTask(monsterIDs, tier, extend), 1000);
  573. }
  574. }
  575.  
  576. // simulate rerolling of slayer task until desired task is met
  577. window.rerollSlayerTaskFast = (monsterIDs, tier, extend = true, verbose = false) => {
  578. const task = combatManager.slayerTask;
  579. if (task.taskTimer.active) {
  580. return;
  581. }
  582. // only do something if slayer task timer is not running
  583. if (task.active && monsterIDs.includes(task.monster.id)) {
  584. if (extend && !task.extended) {
  585. // extend task if it is the right monster
  586. if (verbose) {
  587. snippet.log(`extending ${MONSTERS[task.monster.id].name}`);
  588. }
  589. task.extendTask();
  590. }
  591. return;
  592. }
  593. // roll task if we don't have one, or if it has the wrong monster
  594. const monsterSelection = task.getMonsterSelection(tier).map(x => x.id);
  595. const monsterSelectionMap = {};
  596. monsterSelection.forEach(x => monsterSelectionMap[x] = true);
  597. monsterIDs = monsterIDs.filter(x => monsterSelectionMap[x]);
  598. if (monsterIDs.length === 0) {
  599. snippet.log(`no valid monsterIDs provided for tier ${tier}`);
  600. return;
  601. }
  602. // simulate rerolls until one of the target monsters is rolled
  603. let rerolls = 1;
  604. const prob = monsterIDs.length / monsterSelection.length;
  605. while (Math.random() > prob) {
  606. rerolls++;
  607. }
  608. let scAmount = 0;
  609. if (tier > 0) {
  610. scAmount = SlayerTask.data[tier].cost * rerolls;
  611. if (scAmount > player._slayercoins) {
  612. snippet.log(`insufficient slayer coins, needed ${scAmount}, have ${player._slayercoins}`);
  613. return;
  614. }
  615. task.player.removeSlayerCoins(scAmount, true);
  616. }
  617. // randomly pick one of the valid monsters
  618. const monsterID = monsterIDs[rollInteger(0, monsterIDs.length - 1)];
  619. // mimic task.selectTask
  620. task.monster = MONSTERS[monsterID];
  621. task.tier = tier;
  622. task.active = true;
  623. task.extended = false;
  624. task.killsLeft = task.getTaskLength(tier);
  625. task.renderRequired = true;
  626. task.renderNewButton = true;
  627. if (verbose) {
  628. snippet.log(`simulated ${rerolls} rerolls for tier ${tier} task ${MONSTERS[monsterID].name} costing ${scAmount}SC`);
  629. }
  630. }
  631. snippet.end();
  632.  
  633. /////////////////
  634. //ShardsUsed.js//
  635. /////////////////
  636. snippet.name = 'ShardsUsed.js';
  637. snippet.start();
  638. // compute total shards used
  639. window.shardsUsed = () => {
  640. // compute amount of gp spent on summoning shards that have been used (for summoning or agility obstacles)
  641. items.map((x, i) => [x, i])
  642. .filter(x => x[0].type === 'Shard' && x[0].category === 'Summoning')
  643. .map(x => x[1])
  644. .map(x => (itemStats[x].stats[0] - getBankQty(x) - itemStats[x].stats[1]) * items[x].buysFor)
  645. .reduce((a, b) => a + b, 0);
  646. }
  647. snippet.end();
  648.  
  649. ///////////////////
  650. //SpawnAhrenia.js//
  651. ///////////////////
  652. snippet.name = 'SpawnAhrenia.js';
  653. snippet.start();
  654. // spawn Ahrenia
  655. window.spawnAhrenia = (phaseToSpawn = 1) => {
  656. // run
  657. combatManager.runCombat();
  658. // set respawn to 0
  659. if (!petUnlocked[0]) {
  660. unlockPet(0);
  661. }
  662. PETS[0].modifiers.decreasedMonsterRespawnTimer = 0;
  663. player.computeAllStats();
  664. PETS[0].modifiers.decreasedMonsterRespawnTimer = 3000 - TICK_INTERVAL - player.modifiers.decreasedMonsterRespawnTimer + player.modifiers.increasedMonsterRespawnTimer;
  665. player.computeAllStats();
  666. // unlock itm
  667. dungeonCompleteCount[Dungeons.Fire_God_Dungeon] = Math.max(
  668. dungeonCompleteCount[Dungeons.Fire_God_Dungeon],
  669. 1,
  670. );
  671. skillLevel[Skills.Slayer] = Math.max(
  672. skillLevel[Skills.Slayer],
  673. 90,
  674. );
  675. // skip to desired phase
  676. combatManager.selectDungeon(15);
  677. combatManager.dungeonProgress = 19 + phaseToSpawn;
  678. combatManager.loadNextEnemy();
  679. }
  680. snippet.end();
  681.  
  682. ////////////////////
  683. //UnlimitedPool.js//
  684. ////////////////////
  685. snippet.name = 'UnlimitedPool.js';
  686. snippet.start();
  687. // don't cap pool xp
  688. eval(addMasteryXPToPool.toString()
  689. .replace('MASTERY[skill].pool>getMasteryPoolTotalXP(skill)', 'false')
  690. .replace(/^function (\w+)/, "window.$1 = function")
  691. );
  692.  
  693. // don't cap token claiming
  694. eval(claimToken.toString()
  695. .replace('qty>=tokensToFillPool', 'false')
  696. .replace(/^function (\w+)/, "window.$1 = function")
  697. );
  698. snippet.end();
  699.  
  700. /////////////
  701. //Unsell.js//
  702. /////////////
  703. snippet.name = 'Unsell.js';
  704. snippet.start();
  705. // unsell sold items
  706. window.unsell = (id, count = Infinity) => {
  707. if (count < 0) {
  708. return;
  709. }
  710. const timesSold = game.stats.Items.get(id, ItemStats.TimesSold);
  711. const gpFromSales = game.stats.Items.get(id, ItemStats.GpFromSale);
  712. if (timesSold === 0) {
  713. snippet.log("zero times sold");
  714. return;
  715. }
  716. // check if transaction is affordable
  717. const times = Math.min(count, timesSold);
  718. const cost = Math.ceil(gpFromSales / timesSold * times);
  719. if (gp < cost) {
  720. snippet.log("can't afford: " + times + " costs " + cost + " have " + gp);
  721. return;
  722. }
  723. // add item
  724. if (times > 0) {
  725. addItemToBank(id, times);
  726. }
  727. game.stats.Items.add(id, ItemStats.TimesFound, -times);
  728. game.stats.Items.add(id, ItemStats.TimesSold, -times);
  729. // remove cost
  730. gp = Math.floor(gp - cost);
  731. game.stats.Items.add(id, ItemStats.GpFromSale, -cost);
  732. updateGP();
  733. // fix statistics
  734. game.stats.General.add(GeneralStats.TotalItemsSold, -times);
  735. updateBank();
  736. // log transaction
  737. snippet.log("bought " + times + " for " + cost);
  738. }
  739. snippet.end();
  740.  
  741. // footer start
  742. }
  743.  
  744. function loadScript() {
  745. if (typeof isLoaded !== typeof undefined && isLoaded) {
  746. // Only load script after game has opened
  747. clearInterval(scriptLoader);
  748. startSnippets();
  749. }
  750. }
  751.  
  752. const scriptLoader = setInterval(loadScript, 200);
  753. });