Melvor Snippets

Collection of various snippets

当前为 2022-02-01 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Melvor Snippets
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.0.13
  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. const 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. 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. //DefensePure.js//
  52. //////////////////
  53. snippet.name = 'DefensePure.js';
  54. snippet.start();
  55. // Various Defense Pure Calculations
  56. window.defensePure = {};
  57.  
  58. defensePure.defLvlToHPLvl = def => {
  59. const hpXP = exp.level_to_xp(10) + 1;
  60. const minDefXP = exp.level_to_xp(def) + 1;
  61. const maxDefXP = exp.level_to_xp(def + 1);
  62. const minHpXP = hpXP + minDefXP / 3;
  63. const maxHpXP = hpXP + maxDefXP / 3;
  64. const minHp = exp.xp_to_level(minHpXP) - 1;
  65. const maxHp = exp.xp_to_level(maxHpXP) - 1;
  66. return {min: minHp, max: maxHp};
  67. }
  68.  
  69. defensePure.defLvlToCbLvl = def => {
  70. const hp = defensePure.defLvlToHPLvl(def);
  71. const att = 1, str = 1, ran = 1, mag = 1, pray = 1;
  72. const minBase = (def + hp.min + Math.floor(pray / 2)) / 4;
  73. const maxBase = (def + hp.max + Math.floor(pray / 2)) / 4;
  74. const melee = (att + str) * 1.3 / 8;
  75. const ranged = Math.floor(1.5 * ran) * 1.3 / 8;
  76. const magic = Math.floor(1.5 * mag) * 1.3 / 8;
  77. const best = Math.max(melee, ranged, magic);
  78. return {min: minBase + best, max: maxBase + best};
  79. }
  80.  
  81. defensePure.lastHitOnly = (skillID, maxLevel = 1) => {
  82. if (skillXP[skillID] >= exp.level_to_xp(maxLevel + 1) - 1) {
  83. combatManager.stopCombat();
  84. return;
  85. }
  86. // swap weapon based on hp left
  87. let itemID;
  88. if (combatManager.enemy.hitpoints > 1) {
  89. if (skillID === Skills.Magic) {
  90. itemID = Items.Normal_Shortbow;
  91. } else {
  92. // melee or ranged
  93. itemID = Items.Staff_of_Air;
  94. }
  95. } else {
  96. if (skillID === Skills.Ranged) {
  97. itemID = Items.Iron_Throwing_Knife;
  98. } else if (skillID === Skills.Magic) {
  99. itemID = Items.Staff_of_Air;
  100. } else {
  101. // melee
  102. itemID = -1;
  103. }
  104. }
  105. if (player.equipment.slots.Weapon.item.id !== itemID) {
  106. if (itemID === -1) {
  107. player.unequipItem(0, 'Weapon');
  108. } else {
  109. player.equipItem(itemID, 0);
  110. }
  111. }
  112. // loop
  113. setTimeout(() => defensePure.lastHitOnly(skillID, maxLevel), 1000);
  114. }
  115. snippet.end();
  116.  
  117. /////////////////////////////
  118. //FixPerformSkillProcess.js//
  119. /////////////////////////////
  120. snippet.name = 'FixPerformSkillProcess.js';
  121. snippet.start();
  122. // fix perform skill process
  123. eval(performSkillProcess.toString().replace(
  124. 'if(!confirmedAdded&&!offline)return false;',
  125. 'if (!confirmedAdded && !ignoreBankFull && !offline) return false;'
  126. ).replace(
  127. /^function (\w+)/,
  128. 'window.$1 = function'
  129. ));
  130. snippet.end();
  131.  
  132. /////////////////////////
  133. //GetLocalisationKey.js//
  134. /////////////////////////
  135. snippet.name = 'GetLocalisationKey.js';
  136. snippet.start();
  137. // Get Localisation Key for a given string
  138. getLocalisationKey = (text) => {
  139. const list = []
  140. for (const key in loadedLangJson) {
  141. for (const identifier in loadedLangJson[key]) {
  142. if (loadedLangJson[key][identifier] === text) {
  143. list.push({key: key, identifier: identifier});
  144. }
  145. }
  146. }
  147. return list;
  148. }
  149. snippet.end();
  150.  
  151. //////////////////////
  152. //ListRaidUnlocks.js//
  153. //////////////////////
  154. snippet.name = 'ListRaidUnlocks.js';
  155. snippet.start();
  156. // list unlocked raid items
  157. listCrateItems = (unlocked = true) =>
  158. RaidManager.crateItemWeights.filter(x =>
  159. unlocked === game.golbinRaid.ownedCrateItems.has(x.itemID)
  160. ).forEach(x =>
  161. console.log(items[x.itemID].name)
  162. );
  163. // to list the ones you have unlocked:
  164. // listCrateItems()
  165. // to list the ones you haven't unlocked:
  166. // listCrateItems(false)
  167. snippet.end();
  168.  
  169. //////////////////
  170. //MasteryBars.js//
  171. //////////////////
  172. snippet.name = 'MasteryBars.js';
  173. snippet.start();
  174. // Add Mastery Bars
  175. setInterval(() => {
  176. for (const id in SKILLS) {
  177. if (SKILLS[id].hasMastery) {
  178. if ($(`#skill-nav-mastery-${id} .progress-bar`)[0]) {
  179. $(`#skill-nav-mastery-${id} .progress-bar`)[0].style.width =
  180. (MASTERY[id].pool / getMasteryPoolTotalXP(id)) * 100 + '%';
  181. if (MASTERY[id].pool < getMasteryPoolTotalXP(id)) {
  182. $(`#skill-nav-mastery-${id}`)[0].style.setProperty('background', 'rgb(76,80,84)', 'important');
  183. $(`#skill-nav-mastery-${id} .progress-bar`)[0].className = 'progress-bar bg-warning';
  184. } else {
  185. $(`#skill-nav-mastery-${id}`)[0].style.setProperty('background', 'rgb(48,199,141)', 'success');
  186. $(`#skill-nav-mastery-${id} .progress-bar`)[0].className = 'progress-bar bg-success';
  187. }
  188. const tip = $(`#skill-nav-mastery-${id}`)[0]._tippy;
  189. tip.setContent((Math.min(1, MASTERY[id].pool / getMasteryPoolTotalXP(id)) * 100).toFixed(2) + '%');
  190. } else {
  191. const skillItem = $(`#skill-nav-name-${id}`)[0].parentNode;
  192. skillItem.style.flexWrap = 'wrap';
  193. skillItem.style.setProperty('padding-top', '.25rem', 'important');
  194. const progress = document.createElement('div');
  195. const progressBar = document.createElement('div');
  196. progress.id = `skill-nav-mastery-${id}`;
  197. progress.className = 'progress active pointer-enabled';
  198. progress.style.height = '6px';
  199. progress.style.width = '100%';
  200. progress.style.margin = '.25rem 0rem';
  201. if (MASTERY[id].pool < getMasteryPoolTotalXP(id)) {
  202. progress.style.setProperty('background', 'rgb(76,80,84)', 'important');
  203. progressBar.className = 'progress-bar bg-warning';
  204. } else {
  205. progress.style.setProperty('background', 'rgb(48,199,141)', 'success');
  206. progressBar.className = 'progress-bar bg-success';
  207. }
  208. progressBar.style.width = (MASTERY[id].pool / getMasteryPoolTotalXP(id)) * 100 + '%';
  209. progress.appendChild(progressBar);
  210. skillItem.appendChild(progress);
  211. tippy($(`#skill-nav-mastery-${id}`)[0], {
  212. placement: 'right',
  213. content: ((MASTERY[id].pool / getMasteryPoolTotalXP(id)) * 100).toFixed(2) + '%',
  214. });
  215. }
  216. }
  217. }
  218. }, 5000);
  219. snippet.end();
  220.  
  221. ///////////////////
  222. //MasteryBuyer.js//
  223. ///////////////////
  224. snippet.name = 'MasteryBuyer.js';
  225. snippet.start();
  226. // methods to buy base mastery levels
  227. window.masteryBuyer = {
  228. poolXpPerItem: 500000,
  229. };
  230.  
  231. masteryBuyer.availXp = (skillID, minPercent = 95) => {
  232. let minPool = MASTERY[skillID].xp.length * masteryBuyer.poolXpPerItem * minPercent / 100;
  233. return MASTERY[skillID].pool - minPool;
  234. }
  235.  
  236. masteryBuyer.currentBase = (skillID) => {
  237. return Math.min(...MASTERY[skillID].xp.map((_, masteryID) => getMasteryLevel(skillID, masteryID)));
  238. }
  239.  
  240. masteryBuyer.maxAffordableBase = (skillID, minPercent = 95) => {
  241. let xp = masteryBuyer.availXp(skillID, minPercent);
  242. // make bins with mastery levels
  243. let bins = [];
  244. for (let i = 0; i < 100; i++) {
  245. bins[i] = [];
  246. }
  247. MASTERY[skillID].xp.forEach((_, masteryID) => {
  248. let level = getMasteryLevel(skillID, masteryID);
  249. bins[level].push(masteryID);
  250. });
  251. // level one at a time
  252. let maxBase = 0;
  253. bins.forEach((x, i) => {
  254. if (i >= 99) {
  255. return;
  256. }
  257. if (x.length === 0) {
  258. return;
  259. }
  260. let xpRequired = (exp.level_to_xp(i + 1) - exp.level_to_xp(i)) * x.length;
  261. xp -= xpRequired;
  262. if (xp >= 0) {
  263. maxBase = i + 1;
  264. x.forEach(y => bins[i + 1].push(y));
  265. }
  266. });
  267. maxBase = maxBase > 99 ? 99 : maxBase;
  268. return maxBase;
  269. }
  270.  
  271. masteryBuyer.increaseBase = (skillID, minPercent = 95, levelCap = 99) => {
  272. // buy until goal
  273. let goal = masteryBuyer.maxAffordableBase(skillID, minPercent);
  274. if (goal === 0) {
  275. goal = masteryBuyer.currentBase(skillID);
  276. }
  277. if (goal > levelCap) {
  278. goal = levelCap;
  279. }
  280. MASTERY[skillID].xp.forEach((_, masteryID) => {
  281. let level = getMasteryLevel(skillID, masteryID);
  282. if (level >= goal) {
  283. return;
  284. }
  285. masteryPoolLevelUp = goal - level;
  286. levelUpMasteryWithPool(skillID, masteryID);
  287. });
  288. // spend remainder on goal + 1
  289. const xpRequired = exp.level_to_xp(goal + 1) - exp.level_to_xp(goal);
  290. let count = Math.floor(masteryBuyer.availXp(skillID, minPercent) / xpRequired);
  291. masteryPoolLevelUp = 1;
  292. MASTERY[skillID].xp.forEach((_, masteryID) => {
  293. if (count === 0) {
  294. return;
  295. }
  296. let level = getMasteryLevel(skillID, masteryID);
  297. if (level > goal || level >= levelCap) {
  298. return;
  299. }
  300. count--;
  301. levelUpMasteryWithPool(skillID, masteryID);
  302. });
  303. // update total mastery
  304. updateTotalMastery(skillID);
  305. }
  306.  
  307. masteryBuyer.overview = (minPercent = 95) => {
  308. Object.getOwnPropertyNames(SKILLS).forEach(skillID => {
  309. const skill = SKILLS[skillID];
  310. if (!skill.hasMastery) {
  311. return;
  312. }
  313. const maxBase = masteryBuyer.maxAffordableBase(skillID, minPercent);
  314. if (maxBase === 0) {
  315. return;
  316. }
  317. const currentBase = masteryBuyer.currentBase(skillID);
  318. console.log(`${skill.name}: ${currentBase} -> ${maxBase}`);
  319. });
  320. }
  321.  
  322. masteryBuyer.remaining = (skillID, target = 99) => {
  323. let xp = 0;
  324. let xpTarget = exp.level_to_xp(target);
  325. MASTERY[skillID].xp.forEach(masteryXp => {
  326. xp += Math.max(0, xpTarget - masteryXp);
  327. });
  328. xp = Math.round(xp)
  329. console.log(formatNumber(xp))
  330. return xp
  331. }
  332. snippet.end();
  333.  
  334. ///////////////////////
  335. //PrintSynergyList.js//
  336. ///////////////////////
  337. snippet.name = 'PrintSynergyList.js';
  338. snippet.start();
  339. // functions to print synergies per category (cb vs non-cb)
  340. printSynergy = (x, y) => console.log('- [ ]',
  341. x.summoningID,
  342. parseInt(y),
  343. items[x.itemID].name,
  344. items[summoningItems[y].itemID].name,
  345. SUMMONING.Synergies[x.summoningID][y].description,
  346. SUMMONING.Synergies[x.summoningID][y].modifiers
  347. );
  348.  
  349. printCombatSynergyList = () => {
  350. // get combat synergies
  351. summoningItems.filter(x => items[x.itemID].summoningMaxHit).map(x => {
  352. for (y in SUMMONING.Synergies[x.summoningID]) {
  353. printSynergy(x, y);
  354. }
  355. });
  356. }
  357.  
  358. printNonCombatSynergyList = () => {
  359. // get non-combat synergies
  360. summoningItems.filter(x => !items[x.itemID].summoningMaxHit).map(x => {
  361. for (y in SUMMONING.Synergies[x.summoningID]) {
  362. printSynergy(x, y);
  363. }
  364. });
  365. }
  366. snippet.end();
  367.  
  368. /////////////////////
  369. //QuickEquipCape.js//
  370. /////////////////////
  371. snippet.name = 'QuickEquipCape.js';
  372. snippet.start();
  373. // Quick Equip Max/Comp Cape
  374. quickEquipSkillcape = (skill) => {
  375. const capes = [
  376. Items.Cape_of_Completion,
  377. Items.Max_Skillcape,
  378. skillcapeItems[skill],
  379. ];
  380. for (let i = 0; i < capes.length; i++) {
  381. const capeId = capes[i];
  382. if (player.equipment.checkForItemID(capeId)) {
  383. notifyPlayer(skill, `${items[capeId].name} is already equipped.`, "info");
  384. return;
  385. }
  386. const bankId = getBankId(capeId);
  387. if (bankId === -1) {
  388. continue;
  389. }
  390. if (!player.equipItem(capeId, player.selectedEquipmentSet)) {
  391. continue;
  392. }
  393. notifyPlayer(skill, `${items[capeId].name} Equipped.`, "success");
  394. if (skill === 0) {
  395. updateWCRates();
  396. }
  397. return;
  398. }
  399. notifyPlayer(skill, "There's no " + setToUppercase(Skills[skill]) + " Skillcape in your bank *shrug*", "danger");
  400. }
  401. snippet.end();
  402.  
  403. ////////////////////
  404. //ReclaimTokens.js//
  405. ////////////////////
  406. snippet.name = 'ReclaimTokens.js';
  407. snippet.start();
  408. // reclaim tokens
  409. window.reclaimMasteryTokens = () => {
  410. skillXP.forEach((_, s) => {
  411. if (MASTERY[s] === undefined) {
  412. return;
  413. }
  414. const id = Items['Mastery_Token_' + Skills[s]];
  415. const p = Math.floor((MASTERY[s].pool - getMasteryPoolTotalXP(s) ) / Math.floor(getMasteryPoolTotalXP(s)*0.001));
  416. const m = game.stats.Items.statsMap.get(id).stats.get(ItemStats.TimesFound);
  417. const o = getBankQty(id);
  418. const a = Math.min(p, m - o);
  419. const b = getBankId(id);
  420. if (a > 0 && b >= 0) {
  421. bank[b].qty += a;
  422. MASTERY[s].pool -= a * Math.floor(getMasteryPoolTotalXP(s)*0.001);
  423. snippets.log('reclaimed', a, Skills[s], 'tokens');
  424. }
  425. });
  426. }
  427.  
  428. snippet.end();
  429.  
  430. /////////////////////
  431. //RemoveElements.js//
  432. /////////////////////
  433. snippet.name = 'RemoveElements.js';
  434. snippet.start();
  435. // remove various elements
  436. // combat
  437. document.getElementById('offline-combat-alert').remove();
  438.  
  439. // summoning marks
  440. // green
  441. document.getElementById('summoning-category-0').children[0].children[0].children[2].remove();
  442. // orange and red
  443. document.getElementById('summoning-category-0').children[0].children[0].children[1].remove();
  444.  
  445. // summoning tablets
  446. document.getElementById('summoning-category-1').children[0].children[0].children[0].remove()
  447.  
  448. // alt. magic
  449. document.getElementById('magic-container').children[0].children[1].remove();
  450.  
  451. // cloud saving
  452. document.getElementById('header-cloud-save-time').remove();
  453. document.getElementById('header-cloud-save-btn-connected').remove();
  454.  
  455. // minibar-max-cape
  456. document.getElementById('minibar-max-cape').remove();
  457. snippet.end();
  458.  
  459. ///////////////////
  460. //RerollSlayer.js//
  461. ///////////////////
  462. snippet.name = 'RerollSlayer.js';
  463. snippet.start();
  464. //reroll slayer task until desired task is met
  465. window.rerollSlayerTask = (monsterIDs, tier, extend = true) => {
  466. if (window.stopRerolling) {
  467. return;
  468. }
  469. const task = combatManager.slayerTask;
  470. const taskID = task.monster.id;
  471. const taskName = MONSTERS[taskID].name;
  472. if (!combatManager.slayerTask.taskTimer.active) {
  473. // only do something if slayer task timer is not running
  474. if (!combatManager.slayerTask.active || !monsterIDs.includes(taskID)) {
  475. // roll task if we don't have one, or if it has the wrong monster
  476. console.log(`rerolling ${taskName} for tier ${tier} task ${monsterIDs.map(monsterID => MONSTERS[monsterID].name).join(', ')}`);
  477. combatManager.slayerTask.selectTask(tier, true, true, false);
  478. } else if (extend && !task.extended) {
  479. // extend task if it is the right monster
  480. console.log(`extending ${taskName}`);
  481. combatManager.slayerTask.extendTask();
  482. }
  483. }
  484. setTimeout(() => rerollSlayerTask(monsterIDs, tier, extend), 1000);
  485. }
  486. snippet.end();
  487.  
  488. /////////////////
  489. //ShardsUsed.js//
  490. /////////////////
  491. snippet.name = 'ShardsUsed.js';
  492. snippet.start();
  493. // compute total shards used
  494. shardsUsed = () => {
  495. // compute amount of gp spent on summoning shards that have been used (for summoning or agility obstacles)
  496. items.map((x, i) => [x, i])
  497. .filter(x => x[0].type === 'Shard' && x[0].category === 'Summoning')
  498. .map(x => x[1])
  499. .map(x => (itemStats[x].stats[0] - getBankQty(x) - itemStats[x].stats[1]) * items[x].buysFor)
  500. .reduce((a, b) => a + b, 0);
  501. }
  502. snippet.end();
  503.  
  504. ///////////////////
  505. //SpawnAhrenia.js//
  506. ///////////////////
  507. snippet.name = 'SpawnAhrenia.js';
  508. snippet.start();
  509. // spawn Ahrenia
  510. window.spawnAhrenia = (phaseToSpawn = 1) => {
  511. // run
  512. combatManager.runCombat();
  513. // set respawn to 0
  514. if (!petUnlocked[0]) {
  515. unlockPet(0);
  516. }
  517. PETS[0].modifiers.decreasedMonsterRespawnTimer = 0;
  518. player.computeAllStats();
  519. PETS[0].modifiers.decreasedMonsterRespawnTimer = 3000 - TICK_INTERVAL - player.modifiers.decreasedMonsterRespawnTimer + player.modifiers.increasedMonsterRespawnTimer;
  520. player.computeAllStats();
  521. // unlock itm
  522. dungeonCompleteCount[Dungeons.Fire_God_Dungeon] = Math.max(
  523. dungeonCompleteCount[Dungeons.Fire_God_Dungeon],
  524. 1,
  525. );
  526. skillLevel[Skills.Slayer] = Math.max(
  527. skillLevel[Skills.Slayer],
  528. 90,
  529. );
  530. // skip to desired phase
  531. combatManager.selectDungeon(15);
  532. combatManager.dungeonProgress = 19 + phaseToSpawn;
  533. combatManager.loadNextEnemy();
  534. }
  535. snippet.end();
  536.  
  537. ////////////////////
  538. //UnlimitedPool.js//
  539. ////////////////////
  540. snippet.name = 'UnlimitedPool.js';
  541. snippet.start();
  542. // don't cap pool xp
  543. eval(addMasteryXPToPool.toString()
  544. .replace('MASTERY[skill].pool>getMasteryPoolTotalXP(skill)', 'false')
  545. .replace(/^function (\w+)/, "window.$1 = function")
  546. );
  547.  
  548. // don't cap token claiming
  549. eval(claimToken.toString()
  550. .replace('qty>=tokensToFillPool', 'false')
  551. .replace(/^function (\w+)/, "window.$1 = function")
  552. );
  553. snippet.end();
  554.  
  555. // footer start
  556. }
  557.  
  558. function loadScript() {
  559. if (typeof isLoaded !== typeof undefined && isLoaded) {
  560. // Only load script after game has opened
  561. clearInterval(scriptLoader);
  562. startSnippets();
  563. }
  564. }
  565.  
  566. const scriptLoader = setInterval(loadScript, 200);
  567. });