├── .github
└── FUNDING.yml
├── .gitignore
├── LICENSE
├── README.md
├── config.js
├── core
├── CustomRegisters.js
├── Event.js
├── EventEnums.js
└── Feature.js
├── features
├── commands
│ └── InventoryLog.js
├── dungeons
│ ├── AutoRequeueDungeons.js
│ ├── BlazeSolver.js
│ ├── BossSplits.js
│ ├── BoulderSolver.js
│ ├── BoxStarMobs.js
│ ├── ChestProfit.js
│ ├── CreeperBeamsSolver.js
│ ├── CroesusClicks.js
│ ├── CroesusProfit.js
│ ├── CryptsDisplay.js
│ ├── DeathsDisplay.js
│ ├── ExtraStats.js
│ ├── HideNoStarTag.js
│ ├── LividSolver.js
│ ├── MilestoneDisplay.js
│ ├── MimicKilled.js
│ ├── PuzzleDisplay.js
│ ├── RemoveDmgTag.js
│ ├── RunSplits.js
│ ├── RunsLogger.js
│ ├── SecretsClickedBox.js
│ ├── SecretsSound.js
│ ├── TeleportMazeSolver.js
│ ├── ThreeWeirdosSolver.js
│ ├── TicTacToeAlgorithm.js
│ ├── TicTacToeSolver.js
│ ├── TriviaSolver.js
│ └── WaterBoardSolver.js
├── garden
│ ├── GardenDisplay.js
│ ├── GardenEvents.js
│ ├── PestsDisplay.js
│ ├── VisitorBzButton.js
│ └── VisitorProfit.js
├── gui
│ ├── AbstractGui.js
│ ├── Button.js
│ ├── CancelMessage.js
│ ├── CommandAliases.js
│ ├── KeyShortcuts.js
│ └── TitleMessage.js
├── kuudra
│ ├── CratesWaypoints.js
│ └── KuudraSplits.js
├── mining
│ ├── ComissionDisplay.js
│ ├── EmissaryWaypoints.js
│ └── PowderDisplay.js
├── misc
│ ├── ArmorDisplay.js
│ ├── AttributeShardDisplay.js
│ ├── BlockOverlay.js
│ ├── BonzoMaskInvincibility.js
│ ├── ChampionDisplay.js
│ ├── ChatWaypoint.js
│ ├── CompactDisplay.js
│ ├── CopyChat.js
│ ├── CultivatingDisplay.js
│ ├── DrillFuelDisplay.js
│ ├── EnchantedBookDisplay.js
│ ├── EquipmentDisplay.js
│ ├── EtherwarpOverlay.js
│ ├── FactoryHelper.js
│ ├── HideEmptyTooltip.js
│ ├── InventoryButtons.js
│ ├── InventoryHistory.js
│ ├── InventoryHud.js
│ ├── ItemRarity.js
│ ├── MiddleClickGuis.js
│ ├── NoCursorReset.js
│ ├── NoDeathAnimation.js
│ ├── NoEndermanTeleport.js
│ ├── NoLightning.js
│ ├── PhoenixInvincibility.js
│ ├── QuiverDisplay.js
│ ├── RagnarokAxeCooldown.js
│ ├── RemoveFrontView.js
│ ├── RenderItems.js
│ ├── SearchBar.js
│ ├── SlotLocking.js
│ ├── SmolderingPolarizationDisplay.js
│ ├── SystemTimeDisplay.js
│ ├── ToggleSprint.js
│ └── WorldAgeDisplay.js
├── rift
│ ├── BoxBerberis.js
│ ├── EffigiesWaypoint.js
│ ├── GlyphRender.js
│ ├── LavaMaze.js
│ ├── MushroomTimer.js
│ ├── Tubulator.js
│ └── WoodenButtons.js
└── slayers
│ ├── BossSlainTime.js
│ ├── BossSpawnTime.js
│ └── SlayerBossDisplay.js
├── index.js
├── metadata.json
└── shared
├── ChestMenu.js
├── Command.js
├── CustomSplits.js
├── DGlStateManager.js
├── DraggableGui.js
├── EtherwarpHelper.js
├── InventoryButton.js
├── Location.js
├── Persistence.js
├── PuzzleRoomScanner.js
├── Render.js
├── TextHelper.js
└── Vec3.js
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: docilelm
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
12 | polar: # Replace with a single Polar username
13 | buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
14 | thanks_dev: # Replace with a single thanks.dev username
15 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.json
2 | *.toml
3 | Dev.js
4 | !metadata.json
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Doc
2 | ChatTriggers Module [https://www.chattriggers.com/modules/v/Doc]
3 |
4 | # Features
5 |
6 | ## Dungeons
7 | + Box Star Mobs
8 | + Draws a box at star mobs
9 | + Image

10 | + Show Secrets Clicked
11 | + Draws a box at the clicked wither essence/chests/redstone skull
12 | + Run Splits
13 | + Displays your current dungeon run's splits
14 | + Image

15 | + Chest Profit
16 | + Displays the current chest's profit
17 | + Image

18 | + Show Croesus Clicks
19 | + Highlights chests you've _CLICKED_
20 | + The list resets on world change
21 | + Image

22 | + Croesus Profit
23 | + Displays the current chests profit inside of croesus
24 | + Show Extra Stats
25 | + Automatically sends the /showextrastats command at the end of a dungeon run
26 | + Boss Splits
27 | + Displays your current dungeon's boss splits
28 | + Image

29 |
30 | ## Mining
31 | + Emissary Waypoints
32 | + Draws a waypoint on every emissary in the dwarven mines
33 | + Image

34 | + Gemstone Mining Profit
35 | + Displays your current session's mined gemstones and profit made
36 | + Image

37 |
38 | ## Fishing
39 | + Boss Bar
40 | + Displays a boss bar with [hp/max hp] of loot sharing mobs
41 | + Timer Title
42 | + Displays the hypixel's timer as title
43 |
44 | ## Garden
45 | + Visitor Profit Display
46 | + Displays most of the visitor's profit also calculating the special item
47 | + Image

48 |
49 | ## Slayer
50 | + Boss Slain Timer
51 | + Sends a chat message with the time it took to kill the slayer
52 |
53 | ## Tracker
54 | + Ghost Tracker
55 | + Displays your current session's kills, drops, magic find, profit and kill combo
56 | + Image

57 | + Trophy Fishing Tracker
58 | + Displays the trophy fishes you've caught in the current session
59 | + Image

60 | + Powder Mining Tracker
61 | + Displays your current session's chests, powder gained
62 | + Image

63 |
64 | ## Kuudra
65 | + Fatal Tempo Display
66 | + Displays your current fatal tempo time, hits and percent
67 | + Image

68 | + Crates Waypoints
69 | + Draws a waypoint at the crates
70 | + Stops rendering them once p1 of kuudra is done
71 |
72 | ## Misc
73 | + Ragnarok Axe Cooldown
74 | + Displays the current ragnarok axe cooldown
75 | + RngMeter
76 | + Displays your current rng meter for dungeons or slayers
77 | + Dungeons

78 | + Slayers

79 |
80 | ## Commands
81 | + /ping
82 | + Inventory Logs
83 | + saves your current inventory so you can open or remove it later on
84 | + in case you do multiple things and constantly forget about your hotbar/inventory
85 | + /invlogs \ \
86 | + type can be [add, open, remove] name will be the name which the inventory was saved with or will be saved with
87 |
--------------------------------------------------------------------------------
/core/Event.js:
--------------------------------------------------------------------------------
1 | import { customTriggers } from "./CustomRegisters"
2 |
3 | export class Event {
4 | constructor(name, cb, args) {
5 | // Fields needed for this event
6 | this.name = name
7 | this.cb = cb
8 | this.args = args
9 |
10 | // The register itself
11 | this.isCustom = typeof this.name === "number"
12 | this._register = this.isCustom
13 | // Custom triggers are number enums
14 | ? customTriggers.get(this.name)?.(this.cb, this.args)
15 | // Normal are just strings
16 | : register(this.name, this.cb).unregister()
17 |
18 | this.isCustom && Array.isArray(this._register)
19 | ? this._register.forEach(it => it.unregister())
20 | : this._register.unregister()
21 |
22 | // Always start unregistered
23 | this.hasRegistered = false
24 | }
25 |
26 | /**
27 | * - Registers this [event]'s trigger
28 | * @returns this for method chaining
29 | */
30 | register() {
31 | if (this.hasRegistered) return this
32 |
33 | if (this.isCustom && Array.isArray(this._register)) {
34 | for (let idx = 0; idx < this._register.length; idx++)
35 | this._register[idx].register()
36 | this.hasRegistered = true
37 |
38 | return this
39 | }
40 |
41 | this._register.register()
42 | this.hasRegistered = true
43 |
44 | return this
45 | }
46 |
47 | /**
48 | * - Unregisters this [event]'s trigger
49 | * @returns this for method chaining
50 | */
51 | unregister() {
52 | if (!this.hasRegistered) return this
53 |
54 | if (this.isCustom && Array.isArray(this._register)) {
55 | for (let idx = 0; idx < this._register.length; idx++)
56 | this._register[idx].unregister()
57 | this.hasRegistered = false
58 |
59 | return this
60 | }
61 |
62 | this._register.unregister()
63 | this.hasRegistered = false
64 |
65 | return this
66 | }
67 | }
--------------------------------------------------------------------------------
/core/EventEnums.js:
--------------------------------------------------------------------------------
1 | let idx = 0
2 |
3 | const EnumParticleTypes = net.minecraft.util.EnumParticleTypes
4 |
5 | // Just for auto completion
6 | export const ParticleEnums = {
7 | BARRIER: EnumParticleTypes.BARRIER,
8 | BLOCK_CRACK: EnumParticleTypes.BLOCK_CRACK,
9 | BLOCK_DUST: EnumParticleTypes.BLOCK_DUST,
10 | CLOUD: EnumParticleTypes.CLOUD,
11 | CRIT: EnumParticleTypes.CRIT,
12 | CRIT_MAGIC: EnumParticleTypes.CRIT_MAGIC,
13 | DRIP_LAVA: EnumParticleTypes.DRIP_LAVA,
14 | DRIP_WATER: EnumParticleTypes.DRIP_WATER,
15 | ENCHANTMENT_TABLE: EnumParticleTypes.ENCHANTMENT_TABLE,
16 | EXPLOSION_HUGE: EnumParticleTypes.EXPLOSION_HUGE,
17 | EXPLOSION_LARGE: EnumParticleTypes.EXPLOSION_LARGE,
18 | EXPLOSION_NORMAL: EnumParticleTypes.EXPLOSION_NORMAL,
19 | FIREWORKS_SPARK: EnumParticleTypes.FIREWORKS_SPARK,
20 | FLAME: EnumParticleTypes.FLAME,
21 | FOOTSTEP: EnumParticleTypes.FOOTSTEP,
22 | HEART: EnumParticleTypes.HEART,
23 | ITEM_CRACK: EnumParticleTypes.ITEM_CRACK,
24 | ITEM_TAKE: EnumParticleTypes.ITEM_TAKE,
25 | LAVA: EnumParticleTypes.LAVA,
26 | MOB_APPEARANCE: EnumParticleTypes.MOB_APPEARANCE,
27 | NOTE: EnumParticleTypes.NOTE,
28 | PORTAL: EnumParticleTypes.PORTAL,
29 | REDSTONE: EnumParticleTypes.REDSTONE,
30 | SLIME: EnumParticleTypes.SLIME,
31 | SMOKE_LARGE: EnumParticleTypes.SMOKE_LARGE,
32 | SMOKE_NORMAL: EnumParticleTypes.SMOKE_NORMAL,
33 | SNOW_SHOVEL: EnumParticleTypes.SNOW_SHOVEL,
34 | SNOWBALL: EnumParticleTypes.SNOWBALL,
35 | SPELL: EnumParticleTypes.SPELL,
36 | SPELL_INSTANT: EnumParticleTypes.SPELL_INSTANT,
37 | SPELL_MOB: EnumParticleTypes.SPELL_MOB,
38 | SPELL_MOB_AMBIENT: EnumParticleTypes.SPELL_MOB_AMBIENT,
39 | SPELL_WITCH: EnumParticleTypes.SPELL_WITCH,
40 | SUSPENDED: EnumParticleTypes.SUSPENDED,
41 | SUSPENDED_DEPTH: EnumParticleTypes.SUSPENDED_DEPTH,
42 | TOWN_AURA: EnumParticleTypes.TOWN_AURA,
43 | VILLAGER_ANGRY: EnumParticleTypes.VILLAGER_ANGRY,
44 | VILLAGER_HAPPY: EnumParticleTypes.VILLAGER_HAPPY,
45 | WATER_BUBBLE: EnumParticleTypes.WATER_BUBBLE,
46 | WATER_DROP: EnumParticleTypes.WATER_DROP,
47 | WATER_SPLASH: EnumParticleTypes.WATER_SPLASH,
48 | WATER_WAKE: EnumParticleTypes.WATER_WAKE,
49 | }
50 |
51 | export default {
52 | STEP: idx++,
53 | CHAT: idx++,
54 | SOUNDPLAY: idx++,
55 | COMMAND: idx++,
56 | RENDERENTITY: idx++,
57 | POSTRENDERENTITY: idx++,
58 | ENTITYDEATH: idx++,
59 | PACKET: {
60 | CLIENT: {
61 | BLOCKPLACEMENT: idx++,
62 | WINDOWCLICK: idx++,
63 | DIGGING: idx++,
64 | WINDOWCLOSE: idx++,
65 | HELDITEMCHANGE: idx++
66 | },
67 | SERVER: {
68 | CHAT: idx++,
69 | ACTIONBAR: idx++,
70 | SCOREBOARD: idx++,
71 | TABUPDATE: idx++,
72 | TABADD: idx++,
73 | TABHEADERFOOTER: idx++,
74 | WINDOWITEMS: idx++,
75 | WINDOWOPEN: idx++,
76 | WINDOWCLOSE: idx++,
77 | PLAYERPOSLOOK: idx++,
78 | COLLECTITEM: idx++,
79 | ENTITYLOOKMOVE: idx++,
80 | SPAWNPARTICLE: idx++,
81 | SPAWNMOB: idx++,
82 | BLOCKCHANGE: idx++,
83 | SETSLOT: idx++,
84 | MULTIBLOCKCHANGE: idx++
85 | },
86 | CUSTOM: {
87 | BLESSINGCHANGE: idx++,
88 | WINDOWCLOSE: idx++,
89 | TICK: idx++,
90 | OPENEDCHEST: idx++,
91 | MULTIBLOCKCHANGE: idx++
92 | }
93 | },
94 | FORGE: {
95 | ENTITYJOIN: idx++
96 | }
97 | }
--------------------------------------------------------------------------------
/features/commands/InventoryLog.js:
--------------------------------------------------------------------------------
1 | import { ChestMenu } from "../../shared/ChestMenu"
2 | import { addCommand } from "../../shared/Command"
3 | import { createSkull } from "../../shared/InventoryButton"
4 | import { Persistence } from "../../shared/Persistence"
5 | import { TextHelper } from "../../shared/TextHelper"
6 |
7 | const logs = Persistence.getDataFromFile("InventoryLogs.json")
8 | const chest = new ChestMenu(`${TextHelper.PREFIX2} §bInventory§f`, 5)
9 |
10 | let data = []
11 |
12 | const createCustomItem = (nbt) => {
13 | // If it's not a skull we use default item creation
14 | if (nbt.id !== "minecraft:skull") {
15 | const item = new Item(net.minecraft.item.ItemStack.func_77949_a(NBT.parse(nbt).rawNBT))
16 | const lore = nbt.tag.display.Lore
17 | if (lore) item.setLore(lore)
18 |
19 | return item
20 | }
21 |
22 | // Otherwise we start doing funny skull stuff
23 | // it's done this way so it doesn't break the users' textures on other skull items
24 | const texture = nbt.tag.SkullOwner.Properties.textures[0].Value
25 | const item = createSkull(texture)
26 |
27 | const display = nbt.tag.display
28 | const lore = display.Lore
29 | const name = display.Name
30 |
31 | item.setName(name)
32 | item.setStackSize(nbt.Count)
33 | if (lore) item.setLore(lore)
34 |
35 | return item
36 | }
37 |
38 | addCommand("invlog", "Command which opens or logs your current inventory with a specified name", (type, name) => {
39 | type = type?.toLowerCase()
40 |
41 | switch (type) {
42 | case "list":
43 | ChatLib.chat(`${TextHelper.PREFIX} &aInventory names saved \n&6- &b${Object.keys(logs).join("\n&6- &b")}`)
44 | break
45 |
46 | case "add": {
47 | if (name in logs) return ChatLib.chat(`${TextHelper.PREFIX} &cInventory with name &b${name}&c already exists`)
48 | if (!name) return ChatLib.chat(`${TextHelper.PREFIX} &cInvalid inventory name passed`)
49 | logs[name] = {}
50 |
51 | const items = Player.getInventory().getItems()
52 | for (let idx = 0; idx < items.length; idx++) {
53 | let item = items[idx]
54 | if (!item) continue
55 |
56 | logs[name][idx] = item.getNBT().toObject()
57 | }
58 |
59 | ChatLib.chat(`${TextHelper.PREFIX} &aSuccessfully added &b${name} &ato inventory logs`)
60 | break
61 | }
62 |
63 | case "open": {
64 | if (!(name in logs)) return ChatLib.chat(`${TextHelper.PREFIX} &cInventory with name &b${name}&c does not exist`)
65 | const items = logs[name]
66 | Object.entries(items).forEach(([slot, nbt]) => {
67 | slot = slot < 9
68 | ? slot + 36
69 | : slot >= 36
70 | ? slot % 36
71 | : slot
72 | data[slot] = createCustomItem(nbt)
73 | })
74 |
75 | chest
76 | .setItems(data)
77 | .setTitle(`${TextHelper.PREFIX2} &b&lInventory&f &6${name}`)
78 | .open()
79 | break
80 | }
81 |
82 | case "remove": {
83 | if (!(name in logs)) return ChatLib.chat(`${TextHelper.PREFIX} &cInventory with name &b${name}&c does not exist`)
84 | delete logs[name]
85 | ChatLib.chat(`${TextHelper.PREFIX} &aSuccessfully removed &b${name} &afrom inventory logs`)
86 | break
87 | }
88 |
89 | default:
90 | ChatLib.chat(`${TextHelper.PREFIX} &cPlease add a valid mode &7modes: (add, list, remove, open)`)
91 | break
92 | }
93 | })
94 |
95 | register("gameUnload", () => {
96 | Persistence.saveDataToFile("InventoryLogs.json", logs, true, false)
97 | })
--------------------------------------------------------------------------------
/features/dungeons/AutoRequeueDungeons.js:
--------------------------------------------------------------------------------
1 | import config from "../../config"
2 | import { Event } from "../../core/Event"
3 | import EventEnums from "../../core/EventEnums"
4 | import Feature from "../../core/Feature"
5 | import Location from "../../shared/Location"
6 | import { TextHelper } from "../../shared/TextHelper"
7 |
8 | let shouldDownTime = null
9 | let commandReceived = false
10 |
11 | const feat = new Feature("autoRequeueDungeons")
12 | .addEvent(
13 | new Event(EventEnums.PACKET.SERVER.CHAT, (name, msg) => {
14 | if (msg.toLowerCase() === "r") {
15 | ChatLib.chat(`${TextHelper.PREFIX} &a${shouldDownTime} is ready`)
16 | shouldDownTime = null
17 | return
18 | }
19 | if (msg.toLowerCase() !== "dt") return
20 |
21 | shouldDownTime = name
22 | ChatLib.chat(`${TextHelper.PREFIX} &bUser &6${shouldDownTime} &bneeds downtime`)
23 | }, /^Party > (?:\[\d+\] .? ?)?(?:\[[^\]]+\] )?(\w{1,16}): !(\w{1,2})(?: [\w ]+)?$/)
24 | )
25 | .addEvent(
26 | new Event(EventEnums.PACKET.SERVER.CHAT, () => {
27 | if (!Location.inWorld("catacombs")) return
28 |
29 | if (shouldDownTime) {
30 | ChatLib.say(`${shouldDownTime} needs downtime`)
31 | return
32 | }
33 |
34 | commandReceived = true
35 | feat.update()
36 |
37 | if (config().autoRequeueDungeonsChestMode) return
38 |
39 | ChatLib.command("instancerequeue")
40 | }, /^ *> EXTRA STATS <$/)
41 | )
42 | .addSubEvent(
43 | new Event(EventEnums.PACKET.SERVER.CHAT, () => {
44 | ChatLib.command("instancerequeue")
45 | }, /^ [A-z]+ CHEST REWARDS$/),
46 | () => commandReceived && config().autoRequeueDungeonsChestMode && Location.inWorld("catacombs")
47 | )
48 | .onUnregister(() => {
49 | commandReceived = false
50 | if (shouldDownTime) {
51 | ChatLib.chat(`${TextHelper.PREFIX} &aDowntime has been resetted.`)
52 | shouldDownTime = null
53 | }
54 | })
--------------------------------------------------------------------------------
/features/dungeons/BlazeSolver.js:
--------------------------------------------------------------------------------
1 | import config from "../../config"
2 | import { Event } from "../../core/Event"
3 | import EventEnums from "../../core/EventEnums"
4 | import Feature from "../../core/Feature"
5 | import { onPuzzleRotationExit, onPuzzleScheduledRotation } from "../../shared/PuzzleRoomScanner"
6 | import { RenderHelper } from "../../shared/Render"
7 | import { TextHelper } from "../../shared/TextHelper"
8 |
9 | // Credits: https://github.com/UnclaimedBloom6/BloomModule/blob/main/features/BlazeSolver.js
10 |
11 | const blazeHealthRegex = /^\[Lv15\] Blaze [\d,]+\/([\d,]+)❤$/
12 | const Blocks = net.minecraft.init.Blocks
13 | const BlockBedrock = Blocks.field_150357_h
14 | const BlockBanner = Blocks.field_180393_cK
15 | const relativeCoords = {
16 | bannerTop: [-4, 59, -5],
17 | bedrockTop: [5, 68, -8],
18 | bannerBottom1: [-3, 69, 10],
19 | bannerBottom2: [3, 69, 10]
20 | }
21 |
22 | let inBlaze = false
23 | let isTop = false
24 | let blazes = []
25 | let enteredRoomAt = null
26 | let lastBlazes = null
27 |
28 | const reset = () => {
29 | inBlaze = false
30 | isTop = false
31 | blazes = []
32 | enteredRoomAt = null
33 | lastBlazes = null
34 | }
35 |
36 | const feat = new Feature("blazeSolver", "catacombs")
37 | .addSubEvent(
38 | new Event(EventEnums.STEP, () => {
39 | blazes = World.getAllEntitiesOfType(net.minecraft.entity.item.EntityArmorStand)
40 | .filter(it => blazeHealthRegex.test(it.getName().removeFormatting()))
41 | .map(it => [it, Math.floor(it.getName().removeFormatting().match(blazeHealthRegex)[1].replace(/,/g, ""))])
42 |
43 | if (blazes.length === 9) enteredRoomAt = Date.now()
44 | if (blazes.length === 0 && enteredRoomAt && lastBlazes === 1) {
45 | TextHelper.sendPuzzleMsg("Blaze", enteredRoomAt)
46 | if (config().blazeSolverDone) ChatLib.command("pc Blaze Done")
47 |
48 | reset()
49 | feat.update()
50 |
51 | return
52 | }
53 |
54 | blazes.sort((a, b) => a[1] - b[1])
55 |
56 | if (isTop) blazes.reverse()
57 |
58 | lastBlazes = blazes.length
59 |
60 | feat.update()
61 | }, 5),
62 | () => inBlaze
63 | )
64 | .addSubEvent(
65 | new Event("renderWorld", () => {
66 | let nextBlazes = []
67 |
68 | for (let idx = 0; idx < blazes.length; idx++) {
69 | let entity = blazes[idx][0]
70 | let [ x, y, z ] = [ entity.getX(), entity.getY() - 2, entity.getZ() ]
71 | let [ r, g, b ] = idx == 0 ? [0, 255, 0] : idx == 1 ? [250, 250, 51] : [255, 255, 255]
72 |
73 | if (idx <= 1) nextBlazes[idx] = [ x, y, z ]
74 |
75 | RenderHelper.drawEntityBoxFilled(x, y, z, 0.6, 1.8, r, g, b, 255)
76 | }
77 |
78 | if (!nextBlazes[1] || !config().blazeSolverLine) return
79 |
80 | // Draw line to next blaze
81 | RenderHelper.drawLineThroughPoints(nextBlazes, 0, 255, 0, 255, false, 2)
82 | }),
83 | () => inBlaze && blazes.length
84 | )
85 | .addSubEvent(
86 | new Event(EventEnums.RENDERENTITY, (_, __, ___, event) => {
87 | cancel(event)
88 | }, net.minecraft.entity.monster.EntityBlaze),
89 | () => inBlaze && config().blazeSolverHide
90 | )
91 | .onUnregister(() => {
92 | reset()
93 | })
94 |
95 | onPuzzleScheduledRotation((rotation) => {
96 | if (!config().blazeSolver && !config().blazeSolverLine) return
97 |
98 | // Top
99 | const bannerTop = World.getBlockAt(...TextHelper.getRealCoord(relativeCoords.bannerTop, rotation)).type.mcBlock
100 | const bedrockTop = World.getBlockAt(...TextHelper.getRealCoord(relativeCoords.bedrockTop, rotation)).type.mcBlock
101 | // Bottom
102 | const bannerBottom1 = World.getBlockAt(...TextHelper.getRealCoord(relativeCoords.bannerBottom1, rotation)).type.mcBlock
103 | const bannerBottom2 = World.getBlockAt(...TextHelper.getRealCoord(relativeCoords.bannerBottom2, rotation)).type.mcBlock
104 |
105 | inBlaze = (bannerTop === BlockBanner && bedrockTop === BlockBedrock) || (bannerBottom1 === BlockBanner && bannerBottom2 === BlockBanner)
106 | isTop = bannerTop === BlockBanner && bedrockTop === BlockBedrock
107 |
108 | if (!inBlaze) return
109 |
110 | ChatLib.chat(`${TextHelper.PREFIX} &aBlaze room detected`)
111 |
112 | feat.update()
113 | })
114 |
115 | onPuzzleRotationExit(() => {
116 | if (!inBlaze) return
117 |
118 | reset()
119 | feat.update()
120 | })
--------------------------------------------------------------------------------
/features/dungeons/BossSplits.js:
--------------------------------------------------------------------------------
1 | import AtomxApi from "../../../Atomx/AtomxApi"
2 | import { scheduleTask } from "../../core/CustomRegisters"
3 | import { Event } from "../../core/Event"
4 | import EventEnums from "../../core/EventEnums"
5 | import Feature from "../../core/Feature"
6 | import CustomSplits from "../../shared/CustomSplits"
7 | import DraggableGui from "../../shared/DraggableGui"
8 | import Location from "../../shared/Location"
9 | import { Persistence } from "../../shared/Persistence"
10 |
11 | const editGui = new DraggableGui("bossSplits").setCommandName("editbosssplits")
12 | const dungeonFloorRegex = AtomxApi.getRegexData().Dungeons.Floor
13 | const bossSplits = Persistence.getDataFromFileOrLink("BossSplits.json", "https://raw.githubusercontent.com/DocilElm/Doc-Data/refs/heads/main/dungeons/BossSplits.json")
14 | const defaultString = [
15 | `&dTerracotta&f: &a10s`,
16 | `&bGiants&f: &a10s`,
17 | `&aSadan&f: &a10s`,
18 | ].join("\n")
19 |
20 | let previousFloor = null
21 | let split = null
22 |
23 | const onFloor = (floor, feat) => {
24 | if (floor === previousFloor) return
25 |
26 | const floorNum = parseInt(floor.replace(/F|M/, ""))
27 | const floorName = floorNum < 7 ? `F${floorNum}` : floor
28 |
29 | previousFloor = floor
30 | split = new CustomSplits(bossSplits[floorName], () => true)
31 | split.onTimeUpdate = () => scheduleTask(() => feat.update())
32 | split.getEvents().forEach(it => feat.addSubEvent(it, () => Location.inWorld("catacombs")))
33 | feat.update()
34 | }
35 |
36 | editGui.onDraw(() => {
37 | Renderer.translate(editGui.getX(), editGui.getY())
38 | Renderer.scale(editGui.getScale())
39 | Renderer.drawStringWithShadow(defaultString, 0, 0)
40 | Renderer.finishDraw()
41 | })
42 |
43 | const feat = new Feature("dungeonBossSplits", "catacombs")
44 | .addEvent(
45 | new Event(EventEnums.PACKET.SERVER.SCOREBOARD, (floor) => {
46 | onFloor(floor, feat)
47 | }, dungeonFloorRegex)
48 | )
49 | .addSubEvent(
50 | new Event("renderOverlay", () => {
51 | Renderer.retainTransforms(true)
52 | Renderer.translate(editGui.getX(), editGui.getY())
53 | Renderer.scale(editGui.getScale())
54 | Renderer.drawStringWithShadow("&bBoss Splits", 0, 0)
55 |
56 | Renderer.drawStringWithShadow(split.buildStr(), 0, 10)
57 |
58 | Renderer.retainTransforms(false)
59 | Renderer.finishDraw()
60 | }),
61 | () => split !== null && split?.timers?.[0]?.timer !== null
62 | )
63 | .onUnregister(() => {
64 | // References BE GONE!
65 | if (split) {
66 | const events = split.events
67 | for (let idx = events.length - 1; idx >= 0; idx--) {
68 | events[idx].unregister()
69 | events.splice(idx, 1)
70 | }
71 |
72 | const subevents = feat.subevents
73 | for (let idx = subevents.length - 1; idx >= 0; idx--) {
74 | // "renderOverlay" event _should_ always be the first sub event so we ignore it
75 | if (idx === 0) continue
76 |
77 | subevents.splice(idx, 1)
78 | }
79 | split.onTimeUpdate = null
80 | }
81 | split = null
82 | previousFloor = null
83 | })
--------------------------------------------------------------------------------
/features/dungeons/BoulderSolver.js:
--------------------------------------------------------------------------------
1 | import config from "../../config"
2 | import { Event } from "../../core/Event"
3 | import EventEnums from "../../core/EventEnums"
4 | import Feature from "../../core/Feature"
5 | import { Persistence } from "../../shared/Persistence"
6 | import { onPuzzleRotationExit, onPuzzleScheduledRotation } from "../../shared/PuzzleRoomScanner"
7 | import { RenderHelper } from "../../shared/Render"
8 | import { TextHelper } from "../../shared/TextHelper"
9 |
10 | const solutions = Persistence.getDataFromFileOrLink("BoulderDataNew.json", "https://raw.githubusercontent.com/DocilElm/Doc-Data/main/dungeons/BoulderData.json")?.solutions
11 | const relativeCoords = {
12 | ironbar: [ 0, 70, -12 ],
13 | chest: [ 0, 66, -14 ],
14 | firstbox: [ -9, 66, -9 ]
15 | }
16 |
17 | /** @type {BoulderBox[]} */
18 | let renderBlocks = []
19 | let enteredRoomAt = null
20 |
21 | /**
22 | * - Gets the current variant of boulder
23 | * @param {number} rotation
24 | * @returns {string}
25 | * @link Credits to [Bloom](https://github.com/UnclaimedBloom6)
26 | */
27 | const getBoulderGrid = (rotation) => {
28 | const [ rx, ry, rz ] = relativeCoords.firstbox
29 | let str = ""
30 |
31 | for (let z = 0; z <= 15; z += 3) {
32 | for (let x = 0; x <= 18; x += 3) {
33 | let block = World.getBlockAt(...TextHelper.getRealCoord([rx + x, ry, rz + z], rotation))
34 |
35 | if (block.type.getID() === 0) str += "0"
36 | else str += "1"
37 | }
38 | }
39 |
40 | return str
41 | }
42 |
43 | class BoulderBox {
44 | constructor(render, click, rotation) {
45 | this.render = TextHelper.getRealCoord(render, rotation)
46 | this.click = TextHelper.getRealCoord(click, rotation)
47 | }
48 |
49 | onClick(click) {
50 | return click[0] === this.click[0] && click[1] === this.click[1] && click[2] === this.click[2]
51 | }
52 |
53 | toString() {
54 | return `BoulderBox=[render=${this.render}, click=${this.click}]`
55 | }
56 | }
57 |
58 | const feat = new Feature("boulderSolver", "catacombs")
59 | .addSubEvent(
60 | new Event("renderWorld", () => {
61 | for (let idx = 0; idx < renderBlocks.length; idx++) {
62 | let data = renderBlocks[idx]
63 | let block = World.getBlockAt(...data.render)
64 |
65 | RenderHelper.outlineBlock(block, 0, 243, 200, 255)
66 | RenderHelper.filledBlock(block, 0, 243, 200, 50)
67 | }
68 | }),
69 | () => enteredRoomAt && renderBlocks.length
70 | )
71 | .addSubEvent(
72 | new Event(EventEnums.PACKET.CLIENT.BLOCKPLACEMENT, (/**@type {Block}*/block, pos) => {
73 | for (let idx = renderBlocks.length - 1; idx >= 0; idx--) {
74 | let data = renderBlocks[idx]
75 |
76 | if (!data.onClick(pos)) continue
77 |
78 | renderBlocks.splice(idx, 1)
79 | }
80 |
81 | feat.update()
82 | }),
83 | () => enteredRoomAt && renderBlocks.length
84 | )
85 | .addSubEvent(
86 | new Event(EventEnums.PACKET.CUSTOM.OPENEDCHEST, () => {
87 | TextHelper.sendPuzzleMsg("Boulder", enteredRoomAt)
88 | renderBlocks = []
89 | enteredRoomAt = null
90 | }),
91 | () => enteredRoomAt
92 | )
93 | .onUnregister(() => {
94 | renderBlocks = []
95 | enteredRoomAt = null
96 | })
97 |
98 | onPuzzleScheduledRotation((rotation) => {
99 | if (!config().boulderSolver) return
100 |
101 | const block = World.getBlockAt(...TextHelper.getRealCoord(relativeCoords.ironbar, rotation))
102 |
103 | if (block.type.mcBlock !== net.minecraft.init.Blocks.field_150411_aY) return
104 |
105 | const theGrid = getBoulderGrid(rotation)
106 | const currentSolution = solutions[theGrid]
107 | if (!currentSolution) return ChatLib.chat(`${TextHelper.PREFIX} &cBoulder room variant not found in the data`)
108 |
109 | ChatLib.chat(`${TextHelper.PREFIX} &aBoulder room detected`)
110 | enteredRoomAt = Date.now()
111 |
112 | for (let idx = 0; idx < currentSolution.render.length; idx++) {
113 | let render = currentSolution.render[idx]
114 | let click = currentSolution.click[idx]
115 |
116 | renderBlocks.push(new BoulderBox(render, click, rotation))
117 | }
118 |
119 | feat.update()
120 | })
121 |
122 | onPuzzleRotationExit(() => {
123 | renderBlocks = []
124 | enteredRoomAt = null
125 | feat.update()
126 | })
--------------------------------------------------------------------------------
/features/dungeons/BoxStarMobs.js:
--------------------------------------------------------------------------------
1 | import { scheduleTask } from "../../core/CustomRegisters"
2 | import { Event } from "../../core/Event"
3 | import EventEnums from "../../core/EventEnums"
4 | import Feature from "../../core/Feature"
5 | import { RenderHelper } from "../../shared/Render"
6 |
7 | const mobs = new HashMap()
8 |
9 | let useSeverTicks = false
10 |
11 | const scanEntityName = (mcEntity, entityId, feat) => {
12 | const name = mcEntity./* getName */func_70005_c_()
13 | if (!name.includes("✯ ")) return
14 |
15 | const entityBelowId = entityId - (name.includes("Withermancer") ? 3 : 1)
16 | const entityBelow = World.getWorld()./* getEntityByID */func_73045_a(entityBelowId)
17 | if (!entityBelow) return
18 | if (entityBelow instanceof net.minecraft.entity.monster.EntityEnderman) {
19 | mobs.put(entityBelowId, [
20 | /* width */0.6,
21 | /* height */0.7,
22 | /* red */255,
23 | /* green */51,
24 | /* blue */255,
25 | /* alpha */255
26 | ])
27 | feat.update()
28 | return
29 | }
30 |
31 | mobs.put(entityBelowId, [
32 | /* width */entityBelow./* width */field_70130_N,
33 | /* height */entityBelow./* height */field_70131_O + 0.2, // "magic number" - an attempt to try to make the hitbox go over the armor the entity is wearing
34 | /* red */0,
35 | /* green */255,
36 | /* blue */255,
37 | /* alpha */255
38 | ])
39 | feat.update()
40 | }
41 |
42 | const feat = new Feature("boxStarMobs", "catacombs")
43 | .addEvent(
44 | new Event(EventEnums.PACKET.SERVER.SCOREBOARD, () => {
45 | useSeverTicks = true
46 | }, /^Time Elapsed\: 03s$/)
47 | )
48 | .addEvent(
49 | new Event(EventEnums.FORGE.ENTITYJOIN, (mcEntity, entityId) => {
50 | // Scan with client ticks if the server packets aren't able to arrive yet
51 | if (!useSeverTicks) {
52 | Client.scheduleTask(3, () => scanEntityName(mcEntity, entityId, feat))
53 | return
54 | }
55 |
56 | scheduleTask(() => scanEntityName(mcEntity, entityId, feat))
57 | })
58 | )
59 | .addSubEvent(
60 | new Event("renderEntity", (entity, _, pticks) => {
61 | const entityId = entity.entity./* getEntityId */func_145782_y()
62 | const data = mobs.get(entityId)
63 | if (!data) return
64 | if (entity.isDead()) return mobs.remove(entityId)
65 |
66 | const [ width, height, r, g, b, a ] = data
67 |
68 | RenderHelper.drawEntityBox(
69 | entity.getX(),
70 | entity.getY(),
71 | entity.getZ(),
72 | width,
73 | height,
74 | r, g, b, a, 2, false, true, pticks
75 | )
76 | }),
77 | () => mobs.size()
78 | )
79 | .onUnregister(() => {
80 | mobs.clear()
81 | useSeverTicks = false
82 | })
--------------------------------------------------------------------------------
/features/dungeons/ChestProfit.js:
--------------------------------------------------------------------------------
1 | import Price from "../../../Atomx/skyblock/Price"
2 | import { Event } from "../../core/Event"
3 | import EventEnums from "../../core/EventEnums"
4 | import Feature from "../../core/Feature"
5 | import DraggableGui from "../../shared/DraggableGui"
6 | import { TextHelper } from "../../shared/TextHelper"
7 |
8 | // Credits: https://github.com/UnclaimedBloom6/BloomModule/blob/main/features/dungeonChestProfit/DungeonChestProfit.js
9 |
10 | const editGui = new DraggableGui("dungeonProfit").setCommandName("editdungeonProfit")
11 | const essenceRegex = /^(Undead|Wither) Essence x(\d+)$/
12 | const chestNames = ["Wood Chest", "Gold Chest", "Diamond Chest", "Emerald Chest", "Obsidian Chest", "Bedrock Chest"]
13 | const formattedChest = {
14 | "Wood Chest": "&fWood Chest",
15 | "Gold Chest": "&6Gold Chest",
16 | "Diamond Chest": "&bDiamond Chest",
17 | "Emerald Chest": "&2Emerald Chest",
18 | "Obsidian Chest": "&5Obsidian Chest",
19 | "Bedrock Chest": "&8Bedrock Chest",
20 | }
21 |
22 | let inChest = false
23 | let currentChest = null
24 | let chestData = []
25 |
26 | const getValue = (item) => {
27 | if (!item) return 0
28 | const itemName = item.getName().removeFormatting()
29 |
30 | if (essenceRegex.test(itemName)) {
31 | let [ _, type, amount ] = itemName.match(essenceRegex)
32 |
33 | return Math.floor(Price.getSellPrice(`ESSENCE_${type}`.toUpperCase()) * Math.floor(amount))
34 | }
35 |
36 | return Math.floor(Price.getSellPrice(TextHelper.getSkyblockItemID(item))) || 0
37 | }
38 |
39 | editGui.onDraw(() => {
40 | Renderer.retainTransforms(true)
41 | Renderer.translate(editGui.getX(), editGui.getY())
42 | Renderer.scale(editGui.getScale())
43 | Renderer.drawStringWithShadow(`\n&aFree Chest\n&aExample Item &8x25\n&aTotal Profit&f: &a1,000`, 0, 0)
44 | Renderer.retainTransforms(false)
45 | Renderer.finishDraw()
46 | })
47 |
48 | const feat = new Feature("dungeonProfitDisplay", "catacombs")
49 | .addEvent(
50 | new Event(EventEnums.PACKET.SERVER.WINDOWOPEN, (title) => {
51 | inChest = chestNames.includes(title)
52 | if (inChest) currentChest = title
53 | feat.update()
54 | })
55 | )
56 | .addSubEvent(
57 | new Event(EventEnums.PACKET.SERVER.WINDOWITEMS, (items) => {
58 | const fidx = chestData.findIndex(it => it.name.removeFormatting() === currentChest)
59 | if (fidx !== -1) chestData.splice(fidx, 1)
60 |
61 | if (!items[31]) return
62 | const chestItem = new Item(items[31])
63 | const chestLore = chestItem.getLore()
64 |
65 | let chestPrice = Math.floor(chestLore[7]?.removeFormatting()?.replace(/([,]+| Coins)/g, "")) || 0
66 | chestPrice += (Price.getSellPrice(chestLore[8]?.removeFormatting()?.replace(/ /g, "_")?.toUpperCase()) || 0)
67 |
68 | let data = {
69 | name: formattedChest[currentChest],
70 | items: [],
71 | profit: 0,
72 | display: ""
73 | }
74 |
75 | for (let idx = 9; idx < 18; idx++) {
76 | if (!items[idx]) continue
77 | let item = new Item(items[idx])
78 | if (item.getID() === 160) continue
79 |
80 | let itemName = item.getName().removeFormatting() === "Enchanted Book" ? item.getLore()[1] : item.getName()
81 |
82 | data.items.push(`\n${itemName}`)
83 | data.profit += getValue(item)
84 | }
85 |
86 | data.profit = data.profit - chestPrice
87 | let profitColor = data.profit < 0 ? "&c" : "&a"
88 |
89 | data.display = `${data.name}${data.items.join("")}\n&bTotal Profit&f: ${profitColor}${TextHelper.addCommasTrunc(data.profit)}\n\n`
90 |
91 | chestData.push(data)
92 |
93 | inChest = false
94 | feat.update()
95 | }),
96 | () => inChest && currentChest
97 | )
98 | .addSubEvent(
99 | new Event(EventEnums.PACKET.CUSTOM.WINDOWCLOSE, () => {
100 | const fidx = chestData.findIndex(it => it.name.removeFormatting() === currentChest)
101 | if (fidx === -1) return
102 |
103 | chestData.splice(fidx, 1)
104 | }),
105 | () => inChest
106 | )
107 | .addSubEvent(
108 | new Event("renderOverlay", () => {
109 | if (editGui.isOpen()) return
110 |
111 | Renderer.retainTransforms(true)
112 | Renderer.translate(editGui.getX(), editGui.getY())
113 | Renderer.scale(editGui.getScale())
114 |
115 | let str = ""
116 |
117 | for (let idx = 0; idx < chestData.length; idx++) {
118 | str += chestData[idx].display
119 | }
120 |
121 | Renderer.drawStringWithShadow(str, 0, 0)
122 |
123 | Renderer.retainTransforms(false)
124 | Renderer.finishDraw()
125 | }),
126 | () => chestData.length
127 | )
128 | .onUnregister(() => {
129 | chestData = []
130 | inChest = false
131 | currentChest = null
132 | })
--------------------------------------------------------------------------------
/features/dungeons/CreeperBeamsSolver.js:
--------------------------------------------------------------------------------
1 | import config from "../../config"
2 | import { Event } from "../../core/Event"
3 | import EventEnums from "../../core/EventEnums"
4 | import Feature from "../../core/Feature"
5 | import { Persistence } from "../../shared/Persistence"
6 | import { onPuzzleRotationExit, onPuzzleScheduledRotation } from "../../shared/PuzzleRoomScanner"
7 | import { RenderHelper } from "../../shared/Render"
8 | import { TextHelper } from "../../shared/TextHelper"
9 |
10 | const lanterPairs = Persistence.getDataFromFileOrLink("CreeperBeamsSolutions.json", "https://raw.githubusercontent.com/DocilElm/Doc-Data/main/dungeons/CreeperBeamsSolutions.json")
11 | const relativeCoords = {
12 | lantern: [0, 74, 0],
13 | stone: [1, 73, 0]
14 | }
15 | const pairColors = [
16 | [0, 200, 255],
17 | [0, 255, 0],
18 | [255, 102, 102],
19 | [255, 255, 51],
20 | [255, 153, 51]
21 | ]
22 |
23 | let solutions = []
24 | let enteredRoomAt = null
25 |
26 | const equals = (pos, pos2) => Math.floor(pos.x) === Math.floor(pos2.x) && Math.floor(pos.y) === Math.floor(pos2.y) && Math.floor(pos.z) === Math.floor(pos2.z)
27 |
28 | const checkBlocks = (feat, pos, packetBlock) => {
29 | for (let idx = solutions.length - 1; idx >= 0; idx--) {
30 | let data = solutions[idx]
31 | let blocks = data.blocks
32 |
33 | if (
34 | (equals(pos, blocks[0].pos) || equals(pos, blocks[1].pos)) &&
35 | packetBlock === net.minecraft.init.Blocks.field_180397_cI
36 | ) {
37 | data.blacklisted = true
38 | continue
39 | }
40 | if (
41 | (equals(pos, blocks[0].pos) || equals(pos, blocks[1].pos)) &&
42 | packetBlock !== net.minecraft.init.Blocks.field_180397_cI && data.blacklisted
43 | )
44 | data.blacklisted = false
45 | }
46 |
47 | if (solutions.filter((it) => !it.blacklisted).length === 0) {
48 | TextHelper.sendPuzzleMsg("Creeper Beams", enteredRoomAt)
49 | solutions = []
50 | enteredRoomAt = null
51 | }
52 |
53 | feat.update()
54 | }
55 |
56 | const feat = new Feature("creeperBeamsSolver", "catacombs")
57 | .addSubEvent(
58 | new Event("renderWorld", () => {
59 | for (let idx = 0; idx < 4; idx++) {
60 | let data = solutions[idx]
61 | if (!data || data.blacklisted) continue
62 |
63 | let [ block, block1 ] = data.blocks
64 | let [ r, g, b ] = data.color
65 |
66 | RenderHelper.outlineFilledBlock(block, r, g, b, 255)
67 | RenderHelper.outlineFilledBlock(block1, r, g, b, 255)
68 |
69 | if (!config().creeperBeamsSolverLine) continue
70 |
71 | RenderHelper.drawLineThroughPoints(data.coords, r, g, b, 255, false, 2)
72 | }
73 | }),
74 | () => solutions.length && enteredRoomAt
75 | )
76 | .addSubEvent(
77 | new Event(EventEnums.PACKET.SERVER.BLOCKCHANGE, (_, pos, packetBlock) => {
78 | checkBlocks(feat, pos, packetBlock)
79 | }),
80 | () => solutions.length && enteredRoomAt
81 | )
82 | .addSubEvent(
83 | new Event(EventEnums.PACKET.CUSTOM.MULTIBLOCKCHANGE, (_, pos, packetBlock) => {
84 | checkBlocks(feat, pos, packetBlock)
85 | }),
86 | () => solutions.length && enteredRoomAt
87 | )
88 | .onUnregister(() => {
89 | solutions = []
90 | enteredRoomAt = null
91 | })
92 |
93 | onPuzzleScheduledRotation((rotation) => {
94 | if (!config().creeperBeamsSolver) return
95 |
96 | const stoneBlock = World.getBlockAt(...TextHelper.getRealCoord(relativeCoords.stone, rotation))
97 | const lanternBlock = World.getBlockAt(...TextHelper.getRealCoord(relativeCoords.lantern, rotation))
98 | if (stoneBlock.type.mcBlock !== net.minecraft.init.Blocks.field_150348_b || lanternBlock.type.mcBlock !== net.minecraft.init.Blocks.field_180398_cJ) return
99 |
100 | ChatLib.chat(`${TextHelper.PREFIX} &aCreeper Beams detected`)
101 |
102 | let idx = 0
103 |
104 | for (let k in lanterPairs) {
105 | if (idx >= 4) break
106 |
107 | let v = lanterPairs[k]
108 |
109 | let block = World.getBlockAt(...TextHelper.getRealCoord(v[0], rotation))
110 | let block1 = World.getBlockAt(...TextHelper.getRealCoord(v[1], rotation))
111 | if (block.type.mcBlock !== net.minecraft.init.Blocks.field_180398_cJ || block1.type.mcBlock !== net.minecraft.init.Blocks.field_180398_cJ) continue
112 |
113 | solutions.push({
114 | blocks: [block, block1],
115 | coords: [
116 | [ Math.floor(block.getX()) + 0.5, Math.floor(block.getY()) + 0.5, Math.floor(block.getZ()) + 0.5 ],
117 | [ Math.floor(block1.getX()) + 0.5, Math.floor(block1.getY()) + 0.5, Math.floor(block1.getZ()) + 0.5 ]
118 | ],
119 | color: pairColors[idx],
120 | blacklisted: false
121 | })
122 |
123 | idx++
124 | }
125 |
126 | enteredRoomAt = Date.now()
127 |
128 | feat.update()
129 | })
130 |
131 | onPuzzleRotationExit(() => {
132 | solutions = []
133 | enteredRoomAt = null
134 | feat.update()
135 | })
--------------------------------------------------------------------------------
/features/dungeons/CroesusClicks.js:
--------------------------------------------------------------------------------
1 | import { Event } from "../../core/Event"
2 | import EventEnums from "../../core/EventEnums"
3 | import Feature from "../../core/Feature"
4 | import { RenderHelper } from "../../shared/Render"
5 |
6 | // Credits: https://github.com/UnclaimedBloom6/BloomModule/blob/main/Bloom/features/CakeNumbers.js
7 |
8 | const slotsClicked = new Map()
9 |
10 | let inCroesus = false
11 | let cachedItems = null
12 | let currentPage = null
13 |
14 | const feat = new Feature("showCroesusClicks", "dungeon hub")
15 | .addEvent(
16 | new Event(EventEnums.PACKET.SERVER.WINDOWOPEN, (name) => {
17 | inCroesus = name === "Croesus"
18 | cachedItems = null
19 | currentPage = null
20 | feat.update()
21 | })
22 | )
23 | .addEvent(
24 | new Event(EventEnums.PACKET.CUSTOM.WINDOWCLOSE, () => {
25 | inCroesus = false
26 | currentPage = null
27 | feat.update()
28 | })
29 | )
30 | .addSubEvent(
31 | new Event(EventEnums.PACKET.SERVER.WINDOWITEMS, (mcItems) => {
32 | cachedItems = mcItems.map(it => it && new Item(it))
33 | const page = cachedItems?.[53]?.getID() === 160 ? "Page1" : cachedItems?.[53]?.getLore()?.[1]?.removeFormatting()?.replace(/ /g, "")
34 |
35 | if (!slotsClicked.has(page)) slotsClicked.set(page, new Map())
36 |
37 | currentPage = slotsClicked.get(page)
38 | feat.update()
39 | }),
40 | () => inCroesus
41 | )
42 | .addSubEvent(
43 | new Event(EventEnums.PACKET.CLIENT.WINDOWCLICK, (_, slot) => {
44 | if (slot <= 0 || slot >= 44 || currentPage.has(slot)) return
45 | if (!cachedItems[slot] || cachedItems[slot]?.getID() === 160 || cachedItems[slot]?.getID() === 262) return
46 |
47 | currentPage.set(slot, RenderHelper.getSlotRenderPosition(slot))
48 | }),
49 | () => inCroesus && currentPage
50 | )
51 | .addSubEvent(
52 | new Event("renderOverlay", () => {
53 | if (!currentPage) return
54 |
55 | for (let v of currentPage.values()) {
56 | let [ x, y ] = v
57 |
58 | Renderer.retainTransforms(true)
59 | Renderer.translate(x + .5, y, 100)
60 | Renderer.scale(0.9)
61 | Renderer.drawRect(Renderer.color(0, 255, 0, 150), 0, 0, 16, 16)
62 | Renderer.retainTransforms(false)
63 | }
64 | }),
65 | () => inCroesus && currentPage
66 | )
67 | .onUnregister(() => {
68 | slotsClicked.clear()
69 | inCroesus = false
70 | cachedItems = null
71 | currentPage = null
72 | })
--------------------------------------------------------------------------------
/features/dungeons/CryptsDisplay.js:
--------------------------------------------------------------------------------
1 | import Dungeons from "../../../Atomx/skyblock/Dungeons"
2 | import { Event } from "../../core/Event"
3 | import Feature from "../../core/Feature"
4 | import DraggableGui from "../../shared/DraggableGui"
5 |
6 | const editGui = new DraggableGui("cryptsDisplay").setCommandName("editcryptsDisplay")
7 |
8 | editGui.onDraw(() => {
9 | Renderer.translate(editGui.getX(), editGui.getY())
10 | Renderer.scale(editGui.getScale())
11 | Renderer.drawStringWithShadow("&aCrypts&f: &65", 0, 0)
12 | Renderer.finishDraw()
13 | })
14 |
15 | new Feature("cryptsDisplay", "catacombs")
16 | .addEvent(
17 | new Event("renderOverlay", () => {
18 | if (editGui.isOpen()) return
19 |
20 | const crypts = Dungeons.getCryptsAmount()
21 |
22 | Renderer.translate(editGui.getX(), editGui.getY())
23 | Renderer.scale(editGui.getScale())
24 | Renderer.drawStringWithShadow(`&aCrypts&f: ${crypts >= 5 ? "&6" : "&c"}${crypts}`, 0, 0)
25 | Renderer.finishDraw()
26 | })
27 | )
--------------------------------------------------------------------------------
/features/dungeons/DeathsDisplay.js:
--------------------------------------------------------------------------------
1 | import Dungeons from "../../../Atomx/skyblock/Dungeons"
2 | import { Event } from "../../core/Event"
3 | import Feature from "../../core/Feature"
4 | import DraggableGui from "../../shared/DraggableGui"
5 |
6 | const editGui = new DraggableGui("deathsDisplay").setCommandName("editdeathsDisplay")
7 |
8 | editGui.onDraw(() => {
9 | Renderer.translate(editGui.getX(), editGui.getY())
10 | Renderer.scale(editGui.getScale())
11 | Renderer.drawStringWithShadow("&8&lDeaths&f: &43", 0, 0)
12 | Renderer.finishDraw()
13 | })
14 |
15 | new Feature("deathsDisplay", "catacombs")
16 | .addEvent(
17 | new Event("renderOverlay", () => {
18 | if (editGui.isOpen()) return
19 |
20 | const amount = Dungeons.getTeamDeaths()
21 |
22 | Renderer.translate(editGui.getX(), editGui.getY())
23 | Renderer.scale(editGui.getScale())
24 | Renderer.drawStringWithShadow(`&8&lDeaths&f: ${amount >= 3 ? "&4" : "&c"}${amount}`, 0, 0)
25 | Renderer.finishDraw()
26 | })
27 | )
--------------------------------------------------------------------------------
/features/dungeons/ExtraStats.js:
--------------------------------------------------------------------------------
1 | import { Event } from "../../core/Event"
2 | import EventEnums from "../../core/EventEnums"
3 | import Feature from "../../core/Feature"
4 |
5 | new Feature("showExtraStats", "catacombs")
6 | .addEvent(
7 | new Event(EventEnums.PACKET.SERVER.CHAT, () => ChatLib.command("showextrastats"), /^ *> EXTRA STATS <$/)
8 | )
--------------------------------------------------------------------------------
/features/dungeons/HideNoStarTag.js:
--------------------------------------------------------------------------------
1 | import { scheduleTask } from "../../core/CustomRegisters"
2 | import { Event } from "../../core/Event"
3 | import EventEnums from "../../core/EventEnums"
4 | import Feature from "../../core/Feature"
5 |
6 | const blazeHealthRegex = /^\[Lv15\] Blaze [\d,]+\/([\d,]+)❤$/
7 | const noStarTagRegex = /^(?:\[Lv\d+\] )?[\w ]+ [\d,.]+\w(?:\/[\d,.]+\w)?❤$/
8 |
9 | new Feature("hideNoneStarredTags", "catacombs")
10 | .addEvent(
11 | new Event(EventEnums.FORGE.ENTITYJOIN, (entity) => {
12 | scheduleTask(() => {
13 | const name = entity.func_95999_t()?.removeFormatting()
14 | if (!name || blazeHealthRegex.test(name) || !noStarTagRegex.test(name)) return
15 |
16 | entity.func_70106_y()
17 | })
18 | }, net.minecraft.entity.item.EntityArmorStand)
19 | )
--------------------------------------------------------------------------------
/features/dungeons/LividSolver.js:
--------------------------------------------------------------------------------
1 | import { scheduleTask } from "../../core/CustomRegisters"
2 | import { Event } from "../../core/Event"
3 | import EventEnums from "../../core/EventEnums"
4 | import Feature from "../../core/Feature"
5 | import { RenderHelper } from "../../shared/Render"
6 | import { TextHelper } from "../../shared/TextHelper"
7 |
8 | // Credits: https://github.com/UnclaimedBloom6/BloomModule/blob/main/features/LividSolver.js
9 |
10 | const livids = [
11 | [0, "Vendetta", "§f"],
12 | [2, "Crossed", "§d"],
13 | [4, "Arcade", "§e"],
14 | [5, "Smile", "§a"],
15 | [7, "Doctor", "§7"],
16 | [10, "Purple", "§5"],
17 | [11, "Scream", "§9"],
18 | [13, "Frog", "§2"],
19 | [14, "Hockey", "§c"]
20 | ]
21 |
22 | let livid = null
23 | let lividData = null
24 | let inf5 = false
25 |
26 | const getIdFromBlock = (blockIn) => {
27 | return net.minecraft.block.Block./* getIdFromBlock */func_149682_b(blockIn)
28 | }
29 |
30 | const feat = new Feature("lividSolver", "catacombs")
31 | .addEvent(
32 | new Event(EventEnums.PACKET.SERVER.CHAT, () => {
33 | inf5 = true
34 | feat.update()
35 | }, /^\[BOSS\] Livid\: Welcome\, you\'ve arrived right on time\. I am Livid, the Master of Shadows\.$/)
36 | )
37 | .addEvent(
38 | new Event(EventEnums.PACKET.SERVER.CHAT, () => {
39 | // If livid data is not set whenever the livids spawn
40 | // we default to red color livid
41 | scheduleTask(() => {
42 | if (livid) return
43 |
44 | lividData = livids[8]
45 | feat.update()
46 | }, 2)
47 | }, /^\[BOSS\] Livid\: I respect you for making it to here\, but I\'ll be your undoing\.$/)
48 | )
49 | .addSubEvent(
50 | new Event(EventEnums.PACKET.SERVER.MULTIBLOCKCHANGE, (mcBlocks) => {
51 | mcBlocks.forEach((it) => {
52 | const pos = it./* getPos */func_180090_a()
53 | const [ x, y, z ] = [
54 | pos./* getX */func_177958_n(),
55 | pos./* getY */func_177956_o(),
56 | pos./* getZ */func_177952_p()
57 | ]
58 |
59 | if (x !== 5 || y !== 108 || z !== 43) return
60 |
61 | const blockState = it./* getBlockState */func_180088_c()
62 | const blockIn = blockState./* getBlock */func_177230_c()
63 | const blockId = getIdFromBlock(blockIn)
64 | if (blockId !== 35) return livid = null
65 |
66 | const metadata = blockIn./* getMetaFromState */func_176201_c(blockState)
67 | lividData = livids.find(a => a[0] == metadata)
68 | ChatLib.chat(`${TextHelper.PREFIX} &bLivid Solver&f: ${lividData[2]}${lividData[1]} Livid`)
69 | })
70 | feat.update()
71 | }),
72 | () => inf5
73 | )
74 | .addSubEvent(
75 | new Event(EventEnums.FORGE.ENTITYJOIN, (mcEntity) => {
76 | scheduleTask(() => {
77 | const name = mcEntity./* getName */func_70005_c_()
78 | if (name !== `${lividData?.[1]} Livid`) return
79 |
80 | livid = mcEntity
81 | feat.update()
82 | })
83 | }, net.minecraft.client.entity.EntityOtherPlayerMP),
84 | () => lividData
85 | )
86 | .addSubEvent(
87 | new Event("renderWorld", () => {
88 | RenderHelper.drawEntityBox(
89 | livid./* posX */field_70165_t,
90 | livid./* posY */field_70163_u,
91 | livid./* posZ */field_70161_v,
92 | 0.6,
93 | 1.8,
94 | 0, 255, 255, 255, 2, false
95 | )
96 | }),
97 | () => livid
98 | )
99 | .onUnregister(() => {
100 | livid = null
101 | lividData = null
102 | inf5 = false
103 | })
--------------------------------------------------------------------------------
/features/dungeons/MilestoneDisplay.js:
--------------------------------------------------------------------------------
1 | import Dungeons from "../../../Atomx/skyblock/Dungeons"
2 | import { Event } from "../../core/Event"
3 | import Feature from "../../core/Feature"
4 | import DraggableGui from "../../shared/DraggableGui"
5 |
6 | const editGui = new DraggableGui("milestoneDisplay").setCommandName("editmilestoneDisplay")
7 |
8 | editGui.onDraw(() => {
9 | Renderer.translate(editGui.getX(), editGui.getY())
10 | Renderer.scale(editGui.getScale())
11 | Renderer.drawStringWithShadow("&bMilestone&f: &69", 0, 0)
12 | Renderer.finishDraw()
13 | })
14 |
15 | new Feature("milestoneDisplay", "catacombs")
16 | .addEvent(
17 | new Event("renderOverlay", () => {
18 | if (editGui.isOpen()) return
19 |
20 | const amount = Dungeons.getCurrentMilestoneNum()
21 |
22 | Renderer.translate(editGui.getX(), editGui.getY())
23 | Renderer.scale(editGui.getScale())
24 | Renderer.drawStringWithShadow(`&bMilestone&f: ${amount >= 3 ? "&6" : "&c"}${amount}`, 0, 0)
25 | Renderer.finishDraw()
26 | })
27 | )
--------------------------------------------------------------------------------
/features/dungeons/MimicKilled.js:
--------------------------------------------------------------------------------
1 | import config from "../../config"
2 | import { Event } from "../../core/Event"
3 | import EventEnums from "../../core/EventEnums"
4 | import Feature from "../../core/Feature"
5 |
6 | let msgSent = false
7 |
8 | new Feature("sendMimicDead", "catacombs")
9 | .addEvent(
10 | new Event(EventEnums.ENTITYDEATH, (entity) => {
11 | if (msgSent || !entity.entity.func_70631_g_() || entity.entity.func_82169_q(0)) return
12 | ChatLib.command(`pc ${config().mimicDeadMessage}`)
13 | msgSent = true
14 | }, net.minecraft.entity.monster.EntityZombie)
15 | )
16 | .onUnregister(() => msgSent = false)
--------------------------------------------------------------------------------
/features/dungeons/PuzzleDisplay.js:
--------------------------------------------------------------------------------
1 | import AtomxApi from "../../../Atomx/AtomxApi"
2 | import Dungeons from "../../../Atomx/skyblock/Dungeons"
3 | import config from "../../config"
4 | import { Event } from "../../core/Event"
5 | import EventEnums from "../../core/EventEnums"
6 | import Feature from "../../core/Feature"
7 | import DraggableGui from "../../shared/DraggableGui"
8 |
9 | const editGui = new DraggableGui("puzzlesDisplay").setCommandName("editpuzzlesDisplay")
10 | const PuzzleEnums = {
11 | 0: "&6✦",
12 | 1: "&a✔",
13 | 2: "&c✖"
14 | }
15 | const puzzleRegex = AtomxApi.getRegexData()?.Dungeons?.PuzzlesAmount
16 |
17 | let puzzles = {}
18 | let puzzleCount = 0
19 |
20 | editGui.onDraw(() => {
21 | Renderer.translate(editGui.getX(), editGui.getY())
22 | Renderer.scale(editGui.getScale())
23 | Renderer.drawStringWithShadow("&d&lPuzzles&f: &65\n&d&lBoulder &6✦\n&d&lThree Weirdos &a✔", 0, 0)
24 | Renderer.finishDraw()
25 | })
26 |
27 | const feat = new Feature("puzzlesDisplay", "catacombs")
28 | .addEvent(
29 | new Event(EventEnums.PACKET.SERVER.TABADD, (amount) => {
30 | puzzleCount = +amount
31 | feat.update()
32 | }, puzzleRegex)
33 | )
34 | .addSubEvent(
35 | new Event("renderOverlay", () => {
36 | if (editGui.isOpen()) return
37 |
38 | Renderer.translate(editGui.getX(), editGui.getY())
39 | Renderer.scale(editGui.getScale())
40 | Renderer.drawStringWithShadow(`&d&lPuzzles&f: ${puzzleCount >= 4 ? "&6" : "&a"}${puzzleCount}\n${Object.values(puzzles).join("\n")}`, 0, 0)
41 | Renderer.finishDraw()
42 | }),
43 | () => puzzleCount !== null
44 | )
45 | .onUnregister(() => {
46 | puzzles = {}
47 | puzzleCount = 0
48 | })
49 |
50 | Dungeons.onPuzzleEvent((puzzleName, event, failedBy) => {
51 | if (!config().puzzlesDisplay) return
52 |
53 | puzzles[puzzleName] = `&d&l${puzzleName} ${PuzzleEnums[event]} ${failedBy ?? ""}`
54 | })
--------------------------------------------------------------------------------
/features/dungeons/RemoveDmgTag.js:
--------------------------------------------------------------------------------
1 | import { Event } from "../../core/Event"
2 | import EventEnums from "../../core/EventEnums"
3 | import Feature from "../../core/Feature"
4 |
5 | // Credits: https://github.com/UnclaimedBloom6/BloomModule/blob/main/Bloom/features/HideGrayNumbers.js
6 |
7 | new Feature("removeDamageTag", "catacombs")
8 | .addEvent(
9 | new Event(EventEnums.PACKET.SERVER.SPAWNMOB, (entityID) => {
10 | const entity = World.getWorld().func_73045_a(entityID)
11 | if (!entity || !(entity instanceof net.minecraft.entity.item.EntityArmorStand)) return
12 | const name = entity.func_95999_t()
13 | if (!name || !/^.?\d[\d,.]+.*?$/.test(name?.removeFormatting())) return
14 |
15 | entity.func_70106_y()
16 | })
17 | )
--------------------------------------------------------------------------------
/features/dungeons/RunSplits.js:
--------------------------------------------------------------------------------
1 | import config from "../../config"
2 | import { Event } from "../../core/Event"
3 | import Feature from "../../core/Feature"
4 | import CustomSplits from "../../shared/CustomSplits"
5 | import DraggableGui from "../../shared/DraggableGui"
6 | import Location from "../../shared/Location"
7 | import { Persistence } from "../../shared/Persistence"
8 |
9 | const editGui = new DraggableGui("runSplits").setCommandName("editrunSplits")
10 | const RunSplits = Persistence.getDataFromFileOrLink("RunSplits.json", "https://raw.githubusercontent.com/DocilElm/Doc-Data/main/dungeons/RunSplits.json")
11 | const split = new CustomSplits(RunSplits, () => Location.inWorld("catacombs"))
12 | const exampleStr = split.buildExampleStr()
13 | split.formatTime = config().dungeonRunSplitsFormat
14 |
15 | config().getConfig().registerListener("dungeonRunSplitsFormat", (_, n) => {
16 | split.formatTime = n
17 | })
18 |
19 | editGui.onDraw(() => {
20 | Renderer.retainTransforms(true)
21 | Renderer.translate(editGui.getX(), editGui.getY())
22 | Renderer.scale(editGui.getScale())
23 | Renderer.drawStringWithShadow("&aRun Splits", 0, 0)
24 | Renderer.drawStringWithShadow(exampleStr, 0, 10)
25 | Renderer.retainTransforms(false)
26 | Renderer.finishDraw()
27 | })
28 |
29 | const feat = new Feature("dungeonRunSplits", "catacombs")
30 | .addEvent(
31 | new Event("renderOverlay", () => {
32 | Renderer.retainTransforms(true)
33 | Renderer.translate(editGui.getX(), editGui.getY())
34 | Renderer.scale(editGui.getScale())
35 | Renderer.drawStringWithShadow("&aRun Splits", 0, 0)
36 |
37 | Renderer.drawStringWithShadow(split.buildStr(), 0, 10)
38 |
39 | Renderer.retainTransforms(false)
40 | Renderer.finishDraw()
41 | })
42 | )
43 | .onUnregister(() => split.reset())
44 |
45 | split.getEvents().forEach(it => feat.addEvent(it))
--------------------------------------------------------------------------------
/features/dungeons/RunsLogger.js:
--------------------------------------------------------------------------------
1 | import config from "../../config"
2 | import { TextHelper } from "../../shared/TextHelper"
3 | import Dungeon from "../../../tska/skyblock/dungeon/Dungeon"
4 | import Feature from "../../core/Feature"
5 | import { Event } from "../../core/Event"
6 | import EventEnums from "../../core/EventEnums"
7 | import { addCommand } from "../../shared/Command"
8 | import { Persistence } from "../../shared/Persistence"
9 |
10 | const extraStatsRegex = /^ *> EXTRA STATS <$/
11 | const floorStatsRegex = /^ *(Master Mode )?The Catacombs - Floor ([VI]+) Stats$/
12 | const teamScoreRegex = /^ *Team Score: (\d+) \((\w\+?)\)$/
13 | const defeatedRegex = /^ *☠ Defeated (?:[\w, ]+) in ([\dms ]+)$/
14 | const deathsRegex = /^ *Deaths: (\d+)$/
15 | const secretsFoundRegex = /^ *Secrets Found: (\d+)$/
16 | const date = new Date()
17 | const todayDate = `${date.getDate()}/${date.getMonth() + 1}/${date.getFullYear()}`
18 |
19 | let currFloor = null
20 | let hasAdded = false
21 | let currentData = {
22 | deaths: 0,
23 | secrets: 0,
24 | time: null,
25 | milestone: 0,
26 | score: 0,
27 | scoreStr: null,
28 | taken: null
29 | }
30 |
31 | const reset = () => {
32 | currFloor = null
33 | currentData = {
34 | deaths: 0,
35 | secrets: 0,
36 | time: null,
37 | milestone: 0,
38 | score: 0,
39 | scoreStr: null,
40 | taken: null
41 | }
42 | }
43 |
44 | const getArrayData = (k, arr) => {
45 | if (!arr) return
46 | let totalSecrets = arr.reduce((a, b) => a + b.secrets, 0)
47 | let totalRuns = arr.length
48 | let averageSecrets = totalSecrets / totalRuns
49 | let sRuns = arr.filter((it) => it.scoreStr === "S").length
50 | let sPlusRuns = arr.filter((it) => it.scoreStr === "S+").length
51 | let otherRuns = arr.filter((it) => it.scoreStr !== "S" && it.scoreStr !== "S+").length
52 |
53 | return `&f- &b${k} Stats S &e${sRuns} &bS+ &6${sPlusRuns} &bOther &7${otherRuns} &bSecret Average &a${averageSecrets.toFixed(2)}\n`
54 | }
55 |
56 | const getDataFromDate = (date, floor) => {
57 | if (!floor) {
58 | let data = Persistence.runsData[date]
59 | if (!data) return
60 |
61 | let str = ""
62 |
63 | for (let k of Object.keys(data)) {
64 | let arr = data[k]
65 | if (!arr) continue
66 |
67 | str += getArrayData(k, arr)
68 | }
69 |
70 | return str.trim()
71 | }
72 |
73 | floor = floor.toUpperCase()
74 | return getArrayData(floor, Persistence.runsData?.[date]?.[floor])?.trim()
75 | }
76 |
77 | const feat = new Feature("runslogger", "catacombs")
78 | .addEvent(
79 | new Event(EventEnums.PACKET.SERVER.CHAT, () => {
80 | if (config().showExtraStats) return
81 | ChatLib.command("showextrastats")
82 | }, extraStatsRegex)
83 | )
84 | .addEvent(
85 | new Event(EventEnums.PACKET.SERVER.CHAT, (mm, roman) => {
86 | const floor = TextHelper.decodeNumeral(roman)
87 | currFloor = mm ? `M${floor}` : `F${floor}`
88 | feat.update()
89 | }, floorStatsRegex)
90 | )
91 | .addSubEvent(
92 | new Event(EventEnums.PACKET.SERVER.CHAT, (score, scoreStr) => {
93 | currentData.score = +score
94 | currentData.scoreStr = scoreStr
95 | }, teamScoreRegex),
96 | () => currFloor
97 | )
98 | .addSubEvent(
99 | new Event(EventEnums.PACKET.SERVER.CHAT, (time) => {
100 | currentData.time = time
101 | feat.update()
102 | }, defeatedRegex),
103 | () => currFloor
104 | )
105 | .addSubEvent(
106 | new Event(EventEnums.PACKET.SERVER.CHAT, (deaths) => {
107 | currentData.deaths = +deaths
108 | }, deathsRegex),
109 | () => currFloor && currentData.time
110 | )
111 | .addSubEvent(
112 | new Event(EventEnums.PACKET.SERVER.CHAT, (secrets) => {
113 | if (hasAdded) return reset()
114 | currentData.secrets = +secrets
115 | currentData.milestone = Dungeon.getMilestone(true)
116 | currentData.taken = Date.now()
117 |
118 | if (!(todayDate in Persistence.runsData)) Persistence.runsData[todayDate] = {}
119 | if (!(currFloor in Persistence.runsData[todayDate])) Persistence.runsData[todayDate][currFloor] = []
120 |
121 | Persistence.runsData[todayDate][currFloor].push(currentData)
122 |
123 | hasAdded = true
124 | reset()
125 | }, secretsFoundRegex),
126 | () => currFloor && currentData.time
127 | )
128 | .onUnregister(() => {
129 | reset()
130 | hasAdded = false
131 | })
132 |
133 | addCommand("runs", "&bShows your runs logged data", (date, floor) => {
134 | new Thread(() => {
135 | if (!date) {
136 | const msg = new Message(`${TextHelper.PREFIX} &aRuns Logger Data&f:`)
137 |
138 | for (let k of Object.keys(Persistence.runsData)) {
139 | msg.addTextComponent(
140 | new TextComponent(`\n&f- &b${k}`)
141 | .setClick("run_command", `/doc runs ${k}`)
142 | .setHover("show_text", `&aClick to run /doc runs ${k}`)
143 | )
144 | }
145 |
146 | msg.chat()
147 | return
148 | }
149 |
150 | if (date.toLowerCase() === "today") date = todayDate
151 | const data = getDataFromDate(date, floor)
152 | const space = floor ? " " : "\n"
153 | ChatLib.chat(`${TextHelper.PREFIX} &aRuns Logger Stats&f:${space}${data || "&cNo data found for the specified date"}`)
154 | }).start()
155 | })
--------------------------------------------------------------------------------
/features/dungeons/SecretsClickedBox.js:
--------------------------------------------------------------------------------
1 | import config from "../../config"
2 | import { Event } from "../../core/Event"
3 | import EventEnums from "../../core/EventEnums"
4 | import Feature from "../../core/Feature"
5 | import { RenderHelper } from "../../shared/Render"
6 |
7 | // Wither skull / Redstone skull
8 | const allowedIDs = new Set(["e0f3e929-869e-3dca-9504-54c666ee6f23", "fed95410-aba1-39df-9b95-1d4f361eb66e"])
9 | const secretBlocks = new Set(["minecraft:chest", "minecraft:lever", "minecraft:skull", "minecraft:trapped_chest"])
10 | const lockedRegex = /^That chest is locked!$/
11 | const blocksToHighlight = new Map()
12 |
13 | const checkSkullTexture = (bp) => allowedIDs.has(World.getWorld()?.func_175625_s(bp)?.func_152108_a()?.id?.toString())
14 |
15 | let locked = false
16 |
17 | const feat = new Feature("showSecretsClicked", "catacombs")
18 | .addEvent(
19 | new Event(EventEnums.PACKET.CLIENT.BLOCKPLACEMENT, (ctBlock, _, bp) => {
20 | const blockName = ctBlock.type.getRegistryName()
21 |
22 | if (!secretBlocks.has(blockName) || blockName === "minecraft:skull" && !checkSkullTexture(bp)) return
23 |
24 | const blockString = ctBlock.toString()
25 | if (blocksToHighlight.has(blockString)) return
26 |
27 | blocksToHighlight.set(blockString, {
28 | block: ctBlock
29 | })
30 |
31 | feat.update()
32 |
33 | Client.scheduleTask(20, () => {
34 | blocksToHighlight.delete(blockString)
35 | locked = false
36 | feat.update()
37 | })
38 | }, false)
39 | )
40 | .addEvent(
41 | new Event(EventEnums.PACKET.SERVER.CHAT, () => locked = true, lockedRegex)
42 | )
43 | .addSubEvent(
44 | new Event("renderWorld", () => {
45 | blocksToHighlight.forEach(obj => {
46 | const block = obj.block
47 | const isChest = block.type.mcBlock instanceof net.minecraft.block.BlockChest
48 |
49 | const r = locked && isChest ? 255 : config().showSecretsClickedColor[0]
50 | const g = locked && isChest ? 0 : config().showSecretsClickedColor[1]
51 | const b = locked && isChest ? 0 : config().showSecretsClickedColor[2]
52 |
53 | RenderHelper.filledBlock(block, r, g, b, 51, true)
54 | RenderHelper.outlineBlock(block, r, g, b, 255, true, 2)
55 | })
56 | }), () => blocksToHighlight.size
57 | )
--------------------------------------------------------------------------------
/features/dungeons/SecretsSound.js:
--------------------------------------------------------------------------------
1 | import config from "../../config"
2 | import { Event } from "../../core/Event"
3 | import EventEnums from "../../core/EventEnums"
4 | import Feature from "../../core/Feature"
5 |
6 | const secretItems = new Set(["Healing VIII Splash Potion", "Healing Potion 8 Splash Potion", "Decoy", "Inflatable Jerry", "Spirit Leap", "Trap", "Training Weights", "Defuse Kit", "Dungeon Chest Key", "Treasure Talisman", "Revive Stone", "Architect's First Draft"])
7 | const allowedIDs = new Set(["e0f3e929-869e-3dca-9504-54c666ee6f23", "fed95410-aba1-39df-9b95-1d4f361eb66e"])
8 | const secretBlocks = new Set(["minecraft:chest", "minecraft:lever", "minecraft:skull", "minecraft:trapped_chest"])
9 | // [SoundType, Pitch]
10 | const soundsList = [
11 | ["mob.blaze.hit", 2],
12 | ["fire.ignite", 1],
13 | ["random.orb", 1],
14 | ["random.break", 2],
15 | ["mob.guardian.land.hit", 2]
16 | ]
17 | const itemEntities = new Map()
18 |
19 | let currentBlockClicked = null
20 |
21 | const playSound = () => {
22 | World.playSound(
23 | soundsList[config().secretsSoundType][0], // Sound
24 | 1,
25 | soundsList[config().secretsSoundType][1] // Pitch
26 | )
27 | }
28 |
29 | const checkSkullTexture = (blockPos) => {
30 | const textureID = World.getWorld().func_175625_s(blockPos)?.func_152108_a()?.id?.toString()
31 |
32 | if (!textureID) return
33 |
34 | return allowedIDs.has(textureID)
35 | }
36 |
37 | const feat = new Feature("secretsSound", "catacombs")
38 | .addEvent(
39 | new Event(EventEnums.FORGE.ENTITYJOIN, (entity, entityID) => {
40 | itemEntities.set(entityID, entity)
41 |
42 | feat.update()
43 | }, net.minecraft.entity.item.EntityItem)
44 | )
45 | .addSubEvent(
46 | new Event(EventEnums.PACKET.SERVER.COLLECTITEM, (entityID) => {
47 | if (!itemEntities.has(entityID)) return
48 |
49 | const entity = itemEntities.get(entityID)
50 | const name = entity.func_92059_d()?.func_82833_r()
51 | if (!name || !secretItems.has(name.removeFormatting())) return
52 |
53 | playSound()
54 | itemEntities.delete(entityID)
55 | }),
56 | () => itemEntities.size
57 | )
58 | .addEvent(
59 | new Event(EventEnums.PACKET.CLIENT.BLOCKPLACEMENT, (ctBlock, _, blockPos) => {
60 | const blockName = ctBlock.type.getRegistryName()
61 |
62 | if (
63 | !secretBlocks.has(blockName) ||
64 | blockName === "minecraft:skull" && !checkSkullTexture(blockPos) ||
65 | ctBlock.toString() === currentBlockClicked
66 | ) return
67 |
68 | playSound()
69 | currentBlockClicked = ctBlock.toString()
70 |
71 | // Reset the last block clicked after 20 ticks
72 | Client.scheduleTask(20, () => currentBlockClicked = null)
73 | }, false)
74 | )
75 | .addEvent(
76 | new Event(EventEnums.SOUNDPLAY, (_, __, vol) => vol === 0.10000000149011612 && playSound(), "mob.bat.hurt")
77 | )
78 | .addEvent(
79 | new Event(EventEnums.SOUNDPLAY, (_, __, vol) => vol === 0.10000000149011612 && playSound(), "mob.bat.death")
80 | )
81 | .onUnregister(() => {
82 | currentBlockClicked = null
83 | itemEntities.clear()
84 | })
--------------------------------------------------------------------------------
/features/dungeons/ThreeWeirdosSolver.js:
--------------------------------------------------------------------------------
1 | import { Event } from "../../core/Event"
2 | import EventEnums from "../../core/EventEnums"
3 | import Feature from "../../core/Feature"
4 | import { RenderHelper } from "../../shared/Render"
5 |
6 | // Credits: https://github.com/UnclaimedBloom6/BloomModule/blob/main/features/ThreeWeirdosSolver.js
7 |
8 | const solutions = [
9 | /The reward is not in my chest!/,
10 | /At least one of them is lying, and the reward is not in \w+'s chest.?/,
11 | /My chest doesn't have the reward\. We are all telling the truth.?/,
12 | /My chest has the reward and I'm telling the truth!/,
13 | /The reward isn't in any of our chests.?/,
14 | /Both of them are telling the truth\. Also, \w+ has the reward in their chest.?/,
15 | ]
16 | const directions = [[1, 0], [-1, 0], [0, 1], [0, -1]]
17 | const npcRegex = /^\[NPC\] (\w+): (.*)$/
18 | const puzzleDoneRegex = /^PUZZLE SOLVED! (\w{1,16}) wasn't fooled by \w+! Good job!$/
19 | const puzzleFailedRegex = /^PUZZLE FAIL\! (\w{1,16}) was fooled by \w+! Yikes!$/
20 |
21 | let currentChest = null
22 |
23 | const handleChest = (npcName) => {
24 | if (currentChest) return
25 |
26 | const armorStand = World.getAllEntitiesOfType(net.minecraft.entity.item.EntityArmorStand).find(a => a?.getName()?.removeFormatting() === npcName)
27 | if (!armorStand) return
28 |
29 | let [ x, y, z ] = [ Math.floor(armorStand.getX()), armorStand.getY(), Math.floor(armorStand.getZ()) ]
30 |
31 | for (let dir of directions) {
32 | let [dx, dz] = dir
33 | let block = World.getBlockAt(x+dx, y, z+dz)
34 |
35 | if (block.type.getID() !== 54) continue
36 |
37 | currentChest = block
38 | return
39 | }
40 | }
41 |
42 | const feat = new Feature("threeWeirdosSolver", "catacombs")
43 | .addEvent(
44 | new Event(EventEnums.PACKET.SERVER.CHAT, (npcName, message, event) => {
45 | if (!solutions.some(solution => solution.test(message))) return
46 |
47 | cancel(event)
48 | ChatLib.chat(`&e[NPC] &b&l${npcName}&f: &a&l${message}`)
49 | handleChest(npcName)
50 |
51 | feat.update()
52 | }, npcRegex)
53 | )
54 | .addSubEvent(
55 | new Event("renderWorld", () => {
56 | RenderHelper.outlineFilledBlock(currentChest, 0, 255, 0, 255)
57 | }),
58 | () => currentChest
59 | )
60 | .addSubEvent(
61 | new Event(EventEnums.PACKET.SERVER.CHAT, () => {
62 | currentChest = null
63 | feat.update()
64 | }, puzzleDoneRegex),
65 | () => currentChest
66 | )
67 | .addSubEvent(
68 | new Event(EventEnums.PACKET.SERVER.CHAT, () => {
69 | currentChest = null
70 | feat.update()
71 | }, puzzleFailedRegex),
72 | () => currentChest
73 | )
74 | .onUnregister(() => {
75 | currentChest = null
76 | })
--------------------------------------------------------------------------------
/features/dungeons/TicTacToeAlgorithm.js:
--------------------------------------------------------------------------------
1 | // Huge thanks to bloom (@unclaimedbloom6)
2 | // for helping me out understanding this algorithm
3 |
4 | const checkWinner = (board) => Array(8).fill().map((_,i)=>"012345678036147258048246".slice(i*3,i*3+3).split("").reduce((a,b)=>!!b?[...a,board[parseInt(b)]]:a,[]).filter(a=>!!a)).map(a=>a.length==3?[...new Set(a)]:0).reduce((a,b)=>!!b&&b.length==1?b[0]:a,null)
5 |
6 | const minmax = (board, depth = 0, isPlayer = false, alpha = -Infinity, beta = Infinity) => {
7 | const winner = checkWinner(board)
8 | if (winner === "O") return 100 - depth
9 | if (winner === "X") return depth - 100
10 | if (board.every(it => it)) return 0
11 |
12 | if (isPlayer) {
13 | let bestScore = -Infinity
14 |
15 | for (let idx = 0; idx < board.length; idx++) {
16 | if (board[idx]) continue
17 |
18 | board[idx] = "O"
19 | let score = minmax(board, depth + 1, false, alpha, beta)
20 | board[idx] = null
21 |
22 | bestScore = Math.max(bestScore, score)
23 | alpha = Math.max(alpha, score)
24 |
25 | if (beta <= alpha) break
26 | }
27 |
28 | return bestScore
29 | }
30 |
31 | let bestScore = Infinity
32 |
33 | for (let idx = 0; idx < board.length; idx++) {
34 | if (board[idx]) continue
35 |
36 | board[idx] = "X"
37 | let score = minmax(board, depth + 1, true, alpha, beta)
38 | board[idx] = null
39 |
40 | bestScore = Math.min(bestScore, score)
41 | beta = Math.min(beta, score)
42 |
43 | if (beta <= alpha) break
44 | }
45 |
46 | return bestScore
47 | }
48 |
49 | export const findBestMove = (board) => {
50 | let bestMove = -1
51 | let bestScore = -Infinity
52 |
53 | // Hardcode starting at the center or corner as player
54 | if (board.filter(it => !!it).length === 1) {
55 | if (!board[4]) return 4
56 | return 0
57 | }
58 |
59 | for (let idx = 0; idx < board.length; idx++) {
60 | if (board[idx]) continue
61 |
62 | board[idx] = "O"
63 | let score = minmax(board)
64 | board[idx] = null
65 |
66 | if (score < bestScore) continue
67 |
68 | bestScore = score
69 | bestMove = idx
70 | }
71 |
72 | return bestMove
73 | }
--------------------------------------------------------------------------------
/features/garden/GardenDisplay.js:
--------------------------------------------------------------------------------
1 | import { Event } from "../../core/Event"
2 | import Feature from "../../core/Feature"
3 | import DraggableGui from "../../shared/DraggableGui"
4 | import TabListData from "../../../Atomx/skyblock/TabListData"
5 |
6 | const defaultString = `&a&lGarden Display\n&aVisitor in&f: &b1m 10s\n&aJacob's contest in&f: &69m\n&eOrganic matter&f: &6100k\n&9Fuel&f: &6100k\n&aTime Left&f: &b1m 10s\n&aStored Compost&f: &6100`
7 | const editGui = new DraggableGui("gardenDisplay").setCommandName("editgardenDisplay")
8 |
9 | editGui.onDraw(() => {
10 | Renderer.translate(editGui.getX(), editGui.getY())
11 | Renderer.scale(editGui.getScale())
12 | Renderer.drawStringWithShadow(defaultString, 0, 0)
13 | Renderer.finishDraw()
14 | })
15 |
16 | new Feature("gardenDisplay", "garden")
17 | .addEvent(
18 | new Event("renderOverlay", () => {
19 | if (editGui.isOpen()) return
20 |
21 | Renderer.translate(editGui.getX(), editGui.getY())
22 | Renderer.scale(editGui.getScale())
23 | Renderer.drawStringWithShadow(
24 | `&a&lGarden Display\n&aVisitor in&f: &b${TabListData.getNextVisitor()} &f(&b${TabListData.getTotalVisitors()}&f)\n&aJacob's contest in&f: &6${TabListData.getJacobContest()}\n&eOrganic matter&f: &6${TabListData.getOrganicMatter()}\n&9Fuel&f: &6${TabListData.getFuel()}\n&aTime Left&f: &b${TabListData.getTimeLeft()}\n&aStored Compost&f: &6${TabListData.getStoredCompost()}`,
25 | 0,
26 | 0
27 | )
28 | Renderer.finishDraw()
29 | })
30 | )
--------------------------------------------------------------------------------
/features/garden/GardenEvents.js:
--------------------------------------------------------------------------------
1 | import { Event } from "../../core/Event"
2 | import EventEnums from "../../core/EventEnums"
3 | import Feature from "../../core/Feature"
4 | import { GardenApi } from "../../shared/Persistence"
5 |
6 | const visitorSet = new Set(Object.keys(GardenApi.Visitors))
7 |
8 | let inVisitor = false
9 | let triggeredListeners = false
10 |
11 | // Events listeners
12 | const _onVisior = []
13 | const _onVisiorClose = []
14 |
15 | /**
16 | * - Runs the given function whenever a visitor gui is detected
17 | * - Passing through the Visitor's name and the AcceptButton to
18 | * - Loop through the lore and get the data from
19 | * @param {(name: string, acceptButton: Item) => void} fn
20 | */
21 | export const onVisitor = (fn) => _onVisior.push(fn)
22 |
23 | /**
24 | * - Runs the given function whenever a visitor gui has been closed
25 | * - Checks whether the last gui was a visitor one and if it was not
26 | * - it will not run the function
27 | * @param {() => void} fn
28 | */
29 | export const onVisitorClose = (fn) => _onVisiorClose.push(fn)
30 |
31 | const feat = new Feature("gardenEvents", "garden")
32 | .addEvent(
33 | new Event(EventEnums.PACKET.SERVER.WINDOWOPEN, (title) => {
34 | if (inVisitor && !visitorSet.has(title)) for (let fn of _onVisiorClose) fn()
35 |
36 | inVisitor = visitorSet.has(title)
37 | feat.update()
38 | })
39 | )
40 | .addSubEvent(
41 | new Event(EventEnums.PACKET.CUSTOM.WINDOWCLOSE, () => {
42 | inVisitor = false
43 | for (let fn of _onVisiorClose) fn()
44 | triggeredListeners = false
45 |
46 | feat.update()
47 | }),
48 | () => inVisitor
49 | )
50 | .addSubEvent(
51 | new Event(EventEnums.PACKET.SERVER.WINDOWITEMS, (items) => {
52 | if (!items[13] || !items[29]) {
53 | inVisitor = false
54 | triggeredListeners = false
55 | feat.update()
56 | return
57 | }
58 |
59 | const visitorSkull = new Item(items[13])
60 | const isVisitor = /^Offers Accepted: [\d]+$/.test(visitorSkull.getLore()?.[4]?.removeFormatting())
61 | if (!isVisitor) {
62 | inVisitor = false
63 | triggeredListeners = false
64 | feat.update()
65 | return
66 | }
67 |
68 | const visitorName = visitorSkull.getName()
69 |
70 | for (let fn of _onVisior) {
71 | if (triggeredListeners) break
72 | fn(visitorName, new Item(items[29]))
73 | }
74 |
75 | triggeredListeners = true
76 | }),
77 | () => inVisitor
78 | )
79 | .onUnregister(() => {
80 | inVisitor = false
81 | })
--------------------------------------------------------------------------------
/features/garden/PestsDisplay.js:
--------------------------------------------------------------------------------
1 | import { Event } from "../../core/Event"
2 | import Feature from "../../core/Feature"
3 | import DraggableGui from "../../shared/DraggableGui"
4 | import TabListData from "../../../Atomx/skyblock/TabListData"
5 |
6 | const defaultString = `&4&lPests Display\n&cAlive&f: &c&l${Player.getName()}\n&cInfested Plots&f: &c&lnull\n&eSpray&f: &bnull\n&aBonus&f: &6null`
7 | const editGui = new DraggableGui("pestsDisplay").setCommandName("editpestsDisplay")
8 |
9 | editGui.onDraw(() => {
10 | Renderer.translate(editGui.getX(), editGui.getY())
11 | Renderer.scale(editGui.getScale())
12 | Renderer.drawStringWithShadow(defaultString, 0, 0)
13 | Renderer.finishDraw()
14 | })
15 |
16 | new Feature("pestsDisplay", "garden")
17 | .addEvent(
18 | new Event("renderOverlay", () => {
19 | if (editGui.isOpen()) return
20 |
21 | Renderer.translate(editGui.getX(), editGui.getY())
22 | Renderer.scale(editGui.getScale())
23 | Renderer.drawStringWithShadow(
24 | `&4&lPests Display\n&cAlive&f: &c&l${TabListData.getPestsAlive()}\n&cInfested Plots&f: &c&l${TabListData.getInfestedPlots()}\n&eSpray&f: &b${TabListData.getCurrentSpray()}\n&aBonus&f: &6${TabListData.getBonusFortune()}`,
25 | 0,
26 | 0
27 | )
28 | Renderer.finishDraw()
29 | })
30 | )
--------------------------------------------------------------------------------
/features/garden/VisitorProfit.js:
--------------------------------------------------------------------------------
1 | import AtomxApi from "../../../Atomx/AtomxApi"
2 | import Price from "../../../Atomx/skyblock/Price"
3 | import config from "../../config"
4 | import { Event } from "../../core/Event"
5 | import EventEnums from "../../core/EventEnums"
6 | import Feature from "../../core/Feature"
7 | import DraggableGui from "../../shared/DraggableGui"
8 | import { TextHelper } from "../../shared/TextHelper"
9 | import { onVisitor } from "./GardenEvents"
10 |
11 | const editGui = new DraggableGui("visitorProfit").setCommandName("editvisitorProfit")
12 | const visitorsList = new Map()
13 | // Fun regex
14 | const requiredItemsRegex = /^ ([A-z ]+)([\d,]+)?$/
15 | const copperRegex = /^ \+([\d,]+) Copper$/
16 | const rareItemregex = /^ (?:◆)?([\w ]+)$/
17 | const visitorDialogRegex = /^\[NPC\] ([\w\. ]+): (.+)$/
18 |
19 | editGui.onDraw(() => {
20 | Renderer.translate(editGui.getX(), editGui.getY())
21 | Renderer.scale(editGui.getScale())
22 | Renderer.drawStringWithShadow(`&b${Player.getName()}\n &aEnchanted Life &8x1\n&aProfit&f: &c0 &7(&c0&7)`, 0, 0)
23 | Renderer.finishDraw()
24 | })
25 |
26 | const feat = new Feature("visitorProfitDisplay", "garden")
27 | .addSubEvent(
28 | new Event("renderOverlay", () => {
29 | if (editGui.isOpen()) return
30 |
31 | let str = ""
32 |
33 | visitorsList.forEach(v => {
34 | const name = v.name
35 | const items = v.requiredItems.join("\n")
36 | const copper = v.copperAmount
37 | const profit = v.profit
38 | const profitFormat = profit <= 0 ? "&c" : "&a"
39 | const rareItem = v.rareItem ? `\n &e${v.rareItem}` : ""
40 |
41 | str += `\n${name}\n ${items}${rareItem}\n&bProfit&f: ${profitFormat}${TextHelper.addCommasTrunc(profit)} &7(&c${copper}&7)\n`
42 | })
43 |
44 | Renderer.translate(editGui.getX(), editGui.getY())
45 | Renderer.scale(editGui.getScale())
46 | Renderer.drawStringWithShadow(str, 0, 0)
47 | Renderer.finishDraw()
48 | }),
49 | () => visitorsList.size
50 | )
51 | .addSubEvent(
52 | new Event(EventEnums.PACKET.SERVER.CHAT, (name) => {
53 | if (!visitorsList.has(name)) return
54 |
55 | visitorsList.delete(name)
56 | feat.update()
57 | }, visitorDialogRegex),
58 | () => visitorsList.size
59 | )
60 | .addSubEvent(
61 | new Event(EventEnums.STEP, () => {
62 | if (Player.getX() === Player.getLastX() && Player.getZ() === Player.getLastZ()) return
63 |
64 | World.getAllEntitiesOfType(net.minecraft.entity.item.EntityArmorStand)
65 | .forEach(it => {
66 | if (it.getName().startsWith("§f")) return
67 | const name = it.getName()?.removeFormatting()
68 | if (!name || !visitorsList.has(name) || it.distanceTo(Player.getPlayer()) < 15) return
69 |
70 | visitorsList.delete(name)
71 | feat.update()
72 | })
73 | }, 1),
74 | () => visitorsList.size
75 | )
76 | .onUnregister(() => {
77 | visitorsList.clear()
78 | })
79 |
80 | onVisitor((visitorName, acceptButton) => {
81 | if (!config().visitorProfitDisplay) return
82 |
83 | const currentData = {
84 | name: visitorName,
85 | requiredItems: [],
86 | copperAmount: 0,
87 | totalPrice: 0,
88 | profit: 0,
89 | rareItem: null
90 | }
91 | const acceptLore = acceptButton.getLore()
92 |
93 | for (let idx = 2; idx < acceptLore.length; idx++) {
94 | let lore = acceptLore[idx].removeFormatting()
95 |
96 | let requiredItemsMatch = lore.match(requiredItemsRegex)
97 | if (idx < 5 && requiredItemsMatch) {
98 | let requiredName = requiredItemsMatch[1].replace(/ x/, "").replace(/ /g, "_").toUpperCase()
99 | let amount = Math.floor(requiredItemsMatch[2]) || 1
100 |
101 | let price = Price.getSellPrice(AtomxApi.getGardenItemID()[requiredName])
102 | let totalPrice = price * amount
103 |
104 | currentData.requiredItems.push(acceptLore[idx])
105 | currentData.totalPrice += (totalPrice || 0)
106 |
107 | continue
108 | }
109 |
110 | let copperMatch = lore.match(copperRegex)
111 | if (copperMatch) {
112 | currentData.copperAmount = Math.floor(copperMatch[1])
113 |
114 | continue
115 | }
116 |
117 | let rareItemMatch = lore.match(rareItemregex)
118 | if (rareItemMatch && idx > 5) {
119 | let rareItemName = rareItemMatch[1].replace(/^ /, "")
120 | let rareName = AtomxApi.getGardenRareItems()[rareItemName]
121 |
122 | if (!rareName) continue
123 |
124 | currentData.rareItem = rareName
125 | ChatLib.chat(`${TextHelper.PREFIX} &aVisitor &b${visitorName.removeFormatting()}&a has &b${rareItemName}`)
126 |
127 | continue
128 | }
129 | }
130 |
131 | const totalItemPrice = currentData.totalPrice
132 | const totalCopperPrice = (Price.getSellPrice("ENCHANTMENT_GREEN_THUMB_1") / 1500) * currentData.copperAmount
133 | currentData.profit = (totalCopperPrice - totalItemPrice)
134 |
135 | if (currentData.rareItem) {
136 | const rareItemPrice = Price.getSellPrice(currentData.rareItem)
137 | const currentProfit = currentData.profit <= 0 ? -currentData.profit : +currentData.profit
138 |
139 | currentData.profit = (rareItemPrice - currentProfit)
140 | }
141 |
142 | visitorsList.set(visitorName.removeFormatting(), currentData)
143 | feat.update()
144 | })
--------------------------------------------------------------------------------
/features/gui/AbstractGui.js:
--------------------------------------------------------------------------------
1 | import ElementUtils from "../../../DocGuiLib/core/Element"
2 | import { CenterConstraint, OutlineEffect, ScrollComponent, UIBlock, UIText } from "../../../Elementa"
3 | import { Button } from "./Button"
4 |
5 | export const textInputScheme = {
6 | TextInput: {
7 | background: { color: [45, 58, 75, 80] }
8 | },
9 | Keybind: {
10 | background: { color: [45, 58, 75, 80] }
11 | }
12 | }
13 |
14 | const Effect = Java.type("gg.essential.elementa.effects.Effect")
15 |
16 | /**
17 | * @param {number[]} color [0 - 255]
18 | * @param {number} thickness The thickness of the line
19 | * @param {UIComponent[]} comps The components this effect should use to calculate the width of the line
20 | *
21 | */
22 | export const bottomLineEffect = (color, thickness = 1, comps = [], shouldPad = false) => {
23 | return new JavaAdapter(Effect, {
24 | getData() {
25 | const res = [null, 0]
26 | if (comps.length === 1) {
27 | res[0] = comps[0].getLeft()
28 | res[1] = comps[0].getWidth()
29 | return res
30 | }
31 |
32 | let pad = (comps[1].getLeft() - comps[0].getRight()) / 2 + 0.25
33 |
34 | for (let idx = 0; idx < comps.length; idx++) {
35 | let comp = comps[idx]
36 | if (idx === 0) res[0] = comp.getLeft()
37 | res[1] += comp.getWidth() + (shouldPad ? pad : 0)
38 | }
39 |
40 | return res
41 | },
42 | beforeChildrenDraw() {
43 | const bounds = this.boundComponent
44 | const [ y, height ] = [ bounds.getTop(), bounds.getHeight() ]
45 |
46 | let [ startX, totalWidth ] = this.getData()
47 |
48 | Renderer.drawLine(
49 | Renderer.color(...color),
50 | startX,
51 | y + height + thickness,
52 | startX + totalWidth,
53 | y + height + thickness,
54 | thickness
55 | )
56 | }
57 | })
58 | }
59 |
60 | export class AbstractGui {
61 | constructor(name, columns, columnData = { startX: 5, padding: 28 }) {
62 | this.list = []
63 | this.columns = columns
64 | this.columnData = columnData
65 | this.title = `&b&lDoc ${name}`.addColor()
66 |
67 | this.bgBoxComp = new UIBlock(ElementUtils.getJavaColor([3, 7, 17, 255]))
68 | .setX(new CenterConstraint())
69 | .setY(new CenterConstraint())
70 | .setWidth((25).percent())
71 | .setHeight((50).percent())
72 | .enableEffect(new OutlineEffect(ElementUtils.getJavaColor([4, 103, 132, 255]), 1))
73 |
74 | this.titleComp = new UIText(this.title)
75 | .setX(new CenterConstraint())
76 | .setY((5).percent())
77 | .setChildOf(this.bgBoxComp)
78 |
79 | if (this.columns) {
80 | this._makeColumns()
81 | }
82 |
83 | this.scrollComp = new ScrollComponent()
84 | .setX(new CenterConstraint())
85 | .setY((15).percent())
86 | .setWidth((100).percent())
87 | .setHeight((60).percent())
88 | .setChildOf(this.bgBoxComp)
89 |
90 | this.addButton = new Button("&aAdd", [45, 58, 75, 255], [35, 196, 3, 200])
91 | this.addButton.onClick(() => this.onAdd())
92 | this.addButton
93 | .component
94 | .setX(new CenterConstraint())
95 | .setY((80).percent())
96 | .setWidth((40).percent())
97 | .setHeight((10).percent())
98 | .setChildOf(this.bgBoxComp)
99 | }
100 |
101 | _makeColumns() {
102 | if (this.columns.length === 1) {
103 | new UIText(this.columns[0])
104 | .setX(new CenterConstraint())
105 | .setY((10).percent())
106 | .setChildOf(this.bgBoxComp)
107 | return
108 | }
109 |
110 | for (let idx = 0; idx < this.columns.length; idx++) {
111 | let text = this.columns[idx]
112 | let x = this.columnData.startX + (this.columnData.padding * idx)
113 | new UIText(text)
114 | .setX((x).percent())
115 | .setY((10).percent())
116 | .setChildOf(this.bgBoxComp)
117 | }
118 | }
119 |
120 | /**
121 | * - Meant to be overriden
122 | */
123 | onAdd() {}
124 | }
--------------------------------------------------------------------------------
/features/gui/Button.js:
--------------------------------------------------------------------------------
1 | import ElementUtils from "../../../DocGuiLib/core/Element"
2 | import { CenterConstraint, OutlineEffect, UIBlock, UIText } from "../../../Elementa"
3 |
4 | export class Button {
5 | constructor(text, color, outlineColor) {
6 | this.component = new UIBlock(ElementUtils.getJavaColor(color)).enableEffect(new OutlineEffect(ElementUtils.getJavaColor(outlineColor), 0.5))
7 | this.text = text
8 | this.textComponent = new UIText(text.addColor())
9 | .setX(new CenterConstraint())
10 | .setY(new CenterConstraint())
11 | .setChildOf(this.component)
12 | this.listeners = []
13 |
14 | this.component.onMouseClick((comp, event) => {
15 | for (let idx = 0; idx < this.listeners.length; idx++) {
16 | let fn = this.listeners[idx]
17 | fn(comp, event)
18 | }
19 | })
20 | }
21 |
22 | /**
23 | * @param {(UIComponent, UIClickEvent) => void} fn
24 | */
25 | onClick(fn) {
26 | this.listeners.push(fn)
27 | }
28 | }
--------------------------------------------------------------------------------
/features/gui/CancelMessage.js:
--------------------------------------------------------------------------------
1 | import ElementUtils from "../../../DocGuiLib/core/Element"
2 | import HandleGui from "../../../DocGuiLib/core/Gui"
3 | import TextInputElement from "../../../DocGuiLib/elements/TextInput"
4 | import { CenterConstraint, CramSiblingConstraint, UIBlock, UIText } from "../../../Elementa"
5 | import { addCommand } from "../../shared/Command"
6 | import { Persistence } from "../../shared/Persistence"
7 | import { TextHelper } from "../../shared/TextHelper"
8 | import { AbstractGui, bottomLineEffect, textInputScheme } from "./AbstractGui"
9 | import { Button } from "./Button"
10 |
11 | const gui = new HandleGui()
12 |
13 | class CMessage {
14 | constructor(parent, criteria, list) {
15 | this.parent = parent
16 | this.criteria = criteria
17 | this.list = list
18 | this.list.push(this)
19 | this.id = this.list.length - 1
20 | this.dirty = false
21 | this._register = null
22 | this.init()
23 | }
24 |
25 | init() {
26 | this.mainBox = new UIBlock(ElementUtils.getJavaColor([0, 0, 0, 0]))
27 | .setX((5).percent())
28 | .setY(new CramSiblingConstraint(5))
29 | .setWidth((98).percent())
30 | .setHeight((10).percent())
31 | .setChildOf(this.parent)
32 |
33 | this.criteriaInput = new TextInputElement(this.criteria, 1, 1, 55, 90)
34 | this.criteriaInput
35 | ._setPosition(
36 | (15).percent(),
37 | (0).percent()
38 | )
39 | ._create(textInputScheme)
40 | .setChildOf(this.mainBox)
41 |
42 | this.removeButton = new Button("&cX", [0, 0, 0, 0], [196, 3, 3, 255])
43 | this.removeButton
44 | .component
45 | .setX(new CramSiblingConstraint(5))
46 | .setY((1).percent())
47 | .setWidth((8).percent())
48 | .setHeight((90).percent())
49 | .setChildOf(this.mainBox)
50 |
51 | this.removeButton.onClick(() => this.remove())
52 |
53 | this.mainBox.enableEffect(bottomLineEffect([45, 58, 75, 150], 1.5, [
54 | this.criteriaInput.bgBox
55 | ]))
56 | }
57 |
58 | create(internal = false) {
59 | const inputValue = this.criteriaInput.getText()
60 | if (!inputValue && !internal) return
61 | if (this._register) this.removeRegister()
62 |
63 | if (!internal) this.criteria = inputValue
64 | Persistence.data.cancelMessage[this.criteria] = 0
65 |
66 | this._register = register("chat", (event) => {
67 | if (this.dirty) return this.removeRegister()
68 | cancel(event)
69 | }).setCriteria(this.criteria).setPriority(Priority.LOWEST)
70 | }
71 |
72 | /**
73 | * - Sets this [CancelMessage] dirty meaning it was deleted
74 | */
75 | markDirty() {
76 | this.dirty = true
77 | }
78 |
79 | remove() {
80 | this.removeRegister()
81 | this.parent.removeChild(this.mainBox)
82 | delete Persistence.data.cancelMessage[this.criteria]
83 | this.list.splice(this.id, 1)
84 |
85 | ChatLib.chat(`${TextHelper.PREFIX} &cRemoved Cancel Message with criteria &b${this.criteria}`)
86 | }
87 |
88 | removeRegister() {
89 | if (!this._register) return
90 |
91 | this.markDirty()
92 | this._register.unregister()
93 | this._register = null
94 | }
95 | }
96 |
97 | const cancelMsg = new class CancelMessage extends AbstractGui {
98 | constructor() {
99 | super("Cancel Message", ["Criteria"])
100 | }
101 |
102 | _addMsg(criteria) {
103 | if (this.list.some(it => it.criteria === criteria)) return
104 | new CMessage(this.scrollComp, criteria, this.list).create(true)
105 | }
106 |
107 | onAdd() {
108 | new CMessage(this.scrollComp, "", this.list)
109 | }
110 |
111 | onSave() {
112 | for (let idx = 0; idx < this.list.length; idx++) {
113 | let cmsg = this.list[idx]
114 | cmsg.create()
115 | }
116 |
117 | ChatLib.chat(`${TextHelper.PREFIX} &aSuccessfully created Cancel Messages`)
118 | }
119 | }
120 |
121 | gui._drawNormal(cancelMsg.bgBoxComp)
122 |
123 | Object.keys(Persistence.data.cancelMessage)?.forEach(key => {
124 | cancelMsg._addMsg(key)
125 | })
126 |
127 | addCommand("cmsg", "Opens the CancelMessage UI", () => gui.ctGui.open())
128 | gui.registers.onClose(() => cancelMsg.onSave())
--------------------------------------------------------------------------------
/features/gui/CommandAliases.js:
--------------------------------------------------------------------------------
1 | import ElementUtils from "../../../DocGuiLib/core/Element"
2 | import HandleGui from "../../../DocGuiLib/core/Gui"
3 | import TextInputElement from "../../../DocGuiLib/elements/TextInput"
4 | import { CramSiblingConstraint, UIBlock } from "../../../Elementa"
5 | import { addCommand } from "../../shared/Command"
6 | import { Persistence } from "../../shared/Persistence"
7 | import { TextHelper } from "../../shared/TextHelper"
8 | import { AbstractGui, bottomLineEffect, textInputScheme } from "./AbstractGui"
9 | import { Button } from "./Button"
10 |
11 | const gui = new HandleGui()
12 |
13 | let commandCooldown = null
14 |
15 | class Alias {
16 | constructor(parent, command, alias, list) {
17 | this.command = command
18 | this.alias = alias
19 | this.parent = parent
20 | this.list = list
21 | this.list.push(this)
22 | this.id = this.list.length - 1
23 | this.init()
24 | }
25 |
26 | init() {
27 | this.mainBox = new UIBlock(ElementUtils.getJavaColor([0, 0, 0, 0]))
28 | .setX((5).percent())
29 | .setY(new CramSiblingConstraint(5))
30 | .setWidth((98).percent())
31 | .setHeight((10).percent())
32 | .setChildOf(this.parent)
33 |
34 | this.aliasInput = new TextInputElement(this.alias, 1, 1, 25, 90)
35 | this.aliasInput._setPosition(
36 | (10).percent(),
37 | (0).percent()
38 | )
39 | this.aliasInput._create(textInputScheme).setChildOf(this.mainBox)
40 |
41 | this.commandInput = new TextInputElement(this.command, 1, 1, 25, 90)
42 | this.commandInput._setPosition(
43 | (40).percent(),
44 | (0).percent()
45 | )
46 | this.commandInput._create(textInputScheme).setChildOf(this.mainBox)
47 |
48 | this.removeButton = new Button("&cX", [0, 0, 0, 0], [196, 3, 3, 255])
49 | this.removeButton
50 | .component
51 | .setX(new CramSiblingConstraint(5))
52 | .setY((1).percent())
53 | .setWidth((8).percent())
54 | .setHeight((90).percent())
55 | .setChildOf(this.mainBox)
56 |
57 | this.removeButton.onClick(() => this.remove())
58 |
59 | this.mainBox.enableEffect(bottomLineEffect([45, 58, 75, 150], 1.5, [
60 | this.aliasInput.bgBox,
61 | this.commandInput.bgBox
62 | ], true))
63 | }
64 |
65 | create() {
66 | const cmdValue = this.commandInput.getText()
67 | const aliasValue = this.aliasInput.getText()
68 | if (!cmdValue || !aliasValue) return
69 |
70 | this.command = cmdValue.replace(/\//, "")
71 | this.alias = aliasValue.replace(/\//, "")
72 |
73 | Persistence.data.commandAliases[this.alias] = { command: this.command }
74 | }
75 |
76 | remove() {
77 | this.parent.removeChild(this.mainBox)
78 | delete Persistence.data.commandAliases[this.alias]
79 | this.list.splice(this.id, 1)
80 |
81 | ChatLib.chat(`${TextHelper.PREFIX} &cRemoved Command Alias with alias &b${this.alias}`)
82 | }
83 |
84 | /**
85 | * - Checks whether the command sent matches this [alias] for this [AliasCommand]
86 | * - if it does run the [command] for this class
87 | * @param {string} msg
88 | * @param {CancellableEvent} event
89 | * @returns
90 | */
91 | messageSent(msg, event) {
92 | if (msg === this.alias) {
93 | cancel(event)
94 | if (commandCooldown && (Date.now() - commandCooldown) <= 3000) return
95 | ChatLib.command(this.command, TextHelper.shouldSendAsClient(this.command.split(" ")?.[0]))
96 | commandCooldown = Date.now()
97 |
98 | return
99 | }
100 |
101 | const cmd = msg.split(" ")[0]
102 | const args = msg.split(`${cmd} `)[1]
103 |
104 | if (cmd !== this.alias || !args) return
105 |
106 | cancel(event)
107 | if (commandCooldown && (Date.now() - commandCooldown) <= 3000) return
108 | ChatLib.command(`${this.command} ${args}`, TextHelper.shouldSendAsClient(this.command.split(" ")?.[0]))
109 | commandCooldown = Date.now()
110 | }
111 | }
112 |
113 | const cmdAlias = new class CommandAliases extends AbstractGui {
114 | constructor() {
115 | super("Command Aliases", ["Alias", "Criteria"], { startX: 15, padding: 30 })
116 |
117 | register("messageSent", (msg, event) => {
118 | if (!msg.startsWith("/")) return
119 |
120 | for (let idx = 0; idx < this.list.length; idx++) {
121 | let alias = this.list[idx]
122 | alias.messageSent(msg.replace(/\//g, ""), event)
123 | }
124 | })
125 | }
126 |
127 | _addAlias(alias, command) {
128 | if (this.list.some(it => it.alias.toLowerCase() === alias.toLowerCase() && it.command.toLowerCase() === command.toLowerCase())) return
129 | new Alias(this.scrollComp, command, alias, this.list)
130 | }
131 |
132 | onAdd() {
133 | new Alias(this.scrollComp, "", "", this.list)
134 | }
135 |
136 | onSave() {
137 | for (let idx = 0; idx < this.list.length; idx++) {
138 | let alias = this.list[idx]
139 | alias.create()
140 | }
141 |
142 | ChatLib.chat(`${TextHelper.PREFIX} &aSuccessfully created Command Aliases`)
143 | }
144 | }
145 |
146 | gui._drawNormal(cmdAlias.bgBoxComp)
147 |
148 | Object.keys(Persistence.data.commandAliases)?.forEach(key => {
149 | const obj = Persistence.data.commandAliases[key]
150 | cmdAlias._addAlias(key, obj.command)
151 | })
152 |
153 | addCommand("aliasc", "Opens the Command Aliases UI", () => gui.ctGui.open())
154 | gui.registers.onClose(() => cmdAlias.onSave())
--------------------------------------------------------------------------------
/features/kuudra/CratesWaypoints.js:
--------------------------------------------------------------------------------
1 | import { Event } from "../../core/Event"
2 | import EventEnums from "../../core/EventEnums"
3 | import Feature from "../../core/Feature"
4 | import { RenderHelper } from "../../shared/Render"
5 |
6 | const criterias = [
7 | "[NPC] Elle: Okay adventurers, I will go and fish up Kuudra!",
8 | "[NPC] Elle: Phew! The Ballista is finally ready! It should be strong enough to tank Kuudra's blows now!",
9 | "[NPC] Elle: OMG! Great work collecting my supplies!",
10 | /^(?:\[\w{1,3}\+*\] )?\w{1,16} recovered a Fuel Cell and charged the Ballista! \(100%\)$/,
11 | "[NPC] Elle: POW! SURELY THAT'S IT! I don't think he has any more in him!"
12 | ]
13 | const EntityGiantZombie = net.minecraft.entity.monster.EntityGiantZombie
14 | const entities = new Map()
15 |
16 | let shouldRender = false
17 | let crates = []
18 |
19 | const feat = new Feature("cratesWaypoints", "kuudra")
20 | .addEvent(
21 | new Event(EventEnums.FORGE.ENTITYJOIN, (entity, entityID) => {
22 | if (entities.has(entityID) || entity.field_70163_u > 67) return
23 |
24 | entities.set(entityID, new Entity(entity))
25 | feat.update()
26 | }, EntityGiantZombie)
27 | )
28 | .addSubEvent(
29 | new Event(EventEnums.STEP, () => {
30 | crates = []
31 |
32 | entities.forEach((/** @type {Entity}*/v, k) => {
33 | if (v.isDead()) return entities.delete(k)
34 |
35 | const yaw = v.getYaw()
36 | const distance = v.distanceTo(Player.getPlayer())
37 |
38 | crates.push([
39 | v.getX() + 5 * Math.cos((yaw + 130) * (Math.PI / 180)),
40 | v.getZ() + 5 * Math.sin((yaw + 130) * (Math.PI / 180)),
41 | distance.toFixed(2)
42 | ])
43 | })
44 |
45 | feat.update()
46 | }, 5),
47 | () => entities.size && shouldRender
48 | )
49 | .addSubEvent(
50 | new Event("renderWorld", () => {
51 | for (let arr of crates) {
52 | let [ x, z, distance ] = arr
53 | let yText = distance <= 15 ? 78 : 80
54 |
55 | RenderHelper.renderBeaconBeam(x, x, z, 10, 217, 204, 255, false)
56 | Tessellator.drawString(`§a${distance}m`, x + 0.5, yText, z + 0.5, Renderer.WHITE, true)
57 | }
58 | }),
59 | () => crates.length && shouldRender
60 | )
61 | .onUnregister(() => {
62 | crates = []
63 | shouldRender = false
64 | entities.clear()
65 | })
66 |
67 | criterias.forEach((it, idx) => feat.addEvent(
68 | new Event(EventEnums.PACKET.SERVER.CHAT, () => {
69 | if (idx > 1) {
70 | shouldRender = false
71 | feat.update()
72 | return
73 | }
74 |
75 | shouldRender = true
76 | feat.update()
77 | }, it)
78 | ))
--------------------------------------------------------------------------------
/features/kuudra/KuudraSplits.js:
--------------------------------------------------------------------------------
1 | import { Event } from "../../core/Event"
2 | import EventEnums from "../../core/EventEnums"
3 | import Feature from "../../core/Feature"
4 | import DraggableGui from "../../shared/DraggableGui"
5 | import { TextHelper } from "../../shared/TextHelper"
6 |
7 | const editGui = new DraggableGui("kuudrSplitsDisplay").setCommandName("editkuudrSplitsDisplay")
8 |
9 | let phasesMsg = [
10 | {
11 | criteria: "[NPC] Elle: Okay adventurers, I will go and fish up Kuudra!",
12 | time: null,
13 | displayText: `&bSupplies&f:`,
14 | sent: false
15 | },
16 | {
17 | criteria: "[NPC] Elle: OMG! Great work collecting my supplies!",
18 | time: null,
19 | displayText: `&bBuild&f:`,
20 | sent: false
21 | },
22 | {
23 | criteria: "[NPC] Elle: Phew! The Ballista is finally ready! It should be strong enough to tank Kuudra's blows now!",
24 | time: null,
25 | displayText: `&bFuel/Stun&f:`,
26 | sent: false
27 | },
28 | {
29 | criteria: "[NPC] Elle: POW! SURELY THAT'S IT! I don't think he has any more in him!",
30 | time: null,
31 | displayText: `&bClear&f:`,
32 | sent: false
33 | },
34 | {
35 | criteria: /^ *Tokens Earned\: [\d,.]+$/,
36 | time: null,
37 | displayText: null,
38 | sent: false
39 | }
40 | ]
41 |
42 | editGui.onDraw(() => {
43 | Renderer.retainTransforms(true)
44 | Renderer.translate(editGui.getX(), editGui.getY())
45 | Renderer.scale(editGui.getScale())
46 | Renderer.drawStringWithShadow("&bSupplies&f: &c0s\n&bBuild&f: &c0s\n&bFuel/Stun&f: &c0s\n&bClear&f: &c0s", 0, 0)
47 | Renderer.retainTransforms(false)
48 | Renderer.finishDraw()
49 | })
50 |
51 | const feat = new Feature("kuudraSplits", "kuudra")
52 | .addEvent(
53 | new Event("renderOverlay", () => {
54 | if (editGui.isOpen()) return
55 |
56 | Renderer.retainTransforms(true)
57 | Renderer.translate(editGui.getX(), editGui.getY())
58 | Renderer.scale(editGui.getScale())
59 |
60 | for (let idx = 0; idx < phasesMsg.length; idx++) {
61 | let v = phasesMsg[idx]
62 | let v2 = phasesMsg[idx + 1]
63 | if (!v.displayText) continue
64 | let y = 0 + (10 * idx)
65 |
66 | if (!v.time) {
67 | Renderer.drawStringWithShadow(`${v.displayText} &c0s`, 0, y)
68 | continue
69 | }
70 |
71 | if (v2?.time) {
72 | Renderer.drawStringWithShadow(`${v.displayText} &a${TextHelper.getSecondsSince(v2.time, v.time)}`, 0, y)
73 | if (!v.sent) {
74 | ChatLib.chat(`${TextHelper.PREFIX} ${v.displayText} &a${TextHelper.getSecondsSince(v2.time, v.time)}`)
75 | v.sent = true
76 | }
77 | continue
78 | }
79 |
80 | Renderer.drawStringWithShadow(`${v.displayText} &a${TextHelper.getSecondsSince(Date.now(), v.time)}`, 0, y)
81 | }
82 |
83 | Renderer.retainTransforms(false)
84 | Renderer.finishDraw()
85 | })
86 | )
87 | .onUnregister(() => {
88 | phasesMsg.forEach(it => {
89 | it.time = null
90 | it.sent = false
91 | })
92 | })
93 |
94 | phasesMsg.forEach(it => feat.addEvent(
95 | new Event(EventEnums.PACKET.SERVER.CHAT, () => {
96 | if (it.time) return
97 | it.time = Date.now()
98 | }, it.criteria)
99 | ))
--------------------------------------------------------------------------------
/features/mining/ComissionDisplay.js:
--------------------------------------------------------------------------------
1 | import { Event } from "../../core/Event"
2 | import EventEnums from "../../core/EventEnums"
3 | import Feature from "../../core/Feature"
4 | import DraggableGui from "../../shared/DraggableGui"
5 |
6 | const editGui = new DraggableGui("comissionsDisplay").setCommandName("editcomissionsDisplay")
7 | const comissionsRegex = /^ ([\w' ]+): (?:[\d,.]+%|DONE)$/
8 | const mapList = new Set()
9 |
10 | editGui.onDraw(() => {
11 | Renderer.retainTransforms(true)
12 | Renderer.translate(editGui.getX(), editGui.getY())
13 | Renderer.scale(editGui.getScale())
14 | Renderer.drawStringWithShadow("&bCliffside Veins Titanium&f: &c10%\n&bGoblin Raid Slayer&f: &aDONE", 0, 0)
15 | Renderer.retainTransforms(false)
16 | Renderer.finishDraw()
17 | })
18 |
19 | const feat = new Feature("comissionDisplay", ["Dwarven Mines", "Crystal Hollows", "Mineshaft"])
20 | .addEvent(
21 | new Event(EventEnums.STEP, () => {
22 | mapList.clear()
23 |
24 | TabList.getNames().forEach(name => {
25 | if (!comissionsRegex.test(name.removeFormatting())) return
26 |
27 | mapList.add(name)
28 | })
29 |
30 | feat.update()
31 | }, 1)
32 | )
33 | .addSubEvent(
34 | new Event("renderOverlay", () => {
35 | if (editGui.isOpen()) return
36 |
37 | let y = 0
38 | Renderer.retainTransforms(true)
39 | Renderer.translate(editGui.getX(), editGui.getY())
40 | Renderer.scale(editGui.getScale())
41 |
42 | mapList.forEach(format => {
43 | Renderer.drawStringWithShadow(`&b${format.replace("§r §r§f", "")}`, 0, 10 * y)
44 | y++
45 | })
46 |
47 | Renderer.retainTransforms(false)
48 | Renderer.finishDraw()
49 | }),
50 | () => mapList.size
51 | )
--------------------------------------------------------------------------------
/features/mining/EmissaryWaypoints.js:
--------------------------------------------------------------------------------
1 | import { Event } from "../../core/Event"
2 | import Feature from "../../core/Feature"
3 | import { RenderHelper } from "../../shared/Render"
4 |
5 | const emissaryCoords = {
6 | "Emissary Ceanna": [42, 134, 22],
7 | "Emissary Wilson": [171, 150, 31],
8 | "Emissary Lilith": [58, 198, -8],
9 | "Emissary Fraiser": [-132, 174, -50],
10 | "Emissary Eliza": [-37, 200, -131],
11 | "Emissary Braum": [89, 198, -92],
12 | "Emissary Carlton": [-73, 153, -11]
13 | }
14 |
15 | new Feature("emissaryWaypoints", "dwarven mines")
16 | .addEvent(
17 | new Event("renderWorld", () => {
18 | Object.keys(emissaryCoords).forEach(it => {
19 | const [ x, y, z ] = emissaryCoords[it]
20 |
21 | RenderHelper.renderWaypoint(it, x, y, z, 0, 255, 255, 255, true)
22 | })
23 | })
24 | )
--------------------------------------------------------------------------------
/features/mining/PowderDisplay.js:
--------------------------------------------------------------------------------
1 | import { Event } from "../../core/Event"
2 | import EventEnums from "../../core/EventEnums"
3 | import Feature from "../../core/Feature"
4 | import DraggableGui from "../../shared/DraggableGui"
5 |
6 | const editGui = new DraggableGui("powderDisplay").setCommandName("editpowderDisplay")
7 | const powderRegex = /^ (Mithril|Gemstone|Glacite): (?:[\d,.]+)$/
8 | const powderColors = {
9 | "Mithril": "&a",
10 | "Gemstone": "&5",
11 | "Glacite": "&3"
12 | }
13 |
14 | let powderList = {}
15 |
16 | editGui.onDraw(() => {
17 | Renderer.retainTransforms(true)
18 | Renderer.translate(editGui.getX(), editGui.getY())
19 | Renderer.scale(editGui.getScale())
20 | Renderer.drawStringWithShadow("&aMithril&f: &210,000\n&5Gemstone&f: &d10,000\n&3Glacite&f: &b10,000", 0, 0)
21 | Renderer.retainTransforms(false)
22 | Renderer.finishDraw()
23 | })
24 |
25 | const feat = new Feature("powderDisplay", ["Dwarven Mines", "Crystal Hollows"])
26 | .addEvent(
27 | new Event(EventEnums.PACKET.SERVER.TABADD, (type, _, formatted) => {
28 | powderList[type] = formatted.replace("§r ", powderColors[type])
29 | feat.update()
30 | }, powderRegex)
31 | )
32 | .addEvent(
33 | new Event(EventEnums.PACKET.SERVER.TABUPDATE, (type, _, formatted) => {
34 | powderList[type] = formatted.replace("§r ", powderColors[type])
35 | feat.update()
36 | }, powderRegex)
37 | )
38 | .addSubEvent(
39 | new Event("renderOverlay", () => {
40 | if (editGui.isOpen()) return
41 |
42 | Renderer.retainTransforms(true)
43 | Renderer.translate(editGui.getX(), editGui.getY())
44 | Renderer.scale(editGui.getScale())
45 | Renderer.drawStringWithShadow(Object.values(powderList).join("\n"), 0, 0)
46 | Renderer.retainTransforms(false)
47 | Renderer.finishDraw()
48 | }),
49 | () => powderList.Mithril
50 | )
51 | .onUnregister(() => powderList = {})
--------------------------------------------------------------------------------
/features/misc/ArmorDisplay.js:
--------------------------------------------------------------------------------
1 | import config from "../../config"
2 | import { Event } from "../../core/Event"
3 | import Feature from "../../core/Feature"
4 | import DraggableGui from "../../shared/DraggableGui"
5 |
6 | const editGui = new DraggableGui("armorDisplay").setCommandName("editarmorDisplay")
7 | const slotColor = Renderer.color(100, 100, 100, 150)
8 | const slotBorderColor = Renderer.color(50, 50, 50, 150)
9 | const barrier = new Item("minecraft:barrier")
10 |
11 | const drawSlotBackground = (/** @type {Item} */item, x, y, internal = false) => {
12 | if (!item && !config().armorDisplayBarrier && !internal) return
13 |
14 | if (config().armorDisplayBackground) {
15 | Renderer.drawRect(slotColor, x, y, 16, 16)
16 |
17 | // Top line
18 | Renderer.drawLine(slotBorderColor, x, y, x + 16, y, 1)
19 |
20 | // Left line
21 | Renderer.drawLine(slotBorderColor, x, y, x, y + 16, 1)
22 |
23 | // Right line
24 | Renderer.drawLine(slotBorderColor, x + 16, y, x + 16, y + 16, 1)
25 |
26 | // Bottom line
27 | Renderer.drawLine(slotBorderColor, x, y + 16, x + 16, y + 16, 1)
28 | }
29 |
30 | if (!item) return barrier.draw(x, y)
31 |
32 | item.draw(x, y)
33 | }
34 |
35 | editGui.onDraw(() => {
36 | Renderer.retainTransforms(true)
37 | Renderer.translate(editGui.getX(), editGui.getY())
38 | Renderer.scale(editGui.getScale())
39 |
40 | drawSlotBackground(null, 0, 0, true)
41 | drawSlotBackground(null, 0, 18, true)
42 | drawSlotBackground(null, 0, 18, true)
43 | drawSlotBackground(null, 0, 18, true)
44 |
45 | Renderer.retainTransforms(false)
46 | Renderer.finishDraw()
47 | })
48 |
49 | new Feature("armorDisplay")
50 | .addEvent(
51 | new Event("renderOverlay", () => {
52 | if (editGui.isOpen()) return
53 |
54 | Renderer.retainTransforms(true)
55 | Renderer.translate(editGui.getX(), editGui.getY())
56 | Renderer.scale(editGui.getScale())
57 |
58 | drawSlotBackground(Player.armor.getHelmet(), 0, 0)
59 | drawSlotBackground(Player.armor.getChestplate(), 0, 18)
60 | drawSlotBackground(Player.armor.getLeggings(), 0, 18)
61 | drawSlotBackground(Player.armor.getBoots(), 0, 18)
62 |
63 | Renderer.retainTransforms(false)
64 | Renderer.finishDraw()
65 | })
66 | )
--------------------------------------------------------------------------------
/features/misc/AttributeShardDisplay.js:
--------------------------------------------------------------------------------
1 | import config from "../../config"
2 | import { Event } from "../../core/Event"
3 | import Feature from "../../core/Feature"
4 | import { TextHelper } from "../../shared/TextHelper"
5 |
6 | const cache = new Map()
7 | const cacheNumber = new Map()
8 | const formattedRegex = /^(§\w(?:§\w)*?)([\w ]+) ([IVXLCDM\d]+)(?: ✖)?$/
9 |
10 | config().getConfig().registerListener("attributeShardAbbreviationColor", () => {
11 | cache.clear()
12 | })
13 |
14 | const getName = (format, name) => {
15 | name = name.replace(/\-/g, " ")
16 | if (cache.has(`${format}${name}`)) return cache.get(`${format}${name}`)
17 |
18 | const spaces = name.split(" ")
19 |
20 | let str = spaces.length > 1
21 | ? `${spaces[0][0]}${spaces[1][0]}`.toUpperCase().removeFormatting()?.replace(/§z/g, "")
22 | : `${name.slice(0, 3)}`.toUpperCase().removeFormatting()?.replace(/§z/g, "")
23 |
24 | const result = config().attributeShardAbbreviationColor
25 | ? `${format}${str}`
26 | : str
27 |
28 | cache.set(`${format}${name}`, result)
29 |
30 | return result
31 | }
32 |
33 | const getLevel = (roman) => {
34 | if (cacheNumber.has(roman)) return cacheNumber.get(roman)
35 |
36 | const result = TextHelper.decodeNumeral(roman)
37 | cacheNumber.set(roman, result)
38 |
39 | return result
40 | }
41 |
42 | new Feature("attributeShardLevel")
43 | .addEvent(
44 | new Event("renderSlot", (/** @type {Slot} */slot) => {
45 | const item = slot.getItem()
46 | if (!item || item.getID() !== 409) return
47 |
48 | const lore = item.getLore()
49 | if (!lore || lore?.[0]?.removeFormatting() !== "Attribute Shard") return
50 |
51 | const enchantName = lore[1]
52 | const match = enchantName.match(formattedRegex)
53 | if (!match) return
54 |
55 | const level = getLevel(match[3])
56 |
57 | Tessellator.pushMatrix()
58 | Tessellator.disableLighting()
59 |
60 | if (config().attributeShardAbbreviation) {
61 | Renderer.translate(slot.getDisplayX(), slot.getDisplayY(), 280)
62 | Renderer.scale(0.9)
63 | Renderer.drawStringWithShadow(getName(match[1], match[2]), 0, 0)
64 | }
65 |
66 | Renderer.translate(slot.getDisplayX() + (16 - Renderer.getStringWidth(level)), slot.getDisplayY() + 8, 280)
67 | Renderer.drawStringWithShadow(level, 0, 0)
68 |
69 | Tessellator.enableLighting()
70 | Tessellator.popMatrix()
71 | })
72 | )
--------------------------------------------------------------------------------
/features/misc/BlockOverlay.js:
--------------------------------------------------------------------------------
1 | import config from "../../config"
2 | import { Event } from "../../core/Event"
3 | import Feature from "../../core/Feature"
4 | import { DGlStateManager } from "../../shared/DGlStateManager"
5 | import { RenderHelper } from "../../shared/Render"
6 |
7 | const Blocks = net.minecraft.init.Blocks
8 | const BlockFlowingLava = Blocks.field_150356_k
9 | const BlockLava = Blocks.field_150353_l
10 | const BlockFlowingWater = Blocks.field_150358_i
11 | const BlockWater = Blocks.field_150355_j
12 | const BlockAir = Blocks.field_150350_a
13 | const cachedColors = new Map()
14 |
15 | const getColor = (colors) => {
16 | const [ r, g, b, a ] = colors
17 |
18 | if (cachedColors.has(colors.toString())) return cachedColors.get(colors.toString())
19 |
20 | const javaColor = new java.awt.Color(r / 255, g / 255, b / 255, a / 255)
21 |
22 | cachedColors.set(colors.toString(), javaColor)
23 |
24 | return javaColor
25 | }
26 |
27 | new Feature("blockOverlay")
28 | .addEvent(
29 | new Event("drawBlockHighlight", ({x, y, z}, event) => {
30 | const ctBlock = World.getBlockAt(x, y, z)
31 | const mcBlock = ctBlock.type.mcBlock
32 |
33 | if (mcBlock == BlockAir ||
34 | mcBlock == BlockFlowingLava ||
35 | mcBlock == BlockFlowingWater ||
36 | mcBlock == BlockLava ||
37 | mcBlock == BlockWater) return
38 |
39 | // If third person disable phase
40 | const phase = !(Client.settings.getSettings()?.field_74320_O === 1)
41 | const color = getColor(config().blockOverlayColor)
42 | const pticks = event.partialTicks
43 |
44 | const [ r, g, b, a ] = [color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha()]
45 | const [ r1, g1, b1 ] = [color.getRed(), color.getGreen(), color.getBlue()]
46 |
47 | cancel(event)
48 |
49 | const [ rx, ry, rz ] = RenderHelper.getInterp(pticks)
50 | const aabb = RenderHelper.getCTBlockAxis(ctBlock)
51 |
52 | GL11.glLineWidth(2)
53 | DGlStateManager
54 | .pushMatrix()
55 | .disableTexture2D()
56 | .enableBlend()
57 | .disableAlpha()
58 | .tryBlendFuncSeparate(770, 771, 1, 0)
59 | .translate(-rx, -ry, -rz)
60 |
61 | if (phase) DGlStateManager.disableDepth()
62 |
63 | RenderHelper.drawOutlinedBoundingBox(aabb, r, g, b, a)
64 | if (config().blockOverlayFill) RenderHelper.drawFilledBoundingBox(aabb, r1, g1, b1, 50)
65 |
66 | if (phase) DGlStateManager.enableDepth()
67 |
68 | DGlStateManager
69 | .enableTexture2D()
70 | .disableBlend()
71 | .enableAlpha()
72 | .translate(rx, ry, rz)
73 | .popMatrix()
74 | })
75 | )
--------------------------------------------------------------------------------
/features/misc/BonzoMaskInvincibility.js:
--------------------------------------------------------------------------------
1 | import { Event } from "../../core/Event"
2 | import EventEnums from "../../core/EventEnums"
3 | import Feature from "../../core/Feature"
4 | import DraggableGui from "../../shared/DraggableGui"
5 |
6 | const editGui = new DraggableGui("bonzoMaskInvincibilityTimer").setCommandName("editbonzoMaskInvincibilityTimer")
7 | const procRegex = /^Your( ⚚)? Bonzo\'s Mask saved your life\!$/
8 |
9 | let proc = null
10 |
11 | editGui.onDraw(() => {
12 | Renderer.translate(editGui.getX(), editGui.getY())
13 | Renderer.scale(editGui.getScale())
14 | Renderer.drawStringWithShadow("&9Bonzo's Mask&f: &a0.00s", 0, 0)
15 | Renderer.finishDraw()
16 | })
17 |
18 | const feat = new Feature("bonzoMaskInvincibilityTimer")
19 | .addEvent(
20 | new Event(EventEnums.PACKET.SERVER.CHAT, () => {
21 | proc = 60
22 |
23 | feat.update()
24 | }, procRegex)
25 | )
26 | .addSubEvent(
27 | new Event(EventEnums.PACKET.CUSTOM.TICK, () => {
28 | if (proc === 0) {
29 | proc = null
30 | feat.update()
31 |
32 | return
33 | }
34 |
35 | proc--
36 | }),
37 | () => proc
38 | )
39 | .addSubEvent(
40 | new Event("renderOverlay", () => {
41 | if (editGui.isOpen()) return
42 |
43 | const timeRemaining = (proc * 0.05).toFixed(2)
44 |
45 | Renderer.translate(editGui.getX(), editGui.getY())
46 | Renderer.scale(editGui.getScale())
47 | Renderer.drawStringWithShadow(`&9Bonzo's Mask&f: &a${timeRemaining}s`, 0, 0)
48 | Renderer.finishDraw()
49 | }),
50 | () => proc
51 | )
52 | .onUnregister(() => proc = null)
--------------------------------------------------------------------------------
/features/misc/ChampionDisplay.js:
--------------------------------------------------------------------------------
1 | import { Event } from "../../core/Event"
2 | import EventEnums from "../../core/EventEnums"
3 | import Feature from "../../core/Feature"
4 | import DraggableGui from "../../shared/DraggableGui"
5 | import { TextHelper } from "../../shared/TextHelper"
6 |
7 | const editGui = new DraggableGui("championDisplay").setCommandName("editchampionDisplay")
8 | const championLevel = [0, 50000, 100000, 250000, 500000, 1000000, 1500000, 2000000, 2500000, 3000000]
9 | const cachedLevel = new Map()
10 |
11 | let itemAttributes = null
12 |
13 | const getLevel = (amount) => {
14 | if (!amount) return 0
15 | if (cachedLevel.has(amount)) return cachedLevel.get(amount)
16 |
17 | for (let idx = 0; idx < championLevel.length; idx++) {
18 | let min = championLevel[idx]
19 | if (idx + 1 >= championLevel.length) return idx + 1
20 | let max = championLevel[idx + 1]
21 |
22 | if (amount >= min && amount <= max) {
23 | cachedLevel.set(amount, idx + 1)
24 | return idx + 1
25 | }
26 | }
27 | }
28 |
29 | editGui.onDraw(() => {
30 | Renderer.translate(editGui.getX(), editGui.getY())
31 | Renderer.scale(editGui.getScale())
32 | Renderer.drawStringWithShadow("&bChampion &610&f: &63,000,000", 0, 0)
33 | Renderer.finishDraw()
34 | })
35 |
36 | const feat = new Feature("championxpDisplay")
37 | .addEvent(
38 | new Event(EventEnums.STEP, () => {
39 | const heldItem = Player.getHeldItem()
40 | const extraattributes = TextHelper.getExtraAttribute(Player.getHeldItem())
41 |
42 | if (!heldItem || !extraattributes || !extraattributes?.champion_combat_xp) {
43 | itemAttributes = null
44 | feat.update()
45 |
46 | return
47 | }
48 |
49 | itemAttributes = extraattributes
50 | feat.update()
51 | }, 1)
52 | )
53 | .addSubEvent(
54 | new Event("renderOverlay", () => {
55 | const str = `&bChampion &6${getLevel(itemAttributes.champion_combat_xp)}&f: &6${TextHelper.addCommasTrunc(itemAttributes?.champion_combat_xp ?? 0)}`
56 |
57 | Renderer.translate(editGui.getX(), editGui.getY())
58 | Renderer.scale(editGui.getScale())
59 | Renderer.drawStringWithShadow(str, 0, 0)
60 | Renderer.finishDraw()
61 | }),
62 | () => itemAttributes
63 | )
--------------------------------------------------------------------------------
/features/misc/ChatWaypoint.js:
--------------------------------------------------------------------------------
1 | import config from "../../config"
2 | import { Event } from "../../core/Event"
3 | import EventEnums from "../../core/EventEnums"
4 | import Feature from "../../core/Feature"
5 | import { addCommand } from "../../shared/Command"
6 | import { Persistence } from "../../shared/Persistence"
7 | import { RenderHelper } from "../../shared/Render"
8 | import { TextHelper } from "../../shared/TextHelper"
9 |
10 | const chatRegex = /^(Co-op|Party)?(?: > )?(?:\[\d+\] .? ?)?(?:\[[^\]]+\] )?(\w{1,16}): x: (-?\d+), y: (-?\d+), z: (-?\d+) ?$/
11 | const coords = new Map()
12 |
13 | const distanceTo = (x, y, z, x1, y1, z1) => Math.hypot(x - x1, y - y1, z - z1)
14 |
15 | const feat = new Feature("chatWaypoint")
16 | .addEvent(
17 | new Event(EventEnums.PACKET.SERVER.CHAT, (type, username, x1, y1, z1) => {
18 | if (Persistence.data.chatWaypointNames.some(it => it === username.toLowerCase())) return
19 | if (!type && !config().chatWaypointAll) return
20 | if (type === "Co-op" && !config().chatWaypointCoop) return
21 | if (type === "Party" && !config().chatWaypointParty) return
22 |
23 | const coord = [ Math.floor(x1), Math.floor(y1), Math.floor(z1) ]
24 | if (coords.has(`${coord}`)) return
25 |
26 | coords.set(`${coord}`, coord)
27 | ChatLib.chat(`${TextHelper.PREFIX} &aSet waypoint at &b${coord[0]}, ${coord[1]}, ${coord[2]}`)
28 |
29 | feat.update()
30 | }, chatRegex)
31 | )
32 | .addSubEvent(
33 | new Event("renderWorld", () => {
34 | coords.forEach(it => {
35 | const [ x, y, z ] = it
36 | const distance = distanceTo(Player.getX(), Player.getY(), Player.getZ(), x, y, z)
37 |
38 | if (distance < 3) return coords.delete(`${it}`)
39 |
40 | RenderHelper.renderWaypoint(
41 | `${distance.toFixed(2)}m`,
42 | x, y, z,
43 | 0, 255, 0, 255
44 | )
45 | })
46 | }),
47 | () => coords.size
48 | )
49 | .onUnregister(() => {
50 | coords.clear()
51 | })
52 |
53 | // Blacklist command
54 | addCommand("ctw", "&6Chat waypoints Blacklist commands", (mode, name) => {
55 | if (!mode)
56 | return ChatLib.chat(`${TextHelper.PREFIX} &cMakes sure to add a mode &7(modes: add, remove, clear)&c and a username.&7(clear does not require a username)`)
57 |
58 | switch (mode.toLowerCase()) {
59 | case "add":
60 | Persistence.data.chatWaypointNames.push(name.toLowerCase())
61 | Persistence.data.save()
62 |
63 | ChatLib.chat(`${TextHelper.PREFIX} &c${name.toLowerCase()} &aWas added to the blacklist`)
64 | break
65 |
66 | case "clear":
67 | coords.clear()
68 | ChatLib.chat(`${TextHelper.PREFIX} &aAll waypoints have been deleted`)
69 | break
70 |
71 | case "remove": {
72 | const idx = Persistence.data.chatWaypointNames.indexOf(name.toLowerCase())
73 | Persistence.data.chatWaypointNames.splice(idx, 1)
74 | Persistence.data.save()
75 |
76 | ChatLib.chat(`${TextHelper.PREFIX} &b${name.toLowerCase()} &aWas removed from the blacklist`)
77 | break
78 | }
79 |
80 | default:
81 | ChatLib.chat(`${TextHelper.PREFIX} &cYou did not enter a correct mode.`)
82 | break
83 | }
84 | })
--------------------------------------------------------------------------------
/features/misc/CompactDisplay.js:
--------------------------------------------------------------------------------
1 | import { Event } from "../../core/Event"
2 | import EventEnums from "../../core/EventEnums"
3 | import Feature from "../../core/Feature"
4 | import DraggableGui from "../../shared/DraggableGui"
5 | import { TextHelper } from "../../shared/TextHelper"
6 |
7 | const editGui = new DraggableGui("compactDisplay").setCommandName("editcompactdisplay")
8 | const compactLevel = [0, 100, 500, 1500, 5000, 15000, 50000, 150000, 500000, 1000000]
9 | const cachedLevel = new Map()
10 |
11 | let totalXp = null
12 |
13 | const getLevel = (amount) => {
14 | if (!amount) return 0
15 | if (cachedLevel.has(amount)) return cachedLevel.get(amount)
16 |
17 | for (let idx = 0; idx < compactLevel.length; idx++) {
18 | let min = compactLevel[idx]
19 | if (idx + 1 >= compactLevel.length) return idx + 1
20 | let max = compactLevel[idx + 1]
21 |
22 | if (amount >= min && amount <= max) {
23 | cachedLevel.set(amount, idx + 1)
24 | return idx + 1
25 | }
26 | }
27 | }
28 |
29 | editGui.onDraw(() => {
30 | Renderer.translate(editGui.getX(), editGui.getY())
31 | Renderer.scale(editGui.getScale())
32 | Renderer.drawStringWithShadow("&bCompact &610&f: &61,000,000", 0, 0)
33 | Renderer.finishDraw()
34 | })
35 |
36 | const feat = new Feature("compactDisplay")
37 | .addEvent(
38 | new Event(EventEnums.STEP, () => {
39 | const heldItem = Player.getHeldItem()
40 | const extraattributes = TextHelper.getExtraAttribute(Player.getHeldItem())
41 |
42 | if (!heldItem || !extraattributes || !extraattributes?.compact_blocks) {
43 | totalXp = null
44 | feat.update()
45 |
46 | return
47 | }
48 |
49 | totalXp = extraattributes.compact_blocks
50 | feat.update()
51 | }, 1)
52 | )
53 | .addSubEvent(
54 | new Event("renderOverlay", () => {
55 | if (editGui.isOpen()) return
56 |
57 | const str = `&bCompact &6${getLevel(totalXp)}&f: &6${TextHelper.addCommasTrunc(totalXp ?? 0)}`
58 |
59 | Renderer.translate(editGui.getX(), editGui.getY())
60 | Renderer.scale(editGui.getScale())
61 | Renderer.drawStringWithShadow(str, 0, 0)
62 | Renderer.finishDraw()
63 | }),
64 | () => totalXp !== null
65 | )
66 | .onUnregister(() => {
67 | totalXp = null
68 | })
--------------------------------------------------------------------------------
/features/misc/CopyChat.js:
--------------------------------------------------------------------------------
1 | import { Event } from "../../core/Event"
2 | import Feature from "../../core/Feature"
3 | import { TextHelper } from "../../shared/TextHelper"
4 |
5 | const Mouse = org.lwjgl.input.Mouse
6 |
7 | const getStr = (str, width, chatGui, y) => {
8 | const scale = Renderer.screen.getScale()
9 | const isCtrlDown = Client.isControlDown()
10 | const normalWidth = Renderer.getStringWidth(" ") * Renderer.screen.getScale()
11 | let count = 0
12 |
13 | for (let idx = 0; idx < width; idx++) {
14 | let comp = chatGui.func_146236_a(idx, y)
15 | if (!comp && count > 10) break
16 | if (!comp) {
17 | count++
18 | idx += normalWidth
19 | continue
20 | }
21 |
22 | let text = isCtrlDown
23 | ? comp.func_150261_e()?.replace(/§/g, "&")
24 | : comp.func_150261_e()?.removeFormatting()?.replace(/§z/g, "")
25 |
26 | str += text
27 | idx += (Renderer.getStringWidth(comp.func_150261_e()) * scale) - 1
28 | count = 0
29 | }
30 |
31 | const ny = y - (10 * scale)
32 | const compBelow = chatGui.func_146236_a(15, ny)
33 | if (compBelow) {
34 | const msg = compBelow.func_150261_e().removeFormatting()
35 | if (/^ \w/.test(msg)) {
36 | str = getStr(str, width, chatGui, ny)
37 | }
38 | }
39 |
40 | return str
41 | }
42 |
43 | new Feature("copyChat")
44 | .addEvent(
45 | new Event("guiMouseClick", (_, __, mouseButton) => {
46 | if (!Client.isInChat() || mouseButton !== 1) return
47 |
48 | const y = Mouse.getY()
49 | const chatGui = Client.getChatGUI()
50 | const chatWidth = Client.getChatGUI().func_146228_f() * Renderer.screen.getScale()
51 |
52 | let str = getStr("", chatWidth, chatGui, y)
53 |
54 | ChatLib.command(`ct copy ${str}`, true)
55 | ChatLib.chat(`${TextHelper.PREFIX} &aCopied message to clipboard`)
56 | })
57 | )
--------------------------------------------------------------------------------
/features/misc/CultivatingDisplay.js:
--------------------------------------------------------------------------------
1 | import { Event } from "../../core/Event"
2 | import EventEnums from "../../core/EventEnums"
3 | import Feature from "../../core/Feature"
4 | import DraggableGui from "../../shared/DraggableGui"
5 | import { TextHelper } from "../../shared/TextHelper"
6 |
7 | const editGui = new DraggableGui("cultivatingDisplay").setCommandName("editcultivatingdisplay")
8 | const cultivatingLevel = [0, 1000, 5000, 25000, 100000, 300000, 1500000, 5000000, 20000000, 100000000]
9 | const cachedLevel = new Map()
10 |
11 | let totalXp = null
12 |
13 | const getLevel = (amount) => {
14 | if (!amount) return 0
15 | if (cachedLevel.has(amount)) return cachedLevel.get(amount)
16 |
17 | for (let idx = 0; idx < cultivatingLevel.length; idx++) {
18 | let min = cultivatingLevel[idx]
19 | if (idx + 1 >= cultivatingLevel.length) return idx + 1
20 | let max = cultivatingLevel[idx + 1]
21 |
22 | if (amount >= min && amount <= max) {
23 | cachedLevel.set(amount, idx + 1)
24 | return idx + 1
25 | }
26 | }
27 | }
28 |
29 | editGui.onDraw(() => {
30 | Renderer.translate(editGui.getX(), editGui.getY())
31 | Renderer.scale(editGui.getScale())
32 | Renderer.drawStringWithShadow("&bCultivating &610&f: &6100,000,000", 0, 0)
33 | Renderer.finishDraw()
34 | })
35 |
36 | const feat = new Feature("cultivatingDisplay")
37 | .addEvent(
38 | new Event(EventEnums.STEP, () => {
39 | const heldItem = Player.getHeldItem()
40 | const extraattributes = TextHelper.getExtraAttribute(Player.getHeldItem())
41 |
42 | if (!heldItem || !extraattributes || !extraattributes?.farmed_cultivating) {
43 | totalXp = null
44 | feat.update()
45 |
46 | return
47 | }
48 |
49 | totalXp = extraattributes.farmed_cultivating
50 | feat.update()
51 | }, 1)
52 | )
53 | .addSubEvent(
54 | new Event("renderOverlay", () => {
55 | if (editGui.isOpen()) return
56 |
57 | const str = `&bCultivating &6${getLevel(totalXp)}&f: &6${TextHelper.addCommasTrunc(totalXp ?? 0)}`
58 |
59 | Renderer.translate(editGui.getX(), editGui.getY())
60 | Renderer.scale(editGui.getScale())
61 | Renderer.drawStringWithShadow(str, 0, 0)
62 | Renderer.finishDraw()
63 | }),
64 | () => totalXp !== null
65 | )
66 | .onUnregister(() => {
67 | totalXp = null
68 | })
--------------------------------------------------------------------------------
/features/misc/DrillFuelDisplay.js:
--------------------------------------------------------------------------------
1 | import { Event } from "../../core/Event"
2 | import EventEnums from "../../core/EventEnums"
3 | import Feature from "../../core/Feature"
4 | import DraggableGui from "../../shared/DraggableGui"
5 | import { TextHelper } from "../../shared/TextHelper"
6 |
7 | const editGui = new DraggableGui("drillFuelDisplay").setCommandName("editdrillfueldisplay")
8 |
9 | let currentFuel = null
10 |
11 | editGui.onDraw(() => {
12 | Renderer.translate(editGui.getX(), editGui.getY())
13 | Renderer.scale(editGui.getScale())
14 | Renderer.drawStringWithShadow("&bDrill Fuel&f: &a1,000", 0, 0)
15 | Renderer.finishDraw()
16 | })
17 |
18 | const feat = new Feature("drillFuelDisplay")
19 | .addEvent(
20 | new Event(EventEnums.STEP, () => {
21 | const heldItem = Player.getHeldItem()
22 | const extraattributes = TextHelper.getExtraAttribute(Player.getHeldItem())
23 |
24 | if (!heldItem || !extraattributes || !extraattributes?.drill_fuel) {
25 | currentFuel = null
26 | feat.update()
27 |
28 | return
29 | }
30 |
31 | currentFuel = extraattributes.drill_fuel
32 | feat.update()
33 | }, 1)
34 | )
35 | .addSubEvent(
36 | new Event("renderOverlay", () => {
37 | if (editGui.isOpen()) return
38 |
39 | Renderer.translate(editGui.getX(), editGui.getY())
40 | Renderer.scale(editGui.getScale())
41 | Renderer.drawStringWithShadow(`&bDrill Fuel&f: &a${TextHelper.addCommasTrunc(currentFuel)}`, 0, 0)
42 | Renderer.finishDraw()
43 | }),
44 | () => currentFuel
45 | )
46 | .onUnregister(() => {
47 | currentFuel = null
48 | })
--------------------------------------------------------------------------------
/features/misc/EnchantedBookDisplay.js:
--------------------------------------------------------------------------------
1 | import config from "../../config"
2 | import { Event } from "../../core/Event"
3 | import Feature from "../../core/Feature"
4 | import { TextHelper } from "../../shared/TextHelper"
5 |
6 | const cache = new Map()
7 | const cacheNumber = new Map()
8 | const formattedRegex = /^(§\w(?:§\w)*?)([\w\- ]+) ([IVXLCDM\d]+)$/
9 |
10 | config().getConfig().registerListener("enchantedBookAbbreviationColor", () => {
11 | cache.clear()
12 | })
13 |
14 | const getName = (format, name) => {
15 | name = name.replace(/\-/g, " ")
16 | if (cache.has(`${format}${name}`)) return cache.get(`${format}${name}`)
17 |
18 | const spaces = name.split(" ")
19 | const ultimate = format.endsWith("§d§l")
20 |
21 | let str = spaces.length > 1
22 | ? `${spaces[0][0]}${spaces[1][0]}`.toUpperCase().removeFormatting()?.replace(/§z/g, "")
23 | : `${name.slice(0, 3)}`.toUpperCase().removeFormatting()?.replace(/§z/g, "")
24 |
25 | const result = ultimate || config().enchantedBookAbbreviationColor
26 | ? `${format}${str}`
27 | : str
28 |
29 | cache.set(`${format}${name}`, result)
30 |
31 | return result
32 | }
33 |
34 | const getLevel = (roman) => {
35 | if (cacheNumber.has(roman)) return cacheNumber.get(roman)
36 |
37 | const result = TextHelper.decodeNumeral(roman)
38 | cacheNumber.set(roman, result)
39 |
40 | return result
41 | }
42 |
43 | new Feature("enchantedBookLevel")
44 | .addEvent(
45 | new Event("renderSlot", (/** @type {Slot} */slot) => {
46 | const item = slot.getItem()
47 | if (!item || item.getID() !== 403) return
48 |
49 | const lore = item.getLore()
50 | if (!lore || lore?.[0]?.removeFormatting() !== "Enchanted Book") return
51 |
52 | const enchantName = lore[1]
53 | const match = enchantName.match(formattedRegex)
54 | if (!match) return
55 |
56 | const level = getLevel(match[3])
57 | if (level == null) return
58 |
59 | Tessellator.pushMatrix()
60 | Tessellator.disableLighting()
61 |
62 | if (config().enchantedBookAbbreviation) {
63 | Renderer.translate(slot.getDisplayX(), slot.getDisplayY(), 280)
64 | Renderer.scale(0.9)
65 | Renderer.drawStringWithShadow(getName(match[1], match[2]), 0, 0)
66 | }
67 |
68 | Renderer.translate(slot.getDisplayX() + (16 - Renderer.getStringWidth(level)), slot.getDisplayY() + 8, 280)
69 | Renderer.drawStringWithShadow(level, 0, 0)
70 |
71 | Tessellator.enableLighting()
72 | Tessellator.popMatrix()
73 | })
74 | )
--------------------------------------------------------------------------------
/features/misc/EquipmentDisplay.js:
--------------------------------------------------------------------------------
1 | import config from "../../config"
2 | import { Event } from "../../core/Event"
3 | import EventEnums from "../../core/EventEnums"
4 | import Feature from "../../core/Feature"
5 | import { InventoryButton } from "../../shared/InventoryButton"
6 | import { Persistence } from "../../shared/Persistence"
7 | import { RenderHelper } from "../../shared/Render"
8 | import { TextHelper } from "../../shared/TextHelper"
9 |
10 | const equipmentSlots = [10, 19, 28, 37]
11 | const armorSlots = [5, 6, 7, 8]
12 | const barrier = new Item("minecraft:barrier")
13 | const buttons = new Map()
14 |
15 | let inEquipment = false
16 | let inInv = false
17 |
18 | const updateButtons = (feat) => {
19 | if (!feat) return
20 |
21 | const equipmentList = Persistence.data.equipments
22 | if (!equipmentList) return
23 |
24 | for (let idx = 0; idx < equipmentList.length; idx++) {
25 | let v = equipmentList[idx]
26 | if (!v) continue
27 |
28 | let [ texture, lore ] = v
29 | let inList = buttons.get(armorSlots[idx])
30 |
31 | // If the [slot] is in the list and the [texture] matches
32 | // this [texture] we continue (meaning we don't re-add the same butotn)
33 | if (inList && inList.texture === texture) continue
34 |
35 | // Otherwise if the [slot] is in the list and the
36 | // [texture] doesn't match this [texture] delete the old instance
37 | if (inList && inList.texture !== texture) {
38 | inList.delete()
39 | }
40 |
41 | // Creating the button itself
42 | if (texture === "no") {
43 | // If the [texture] is [no] this means that the slot was empty
44 | // so we can just create the button with a [barrier] item
45 | new InventoryButton(armorSlots[idx], null, buttons)
46 | .setOffset(-27)
47 | .setCommand("equipment")
48 | .setItem(barrier)
49 | .setCheckFn(() => Client.currentGui.get() instanceof net.minecraft.client.gui.inventory.GuiInventory && config().equipmentsDisplay && inInv)
50 |
51 | continue
52 | }
53 |
54 | if (!texture) continue
55 |
56 | // Otherwise if the [texture] exists
57 | // we'll create the item via the [createItemByTexture] method
58 | // and add its lore as the hover value
59 | new InventoryButton(armorSlots[idx], null, buttons)
60 | .setOffset(-27)
61 | .setCommand("equipment")
62 | .createItemByTexture(texture)
63 | .setCheckFn(() => Client.currentGui.get() instanceof net.minecraft.client.gui.inventory.GuiInventory && config().equipmentsDisplay && inInv)
64 | .onMouseHover((mx, my) => {
65 | RenderHelper.drawHoveringText(lore, mx, my)
66 | })
67 | }
68 |
69 | // Updating our [Feature] instance
70 | // due to it having [SubEvents] that depend on this function adding/removing
71 | // items to the list/map
72 | feat.update()
73 | }
74 |
75 | const feat = new Feature("equipmentsDisplay")
76 | .addEvent(
77 | new Event(EventEnums.PACKET.SERVER.WINDOWOPEN, (title) => {
78 | inEquipment = title === "Your Equipment and Stats"
79 | feat.update()
80 | })
81 | )
82 | .addEvent(
83 | new Event("guiOpened", (event) => {
84 | inInv = event.gui instanceof net.minecraft.client.gui.inventory.GuiInventory
85 | feat.update()
86 | })
87 | )
88 | .addEvent(
89 | new Event(EventEnums.PACKET.CUSTOM.WINDOWCLOSE, () => {
90 | inInv = false
91 | feat.update()
92 | })
93 | )
94 | .addSubEvent(
95 | new Event(EventEnums.PACKET.SERVER.WINDOWITEMS, (items) => {
96 | for (let idx = 0; idx < equipmentSlots.length; idx++) {
97 | let slot = equipmentSlots[idx]
98 | let mcItem = items[slot]
99 | if (!mcItem) continue
100 | let item = new Item(mcItem)
101 |
102 | if (item.getName().removeFormatting() === "Empty Equipment Slot") {
103 | Persistence.data.equipments[idx] = ["no"]
104 | continue
105 | }
106 |
107 | let texture = item.getNBT()?.toObject()?.tag?.SkullOwner?.Properties?.textures?.[0]?.Value
108 | if (!texture) {
109 | ChatLib.chat(`${TextHelper.PREFIX} &cError while attempting to get texture data for item&f: ${item.getName()}`)
110 | continue
111 | }
112 |
113 | Persistence.data.equipments[idx] = [texture, item.getLore().map(it => it)]
114 | }
115 |
116 | Persistence.data.save()
117 | // Call the method to update equipments being displayed
118 | updateButtons(feat)
119 |
120 | inEquipment = false
121 | feat.update()
122 | }),
123 | () => inEquipment
124 | )
125 | .addSubEvent(
126 | new Event("guiRender", () => {
127 | buttons.forEach(it => it.draw())
128 | }),
129 | () => buttons.size && inInv
130 | )
131 | .onRegister(() => {
132 | // Update the initial button list from cache (if it exists)
133 | updateButtons(feat)
134 | })
135 | .onUnregister(() => {
136 | inInv = false
137 | inEquipment = false
138 | buttons.forEach(v => v.delete())
139 | buttons.clear()
140 | })
--------------------------------------------------------------------------------
/features/misc/EtherwarpOverlay.js:
--------------------------------------------------------------------------------
1 | import config from "../../config"
2 | import { Event } from "../../core/Event"
3 | import EventEnums from "../../core/EventEnums"
4 | import Feature from "../../core/Feature"
5 | import EtherwarpHelper from "../../shared/EtherwarpHelper"
6 | import { RenderHelper } from "../../shared/Render"
7 | import { TextHelper } from "../../shared/TextHelper"
8 |
9 | const allowedID = new Set(["ASPECT_OF_THE_END", "ASPECT_OF_THE_VOID", "ETHERWARP_CONDUIT"])
10 |
11 | let holdingAotv = false
12 |
13 | const feat = new Feature("etherwarpOverlay")
14 | .addEvent(
15 | new Event(EventEnums.PACKET.CLIENT.HELDITEMCHANGE, () => {
16 | holdingAotv = allowedID.has(TextHelper.getSkyblockItemID(Player.getHeldItem()))
17 |
18 | feat.update()
19 | })
20 | )
21 | .addSubEvent(
22 | new Event("renderWorld", () => {
23 | if (!Player.isSneaking()) return
24 |
25 | const [ status, block ] = EtherwarpHelper.getEtherwarpBlockSuccess(61)
26 | if (!status) return
27 |
28 | const [ r, g, b, a ] = [
29 | config().etherwarpOverlayColor[0],
30 | config().etherwarpOverlayColor[1],
31 | config().etherwarpOverlayColor[2],
32 | config().etherwarpOverlayColor[3],
33 | ]
34 |
35 | RenderHelper.outlineBlock(block, r, g, b, a, true)
36 | RenderHelper.filledBlock(block, r, g, b, 50, true)
37 | }),
38 | () => holdingAotv
39 | )
--------------------------------------------------------------------------------
/features/misc/FactoryHelper.js:
--------------------------------------------------------------------------------
1 | import { scheduleTask } from "../../core/CustomRegisters"
2 | import { Event } from "../../core/Event"
3 | import EventEnums from "../../core/EventEnums"
4 | import Feature from "../../core/Feature"
5 | import { RenderHelper } from "../../shared/Render"
6 | import { TextHelper } from "../../shared/TextHelper"
7 |
8 | const rabbitSlots = [ 28, 29, 30, 31, 32, 33, 34 ]
9 | const chocolateCostRegex = /Cost ([\d,.]+) Chocolate/
10 | const employedRabbitRegex = /^Rabbit \w+ - \[\d+\] \w+$/
11 | const rabbitUpgradeRegex = /^Rabbit \w+ has been promoted to \[\d+\] \w+\!$/
12 | const notEnoughChocoRegex = /^You don\'t have enough Chocolate\!$/
13 |
14 | let renderIdx = null
15 | let inFactory = false
16 |
17 | const feat = new Feature("rabbitHelper")
18 | .addEvent(
19 | new Event(EventEnums.PACKET.SERVER.WINDOWOPEN, (title) => {
20 | scheduleTask(() => {
21 | inFactory = title === "Chocolate Factory"
22 | feat.update()
23 | }, 2)
24 | })
25 | )
26 | .addEvent(
27 | new Event(EventEnums.PACKET.CUSTOM.WINDOWCLOSE, () => {
28 | inFactory = false
29 | renderIdx = null
30 | feat.update()
31 | })
32 | )
33 | .addEvent(
34 | new Event(EventEnums.PACKET.SERVER.CHAT, () => {
35 | renderIdx = null
36 | inFactory = true
37 | feat.update()
38 | }, rabbitUpgradeRegex)
39 | )
40 | .addEvent(
41 | new Event(EventEnums.PACKET.SERVER.CHAT, () => {
42 | renderIdx = null
43 | inFactory = true
44 | feat.update()
45 | }, notEnoughChocoRegex)
46 | )
47 | .addSubEvent(
48 | new Event(EventEnums.PACKET.CUSTOM.TICK, () => {
49 | const container = Player.getContainer()
50 | const items = container.getItems()
51 |
52 | renderIdx = null
53 |
54 | const chocolatePurse = parseFloat(items?.[13]?.getName()?.removeFormatting()?.replace(/[,.]/g, ""))
55 | if (chocolatePurse == null)
56 | return ChatLib.chat(`${TextHelper.PREFIX} &clooks like we couldn't find the chocolates you currently have.`)
57 | if (!chocolatePurse) return
58 |
59 | const rabbitData = rabbitSlots.map((it, idx) => {
60 | const item = items[it]
61 | if (!item || item.getID() !== 397 || !employedRabbitRegex.test(item.getName().removeFormatting())) return
62 |
63 | const chocoCost = parseFloat(item.getLore().map(lore => lore.removeFormatting()).join(" ").match(chocolateCostRegex)?.[1]?.replace(/[,.]/g, ""))
64 | const costPerCPS = chocoCost / (idx + 1)
65 |
66 | return {
67 | idx: it,
68 | costPerCPS,
69 | canBuy: chocoCost < chocolatePurse
70 | }
71 | })
72 |
73 | const mostEfficient = rabbitData.reduce((a, b) => {
74 | if (!("idx" in a) && b?.canBuy) return a = b
75 | if (!("idx" in a)) return a // if we still cannot buy it we keep returning
76 |
77 | if (b?.costPerCPS > a?.costPerCPS || !b?.canBuy) return a
78 |
79 | a = b
80 |
81 | return a
82 | }, {})
83 |
84 | renderIdx = mostEfficient?.idx
85 | inFactory = false
86 | feat.update()
87 | }),
88 | () => inFactory && Player.getContainer().getName() === "Chocolate Factory"
89 | )
90 | .addSubEvent(
91 | new Event("renderOverlay", () => {
92 | if (!Client.isInGui()) return
93 |
94 | let pos = RenderHelper.getSlotRenderPosition(renderIdx)
95 | if (!pos || pos?.[0] == null) return
96 |
97 | const [ x, y ] = pos
98 |
99 | Renderer.translate(0, 0, 100)
100 | Renderer.drawRect(
101 | Renderer.GREEN,
102 | x,
103 | y,
104 | 16,
105 | 16
106 | )
107 | }),
108 | () => renderIdx
109 | )
110 | .onUnregister(() => {
111 | renderIdx = null
112 | inFactory = false
113 | })
--------------------------------------------------------------------------------
/features/misc/HideEmptyTooltip.js:
--------------------------------------------------------------------------------
1 | import { Event } from "../../core/Event"
2 | import Feature from "../../core/Feature"
3 |
4 | new Feature("hideEmptyTooltip")
5 | .addEvent(
6 | new Event("itemTooltip", (lore, _, event) => {
7 | if (lore.length > 1 || lore[0].trim() !== "§o §r") return
8 |
9 | cancel(event)
10 | })
11 | )
--------------------------------------------------------------------------------
/features/misc/ItemRarity.js:
--------------------------------------------------------------------------------
1 | import config from "../../config"
2 | import { Event } from "../../core/Event"
3 | import Feature from "../../core/Feature"
4 | import { TextHelper } from "../../shared/TextHelper"
5 |
6 | const itemRegex = /^(?:§f§f)?(?:§\w(?:§\w)?(?:(?:✿|BUY|SELL|\[Lvl \d+\]) ))?(?:§\w\[.+\] )?(?:§\w+ (§\w)Rift Necklace$)?(§\w)?/
7 | const colors = {
8 | "§f": [255, 255, 255],
9 | "§a": [77, 231, 77],
10 | "§9": [85, 85, 255],
11 | "§5": [151, 0, 151],
12 | "§6": [255, 170, 0],
13 | "§d": [255, 85, 255],
14 | "§b": [85, 255, 255],
15 | "§c": [255, 85, 85],
16 | "§4": [170, 0, 0]
17 | }
18 |
19 | new Feature("renderItemRarity")
20 | .addEvent(
21 | new Event("renderSlot", (slot) => {
22 | const item = slot.getItem()
23 | if (!item || !TextHelper.getSkyblockItemID(item)) return
24 |
25 | const itemName = item.getName()
26 | const match = itemName.match(itemRegex)
27 | const format = match?.[1] ?? match?.[2]
28 |
29 | if (!match || !(format in colors)) return
30 |
31 | const [ r, g, b ] = colors[format]
32 | const color = Renderer.color(r, g, b, Math.floor(config().renderItemRarityOpacity * 255))
33 | const [ x1, y1, x2, y2 ] = [
34 | slot.getDisplayX(),
35 | slot.getDisplayY(),
36 | slot.getDisplayX() + 16,
37 | slot.getDisplayY() + 16,
38 | ]
39 |
40 | Tessellator.pushMatrix()
41 | Tessellator.disableLighting()
42 | Tessellator.enableBlend()
43 | Tessellator.enableAlpha()
44 |
45 | if (config().renderItemRarityShape === 0) {
46 | // Top line
47 | Renderer.drawLine(color, x1, y1, x2, y1, 1.5)
48 | // Left line
49 | Renderer.drawLine(color, x1, y1, x1, y2, 1.5)
50 | // Right line
51 | Renderer.drawLine(color, x2, y1, x2, y2, 1.5)
52 | // Bottom line
53 | Renderer.drawLine(color, x1, y2, x2, y2, 1.5)
54 | }
55 |
56 | if (config().renderItemRarityShape === 1)
57 | Renderer.drawRect(color, x1, y1, 16, 16)
58 |
59 | if (config().renderItemRarityShape === 2) {
60 | // Top line
61 | Renderer.drawLine(color, x1, y1, x2, y1, 1.5)
62 | // Left line
63 | Renderer.drawLine(color, x1, y1, x1, y2, 1.5)
64 | // Right line
65 | Renderer.drawLine(color, x2, y1, x2, y2, 1.5)
66 | // Bottom line
67 | Renderer.drawLine(color, x1, y2, x2, y2, 1.5)
68 |
69 | Renderer.drawRect(color, x1, y1, 16, 16)
70 | }
71 |
72 | // Ternary just for pain
73 | if (config().renderItemRarityShape >= 3)
74 | Renderer.drawCircle(color, x1 + 8, y1 + 8, 9, config().renderItemRarityShape === 3 ? 6 : 10)
75 |
76 | Tessellator.enableLighting()
77 | Tessellator.disableAlpha()
78 | Tessellator.disableBlend()
79 | Tessellator.popMatrix()
80 | })
81 | )
--------------------------------------------------------------------------------
/features/misc/MiddleClickGuis.js:
--------------------------------------------------------------------------------
1 | import { Event } from "../../core/Event"
2 | import Feature from "../../core/Feature"
3 | import { TextHelper } from "../../shared/TextHelper"
4 |
5 | // Credits: https://github.com/Skytils/SkytilsMod/blob/1.x/src/main/kotlin/gg/skytils/skytilsmod/features/impl/misc/MiscFeatures.kt#L428
6 |
7 | const avoidGuis = [
8 | "Wardrobe",
9 | "Drill Anvil",
10 | "Anvil",
11 | "Storage",
12 | "The Hex",
13 | "Composter",
14 | "Auctions",
15 | "Abiphone"
16 | ]
17 | const avoidGuisNeu = [
18 | "Chronomatron (",
19 | "Ultrasequencer (",
20 | "Superpairs ("
21 | ]
22 | const hasneu = net.minecraftforge.fml.common.Loader.isModLoaded("notenoughupdates")
23 |
24 | new Feature("middleClickGui")
25 | .addEvent(
26 | new Event("guiMouseClick", (_, __, mbtn, gui, event) => {
27 | if (mbtn !== 0 || !(gui instanceof net.minecraft.client.gui.inventory.GuiChest)) return
28 |
29 | const container = Player.getContainer()
30 | const containerSize = container.getSize() - 36
31 |
32 | const slot = gui.getSlotUnderMouse()?.field_75222_d
33 | if (!slot || slot >= containerSize || avoidGuis.some(it => container.getName()?.startsWith(it))) return
34 | if (hasneu && avoidGuisNeu.some(it => container.getName()?.startsWith(it))) return
35 |
36 | const item = container.getItems()[slot]
37 | if (
38 | item?.getName()?.removeFormatting() === "Reforge Item" ||
39 | item?.getName()?.removeFormatting() === "Salvage Item"
40 | ) return
41 | if (TextHelper.getSkyblockItemID(item)) return
42 |
43 | cancel(event)
44 | container.click(slot, false, "MIDDLE")
45 | })
46 | )
--------------------------------------------------------------------------------
/features/misc/NoCursorReset.js:
--------------------------------------------------------------------------------
1 | import config from "../../config"
2 | import { Event } from "../../core/Event"
3 | import EventEnums from "../../core/EventEnums"
4 | import Feature from "../../core/Feature"
5 | import { TextHelper } from "../../shared/TextHelper"
6 |
7 | // Credits: https://github.com/Skytils/SkytilsMod/blob/1.x/src/main/kotlin/gg/skytils/skytilsmod/mixins/hooks/util/MouseHelperHook.kt
8 |
9 | const blacklistedMods = ["skytils", "skyblockaddons"]
10 | const hasBlacklisted = blacklistedMods.some(it => net.minecraftforge.fml.common.Loader.isModLoaded(it))
11 | const Mouse = Java.type("org.lwjgl.input.Mouse")
12 | const GLDisplay = Java.type("org.lwjgl.opengl.Display")
13 |
14 | // Saving the original [mouseHelper] to re-set it once the module is unloaded
15 | let originalMouseHelper = Client.getMinecraft().field_71417_B // mouseHelper
16 | let windowClosed = null
17 |
18 | if (!hasBlacklisted) {
19 | // Set the mc instance's [mouseHelper] to a custom made one
20 | // which will have a check before [ungrabMouseCursor] calls [setCursorPosition]
21 | Client.getMinecraft().field_71417_B = new JavaAdapter(net.minecraft.util.MouseHelper, {
22 | // ungrabMouseCursor()
23 | func_74373_b() {
24 | // If no cursor reset is set to false
25 | // we implement the normal behavior
26 | if (!config().noCursorReset) {
27 | Mouse.setCursorPosition(GLDisplay.getWidth() / 2, GLDisplay.getHeight() / 2)
28 | Mouse.setGrabbed(false)
29 |
30 | return
31 | }
32 |
33 | // Check whether we should re-set the position of the cursor or not
34 | // by checking if the time between [windowClosed] and [now] (whenever this method is called)
35 | // is below 100ms
36 | if (!Client.isInGui() || !windowClosed || (Date.now() - windowClosed) > 100) {
37 | Mouse.setCursorPosition(GLDisplay.getWidth() / 2, GLDisplay.getHeight() / 2)
38 | windowClosed = null
39 | }
40 |
41 | Mouse.setGrabbed(false)
42 | }
43 | })
44 | }
45 |
46 | config().getConfig().registerListener("noCursorReset", (_, v) => {
47 | if (!v || !hasBlacklisted) return
48 |
49 | ChatLib.chat(`${TextHelper.PREFIX} &cFeature &7[&6NoCursorReset&7]&c was enabled while having blacklisted mods meaning this feature will not work because you have either &6Skytils &cor &6SkyblockAddons&r`)
50 | })
51 |
52 | new Feature("noCursorReset")
53 | .addEvent(
54 | new Event(EventEnums.PACKET.SERVER.WINDOWCLOSE, () => {
55 | windowClosed = Date.now()
56 | })
57 | )
58 |
59 | register("gameUnload", () => {
60 | if (hasBlacklisted) return
61 |
62 | // field_71417_B - mouseHelper
63 | Client.getMinecraft().field_71417_B = originalMouseHelper
64 | windowClosed = null
65 | })
--------------------------------------------------------------------------------
/features/misc/NoDeathAnimation.js:
--------------------------------------------------------------------------------
1 | import { Event } from "../../core/Event"
2 | import Feature from "../../core/Feature"
3 |
4 | // Credits: https://github.com/UnclaimedBloom6/BloomModule/blob/main/Bloom/features/NoDeathAnimation.js
5 | // straight up copy and paste ^
6 |
7 | const lividRegex = /^\w+ Livid$/
8 |
9 | new Feature("noDeathAnimation")
10 | .addEvent(
11 | new Event("entityDeath", (entity) => {
12 | if (lividRegex.test(entity?.getName()?.removeFormatting())) return
13 |
14 | entity.getEntity().func_70106_y()
15 | })
16 | )
--------------------------------------------------------------------------------
/features/misc/NoEndermanTeleport.js:
--------------------------------------------------------------------------------
1 | import { Event } from "../../core/Event"
2 | import Feature from "../../core/Feature"
3 |
4 | const feat = new Feature("noEndermanTeleport")
5 | .addEvent(
6 | new Event(net.minecraftforge.event.entity.living.EnderTeleportEvent, (event) => {
7 | cancel(event)
8 | })
9 | )
--------------------------------------------------------------------------------
/features/misc/NoLightning.js:
--------------------------------------------------------------------------------
1 | import { Event } from "../../core/Event"
2 | import EventEnums from "../../core/EventEnums"
3 | import Feature from "../../core/Feature"
4 |
5 | // Credits: https://github.com/UnclaimedBloom6/BloomModule/blob/main/Bloom/features/HideLightning.js
6 | // straight up copy and paste ^
7 |
8 | new Feature("noLightning")
9 | .addEvent(
10 | new Event(EventEnums.RENDERENTITY, (_, __, ___, event) => {
11 | cancel(event)
12 | }, net.minecraft.entity.effect.EntityLightningBolt)
13 | )
--------------------------------------------------------------------------------
/features/misc/PhoenixInvincibility.js:
--------------------------------------------------------------------------------
1 | import { Event } from "../../core/Event"
2 | import EventEnums from "../../core/EventEnums"
3 | import Feature from "../../core/Feature"
4 | import DraggableGui from "../../shared/DraggableGui"
5 |
6 | const editGui = new DraggableGui("phoenixInvincibilityTimer").setCommandName("editphoenixInvincibilityTimer")
7 | const procRegex = /^Your Phoenix Pet saved you from certain death\!$/
8 |
9 | let proc = null
10 |
11 | editGui.onDraw(() => {
12 | Renderer.translate(editGui.getX(), editGui.getY())
13 | Renderer.scale(editGui.getScale())
14 | Renderer.drawStringWithShadow("&cPhoenix Pet&f: &a0.00s", 0, 0)
15 | Renderer.finishDraw()
16 | })
17 |
18 | const feat = new Feature("phoenixInvincibilityTimer")
19 | .addEvent(
20 | new Event(EventEnums.PACKET.SERVER.CHAT, () => {
21 | proc = 80
22 |
23 | feat.update()
24 | }, procRegex)
25 | )
26 | .addSubEvent(
27 | new Event(EventEnums.PACKET.CUSTOM.TICK, () => {
28 | if (proc === 0) {
29 | proc = null
30 | feat.update()
31 |
32 | return
33 | }
34 |
35 | proc--
36 | }),
37 | () => proc
38 | )
39 | .addSubEvent(
40 | new Event("renderOverlay", () => {
41 | if (editGui.isOpen()) return
42 |
43 | const timeRemaining = (proc * 0.05).toFixed(2)
44 |
45 | Renderer.translate(editGui.getX(), editGui.getY())
46 | Renderer.scale(editGui.getScale())
47 | Renderer.drawStringWithShadow(`&cPhoenix Pet&f: &a${timeRemaining}s`, 0, 0)
48 | Renderer.finishDraw()
49 | }),
50 | () => proc
51 | )
52 | .onUnregister(() => proc = null)
--------------------------------------------------------------------------------
/features/misc/QuiverDisplay.js:
--------------------------------------------------------------------------------
1 | import { Event } from "../../core/Event"
2 | import EventEnums from "../../core/EventEnums"
3 | import Feature from "../../core/Feature"
4 | import DraggableGui from "../../shared/DraggableGui"
5 | import { Persistence } from "../../shared/Persistence"
6 |
7 | const editGui = new DraggableGui("quiverDisplay").setCommandName("editquiverdisplay")
8 | const MCItem = net.minecraft.item.Item
9 |
10 | editGui.onDraw(() => {
11 | Renderer.translate(editGui.getX(), editGui.getY())
12 | Renderer.scale(editGui.getScale())
13 | Renderer.drawStringWithShadow("&9Explosive Arrow &7(&e2329&7)", 0, 0)
14 | Renderer.finishDraw()
15 | })
16 |
17 | const feat = new Feature("quiverDisplay")
18 | .addEvent(
19 | new Event(EventEnums.PACKET.SERVER.SETSLOT, (slotId, itemStack) => {
20 | if (slotId !== 44 || !itemStack) return
21 | const id = MCItem./* getIdFromItem */func_150891_b(itemStack./* getItem */func_77973_b())
22 | if (id !== 288) return
23 |
24 | const item = new Item(itemStack)
25 | const arrowsLore = item.getLore()?.[5]
26 | if (!arrowsLore || !arrowsLore.includes("Active Arrow")) return
27 | // Lazy good
28 | Persistence.data.quiverArrows = arrowsLore.replace("§5§o§7Active Arrow: ", "")
29 |
30 | feat.update()
31 | })
32 | )
33 | .addEvent(
34 | new Event("renderOverlay", () => {
35 | if (editGui.isOpen() || !Persistence.data.quiverArrows) return
36 |
37 | Renderer.translate(editGui.getX(), editGui.getY())
38 | Renderer.scale(editGui.getScale())
39 | Renderer.drawStringWithShadow(Persistence.data.quiverArrows, 0, 0)
40 | Renderer.finishDraw()
41 | })
42 | )
--------------------------------------------------------------------------------
/features/misc/RagnarokAxeCooldown.js:
--------------------------------------------------------------------------------
1 | import { Event } from "../../core/Event"
2 | import EventEnums from "../../core/EventEnums"
3 | import Feature from "../../core/Feature"
4 | import DraggableGui from "../../shared/DraggableGui"
5 |
6 | const editGui = new DraggableGui("ragaxecd").setCommandName("editragaxecd")
7 | const ragaxeRegex = /^[\d,]+\/[\d,]+❤ [\d,]+❈ Defense CASTING IN 3s/
8 | const ragaxeCancelRegex = /^Ragnarock was cancelled due to taking damage!$/
9 |
10 | let castcancel = null
11 | let ticks = null
12 |
13 | editGui.onDraw(() => {
14 | Renderer.translate(editGui.getX(), editGui.getY())
15 | Renderer.scale(editGui.getScale())
16 | Renderer.drawStringWithShadow("&aAxe Cooldown&f: &a0.00s", 0, 0)
17 | Renderer.finishDraw()
18 | })
19 |
20 | const feat = new Feature("ragnarokAxeTimer")
21 | .addEvent(
22 | new Event(EventEnums.PACKET.SERVER.ACTIONBAR, () => {
23 | if (castcancel && Date.now() - castcancel < 5000) return
24 | // TODO: add the check for mage and change the timer to their ability cd
25 | ticks = 400
26 | feat.update()
27 | }, ragaxeRegex)
28 | )
29 | .addSubEvent(
30 | new Event(EventEnums.PACKET.SERVER.CHAT, () => {
31 | ticks = null
32 | castcancel = Date.now()
33 | feat.update()
34 | }, ragaxeCancelRegex),
35 | () => ticks
36 | )
37 | .addSubEvent(
38 | new Event(EventEnums.PACKET.CUSTOM.TICK, () => {
39 | if (ticks === 0) {
40 | ticks = null
41 | feat.update()
42 |
43 | return
44 | }
45 |
46 | ticks--
47 | }),
48 | () => ticks
49 | )
50 | .addSubEvent(
51 | new Event("renderOverlay", () => {
52 | if (editGui.isOpen()) return
53 |
54 | const timeRemaining = (ticks * 0.05).toFixed(2)
55 | const format = ticks > 50 ? "&c" : "&a"
56 |
57 | Renderer.translate(editGui.getX(), editGui.getY())
58 | Renderer.scale(editGui.getScale())
59 | Renderer.drawStringWithShadow(`&aAxe Cooldown&f: ${format}${timeRemaining}`, 0, 0)
60 | Renderer.finishDraw()
61 | }),
62 | () => ticks
63 | )
64 | .onUnregister(() => {
65 | ticks = null
66 | castcancel = null
67 | })
--------------------------------------------------------------------------------
/features/misc/RemoveFrontView.js:
--------------------------------------------------------------------------------
1 | import { Event } from "../../core/Event"
2 | import Feature from "../../core/Feature"
3 |
4 | new Feature("removeFrontView")
5 | .addEvent(
6 | new Event("renderOverlay", () => {
7 | if (Client.settings.getSettings().field_74320_O !== 2) return
8 |
9 | Client.settings.getSettings().field_74320_O = 0
10 | })
11 | )
--------------------------------------------------------------------------------
/features/misc/RenderItems.js:
--------------------------------------------------------------------------------
1 | import config from "../../config"
2 | import { scheduleTask } from "../../core/CustomRegisters"
3 | import { Event } from "../../core/Event"
4 | import EventEnums from "../../core/EventEnums"
5 | import Feature from "../../core/Feature"
6 | import { Render3D } from "../../../tska/rendering/Render3D"
7 | import Location from "../../../tska/skyblock/Location"
8 |
9 | const entities = new HashMap()
10 | const itemRegex = /^(?:§f§f)?(?:§\w(?:§\w)?(?:(?:✿|BUY|SELL|\[Lvl \d+\]) ))?(?:§\w\[.+\] )?(?:§\w+ (§\w)Rift Necklace$)?(§\w)?/
11 | const colors = {
12 | "§f": [255, 255, 255],
13 | "§a": [77, 231, 77],
14 | "§9": [85, 85, 255],
15 | "§5": [151, 0, 151],
16 | "§6": [255, 170, 0],
17 | "§d": [255, 85, 255],
18 | "§b": [85, 255, 255],
19 | "§c": [255, 85, 85],
20 | "§4": [170, 0, 0]
21 | }
22 |
23 | const feat = new Feature("renderItems")
24 | .addEvent(
25 | new Event(EventEnums.FORGE.ENTITYJOIN, (mcEntity) => {
26 | scheduleTask(() => {
27 | const mcItem = mcEntity./* getEntityItem */func_92059_d()
28 | const name = mcItem./* getDisplayName */func_82833_r()
29 | if (!name.startsWith("§")) return
30 |
31 | const m = name.match(itemRegex)
32 | let rarity = m?.[1] ?? m?.[2]
33 | if (!rarity in colors) rarity = "§f"
34 |
35 | entities.put(mcEntity, {
36 | color: (colors[rarity] || [255, 255, 255]),
37 | displayStr: `&ax${mcItem./* stackSize */field_77994_a} &f${config().renderItemsName === 1 ? name.removeFormatting() : name}`
38 | })
39 | feat.update()
40 | })
41 | }, net.minecraft.entity.item.EntityItem)
42 | )
43 | .addSubEvent(
44 | new Event(EventEnums.POSTRENDERENTITY, (/** @type {Entity} */entity, /** @type {Vec3i}*/pos) => {
45 | const obj = entities.get(entity.entity)
46 | if (!obj) return
47 | if (entity.isDead()) return entities.remove(entity.entity)
48 |
49 | const [ x, y, z, width, height ] = [
50 | pos.x, pos.y + (entity.getHeight() / 2), pos.z,
51 | entity.getWidth(), entity.getHeight()
52 | ]
53 |
54 | if (config().renderItemsMode === 1 || config().renderItemsMode === 2 && Location.inWorld("catacombs")) {
55 | const distance = entity.distanceTo(Player.getPlayer())
56 | obj.color = distance < 3.5 ? [ 0, 255, 0 ] : [ 255, 0, 0 ]
57 | }
58 |
59 | const [ r, g, b ] = obj.color
60 | const displayText = obj.displayStr
61 |
62 | Render3D.renderEntityBox(x, y, z, width, height, r, g, b, 255, 2, true, false)
63 | Render3D.renderEntityBoxFilled(x, y, z, width, height, r, g, b, 150, true, false)
64 |
65 | if (config().renderItemsName !== 0) {
66 | const [ rx, ry, rz ] = Render3D.lerpViewEntity()
67 | Render3D.renderString(displayText, x + rx, y + 0.8 + ry, z + rz, [], false, 0.05, false)
68 | }
69 | }, net.minecraft.entity.item.EntityItem),
70 | () => entities.size()
71 | )
72 | .onUnregister(() => {
73 | entities.clear()
74 | })
--------------------------------------------------------------------------------
/features/misc/SmolderingPolarizationDisplay.js:
--------------------------------------------------------------------------------
1 | import { Event } from "../../core/Event"
2 | import EventEnums from "../../core/EventEnums"
3 | import Feature from "../../core/Feature"
4 | import DraggableGui from "../../shared/DraggableGui"
5 | import { Persistence } from "../../shared/Persistence"
6 | import { TextHelper } from "../../shared/TextHelper"
7 |
8 | const editGui = new DraggableGui("smolderingPolarization").setCommandName("editsmolderingpolarization")
9 |
10 | let registerTime = null
11 | let inEffects = false
12 |
13 | editGui.onDraw(() => {
14 | Renderer.translate(editGui.getX(), editGui.getY())
15 | Renderer.scale(editGui.getScale())
16 | Renderer.drawStringWithShadow(`&aSmoldering Polarization&f: &b59m 59s`, 0, 0)
17 | Renderer.finishDraw()
18 | })
19 |
20 | const formatTime = (time) => {
21 | const hours = Math.floor(time / 3600)
22 | const minutes = Math.floor((time % 3600) / 60)
23 | const seconds = Math.floor(time % 60)
24 |
25 | if (hours <= 0 && minutes > 0) return `${minutes}m ${seconds}s`
26 | if (hours <= 0 && minutes <= 0) return `&c${seconds}s`
27 | return `${hours}hr ${minutes}m ${seconds}s`
28 | }
29 |
30 | const feat = new Feature("smolderingPolarizationDisplay")
31 | .addEvent(
32 | new Event(/*EventEnums.PACKET.SERVER.CHAT*/ EventEnums.CHAT, () => {
33 | Persistence.data.smolderingPolarization.time += 3600000
34 | registerTime = Date.now() + (Persistence.data.smolderingPolarization.time || 1)
35 | feat.update()
36 | }, /^You ate a Re\-heated Gummy Polar Bear\!$/)
37 | )
38 | .addEvent(
39 | new Event(EventEnums.PACKET.SERVER.WINDOWOPEN, (name) => {
40 | inEffects = /^(?:\(\d+\/\d+\) )?Active Effects$/.test(name)
41 | feat.update()
42 | })
43 | )
44 | .addEvent(
45 | new Event(EventEnums.PACKET.CUSTOM.WINDOWCLOSE, () => {
46 | inEffects = false
47 | feat.update()
48 | })
49 | )
50 | .addSubEvent(
51 | new Event(EventEnums.PACKET.SERVER.WINDOWITEMS, (mcItems) => {
52 | for (let idx = 0; idx < mcItems.length; idx++) {
53 | if (idx > 53) break
54 | let mcItem = mcItems[idx]
55 | if (!mcItem) continue
56 |
57 | let name = mcItem./* getDisplayName */func_82833_r()
58 | if (name?.removeFormatting() !== "Smoldering Polarization I") continue
59 |
60 | let lore = mcItem./* getTooltip */func_82840_a(Player.getPlayer(), Client.getMinecraft()./* gameSettings */field_71474_y./* advancedItemTooltips */field_82882_x)
61 | let remainingTime = lore[6]?.removeFormatting()
62 | if (!remainingTime) continue
63 |
64 | let match = remainingTime.match(/^Remaining\: (\d+\:)?(\d+\:)?(\d+)?$/)
65 | if (!match) continue
66 |
67 | let [ _, hours, minutes, seconds ] = match
68 | if (hours && !minutes) {
69 | let total = 0
70 | let mins = +hours.replace(":", "")
71 |
72 | total += mins * 60000
73 | total += seconds * 1000
74 | Persistence.data.smolderingPolarization.time = total
75 | registerTime = Date.now() + (Persistence.data.smolderingPolarization.time || 1)
76 | ChatLib.chat(`${TextHelper.PREFIX} &bDetected Smoldering Polarization`)
77 | break
78 | }
79 |
80 | let total = 0
81 | let hrs = +hours.replace(":", "")
82 | let mins = +minutes.replace(":", "")
83 | total += hrs * 3600000
84 | total += mins * 60000
85 | total += seconds * 1000
86 |
87 | Persistence.data.smolderingPolarization.time = total
88 | registerTime = Date.now() + (Persistence.data.smolderingPolarization.time || 1)
89 | ChatLib.chat(`${TextHelper.PREFIX} &bDetected Smoldering Polarization`)
90 | break
91 | }
92 |
93 | inEffects = false
94 | feat.update()
95 | }),
96 | () => inEffects
97 | )
98 | .addSubEvent(
99 | new Event("renderOverlay", () => {
100 | if (editGui.isOpen()) return
101 |
102 | const msTime = registerTime - Date.now()
103 | if (msTime < 0) {
104 | registerTime = null
105 | Persistence.data.smolderingPolarization.time = 0
106 | return feat.update()
107 | }
108 |
109 | Renderer.translate(editGui.getX(), editGui.getY())
110 | Renderer.scale(editGui.getScale())
111 | Renderer.drawStringWithShadow(`&aSmoldering Polarization&f: &b${formatTime((msTime) / 1000)}`, 0, 0)
112 | Renderer.finishDraw()
113 | }),
114 | () => registerTime && Persistence.data.smolderingPolarization.time > 0
115 | )
116 | .onRegister(() => {
117 | registerTime = Date.now() + (Persistence.data.smolderingPolarization.time || 1)
118 | feat.update()
119 | })
120 | .onUnregister(() => {
121 | inEffects = false
122 | if (!registerTime) return
123 |
124 | const nwTime = registerTime - Date.now()
125 | Persistence.data.smolderingPolarization.time = nwTime < 0 ? 0 : nwTime
126 |
127 | registerTime = null
128 | })
--------------------------------------------------------------------------------
/features/misc/SystemTimeDisplay.js:
--------------------------------------------------------------------------------
1 | import config from "../../config"
2 | import { Event } from "../../core/Event"
3 | import Feature from "../../core/Feature"
4 | import DraggableGui from "../../shared/DraggableGui"
5 |
6 | const editGui = new DraggableGui("systemDisplay").setCommandName("editsystemDisplay")
7 | const colorList = ["&4", "&c", "&6", "&e", "&2", "&a", "&b", "&3", "&1", "&9", "&d", "&5", "&f", "&7", "&8", "&0"]
8 |
9 | editGui.onDraw(() => {
10 | Renderer.retainTransforms(true)
11 | Renderer.translate(editGui.getX(), editGui.getY())
12 | Renderer.scale(editGui.getScale())
13 | Renderer.drawStringWithShadow(`${colorList[config().systemTimeDisplayColor]}04:24:11 PM`, 0, 0)
14 | Renderer.retainTransforms(false)
15 | Renderer.finishDraw()
16 | })
17 |
18 | const getTime = () => {
19 | const time = new Date(Date.now())
20 | let seconds = time.getSeconds()
21 | let mins = time.getMinutes()
22 | let hours = time.getHours()
23 | hours = hours % 12 || 12
24 |
25 | seconds = seconds < 10 ? `0${seconds}` : seconds
26 | mins = mins < 10 ? `0${mins}` : mins
27 | hours = hours < 10 ? `0${hours}` : hours
28 |
29 | return `${hours}:${mins}:${seconds} ${time.getHours() >= 12 ? "PM" : "AM"}`
30 | }
31 |
32 | new Feature("systemTimeDisplay")
33 | .addEvent(
34 | new Event("renderOverlay", () => {
35 | if (editGui.isOpen()) return
36 |
37 | Renderer.retainTransforms(true)
38 | Renderer.translate(editGui.getX(), editGui.getY())
39 | Renderer.scale(editGui.getScale())
40 | Renderer.drawStringWithShadow(`${colorList[config().systemTimeDisplayColor]}${getTime()}`, 0, 0)
41 | Renderer.retainTransforms(false)
42 | Renderer.finishDraw()
43 | })
44 | )
--------------------------------------------------------------------------------
/features/misc/ToggleSprint.js:
--------------------------------------------------------------------------------
1 | import { Keybind } from "../../../KeybindFix"
2 | import config from "../../config"
3 | import DraggableGui from "../../shared/DraggableGui"
4 | import { Persistence } from "../../shared/Persistence"
5 | import { TextHelper } from "../../shared/TextHelper"
6 |
7 | const toggleSprintKeybind = new Keybind("§fToggle Sprint", Keyboard.KEY_NONE, "Doc")
8 | const sprintKey = new KeyBind(Client.getMinecraft().field_71474_y.field_151444_V)
9 | const forwardKey = new KeyBind(Client.getMinecraft().field_71474_y.field_74351_w)
10 | const editGui = new DraggableGui("toggleSprintDisplay", config().toggleSprintText ?? "&bToggle Sprint&f: &aEnabled").setCommandName("edittoggleSprint")
11 |
12 | let hasEnabled = false
13 |
14 | editGui.onDraw(() => {
15 | Renderer.translate(editGui.getX(), editGui.getY())
16 | Renderer.scale(editGui.getScale())
17 | Renderer.drawStringWithShadow(config().toggleSprintText ?? "&bToggle Sprint&f: &aEnabled", 0, 0)
18 | Renderer.finishDraw()
19 | })
20 |
21 | const renderOverlay = register("renderOverlay", () => {
22 | if (editGui.isOpen()) return
23 |
24 | Renderer.translate(editGui.getX(), editGui.getY())
25 | Renderer.scale(editGui.getScale())
26 | Renderer.drawStringWithShadow(config().toggleSprintText, 0, 0)
27 | Renderer.finishDraw()
28 | }).unregister()
29 |
30 | toggleSprintKeybind.registerKeyPress(() => {
31 | Persistence.data.toggleSprint = !Persistence.data.toggleSprint
32 |
33 | if (Persistence.data.toggleSprint && config().toggleSprintDisplay) renderOverlay.register()
34 | else if (config().toggleSprintDisplay) renderOverlay.unregister()
35 |
36 | ChatLib.chat(`${TextHelper.PREFIX} &bToggle Sprint&f: ${Persistence.data.toggleSprint ? "&aEnabled" : "&cDisabled"}`)
37 | })
38 |
39 | forwardKey.registerKeyDown(() => {
40 | if (!Persistence.data.toggleSprint && hasEnabled) {
41 | sprintKey.setState(false)
42 | hasEnabled = false
43 |
44 | return
45 | }
46 |
47 | if (!World.isLoaded() || !Persistence.data.toggleSprint) return
48 |
49 | sprintKey.setState(true)
50 | hasEnabled = true
51 | })
--------------------------------------------------------------------------------
/features/misc/WorldAgeDisplay.js:
--------------------------------------------------------------------------------
1 | import { Event } from "../../core/Event"
2 | import Feature from "../../core/Feature"
3 | import DraggableGui from "../../shared/DraggableGui"
4 |
5 | const editGui = new DraggableGui("worldAgeDisplay").setCommandName("editworldAgeDisplay")
6 |
7 | editGui.onDraw(() => {
8 | Renderer.retainTransforms(true)
9 | Renderer.translate(editGui.getX(), editGui.getY())
10 | Renderer.scale(editGui.getScale())
11 | Renderer.drawStringWithShadow("&bDay&f: &61.05", 0, 0)
12 | Renderer.retainTransforms(false)
13 | Renderer.finishDraw()
14 | })
15 |
16 | new Feature("worldAgeDisplay")
17 | .addEvent(
18 | new Event("renderOverlay", () => {
19 | if (editGui.isOpen()) return
20 |
21 | Renderer.retainTransforms(true)
22 | Renderer.translate(editGui.getX(), editGui.getY())
23 | Renderer.scale(editGui.getScale())
24 | Renderer.drawStringWithShadow(`&bDay&f: &6${(World.getTime() / 24000).toFixed(2)}`, 0, 0)
25 | Renderer.retainTransforms(false)
26 | Renderer.finishDraw()
27 | })
28 | )
--------------------------------------------------------------------------------
/features/rift/BoxBerberis.js:
--------------------------------------------------------------------------------
1 | import { Event } from "../../core/Event"
2 | import EventEnums, { ParticleEnums } from "../../core/EventEnums"
3 | import Feature from "../../core/Feature"
4 | import { RenderHelper } from "../../shared/Render"
5 |
6 | let block = null
7 |
8 | const feat = new Feature("boxBerberis", "the rift", "dreadfarm")
9 | .addEvent(
10 | new Event(EventEnums.PACKET.SERVER.SPAWNPARTICLE, (type, [x, y, z]) => {
11 | if (type !== ParticleEnums.FIREWORKS_SPARK || Math.hypot(Player.getX() - x, Player.getZ() - z) > 20) return
12 |
13 | const sideBlock = World.getBlockAt(x - 1, y, z - 1)
14 | const blockBelow = World.getBlockAt(x - 1, y - 1, z - 1)
15 |
16 | if (
17 | sideBlock.type.mcBlock !== net.minecraft.init.Blocks.field_150330_I ||
18 | blockBelow.type.mcBlock !== net.minecraft.init.Blocks.field_150458_ak
19 | ) return
20 |
21 | block = sideBlock
22 | feat.update()
23 | })
24 | )
25 | .addSubEvent(
26 | new Event("renderWorld", () => {
27 | RenderHelper.outlineFilledBlock(block, 0, 255, 255, 255, false)
28 | }),
29 | () => block
30 | )
31 | .onUnregister(() => {
32 | block = null
33 | })
--------------------------------------------------------------------------------
/features/rift/EffigiesWaypoint.js:
--------------------------------------------------------------------------------
1 | import { Event } from "../../core/Event"
2 | import EventEnums from "../../core/EventEnums"
3 | import Feature from "../../core/Feature"
4 | import Location from "../../shared/Location"
5 | import { RenderHelper } from "../../shared/Render"
6 |
7 | const effigiesCoords = [
8 | [ 150, 73, 95 ],
9 | [ 193, 87, 119 ],
10 | [ 235, 103, 147 ],
11 | [ 293, 90, 134 ],
12 | [ 262, 93, 94 ],
13 | [ 240, 123, 118 ]
14 | ]
15 |
16 | let waypoints = []
17 |
18 | const feat = new Feature("effigiesWaypoint", "the rift")
19 | .addEvent(
20 | new Event(EventEnums.PACKET.SERVER.SCOREBOARD, (_, format) => {
21 | const m = format.match(/§(\w)⧯/g)
22 |
23 | for (let idx = 0; idx < m.length; idx++) {
24 | let it = m[idx]?.[1]
25 | if (!it) continue
26 |
27 | if (it === "c" && waypoints[idx]) {
28 | waypoints.splice(idx, 1)
29 | feat.update()
30 | continue
31 | }
32 | if (it === "c") continue
33 |
34 | waypoints[idx] = effigiesCoords[idx]
35 | }
36 |
37 | feat.update()
38 | }, /^Effigies: ⧯⧯⧯⧯⧯⧯$/)
39 | )
40 | .addSubEvent(
41 | new Event("renderWorld", () => {
42 | for (let coord of waypoints) {
43 | if (!coord) continue
44 | let distance = Math.hypot(Player.getX() - coord[0], Player.getZ() - coord[2])
45 | RenderHelper.renderWaypoint(
46 | `${distance.toFixed(2)}m`,
47 | coord[0],
48 | coord[1],
49 | coord[2],
50 | 0, 255, 255, 255
51 | )
52 | }
53 | }),
54 | () => waypoints.length
55 | )
56 | .onUnregister(() => {
57 | waypoints = []
58 | })
59 |
60 | Location.onAreaChange((areaName) => {
61 | if (areaName?.includes("stillgore")) return
62 |
63 | waypoints = []
64 | feat.update()
65 | })
--------------------------------------------------------------------------------
/features/rift/GlyphRender.js:
--------------------------------------------------------------------------------
1 | import { Event } from "../../core/Event"
2 | import Feature from "../../core/Feature"
3 |
4 | const glyphLocation = [
5 | ["by the pond below", [-57.5, 70, -95.5]],
6 | ["in the windmill", [-77.5, 71, -148.5]],
7 | ["by the water tower", [-27.5, 72, -135.5]],
8 | ["in the barn", [-51.5, 71.5, -168.5]],
9 | ["next to a glutton", [-108.5, 70.5, -99.5]],
10 | ["at the top of the infested house's chimney", [-33.5, 86, -93.5]],
11 | ["in a lot of cake", [-94.5, 75.5, -85.5]],
12 | ["in a server room", [-57.5, 78.5, -14.5]]
13 | ]
14 |
15 | new Feature("glyphRender", "the rift", ["dreadfarm", "west village"])
16 | .addEvent(
17 | new Event("renderWorld", () => {
18 | for (let it of glyphLocation) {
19 | let [ name, coord ] = it
20 | Tessellator.drawString(name, coord[0] + 0.5, coord[1], coord[2] + 0.5, Renderer.AQUA, false, .05, false)
21 | }
22 | })
23 | )
--------------------------------------------------------------------------------
/features/rift/LavaMaze.js:
--------------------------------------------------------------------------------
1 | import { Event } from "../../core/Event"
2 | import Feature from "../../core/Feature"
3 | import { Persistence } from "../../shared/Persistence"
4 | import { RenderHelper } from "../../shared/Render"
5 |
6 | const LavaMazeData = Persistence.getDataFromFileOrLink("LavaMaze.json", "https://raw.githubusercontent.com/DocilElm/Rift/main/Rift/data/LavaMaze.json")
7 |
8 | new Feature("lavaMazeRender", "the rift", "mirrorverse")
9 | .addEvent(
10 | new Event("renderWorld", () => {
11 | for (let coord of LavaMazeData) {
12 | RenderHelper.outlineFilledBlock(
13 | World.getBlockAt(coord[0], 51, coord[1]),
14 | 0, 255, 255, 255
15 | )
16 | }
17 | })
18 | )
--------------------------------------------------------------------------------
/features/rift/MushroomTimer.js:
--------------------------------------------------------------------------------
1 | import { Event } from "../../core/Event"
2 | import EventEnums from "../../core/EventEnums"
3 | import Feature from "../../core/Feature"
4 | import { TextHelper } from "../../shared/TextHelper"
5 |
6 | let holding = false
7 | let ticks = 0
8 | let coord = null
9 |
10 | const feat = new Feature("mushroomTimer", "the rift", ["dreadfarm", "west village"])
11 | .addEvent(
12 | new Event(EventEnums.PACKET.CLIENT.HELDITEMCHANGE, () => {
13 | holding = TextHelper.getSkyblockItemID(Player.getHeldItem()) === "FARMING_WAND"
14 | ticks = 0
15 | feat.update()
16 | })
17 | )
18 | .addSubEvent(
19 | new Event(EventEnums.PACKET.CUSTOM.TICK, () => {
20 | const lookingAt = Player.lookingAt()
21 | const lookingAtMushroom = lookingAt?.type?.mcBlock === net.minecraft.init.Blocks.field_150338_P
22 | if (!lookingAtMushroom) {
23 | ticks = 0
24 | feat.update()
25 | return
26 | }
27 | if (ticks && lookingAtMushroom) {
28 | ticks--
29 | feat.update()
30 | return
31 | }
32 |
33 | ticks = 80
34 | coord = [ lookingAt.getX(), lookingAt.getY(), lookingAt.getZ() ]
35 | feat.update()
36 | }),
37 | () => holding
38 | )
39 | .addSubEvent(
40 | new Event("renderWorld", () => {
41 | const time = ticks * 0.05
42 | const color = time > 1 ? Renderer.RED : Renderer.GREEN
43 |
44 | Tessellator.drawString(`${time.toFixed(2)}`, coord[0] + 0.5, coord[1] + 0.5, coord[2] + 0.5, color, false, 2)
45 | }),
46 | () => ticks
47 | )
48 | .onUnregister(() => {
49 | holding = false
50 | ticks = 0
51 | })
--------------------------------------------------------------------------------
/features/rift/Tubulator.js:
--------------------------------------------------------------------------------
1 | import { Event } from "../../core/Event"
2 | import Feature from "../../core/Feature"
3 | import { Persistence } from "../../shared/Persistence"
4 | import { RenderHelper } from "../../shared/Render"
5 |
6 | const TubulatorData = Persistence.getDataFromFileOrLink("Tubulator.json", "https://raw.githubusercontent.com/DocilElm/Rift/main/Rift/data/Tubulator.json")
7 |
8 | new Feature("tubulatorRender", "the rift", "mirrorverse")
9 | .addEvent(
10 | new Event("renderWorld", () => {
11 | for (let coord of TubulatorData) {
12 | RenderHelper.outlineFilledBlock(
13 | World.getBlockAt(coord[0], coord[1], coord[2]),
14 | 0, 255, 255, 255
15 | )
16 | }
17 | })
18 | )
--------------------------------------------------------------------------------
/features/rift/WoodenButtons.js:
--------------------------------------------------------------------------------
1 | import { Event } from "../../core/Event"
2 | import EventEnums from "../../core/EventEnums"
3 | import Feature from "../../core/Feature"
4 | import { addCommand } from "../../shared/Command"
5 | import { Persistence } from "../../shared/Persistence"
6 | import { RenderHelper } from "../../shared/Render"
7 | import { TextHelper } from "../../shared/TextHelper"
8 |
9 | const WoodenButtonsData = Persistence.getDataFromFileOrLink("WoodenButtons.json", "https://raw.githubusercontent.com/DocilElm/Rift/main/Rift/data/WoodenButtons.json")
10 | const waypoints = [[-67, 71, -122], [-86, 71, -129], [-115, 72, -103], [-90, 71, -111], [-83, 70, -85], [-106, 78, -95], [-46, 77, -91], [-42, 74, -85], [-33, 72, -85], [-89, 75, -75]]
11 |
12 | new Feature("woodenButtons", "the rift", ["dreadfarm", "west village"])
13 | .addEvent(
14 | new Event("renderWorld", () => {
15 | for (let coord of waypoints) {
16 | RenderHelper.renderBeaconBeam(coord[0], coord[1], coord[2], 0, 255, 255, 255, true)
17 | }
18 |
19 | for (let idx = 0; idx < WoodenButtonsData.length; idx++) {
20 | let coord = WoodenButtonsData[idx]
21 | if (Persistence.data.clickedWoodenButtons.some(it => it === coord.toString())) continue
22 |
23 | Tessellator.drawString(idx + 1, coord[0] + 0.5, coord[1], coord[2] + 0.5, Renderer.GREEN, false, .05, false)
24 | }
25 | })
26 | )
27 | .addEvent(
28 | new Event(EventEnums.PACKET.CLIENT.BLOCKPLACEMENT, (_, coords) => {
29 | const idx = WoodenButtonsData.findIndex((it) => it.toString() === coords.toString())
30 | if (idx === -1) return
31 |
32 | Persistence.data.clickedWoodenButtons.push(coords.toString())
33 | })
34 | )
35 |
36 | addCommand("rsbtn", "Resets the buttons from WoodenButtons feature", () => {
37 | Persistence.data.clickedWoodenButtons = []
38 | ChatLib.chat(`${TextHelper.PREFIX} &aRemoved all saved wooden buttons`)
39 | })
--------------------------------------------------------------------------------
/features/slayers/BossSlainTime.js:
--------------------------------------------------------------------------------
1 | import { Event } from "../../core/Event"
2 | import EventEnums from "../../core/EventEnums"
3 | import Feature from "../../core/Feature"
4 | import { TextHelper } from "../../shared/TextHelper"
5 |
6 | let serverTicks = 0
7 | let spawnedAtTicks = 0
8 | let spawnedAtTime = null
9 | let killedAtTicks = null
10 | let killedAtTime = null
11 |
12 | const feat = new Feature("slayerBossTime")
13 | .addEvent(
14 | new Event(EventEnums.PACKET.CUSTOM.TICK, () => {
15 | serverTicks++
16 | })
17 | )
18 | .addEvent(
19 | new Event(EventEnums.PACKET.SERVER.SCOREBOARD, () => {
20 | spawnedAtTicks = serverTicks
21 | spawnedAtTime = Date.now()
22 | feat.update()
23 | }, /^Slay the boss\!$/)
24 | )
25 | .addSubEvent(
26 | new Event(EventEnums.PACKET.SERVER.SCOREBOARD, () => {
27 | killedAtTime = Date.now()
28 | killedAtTicks = serverTicks
29 | }, /^Boss slain\!$/),
30 | () => spawnedAtTime
31 | )
32 | .addSubEvent(
33 | new Event(EventEnums.PACKET.SERVER.CHAT, () => {
34 | if (!killedAtTime) killedAtTime = Date.now()
35 | if (!killedAtTicks) killedAtTicks = serverTicks
36 |
37 | new TextComponent(`${TextHelper.PREFIX} &aBoss Slain! Real Time&f: &6${((killedAtTime - spawnedAtTime) / 1000).toFixed(2)}s &aServer Time&f: &6${((killedAtTicks - spawnedAtTicks) * 0.05).toFixed(2)}s`)
38 | .setHover("show_text", "&cServer time might not be 100% accurate")
39 | .chat()
40 | serverTicks = 0
41 | spawnedAtTicks = 0
42 | spawnedAtTime = null
43 | killedAtTime = null
44 | killedAtTicks = null
45 | feat.update()
46 | }, /^ SLAYER QUEST COMPLETE\!$/),
47 | () => spawnedAtTime
48 | )
49 | .onUnregister(() => {
50 | serverTicks = 0
51 | spawnedAtTicks = 0
52 | spawnedAtTime = null
53 | killedAtTime = null
54 | killedAtTicks = null
55 | })
--------------------------------------------------------------------------------
/features/slayers/BossSpawnTime.js:
--------------------------------------------------------------------------------
1 | import { Event } from "../../core/Event"
2 | import EventEnums from "../../core/EventEnums"
3 | import Feature from "../../core/Feature"
4 | import { TextHelper } from "../../shared/TextHelper"
5 |
6 | let serverTicks = 0
7 | let startedAtTicks = 0
8 | let startedAtTime = Date.now()
9 |
10 | const feat = new Feature("slayerBossSpawnTime")
11 | .addEvent(
12 | new Event(EventEnums.PACKET.CUSTOM.TICK, () => {
13 | serverTicks++
14 | })
15 | )
16 | .addEvent(
17 | new Event(EventEnums.PACKET.SERVER.CHAT, () => {
18 | startedAtTicks = serverTicks
19 | startedAtTime = Date.now()
20 | feat.update()
21 | }, /^ SLAYER QUEST STARTED!$/)
22 | )
23 | .addSubEvent(
24 | new Event(EventEnums.PACKET.SERVER.SCOREBOARD, () => {
25 | new TextComponent(`${TextHelper.PREFIX} &aSlayer Boss Spawned! &aReal Time&f: &6${((Date.now() - startedAtTime) / 1000).toFixed(2)}s &aServer Time&f: &6${((serverTicks - startedAtTicks) * 0.05).toFixed(2)}s`)
26 | .setHover("show_text", "&cServer time might not be 100% accurate")
27 | .chat()
28 |
29 | serverTicks = 0
30 | startedAtTicks = 0
31 | startedAtTime = null
32 | feat.update()
33 | }, /^Slay the boss\!$/),
34 | () => startedAtTime
35 | )
36 | .onUnregister(() => {
37 | serverTicks = 0
38 | startedAtTicks = 0
39 | startedAtTime = null
40 | })
--------------------------------------------------------------------------------
/features/slayers/SlayerBossDisplay.js:
--------------------------------------------------------------------------------
1 | import { scheduleTask } from "../../core/CustomRegisters"
2 | import { Event } from "../../core/Event"
3 | import EventEnums from "../../core/EventEnums"
4 | import Feature from "../../core/Feature"
5 | import { addCommand } from "../../shared/Command"
6 | import DraggableGui from "../../shared/DraggableGui"
7 | import { RenderHelper } from "../../shared/Render"
8 | import { TextHelper } from "../../shared/TextHelper"
9 |
10 | const editGui = new DraggableGui("slayerBossDisplay").setCommandName("editslayerbossdisplay")
11 | const spawnedByRegex = /^Spawned by: (\w+)$/
12 | const entities = new HashMap()
13 |
14 | let currentBoss = {
15 | spawnedBy: null,
16 | timeEntity: null,
17 | hpEntity: null,
18 | id: null
19 | }
20 | let carryingUser = null
21 |
22 | const reset = () => {
23 | currentBoss = {
24 | spawnedBy: null,
25 | timeEntity: null,
26 | hpEntity: null,
27 | id: null
28 | }
29 | }
30 |
31 | editGui.onDraw(() => {
32 | Renderer.retainTransforms(true)
33 | Renderer.translate(editGui.getX(), editGui.getY())
34 | Renderer.scale(editGui.getScale())
35 |
36 | const time = "&c02:59"
37 | const spawnedBy = `&eSpawned by: &b${Player.getName()}`
38 | const name = "&c☠ &bVoidgloom Seraph IV &e64.2M&c❤"
39 |
40 | Renderer.drawStringWithShadow(time, Renderer.getStringWidth(spawnedBy.removeFormatting()) / 2, 0)
41 | Renderer.drawStringWithShadow(spawnedBy, 15, 10)
42 | Renderer.drawStringWithShadow(name, 0, 20)
43 |
44 | Renderer.retainTransforms(false)
45 | Renderer.finishDraw()
46 | })
47 |
48 | const feat = new Feature("slayerBossDisplay")
49 | .addEvent(
50 | new Event(EventEnums.FORGE.ENTITYJOIN, (mcEntity, entityId) => {
51 | scheduleTask(() => {
52 | const entityName = mcEntity./* getName */func_70005_c_()
53 | const match = entityName?.removeFormatting()?.match(spawnedByRegex)
54 | if (!match) return
55 |
56 | const [ _, spawnedBy ] = match
57 |
58 | // -1 -> timer (02:59)
59 | // -2 -> armrostand name (☠ Voidgloom Seraph IV 64.2M❤)
60 | // -3 -> actual entity (Enderman)
61 | const obj = {
62 | spawnedBy: entityName, // This name should never change therefor we can set it static
63 | timeEntity: World.getWorld()./* getEntityByID */func_73045_a(entityId - 1),
64 | hpEntity: World.getWorld()./* getEntityByID */func_73045_a(entityId - 2),
65 | id: entityId - 3
66 | }
67 | entities.put(entityId - 3, obj)
68 |
69 | // Prioritize user's boss
70 | if (spawnedBy === Player.getName() || carryingUser === spawnedBy.toLowerCase())
71 | currentBoss = obj
72 |
73 | feat.update()
74 | })
75 | })
76 | )
77 | .addSubEvent(
78 | new Event("renderEntity", (entity, _, pticks) => {
79 | if (entity.entity.func_145782_y() !== currentBoss?.id) return
80 | RenderHelper.drawEntityBox(
81 | entity.getX(),
82 | entity.getY(),
83 | entity.getZ(),
84 | entity.getWidth(),
85 | entity.getHeight(),
86 | 0, 255, 255, 255, 2, false, true, pticks
87 | )
88 | }),
89 | () => currentBoss
90 | )
91 | .addSubEvent(
92 | new Event("renderOverlay", () => {
93 | if (editGui.isOpen()) return
94 | // I am lazy
95 | if (!currentBoss.hpEntity) return
96 | if (currentBoss.hpEntity./* isDead */field_70128_L) {
97 | entities.clear()
98 | reset()
99 | feat.update()
100 | return
101 | }
102 |
103 | Renderer.retainTransforms(true)
104 | Renderer.translate(editGui.getX(), editGui.getY())
105 | Renderer.scale(editGui.getScale())
106 |
107 | const spawnedByName = currentBoss.spawnedBy
108 | const timeEntityName = currentBoss.timeEntity./* getName */func_70005_c_()
109 | const hpEntityName = currentBoss.hpEntity./* getName */func_70005_c_()
110 |
111 | Renderer.drawStringWithShadow(timeEntityName, Renderer.getStringWidth(spawnedByName.removeFormatting()) / 2, 0)
112 | Renderer.drawStringWithShadow(spawnedByName, 15, 10)
113 | Renderer.drawStringWithShadow(hpEntityName, 0, 20)
114 |
115 | Renderer.retainTransforms(false)
116 | Renderer.finishDraw()
117 | }),
118 | () => currentBoss
119 | )
120 | .addSubEvent(
121 | new Event("clicked", (_, __, mbtn, isDown) => {
122 | if (mbtn !== 2 || !isDown) return
123 | const entityId = Player.lookingAt().entity?.func_145782_y()
124 | if (!entityId || !entities.containsKey(entityId)) {
125 | if (currentBoss) reset() && feat.update()
126 | return
127 | }
128 |
129 | currentBoss = entities.get(entityId)
130 | feat.update()
131 | }),
132 | () => entities.size()
133 | )
134 | .onUnregister(() => {
135 | entities.clear()
136 | reset()
137 | })
138 |
139 | addCommand("slayercarry", "Set a user to the carry mode for Slayer Boss Display", (name) => {
140 | if (!name) {
141 | reset()
142 | carryingUser = null
143 | feat.update()
144 | ChatLib.chat(`${TextHelper.PREFIX} &cCarry user cleared.`)
145 | return
146 | }
147 |
148 | carryingUser = name.toLowerCase()
149 | ChatLib.chat(`${TextHelper.PREFIX} &aSuccessfully set user &b${name} &afor carry mode.`)
150 | })
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | // Dungeons
2 | import "./features/dungeons/BoxStarMobs"
3 | import "./features/dungeons/SecretsClickedBox"
4 | import "./features/dungeons/RunSplits"
5 | import "./features/dungeons/CroesusClicks"
6 | import "./features/dungeons/ExtraStats"
7 | import "./features/dungeons/CryptsDisplay"
8 | import "./features/dungeons/DeathsDisplay"
9 | import "./features/dungeons/MilestoneDisplay"
10 | import "./features/dungeons/PuzzleDisplay"
11 | import "./features/dungeons/MimicKilled"
12 | import "./features/dungeons/RemoveDmgTag"
13 | import "./features/dungeons/HideNoStarTag"
14 | import "./features/dungeons/SecretsSound"
15 | import "./features/dungeons/CroesusProfit"
16 | import "./features/dungeons/ChestProfit"
17 | import "./features/dungeons/BlazeSolver"
18 | import "./features/dungeons/BoulderSolver"
19 | import "./features/dungeons/CreeperBeamsSolver"
20 | import "./features/dungeons/ThreeWeirdosSolver"
21 | import "./features/dungeons/WaterBoardSolver"
22 | import "./features/dungeons/TriviaSolver"
23 | import "./features/dungeons/TicTacToeSolver"
24 | import "./features/dungeons/LividSolver"
25 | import "./features/dungeons/BossSplits"
26 | import "./features/dungeons/AutoRequeueDungeons"
27 | import "./features/dungeons/TeleportMazeSolver"
28 | import "./features/dungeons/RunsLogger"
29 | // Mining
30 | import "./features/mining/EmissaryWaypoints"
31 | import "./features/mining/PowderDisplay"
32 | import "./features/mining/ComissionDisplay"
33 | // Garden
34 | import "./features/garden/GardenDisplay"
35 | import "./features/garden/PestsDisplay"
36 | import "./features/garden/VisitorProfit"
37 | import "./features/garden/VisitorBzButton"
38 | // Gui
39 | import "./features/gui/CommandAliases"
40 | import "./features/gui/KeyShortcuts"
41 | import "./features/gui/CancelMessage"
42 | import "./features/gui/TitleMessage"
43 | // Kuudra
44 | import "./features/kuudra/KuudraSplits"
45 | import "./features/kuudra/CratesWaypoints"
46 | // Commands
47 | import "./features/commands/InventoryLog"
48 | // Rift
49 | import "./features/rift/BoxBerberis"
50 | import "./features/rift/MushroomTimer"
51 | import "./features/rift/WoodenButtons"
52 | import "./features/rift/GlyphRender"
53 | import "./features/rift/LavaMaze"
54 | import "./features/rift/Tubulator"
55 | import "./features/rift/EffigiesWaypoint"
56 | // Slayers
57 | import "./features/slayers/BossSlainTime"
58 | import "./features/slayers/BossSpawnTime"
59 | import "./features/slayers/SlayerBossDisplay"
60 | // Misc
61 | import "./features/misc/BlockOverlay"
62 | import "./features/misc/MiddleClickGuis"
63 | import "./features/misc/RemoveFrontView"
64 | import "./features/misc/NoDeathAnimation"
65 | import "./features/misc/NoLightning"
66 | import "./features/misc/ItemRarity"
67 | import "./features/misc/BonzoMaskInvincibility"
68 | import "./features/misc/PhoenixInvincibility"
69 | import "./features/misc/CopyChat"
70 | import "./features/misc/ChampionDisplay"
71 | import "./features/misc/EtherwarpOverlay"
72 | import "./features/misc/FactoryHelper"
73 | import "./features/misc/ArmorDisplay"
74 | import "./features/misc/WorldAgeDisplay"
75 | import "./features/misc/SystemTimeDisplay"
76 | import "./features/misc/RagnarokAxeCooldown"
77 | import "./features/misc/NoCursorReset"
78 | import "./features/misc/EnchantedBookDisplay"
79 | import "./features/misc/AttributeShardDisplay"
80 | import "./features/misc/ChatWaypoint"
81 | import "./features/misc/HideEmptyTooltip"
82 | import "./features/misc/InventoryButtons"
83 | import "./features/misc/EquipmentDisplay"
84 | import "./features/misc/RenderItems"
85 | import "./features/misc/SlotLocking"
86 | import "./features/misc/SearchBar"
87 | import "./features/misc/CultivatingDisplay"
88 | import "./features/misc/InventoryHud"
89 | import "./features/misc/CompactDisplay"
90 | import "./features/misc/DrillFuelDisplay"
91 | import "./features/misc/NoEndermanTeleport"
92 | import "./features/misc/InventoryHistory"
93 | import "./features/misc/QuiverDisplay"
94 | import "./features/misc/ToggleSprint"
95 | import "./features/misc/SmolderingPolarizationDisplay"
--------------------------------------------------------------------------------
/metadata.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Doc",
3 | "creator": "DocilElm",
4 | "description": "Quality of life features for hypixel skyblock",
5 | "version": "4.23.10",
6 | "entry": "index.js",
7 | "changelog": "",
8 | "requires": [
9 | "requestV2",
10 | "PromiseV2",
11 | "PogData",
12 | "Atomx",
13 | "DocGuiLib",
14 | "Elementa",
15 | "KeybindFix",
16 | "Amaterasu",
17 | "tska"
18 | ]
19 | }
--------------------------------------------------------------------------------
/shared/ChestMenu.js:
--------------------------------------------------------------------------------
1 | // Credits to https://chattriggers.com/modules/v/ChestMenu
2 |
3 | const InventoryBasic = net.minecraft.inventory.InventoryBasic
4 | const GuiChest = net.minecraft.client.gui.inventory.GuiChest
5 |
6 | export class ChestMenu {
7 | constructor(name, rows) {
8 | this.items = []
9 | this.slotAmount = rows * 9
10 | this.basicinv = new InventoryBasic(
11 | name.addColor(),
12 | true,
13 | this.slotAmount
14 | )
15 | this.gui = null
16 | }
17 |
18 | setTitle(title) {
19 | this.basicinv.func_110133_a(title.addColor())
20 | return this
21 | }
22 |
23 | setItem(idx, item) {
24 | if (idx < 0 || idx >= this.slotAmount) return
25 | this.items[idx] = item
26 | this.basicinv.func_70299_a(idx, item?.itemStack || null)
27 | return this
28 | }
29 |
30 | setItems(list) {
31 | this.clear()
32 |
33 | for (let idx = 0; idx < list.length; idx++) {
34 | let item = list[idx]
35 | this.setItem(idx, item)
36 | }
37 |
38 | return this
39 | }
40 |
41 | clear() {
42 | this.items = []
43 | this.basicinv.func_174888_l()
44 | return this
45 | }
46 |
47 | open() {
48 | if (!this.gui) {
49 | this.gui = new JavaAdapter(GuiChest, {
50 | // cancel all [keyTyped] inputs and only listen for
51 | // [ESC] and inventory key (Default is E so whenever E is pressed close gui)
52 | func_73869_a(_, keycode) {
53 | if (keycode === 1 || keycode === this.field_146297_k.field_71474_y.field_151445_Q.func_151463_i()) {
54 | Player.getPlayer().func_71053_j()
55 | }
56 | },
57 | // cancel [handleMouseInput] so the user cannot click in the gui
58 | func_146274_d() {}
59 | }, Player.getPlayer().field_71071_by, this.basicinv)
60 | }
61 | GuiHandler.openGui(this.gui)
62 | }
63 | }
--------------------------------------------------------------------------------
/shared/Command.js:
--------------------------------------------------------------------------------
1 | import config from "../config"
2 | import { TextHelper } from "./TextHelper"
3 |
4 | const commands = {}
5 |
6 | /**
7 | * @param {string} name
8 | * @param {string} description
9 | * @param {(...args: string) () => void} fn
10 | */
11 | export const addCommand = (name, description, fn) => {
12 | const component = new TextComponent(`&a- ${name} &b${description}`)
13 | .setHover("show_text", `Click to run /doc ${name}`)
14 | .setClick("run_command", `/doc ${name}`)
15 |
16 | commands[name] = {
17 | description,
18 | chat: () => component.chat(),
19 | fn
20 | }
21 | }
22 |
23 | addCommand("help", "Shows this list")
24 |
25 | register("command", (...args) => {
26 | if (!args?.[0]) return config().getConfig().openGui()
27 |
28 | if (args[0].toLowerCase() === "help") {
29 | ChatLib.chat(`${TextHelper.PREFIX} &aCommand List`)
30 | Object.keys(commands).forEach(k => {
31 | commands[k].chat()
32 | })
33 |
34 | return
35 | }
36 |
37 | const cmd = commands[args[0]]
38 | if (!cmd) return ChatLib.chat(`${TextHelper.PREFIX} &cInvalid command.`)
39 |
40 | cmd.fn?.(...args.slice(1))
41 | })
42 | .setTabCompletions((arg) => {
43 | if (arg.length > 1) return []
44 | const allCommands = Object.keys(commands)
45 | if (!arg[0]) return allCommands
46 |
47 | const curr = allCommands.find(it => it.toLowerCase().startsWith(arg[0]?.toLowerCase()))
48 | if (!curr) return []
49 |
50 | return [curr]
51 | })
52 | .setName("doc")
--------------------------------------------------------------------------------
/shared/DGlStateManager.js:
--------------------------------------------------------------------------------
1 | export class DGlStateManager {
2 | static pushMatrix() {
3 | GlStateManager.func_179094_E()
4 |
5 | return this
6 | }
7 |
8 | static popMatrix() {
9 | GlStateManager.func_179121_F()
10 |
11 | return this
12 | }
13 |
14 | static translate(x, y, z) {
15 | GlStateManager.func_179137_b(x, y, z)
16 |
17 | return this
18 | }
19 |
20 | static tryBlendFuncSeparate(srcFactor, dstFactor, srcFactorAlpha, dstFactorAlpha) {
21 | GlStateManager.func_179120_a(srcFactor, dstFactor, srcFactorAlpha, dstFactorAlpha)
22 |
23 | return this
24 | }
25 |
26 | static color(r, g, b, a) {
27 | GlStateManager.func_179131_c(r, g, b, a)
28 |
29 | return this
30 | }
31 |
32 | static bindTexture(int) {
33 | GlStateManager.func_179144_i(int)
34 | return this
35 | }
36 |
37 | static scale(x, y) {
38 | GlStateManager.func_179152_a(x, y || x, 1)
39 | return this
40 | }
41 |
42 | static enableBlend() {
43 | GlStateManager.func_179147_l()
44 |
45 | return this
46 | }
47 |
48 | static enableAlpha() {
49 | GlStateManager.func_179141_d()
50 |
51 | return this
52 | }
53 |
54 | static enableTexture2D() {
55 | GlStateManager.func_179098_w()
56 |
57 | return this
58 | }
59 |
60 | static enableDepth() {
61 | GlStateManager.func_179126_j()
62 |
63 | return this
64 | }
65 |
66 | static enableCull() {
67 | GlStateManager.func_179089_o()
68 |
69 | return this
70 | }
71 |
72 | static enableLighting() {
73 | GlStateManager.func_179145_e()
74 |
75 | return this
76 | }
77 |
78 | static disableTexture2D() {
79 | GlStateManager.func_179090_x()
80 |
81 | return this
82 | }
83 |
84 | static disableLighting() {
85 | GlStateManager.func_179140_f()
86 |
87 | return this
88 | }
89 |
90 | static disableAlpha() {
91 | GlStateManager.func_179118_c()
92 |
93 | return this
94 | }
95 |
96 | static disableBlend() {
97 | GlStateManager.func_179084_k()
98 |
99 | return this
100 | }
101 |
102 | static disableDepth() {
103 | GlStateManager.func_179097_i()
104 |
105 | return this
106 | }
107 |
108 | static disableCull() {
109 | GlStateManager.func_179129_p()
110 |
111 | return this
112 | }
113 |
114 | static resetColor() {
115 | GlStateManager.func_179117_G()
116 |
117 | return this
118 | }
119 | }
--------------------------------------------------------------------------------
/shared/EtherwarpHelper.js:
--------------------------------------------------------------------------------
1 | // Full credits to BloomCore
2 | // pretty much the entire logic of this was taken from it
3 |
4 | import Vec3 from "./Vec3"
5 |
6 | // If one of these blocks is above the targeted etherwarp block, it is a valid teleport.
7 | // However if the block itself is being targetted, then it is not a valid block to etherwarp to.
8 | const validEtherwarpFeetBlocks = new Set([
9 | "minecraft:air",
10 | "minecraft:fire",
11 | "minecraft:carpet",
12 | "minecraft:skull",
13 | "minecraft:lever",
14 | "minecraft:stone_button",
15 | "minecraft:wooden_button",
16 | "minecraft:torch",
17 | "minecraft:string",
18 | "minecraft:tripwire_hook",
19 | "minecraft:tripwire",
20 | "minecraft:rail",
21 | "minecraft:activator_rail",
22 | "minecraft:snow_layer",
23 | "minecraft:carrots",
24 | "minecraft:wheat",
25 | "minecraft:potatoes",
26 | "minecraft:nether_wart",
27 | "minecraft:pumpkin_stem",
28 | "minecraft:melon_stem",
29 | "minecraft:redstone_torch",
30 | "minecraft:redstone_wire",
31 | "minecraft:red_flower",
32 | "minecraft:yellow_flower",
33 | "minecraft:sapling",
34 | "minecraft:flower_pot",
35 | "minecraft:deadbush",
36 | "minecraft:tallgrass",
37 | "minecraft:ladder",
38 | "minecraft:double_plant",
39 | "minecraft:unpowered_repeater",
40 | "minecraft:powered_repeater",
41 | "minecraft:unpowered_comparator",
42 | "minecraft:powered_comparator",
43 | "minecraft:web",
44 | "minecraft:waterlily",
45 | "minecraft:water",
46 | "minecraft:lava",
47 | "minecraft:torch",
48 | "minecraft:vine",
49 | "minecraft:brown_mushroom",
50 | "minecraft:red_mushroom",
51 | "minecraft:piston_extension",
52 | ])
53 |
54 | export default class EtherwarpHelper {
55 | /**
56 | * - Checks whether the given block is a valid block to etherwarp onto
57 | * @param {Block} block
58 | * @returns {boolean}
59 | */
60 | static isValidEtherwarpBlock(block) {
61 | if (!block) return false
62 | if (!(block instanceof Block)) block = World.getBlockAt(...block)
63 | if (block.type.getID() == 0) return false
64 |
65 | // Checking the actual block to etherwarp ontop of
66 | // Can be at foot level, but not etherwarped onto directly.
67 | if (validEtherwarpFeetBlocks.has(block.type.getRegistryName())) return false
68 |
69 | // The block at foot level
70 | const blockAbove = World.getBlockAt(block.getX(), block.getY() + 1, block.getZ())
71 | if (!validEtherwarpFeetBlocks.has(blockAbove.type.getRegistryName())) return false
72 |
73 | // The block at head height
74 | const blockAboveAbove = World.getBlockAt(block.getX(), block.getY() + 2, block.getZ())
75 |
76 | return validEtherwarpFeetBlocks.has(blockAboveAbove.type.getRegistryName())
77 | }
78 |
79 | /**
80 | * @param {number[]} start
81 | * @param {number[]} end
82 | * @param {number} distance
83 | * @returns {Block?}
84 | */
85 | static traverseVoxels(start, end, distance = 60) {
86 | const direction = end.map((v, i) => v - start[i])
87 | const step = direction.map(a => Math.sign(a))
88 | const thing = direction.map(a => 1/a)
89 | const tDelta = thing.map((v, i) => Math.min(v * step[i], 1))
90 | const tMax = thing.map((v, i) => Math.abs((Math.floor(start[i]) + Math.max(step[i], 0) - start[i]) * v))
91 | const limit = distance + 20
92 |
93 | let startPos = start.map(it => Math.floor(it))
94 | let endPos = end.map(it => Math.floor(it))
95 |
96 | for (let idx = 0; idx < limit; idx++) {
97 | let block = World.getBlockAt(...startPos)
98 | if (block.type.getID() !== 0) return block
99 |
100 | if (startPos.every((v, i) => v == endPos[i])) break
101 |
102 | // Find the next direction to step in
103 | let minIdx = tMax.indexOf(Math.min(...tMax))
104 | tMax[minIdx] += tDelta[minIdx]
105 | startPos[minIdx] += step[minIdx]
106 | }
107 |
108 | return null
109 | }
110 |
111 | static getEtherwarpBlockSuccess(distance = 60) {
112 | let lookVec = Vec3.fromPitchYaw(Player.getPitch(), Player.getYaw()).multiply(distance)
113 | let startPos = [Player.getRenderX(), Player.getRenderY() + 1.54, Player.getRenderZ()]
114 | let endPos = lookVec.getComponents().map((v, i) => v + startPos[i])
115 |
116 | const ether = this.traverseVoxels(startPos, endPos, distance)
117 |
118 | return [this.isValidEtherwarpBlock(ether), ether]
119 | }
120 | }
--------------------------------------------------------------------------------
/shared/PuzzleRoomScanner.js:
--------------------------------------------------------------------------------
1 | import { scheduleTask } from "../core/CustomRegisters"
2 | import { Event } from "../core/Event"
3 | import Feature from "../core/Feature"
4 | import { TextHelper } from "./TextHelper"
5 |
6 | const listeners = []
7 | const listenersExit = []
8 | const listenersSch = []
9 | const offsetsToCheck = [
10 | [0, 16],
11 | [-16, 0],
12 | [0, -16],
13 | [16, 0]
14 | ]
15 | const cachedRotations = new Map()
16 |
17 | let lastIdx = null
18 |
19 | /**
20 | * - Runs the given function whenever the rotation
21 | * - is found for the current room
22 | * @param {(rotation: number, posIdx: number) => void} fn
23 | * @returns
24 | */
25 | export const onPuzzleRotation = (fn) => listeners.push(fn)
26 |
27 | /**
28 | * - Runs the given function whenever the rotation
29 | * - is found for the current room but schedules the task
30 | * - 2 server ticks after
31 | * @param {(rotation: number, posIdx: number) => void} fn
32 | * @returns
33 | */
34 | export const onPuzzleScheduledRotation = (fn) => listenersSch.push(fn)
35 |
36 | /**
37 | * - Runs the given function whenever the player leaves a room
38 | * @param {() => void} fn
39 | * @returns
40 | */
41 | export const onPuzzleRotationExit = (fn) => listenersExit.push(fn)
42 |
43 | // Big thank bloom
44 | const getPuzzleRotation = () => {
45 | const xIndex = Math.floor((Player.getX() + 200) / 32)
46 | const zIndex = Math.floor((Player.getZ() + 200) / 32)
47 | const centerX = xIndex * 32 - 200 + 15
48 | const centerZ = zIndex * 32 - 200 + 15
49 |
50 | let rotation = null
51 |
52 | for (let i = 0; i < offsetsToCheck.length; i++) {
53 | let [ dx, dz ] = offsetsToCheck[i]
54 | let [ rx, ry, rz ] = [ centerX + dx, 68, centerZ + dz ]
55 |
56 | let block = World.getBlockAt(rx, ry, rz)
57 | let bottomBlock = World.getBlockAt(rx, ry - 1, rz)
58 | let topBlock = World.getBlockAt(rx, ry + 1, rz)
59 |
60 | // early enter blood with blood door closed
61 | if (
62 | bottomBlock.type.getID() === 7 &&
63 | topBlock.type.getID() === 159 &&
64 | block.type.getID() !== 0
65 | ) return i * 90
66 |
67 | if (bottomBlock.type.getID() !== 7 || topBlock.type.getID() !== 0) continue
68 | if (block.type.getID() === 0) continue
69 |
70 | // If the rotation has already been set, there is more than one door
71 | if (rotation !== null) return
72 |
73 | rotation = i*90
74 | }
75 |
76 | return rotation
77 | }
78 |
79 | new Feature("puzzleRoomScanner", "catacombs")
80 | .addEvent(
81 | new Event("tick", () => {
82 | const posIdx = TextHelper.getDungeonsPosIndex()
83 |
84 | if (lastIdx && lastIdx !== posIdx) {
85 | for (let idx = 0; idx < listenersExit.length; idx++) {
86 | listenersExit[idx]()
87 | }
88 | }
89 |
90 | if (lastIdx === posIdx) return
91 |
92 | lastIdx = posIdx
93 | const rotation = cachedRotations.get(posIdx) ?? getPuzzleRotation()
94 | if (rotation == null) return
95 |
96 | if (!cachedRotations.has(posIdx)) cachedRotations.set(posIdx, rotation)
97 |
98 | for (let idx = 0; idx < listeners.length; idx++) {
99 | listeners[idx](rotation, posIdx)
100 | }
101 |
102 | scheduleTask(() => {
103 | for (let idx = 0; idx < listenersSch.length; idx++) {
104 | listenersSch[idx](rotation, posIdx)
105 | }
106 | }, 2)
107 | })
108 | )
109 | .onUnregister(() => {
110 | lastIdx = null
111 | cachedRotations.clear()
112 | })
--------------------------------------------------------------------------------