Melvor Snippets

Collection of various snippets

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

  1. // ==UserScript==
  2. // @name Melvor Snippets
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.0.1
  5. // @description Collection of various snippets
  6. // @author GMiclotte
  7. // @match https://*.melvoridle.com/*
  8. // @exclude https://wiki.melvoridle.com*
  9. // @noframes
  10. // @grant none
  11. // ==/UserScript==
  12.  
  13. function script() {
  14. // header end
  15.  
  16. ////////////////
  17. //Mastery bars//
  18. ////////////////
  19. setInterval(() => {
  20. for (const id in SKILLS) {
  21. if (SKILLS[id].hasMastery) {
  22. if ($(`#skill-nav-mastery-${id} .progress-bar`)[0]) {
  23. $(`#skill-nav-mastery-${id} .progress-bar`)[0].style.width =
  24. (MASTERY[id].pool / getMasteryPoolTotalXP(id)) * 100 + '%';
  25. if (MASTERY[id].pool < getMasteryPoolTotalXP(id)) {
  26. $(`#skill-nav-mastery-${id}`)[0].style.setProperty('background', 'rgb(76,80,84)', 'important');
  27. $(`#skill-nav-mastery-${id} .progress-bar`)[0].className = 'progress-bar bg-warning';
  28. } else {
  29. $(`#skill-nav-mastery-${id}`)[0].style.setProperty('background', 'rgb(48,199,141)', 'success');
  30. $(`#skill-nav-mastery-${id} .progress-bar`)[0].className = 'progress-bar bg-success';
  31. }
  32. const tip = $(`#skill-nav-mastery-${id}`)[0]._tippy;
  33. tip.setContent((Math.min(1, MASTERY[id].pool / getMasteryPoolTotalXP(id)) * 100).toFixed(2) + '%');
  34. } else {
  35. const skillItem = $(`#skill-nav-name-${id}`)[0].parentNode;
  36. skillItem.style.flexWrap = 'wrap';
  37. skillItem.style.setProperty('padding-top', '.25rem', 'important');
  38. const progress = document.createElement('div');
  39. const progressBar = document.createElement('div');
  40. progress.id = `skill-nav-mastery-${id}`;
  41. progress.className = 'progress active pointer-enabled';
  42. progress.style.height = '6px';
  43. progress.style.width = '100%';
  44. progress.style.margin = '.25rem 0rem';
  45. if (MASTERY[id].pool < getMasteryPoolTotalXP(id)) {
  46. progress.style.setProperty('background', 'rgb(76,80,84)', 'important');
  47. progressBar.className = 'progress-bar bg-warning';
  48. } else {
  49. progress.style.setProperty('background', 'rgb(48,199,141)', 'success');
  50. progressBar.className = 'progress-bar bg-success';
  51. }
  52. progressBar.style.width = (MASTERY[id].pool / getMasteryPoolTotalXP(id)) * 100 + '%';
  53. progress.appendChild(progressBar);
  54. skillItem.appendChild(progress);
  55. tippy($(`#skill-nav-mastery-${id}`)[0], {
  56. placement: 'right',
  57. content: ((MASTERY[id].pool / getMasteryPoolTotalXP(id)) * 100).toFixed(2) + '%',
  58. });
  59. }
  60. }
  61. }
  62. }, 5000);
  63.  
  64. //////////////////////////
  65. //buy mastery level base//
  66. //////////////////////////
  67. window.masteryBuyer = {
  68. poolXpPerItem: 500000,
  69. };
  70. masteryBuyer.availXp = (skillID, minPercent = 95) => {
  71. let minPool = MASTERY[skillID].xp.length * masteryBuyer.poolXpPerItem * minPercent / 100;
  72. return MASTERY[skillID].pool - minPool;
  73. }
  74. masteryBuyer.currentBase = (skillID) => {
  75. return Math.min(...MASTERY[skillID].xp.map((_, masteryID) => getMasteryLevel(skillID, masteryID)));
  76. }
  77. masteryBuyer.maxAffordableBase = (skillID, minPercent = 95) => {
  78. let xp = masteryBuyer.availXp(skillID, minPercent);
  79. // make bins with mastery levels
  80. let bins = [];
  81. for (let i = 0; i < 100; i++) {
  82. bins[i] = [];
  83. }
  84. MASTERY[skillID].xp.forEach((_, masteryID) => {
  85. let level = getMasteryLevel(skillID, masteryID);
  86. bins[level].push(masteryID);
  87. });
  88. // level one at a time
  89. let maxBase = 0;
  90. bins.forEach((x, i) => {
  91. if (i >= 99) {
  92. return;
  93. }
  94. if (x.length === 0) {
  95. return;
  96. }
  97. let xpRequired = (exp.level_to_xp(i + 1) - exp.level_to_xp(i)) * x.length;
  98. xp -= xpRequired;
  99. if (xp >= 0) {
  100. maxBase = i + 1;
  101. x.forEach(y => bins[i + 1].push(y));
  102. }
  103. });
  104. maxBase = maxBase > 99 ? 99 : maxBase;
  105. return maxBase;
  106. }
  107. masteryBuyer.increaseBase = (skillID, minPercent = 95, levelCap = 99) => {
  108. // buy until goal
  109. let goal = masteryBuyer.maxAffordableBase(skillID, minPercent);
  110. if (goal === 0) {
  111. goal = masteryBuyer.currentBase(skillID);
  112. }
  113. if (goal > levelCap) {
  114. goal = levelCap;
  115. }
  116. MASTERY[skillID].xp.forEach((_, masteryID) => {
  117. let level = getMasteryLevel(skillID, masteryID);
  118. if (level >= goal) {
  119. return;
  120. }
  121. masteryPoolLevelUp = goal - level;
  122. levelUpMasteryWithPool(skillID, masteryID);
  123. });
  124. // spend remainder on goal + 1
  125. const xpRequired = exp.level_to_xp(goal + 1) - exp.level_to_xp(goal);
  126. let count = Math.floor(masteryBuyer.availXp(skillID, minPercent) / xpRequired);
  127. masteryPoolLevelUp = 1;
  128. MASTERY[skillID].xp.forEach((_, masteryID) => {
  129. if (count === 0) {
  130. return;
  131. }
  132. let level = getMasteryLevel(skillID, masteryID);
  133. if (level > goal || level >= levelCap) {
  134. return;
  135. }
  136. count--;
  137. levelUpMasteryWithPool(skillID, masteryID);
  138. });
  139. // update total mastery
  140. updateTotalMastery(skillID);
  141. }
  142. masteryBuyer.overview = (minPercent = 95) => {
  143. Object.getOwnPropertyNames(SKILLS).forEach(skillID => {
  144. const skill = SKILLS[skillID];
  145. if (!skill.hasMastery) {
  146. return;
  147. }
  148. const maxBase = masteryBuyer.maxAffordableBase(skillID, minPercent);
  149. if (maxBase === 0) {
  150. return;
  151. }
  152. const currentBase = masteryBuyer.currentBase(skillID);
  153. console.log(`${skill.name}: ${currentBase} -> ${maxBase}`);
  154. });
  155. }
  156. masteryBuyer.remaining = (skillID, target = 99) => {
  157. let xp = 0;
  158. let xpTarget = exp.level_to_xp(target);
  159. MASTERY[skillID].xp.forEach(masteryXp => {
  160. xp += Math.max(0, xpTarget - masteryXp);
  161. });
  162. xp = Math.round(xp)
  163. console.log(formatNumber(xp))
  164. return xp
  165. }
  166.  
  167. /////////////////////////////
  168. //Quick Equip Max/Comp Cape//
  169. /////////////////////////////
  170. quickEquipSkillcape = (skill) => {
  171. const capes = [
  172. CONSTANTS.item.Cape_of_Completion,
  173. CONSTANTS.item.Max_Skillcape,
  174. skillcapeItems[skill],
  175. ];
  176. for (let i = 0; i < capes.length; i++) {
  177. const capeId = capes[i];
  178. if (player.equipment.checkForItemID(capeId)) {
  179. notifyPlayer(skill, `${items[capeId].name} is already equipped.`, "info");
  180. return;
  181. }
  182. const bankId = getBankId(capeId);
  183. if (bankId === -1) {
  184. continue;
  185. }
  186. if (!player.equipItem(capeId, player.selectedEquipmentSet)) {
  187. continue;
  188. }
  189. notifyPlayer(skill, `${items[capeId].name} Equipped.`, "success");
  190. if (skill === 0) {
  191. updateWCRates();
  192. }
  193. return;
  194. }
  195. notifyPlayer(skill, "There's no " + setToUppercase(skillName[skill]) + " Skillcape in your bank *shrug*", "danger");
  196. }
  197.  
  198. ///////////////////
  199. //remove elements//
  200. ///////////////////
  201. // combat
  202. document.getElementById('offline-combat-alert').remove();
  203.  
  204. // summoning marks
  205. // green
  206. document.getElementById('summoning-category-0').children[0].children[0].children[2].remove();
  207. // orange and red
  208. document.getElementById('summoning-category-0').children[0].children[0].children[1].remove();
  209.  
  210. // summoning tablets
  211. document.getElementById('summoning-notice').remove();
  212.  
  213. // alt. magic
  214. document.getElementById('magic-container').children[0].children[1].remove();
  215.  
  216. // cloud saving
  217. document.getElementById('header-cloud-save-time').remove();
  218. document.getElementById('header-cloud-save-btn-connected').remove();
  219.  
  220. // minibar-max-cape
  221. document.getElementById('minibar-max-cape').remove();
  222.  
  223. /////////////////
  224. //reroll slayer//
  225. /////////////////
  226. window.rerollSlayerTask = (monsterIDs, tier) => {
  227. if (window.stopRerolling) {
  228. return;
  229. }
  230. const task = combatManager.slayerTask;
  231. const taskID = task.monster.id;
  232. const taskName = MONSTERS[taskID].name;
  233. if (!combatManager.slayerTask.taskTimer.active) {
  234. // only do something if slayer task timer is not running
  235. if (!combatManager.slayerTask.active || !monsterIDs.includes(taskID)) {
  236. // roll task if we don't have one, or if it has the wrong monster
  237. console.log(`rerolling ${taskName} for tier ${tier} task ${monsterIDs.map(monsterID => MONSTERS[monsterID].name).join(', ')}`);
  238. combatManager.slayerTask.selectTask(tier, true, true, false);
  239. } else if (!task.extended) {
  240. // extend task if it is the right monster
  241. console.log(`extending ${taskName}`);
  242. combatManager.slayerTask.extendTask();
  243. }
  244. }
  245. setTimeout(() => rerollSlayerTask(monsterIDs, tier), 1000);
  246. }
  247.  
  248. //////////////////
  249. //mining swapper//
  250. //////////////////
  251. window.rockOrder = [];
  252. setInterval(() => {
  253. if (currentRock === null) {
  254. return;
  255. }
  256. for (let i = 0; i < window.rockOrder.length; i++) {
  257. let rock = window.rockOrder[i];
  258. if (miningData[rock].level > skillLevel[CONSTANTS.skill.Mining]) {
  259. continue;
  260. }
  261. if (!rockData[rock].depleted) {
  262. if (currentRock === rock) {
  263. return;
  264. } else {
  265. console.log("start mining " + rock);
  266. mineRock(rock);
  267. return;
  268. }
  269. }
  270. }
  271. }, 1000);
  272.  
  273. ///////////////
  274. //shards used//
  275. ///////////////
  276. // compute amount of gp spent on summoning shards that have been used (for summoning or agility obstacles)
  277. items.map((x, i) => [x, i])
  278. .filter(x => x[0].type === 'Shard' && x[0].category === 'Summoning')
  279. .map(x => x[1])
  280. .map(x => (itemStats[x].stats[0] - getBankQty(x) - itemStats[x].stats[1]) * items[x].buysFor)
  281. .reduce((a, b) => a + b, 0);
  282.  
  283. /////////////////////
  284. //Show Fish Cooking//
  285. /////////////////////
  286. // make cookable array and sort it based on cooking milestone index
  287. const cookable = items.filter(x => x.cookingID !== undefined);
  288. cookable.forEach((x, i) => {
  289. for (let j = 0; j < MILESTONES.Cooking.length; j++) {
  290. const ms = MILESTONES.Cooking[j];
  291. if (ms.name === x.name) {
  292. cookable[i].msId = j;
  293. return;
  294. }
  295. }
  296. });
  297. cookable.sort((a, b) => a.msId - b.msId);
  298. // set to true to show raw foods with 0 amount banked
  299. let showAllRaws = true;
  300. // override updateAvailableFood
  301. updateAvailableFood = () => {
  302. $("#cooking-food-dropdown").html("");
  303. let selectedFoodExists = 0;
  304. cookable.forEach(raw => {
  305. let onClick = 'void(0)';
  306. let required = `<div className="font-size-sm"><small>Level ${raw.cookingLevel} Required</small></div>`
  307. if (skillLevel[3] >= raw.cookingLevel) {
  308. onClick = `selectFood(${raw.id})`;
  309. required = '';
  310. }
  311. const bankId = getBankId(raw.id);
  312. const qty = bankId === -1 ? 0 : bank[bankId].qty;
  313. if (!showAllRaws && qty === 0) {
  314. return;
  315. }
  316. $("#cooking-food-dropdown").append(''
  317. + `<a class="dropdown-item pointer-enabled" id="skill-cooking-food-${raw.msId}" onClick="${onClick}">`
  318. + ' <div class="media d-flex align-items-center push mb-0">'
  319. // img
  320. + ' <div class="mr-2">'
  321. + ' <img class="skill-icon-sm" src="' + raw.media + '">'
  322. + ' </div>'
  323. + ' <div class="media-body">'
  324. // name
  325. + ' <div class="font-w600 font-size-sm">'
  326. + raw.name
  327. + ' </div>'
  328. // required level
  329. + required
  330. // qty
  331. + ' <div class="font-w600 font-size-sm">'
  332. + ' <span class="badge badge-pill badge-primary">'
  333. + formatNumber(qty)
  334. + ' </span>'
  335. + ' </div>'
  336. // xp and healing
  337. + ' <div class="font-size-sm text-info">'
  338. + ' <small>'
  339. + `${raw.cookingXP} XP`
  340. + ' </small>'
  341. + ' <small class="text-success ml-2">'
  342. + ' <img class="skill-icon-xxs mr-1" src="https://melvorcdn.fra1.cdn.digitaloceanspaces.com/current/assets/media/skills/combat/hitpoints.svg">'
  343. + `${items[raw.cookedItemID].healsFor * numberMultiplier} HP`
  344. + ' </small>'
  345. + ' </div>'
  346. + ' </div>'
  347. + ' </div>'
  348. + '</a>'
  349. );
  350. if (selectedFood === raw.id) {
  351. $("#skill-cooking-food-selected-qty").text(formatNumber(qty));
  352. selectedFoodExists++;
  353. }
  354. });
  355. if (selectedFoodExists < 1) {
  356. $("#skill-cooking-food-selected-qty").text(0);
  357. }
  358. }
  359.  
  360. /////////////////////
  361. //don't cap pool xp//
  362. /////////////////////
  363. eval(addMasteryXPToPool.toString()
  364. .replace('MASTERY[skill].pool>getMasteryPoolTotalXP(skill)', 'false')
  365. .replace(/^function (\w+)/, "window.$1 = function")
  366. );
  367.  
  368. ////////////////////////////
  369. //don't cap token claiming//
  370. ////////////////////////////
  371. eval(claimToken.toString()
  372. .replace('qty>=tokensToFillPool', 'false')
  373. .replace(/^function (\w+)/, "window.$1 = function")
  374. );
  375.  
  376. /////////////////////
  377. //unsell sold items//
  378. /////////////////////
  379. unsell = (id, count = Infinity) => {
  380. if (count < 0) {
  381. return;
  382. }
  383. let stats = itemStats[id].stats;
  384. if (stats[Stats.TimesSold] === 0) {
  385. console.log("zero times sold");
  386. return;
  387. }
  388. // check if transaction is affordable
  389. let times = Math.min(count, stats[Stats.TimesSold]);
  390. let cost = Math.ceil(stats[Stats.GpFromSale] / stats[Stats.TimesSold] * times);
  391. if (gp < cost) {
  392. console.log("can't afford: " + times + " costs " + cost + " have " + gp);
  393. return;
  394. }
  395. // add item
  396. if (times > 0) {
  397. addItemToBank(id, times);
  398. }
  399. stats[Stats.TimesFound] -= times;
  400. stats[Stats.TimesSold] -= times;
  401. // remove cost
  402. gp = Math.floor(gp - cost);
  403. stats[Stats.GpFromSale] -= cost;
  404. updateGP();
  405. // fix statistics
  406. statsGeneral[0].count -= cost;
  407. statsGeneral[1].count -= times;
  408. updateStats();
  409. // log transaction
  410. console.log("bought " + times + " for " + cost);
  411. }
  412.  
  413. // footer start
  414. }
  415.  
  416. // inject the script
  417. (function () {
  418. function injectScript(main) {
  419. const scriptElement = document.createElement('script');
  420. scriptElement.textContent = `try {(${main})();} catch (e) {console.log(e);}`;
  421. document.body.appendChild(scriptElement).parentNode.removeChild(scriptElement);
  422. }
  423.  
  424. function loadScript() {
  425. if ((window.isLoaded && !window.currentlyCatchingUp)
  426. || (typeof unsafeWindow !== 'undefined' && unsafeWindow.isLoaded && !unsafeWindow.currentlyCatchingUp)) {
  427. // Only load script after game has opened
  428. clearInterval(scriptLoader);
  429. injectScript(script);
  430. }
  431. }
  432.  
  433. const scriptLoader = setInterval(loadScript, 200);
  434. })();