├── LeftClickEtherwarp ├── data │ └── .gitignore ├── metadata.json └── index.js ├── .gitattributes ├── IHateCarpet ├── metadata.json └── index.js ├── ZeroPingEtherwarp ├── metadata.json ├── utils.js └── index.js ├── PianoProdigy ├── metadata.json └── index.js ├── AutoCroesus ├── metadata.json ├── data │ └── defaults │ │ ├── always_buy.txt │ │ └── worthless.txt ├── build.sh ├── util │ ├── logger.js │ ├── help_command.js │ ├── prices.js │ └── utils.js ├── extra │ └── lootLogs.js └── index.js ├── ChocolateHelper ├── metadata.json ├── util │ ├── item_scrapers │ │ ├── factoryItem.js │ │ ├── timeTower.js │ │ ├── chocolateItem.js │ │ ├── jackrabbit.js │ │ ├── chocolateProduction.js │ │ └── employee.js │ ├── scraper.js │ ├── config.js │ └── utils.js ├── features │ ├── noItemPickup.js │ ├── guiElements.js │ ├── clickMe.js │ ├── autoClicker.js │ ├── bestUpgrade.js │ ├── autoPickUpHoppity.js │ ├── chocolateEggs.js │ └── rabbitsCounter.js ├── index.js └── gui │ ├── rabbits.js │ └── eggs.js ├── .gitignore └── README.md /LeftClickEtherwarp/data/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /IHateCarpet/metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "IHateCarpet", 3 | "description": "Lagback rates decrease by 57467%", 4 | "creator": "UnclaimedBloom6", 5 | "entry": "index.js" 6 | } -------------------------------------------------------------------------------- /ZeroPingEtherwarp/metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ZeroPingEtherwarp", 3 | "creator": "UnclaimedBloom6", 4 | "version": "0.1.0", 5 | "entry": "index.js", 6 | "requires": [ 7 | "PogData" 8 | ] 9 | } -------------------------------------------------------------------------------- /PianoProdigy/metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "PianoProdigy", 3 | "description": "Ping based games are very cool!", 4 | "creator": "UnclaimedBloom6", 5 | "entry": "index.js", 6 | "requires": [ 7 | "PogData" 8 | ] 9 | } -------------------------------------------------------------------------------- /AutoCroesus/metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "AutoCroesus", 3 | "description": "I hate croesus", 4 | "creator": "UnclaimedBloom6", 5 | "entry": "index.js", 6 | "requires": [ 7 | "PogData", 8 | "requestV2", 9 | "PromiseV2" 10 | ] 11 | } -------------------------------------------------------------------------------- /ChocolateHelper/metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ChocolateHelper", 3 | "description": "Obesity", 4 | "creator": "UnclaimedBloom6", 5 | "entry": "index.js", 6 | "requires": [ 7 | "BloomCore", 8 | "PogData", 9 | "Vigilance" 10 | ] 11 | } -------------------------------------------------------------------------------- /LeftClickEtherwarp/metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "LeftClickEtherwarp", 3 | "description": "Etherwarps when left clicking an etherwarp item!", 4 | "creator": "UnclaimedBloom6", 5 | "entry": "index.js", 6 | "requires": [ 7 | "PogData" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /AutoCroesus/data/defaults/always_buy.txt: -------------------------------------------------------------------------------- 1 | NECRON_HANDLE 2 | DARK_CLAYMORE 3 | FIRST_MASTER_STAR 4 | SECOND_MASTER_STAR 5 | THIRD_MASTER_STAR 6 | FOURTH_MASTER_STAR 7 | FIFTH_MASTER_STAR 8 | SHADOW_FURY 9 | SHADOW_WARP_SCROLL 10 | IMPLOSION_SCROLL 11 | WITHER_SHIELD_SCROLL 12 | DYE_LIVID -------------------------------------------------------------------------------- /AutoCroesus/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd "$(dirname "$0")" 4 | cd ../ 5 | 6 | rm -rf AutoCroesus/out/ 7 | 8 | mkdir AutoCroesus/out 9 | mkdir AutoCroesus/out/AutoCroesus 10 | 11 | rsync -rv --exclude-from=".gitignore"\ 12 | --exclude="build.sh"\ 13 | --exclude="data/*.json"\ 14 | --exclude="data/*.txt"\ 15 | --exclude="out/"\ 16 | AutoCroesus/* AutoCroesus/out/AutoCroesus/ 17 | 18 | cd AutoCroesus/out/ 19 | zip -r AutoCroesus.zip AutoCroesus/ 20 | rm -r AutoCroesus -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Zero Ping Etherwarp 2 | ZeroPingEtherwarp/data.json 3 | 4 | # Left Click Etherwarp 5 | LeftClickEtherwarp/data/data.json 6 | 7 | # Chocolate Helper 8 | ChocolateHelper/data/ 9 | ChocolateHelper/debug/ 10 | ChocolateHelper/upgrades/ 11 | 12 | # Auto Croesus 13 | AutoCroesus/out 14 | AutoCroesus/data/always_buy.txt 15 | AutoCroesus/data/worthless.txt 16 | AutoCroesus/data/binValues.json 17 | AutoCroesus/data/bzValues.json 18 | AutoCroesus/data/data.json 19 | AutoCroesus/data/items.json 20 | AutoCroesus/data/runLoot.txt 21 | AutoCroesus/data/log.txt 22 | -------------------------------------------------------------------------------- /ChocolateHelper/util/item_scrapers/factoryItem.js: -------------------------------------------------------------------------------- 1 | import { decodeNumeral } from "../../../BloomCore/utils/Utils" 2 | import { data } from "../utils" 3 | 4 | 5 | export const doFactoryItem = (itemName, lore, slot) => { 6 | const match = itemName.match(/^§6Chocolate Factory ([IXVCM]+)$/) 7 | if (!match) return false 8 | 9 | data.factoryTier = decodeNumeral(match[1]) 10 | data.factoryItemSlot = slot 11 | 12 | for (let line of lore) { 13 | if (line !== "§5§o§eClick to prestige!") continue 14 | data.canPrestige = true 15 | break 16 | } 17 | 18 | return true 19 | } -------------------------------------------------------------------------------- /ChocolateHelper/features/noItemPickup.js: -------------------------------------------------------------------------------- 1 | import { sendWindowClick } from "../../BloomCore/utils/Utils" 2 | import config from "../util/config" 3 | import { data } from "../util/utils" 4 | 5 | 6 | register("guiMouseClick", (x, y, btn, gui, event) => { 7 | if (!data.inFactory || btn !== 0 || !config.noItemPickup) return 8 | const slot = Client.currentGui.getSlotUnderMouse() 9 | if (!slot) return 10 | 11 | const ind = slot.getIndex() 12 | // ChatLib.chat(`btn: ${btn}, slot: ${ind}`) 13 | cancel(event) 14 | const inv = Player.getContainer() 15 | sendWindowClick(inv.getWindowId(), ind, 0) 16 | }) 17 | -------------------------------------------------------------------------------- /AutoCroesus/util/logger.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Mainly used to copy the lore of items to make it easier to debug when other users are having problems with the module 4 | */ 5 | export default new class Logger { 6 | constructor() { 7 | this.str = "" 8 | } 9 | 10 | push(str) { 11 | if (this.str !== "") { 12 | this.str += "\n" 13 | } 14 | 15 | this.str += str 16 | } 17 | 18 | copy() { 19 | if (this.str) { 20 | ChatLib.command(`ct copy ${this.str}`, true) 21 | return 22 | } 23 | 24 | const str = FileLib.read("AutoCroesus", "data/log.txt") 25 | ChatLib.command(`ct copy ${str}`, true) 26 | } 27 | 28 | clear() { 29 | this.str = "" 30 | } 31 | 32 | write() { 33 | FileLib.write("AutoCroesus", "data/log.txt", this.str, true) 34 | } 35 | } -------------------------------------------------------------------------------- /ChocolateHelper/util/item_scrapers/timeTower.js: -------------------------------------------------------------------------------- 1 | import { decodeNumeral } from "../../../BloomCore/utils/Utils" 2 | import config from "../config" 3 | import { data, updateBestUpgrade } from "../utils" 4 | 5 | export const doTimeTowerItem = (itemName, lore, slot) => { 6 | const nameMatch = itemName.match(/^§dTime Tower\s*(.+)?$/) 7 | if (!nameMatch) return false 8 | 9 | let level = 0 10 | const [_, tierNumeral] = nameMatch 11 | if (tierNumeral) level = decodeNumeral(tierNumeral) 12 | 13 | data.timeTower.level = level 14 | data.timeTower.slot = slot 15 | data.timeTower.cost = null 16 | 17 | 18 | for (let line of lore) { 19 | let costMatch = line.match(/^§5§o§6([\d,\.]+) Chocolate$/) 20 | if (!costMatch) continue 21 | 22 | data.timeTower.cost = parseInt(costMatch[1].replace(/,/g, "")) 23 | break 24 | } 25 | 26 | if (data.fullyLoaded && config.upgradeIncludeTimeTower) updateBestUpgrade(true) 27 | 28 | return true 29 | } -------------------------------------------------------------------------------- /ChocolateHelper/util/item_scrapers/chocolateItem.js: -------------------------------------------------------------------------------- 1 | import { data } from "../utils" 2 | 3 | export const doChocolateItem = (itemName, lore, slot) => { 4 | const chocolateMatch = itemName.match(/^§e([\d,\.]+) §6Chocolate$/) 5 | if (!chocolateMatch) return false 6 | 7 | data.chocolate = parseFloat(chocolateMatch[1].replace(/,/g, "")) 8 | data.chocolateSlot = slot 9 | 10 | for (let line of lore) { 11 | 12 | let cpsMatch = line.match(/^§5§o§6([\d,\.]+) §8per second$/) 13 | if (cpsMatch) { 14 | data.cps = parseFloat(cpsMatch[1].replace(/,/g, "")) 15 | data.baseCps = data.cps / data.cpsMultiplier 16 | continue 17 | } 18 | 19 | let allTimeChocolateMatch = line.match(/^§5§o§7All-time Chocolate: §6([\d,\.]+)$/) 20 | if (allTimeChocolateMatch) { 21 | data.allTimeChocolate = parseFloat(allTimeChocolateMatch[1].replace(/,/g, "")) 22 | continue 23 | } 24 | } 25 | 26 | return true 27 | } -------------------------------------------------------------------------------- /ChocolateHelper/util/item_scrapers/jackrabbit.js: -------------------------------------------------------------------------------- 1 | import { decodeNumeral } from "../../../BloomCore/utils/Utils" 2 | import config from "../config" 3 | import { data, updateBestUpgrade } from "../utils" 4 | 5 | export const doJackrabbitItem = (itemName, lore, slot) => { 6 | const nameMatch = itemName.match(/^§dCoach Jackrabbit\s*(.+)?$/) 7 | if (!nameMatch) return false 8 | 9 | let level = 0 10 | const [_, tierNumeral] = nameMatch 11 | if (tierNumeral) level = decodeNumeral(tierNumeral) 12 | 13 | 14 | data.jackrabbit.level = level 15 | data.jackrabbit.slot = slot 16 | data.jackrabbit.cost = null 17 | 18 | for (let line of lore) { 19 | let costMatch = line.match(/^§5§o§6([\d,\.]+) Chocolate$/) 20 | if (!costMatch) continue 21 | 22 | data.jackrabbit.cost = parseInt(costMatch[1].replace(/,/g, "")) 23 | break 24 | } 25 | 26 | if (data.fullyLoaded && config.upgradeIncludeJackrabbit) updateBestUpgrade(true) 27 | 28 | return true 29 | } -------------------------------------------------------------------------------- /ChocolateHelper/features/guiElements.js: -------------------------------------------------------------------------------- 1 | import { fn } from "../../BloomCore/utils/Utils" 2 | import config from "../util/config" 3 | import { data, highlightSlot } from "../util/utils" 4 | 5 | 6 | // Chocolate display and CPS 7 | register("renderSlot", (slot, gui, event) => { 8 | if (!data.inFactory || !config.showChocolate) return 9 | if (slot.getIndex() !== 13) return 10 | 11 | 12 | const lines = [ 13 | `&e${fn(data.chocolate)}`, 14 | `&6${fn(data.cps)}` 15 | ] 16 | 17 | Renderer.translate(slot.getDisplayX()+8, slot.getDisplayY(), 800) 18 | Renderer.retainTransforms(true) 19 | 20 | lines.forEach((line, i) => { 21 | let lineWidth = Renderer.getStringWidth(line) 22 | Renderer.drawString(line, -lineWidth/2, i*10-20) 23 | }) 24 | Renderer.retainTransforms(false) 25 | Renderer.finishDraw() 26 | }) 27 | 28 | // Prestige highlight 29 | register("renderSlot", (slot) => { 30 | if (!config.showPrestigeHighlight || !data.canPrestige || data.factoryItemSlot == null || slot.getIndex() !== data.factoryItemSlot) return 31 | 32 | highlightSlot(slot, 0, 255, 0, 255) 33 | }) -------------------------------------------------------------------------------- /ChocolateHelper/features/clickMe.js: -------------------------------------------------------------------------------- 1 | import { onSetSlotReceived } from "../../BloomCore/utils/Events" 2 | import { sendWindowClick } from "../../BloomCore/utils/Utils" 3 | import config from "../util/config" 4 | import { data } from "../util/utils" 5 | 6 | const clickMeItems = [ 7 | /^§6§lGolden Rabbit §8- .+$/, 8 | /^§e§lCLICK ME!$/ 9 | ] 10 | 11 | onSetSlotReceived((item, slot) => { 12 | if (!item || !data.inFactory || (!config.autoClickMe && !config.clickMeSound)) return 13 | 14 | const ctItem = new Item(item) 15 | const itemName = ctItem.getName() 16 | if (itemName.includes('CAUGHT!')) ChatLib.chat(`&6[ChocolateHelper] &aClicked&r ${itemName}`) 17 | if (!clickMeItems.some(a => a.test(itemName))) return 18 | 19 | if (config.clickMeSound) { 20 | World.playSound("random.successful_hit", 1, 0) 21 | } 22 | 23 | if (!config.autoClickMe) return 24 | 25 | setTimeout(() => { 26 | const inv = Player.getContainer() 27 | let slotToClick = slot 28 | if (!inv || !data.inFactory) return 29 | 30 | sendWindowClick(inv.getWindowId(), slotToClick, 0) 31 | }, Math.floor(500 + Math.random() * 2000)); 32 | }) 33 | -------------------------------------------------------------------------------- /AutoCroesus/data/defaults/worthless.txt: -------------------------------------------------------------------------------- 1 | DUNGEON_DISC_5 2 | DUNGEON_DISC_4 3 | DUNGEON_DISC_3 4 | DUNGEON_DISC_2 5 | DUNGEON_DISC_1 6 | MAXOR_THE_FISH 7 | STORM_THE_FISH 8 | GOLDOR_THE_FISH 9 | ENCHANTMENT_ULTIMATE_NO_PAIN_NO_GAIN_1 10 | ENCHANTMENT_ULTIMATE_NO_PAIN_NO_GAIN_2 11 | ENCHANTMENT_ULTIMATE_NO_PAIN_NO_GAIN_3 12 | ENCHANTMENT_ULTIMATE_NO_PAIN_NO_GAIN_4 13 | ENCHANTMENT_ULTIMATE_NO_PAIN_NO_GAIN_5 14 | ENCHANTMENT_ULTIMATE_COMBO_1 15 | ENCHANTMENT_ULTIMATE_COMBO_2 16 | ENCHANTMENT_ULTIMATE_COMBO_3 17 | ENCHANTMENT_ULTIMATE_COMBO_4 18 | ENCHANTMENT_ULTIMATE_COMBO_5 19 | ENCHANTMENT_ULTIMATE_BANK_1 20 | ENCHANTMENT_ULTIMATE_BANK_2 21 | ENCHANTMENT_ULTIMATE_BANK_3 22 | ENCHANTMENT_ULTIMATE_BANK_4 23 | ENCHANTMENT_ULTIMATE_BANK_5 24 | ENCHANTMENT_ULTIMATE_JERRY_1 25 | ENCHANTMENT_ULTIMATE_JERRY_2 26 | ENCHANTMENT_ULTIMATE_JERRY_3 27 | ENCHANTMENT_ULTIMATE_JERRY_4 28 | ENCHANTMENT_ULTIMATE_JERRY_5 29 | ENCHANTMENT_FEATHER_FALLING_6 30 | ENCHANTMENT_FEATHER_FALLING_7 31 | ENCHANTMENT_FEATHER_FALLING_8 32 | ENCHANTMENT_FEATHER_FALLING_9 33 | ENCHANTMENT_FEATHER_FALLING_10 34 | ENCHANTMENT_INFINITE_QUIVER_6 35 | ENCHANTMENT_INFINITE_QUIVER_7 36 | ENCHANTMENT_INFINITE_QUIVER_8 37 | ENCHANTMENT_INFINITE_QUIVER_9 38 | ENCHANTMENT_INFINITE_QUIVER_10 -------------------------------------------------------------------------------- /ChocolateHelper/features/autoClicker.js: -------------------------------------------------------------------------------- 1 | import { sendWindowClick } from "../../BloomCore/utils/Utils" 2 | import config from "../util/config" 3 | import { data, highlightSlot } from "../util/utils" 4 | 5 | const acKey = new KeyBind("Click!", Keyboard.KEY_NONE, "Chocolate!") 6 | 7 | let lastClick = Date.now() 8 | let isClicking = false 9 | let lastKeyState = false 10 | 11 | register("tick", () => { 12 | // The keybind logic 13 | if (!data.inFactory) { 14 | isClicking = false 15 | lastKeyState = false 16 | return 17 | } 18 | const currKeyState = Keyboard.isKeyDown(acKey.getKeyCode()) 19 | 20 | if (currKeyState && !lastKeyState) isClicking = !isClicking 21 | lastKeyState = currKeyState 22 | 23 | // And the actual autoclicker 24 | if (!isClicking || Date.now() - lastClick < 1000 / config.autoClickerCps || data.chocolateSlot == null || data.isBuyingUpgrades) return 25 | lastClick = Date.now() 26 | const inv = Player.getContainer() 27 | // inv.click(13, false, "MIDDLE") 28 | sendWindowClick(inv.getWindowId(), data.chocolateSlot, 0) 29 | }) 30 | 31 | register("renderSlot", (slot, gui, event) => { 32 | if (slot.getIndex() !== 13 || !data.inFactory || !isClicking) return 33 | 34 | let r = 0 35 | if (data.isBuyingUpgrades) r = 255 36 | highlightSlot(slot, 255, 255, 0, 125) 37 | }) -------------------------------------------------------------------------------- /ChocolateHelper/util/item_scrapers/chocolateProduction.js: -------------------------------------------------------------------------------- 1 | import { data } from "../utils" 2 | 3 | export const doChocolateProductionItem = (itemName, lore) => { 4 | if (itemName !== "§6Chocolate Production") return false 5 | 6 | for (let line of lore) { 7 | let totalMultiMatch = line.match(/^§5§o§7Total Multiplier: §6([\d\,.]+)x$/) 8 | if (totalMultiMatch) { 9 | let [_, multiplier] = totalMultiMatch 10 | data.cpsMultiplier = parseFloat(multiplier) 11 | data.baseCps = data.cps / data.cpsMultiplier 12 | continue 13 | } 14 | 15 | // Cps Sources 16 | // https://regex101.com/r/NH3uID/1 17 | let cpsSourceMatch = line.match(/^§5§o §6\+([\d\.,]+) §8\((?:§.)*(.+)§8\)$/) 18 | if (cpsSourceMatch) { 19 | let [_, cps, source] = cpsSourceMatch 20 | data.cpsSources[source] = parseFloat(cps.replace(/,/g, "")) 21 | continue 22 | } 23 | 24 | // Multipliers 25 | // https://regex101.com/r/zOhycJ/1 26 | let multiplierMatch = line.match(/^§5§o §6\+([\d\.]+)x §8\((?:§.)*(.+)§8\)$/) 27 | if (multiplierMatch) { 28 | let [_, multiplier, source] = multiplierMatch 29 | data.productionMultipliers[source] = parseFloat(multiplier) 30 | continue 31 | } 32 | } 33 | 34 | return true 35 | } -------------------------------------------------------------------------------- /IHateCarpet/index.js: -------------------------------------------------------------------------------- 1 | const BlockPoss = Java.type("net.minecraft.util.BlockPos") 2 | const setAir = (x, y, z) => World.getWorld().func_175698_g(new BlockPoss(x, y, z)) 3 | const setBlockState = (x, y, z, state) => World.getWorld().func_175656_a(new BlockPoss(x, y, z), state) 4 | 5 | let carpets = [] 6 | register("tick", () => { 7 | const pX = Math.floor(Player.getX()) 8 | const pY = Math.floor(Player.getY()) 9 | const pZ = Math.floor(Player.getZ()) 10 | for (let x = pX-3; x < pX+2; x++) { 11 | for (let y = pY-3; y < pY+2; y++) { 12 | for (let z = pZ-3; z < pZ+2; z++) { 13 | let block = World.getBlockAt(x, y, z) 14 | if (!block || block.type.getID() !== 171) continue 15 | let blockBelow = World.getBlockAt(x, y-1, z) 16 | if (!blockBelow) continue 17 | carpets.push([block.getState(), x, y, z]) 18 | setAir(x, y, z) 19 | } 20 | } 21 | } 22 | // ChatLib.chat(carpets.length) 23 | }) 24 | 25 | register("tick", () => { 26 | const x0 = Math.floor(Player.getX()) 27 | const y0 = Math.floor(Player.getY()) 28 | const z0 = Math.floor(Player.getZ()) 29 | for (let i = 0; i < carpets.length; i++) { 30 | let [oldState, x1, y1, z1] = carpets[i] 31 | if (Math.abs(x1 - x0) > 3 || Math.abs(y1 - y0) > 3 || Math.abs(z1 - z0) > 3) { 32 | setBlockState(x1, y1, z1, oldState) 33 | carpets.splice(i, 1) 34 | } 35 | } 36 | }) 37 | -------------------------------------------------------------------------------- /ChocolateHelper/features/bestUpgrade.js: -------------------------------------------------------------------------------- 1 | import { sendWindowClick } from "../../BloomCore/utils/Utils" 2 | import config from "../util/config" 3 | import { data, highlightSlot, updateBestUpgrade } from "../util/utils" 4 | 5 | let lastClick = null 6 | let waitingForUpgrade = false 7 | 8 | // Rendering 9 | register("renderSlot", (slot) => { 10 | if (!config.showBestUpgrade || !data.bestUpgrade) return 11 | 12 | if (slot.getIndex() !== data.bestUpgrade.slot) return 13 | 14 | const canAfford = data.bestUpgrade.cost < data.chocolate 15 | 16 | let [r, g, b] = canAfford ? [0, 255, 0] : [255, 0, 0] 17 | 18 | highlightSlot(slot, r, g, b, 255) 19 | }) 20 | 21 | const resetWaiting = () => { 22 | waitingForUpgrade = false 23 | } 24 | 25 | register("chat", resetWaiting).setCriteria(/^Rabbit \w+ has been promoted to \[\d+\] .+!$/) 26 | register("chat", resetWaiting).setCriteria(/^You upgraded to Time Tower.+!$/) 27 | register("chat", resetWaiting).setCriteria(/^You upgraded to Coach Jackrabbit.+!$/) 28 | 29 | // Auto Clicking 30 | register("step", () => { 31 | if (!config.autoBuyBestUpgrade || !data.bestUpgrade || data.chocolate < data.bestUpgrade.cost) return 32 | // Can't click yet 33 | const timeSinceUpgrade = Date.now() - lastClick 34 | if (timeSinceUpgrade < 1000 / config.maxUpgradesPerSecond) return 35 | if (waitingForUpgrade && timeSinceUpgrade < 1000) return 36 | 37 | const inv = Player.getContainer() 38 | 39 | lastClick = Date.now() 40 | waitingForUpgrade = true 41 | sendWindowClick(inv.getWindowId(), data.bestUpgrade.slot) 42 | }) 43 | -------------------------------------------------------------------------------- /ChocolateHelper/util/item_scrapers/employee.js: -------------------------------------------------------------------------------- 1 | import { appendToFile } from "../../../BloomCore/utils/Utils" 2 | import config from "../config" 3 | import { data, employeeCpsValues, pogObj, updateBestUpgrade } from "../utils" 4 | 5 | export const doEmployeeItem = (itemName, lore, slot) => { 6 | // https://regex101.com/r/bAFbya/2 7 | const employeeMatch = itemName.match(/^(§.Rabbit .+?)(?:§8 - )?(?:§7\[([\d,]+)§7\] §..+|§cUnemployed)?$/) 8 | if (!employeeMatch) return false 9 | 10 | let [_, employee, countStr] = employeeMatch 11 | 12 | const level = countStr ? parseInt(countStr) : 0 13 | 14 | const employeeName = employee.removeFormatting() 15 | 16 | // Don't know this employee's cps 17 | if (!(employeeName in employeeCpsValues)) return false 18 | 19 | const employeeCps = employeeCpsValues[employeeName] 20 | 21 | data.employees[employeeName] = { 22 | colored: employee + "§r", 23 | level, 24 | cps: employeeCps, 25 | totalCps: level * employeeCps, 26 | cost: null, 27 | slot 28 | } 29 | 30 | 31 | for (let line of lore) { 32 | let costMatch = line.match(/^§5§o§6([\d,\.]+) Chocolate$/) 33 | if (costMatch) { 34 | data.employees[employeeName].cost = parseFloat(costMatch[1].replace(/,/g, "")) 35 | continue 36 | } 37 | } 38 | 39 | if (pogObj.debug) appendToFile("ChocolateHelper", `debug/${employeeName.replace(/ /g, "_").toLowerCase()}.txt`, `${data.employees[employeeName].cost}`) 40 | 41 | if (data.fullyLoaded && config.upgradeIncludeEmployees) updateBestUpgrade(true) 42 | 43 | return true 44 | } -------------------------------------------------------------------------------- /ChocolateHelper/index.js: -------------------------------------------------------------------------------- 1 | import { data, pogObj, prefix } from "./util/utils" 2 | import config from "./util/config" 3 | 4 | // TODO: 5 | // Make it so that upgrades can be bought faster than your ping. Store upgrade costs locally. 6 | 7 | import "./util/scraper" 8 | 9 | import "./features/autoClicker" 10 | import "./features/bestUpgrade" 11 | import "./features/chocolateEggs" 12 | import "./features/clickMe" 13 | import "./features/guiElements" 14 | import "./features/noItemPickup" 15 | import "./features/rabbitsCounter" 16 | import "./features/autoPickUpHoppity" 17 | import { deepCopyObject } from "../BloomCore/utils/Utils" 18 | 19 | import './gui/eggs' 20 | import './gui/rabbits' 21 | 22 | 23 | register("command", (...args) => { 24 | if (!args[0]) { 25 | config.openGUI() 26 | return 27 | } 28 | 29 | if (args[0] == "debug") { 30 | pogObj.debug = !pogObj.debug 31 | ChatLib.chat(`${prefix} &aDebug mode ${pogObj.debug ? "Enabled" : "&cDisabled"}&a.`) 32 | pogObj.debug ? debugLines.register() : debugLines.unregister() 33 | return 34 | } 35 | }).setTabCompletions(["debug"]).setName("chocolatehelper").setAliases(["/ch", "choco"]) 36 | 37 | 38 | const debugLines = register("postGuiRender", () => { 39 | if (!data.inFactory) return 40 | 41 | const jsonCopy = deepCopyObject(data) 42 | delete jsonCopy.queuedSlotClicks 43 | 44 | const jsonStr = JSON.stringify(jsonCopy, null, 4) 45 | 46 | const LINE_HEIGHT = 9 47 | 48 | const lines = jsonStr.split("\n").length 49 | const screenHeight = Renderer.screen.getHeight() 50 | let scale = 1 51 | 52 | if (lines * LINE_HEIGHT > screenHeight) { 53 | scale = screenHeight / (lines * LINE_HEIGHT) 54 | } 55 | 56 | Renderer.scale(scale) 57 | Renderer.drawString(jsonStr, 0, 0) 58 | }).unregister() 59 | -------------------------------------------------------------------------------- /PianoProdigy/index.js: -------------------------------------------------------------------------------- 1 | import PogObject from "../PogData" 2 | 3 | const S2FPacketSetSlot = Java.type("net.minecraft.network.play.server.S2FPacketSetSlot") 4 | const C0EPacketClickWindow = Java.type("net.minecraft.network.play.client.C0EPacketClickWindow") 5 | 6 | let data = new PogObject("PianoProdigy", { 7 | "uuid": null, 8 | "delay": 0 9 | }, "data.json") 10 | 11 | const sendWindowClick = (windowId, slot, clickType, actionNumber=0) => Client.sendPacket(new C0EPacketClickWindow(windowId ?? Player.getContainer().getWindowId(), slot, clickType ?? 0, 0, null, actionNumber)) 12 | const slots = [37, 38, 39, 40, 41, 42, 43] 13 | let canClick = false 14 | 15 | const funkyStuff = () => { 16 | canClick = false 17 | let inv = Player.getContainer() 18 | if (!inv || !inv.getName().startsWith("Harp - ") || inv.getSize() < 54) return 19 | for (let s of slots) { 20 | let item = inv.getStackInSlot(s-9) 21 | if (!item) continue 22 | let name = item.getName() 23 | let split = name.split(" ") 24 | if (split.length < 2) continue 25 | let color = split[1][1] 26 | if (color == "7") continue 27 | sendWindowClick(null, s, 0) 28 | canClick = false 29 | } 30 | } 31 | 32 | register("packetReceived", () => { 33 | if (canClick) return 34 | canClick = true 35 | Client.scheduleTask(data.delay, funkyStuff) 36 | }).setPacketClass(S2FPacketSetSlot) 37 | 38 | register("step", () => { 39 | if (data.uuid == Player.getUUID()) return 40 | data.uuid = Player.getUUID() 41 | data.save() 42 | ChatLib.chat(`&a/harpdelay to customize the delay before clicking. Lower values recommended for faster songs.`) 43 | }).setFps(2) 44 | 45 | register("command", (d) => { 46 | if (!d) return ChatLib.chat(`&aCurrent harp delay: &6${data.delay}`) 47 | data.delay = parseInt(d) 48 | data.save() 49 | ChatLib.chat(`&aHarp delay set to &6${data.delay}`) 50 | }).setName("harpdelay") -------------------------------------------------------------------------------- /ChocolateHelper/features/autoPickUpHoppity.js: -------------------------------------------------------------------------------- 1 | import { onSetSlotReceived, onOpenWindowPacket } from "../../BloomCore/utils/Events" 2 | import { sendWindowClick } from "../../BloomCore/utils/Utils" 3 | import config from "../util/config" 4 | let isHoppityCalling = false 5 | 6 | // Auto Pick-Up 7 | register('chat', (caller) => { 8 | if (!config.autoPickUpCall) return 9 | isHoppityCalling = caller.includes('Hoppity') 10 | }).setCriteria(/^✆ (\w+) ✆ /) 11 | 12 | register('chat', (event) => { 13 | if (!isHoppityCalling) return 14 | let command = new Message(event).getMessageParts().find(text => text.getClickValue() != null)?.getClickValue()?.replace('/','') 15 | ChatLib.command(command) 16 | }).setCriteria(/^✆ RING\.\.\. \[PICK UP\]$/) 17 | 18 | // Auto Open 19 | register('chat', (event) => { 20 | if (!isHoppityCalling) return 21 | isHoppityCalling = false // Avoid it somehow triggering before another call 22 | let command = new Message(event).getMessageParts().find(text => text.getText().includes('Yes') && text.getClickValue() != null)?.getClickValue()?.replace('/','') 23 | ChatLib.command(command) 24 | }).setCriteria(/^Select an option: \[Yes\] \[No\] $/) 25 | 26 | // Auto Buy 27 | let wId = null 28 | onOpenWindowPacket((title, windowId) => { 29 | if (title == '§rHoppity§r' && config.autoPickUpCall) wId = windowId 30 | }) 31 | 32 | onSetSlotReceived((item, slot, windowId) => { 33 | if (wId != windowId || !item || slot > 53 || !config.autoPickUpCall) return 34 | 35 | const ctItem = new Item(item) 36 | const lore = ctItem.getLore() 37 | 38 | if (!lore.some(line => line.includes('Click to trade!'))) return 39 | if (lore.some(line => line.includes('You have already found')) && config.buyOnlyUnique) return 40 | 41 | setTimeout(() => { 42 | const inv = Player.getContainer() 43 | let slotToClick = slot 44 | if (!inv) return 45 | 46 | sendWindowClick(inv.getWindowId(), slotToClick, 0) 47 | }, Math.floor(500 + Math.random() * 2000)); 48 | }) 49 | 50 | 51 | -------------------------------------------------------------------------------- /ChocolateHelper/gui/rabbits.js: -------------------------------------------------------------------------------- 1 | import config from "../util/config"; 2 | import { isHoppity, leftRightAlignFormat, pogObj } from "../util/utils"; 3 | 4 | let colorCodes = ['f', 'a', '9', '5', '6', 'd', 'b'] 5 | let rabbitsArray = Object.keys(pogObj.rabbits).filter(key => key != 'x' && key != 'y' && !key.includes('total') && !key.includes('last')) 6 | 7 | let messages = [] 8 | let totalMessage 9 | let rabbitsMessages = [] 10 | let uniqueMessage 11 | const WIDTH = 150 12 | 13 | const rabbitDisplay = register('renderOverlay', () => { 14 | Renderer.drawStringWithShadow(messages[0], pogObj.rabbits.x, pogObj.rabbits.y) 15 | messages[1].forEach((a, index) => { 16 | Renderer.drawStringWithShadow(a, pogObj.rabbits.x, pogObj.rabbits.y + 10*(index+1)) 17 | }) 18 | Renderer.drawStringWithShadow(messages[2], pogObj.rabbits.x, pogObj.rabbits.y + 10*(rabbitsArray.length+1)) 19 | }).unregister() 20 | 21 | register('step', () => { 22 | if (!config.showRabbitCount) return 23 | if (config.rabbitsGui.isOpen() && !isHoppity()) testDisplay.register() 24 | else testDisplay.unregister() 25 | 26 | if (isHoppity()) { 27 | rabbitDisplay.register() 28 | } else { 29 | rabbitDisplay.unregister() 30 | return 31 | } 32 | 33 | totalMessage = leftRightAlignFormat("&3Total", `${pogObj.rabbits.totalUniques}&8/&3${pogObj.rabbits.total} [${pogObj.rabbits.totalUniques + pogObj.rabbits.totalDuplicates}]`, WIDTH) 34 | rabbitsArray.forEach((key, index) => { 35 | let color = `&${colorCodes[index]}` 36 | rabbitsMessages[index] = leftRightAlignFormat(`${color}${key.charAt(0).toUpperCase()+key.slice(1)}`, `${pogObj.rabbits[key].unique}&8/${color}${pogObj.rabbits[key].total} [${pogObj.rabbits[key].duplicates}]`, WIDTH) 37 | }) 38 | uniqueMessage = leftRightAlignFormat("&3Last Unique", `${pogObj.rabbits.lastUnique}`, WIDTH) 39 | messages[0] = totalMessage 40 | messages[1] = rabbitsMessages 41 | messages[2] = uniqueMessage 42 | }).setFps(1) 43 | 44 | register('dragged', (dx, dy, x, y, bn) => { 45 | if (!config.rabbitsGui.isOpen() || bn == 2) return 46 | pogObj.rabbits.x = x 47 | pogObj.rabbits.y = y 48 | pogObj.save() 49 | }) 50 | 51 | const testDisplay = register('renderOverlay', () => { 52 | Renderer.drawStringWithShadow(leftRightAlignFormat("&3Total", `${pogObj.rabbits.totalUniques}&8/&3${pogObj.rabbits.total} [${pogObj.rabbits.totalUniques + pogObj.rabbits.totalDuplicates}]`, WIDTH), pogObj.rabbits.x, pogObj.rabbits.y) 53 | }).unregister() 54 | -------------------------------------------------------------------------------- /ChocolateHelper/features/chocolateEggs.js: -------------------------------------------------------------------------------- 1 | import { renderBoxOutline, renderFilledBox } from "../../BloomCore/RenderUtils" 2 | import { EntityArmorStand, getEntitySkullTexture, getEntityXYZ } from "../../BloomCore/utils/Utils" 3 | import config from "../util/config" 4 | import { isHoppity, pogObj, skullTextures } from "../util/utils" 5 | 6 | let eggs = [] 7 | let mostRecentEgg = null 8 | let isRegistered = false 9 | 10 | register("chat", (egg) => { 11 | mostRecentEgg = egg 12 | }).setCriteria(/^HOPPITY'S HUNT A (.+) has appeared!$/) 13 | 14 | register("worldUnload", () => { 15 | mostRecentEgg = null 16 | }) 17 | 18 | register("step", () => { 19 | if (!isHoppity() || !config.eggEsp) { 20 | eggEsp.unregister() 21 | return 22 | } else { 23 | eggEsp.register() 24 | } 25 | 26 | const stands = World.getAllEntitiesOfType(EntityArmorStand) 27 | eggs = [] 28 | stands.forEach(entity => { 29 | const skullTexture = getEntitySkullTexture(entity) 30 | 31 | if (!skullTexture) return 32 | 33 | Object.entries(skullTextures).forEach(([eggName, eggTexture]) => { 34 | if (skullTexture !== eggTexture) return 35 | 36 | let [r, g, b] = [1, 1, 0] 37 | // Hightlight and/or announce most recent egg 38 | if (mostRecentEgg && mostRecentEgg == eggName.removeFormatting()) { 39 | // Announce egg location 40 | if (pogObj.lastEggAnnounced !== mostRecentEgg && config.announceEggLocations) { 41 | pogObj.lastEggAnnounced = mostRecentEgg 42 | 43 | const [x, y, z] = getEntityXYZ(entity) 44 | ChatLib.command(`ac ${mostRecentEgg} is at x: ${Math.floor(x)}, y: ${Math.floor(y+1)}, z: ${Math.floor(z)}`, false) 45 | } 46 | } 47 | 48 | let eggType = eggName.split(' ')[1].toLowerCase() 49 | 50 | if (eggType in pogObj.eggs && pogObj.eggs[eggType].isAvailable) r = 0 51 | 52 | eggs.push({ 53 | entity: entity, 54 | color: [r, g, b], 55 | name: eggName 56 | }) 57 | }) 58 | }) 59 | }).setFps(1) 60 | 61 | const eggEsp = register("renderWorld", () => { 62 | eggs.forEach(e => { 63 | const { entity, color, name } = e 64 | const [x, y, z] = getEntityXYZ(entity) 65 | const [r, g, b] = color 66 | renderFilledBox(x, y+1.4, z, 0.8, 0.8, r, g, b, 0.2, true) 67 | renderBoxOutline(x, y+1.4, z, 0.8, 0.8, r, g, b, 1, 2, true) 68 | Tessellator.drawString(name, x, y+3, z) 69 | }) 70 | }).unregister() -------------------------------------------------------------------------------- /ChocolateHelper/util/scraper.js: -------------------------------------------------------------------------------- 1 | import { onOpenWindowPacket, onSetSlotReceived, onWindowItemsPacket } from "../../BloomCore/utils/Events"; 2 | import { C0DPacketCloseWindow, appendToFile, decodeNumeral } from "../../BloomCore/utils/Utils"; 3 | import config from "./config"; 4 | import { doChocolateItem } from "./item_scrapers/chocolateItem"; 5 | import { doChocolateProductionItem } from "./item_scrapers/chocolateProduction"; 6 | import { doEmployeeItem } from "./item_scrapers/employee"; 7 | import { doFactoryItem } from "./item_scrapers/factoryItem"; 8 | import { doJackrabbitItem } from "./item_scrapers/jackrabbit"; 9 | import { doTimeTowerItem } from "./item_scrapers/timeTower"; 10 | import { data, pogObj, resetData, updateBestUpgrade } from "./utils"; 11 | 12 | /** 13 | * Most of the item name and lore parsing is done here 14 | * It is a mess, so better to keep it all in one file. 15 | */ 16 | 17 | onOpenWindowPacket((title, windowId) => { 18 | if (pogObj.debug) appendToFile("ChocolateHelper", "debug/titles.txt", title) 19 | 20 | if (title !== "§rChocolate Factory§r") return 21 | 22 | data.windowId = windowId 23 | data.inFactory = true 24 | 25 | }) 26 | 27 | onWindowItemsPacket((_, windowId) => { 28 | if (windowId == data.windowId && !data.inFactory) { 29 | data.inFactory = true 30 | } 31 | }) 32 | 33 | register("packetSent", () => { 34 | resetData() 35 | data.windowId = null 36 | }).setFilteredClass(C0DPacketCloseWindow) 37 | 38 | register("guiClosed", () => { 39 | resetData() 40 | }) 41 | 42 | 43 | 44 | onSetSlotReceived((item, slot, windowId, event) => { 45 | // To deal with the fuckery which occurs when navigating between guis 46 | if (windowId == data.windowId && !data.inFactory && Client.isInGui()) { 47 | data.inFactory = true 48 | } 49 | 50 | if (!data.inFactory || windowId !== data.windowId || !item) return 51 | 52 | try { 53 | const ctItem = new Item(item) 54 | const itemName = ctItem.getName() 55 | const lore = ctItem.getLore() 56 | 57 | if (pogObj.debug) { 58 | appendToFile("ChocolateHelper", "debug/itemnames.txt", `\n${itemName}`) 59 | appendToFile("ChocolateHelper", "debug/lore.txt", `\n${lore.join("\n")}`) 60 | } 61 | 62 | if (slot == 53) { 63 | data.fullyLoaded = true 64 | updateBestUpgrade(true) 65 | } 66 | 67 | if (doFactoryItem(itemName, lore, slot)) return 68 | if (doChocolateItem(itemName, lore, slot)) return 69 | if (doEmployeeItem(itemName, lore, slot)) return 70 | if (doChocolateProductionItem(itemName, lore)) return 71 | if (doJackrabbitItem(itemName, lore, slot)) return 72 | if (doTimeTowerItem(itemName, lore, slot)) return 73 | } catch(e) { 74 | console.log("Error on scraper#onSetSlotReceived: " + e) 75 | } 76 | }) -------------------------------------------------------------------------------- /LeftClickEtherwarp/index.js: -------------------------------------------------------------------------------- 1 | import PogObject from "../PogData" 2 | 3 | const MouseEvent = Java.type("net.minecraftforge.client.event.MouseEvent") 4 | const C08PacketPlayerBlockPlacement = Java.type("net.minecraft.network.play.client.C08PacketPlayerBlockPlacement") 5 | const MCItemStack = Java.type("net.minecraft.item.ItemStack") 6 | 7 | const sneakKey = new KeyBind(Client.getMinecraft().field_71474_y.field_74311_E) 8 | 9 | /** 10 | * Gets the Skyblock item ID of the given MCItem or CT Item 11 | * @param {Item | MCItemStack} item 12 | */ 13 | const getSkyblockItemID = (item) => { 14 | if (item instanceof MCItemStack) item = new Item(item) 15 | if (!(item instanceof Item)) return null 16 | 17 | const extraAttributes = item.getNBT()?.getCompoundTag("tag")?.getCompoundTag("ExtraAttributes") 18 | const itemID = extraAttributes?.getString("id") ?? null 19 | 20 | if (itemID !== "ENCHANTED_BOOK") return itemID 21 | 22 | // Enchanted books are a pain in the ass 23 | const enchantments = extraAttributes.getCompoundTag("enchantments") 24 | const enchants = [...enchantments.getKeySet()] 25 | if (!enchants.length) return null 26 | 27 | const enchantment = enchants[0] 28 | const level = enchantments.getInteger(enchants[0]) 29 | 30 | return `ENCHANTMENT_${enchantment.toUpperCase()}_${level}` 31 | } 32 | 33 | const sendUseItem = () => { 34 | Client.sendPacket(new C08PacketPlayerBlockPlacement(Player.getHeldItem()?.getItemStack() ?? null)) 35 | } 36 | 37 | const isHoldingEtherwarpItem = () => { 38 | const held = Player.getHeldItem() 39 | const sbId = getSkyblockItemID(held) 40 | 41 | if (sbId !== "ASPECT_OF_THE_END" && sbId !== "ASPECT_OF_THE_VOID" && sbId !== "ETHERWARP_CONDUIT") return false 42 | 43 | return held.getNBT()?.toObject()?.tag?.ExtraAttributes?.ethermerge == 1 || sbId == "ETHERWARP_CONDUIT" 44 | } 45 | 46 | const pogObj = new PogObject("LeftClickEtherwarp", { 47 | enabled: false, 48 | }, "data/data.json") 49 | 50 | let shouldUnsneak = true 51 | 52 | // le toggle 53 | register("command", () => { 54 | pogObj.enabled = !pogObj.enabled 55 | pogObj.save() 56 | ChatLib.chat(`&aLeft Click Etherwarp ${pogObj.enabled ? "&aEnabled" : "&cDisabled"}`) 57 | }).setName("leftclicketherwarp").setAliases(["lcew"]) 58 | 59 | // Left click detection 60 | register(MouseEvent, (event) => { 61 | const held = Player.getHeldItem() 62 | const item = getSkyblockItemID(held) 63 | if (!pogObj.enabled) return 64 | 65 | const btn = event.button 66 | const state = event.buttonstate 67 | if (btn !== 0 || !state || !isHoldingEtherwarpItem() || !Client.isTabbedIn()) return 68 | 69 | // Cancel the left click 70 | cancel(event) 71 | 72 | // Don't press the sneak key down if you're already sneaking 73 | const shouldSneak = !Player.isSneaking() && item !== "ETHERWARP_CONDUIT" 74 | if (shouldSneak) sneakKey.setState(true) 75 | 76 | Client.scheduleTask(0, () => { 77 | sendUseItem() 78 | if (!shouldUnsneak) return 79 | 80 | if (shouldSneak) sneakKey.setState(false) 81 | }) 82 | }) 83 | 84 | register("tick", () => { 85 | const sneakKeycode = sneakKey.getKeyCode() 86 | 87 | if (Keyboard.isKeyDown(sneakKeycode)) { 88 | shouldUnsneak = false 89 | return 90 | } 91 | 92 | shouldUnsneak = true 93 | }) -------------------------------------------------------------------------------- /AutoCroesus/util/help_command.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | export const printHelp = () => { 4 | new Message( 5 | `&b&lAuto Croesus &aCommands &7(Hover to see description)\n`, 6 | new TextComponent(`&a//ac\n`) 7 | .setHover("show_text", "Shows this message."), 8 | new TextComponent(`&a//ac go\n`) 9 | .setHover("show_text", "Start looting Croesus."), 10 | new TextComponent(`&a//ac forcego\n`) 11 | .setHover("show_text", "Start looting Croesus without checking when the last time prices were updated was."), 12 | new TextComponent(`&a//ac api\n`) 13 | .setHover("show_text", "Refresh API."), 14 | new TextComponent(`&a//ac settings\n`) 15 | .setHover("show_text", "View current settings."), 16 | new TextComponent(`&a//ac overlay\n`) 17 | .setHover("show_text", "Toggle the overlay for the run loot and unclaimed chests (Will still show without auto looting on)."), 18 | new TextComponent(`&a//ac delay \n`) 19 | .setHover("show_text", "Throttle how quickly the module will be allowed to click (Values lower than your ping will not make a difference)."), 20 | new TextComponent(`&a//ac loot help|floor:[floor] limit:[limit] score:[minscore]\n`) 21 | .setHover("show_text", "Shows all of the loot collected from using the module, with optional params to filter the loot based on floor, score or limit the number of runs to show."), 22 | "\n", 23 | new TextComponent(`&a//ac kismet\n`) 24 | .setHover("show_text", "Toggle rerolling."), 25 | new TextComponent(`&a//ac kismet \n`) 26 | .setHover("show_text", "Configure how much profit is required for the chest to not be rerolled.\nEg 2,000,000 would mean any chest with >=2m profit will not be rerolled."), 27 | new TextComponent(`&a//ac kismet \n`) 28 | .setHover("show_text", "Add or remove the floor from the floors to kismet."), 29 | new TextComponent(`&a//ac key\n`) 30 | .setHover("show_text", "Toggle using chest keys."), 31 | new TextComponent(`&a//ac key \n`) 32 | .setHover("show_text", "Configure how much profit is needed for a chest key to be used on a chest."), 33 | "\n", 34 | new TextComponent(`&a//ac alwaysbuy\n`) 35 | .setHover("show_text", "Prints the list of items which the looter will always buy no matter what they are worth."), 36 | new TextComponent(`&a//ac alwaysbuy \n`) 37 | .setHover("show_text", "Adds the item with the given Skyblock Item ID to the list of items to always be bought."), 38 | new TextComponent(`&a//ac alwaysbuy reset\n`) 39 | .setHover("show_text", "Resets the list of items to always buy back to their\ndefaults found in data/defaults/always_buy.txt"), 40 | 41 | new TextComponent(`&a//ac worthless\n`) 42 | .setHover("show_text", "Prints the list of items with their values hard coded to 0."), 43 | new TextComponent(`&a//ac worthless \n`) 44 | .setHover("show_text", "Adds the item with the given Skyblock Item ID to the list of worthless items."), 45 | new TextComponent(`&a//ac worthless reset\n`) 46 | .setHover("show_text", "Resets the list of worthless items back to their\ndefaults found in data/defaults/worthless.txt"), 47 | ).chat() 48 | } -------------------------------------------------------------------------------- /ChocolateHelper/gui/eggs.js: -------------------------------------------------------------------------------- 1 | import { isHoppity, leftRightAlignFormat, pogObj } from "../util/utils" 2 | import config from "../util/config" 3 | 4 | const respawnTime = 60*20*1000 // 20 minutes in ms 5 | let breakfastTimer = null 6 | let lunchTimer = null 7 | let dinnerTimer = null 8 | 9 | let messages = [] // Breakfast, Lunch, Dinner 10 | const WIDTH = 150 11 | 12 | register('step', () => { 13 | if (!config.showEggTimers) return eggDisplay.unregister() 14 | if (config.eggGui.isOpen() && !isHoppity()) testDisplay.register() 15 | else testDisplay.unregister() 16 | 17 | if (isHoppity()) { 18 | eggDisplay.register() 19 | } else { 20 | eggDisplay.unregister() 21 | return 22 | } 23 | 24 | breakfastTimer = twentyMinuteTimer(pogObj.eggs.breakfast.lastSpawn, 'breakfast') ?? "Soon!" 25 | lunchTimer = twentyMinuteTimer(pogObj.eggs.lunch.lastSpawn, 'lunch') ?? "Soon!" 26 | dinnerTimer = twentyMinuteTimer(pogObj.eggs.dinner.lastSpawn, 'dinner') ?? "Soon!" 27 | messages[0] = leftRightAlignFormat("&6Breakfast", pogObj.eggs.breakfast.isAvailable ? `Ready! &8(&6${breakfastTimer}&8)` : `${breakfastTimer}`, WIDTH) 28 | messages[1] = leftRightAlignFormat("&9Lunch", pogObj.eggs.lunch.isAvailable ? `Ready! &8(&9${lunchTimer}&8)` : `${lunchTimer}`, WIDTH) 29 | messages[2] = leftRightAlignFormat("&aDinner", pogObj.eggs.dinner.isAvailable ? `Ready! &8(&a${dinnerTimer}&8)` : `${dinnerTimer}`, WIDTH) 30 | }).setFps(1) 31 | 32 | register("chat", (egg) => { // 20 minute timer when they appear 33 | pogObj.eggs[egg.toLowerCase()].isAvailable = true 34 | pogObj.eggs[egg.toLowerCase()].lastSpawn = Date.now() 35 | pogObj.save() 36 | }).setCriteria(/^HOPPITY'S HUNT A Chocolate (.+) Egg has appeared!$/) 37 | 38 | register("chat", (egg) => { // 20 minute timer when they appear 39 | pogObj.eggs[egg.toLowerCase()].isAvailable = false 40 | pogObj.save() 41 | }).setCriteria(/^HOPPITY'S HUNT You found a Chocolate (.+) Egg .+!$/) 42 | 43 | function twentyMinuteTimer(epoch, eggName) { 44 | if (epoch == null) return 45 | if (Date.now() - epoch >= respawnTime) { // Should be ready now 46 | pogObj.eggs[eggName].isAvailable = true // figure out whose epoch it is and set to available 47 | pogObj.eggs[eggName].lastSpawn += (respawnTime * Math.floor((Date.now() - epoch) / respawnTime ))// add 20 minutes to the epoch time so it doesnt keep trying to set to available 48 | pogObj.save() 49 | } 50 | 51 | let ms = respawnTime + epoch - Date.now() 52 | let totalSeconds = Math.floor(ms / 1000); 53 | 54 | let minutes = Math.floor(totalSeconds / 60); 55 | let seconds = totalSeconds % 60; 56 | 57 | if (minutes > 0) { 58 | return `${minutes}m${seconds}s`; 59 | } else { 60 | return `${seconds}s`; 61 | }; 62 | } 63 | 64 | const eggDisplay = register('renderOverlay', () => { 65 | messages.forEach((a, index) => { 66 | Renderer.drawStringWithShadow(a, pogObj.eggs.x, pogObj.eggs.y + 10*(index)) 67 | }) 68 | }).unregister() 69 | 70 | register('dragged', (dx, dy, x, y, bn) => { 71 | if (!config.eggGui.isOpen() || bn == 2) return 72 | pogObj.eggs.x = x 73 | pogObj.eggs.y = y 74 | pogObj.save() 75 | }) 76 | 77 | const testDisplay = register('renderOverlay', () => { 78 | Renderer.drawStringWithShadow(leftRightAlignFormat("&6Breakfast", "Ready! &8(&619m20s&8)", WIDTH), pogObj.eggs.x, pogObj.eggs.y) 79 | Renderer.drawStringWithShadow(leftRightAlignFormat("&9Lunch", "Ready! &8(&914m20s&8)", WIDTH), pogObj.eggs.x, pogObj.eggs.y + 10) 80 | Renderer.drawStringWithShadow(leftRightAlignFormat("&aDinner", "Ready! &8(&a9m20s&8)", WIDTH), pogObj.eggs.x, pogObj.eggs.y + 20) 81 | }).unregister() -------------------------------------------------------------------------------- /ChocolateHelper/features/rabbitsCounter.js: -------------------------------------------------------------------------------- 1 | import { onOpenWindowPacket, onSetSlotReceived } from "../../BloomCore/utils/Events" 2 | import { pogObj } from "../util/utils" 3 | 4 | const C0DPacketCloseWindow = Java.type('net.minecraft.network.play.client.C0DPacketCloseWindow') 5 | 6 | let temporaryMap = new Map([ // Map to store temporarily 7 | ['common', { unique: 0, duplicates: 0, total: 0 }], 8 | ['uncommon', { unique: 0, duplicates: 0, total: 0 }], 9 | ['rare', { unique: 0, duplicates: 0, total: 0 }], 10 | ['epic', { unique: 0, duplicates: 0, total: 0 }], 11 | ['legendary', { unique: 0, duplicates: 0, total: 0 }], 12 | ['mythic', { unique: 0, duplicates: 0, total: 0 }], 13 | ['divine', { unique: 0, duplicates: 0, total: 0 }] 14 | ]) 15 | 16 | // Import through the menu 17 | let wId 18 | let shouldImport = false 19 | let reachedEnd = false 20 | 21 | onOpenWindowPacket((title, windowId) => { 22 | let match = title.match(/§r\((\d+)\/(\d+)\) Hoppity\'s Collection§r/) // §r(1/17) Hoppity's Collection§r 23 | 24 | if (!match) return 25 | wId = windowId 26 | 27 | let [_, a, b] = match 28 | 29 | if (a == 1) { // Reset the temporary map 30 | [...temporaryMap.keys()].forEach(rarity => { 31 | temporaryMap.get(rarity).unique = 0 32 | temporaryMap.get(rarity).duplicates = 0 33 | temporaryMap.get(rarity).total = 0 34 | }) 35 | shouldImport = true 36 | reachedEnd = false 37 | expectedTotal = -1 38 | if (pogObj.rabbits.common.unique == 0) ChatLib.chat('&6[ChocolateHelper] &aScroll through the pages to import the rabbit data.') 39 | } 40 | 41 | if (a != b || !shouldImport) return 42 | reachedEnd = true 43 | }) 44 | 45 | register('packetSent', () => { // Save on gui close to avoid the last page not counting 46 | if (!shouldImport || !reachedEnd) return 47 | 48 | let totalPossible = [...temporaryMap.values()].map(value => value.total).reduce((acc, total) => acc + total, 0) 49 | let totalDuplicates = 0 50 | let totalUniques = 0 51 | 52 | if (expectedTotal != totalPossible) { 53 | ChatLib.chat(`&6[ChocolateHelper] &cUnexpected total number of rabbits (&4${totalPossible}&c)`) 54 | return 55 | } 56 | 57 | [...temporaryMap.keys()].forEach(rarity => { 58 | totalUniques += pogObj.rabbits[rarity].unique = temporaryMap.get(rarity).unique 59 | totalDuplicates += pogObj.rabbits[rarity].duplicates = temporaryMap.get(rarity).duplicates 60 | pogObj.rabbits[rarity].total = temporaryMap.get(rarity).total 61 | }) 62 | 63 | pogObj.rabbits.total = totalPossible 64 | pogObj.rabbits.totalDuplicates = totalDuplicates 65 | pogObj.rabbits.totalUniques = totalUniques 66 | pogObj.save() 67 | shouldImport = false 68 | ChatLib.chat('&6[ChocolateHelper] &aSuccessfully updated the rabbit data.') 69 | }).setFilteredClass(C0DPacketCloseWindow) 70 | 71 | let expectedTotal = -1 72 | onSetSlotReceived((item, slot, windowId) => { 73 | if (wId != windowId || slot > 53 || !item) return 74 | 75 | const ctItem = new Item(item) 76 | const lore = ctItem.getLore() 77 | 78 | if (ctItem.getName().includes('Hoppity\'s') && expectedTotal == -1) expectedTotal = parseInt(lore.find(line => line.startsWith("§5§o§2§l§m")).split('/')[1].removeFormatting()) 79 | 80 | let duplicateMatch = null 81 | let rarityMatch = null 82 | 83 | for (let line of lore) { 84 | if (!duplicateMatch) duplicateMatch = line.match(/^§5§o§7Duplicates Found: §a(\d+)$/) // §5§o§7Duplicates Found: §a0 85 | if (!rarityMatch) rarityMatch = line.match(/^§5§o§.§L(\w+) RABBIT$/) // §5§o§6§LLEGENDARY RABBIT 86 | } 87 | 88 | if (!rarityMatch) return 89 | 90 | let rarity = rarityMatch[1].toLowerCase() 91 | temporaryMap.get(rarity).total += 1 // Add to the total number of rabbits that exist on that rarity 92 | if (ctItem.getID() == 351) return // If not found dont continue 93 | 94 | let duplicates = 0 // 0 by default 95 | if (duplicateMatch) duplicates = parseInt(duplicateMatch[1]) 96 | temporaryMap.get(rarity).unique += 1 // If theres a duplicate match, that means that the rabbit has been found, thus unique 97 | temporaryMap.get(rarity).duplicates += duplicates 98 | }) 99 | 100 | // Add through eggs 101 | register('chat', (rarity, event) => { 102 | rarity = rarity.toLowerCase().removeFormatting() 103 | 104 | let hoverValue = new Message(event).getMessageParts().find(text => text.getHoverValue() != null)?.getHoverValue() 105 | if (!hoverValue) return 106 | 107 | if (hoverValue.includes('NEW RABBIT!')) { // https://i.imgur.com/692Fkja.png 108 | pogObj.rabbits[rarity].unique += 1 109 | pogObj.rabbits[rarity].totalUniques += 1 110 | pogObj.rabbits.totalUniques += 1 111 | ChatLib.chat("&6[ChocolateHelper] &aUnique rabbit! Since last: " + pogObj.rabbits.lastUnique) 112 | pogObj.rabbits.lastUnique = 0 113 | } 114 | else { 115 | pogObj.rabbits[rarity].duplicates += 1 116 | pogObj.rabbits[rarity].totalDuplicates += 1 117 | pogObj.rabbits.totalDuplicates += 1 118 | pogObj.rabbits.lastUnique += 1 119 | } 120 | pogObj.save() 121 | }).setCriteria(/&r&D&LHOPPITY'S HUNT &7You found .+ &.\(&.&.(.+)&.\)!&r/) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RandomStuff 2 | A collection of random stuff which aren't significant enough to deserve their own seperate repositories. 3 | 4 | # AutoCroesus 5 | Automatically claims chests in the Croesus NPC in the Dungeon Hub, with lots of options to configure how the module behaves. 6 | By far the largest module here currently, probably deserves its own repo. 7 | 8 | When the user runs //ac go, the module will search for the Croesus npc and check to see if it's close enough to click. Once the chest menu opens, it will find the first run with unclaimed chests and click on it. 9 | Once that run is opened, it will scan the items in the gui, extract the item names and the chest costs for each of the chests, parse them into the Skyblock item IDs, and then calculate how much profit each chest is based off of the Hypixel Bazaar API and Moulberry's lowestbin.json API (which are refreshed automatically when turning on auto claiming after 30 minutes or longer). 10 | 11 | If the user has rerolls enabled for the current floor, and the bedrock chest profit is lower than the configured reroll profit, then the bedrock chest will be rerolled and the run will be reopned and have its profit recalculated. 12 | * If rerolls are turned on but the user does not have kismets available, then every chest on this floor will be skipped until rerolls are turned off or the user has kismets. 13 | 14 | Since the wood chest is always profit, that means that there will always be at least one profitable chest, so at least one chest is always claimed for each run. 15 | If the user has chest keys turned on, and the chest with the second highest profit chest is profitable enough (Configurable), then that second chest will be opened as well. 16 | 17 | This process will continue for every chest, and will also loot chests on other pages until there are no more chests to loot, and the module will stop. 18 | 19 | When the loot for a run is calculated, it will log the floor, the run score, the chest cost and all of the items which will be claimed to a file which can be viewed at any time in the future with the "/autocroesus loot" command. This will show every single item and essence which this module has claimed, and will show you on average how much profit each run on that floor is worth based on your runs. 20 | This command has several filters available to only show runs on a specific floor, with a specific score, and only show the first x runs etc, so run "/autocroesus loot help" for an example on how to use it. 21 | ![runlogs](https://i.imgur.com/ErOcENh.png) 22 | 23 | The module also contains an overlay which highlights runs which haven't been looted yet and shows the profits for each chest (sorted in descending order) without the auto claiming part being enabled. 24 | ![overlay](https://i.imgur.com/KMCPnXq.png) 25 | 26 | Due to the risk associated with items having their prices be manipulated, there are two lists of items included with the module: 27 | * An "Always Buy" list, where no matter how much profit the overall chest is, if it contains one of these items then the chest will always be opened. 28 | * And a "Worthless" list, where every item in this list has their value set to 0, which means that low value books like Bank or Combo can't be manipulated and claimed over more profitable chests. 29 | 30 | Both of these lists are configurable, and you can change and view their items at any time, or reset them back to their defaults. 31 | 32 | Since inventory interactions are ping dependent, people with low ping will be able to claim chests significantly quicker than people with high ping, to the point where it could look suspicious to other people in chat when rare rewards start popping up impossibly fast. For this reason, you can configure how long the module will wait in between each click, with the default delay set to 250ms. 33 | 34 | I'm not sure how safe it would be from a watchdog perspective, but if you have <50ms ping, and set the delay to 50ms or less, please send me a video of what it looks like looting a whole bunch of chests because I think that would be hilarious. 35 | 36 | Here's a small demo of the module looting a bunch of M5 runs with a delay of 250ms, chest keys enabled and no kismets: https://youtu.be/LUuHczvyfpE 37 | 38 | # ZeroPingEtherwarp 39 | Instantly teleports you to the target block when attempting to etherwarp. 40 | 41 | Sends packets to simulate true zero ping. After you click, you can immediately change directions and teleport again even before the server has processed the first teleport. 42 | Safeguards are in place to protect against excessive packets being sent, however there are no checks for whether or not etherwarping is actually possible in the area you're in. 43 | Use with caution, don't be stupid with it. 44 | 45 | Use /zeropingetherwarp or /zpew to show the available commands for the module. 46 | 47 | ## How it works 48 | Normally when you etherwarp, you send a use item packet, the server does a raycast to find the block to put you on, and then sends you an S08PacketPlayerPosLook packet to tell your client where you should be teleported to. In response, you send a C06PacketPlayerPosLook packet to confirm the teleport and sync back with the server. 49 | 50 | With zero ping etherwarp, when you right click with an etherwarp item, the module will do a raycast and predict where the server will put you, then teleport you to that location and send the C06PacketPlayerPosLook as well. When the server has registered that you've tried to etherwarp and sends the S08PacketPlayerPosLook packet, the coordinates are checked with the ones which the module predicted, and if they match, the packet event is cancelled since the C06 response has already been sent and you are already at that location. 51 | 52 | ![How it works](https://i.imgur.com/sQTRaEj.png) 53 | 54 | # Left Click Etherwarp 55 | When left clicking with an item with etherwarp on it, will automatically sneak and then right click. Goes well with ZeroPingEtherwarp. 56 | 57 | # ChocolateHelper 58 | Many features for the Chocolate Factory that aim to automate Hypixel's Cookie Clicker and also some good legit features. 59 | 60 | # PianoProdigy 61 | Harp should not be part of progressing. 62 | 63 | Does harp for you! 64 | 65 | /harpdelay \ to change delay. Lower values recommended for faster songs. 66 | 67 | Detects the wool block one slot before you should click, so low ping players should have their delay set higher than players with high ping. For me (250ms) on the faster songs, 2-3 ticks of delay works reliably. 68 | 69 | 70 | # IHateCarpet 71 | Literally just turns carpets near you into ghost blocks temporarily so watchdog screams at you less. 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /AutoCroesus/extra/lootLogs.js: -------------------------------------------------------------------------------- 1 | import { getSellPrice } from "../util/prices" 2 | import { appendToFile, formatNumber, getFormattedNameFromId } from "../util/utils" 3 | 4 | const getScoreFromChestCount = (chestCount) => { 5 | // Less than A 6 | if (chestCount == 3) { 7 | return 229 8 | } 9 | 10 | // A 11 | if (chestCount == 4) { 12 | return 230 13 | } 14 | 15 | // S, assumes all floors below F5 can't get S+ because there's no way to differentiate based on chest count 16 | if (chestCount == 5) { 17 | return 270 18 | } 19 | 20 | // S+ 21 | if (chestCount == 6) { 22 | return 300 23 | } 24 | 25 | // Should never happen 26 | return 0 27 | } 28 | 29 | export const logLoot = (floor, chestInfo, chestCount) => { 30 | const totalCost = chestInfo.reduce((a, b) => a + b.cost, 0) 31 | // const totalProfit = chestInfo.reduce((a, b) => a + b.profit, 0) 32 | const score = getScoreFromChestCount(chestCount) 33 | 34 | // ChestInfo should only contain the info for the chests which were actually claimed. So one normally, two if chest key used. 35 | const combinedItems = chestInfo.reduce((totalLoot, chest) => { 36 | for (let entry of chest.items) { 37 | let { id, qty } = entry 38 | 39 | if (!(id in totalLoot)) totalLoot[id] = 0 40 | 41 | totalLoot[id] += qty 42 | } 43 | 44 | return totalLoot 45 | }, {}) 46 | 47 | const combinedItemStr = Object.entries(combinedItems).map(([sbid, qty]) => `${sbid}:${qty}`).join(" ") 48 | 49 | // Format: FLOOR SCORE CHEST_COST ...SKYBLOCK_ID:QTY 50 | const final = `${floor} ${score} ${totalCost} ${combinedItemStr}` 51 | 52 | appendToFile("AutoCroesus", "data/runLoot.txt", final) 53 | } 54 | 55 | const getFiltersFromArgs = (...args) => { 56 | const final = { 57 | score: 300, // Minimum score 58 | floor: null, 59 | limit: null // Max number of runs to include in the log 60 | } 61 | 62 | for (let segment of args) { 63 | let scoreMatch = segment.match(/^(?:score|s):(\d+)$/) 64 | if (scoreMatch) { 65 | let score = parseInt(scoreMatch[1]) 66 | if (score < 0 || score > 317) { 67 | ChatLib.chat(`Score ${score} not in valid range 0 - 317`) 68 | return null 69 | } 70 | 71 | final.score = score 72 | } 73 | 74 | let floorMatch = segment.match(/^(?:f|floor):([fFmM][1-7])$/) 75 | if (floorMatch) { 76 | final.floor = floorMatch[1].toUpperCase() 77 | } 78 | 79 | let limitMatch = segment.match(/^(?:l|limit):(\d+)$/) 80 | if (limitMatch) { 81 | final.limit = parseInt(limitMatch[1]) 82 | } 83 | } 84 | 85 | return final 86 | } 87 | 88 | export const handleLootCommand = (...args) => { 89 | if (args[1] == "help") { 90 | ChatLib.chat(`&e//ac loot [floor:] [limit:] [score:]`) 91 | ChatLib.chat(`For example: "&b//ac loot floor:F5 limit:100 score:300&f" will show the last 100 runs logged on Floor 5 with a score of 300 or more.`) 92 | return 93 | } 94 | 95 | const filters = getFiltersFromArgs(...args) 96 | if (!filters) { 97 | ChatLib.chat(`Invalid Filters`) 98 | return 99 | } 100 | 101 | const { score, floor, limit } = filters 102 | 103 | if (!FileLib.exists("AutoCroesus", "data/runLoot.txt")) { 104 | ChatLib.chat(`No loot has been logged!`) 105 | return 106 | } 107 | 108 | const lines = FileLib.read("AutoCroesus", "data/runLoot.txt").split("\n").reverse() 109 | let dungeons = 0 110 | let loot = {} 111 | let totalChestCost = 0 112 | for (let line of lines) { 113 | let [runFloor, runScore, chestCost, ...runLoot] = line.split(" ") 114 | if (floor && runFloor !== floor) continue 115 | if (runScore < score) continue 116 | dungeons++ 117 | totalChestCost += parseInt(chestCost) 118 | 119 | for (let itemInfo of runLoot) { 120 | let [itemID, quantStr] = itemInfo.split(":") 121 | 122 | if (!(itemID in loot)) loot[itemID] = 0 123 | loot[itemID] += parseInt(quantStr) 124 | 125 | } 126 | 127 | if (dungeons == limit) break 128 | } 129 | 130 | let totalProfit = -totalChestCost 131 | let floorColor = floor?.startsWith("M") ? "&c&l" : "&a" 132 | let renderStr = `&aLoot from &e${formatNumber(dungeons)} &aruns on ${floorColor}${floor ?? "All Floors"}&a:` 133 | // ["ITEM_ID", ITEM_VALUE, QUANTITY] 134 | // Sort by total value qty*value 135 | const itemInfo = Object.entries(loot).reduce((a, [item, quant]) => { 136 | let value = getSellPrice(item, true) ?? 0 137 | 138 | totalProfit += value * quant 139 | 140 | a.push([item, value, quant]) 141 | return a 142 | }, []).sort((a, b) => b[1]*b[2] - a[1]*a[2]) 143 | 144 | let totalSellPrice = totalProfit + totalChestCost 145 | let extraLoot = [0, 0] // [amount, value] 146 | 147 | for (let i = 0; i < itemInfo.length; i++) { 148 | let entry = itemInfo[i] 149 | let [itemID, value, quantity] = entry 150 | let totalValue = value * quantity 151 | 152 | if (i >= 25) { 153 | extraLoot[0]++ 154 | extraLoot[1] += totalValue 155 | continue 156 | } 157 | 158 | renderStr += `\n&b${formatNumber(quantity)}x &a${getFormattedNameFromId(itemID)} &a(&6${formatNumber(Math.floor(value))}&a) = &6${formatNumber(Math.floor(totalValue))} &8(${(totalValue / totalSellPrice*100).toFixed(2)}%)` 159 | } 160 | 161 | if (extraLoot[0] > 0) { 162 | renderStr += `\n&a... and ${extraLoot[0]} more (&6${formatNumber(Math.floor(extraLoot[1]))}&a)` 163 | } 164 | 165 | renderStr += `\n&cTotal Chest Cost: &6${formatNumber(Math.floor(totalChestCost))}` 166 | renderStr += `\n&cTotal Sell Price: &6${formatNumber(Math.floor(totalSellPrice))}` 167 | renderStr += `\n&eTotal Profit: &6${formatNumber(Math.floor(totalProfit))}` 168 | renderStr += `\n&bProfit/Run: &6${formatNumber(Math.floor(totalProfit/dungeons))}` 169 | 170 | const averageProfit = Math.floor(totalProfit / dungeons) 171 | 172 | ChatLib.chat(`&aAverage profit from &e${dungeons} &aruns on &b${floor ?? "All Floors"}&a: &6${formatNumber(averageProfit)}`) 173 | new TextComponent(`&aTotal Profit: &6${formatNumber(Math.floor(totalProfit))}`).setHover("show_text", renderStr).chat() 174 | } -------------------------------------------------------------------------------- /ChocolateHelper/util/config.js: -------------------------------------------------------------------------------- 1 | 2 | import { 3 | @ButtonProperty, 4 | @CheckboxProperty, 5 | Color, 6 | @ColorProperty, 7 | @PercentSliderProperty, 8 | @SelectorProperty, 9 | @SwitchProperty, 10 | @TextProperty, 11 | @Vigilant, 12 | @SliderProperty, 13 | @NumberProperty, 14 | } from '../../Vigilance/index'; 15 | 16 | @Vigilant("ChocolateHelper/data", "Chocolate Helper!", { 17 | getCategoryComparator: () => (a, b) => { 18 | const categories = []; 19 | return categories.indexOf(a.name) - categories.indexOf(b.name); 20 | } 21 | }) 22 | class Config { 23 | constructor() { 24 | this.initialize(this) 25 | this.addDependency("Move Rabbit Count", "Show Rabbit Count") 26 | this.addDependency("Move Egg Timers", "Show Egg Timers") 27 | } 28 | 29 | eggGui = new Gui() 30 | rabbitsGui = new Gui() 31 | // Clicking 32 | 33 | @SwitchProperty({ 34 | name: "No Item Pickup", 35 | description: "Prevents you from picking up clicked items in the chocolate factory gui.", 36 | category: "Clicking", 37 | subcategory: "Manual Clicks" 38 | }) 39 | noItemPickup = false; 40 | 41 | @SliderProperty({ 42 | name: "Auto Clicker CPS", 43 | description: "How many clicks per second the auto clicker will click the cookie. Will only click whilst keybind is held.\nThe auto clicker can be toggled via the keybind in your controls.", 44 | category: "Clicking", 45 | subcategory: "Auto Clicker", 46 | min: 1, 47 | max: 20 48 | }) 49 | autoClickerCps = 7; 50 | 51 | @SwitchProperty({ 52 | name: "Auto Click Me", 53 | description: "Automatically clicks the \"CLICK ME\" items when they spawn.", 54 | category: "Clicking", 55 | subcategory: "Click Me" 56 | }) 57 | autoClickMe = false; 58 | 59 | @SwitchProperty({ 60 | name: "Click Me Sound", 61 | description: "Plays a sound whenever a \"CLICK ME\" item spawns in the chocolate factory.", 62 | category: "Clicking", 63 | subcategory: "Click Me" 64 | }) 65 | clickMeSound = false; 66 | 67 | @SwitchProperty({ 68 | name: "Auto Pick Up Hoppity Calls", 69 | description: "Automatically picks up and buys the rabbit from Hoppity\'s call (needs the contact)", 70 | category: "Clicking", 71 | subcategory: "Hoppity" 72 | }) 73 | autoPickUpCall = false; 74 | 75 | @SwitchProperty({ 76 | name: "Buy Only Unique Rabbits", 77 | description: "Doesn\'t buy duplicate rabbits from Hoppity\'s calls.", 78 | category: "Clicking", 79 | subcategory: "Hoppity" 80 | }) 81 | buyOnlyUnique = false; 82 | 83 | // Overlays 84 | 85 | @SwitchProperty({ 86 | name: "Show Chocolate", 87 | description: "Shows your current chocolate and cps above the chocolate item.", 88 | category: "Overlays", 89 | subcategory: "Gui" 90 | }) 91 | showChocolate = false; 92 | 93 | @SwitchProperty({ 94 | name: "Show Egg Timers", 95 | description: "Shows egg timers for Hoppity.", 96 | category: "Overlays", 97 | subcategory: "Gui" 98 | }) 99 | showEggTimers = false; 100 | 101 | @ButtonProperty({ 102 | name: "Move Egg Timers", 103 | description: "Moves the egg timers", 104 | category: "Overlays", 105 | subcategory: "Gui", 106 | placeholder: "Move" 107 | }) 108 | moveEggTimer() { 109 | this.eggGui.open() 110 | }; 111 | 112 | @SwitchProperty({ 113 | name: "Show Rabbit Count", 114 | description: "Shows the amount of unique rabbits and duplicates.", 115 | category: "Overlays", 116 | subcategory: "Gui" 117 | }) 118 | showRabbitCount = false; 119 | 120 | @ButtonProperty({ 121 | name: "Move Rabbit Count", 122 | description: "Moves the rabbit count gui.", 123 | category: "Overlays", 124 | subcategory: "Gui", 125 | placeholder: "Move" 126 | }) 127 | moveRabbitCount() { 128 | this.rabbitsGui.open() 129 | }; 130 | 131 | @SwitchProperty({ 132 | name: "Show Prestige", 133 | description: "Highlights the prestige slot when you can pestige.", 134 | category: "Overlays", 135 | subcategory: "slots" 136 | }) 137 | showPrestigeHighlight = false; 138 | 139 | // Upgrades 140 | 141 | @SwitchProperty({ 142 | name: "&6Show Best Upgrade", 143 | description: "Shows the best upgrade of all of the individual upgrades you have enabled.", 144 | category: "Upgrades" 145 | }) 146 | showBestUpgrade = false; 147 | 148 | @SwitchProperty({ 149 | name: "Auto Buy Best Upgrade", 150 | description: "Automatically buys the best upgrade when you have enough chocolate.", 151 | category: "Upgrades" 152 | }) 153 | autoBuyBestUpgrade = false; 154 | 155 | @SwitchProperty({ 156 | name: "Show Queued Clicks", 157 | description: "Shows a small number in the corner of the slot showing how many times a slot will be clicked.", 158 | category: "Upgrades" 159 | }) 160 | showQueuedClicks = false; 161 | 162 | @SliderProperty({ 163 | name: "Max CPS", 164 | description: "The most amount of clicks per second that upgrades can be bought at. \n&cThis will be bottlenecked by your ping.", 165 | category: "Upgrades", 166 | min: 1, 167 | max: 20 168 | }) 169 | maxUpgradesPerSecond = 10; 170 | 171 | @SwitchProperty({ 172 | name: "Employees", 173 | description: "Includes employees when calculating the best upgrade.", 174 | category: "Upgrades", 175 | subcategory: "Upgrades" 176 | }) 177 | upgradeIncludeEmployees = true; 178 | 179 | @SwitchProperty({ 180 | name: "Jackrabbit", 181 | description: "Includes jackrabbit when calculating the best upgrade.", 182 | category: "Upgrades", 183 | subcategory: "Upgrades" 184 | }) 185 | upgradeIncludeJackrabbit = true; 186 | 187 | @SwitchProperty({ 188 | name: "Time Tower", 189 | description: "Includes the time tower when calculating the best upgrade.", 190 | category: "Upgrades", 191 | subcategory: "Upgrades" 192 | }) 193 | upgradeIncludeTimeTower = true; 194 | 195 | // Eggs 196 | 197 | @SwitchProperty({ 198 | name: "Egg ESP", 199 | description: "Shows you where chocolate eggs are in the world.", 200 | category: "Eggs" 201 | }) 202 | eggEsp = false; 203 | 204 | @SwitchProperty({ 205 | name: "Announce Egg Location", 206 | description: "Announces the coordinates of newly spawned chocolate eggs in chat when they first spawn.", 207 | category: "Eggs" 208 | }) 209 | announceEggLocations = false; 210 | 211 | } 212 | export default new Config() -------------------------------------------------------------------------------- /AutoCroesus/util/prices.js: -------------------------------------------------------------------------------- 1 | import Promise from "../../PromiseV2"; 2 | import request from "../../requestV2"; 3 | import logger from "./logger"; 4 | 5 | export const worthless = new Set() 6 | export const alwaysBuy = new Set() 7 | 8 | const writeAlwaysBuy = () => { 9 | const toWrite = [...alwaysBuy].join("\n") 10 | FileLib.write("AutoCroesus", "data/always_buy.txt", toWrite) 11 | } 12 | 13 | const writeWorthless = () => { 14 | const toWrite = [...worthless].join("\n") 15 | FileLib.write("AutoCroesus", "data/worthless.txt", toWrite) 16 | } 17 | 18 | export const initAlwaysBuy = () => { 19 | // Grab always buy and worthless items from default as they've not been initialized yet 20 | ChatLib.chat(`Initializing data/always_buy.txt`) 21 | const defaultData = FileLib.read("AutoCroesus", "data/defaults/always_buy.txt") 22 | FileLib.write("AutoCroesus", "data/always_buy.txt", defaultData) 23 | } 24 | 25 | export const loadAlwaysBuy = () => { 26 | const alwaysBuyLines = FileLib.read("AutoCroesus", "data/always_buy.txt").split("\n") 27 | alwaysBuy.clear() 28 | for (let itemID of alwaysBuyLines) { 29 | alwaysBuy.add(itemID) 30 | } 31 | } 32 | 33 | export const initWorthless = () => { 34 | // Grab always buy and worthless items from default as they've not been initialized yet 35 | ChatLib.chat(`Initializing data/worthless.txt`) 36 | const defaultData = FileLib.read("AutoCroesus", "data/defaults/worthless.txt") 37 | FileLib.write("AutoCroesus", "data/worthless.txt", defaultData) 38 | } 39 | 40 | export const loadWorthless = () => { 41 | const worthlessLines = FileLib.read("AutoCroesus", "data/worthless.txt").split("\n") 42 | worthless.clear() 43 | for (let itemID of worthlessLines) { 44 | worthless.add(itemID) 45 | } 46 | } 47 | 48 | let cachedBzValues = {} 49 | let cachedItems = {} 50 | let cachedBins = {} 51 | 52 | let itemIdMap = {} // {sbID: APIItemData} 53 | 54 | if (FileLib.exists("AutoCroesus", "data/bzValues.json")) { 55 | cachedBzValues = JSON.parse(FileLib.read("AutoCroesus", "data/bzValues.json")) 56 | } 57 | 58 | if (FileLib.exists("AutoCroesus", "data/items.json")) { 59 | cachedItems = JSON.parse(FileLib.read("AutoCroesus", "data/items.json")) 60 | itemIdMap = {} 61 | 62 | for (let item of cachedItems) { 63 | itemIdMap[item.id] = item 64 | } 65 | } 66 | 67 | if (FileLib.exists("AutoCroesus", "data/binValues.json")) { 68 | cachedBins = JSON.parse(FileLib.read("AutoCroesus", "data/binValues.json")) 69 | } 70 | 71 | export const getBzValues = () => cachedBzValues 72 | export const getSkyblockItems = () => cachedItems 73 | export const getBinValues = () => cachedBins 74 | 75 | export const getItemApiData = (sbId) => { 76 | if (!(sbId in itemIdMap)) { 77 | return null 78 | } 79 | 80 | return itemIdMap[sbId] 81 | } 82 | 83 | const handleBzData = (resp) => { 84 | if (!resp.success) { 85 | return [false, resp.cause] 86 | } 87 | 88 | let data = {} 89 | if (FileLib.exists("AutoCroesus", "data/bzValues.json")) { 90 | data = JSON.parse(FileLib.read("AutoCroesus", "data/bzValues.json")) 91 | } 92 | 93 | const products = resp.products 94 | 95 | for (let entry of Object.entries(products)) { 96 | let [itemID, info] = entry 97 | 98 | // Take the top three order and average their prices to try and get an accurate value for the item 99 | let buyOrders = info.buy_summary 100 | let sellOrderValue = info.quick_status.buyPrice 101 | if (buyOrders.length) { 102 | let sample = buyOrders.slice(0, 5) 103 | sellOrderValue = sample.reduce((a, b) => a + b.pricePerUnit, 0) / sample.length 104 | } 105 | 106 | // Insta sells go directly to buy orders 107 | let sellOrders = info.sell_summary 108 | let instaSellValue = info.quick_status.sellPrice 109 | if (sellOrders.length) { 110 | let sample = sellOrders.slice(0, 5) 111 | instaSellValue = sample.reduce((a, b) => a + b.pricePerUnit, 0) / sample.length 112 | } 113 | 114 | data[itemID] = { 115 | sellOrderValue, 116 | instaSellValue, 117 | } 118 | } 119 | 120 | cachedBzValues = data 121 | FileLib.write("AutoCroesus", "data/bzValues.json", JSON.stringify(data, null, 4), true) 122 | 123 | return [true, ""] 124 | } 125 | 126 | const handleItemResp = (resp) => { 127 | if (!resp.success) { 128 | return [false, resp.cause] 129 | } 130 | 131 | cachedItems = resp.items 132 | for (let item of cachedItems) { 133 | itemIdMap[item.id] = item 134 | } 135 | 136 | FileLib.write("AutoCroesus", "data/items.json", JSON.stringify(resp.items, null, 4), true) 137 | 138 | return [true, ""] 139 | } 140 | 141 | const handleBinResp = (resp) => { 142 | cachedBins = resp 143 | FileLib.write("AutoCroesus", "data/binValues.json", JSON.stringify(resp, null, 4)) 144 | 145 | return [true, ""] 146 | } 147 | 148 | export const updatePrices = () => new Promise((resolve, reject) => { 149 | Promise.all([ 150 | request({ 151 | url: "https://api.hypixel.net/skyblock/bazaar", 152 | json: true 153 | }), 154 | request({ 155 | url: "https://api.hypixel.net/v2/resources/skyblock/items", 156 | json: true 157 | }), 158 | request({ 159 | url: "https://moulberry.codes/lowestbin.json", 160 | json: true 161 | }), 162 | ]).then(([bzResp, itemResp, binResp]) => { 163 | const bzSuccess = handleBzData(bzResp) 164 | const itemSuccesss = handleItemResp(itemResp) 165 | const binSuccess = handleBinResp(binResp) 166 | 167 | if (!bzSuccess[0]) { 168 | logger.push(`Failed bzSuccess: ${JSON.stringify(bzSuccess[1])}`) 169 | reject(bzSuccess[1]) 170 | return 171 | } 172 | 173 | if (!itemSuccesss[0]) { 174 | logger.push(`Failed itemSuccesss: ${JSON.stringify(itemSuccesss[1])}`) 175 | reject(itemSuccesss[1]) 176 | return 177 | } 178 | 179 | if (!binSuccess[0]) { 180 | logger.push(`Failed binSuccess: ${JSON.stringify(binSuccess[1])}`) 181 | reject(binSuccess[1]) 182 | return 183 | } 184 | 185 | resolve() 186 | }).catch(e => { 187 | logger.push(`Failed to grab prices: ${JSON.stringify(e)}`) 188 | reject(e) 189 | }) 190 | 191 | }) 192 | 193 | 194 | export const getSellPrice = (sbID, useSellOrder=true) => { 195 | // Override to avoid price manipulation 196 | if (worthless.has(sbID)) { 197 | logger.push(` ${sbID} is WORTHLESS`) 198 | return 0 199 | } 200 | 201 | const bzData = getBzValues() 202 | if (sbID in bzData) { 203 | if (useSellOrder) { 204 | return bzData[sbID].sellOrderValue 205 | } 206 | return bzData[sbID].instaSellValue 207 | } 208 | 209 | const binData = getBinValues() 210 | if (sbID in binData) { 211 | return binData[sbID] 212 | } 213 | 214 | logger.push(`Could not find price for ${sbID} (${Object.keys(bzData).length} items in BZ, ${Object.keys(binData).length} items in BIN)`) 215 | 216 | return null 217 | } 218 | 219 | const itemIdExists = (itemID) => { 220 | if (itemID in itemIdMap || itemID in cachedBzValues) { 221 | return true 222 | } 223 | 224 | return false 225 | } 226 | 227 | export const addAlwaysBuy = (itemId) => { 228 | const upper = itemId.toUpperCase() 229 | if (alwaysBuy.has(upper)) { 230 | ChatLib.chat(`&cRemoved &f${upper} &cfrom Always Buy list!`) 231 | alwaysBuy.delete(upper) 232 | writeAlwaysBuy() 233 | return 234 | } 235 | 236 | if (!itemIdExists(upper)) { 237 | ChatLib.chat(`&cWarning: Could not find &f${upper} &cin the Skyblock items database. This could be a new item, so it will be added to the list anyway.`) 238 | } 239 | 240 | ChatLib.chat(`&aAdded &f${upper} &ato the list of items to always be bought.`) 241 | alwaysBuy.add(upper) 242 | writeAlwaysBuy() 243 | } 244 | 245 | export const addWorthlessItem = (itemId) => { 246 | const upper = itemId.toUpperCase() 247 | if (worthless.has(upper)) { 248 | worthless.delete(upper) 249 | ChatLib.chat(`&cRemoved &f${upper} &cfrom Worthless list!`) 250 | writeWorthless() 251 | return 252 | } 253 | 254 | if (!itemIdExists(upper)) { 255 | ChatLib.chat(`&cWarning: Could not find &f${upper} &cin the Skyblock items database. This could be a new item, so it will be added to the list anyway.`) 256 | } 257 | 258 | ChatLib.chat(`&aAdded &f${upper} &ato the list of worthless items.`) 259 | worthless.add(upper) 260 | writeWorthless() 261 | 262 | } -------------------------------------------------------------------------------- /ZeroPingEtherwarp/utils.js: -------------------------------------------------------------------------------- 1 | 2 | const MCItemStack = Java.type("net.minecraft.item.ItemStack") 3 | export const C04PacketPlayerPosition = Java.type("net.minecraft.network.play.client.C03PacketPlayer$C04PacketPlayerPosition") 4 | export const C06PacketPlayerPosLook = Java.type("net.minecraft.network.play.client.C03PacketPlayer$C06PacketPlayerPosLook") 5 | export const C05PacketPlayerLook = Java.type("net.minecraft.network.play.client.C03PacketPlayer$C05PacketPlayerLook") 6 | export const C03PacketPlayer = Java.type("net.minecraft.network.play.client.C03PacketPlayer") 7 | export const S08PacketPlayerPosLook = Java.type("net.minecraft.network.play.server.S08PacketPlayerPosLook") 8 | export const C0BPacketEntityAction = Java.type("net.minecraft.network.play.client.C0BPacketEntityAction") 9 | export const C08PacketPlayerBlockPlacement = Java.type("net.minecraft.network.play.client.C08PacketPlayerBlockPlacement") 10 | 11 | export const eyeHeights = { 12 | normal: 1.54, 13 | standing: 1.62, 14 | modern: 1.27, 15 | } 16 | 17 | const validEtherwarpItems = [ 18 | "ASPECT_OF_THE_END", 19 | "ASPECT_OF_THE_VOID", 20 | "ETHERWARP_CONDUIT", 21 | ] 22 | 23 | export const isEtherwarpItem = (item, itemID) => { 24 | 25 | if (!validEtherwarpItems.includes(itemID)) { 26 | return false 27 | } 28 | 29 | // Etherwarp conduit doesn't have the ethermerge NBT tag, the ability is there by default 30 | return item.getNBT()?.toObject()?.tag?.ExtraAttributes?.ethermerge == 1 || itemID == "ETHERWARP_CONDUIT" 31 | } 32 | 33 | export const getTunerBonusDistance = () => { 34 | return Player.getHeldItem()?.getNBT()?.toObject()?.tag?.ExtraAttributes?.tuned_transmission || 0 35 | } 36 | 37 | // If one of these blocks is above the targeted etherwarp block, it is a valid teleport. 38 | // However if the block itself is being targetted, then it is not a valid block to etherwarp to. 39 | const etherBlockIds = [ 40 | "minecraft:air", 41 | "minecraft:fire", 42 | "minecraft:carpet", 43 | "minecraft:skull", 44 | "minecraft:lever", 45 | "minecraft:stone_button", 46 | "minecraft:wooden_button", 47 | "minecraft:torch", 48 | "minecraft:tripwire_hook", 49 | "minecraft:tripwire", 50 | "minecraft:rail", 51 | "minecraft:activator_rail", 52 | "minecraft:snow_layer", 53 | "minecraft:carrots", 54 | "minecraft:wheat", 55 | "minecraft:potatoes", 56 | "minecraft:nether_wart", 57 | "minecraft:pumpkin_stem", 58 | "minecraft:melon_stem", 59 | "minecraft:redstone_torch", 60 | "minecraft:redstone_wire", 61 | "minecraft:red_flower", 62 | "minecraft:yellow_flower", 63 | "minecraft:sapling", 64 | "minecraft:flower_pot", 65 | "minecraft:deadbush", 66 | "minecraft:tallgrass", 67 | "minecraft:ladder", 68 | "minecraft:double_plant", 69 | "minecraft:unpowered_repeater", 70 | "minecraft:powered_repeater", 71 | "minecraft:unpowered_comparator", 72 | "minecraft:powered_comparator", 73 | "minecraft:web", 74 | "minecraft:water", 75 | "minecraft:lava", 76 | "minecraft:torch", 77 | "minecraft:vine", 78 | "minecraft:brown_mushroom", 79 | "minecraft:red_mushroom", 80 | "minecraft:piston_extension", 81 | "minecraft:portal", 82 | "minecraft:reeds", 83 | "minecraft:flowing_lava", 84 | "minecraft:cocoa", 85 | ].map(a => new BlockType(a).getID()) 86 | 87 | // Make it so that the array can be directly indexed into via the block id instead of having to calculate a hash with a set 88 | // Each index corresponds to a block ID, where that index can either be true or false depending on if this is a valid ether foot block 89 | const validEtherwarpFeetBlocks = new Array(500).fill(false).map((_, i) => etherBlockIds.includes(i)) 90 | 91 | const isValidEtherwarpFootBlock = (blockID) => { 92 | if (blockID == null || blockID < 0 || blockID >= validEtherwarpFeetBlocks.length) { 93 | return false 94 | } 95 | 96 | return validEtherwarpFeetBlocks[blockID] 97 | } 98 | 99 | export const simEtherwarp = (x0, y0, z0, x1, y1, z1) => { 100 | // Initialize Shit 101 | let x = Math.floor(x0) 102 | let y = Math.floor(y0) 103 | let z = Math.floor(z0) 104 | 105 | let endX = Math.floor(x1) 106 | let endY = Math.floor(y1) 107 | let endZ = Math.floor(z1) 108 | 109 | const dirX = x1 - x0 110 | const dirY = y1 - y0 111 | const dirZ = z1 - z0 112 | 113 | const stepX = Math.sign(dirX) 114 | const stepY = Math.sign(dirY) 115 | const stepZ = Math.sign(dirZ) 116 | 117 | const thingX = 1 / dirX 118 | const thingY = 1 / dirY 119 | const thingZ = 1 / dirZ 120 | 121 | const tDeltaX = Math.min(thingX * stepX, 1) 122 | const tDeltaY = Math.min(thingY * stepY, 1) 123 | const tDeltaZ = Math.min(thingZ * stepZ, 1) 124 | 125 | let tMaxX = Math.abs((x + Math.max(stepX, 0) - x0) * thingX) 126 | let tMaxY = Math.abs((y + Math.max(stepY, 0) - y0) * thingY) 127 | let tMaxZ = Math.abs((z + Math.max(stepZ, 0) - z0) * thingZ) 128 | 129 | let iters = 0 130 | while (true && iters++ < 1000) { 131 | 132 | // Do block check function stuff 133 | let currentBlock = World.getBlockAt(x, y, z) 134 | let currentId = currentBlock.type.getID() 135 | 136 | // End Reached, solid block found 137 | if (!validEtherwarpFeetBlocks[currentId]) { 138 | // Cannot stand ontop 139 | if (isValidEtherwarpFootBlock(currentBlock.type.getID())) return null 140 | 141 | // Block the player's feet will be in after etherwarping 142 | let footBlock = World.getBlockAt(x, y+1, z) 143 | if (!isValidEtherwarpFootBlock(footBlock.type.getID())) return null 144 | 145 | // Head block after etherwarp 146 | let headBlock = World.getBlockAt(x, y+2, z) 147 | if (!isValidEtherwarpFootBlock(headBlock.type.getID())) return null 148 | 149 | return [x, y, z] 150 | } 151 | 152 | // End Reached without finding a block 153 | if (x == endX && y == endY && z == endZ) { 154 | return null 155 | } 156 | 157 | // Find the next direction to step in 158 | if (tMaxX < tMaxY) { 159 | if (tMaxX < tMaxZ) { 160 | tMaxX += tDeltaX 161 | x += stepX 162 | } 163 | else { 164 | tMaxZ += tDeltaZ 165 | z += stepZ 166 | } 167 | } 168 | else { 169 | if (tMaxY < tMaxZ) { 170 | tMaxY += tDeltaY 171 | y += stepY 172 | } 173 | else { 174 | tMaxZ += tDeltaZ 175 | z += stepZ 176 | } 177 | } 178 | } 179 | 180 | return null 181 | } 182 | 183 | /** 184 | * Gets the Skyblock item ID of the given MCItem or CT Item 185 | * @param {Item | MCItemStack} item 186 | */ 187 | export const getSkyblockItemID = (item) => { 188 | if (item instanceof MCItemStack) item = new Item(item) 189 | if (!(item instanceof Item)) return null 190 | 191 | const extraAttributes = item.getNBT()?.getCompoundTag("tag")?.getCompoundTag("ExtraAttributes") 192 | const itemID = extraAttributes?.getString("id") ?? null 193 | 194 | if (itemID !== "ENCHANTED_BOOK") return itemID 195 | 196 | // Enchanted books are a pain in the ass 197 | const enchantments = extraAttributes.getCompoundTag("enchantments") 198 | const enchants = [...enchantments.getKeySet()] 199 | if (!enchants.length) return null 200 | 201 | const enchantment = enchants[0] 202 | const level = enchantments.getInteger(enchants[0]) 203 | 204 | return `ENCHANTMENT_${enchantment.toUpperCase()}_${level}` 205 | } 206 | 207 | // For etherwarp shit to be perfectly accurate 208 | let lastSentCoords = null 209 | let lastSentLook = null 210 | export const getLastSentCoord = () => lastSentCoords ? [...lastSentCoords] : null 211 | /** 212 | * 213 | * @returns {[Number, Number] | null} - The last [pitch, yaw] sent to the server 214 | */ 215 | export const getLastSentLook = () => lastSentLook ? [...lastSentLook] : null 216 | 217 | const getPacketCoord = (c03Packet) => { 218 | const x = c03Packet.func_149464_c() 219 | const y = c03Packet.func_149467_d() 220 | const z = c03Packet.func_149472_e() 221 | return [x, y, z] 222 | } 223 | 224 | const getPacketLook = (c03Packet) => { 225 | const pitch = c03Packet.func_149470_h() 226 | const yaw = c03Packet.func_149462_g() 227 | return [pitch, yaw] 228 | } 229 | 230 | register("packetSent", (packet) => { 231 | if (packet instanceof C04PacketPlayerPosition) lastSentCoords = getPacketCoord(packet) 232 | else if (packet instanceof C05PacketPlayerLook) lastSentLook = getPacketLook(packet) 233 | else if (packet instanceof C06PacketPlayerPosLook) { 234 | lastSentCoords = getPacketCoord(packet) 235 | lastSentLook = getPacketLook(packet) 236 | } 237 | }).setFilteredClass(C03PacketPlayer) 238 | 239 | register("worldUnload", () => { 240 | lastSentLook = null 241 | lastSentCoords = null 242 | }) 243 | register("worldLoad", () => { 244 | lastSentLook = null 245 | lastSentCoords = null 246 | }) -------------------------------------------------------------------------------- /ChocolateHelper/util/utils.js: -------------------------------------------------------------------------------- 1 | import { deepCopyObject, readFileLines } from "../../BloomCore/utils/Utils" 2 | import PogObject from "../../PogData" 3 | import config from "./config" 4 | 5 | 6 | export const prefix = `&b[&dChocolateHelper&b]&r` 7 | 8 | // Where most of the stuff grabbed from the scraper is sent to. 9 | export let data = { 10 | inFactory: false, 11 | fullyLoaded: false, // Flips to true when every item in the gui has been loaded 12 | windowId: null, 13 | chocolate: 0, 14 | chocolateSlot: null, 15 | allTimeChocolate: 0, 16 | cps: 0, 17 | baseCps: 0, 18 | cpsMultiplier: 0, 19 | factoryTier: 1, 20 | canPrestige: false, 21 | factoryItemSlot: null, 22 | productionMultipliers: {}, // {"Hoppity's Collection": 0.693, ...} 23 | cpsSources: {}, // {"Rabbit Employees": 2630, ...} 24 | bestUpgrade: null, // {slot: 10, cost: 100000000, costPerCps: 420} 25 | employees: {}, 26 | jackrabbit: { 27 | level: 0, 28 | cost: null, 29 | slot: null, 30 | }, 31 | timeTower: { 32 | level: 0, 33 | cost: null, 34 | slot: null, 35 | }, 36 | rabbitShrine: { 37 | level: 0, 38 | cost: null, 39 | slot: null, 40 | }, 41 | 42 | } 43 | 44 | export const pogObj = new PogObject("ChocolateHelper", { 45 | lastEggAnnounced: null, 46 | debug: false, 47 | eggs: { 48 | breakfast: { 49 | isAvailable: null, 50 | lastSpawn: null, 51 | }, 52 | lunch : { 53 | isAvailable: null, 54 | lastSpawn: null, 55 | }, 56 | dinner : { 57 | isAvailable: null, 58 | lastSpawn: null, 59 | }, 60 | x: Renderer.screen.getWidth()/2, 61 | y: Renderer.screen.getHeight()/2 62 | }, 63 | rabbits: { 64 | common: { 65 | unique: 0, 66 | duplicates: 0, 67 | total: 0, 68 | }, 69 | uncommon: { 70 | unique: 0, 71 | duplicates: 0, 72 | total: 0, 73 | }, 74 | rare: { 75 | unique: 0, 76 | duplicates: 0, 77 | total: 0, 78 | }, 79 | epic: { 80 | unique: 0, 81 | duplicates: 0, 82 | total: 0, 83 | }, 84 | legendary: { 85 | unique: 0, 86 | duplicates: 0, 87 | total: 0, 88 | }, 89 | mythic: { 90 | unique: 0, 91 | duplicates: 0, 92 | total: 0, 93 | }, 94 | divine: { 95 | unique: 0, 96 | duplicates: 0, 97 | total: 0, 98 | }, 99 | total: 0, 100 | totalDuplicates: 0, 101 | totalUniques: 0, 102 | lastUnique: 0, 103 | x: Renderer.screen.getWidth()/2, 104 | y: Renderer.screen.getHeight()/2 105 | } 106 | }, "data/persistent.json") 107 | pogObj.autosave() 108 | 109 | const dataCopy = deepCopyObject(data) 110 | delete dataCopy.windowId // Want to preserve the window id 111 | 112 | export const resetData = () => { 113 | const clearedData = deepCopyObject(dataCopy) 114 | Object.entries(clearedData).forEach(([k, v]) => data[k] = v) 115 | } 116 | 117 | // Base CPS for each employee 118 | export const employeeCpsValues = { 119 | "Rabbit Bro": 1, 120 | "Rabbit Cousin": 2, 121 | "Rabbit Sis": 3, 122 | "Rabbit Daddy": 4, 123 | "Rabbit Granny": 5, 124 | "Rabbit Uncle": 6, 125 | "Rabbit Dog": 7, 126 | } 127 | const employees = Object.keys(employeeCpsValues) 128 | 129 | export const skullTextures = { 130 | "§6Chocolate Breakfast Egg": "ewogICJ0aW1lc3RhbXAiIDogMTcxMTQ2MjY3MzE0OSwKICAicHJvZmlsZUlkIiA6ICJiN2I4ZTlhZjEwZGE0NjFmOTY2YTQxM2RmOWJiM2U4OCIsCiAgInByb2ZpbGVOYW1lIiA6ICJBbmFiYW5hbmFZZzciLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYTQ5MzMzZDg1YjhhMzE1ZDAzMzZlYjJkZjM3ZDhhNzE0Y2EyNGM1MWI4YzYwNzRmMWI1YjkyN2RlYjUxNmMyNCIKICAgIH0KICB9Cn0", 131 | "§9Chocolate Lunch Egg": "ewogICJ0aW1lc3RhbXAiIDogMTcxMTQ2MjU2ODExMiwKICAicHJvZmlsZUlkIiA6ICI3NzUwYzFhNTM5M2Q0ZWQ0Yjc2NmQ4ZGUwOWY4MjU0NiIsCiAgInByb2ZpbGVOYW1lIiA6ICJSZWVkcmVsIiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzdhZTZkMmQzMWQ4MTY3YmNhZjk1MjkzYjY4YTRhY2Q4NzJkNjZlNzUxZGI1YTM0ZjJjYmM2NzY2YTAzNTZkMGEiCiAgICB9CiAgfQp9", 132 | "§aChocolate Dinner Egg": "ewogICJ0aW1lc3RhbXAiIDogMTcxMTQ2MjY0OTcwMSwKICAicHJvZmlsZUlkIiA6ICI3NGEwMzQxNWY1OTI0ZTA4YjMyMGM2MmU1NGE3ZjJhYiIsCiAgInByb2ZpbGVOYW1lIiA6ICJNZXp6aXIiLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZTVlMzYxNjU4MTlmZDI4NTBmOTg1NTJlZGNkNzYzZmY5ODYzMTMxMTkyODNjMTI2YWNlMGM0Y2M0OTVlNzZhOCIKICAgIH0KICB9Cn0", 133 | } 134 | 135 | export const getTimeTowerUpgradeCost = (currentTier) => { 136 | const baseCost = 5_500_000 + (500_000 * (data.factoryTier - 1)) 137 | // Cost increases by an order of this amount per level from the base cost 138 | const levelMultipliers = [1, 2, 3, 4, 6, 8, 10, 12, 14, 16, 20, 24, 30, 40] 139 | 140 | if (currentTier > levelMultipliers.length) return null 141 | 142 | return baseCost * levelMultipliers[currentTier - 1] 143 | } 144 | 145 | export const getRabbitShrineCost = (currentTier) => { 146 | 147 | const baseCost = 10_000_000 148 | const levelMultipliers = [1, 2, 3, 4, 6, 8, 10, 12, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70] 149 | 150 | if (currentTier > levelMultipliers.length) return 151 | return baseCost * levelMultipliers[currentTier] 152 | } 153 | 154 | 155 | // const jackrabbitCosts = readFileLines("ChocolateHelper", "upgrades/jackrabbit.txt").map(v => parseInt(v)) 156 | 157 | export const getJackrabbitUpgradeCost = (currentTier) => { 158 | if (currentTier > jackrabbitCosts.length) return null 159 | 160 | return jackrabbitCosts[currentTier] 161 | } 162 | 163 | /** 164 | * Updates the best upgrade to get. If recursive, will find all of the best upgrades until chocolate runs out. 165 | * @param {Boolean} recursive - Tries to upgrade as many times as possible. Will also add the slots to the click queue if auto upgrades are enabled. 166 | * This should only be ran when auto upgrades is on. 167 | * @param {*} currentLevels - An object containing the current levels of each possible upgrade. 168 | * @returns 169 | */ 170 | export const updateBestUpgrade = () => { 171 | // Auto upgrading is in progress, we don't wanna continue otherwise it'll 172 | // mess up the order upgrades are bought and waste chocolate. 173 | 174 | let best = null 175 | 176 | // All upgradable objects must be an object containing 'cost' and 'slot'. 177 | const processUpgrade = (name, upgradableObject, cpsGain, maxTier) => { 178 | const { level, slot, cost } = upgradableObject 179 | 180 | // This upgrade is already maxed 181 | if (level == maxTier) return 182 | 183 | 184 | if (cost == null || slot == null) return 185 | // ChatLib.chat(`${name}: ${cost / cpsGain} c/cps | ${cpsGain / data.cps} t/cps`) 186 | 187 | const costPerCps = cost / cpsGain 188 | // const upgradeEfficiency = cpsGain / (cost * cost / data.cps) 189 | // ChatLib.chat(`${name}: ${(upgradeEfficiency*100000000).toFixed(2)}`) 190 | if (best && best.costPerCps < costPerCps) return 191 | 192 | best = { 193 | name, 194 | slot, 195 | cost, 196 | costPerCps 197 | } 198 | } 199 | 200 | if (config.upgradeIncludeEmployees) { 201 | Object.entries(data.employees).forEach(([employee, info]) => { 202 | const { level, slot, cost } = info 203 | 204 | // Need previous employee to be lvl >20 first 205 | const employeeIndex = employees.indexOf(employee) 206 | if (employeeIndex > 0) { 207 | const prevEmployee = employees[employeeIndex-1] 208 | const prevEmployeeLevel = data.employees[prevEmployee]?.level 209 | 210 | if (prevEmployeeLevel < 20) return 211 | } 212 | 213 | processUpgrade(employee, info, info.cps) 214 | }) 215 | } 216 | 217 | if (config.upgradeIncludeJackrabbit) { 218 | processUpgrade("Jackrabbit", data.jackrabbit, data.baseCps * 0.01) 219 | } 220 | 221 | if (config.upgradeIncludeTimeTower) { 222 | processUpgrade("Time Tower", data.timeTower, data.baseCps * 0.1 / 8) 223 | } 224 | 225 | data.bestUpgrade = best 226 | } 227 | 228 | /** 229 | * 230 | * @param {Slot} slot 231 | * @param {Number} r - 0-255 232 | * @param {Number} g - 0-255 233 | * @param {Number} b - 0-255 234 | * @param {Number} a - 0-255 235 | */ 236 | export const highlightSlot = (slot, r, g, b, a) => { 237 | const x = slot.getDisplayX() 238 | const y = slot.getDisplayY() 239 | 240 | Renderer.translate(x, y) 241 | Renderer.drawRect(Renderer.color(r, g, b, a), 0, 0, 16, 16) 242 | } 243 | 244 | let inHoppity = false 245 | register('step', () => { 246 | inHoppity = Scoreboard?.getLines()?.findIndex(line => line?.getName()?.removeFormatting()?.replace(/[^\u0000-\u007F]/g, "")?.includes(' Spring ')) != -1 247 | }).setFps(1) 248 | 249 | export const isHoppity = () => { 250 | return inHoppity 251 | } 252 | 253 | export function leftRightAlignFormat(text, counter, totalWidth) { 254 | const textWidth = Renderer.getStringWidth(text); 255 | const counterWidth = Renderer.getStringWidth(counter); 256 | const spaceWidth = Renderer.getStringWidth(" "); 257 | const spacesNeeded = Math.max(0, Math.floor((totalWidth - textWidth - counterWidth) / spaceWidth)); 258 | 259 | return text + " ".repeat(spacesNeeded) + counter; 260 | } -------------------------------------------------------------------------------- /ZeroPingEtherwarp/index.js: -------------------------------------------------------------------------------- 1 | import PogObject from "../PogData" 2 | import { 3 | C06PacketPlayerPosLook, 4 | C08PacketPlayerBlockPlacement, 5 | C0BPacketEntityAction, 6 | eyeHeights, 7 | getLastSentCoord, 8 | getLastSentLook, 9 | getSkyblockItemID, 10 | getTunerBonusDistance, 11 | isEtherwarpItem, 12 | S08PacketPlayerPosLook, 13 | simEtherwarp, 14 | } from "./utils" 15 | 16 | const dataObject = new PogObject("ZeroPingEtherwarp", { 17 | firstTime: true, 18 | enabled: false, 19 | keepMotion: false, 20 | eyeHeight: eyeHeights.normal, 21 | debug: false, 22 | debugFails: false, 23 | }, "data.json") 24 | 25 | const firstInstallTrigger = register("tick", () => { 26 | firstInstallTrigger.unregister() 27 | 28 | if (!dataObject.firstTime) { 29 | return 30 | } 31 | 32 | dataObject.firstTime = false 33 | dataObject.save() 34 | 35 | const lines = [ 36 | "", 37 | "Although initial testing has shown that this does not ban", 38 | "immediately, there may be some cases where this module will", 39 | "try to teleport to places where it can't. The prediction is", 40 | "good, but it's not perfect. There are no checks to prevent", 41 | "you from trying to teleport in rooms or areas which you", 42 | "shouldn't normally be able to teleport in, like certain puzzles", 43 | "or Skyblock areas. Lagbacks may also cause teleports to fail,", 44 | "as well as server lag. There are safeguards built in to limit", 45 | "how many fails are allowed before the module reverts back to", 46 | "normal etherwarps for a little bit, but don't be stupid with it.", 47 | "", 48 | ] 49 | 50 | const final = [ 51 | `&b&m${ChatLib.getChatBreak(" ")}`, 52 | ChatLib.getCenteredText("&b&lZero Ping Etherwarp"), 53 | `${lines.join("\n")}`, 54 | ChatLib.getCenteredText("&4&lThis module is high risk to use."), 55 | `&b&m${ChatLib.getChatBreak(" ")}`, 56 | ].join("\n") 57 | 58 | ChatLib.chat(final) 59 | }) 60 | 61 | register("command", (arg1, arg2) => { 62 | if (!arg1) { 63 | const message = [ 64 | "&b/zpew - &3Shows this message", 65 | "&b/zpew toggle - &3Toggle the module", 66 | "&b/zpew keepmotion - &3Preserves momentum after teleporting", 67 | `&b/zpew eyepos <${Object.keys(eyeHeights).join(", ")}> - &3Change the height above your feet that the etherwarp prediction raycast will start.` 68 | ].join("\n") 69 | ChatLib.chat(message) 70 | return 71 | } 72 | 73 | if (arg1 == "toggle") { 74 | dataObject.enabled = !dataObject.enabled 75 | dataObject.save() 76 | ChatLib.chat(`&aZero Ping Etherwarp ${dataObject.enabled ? "&aEnabled" : "&cDisabled"}&a.`) 77 | return 78 | } 79 | 80 | if (arg1 == "keepmotion") { 81 | dataObject.keepMotion = !dataObject.keepMotion 82 | dataObject.save() 83 | ChatLib.chat(`&aKeep Motion ${dataObject.keepMotion ? "&aEnabled" : "&cDisabled"}&a.`) 84 | return 85 | } 86 | if (arg1 == "eyepos") { 87 | if (!arg2) { 88 | ChatLib.chat(`Current eye height: ${dataObject.eyeHeight}`) 89 | return 90 | } 91 | 92 | if (!(arg2 in eyeHeights)) { 93 | ChatLib.chat(`Possible eye heights: ${Object.keys(eyeHeights).join(", ")}`) 94 | return 95 | } 96 | 97 | dataObject.eyeHeight = eyeHeights[arg2] 98 | dataObject.save() 99 | ChatLib.chat(`Eye height changed to ${arg2} (${eyeHeights[arg2]} blocks)`) 100 | return 101 | } 102 | 103 | if (arg1 == "debug") { 104 | if (!arg2) { 105 | dataObject.debug = !dataObject.debug 106 | dataObject.save() 107 | ChatLib.chat(`ZeroPingEtherwarp Debug mode ${dataObject.debug ? "&aEnabled" : "&cDisabled"}&r.`) 108 | return 109 | } 110 | 111 | if (arg2 == "fails") { 112 | dataObject.debugFails = !dataObject.debugFails 113 | dataObject.save() 114 | ChatLib.chat(`Debug Fails Only ${dataObject.debugFails ? "&aEnabled" : "&cDisabled"}&r.`) 115 | return 116 | } 117 | 118 | ChatLib.chat(`Unknown arg`) 119 | return 120 | } 121 | }).setTabCompletions(args => { 122 | const firstArgs = ["toggle", "keepmotion", "eyepos"] 123 | 124 | if (args.length == 0) { 125 | return firstArgs 126 | } 127 | 128 | if (args[0] == "eyepos") { 129 | if (args.length == 1) { 130 | return Object.keys(eyeHeights) 131 | } 132 | 133 | return Object.keys(eyeHeights).filter(a => a.startsWith(args[1].toLowerCase())) 134 | } 135 | 136 | if (args[0] == "debug") { 137 | return ["fails"] 138 | } 139 | 140 | return firstArgs.filter(a => a.startsWith(args[0].toLowerCase())) 141 | }).setName("zeropingetherwarp").setAliases(["zpew"]) 142 | 143 | const FAILWATCHPERIOD = 20 // 20 Seconds 144 | const MAXFAILSPERFAILPERIOD = 3 // 3 fails allowed per 20 seconds. Higher numbers of fails could cause timer bans 145 | const MAXQUEUEDPACKETS = 3 // Longest chain of queued zero ping teleports at a time 146 | const recentFails = [] // Timestamps of the most recent failed teleports 147 | const recentlySentC06s = [] // [{pitch, yaw, x, y, z, sentAt}, ...] in the order the packets were sent 148 | let isSneaking = false 149 | 150 | register("packetSent", (packet) => { 151 | const action = packet.func_180764_b() 152 | 153 | if (action == C0BPacketEntityAction.Action.START_SNEAKING) { 154 | isSneaking = true 155 | } 156 | else if (action == C0BPacketEntityAction.Action.STOP_SNEAKING) { 157 | isSneaking = false 158 | } 159 | }).setFilteredClass(C0BPacketEntityAction) 160 | 161 | register("worldUnload", () => { 162 | isSneaking = false 163 | }) 164 | 165 | register("worldLoad", () => { 166 | isSneaking = false 167 | }) 168 | 169 | const checkAllowedFails = () => { 170 | // Queue of teleports too long 171 | if (recentlySentC06s.length >= MAXQUEUEDPACKETS) return false 172 | 173 | // Filter old fails 174 | while (recentFails.length > 0 && Date.now() - recentFails[0] > FAILWATCHPERIOD * 1000) { 175 | recentFails.shift() 176 | } 177 | 178 | return recentFails.length < MAXFAILSPERFAILPERIOD 179 | } 180 | 181 | // Have to do this since scheduleTask sometimes doesn't fucking run, so we've gotta send it at the start of the next tick. 182 | let c06ToSend = null 183 | const c06Sender = register("tick", () => { 184 | c06Sender.unregister() 185 | 186 | // Should never happen but best to be careful 187 | if (!c06ToSend) { 188 | return 189 | } 190 | 191 | const { x, y, z, pitch, yaw } = c06ToSend 192 | 193 | let keepMotion = dataObject.keepMotion 194 | const blockInside = World.getBlockAt(Math.floor(x), Math.floor(y), Math.floor(z)) 195 | const blockUnder = World.getBlockAt(Math.floor(x), Math.floor(y) - 1, Math.floor(z)) 196 | 197 | if (blockUnder.type.getRegistryName() == "minecraft:hopper") { 198 | keepMotion = false 199 | } 200 | if (blockInside.type.getRegistryName() == "minecraft:skull") { 201 | keepMotion = false 202 | } 203 | 204 | Player.getPlayer().func_70107_b(x, y, z) 205 | Client.sendPacket(new C06PacketPlayerPosLook(x, y, z, yaw, pitch, Player.asPlayerMP().isOnGround())) 206 | 207 | if (!keepMotion) { 208 | Player.getPlayer().func_70016_h(0, 0, 0) //.setVelocity() 209 | } 210 | }).unregister() 211 | 212 | const doZeroPingEtherwarp = (x0, y0, z0, pitch, yaw) => { 213 | const distance = 57 + getTunerBonusDistance() - 1 214 | 215 | yaw %= 360 216 | if (yaw < 0) { 217 | yaw += 360 218 | } 219 | 220 | const f = Math.cos(-yaw * 0.017453292 - Math.PI) 221 | const f1 = Math.sin(-yaw * 0.017453292 - Math.PI) 222 | const f2 = -Math.cos(-pitch * 0.017453292) 223 | const f3 = Math.sin(-pitch * 0.017453292) 224 | 225 | const dx = f1 * f2 * distance 226 | const dy = f3 * distance 227 | const dz = f * f2 * distance 228 | 229 | const eyePos = y0 + (isSneaking ? dataObject.eyeHeight : eyeHeights.standing) 230 | const etherSpot = simEtherwarp(x0, eyePos, z0, x0+dx, eyePos+dy, z0+dz) 231 | 232 | if (!etherSpot) { 233 | return 234 | } 235 | 236 | let [x, y, z] = etherSpot 237 | 238 | x += 0.5 239 | y += 1.05 240 | z += 0.5 241 | 242 | recentlySentC06s.push({ pitch, yaw, x, y, z, sentAt: Date.now() }) 243 | 244 | if (dataObject.debug) { 245 | return 246 | } 247 | 248 | // The danger zone 249 | // At the end of this tick, send the C06 packet which would normally be sent after the server teleports you 250 | // and then set the player's position to the destination. The C06 being sent is what makes this true zero ping. 251 | c06ToSend = { x, y, z, pitch, yaw } 252 | c06Sender.register() 253 | } 254 | 255 | // Don't teleport when looking at these blocks 256 | const blacklistedIds = [ 257 | 54, // Chest 258 | 146, // Trapped Chest 259 | ] 260 | 261 | // Detect when the player is trying to etherwarp 262 | register("packetSent", (packet) => { 263 | if (!dataObject.enabled) return 264 | 265 | // Dir = 255 means no block was clicked 266 | const dir = packet.func_149568_f() 267 | if (dir !== 255) return 268 | 269 | const held = Player.getHeldItem() 270 | const sbId = getSkyblockItemID(held) 271 | 272 | // Not holding etherwarp item or looking at chest 273 | if (!isEtherwarpItem(held, sbId) || blacklistedIds.includes(Player.lookingAt()?.getType()?.getID())) { 274 | return 275 | } 276 | 277 | // Enable zero ping for etherwarp conduit 278 | if (!isSneaking && sbId !== "ETHERWARP_CONDUIT") { 279 | return 280 | } 281 | 282 | const lastLook = getLastSentLook() 283 | const lastStand = getLastSentCoord() 284 | 285 | // Always sync with the server 286 | if (!lastLook || !lastStand) { 287 | return 288 | } 289 | 290 | // Failsafe 291 | if (!checkAllowedFails()) { 292 | ChatLib.chat(`&cZero ping etherwarp teleport aborted.\n&c${recentFails.length} fails last ${FAILWATCHPERIOD}s\n&c${recentlySentC06s.length} C06's queued currently`) 293 | return 294 | } 295 | 296 | const [x0, y0, z0] = lastStand 297 | const [pitch, yaw] = lastLook 298 | 299 | doZeroPingEtherwarp(x0, y0, z0, pitch, yaw) 300 | }).setFilteredClass(C08PacketPlayerBlockPlacement) 301 | 302 | // For whatever rounding errors etc occur 303 | const isWithinTolerence = (n1, n2) => Math.abs(n1 - n2) < 1e-4 304 | 305 | // Listening for server teleport packets 306 | register("packetReceived", (packet, event) => { 307 | if (!dataObject.enabled || recentlySentC06s.length == 0) return 308 | 309 | const { pitch, yaw, x, y, z, sentAt } = recentlySentC06s.shift() 310 | 311 | const newPitch = packet.func_148930_g() 312 | const newYaw = packet.func_148931_f() 313 | const newX = packet.func_148932_c() 314 | const newY = packet.func_148928_d() 315 | const newZ = packet.func_148933_e() 316 | 317 | // All of the values of this S08 packet must match up to the last C06 packet which was sent when you teleported. 318 | const lastPresetPacketComparison = { 319 | pitch: isWithinTolerence(pitch, newPitch) || newPitch == 0, 320 | yaw: isWithinTolerence(yaw, newYaw) || newYaw == 0, 321 | x: x == newX, 322 | y: y == newY, 323 | z: z == newZ 324 | } 325 | 326 | const wasPredictionCorrect = Object.values(lastPresetPacketComparison).every(a => a == true) 327 | 328 | if (dataObject.debug || !wasPredictionCorrect && dataObject.debugFails) { 329 | const msg = [ 330 | `${wasPredictionCorrect ? "&a" : "&c"}Last Etherwarp:`, 331 | `${lastPresetPacketComparison.pitch ? "&a" : "&c"}Pitch ${pitch} -> ${newPitch}`, 332 | `${lastPresetPacketComparison.yaw ? "&a" : "&c"}Yaw ${yaw} -> ${newYaw}`, 333 | `${lastPresetPacketComparison.x ? "&a" : "&c"}X ${x} -> ${newX}`, 334 | `${lastPresetPacketComparison.y ? "&a" : "&c"}Y ${y} -> ${newY}`, 335 | `${lastPresetPacketComparison.z ? "&a" : "&c"}Z ${z} -> ${newZ}`, 336 | ].join("\n") 337 | 338 | ChatLib.chat(msg) 339 | } 340 | 341 | if (dataObject.debug) { 342 | return 343 | } 344 | 345 | // The etherwarp was predicted correctly, cancel the packet since we've already sent the response back when we tried to teleport 346 | if (wasPredictionCorrect) { 347 | cancel(event) 348 | return 349 | } 350 | 351 | // The etherwarp was not predicted correctly 352 | recentFails.push(Date.now()) 353 | 354 | // Discard the rest of the queued teleports to check since one earlier in the chain failed 355 | while (recentlySentC06s.length > 0) { 356 | recentlySentC06s.pop() 357 | } 358 | 359 | }).setFilteredClass(S08PacketPlayerPosLook) 360 | -------------------------------------------------------------------------------- /AutoCroesus/util/utils.js: -------------------------------------------------------------------------------- 1 | import PogObject from "../../PogData" 2 | import logger from "./logger" 3 | import { alwaysBuy, getItemApiData, getSellPrice, getSkyblockItems } from "./prices" 4 | 5 | 6 | const C02PacketUseEntity = Java.type("net.minecraft.network.play.client.C02PacketUseEntity") 7 | const EntityOtherPlayerMP = Java.type("net.minecraft.client.entity.EntityOtherPlayerMP") 8 | const MCEntity = Java.type("net.minecraft.entity.Entity") 9 | const Vec3 = Java.type("net.minecraft.util.Vec3") 10 | const EntityArmorStand = Java.type("net.minecraft.entity.item.EntityArmorStand") 11 | 12 | export const acPogObj = new PogObject("AutoCroesus", { 13 | lastApiUpdate: null, 14 | 15 | minClickDelay: 500, 16 | noClick: false, 17 | showChestInfo: false, 18 | 19 | useKismets: false, 20 | kismetMinProfit: 2_000_000, 21 | kismetFloors: [], 22 | 23 | useChestKeys: true, 24 | chestKeyMinProfit: 200_000, 25 | 26 | 27 | }, "data/data.json") 28 | 29 | acPogObj.autosave() 30 | 31 | export const numerals = ["", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X"] 32 | const REACH = 4 33 | export const CHEST_REGEX = /^(§.)(Wood|Gold|Diamond|Emerald|Obsidian|Bedrock)$/ 34 | 35 | export const formattedBool = (bool) => bool ? "&atrue" : "&cfalse" 36 | export const formatNumber = (num) => num?.toString()?.replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,') 37 | 38 | export const tierColors = { 39 | "COMMON": "&f", 40 | "UNCOMMON": "&a", 41 | "RARE": "&9", 42 | "EPIC": "&5", 43 | "LEGENDARY": "&6", 44 | "MYTHIC": "&d", 45 | "SPECIAL": "&c", 46 | "VERY_SPECIAL": "&c", 47 | "SUPREME": "&4", 48 | } 49 | 50 | // These items are not in the Skyblock items API endpoint, and thus can't have their item 51 | // names automatically be mapped to item IDs. 52 | export const itemReplacements = { 53 | "Shiny Wither Boots": "WITHER_BOOTS", 54 | "Shiny Wither Leggings": "WITHER_LEGGINGS", 55 | "Shiny Wither Chestplate": "WITHER_CHESTPLATE", 56 | "Shiny Wither Helmet": "WITHER_HELMET", 57 | "Shiny Necron's Handle": "NECRON_HANDLE", 58 | "Wither Shard": "SHARD_WITHER", 59 | "Thorn Shard": "SHARD_THORN", 60 | "Apex Dragon Shard": "SHARD_APEX_DRAGON", 61 | "Power Dragon Shard": "SHARD_POWER_DRAGON", 62 | "Scarf Shard": "SHARD_SCARF", 63 | "Necron Dye": "DYE_NECRON", 64 | "Livid Dye": "DYE_LIVID", 65 | } 66 | 67 | export const tryClickEntity = (entity) => { 68 | let finalEntity = entity 69 | if (entity instanceof Entity) { 70 | finalEntity = entity.getEntity() 71 | } 72 | if (!(finalEntity instanceof MCEntity)) { 73 | return false 74 | } 75 | 76 | const packet = new C02PacketUseEntity(finalEntity, new Vec3(0, 0, 0)) 77 | Client.sendPacket(packet) 78 | 79 | return true 80 | } 81 | 82 | const findCroesusEntity = () => { 83 | const stands = World.getAllEntitiesOfType(EntityArmorStand).filter(a => a.getName() == "Croesus") 84 | if (!stands.length) return null 85 | 86 | const displayStand = stands[0] 87 | 88 | const players = World.getAllEntitiesOfType(EntityOtherPlayerMP).filter(a => { 89 | const distX = (displayStand.getX() - a.getX()) 90 | const distY = (displayStand.getY() - a.getY()) 91 | const distZ = (displayStand.getZ() - a.getZ()) 92 | 93 | // ChatLib.chat(`distx=${distX} disty=${distY} distz=${distZ}`) 94 | 95 | return a.getUUID().version() == 2 && distX == 0 && distY == 0 && distZ == 0 96 | }) 97 | 98 | if (!players.length) return null 99 | 100 | if (players.length > 1) { 101 | ChatLib.chat(`Found multiple possible croesus entities?`) 102 | return null 103 | } 104 | 105 | return players[0] 106 | } 107 | 108 | export const tryClickCroesus = () => { 109 | const croesusEntity = findCroesusEntity() 110 | 111 | if (!croesusEntity) { 112 | logger.push("Could not find croesus entity") 113 | return false 114 | } 115 | 116 | const distSq = (croesusEntity.getX() - Player.getX())**2 + (croesusEntity.getY() - Player.getY())**2 + (croesusEntity.getZ() - Player.getZ())**2 117 | 118 | // Too far away 119 | if (distSq > REACH**2) { 120 | logger.push("Croesus entity is too far away") 121 | return false 122 | } 123 | 124 | return tryClickEntity(croesusEntity.getEntity()) 125 | } 126 | 127 | export const chestSlots = [10, 11, 12, 13, 14, 15, 16, 19, 20, 21, 22, 23, 24, 25, 28, 29, 30, 31, 32, 33, 34, 37, 38, 39, 40, 41, 42, 43] 128 | 129 | export const isInvLoaded = (inv) => { 130 | return inv.getSize() > 45 && inv.getItems()[inv.getSize() - 45] !== null 131 | } 132 | 133 | export const inCroesus = () => { 134 | const inv = Player.getContainer() 135 | return isInvLoaded(inv) && inv.getName() == "Croesus" 136 | } 137 | 138 | export const inRunGui = () => { 139 | const inv = Player.getContainer() 140 | return /^(?:Master )?Catacombs - ([FloorVI\d ]*)$/.test(inv.getName()) 141 | } 142 | 143 | export const getCurrPage = () => { 144 | const inv = Player.getContainer() 145 | 146 | const nextItem = inv.getStackInSlot(53) 147 | const prevItem = inv.getStackInSlot(45) 148 | 149 | if (!nextItem || !prevItem) return null 150 | 151 | if (nextItem.getName() == "§aNext Page") { 152 | const match = nextItem.getLore()[1].match(/^§5§o§ePage (\d+)$/) 153 | if (!match) { 154 | return null 155 | } 156 | 157 | return parseInt(match[1]) - 1 158 | } 159 | 160 | if (prevItem.getName() == "§aPrevious Page") { 161 | const match = prevItem.getLore()[1].match(/^§5§o§ePage (\d+)$/) 162 | if (!match) { 163 | return null 164 | } 165 | 166 | return parseInt(match[1]) + 1 167 | } 168 | 169 | return 1 170 | } 171 | 172 | export const findUnopenedChest = (inv, excludedIndexes=[], page, canKismet=true) => { 173 | const items = inv.getItems() 174 | 175 | for (let i = 0; i < chestSlots.length; i++) { 176 | let ind = chestSlots[i] 177 | // Index including pages 178 | let extendedIndex = ind + (page - 1) * 54 179 | 180 | if (excludedIndexes.includes(extendedIndex)) { 181 | continue 182 | } 183 | 184 | let item = items[ind] 185 | 186 | // They are ordered, none can appear after here 187 | if (!item) { 188 | logger.push(`Item in index ${i} is null`) 189 | return [null, null] 190 | } 191 | 192 | if (item.getRegistryName() !== "minecraft:skull") { 193 | logger.push(`Index ${i} is not a skull`) 194 | continue 195 | } 196 | 197 | if (!item.getLore().includes("§5§o§cNo chests opened yet!")) { 198 | logger.push(`Index ${i} already looted`) 199 | continue 200 | } 201 | 202 | // Find the floor 203 | let dungeonType = item.getName().removeFormatting() 204 | let floorMatch = item.getLore()[1].match(/^(?:§.)+§eFloor (\w+)$/) 205 | if (!floorMatch) { 206 | logger.push(`Could not match floor: "${item.getLore()[1]}"`) 207 | excludedIndexes.push(extendedIndex) 208 | return [null, null] 209 | } 210 | 211 | let floorNum = parseInt(floorMatch[1]) 212 | if (isNaN(floorNum)) { 213 | floorNum = decodeNumeral(floorMatch[1]) 214 | } 215 | 216 | let floorLetter = dungeonType == "Master Mode The Catacombs" ? "M" : "F" 217 | // let floorNum = decodeNumeral(floorMatch[1]) 218 | let floor = `${floorLetter}${floorNum}` 219 | 220 | // Kismetting for this floor has been disabled for this session 221 | if (!canKismet && acPogObj.kismetFloors.includes(floor)) { 222 | excludedIndexes.push(extendedIndex) 223 | continue 224 | } 225 | 226 | return [ind, floor] 227 | } 228 | 229 | return [null, null] 230 | } 231 | 232 | const numeralValues = { 233 | "I": 1, 234 | "V": 5, 235 | "X": 10, 236 | "L": 50, 237 | "C": 100, 238 | "D": 500, 239 | "M": 1000 240 | } 241 | 242 | /** 243 | * Decodes a roman numeral into it's respective number. Eg VII -> 7, LII -> 52 etc. 244 | * Returns null if the numeral is invalid. 245 | * Supported symbols: I, V, X, L, C, D, M 246 | * @param {String} numeral 247 | * @returns {Number | null} 248 | */ 249 | export const decodeNumeral = (numeral) => { 250 | if (!numeral.match(/^[IVXLCDM]+$/)) return null 251 | let sum = 0 252 | for (let i = 0; i < numeral.length; i++) { 253 | let curr = numeralValues[numeral[i]] 254 | let next = i < numeral.length-1 ? numeralValues[numeral[i+1]] : 0 255 | 256 | if (curr < next) { 257 | sum += next - curr 258 | i++ 259 | continue 260 | } 261 | sum += curr 262 | } 263 | return sum 264 | } 265 | 266 | const tryParseBook = (line) => { 267 | const match = line.match(/^(?:§.)*Enchanted Book \((§d§l)?([\w ]+) (\w+)(?:§.)*\)$/) // https://regex101.com/r/zr3NwH/1 268 | if (!match) return null 269 | 270 | const [_, ultFormatting, bookName, tierStr] = match 271 | 272 | let tier = parseInt(tierStr) 273 | if (isNaN(tier)) { 274 | tier = decodeNumeral(tierStr) 275 | } 276 | const ult = !!ultFormatting 277 | 278 | const sbID = `ENCHANTMENT_${ult ? "ULTIMATE_" : ""}${bookName.toUpperCase().replace(/ /g, "_")}_${tier}`.replace("ULTIMATE_ULTIMATE_", "ULTIMATE_") 279 | 280 | return [sbID, 1] 281 | } 282 | 283 | const tryParseEssence = (line) => { 284 | const match = line.match(/^§5§o§d(\w+) Essence §8x(\d+)$/) 285 | if (!match) return null 286 | 287 | const [_, essType, qtyStr] = match 288 | 289 | const qty = parseInt(qtyStr) 290 | const sbID = `ESSENCE_${essType.toUpperCase()}` 291 | 292 | return [sbID, qty] 293 | } 294 | 295 | const tryParseLine = (line) => { 296 | const bookInfo = tryParseBook(line) 297 | if (bookInfo) { 298 | return bookInfo 299 | } 300 | 301 | const essenceInfo = tryParseEssence(line) 302 | if (essenceInfo) { 303 | return essenceInfo 304 | } 305 | 306 | const itemUnformatted = line.removeFormatting().trim() 307 | 308 | if (itemUnformatted in itemReplacements) { 309 | return [itemReplacements[itemUnformatted], 1] 310 | } 311 | 312 | const itemInfo = getSkyblockItems() 313 | const entry = itemInfo.find(a => a.name == itemUnformatted && !a.id.startsWith("STARRED_")) 314 | 315 | if (!entry) { 316 | logger.push(`No item ID found for item "${line}"`) 317 | return [false, `Could not find item ID for line "${line}&r"`] 318 | } 319 | 320 | return [entry.id, 1] 321 | } 322 | 323 | export const parseRewards = (itemLines, costStr) => { 324 | const chestInfo = { 325 | cost: 0, 326 | value: 0, 327 | profit: 0, 328 | items: [] // [{id: SKYBLOCK_ID, qty: 2, value: 1000}, ...] 329 | } 330 | 331 | // Parse the chest cost 332 | logger.push(`Chest Cost is "${costStr}"`) 333 | if (costStr !== "§5§o§aFREE") { 334 | const costMatch = costStr.match(/^§5§o§6([\d,]+) Coins$/) 335 | // If the cost can't be found, then it's safer to just not claim this chest and have the user do it manually 336 | if (!costMatch) { 337 | logger.push("Could not match chest cost with regex") 338 | return [false, "Could not find chest cost"] 339 | } 340 | 341 | chestInfo.cost = parseInt(costMatch[1].replace(/,/g, "")) 342 | } 343 | 344 | // And now the items 345 | for (let line of itemLines) { 346 | logger.push(`Trying to parse line "${line}"`) 347 | let result = tryParseLine(line) 348 | 349 | // Could not parse this line. Return the entire line to print back to the user for debugging 350 | if (!result) { 351 | logger.push(`Failed to parse`) 352 | return [false, `Could not parse line: "${line}&r"`] 353 | } 354 | 355 | let [sbID, qty] = result 356 | let itemValue = getSellPrice(sbID, true) 357 | 358 | if (itemValue === null) { 359 | logger.push(`Item value of ${sbID} was null`) 360 | return [false, `Could not find value of \"${line}\"`] 361 | } 362 | 363 | chestInfo.value += itemValue * qty 364 | 365 | chestInfo.items.push({ 366 | id: sbID, 367 | qty: qty, 368 | value: itemValue, 369 | displayName: line.replace(/^§5§o/, "") 370 | }) 371 | } 372 | 373 | chestInfo.items.sort((a, b) => b.value * b.qty - a.value * a.qty) 374 | chestInfo.profit = chestInfo.value - chestInfo.cost 375 | 376 | return [true, chestInfo] 377 | } 378 | 379 | export const sortChestData = (chestData) => { 380 | chestData.sort((a, b) => { 381 | if (b.items.some(a => alwaysBuy.has(a.id))) { 382 | return Infinity 383 | } 384 | 385 | return b.profit - a.profit 386 | }) 387 | } 388 | 389 | /** 390 | * Appends a string to a new line in a file. If the file does not exist, the file is created. 391 | * @param {String} moduleName 392 | * @param {String} filePath 393 | * @param {String} toWrite 394 | * @returns 395 | */ 396 | export const appendToFile = (moduleName, filePath, toWrite) => { 397 | if (!FileLib.exists(moduleName, filePath)) { 398 | FileLib.write(moduleName, filePath, toWrite, true) 399 | return 400 | } 401 | 402 | FileLib.append(moduleName, filePath, `\n${toWrite}`) 403 | } 404 | 405 | export const titleCase = (str) => { 406 | return str.replace(/(\b[a-z])/g, (a) => a.toUpperCase()) 407 | } 408 | 409 | export const getFormattedNameFromId = (itemID) => { 410 | 411 | if (itemID.startsWith("ENCHANTMENT_ULTIMATE")) { 412 | let [_, enchant, tier] = itemID.match(/^ENCHANTMENT_ULTIMATE_([\w_]+)_(\d+)$/) 413 | 414 | if (itemID.startsWith("ENCHANTMENT_ULTIMATE_WISE")) enchant = "ULTIMATE_WISE" 415 | if (itemID.startsWith("ENCHANTMENT_ULTIMATE_JERRY")) enchant = "ULTIMATE_JERRY" 416 | 417 | enchant = titleCase(enchant.replace(/_/g, " ").toLowerCase()) 418 | 419 | return `&aEnchanted Book (&d&l${enchant} ${numerals[parseInt(tier)]}&a)&r` 420 | } 421 | 422 | if (itemID.startsWith("ENCHANTMENT_")) { 423 | let [_, enchant, tier] = itemID.match(/^ENCHANTMENT_([\w_]+)_(\d+)$/) 424 | 425 | enchant = titleCase(enchant.replace(/_/g, " ").toLowerCase()) 426 | let enchantColor = "" 427 | 428 | if (tier >= 9) { 429 | enchantColor = "&d" 430 | } 431 | else if (tier == 8) { 432 | enchantColor = "&6" 433 | } 434 | else if (tier == 7) { 435 | enchantColor = "&5" 436 | } 437 | else if (tier == 6) { 438 | enchantColor = "&9" 439 | } 440 | else if (tier == 5) { 441 | enchantColor = "&a" 442 | } 443 | else { 444 | enchantColor = "&f" 445 | } 446 | 447 | return `&aEnchanted Book (${enchantColor}${enchant} ${numerals[parseInt(tier)]}&a)&r` 448 | } 449 | 450 | if (itemID.startsWith("ESSENCE_")) { 451 | const [_, essType] = itemID.match(/^ESSENCE_(.+)$/) 452 | 453 | const essName = titleCase(essType.replace(/_/g, " ").toLowerCase()) 454 | 455 | return `&d${essName} Essence&r` 456 | } 457 | 458 | const itemEntry = getItemApiData(itemID) 459 | 460 | if (!itemEntry) return itemID 461 | 462 | const tier = itemEntry.tier 463 | const name = itemEntry.name 464 | 465 | return `${tierColors[tier]}${name}` 466 | } -------------------------------------------------------------------------------- /AutoCroesus/index.js: -------------------------------------------------------------------------------- 1 | 2 | import { handleLootCommand, logLoot } from "./extra/lootLogs" 3 | import { printHelp } from "./util/help_command" 4 | import logger from "./util/logger" 5 | import { addAlwaysBuy, addWorthlessItem, alwaysBuy, initAlwaysBuy, initWorthless, loadAlwaysBuy, loadWorthless, updatePrices, worthless } from "./util/prices" 6 | import { 7 | acPogObj, 8 | appendToFile, 9 | CHEST_REGEX, 10 | findUnopenedChest, 11 | formatNumber, 12 | formattedBool, 13 | getCurrPage, 14 | getFormattedNameFromId, 15 | inCroesus, 16 | inRunGui, 17 | isInvLoaded, 18 | parseRewards, 19 | sortChestData, 20 | tryClickCroesus 21 | } from "./util/utils" 22 | 23 | if (!FileLib.exists("AutoCroesus", "data/always_buy.txt")) { 24 | initAlwaysBuy() 25 | } 26 | loadAlwaysBuy() 27 | 28 | if (!FileLib.exists("AutoCroesus", "data/worthless.txt")) { 29 | initWorthless() 30 | } 31 | loadWorthless() 32 | 33 | // Global variable spam yay! 34 | let autoClaiming = false 35 | let failedIndexes = [] // Indexes of chests which could not be claimed automatically. 36 | let loggedIndexes = [] 37 | let currChestData = null // Stored globally for overlay 38 | 39 | let chestClaimInfo = null // {page: 1, runSlot: 10, chestSlot:10} chestSlot can be null, other fields cannot 40 | 41 | let waitingForCroesus = false // Waiting for croesus to open, no spam clicking! 42 | let waitingForRunToOpen = false 43 | let waitingForChestToOpen = false // Prevents spam clicking 44 | let lastPageOn = null // More spam click prevention! 45 | let waitingOnPage = null 46 | 47 | let tryingToKismet = false 48 | let canKismet = true // Flips to false if kismetting is enabled but no kismets are available 49 | 50 | let lastClick = null 51 | let indexToClick = null // Store this to allow for click speed throttling 52 | 53 | register("tick", () => { 54 | if (indexToClick == null || Date.now() - lastClick < acPogObj.minClickDelay) return 55 | 56 | const inv = Player.getContainer() 57 | if (inv.getSize() <= indexToClick) return 58 | 59 | lastClick = Date.now() 60 | 61 | // Debug 62 | if (acPogObj.noClick) { 63 | ChatLib.chat(`Click ${indexToClick}`) 64 | indexToClick = null 65 | return 66 | } 67 | 68 | inv.click(indexToClick) 69 | indexToClick = null 70 | 71 | }) 72 | 73 | register("tick", () => { 74 | if (!autoClaiming || waitingForCroesus) return 75 | 76 | const inv = Player.getContainer() 77 | if (inv.getSize() > 45) return 78 | 79 | if (waitingForRunToOpen || waitingForChestToOpen) { 80 | ChatLib.chat(`Sequence out of sync, stopping. waitingForRun: ${waitingForRunToOpen} waitingCroesus: ${waitingForCroesus} lastPageOn: ${lastPageOn}`) 81 | reset() 82 | return 83 | } 84 | 85 | startClaiming() 86 | }) 87 | 88 | // Logic for inside of the main Croesus menu 89 | register("tick", () => { 90 | if (!inCroesus()) { 91 | slotRenderer.unregister() 92 | return 93 | } 94 | 95 | if (acPogObj.showChestInfo || acPogObj.noClick) { 96 | slotRenderer.register() 97 | } 98 | 99 | if (!autoClaiming || waitingForRunToOpen) { 100 | return 101 | } 102 | 103 | waitingForCroesus = false 104 | // Don't reset for chest keys 105 | if (!chestClaimInfo) { 106 | currChestData = [] 107 | } 108 | 109 | const inv = Player.getContainer() 110 | const items = inv.getItems() 111 | const page = getCurrPage() 112 | 113 | if (page == null || waitingOnPage !== null && page !== waitingOnPage) return 114 | 115 | // chestClaimInfo is already set, which means we're coming back from opening another chest (probably using a chest key or kismet) 116 | if (chestClaimInfo && chestClaimInfo.runSlot !== null) { 117 | 118 | // Try to get to the correct page 119 | if (page !== chestClaimInfo.page) { 120 | if (lastPageOn == page) return 121 | 122 | lastPageOn = page 123 | indexToClick = 53 124 | return 125 | } 126 | 127 | // We're on the right page now 128 | lastPageOn = null 129 | indexToClick = chestClaimInfo.runSlot 130 | waitingForRunToOpen = true 131 | return 132 | } 133 | 134 | // Search for unopened chests 135 | const [slotIndex, floor] = findUnopenedChest(inv, failedIndexes, page, canKismet) 136 | 137 | if (slotIndex) { 138 | chestClaimInfo = { 139 | floor, 140 | page: page, 141 | runSlot: slotIndex, 142 | chestSlot: null, 143 | skipKismet: false, // Used when a chest has already been rerolled 144 | } 145 | 146 | logger.push(`Clicking unopened chest at slot index ${slotIndex} (${slotIndex % 54})`) 147 | waitingForRunToOpen = true 148 | indexToClick = slotIndex 149 | return 150 | } 151 | 152 | if (items[53] && items[53].getRegistryName() == "minecraft:arrow") { 153 | if (lastPageOn == page) return 154 | 155 | logger.push("Moving to next page") 156 | lastPageOn = page 157 | indexToClick = 53 158 | waitingOnPage = page + 1 159 | return 160 | } 161 | 162 | logger.push("All done!") 163 | ChatLib.chat(`&aAll chests looted!`) 164 | logger.write() 165 | reset() 166 | Client.currentGui.close() 167 | }) 168 | 169 | register("tick", () => { 170 | if (!autoClaiming && !acPogObj.showChestInfo) { 171 | reset() 172 | return 173 | } 174 | 175 | const inv = Player.getContainer() 176 | 177 | if (!inRunGui()) { 178 | if (!chestClaimInfo) { 179 | currChestData = null 180 | } 181 | return 182 | } 183 | 184 | if (!isInvLoaded(inv)) return 185 | if (waitingForChestToOpen) return 186 | 187 | waitingForRunToOpen = false 188 | lastPageOn = null 189 | waitingOnPage = null 190 | 191 | if (chestClaimInfo && chestClaimInfo.chestSlot !== null) { 192 | waitingForChestToOpen = true 193 | indexToClick = chestClaimInfo.chestSlot 194 | logger.push(`Clicking ${indexToClick} early and returning`) 195 | chestClaimInfo.chestSlot = null 196 | return 197 | } 198 | 199 | 200 | const items = inv.getItems() 201 | const chestData = [] 202 | 203 | // Find the chest items and parse the loot shown in their tooltips 204 | for (let i = 0; i < 27; i++) { 205 | let item = items[i] 206 | 207 | if (!item) continue 208 | 209 | let match = item.getName().match(CHEST_REGEX) 210 | if (!match) { 211 | // No spamming logs 212 | if (autoClaiming) { 213 | logger.push(`index ${i} does not match chest regex: "${item.getName()}"`) 214 | } 215 | 216 | continue 217 | } 218 | 219 | let [_, color, chestName] = match 220 | 221 | let lore = item.getLore() 222 | let lootEnd = lore.indexOf("§5§o") // Empty line at the bottom of 223 | let chestFormatted = `${color}${chestName}` 224 | 225 | // No spamming logs 226 | if (autoClaiming) { 227 | logger.push(`Chest Name: "${chestName}", Formatted: "${chestFormatted}", lootEnd: ${lootEnd}\nLore:\n ${lore.join("\n ")}`) 228 | } 229 | 230 | 231 | if (!lootEnd) { 232 | // No spamming logs 233 | if (autoClaiming) { 234 | logger.push(`Loot end not found`) 235 | } 236 | 237 | ChatLib.chat(`&cCould not find loot end!`) 238 | reset() 239 | return 240 | } 241 | 242 | let costInd = lore.indexOf("§5§o§7Cost") 243 | // No spamming logs 244 | if (autoClaiming) { 245 | logger.push(`Cost index is ${costInd}`) 246 | } 247 | 248 | if (!costInd) { 249 | ChatLib.chat(`&cCould not find cost index!`) 250 | reset() 251 | return 252 | } 253 | 254 | // All of the annoying item and cost parsing is done here 255 | let [success, rewardData] = parseRewards(lore.slice(2, lootEnd), lore[costInd+1]) 256 | 257 | if (!success) { 258 | if (chestClaimInfo) { 259 | if (autoClaiming) { 260 | logger.push(`Failed to check this chest: ${rewardData}`) 261 | } 262 | ChatLib.chat(`Failed to check ${chestFormatted} Chest: &r${rewardData}\n&eThis run will be skipped as the info for this chest is incomplete.`) 263 | failedIndexes.push(chestClaimInfo.runSlot + (chestClaimInfo.page - 1) * 54) 264 | chestClaimInfo = null 265 | // Go back to main croesus menu 266 | indexToClick = 30 267 | } 268 | return 269 | } 270 | 271 | rewardData.slot = i 272 | 273 | rewardData.chestName = chestName 274 | rewardData.chestColor = color 275 | 276 | chestData.push(rewardData) 277 | } 278 | 279 | sortChestData(chestData) 280 | 281 | currChestData = chestData 282 | 283 | if (!autoClaiming) return 284 | 285 | const bedrockChest = chestData.find(a => a.chestName == "Bedrock") ?? null 286 | const hasAlwaysBuyItem = bedrockChest && bedrockChest.items.some(a => alwaysBuy.has(a.id)) 287 | 288 | if (!hasAlwaysBuyItem && !chestClaimInfo.skipKismet && acPogObj.useKismets && bedrockChest !== null && acPogObj.kismetFloors.includes(chestClaimInfo.floor)) { 289 | const profit = bedrockChest.profit 290 | if (profit < acPogObj.kismetMinProfit) { 291 | tryingToKismet = true 292 | indexToClick = bedrockChest.slot 293 | waitingForChestToOpen = true 294 | return 295 | } 296 | } 297 | 298 | ChatLib.chat(`Claiming the ${chestData[0].chestColor}${chestData[0].chestName} Chest`) 299 | 300 | const chestsToClaim = [chestData[0]] 301 | if (chestData[1].profit >= acPogObj.chestKeyMinProfit && acPogObj.useChestKeys) { 302 | ChatLib.chat(`Using chest key on the ${chestData[1].chestColor}${chestData[1].chestName} Chest`) 303 | chestClaimInfo.chestSlot = chestData[1].slot 304 | chestsToClaim.push(chestData[1]) 305 | } 306 | 307 | // Log the loot for this floor 308 | const runIndex = chestClaimInfo.runSlot + (chestClaimInfo.page - 1) * 54 309 | if (!loggedIndexes.includes(runIndex)) { 310 | // ChatLib.chat(`Logged loot for page ${chestClaimInfo.page} run slot ${chestClaimInfo.runSlot}`) 311 | loggedIndexes.push(runIndex) 312 | logLoot(chestClaimInfo.floor, chestsToClaim, chestData.length) 313 | } 314 | 315 | const toClick = chestData[0].slot 316 | indexToClick = toClick 317 | waitingForChestToOpen = true 318 | failedIndexes.push(chestClaimInfo.runSlot + (chestClaimInfo.page - 1) * 54) 319 | }) 320 | 321 | register("tick", () => { 322 | if (!waitingForChestToOpen) return 323 | const inv = Player.getContainer() 324 | 325 | if (!isInvLoaded(inv) || inv.getSize() < 32) return 326 | const match = inv.getName().match(/^(\w+) Chest$/) 327 | 328 | if (!match) return 329 | 330 | const chestName = match[1] 331 | 332 | waitingForChestToOpen = false 333 | 334 | const items = inv.getItems() 335 | if (tryingToKismet && chestName == "Bedrock" && !chestClaimInfo.skipKismet) { 336 | const kismetSlot = items[50] 337 | tryingToKismet = false 338 | 339 | if (!kismetSlot || kismetSlot.getName() !== "§aReroll Chest" || kismetSlot.getLore().includes("§5§o§eBring a Kismet Feather")) { 340 | canKismet = false 341 | ChatLib.chat(`&cCould not find kismets. Auto claiming for ${chestClaimInfo.floor} is disabled until kismetting is turned off or kismets are available to use.`) 342 | failedIndexes.push(chestClaimInfo.chestSlot + (chestClaimInfo.page - 1) * 54) 343 | chestClaimInfo = null 344 | Client.currentGui.close() 345 | return 346 | } 347 | 348 | if (kismetSlot.getLore().includes("§5§o§aYou already rerolled a chest!")) { 349 | ChatLib.chat(`&eAlready rerolled!`) 350 | chestClaimInfo.skipKismet = true 351 | indexToClick = 49 352 | waitingForRunToOpen = true 353 | return 354 | } 355 | 356 | chestClaimInfo.skipKismet = true 357 | indexToClick = 50 358 | return 359 | } 360 | 361 | indexToClick = 31 362 | 363 | // We're done looting this run (Unless using a chest key) 364 | if (chestClaimInfo.chestSlot === null) { 365 | chestClaimInfo = null 366 | } 367 | }) 368 | 369 | const reset = () => { 370 | chestClaimInfo = null 371 | autoClaiming = false 372 | 373 | waitingForCroesus = false 374 | waitingForRunToOpen = false 375 | waitingForChestToOpen = false 376 | lastPageOn = null 377 | waitingOnPage = null 378 | 379 | indexToClick = null 380 | tryingToKismet = false 381 | canKismet = true 382 | failedIndexes = [] 383 | } 384 | 385 | const startClaiming = () => { 386 | autoClaiming = true 387 | 388 | if (!tryClickCroesus()) { 389 | autoClaiming = false 390 | ChatLib.chat(`Could not click Croesus (Too far away?)`) 391 | reset() 392 | return 393 | } 394 | 395 | waitingForCroesus = true 396 | } 397 | 398 | const updateApiData = (onDone=null) => { 399 | updatePrices().then(() => { 400 | ChatLib.chat(`&aSuccessfully grabbed data from API!`) 401 | acPogObj.lastApiUpdate = Date.now() 402 | if (onDone) { 403 | onDone() 404 | } 405 | }).catch((reason) => { 406 | logger.write() 407 | ChatLib.chat(`&cFailed to grab data from API: ${reason}`) 408 | ChatLib.chat(`&cTo try again, run //ac api`) 409 | ChatLib.chat(`&eIf this keeps happening, contact UnclaimedBloom6 on Discord.`) 410 | }) 411 | } 412 | 413 | const printSettings = () => { 414 | const kismetFloorsFormatted = acPogObj.kismetFloors.map(floor => { 415 | if (floor.startsWith("M")) { 416 | return "&c&l" + floor + "&r" 417 | } 418 | return "&a" + floor + "&r" 419 | }) 420 | 421 | new Message( 422 | `&b&lAutoCroesus &aSettings\n`, 423 | `&7Hover over a setting to view the command to change it.\n`, 424 | new TextComponent(` Chest Info Overlay: ${formattedBool(acPogObj.showChestInfo)}\n`) 425 | .setHover("show_text", "//ac overlay"), 426 | `\n`, 427 | new TextComponent(` Min Click Delay: &6${acPogObj.minClickDelay}ms\n`) 428 | .setHover("show_text", "//ac delay \n&cWarning: Setting this to a low value with low ping\n&cwill make this module absolutely fucking ZOOM.\n&cBe safe!"), 429 | `\n`, 430 | new TextComponent(` Use Chest Keys: ${formattedBool(acPogObj.useChestKeys)}\n`) 431 | .setHover("show_text", "//ac key"), 432 | new TextComponent(` Min Chest Key Profit: &6${formatNumber(acPogObj.chestKeyMinProfit)}\n`) 433 | .setHover("show_text", "//ac key "), 434 | `\n`, 435 | new TextComponent(` Use Kismets: ${formattedBool(acPogObj.useKismets)}\n`) 436 | .setHover("show_text", "//ac kismet"), 437 | new TextComponent(` Min Kismet Profit: &6${formatNumber(acPogObj.kismetMinProfit)}\n`) 438 | .setHover("show_text", "//ac kismet "), 439 | new TextComponent(` Kismet Floors: &a${kismetFloorsFormatted.join(", ") || "&cNONE"}\n`) 440 | .setHover("show_text", "//ac kismet "), 441 | ).chat() 442 | } 443 | 444 | register("command", (...args) => { 445 | if (!args || !args[0]) { 446 | printHelp() 447 | return 448 | } 449 | 450 | if (args[0] == "reset") { 451 | reset() 452 | ChatLib.chat(`Reset!`) 453 | } 454 | 455 | if (args[0] == "loot") { 456 | handleLootCommand(...args) 457 | return 458 | } 459 | 460 | if (args[0] == "settings" || args[0] == "config" || args[0] == "s" || args[0] == "c") { 461 | printSettings() 462 | return 463 | } 464 | 465 | if (args[0] == "overlay") { 466 | acPogObj.showChestInfo = !acPogObj.showChestInfo 467 | ChatLib.chat(`Chest Profit Overlay is now ${formattedBool(acPogObj.showChestInfo)}`) 468 | } 469 | 470 | if (args[0] == "delay") { 471 | const delay = parseInt(args[1]) 472 | if (isNaN(delay)) { 473 | ChatLib.chat(`//ac delay `) 474 | return 475 | } 476 | 477 | acPogObj.minClickDelay = delay 478 | ChatLib.chat(`Min Click Delay is now &6${delay}ms`) 479 | 480 | if (delay < 150) { 481 | ChatLib.chat(`&cWarning: Setting the delay to a low value with low ping will claim chests so quickly that people in chat might notice. Be careful setting this so low.`) 482 | } 483 | 484 | return 485 | } 486 | 487 | if (args[0] == "kismet" || args[0] == "reroll") { 488 | // Add/remove floors to kismet on 489 | const isFloor = /^[fFmM][1-7]$/.test(args[1]) 490 | if (isFloor) { 491 | const upper = args[1].toUpperCase() 492 | const existingInd = acPogObj.kismetFloors.indexOf(upper) 493 | if (existingInd !== -1) { 494 | acPogObj.kismetFloors.splice(existingInd, 1) 495 | ChatLib.chat(`Removed ${upper} from kismet floors`) 496 | return 497 | } 498 | acPogObj.kismetFloors.push(upper) 499 | ChatLib.chat(`Added ${upper} to kismet floors`) 500 | return 501 | } 502 | 503 | // Change kismet min profit 504 | let value = parseInt((args[1] ?? "").replace(/[,._]/g, "")) 505 | if (!isNaN(value)) { 506 | acPogObj.kismetMinProfit = value 507 | ChatLib.chat(`Min kismet profit is now ${formatNumber(value)}`) 508 | return 509 | } 510 | 511 | // Toggle using kismets 512 | acPogObj.useKismets = !acPogObj.useKismets 513 | ChatLib.chat(`Use Kismets is now ${formattedBool(acPogObj.useKismets)}`) 514 | } 515 | 516 | if (args[0] == "key" || args[0] == "chestkey") { 517 | let value = parseInt(args[1]?.replace(/[,_]/g, "")) 518 | if (!isNaN(value)) { 519 | acPogObj.chestKeyMinProfit = value 520 | ChatLib.chat(`Min chest key profit is now ${formatNumber(value)}`) 521 | return 522 | } 523 | 524 | acPogObj.useChestKeys = !acPogObj.useChestKeys 525 | ChatLib.chat(`Use Chest Keys is now ${formattedBool(acPogObj.useChestKeys)}`) 526 | } 527 | 528 | if (args[0] == "go") { 529 | const sinceUpdate = Date.now() - acPogObj.lastApiUpdate 530 | logger.clear() 531 | 532 | // Updated recently enough 533 | if (sinceUpdate <= 1_800_000) { 534 | autoClaiming = true 535 | return 536 | } 537 | 538 | logger.push("Updating prices from API") 539 | ChatLib.chat(`&ePrices have not been updated in over 30 minutes. Grabbing data...`) 540 | 541 | updateApiData(() => { 542 | logger.push("Successfully updated prices, starting to claim now") 543 | autoClaiming = true 544 | }) 545 | } 546 | 547 | if (args[0] == "forcego") { 548 | ChatLib.chat(`&aClaiming without updating API.`) 549 | autoClaiming = true 550 | return 551 | } 552 | 553 | if (args[0] == "api") { 554 | ChatLib.chat(`&aGrabbing data...`) 555 | updateApiData() 556 | return 557 | } 558 | 559 | if (args[0] == "alwaysbuy") { 560 | if (!args[1]) { 561 | let components = [] 562 | for (let id of alwaysBuy) { 563 | let formattedName = getFormattedNameFromId(id) ?? "&c&lUNKNOWN ITEM" 564 | components.push(new TextComponent(` ${formattedName}\n`).setHover("show_text", id).setClick("run_command", `//ac alwaysbuy ${id}`)) 565 | } 566 | 567 | new Message( 568 | `&b&lAlways Buy Items:\n`, 569 | ...components 570 | ).chat() 571 | 572 | return 573 | } 574 | 575 | if (args[1] == "reset") { 576 | ChatLib.chat(`&aResetting the list of items to always buy to their defaults.`) 577 | initAlwaysBuy() 578 | loadAlwaysBuy() 579 | return 580 | } 581 | 582 | addAlwaysBuy(args[1]) 583 | return 584 | } 585 | 586 | if (args[0] == "worthless") { 587 | if (!args[1]) { 588 | let components = [] 589 | for (let id of worthless) { 590 | let formattedName = getFormattedNameFromId(id) ?? "&c&lUNKNOWN ITEM" 591 | components.push(new TextComponent(` ${formattedName}\n`).setHover("show_text", id).setClick("run_command", `//ac worthless ${id}`)) 592 | } 593 | 594 | new Message( 595 | `&b&lWorthless Items:\n`, 596 | ...components 597 | ).chat() 598 | 599 | return 600 | } 601 | 602 | if (args[1] == "reset") { 603 | ChatLib.chat(`&aResetting the list of worthless items to their defaults.`) 604 | initWorthless() 605 | loadWorthless() 606 | return 607 | } 608 | 609 | addWorthlessItem(args[1]) 610 | return 611 | } 612 | 613 | if (args[0] == "noclick") { 614 | acPogObj.noClick = !acPogObj.noClick 615 | ChatLib.chat(`No Click is now set to ${formattedBool(acPogObj.noClick)}`) 616 | return 617 | } 618 | 619 | if (args[0] == "copy") { 620 | logger.copy() 621 | ChatLib.chat(`&aCopied AutoCroesus log to clipboard. (${logger.str.length} chars)`) 622 | return 623 | } 624 | 625 | }).setName("autocroesus").setAliases("/ac") 626 | 627 | register("postGuiRender", () => { 628 | if (!currChestData || !inRunGui()) return 629 | 630 | const x = 5 631 | const y = 5 632 | 633 | Renderer.translate(x, y, 1000) 634 | Renderer.scale(0.6) 635 | 636 | let final = "" 637 | for (let chestInfo of currChestData) { 638 | let { cost, value, profit, items, chestColor, chestName } = chestInfo 639 | 640 | let profitStr = (profit <= 0 ? "&c" : "&a+") + `${formatNumber(Math.floor(profit))}` 641 | final += `${chestColor}${chestName} Chest &6(${formatNumber(cost)}) ${profitStr}` 642 | 643 | for (let item of items) { 644 | let { id, qty, value, displayName } = item 645 | 646 | final += `\n ${displayName} ${value > 0 ? "&a" : "&e"}+${formatNumber(Math.floor(value * qty))}` 647 | } 648 | 649 | final += "\n\n" 650 | } 651 | 652 | Renderer.drawString(final, x, y) 653 | Renderer.finishDraw() 654 | }) 655 | 656 | const slotRenderer = register("renderSlot", (slot) => { 657 | if (acPogObj.noClick && slot.getIndex() < 54 && autoClaiming) { 658 | const x = slot.getDisplayX() 659 | const y = slot.getDisplayY() 660 | 661 | const txt = `${slot.getIndex()}` 662 | const width = Renderer.getStringWidth(txt) 663 | 664 | Renderer.translate(x, y, 1000) 665 | Renderer.scale(0.6) 666 | Renderer.drawString(txt, 5, 5) 667 | Renderer.finishDraw() 668 | } 669 | 670 | const item = slot.getItem() 671 | if (!item || !item.getLore().includes("§5§o§cNo chests opened yet!")) { 672 | return 673 | } 674 | 675 | const x = slot.getDisplayX() 676 | const y = slot.getDisplayY() 677 | 678 | Renderer.drawRect(Renderer.color(0, 255, 0, 175), x, y, 16, 16) 679 | }).unregister() 680 | 681 | 682 | const killSwitchKeys = [ 683 | Keyboard.KEY_LSHIFT, 684 | Keyboard.KEY_ESCAPE, 685 | ] 686 | 687 | // Kill switch 688 | register("tick", () => { 689 | if (!autoClaiming) return 690 | 691 | if (killSwitchKeys.some(a => Keyboard.isKeyDown(a))) { 692 | reset() 693 | ChatLib.chat(`Kill switch activated!`) 694 | } 695 | }) 696 | --------------------------------------------------------------------------------