您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
The cheat script for DH3
// ==UserScript== // @name DH3 KindaSafe Cheat // @namespace http://tampermonkey.net/ // @version 1.5.1 // @description The cheat script for DH3 // @author Lasse Brustad // @match dh3.diamondhunt.co // @grant none // @license MIT // ==/UserScript== /* jshint esversion:8 */ (function() { 'use strict'; // config for cheats - "true" = the cheat is on, "!true" or "false" = it's off const cheats = { // auto fight autoCombat: !true, // auto smelt ores autoSmelt: !true, // auto chop trees, it's fully automated with a tiny issue that doesn't matter autoChop: !true, // auto consume potions you have autoDrinkPotions: !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 - START */ // info about the areas, better leave this as it is if you don't know what you're doing areas: { // areaName: { // energy: cost, (1e3 = 1000, 1e4 = 10000, 1_000 = 1000) // points: cost // }, // Fields fields: { energy: 50, points: 900 }, // Forest forest: { energy: 200, points: 1800 }, // Caves caves: { energy: 500, points: 3600 }, // Lava Dungeon lavaDungeon: { energy: 2e3, points: 5400 }, // Northern Fields northernFields: { energy: 5e3, points: 7200 }, // Cemetery cemetery: { energy: 10e3, points: 9e3 }, // Ocean ocean: { energy: 16e3, points: 10800 }, // Castle Dungeon dungeon: { energy: 30e3, points: 13200 }, // Dungeon Hole - !!! MUST BE VERIFIED !!! dungeonHole: { energy: 60e3, points: 25e3 } }, /** * <<< Areas - ENDS */ // replace "fields" with the "areaName" from the list above area: 'fields', // don't teleport when you meet a shiny entity, no matter if the list chosen below says something else // basically, as far as the entity is shiny, fight it forceFightShiny: !true, // kill entity with disintegrate when it's possible? disintegrateMonster: !true, // want to disable any spell? just comment it out with double '//' in front of the line spellOrder: [ //'disabledSpell', 'ghostScan', 'invisibility', 'freeze', 'reflect', 'poison', 'heal', 'fire' ], // replace "fight" with the name of any of the combat list names, only one will be active listName: 'fight', // 'none' here will disable this cheat /** * >>> Combat Lists - START */ // fight: when you meet any of the entities in this list, teleport won't be used fight: [ // example fields 'exampleChicken', // enabled 'exampleRat', // enabled //'exampleBee', // disabled // fields 'chicken', 'rat', 'bee', // forest 'snake', 'ent', //'thief', // caves 'spider', 'bear', 'skeleton', // lava dungeon //'lavaAlien', 'bat', 'fireMage', // NF 'boneHead', //'babyPolarBear', //'mammaPolarBear', //'yeti', // cemetery //'ghost', //'skeletonGhost', 'reaper', // ocean 'pufferFish', 'shark', 'tridentSoldier', /* I'm just guessing on the names below here, so I can't make * sure anything below this point will work at all! */ // castle dungeon 'skeletonMonks', 'dungeonSpider', 'stoneWomen', // dungeon hole 'babyRedDragon', 'babyBlueDragon', 'babyYellowDragon', ], // skip: if you meet entities in this list, teleport will be used skip: [ //'spider' 'skeleton' ], // you can add as many custom lists as you want, but they will work just like "fight" // bonesMode: this is a custom list. when you're fighting in caves, you'll only fight Skeleton bonesMode: [ 'skeleton' ], // magicMode: another custom list. this is a mode for getting magic lvl as fast as // possible, so I recommend fighting fields or forest when using this list magicMode: [], /** * <<< Combat Lists - ENDS */ } // additional config for autoSmelt const smeltConfig = { /** * >>> Intelligent Auto Smelt - START */ // when this is on, the script will use the "ores" list below as the order to smelt ores // if you don't have the minimum amount of the ore, the script will smelt the next ore instead smeltAllAvaliable: true, /** * <<< Intelligent Auto Smelt - ENDS */ /** * >>> Auto Smelt Ore Limits - START */ // this is the list of ores that can be smelted, the order matters for "smeltAllAvaliable", // but the limits will be used all the time, so set it up while thinking about oil/charcoal and time ores: { titanium: { // minimum amount before smelting, higher than capacity will be reduced to maximum capacity min: 5, // maximum amount to autosmelt each time, 0 = no limit/furnace capacity max: 10 }, sand: { min: 100, max: 0 }, promethium: { min: 5, max: 50 }, gold: { min: 20, max: 100 }, silver: { min: 50, max: 250 }, iron: { min: 100, max: 500 }, copper: { min: 200, max: 1000 } } /** * <<< Auto Smelt Ore Limits - ENDS */ } // additional settings for auto drink potions const potionConfig = { /** * >>> Auto Drink Potions - START */ potions: { // what is this, and how to modify it? //namePotion: { // use: true, // you know how this works now, I guess? // time: 300, // the base time for a potion without any bonus time, and in seconds // more: 0 // whenever there's more settings, it might be named well //}, // SD potion stardustPotion: { use: !true, time: 5 * 60 // 5 min }, // Sand potion sandPotion: { use: !true, time: 1 * 60 * 60 // 1 hour }, // FP potion - This name isn't really modified in the source code of DH3, // it's actually combatCooldownPotion, but my code will translate it for ya :D fightPointPotion: { use: !true, time: 10 * 60, // 10 min // use even when you can reset the timer useWhenResetIsReady: !true, // stop using when you have x fight points stopUsingAt: 4000 // max - 4000 FP }, // Compost potion compostPotion: { use: !true, time: 30 * 60 // 30 min }, // Oil potion oilPotion: { use: !true, time: 30 * 60 // 30 min }, // Bone potion bonePotion: { use: !true, time: 2 * 60 * 60 // 2 hours }, // Tree starter potion treeStarterPotion: { use: !true, time: 10 * 60, // 10 min // require at least x empty patches before use emptyTreePatches: 3 }, // Bar potion barPotion: { use: !true, time: 10 * 60 // 10 min }, // Large SD potion largeStardustPotion: { use: !true, time: 5 * 60 // 5 min }, // Furnace potion largeFurnacePotion: { use: !true, time: 30 * 60 // 30 min }, // Pirate potion largePiratePotion: { use: !true, time: 3 * 60 * 60 // 3 hours }, // Rocket speed potion largeRocketSpeedPotion: { use: !true, time: 2 * 60 * 60 // 2 hours }, // Large bar potion largeBarPotion: { use: !true, time: 10 * 60 // 10 min }, // Large oil factory potion largeOilFactoryPotion: { use: !true, time: 30 * 60 // 30 min }, // hugeStardustPotion hugeStardustPotion: { use: !true, time: 5 * 60 // 5 min } } /** * <<< Auto Drink Potions - ENDS */ } // 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 }; /** * >>> Advanced Config Section! * * This is an advanced section for complicated features which requires JS knowledge! */ // just don't touch this, at least, do not remove or edit existing code, it's // critically used in parts of the script this is just to keep active data from // cheat features, like, if stuff is currently enabled or not const cheatData = { combat: { fightSoon: false, error: false, useSpells: false, // I know, the "key" here is kept as it is because of // the history of DH3 KindaSafe Cheat's development, lol disabled: JSON.parse(localStorage.getItem('lbdata-combatError')) || false }, smelting: { queue: false }, brewing: { autoPotions: {} } }; // advanced auto drink potions configs const advancedPotionsExtraCheckFn = { // any potion that requires additional testing should have it's test code here! // DO NOT FORGET: the function should return `true` if the potion is ready for use // there's no need for testing time or if there's any left of the potion // Example with all valid values from "data", but this // example will never run, so it can be left here :) examplePotion(data) { // "name" and "amount" is the same value, but it's easier // to read one over the other depending on the use case const { name, amount, timer } = data.strings; // test if there's any potions left of this potion. // here I would use "amount" instead of "name" if (window.getItem(amount) === 0) { // auto brew a new one if you're empty? ask for help if it's wanted (: // return false because you know you're out of this potion anyway here return false; } // test the time left. using 60 sec in this example if (window.getItem(timer) < 60) { // ignore the built-in time left check? data.ignoreTime(); // return true to not stop the chance for the potion to be used return true; } // I really don't recommend it, but you can access these as well: // WARNING! calling this will result in a crash loop! data.loop(); // it's the main loop that is running every 1 sec // WARNING! calling this might put your account in a high // risk of being detected for cheats which can lead to ban! data.getStrings('examplePotion'); // modifies "data.strings" // this is useless here, just saying data.getRealPotName('examplePotion'); // returns the actual string used to drink the potion // this one is the handler that will drink the potion, // and using it here is without any built-in security testing data.handler(); // want to use it? then you're on your own when developing the security! }, // FP potion fightPointPotion(data) { // if auto combat is turned off for some reason, don't use FP potion if (cheatData.combat.disabled || !cheats.autoCombat) return false; // get additional settings const { useWhenResetIsReady, stopUsingAt } = potionConfig.potions.fightPointPotion; const stopAt = window.getItem('maxFightPoints') - Math.abs(stopUsingAt); // if you're less than 4k FP away from full FP, don't use FP potion if (stopAt <= 0 && window.getItem('fightPoints') >= stopAt) return false; // check if FP reset is ready if (window.getItem('heroCooldownResetTimer') == 0) return useWhenResetIsReady; // otherwise, return true, that's it return true; }, // Tree starter potion treeStarterPotion(data) { const { amount, timer } = data.strings; // don't use more than 1 if (window.getItem(timer) != 0) return false; // count unlocked patches let unlockedPatches = []; for (let i = 1; i <= 6; i++) { const treePatch = window.getItem(`treeUnlocked${i}`); if (treePatch) unlockedPatches.push(`tree${i}`); } // require 3 woodcutting patches to be empty let emptyPatches = 0; for (let key of unlockedPatches) { emptyPatches += window.getItem(key) === 'none'; // check if it's x empty patches now if (emptyPatches === potionConfig.potions.treeStarterPotion.emptyTreePatches) { return true; } } return false; } } /** * <<< Advanced Config Section! */ /** * >>> INFO * * 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? I don't ;) * * <<< INFO */ // logging / debugging const debugLevels = [ 'silent', 'normal logging', 'additional logging', 'debugging' ]; let debugLevel = parseInt(localStorage.getItem('lbdata-debug')) || 1; function debug(lvl, ...msg) { if (lvl > debugLevel) { return; } window.lastMessage = msg.join(' - '); 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); } } window.setDebugLevel = (lvl = debugLevel) => { if (typeof lvl !== 'number' || (lvl < 0 && lvl >= debugLevels.length)) { console.log(`Didn't change the logging level, it has to be a number ` + `between 0 and ${debugLevels.length - 1}.`); 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]}`); } } async function wait(ms) { await new Promise(resolve => { setTimeout(resolve, ms); }); } // auto combat function castSpell(spell) { // stole the spell safety check from DH3 Fixed #yolo - need improvements! if( document.getElementById("navigation-right-combat-fighting").style.display != "none" && document.getElementById(`combat-spell-${spell}`).style.display != "none" ){ document.getElementById(`combat-spell-${spell}`).click(); return true; } } async function inCombat() { // avoid spamming if (!cheatData.combat.useSpells) return; cheatData.combat.fightSoon = false; cheatData.combat.useSpells = false; // get the entity name const entity = window.getItem('monsterName'); // stop here if it's in castle! if (entity.startsWith('knight')) return; // use all spells in choosen order for (let spell of combatConfig.spellOrder) { await wait(Math.floor(Math.random() * 150) + 100); castSpell(spell); } // if the entity isn't in the list, cast teleport! const { listName, forceFightShiny } = combatConfig; const noTeleport = combatConfig[listName]; const isCustomList = !['fight', 'skip', 'none'].includes(listName); if (forceFightShiny && !!window.getItem('shinyMonster')) { await wait(Math.floor(Math.random() * 150) + 100); castSpell('disintegrate'); // no, no, NO! don't teleport if the entity is shiny! return; } // teleport? if ( (!noTeleport.includes(entity) && listName === 'fight') || (noTeleport.includes(entity) && listName === 'skip') || (noTeleport.includes(entity) && isCustomList) ) { await wait(Math.floor(Math.random() * 150) + 350); castSpell('teleport'); } // disintegrate? if (combatConfig.disintegrateMonster) { await wait(Math.floor(Math.random() * 150) + 100); castSpell('disintegrate'); } } function autoCombat() { const { fightSoon, error, disabled } = cheatData.combat; // damn, didn't expect this function to require that much anti-fail testing if (error || disabled) return; if (window.getItem('monsterName') != 'none' && window.getItem('combatCountDown') == 0) { inCombat(); return; } if (fightSoon || window.getItem('teleportCooldown') != 0) { return; } if (!Object.keys(combatConfig.areas).includes(combatConfig.area)) { cheatData.combat.error = true; debug(2, `The script has no area called ${combatConfig.area} registered!`); return; } const { points, energy } = combatConfig.areas[combatConfig.area]; if (window.getAreaTimer(points) > window.getItem('fightPoints')) { return; } if (window.getEnergyReduction(energy) > window.getItem('energy')) { return; } // FIGHT!!! cheatData.combat.fightSoon = true; cheatData.combat.useSpells = true; safeDelay(() => { const { points, energy } = combatConfig.areas[combatConfig.area]; if ( window.getItem('monsterName') != 'none' || window.getItem('teleportCooldown') >= 1 || window.getAreaTimer(points) > window.getItem('fightPoints') || window.getEnergyReduction(energy) > window.getItem('energy') ) { cheatData.combat.fightSoon = false; cheatData.combat.useSpells = false; return; } window.sendBytes(`FIGHT=${combatConfig.area}`); }) } // settings for autoCombat while playing window.setCombatStatus = (state = null) => { if (typeof state === 'boolean') { cheatData.combat.disabled = !state; console.log(`Set the state to ${state ? 'active' : 'inactive'}`); } else if (typeof state === 'string' && typeof combatConfig.areas[state] === 'object') { combatConfig.area = state; console.log(`Set the area to ${state}`); } else if (state === null) { console.log(`Current state: Fighting ${combatConfig.area} is ` + `${!cheatData.combat.disabled ? 'active' : 'inactive'}`); } } unload(e => { localStorage.setItem('lbdata-combatError', JSON.stringify(cheatData.combat.disabled)); localStorage.setItem('lbdata-combatArea', combatConfig.area); }); // auto smelt ores window.smeltOre = localStorage.getItem('lbdata-smeltOre') || 'copper'; // the ore to smelt if (cheats.autoSmelt) { unload(e => { localStorage.setItem('lbdata-smeltOre', window.smeltOre); }); } // cheatSmelt function burningResource(resource) { const oilCost = window.getOilCost(resource) || 0; const charcoalCost = window.getCharcoalCost(resource) || 0; if (oilCost == 0 && charcoalCost == 0) { debug(2, `Can't smelt "${resource}"`); return null; } return { burnable: ['oil', 'charcoal'][+(oilCost == 0)], amount: Math.max(oilCost, charcoalCost) }; } function getFirstSmeltable() { let data = null, usable = { oil: window.getItem('oil'), charcoal: window.getItem('charcoal') }; for (let ore in smeltConfig.ores) { const owned = window.getItem(ore); let { min } = smeltConfig.ores[ore]; if (owned < min) continue; const burningInfo = burningResource(ore); if (burningInfo === null) continue; const minCost = burningInfo.amount * min; if (minCost > usable[burningInfo.burnable]) continue; data = { ore, type: burningInfo.burnable, cost: burningInfo.amount } break; } // done! return data; } class Smeltable { constructor(ore, cost = null, type = null) { // type of ore this.ore = ore; if (type !== null && cost !== null) { // oil / charcoal this.type = type; this.cost = cost; } else { const { burnable, amount } = burningResource(ore); this.type = burnable; this.cost = amount; } } test() { // don't try, just don't - security first! if (window.isSmelting()) return false; // prepare needed data const burn = window.getItem(this.type); const capacity = window.getItem('furnaceCapacity'); const owned = window.getItem(this.ore); const data = smeltConfig.ores[this.ore]; let limit = data.max; if (limit < data.min) { limit = capacity; } // prepare variables to be set let amount = Math.min( owned, limit, capacity, Math.floor(burn / this.cost) ); // last test to pass if (amount < data.min) { debug(3, 'Failed the last test, crap!', { amount, limit, min: data.min, max: data.max }); return false; } return amount } smelt() { // run the test const amount = this.test(); if (typeof amount !== 'number') { return amount; } // smelt it! window.smelt(this.ore, amount); } } function cheatSmelt() { if (window.isSmelting() || cheatData.smelting.queue) return; let smeltable; if (smeltConfig.smeltAllAvaliable) { const { ore, cost, type } = getFirstSmeltable(); smeltable = new Smeltable(ore, cost, type); } else { smeltable = new Smeltable(window.smeltOre) } cheatData.smelting.queue = smeltable.test() != false; safeDelay(() => { smeltable.smelt(); cheatData.smelting.queue = !true; }); } const clicksItem = window.clicksItem; function clicksItemMod(modified = true) { if (modified) { window.clicksItem = (item, ...args) => { if (typeof smeltConfig.ores[item] === 'object') { window.smeltOre = item; } clicksItem(item, ...args); }; } else { window.clicksItem = clicksItem; } } window.clicksItemMod = clicksItemMod; // 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 drink potions class AutoDrinkPotion { constructor(potionName, potionTime, extraCheckFn = () => false) { this.getStrings = this.getStrings.bind(this); this.getRealPotName = this.getRealPotName.bind(this); this.handler = this.handler.bind(this); this.getPotionTime = this.getPotionTime.bind(this); this.loop = this.loop.bind(this); this.extraCheck = extraCheckFn; this.potionTime = potionTime; this.willDrink = false; const realName = this.getRealPotName(potionName); this.strings = this.getStrings(realName); } getStrings(str) { str = this.getRealPotName(str); const strings = { name: str, amount: str, timer: `${str}Timer` }; return strings; } getRealPotName(name) { const potionsWithWrongName = { fightPointPotion: 'combatCooldownPotion' }; if (typeof potionsWithWrongName[name] !== 'string') { return name; } else { return potionsWithWrongName[name]; } } async handler() { this.willDrink = true; const { name, timer } = this.strings; await wait(Math.floor(Math.random() * 4e3) + 1e3); window.sendBytes(`DRINK=${name}`); await wait(5e3); this.willDrink = false; } getPotionTime() { const modifierStr = window.getBrewingKitDataTypes()[3]; if (modifierStr === '0%') { return this.potionTime } return this.potionTime * (parseInt(modifierStr) / 100 + 1); } loop() { if (this.willDrink) return; let ignoreTime = false; const self = Object.assign(this, { ignoreTime: () => { ignoreTime = true; } }); if (!this.extraCheck(self)) return; const { amount, timer } = this.strings; if (window.getItem(amount) === 0) return; const potionTimeLeft = window.getItem(timer); const potionTimeModified = this.getPotionTime(); if (potionTimeLeft < potionTimeModified || ignoreTime) { this.handler(); } } } for (let potion in potionConfig.potions) { const { use, time } = potionConfig.potions[potion]; const extraCheckFn = advancedPotionsExtraCheckFn[potion] || (fn => true); // will not use if (!use) continue; // will use const obj = new AutoDrinkPotion(potion, time, extraCheckFn); cheatData.brewing.autoPotions[potion] = obj; } // 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) { setInterval(cheatSmelt, 1e3); clicksItemMod(smeltConfig.smeltAllAvaliable); debug(1, 'Cheat - Initialized Auto Smelt'); } // woodcutting if (cheats.autoChop) { cheatChop(); debug(1, 'Cheat - Initialized Auto Chop'); } // farming // brewing if (cheats.autoDrinkPotions) { for (let autoPotion in cheatData.brewing.autoPotions) { debug(3, `Cheat - ${autoPotion} should be enabled`) setInterval(cheatData.brewing.autoPotions[autoPotion].loop, 1e3); } debug(1, 'Cheat - Initialized Auto Drink Potions'); } // fishing if (cheats.autoOxy) { setInterval(cheatAnyBoat, 1e3); debug(1, 'Cheat - Initialized Auto Oxygen Tank'); } else if (cheats.autoRowBoat) { setInterval(cheatAnyBoat, 1e3, 'rowBoat'); debug(1, 'Cheat - Initialized Auto Row Boat'); } else if (cheats.autoCanoeBoat) { setInterval(cheatAnyBoat, 1e3, 'canoeBoat'); debug(1, 'Cheat - Initialized Auto Canoe Boat'); } // cooking } // 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 makeSureUserGetDetected() { if (!['anarox'].includes(window.getItem('username'))) { return; } // try to get users market banned! usernames are specified setInterval(window.sendBytes, 100, 'REFRESH_TRADABLES'); debug(2, `You'll be market banned with this!`); } function init() { if (typeof window.var_username === "string" && window.var_username.length >= 3) { initMods(); debug(1, 'Initialized Mods'); initCheats(); debug(1, 'Initialized Cheats'); makeSureUserGetDetected(); debug(2, 'TEST'); } 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 unload(fn) { window.addEventListener('unload', fn); } // A safe cheat delayer function safeDelay(fn, minTime = 0) { setTimeout(fn, Math.floor(Math.random() * 4e3) + 1e3 + minTime); } })();