您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Helpful tool for auto-mining in Melvor
// ==UserScript== // @name Melvor Auto Miner // @namespace https://github.com/shamus03 // @version 1.6 // @description Helpful tool for auto-mining in Melvor // @author Shamus03 // @match https://*.melvoridle.com/* // @exclude https://wiki.melvoridle.com/* // @grant none // ==/UserScript== (function() { 'use strict' const bus = document.createElement('div') const getItemQuantityInBank = itemId => { let qty = 0 for (const item of bank) { if (item.id === itemId) { qty += item.qty } } return qty } const tryTriggerSemiSkillChanged = () => { SEMIEventBus && typeof SEMIEventBus.TriggerSkillChanged === 'function' && SEMIEventBus.TriggerSkillChanged() } const trySelectOre = ore => { if (rockData[ore].depleted) { return false } if (ore !== currentRock) { setTimeout(() => { mineRock(ore, true) tryTriggerSemiSkillChanged() }) } return true } const ORES = { Rune_Essence: 10, Copper: 0, Tin: 1, Iron: 2, Coal: 3, Silver: 4, Gold: 5, Mithril: 6, Adamantite: 7, Runite: 8, Dragonite: 9, } const ORE_ITEM_IDS = { [ORES.Rune_Essence]: CONSTANTS.item.Rune_Essence, [ORES.Copper]: CONSTANTS.item.Copper_Ore, [ORES.Tin]: CONSTANTS.item.Tin_Ore, [ORES.Iron]: CONSTANTS.item.Iron_Ore, [ORES.Coal]: CONSTANTS.item.Coal_Ore, [ORES.Silver]: CONSTANTS.item.Silver_Ore, [ORES.Gold]: CONSTANTS.item.Gold_Ore, [ORES.Mithril]: CONSTANTS.item.Mithril_Ore, [ORES.Adamantite]: CONSTANTS.item.Adamantite_Ore, [ORES.Runite]: CONSTANTS.item.Runite_Ore, [ORES.Dragonite]: CONSTANTS.item.Dragonite_Ore, } const BAR_RATIOS = {} for (const item of items) { if (item.type !== 'Bar') continue const ratios = {} for (const reqItem of item.smithReq) { ratios[items[reqItem.id].miningID] = reqItem.qty } BAR_RATIOS[item.id] = ratios } const ADDITIONAL_OPTIONS = { None: 'None', Lowest_Mastery: 'Lowest_Mastery', Highest_Ore_Not_Maxed: 'Highest_Ore_Not_Maxed', } const localStorageKey = 'mep-auto-mine-last-selected-bar' let selectedBar = localStorage.getItem(localStorageKey) if (selectedBar === '') { selectedBar = ADDITIONAL_OPTIONS.None } const trySetupBarSelection = () => { const container = document.getElementById('mining-ores-container') if (!container) return false const spacer = document.createElement('div') spacer.classList.add('col-12') const col = document.createElement('div') col.classList.add('col-auto') const lbl = document.createElement('label') lbl.classList.add('form-group', 'form-inline', 'block', 'block-content', 'block-content-full', 'border-4x', 'border-mining', 'border-top') lbl.innerText = 'Auto-Mining:' lbl.innerH col.append(lbl) const sel = document.createElement('select') sel.classList.add('form-control', 'ml-2') lbl.append(sel) sel.addEventListener('change', () => { selectedBar = sel.value localStorage.setItem(localStorageKey, selectedBar) }) const addOption = (text, value) => { const opt = document.createElement('option') opt.innerText = text opt.value = value sel.append(opt) } addOption('None', ADDITIONAL_OPTIONS.None) for (const itemId in BAR_RATIOS) { const item = items[itemId] addOption(item.name, itemId) } for (const k in ADDITIONAL_OPTIONS) { if (k === ADDITIONAL_OPTIONS.None) continue const name = k.replace(/_/g, ' ') addOption(name, k) } sel.value = selectedBar container.prepend(spacer) container.prepend(col) return true } (async function() { while (!trySetupBarSelection()) { await new Promise(resolve => setTimeout(resolve, 1000)) } })() const getOreMasteryLevel = ore => { return MASTERY[CONSTANTS.skill.Mining].xp[ore] } const getOrePriority = () => { switch (selectedBar) { case ADDITIONAL_OPTIONS.Lowest_Mastery: const levels = {} for (const ore of Object.values(ORES)) { levels[ore] = getOreMasteryLevel(ore) } return Object.values(ORES).sort((a, b) => levels[a] - levels[b]) case ADDITIONAL_OPTIONS.Highest_Ore_Not_Maxed: const prios = {} for (const ore of Object.values(ORES).reverse()) { prios[ore] = getOreMasteryLevel(ore) < 13034431 ? 1 : 0 } return Object.values(ORES).reverse().sort((a, b) => prios[b] - prios[a]) } const selectedRatios = BAR_RATIOS[selectedBar] || {} const currentRatios = {} for (const ore of Object.values(ORES)) { const qty = getItemQuantityInBank(ORE_ITEM_IDS[ore]) const needRatio = selectedRatios[ore] || 0 currentRatios[ore] = (needRatio > 0) ? qty === 0 ? 0 : qty / needRatio : Infinity } return Object.values(ORES).sort((a, b) => currentRatios[a] - currentRatios[b]) } const selectBestOre = () => { const orePriority = getOrePriority() for (const ore of orePriority) { if (trySelectOre(ore)) { return } } } const utils = { selectBestOre, getItemQuantityInBank, getOrePriority, } const events = { rockDamaged: 'rock-damaged', rockRespawned: 'rock-respawned', glovesChargesDecreased: 'gloves-charges-decreased', } const originalFunctions = {} const wrap = (name, handler) => { originalFunctions[name] = window[name] window[name] = function(...args) { const result = originalFunctions[name](...args) try { handler(...args) } catch(ex) { console.error(ex) } return result } } wrap('updateRockHP', (ore) => { const { depleted, damage } = rockData[ore] if (damage === 0) { bus.dispatchEvent(new CustomEvent(events.rockRespawned, {detail:{ore}})) } else { bus.dispatchEvent(new CustomEvent(events.rockDamaged, {detail:{ore,depleted,damage}})) } }) wrap('updateGloves', (gloves, skill) => { const { isActive, remainingActions } = glovesTracker[gloves] bus.dispatchEvent(new CustomEvent(events.glovesChargesDecreased, {detail:{gloves,skill,isActive,remainingActions}})) }) bus.addEventListener(events.rockDamaged, e => { if (isMining && selectedBar !== ADDITIONAL_OPTIONS.None) { selectBestOre() } }) const getEquippedGloves = () => { return combatManager.player.equipmentSets[combatManager.player.selectedEquipmentSet].slots.Gloves.item } bus.addEventListener(events.glovesChargesDecreased, ({ detail: { gloves, skill, isActive, remainingActions }}) => { const equippedGloves = getEquippedGloves() if (!isActive || remainingActions > 1 || !equippedGloves || equippedGloves.gloveID !== gloves) return const haveEnoughGp = gp >= glovesCost[gloves] switch (skill) { case CONSTANTS.skill.Mining: if (!isMining) return break case CONSTANTS.skill.Thieving: if (!isThieving) return break default: return } const oldBuyQty = buyQty buyQty = 1 buyShopItem('Gloves', gloves, true) buyQty = oldBuyQty }) window.mep = { bus, events, utils, originalFunctions, } })()