Melvor Idle - AutoMastery

Automatically spends mastery when a pool is about to fill up

Από την 29/10/2020. Δείτε την τελευταία έκδοση.

// ==UserScript==
// @name        Melvor Idle - AutoMastery
// @description Automatically spends mastery when a pool is about to fill up
// @version     1.3
// @namespace   Visua
// @match       https://melvoridle.com/*
// @match       https://www.melvoridle.com/*
// @grant       none
// ==/UserScript==
/* jshint esversion: 6 */

((main) => {
    var script = document.createElement("script");
    script.textContent = `try { (${main})(); } catch (e) { console.log(e); }`;
    document.body.appendChild(script).parentNode.removeChild(script);
})(() => {
    "use strict";

    function autoSpendMasteryPool(skill, xpToBeAdded) {
        const poolXp = MASTERY[skill].pool;
        const poolMax = getMasteryPoolTotalXP(skill);
        if (poolXp + xpToBeAdded >= poolMax) {
            const _masteryPoolLevelUp = masteryPoolLevelUp;
            masteryPoolLevelUp = 1;

            const xpOverCheckpoint = poolXp + xpToBeAdded - (poolMax * window.autoMasteryThresholds[skill]) / 100;

            let masteryToLevel = window.autoMasterySelectedMasteries[skill];
            let reason = "was manually selected";

            if (masteryToLevel !== -1) {
                if (getMasteryLevel(skill, masteryToLevel) >= 99 || getMasteryXpForNextLevel(skill, masteryToLevel) > poolXp) {
                    masteryToLevel = -1;
                }
            }

            if (masteryToLevel === -1) {
                masteryToLevel = MASTERY[skill].xp
                    .map((xp, id) => ({ xp, id }))
                    .reduce(
                        (max, m) => {
                            const toNext = getMasteryXpForNextLevel(skill, m.id);
                            if (toNext > 0 && toNext <= xpOverCheckpoint && m.xp >= max.xp) {
                                return m;
                            } else {
                                return max;
                            }
                        },
                        { xp: 0, id: -1 }
                    ).id;
                reason = "was the highest that could be leveled without dropping below the checkpoint";
            }

            if (masteryToLevel === -1) {
                const min = MASTERY[skill].xp
                    .map((_, id) => ({ toNext: getMasteryXpForNextLevel(skill, id), id }))
                    .reduce((min, m) => ((m.toNext > 0 && m.toNext <= min.toNext) || min.toNext === 0 ? m : min));
                if (min.toNext > 0 && min.toNext < poolXp) {
                    masteryToLevel = min.id;
                    reason = "was the cheapest to level and we are forced to drop below the checkpoint";
                }
            }

            if (masteryToLevel !== -1) {
                const message = `AutoMastery: Leveling up ${getMasteryName(skill, masteryToLevel)} to ${
                    getMasteryLevel(skill, masteryToLevel) + 1
                }`;
                const cost = getMasteryXpForNextLevel(skill, masteryToLevel);
                console.log(
                    `${message} for ${numberWithCommas(Math.round(cost))} XP because it ${reason} (Earned ${numberWithCommas(
                        xpToBeAdded.toFixed(3)
                    )} XP. Pool before: ${((poolXp / poolMax) * 100).toFixed(3)}%. Pool after: ${(
                        ((poolXp + xpToBeAdded - cost) / poolMax) *
                        100
                    ).toFixed(3)}%)`
                );
                autoMasteryNotify(message);
                const _showSpendMasteryXP = showSpendMasteryXP;
                showSpendMasteryXP = () => {};
                try {
                    levelUpMasteryWithPool(skill, masteryToLevel);
                } catch (e) {
                    console.error(e);
                } finally {
                    masteryPoolLevelUp = _masteryPoolLevelUp;
                    showSpendMasteryXP = _showSpendMasteryXP;
                }
                autoSpendMasteryPool(skill, xpToBeAdded);
            }
        }
    }

    function autoMasteryNotify(message) {
        Toastify({
            text: `<div class="text-center"><img class="notification-img" src="assets/media/main/mastery_pool.svg"><span class="badge badge-success">${message}</span></div>`,
            duration: 5000,
            gravity: "bottom",
            position: "center",
            backgroundColor: "transparent",
            stopOnFocus: false,
        }).showToast();
    }

    function autoMastery() {
        window.autoMasteryThresholds = Array(Object.keys(SKILLS).length).fill(95);
        window.autoMasterySelectedMasteries = Array(Object.keys(SKILLS).length).fill(-1);

        const _addMasteryXPToPool = addMasteryXPToPool;
        addMasteryXPToPool = (...args) => {
            try {
                const skill = args[0];
                const xp = args[1];
                if (xp > 0) {
                    autoSpendMasteryPool(skill, xp);
                }
            } catch (e) {
                console.error(e);
            } finally {
                _addMasteryXPToPool(...args);
            }
        };
    }

    function loadScript() {
        if (typeof confirmedLoaded !== "undefined" && confirmedLoaded && !currentlyCatchingUp) {
            clearInterval(interval);
            console.log("Loading AutoMastery");
            autoMastery();
        }
    }

    const interval = setInterval(loadScript, 500);
});