DH3 KindaSafe Cheat

The cheat script for DH3

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

// ==UserScript==
// @name         DH3 KindaSafe Cheat
// @namespace    http://tampermonkey.net/
// @version      1.2.1
// @description  The cheat script for DH3
// @author       Lasse Brustad
// @match        https://dh3.diamondhunt.co/
// @grant        none
// ==/UserScript==

/* jshint esversion:6 */

(function() {
    'use strict';

    // config for cheats
    const cheats = {
        // auto fight, configs starting at line 45
        autoCombat: !true,

        // auto smelt ores, click the ore under the mining tab to choose
        // will automatically fill the furnace to it's maximum capacity
        // configs starting at line 70
        autoSmelt: !true,

        // auto chop trees, it's fully automated with a tiny issue that doesn't matter
        autoChop: !true,

        // auto farm, configs starting at line 76 - not ready yet
        autoFarm: !true,

        // auto consume stardust potions you have
        autoDrinkSD: !true,

        // auto send/collect the Oxygen Tank, you can't get any fish at all with this on
        autoOxy: !true,

        // auto send/collect the Row Boat, this is forced off if autoOxy is on
        autoRowBoat: !true,

        // auto send/collect the Canoe Boat, this is forced off if autoOxy or autoRowBoat is on
        autoCanoeBoat: !true
    };

    const combatConfig = {
        areas: {
            // area name : energy cost

            // this shouldn't be a problem
            fields: 50,

            // don't forget your weapon
            forest: 200,

            // be prepared with armor and weapon
            caves: 500,

            // you might lose against the alien!
            lavaDungeon: 2e3,

            // this script will lose most likely 99% of all attempts
            northernFields: 5e3
        },

        // the area you want to fight automatically from the "areas" list
        area: 'fields' // prefer stored value, later changing the string here have no effect
    }

    // additional config for autoSmelt
    const smeltConfig = {
        // smelt all resources that can be smelted (not ready yet)
        smeltAllAvaliable: true
    }

    // additional configs for autoFarm
    const farmConfig = {
        // plant seeds even if they require bonemeal
        allowBonemealUsage: false,

        // fill bonemeal bin with fishbones, ashes, or bones when possible
        autoFillBonemealBin: false,

        // allow planting seeds that can die
        allowDyingPlants: false
    };

    // config for legal mods
    const mods = {
        // right click the axe to chop down all fully grown trees
        chopTrees: true,

        // right click the rake to harvest all the ready farms
        harvestCrops: true
    };

    /**
     * Code below here is functional code, but you can enable/disable everything in the configs above
     *
     * Remember, cheats is safe to use as it is, but if you mess with the code,
     * don't blame me if you get banned! Cheats is ofc not allowed, but who cares?
     */

    // logging / debugging
    const debugLevels = [
        'silent',
        'normal logging',
        'additional logging',
        'debugging'
    ];

    let debugLevel = parseInt(localStorage.getItem('lbdata-debug')) || 1;

    window.setDebugLevel = (lvl = debugLevel) => {
        if (typeof lvl !== 'number' || (lvl < 0 && lvl > (debugLevels.length - 1))) {
            console.log(`Didn't change the logging level, it has to be a number between 0 and 3.`);
            return;
        }
        const previous = debugLevel;
        debugLevel = lvl;

        if (previous === debugLevel) {
            console.log(`Current debug level is: ${lvl} ~ ${debugLevels[lvl]}`);
        } else {
            console.log(`Debug level is now set to: ${lvl} ~ ${debugLevels[lvl]}`);
        }
    }

    // auto combat
    let willFightSoon = false;
    let fightingError = JSON.parse(localStorage.getItem('lbdata-combatError')) || false;
    function autoCombat() {
        // damn, didn't expect this function to require that much anti-fail testing
        if (fightingError) return;
        if (willFightSoon || window.getItem('heroCooldown') !== 0) return debug(3, `Combat isn't ready yet`);
        if (!Object.keys(combatConfig.areas).includes(combatConfig.area)) {
            fightingError = true;
            debug(2, `The script has no area called ${combatConfig.area} registered!`);
            return;
        }
        if (combatConfig.areas[combatConfig.area] > window.getItem('energy')) return debug(2, `You're low on energy! Eat some food to continue fighting!`);;

        // FIGHT!!!
        willFightSoon = true;
        safeDelay(() => {
            setTimeout(() => {
                willFightSoon = false;
            }, 30e3);
            if (window.getItem('heroCooldown') !== 0) return debug(3, `Combat isn't ready yet`);
            window.sendBytes(`FIGHT=${combatConfig.area}`);
        })
    }

    // settings for autoCombat while playing
    window.setCombatStatus = (state = null) => {
        if (typeof state === 'boolean') {
            fightingError = !state;
            console.log(`Set the state to ${state ? 'active' : 'inactive'}`);
        } else if (typeof state === 'string' && typeof combatConfig.areas[state] === 'number') {
            combatConfig.area = state;
            console.log(`Set the area to ${state}`);
        } else if (state === null) {
            console.log(`Current state: Fighting ${combatConfig.area} is ${!fightingError ? 'active' : 'inactive'}`);
        }
    }

    unload(e => {
        localStorage.setItem('lbdata-combatError', JSON.stringify(fightingError));
        localStorage.setItem('lbdata-combatArea', combatConfig.area);
    });

    // auto smelt ores
    if (cheats.autoSmelt) {
        window.smeltOre = localStorage.getItem('lbdata-smeltOre') || 'copper'; // the ore to smelt
        unload(e => {
            localStorage.setItem('lbdata-smeltOre', window.smeltOre);
        });
    }

    const ores = {
        sand: {
            oil: 1,
            time: 1
        },
        copper: {
            oil: 1,
            time: 1
        },
        iron: {
            oil: 5,
            time: 5
        },
        silver: {
            oil: 20,
            time: 20
        },
        gold: {
            oil: 100,
            time: 60
        },
        promethium: {
            oil: 1000,
            time: 200
        }
    };

    function cheatSmelt() {
        if (typeof ores[window.smeltOre] !== 'object') return debug(1, `Cheat - Unknown ore: "${window.smeltOre}"`);
        function doit() {
            if (window.isSmelting()) {
                // wait till current smelting is over
                const estimated = (window.var_smeltingRequestedAmount - window.var_smeltingCurrentAmount) * window.var_smeltingNeededTimer;
                debug(3, `isSmelting() === true, estimated time: ${estimated}s`);
                safeDelay(doit, estimated * 1e3 / 3);
                return;
            }

            const hasAmount = parseInt(window['var_' + window.smeltOre]),
                  furnaceCapacity = parseInt(window.var_furnaceCapacity);
            if (isNaN(hasAmount) || hasAmount < furnaceCapacity || (ores[window.smeltOre].oil * furnaceCapacity) > parseInt(window.var_oil)) {
                debug(3, `have ${hasAmount} ${window.smeltOre} and ${window.var_oil} oil`);
                // wait even longer when stuff is missing
                safeDelay(doit, 6e3);
                return;
            }

            debug(2, `Starts smelting ${window.var_furnaceCapacity} ${window.smeltOre}!`);
            window.smelt(window.smeltOre, window.var_furnaceCapacity);
            safeDelay(cheatSmelt, ores[window.smeltOre].time * window.var_furnaceCapacity * 1e3);
        }

        safeDelay(doit);
    }

    function clicksItemMod() {
        const orig = window.clicksItem;
        window.clicksItem = (item, ...args) => {
            if (typeof ores[item] === 'object') {
                window.smeltOre = item;
            }
            orig(item, ...args);
        };
    }

    // auto chop trees
    function cheatChop() {
        const treeData = {
            click(n) {
                debug(2, `Auto Chop - Chopping patch ${n}`);
                document.getElementById(`tree-section-${n}`).click();
                setTimeout(() => window.closeDialogue('dialogue-confirm'), 1e3);
            },
            timer(n) {
                return window[`var_treeTimer${n}`] * 1;
            },
            isActive(n) {
                return window[`var_tree${n}`] !== 'none';
            }
        };

        const tasks = [null, false, false, false, false, false, false];
        function chop(num, waitMinSec = 0) {
            if (tasks[num]) return;
            tasks[num] = true;
            safeDelay(() => {
                tasks[num] = false;
                if (!treeData.isActive(num)) return;
                treeData.click(num);
            }, waitMinSec * 1e3);
        }

        function loop() {
            for (let i = 1; i < tasks.length; i++) {
                const timeLeft = treeData.timer(i);
                if (timeLeft < 10) chop(i, timeLeft);
            }
        }
        setInterval(loop, 1e4);
    }

    // auto farm
    const seeds = { // named window.var_${key}Seeds
        // data found at https://anwinity.com/DH3/#farmingBrewing
        redMushroom: {
            // required level to plant
            lvlRequired: 1,

            // stops dying level
            stopsDying: 0,

            // bonemeal required
            bonemeal: 0,

            // time in minutes
            time: 15
        },
        stardust: {
            lvlRequired: 15,
            stopsDying: 0,
            bonemeal: 5,
            time: 30
        },
        dottedGreenLeaf: {
            lvlRequired: 1,
            stopsDying: 15,
            bonemeal: 0,
            time: 15
        },
        greenLeaf: {
            lvlRequired: 10,
            stopsDying: 25,
            bonemeal: 0,
            time: 30
        },
        limeLeaf: {
            lvlRequired: 30,
            stopsDying: 40,
            bonemeal: 5,
            time: 60
        },
        goldLeaf: {
            lvlRequired: 45,
            stopsDying: 55,
            bonemeal: 20,
            time: 120
        },
        crystalLeaf: {
            lvlRequired: 70,
            stopsDying: 80,
            bonemeal: 50,
            time: 300
        },
        // stripedGoldLeaf: {
        //     lvlRequired: 80,
        //     stopsDying: 88,
        //     bonemeal: 120,
        //     time: 600
        // },
        // stripedCrystalLeaf: {
        //     lvlRequired: 90,
        //     stopsDying: 95,
        //     bonemeal: 200,
        //     time: 960
        // },
        tree: {
            lvlRequired: 1,
            stopsDying: 10,
            bonemeal: 0,
            time: 60
        },
        oakTree: {
            lvlRequired: 15,
            stopsDying: 0,
            bonemeal: 5,
            time: 90
        },
        willowTree: {
            lvlRequired: 30,
            stopsDying: 40,
            bonemeal: 10,
            time: 120
        },
        bambooTree: {
            lvlRequired: 40,
            stopsDying: 50,
            bonemeal: 20,
            time: 140
        },
        mapleTree: {
            lvlRequired: 50,
            stopsDying: 60,
            bonemeal: 50,
            time: 220
        },
        lavaTree: {
            lvlRequired: 60,
            stopsDying: 70,
            bonemeal: 60,
            time: 300
        },
        pineTree: {
            lvlRequired: 65,
            stopsDying: 75,
            bonemeal: 75,
            time: 320
        },
        stardustTree: {
            lvlRequired: 70,
            stopsDying: 80,
            bonemeal: 100,
            time: 360
        }
        // appleTree: {
        //     lvlRequired: 45,
        //     stopsDying: 55,
        //     bonemeal: 30,
        //     time: 163
        // },
        // bananaTree: {
        //     lvlRequired: 63,
        //     stopsDying: 73,
        //     bonemeal: 65,
        //     time: 175
        // }
    };

    const plots = [
        window.getItem('plotTimer1') || null,
        window.getItem('plotTimer2') || null,
        window.getItem('plotTimer3') || null,
        window.getItem('plotTimer4') || null,
        window.getItem('plotTimer5') || null,
        window.getItem('plotTimer6') || null
    ];

    function autoFarm() {
        function sortedSeeds() {
            const _seeds = [];
            for (let name of Object.keys(seeds)) {
                const owned = window.getItem(`${name}Seeds`);
                if (owned < 1) continue;
                _seeds.push({name, owned});
            }
            return _seeds.sort((a, b) => a.name.localeCompare(b.name));
        }
    }

    // auto drink stardust potion
    let willConsumeSDPot = false;
    function autoSDPot() {
        const timeLeft = parseInt(window.var_stardustPotionTimer);
        if (timeLeft <= (5 * 60) && parseInt(window.var_stardustPotion) > 0 && !willConsumeSDPot) {
            willConsumeSDPot = true;
            safeDelay(() => {
                window.sendBytes('DRINK=stardustPotion');
                setTimeout(() => {
                    willConsumeSDPot = false;
                }, 3e3);
            });
        }
    }

    // auto boat
    let willUseBoat = false;
    function cheatAnyBoat(str = 'oxygenTank') {
        if (willUseBoat) return;
        const tLeft = window.getItem(str + "Timer");
        if (tLeft > 2) return;
        let task = () => {};
        switch (tLeft) {
            case 0:
                willUseBoat = true;
                task = () => window.sendBytes("SEND_BOAT=" + str);
                break;
            case 1:
                willUseBoat = true;
                task = () => window.sendBytes("COLLECT_BOAT=" + str);
                break;
        }
        safeDelay(() => {
            const timeLeft = window.getItem(str + "Timer");
            if (timeLeft === tLeft) task();
            willUseBoat = false;
        });
    }

    function initCheats() {
        // combat
        if (cheats.autoCombat) {
            setInterval(autoCombat, 1e3);
            debug(1, 'Cheat - Initialized Auto Combat');
        }

        // mining/crafting
        if (cheats.autoSmelt) {
            cheatSmelt();
            clicksItemMod();
            debug(1, 'Cheat - Initialized Auto Smelt');
        }

        // woodcutting
        if (cheats.autoChop) {
            cheatChop();
            debug(1, 'Cheat - Initialized Auto Chop');
        }

        // farming
        if (cheats.autoFarm) {
            autoFarm();
            debug(1, 'Cheat - Initialized Auto Farm');
        }

        // brewing
        if (cheats.autoDrinkSD) {
            setInterval(autoSDPot, 2e3);
            debug(1, 'Cheat - Initialized Auto Drink Stardust Potion');
        }

        // fishing
        if (cheats.autoOxy) {
            setInterval(cheatAnyBoat, 1e3);
            debug(1, 'Cheat - Initialized Auto Oxygen Tank');
        } else if (cheats.autoRowBoat) {
            setInterval(() => cheatAnyBoat('rowBoat'), 1e3);
            debug(1, 'Cheat - Initialized Auto Row Boat');
        } else if (cheats.autoCanoeBoat) {
            setInterval(() => cheatAnyBoat('canoeBoat'), 1e3);
            debug(1, 'Cheat - Initialized Auto Canoe Boat');
        }
    }

    // mod 1 - RightClick axe to chop all trees
    function chopTrees(e) {
        const trees = getEls('#tree-section-woodcutting > center > div');
        e.preventDefault();

        trees.each(el => {
            const timeEl = el.querySelector('.tree-secton-timer');
            const time = timeEl !== null ? timeEl.innerText : '';
            if (time === 'READY') el.click();
        });

        debug(2, 'Chopped all trees');
    }

    // mod 2 - RightClick rake to harvest the crops
    function harvestCrops(e) {
        const trees = getEls('#plot-section-farming > center > div');
        e.preventDefault();

        trees.each(el => {
            const timeEl = el.querySelector('.tree-secton-timer');
            const time = timeEl !== null ? timeEl.innerText : '';
            if (time === 'READY') el.click();
        });

        debug(2, 'Harvested all crops');
    }

    function initMods() {
        if (mods.chopTrees) {
            // init mod 1
            getEls('#item-section-woodcutting-1 [data-tooltip-id=tooltip-axe]')
                .each(el => el.addEventListener('contextmenu', chopTrees));
            debug(1, 'Mod - Initialized Right Click Axe');
        }

        if (mods.harvestCrops) {
            // init mod 2
            getEls('#item-section-farming-1 [data-tooltip-id=tooltip-rake]')
                .each(el => el.addEventListener('contextmenu', harvestCrops));
            debug(1, 'Mod - Initialized Right Click Rake');
        }
    }

    function init() {
        if (typeof window.var_username === "string" && window.var_username.length >= 3) {
            initMods();
            debug(1, 'Initialized Mods');

            initCheats();
            debug(1, 'Initialized Cheats');
        } else {
            setTimeout(init, 2e3);
        }
    }

    setTimeout(init, 2e3);

    /**
     * minor functions for this script to work well with clean code
     */

    function getEls(qSel) {
        let els = document.querySelectorAll(qSel);

        return {
            getRaw() {
                return els;
            },
            getNum(x) {
                return els[x];
            },
            each(fn) {
                els.forEach(fn);
            },
            ids() {
                let res = [];
                for (let key of els) res.push(key.id || 'none');
                return res;
            }
        }
    }

    function debug(lvl, ...msg) {
        if (lvl > debugLevel) {
            return;
        }

        switch (lvl) {
            case 0: // silent
                return;
            case 1: // standard logging
            case 2: // additional logging
                return console.log('DH3 KindaSafe:', ...msg);
            case 3: // debugging
                return console.log('DH3 KindaSafe Debugging:', ...msg);
        }
    }

    function unload(fn) {
        window.addEventListener('unload', fn);
    }

    // A safe cheat delayer
    function safeDelay(fn, minTime = 0) {
        setTimeout(fn, Math.floor(Math.random() * 9e3) + 1e3 + minTime);
    }

    function smeltMax(resource) {
        if (window.isSmelting()) return `Your furnace in unavaliable!`;

        const cost = window.getOilCost(resource);
        if (cost === 0) return `Can't smelt "${resource}"`;

        let cap = window.getItem('furnaceCapacity'),
            max = window.getItem(resource) > cap ? cap : window.getItem(resource);
        if (max === 0) return `You don't have any ${resource}!`;

        const oil = window.getItem('oil');
        max = (max * cost) < oil ? max : Math.min(oil / (max * cost));
        if (max === 0) return `You don't have enought oil to smelt ${resource}`;

        window.smelt(resource, max); // will smelt maximum possible of resource
    }
    window.smeltMax = smeltMax;

    function smeltableResources() {
        const result = [];
        const _ores = Object.keys(ores).reverse();
        for (let ore of _ores) {
            if (ore === window.smeltOre && cheats.autoSmelt) continue;
            if (window.getItem(ore) !== 0) result.push(ore);
        }
        return result;
    }
    window.smeltableResources = smeltableResources;

    function smeltAvaliable() {
        if (window.isSmelting()) return `Your furnace in unavaliable!`;
        const avaliable = smeltableResources();
        if (avaliable.length === 0) return `You don't have any smeltable resources!`;
        smeltMax(avaliable[0]);
    }
    window.smeltAvaliable = smeltAvaliable;
})();