Last active
October 7, 2020 16:18
-
-
Save jwhett/825b3b058f706ed632a42577a42d8ef3 to your computer and use it in GitHub Desktop.
Melvor QOL Combined
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name MelvorQOL | |
// @namespace jwhett | |
// @version 1.1 | |
// @description MelvorIdle QOL improvements | |
// @author Josh Whetton | |
// @match https://*.melvoridle.com/* | |
// @grant none | |
// ==/UserScript== | |
/* eslint-disable func-names, no-console */ | |
function Watcher(fn, interval, type, ...args) { | |
this.fn = fn; | |
this.interval = interval; | |
this.type = type; | |
this.args = args; | |
} | |
Watcher.prototype.start = function () { | |
if (this.intervalID != null) return; // either null or undefined | |
this.intervalID = setInterval(this.fn, this.interval, ...this.args); | |
this.startTime = new Date().getTime(); | |
}; | |
Watcher.prototype.stop = function () { | |
if (this.intervalID == null) return; // either null or undefined | |
clearInterval(this.intervalID); | |
this.intervalID = null; | |
console.log(`Ran for ${this.duration()} seconds`); | |
}; | |
Watcher.prototype.restart = function () { | |
this.stop(); | |
this.start(); | |
}; | |
Watcher.prototype.duration = function () { | |
const d = Math.floor((new Date().getTime() - this.startTime) / 1000); | |
/* eslint-disable no-restricted-globals */ | |
if (isNaN(d)) { | |
return 0; | |
} | |
return d; | |
/* eslint-enable no-restricted-globals */ | |
}; | |
/* eslint-disable no-undef, no-console, max-len, no-plusplus, no-unused-vars */ | |
function MyFood(id, name, qty) { | |
this.id = id; | |
this.name = name; | |
this.qty = qty; | |
this.MAX = 6969696969; | |
} | |
function allOfTypeInItems(t) { | |
return items.filter((i) => i.type === t); | |
} | |
function allCraftingItems() { | |
return items.filter((i) => items[i].craftingID !== undefined); | |
} | |
function allOfTypeInBank(t) { | |
return bank.filter((i) => (i.type === t && !i.locked)); | |
} | |
function itemInBank(id) { | |
return bank.filter((i) => i.id === id).pop(); | |
} | |
function sellAllOfType(t) { | |
const things = allOfTypeInBank(t); | |
things.forEach((thing) => { | |
try { | |
sellItem(thing.id); | |
} catch (err) { | |
console.error(`oops! hit an error when selling an item: ${err}`); | |
} | |
}); | |
} | |
function sellAllOfNameSubstring(s) { | |
const things = bank.filter((i) => i.name.includes(s)); | |
things.forEach((thing) => { | |
try { | |
sellItem(thing.id); | |
} catch (err) { | |
console.error(`oops! hit an error when selling an item: ${err}`); | |
} | |
}); | |
} | |
function learnTokens() { | |
const tokens = allOfTypeInBank('Token'); | |
tokens.forEach((token) => { | |
for (let i = 0; i < token.qty; i++) { | |
try { | |
claimBankToken(currentBank, token.id); | |
} catch (err) { | |
console.error(`oops! hit an error when learning a token: ${err}`); | |
} | |
} | |
}); | |
} | |
function buryBones() { | |
const bones = allOfTypeInBank('Bones'); | |
bones.forEach((bone) => { | |
if (bone.qty >= 10) { | |
let cname = bone.name.replaceAll(' ', '_'); | |
try { | |
buryItem(currentBank, CONSTANTS.item[cname], 6969696969); | |
} catch (err) { | |
console.error(`oops! hit an error when burying bones: ${err}`); | |
} | |
} | |
}); | |
} | |
function haveMaterialsForCrafting(item) { | |
const required = []; | |
let haveAllRequired; | |
item.craftReq.forEach((req) => { | |
required.push(checkBankForItem(req.id)); | |
}); | |
if (required.some((thing) => thing === false)) { | |
haveAllRequired = false; | |
} else { | |
haveAllRequired = true; | |
} | |
/* eslint-disable object-shorthand */ | |
return { haveAllRequired: haveAllRequired, forItem: item }; | |
/* eslint-enable object-shorthand */ | |
} | |
function findFood() { | |
const foodList = []; | |
const allFoodItems = allOfTypeInBank('Food'); | |
allFoodItems.forEach((food) => { | |
foodList.push(new MyFood(food.id, food.name, food.qty)); | |
}); | |
return foodList; | |
} | |
function isOutOfEquippedFood() { | |
return equippedFood[0].itemID === equippedFood[1].itemID && equippedFood[0].itemID === equippedFood[2].itemID; | |
} | |
function getEquippedFoodCount() { return equippedFood[currentCombatFood].qty; } | |
function equipNextFood() { | |
for (let i = 0; i < equippedFood.length; i++) { | |
try { | |
if (equippedFood[i].qty > 0) { | |
selectEquippedFood(i); | |
return true; | |
} | |
} catch (err) { | |
console.error(`oops! we hit an error equipping next food: ${err}`); | |
} | |
} | |
return false; | |
} | |
function foodTracker() { | |
if (!isInCombat) return; // we're not in combat, don't need to watch food | |
if (getEquippedFoodCount() > 0) return; // we have equipped food to eat | |
const foodList = findFood(); | |
if (isOutOfEquippedFood() && foodList.length === 0) { // completely out of food | |
try { | |
stopCombat(false, true, true); // death, stopDungeon, runAway | |
console.log(`Dropped out of combat due to lack of food at ${new Date()}`); | |
} catch (err) { | |
console.error(`oops! hit an error when stopping combat: ${err}`); | |
} | |
} else if (equipNextFood()) { // we have food in pocket, but need to equip it | |
/* eslint-disable no-useless-return */ | |
return; // We successfully swapped to equipped food | |
/* eslint-enable no-useless-return */ | |
} else { | |
const f = foodList.pop(); | |
try { | |
equipFood(currentBank, f.id, f.qty); | |
} catch (err) { | |
console.error(`oops! hit an error when equipping food from bank: ${err}`); | |
} | |
} | |
} | |
/* eslint-disable no-undef, no-console, no-unused-vars, no-restricted-syntax, max-len, no-plusplus */ | |
function getSeeds() { | |
return items.filter((item) => item.type === 'Seeds'); | |
} | |
function patchHasGrown(p) { | |
return p.hasGrown && p.seedID !== 0; | |
} | |
function patchIsEmpty(p) { | |
return !p.hasGrown && p.seedID === 0; | |
} | |
function patchIsReady(p) { | |
return ((patchHasGrown(p)) || (patchIsEmpty(p))); | |
} | |
function getConstantIDByName(n) { | |
const cname = n.replaceAll(' ', '_').substring(0, n.length - 1); | |
if (CONSTANTS.item[cname] === undefined) { | |
/* eslint-disable prefer-template */ | |
return CONSTANTS.item[cname + 's']; | |
/* eslint-enable prefer-template */ | |
} | |
return CONSTANTS.item[cname]; | |
} | |
function getNextSeedIDByTier(t) { | |
/* eslint-disable arrow-body-style */ | |
for (let s of allOfTypeInBank('Seeds').sort((a, b) => { return b.qty - a.qty; })) { | |
/* eslint-enable arrow-body-style */ | |
if (items[getConstantIDByName(s.name)].tier === t.substring(0, t.length - 1)) return getConstantIDByName(s.name); | |
} | |
return undefined; | |
} | |
function reapAndSow() { | |
for (let locationID = 0; locationID < newFarmingAreas.length; locationID++) { | |
for (let patchID = 0; patchID < newFarmingAreas[locationID].patches.length; patchID++) { | |
const patch = newFarmingAreas[locationID].patches[patchID]; | |
selectedPatch = [newFarmingAreas[locationID].id, patchID]; // Melvor global | |
selectedSeed = 0; // Melvor global; seed 0 is empty | |
// can we plant? | |
/* eslint-disable no-continue */ | |
if (!patch.unlocked) continue; | |
if (!patchIsReady(patch)) continue; | |
/* eslint-enable no-continue */ | |
// what seed do i plant? | |
if (patchIsEmpty(patch)) { | |
// no harvest in this block | |
selectedSeed = getNextSeedIDByTier(newFarmingAreas[locationID].areaName); | |
} else { | |
// try to plant the same seed | |
// we'll need to harvest before leaving block | |
if (checkBankForItem(patch.seedID)) { | |
selectedSeed = patch.seedID; | |
} else { | |
// don't have the same seed to plant | |
selectedSeed = getNextSeedIDByTier(newFarmingAreas[locationID].areaName); | |
} | |
try { | |
harvestSeed(locationID, patchID); | |
} catch (err) { | |
console.error(`oops! hit an issue harvesting: ${err}`); | |
} | |
} | |
if (selectedSeed === undefined) { | |
return; // don't have a seed to plant | |
} | |
try { | |
addGloop(locationID, patchID); | |
plantSeed(); | |
} catch (err) { | |
console.error(`oops! hit an issue planting seeds: ${err}`); | |
} | |
} | |
} | |
} | |
/* eslint-disable no-unused-vars, no-undef */ | |
function combatAutoEat() { | |
const missingHP = maxHitpoints - combatData.player.hitpoints; | |
if (equippedFood[currentCombatFood].qty > 0 && combatData.player.hitpoints < maxHitpoints) { | |
if (missingHP >= items[equippedFood[currentCombatFood].itemID].healsFor * 10) { | |
eatFood(); | |
} | |
} | |
if (equippedFood[currentCombatFood].qty === 0) { | |
lootAll(); | |
stopCombat(); | |
} | |
} | |
/* eslint-disable no-unused-vars, no-undef, no-console */ | |
function fireWatcher() { | |
if (!isCooking || cookingFireActive) return; | |
try { | |
lightCookingFire(); | |
} catch (err) { | |
console.error(`oops! couldn't start a cooking fire: ${err}`); | |
} | |
} | |
/* eslint-disable no-unused-vars, no-undef */ | |
function saveAndDownload() { | |
downloadSave(); // download locally | |
forceSync(); // save to the cloud | |
} | |
/* eslint-disable no-undef, no-restricted-syntax, no-unused-vars, no-console */ | |
const watchers = {}; | |
for (let t of ['Junk', 'Gem', 'Havest', 'Logs']) { | |
watchers[t] = new Watcher(sellAllOfType, 120000, t, t); | |
} | |
watchers.Bones = new Watcher(buryBones, 15000, 'Bury bones'); | |
watchers.Sort = new Watcher(sortBank, 60000, "Bank Sorting"); | |
watchers.Looting = new Watcher(lootAll, 15000, 'Auto-looting'); | |
// watchers.Bonfire = new Watcher(lightBonfire, 1000, "XP Bonfire"); | |
watchers.Token = new Watcher(learnTokens, 15000, 'Learn tokens'); | |
watchers.Farming = new Watcher(reapAndSow, 60000, 'Auto-farming'); | |
// watchers.Fire = new Watcher(lightCookingFire, 250, 'Cooking fire'); | |
watchers.Eating = new Watcher(combatAutoEat, 1500, 'Combat eating'); | |
// watchers.Food = new Watcher(foodTracker, 10000, 'Combat food swap'); | |
// watchers.Save = new Watcher(saveAndDownload, 1600000, 'Game saving'); | |
watchers.Burnt = new Watcher(sellAllOfNameSubstring, 5000, 'Burnt food seller', 'Burnt'); | |
watchers.Feathers = new Watcher(sellAllOfNameSubstring, 5000, 'Feather seller', 'Feathers'); | |
function showWatchers() { | |
console.log(`Status as of ${new Date()}:`); | |
for (let w in watchers) { | |
if (watchers[w].duration() > 0) { | |
console.log(` => [ RUNNING ] ${watchers[w].type} running for ${watchers[w].duration()} seconds.`); | |
} else { | |
console.log(` => [ OFFLINE ] ${watchers[w].type}`); | |
} | |
} | |
} | |
for (let watcher in watchers) {watchers[watcher].start()} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment