diff --git a/README.md b/README.md index 6fea1c1..fa40cb4 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ - [Setup](#setup) - [Commands](#commands) - [Features](#features) -
+
## Website @@ -37,22 +37,23 @@ You can also view this page on the VolcAddons [website](https://zhenga8533.githu ## Setup ### Before Installation: + 1. **Install Forge:** Ensure that you have [Forge](https://files.minecraftforge.net/net/minecraftforge/forge/index_1.8.9.html) installed to enable mod compatibility. 2. **Install ChatTriggers:** You'll need [ChatTriggers](https://www.chattriggers.com) installed for advanced in-game functionality. 3. **Choices:** Choose one of the two options below to follow. - ### Forge Installation: + 1. **Download:** Download the [VolcAddons-1.1.jar](https://raw.githubusercontent.com/zhenga8533/VolcAddons/main/forge/VolcAddons-1.1.jar) file. 2. **Integration:** Drag into Minecraft mods folder (NOT the CT modules folder). 3. **El Fin v1:** You should be good to go! The mod will alert you if there is ever a new release in which you can just run `/updateva`! - ### Manual Installation: + 1. **Download:** Get the zip file, available either on our [Discord server](https://discord.gg/ftxB4kG2tw) or through [GitHub Releases](https://github.com/zhenga8533/VolcAddons/releases). 2. **Unzip:** Extract the downloaded zip file. You should now see a folder named "VolcAddons". @@ -70,213 +71,217 @@ You can also view this page on the VolcAddons [website](https://zhenga8533.githu After you finish installing, go check out some other useful SkyBlock mods: https://sbmw.ca/mod-lists/skyblock-mod-list/!
- ## Commands + ### General Commands + - **Settings** - - `/va gui`: Allows user to move all active GUI locations. - - `/va help`: Prints out the command help menu in game chat. - - `/va settings`: Opens the settings' menu. - - `/va toggles`: Opens the setting toggles' menu. - - `/va version`: Checks if you are currently on latest version and prints out changelog. + - `/va gui`: Allows user to move all active GUI locations. + - `/va help`: Prints out the command help menu in game chat. + - `/va settings`: Opens the settings' menu. + - `/va toggles`: Opens the setting toggles' menu. + - `/va version`: Checks if you are currently on latest version and prints out changelog. - **Waypoints** - - `/va cat`: Controls Montezuma Soul Piece location waypoints. - - `/va enigma`: Controls Enigma Soul location waypoints. - - `/va npc`: Creates waypoints to user inputted rift NPCs. - - `/va zone`: Creates waypoints to user inputted rift locations. - - `/va waypoint`: Creates waypoints to user inputted coordinates. + - `/va cat`: Controls Montezuma Soul Piece location waypoints. + - `/va enigma`: Controls Enigma Soul location waypoints. + - `/va npc`: Creates waypoints to user inputted rift NPCs. + - `/va zone`: Creates waypoints to user inputted rift locations. + - `/va waypoint`: Creates waypoints to user inputted coordinates. - **Lists** - - `/va cd`: Tracks the cooldown of items using user inputted times. - - `/va blacklist`: Block users from using leader/party commands. - - `/va emotelist`: Set words to replace in user sent messages. - - `/va warplist`: Set locations in which Diana burrow warp uses. - - `/va whitelist`: Set player party invites to auto join. + - `/va cd`: Tracks the cooldown of items using user inputted times. + - `/va blacklist`: Block users from using leader/party commands. + - `/va emotelist`: Set words to replace in user sent messages. + - `/va warplist`: Set locations in which Diana burrow warp uses. + - `/va whitelist`: Set player party invites to auto join. - **Economy** - - `/va attribute`: Various calculations that deal with attribute values. - - `/va calc`: Various calculations that deal with general economical values. - - `/va nw`: Calculates the networth of a SkyBlock profile using custom API. + - `/va attribute`: Various calculations that deal with attribute values. + - `/va calc`: Various calculations that deal with general economical values. + - `/va nw`: Calculates the networth of a SkyBlock profile using custom API. - **Misc** - - `/va splits`: Prints out Kuudra splits' stats. - - `/sk`: Opens the SkyCrypt profile of inputted user. + - `/va splits`: Prints out Kuudra splits' stats. + - `/sk`: Opens the SkyCrypt profile of inputted user. ### Feature Commands + - **Stats** - - `/va stats`: Prints out SkyBlock tab stats. - - `/va pet`: Prints out current pet. - - `/va pt`: Prints out daily playtime. - - `/va sf`: Prints out current soulflow *(requires accessory in inventory)*. + - `/va stats`: Prints out SkyBlock tab stats. + - `/va pet`: Prints out current pet. + - `/va pt`: Prints out daily playtime. + - `/va sf`: Prints out current soulflow _(requires accessory in inventory)_. - **Status** - - `/va fps`: Prints out client's FPS. - - `/va tps`: Prints out server's TPS. - - `/va ping`: Prints out client's ping. - - `/va pitch`: Prints out player's exact pitch. - - `/va yaw`: Prints out player's exact yaw. -- **Leader** *(Used in party chat)* - - `?`: Toggles party all invite. - - `?invite [ign]`: Invites a player to the party. - - `?demote`: Demotes the sender. - - `?promote`: Promotes the sender. - - `?[1-7]`: Runs the specified join instance command. - - `?stream [num]`: Runs the stream command to open the party. - - `?transfer`: Transfers the party to sender. - - `?warp`: Warps party into lobby. -- **Party** *(Used in party chat)* - - `?8ball`: Calls upon the Magic 8 Ball. - - `?`: Flips a coin. - - `?`: Sends coordinates of players in patcher format. - - `?`: Rolls a 6 sided dice. - - `?`: Sends specified stat to party. - - `?help`: Prints out all party commands to chat. - - `?`: Forces users to go into main lobby. - - `?rps`: Jan, ken, pon. - - `?`: I am very quirky. -
+ - `/va fps`: Prints out client's FPS. + - `/va tps`: Prints out server's TPS. + - `/va ping`: Prints out client's ping. + - `/va pitch`: Prints out player's exact pitch. + - `/va yaw`: Prints out player's exact yaw. +- **Leader** _(Used in party chat)_ + - `?`: Toggles party all invite. + - `?invite [ign]`: Invites a player to the party. + - `?demote`: Demotes the sender. + - `?promote`: Promotes the sender. + - `?[1-7]`: Runs the specified join instance command. + - `?stream [num]`: Runs the stream command to open the party. + - `?transfer`: Transfers the party to sender. + - `?warp`: Warps party into lobby. +- **Party** _(Used in party chat)_ - `?8ball`: Calls upon the Magic 8 Ball. - `?`: Flips a coin. - `?`: Sends coordinates of players in patcher format. - `?`: Rolls a 6 sided dice. - `?`: Sends specified stat to party. - `?help`: Prints out all party commands to chat. - `?`: Forces users to go into main lobby. - `?rps`: Jan, ken, pon. - `?`: I am very quirky. +
## Features + ### General Features + - **General** - - Armor Display (`/moveArmor`) - - Equipment Display - - Remove Selfie Mode - - Render Waypoint - - Skill Tracker (`/moveSkills`, `/resetSkills`) + - Armor Display (`/moveArmor`) + - Equipment Display + - Remove Selfie Mode + - Render Waypoint + - Skill Tracker (`/moveSkills`, `/resetSkills`) - **Inventory** - - Searchbar (`/moveSearch`) - - Slot Binding (`/resetBinds`, `/saveBinds`, `/loadBinds`) + - Searchbar (`/moveSearch`) + - Slot Binding (`/resetBinds`, `/saveBinds`, `/loadBinds`) - **Server** - - Hide Far Entities - - Hide Close Players - - Hide All Particles - - Server Alert - - Server Status (`/moveStatus`) - - SkyBlock Stats Display (`/moveStats`) + - Hide Far Entities + - Hide Close Players + - Hide All Particles + - Server Alert + - Server Status (`/moveStatus`) + - SkyBlock Stats Display (`/moveStats`) - **Timer** - - Item Cooldown Alert (`/va cd`) - - Reminder Text - - Reminder Time + - Item Cooldown Alert (`/va cd`) + - Reminder Text + - Reminder Time - **Yapping** - - Autocorrect Commands - - Custom Emotes (`/va emote`) - - Discord Webhook - - Image Viewer + - Autocorrect Commands + - Custom Emotes (`/va emote`) + - Discord Webhook + - Image Viewer ### Party Features + - **General** - - Anti Ghost Party - - Auto Join Reparty - - Auto Transfer - - Guild Join Message - - Party Join Message - - Server Kick Announce - - Whitelist Rejoin (`/va wl`) + - Anti Ghost Party + - Auto Join Reparty + - Auto Transfer + - Guild Join Message + - Party Join Message + - Server Kick Announce + - Whitelist Rejoin (`/va wl`) - **Party Commands** - - Leader Chat Commands (`/va bl`) - - Party Chat Commands + - Leader Chat Commands (`/va bl`) + - Party Chat Commands ### Economy Features + - **General** - - Bits Alert - - Coin Tracker (`/moveCoins`, `/resetCoins`) + - Bits Alert + - Coin Tracker (`/moveCoins`, `/resetCoins`) - **Pricing** - - Container Value (`/moveContainer`) - - Item Price (`/moveValue`) + - Container Value (`/moveContainer`) + - Item Price (`/moveValue`) ### Combat Features + - **Bestiary** - - Bestiary GUI - - Broodmother Detect (`/moveBrood`) - - Kill Counter (`/moveKills`, `/resetKills`) + - Bestiary GUI + - Broodmother Detect (`/moveBrood`) + - Kill Counter (`/moveKills`, `/resetKills`) - **General** - - Combo Display (`/moveCombo`) - - Damage Tracker - - Low Health Alert - - Ragnarok Detection + - Combo Display (`/moveCombo`) + - Damage Tracker + - Low Health Alert + - Ragnarok Detection - **Gyrokinetic Wand** - - Cell Alignment Alert - - Cell Alignment Timer (`/moveGyro`) + - Cell Alignment Alert + - Cell Alignment Timer (`/moveGyro`) - **Slayer** - - Boss Announce - - Miniboss Announce - - Boss Highlight - - Miniboss Highlight - - Slayer Spawn Warning + - Boss Announce + - Miniboss Announce + - Boss Highlight + - Miniboss Highlight + - Slayer Spawn Warning ### Mining Features + - **Crystal Hollows** - - Wishing Compass Locator + - Wishing Compass Locator - **Jinx** - - 2x Powder Alert - - Powder Chest Detect (`/moveChest`) - - Powder Chest Hider - - Powder Tracker (`/movePowder`, `/resetPowder`) + - 2x Powder Alert + - Powder Chest Detect (`/moveChest`) + - Powder Chest Hider + - Powder Tracker (`/movePowder`, `/resetPowder`) ### Farming Features + - **General** - - Jacob Reward Highlight - - Farming Discord Webhook + - Jacob Reward Highlight + - Farming Discord Webhook - **Garden** - - Composter Display (`/moveCompost`) - - Garden Warp Override - - Garden Visitor Display (`/moveVisitors`) - - Next Visitor Display (`/moveNext`) + - Composter Display (`/moveCompost`) + - Garden Warp Override + - Garden Visitor Display (`/moveVisitors`) + - Next Visitor Display (`/moveNext`) - **Pests** - - Infestation Alert - - Pest Alert - - Plot Highlight - - Pesthunter Display (`/movePest`) - - Spray Display (`moveSpray`) + - Infestation Alert + - Pest Alert + - Plot Highlight + - Pesthunter Display (`/movePest`) + - Spray Display (`moveSpray`) ### Event Features + - **Great Spook** - - Math Teacher Solver - - Primal Fear Alert - - Primal Feat Highlight + - Math Teacher Solver + - Primal Fear Alert + - Primal Feat Highlight - **Inquisitor** - - Inquisitor Detect - - Inquisitor Announce - - Inquisitor Counter (`moveInq`, `/resetInq`) + - Inquisitor Detect + - Inquisitor Announce + - Inquisitor Counter (`moveInq`, `/resetInq`) - **Mythological Ritual** - - Diana Waypoint - - Diana Warp (`/va warplist`) - - Burrow Detection + - Diana Waypoint + - Diana Warp (`/va warplist`) + - Burrow Detection ### Crimson Isles + - **Fishing** - - Golden Fish Timer (`/moveGolden`) - - Mythic Creature Announce - - Mythic Creature Detect - - Trophy Fish Counter (`/moveTrophy`, `/resetTrophy`) + - Golden Fish Timer (`/moveGolden`) + - Mythic Creature Announce + - Mythic Creature Detect + - Trophy Fish Counter (`/moveTrophy`, `/resetTrophy`) - **Vanquisher** - - Vanquisher Announce - - Vanquisher Auto-Warp - - Vanquisher Counter (`/moveCounter`, `/resetCounter`) - - Vanquisher Detect (`/moveVanq`) + - Vanquisher Announce + - Vanquisher Auto-Warp + - Vanquisher Counter (`/moveCounter`, `/resetCounter`) + - Vanquisher Detect (`/moveVanq`) ### Dungeons + - **Star Detect** - - Star Mob Highlight + - Star Mob Highlight ### Kuudra + - **General** - - Crate Waypoints - - Kuudra Alerts - - Kuudra HP Display - - Kuudra Spawn Announce - - Kuudra Splits (`/moveSplits`, `/va splits`) - - Supply Pile Highlight + - Crate Waypoints + - Kuudra Alerts + - Kuudra HP Display + - Kuudra Spawn Announce + - Kuudra Splits (`/moveSplits`, `/va splits`) + - Supply Pile Highlight - **Profit** - - Kuudra Profit Display (`/moveKP`) - - Kuudra Profit Tracker (`/moveKPT`, `/resetKPT`) + - Kuudra Profit Display (`/moveKP`) + - Kuudra Profit Tracker (`/moveKPT`, `/resetKPT`) ### Rift + - **General** - - DDR Helper - - Enigma Soul Waypoint - - Montezuma Soul Waypoint + - DDR Helper + - Enigma Soul Waypoint + - Montezuma Soul Waypoint - **Vampire** - - Announce Mania Phase - - Effigy Waypoint - - Enlarge Impel Message - - Vampire Attack Display (`/moveVamp`) - - Vampire Hitbox - + - Announce Mania Phase + - Effigy Waypoint + - Enlarge Impel Message + - Vampire Attack Display (`/moveVamp`) + - Vampire Hitbox diff --git a/changelog.json b/changelog.json index dbef28c..548dd99 100644 --- a/changelog.json +++ b/changelog.json @@ -1,18 +1,9 @@ [ - "- Added `/va eggs`", - "- Added crosshair Y/P display", - "- Added bestiary counter world option", - "- Added gold toggle to stray alert", - "- Added more views in `/va gui`", - "- Added highlight colors in `/va gui`", - "- Changed right click to hide element in `/va gui`", - "- Changed zone and npc list format", - "- Changed egg waypoints to show in chat", - "- Changed party commands to work in AC", - "- Changed rand IDs to be parsed out", - "- Changed span of pt alerts", - "- Fixed Hypixel ?w counter", - "- Fixed worker highlight bricking", - "- Fixed ?w upload failing", - "- Fixed title canceling" -] \ No newline at end of file + "- Added pet level value", + "- Changed pick display to detect worlds", + "- Changed code structure", + "- Fixed undefined level display", + "- Fixed attribute value calc", + "- Fixed bazaar values", + "- Fixed enchant value" +] diff --git a/docs/_posts/2023-11-15-v2_8_0.md b/docs/_posts/2023-11-15-v2_8_0.md index f0be943..da8f5a7 100644 --- a/docs/_posts/2023-11-15-v2_8_0.md +++ b/docs/_posts/2023-11-15-v2_8_0.md @@ -11,14 +11,18 @@ author: Volcaronitee ## [Changelog](https://github.com/zhenga8533/VolcAddons/releases/tag/v2.8.0) (download by clicking [here](https://github.com/zhenga8533/VolcAddons/releases/download/v2.8.0/VolcAddons.zip)) {: .box-note} + #### New Features + - Added Great Spook stuff (under Combat category) - Added daily playtime to stats tracking - Added equipment display - Added god roll min price slider {: .box-warning} + #### Changes/Fixes + - Changed party command options to toggles - Changed some Diana shit to be less shit (but still shit) - Changed slot binding to work with custom keybinds @@ -39,5 +43,7 @@ author: Volcaronitee - Fixed Skyblock detection {: .box-error} + #### Deprecated + - Removed mouse recall diff --git a/docs/_posts/2023-11-17-v2_8_1.md b/docs/_posts/2023-11-17-v2_8_1.md index e4d57d9..ada3ffe 100644 --- a/docs/_posts/2023-11-17-v2_8_1.md +++ b/docs/_posts/2023-11-17-v2_8_1.md @@ -11,21 +11,27 @@ author: Volcaronitee ## [Changelog](https://github.com/zhenga8533/VolcAddons/releases/tag/v2.8.1) (download by clicking [here](https://github.com/zhenga8533/VolcAddons/releases/download/v2.8.1/VolcAddons.zip)) {: .box-note} + #### New Features + - Added pest features - Added spray display - Added yaw/pitch to stats display {: .box-warning} + #### Changes/Fixes + - Fixed playtime tracking in game menu - Fixed most Garden features - - Fixed Visitor display - - Fixed next visitor counter - - Fixed Jacob Highlight + - Fixed Visitor display + - Fixed next visitor counter + - Fixed Jacob Highlight - Fixed slot binding crash - Fixed container/item value rendering {: .box-error} + #### Deprecated + - None! diff --git a/docs/_posts/2023-11-28-v2_8_2.md b/docs/_posts/2023-11-28-v2_8_2.md index 9fc5549..aed53c1 100644 --- a/docs/_posts/2023-11-28-v2_8_2.md +++ b/docs/_posts/2023-11-28-v2_8_2.md @@ -11,7 +11,9 @@ author: Volcaronitee ## [Changelog](https://github.com/zhenga8533/VolcAddons/releases/tag/v2.8.2) (download by clicking [here](https://github.com/zhenga8533/VolcAddons/releases/download/v2.8.2/VolcAddons.zip)) {: .box-note} + #### New Features + - Added upgarde calc to '/va attribute' - Added pesthunter bonus display - Added new enchantments to value calc @@ -19,7 +21,9 @@ author: Volcaronitee - Added /va {: .box-warning} + #### Changes/Fixes + - Updated API and bestiary pathing - Updated '/va calc gdrag' logic - Changed next visitor to track 6th visitor @@ -30,5 +34,7 @@ author: Volcaronitee - Fixed '/va calc compost' {: .box-error} + #### Deprecated + - None! diff --git a/docs/_posts/2023-12-16-v2_8_3.md b/docs/_posts/2023-12-16-v2_8_3.md index d1dc8ae..95331d3 100644 --- a/docs/_posts/2023-12-16-v2_8_3.md +++ b/docs/_posts/2023-12-16-v2_8_3.md @@ -11,18 +11,22 @@ author: Volcaronitee ## [Changelog](https://github.com/zhenga8533/VolcAddons/releases/tag/v2.8.3) (download by clicking [here](https://github.com/zhenga8533/VolcAddons/releases/download/v2.8.3/VolcAddons.zip)) {: .box-note} + #### New Features + - Added wishing compass guestimator - Added party/guild join messages - Added container functionality to some menus - - Accessory Bag - - Sacks - - Wardrobe - - Vault - - Museum + - Accessory Bag + - Sacks + - Wardrobe + - Vault + - Museum {: .box-warning} + #### Changes/Fixes + - Changed container value to label enchanted books - Changed API backend to track over weekly price - Changed '/va cd' to track item id @@ -33,5 +37,7 @@ author: Volcaronitee - Fixed burrow detect spamming message twice {: .box-error} + #### Deprecated + - None! diff --git a/docs/_posts/2023-12-30-v2_8_4.md b/docs/_posts/2023-12-30-v2_8_4.md index c09be92..ed7d924 100644 --- a/docs/_posts/2023-12-30-v2_8_4.md +++ b/docs/_posts/2023-12-30-v2_8_4.md @@ -11,13 +11,17 @@ author: Volcaronitee ## [Changelog](https://github.com/zhenga8533/VolcAddons/releases/tag/v2.8.4) (download by clicking [here](https://github.com/zhenga8533/VolcAddons/releases/download/v2.8.4/VolcAddons.zip)) {: .box-note} + #### New Features + - Added fairy soul waypoints - Added `/va vl` (preset valuelist) - Created a website: `https://zhenga8533.github.io/VolcAddons/` (prettier README) {: .box-warning} + #### Changes/Fixes + - Changed uncommon command parsing to be less - Changed some GUI commands - Changed list printing @@ -29,5 +33,7 @@ author: Volcaronitee - Fixed symbols remaining in names when using commands {: .box-error} + #### Deprecated + - Removed `/va apex` diff --git a/docs/_posts/2024-01-06-v2_8_5.md b/docs/_posts/2024-01-06-v2_8_5.md index cd40da0..7450adb 100644 --- a/docs/_posts/2024-01-06-v2_8_5.md +++ b/docs/_posts/2024-01-06-v2_8_5.md @@ -11,7 +11,9 @@ author: Volcaronitee ## [Changelog](https://github.com/zhenga8533/VolcAddons/releases/tag/v2.8.5) (download by clicking [here](https://github.com/zhenga8533/VolcAddons/releases/download/v2.8.5/VolcAddons.zip)) {: .box-note} + #### New Features + - Added ignorelist (renamed blacklist) - Added spamlist - Added pest warp (`/pesttp`) @@ -21,7 +23,9 @@ author: Volcaronitee - Added toggle for join message when leader {: .box-warning} + #### Changes/Fixes + - Changed blacklist to auto kick - Changed warplist to dianalist - Changed file pathing @@ -31,5 +35,7 @@ author: Volcaronitee - Fixed emote replace loop {: .box-error} + #### Deprecated + - Removed API key diff --git a/docs/_posts/2024-01-20-v2_8_6.md b/docs/_posts/2024-01-20-v2_8_6.md index c42bcd9..035e782 100644 --- a/docs/_posts/2024-01-20-v2_8_6.md +++ b/docs/_posts/2024-01-20-v2_8_6.md @@ -11,7 +11,9 @@ author: Volcaronitee ## [Changelog](https://github.com/zhenga8533/VolcAddons/releases/tag/v2.8.6) (download by clicking [here](https://github.com/zhenga8533/VolcAddons/releases/download/v2.8.6/VolcAddons.zip)) {: .box-note} + #### New Features + - Added calculator to search bar - Added direction to status display - Added `/va attributelist` @@ -20,7 +22,9 @@ author: Volcaronitee - Added legion to stats display {: .box-warning} + #### Changes/Fixes + - Changed some auction API logic - Changed godroll tracking to only detect Kuudra pieces - Changed item value to include dye @@ -30,5 +34,7 @@ author: Volcaronitee - Fixed bits alert in cookie menu {: .box-error} + #### Deprecated + - None! diff --git a/docs/_posts/2024-02-04-v2_8_7.md b/docs/_posts/2024-02-04-v2_8_7.md index 4ad530a..441886f 100644 --- a/docs/_posts/2024-02-04-v2_8_7.md +++ b/docs/_posts/2024-02-04-v2_8_7.md @@ -11,13 +11,17 @@ author: Volcaronitee ## [Changelog](https://github.com/zhenga8533/VolcAddons/releases/tag/v2.8.7) (download by clicking [here](https://github.com/zhenga8533/VolcAddons/releases/download/v2.8.7/VolcAddons.zip)) {: .box-note} + #### New Features + - Added `/kv` (Kuudra view) - Added time until next level to skill tracker - Added setting to toggle text shadows {: .box-warning} + #### Changes/Fixes + - Changed auction API tracking - Changed function file pathing - Changed errors to print in console instead of chat @@ -29,5 +33,7 @@ author: Volcaronitee - Fixed master star on specific items {: .box-error} + #### Deprecated + - Removed unnecessary comments diff --git a/docs/_posts/2024-02-18-v2_8_8.md b/docs/_posts/2024-02-18-v2_8_8.md index 199735a..b4eaa7c 100644 --- a/docs/_posts/2024-02-18-v2_8_8.md +++ b/docs/_posts/2024-02-18-v2_8_8.md @@ -11,14 +11,18 @@ author: Volcaronitee ## [Changelog](https://github.com/zhenga8533/VolcAddons/releases/tag/v2.8.8) (download by clicking [here](https://github.com/zhenga8533/VolcAddons/releases/download/v2.8.8/VolcAddons.zip)) {: .box-note} + #### New Features + - Added Kuudra crate edit - Added extra details to `/kv` - Added auto `/kv` - Added Garden bounding box {: .box-warning} + #### Changes/Fixes + - Changed rand ID generation - Changed some time formatting - Changed Hypixel API call @@ -28,5 +32,7 @@ author: Volcaronitee - Fixed pet display with Montezuma {: .box-error} + #### Deprecated + - None! diff --git a/docs/_posts/2024-02-23-v2_8_9.md b/docs/_posts/2024-02-23-v2_8_9.md index 950d9d1..44f4758 100644 --- a/docs/_posts/2024-02-23-v2_8_9.md +++ b/docs/_posts/2024-02-23-v2_8_9.md @@ -11,13 +11,17 @@ author: Volcaronitee ## [Changelog](https://github.com/zhenga8533/VolcAddons/releases/tag/v2.8.9) (download by clicking [here](https://github.com/zhenga8533/VolcAddons/releases/download/v2.8.9/VolcAddons.zip)) {: .box-note} + #### New Features + - Added more toggles to `?w` - Added control to stack overlay horizontally - Added day counter to server status {: .box-warning} + #### Changes/Fixes + - Changed `/kv` to parse out bad rags - Fixed broken overlays (maybe) - Fixed auto `/kv` @@ -28,5 +32,7 @@ author: Volcaronitee - Fixed inquis announce {: .box-error} + #### Deprecated + - None! diff --git a/docs/_posts/2024-03-14-v2_8_10.md b/docs/_posts/2024-03-14-v2_8_10.md index d8e2379..8a06083 100644 --- a/docs/_posts/2024-03-14-v2_8_10.md +++ b/docs/_posts/2024-03-14-v2_8_10.md @@ -11,13 +11,17 @@ author: Volcaronitee ## [Changelog](https://github.com/zhenga8533/VolcAddons/releases/tag/v2.8.10) (download by clicking [here](https://github.com/zhenga8533/VolcAddons/releases/download/v2.8.10/VolcAddons.zip)) {: .box-note} + #### New Features + - Added true Jyrre time display - Added display chunk border hotkey - Added bestiary display {: .box-warning} + #### Changes/Fixes + - Changed ?rps invalid message - Changed visitor display to count to max - Fixed various features to work with tab widgets @@ -30,6 +34,8 @@ author: Volcaronitee - Fixed overlay defaulting to horizontal {: .box-error} + #### Deprecated + - Removed garden visitor warp - Removed next visitor display (merged) diff --git a/docs/_posts/2024-04-07-v2_9_0.md b/docs/_posts/2024-04-07-v2_9_0.md index b6fdf0c..1bd4c3c 100644 --- a/docs/_posts/2024-04-07-v2_9_0.md +++ b/docs/_posts/2024-04-07-v2_9_0.md @@ -11,12 +11,16 @@ author: Volcaronitee ## [Changelog](https://github.com/zhenga8533/VolcAddons/releases/tag/v2.9.0) (download by clicking [here](https://github.com/zhenga8533/VolcAddons/releases/download/v2.9.0/VolcAddons.zip)) {: .box-note} + #### New Features + - Added bingo card display - Added `/va gui reset` {: .box-warning} + #### Changes/Fixes + - Updated some overlay examples - Changed `/pesttp` to also track pest widget - Fixed fairy soul popping too many from list @@ -27,5 +31,7 @@ author: Volcaronitee - Fixed daily PT not resetting on weekly login {: .box-error} + #### Deprecated + - None! diff --git a/docs/_posts/2024-04-13-v2_9_1.md b/docs/_posts/2024-04-13-v2_9_1.md index f3c06e7..c7198a0 100644 --- a/docs/_posts/2024-04-13-v2_9_1.md +++ b/docs/_posts/2024-04-13-v2_9_1.md @@ -11,18 +11,24 @@ author: Volcaronitee ## [Changelog](https://github.com/zhenga8533/VolcAddons/releases/tag/v2.9.1) (download by clicking [here](https://github.com/zhenga8533/VolcAddons/releases/download/v2.9.1/VolcAddons.zip)) {: .box-note} + #### New Features + - Added commissions display - Added commission waypoints - Added corpse announce - Added fossil helper {: .box-warning} + #### Changes/Fixes + - Changed mana drain range to display a ryōiki tenkai - Changed powder tracker to also track Glacite - Fixed powder tracker {: .box-error} + #### Deprecated + - Removed 2x Powder Webhook diff --git a/docs/_posts/2024-04-18-v2_9_2.md b/docs/_posts/2024-04-18-v2_9_2.md index 46f3e97..1c91229 100644 --- a/docs/_posts/2024-04-18-v2_9_2.md +++ b/docs/_posts/2024-04-18-v2_9_2.md @@ -11,14 +11,18 @@ author: Volcaronitee ## [Changelog](https://github.com/zhenga8533/VolcAddons/releases/tag/v2.9.2) (download by clicking [here](https://github.com/zhenga8533/VolcAddons/releases/download/v2.9.2/VolcAddons.zip)) {: .box-note} + #### New Features + - Added Hoppity event experimental features - Added widget display (`/va wgl`) - Added `/missingSkins` - Added corpse waypoints {: .box-warning} + #### Changes/Fixes + - Compacted some chat messages to be 1 message - Changed fossil finder to the worst Leetcode algorithm - Changed chat waypoints to detect item @@ -29,5 +33,7 @@ author: Volcaronitee - Fixed powder tracker timer {: .box-error} + #### Deprecated + - Removed bestiary display (`/va wgl add bestiary`) diff --git a/docs/_posts/2024-04-24-v2_9_3.md b/docs/_posts/2024-04-24-v2_9_3.md index 369ef1c..6c07b37 100644 --- a/docs/_posts/2024-04-24-v2_9_3.md +++ b/docs/_posts/2024-04-24-v2_9_3.md @@ -11,13 +11,17 @@ author: Volcaronitee ## [Changelog](https://github.com/zhenga8533/VolcAddons/releases/tag/v2.9.3) (download by clicking [here](https://github.com/zhenga8533/VolcAddons/releases/download/v2.9.3/VolcAddons.zip)) {: .box-note} + #### New Features + - Added more Hoppity features - Added wardrobe hotkeys - Added `/va pl` (add party command prefixes) {: .box-warning} + #### Changes/Fixes + - Changed comm waypoint to optional show line - Changed fossil helper to bug out less - Changed skill tracker to use container menu @@ -27,5 +31,7 @@ author: Volcaronitee - Fixed first title sometimes not rendering {: .box-error} + #### Deprecated + - Removed total trophy fish overlay diff --git a/docs/_posts/2024-04-28-v2_9_4.md b/docs/_posts/2024-04-28-v2_9_4.md index 4e204a9..9299bcf 100644 --- a/docs/_posts/2024-04-28-v2_9_4.md +++ b/docs/_posts/2024-04-28-v2_9_4.md @@ -11,13 +11,17 @@ author: Volcaronitee ## [Changelog](https://github.com/zhenga8533/VolcAddons/releases/tag/v2.9.4) (download by clicking [here](https://github.com/zhenga8533/VolcAddons/releases/download/v2.9.4/VolcAddons.zip)) {: .box-note} + #### New Features + - Added option to render overlay background - Added choco egg timers - Added `/viewrecipe` button in GUI {: .box-warning} + #### Changes/Fixes + - Changed egg waypoints to dissapear on loot - Changed `/pesttp` detection - Changed garden box to be red/green if pests/spray @@ -28,5 +32,7 @@ author: Volcaronitee - Fixed overlay death {: .box-error} + #### Deprecated + - None! diff --git a/docs/_posts/2024-05-02-v2_9_5.md b/docs/_posts/2024-05-02-v2_9_5.md index 10b429f..ecfd178 100644 --- a/docs/_posts/2024-05-02-v2_9_5.md +++ b/docs/_posts/2024-05-02-v2_9_5.md @@ -11,13 +11,17 @@ author: Volcaronitee ## [Changelog](https://github.com/zhenga8533/VolcAddons/releases/tag/v2.9.5) (download by clicking [here](https://github.com/zhenga8533/VolcAddons/releases/download/v2.9.5/VolcAddons.zip)) {: .box-note} + #### New Features + - Added storage preview - Added more data to Chocolate Factory - Added pick display {: .box-warning} + #### Changes/Fixes + - Changed overlay to default with background - Changed equipment display to be persistant - Changed searchbar to work with containers @@ -29,6 +33,8 @@ author: Volcaronitee - Fixed choco display not updating if highlight is off {: .box-error} + #### Deprecated + - Removed Broodmother overlay - Removed Stats from stats toggles (/va wgl add stats) diff --git a/docs/_posts/2024-05-05-v2_9_6.md b/docs/_posts/2024-05-05-v2_9_6.md index 9d35ce8..02c119f 100644 --- a/docs/_posts/2024-05-05-v2_9_6.md +++ b/docs/_posts/2024-05-05-v2_9_6.md @@ -11,13 +11,17 @@ author: Volcaronitee ## [Changelog](https://github.com/zhenga8533/VolcAddons/releases/tag/v2.9.6) (download by clicking [here](https://github.com/zhenga8533/VolcAddons/releases/download/v2.9.6/VolcAddons.zip)) {: .box-note} + #### New Features + - Added attribute abbreviations - Added tower and barn data to choco - Added sold auction highlight {: .box-warning} + #### Changes/Fixes + - Changed armor/equip display to show empty - Changed time formatting - Changed pests features to be more reliable @@ -30,5 +34,7 @@ author: Volcaronitee - Fixed world not being set properly {: .box-error} + #### Deprecated + - None! diff --git a/docs/_posts/2024-05-12-v2_9_7.md b/docs/_posts/2024-05-12-v2_9_7.md index 41c04c5..0af5b87 100644 --- a/docs/_posts/2024-05-12-v2_9_7.md +++ b/docs/_posts/2024-05-12-v2_9_7.md @@ -11,7 +11,9 @@ author: Volcaronitee ## [Changelog](https://github.com/zhenga8533/VolcAddons/releases/tag/v2.9.7) (download by clicking [here](https://github.com/zhenga8533/VolcAddons/releases/download/v2.9.7/VolcAddons.zip)) {: .box-note} + #### New Features + - Added calendar real time - Added inventory buttons (`/va buttons`) - Added bestiary tracker @@ -20,7 +22,9 @@ author: Volcaronitee - Reworked autocomplete {: .box-warning} + #### Changes/Fixes + - Changed overlay boxes to have outlines - Changed equipment display to also display buttons - Changed slot binding commands (`/va binds`) @@ -37,7 +41,9 @@ author: Volcaronitee - Fixed no pet display {: .box-error} + #### Deprecated + - Removed `/va test` - Removed `/va be` - Removed Garden webhook diff --git a/docs/_posts/2024-05-17-v2_9_8.md b/docs/_posts/2024-05-17-v2_9_8.md index f1587cd..019e373 100644 --- a/docs/_posts/2024-05-17-v2_9_8.md +++ b/docs/_posts/2024-05-17-v2_9_8.md @@ -11,15 +11,19 @@ author: Volcaronitee ## [Changelog](https://github.com/zhenga8533/VolcAddons/releases/tag/v2.9.8) (download by clicking [here](https://github.com/zhenga8533/VolcAddons/releases/download/v2.9.8/VolcAddons.zip)) {: .box-note} + #### New Features + - Added socket beta testing [WIP] - - Alloy tracking? - - Cache ?w from imgur? - - Mining event tracking? - - Users tracking? + - Alloy tracking? + - Cache ?w from imgur? + - Mining event tracking? + - Users tracking? {: .box-warning} + #### Changes/Fixes + - Changed calendar time to track icon - Changed worker to calc Jackrabbit/Tower - Changed overlay to show border @@ -33,5 +37,7 @@ author: Volcaronitee - Fixed dict list printing {: .box-error} + #### Deprecated + - None! diff --git a/docs/_posts/2024-05-24-v2_9_9.md b/docs/_posts/2024-05-24-v2_9_9.md index e389d10..f7348bd 100644 --- a/docs/_posts/2024-05-24-v2_9_9.md +++ b/docs/_posts/2024-05-24-v2_9_9.md @@ -11,21 +11,25 @@ author: Volcaronitee ## [Changelog](https://github.com/zhenga8533/VolcAddons/releases/tag/v2.9.9) (download by clicking [here](https://github.com/zhenga8533/VolcAddons/releases/download/v2.9.9/VolcAddons.zip)) {: .box-note} + #### New Features + - Added global toggle - Added socket toggle - Added additional mining event checks - Added `/va [npc, zone]` scraped info - Added `/va preview` - Added more container value calcs - - Auction - - Bazaar - - Composter + - Auction + - Bazaar + - Composter - Added dungeon chest profit - Added max supercraft {: .box-warning} + #### Changes/Fixes + - Changed rabbit highlight to be selectable - Changed chocolate factory to support 2.0 - Changed storage preview to be hoverable @@ -37,7 +41,9 @@ author: Volcaronitee - Fixed egg reset {: .box-error} + #### Deprecated + - Removed image viewer - Removed Gyrokinetic features - Removed Jacob highlight diff --git a/docs/_posts/2024-05-29-v2_9_10.md b/docs/_posts/2024-05-29-v2_9_10.md index 68c338f..fb5af5d 100644 --- a/docs/_posts/2024-05-29-v2_9_10.md +++ b/docs/_posts/2024-05-29-v2_9_10.md @@ -11,13 +11,17 @@ author: Volcaronitee ## [Changelog](https://github.com/zhenga8533/VolcAddons/releases/tag/v2.9.10) (download by clicking [here](https://github.com/zhenga8533/VolcAddons/releases/download/v2.9.10/VolcAddons.zip)) {: .box-note} + #### New Features + - Added `/va rabbits` - Added egg tracking (rose gold = new spawn) - Added collected egg spawns {: .box-warning} + #### Changes/Fixes + - Changed word deletion to be less - Fixed worker rendering and calc - Fixed tower calc @@ -25,5 +29,7 @@ author: Volcaronitee - Fixed egg timer reset {: .box-error} + #### Deprecated + - None! diff --git a/docs/_posts/2024-06-05-v2_9_11.md b/docs/_posts/2024-06-05-v2_9_11.md index 6bea5e7..5c195fe 100644 --- a/docs/_posts/2024-06-05-v2_9_11.md +++ b/docs/_posts/2024-06-05-v2_9_11.md @@ -11,13 +11,17 @@ author: Volcaronitee ## [Changelog](https://github.com/zhenga8533/VolcAddons/releases/tag/v2.9.11) (download by clicking [here](https://github.com/zhenga8533/VolcAddons/releases/download/v2.9.11/VolcAddons.zip)) {: .box-note} + #### New Features + - Added `/VATest` - Added stray rabbit alert - Added playtime alerts {: .box-warning} + #### Changes/Fixes + - Changed some num format to be compact - Changed calendar time to work with forge - Fixed worker highlight @@ -32,5 +36,7 @@ author: Volcaronitee - Fixed binding working outside inv {: .box-error} + #### Deprecated + - None! diff --git a/docs/_posts/2024-06-09-v2_9_12.md b/docs/_posts/2024-06-09-v2_9_12.md index dbd9784..b4ac420 100644 --- a/docs/_posts/2024-06-09-v2_9_12.md +++ b/docs/_posts/2024-06-09-v2_9_12.md @@ -11,7 +11,9 @@ author: Volcaronitee ## [Changelog](https://github.com/zhenga8533/VolcAddons/releases/tag/v2.9.12) (download by clicking [here](https://github.com/zhenga8533/VolcAddons/releases/download/v2.9.12/VolcAddons.zip)) {: .box-note} + #### New Features + - Added `/va eggs` - Added crosshair Y/P display - Added bestiary counter world option @@ -20,7 +22,9 @@ author: Volcaronitee - Added highlight colors in `/va gui` {: .box-warning} + #### Changes/Fixes + - Changed right click to hide element in `/va gui` - Changed zone and npc list format - Changed egg waypoints to show in chat @@ -33,5 +37,7 @@ author: Volcaronitee - Fixed title canceling {: .box-error} + #### Deprecated + - None! diff --git a/docs/_posts/2024-06-28-v2_9_13.md b/docs/_posts/2024-06-28-v2_9_13.md new file mode 100644 index 0000000..79fe1eb --- /dev/null +++ b/docs/_posts/2024-06-28-v2_9_13.md @@ -0,0 +1,34 @@ +--- +layout: post +title: v2.9.13 Price +gh-repo: zhenga8533/VolcAddons +gh-badge: [star, fork, follow] +tags: [release] +comments: true +author: Volcaronitee +--- + +## [Changelog](https://github.com/zhenga8533/VolcAddons/releases/tag/v2.9.13) (download by clicking [here](https://github.com/zhenga8533/VolcAddons/releases/download/v2.9.13/VolcAddons.zip)) + +{: .box-note} + +#### New Features + +- Added pet level value + +{: .box-warning} + +#### Changes/Fixes + +- Changed pick display to detect worlds +- Changed code structure +- Fixed undefined level display +- Fixed attribute value calc +- Fixed bazaar values +- Fixed enchant value + +{: .box-error} + +#### Deprecated + +- None! diff --git a/docs/faq.md b/docs/faq.md index defb98a..8ad6514 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -8,26 +8,24 @@ subtitle: A list of good questions and bad answers. The module was getting too large and I did not want to annoy them with verifying it. You can still use the version on ChatTriggers, but I just won't be updating that one. - **2. LF Rats** There will not be any rats (I think), but I am as trustworthy as the next person so take that what you will. - **3. Setup did not work** - Check if you placed correct folder in correct location. Run `/ct files`, click modules, click VolcAddons, and verify you see multiple files and folders. - Make sure you are using ChatTriggers v2.2.0 -- Run `/ct console js` and if there is red text please open a discord thread [here](https://discord.gg/ftxB4kG2tw). +- Try the steps provided [here](https://github.com/ChatTriggers/ChatTriggers/wiki/Fixing-broken-imports). +- Run `/ct console js` and if there is red text please open a discord thread [here](https://discord.gg/ftxB4kG2tw). **4. `"Missing arguments! Usage: /viewauction "`** - Another mod is turning `/va` into a shortcut for `/viewauction`. You should still be able to use `/volc`. - **5. Don't see your issue or want to report one?** -Please open a thread on the VolcAddons Discord server: [here](https://discord.gg/ftxB4kG2tw). \ No newline at end of file +Please open a thread on the VolcAddons Discord server: [here](https://discord.gg/ftxB4kG2tw). diff --git a/docs/setup.md b/docs/setup.md index 53148e1..054ac98 100644 --- a/docs/setup.md +++ b/docs/setup.md @@ -5,22 +5,25 @@ subtitle: Installation Guide (2 methods) --- ### Before Installation: + 1. **Install Forge:** Ensure that you have [Forge](https://files.minecraftforge.net/net/minecraftforge/forge/index_1.8.9.html) installed to enable mod compatibility. 2. **Install ChatTriggers:** You'll need [ChatTriggers](https://www.chattriggers.com) installed for advanced in-game functionality. -3. **Choices:** Choose one of the two options below to follow. +3. **Fixing Broken Imports:**: Follow all the steps listed [here](https://github.com/ChatTriggers/ChatTriggers/wiki/Fixing-broken-imports). +4. **Choices:** Choose one of the two options below to follow. ### Forge Installation: + 1. **Download:** Download the [VolcAddons-1.0.jar](https://raw.githubusercontent.com/zhenga8533/VolcAddons/main/forge/VolcAddons-1.0.jar) file. 2. **Integration:** Drag into Minecraft mods folder (NOT the CT modules folder). 3. **El Fin v1:** You should be good to go! The mod will alert you if there is ever a new release in which you can just run `/updateva`! - ### Manual Installation: + 1. **Download:** Get the zip file, available either on our [Discord server](https://discord.gg/ftxB4kG2tw) or through [GitHub Releases](https://github.com/zhenga8533/VolcAddons/releases). 2. **Unzip:** Extract the downloaded zip file. You should now see a folder named "VolcAddons". @@ -35,4 +38,4 @@ subtitle: Installation Guide (2 methods) 7. **El Fin v2:** Now just type `/ct load` and your job is done! -**After you finish installing, go check out some other useful [SkyBlock mods](https://sbmw.ca/mod-lists/skyblock-mod-list/)!** \ No newline at end of file +**After you finish installing, go check out some other useful [SkyBlock mods](https://sbmw.ca/mod-lists/skyblock-mod-list/)!** diff --git a/features/combat/Bestiary.js b/features/combat/Bestiary.js index bf6a629..f316a64 100644 --- a/features/combat/Bestiary.js +++ b/features/combat/Bestiary.js @@ -3,44 +3,55 @@ */ import request from "../../../requestV2"; -import Settings from "../../utils/Settings"; import { BOLD, GOLD, GRAY, GREEN, LOGO, RED, WHITE } from "../../utils/Constants"; -import { formatTime, romanToNum } from "../../utils/functions/format"; -import { registerWhen } from "../../utils/RegisterTils"; import { data } from "../../utils/Data"; - +import { formatTime } from "../../utils/functions/format"; /** * Makes a PULL request to get bestiary data from the player's info using the Hypixel API. */ let bestiaryApi = undefined; function updateBestiary(profileId, args) { - if (profileId === undefined) return; - const callback = bestiaryApi === undefined; + if (profileId === undefined) return; + const callback = bestiaryApi === undefined; - // Make an API request to Hypixel API to get the player's bestiary data from their profile. - request({ - url: `https://api.hypixel.net/v2/skyblock/profile?key=DEPRECATED&profile=${profileId}`, - json: true - }).then(response => { - // Update the 'bestiary' variable with the bestiary data from the API response. - bestiaryApi = response.profile.members[data.lastID]?.bestiary?.kills; - Object.keys(bestiary).forEach(key => bestiary[key].updateKills()); - if (callback) getBestiary(args); - }).catch(err => console.error(`VolcAddons: ${err.cause ?? err}`)); + // Make an API request to Hypixel API to get the player's bestiary data from their profile. + request({ + url: `https://api.hypixel.net/v2/skyblock/profile?key=DEPRECATED&profile=${profileId}`, + json: true, + }) + .then((response) => { + // Update the 'bestiary' variable with the bestiary data from the API response. + bestiaryApi = response.profile.members[data.lastID]?.bestiary?.kills; + Object.keys(bestiary).forEach((key) => bestiary[key].updateKills()); + if (callback) getBestiary(args); + }) + .catch((err) => console.error(`VolcAddons: ${err.cause ?? err}`)); } /** * Variable and class to track mob bestiary data. */ const KILL_BRACKETS = [ - [20, 40, 60, 100, 200, 400, 800, 1400, 2000, 3000, 6000, 12000, 20000, 30000, 40000, 50000, 60000, 72000, 86000, 100000, 200000, 400000, 600000, 800000, 1000000], - [5, 10, 15, 25, 50, 100, 200, 350, 500, 750, 1500, 3000, 5000, 7500, 10000, 12500, 15000, 18000, 21500, 25000, 50000, 100000, 150000, 200000, 250000], - [4, 8, 12, 16, 20, 40, 80, 140, 200, 300, 600, 1200, 2000, 3000, 4000, 5000, 6000, 7200, 8600, 10000, 20000, 40000, 60000, 80000, 100000], - [2, 4, 6, 10, 15, 20, 25, 35, 50, 75, 150, 300, 500, 750, 1000, 1350, 1650, 2000, 2500, 3000, 5000, 10000, 15000, 20000, 25000], - [1, 2, 3, 5, 7, 10, 20, 25, 30, 60, 120, 200, 300, 400, 500, 600, 720, 860, 1000, 2000, 4000, 6000, 8000, 10000], - [1, 2, 3, 5, 7, 9, 14, 17, 21, 25, 50, 80, 125, 175, 250, 325, 425, 525, 625, 750, 1500, 3000, 4500, 6000, 7500], - [1, 2, 3, 5, 7, 9, 11, 14, 17, 20, 30, 40, 55, 75, 100, 150, 200, 275, 375, 500, 1000, 1500, 2000, 2500, 3000] + [ + 20, 40, 60, 100, 200, 400, 800, 1400, 2000, 3000, 6000, 12000, 20000, 30000, 40000, 50000, 60000, 72000, 86000, + 100000, 200000, 400000, 600000, 800000, 1000000, + ], + [ + 5, 10, 15, 25, 50, 100, 200, 350, 500, 750, 1500, 3000, 5000, 7500, 10000, 12500, 15000, 18000, 21500, 25000, 50000, + 100000, 150000, 200000, 250000, + ], + [ + 4, 8, 12, 16, 20, 40, 80, 140, 200, 300, 600, 1200, 2000, 3000, 4000, 5000, 6000, 7200, 8600, 10000, 20000, 40000, + 60000, 80000, 100000, + ], + [ + 2, 4, 6, 10, 15, 20, 25, 35, 50, 75, 150, 300, 500, 750, 1000, 1350, 1650, 2000, 2500, 3000, 5000, 10000, 15000, + 20000, 25000, + ], + [1, 2, 3, 5, 7, 10, 20, 25, 30, 60, 120, 200, 300, 400, 500, 600, 720, 860, 1000, 2000, 4000, 6000, 8000, 10000], + [1, 2, 3, 5, 7, 9, 14, 17, 21, 25, 50, 80, 125, 175, 250, 325, 425, 525, 625, 750, 1500, 3000, 4500, 6000, 7500], + [1, 2, 3, 5, 7, 9, 11, 14, 17, 20, 30, 40, 55, 75, 100, 150, 200, 275, 375, 500, 1000, 1500, 2000, 2500, 3000], ]; /** @@ -54,43 +65,43 @@ const KILL_BRACKETS = [ * @param {Number} time - The time required to kill the mob. */ class Mob { - constructor(names, levels, bracket, maxLevel, time) { - this.names = names; - this.levels = levels; - this.bracket = KILL_BRACKETS[bracket - 1]; - this.maxLevel = maxLevel - 1; - this.level = 0; - this.kills = 0; - this.next = 0; - this.time = time; - } - - /** - * Updates the number of kills, current level, and time needed for the next kill. - * - * @memberof Mob - */ - updateKills() { - // Calculate total kills by iterating through all the names and levels - this.names.forEach(name => { - this.levels.forEach(level => { - let kills = bestiaryApi[`${name}_${level}`] || 0; - this.kills += kills; - }); - }); + constructor(names, levels, bracket, maxLevel, time) { + this.names = names; + this.levels = levels; + this.bracket = KILL_BRACKETS[bracket - 1]; + this.maxLevel = maxLevel - 1; + this.level = 0; + this.kills = 0; + this.next = 0; + this.time = time; + } - // Determine the current level based on the kill count and bracket - for (let i = this.bracket.length - 1; i >= 0; i--) { - if (this.kills > this.bracket[i]) { - this.level = i; - break; - } - } + /** + * Updates the number of kills, current level, and time needed for the next kill. + * + * @memberof Mob + */ + updateKills() { + // Calculate total kills by iterating through all the names and levels + this.names.forEach((name) => { + this.levels.forEach((level) => { + let kills = bestiaryApi[`${name}_${level}`] || 0; + this.kills += kills; + }); + }); - // Calculate the number of kills needed for the next level and the time required for it - this.next = Math.max(this.bracket[Math.min(this.level + 1, this.maxLevel - 1)] - this.kills, 0); - this.nextTime = this.next * this.time; + // Determine the current level based on the kill count and bracket + for (let i = this.bracket.length - 1; i >= 0; i--) { + if (this.kills > this.bracket[i]) { + this.level = i; + break; + } } + + // Calculate the number of kills needed for the next level and the time required for it + this.next = Math.max(this.bracket[Math.min(this.level + 1, this.maxLevel - 1)] - this.kills, 0); + this.nextTime = this.next * this.time; + } } /** @@ -98,231 +109,333 @@ class Mob { * "Mob Name": [[entities], [levels], bracket, max level, player kills] */ const bestiary = { - // Your Island - "Creeper": new Mob(["creeper"], [1], 1, 5, 5), - "Enderman": new Mob(["enderman"], [1, 15], 1, 5, 5), - "Skeleton": new Mob(["skeleton"], [1, 15], 1, 5, 5), - "Slime": new Mob(["slime"], [1], 1, 5, 5), - "Spider": new Mob(["spider"], [1, 15], 1, 5, 5), - "Witch": new Mob(["witch"], [1, 15], 1, 5, 60), - "Zombie": new Mob(["zombie"], [1, 15], 1, 5, 5), - // Hub - "Crypt Ghoul": new Mob(["unburried_zombie"], [30], 1, 15, 0.5), - "Golden Ghoul": new Mob(["unburried_zombie"], [60], 3, 15, 5), - "Graveyard Zombie": new Mob(["graveyard_zombie"], [1], 1, 5, 0.5), - "Old Wolf": new Mob(["old_wolf"], [50], 3, 15, 10), - "Wolf": new Mob(["ruin_wolf"], [15], 1, 15, 0.5), - "Zombie Villager": new Mob(["zombie_villager"], [1], 4, 15, 1), - // The Farming Islands - "Chicken": new Mob(["farming_chicken"], [1], 1, 5, 0.5), - "Cow": new Mob(["farming_cow"], [1], 1, 5, 0.5), - "Mushroom Cow": new Mob(["mushroom_cow"], [1], 1, 5, 3), - "Pig": new Mob(["farming_pig"], [1], 1, 5, 0.5), - "Rabbit": new Mob(["farming_rabbit"], [1], 1, 5, 3), - "Sheep": new Mob(["farming_sheep"], [1], 1, 5, 3), - // Garden - "Mosquito": new Mob(["pest_mosquito"], [1], 6, 15, 120), - "Rat": new Mob(["pest_rat"], [1], 6, 15, 120), - "Slug": new Mob(["pest_slug"], [1], 6, 15, 120), - "Locust": new Mob(["pest_locust"], [1], 6, 15, 120), - "Fly": new Mob(["pest_fly"], [1], 6, 15, 120), - "Worm": new Mob(["pest_worm"], [1], 6, 15, 120), - "Mite": new Mob(["pest_mite"], [1], 6, 15, 120), - "Moth": new Mob(["pest_moth"], [1], 6, 15, 120), - "Beetle": new Mob(["pest_beetle"], [1], 6, 15, 120), - "Cricket": new Mob(["pest_cricket"], [1], 6, 15, 120), - // Spider's Den - "Arachne": new Mob(["arachne"], [300, 500], 7, 20, 30), - "Arachne's Brood": new Mob(["arachne_brood"], [100, 200], 4, 15, 3), - "Arachne's Keeper": new Mob(["arachne_keeper"], [100], 5, 15, 30), - "Brood Mother": new Mob(["brood_mother_spider"], [12], 5, 15, 60), - "Dasher Spider": new Mob(["dasher_spider"], [4, 42, 45, 50], 2, 15, 1), - "Gravel Skeleton": new Mob(["respawning_skeleton"], [2], 3, 15, 1), - "Rain Slime": new Mob(["random_slime"], [8], 4, 15, 5), - "Silverfish": new Mob(["jockey_shot_silverfish", "splitter_spider_silverfish"], [2, 3, 42, 45, 50], 1, 15, 1), - "Spider Jockey": new Mob(["spider_jockey"], [3, 42], 2, 15, 1), - "Splitter Spider": new Mob(["splitter_spider"], [4, 42, 45, 50], 2, 15, 1), - "Voracious Spider": new Mob(["voracious_spider"], [10, 42, 45, 50], 1, 15, 1), - "Weaver Spider": new Mob(["weaver_spider"], [3, 42, 45, 50], 2, 15, 1), - // The End - "Dragon": new Mob(["unstable_dragon", "strong_dragon", "superior_dragon", "wise_dragon", "young_dragon", "old_dragon", "protector_dragon"], [100], 5, 20, 45), - "Enderman": new Mob(["enderman"], [42, 45, 50], 4, 25, 0.5), - "Endermite": new Mob(["endermite", "nest_endermite"], [37, 40, 50], 5, 25, 5), - "Endstone Protector": new Mob(["corrupted_protector"], [100], 7, 20, 120), - "Obsidian Defender": new Mob(["obsidian_wither"], [55], 4, 25, 2), - "Voidling Extremist": new Mob(["voidling_extremist"], [100], 3, 15, 6), - "Voidling Fanatic": new Mob(["voidling_fanatic"], [85], 4, 25, 1.5), - "Watcher": new Mob(["watcher"], [55], 4, 25, 2), - "Zealot": new Mob(["zealot_enderman", "zealot_bruiser"], [55, 100], 3, 25, 3), - // Crimson Isle - "Ashfang": new Mob(["ashfang"], [200], 5, 20, 90), - "Barbarian Duke X": new Mob(["barbarian_duke_x"], [200], 5, 20, 45), - "Bladesoul": new Mob(["bladesoul"], [200], 5, 20, 45), - "Blaze": new Mob(["blaze", "bezal", "mutated_blaze"], [25, 70, 80, 70], 4, 20, 3), - "Flaming Spider": new Mob(["flaming_spider"], [80], 3, 20, 3), - "Flare": new Mob(["flare"], [90], 1, 20, 0.5), - "Ghast": new Mob(["ghast", "dive_ghast"], [85, 90], 4, 20, 4), - "Mage Outlaw": new Mob(["mage_outlaw"], [200], 5, 20, 45), - "Magma Boss": new Mob(["magma_boss"], [500], 5, 20, 90), - "Magma Cube": new Mob(["magma_cube", "pack_magma_cube"], [75, 90], 3, 20, 2), - "Matcho": new Mob(["matcho"], [100], 5, 15, 30), - "Millenia-Aged Blaze": new Mob(["old_blaze"], [110], 3, 15, 10), - "Mushroom Bull": new Mob(["charging_mushroom_cow"], [80], 3, 20, 1), - "Pigman": new Mob(["magma_cube_rider", "kada_knight"], [90], 3, 20, 2), - "Smoldering Blaze": new Mob(["smoldering_blaze"], [95], 2, 20, 2), - "Tentacle": new Mob(["tentacle", "hellwisp"], [1, 100], 5, 20, 15), - "Vanquisher": new Mob(["vanquisher"], [100], 5, 20, 180), - "Wither Skeleton": new Mob(["wither_skeleton"], [70], 3, 20, 3), - "Wither Spectre": new Mob(["wither_spectre"], [70], 3, 20, 1.5), - // Deep Cavern - "Emerald Slime": new Mob(["emerald_slime"], [5, 10, 15], 1, 10, 1), - "Lapis Zombie": new Mob(["lapis_zombie"], [7], 1, 10, 1), - "Miner Skeleton": new Mob(["diamond_skeleton"], [15, 20], 1, 10, 1), - "Miner Zombie": new Mob(["diamond_zombie"], [15, 20], 1, 10, 1), - "Redstone Pigman": new Mob(["redstone_pigman"], [10], 1, 10, 1), - "Creeper": new Mob(["invisible_creeper"], [3], 3, 10, 2), - // Dwarven Mines - "Ghost": new Mob(["caverns_ghost"], [250], 2, 25, 0.5), - "Goblin": new Mob(["goblin_weakling_melee", "goblin_weakling_bow", "goblin_creepertamer", "goblin_battler", "goblin_knife_thrower", "goblin_flamethrower", "goblin_murderlover"], [25, 40, 50, 70, 100, 200], 2, 20, 0.5), - "Goblin Raider": new Mob(["goblin_weakling_melee", "goblin_weakling_bow", "goblin_creepertamer", "goblin_creeper", "goblin_battler", "goblin_murderlover", "goblin_golem"], [5, 20, 60, 90, 150], 4, 15, 5), - "Golden Goblin": new Mob(["goblin"], [50], 5, 15, 15), - "Ice Walker": new Mob(["ice_walker"], [45], 2, 15, 3), - "Powder Ghast": new Mob(["powder_ghast"], [1], 1, 5, 180), - "Star Sentry": new Mob(["crystal_sentry"], [50], 4, 15, 30), - "Treasure Hoarder": new Mob(["treasure_hoarder"], [70], 3, 15, 5), - // Crystal Hollows - "Automaton": new Mob(["automaton"], [100, 150], 2, 15, 2), - "Bal": new Mob(["bal_boss"], [100], 6, 15, 90), - "Butterfly": new Mob(["butterfly"], [100], 4, 15, 15), - "Grunt": new Mob(["team_treasurite_grunt", "team_treasurite_viper", "team_treasurite_wendy", "team_treasurite_sebastian", "team_treasurite_corleone"], [50, 100, 200], 3, 15, 5), - "Key Guardian": new Mob(["key_guardian"], [100], 6, 15, 45), - "Sludge": new Mob(["sludge"], [5, 10, 100], 3, 15, 3), - "Thyst": new Mob(["thyst"], [20], 3, 15, 5), - "Worm": new Mob(["worm", "scatha"], [5, 10], 5, 15, 60), - "Yog": new Mob(["yog"], [100], 3, 15, 15), - // The Park - "Howling Spirit": new Mob(["howling_spirit"], [35], 2, 15, 2), - "Pack Spirit": new Mob(["pack_spirit"], [30], 2, 15, 2), - "Soul of the Alpha": new Mob(["soul_of_the_alpha"], [55], 4, 15, 6), - // Spooky Festival - "Crazy Witch": new Mob(["batty_witch"], [60], 2, 10, 10), - "Headless Horseman": new Mob(["horseman_horse"], [100], 7, 20, 30), - "Phantom Spirit": new Mob(["phantom_spirit"], [35], 2, 10, 10), - "Scary Jerry": new Mob(["scary_jerry"], [30], 2, 10, 10), - "Trick or Treater": new Mob(["trick_or_treater"], [30], 2, 10, 10), - "Wither Gourd": new Mob(["wither_gourd"], [40], 2, 10, 10), - "Wraith": new Mob(["wraith"], [50], 2, 10, 10), - // The Catacombs - "Angry Archeologist": new Mob(["diamond_guy", "master_diamond_guy"], [80, 90, 100, 110, 120, 130, 140, 150, 160, 170], 5, 25, 30), - "Bat": new Mob(["dungeon_secret_bat"], [1], 4, 15, 180), - "Cellar Spider": new Mob(["cellar_spider", "master_cellar_spider"], [45, 65, 75, 85, 95, 105, 115, 125], 4, 15, 60), - "Crypt Dreadlord": new Mob(["crypt_dreadlord", "master_cryptdreadlord"], [47, 67, 77, 87, 97, 107, 117, 127], 4, 25, 5), - "Crypt Lurker": new Mob(["crypt_lurker", "master_crypt_lurker"], [41, 61, 71, 81, 91, 101, 111, 121], 4, 25, 5), - "Crypt Souleater": new Mob(["crypt_souleater", "master_crypt_souleater"], [45, 65, 75, 85, 95, 105, 115, 125], 4, 25, 5), - "Fels": new Mob(["tentaclees", "master_tentaclees"], [90, 100, 110], 4, 25, 10), - "Golem": new Mob(["sadan_golem", "master_sadan_golem"], [1], 4, 15, 45), - "King Midas": new Mob(["king_midas", "master_king_midas"], [130, 140, 150, 160, 170], 5, 10, 600), - "Lonely Spider": new Mob(["lonely_spider", "master_lonely_spider"], [35, 55, 65, 75, 85, 95, 105, 115], 4, 25, 15), - "Lost Adventurer": new Mob(["lost_adventurer", "master_lost_adventurer"], [80, 85, 90, 100, 110, 120, 130, 140, 150, 160], 5, 25, 30), - "Mimic": new Mob(["mimic", "master_mimic"], [115, 125], 4, 15, 300), - "Scared Skeleton": new Mob(["scared_skeleton", "master_scared_skeleton"], [42, 60, 62, 70, 72], 3, 15, 5), - "Shadow Assassin": new Mob(["shadow_assassin", "master_shadow_assassin"], [120, 130, 140, 150, 160, 170, 171], 5, 25, 30), - "Skeleton Grunt": new Mob(["skeleton_grunt", "master_skeleton_grunt"], [40, 60, 70, 80], 3, 15, 5), - "Skeleton Lord": new Mob(["skeleton_lord", "master_skeleton_lord"], [150], 5, 20, 10), - "Skeleton Master": new Mob(["skeleton_master", "master_skeleton_master"], [78, 88, 98, 108, 118, 128], 4, 25, 5), - "Skeleton Soldier": new Mob(["skeleton_soldier", "master_skeleton_soldier"], [66, 76, 86, 96, 106, 116, 126], 1, 15, 5), - "Skeletor": new Mob(["skeletor", "skeletor_prime", "master_skeletor", "master_skeletor_prime"], [80, 90, 100, 110], 5, 25, 5), - "Sniper": new Mob(["sniper_skeleton", "master_sniper_skeleton"], [43, 63, 73, 83, 93, 103, 113, 123], 3, 15, 60), - "Super Archer": new Mob(["super_archer", "master_super_archer"], [90, 100, 110, 120], 5, 25, 30), - "Super Tank Zombie": new Mob(["super_tank_zombie", "master_super_tank_zombie"], [90, 100, 110, 120], 4, 25, 5), - "Tank Zombie": new Mob(["crypt_tank_zombie", "master_crypt_tank_zombie"], [40, 60, 70, 80, 90], 3, 15, 5), - "Terracotta": new Mob(["sadan_statue", "master_sadan_statue"], [1], 1, 15, 3), - "Undead": new Mob(["watcher_summon_undead", "master_watcher_summon_undead"], [1, 2, 3, 4, 5, 6, 7, 8], 2, 15, 10), - "Undead Skeleton": new Mob(["dungeon_respawning_skeleton", "master_dungeon_respawning_skeleton"], [40, 60, 61, 70, 71, 80, 81, 90, 91, 100, 101, 110, 111, 120], 4, 25, 5), - "Wither Guard": new Mob(["wither_guard", "master_wither_guard"], [100], 5, 25, 5), - "Wither Husk": new Mob(["master_wither_husk"], [100], 5, 25, 5), - "Wither Miner": new Mob(["wither_miner", "master_wither_miner"], [100], 4, 25, 5), - "Withermancer": new Mob(["crypt_witherskeleton", "master_crypt_witherskeleton"], [90, 100, 110, 120], 4, 25, 5), - "Zombie Commander": new Mob(["zombie_commander", "master_zombie_commander"], [110, 120], 4, 20, 15), - "Zombie Grunt": new Mob(["zombie_grunt", "master_zombie_grunt"], [40, 60, 70], 3, 15, 5), - "Zombie Knight": new Mob(["zombie_knight", "master_zombie_knight"], [86, 96, 106, 116, 126], 4, 25, 5), - "Zombie Lord": new Mob(["zombie_lord", "master_zombie_lord"], [150], 5, 20, 15), - "Zombie Soldier": new Mob(["zombie_soldier", "master_zombie_soldier"], [83, 93, 103, 113, 123], 1, 15, 5), - // Fishing - "Squid": new Mob(["pond_squid"], [1], 2, 15, 7), - "Night Squid": new Mob(["night_squid"], [6], 4, 15, 20), - "Sea Walker": new Mob(["sea_walker"], [4], 3, 15, 20), - "Sea Guardian": new Mob(["sea_guardian"], [10], 3, 15, 20), - "Sea Witch": new Mob(["sea_witch"], [15], 3, 15, 20), - "Sea Archer": new Mob(["sea_archer"], [15], 3, 15, 20), - "Rider of the Deep": new Mob(["zombie_deep"], [20], 3, 15, 20), - "Catfish": new Mob(["catfish"], [23], 4, 15, 20), - "Carrot King": new Mob(["carrot_king"], [25], 5, 15, 30), - "Sea Leech": new Mob(["sea_leech"], [30], 4, 15, 20), - "Guardian Defender": new Mob(["guardian_defender"], [45], 4, 15, 20), - "Deep Sea Protector": new Mob(["deep_sea_protector"], [60], 4, 15, 20), - "Water Hydra": new Mob(["water_hydra"], [100], 5, 15, 180), - "The Sea Emperor": new Mob(["skeleton_emperor"], [150], 7, 15, 300), - "Oasis Rabbit": new Mob(["oasis_rabbit"], [10], 4, 10, 15), - "Oasis Sheep": new Mob(["oasis_sheep"], [10], 4, 10, 15), - "Water Worm": new Mob(["water_worm"], [20], 4, 15, 20), - "Poisoned Water Worm": new Mob(["poisoned_water_worm"], [25], 4, 15, 20), - "Zombie Miner": new Mob(["zombie_miner"], [150], 6, 15, 60), - "Scarecrow": new Mob(["scarecrow"], [9], 3, 15, 15), - "Nightmare": new Mob(["nightmare"], [24], 4, 15, 15), - "Werewolf": new Mob(["werewolf"], [50], 4, 15, 30), - "Phantom Fisherman": new Mob(["phantom_fisherman"], [160], 6, 15, 60), - "Grim Reaper": new Mob(["grim_reaper"], [190], 7, 15, 300), - "Frozen Steve": new Mob(["frozen_steve"], [7], 3, 15, 15), - "Frosty The Snowman": new Mob(["frosty_the_snowman"], [13], 3, 15, 15), - "Grinch": new Mob(["grinch"], [21], 6, 15, 120), - "Yeti": new Mob(["yeti"], [175], 6, 15, 300), - "Nutcracker": new Mob(["nutcracker"], [50], 5, 15, 15), - "Reindrake": new Mob(["reindrake"], [100], 7, 15, 1800), - "Nurse Shark": new Mob(["nurse_shark"], [6], 3, 15, 15), - "Blue Shark": new Mob(["blue_shark"], [20], 4, 15, 30), - "Tiger Shark": new Mob(["tiger_shark"], [50], 4, 15, 45), - "Great White Shark": new Mob(["great_white_shark"], [180], 5, 15, 90), - "Plhlegblast": new Mob(["pond_squid"], [300], 7, 5, 1800), - "Magma Slug": new Mob(["magma_slug"], [200], 2, 15, 15), - "Moogma": new Mob(["moogma"], [210], 3, 15, 15), - "Lava Leech": new Mob(["lava_leech"], [220], 3, 15, 15), - "Pyroclastic Worm": new Mob(["pyroclastic_worm"], [240], 3, 15, 15), - "Lava Flame": new Mob(["lava_flame"], [230], 4, 15, 15), - "Fire Eel": new Mob(["fire_eel"], [240], 4, 15, 15), - "Taurus": new Mob(["pig_rider"], [250], 4, 15, 15), - "Thunder": new Mob(["thunder"], [400], 5, 15, 300), - "Lord Jawbus": new Mob(["lord_jawbus"], [600], 6, 15, 600), - "Flaming Worm": new Mob(["flaming_worm"], [50], 3, 15, 10), - "Lava Blaze": new Mob(["lava_blaze"], [100], 4, 15, 10), - "Lava Pigman": new Mob(["lava_pigman"], [100], 4, 15, 10), - "Agarimoo": new Mob(["agarimoo"], [35], 3, 15, 15), - // Mythological Creatures - "Gaia Construct": new Mob(["gaia_construct"], [140, 260], 4, 20, 20), - "Minos Champion": new Mob(["minos_champion"], [175, 310], 5, 20, 20), - "Minos Hunter": new Mob(["minos_hunter"], [15, 60, 125], 5, 20, 20), - "Minos Inquisitor": new Mob(["minos_inquisitor"], [750], 7, 20, 300), - "Minotaur": new Mob(["minotaur"], [45, 120, 210], 4, 20, 20), - "Siamese Lynx": new Mob(["siamese_lynx"], [25, 85, 155], 4, 20, 20), - // Jerry - "Blue Jerry": new Mob(["mayor_jerry_blue"], [2], 5, 10, 1440), - "Golden Jerry": new Mob(["mayor_jerry_golden"], [5], 7, 10, 7200), - "Green Jerry": new Mob(["mayor_jerry_green"], [1], 4, 10, 720), - "Purple Jerry": new Mob(["mayor_jerry_purple"], [3], 6, 10, 2880), - // Kuudra - "Blazing Golem": new Mob(["blazing_golem"], [100, 200, 300, 400, 500], 3, 10, 30), - "Blight": new Mob(["blight"], [100, 200, 300, 400, 500], 3, 20, 15), - "Dropship": new Mob(["dropship"], [100, 200, 300, 400, 500], 3, 10, 30), - "Explosive Imp": new Mob(["explosive_imp"], [100, 200, 300, 400, 500], 3, 20, 30), - "Inferno Magma Cube": new Mob(["inferno_magma_cube"], [100, 200, 300, 400, 500], 3, 20, 15), - "Kuudra Berserker": new Mob(["kuudra_berserker"], [100, 200, 300, 400, 500], 3, 20, 15), - "Kuudra Follower": new Mob(["kuudra_follower"], [100, 200, 300, 400, 500], 2, 20, 15), - "Kuudra Knocker": new Mob(["kuudra_knocker"], [100, 200, 300, 400, 500], 3, 20, 15), - "Kuudra Landmine": new Mob(["kuudra_landmine"], [100, 200, 300, 400, 500], 3, 20, 15), - "Kuudra Slasher": new Mob(["kuudra_slasher"], [100, 200, 300, 400, 500], 5, 10, 60), - "Magma Follower": new Mob(["magma_follower"], [100, 200, 300, 400, 500], 5, 10, 30), - "Wandering Blaze": new Mob(["wandering_blaze"], [100, 200, 300, 400, 500], 4, 20, 15), - "Wither Sentry": new Mob(["wither_sentry"], [100, 200, 300, 400, 500], 4, 10, 30), + // Your Island + Creeper: new Mob(["creeper"], [1], 1, 5, 5), + Enderman: new Mob(["enderman"], [1, 15], 1, 5, 5), + Skeleton: new Mob(["skeleton"], [1, 15], 1, 5, 5), + Slime: new Mob(["slime"], [1], 1, 5, 5), + Spider: new Mob(["spider"], [1, 15], 1, 5, 5), + Witch: new Mob(["witch"], [1, 15], 1, 5, 60), + Zombie: new Mob(["zombie"], [1, 15], 1, 5, 5), + // Hub + "Crypt Ghoul": new Mob(["unburried_zombie"], [30], 1, 15, 0.5), + "Golden Ghoul": new Mob(["unburried_zombie"], [60], 3, 15, 5), + "Graveyard Zombie": new Mob(["graveyard_zombie"], [1], 1, 5, 0.5), + "Old Wolf": new Mob(["old_wolf"], [50], 3, 15, 10), + Wolf: new Mob(["ruin_wolf"], [15], 1, 15, 0.5), + "Zombie Villager": new Mob(["zombie_villager"], [1], 4, 15, 1), + // The Farming Islands + Chicken: new Mob(["farming_chicken"], [1], 1, 5, 0.5), + Cow: new Mob(["farming_cow"], [1], 1, 5, 0.5), + "Mushroom Cow": new Mob(["mushroom_cow"], [1], 1, 5, 3), + Pig: new Mob(["farming_pig"], [1], 1, 5, 0.5), + Rabbit: new Mob(["farming_rabbit"], [1], 1, 5, 3), + Sheep: new Mob(["farming_sheep"], [1], 1, 5, 3), + // Garden + Mosquito: new Mob(["pest_mosquito"], [1], 6, 15, 120), + Rat: new Mob(["pest_rat"], [1], 6, 15, 120), + Slug: new Mob(["pest_slug"], [1], 6, 15, 120), + Locust: new Mob(["pest_locust"], [1], 6, 15, 120), + Fly: new Mob(["pest_fly"], [1], 6, 15, 120), + Worm: new Mob(["pest_worm"], [1], 6, 15, 120), + Mite: new Mob(["pest_mite"], [1], 6, 15, 120), + Moth: new Mob(["pest_moth"], [1], 6, 15, 120), + Beetle: new Mob(["pest_beetle"], [1], 6, 15, 120), + Cricket: new Mob(["pest_cricket"], [1], 6, 15, 120), + // Spider's Den + Arachne: new Mob(["arachne"], [300, 500], 7, 20, 30), + "Arachne's Brood": new Mob(["arachne_brood"], [100, 200], 4, 15, 3), + "Arachne's Keeper": new Mob(["arachne_keeper"], [100], 5, 15, 30), + "Brood Mother": new Mob(["brood_mother_spider"], [12], 5, 15, 60), + "Dasher Spider": new Mob(["dasher_spider"], [4, 42, 45, 50], 2, 15, 1), + "Gravel Skeleton": new Mob(["respawning_skeleton"], [2], 3, 15, 1), + "Rain Slime": new Mob(["random_slime"], [8], 4, 15, 5), + Silverfish: new Mob(["jockey_shot_silverfish", "splitter_spider_silverfish"], [2, 3, 42, 45, 50], 1, 15, 1), + "Spider Jockey": new Mob(["spider_jockey"], [3, 42], 2, 15, 1), + "Splitter Spider": new Mob(["splitter_spider"], [4, 42, 45, 50], 2, 15, 1), + "Voracious Spider": new Mob(["voracious_spider"], [10, 42, 45, 50], 1, 15, 1), + "Weaver Spider": new Mob(["weaver_spider"], [3, 42, 45, 50], 2, 15, 1), + // The End + Dragon: new Mob( + [ + "unstable_dragon", + "strong_dragon", + "superior_dragon", + "wise_dragon", + "young_dragon", + "old_dragon", + "protector_dragon", + ], + [100], + 5, + 20, + 45 + ), + Enderman: new Mob(["enderman"], [42, 45, 50], 4, 25, 0.5), + Endermite: new Mob(["endermite", "nest_endermite"], [37, 40, 50], 5, 25, 5), + "Endstone Protector": new Mob(["corrupted_protector"], [100], 7, 20, 120), + "Obsidian Defender": new Mob(["obsidian_wither"], [55], 4, 25, 2), + "Voidling Extremist": new Mob(["voidling_extremist"], [100], 3, 15, 6), + "Voidling Fanatic": new Mob(["voidling_fanatic"], [85], 4, 25, 1.5), + Watcher: new Mob(["watcher"], [55], 4, 25, 2), + Zealot: new Mob(["zealot_enderman", "zealot_bruiser"], [55, 100], 3, 25, 3), + // Crimson Isle + Ashfang: new Mob(["ashfang"], [200], 5, 20, 90), + "Barbarian Duke X": new Mob(["barbarian_duke_x"], [200], 5, 20, 45), + Bladesoul: new Mob(["bladesoul"], [200], 5, 20, 45), + Blaze: new Mob(["blaze", "bezal", "mutated_blaze"], [25, 70, 80, 70], 4, 20, 3), + "Flaming Spider": new Mob(["flaming_spider"], [80], 3, 20, 3), + Flare: new Mob(["flare"], [90], 1, 20, 0.5), + Ghast: new Mob(["ghast", "dive_ghast"], [85, 90], 4, 20, 4), + "Mage Outlaw": new Mob(["mage_outlaw"], [200], 5, 20, 45), + "Magma Boss": new Mob(["magma_boss"], [500], 5, 20, 90), + "Magma Cube": new Mob(["magma_cube", "pack_magma_cube"], [75, 90], 3, 20, 2), + Matcho: new Mob(["matcho"], [100], 5, 15, 30), + "Millenia-Aged Blaze": new Mob(["old_blaze"], [110], 3, 15, 10), + "Mushroom Bull": new Mob(["charging_mushroom_cow"], [80], 3, 20, 1), + Pigman: new Mob(["magma_cube_rider", "kada_knight"], [90], 3, 20, 2), + "Smoldering Blaze": new Mob(["smoldering_blaze"], [95], 2, 20, 2), + Tentacle: new Mob(["tentacle", "hellwisp"], [1, 100], 5, 20, 15), + Vanquisher: new Mob(["vanquisher"], [100], 5, 20, 180), + "Wither Skeleton": new Mob(["wither_skeleton"], [70], 3, 20, 3), + "Wither Spectre": new Mob(["wither_spectre"], [70], 3, 20, 1.5), + // Deep Cavern + "Emerald Slime": new Mob(["emerald_slime"], [5, 10, 15], 1, 10, 1), + "Lapis Zombie": new Mob(["lapis_zombie"], [7], 1, 10, 1), + "Miner Skeleton": new Mob(["diamond_skeleton"], [15, 20], 1, 10, 1), + "Miner Zombie": new Mob(["diamond_zombie"], [15, 20], 1, 10, 1), + "Redstone Pigman": new Mob(["redstone_pigman"], [10], 1, 10, 1), + Creeper: new Mob(["invisible_creeper"], [3], 3, 10, 2), + // Dwarven Mines + Ghost: new Mob(["caverns_ghost"], [250], 2, 25, 0.5), + Goblin: new Mob( + [ + "goblin_weakling_melee", + "goblin_weakling_bow", + "goblin_creepertamer", + "goblin_battler", + "goblin_knife_thrower", + "goblin_flamethrower", + "goblin_murderlover", + ], + [25, 40, 50, 70, 100, 200], + 2, + 20, + 0.5 + ), + "Goblin Raider": new Mob( + [ + "goblin_weakling_melee", + "goblin_weakling_bow", + "goblin_creepertamer", + "goblin_creeper", + "goblin_battler", + "goblin_murderlover", + "goblin_golem", + ], + [5, 20, 60, 90, 150], + 4, + 15, + 5 + ), + "Golden Goblin": new Mob(["goblin"], [50], 5, 15, 15), + "Ice Walker": new Mob(["ice_walker"], [45], 2, 15, 3), + "Powder Ghast": new Mob(["powder_ghast"], [1], 1, 5, 180), + "Star Sentry": new Mob(["crystal_sentry"], [50], 4, 15, 30), + "Treasure Hoarder": new Mob(["treasure_hoarder"], [70], 3, 15, 5), + // Crystal Hollows + Automaton: new Mob(["automaton"], [100, 150], 2, 15, 2), + Bal: new Mob(["bal_boss"], [100], 6, 15, 90), + Butterfly: new Mob(["butterfly"], [100], 4, 15, 15), + Grunt: new Mob( + [ + "team_treasurite_grunt", + "team_treasurite_viper", + "team_treasurite_wendy", + "team_treasurite_sebastian", + "team_treasurite_corleone", + ], + [50, 100, 200], + 3, + 15, + 5 + ), + "Key Guardian": new Mob(["key_guardian"], [100], 6, 15, 45), + Sludge: new Mob(["sludge"], [5, 10, 100], 3, 15, 3), + Thyst: new Mob(["thyst"], [20], 3, 15, 5), + Worm: new Mob(["worm", "scatha"], [5, 10], 5, 15, 60), + Yog: new Mob(["yog"], [100], 3, 15, 15), + // The Park + "Howling Spirit": new Mob(["howling_spirit"], [35], 2, 15, 2), + "Pack Spirit": new Mob(["pack_spirit"], [30], 2, 15, 2), + "Soul of the Alpha": new Mob(["soul_of_the_alpha"], [55], 4, 15, 6), + // Spooky Festival + "Crazy Witch": new Mob(["batty_witch"], [60], 2, 10, 10), + "Headless Horseman": new Mob(["horseman_horse"], [100], 7, 20, 30), + "Phantom Spirit": new Mob(["phantom_spirit"], [35], 2, 10, 10), + "Scary Jerry": new Mob(["scary_jerry"], [30], 2, 10, 10), + "Trick or Treater": new Mob(["trick_or_treater"], [30], 2, 10, 10), + "Wither Gourd": new Mob(["wither_gourd"], [40], 2, 10, 10), + Wraith: new Mob(["wraith"], [50], 2, 10, 10), + // The Catacombs + "Angry Archeologist": new Mob( + ["diamond_guy", "master_diamond_guy"], + [80, 90, 100, 110, 120, 130, 140, 150, 160, 170], + 5, + 25, + 30 + ), + Bat: new Mob(["dungeon_secret_bat"], [1], 4, 15, 180), + "Cellar Spider": new Mob(["cellar_spider", "master_cellar_spider"], [45, 65, 75, 85, 95, 105, 115, 125], 4, 15, 60), + "Crypt Dreadlord": new Mob( + ["crypt_dreadlord", "master_cryptdreadlord"], + [47, 67, 77, 87, 97, 107, 117, 127], + 4, + 25, + 5 + ), + "Crypt Lurker": new Mob(["crypt_lurker", "master_crypt_lurker"], [41, 61, 71, 81, 91, 101, 111, 121], 4, 25, 5), + "Crypt Souleater": new Mob( + ["crypt_souleater", "master_crypt_souleater"], + [45, 65, 75, 85, 95, 105, 115, 125], + 4, + 25, + 5 + ), + Fels: new Mob(["tentaclees", "master_tentaclees"], [90, 100, 110], 4, 25, 10), + Golem: new Mob(["sadan_golem", "master_sadan_golem"], [1], 4, 15, 45), + "King Midas": new Mob(["king_midas", "master_king_midas"], [130, 140, 150, 160, 170], 5, 10, 600), + "Lonely Spider": new Mob(["lonely_spider", "master_lonely_spider"], [35, 55, 65, 75, 85, 95, 105, 115], 4, 25, 15), + "Lost Adventurer": new Mob( + ["lost_adventurer", "master_lost_adventurer"], + [80, 85, 90, 100, 110, 120, 130, 140, 150, 160], + 5, + 25, + 30 + ), + Mimic: new Mob(["mimic", "master_mimic"], [115, 125], 4, 15, 300), + "Scared Skeleton": new Mob(["scared_skeleton", "master_scared_skeleton"], [42, 60, 62, 70, 72], 3, 15, 5), + "Shadow Assassin": new Mob( + ["shadow_assassin", "master_shadow_assassin"], + [120, 130, 140, 150, 160, 170, 171], + 5, + 25, + 30 + ), + "Skeleton Grunt": new Mob(["skeleton_grunt", "master_skeleton_grunt"], [40, 60, 70, 80], 3, 15, 5), + "Skeleton Lord": new Mob(["skeleton_lord", "master_skeleton_lord"], [150], 5, 20, 10), + "Skeleton Master": new Mob(["skeleton_master", "master_skeleton_master"], [78, 88, 98, 108, 118, 128], 4, 25, 5), + "Skeleton Soldier": new Mob( + ["skeleton_soldier", "master_skeleton_soldier"], + [66, 76, 86, 96, 106, 116, 126], + 1, + 15, + 5 + ), + Skeletor: new Mob( + ["skeletor", "skeletor_prime", "master_skeletor", "master_skeletor_prime"], + [80, 90, 100, 110], + 5, + 25, + 5 + ), + Sniper: new Mob(["sniper_skeleton", "master_sniper_skeleton"], [43, 63, 73, 83, 93, 103, 113, 123], 3, 15, 60), + "Super Archer": new Mob(["super_archer", "master_super_archer"], [90, 100, 110, 120], 5, 25, 30), + "Super Tank Zombie": new Mob(["super_tank_zombie", "master_super_tank_zombie"], [90, 100, 110, 120], 4, 25, 5), + "Tank Zombie": new Mob(["crypt_tank_zombie", "master_crypt_tank_zombie"], [40, 60, 70, 80, 90], 3, 15, 5), + Terracotta: new Mob(["sadan_statue", "master_sadan_statue"], [1], 1, 15, 3), + Undead: new Mob(["watcher_summon_undead", "master_watcher_summon_undead"], [1, 2, 3, 4, 5, 6, 7, 8], 2, 15, 10), + "Undead Skeleton": new Mob( + ["dungeon_respawning_skeleton", "master_dungeon_respawning_skeleton"], + [40, 60, 61, 70, 71, 80, 81, 90, 91, 100, 101, 110, 111, 120], + 4, + 25, + 5 + ), + "Wither Guard": new Mob(["wither_guard", "master_wither_guard"], [100], 5, 25, 5), + "Wither Husk": new Mob(["master_wither_husk"], [100], 5, 25, 5), + "Wither Miner": new Mob(["wither_miner", "master_wither_miner"], [100], 4, 25, 5), + Withermancer: new Mob(["crypt_witherskeleton", "master_crypt_witherskeleton"], [90, 100, 110, 120], 4, 25, 5), + "Zombie Commander": new Mob(["zombie_commander", "master_zombie_commander"], [110, 120], 4, 20, 15), + "Zombie Grunt": new Mob(["zombie_grunt", "master_zombie_grunt"], [40, 60, 70], 3, 15, 5), + "Zombie Knight": new Mob(["zombie_knight", "master_zombie_knight"], [86, 96, 106, 116, 126], 4, 25, 5), + "Zombie Lord": new Mob(["zombie_lord", "master_zombie_lord"], [150], 5, 20, 15), + "Zombie Soldier": new Mob(["zombie_soldier", "master_zombie_soldier"], [83, 93, 103, 113, 123], 1, 15, 5), + // Fishing + Squid: new Mob(["pond_squid"], [1], 2, 15, 7), + "Night Squid": new Mob(["night_squid"], [6], 4, 15, 20), + "Sea Walker": new Mob(["sea_walker"], [4], 3, 15, 20), + "Sea Guardian": new Mob(["sea_guardian"], [10], 3, 15, 20), + "Sea Witch": new Mob(["sea_witch"], [15], 3, 15, 20), + "Sea Archer": new Mob(["sea_archer"], [15], 3, 15, 20), + "Rider of the Deep": new Mob(["zombie_deep"], [20], 3, 15, 20), + Catfish: new Mob(["catfish"], [23], 4, 15, 20), + "Carrot King": new Mob(["carrot_king"], [25], 5, 15, 30), + "Sea Leech": new Mob(["sea_leech"], [30], 4, 15, 20), + "Guardian Defender": new Mob(["guardian_defender"], [45], 4, 15, 20), + "Deep Sea Protector": new Mob(["deep_sea_protector"], [60], 4, 15, 20), + "Water Hydra": new Mob(["water_hydra"], [100], 5, 15, 180), + "The Sea Emperor": new Mob(["skeleton_emperor"], [150], 7, 15, 300), + "Oasis Rabbit": new Mob(["oasis_rabbit"], [10], 4, 10, 15), + "Oasis Sheep": new Mob(["oasis_sheep"], [10], 4, 10, 15), + "Water Worm": new Mob(["water_worm"], [20], 4, 15, 20), + "Poisoned Water Worm": new Mob(["poisoned_water_worm"], [25], 4, 15, 20), + "Zombie Miner": new Mob(["zombie_miner"], [150], 6, 15, 60), + Scarecrow: new Mob(["scarecrow"], [9], 3, 15, 15), + Nightmare: new Mob(["nightmare"], [24], 4, 15, 15), + Werewolf: new Mob(["werewolf"], [50], 4, 15, 30), + "Phantom Fisherman": new Mob(["phantom_fisherman"], [160], 6, 15, 60), + "Grim Reaper": new Mob(["grim_reaper"], [190], 7, 15, 300), + "Frozen Steve": new Mob(["frozen_steve"], [7], 3, 15, 15), + "Frosty The Snowman": new Mob(["frosty_the_snowman"], [13], 3, 15, 15), + Grinch: new Mob(["grinch"], [21], 6, 15, 120), + Yeti: new Mob(["yeti"], [175], 6, 15, 300), + Nutcracker: new Mob(["nutcracker"], [50], 5, 15, 15), + Reindrake: new Mob(["reindrake"], [100], 7, 15, 1800), + "Nurse Shark": new Mob(["nurse_shark"], [6], 3, 15, 15), + "Blue Shark": new Mob(["blue_shark"], [20], 4, 15, 30), + "Tiger Shark": new Mob(["tiger_shark"], [50], 4, 15, 45), + "Great White Shark": new Mob(["great_white_shark"], [180], 5, 15, 90), + Plhlegblast: new Mob(["pond_squid"], [300], 7, 5, 1800), + "Magma Slug": new Mob(["magma_slug"], [200], 2, 15, 15), + Moogma: new Mob(["moogma"], [210], 3, 15, 15), + "Lava Leech": new Mob(["lava_leech"], [220], 3, 15, 15), + "Pyroclastic Worm": new Mob(["pyroclastic_worm"], [240], 3, 15, 15), + "Lava Flame": new Mob(["lava_flame"], [230], 4, 15, 15), + "Fire Eel": new Mob(["fire_eel"], [240], 4, 15, 15), + Taurus: new Mob(["pig_rider"], [250], 4, 15, 15), + Thunder: new Mob(["thunder"], [400], 5, 15, 300), + "Lord Jawbus": new Mob(["lord_jawbus"], [600], 6, 15, 600), + "Flaming Worm": new Mob(["flaming_worm"], [50], 3, 15, 10), + "Lava Blaze": new Mob(["lava_blaze"], [100], 4, 15, 10), + "Lava Pigman": new Mob(["lava_pigman"], [100], 4, 15, 10), + Agarimoo: new Mob(["agarimoo"], [35], 3, 15, 15), + // Mythological Creatures + "Gaia Construct": new Mob(["gaia_construct"], [140, 260], 4, 20, 20), + "Minos Champion": new Mob(["minos_champion"], [175, 310], 5, 20, 20), + "Minos Hunter": new Mob(["minos_hunter"], [15, 60, 125], 5, 20, 20), + "Minos Inquisitor": new Mob(["minos_inquisitor"], [750], 7, 20, 300), + Minotaur: new Mob(["minotaur"], [45, 120, 210], 4, 20, 20), + "Siamese Lynx": new Mob(["siamese_lynx"], [25, 85, 155], 4, 20, 20), + // Jerry + "Blue Jerry": new Mob(["mayor_jerry_blue"], [2], 5, 10, 1440), + "Golden Jerry": new Mob(["mayor_jerry_golden"], [5], 7, 10, 7200), + "Green Jerry": new Mob(["mayor_jerry_green"], [1], 4, 10, 720), + "Purple Jerry": new Mob(["mayor_jerry_purple"], [3], 6, 10, 2880), + // Kuudra + "Blazing Golem": new Mob(["blazing_golem"], [100, 200, 300, 400, 500], 3, 10, 30), + Blight: new Mob(["blight"], [100, 200, 300, 400, 500], 3, 20, 15), + Dropship: new Mob(["dropship"], [100, 200, 300, 400, 500], 3, 10, 30), + "Explosive Imp": new Mob(["explosive_imp"], [100, 200, 300, 400, 500], 3, 20, 30), + "Inferno Magma Cube": new Mob(["inferno_magma_cube"], [100, 200, 300, 400, 500], 3, 20, 15), + "Kuudra Berserker": new Mob(["kuudra_berserker"], [100, 200, 300, 400, 500], 3, 20, 15), + "Kuudra Follower": new Mob(["kuudra_follower"], [100, 200, 300, 400, 500], 2, 20, 15), + "Kuudra Knocker": new Mob(["kuudra_knocker"], [100, 200, 300, 400, 500], 3, 20, 15), + "Kuudra Landmine": new Mob(["kuudra_landmine"], [100, 200, 300, 400, 500], 3, 20, 15), + "Kuudra Slasher": new Mob(["kuudra_slasher"], [100, 200, 300, 400, 500], 5, 10, 60), + "Magma Follower": new Mob(["magma_follower"], [100, 200, 300, 400, 500], 5, 10, 30), + "Wandering Blaze": new Mob(["wandering_blaze"], [100, 200, 300, 400, 500], 4, 20, 15), + "Wither Sentry": new Mob(["wither_sentry"], [100, 200, 300, 400, 500], 4, 10, 30), }; /** @@ -332,53 +445,71 @@ const bestiary = { * @param {Number} amount - The number of entries to include in the final sorted bestiary. */ function sortBestiary(val, amount) { - // Filtering the bestiary based on the provided criteria and amount - const filteredBestiary = Object.entries(bestiary).filter(([key, value]) => - val === "bracket" ? value.bracket === KILL_BRACKETS[amount - 1] && value.next !== 0 : value.next !== 0 - ); + // Filtering the bestiary based on the provided criteria and amount + const filteredBestiary = Object.entries(bestiary).filter(([key, value]) => + val === "bracket" ? value.bracket === KILL_BRACKETS[amount - 1] && value.next !== 0 : value.next !== 0 + ); - // Sorting the filtered bestiary in descending order based on the provided criteria - const sortedBestiary = filteredBestiary.sort((a, b) => b[1][val === "bracket" ? "next" : val] - a[1][val === "bracket" ? "next" : val]) - .slice(val === "bracket" ? -10 : -amount).reduce((acc, [key, value]) => { - acc[key] = value; - return acc; + // Sorting the filtered bestiary in descending order based on the provided criteria + const sortedBestiary = filteredBestiary + .sort((a, b) => b[1][val === "bracket" ? "next" : val] - a[1][val === "bracket" ? "next" : val]) + .slice(val === "bracket" ? -10 : -amount) + .reduce((acc, [key, value]) => { + acc[key] = value; + return acc; }, {}); - // Displaying the sorted bestiary information in chat - ChatLib.chat(`\n${LOGO + WHITE + BOLD}Leftover Bestiary: `); - Object.keys(sortedBestiary).forEach((key) => { - ChatLib.chat(`${GOLD + BOLD + key}: ${GREEN}Needs ${RED + sortedBestiary[key].next + GREEN} kills! (${RED + formatTime(sortedBestiary[key].nextTime) + GREEN})`); - }); + // Displaying the sorted bestiary information in chat + ChatLib.chat(`\n${LOGO + WHITE + BOLD}Leftover Bestiary: `); + Object.keys(sortedBestiary).forEach((key) => { + ChatLib.chat( + `${GOLD + BOLD + key}: ${GREEN}Needs ${RED + sortedBestiary[key].next + GREEN} kills! (${ + RED + formatTime(sortedBestiary[key].nextTime) + GREEN + })` + ); + }); } - + /** * Gets the bestiary information based on the provided arguments and displays the result in chat. * * @param {String[]} args - An array of arguments containing information about the bestiary query. */ export function getBestiary(args) { - if (bestiaryApi !== undefined) switch(args[1]) { - case "kills": - case "kill": - sortBestiary("next", args[2] || 10); - break; - case "time": - sortBestiary("nextTime", args[2] || 10); - break; - case "bracket" : - if (!isNaN(args[2]) && args[2] >= 1 && args[2] <= 7) { - sortBestiary("bracket", args[2]); - break; - } - default: - const key = args.slice(1).map(item => item.charAt(0).toUpperCase() + item.slice(1)).join(' '); - const mob = bestiary[key]; - if (mob === undefined) { - ChatLib.chat(`\n${LOGO + RED}Error: Invalid argument "${args[1]}"!`); - ChatLib.chat(`${LOGO + RED}Please input as ${WHITE}/va be ${GRAY}<${WHITE}[name], kill, time, bracket [1-7]${GRAY}> ${WHITE}[amount]`); - } else - ChatLib.chat(`${LOGO + GOLD + BOLD + key}: ${GREEN}Needs ${RED + mob.next + GREEN} kills! (${RED + formatTime(mob.nextTime) + GREEN})`); - break; + if (bestiaryApi !== undefined) + switch (args[1]) { + case "kills": + case "kill": + sortBestiary("next", args[2] || 10); + break; + case "time": + sortBestiary("nextTime", args[2] || 10); + break; + case "bracket": + if (!isNaN(args[2]) && args[2] >= 1 && args[2] <= 7) { + sortBestiary("bracket", args[2]); + break; + } + default: + const key = args + .slice(1) + .map((item) => item.charAt(0).toUpperCase() + item.slice(1)) + .join(" "); + const mob = bestiary[key]; + if (mob === undefined) { + ChatLib.chat(`\n${LOGO + RED}Error: Invalid argument "${args[1]}"!`); + ChatLib.chat( + `${ + LOGO + RED + }Please input as ${WHITE}/va be ${GRAY}<${WHITE}[name], kill, time, bracket [1-7]${GRAY}> ${WHITE}[amount]` + ); + } else + ChatLib.chat( + `${LOGO + GOLD + BOLD + key}: ${GREEN}Needs ${RED + mob.next + GREEN} kills! (${ + RED + formatTime(mob.nextTime) + GREEN + })` + ); + break; } - updateBestiary(data.lastID, args); + updateBestiary(data.lastID, args); } diff --git a/features/combat/BestiaryDisplay.js b/features/combat/BestiaryDisplay.js index 2f29d9d..099aa9c 100644 --- a/features/combat/BestiaryDisplay.js +++ b/features/combat/BestiaryDisplay.js @@ -1,12 +1,11 @@ -import Settings from "../../utils/Settings"; -import { formatTime, romanToNum, unformatNumber } from "../../utils/functions/format"; -import { registerWhen } from "../../utils/RegisterTils"; -import { Overlay } from "../../utils/Overlay"; -import { data } from "../../utils/Data"; import { BOLD, GOLD, GRAY, GREEN, LOGO, WHITE, YELLOW } from "../../utils/Constants"; +import { data } from "../../utils/Data"; import { Json } from "../../utils/Json"; import Location from "../../utils/Location"; - +import { Overlay } from "../../utils/Overlay"; +import { registerWhen } from "../../utils/RegisterTils"; +import Settings from "../../utils/Settings"; +import { formatTime, romanToNum, unformatNumber } from "../../utils/functions/format"; /** * Parse bestiary level inventory @@ -18,166 +17,175 @@ const bestiaryData = [[], []]; * Trigger to record and track bestiary menu levels. */ const setLevels = register("guiRender", () => { - const container = Player.getContainer(); - - if (bestiaryData[0].length === 0) { - rows: for (let i = 1; i < 5; i++) { - for (let j = 1; j < 8; j++) { - let index = i*9 + j; - let item = container.getStackInSlot(index); - if (item === null || item.getRegistryName() === "minecraft:stained_glass_pane") break rows; - - let lore = item.getLore(); - let completed = lore[lore.length - 4] === "§5§o§7Overall Progress: §b100% §7(§c§lMAX!§7)"; - bestiaryData[1].push(completed); - bestiaryData[0].push(completed ? 1 : romanToNum(item.getName().split(' ').pop())); - } - } - } + const container = Player.getContainer(); - bestiaryData[0].forEach((level, i) => { - let index = 2*parseInt(i/7) + 10 + i; + if (bestiaryData[0].length === 0) { + rows: for (let i = 1; i < 5; i++) { + for (let j = 1; j < 8; j++) { + let index = i * 9 + j; let item = container.getStackInSlot(index); - if (item === null) return; - item.setStackSize(isNaN(level) ? 0 : level); - }) + if (item === null || item.getRegistryName() === "minecraft:stained_glass_pane") break rows; + + let lore = item.getLore(); + let completed = lore[lore.length - 4] === "§5§o§7Overall Progress: §b100% §7(§c§lMAX!§7)"; + bestiaryData[1].push(completed); + bestiaryData[0].push(completed ? 1 : romanToNum(item.getName().split(" ").pop())); + } + } + } + + bestiaryData[0].forEach((level, i) => { + let index = 2 * parseInt(i / 7) + 10 + i; + let item = container.getStackInSlot(index); + if (item === null) return; + item.setStackSize(isNaN(level) ? 0 : level); + }); }).unregister(); /** * Trigger to highlight uncompleted bestiary milestones in red. */ -const setHighlight = register('guiRender', () => { - bestiaryData[1].forEach((complete, i) => { - // Credit to https://www.chattriggers.com/modules/v/ExperimentationTable - if (complete) return; - let index = 2*parseInt(i/7) + 10 + i - const x = index % 9; - const y = Math.floor(index / 9); - const renderX = Renderer.screen.getWidth() / 2 + ((x - 4) * 18); - const renderY = (Renderer.screen.getHeight() + 10) / 2 + ((y - Player.getContainer().getSize() / 18) * 18); - - Renderer.translate(0, 0, 100); - Renderer.drawRect(Renderer.color(255, 87, 51, 128), renderX - 9, renderY - 9, 17, 17); - }) +const setHighlight = register("guiRender", () => { + bestiaryData[1].forEach((complete, i) => { + // Credit to https://www.chattriggers.com/modules/v/ExperimentationTable + if (complete) return; + let index = 2 * parseInt(i / 7) + 10 + i; + const x = index % 9; + const y = Math.floor(index / 9); + const renderX = Renderer.screen.getWidth() / 2 + (x - 4) * 18; + const renderY = (Renderer.screen.getHeight() + 10) / 2 + (y - Player.getContainer().getSize() / 18) * 18; + + Renderer.translate(0, 0, 100); + Renderer.drawRect(Renderer.color(255, 87, 51, 128), renderX - 9, renderY - 9, 17, 17); + }); }).unregister(); /** * Register/unregister bestiary stack size */ -registerWhen(register("guiOpened", () => { +registerWhen( + register("guiOpened", () => { Client.scheduleTask(1, () => { - const containerName = Player.getContainer().getName(); - if ((!containerName.includes("Bestiary ➜") && !containerName.startsWith("Fishing ➜"))) return; - setLevels.register(); - setHighlight.register(); + const containerName = Player.getContainer().getName(); + if (!containerName.includes("Bestiary ➜") && !containerName.startsWith("Fishing ➜")) return; + setLevels.register(); + setHighlight.register(); }); -}), () => Settings.bestiaryGUI); -registerWhen(register("guiClosed", () => { + }), + () => Settings.bestiaryGUI +); +registerWhen( + register("guiClosed", () => { setLevels.unregister(); setHighlight.unregister(); bestiaryData[0] = []; bestiaryData[1] = []; -}), () => Settings.bestiaryGUI); - + }), + () => Settings.bestiaryGUI +); /** * Bestiary widget tracker. */ const maxBestiary = new Json("bestiary.json", false).getData(); register("guiOpened", () => { - Client.scheduleTask(1, () => { - const containerName = Player.getContainer().getName(); - if ((!containerName.includes("Bestiary ➜") && !containerName.startsWith("Fishing ➜"))) return; - - const items = Player.getContainer().getItems(); - for (let i = 1; i < 5; i++) { - for (let j = 1; j < 8; j++) { - let index = i * 9 + j; - let item = items[index]; - if (item === null || item.getRegistryName() === "minecraft:stained_glass_pane") break; - - let name = item.getName().removeFormatting().split(' ').slice(0, -1).join(' '); - let lore = item.getLore(); - let ind = lore.findIndex(line => line.startsWith("§5§o§7Overall Progress: §b")); - let max = unformatNumber(lore[ind + 1].removeFormatting().split('/')[1]); - if (max !== 0 && name !== '') maxBestiary[name] = max; - } - } - }); + Client.scheduleTask(1, () => { + const containerName = Player.getContainer().getName(); + if (!containerName.includes("Bestiary ➜") && !containerName.startsWith("Fishing ➜")) return; + + const items = Player.getContainer().getItems(); + for (let i = 1; i < 5; i++) { + for (let j = 1; j < 8; j++) { + let index = i * 9 + j; + let item = items[index]; + if (item === null || item.getRegistryName() === "minecraft:stained_glass_pane") break; + + let name = item.getName().removeFormatting().split(" ").slice(0, -1).join(" "); + let lore = item.getLore(); + let ind = lore.findIndex((line) => line.startsWith("§5§o§7Overall Progress: §b")); + let max = unformatNumber(lore[ind + 1].removeFormatting().split("/")[1]); + if (max !== 0 && name !== "") maxBestiary[name] = max; + } + } + }); }); -const bestiaryExample = -`§6§lWolf: §f3 (10800.00/hr) +const bestiaryExample = `§6§lWolf: §f3 (10800.00/hr) §eNext: §715m39s §eMax: §73h24m32s`; const bestiaryOverlay = new Overlay("bestiaryCounter", data.BEL, "moveBe", bestiaryExample); // Dict of [start, now, next] -let beCounter = {"all": {}}; +let beCounter = { all: {} }; let beTime = 0; register("command", () => { - beCounter = {"all": {}}; - beTime = 0; - bestiaryOverlay.setMessage(""); - ChatLib.chat(`${LOGO + GREEN}Successfully reset bestiary counter.`); + beCounter = { all: {} }; + beTime = 0; + bestiaryOverlay.setMessage(""); + ChatLib.chat(`${LOGO + GREEN}Successfully reset bestiary counter.`); }).setName("resetBe"); -registerWhen(register("step", () => { +registerWhen( + register("step", () => { if (!World.isLoaded()) return; const tablist = TabList.getNames(); - let index = tablist.findIndex(name => name.startsWith("§r§6§lBestiary:§r")) + 1; + let index = tablist.findIndex((name) => name.startsWith("§r§6§lBestiary:§r")) + 1; if (index === 0) return; // Set counter specified in settings let counter = beCounter["all"]; if (Settings.bestiaryCounter === 2) { - const location = Location.getWorld(); - if (!beCounter.hasOwnProperty(location)) beCounter[location] = {}; - counter = beCounter[location]; + const location = Location.getWorld(); + if (!beCounter.hasOwnProperty(location)) beCounter[location] = {}; + counter = beCounter[location]; } // Update bestiary data using widget while (tablist[index].startsWith("§r ") && !tablist[index].endsWith("§r§3§lInfo§r")) { - let beData = tablist[index++].removeFormatting().trim().split(' '); - let levelData = beData[beData.length - 1]; - let name = beData.slice(0, -2).join(' '); - if (levelData === "MAX" || name === '') continue; - - let count = levelData.split('/'); - let now = unformatNumber(count[0]); - let next = unformatNumber(count[1]); - - if (counter.hasOwnProperty(name)) { - counter[name][1] = now; - counter[name][2] = Math.max(next, counter[name][2]); - } else counter[name] = [now, now, next]; + let beData = tablist[index++].removeFormatting().trim().split(" "); + let levelData = beData[beData.length - 1]; + let name = beData.slice(0, -2).join(" "); + if (levelData === "MAX" || name === "") continue; + + let count = levelData.split("/"); + let now = unformatNumber(count[0]); + let next = unformatNumber(count[1]); + + if (counter.hasOwnProperty(name)) { + counter[name][1] = now; + counter[name][2] = Math.max(next, counter[name][2]); + } else counter[name] = [now, now, next]; } // Sort by now - start - const keys = Object.keys(counter).filter(key => { + const keys = Object.keys(counter) + .filter((key) => { return counter[key][0] !== counter[key][1]; - }).sort((a, b) => { + }) + .sort((a, b) => { return counter[b][1] - counter[b][0] - counter[a][1] + counter[a][0]; - }); + }); // Update time if not empty if (keys.length > 0) beTime += 1; else return; // Set overlay message - let message = ''; - keys.forEach(key => { - let kills = counter[key][1] - counter[key][0]; - let rate = kills / beTime; - let next = (counter[key][2] - counter[key][1]) / rate; - let max = ((maxBestiary[key] ?? 0) - counter[key][1]) / rate; - - if (message !== "") message += '\n'; - message += `${GOLD + BOLD + key}: ${WHITE + kills} (${(rate * 3600).toFixed(2)}/hr)`; - message += `\n ${YELLOW}Next: ${GRAY + formatTime(next, 0, 3)}`; - message += `\n ${YELLOW}Max: ${GRAY + formatTime(max, 0, 3)}`; + let message = ""; + keys.forEach((key) => { + let kills = counter[key][1] - counter[key][0]; + let rate = kills / beTime; + let next = (counter[key][2] - counter[key][1]) / rate; + let max = ((maxBestiary[key] ?? 0) - counter[key][1]) / rate; + + if (message !== "") message += "\n"; + message += `${GOLD + BOLD + key}: ${WHITE + kills} (${(rate * 3600).toFixed(2)}/hr)`; + message += `\n ${YELLOW}Next: ${GRAY + formatTime(next, 0, 3)}`; + message += `\n ${YELLOW}Max: ${GRAY + formatTime(max, 0, 3)}`; }); bestiaryOverlay.setMessage(message); -}).setFps(1), () => Settings.bestiaryCounter !== 0); + }).setFps(1), + () => Settings.bestiaryCounter !== 0 +); diff --git a/features/combat/ComboDisplay.js b/features/combat/ComboDisplay.js index 17cfb05..d5e1183 100644 --- a/features/combat/ComboDisplay.js +++ b/features/combat/ComboDisplay.js @@ -1,15 +1,13 @@ -import Settings from "../../utils/Settings"; import { AQUA, BOLD, DARK_AQUA, DARK_GRAY, GOLD } from "../../utils/Constants"; -import { registerWhen } from "../../utils/RegisterTils"; -import { Overlay } from "../../utils/Overlay"; import { data } from "../../utils/Data"; - +import { Overlay } from "../../utils/Overlay"; +import { registerWhen } from "../../utils/RegisterTils"; +import Settings from "../../utils/Settings"; /** * Variables used to track and display combo count and stats. */ -const comboExample = -`${GOLD + BOLD}+34 Kill Combo +const comboExample = `${GOLD + BOLD}+34 Kill Combo ${AQUA}+69✯ Magic Find ${DARK_GRAY}+${GOLD}420 coins per kill ${DARK_AQUA}+911☯ Combat Wisdom`; @@ -21,46 +19,58 @@ let stats = ["", 0, 0, 0]; * Resets and updates overlay message to match stats. */ function updateOverlay() { - let comboMessage = ''; - comboMessage = stats[0]; - if (stats[1] !== 0) comboMessage += `\n${AQUA}+${stats[1]}✯ Magic Find`; - if (stats[2] !== 0) comboMessage += `\n${DARK_AQUA}+${stats[2]}☯ Combat Wisdom`; - if (stats[3] !== 0) comboMessage += `\n${DARK_GRAY}+${GOLD + stats[3]} coins per kill`; + let comboMessage = ""; + comboMessage = stats[0]; + if (stats[1] !== 0) comboMessage += `\n${AQUA}+${stats[1]}✯ Magic Find`; + if (stats[2] !== 0) comboMessage += `\n${DARK_AQUA}+${stats[2]}☯ Combat Wisdom`; + if (stats[3] !== 0) comboMessage += `\n${DARK_GRAY}+${GOLD + stats[3]} coins per kill`; - comboOverlay.setMessage(comboMessage); + comboOverlay.setMessage(comboMessage); } /** * Processes chat messages and updates statistics based on provided information. */ -registerWhen(register("chat", (color, kills, bonus, event) => { - const stat = bonus.split(' ')[0].removeFormatting(); - const amount = parseInt(stat.replace(/[^0-9]/g, '')); - if (stat.includes('✯')) stats[1] += amount; - else if (stat.includes('☯')) stats[2] += amount; +registerWhen( + register("chat", (color, kills, bonus, event) => { + const stat = bonus.split(" ")[0].removeFormatting(); + const amount = parseInt(stat.replace(/[^0-9]/g, "")); + if (stat.includes("✯")) stats[1] += amount; + else if (stat.includes("☯")) stats[2] += amount; else stats[3] += amount; cancel(event); stats[0] = `${color + BOLD + kills}:`; updateOverlay(); -}).setCriteria("&r${color}&l+${kills} &r&8${bonus}&r"), () => Settings.comboDisplay); + }).setCriteria("&r${color}&l+${kills} &r&8${bonus}&r"), + () => Settings.comboDisplay +); /** * Updates overlay with formatted kill combo message. */ -registerWhen(register("chat", (color, kills) => { +registerWhen( + register("chat", (color, kills) => { stats[0] = `${color + kills} Kill Combo:`; updateOverlay(); -}).setCriteria("&r${color}+${kills} Kill Combo&r"), () => Settings.comboDisplay); + }).setCriteria("&r${color}+${kills} Kill Combo&r"), + () => Settings.comboDisplay +); /** * Resets statistics and overlay message. */ -registerWhen(register("chat", () => { +registerWhen( + register("chat", () => { stats = ["", 0, 0, 0]; comboOverlay.setMessage(""); -}).setCriteria("Your Kill Combo has expired! You reached a ${kills} Kill Combo!"), () => Settings.comboDisplay); -registerWhen(register("worldUnload", () => { + }).setCriteria("Your Kill Combo has expired! You reached a ${kills} Kill Combo!"), + () => Settings.comboDisplay +); +registerWhen( + register("worldUnload", () => { stats = ["", 0, 0, 0]; comboOverlay.setMessage(""); -}), () => Settings.comboDisplay); + }), + () => Settings.comboDisplay +); diff --git a/features/combat/DamageTracker.js b/features/combat/DamageTracker.js index b59849b..0b2535c 100644 --- a/features/combat/DamageTracker.js +++ b/features/combat/DamageTracker.js @@ -1,8 +1,7 @@ -import Settings from "../../utils/Settings"; import { BOLD, DARK_GRAY, EntityArmorStand, GOLD, GRAY, LOGO, RED, WHITE, YELLOW } from "../../utils/Constants"; -import { formatNumber, formatTime } from "../../utils/functions/format"; import { registerWhen } from "../../utils/RegisterTils"; - +import Settings from "../../utils/Settings"; +import { formatNumber } from "../../utils/functions/format"; /** * Variable used to track all damage ticks around the player. @@ -17,105 +16,120 @@ let start = 0; let last = 0; function statisticalAnalysis(arr) { - // Sort array - const sortedArray = arr.map((value) => parseFloat(value / 20)).sort((a, b) => a - b); - - // Max, Min, and Range - const max = sortedArray[sortedArray.length - 1]; - const min = sortedArray[0]; - const range = max - min; - - // Mean - const sum = sortedArray.reduce((accumulator, currentValue) => accumulator + currentValue, 0); - const mean = sum / sortedArray.length; - - // Median - const median = sortedArray.length % 2 === 0 ? - (sortedArray[sortedArray.length / 2 - 1] + sortedArray[sortedArray.length / 2]) / 2 : - sortedArray[Math.floor(sortedArray.length / 2)]; - - // Inter Quartiles - const lowerQ = calculatePercentile(sortedArray, 25); - const upperQ = calculatePercentile(sortedArray, 75); - const iqr = upperQ - lowerQ; - - // Variance and Standard Deviation - const variance = sortedArray.reduce((accumulator, currentValue) => { - const diff = currentValue - mean; - return accumulator + diff * diff; - }, 0); - const stdDev = Math.sqrt(variance / sortedArray.length); - - const mode = calculateMode(sortedArray); - - return { sum, max, min, range, mean, median, mode, lowerQ, upperQ, iqr, variance, stdDev }; - } - + // Sort array + const sortedArray = arr.map((value) => parseFloat(value / 20)).sort((a, b) => a - b); + + // Max, Min, and Range + const max = sortedArray[sortedArray.length - 1]; + const min = sortedArray[0]; + const range = max - min; + + // Mean + const sum = sortedArray.reduce((accumulator, currentValue) => accumulator + currentValue, 0); + const mean = sum / sortedArray.length; + + // Median + const median = + sortedArray.length % 2 === 0 + ? (sortedArray[sortedArray.length / 2 - 1] + sortedArray[sortedArray.length / 2]) / 2 + : sortedArray[Math.floor(sortedArray.length / 2)]; + + // Inter Quartiles + const lowerQ = calculatePercentile(sortedArray, 25); + const upperQ = calculatePercentile(sortedArray, 75); + const iqr = upperQ - lowerQ; + + // Variance and Standard Deviation + const variance = sortedArray.reduce((accumulator, currentValue) => { + const diff = currentValue - mean; + return accumulator + diff * diff; + }, 0); + const stdDev = Math.sqrt(variance / sortedArray.length); + + const mode = calculateMode(sortedArray); + + return { + sum, + max, + min, + range, + mean, + median, + mode, + lowerQ, + upperQ, + iqr, + variance, + stdDev, + }; +} + function calculatePercentile(arr, percentile) { - const index = (percentile / 100) * (arr.length - 1); - const lowerIndex = Math.floor(index); - const upperIndex = Math.ceil(index); - const lowerValue = arr[lowerIndex]; - const upperValue = arr[upperIndex]; - const interpolationFactor = index - lowerIndex; - - return lowerValue + (upperValue - lowerValue) * interpolationFactor; + const index = (percentile / 100) * (arr.length - 1); + const lowerIndex = Math.floor(index); + const upperIndex = Math.ceil(index); + const lowerValue = arr[lowerIndex]; + const upperValue = arr[upperIndex]; + const interpolationFactor = index - lowerIndex; + + return lowerValue + (upperValue - lowerValue) * interpolationFactor; } - + function calculateMode(arr) { - const countMap = new Map(); - - // Iterate through the array to count the occurrences of each number - for (const number of arr) { - if (countMap.has(number)) { - countMap.set(number, countMap.get(number) + 1); - } else { - countMap.set(number, 1); - } + const countMap = new Map(); + + // Iterate through the array to count the occurrences of each number + for (const number of arr) { + if (countMap.has(number)) { + countMap.set(number, countMap.get(number) + 1); + } else { + countMap.set(number, 1); } + } - let mode = null; - let maxCount = 0; + let mode = null; + let maxCount = 0; - // Find the number with the highest count - countMap.forEach((count, number) => { - if (count > maxCount) { - mode = number; - maxCount = count; - } - }); + // Find the number with the highest count + countMap.forEach((count, number) => { + if (count > maxCount) { + mode = number; + maxCount = count; + } + }); - return mode; + return mode; } /** * Tracks any instance of damage around the player and displays it in chat. */ -registerWhen(register("tick", () => { +registerWhen( + register("tick", () => { const player = Player.asPlayerMP().getEntity(); const stands = World.getWorld() - .func_72839_b(player, player.func_174813_aQ().func_72314_b(16, 16, 16)) - .filter(entity => entity instanceof EntityArmorStand); + .func_72839_b(player, player.func_174813_aQ().func_72314_b(16, 16, 16)) + .filter((entity) => entity instanceof EntityArmorStand); let ticked = false; // Get damage stands - stands.forEach(stand => { - const name = stand.func_95999_t(); - - if (name.startsWith("§7") && !isNaN(name.removeFormatting().replace(/,/g, ''))) nonCrits.push(name); - else if (name.startsWith("§f✧")) crits.push(name); - else if (name.startsWith("§f✯")) overloads.push(name); - else return; - - last = Date.now() / 1000; - ticked = true; - - const num = name.removeFormatting().replace(/[^0-9.]/g, ''); - if (!unique.has(num)) { - unique.add(num); - ChatLib.chat(name); - } - damages.push(num); + stands.forEach((stand) => { + const name = stand.func_95999_t(); + + if (name.startsWith("§7") && !isNaN(name.removeFormatting().replace(/,/g, ""))) nonCrits.push(name); + else if (name.startsWith("§f✧")) crits.push(name); + else if (name.startsWith("§f✯")) overloads.push(name); + else return; + + last = Date.now() / 1000; + ticked = true; + + const num = name.removeFormatting().replace(/[^0-9.]/g, ""); + if (!unique.has(num)) { + unique.add(num); + ChatLib.chat(name); + } + damages.push(num); }); // Check for initial damage tick @@ -124,12 +138,13 @@ registerWhen(register("tick", () => { // Do calcs when no new damage ticks if (!ticked && last - start > 1) { - if (Settings.damageTracker === 2) { - const { sum, max, min, range, mean, median, mode, lowerQ, upperQ, iqr, variance, stdDev } = statisticalAnalysis(damages); - - const time = Date.now()/1000 - start; - ChatLib.chat( -`\n${LOGO + GOLD + BOLD}Damage Statistical Analysis ${GRAY}[${formattime(time, 2)}] + if (Settings.damageTracker === 2) { + const { sum, max, min, range, mean, median, mode, lowerQ, upperQ, iqr, variance, stdDev } = + statisticalAnalysis(damages); + + const time = Date.now() / 1000 - start; + ChatLib.chat( + `\n${LOGO + GOLD + BOLD}Damage Statistical Analysis ${GRAY}[${formattime(time, 2)}] ${RED + BOLD}Extremas: ${DARK_GRAY}- ${YELLOW}Max Damage: ${WHITE + formatNumber(max * 20)} @@ -146,12 +161,15 @@ ${RED + BOLD}Interquartiles: ${RED + BOLD}Dispersion: ${DARK_GRAY}- ${YELLOW}Variance: ${WHITE + formatNumber(variance)} ${DARK_GRAY}- ${YELLOW}Standard Deviation: ${WHITE + formatNumber(stdDev)} -\n${GOLD + BOLD}DPS: ${WHITE + formatNumber(sum / time)}\n`); - } - - unique.clear(); - damages.length = 0; - start = 0; - last = 0; +\n${GOLD + BOLD}DPS: ${WHITE + formatNumber(sum / time)}\n` + ); + } + + unique.clear(); + damages.length = 0; + start = 0; + last = 0; } -}), () => Settings.damageTracker !== 0); + }), + () => Settings.damageTracker !== 0 +); diff --git a/features/combat/EntityDetect.js b/features/combat/EntityDetect.js index 6647333..23b946c 100644 --- a/features/combat/EntityDetect.js +++ b/features/combat/EntityDetect.js @@ -1,10 +1,9 @@ -import Settings from "../../utils/Settings"; -import { SMA, EntityArmorStand, STAND_CLASS } from "../../utils/Constants"; -import { convertToPascalCase, unformatNumber } from "../../utils/functions/format"; -import { registerWhen } from "../../utils/RegisterTils"; +import { EntityArmorStand, SMA, STAND_CLASS } from "../../utils/Constants"; import { data } from "../../utils/Data"; +import { registerWhen } from "../../utils/RegisterTils"; +import Settings from "../../utils/Settings"; import Waypoint from "../../utils/Waypoint"; - +import { convertToPascalCase, unformatNumber } from "../../utils/functions/format"; /** * Variables used to track active entities. @@ -24,63 +23,63 @@ let yBound = 0; * @returns {Boolean} - True if entity class is identified and added to the list. */ const CLASS_TYPES = ["client.entity", "entity.monster", "entity.boss", "entity.passive"]; -function testClass(entity, health, index=0) { - // Set color map - const [uR, uG, uB] = data.colorlist[entity]?.split(' ') ?? []; - const rgb = Settings.hitboxColor; - const r = (isNaN(uR) ? Math.random() * (255 - rgb.blue) : uR) / 255; - const g = (isNaN(uG) ? Math.random() * (255 - rgb.red) : uG) / 255; - const b = (isNaN(uB) ? Math.random() * (255 - rgb.green) : uB) / 255; +function testClass(entity, health, index = 0) { + // Set color map + const [uR, uG, uB] = data.colorlist[entity]?.split(" ") ?? []; + const rgb = Settings.hitboxColor; + const r = (isNaN(uR) ? Math.random() * (255 - rgb.blue) : uR) / 255; + const g = (isNaN(uG) ? Math.random() * (255 - rgb.red) : uG) / 255; + const b = (isNaN(uB) ? Math.random() * (255 - rgb.green) : uB) / 255; - try { - const type = `net.minecraft.${CLASS_TYPES[index]}.Entity${entity}`; - const entityClass = Java.type(type).class; - World.getAllEntitiesOfType(entityClass); + try { + const type = `net.minecraft.${CLASS_TYPES[index]}.Entity${entity}`; + const entityClass = Java.type(type).class; + World.getAllEntitiesOfType(entityClass); - entityWaypoints[entity] = [new Waypoint([r, g, b], 3, true, false, false), entityClass, health]; - return true; - } catch(err) { - if (index === CLASS_TYPES.length - 1) { - standWaypoints[entity] = new Waypoint([r, g, b], 3, true, false, false); - return false; - } - - return testClass(entity, health, index + 1); + entityWaypoints[entity] = [new Waypoint([r, g, b], 3, true, false, false), entityClass, health]; + return true; + } catch (err) { + if (index === CLASS_TYPES.length - 1) { + standWaypoints[entity] = new Waypoint([r, g, b], 3, true, false, false); + return false; } + + return testClass(entity, health, index + 1); + } } /** * Updates entity list based on mob data, processing HP and class information. */ export function updateEntityList() { - // Reset entity list - xBound = 0; - yBound = 0; - Object.keys(entityWaypoints).forEach(entity => { - entityWaypoints[entity][0].delete(); - delete entityWaypoints[entity]; - }); - Object.keys(standWaypoints).forEach(stand => { - standWaypoints[stand].delete(); - delete standWaypoints[stand]; - }); + // Reset entity list + xBound = 0; + yBound = 0; + Object.keys(entityWaypoints).forEach((entity) => { + entityWaypoints[entity][0].delete(); + delete entityWaypoints[entity]; + }); + Object.keys(standWaypoints).forEach((stand) => { + standWaypoints[stand].delete(); + delete standWaypoints[stand]; + }); - // Update moblist objects - data.moblist.forEach(mob => { - const args = mob.split(' '); - const health = unformatNumber(args.pop()); - const entity = convertToPascalCase(health === 0 ? mob : args.join(' ')); - - if (!USED.has(mob) && !testClass(entity, health)) { - // Check for bounds otherwise set as armor stand detection - const remaining = parseInt(mob.substring(1)); - if (isNaN(remaining)) return; - - const front = mob[0].toLowerCase(); - if (front === 'x') xBound = remaining; - else if (front === 'y') yBound = remaining; - } - }); + // Update moblist objects + data.moblist.forEach((mob) => { + const args = mob.split(" "); + const health = unformatNumber(args.pop()); + const entity = convertToPascalCase(health === 0 ? mob : args.join(" ")); + + if (!USED.has(mob) && !testClass(entity, health)) { + // Check for bounds otherwise set as armor stand detection + const remaining = parseInt(mob.substring(1)); + if (isNaN(remaining)) return; + + const front = mob[0].toLowerCase(); + if (front === "x") xBound = remaining; + else if (front === "y") yBound = remaining; + } + }); } updateEntityList(); @@ -88,47 +87,57 @@ updateEntityList(); * Creates colored entity list from entity data in `entityList`. * Determines color based on class and filters by HP if applicable. */ -registerWhen(register("step", () => { +registerWhen( + register("step", () => { // Refresh entities every 0.5s - Object.keys(entityWaypoints).forEach(entity => { - // Match coloring - const entityWaypoint = entityWaypoints[entity][0]; - const entityClass = entityWaypoints[entity][1]; - const health = entityWaypoints[entity][2]; - entityWaypoint.clear(); + Object.keys(entityWaypoints).forEach((entity) => { + // Match coloring + const entityWaypoint = entityWaypoints[entity][0]; + const entityClass = entityWaypoints[entity][1]; + const health = entityWaypoints[entity][2]; + entityWaypoint.clear(); - // Add entities - World.getAllEntitiesOfType(entityClass).forEach(entity => { - const mob = entity.getEntity(); - if ((health !== 0 && mob.func_110148_a(SMA.field_111267_a).func_111125_b() !== health) || - xBound !== 0 && Math.abs(Player.getX() - entity.getX()) > xBound || - yBound !== 0 && Math.abs(Player.getY() - entity.getY()) > yBound || - mob.func_110143_aJ() === 0) return; - - entityWaypoint.push(['', entity]); - }); + // Add entities + World.getAllEntitiesOfType(entityClass).forEach((entity) => { + const mob = entity.getEntity(); + if ( + (health !== 0 && mob.func_110148_a(SMA.field_111267_a).func_111125_b() !== health) || + (xBound !== 0 && Math.abs(Player.getX() - entity.getX()) > xBound) || + (yBound !== 0 && Math.abs(Player.getY() - entity.getY()) > yBound) || + mob.func_110143_aJ() === 0 + ) + return; + + entityWaypoint.push(["", entity]); + }); }); // Refresh stands every 0.5s const keys = Object.keys(standWaypoints); - keys.forEach(key => standWaypoints[key].clear()); + keys.forEach((key) => standWaypoints[key].clear()); if (keys.length === 0) return; - World.getAllEntitiesOfType(STAND_CLASS).forEach(stand => { - keys.forEach(key => { - if (stand.getName().includes(key)) { - // Find closest entity to armor stand - const standEntity = stand.getEntity(); - const closestEntity = World.getWorld().func_72839_b(standEntity, standEntity.func_174813_aQ().func_72314_b(1, 5, 1)) - .filter(entity => entity && !(entity instanceof EntityArmorStand) && entity !== Player.getPlayer()) - .reduce((closest, entity) => { - const distance = stand.distanceTo(entity); - return distance < closest.distance ? { entity, distance } : closest; - }, { entity: standEntity, distance: 20 }); + World.getAllEntitiesOfType(STAND_CLASS).forEach((stand) => { + keys.forEach((key) => { + if (stand.getName().includes(key)) { + // Find closest entity to armor stand + const standEntity = stand.getEntity(); + const closestEntity = World.getWorld() + .func_72839_b(standEntity, standEntity.func_174813_aQ().func_72314_b(1, 5, 1)) + .filter((entity) => entity && !(entity instanceof EntityArmorStand) && entity !== Player.getPlayer()) + .reduce( + (closest, entity) => { + const distance = stand.distanceTo(entity); + return distance < closest.distance ? { entity, distance } : closest; + }, + { entity: standEntity, distance: 20 } + ); - if (!closestEntity) return; - standWaypoints[key].push(['', closestEntity.entity]); - } - }); + if (!closestEntity) return; + standWaypoints[key].push(["", closestEntity.entity]); + } + }); }); -}).setFps(2), () => Object.keys(entityWaypoints).length > 0); + }).setFps(2), + () => Object.keys(entityWaypoints).length > 0 +); diff --git a/features/combat/GyroTimer.js b/features/combat/GyroTimer.js index 0fc1434..30b35ef 100644 --- a/features/combat/GyroTimer.js +++ b/features/combat/GyroTimer.js @@ -2,12 +2,11 @@ * ARCHIVED */ -import Settings from "../../utils/Settings"; import { BOLD, DARK_RED, GREEN, RED, RESET } from "../../utils/Constants"; -import { registerWhen } from "../../utils/RegisterTils"; -import { Overlay } from "../../utils/Overlay"; import { data } from "../../utils/Data"; - +import { Overlay } from "../../utils/Overlay"; +import { registerWhen } from "../../utils/RegisterTils"; +import Settings from "../../utils/Settings"; /** * Variables used to track and display Cells Alignment time. @@ -21,23 +20,26 @@ const gyroOverlay = new Overlay("gyroTimer", data.GL, "moveGyro", gyroExample); * Detects whenever you get affected by "Cell's Alignment". */ register("chat", () => { - align = 6.0; - cd = 10; + align = 6.0; + cd = 10; }).setCriteria("You aligned ${message}"); register("chat", () => { - align = 6.0; + align = 6.0; }).setCriteria("${player} casted Cells Alignment on you!"); /** * Adjustes the align timer overlay and alerts player whenever align is about to run out. */ -registerWhen(register("tick", () => { +registerWhen( + register("tick", () => { if (align > 0) { - if (Settings.gyroAlert && align > 0.5 && align < 1 && cd === 0) - Client.showTitle(`${DARK_RED + BOLD}USE ALIGN`, "", 0, 25, 5); - align = (align - 0.05).toFixed(2); - gyroOverlay.setMessage(`${GREEN + BOLD}Align Timer: ${RESET + align}s`); + if (Settings.gyroAlert && align > 0.5 && align < 1 && cd === 0) + Client.showTitle(`${DARK_RED + BOLD}USE ALIGN`, "", 0, 25, 5); + align = (align - 0.05).toFixed(2); + gyroOverlay.setMessage(`${GREEN + BOLD}Align Timer: ${RESET + align}s`); } else gyroOverlay.setMessage(`${GREEN + BOLD}Align Timer: ${RED}NO ALIGN`); - + if (cd > 0) cd = (cd - 0.05).toFixed(2); -}), () => Settings.gyroAlert || Settings.gyroTimer); + }), + () => Settings.gyroAlert || Settings.gyroTimer +); diff --git a/features/combat/HealthAlert.js b/features/combat/HealthAlert.js index fc3e5b3..d8c070f 100644 --- a/features/combat/HealthAlert.js +++ b/features/combat/HealthAlert.js @@ -1,9 +1,8 @@ -import Settings from "../../utils/Settings"; import { BOLD, DARK_RED, RESET } from "../../utils/Constants"; import { registerWhen } from "../../utils/RegisterTils"; +import Settings from "../../utils/Settings"; import { setTitle } from "../../utils/Title"; - /** * Variable used to represent player entity. */ @@ -12,16 +11,26 @@ let player = undefined; /** * Tracks player health and alerts whenever below the chosen threshold. */ -registerWhen(register("step", () => { +registerWhen( + register("step", () => { if (player === undefined) return; if (player.func_110143_aJ() / player.func_110138_aP() < Settings.healthAlert) - setTitle(`${DARK_RED + BOLD}WARNING: HEALTH BELOW ${RESET + Math.round(Settings.healthAlert * 100)}%${DARK_RED}!`, "", 0, 25, 5, 10); -}).setFps(2), () => Settings.healthAlert !== 0); + setTitle( + `${DARK_RED + BOLD}WARNING: HEALTH BELOW ${RESET + Math.round(Settings.healthAlert * 100)}%${DARK_RED}!`, + "", + 0, + 25, + 5, + 10 + ); + }).setFps(2), + () => Settings.healthAlert !== 0 +); /** * Reload player entity on every world join. */ export function setPlayer() { - player = Player.asPlayerMP().getEntity(); -}; + player = Player.asPlayerMP().getEntity(); +} diff --git a/features/combat/KillCounter.js b/features/combat/KillCounter.js index d481d2a..5ee7376 100644 --- a/features/combat/KillCounter.js +++ b/features/combat/KillCounter.js @@ -1,11 +1,10 @@ -import Settings from "../../utils/Settings"; import { BOLD, DARK_RED, EntityArmorStand, GRAY, GREEN, LOGO, RED, RESET } from "../../utils/Constants"; -import { formatNumber, formatTime } from "../../utils/functions/format"; -import { registerWhen } from "../../utils/RegisterTils"; +import { data } from "../../utils/Data"; import { Overlay } from "../../utils/Overlay"; +import { registerWhen } from "../../utils/RegisterTils"; +import Settings from "../../utils/Settings"; import { getPaused } from "../../utils/Stat"; -import { data } from "../../utils/Data"; - +import { formatNumber, formatTime } from "../../utils/functions/format"; /** * Variables used to track and display item and vanquisher kill counts. @@ -15,8 +14,7 @@ let mobs = {}; let items = {}; let total = 0; let time = 0; -const counterExample = -`${RED + BOLD}メリオダス: ${RESET}0/0 +const counterExample = `${RED + BOLD}メリオダス: ${RESET}0/0 ${RED + BOLD}ディアンヌ: ${RESET}∞/∞ ${RED + BOLD}バン: ${RESET}0*∞ ${RED + BOLD}キング: ${RESET}1^∞ @@ -30,25 +28,27 @@ const counterOverlay = new Overlay("killCounter", data.JL, "moveKills", counterE counterOverlay.setMessage(""); function updateCounter() { - // Sort the mobs object - const sortedMobs = Object.keys(mobs) - .sort((a, b) => mobs[b] - mobs[a]); + // Sort the mobs object + const sortedMobs = Object.keys(mobs).sort((a, b) => mobs[b] - mobs[a]); - // Format overlay + change message - const messageLines = sortedMobs.map(mob => { - const kills = mobs[mob]; - return `${RED + BOLD + mob}: ${RESET + formatNumber(kills) + GRAY} (${formatNumber(kills / time * 3600)}/hr)`; - }); + // Format overlay + change message + const messageLines = sortedMobs.map((mob) => { + const kills = mobs[mob]; + return `${RED + BOLD + mob}: ${RESET + formatNumber(kills) + GRAY} (${formatNumber((kills / time) * 3600)}/hr)`; + }); - counterOverlay.setMessage(messageLines.join('\n') + - `\n\n${DARK_RED + BOLD}Total: ${RESET + formatNumber(total) + GRAY} (${formatNumber(total / time * 3600)}/hr)` + - `\n${DARK_RED + BOLD}Time: ${RESET + formatTime(time)}`); + counterOverlay.setMessage( + messageLines.join("\n") + + `\n\n${DARK_RED + BOLD}Total: ${RESET + formatNumber(total) + GRAY} (${formatNumber((total / time) * 3600)}/hr)` + + `\n${DARK_RED + BOLD}Time: ${RESET + formatTime(time)}` + ); } /** * Uses the "Book of Stats" to track whenever player mobs an entity and updates the Vanquisher Overlay. */ -registerWhen(register("entityDeath", (death) => { +registerWhen( + register("entityDeath", (death) => { // Check return const held = Player.getHeldItem(); if (held === null) return; @@ -61,8 +61,8 @@ registerWhen(register("entityDeath", (death) => { const newKills = extraAttributes.getInteger("stats_book"); if (!(heldItemId in items)) { - items[heldItemId] = newKills; - return; + items[heldItemId] = newKills; + return; } const killsDiff = Math.abs(newKills - items[heldItemId]); @@ -70,13 +70,22 @@ registerWhen(register("entityDeath", (death) => { // Get surrounding death to find title stand const deathEntity = death.getEntity(); - World.getWorld().func_72839_b(deathEntity, deathEntity.func_174813_aQ().func_72314_b(1, 3, 1)).filter(entity => - entity instanceof EntityArmorStand && - entity?.func_95999_t()?.removeFormatting().endsWith("❤") && - !stands.has(entity.persistentID) - ).forEach(entity => { + World.getWorld() + .func_72839_b(deathEntity, deathEntity.func_174813_aQ().func_72314_b(1, 3, 1)) + .filter( + (entity) => + entity instanceof EntityArmorStand && + entity?.func_95999_t()?.removeFormatting().endsWith("❤") && + !stands.has(entity.persistentID) + ) + .forEach((entity) => { const title = entity?.func_95999_t()?.removeFormatting(); - const name = title.replace(/[^a-zA-Z ]/g, '').split(' ').slice(0, -1).join(' ').replace("Lv ", ""); + const name = title + .replace(/[^a-zA-Z ]/g, "") + .split(" ") + .slice(0, -1) + .join(" ") + .replace("Lv ", ""); if (name in mobs) mobs[name]++; else mobs[name] = 1; @@ -85,27 +94,32 @@ registerWhen(register("entityDeath", (death) => { items[heldItemId]++; updateCounter(); stands.add(entity.persistentID); - }); -}), () => Settings.killCounter); + }); + }), + () => Settings.killCounter +); /** * Track time and reset stand ids */ -registerWhen(register("step", () => { +registerWhen( + register("step", () => { if (Object.keys(mobs).length === 0 || getPaused()) return; - + time++; updateCounter(); stands.clear(); -}).setFps(1), () => Settings.vanqCounter !== 0); + }).setFps(1), + () => Settings.vanqCounter !== 0 +); /** * Command to reset the stats for the overall counter. */ register("command", () => { - mobs = {} - total = 0; - time = 0; - counterOverlay.setMessage(""); - ChatLib.chat(`${LOGO + GREEN}Successfully reset kill counter!`) + mobs = {}; + total = 0; + time = 0; + counterOverlay.setMessage(""); + ChatLib.chat(`${LOGO + GREEN}Successfully reset kill counter!`); }).setName("resetKills"); diff --git a/features/combat/ManaDrain.js b/features/combat/ManaDrain.js index 50de3be..01cb2cb 100644 --- a/features/combat/ManaDrain.js +++ b/features/combat/ManaDrain.js @@ -1,11 +1,10 @@ import RenderLib from "../../../RenderLib"; -import Settings from "../../utils/Settings"; -import Waypoint from "../../utils/Waypoint"; import { AQUA, PLAYER_CLASS } from "../../utils/Constants"; import { registerWhen } from "../../utils/RegisterTils"; -import { isPlayer } from "../../utils/functions/player"; +import Settings from "../../utils/Settings"; import { setTitle } from "../../utils/Title"; - +import Waypoint from "../../utils/Waypoint"; +import { isPlayer } from "../../utils/functions/player"; /** * Register hitbox rendering @@ -13,37 +12,40 @@ import { setTitle } from "../../utils/Title"; const nearWaypoints = new Waypoint([0.41, 0.76, 0.96], 3, true, false, false); const remderWorld = register("renderWorld", (pt) => { - const entity = Player.asPlayerMP().getEntity(); - const x = entity.field_70165_t * pt - entity.field_70142_S * (pt - 1); - const y = entity.field_70163_u * pt - entity.field_70137_T * (pt - 1); - const z = entity.field_70161_v * pt - entity.field_70136_U * (pt - 1); - RenderLib.drawSphere(x, y + 1, z, 5, 20, 20, -90, 0, 0, 1, 1, 0, 0.5, false, false); + const entity = Player.asPlayerMP().getEntity(); + const x = entity.field_70165_t * pt - entity.field_70142_S * (pt - 1); + const y = entity.field_70163_u * pt - entity.field_70137_T * (pt - 1); + const z = entity.field_70161_v * pt - entity.field_70136_U * (pt - 1); + RenderLib.drawSphere(x, y + 1, z, 5, 20, 20, -90, 0, 0, 1, 1, 0, 0.5, false, false); }).unregister(); /** * Updates nearby players */ -registerWhen(register("step", () => { +registerWhen( + register("step", () => { nearWaypoints.clear(); const heldName = Player.getHeldItem()?.getName(); // Check if player is holding a mana draining item - const render = heldName !== undefined && (heldName.includes("flux Power Orb") || heldName.endsWith("End Stone Sword")) + const render = + heldName !== undefined && (heldName.includes("flux Power Orb") || heldName.endsWith("End Stone Sword")); if (render) { - remderWorld.register(); + remderWorld.register(); } else { - remderWorld.unregister(); - return; + remderWorld.unregister(); + return; } // Add waypoints for nearby players const player = Player.asPlayerMP(); - World.getAllEntitiesOfType(PLAYER_CLASS).forEach(other => { - if (isPlayer(other) && player.distanceTo(other) < 5) - nearWaypoints.push(['', other]); + World.getAllEntitiesOfType(PLAYER_CLASS).forEach((other) => { + if (isPlayer(other) && player.distanceTo(other) < 5) nearWaypoints.push(["", other]); }); // Show title const length = nearWaypoints.getWaypoints().length; setTitle(`${length + AQUA} nearby player${length === 1 ? "" : "s"}!`, "", 0, 15, 5, 50); -}).setFps(2), () => Settings.manaDrain); + }).setFps(2), + () => Settings.manaDrain +); diff --git a/features/combat/RagDetect.js b/features/combat/RagDetect.js index 79ec8ae..e5aafe2 100644 --- a/features/combat/RagDetect.js +++ b/features/combat/RagDetect.js @@ -1,9 +1,8 @@ -import Settings from "../../utils/Settings"; -import { BOLD, DARK_GRAY, GOLD, GRAY, RED, WHITE } from "../../utils/Constants"; +import { BOLD, DARK_GRAY, GOLD, RED, WHITE } from "../../utils/Constants"; import { registerWhen } from "../../utils/RegisterTils"; -import { formatNumber } from "../../utils/functions/format"; +import Settings from "../../utils/Settings"; import { setTitle } from "../../utils/Title"; - +import { formatNumber } from "../../utils/functions/format"; /** * Variable used to represent player's held item. @@ -13,19 +12,36 @@ let heldItem = undefined; /** * Tracks action bar for "CASTING" and held item to detect when Ragnarok ability goes off. */ -registerWhen(register("actionBar", () => { +registerWhen( + register("actionBar", () => { if (Player.getHeldItem() === null) return; heldItem = Player.getHeldItem().getNBT().getCompoundTag("tag").getCompoundTag("ExtraAttributes").getString("id"); if (heldItem.equals("RAGNAROCK_AXE")) { - const strength = Player.getHeldItem().getLore().find(line => line.startsWith("§5§o§7Strength:"))?.split(' ')?.[1]?.substring(3) ?? 0; - setTitle(`${GOLD + BOLD}AWOOGA!`, strength === 0 ? "" : `${DARK_GRAY}+${WHITE + formatNumber(strength * 1.5) + RED} Strength`, 0, 25, 99); + const strength = + Player.getHeldItem() + .getLore() + .find((line) => line.startsWith("§5§o§7Strength:")) + ?.split(" ")?.[1] + ?.substring(3) ?? 0; + setTitle( + `${GOLD + BOLD}AWOOGA!`, + strength === 0 ? "" : `${DARK_GRAY}+${WHITE + formatNumber(strength * 1.5) + RED} Strength`, + 0, + 25, + 99 + ); } -}).setCriteria("${before}CASTING"), () => Settings.ragDetect); + }).setCriteria("${before}CASTING"), + () => Settings.ragDetect +); /** * Tracks chat for rag cancelled message to display alert on screen. */ -registerWhen(register("chat", () => { +registerWhen( + register("chat", () => { setTitle(`${RED + BOLD}RAGNAROCK CANCELLED!`, "", 0, 25, 5, 99); -}).setCriteria("Ragnarock was cancelled due to taking damage!"), () => Settings.ragDetect); + }).setCriteria("Ragnarock was cancelled due to taking damage!"), + () => Settings.ragDetect +); diff --git a/features/combat/SlayerDetect.js b/features/combat/SlayerDetect.js index 3782a35..4ab5211 100644 --- a/features/combat/SlayerDetect.js +++ b/features/combat/SlayerDetect.js @@ -1,13 +1,24 @@ +import { + BLAZE_CLASS, + BOLD, + DARK_GREEN, + ENDERMAN_CLASS, + GREEN, + RED, + SMA, + SPIDER_CLASS, + WHITE, + WOLF_CLASS, + ZOMBIE_CLASS, +} from "../../utils/Constants"; import location from "../../utils/Location"; +import { registerWhen } from "../../utils/RegisterTils"; import Settings from "../../utils/Settings"; +import { delay } from "../../utils/ThreadTils"; +import { setTitle } from "../../utils/Title"; import Waypoint from "../../utils/Waypoint"; -import { BLAZE_CLASS, BOLD, DARK_GREEN, ENDERMAN_CLASS, GREEN, RED, SMA, SPIDER_CLASS, WHITE, WOLF_CLASS, ZOMBIE_CLASS } from "../../utils/Constants"; import { romanToNum } from "../../utils/functions/format"; import { announceMob } from "../../utils/functions/misc"; -import { registerWhen } from "../../utils/RegisterTils"; -import { delay } from "../../utils/ThreadTils"; -import { setTitle } from "../../utils/Title"; - /** * Slayer alert variables. @@ -16,136 +27,164 @@ let miniCD = false; let bossCD = false; let slainCD = false; let questStart = true; -export function getSlayerBoss() { return bossCD }; +export function getSlayerBoss() { + return bossCD; +} /** * Uses sound name, volume, and pitch, to detect when slayer miniboss spawns. */ -registerWhen(register("soundPlay", (_, __, vol, pitch) => { +registerWhen( + register("soundPlay", (_, __, vol, pitch) => { if (miniCD || vol != 0.6000000238418579 || pitch != 1.2857142686843872) return; - + if (Settings.miniAlert === 3) setTitle(`${GREEN + BOLD}SLAYER MINIBOSS SPAWNED!`, "", 5, 25, 5, 55); else announceMob(Settings.miniAlert, "Miniboss Slayer", Player.getX(), Player.getY(), Player.getZ()); miniCD = true; - delay(() => miniCD = false, 3000); -}).setCriteria("random.explode"), () => Settings.miniAlert !== 0); + delay(() => (miniCD = false), 3000); + }).setCriteria("random.explode"), + () => Settings.miniAlert !== 0 +); /** * Uses scoreboard to detect if a slayer boss is active. */ -registerWhen(register("tick", () => { +registerWhen( + register("tick", () => { if (bossCD) { - // Announce if boss is dead - if (!slainCD && Scoreboard?.getLines()?.find(line => line.getName().startsWith("§aBoss slain!")) !== undefined) { - slainCD = true; - if (Settings.bossAlert === 3) setTitle(`${DARK_GREEN + BOLD}SLAYER BOSS SLAIN!`, "", 5, 25, 5, 56); - else if (Settings.bossAlert === 2) ChatLib.command("pc Slayer Boss Slain!"); - else if (Settings.bossAlert === 1) { - const id = `@${(Math.random() + 1).toString(36).substring(6)} ${(Math.random() + 1).toString(36).substring(9)}`; - ChatLib.command("ac Slayer Boss Slain! " + id); - } + // Announce if boss is dead + if ( + !slainCD && + Scoreboard?.getLines()?.find((line) => line.getName().startsWith("§aBoss slain!")) !== undefined + ) { + slainCD = true; + if (Settings.bossAlert === 3) setTitle(`${DARK_GREEN + BOLD}SLAYER BOSS SLAIN!`, "", 5, 25, 5, 56); + else if (Settings.bossAlert === 2) ChatLib.command("pc Slayer Boss Slain!"); + else if (Settings.bossAlert === 1) { + const id = `@${(Math.random() + 1).toString(36).substring(6)} ${(Math.random() + 1) + .toString(36) + .substring(9)}`; + ChatLib.command("ac Slayer Boss Slain! " + id); } - return; + } + return; } // Check for slain boss line to reset tracker - if (!questStart || Scoreboard?.getLines()?.find(line => line.getName().startsWith("§eSlay the boss!")) === undefined) return; - + if ( + !questStart || + Scoreboard?.getLines()?.find((line) => line.getName().startsWith("§eSlay the boss!")) === undefined + ) + return; + bossCD = true; slainCD = false; questStart = false; if (Settings.bossAlert === 3) setTitle(`${RED + BOLD}SLAYER BOSS SPAWNED!`, "", 5, 25, 5, 58); - else if (Settings.bossAlert !== 0) announceMob(Settings.bossAlert, "Boss Slayer", Player.getX(), Player.getY(), Player.getZ()); -}), () => Settings.bossAlert !== 0 || Settings.slayerSpawn !== 0 || -(location.getWorld() === "The Rift" && (Settings.vampireAttack || Settings.announceMania))); + else if (Settings.bossAlert !== 0) + announceMob(Settings.bossAlert, "Boss Slayer", Player.getX(), Player.getY(), Player.getZ()); + }), + () => + Settings.bossAlert !== 0 || + Settings.slayerSpawn !== 0 || + (location.getWorld() === "The Rift" && (Settings.vampireAttack || Settings.announceMania)) +); /** * Close to spawn alert */ let warned = false; -registerWhen(register("step", () => { +registerWhen( + register("step", () => { if (warned) return; if (Settings.slayerSpawn === 100) { - if (Scoreboard?.getLines()?.find(line => line.getName().includes("Slay the boss!")) !== undefined) { - setTitle(`${RED + BOLD}SLAYER BOSS SPAWNED!`, "", 5, 25, 5, 58); - warned = true; - } - return; + if (Scoreboard?.getLines()?.find((line) => line.getName().includes("Slay the boss!")) !== undefined) { + setTitle(`${RED + BOLD}SLAYER BOSS SPAWNED!`, "", 5, 25, 5, 58); + warned = true; + } + return; } - const bossLine = Scoreboard?.getLines()?.find(line => line.getName().endsWith("lls")); + const bossLine = Scoreboard?.getLines()?.find((line) => line.getName().endsWith("lls")); if (bossLine === undefined) return; - const [_, c, t] = bossLine.getName().removeFormatting().match(/(\d+)\/(\d+)/); - const percent = Math.round(c/t * 100); + const [_, c, t] = bossLine + .getName() + .removeFormatting() + .match(/(\d+)\/(\d+)/); + const percent = Math.round((c / t) * 100); if (percent > Settings.slayerSpawn) { - setTitle(`${RED + BOLD}SLAYER BOSS SPAWNING SOON (${WHITE + percent}%${RED})`, "", 5, 25, 5, 57); - warned = true; + setTitle(`${RED + BOLD}SLAYER BOSS SPAWNING SOON (${WHITE + percent}%${RED})`, "", 5, 25, 5, 57); + warned = true; } -}).setFps(2), () => Settings.slayerSpawn !== 0); + }).setFps(2), + () => Settings.slayerSpawn !== 0 +); /** * Uses chat to track slayer quest state. */ -register("chat", () => { delay(() => questStart = true, 500) }).setCriteria(" SLAYER QUEST STARTED!"); register("chat", () => { - bossCD = false; - warned = false; + delay(() => (questStart = true), 500); +}).setCriteria(" SLAYER QUEST STARTED!"); +register("chat", () => { + bossCD = false; + warned = false; }).setCriteria(" SLAYER QUEST COMPLETE!"); register("chat", () => { - bossCD = false; - warned = false; + bossCD = false; + warned = false; }).setCriteria(" SLAYER QUEST FAILED!"); - /** * Boss and miniboss highlighting. * Varibles for different slayer mob classes. */ const MOB_CLASSES = { - "Revenant": ZOMBIE_CLASS, - "Tarantula": SPIDER_CLASS, - "Sven": WOLF_CLASS, - "Voidgloom": ENDERMAN_CLASS, - "Inferno": BLAZE_CLASS + Revenant: ZOMBIE_CLASS, + Tarantula: SPIDER_CLASS, + Sven: WOLF_CLASS, + Voidgloom: ENDERMAN_CLASS, + Inferno: BLAZE_CLASS, }; const BOSS_HPS = { - "Revenant": [500, 20_000, 400_000, 1_500_000, 10_000_000], - "Tarantula": [750, 30_000, 900_000, 2_400_000], - "Sven": [2_000, 40_000, 750_000, 2_000_000], - "Voidgloom": [300_000, 12_000_000, 50_000_000, 210_000_000], - "Inferno": [2_500_000, 10_000_000, 75_000_000, 150_000_000] -} + Revenant: [500, 20_000, 400_000, 1_500_000, 10_000_000], + Tarantula: [750, 30_000, 900_000, 2_400_000], + Sven: [2_000, 40_000, 750_000, 2_000_000], + Voidgloom: [300_000, 12_000_000, 50_000_000, 210_000_000], + Inferno: [2_500_000, 10_000_000, 75_000_000, 150_000_000], +}; const MINI_HPS = { - "Revenant": [new Set([24_000]), new Set([90_000, 360_000]), new Set([600_000, 2_400_000])], - "Tarantula": [new Set([54_000]), new Set([144_000, 576_000])], - "Sven": [new Set([45_000]), new Set([120_000, 480_000])], - "Voidgloom": [new Set([12_000_000]), new Set([25_000_000, 75_000_000])], - "Inferno": [new Set([12_000_000]), new Set([25_000_000, 75_000_000])] + Revenant: [new Set([24_000]), new Set([90_000, 360_000]), new Set([600_000, 2_400_000])], + Tarantula: [new Set([54_000]), new Set([144_000, 576_000])], + Sven: [new Set([45_000]), new Set([120_000, 480_000])], + Voidgloom: [new Set([12_000_000]), new Set([25_000_000, 75_000_000])], + Inferno: [new Set([12_000_000]), new Set([25_000_000, 75_000_000])], }; const SLAYER_COLORS = { - "Revenant": [0, 0.08, 0.05], - "Tarantula": [0.55, 0, 0], - "Sven": [255, 255, 255], - "Voidgloom": [0.58, 0, 0.83], - "Inferno": [0.55, 0, 0] -} + Revenant: [0, 0.08, 0.05], + Tarantula: [0.55, 0, 0], + Sven: [255, 255, 255], + Voidgloom: [0.58, 0, 0.83], + Inferno: [0.55, 0, 0], +}; const bossWaypoints = new Waypoint([1, 1, 1], 2, true, true, false); const miniWaypoints = new Waypoint([1, 1, 1], 2, true, true, false); /** * Track world for mobs that match miniboss healths depending on slayer quest. */ -registerWhen(register("step", () => { +registerWhen( + register("step", () => { bossWaypoints.clear(); miniWaypoints.clear(); - const index = Scoreboard.getLines().findIndex(line => line.getName().startsWith("Slayer Quest")) - 1; + const index = Scoreboard.getLines().findIndex((line) => line.getName().startsWith("Slayer Quest")) - 1; if (index === -2) return; // Get slayer data - const quest = Scoreboard.getLineByIndex(index).getName().removeFormatting().split(' '); + const quest = Scoreboard.getLineByIndex(index).getName().removeFormatting().split(" "); const tier = romanToNum(quest[quest.length - 1]); const type = quest[0]; const mobClass = MOB_CLASSES[type]; @@ -160,9 +199,11 @@ registerWhen(register("step", () => { miniWaypoints.setColor(SLAYER_COLORS[type]); // Check mobs - World.getAllEntitiesOfType(mobClass).forEach(mob => { - const hp = mob.getEntity().func_110148_a(SMA.field_111267_a).func_111125_b(); - if (bossHP == hp && Settings.bossHighlight) bossWaypoints.push([RED + "Boss", mob]); - else if (miniSet.has(hp) && Settings.miniHighlight) miniWaypoints.push([RED + "Mini", mob]); + World.getAllEntitiesOfType(mobClass).forEach((mob) => { + const hp = mob.getEntity().func_110148_a(SMA.field_111267_a).func_111125_b(); + if (bossHP == hp && Settings.bossHighlight) bossWaypoints.push([RED + "Boss", mob]); + else if (miniSet.has(hp) && Settings.miniHighlight) miniWaypoints.push([RED + "Mini", mob]); }); -}).setFps(2), () => Settings.bossHighlight || Settings.miniHighlight); + }).setFps(2), + () => Settings.bossHighlight || Settings.miniHighlight +); diff --git a/features/combat/WatcherAlert.js b/features/combat/WatcherAlert.js index 4d2acc9..49a3bae 100644 --- a/features/combat/WatcherAlert.js +++ b/features/combat/WatcherAlert.js @@ -1,21 +1,26 @@ import Settings from "../../Settings"; -import { AMOGUS, BOLD, GOLD, GREEN } from "../../utils/Constants"; +import { AMOGUS, BOLD, GOLD } from "../../utils/Constants"; import { playSound } from "../../utils/functions/misc"; import { registerWhen } from "../../utils/RegisterTils"; import { setTitle } from "../../utils/Title"; - /** * Tracks chat messages from "The Watcher" to call an emergency meeting whenever he finishes spawning (meant for f11 blood camps). * Includes two messages since messages glitch out if mobs are killed too fast. */ -registerWhen(register("chat", () => { +registerWhen( + register("chat", () => { if (cd) return; setTitle(`${GOLD + BOLD}BLOOD COMPLETE!`, "", 0, 25, 5, 70); playSound(AMOGUS, 3000); -}).setCriteria("[BOSS] The Watcher: You have proven yourself. You may pass."), () => Settings.watcherAlert); -registerWhen(register("chat", () => { + }).setCriteria("[BOSS] The Watcher: You have proven yourself. You may pass."), + () => Settings.watcherAlert +); +registerWhen( + register("chat", () => { if (cd) return; setTitle(`${GOLD + BOLD}BLOOD SPAWNED!`, "", 0, 25, 5, 70); playSound(AMOGUS, 3000); -}).setCriteria("[BOSS] The Watcher: That will be enough for now."), () => Settings.watcherAlert); + }).setCriteria("[BOSS] The Watcher: That will be enough for now."), + () => Settings.watcherAlert +); diff --git a/features/container/ArmorDisplay.js b/features/container/ArmorDisplay.js index 573bb5f..d9f8f72 100644 --- a/features/container/ArmorDisplay.js +++ b/features/container/ArmorDisplay.js @@ -1,106 +1,118 @@ -import Settings from "../../utils/Settings"; -import { registerWhen } from "../../utils/RegisterTils"; -import { Overlay } from "../../utils/Overlay"; import { data, itemNBTs } from "../../utils/Data"; +import { Overlay } from "../../utils/Overlay"; +import { registerWhen } from "../../utils/RegisterTils"; +import Settings from "../../utils/Settings"; import { compressNBT, decompressNBT, parseTexture } from "../../utils/functions/misc"; import { Button } from "./ContainerButtons"; - /** * Render armor pieces as icons */ const barrier = new Item("minecraft:barrier"); const pieces = [null, null, null, null]; new Overlay("armorDisplay", data.UL, "moveArmor", "Armor", ["all"], "renderOverlay", () => { - let yDiff = -15 * data.UL[2]; + let yDiff = -15 * data.UL[2]; - pieces.forEach(piece => { - yDiff += 15 * data.UL[2]; - if (piece === null) { - barrier.draw(data.UL[0], data.UL[1] + yDiff, data.UL[2]); - return; - } + pieces.forEach((piece) => { + yDiff += 15 * data.UL[2]; + if (piece === null) { + barrier.draw(data.UL[0], data.UL[1] + yDiff, data.UL[2]); + return; + } - // Draw icon - piece.draw(data.UL[0], data.UL[1] + yDiff, data.UL[2]); + // Draw icon + piece.draw(data.UL[0], data.UL[1] + yDiff, data.UL[2]); - // Draw cd/stars - const size = piece.getStackSize(); - if (size > 1) Renderer.drawString(size, data.UL[0] - Renderer.getStringWidth(size), data.UL[1] + yDiff); - }); + // Draw cd/stars + const size = piece.getStackSize(); + if (size > 1) Renderer.drawString(size, data.UL[0] - Renderer.getStringWidth(size), data.UL[1] + yDiff); + }); - return true; + return true; }); /** * Get player armor pieces */ -registerWhen(register("tick", () => { +registerWhen( + register("tick", () => { const armor = Player.armor; pieces[0] = armor.getHelmet(); pieces[1] = armor.getChestplate(); pieces[2] = armor.getLeggings(); pieces[3] = armor.getBoots(); -}), () => Settings.armorDisplay); - + }), + () => Settings.armorDisplay +); /** * Render equipment pieces as icons */ const buttons = []; let equipment = itemNBTs.equip.map((nbt, index) => { - if (nbt === null) return null; - - const item = new Item(net.minecraft.item.ItemStack.func_77949_a(NBT.parse(decompressNBT(nbt)).rawNBT)); - let texture; - - if (item.getUnlocalizedName() === "item.skull") { // Fix skull textures not rendering - const skullNBT = item.getNBT().getCompoundTag("tag").getCompoundTag("SkullOwner"); - texture = skullNBT.getCompoundTag("Properties").getTagList("textures", 0).func_150305_b(0).func_74779_i("Value"); - const skull = parseTexture(texture); - item.getNBT().getCompoundTag("tag").set("SkullOwner", skull); - } else texture = "stained_glass_pane"; - - // Create inv eq button - buttons.push(new Button("eq", index * 9, () => { + if (nbt === null) return null; + + const item = new Item(net.minecraft.item.ItemStack.func_77949_a(NBT.parse(decompressNBT(nbt)).rawNBT)); + let texture; + + if (item.getUnlocalizedName() === "item.skull") { + // Fix skull textures not rendering + const skullNBT = item.getNBT().getCompoundTag("tag").getCompoundTag("SkullOwner"); + texture = skullNBT.getCompoundTag("Properties").getTagList("textures", 0).func_150305_b(0).func_74779_i("Value"); + const skull = parseTexture(texture); + item.getNBT().getCompoundTag("tag").set("SkullOwner", skull); + } else texture = "stained_glass_pane"; + + // Create inv eq button + buttons.push( + new Button( + "eq", + index * 9, + () => { ChatLib.command("equipment"); - }, "equipment", texture, data.equipmentLore[index] ?? [])); - - return item; + }, + "equipment", + texture, + data.equipmentLore[index] ?? [] + ) + ); + + return item; }); /** * Equipment button handling */ const click = register("guiMouseClick", (x, y, button, gui) => { - const left = gui?.getGuiLeft() ?? 0; - const top = gui?.getGuiTop() ?? 0; + const left = gui?.getGuiLeft() ?? 0; + const top = gui?.getGuiTop() ?? 0; - Object.keys(buttons).forEach(key => { - buttons[key].click(left, top, x, y, button); - }); + Object.keys(buttons).forEach((key) => { + buttons[key].click(left, top, x, y, button); + }); }).unregister(); const render = register("guiRender", (x, y, gui) => { - const top = gui?.getGuiTop() ?? 0; - const left = gui?.getGuiLeft() ?? 0; + const top = gui?.getGuiTop() ?? 0; + const left = gui?.getGuiLeft() ?? 0; - Object.keys(buttons).forEach(key => { - buttons[key].draw(left, top); - buttons[key].hover(left, top, x, y); - }); + Object.keys(buttons).forEach((key) => { + buttons[key].draw(left, top); + buttons[key].hover(left, top, x, y); + }); }).unregister(); const close = register("guiClosed", () => { - // Set registers - render.unregister(); - click.unregister(); - close.unregister(); + // Set registers + render.unregister(); + click.unregister(); + close.unregister(); }).unregister(); -registerWhen(register("guiOpened", (event) => { +registerWhen( + register("guiOpened", (event) => { const gui = event.gui; - const name = gui.class.toString().split('.'); + const name = gui.class.toString().split("."); container = name[name.length - 1]; if (container !== "GuiInventory" && container !== "GuiChest") return; @@ -108,78 +120,86 @@ registerWhen(register("guiOpened", (event) => { close.register(); render.register(); Client.scheduleTask(1, () => { - click.register(); - close.register(); - render.register(); + click.register(); + close.register(); + render.register(); }); -}), () => Settings.equipDisplay); + }), + () => Settings.equipDisplay +); /** * Equipment Overlay */ new Overlay("equipDisplay", data.EQL, "moveEq", "Equip", ["all"], "renderOverlay", () => { - if (!Settings.equipDisplay) return; - let yDiff = -15 * data.EQL[2]; - - equipment.forEach(piece => { - yDiff += 15 * data.EQL[2]; - if (piece === null) { - barrier.draw(data.EQL[0], data.EQL[1] + yDiff, data.EQL[2]); - return; - } - - // Draw icon - piece.draw(data.EQL[0], data.EQL[1] + yDiff, data.EQL[2]); - - // Draw cd/stars - const size = piece.getStackSize(); - if (size > 1) Renderer.drawString(size, data.EQL[0] - Renderer.getStringWidth(size), data.EQL[1] + yDiff); - }); + if (!Settings.equipDisplay) return; + let yDiff = -15 * data.EQL[2]; + + equipment.forEach((piece) => { + yDiff += 15 * data.EQL[2]; + if (piece === null) { + barrier.draw(data.EQL[0], data.EQL[1] + yDiff, data.EQL[2]); + return; + } + + // Draw icon + piece.draw(data.EQL[0], data.EQL[1] + yDiff, data.EQL[2]); + + // Draw cd/stars + const size = piece.getStackSize(); + if (size > 1) Renderer.drawString(size, data.EQL[0] - Renderer.getStringWidth(size), data.EQL[1] + yDiff); + }); - return true; + return true; }); /** * Get player equipment pieces */ function updateEquipment() { - const container = Player.getContainer(); - if (container.getName() !== "Your Equipment and Stats") return; - - equipment = [ - container.getStackInSlot(10), - container.getStackInSlot(19), - container.getStackInSlot(28), - container.getStackInSlot(37) - ]; - - for (let i = 0; i < 4; i++) { - let item = equipment[i]; - if(item.getID() === 160) { - buttons[i].setItem("stained_glass_pane", []); - continue; - } - - let skullNBT = item.getNBT().getCompoundTag("tag").getCompoundTag("SkullOwner"); - let texture = skullNBT.getCompoundTag("Properties").getTag("textures").getRawNBT(); - buttons[i].setItem(texture.func_150305_b(0).func_74779_i("Value"), item.getLore()); + const container = Player.getContainer(); + if (container.getName() !== "Your Equipment and Stats") return; + + equipment = [ + container.getStackInSlot(10), + container.getStackInSlot(19), + container.getStackInSlot(28), + container.getStackInSlot(37), + ]; + + for (let i = 0; i < 4; i++) { + let item = equipment[i]; + if (item.getID() === 160) { + buttons[i].setItem("stained_glass_pane", []); + continue; } + + let skullNBT = item.getNBT().getCompoundTag("tag").getCompoundTag("SkullOwner"); + let texture = skullNBT.getCompoundTag("Properties").getTag("textures").getRawNBT(); + buttons[i].setItem(texture.func_150305_b(0).func_74779_i("Value"), item.getLore()); + } } -registerWhen(register("guiMouseClick", () => { +registerWhen( + register("guiMouseClick", () => { Client.scheduleTask(1, updateEquipment); -}), () => Settings.equipDisplay); -registerWhen(register("guiOpened", () => { + }), + () => Settings.equipDisplay +); +registerWhen( + register("guiOpened", () => { Client.scheduleTask(1, updateEquipment); -}), () => Settings.equipDisplay); + }), + () => Settings.equipDisplay +); /** * Persistant armor and equip. */ register("gameUnload", () => { - itemNBTs.armor = pieces.map(piece => piece === null ? null : compressNBT(piece.getNBT().toObject())); - itemNBTs.equip = equipment.map((piece, index) => { - if (piece?.getLore()?.length > 1) data.equipmentLore[index] = [...piece.getLore()]; - return piece === null ? null : compressNBT(piece.getNBT().toObject()) - }); -}).setPriority(Priority.HIGHEST);; + itemNBTs.armor = pieces.map((piece) => (piece === null ? null : compressNBT(piece.getNBT().toObject()))); + itemNBTs.equip = equipment.map((piece, index) => { + if (piece?.getLore()?.length > 1) data.equipmentLore[index] = [...piece.getLore()]; + return piece === null ? null : compressNBT(piece.getNBT().toObject()); + }); +}).setPriority(Priority.HIGHEST); diff --git a/features/container/AttributeAbbrev.js b/features/container/AttributeAbbrev.js index 70e3996..1d7fbec 100644 --- a/features/container/AttributeAbbrev.js +++ b/features/container/AttributeAbbrev.js @@ -1,23 +1,29 @@ -import Settings from "../../utils/Settings"; import { AQUA } from "../../utils/Constants"; import { registerWhen } from "../../utils/RegisterTils"; +import Settings from "../../utils/Settings"; - -registerWhen(register("renderItemIntoGui", (item, x, y) => { - const attributes = item?.getNBT()?.getCompoundTag("tag")?.getCompoundTag("ExtraAttributes")?.getCompoundTag("attributes"); +registerWhen( + register("renderItemIntoGui", (item, x, y) => { + const attributes = item + ?.getNBT() + ?.getCompoundTag("tag") + ?.getCompoundTag("ExtraAttributes") + ?.getCompoundTag("attributes"); if (attributes.hasNoTags()) return; - let overlay = ''; - attributes.getKeySet().forEach(key => { - const words = key.split('_'); - let abbreviation = ''; - for (let i = 0; i < words.length; i++) { - abbreviation += words[i][0].toUpperCase(); - } - overlay += AQUA + abbreviation + '\n'; + let overlay = ""; + attributes.getKeySet().forEach((key) => { + const words = key.split("_"); + let abbreviation = ""; + for (let i = 0; i < words.length; i++) { + abbreviation += words[i][0].toUpperCase(); + } + overlay += AQUA + abbreviation + "\n"; }); Renderer.scale(0.8, 0.8); Renderer.translate(0, 0, 275); Renderer.drawString(overlay, x * 1.25 + 1, y * 1.25 + 1); -}), () => Settings.attributeAbbrev); + }), + () => Settings.attributeAbbrev +); diff --git a/features/container/ContainerButtons.js b/features/container/ContainerButtons.js index 2a14602..5b41f51 100644 --- a/features/container/ContainerButtons.js +++ b/features/container/ContainerButtons.js @@ -1,42 +1,60 @@ -import Settings from "../../utils/Settings"; -import { BOLD, BUTTON_PRESETS, DARK_GRAY, DARK_GREEN, DataFlavor, GOLD, GREEN, GuiChest, GuiInventory, GuiTextField, InventoryBasic, LOGO, RED, Toolkit, UNDERLINE, YELLOW } from "../../utils/Constants"; +import { + BOLD, + BUTTON_PRESETS, + DARK_GRAY, + DARK_GREEN, + DataFlavor, + GOLD, + GREEN, + GuiChest, + GuiInventory, + GuiTextField, + InventoryBasic, + LOGO, + RED, + Toolkit, + UNDERLINE, + YELLOW, +} from "../../utils/Constants"; import { data } from "../../utils/Data"; -import { registerWhen } from "../../utils/RegisterTils"; import { printList } from "../../utils/ListTils"; +import { registerWhen } from "../../utils/RegisterTils"; +import Settings from "../../utils/Settings"; import { parseTexture } from "../../utils/functions/misc"; import { drawBox, drawLore } from "../../utils/functions/render"; - // Container offsets from top left [x, y] const OFFSETS = { - "top": [8, -18], - "right": [178, 12], - "bottom": [8, 42], - "left": [-18, 12], - "inv1": [80, 8], - "inv2": [80, 26], - "inv3": [80, 44], - "inv4": [80, 62], - "eq": [-18, 8], + top: [8, -18], + right: [178, 12], + bottom: [8, 42], + left: [-18, 12], + inv1: [80, 8], + inv2: [80, 26], + inv3: [80, 44], + inv4: [80, 62], + eq: [-18, 8], }; -let clientCommands = new Set(net.minecraftforge.client.ClientCommandHandler.instance?.getCommandSet()?.map(key => key.func_71517_b()) ?? ["va"]); +let clientCommands = new Set( + net.minecraftforge.client.ClientCommandHandler.instance?.getCommandSet()?.map((key) => key.func_71517_b()) ?? ["va"] +); const COLOR_SCHEMES = [ - [Renderer.color(139, 139, 139, 128), Renderer.color(198, 198, 198, 255)], // Default - [Renderer.color(0, 0, 0, 0), Renderer.color(0, 0, 0, 0)], // Transparent - [Renderer.color(255, 255, 255, 16), Renderer.color(169, 169, 169, 128)], // Semi-Transparent - [Renderer.color(82, 92, 136, 128), Renderer.color(44, 53, 77, 255)] // FurfSky + [Renderer.color(139, 139, 139, 128), Renderer.color(198, 198, 198, 255)], // Default + [Renderer.color(0, 0, 0, 0), Renderer.color(0, 0, 0, 0)], // Transparent + [Renderer.color(255, 255, 255, 16), Renderer.color(169, 169, 169, 128)], // Semi-Transparent + [Renderer.color(82, 92, 136, 128), Renderer.color(44, 53, 77, 255)], // FurfSky ]; const BOX_HIGHLIGHT = Renderer.color(0, 255, 255, 64); const BORDER_HIGHLIGHT = Renderer.color(0, 255, 255, 255); // Editing inputs and rendering const editing = { - "id": "Top0", - "loc": "Top", - "index": 0, - "active": false -} + id: "Top0", + loc: "Top", + index: 0, + active: false, +}; const commandInput = new GuiTextField(0, Client.getMinecraft().field_71466_p, 10, 10, 176, 16); const iconInput = new GuiTextField(0, Client.getMinecraft().field_71466_p, 10, 30, 176, 16); @@ -45,411 +63,459 @@ let buttons = {}; let container; export class Button { - #clicked; - #loc; - #index; - #id; - #x; - #y; - #command; - #icon; - #item; - #invOnly; - #hovered; - #edit = false; - - /** - * Boop. Another poorly written class that patches up errors as they came up ^_^ - * - * @param {String} loc - "Top", "Right", "Bottom", or "Left" used to set offset. - * @param {Number} index - Relative inventory index position. Will be used to determine if button should be rendered - * @param {Function} clicked - Callback function to be called when button is pressed. - * @param {String} command - Command args that are called in the callback. Used to cache data. - * @param {String} icon - Minecraft item id used to draw logo. Barrier icon is reserved for edit buttons. - */ - constructor(loc, index, clicked, command="", icon="barrier", lore) { - this.#clicked = clicked; - this.#loc = loc; - this.#index = index; - this.#id = loc + index.toString(); - this.#command = command; - this.#x = OFFSETS[this.#loc][0] + 18 * (this.#index % 9); - this.#y = OFFSETS[this.#loc][1] + 18 * ~~(this.#index / 9); - this.#invOnly = loc.startsWith("inv"); - this.setItem(icon, lore); - } - - /** - * Returns Button.#index - * - * @returns {Number} - Button's container index. - */ - getIndex() { - return this.#index; - } - - /** - * Returns Button.#item - * - * @returns {String} - Button's icon ID. - */ - getIcon() { - return this.#icon; - } - - /** - * Updates Button.#item using icon, otherwise defaults to redstone_block. - * - * @param {String} icon - Name used to find Minecraft item ID. - */ - setItem(icon, lore) { - try { - this.#edit = icon === "barrier"; - const texture = icon === "skull" ? Toolkit.getDefaultToolkit().getSystemClipboard().getData(DataFlavor.stringFlavor) : - icon.length > 32 ? icon : - icon.length === 32 ? data.buttons[this.#id][3] : undefined; - - if (texture !== undefined) { // Skull textures - const tag = new NBTTagCompound(new net.minecraft.nbt.NBTTagCompound()); - tag.set("SkullOwner", parseTexture(texture)); - - const item = new Item(397).setDamage(3); - item.itemStack.func_77982_d(tag.rawNBT); - if (lore !== undefined) item.setLore([...lore]); // Make sure lore is an array - this.#item = item; - this.#icon = texture; - } else { - this.#item = new Item("minecraft:" + icon); - this.#icon = icon; - this.#edit = icon === "barrier"; - } - } catch (_) { - ChatLib.chat(`${LOGO + RED}Error: Invalid icon ID "${icon}"!`); - this.#item = new Item("minecraft:redstone_block"); - this.#icon = "redstone_block"; - } - } - - /** - * Updates Button.#command and Button.#clicked to callback. - * - * @param {String} command - Command to run when button is pressed. - */ - setCommand(command) { - this.#command = command; - const isClient = clientCommands.has(command.split(' ')[0]); - - this.#clicked = () => { - ChatLib.command(command, isClient); - } - } - - /** - * Saves button caching data into specified data variable. - * - * @param {Object} cache - Data variasble to save Button data to. - */ - save(cache) { - if (this.#id.startsWith("eq")) return; - cache[this.#id] = [this.#loc, this.#index, this.#command, this.#icon]; + #clicked; + #loc; + #index; + #id; + #x; + #y; + #command; + #icon; + #item; + #invOnly; + #hovered; + #edit = false; + + /** + * Boop. Another poorly written class that patches up errors as they came up ^_^ + * + * @param {String} loc - "Top", "Right", "Bottom", or "Left" used to set offset. + * @param {Number} index - Relative inventory index position. Will be used to determine if button should be rendered + * @param {Function} clicked - Callback function to be called when button is pressed. + * @param {String} command - Command args that are called in the callback. Used to cache data. + * @param {String} icon - Minecraft item id used to draw logo. Barrier icon is reserved for edit buttons. + */ + constructor(loc, index, clicked, command = "", icon = "barrier", lore) { + this.#clicked = clicked; + this.#loc = loc; + this.#index = index; + this.#id = loc + index.toString(); + this.#command = command; + this.#x = OFFSETS[this.#loc][0] + 18 * (this.#index % 9); + this.#y = OFFSETS[this.#loc][1] + 18 * ~~(this.#index / 9); + this.#invOnly = loc.startsWith("inv"); + this.setItem(icon, lore); + } + + /** + * Returns Button.#index + * + * @returns {Number} - Button's container index. + */ + getIndex() { + return this.#index; + } + + /** + * Returns Button.#item + * + * @returns {String} - Button's icon ID. + */ + getIcon() { + return this.#icon; + } + + /** + * Updates Button.#item using icon, otherwise defaults to redstone_block. + * + * @param {String} icon - Name used to find Minecraft item ID. + */ + setItem(icon, lore) { + try { + this.#edit = icon === "barrier"; + const texture = + icon === "skull" + ? Toolkit.getDefaultToolkit().getSystemClipboard().getData(DataFlavor.stringFlavor) + : icon.length > 32 + ? icon + : icon.length === 32 + ? data.buttons[this.#id][3] + : undefined; + + if (texture !== undefined) { + // Skull textures + const tag = new NBTTagCompound(new net.minecraft.nbt.NBTTagCompound()); + tag.set("SkullOwner", parseTexture(texture)); + + const item = new Item(397).setDamage(3); + item.itemStack.func_77982_d(tag.rawNBT); + if (lore !== undefined) item.setLore([...lore]); // Make sure lore is an array + this.#item = item; + this.#icon = texture; + } else { + this.#item = new Item("minecraft:" + icon); + this.#icon = icon; + this.#edit = icon === "barrier"; + } + } catch (_) { + ChatLib.chat(`${LOGO + RED}Error: Invalid icon ID "${icon}"!`); + this.#item = new Item("minecraft:redstone_block"); + this.#icon = "redstone_block"; } - - /** - * Draws button background and icon onto screen. - * - * @param {Number} dx - Left most coordinate of current GUI. - * @param {Number} dy - Top most coordinate of current GUI. - */ - draw(dx, dy) { - if (this.#invOnly && container !== "GuiInventory") return; - - const size = Player.getContainer().getSize(); - const x = dx + this.#x; - const y = dy + this.#y + (this.#loc !== "bottom" ? 0 : - 18 * ~~(size / 9) + (size > 45 ? 0 : 36)); - - // Draw box - const boxColor = this.#hovered ? BOX_HIGHLIGHT : COLOR_SCHEMES[Settings.containerButtons - 1][0]; - const borderColor = this.#hovered ? BORDER_HIGHLIGHT : COLOR_SCHEMES[Settings.containerButtons - 1][1]; - drawBox(x, y, 100, 16, 16, boxColor, borderColor); - this.#item.draw(x, y, 1, 100); + } + + /** + * Updates Button.#command and Button.#clicked to callback. + * + * @param {String} command - Command to run when button is pressed. + */ + setCommand(command) { + this.#command = command; + const isClient = clientCommands.has(command.split(" ")[0]); + + this.#clicked = () => { + ChatLib.command(command, isClient); + }; + } + + /** + * Saves button caching data into specified data variable. + * + * @param {Object} cache - Data variasble to save Button data to. + */ + save(cache) { + if (this.#id.startsWith("eq")) return; + cache[this.#id] = [this.#loc, this.#index, this.#command, this.#icon]; + } + + /** + * Draws button background and icon onto screen. + * + * @param {Number} dx - Left most coordinate of current GUI. + * @param {Number} dy - Top most coordinate of current GUI. + */ + draw(dx, dy) { + if (this.#invOnly && container !== "GuiInventory") return; + + const size = Player.getContainer().getSize(); + const x = dx + this.#x; + const y = dy + this.#y + (this.#loc !== "bottom" ? 0 : 18 * ~~(size / 9) + (size > 45 ? 0 : 36)); + + // Draw box + const boxColor = this.#hovered ? BOX_HIGHLIGHT : COLOR_SCHEMES[Settings.containerButtons - 1][0]; + const borderColor = this.#hovered ? BORDER_HIGHLIGHT : COLOR_SCHEMES[Settings.containerButtons - 1][1]; + drawBox(x, y, 100, 16, 16, boxColor, borderColor); + this.#item.draw(x, y, 1, 100); + } + + /** + * Checks if mouse is hovering over button to render linked command. + * + * @param {Number} dx - Left most coordinate of current GUI. + * @param {Number} dy - Top most coordinate of current GUI. + * @param {Number} hx - Current x pos of mouse. + * @param {Number} hy - Current y pos of mouse. + */ + hover(dx, dy, hx, hy) { + if (this.#invOnly && container !== "GuiInventory") return; + + const size = Player.getContainer().getSize(); + const x = dx + this.#x; + const y = dy + this.#y + (this.#loc !== "bottom" ? 0 : 18 * ~~(size / 9) + (size > 45 ? 0 : 36)); + if (hx < x || hx > x + 16 || hy < y || hy > y + 16) { + this.#hovered = false; + return; } - /** - * Checks if mouse is hovering over button to render linked command. - * - * @param {Number} dx - Left most coordinate of current GUI. - * @param {Number} dy - Top most coordinate of current GUI. - * @param {Number} hx - Current x pos of mouse. - * @param {Number} hy - Current y pos of mouse. - */ - hover(dx, dy, hx, hy) { - if (this.#invOnly && container !== "GuiInventory") return; - - const size = Player.getContainer().getSize(); - const x = dx + this.#x; - const y = dy + this.#y + (this.#loc !== "bottom" ? 0 : - 18 * ~~(size / 9) + (size > 45 ? 0 : 36)); - if (hx < x || hx > x + 16 || hy < y || hy > y + 16) { - this.#hovered = false; - return; + this.#hovered = true; + if (this.#item.getLore().length === 1) { + Renderer.translate(0, 0, 500); + Renderer.drawRect( + Renderer.color(0, 0, 0, 128), + hx + 2, + hy - 16, + Renderer.getStringWidth("/" + this.#command) + 6, + 14 + ); + Renderer.translate(0, 0, 500); + Renderer.drawString(UNDERLINE + (this.#edit ? `Edit ${this.#id}` : "/" + this.#command), hx + 5, hy - 13); + } else drawLore(hx, hy, this.#item.getLore()); + } + + /** + * Checks if mouse presses on button. + * + * @param {Number} dx - Left most coordinate of current GUI. + * @param {Number} dy - Top most coordinate of current GUI. + * @param {Number} cx - X pos of mouse press. + * @param {Number} cy - Y pos of mouse press. + * @param {Number} button - 0 for left click, 1 for right click. + * @returns {Boolean} True if button was pressed, false otherwise. + */ + click(dx, dy, cx, cy, button) { + if (this.#invOnly && container !== "GuiInventory") return; + + const size = Player.getContainer().getSize(); + const x = dx + this.#x; + const y = dy + this.#y + (this.#loc !== "bottom" ? 0 : 18 * ~~(size / 9) + (size > 45 ? 0 : 36)); + if (cx < x || cx > x + 16 || cy < y || cy > y + 16) return false; + + if (editing.active) { + if (this.#id.startsWith("eq")) return false; + else if (button === 0) { + if (this.#edit) { + this.#clicked(); + return true; } - this.#hovered = true; - if (this.#item.getLore().length === 1) { - Renderer.translate(0, 0, 500); - Renderer.drawRect(Renderer.color(0, 0, 0, 128), hx + 2, hy - 16, Renderer.getStringWidth('/' + this.#command) + 6, 14); - Renderer.translate(0, 0, 500); - Renderer.drawString(UNDERLINE + (this.#edit ? `Edit ${this.#id}` : '/' + this.#command), hx + 5, hy - 13); - } else drawLore(hx, hy, this.#item.getLore()); - } + editing.id = this.#id; + editing.loc = this.#loc; + editing.index = this.#index; + + inputKey.register(); + inputRender.register(); + iconInput.func_146180_a(this.#icon); + commandInput.func_146180_a(this.#command); + commandInput.func_146195_b(true); + } else { + delete buttons[this.#id]; + buttons[this.#id] = new Button( + this.#loc, + this.#index, + () => { + editing.id = this.#id; + editing.loc = this.#loc; + editing.index = this.#index; - /** - * Checks if mouse presses on button. - * - * @param {Number} dx - Left most coordinate of current GUI. - * @param {Number} dy - Top most coordinate of current GUI. - * @param {Number} cx - X pos of mouse press. - * @param {Number} cy - Y pos of mouse press. - * @param {Number} button - 0 for left click, 1 for right click. - * @returns {Boolean} True if button was pressed, false otherwise. - */ - click(dx, dy, cx, cy, button) { - if (this.#invOnly && container !== "GuiInventory") return; - - const size = Player.getContainer().getSize(); - const x = dx + this.#x; - const y = dy + this.#y + (this.#loc !== "bottom" ? 0 : - 18 * ~~(size / 9) + (size > 45 ? 0 : 36)); - if (cx < x || cx > x + 16 || cy < y || cy > y + 16) return false; - - if (editing.active) { - if (this.#id.startsWith("eq")) return false; - else if (button === 0) { - if (this.#edit) { - this.#clicked(); - return true; - } - - editing.id = this.#id; - editing.loc = this.#loc; - editing.index = this.#index; - - inputKey.register(); - inputRender.register(); - iconInput.func_146180_a(this.#icon); - commandInput.func_146180_a(this.#command); - commandInput.func_146195_b(true); - } else { - delete buttons[this.#id]; - buttons[this.#id] = new Button(this.#loc, this.#index, () => { - editing.id = this.#id; - editing.loc = this.#loc; - editing.index = this.#index; - - inputKey.register(); - inputRender.register(); - }, "", "barrier"); - } - } else if (button === 0) this.#clicked(); - - return true; - } + inputKey.register(); + inputRender.register(); + }, + "", + "barrier" + ); + } + } else if (button === 0) this.#clicked(); + + return true; + } } /** * Unregisters edit registers and clears input fields. */ function resetEdit() { - inputKey.unregister(); - inputRender.unregister(); - commandInput.func_146180_a(""); - iconInput.func_146180_a(""); + inputKey.unregister(); + inputRender.unregister(); + commandInput.func_146180_a(""); + iconInput.func_146180_a(""); } /** * Creates a button using input fields as arguments. */ function saveEdit() { - if (commandInput.func_146179_b() === "" || iconInput.func_146179_b() === "barrier") return; - - let command = commandInput.func_146179_b(); // This is also a pointer for some reason - if (buttons.hasOwnProperty(editing.id)) { - buttons[editing.id].setCommand(command); - buttons[editing.id].setItem(iconInput.func_146179_b()); - } else { - const isClient = clientCommands.has(command.split(' ')[0]); - buttons[editing.id] = new Button(editing.loc, editing.index, () => { - ChatLib.command(command, isClient); - }, command, iconInput.func_146179_b()); - delete buttons[editing.id]; - } + if (commandInput.func_146179_b() === "" || iconInput.func_146179_b() === "barrier") return; + + let command = commandInput.func_146179_b(); // This is also a pointer for some reason + if (buttons.hasOwnProperty(editing.id)) { + buttons[editing.id].setCommand(command); + buttons[editing.id].setItem(iconInput.func_146179_b()); + } else { + const isClient = clientCommands.has(command.split(" ")[0]); + buttons[editing.id] = new Button( + editing.loc, + editing.index, + () => { + ChatLib.command(command, isClient); + }, + command, + iconInput.func_146179_b() + ); + delete buttons[editing.id]; + } } const inputClick = register("guiMouseClick", (x, y, button, gui, event) => { - commandInput.func_146192_a(x, y, button); - iconInput.func_146192_a(x, y, button); - if (commandInput.func_146206_l() || iconInput.func_146206_l()) cancel(event); - else { - saveEdit(); - const left = gui?.getGuiLeft() ?? 0; - const top = gui?.getGuiTop() ?? 0; - const size = Player.getContainer().getSize() + (container === "GuiInventory" ? 18 : 0); - - if (!Object.keys(buttons).some(key => { - if (buttons[key].getIndex() > size) return false; - return buttons[key].click(left, top, x, y, button); - })) resetEdit(); - } + commandInput.func_146192_a(x, y, button); + iconInput.func_146192_a(x, y, button); + if (commandInput.func_146206_l() || iconInput.func_146206_l()) cancel(event); + else { + saveEdit(); + const left = gui?.getGuiLeft() ?? 0; + const top = gui?.getGuiTop() ?? 0; + const size = Player.getContainer().getSize() + (container === "GuiInventory" ? 18 : 0); + + if ( + !Object.keys(buttons).some((key) => { + if (buttons[key].getIndex() > size) return false; + return buttons[key].click(left, top, x, y, button); + }) + ) + resetEdit(); + } }).unregister(); const inputKey = register("guiKey", (char, keyCode, _, event) => { - if (commandInput.func_146206_l()) commandInput.func_146201_a(char, keyCode); - else if (iconInput.func_146206_l()) iconInput.func_146201_a(char, keyCode); - else return; - - // Cancel all but escape key - if (keyCode !== 1) cancel(event); - if (keyCode === 28) { // Enter key - saveEdit(); - resetEdit(); - } else if (keyCode === 15) { // Tab key - if (commandInput.func_146206_l()) { - commandInput.func_146195_b(false); - iconInput.func_146195_b(true); - } else { - iconInput.func_146195_b(false); - commandInput.func_146195_b(true); - } + if (commandInput.func_146206_l()) commandInput.func_146201_a(char, keyCode); + else if (iconInput.func_146206_l()) iconInput.func_146201_a(char, keyCode); + else return; + + // Cancel all but escape key + if (keyCode !== 1) cancel(event); + if (keyCode === 28) { + // Enter key + saveEdit(); + resetEdit(); + } else if (keyCode === 15) { + // Tab key + if (commandInput.func_146206_l()) { + commandInput.func_146195_b(false); + iconInput.func_146195_b(true); + } else { + iconInput.func_146195_b(false); + commandInput.func_146195_b(true); } + } }).unregister(); const inputRender = register("guiRender", () => { - Renderer.drawString(`${BOLD}Editing: ${editing.id}`, commandInput.field_146209_f, commandInput.field_146210_g - 20, Settings.textShadow); - Renderer.drawString("Command (ex. \"p list\"):", commandInput.field_146209_f, commandInput.field_146210_g - 10, Settings.textShadow); - commandInput.func_146194_f(); - Renderer.drawString("Icon ID (ex. \"redstone_block\"): ", iconInput.field_146209_f, iconInput.field_146210_g - 10, Settings.textShadow); - iconInput.func_146194_f(); + Renderer.drawString( + `${BOLD}Editing: ${editing.id}`, + commandInput.field_146209_f, + commandInput.field_146210_g - 20, + Settings.textShadow + ); + Renderer.drawString( + 'Command (ex. "p list"):', + commandInput.field_146209_f, + commandInput.field_146210_g - 10, + Settings.textShadow + ); + commandInput.func_146194_f(); + Renderer.drawString( + 'Icon ID (ex. "redstone_block"): ', + iconInput.field_146209_f, + iconInput.field_146210_g - 10, + Settings.textShadow + ); + iconInput.func_146194_f(); }).unregister(); /** * Loops through provided indexes to create buttons for a category. - * + * * @param {Number} start - Starting index of for loop. * @param {Number} end - Ending index of for loop (not inclusive). * @param {Number} increment - Step size of for loop. * @param {String} category - "top", "right", "bottom", or "left" */ function createButtons(start, end, increment, category) { - for (let i = start; i < end; i += increment) { - let id = category + i; - if (buttons.hasOwnProperty(id)) continue; - - let j = i / 1; // Why tf does i act like a pointer - buttons[id] = new Button(category, i, () => { - editing.id = id; - editing.index = j; - editing.loc = category; - - iconInput.func_146180_a(""); - commandInput.func_146180_a(""); - commandInput.func_146195_b(true) - inputKey.register(); - inputRender.register(); - }, "", "barrier"); - } + for (let i = start; i < end; i += increment) { + let id = category + i; + if (buttons.hasOwnProperty(id)) continue; + + let j = i / 1; // Why tf does i act like a pointer + buttons[id] = new Button( + category, + i, + () => { + editing.id = id; + editing.index = j; + editing.loc = category; + + iconInput.func_146180_a(""); + commandInput.func_146180_a(""); + commandInput.func_146195_b(true); + inputKey.register(); + inputRender.register(); + }, + "", + "barrier" + ); + } } /** * Sets up button editing menu for specified container type. - * + * * @param {String} type - "inv" or "chest" for type of container to open and process. */ function setButtons(type) { - editing.active = true; - const setInv = type === "inv"; - - // Open example inventory, credit to: https://www.chattriggers.com/modules/v/ChestMenu - if (setInv) GuiHandler.openGui(new GuiInventory(Player.getPlayer())); - else { - const inv = new InventoryBasic("Container Buttons", true, 54); - const chest = new GuiChest(Player.getPlayer().field_71071_by, inv); - GuiHandler.openGui(chest); - } + editing.active = true; + const setInv = type === "inv"; + + // Open example inventory, credit to: https://www.chattriggers.com/modules/v/ChestMenu + if (setInv) GuiHandler.openGui(new GuiInventory(Player.getPlayer())); + else { + const inv = new InventoryBasic("Container Buttons", true, 54); + const chest = new GuiChest(Player.getPlayer().field_71071_by, inv); + GuiHandler.openGui(chest); + } + + Client.scheduleTask(1, () => { + const gui = Client.currentGui.get(); + if (!gui) return; + const top = gui?.getGuiTop() ?? 0; + const left = gui?.getGuiLeft() ?? 0; - Client.scheduleTask(1, () => { - const gui = Client.currentGui.get(); - if (!gui) return; - const top = gui?.getGuiTop() ?? 0; - const left = gui?.getGuiLeft() ?? 0; - - // Set input field locations - commandInput.field_146209_f = left; - commandInput.field_146210_g = top - 80; - iconInput.field_146209_f = left; - iconInput.field_146210_g = top - 50; - - // Set all inv edit buttons TRBL - createButtons(0, 9, 1, "top"); - createButtons(0, 9, 1, "bottom"); - createButtons(Settings.equipDisplay ? 36 : 0, 99, 9, "left"); - createButtons(0, 99, 9, "right"); - if (setInv) { - createButtons(0, 5, 1, "inv1"); - createButtons(0, 5, 1, "inv2"); - createButtons(0, 5, 1, "inv3"); - createButtons(0, 5, 1, "inv4"); - } - }); - inputClick.register(); + // Set input field locations + commandInput.field_146209_f = left; + commandInput.field_146210_g = top - 80; + iconInput.field_146209_f = left; + iconInput.field_146210_g = top - 50; + + // Set all inv edit buttons TRBL + createButtons(0, 9, 1, "top"); + createButtons(0, 9, 1, "bottom"); + createButtons(Settings.equipDisplay ? 36 : 0, 99, 9, "left"); + createButtons(0, 99, 9, "right"); + if (setInv) { + createButtons(0, 5, 1, "inv1"); + createButtons(0, 5, 1, "inv2"); + createButtons(0, 5, 1, "inv3"); + createButtons(0, 5, 1, "inv4"); + } + }); + inputClick.register(); } /** * Registers for tracking and rendering buttons. */ const click = register("guiMouseClick", (x, y, button, gui) => { - const left = gui?.getGuiLeft() ?? 0; - const top = gui?.getGuiTop() ?? 0; - const size = Player.getContainer().getSize() + (container === "GuiInventory" ? 18 : 0); - - Object.keys(buttons).forEach(key => { - if (buttons[key].getIndex() > size) return; - buttons[key].click(left, top, x, y, button); - }); + const left = gui?.getGuiLeft() ?? 0; + const top = gui?.getGuiTop() ?? 0; + const size = Player.getContainer().getSize() + (container === "GuiInventory" ? 18 : 0); + + Object.keys(buttons).forEach((key) => { + if (buttons[key].getIndex() > size) return; + buttons[key].click(left, top, x, y, button); + }); }).unregister(); const render = register("guiRender", (x, y, gui) => { - const top = gui?.getGuiTop() ?? 0; - const left = gui?.getGuiLeft() ?? 0; - const size = Player.getContainer().getSize() + (container === "GuiInventory" ? 18 : 0); - - Object.keys(buttons).forEach(key => { - if (buttons[key].getIndex() > size) return; - buttons[key].draw(left, top); - buttons[key].hover(left, top, x, y); - }); + const top = gui?.getGuiTop() ?? 0; + const left = gui?.getGuiLeft() ?? 0; + const size = Player.getContainer().getSize() + (container === "GuiInventory" ? 18 : 0); + + Object.keys(buttons).forEach((key) => { + if (buttons[key].getIndex() > size) return; + buttons[key].draw(left, top); + buttons[key].hover(left, top, x, y); + }); }).unregister(); const close = register("guiClosed", () => { - // Clear out edit buttons - Object.keys(buttons).forEach(key => { - if (buttons[key].getIcon() === "barrier") delete buttons[key]; - }); - - // Set registers - render.unregister(); - click.unregister(); - close.unregister(); - inputRender.unregister(); - inputClick.unregister(); - inputKey.unregister(); - editing.active = false; - resetEdit(); + // Clear out edit buttons + Object.keys(buttons).forEach((key) => { + if (buttons[key].getIcon() === "barrier") delete buttons[key]; + }); + + // Set registers + render.unregister(); + click.unregister(); + close.unregister(); + inputRender.unregister(); + inputClick.unregister(); + inputKey.unregister(); + editing.active = false; + resetEdit(); }).unregister(); -registerWhen(register("guiOpened", (event) => { +registerWhen( + register("guiOpened", (event) => { const gui = event.gui; - const name = gui.class.toString().split('.'); + const name = gui.class.toString().split("."); container = name[name.length - 1]; if (container !== "GuiInventory" && container !== "GuiChest") return; @@ -457,115 +523,127 @@ registerWhen(register("guiOpened", (event) => { close.register(); render.register(); Client.scheduleTask(1, () => { - if (!editing.active) click.register(); - close.register(); - render.register(); + if (!editing.active) click.register(); + close.register(); + render.register(); }); -}), () => Settings.containerButtons !== 0); + }), + () => Settings.containerButtons !== 0 +); /** * Persistant buttons. */ register("gameUnload", () => { - data.buttons = {}; - Object.keys(buttons).forEach(key => { - buttons[key].save(data.buttons); - }); + data.buttons = {}; + Object.keys(buttons).forEach((key) => { + buttons[key].save(data.buttons); + }); }).setPriority(Priority.HIGHEST); /** * Loads buttons using cached button data. */ function loadButtons() { - buttons = {}; - Object.keys(data.buttons).forEach(key => { - const button = data.buttons[key]; - const isClient = clientCommands.has(button[2].split(' ')[0]); - - buttons[key] = new Button(button[0], button[1], () => { - ChatLib.command(button[2], isClient); - }, button[2], button[3]); - }); + buttons = {}; + Object.keys(data.buttons).forEach((key) => { + const button = data.buttons[key]; + const isClient = clientCommands.has(button[2].split(" ")[0]); + + buttons[key] = new Button( + button[0], + button[1], + () => { + ChatLib.command(button[2], isClient); + }, + button[2], + button[3] + ); + }); } loadButtons(); /** * Calls through all the commands associated with container buttons feature. - * + * * @param {String[]} args - String arguments passed through command call. */ export function buttonCommands(args) { - const command = args[1]; - const name = args[2]; - - switch(command) { - case "inv": - setButtons("inv"); - break; - case "chest": - setButtons("chest"); - break; - case "save": - data.buttonPresets[name] = {}; - Object.keys(buttons).forEach(key => { - buttons[key].save(data.buttonPresets[name]); - }); - ChatLib.chat(`${LOGO + GREEN}Successfully saved buttons data using key: "${name}".`); - break; - case "delete": - case "remove": - if (data.buttonPresets.hasOwnProperty(name)) { - delete data.buttonPresets[name]; - ChatLib.chat(`${LOGO + GREEN}Successfully deleted button preset using key: "${name}".`); - } else ChatLib.chat(`${LOGO + RED}Error: There are no presets using "${name}" key.`) - break; - case "load": - if (data.buttonPresets.hasOwnProperty(name)) { - data.buttons = data.buttonPresets[name]; - loadButtons(); - ChatLib.chat(`${LOGO + GREEN}Successfully loaded button preset using key: "${name}".`); - } else ChatLib.chat(`${LOGO + RED}Error: There are no presets using "${name}" key.`); - break; - case "list": - case "view": - const buttonKeys = Object.keys(data.buttonPresets); - printList(buttonKeys, "Buttons", parseInt(args[2] ?? 1)); - break; - case "import": - case "parse": - const backup = data.buttons; - try { - const decoded = JSON.parse(FileLib.decodeBase64(Toolkit.getDefaultToolkit().getSystemClipboard().getData(DataFlavor.stringFlavor))); - data.buttons = decoded; - loadButtons(); - ChatLib.chat(`${LOGO + GREEN}Successfully loaded button preset from clipboard!`); - } catch (err) { - data.buttons = backup; - ChatLib.chat(`${LOGO + RED}Error: Unable to process clipboard content!`); - } - break; - case "export": - const compressed = FileLib.encodeBase64(JSON.stringify(data.buttons)); - new Message(`${LOGO + DARK_GREEN}Encoded Button Data:`, new TextComponent(GREEN + compressed) - .setClickAction("run_command") - .setClickValue("/vacopy " + compressed) - .setHoverValue(`${YELLOW}Click to copy data.`) - ).chat(); - break; - case "reset": - data.buttonPresets = BUTTON_PRESETS; - ChatLib.chat(`${LOGO + GREEN}Successfully reset button presets!`); - break; - case "clear": - data.buttons = {}; - loadButtons(); - ChatLib.chat(`${LOGO + GREEN}Successfully cleared current buttons!`); - break; - case "help": - default: - if (command !== "help") ChatLib.chat(`${LOGO + RED}Error: Invalid argument "${command}"!\n`); - ChatLib.chat( -`${LOGO + GOLD + BOLD}Container Buttons Commands: + const command = args[1]; + const name = args[2]; + + switch (command) { + case "inv": + setButtons("inv"); + break; + case "chest": + setButtons("chest"); + break; + case "save": + data.buttonPresets[name] = {}; + Object.keys(buttons).forEach((key) => { + buttons[key].save(data.buttonPresets[name]); + }); + ChatLib.chat(`${LOGO + GREEN}Successfully saved buttons data using key: "${name}".`); + break; + case "delete": + case "remove": + if (data.buttonPresets.hasOwnProperty(name)) { + delete data.buttonPresets[name]; + ChatLib.chat(`${LOGO + GREEN}Successfully deleted button preset using key: "${name}".`); + } else ChatLib.chat(`${LOGO + RED}Error: There are no presets using "${name}" key.`); + break; + case "load": + if (data.buttonPresets.hasOwnProperty(name)) { + data.buttons = data.buttonPresets[name]; + loadButtons(); + ChatLib.chat(`${LOGO + GREEN}Successfully loaded button preset using key: "${name}".`); + } else ChatLib.chat(`${LOGO + RED}Error: There are no presets using "${name}" key.`); + break; + case "list": + case "view": + const buttonKeys = Object.keys(data.buttonPresets); + printList(buttonKeys, "Buttons", parseInt(args[2] ?? 1)); + break; + case "import": + case "parse": + const backup = data.buttons; + try { + const decoded = JSON.parse( + FileLib.decodeBase64(Toolkit.getDefaultToolkit().getSystemClipboard().getData(DataFlavor.stringFlavor)) + ); + data.buttons = decoded; + loadButtons(); + ChatLib.chat(`${LOGO + GREEN}Successfully loaded button preset from clipboard!`); + } catch (err) { + data.buttons = backup; + ChatLib.chat(`${LOGO + RED}Error: Unable to process clipboard content!`); + } + break; + case "export": + const compressed = FileLib.encodeBase64(JSON.stringify(data.buttons)); + new Message( + `${LOGO + DARK_GREEN}Encoded Button Data:`, + new TextComponent(GREEN + compressed) + .setClickAction("run_command") + .setClickValue("/vacopy " + compressed) + .setHoverValue(`${YELLOW}Click to copy data.`) + ).chat(); + break; + case "reset": + data.buttonPresets = BUTTON_PRESETS; + ChatLib.chat(`${LOGO + GREEN}Successfully reset button presets!`); + break; + case "clear": + data.buttons = {}; + loadButtons(); + ChatLib.chat(`${LOGO + GREEN}Successfully cleared current buttons!`); + break; + case "help": + default: + if (command !== "help") ChatLib.chat(`${LOGO + RED}Error: Invalid argument "${command}"!\n`); + ChatLib.chat( + `${LOGO + GOLD + BOLD}Container Buttons Commands: ${DARK_GRAY}- ${GOLD}Base: ${YELLOW}/va buttons ${DARK_GRAY}- ${GOLD}inv: ${YELLOW}Opens edit menu for GuiInventory. @@ -582,7 +660,8 @@ export function buttonCommands(args) { ${DARK_GRAY}- ${GOLD}Edit: ${YELLOW}Left click to edit button, right click to delete button. ${DARK_GRAY}- ${GOLD}Note: ${YELLOW}To use skull textures, copy the texture data to clipboard and use "skull" as icon ID. - ${DARK_GRAY}You can find copy this value by using DevKey and copying Value from the Properties tag.`); - break; - } + ${DARK_GRAY}You can find copy this value by using DevKey and copying Value from the Properties tag.` + ); + break; + } } diff --git a/features/container/ContainerPreview.js b/features/container/ContainerPreview.js index 973fecc..9d204a9 100644 --- a/features/container/ContainerPreview.js +++ b/features/container/ContainerPreview.js @@ -1,12 +1,11 @@ -import Settings from "../../utils/Settings"; -import { BOLD, DARK_GRAY, GOLD, GREEN, LOGO, RED, YELLOW} from "../../utils/Constants"; +import { BOLD, DARK_GRAY, GOLD, GREEN, LOGO, RED, YELLOW } from "../../utils/Constants"; import { data, itemNBTs } from "../../utils/Data"; -import { compressNBT, parseContainerCache } from "../../utils/functions/misc"; -import { registerWhen } from "../../utils/RegisterTils"; -import { Overlay } from "../../utils/Overlay"; -import { drawContainer, drawLore } from "../../utils/functions/render"; import { printList } from "../../utils/ListTils"; - +import { Overlay } from "../../utils/Overlay"; +import { registerWhen } from "../../utils/RegisterTils"; +import Settings from "../../utils/Settings"; +import { compressNBT, parseContainerCache } from "../../utils/functions/misc"; +import { drawContainer } from "../../utils/functions/render"; /** * Log item data as compressed NBT. @@ -17,47 +16,50 @@ new Overlay("containerPreview", data.SPL, "moveSP", "Saved Preview", ["all"], "g /** * Preview commands for container data. - * + * * @param {String[]} args - Command arguments. */ export function previewCommands(args) { - const command = args[1]; - const name = args[2] ?? nameCache.join(''); - - switch(command) { - case "save": - itemNBTs.storageCache[name] = itemsCache.map(item => item === null ? null : compressNBT(item.getNBT().toObject())); - ChatLib.chat(`${LOGO + GREEN}Successfully saved preview data using key: "${name}".`); - break; - case "delete": - case "remove": - delete itemNBTs.storageCache[name]; - ChatLib.chat(`${LOGO + GREEN}Successfully removed preview data using key: "${name}".`); - break; - case "clear": - case "reset": - itemNBTs.storageCache = {}; - ChatLib.chat(`${LOGO + GREEN}Successfully cleared preview data.`); - break; - case "list": - case "view": - const keys = Object.keys(itemNBTs.storageCache); - printList(keys, "Preview", parseInt(args[2] ?? 1)); - break; - case "help": - default: - if (command !== "help") ChatLib.chat(`${LOGO + RED}Error: Invalid argument "${command}"!\n`); - ChatLib.chat( -`${LOGO + GOLD + BOLD}Container Preview Commands: + const command = args[1]; + const name = args[2] ?? nameCache.join(""); + + switch (command) { + case "save": + itemNBTs.storageCache[name] = itemsCache.map((item) => + item === null ? null : compressNBT(item.getNBT().toObject()) + ); + ChatLib.chat(`${LOGO + GREEN}Successfully saved preview data using key: "${name}".`); + break; + case "delete": + case "remove": + delete itemNBTs.storageCache[name]; + ChatLib.chat(`${LOGO + GREEN}Successfully removed preview data using key: "${name}".`); + break; + case "clear": + case "reset": + itemNBTs.storageCache = {}; + ChatLib.chat(`${LOGO + GREEN}Successfully cleared preview data.`); + break; + case "list": + case "view": + const keys = Object.keys(itemNBTs.storageCache); + printList(keys, "Preview", parseInt(args[2] ?? 1)); + break; + case "help": + default: + if (command !== "help") ChatLib.chat(`${LOGO + RED}Error: Invalid argument "${command}"!\n`); + ChatLib.chat( + `${LOGO + GOLD + BOLD}Container Preview Commands: ${DARK_GRAY}- ${GOLD}Base: ${YELLOW}/va preview ${DARK_GRAY}- ${GOLD}save: ${YELLOW}Saves preview data of last opened storage. ${DARK_GRAY}- ${GOLD}delete: ${YELLOW}Deletes preview data of last opened storage. ${DARK_GRAY}- ${GOLD}clear: ${YELLOW}Removes all preview data. ${DARK_GRAY}- ${GOLD}list: ${YELLOW}Lists all preview data. - ${DARK_GRAY}- ${GOLD}help: ${YELLOW}Displays this help message.`); - break; - } + ${DARK_GRAY}- ${GOLD}help: ${YELLOW}Displays this help message.` + ); + break; + } } /** @@ -65,57 +67,70 @@ export function previewCommands(args) { */ let previewCache = []; const savePreview = register("guiRender", (mouseX, mouseY) => { - drawContainer(data.SPL[0], data.SPL[1], Player.getContainer().getName(), CONTAINER_PNGS[Settings.containerPreview - 1], previewCache, mouseX, mouseY); + drawContainer( + data.SPL[0], + data.SPL[1], + Player.getContainer().getName(), + CONTAINER_PNGS[Settings.containerPreview - 1], + previewCache, + mouseX, + mouseY + ); }).unregister(); const cacheItems = register("guiMouseClick", () => { - Client.scheduleTask(4, () => { - const items = Player.getContainer().getItems(); - if (items.length < 54) return; - itemsCache = items.slice(0, 54) - }); + Client.scheduleTask(4, () => { + const items = Player.getContainer().getItems(); + if (items.length < 54) return; + itemsCache = items.slice(0, 54); + }); }).unregister(); const saveCache = register("guiClosed", () => { - // Save cache - if (!itemNBTs.storageCache.hasOwnProperty(nameCache.join(''))) { - if (nameCache[0] === "EC") - itemNBTs.enderchests[nameCache[1]] = itemsCache.map(item => item === null ? null : compressNBT(item.getNBT().toObject())); - else if (nameCache[0] === "BP") - itemNBTs.backpacks[nameCache[1]] = itemsCache.map(item => item === null ? null : compressNBT(item.getNBT().toObject())); - } - - // Unregister events - saveCache.unregister(); - savePreview.unregister(); - cacheItems.unregister(); + // Save cache + if (!itemNBTs.storageCache.hasOwnProperty(nameCache.join(""))) { + if (nameCache[0] === "EC") + itemNBTs.enderchests[nameCache[1]] = itemsCache.map((item) => + item === null ? null : compressNBT(item.getNBT().toObject()) + ); + else if (nameCache[0] === "BP") + itemNBTs.backpacks[nameCache[1]] = itemsCache.map((item) => + item === null ? null : compressNBT(item.getNBT().toObject()) + ); + } + + // Unregister events + saveCache.unregister(); + savePreview.unregister(); + cacheItems.unregister(); }).unregister(); register("guiOpened", () => { - Client.scheduleTask(1, () => { - const name = Player.getContainer().getName(); - const split = name.split(' '); - - // Get container cache - const containerCache = name.startsWith("Ender Chest") ? [itemNBTs.enderchests, "EC"] : - split[1].startsWith("Backpack") ? [itemNBTs.backpacks, "BP"] : undefined; - if (containerCache === undefined) return; - itemsCache = Player.getContainer().getItems().slice(0, 54); - - // Get index of container - const i = (split[1].startsWith("Backpack") ? - split[split.length - 1].replace(/[^0-9]/g, '') : - split[2][1]) - 1; - nameCache = [containerCache[1], i]; - - // Set registers - saveCache.register(); - cacheItems.register(); - if (itemNBTs.storageCache.hasOwnProperty(nameCache.join(''))) { - previewCache = parseContainerCache(itemNBTs.storageCache[nameCache.join('')]); - savePreview.register(); - } - }); + Client.scheduleTask(1, () => { + const name = Player.getContainer().getName(); + const split = name.split(" "); + + // Get container cache + const containerCache = name.startsWith("Ender Chest") + ? [itemNBTs.enderchests, "EC"] + : split[1].startsWith("Backpack") + ? [itemNBTs.backpacks, "BP"] + : undefined; + if (containerCache === undefined) return; + itemsCache = Player.getContainer().getItems().slice(0, 54); + + // Get index of container + const i = (split[1].startsWith("Backpack") ? split[split.length - 1].replace(/[^0-9]/g, "") : split[2][1]) - 1; + nameCache = [containerCache[1], i]; + + // Set registers + saveCache.register(); + cacheItems.register(); + if (itemNBTs.storageCache.hasOwnProperty(nameCache.join(""))) { + previewCache = parseContainerCache(itemNBTs.storageCache[nameCache.join("")]); + savePreview.register(); + } + }); }); /** @@ -127,28 +142,39 @@ const CONTAINER_PNGS = [new Image("container.png"), new Image("container-fs.png" new Overlay("containerPreview", data.CPL, "movePreview", "Preview", ["all"], "guiRender"); const preview = register("guiRender", (mouseX, mouseY) => { - drawContainer(data.CPL[0], data.CPL[1], lastPreview, CONTAINER_PNGS[Settings.containerPreview - 1], previewItems, mouseX, mouseY); + drawContainer( + data.CPL[0], + data.CPL[1], + lastPreview, + CONTAINER_PNGS[Settings.containerPreview - 1], + previewItems, + mouseX, + mouseY + ); }).unregister(); const clear = register("guiClosed", () => { - previewItems = []; - lastPreview = "0"; - preview.unregister(); - clear.unregister(); + previewItems = []; + lastPreview = "0"; + preview.unregister(); + clear.unregister(); }).unregister(); -registerWhen(register("itemTooltip", (_, item) => { +registerWhen( + register("itemTooltip", (_, item) => { const name = item.getName(); if (name.startsWith("§aEnder Chest Page") || name.startsWith("§6Backpack Slot")) { - const split = name.split(' '); - const i = parseInt(split[split.length - 1]) - 1; - if (lastPreview === name) return; + const split = name.split(" "); + const i = parseInt(split[split.length - 1]) - 1; + if (lastPreview === name) return; - lastPreview = name; - previewItems = parseContainerCache(itemNBTs[name.startsWith("§a") ? "enderchests" : "backpacks"][i]); + lastPreview = name; + previewItems = parseContainerCache(itemNBTs[name.startsWith("§a") ? "enderchests" : "backpacks"][i]); - preview.register(); - clear.register(); + preview.register(); + clear.register(); } -}), () => Settings.containerPreview !== 0); + }), + () => Settings.containerPreview !== 0 +); diff --git a/features/container/JyrreTimer.js b/features/container/JyrreTimer.js index c559b81..3015bf8 100644 --- a/features/container/JyrreTimer.js +++ b/features/container/JyrreTimer.js @@ -1,23 +1,26 @@ -import Settings from "../../utils/Settings"; import { NBTTagString } from "../../utils/Constants"; import { registerWhen } from "../../utils/RegisterTils"; +import Settings from "../../utils/Settings"; import { formatTime } from "../../utils/functions/format"; /** * Adds time NBT tag to Jyrre bottles. */ -registerWhen(register("preItemRender", (_, __, ___, gui) => { +registerWhen( + register("preItemRender", (_, __, ___, gui) => { const item = Player.getContainer().getItems()[gui?.getSlotUnderMouse()?.field_75222_d]; if (!item || !item.getName().endsWith("Bottle of Jyrre")) return; - + const itemTag = item.getNBT().getCompoundTag("tag"); const loreTag = itemTag.getCompoundTag("display").getTagMap().get("Lore"); // Check if time already in tooltip const list = new NBTTagList(loreTag); if (list.getStringTagAt(9).startsWith("§7☲")) list.removeTag(9); - + // Add time tag const seconds = itemTag.getCompoundTag("ExtraAttributes").getInteger("bottle_of_jyrre_seconds"); list.insertTag(9, new NBTTagString(`§7☲ Time: §b${formatTime(seconds, 0, 3)}`)); -}), () => Settings.jyrreTimer); + }), + () => Settings.jyrreTimer +); diff --git a/features/container/MaxCraft.js b/features/container/MaxCraft.js index 26470da..74a173e 100644 --- a/features/container/MaxCraft.js +++ b/features/container/MaxCraft.js @@ -1,59 +1,68 @@ -import Settings from "../../utils/Settings"; import { GOLD, YELLOW } from "../../utils/Constants"; import { registerWhen } from "../../utils/RegisterTils"; +import Settings from "../../utils/Settings"; import { commafy, unformatNumber } from "../../utils/functions/format"; - let craftable = 0; const tooltip = register("preItemRender", (_, __, slot) => { - const button = Player.getContainer().getItems()[slot.getSlotIndex()]; - if (!button?.getName()?.startsWith("§aSupercraft")) return; - - // Put the max craftable amount into lore - const lore = button.getLore().join('\n').split('\n').slice(1); - const i = lore.findIndex((line, index) => - line.startsWith("§5§o§7§aCrafting") && !lore[index + 1]?.startsWith(`§5§o${GOLD} Max Craftable:`)); - if (i === -1) return; - - lore.splice(i + 1, 0, `${GOLD}Max Craftable: ${YELLOW + commafy(craftable)}`); - button.setLore(lore); + const button = Player.getContainer().getItems()[slot.getSlotIndex()]; + if (!button?.getName()?.startsWith("§aSupercraft")) return; + + // Put the max craftable amount into lore + const lore = button.getLore().join("\n").split("\n").slice(1); + const i = lore.findIndex( + (line, index) => line.startsWith("§5§o§7§aCrafting") && !lore[index + 1]?.startsWith(`§5§o${GOLD} Max Craftable:`) + ); + if (i === -1) return; + + lore.splice(i + 1, 0, `${GOLD}Max Craftable: ${YELLOW + commafy(craftable)}`); + button.setLore(lore); }).unregister(); const close = register("guiClosed", () => { - tooltip.unregister(); - close.unregister(); + tooltip.unregister(); + close.unregister(); }).unregister(); -registerWhen(register("guiOpened", () => { +registerWhen( + register("guiOpened", () => { Client.scheduleTask(3, () => { - // Check if the container is a supercrafting table - const container = Player.getContainer(); - const button = container.getStackInSlot(32); - const name = button?.getName(); - if (!name.startsWith("§aSupercraft")) return; - - // Get the max craft amount - maxCraft = Infinity; - button.getLore().forEach(line => { - if (!line.startsWith("§5§o §a✔") && !line.startsWith("§5§o §c✖")) return; - - const ratio = line.split(' ')[2].removeFormatting().split('/'); - const current = unformatNumber(ratio[0]); - const required = unformatNumber(ratio[1]); - - maxCraft = Math.min(maxCraft, Math.floor(current / required)); - }); - if (maxCraft === Infinity) maxCraft = 0; - - // Get empty inventory slots to find the max craftable amount - const crafting = name === "§aSupercraft" ? container.getStackInSlot(25).getStackSize() : - unformatNumber(name.split(' ')[1].removeFormatting().replace(/[x(]/g, '')); - const freeSpace = 64 * Player.getInventory().getItems().reduce((acc, item) => acc + (item === null), 0); - craftable = Math.min(maxCraft * crafting, freeSpace); - - // Set registers - tooltip.register(); - close.register(); + // Check if the container is a supercrafting table + const container = Player.getContainer(); + const button = container.getStackInSlot(32); + const name = button?.getName(); + if (!name.startsWith("§aSupercraft")) return; + + // Get the max craft amount + maxCraft = Infinity; + button.getLore().forEach((line) => { + if (!line.startsWith("§5§o §a✔") && !line.startsWith("§5§o §c✖")) return; + + const ratio = line.split(" ")[2].removeFormatting().split("/"); + const current = unformatNumber(ratio[0]); + const required = unformatNumber(ratio[1]); + + maxCraft = Math.min(maxCraft, Math.floor(current / required)); + }); + if (maxCraft === Infinity) maxCraft = 0; + + // Get empty inventory slots to find the max craftable amount + const crafting = + name === "§aSupercraft" + ? container.getStackInSlot(25).getStackSize() + : unformatNumber(name.split(" ")[1].removeFormatting().replace(/[x(]/g, "")); + const freeSpace = + 64 * + Player.getInventory() + .getItems() + .reduce((acc, item) => acc + (item === null), 0); + craftable = Math.min(maxCraft * crafting, freeSpace); + + // Set registers + tooltip.register(); + close.register(); }); -}), () => Settings.maxSupercraft); + }), + () => Settings.maxSupercraft +); diff --git a/features/container/Searchbar.js b/features/container/Searchbar.js index d2059da..a76011c 100644 --- a/features/container/Searchbar.js +++ b/features/container/Searchbar.js @@ -1,11 +1,10 @@ -import Settings from "../../utils/Settings"; import { DARK_GRAY, GuiTextField, ITALIC } from "../../utils/Constants"; -import { getSlotCoords } from "../../utils/functions/find"; -import { registerWhen } from "../../utils/RegisterTils"; import { data, itemNBTs } from "../../utils/Data"; +import { registerWhen } from "../../utils/RegisterTils"; +import Settings from "../../utils/Settings"; +import { getSlotCoords } from "../../utils/functions/find"; import { decompressNBT } from "../../utils/functions/misc"; - // Search bar parameters const loc = data.XL; const searchbar = new GuiTextField(0, Client.getMinecraft().field_71466_p, loc[0], loc[1], 192, 16); @@ -15,134 +14,179 @@ let calc = undefined; let indexes = []; let darken = []; function getHighlights() { - indexes = []; - darken = []; - const text = searchbar.func_146179_b(); - if (text === "") return; - - // Find highlights - const search = text.replace(/[^a-zA-Z0-9&|]/g, "").toLowerCase(); - if (search.length === 0) return; - - // Highlight backpack slots if in storage - if (Player.getContainer().getName() === "Storage") { - getContainers(search); - return; - } - - // Find current container content matches - const contents = search.split('||').map(ors => ors.split('&&')); - Player.getContainer().getItems().forEach((item, index) => { - if (item === null) return; - const name = item.getName().removeFormatting().replace(/[^a-zA-Z0-9]/g, "").toLowerCase(); - const lore = item.getLore().join('').replace(/[^a-zA-Z0-9]/g, "").toLowerCase(); - - let toAdd = contents.length; - contents.forEach(content => { - for (let cont of content) { - if (!name.includes(cont) && !lore.includes(cont)) { - toAdd--; - break; - } - } - }); - if (toAdd !== 0) indexes.push(index); - else darken.push(index); + indexes = []; + darken = []; + const text = searchbar.func_146179_b(); + if (text === "") return; + + // Find highlights + const search = text.replace(/[^a-zA-Z0-9&|]/g, "").toLowerCase(); + if (search.length === 0) return; + + // Highlight backpack slots if in storage + if (Player.getContainer().getName() === "Storage") { + getContainers(search); + return; + } + + // Find current container content matches + const contents = search.split("||").map((ors) => ors.split("&&")); + Player.getContainer() + .getItems() + .forEach((item, index) => { + if (item === null) return; + const name = item + .getName() + .removeFormatting() + .replace(/[^a-zA-Z0-9]/g, "") + .toLowerCase(); + const lore = item + .getLore() + .join("") + .replace(/[^a-zA-Z0-9]/g, "") + .toLowerCase(); + + let toAdd = contents.length; + contents.forEach((content) => { + for (let cont of content) { + if (!name.includes(cont) && !lore.includes(cont)) { + toAdd--; + break; + } + } + }); + if (toAdd !== 0) indexes.push(index); + else darken.push(index); }); } function getContainers(search) { - const contents = search.split('||').map(ors => ors.split('&&')); - itemNBTs.enderchests.forEach((ec, index) => { - if (ec.find(nbt => { - if (nbt === null) return false; - const display = decompressNBT(nbt).tag.display; - const name = display.Name.removeFormatting().replace(/[^a-zA-Z0-9]/g, "").toLowerCase(); - const lore = display.Lore === undefined ? "" : display.Lore.join('').replace(/[^a-zA-Z0-9]/g, "").toLowerCase(); - - return contents.find(content => { - for (let cont of content) { - if (name.includes(cont) || lore.includes(cont)) - return true; - } - }) !== undefined; - }) !== undefined) indexes.push(9 + index); - }); - - itemNBTs.backpacks.forEach((ec, index) => { - if (ec.find(nbt => { - if (nbt === null) return false; - const display = decompressNBT(nbt).tag.display; - const name = display.Name.removeFormatting().replace(/[^a-zA-Z0-9]/g, "").toLowerCase(); - const lore = display.Lore === undefined ? "" : display.Lore.join('').replace(/[^a-zA-Z0-9]/g, "").toLowerCase(); - - return contents.find(content => { - for (let cont of content) { - if (name.includes(cont) || lore.includes(cont)) - return true; - } - }) !== undefined; - }) !== undefined) indexes.push(27 + index); - }); + const contents = search.split("||").map((ors) => ors.split("&&")); + itemNBTs.enderchests.forEach((ec, index) => { + if ( + ec.find((nbt) => { + if (nbt === null) return false; + const display = decompressNBT(nbt).tag.display; + const name = display.Name.removeFormatting() + .replace(/[^a-zA-Z0-9]/g, "") + .toLowerCase(); + const lore = + display.Lore === undefined + ? "" + : display.Lore.join("") + .replace(/[^a-zA-Z0-9]/g, "") + .toLowerCase(); + + return ( + contents.find((content) => { + for (let cont of content) { + if (name.includes(cont) || lore.includes(cont)) return true; + } + }) !== undefined + ); + }) !== undefined + ) + indexes.push(9 + index); + }); + + itemNBTs.backpacks.forEach((ec, index) => { + if ( + ec.find((nbt) => { + if (nbt === null) return false; + const display = decompressNBT(nbt).tag.display; + const name = display.Name.removeFormatting() + .replace(/[^a-zA-Z0-9]/g, "") + .toLowerCase(); + const lore = + display.Lore === undefined + ? "" + : display.Lore.join("") + .replace(/[^a-zA-Z0-9]/g, "") + .toLowerCase(); + + return ( + contents.find((content) => { + for (let cont of content) { + if (name.includes(cont) || lore.includes(cont)) return true; + } + }) !== undefined + ); + }) !== undefined + ) + indexes.push(27 + index); + }); } // Render item highlights and search bar -registerWhen(register("guiRender", (x, y, gui) => { +registerWhen( + register("guiRender", (x, y, gui) => { if (!gui.class.getName().startsWith("net.minecraft.client.gui.inventory.")) return; searchbar.func_146194_f(); - indexes.forEach(index => { - const [x, y] = getSlotCoords(index); - - Renderer.translate(0, 0, 100); - Renderer.drawRect(Renderer.color(255, 255, 255, 255), x - 1, y - 1, 18, 18); + indexes.forEach((index) => { + const [x, y] = getSlotCoords(index); + + Renderer.translate(0, 0, 100); + Renderer.drawRect(Renderer.color(255, 255, 255, 255), x - 1, y - 1, 18, 18); + }); + darken.forEach((index) => { + const [x, y] = getSlotCoords(index); + + Renderer.translate(0, 0, 100); + Renderer.drawRect(Renderer.color(0, 0, 0, 255), x, y, 16, 16); }); - darken.forEach(index => { - const [x, y] = getSlotCoords(index); - - Renderer.translate(0, 0, 100); - Renderer.drawRect(Renderer.color(0, 0, 0, 255), x, y, 16, 16); - }) - if(calc !== undefined) Renderer.drawString(DARK_GRAY + calc, loc[0] - Renderer.getStringWidth(calc) + 190, loc[1] + 4); -}), () => Settings.searchbar); + if (calc !== undefined) + Renderer.drawString(DARK_GRAY + calc, loc[0] - Renderer.getStringWidth(calc) + 190, loc[1] + 4); + }), + () => Settings.searchbar +); /** * Stuff to move searchbox */ const gui = new Gui(); const renderOverlay = register("renderOverlay", () => { - if (!gui.isOpen()) return; - Renderer.drawString(`${ITALIC}x: ${Math.round(loc[0])}, y: ${Math.round(loc[1])}`, loc[0] + 2, loc[1] - 10); - Renderer.drawLine(Renderer.WHITE, loc[0], 1, loc[0], Renderer.screen.getHeight(), 0.5); - Renderer.drawLine(Renderer.WHITE, Renderer.screen.getWidth(), loc[1], 1, loc[1], 0.5); - searchbar.func_146194_f(); + if (!gui.isOpen()) return; + Renderer.drawString(`${ITALIC}x: ${Math.round(loc[0])}, y: ${Math.round(loc[1])}`, loc[0] + 2, loc[1] - 10); + Renderer.drawLine(Renderer.WHITE, loc[0], 1, loc[0], Renderer.screen.getHeight(), 0.5); + Renderer.drawLine(Renderer.WHITE, Renderer.screen.getWidth(), loc[1], 1, loc[1], 0.5); + searchbar.func_146194_f(); }).unregister(); // Render searchbox when moving register("command", () => { - renderOverlay.register(); - gui.open(); -}).setName("moveSearchbox", true).setAliases("moveSearch"); + renderOverlay.register(); + gui.open(); +}) + .setName("moveSearchbox", true) + .setAliases("moveSearch"); // Moving searchbox -registerWhen(register("guiMouseDrag", (x, y) => { +registerWhen( + register("guiMouseDrag", (x, y) => { if (!gui.isOpen()) return; loc[0] = x; loc[1] = y; searchbar.field_146209_f = x; searchbar.field_146210_g = y; -}), () => Settings.searchbar); + }), + () => Settings.searchbar +); // Detect mouse click on box -registerWhen(register("guiMouseClick", (x, y, button) => { +registerWhen( + register("guiMouseClick", (x, y, button) => { searchbar.func_146192_a(x, y, button); Client.scheduleTask(3, getHighlights); -}), () => Settings.searchbar); + }), + () => Settings.searchbar +); // Searchbox key detects -registerWhen(register("guiKey", (char, keyCode, _, event) => { +registerWhen( + register("guiKey", (char, keyCode, _, event) => { if (!searchbar.func_146206_l()) return; // Update searchbar and highlights @@ -152,27 +196,35 @@ registerWhen(register("guiKey", (char, keyCode, _, event) => { // Update calculation calc = undefined; try { - calc = eval(searchbar.func_146179_b()); - if (!isNaN(calc)) { - if (!Number.isInteger(calc)) calc = Math.round(calc * 10000) / 10000; - calc = calc.toString(); - } else calc = ""; - } catch(err) { - calc = undefined; + calc = eval(searchbar.func_146179_b()); + if (!isNaN(calc)) { + if (!Number.isInteger(calc)) calc = Math.round(calc * 10000) / 10000; + calc = calc.toString(); + } else calc = ""; + } catch (err) { + calc = undefined; } // Cancel all but escape key if (keyCode != 1) cancel(event); -}), () => Settings.searchbar); + }), + () => Settings.searchbar +); // Reset search when opening gui -registerWhen(register("guiOpened", () => { +registerWhen( + register("guiOpened", () => { Client.scheduleTask(1, getHighlights); -}), () => Settings.searchbar); + }), + () => Settings.searchbar +); // Exit search when closing gui -registerWhen(register("guiClosed", () => { +registerWhen( + register("guiClosed", () => { if (gui.isOpen()) renderOverlay.unregister(); searchbar.func_146195_b(false); indexes.length = 0; -}), () => Settings.searchbar); + }), + () => Settings.searchbar +); diff --git a/features/container/SlotBinding.js b/features/container/SlotBinding.js index ea5c3c5..00c2948 100644 --- a/features/container/SlotBinding.js +++ b/features/container/SlotBinding.js @@ -1,145 +1,177 @@ -import Settings from "../../utils/Settings"; import { BOLD, DARK_GRAY, GOLD, GREEN, LOGO, RED, YELLOW } from "../../utils/Constants"; -import { getSlotCoords } from "../../utils/functions/find"; -import { registerWhen } from "../../utils/RegisterTils"; import { data } from "../../utils/Data"; import { printList } from "../../utils/ListTils"; - +import { registerWhen } from "../../utils/RegisterTils"; +import Settings from "../../utils/Settings"; +import { getSlotCoords } from "../../utils/functions/find"; // Bind key const bindKey = new KeyBind("Slot Binding", data.bindKey, "./VolcAddons.xdd"); -register("gameUnload", () => { data.bindKey = bindKey.getKeyCode() }).setPriority(Priority.HIGHEST); +register("gameUnload", () => { + data.bindKey = bindKey.getKeyCode(); +}).setPriority(Priority.HIGHEST); let binding = undefined; const HOTBAR = ["36", "37", "38", "39", "40", "41", "42", "43", "44"]; -HOTBAR.forEach(slot => { - if (!data.slotBinds.hasOwnProperty(slot) && !Array.isArray(data.slotBinds[slot])) - data.slotBinds[slot] = []; +HOTBAR.forEach((slot) => { + if (!data.slotBinds.hasOwnProperty(slot) && !Array.isArray(data.slotBinds[slot])) data.slotBinds[slot] = []; }); // Bind slots -registerWhen(register("guiKey", (_, keyCode, gui) => { +registerWhen( + register("guiKey", (_, keyCode, gui) => { if (Player.getContainer().getSize() !== 45 || keyCode !== bindKey.getKeyCode()) return; const bind = gui?.getSlotUnderMouse()?.field_75222_d; if (bind === undefined || bind <= 4) return; if (data.slotBinds.hasOwnProperty(bind) && bind < 36) { - const binded = data.slotBinds[bind]; - data.slotBinds[binded].splice(data.slotBinds[binded].indexOf(bind), 1); - delete data.slotBinds[bind]; + const binded = data.slotBinds[bind]; + data.slotBinds[binded].splice(data.slotBinds[binded].indexOf(bind), 1); + delete data.slotBinds[bind]; } else if (binding === undefined) binding = bind; else if (binding === bind) binding = undefined; else if (binding >= 36 && bind < 36) { - data.slotBinds[bind] = binding; - data.slotBinds[binding].push(bind); - binding = undefined; + data.slotBinds[bind] = binding; + data.slotBinds[binding].push(bind); + binding = undefined; } else if (binding < 36 && bind >= 36) { - data.slotBinds[binding] = bind; - data.slotBinds[bind].push(binding); - binding = undefined; + data.slotBinds[binding] = bind; + data.slotBinds[bind].push(binding); + binding = undefined; } -}), () => Settings.slotBinding); -registerWhen(register("guiClosed", () => { + }), + () => Settings.slotBinding +); +registerWhen( + register("guiClosed", () => { binding = undefined; -}), () => Settings.slotBinding); + }), + () => Settings.slotBinding +); // Swap binded items -registerWhen(register("guiMouseClick", (_, __, button, gui, event) => { - if (gui.class.getName() !== "net.minecraft.client.gui.inventory.GuiInventory" || - button !== 0 || !Keyboard.isKeyDown(Keyboard.KEY_LSHIFT)) return; +registerWhen( + register("guiMouseClick", (_, __, button, gui, event) => { + if ( + gui.class.getName() !== "net.minecraft.client.gui.inventory.GuiInventory" || + button !== 0 || + !Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) + ) + return; const hover = gui?.getSlotUnderMouse()?.field_75222_d ?? 36; const bind = data.slotBinds[hover]; if (hover >= 36 || bind === undefined) return; // playerController.windowClick() - Client.getMinecraft().field_71442_b.func_78753_a(Player.getContainer().getWindowId(), hover, bind - 36, 2, Player.getPlayer()); + Client.getMinecraft().field_71442_b.func_78753_a( + Player.getContainer().getWindowId(), + hover, + bind - 36, + 2, + Player.getPlayer() + ); cancel(event); -}), () => Settings.slotBinding); + }), + () => Settings.slotBinding +); // Render bindings -registerWhen(register("guiRender", (x, y, gui) => { +registerWhen( + register("guiRender", (x, y, gui) => { if (gui.class.getName() !== "net.minecraft.client.gui.inventory.GuiInventory") return; // render binding if (binding !== undefined) { - const [x, y] = getSlotCoords(binding); - - Renderer.translate(0, 0, 200); - Renderer.drawRect(Renderer.AQUA, x, y, 16, 16); + const [x, y] = getSlotCoords(binding); + + Renderer.translate(0, 0, 200); + Renderer.drawRect(Renderer.AQUA, x, y, 16, 16); } // render all binds - Object.keys(data.slotBinds).forEach(bind => { - if (Array.isArray(data.slotBinds[bind]) && data.slotBinds[bind].length === 0) return; - const [x, y] = getSlotCoords(bind); + Object.keys(data.slotBinds).forEach((bind) => { + if (Array.isArray(data.slotBinds[bind]) && data.slotBinds[bind].length === 0) return; + const [x, y] = getSlotCoords(bind); - Renderer.translate(0, 0, 200); - Renderer.drawRect(Renderer.GRAY, x, y, 16, 16); + Renderer.translate(0, 0, 200); + Renderer.drawRect(Renderer.GRAY, x, y, 16, 16); }); // render hovered binds const hover = gui?.getSlotUnderMouse()?.field_75222_d; const bind = data.slotBinds[hover]; if (Array.isArray(bind)) { - bind.forEach(slot => { - const [x, y] = getSlotCoords(hover); - const [dx, dy] = getSlotCoords(slot); - - Renderer.translate(0, 0, 300); - Renderer.drawLine(Renderer.AQUA, x + 8, y + 8, dx + 8, dy + 8, 1); - }); - } else if (bind !== undefined) { + bind.forEach((slot) => { const [x, y] = getSlotCoords(hover); - const [dx, dy] = getSlotCoords(bind); + const [dx, dy] = getSlotCoords(slot); Renderer.translate(0, 0, 300); Renderer.drawLine(Renderer.AQUA, x + 8, y + 8, dx + 8, dy + 8, 1); + }); + } else if (bind !== undefined) { + const [x, y] = getSlotCoords(hover); + const [dx, dy] = getSlotCoords(bind); + + Renderer.translate(0, 0, 300); + Renderer.drawLine(Renderer.AQUA, x + 8, y + 8, dx + 8, dy + 8, 1); } -}), () => Settings.slotBinding); + }), + () => Settings.slotBinding +); /** * Slot binding related commands... - * + * * @param {string[]} args - Command arguments. */ export function slotCommands(args) { - const command = args[1]; - const name = args[2]; - - switch(command) { - case "save": - data.bindPresets[name] = data.slotBinds; - ChatLib.chat(`${LOGO + GREEN}Successfully saved slot bindings using key: "${name}".`); - break; - case "delete": - if (data.bindPresets.hasOwnProperty(name)) { - delete data.bindPresets[name]; - ChatLib.chat(`${LOGO + GREEN}Succesfully deleted slot bindings using key: "${name}"`); - } else ChatLib.chat(`${LOGO + RED}Invalid bind key: "${name}"`); - break; - case "load": - if (data.bindPresets.hasOwnProperty(name)) { - data.slotBinds = data.bindPresets[name]; - ChatLib.chat(`${LOGO + GREEN}Succesfully loaded slot bindings using key: "${name}".`); - } else ChatLib.chat(`${LOGO + RED}Error: There are no presets using "${name}" key.`); - break; - case "list": - case "view": - const bindingKeys = Object.keys(data.bindPresets); - printList(bindingKeys, "Bindings", parseInt(args[2] ?? 1)); - break; - case "clear": - case "reset": - data.slotBinds = {"36": [], "37": [], "38": [], "39": [], "40": [], "41": [], "42": [], "43": [], "44": []}; - ChatLib.chat(`${LOGO + GREEN}Successfully reset slot bindings!`); - break; - case "help": - default: - if (command !== "help") ChatLib.chat(`${LOGO + RED}Error: Invalid argument "${command}"!\n`); - ChatLib.chat( -`${LOGO + GOLD + BOLD}Container Buttons Commands: + const command = args[1]; + const name = args[2]; + + switch (command) { + case "save": + data.bindPresets[name] = data.slotBinds; + ChatLib.chat(`${LOGO + GREEN}Successfully saved slot bindings using key: "${name}".`); + break; + case "delete": + if (data.bindPresets.hasOwnProperty(name)) { + delete data.bindPresets[name]; + ChatLib.chat(`${LOGO + GREEN}Succesfully deleted slot bindings using key: "${name}"`); + } else ChatLib.chat(`${LOGO + RED}Invalid bind key: "${name}"`); + break; + case "load": + if (data.bindPresets.hasOwnProperty(name)) { + data.slotBinds = data.bindPresets[name]; + ChatLib.chat(`${LOGO + GREEN}Succesfully loaded slot bindings using key: "${name}".`); + } else ChatLib.chat(`${LOGO + RED}Error: There are no presets using "${name}" key.`); + break; + case "list": + case "view": + const bindingKeys = Object.keys(data.bindPresets); + printList(bindingKeys, "Bindings", parseInt(args[2] ?? 1)); + break; + case "clear": + case "reset": + data.slotBinds = { + 36: [], + 37: [], + 38: [], + 39: [], + 40: [], + 41: [], + 42: [], + 43: [], + 44: [], + }; + ChatLib.chat(`${LOGO + GREEN}Successfully reset slot bindings!`); + break; + case "help": + default: + if (command !== "help") ChatLib.chat(`${LOGO + RED}Error: Invalid argument "${command}"!\n`); + ChatLib.chat( + `${LOGO + GOLD + BOLD}Container Buttons Commands: ${DARK_GRAY}- ${GOLD}Base: ${YELLOW}/va bind ${DARK_GRAY}- ${GOLD}save : ${YELLOW}Save binding data to presets using key. @@ -147,7 +179,8 @@ export function slotCommands(args) { ${DARK_GRAY}- ${GOLD}load : ${YELLOW}Load binding preset using key. ${DARK_GRAY}- ${GOLD}list: ${YELLOW}View all available binding presets. ${DARK_GRAY}- ${GOLD}clear: ${YELLOW}Removes all bindings. - ${DARK_GRAY}- ${GOLD}help: ${YELLOW}Displays this help message.`); - break; - } + ${DARK_GRAY}- ${GOLD}help: ${YELLOW}Displays this help message.` + ); + break; + } } diff --git a/features/container/SoldHighlight.js b/features/container/SoldHighlight.js index 1639f76..a405861 100644 --- a/features/container/SoldHighlight.js +++ b/features/container/SoldHighlight.js @@ -1,46 +1,51 @@ +import { registerWhen } from "../../utils/RegisterTils"; import Settings from "../../utils/Settings"; import { getSlotCoords } from "../../utils/functions/find"; -import { registerWhen } from "../../utils/RegisterTils"; - let own = []; let coop = []; const renderSold = register("guiRender", () => { - own.forEach(index => { - const [x, y] = getSlotCoords(index); + own.forEach((index) => { + const [x, y] = getSlotCoords(index); - Renderer.translate(0, 0, 100); - Renderer.drawRect(Renderer.GREEN, x, y, 16, 16); - }); + Renderer.translate(0, 0, 100); + Renderer.drawRect(Renderer.GREEN, x, y, 16, 16); + }); - coop.forEach(index => { - const [x, y] = getSlotCoords(index); + coop.forEach((index) => { + const [x, y] = getSlotCoords(index); - Renderer.translate(0, 0, 100); - Renderer.drawRect(Renderer.GOLD, x, y, 16, 16); - }); + Renderer.translate(0, 0, 100); + Renderer.drawRect(Renderer.GOLD, x, y, 16, 16); + }); }).unregister(); const clear = register("guiClosed", () => { - own = []; - coop = []; - renderSold.unregister(); - clear.unregister(); + own = []; + coop = []; + renderSold.unregister(); + clear.unregister(); }).unregister(); -registerWhen(register("guiOpened", () => { +registerWhen( + register("guiOpened", () => { Client.scheduleTask(2, () => { - if (Player.getContainer().getName() !== "Manage Auctions") return; - renderSold.register(); - clear.register(); - - // Fetch all sold items - Player.getContainer().getItems().forEach((item, index) => { - if (item !== null && item.getLore().find(line => line === "§5§o§7Status: §aSold!") !== undefined) { - if (item.getLore().find(line => line === "§5§o§aThis is your own auction!") !== undefined) own.push(index); - else coop.push(index); - } + if (Player.getContainer().getName() !== "Manage Auctions") return; + renderSold.register(); + clear.register(); + + // Fetch all sold items + Player.getContainer() + .getItems() + .forEach((item, index) => { + if (item !== null && item.getLore().find((line) => line === "§5§o§7Status: §aSold!") !== undefined) { + if (item.getLore().find((line) => line === "§5§o§aThis is your own auction!") !== undefined) + own.push(index); + else coop.push(index); + } }); }); -}), () => Settings.auctionHighlight); + }), + () => Settings.auctionHighlight +); diff --git a/features/container/WardrobeHotkey.js b/features/container/WardrobeHotkey.js index 76ec11e..39479d4 100644 --- a/features/container/WardrobeHotkey.js +++ b/features/container/WardrobeHotkey.js @@ -1,104 +1,123 @@ -import Settings from "../../utils/Settings"; import { GREEN, RED } from "../../utils/Constants"; -import { registerWhen } from "../../utils/RegisterTils"; import { data } from "../../utils/Data"; - +import { registerWhen } from "../../utils/RegisterTils"; +import Settings from "../../utils/Settings"; /** * Key press to warp player to closest burrow. */ const wardKey = new KeyBind("Wardrobe Set", data.wardKey, "./VolcAddons.xdd"); -register("gameUnload", () => { data.wardKey = wardKey.getKeyCode() }).setPriority(Priority.HIGHEST); +register("gameUnload", () => { + data.wardKey = wardKey.getKeyCode(); +}).setPriority(Priority.HIGHEST); /** * Sets wardrobe slot item stack size based on linked key. */ function setKeySize() { - const items = Player.getContainer().getItems(); - Object.keys(data.wardBinds).forEach(key => { - const item = items[35 + data.wardBinds[key]]; - if (item !== null) item.setStackSize(key - 1); - }); + const items = Player.getContainer().getItems(); + Object.keys(data.wardBinds).forEach((key) => { + const item = items[35 + data.wardBinds[key]]; + if (item !== null) item.setStackSize(key - 1); + }); } let instructions = ""; -registerWhen(register("guiRender", () => { +registerWhen( + register("guiRender", () => { if (!Player.getContainer()?.getName()?.startsWith("Wardrobe")) return; - Renderer.drawString(instructions, (Renderer.screen.getWidth() - Renderer.getStringWidth(instructions)) / 2 , 80, true); + Renderer.drawString( + instructions, + (Renderer.screen.getWidth() - Renderer.getStringWidth(instructions)) / 2, + 80, + true + ); setKeySize(); -}), () => Settings.wardrobeBinding); + }), + () => Settings.wardrobeBinding +); let settingBind = false; -registerWhen(register("guiClosed", () => { +registerWhen( + register("guiClosed", () => { instructions = ""; settingBind = false; -}), () => Settings.wardrobeBinding); + }), + () => Settings.wardrobeBinding +); const C0EPacketClickWindow = Java.type("net.minecraft.network.play.client.C0EPacketClickWindow"); -registerWhen(register("guiKey", (_, code, gui, event) => { +registerWhen( + register("guiKey", (_, code, gui, event) => { if (settingBind) { - cancel(event); - const slot = gui?.getSlotUnderMouse()?.field_75222_d - 35; - const linkedKey = Object.keys(data.wardBinds).find(key => data.wardBinds[key] === slot); - - // Check if hovering over valid slot - if (isNaN(slot) || slot < 0 || slot > 9) { - if (code === 1) { - instructions = `${RED}Cancelled wardrobe binding.`; - settingBind = false; - return; - } - - instructions = `${RED}Please hover over a valid slot.`; - return; - } + cancel(event); + const slot = gui?.getSlotUnderMouse()?.field_75222_d - 35; + const linkedKey = Object.keys(data.wardBinds).find((key) => data.wardBinds[key] === slot); - // Unbind key + // Check if hovering over valid slot + if (isNaN(slot) || slot < 0 || slot > 9) { if (code === 1) { - if (linkedKey !== undefined) delete data.wardBinds[linkedKey]; - - instructions = `${GREEN}Succesfully unbound slot ${slot}.`; - settingBind = false; - setKeySize(); - return; + instructions = `${RED}Cancelled wardrobe binding.`; + settingBind = false; + return; } - // Check if key is the wardrobe bind key - if (code === wardKey.getKeyCode()) { - instructions = `${RED}You cannot use the wardrobe set key as a hotkey.`; - return; - } + instructions = `${RED}Please hover over a valid slot.`; + return; + } - // Check if key is already used - if (data.wardBinds.hasOwnProperty(code)) { - instructions = `${RED}Key #${code - 1} is already binded to slot ${data.wardBinds[code]}`; - return; - } + // Unbind key + if (code === 1) { + if (linkedKey !== undefined) delete data.wardBinds[linkedKey]; - // Set bind - data.wardBinds[code] = slot; - instructions = `${GREEN}Successfully binded slot ${slot} to key #${code - 1}.`; + instructions = `${GREEN}Succesfully unbound slot ${slot}.`; + settingBind = false; + setKeySize(); + return; + } - // Remove any key already bound - if (linkedKey !== undefined) { - instructions += ` Unbound key #${linkedKey} from slot ${data.wardBinds[linkedKey]}.`; - delete data.wardBinds[linkedKey]; - } + // Check if key is the wardrobe bind key + if (code === wardKey.getKeyCode()) { + instructions = `${RED}You cannot use the wardrobe set key as a hotkey.`; + return; + } - setKeySize(); - settingBind = false; + // Check if key is already used + if (data.wardBinds.hasOwnProperty(code)) { + instructions = `${RED}Key #${code - 1} is already binded to slot ${data.wardBinds[code]}`; return; + } + + // Set bind + data.wardBinds[code] = slot; + instructions = `${GREEN}Successfully binded slot ${slot} to key #${code - 1}.`; + + // Remove any key already bound + if (linkedKey !== undefined) { + instructions += ` Unbound key #${linkedKey} from slot ${data.wardBinds[linkedKey]}.`; + delete data.wardBinds[linkedKey]; + } + + setKeySize(); + settingBind = false; + return; } if (!Player.getContainer()?.getName()?.startsWith("Wardrobe")) return; if (data.wardBinds.hasOwnProperty(code)) { - cancel(event); - Client.sendPacket(new C0EPacketClickWindow(Player.getContainer().getWindowId(), data.wardBinds[code] + 35, 0, 0, null, 0)); - Client.scheduleTask(3, () => Client.sendPacket(new C0EPacketClickWindow(Player.getContainer().getWindowId(), 49, 0, 0, null, 0))); + cancel(event); + Client.sendPacket( + new C0EPacketClickWindow(Player.getContainer().getWindowId(), data.wardBinds[code] + 35, 0, 0, null, 0) + ); + Client.scheduleTask(3, () => + Client.sendPacket(new C0EPacketClickWindow(Player.getContainer().getWindowId(), 49, 0, 0, null, 0)) + ); } else if (code === wardKey.getKeyCode()) { - cancel(event); - settingBind = true; - instructions = "Please hover over wardrobe slot and press hotkey to be binded (press escape to unbind)."; + cancel(event); + settingBind = true; + instructions = "Please hover over wardrobe slot and press hotkey to be binded (press escape to unbind)."; } -}), () => Settings.wardrobeBinding); + }), + () => Settings.wardrobeBinding +); diff --git a/features/crimsonIsle/GoldenFishTimer.js b/features/crimsonIsle/GoldenFishTimer.js index 72b27aa..7eaf6f7 100644 --- a/features/crimsonIsle/GoldenFishTimer.js +++ b/features/crimsonIsle/GoldenFishTimer.js @@ -1,50 +1,63 @@ -import location from "../../utils/Location"; -import Settings from "../../utils/Settings" import { BOLD, DARK_RED, GOLD, RESET, WHITE } from "../../utils/Constants"; -import { formatTime } from "../../utils/functions/format"; -import { registerWhen } from "../../utils/RegisterTils"; -import { Overlay } from "../../utils/Overlay"; import { data } from "../../utils/Data"; - +import location from "../../utils/Location"; +import { Overlay } from "../../utils/Overlay"; +import { registerWhen } from "../../utils/RegisterTils"; +import Settings from "../../utils/Settings"; +import { formatTime } from "../../utils/functions/format"; /** * Variables used to track and display fishing timer. */ let lastCast = 0; let lastFish = 0; -const fishExample = -`${GOLD + BOLD}Last Cast: ${RESET}Yee +const fishExample = `${GOLD + BOLD}Last Cast: ${RESET}Yee ${GOLD + BOLD}Last Fish: ${RESET}Haw`; const fishOverlay = new Overlay("goldenFishAlert", data.TL, "moveGolden", fishExample, ["Crimson Isle"]); /** * Increments time and updates Golden Fish Overlay every second. */ -registerWhen(register("step", () => { +registerWhen( + register("step", () => { lastCast += 1; lastFish += 1; - if (lastCast > 270) - lastFish = 0; - - fishOverlay.setMessage( -`${GOLD + BOLD}Last Cast: ${lastCast > 240 ? DARK_RED : WHITE + formatTime(lastCast)} -${GOLD + BOLD}Last Fish: ${RESET + formatTime(lastCast > 270 ? 0 : lastFish)}`); -}).setFps(1), () => location.getWorld() === "Crimson Isle" && Settings.goldenFishAlert); + if (lastCast > 270) lastFish = 0; + + fishOverlay.setMessage( + `${GOLD + BOLD}Last Cast: ${lastCast > 240 ? DARK_RED : WHITE + formatTime(lastCast)} +${GOLD + BOLD}Last Fish: ${RESET + formatTime(lastCast > 270 ? 0 : lastFish)}` + ); + }).setFps(1), + () => location.getWorld() === "Crimson Isle" && Settings.goldenFishAlert +); /** * Resets "lastCast" variable whenever player right clicks with a fishing rod in hand. */ -registerWhen(register("clicked", (x, y, button, state) => { +registerWhen( + register("clicked", (x, y, button, state) => { if (!button || !state || Player.getHeldItem() === null) return; - if (Player.getHeldItem().getNBT().getCompoundTag("tag").getCompoundTag("ExtraAttributes").getString("id").includes("ROD")) - lastCast = 0; -}), () => location.getWorld() === "Crimson Isle" && Settings.goldenFishAlert); + if ( + Player.getHeldItem() + .getNBT() + .getCompoundTag("tag") + .getCompoundTag("ExtraAttributes") + .getString("id") + .includes("ROD") + ) + lastCast = 0; + }), + () => location.getWorld() === "Crimson Isle" && Settings.goldenFishAlert +); /** * Resets "lastFish" variable whenever the Golden Fish message appears in chat. */ -registerWhen(register("chat", () => { +registerWhen( + register("chat", () => { lastFish = 0; -}).setCriteria("You spot a Golden Fish surface from beneath the lava!"), -() => location.getWorld() === "Crimson Isle" && Settings.goldenFishAlert); + }).setCriteria("You spot a Golden Fish surface from beneath the lava!"), + () => location.getWorld() === "Crimson Isle" && Settings.goldenFishAlert +); diff --git a/features/crimsonIsle/MythicDetect.js b/features/crimsonIsle/MythicDetect.js index 4254aae..c3a18a8 100644 --- a/features/crimsonIsle/MythicDetect.js +++ b/features/crimsonIsle/MythicDetect.js @@ -1,24 +1,27 @@ -import location from "../../utils/Location"; -import Settings from "../../utils/Settings"; import { BLUE, BOLD, DARK_BLUE, DARK_RED, GOLEM_CLASS, GUARDIAN_CLASS, RED, WHITE } from "../../utils/Constants"; -import { announceMob } from "../../utils/functions/misc"; -import { registerWhen } from "../../utils/RegisterTils"; import { data } from "../../utils/Data"; -import Waypoint from "../../utils/Waypoint"; +import location from "../../utils/Location"; +import { registerWhen } from "../../utils/RegisterTils"; +import Settings from "../../utils/Settings"; import { setTitle } from "../../utils/Title"; - +import Waypoint from "../../utils/Waypoint"; +import { announceMob } from "../../utils/functions/misc"; /** * Announce to party/all chat whenever player spawns a mythic lava creature. */ -registerWhen(register("chat", () => { +registerWhen( + register("chat", () => { announceMob(Settings.mythicLavaAnnounce, "Lord Jawbus", Player.getX(), Player.getY(), Player.getZ()); -}).setCriteria("You have angered a legendary creature... Lord Jawbus has arrived."), -() => location.getWorld() === "Crimson Isle" && Settings.mythicLavaAnnounce !== 0); -registerWhen(register("chat", () => { + }).setCriteria("You have angered a legendary creature... Lord Jawbus has arrived."), + () => location.getWorld() === "Crimson Isle" && Settings.mythicLavaAnnounce !== 0 +); +registerWhen( + register("chat", () => { announceMob(Settings.mythicLavaAnnounce, "Thunder", Player.getX(), Player.getY(), Player.getZ()); -}).setCriteria("You hear a massive rumble as Thunder emerges."), -() => location.getWorld() === "Crimson Isle" && Settings.mythicLavaAnnounce !== 0); + }).setCriteria("You hear a massive rumble as Thunder emerges."), + () => location.getWorld() === "Crimson Isle" && Settings.mythicLavaAnnounce !== 0 +); /** * Detects if any mythic lava creatures are near the player. @@ -26,35 +29,40 @@ registerWhen(register("chat", () => { const jaWaypoints = new Waypoint([0.55, 0, 0], 2, true, true, false); const thunderWaypoints = new Waypoint([0, 0, 0.55], 2, true, true, false); -registerWhen(register("step", () => { +registerWhen( + register("step", () => { jaWaypoints.clear(); thunderWaypoints.clear(); - - const thunders = World.getAllEntitiesOfType(GUARDIAN_CLASS).filter(guardian => guardian.getEntity().func_175461_cl()); + + const thunders = World.getAllEntitiesOfType(GUARDIAN_CLASS).filter((guardian) => + guardian.getEntity().func_175461_cl() + ); if (thunders.length > 0) { - // Check if Thunder is dead - let foundDead = false; - thunders.forEach(thunder => { - if (data.moblist.includes("jawbus")) thunderWaypoints.push([BLUE + "T1 Zeus", thunder]); - if (thunder.getEntity().func_110143_aJ() === 0) foundDead = true; - }); + // Check if Thunder is dead + let foundDead = false; + thunders.forEach((thunder) => { + if (data.moblist.includes("jawbus")) thunderWaypoints.push([BLUE + "T1 Zeus", thunder]); + if (thunder.getEntity().func_110143_aJ() === 0) foundDead = true; + }); - // Update HUD - if (foundDead) setTitle(`${DARK_BLUE + BOLD}THUNDER ${RED}DEAD!`, "", 0, 50, 10, 38); - else setTitle(`${DARK_BLUE + BOLD}THUNDER ${WHITE}DETECTED!`, "", 0, 25, 5, 40); + // Update HUD + if (foundDead) setTitle(`${DARK_BLUE + BOLD}THUNDER ${RED}DEAD!`, "", 0, 50, 10, 38); + else setTitle(`${DARK_BLUE + BOLD}THUNDER ${WHITE}DETECTED!`, "", 0, 25, 5, 40); } const jawbussy = World.getAllEntitiesOfType(GOLEM_CLASS); if (jawbussy.length > 0) { - // Check if Jawbus is dead - let foundDead = false; - jawbussy.forEach(jawbus => { - if (data.moblist.includes("jawbus")) jaWaypoints.push([DARK_RED + "Jawbussy", jawbus]); - if (jawbus.getEntity().func_110143_aJ() === 0) foundDead = true; - }); + // Check if Jawbus is dead + let foundDead = false; + jawbussy.forEach((jawbus) => { + if (data.moblist.includes("jawbus")) jaWaypoints.push([DARK_RED + "Jawbussy", jawbus]); + if (jawbus.getEntity().func_110143_aJ() === 0) foundDead = true; + }); - // Update HUD - if (foundDead) setTitle(`${DARK_RED + BOLD}JAWBUS ${RED}DEAD!`, "", 0, 50, 10, 39); - else setTitle(`${DARK_RED + BOLD}JAWBUS ${WHITE}DETECTED!`, "", 0, 25, 5, 41); + // Update HUD + if (foundDead) setTitle(`${DARK_RED + BOLD}JAWBUS ${RED}DEAD!`, "", 0, 50, 10, 39); + else setTitle(`${DARK_RED + BOLD}JAWBUS ${WHITE}DETECTED!`, "", 0, 25, 5, 41); } -}).setFps(2), () => location.getWorld() === "Crimson Isle" && Settings.mythicLavaDetect); + }).setFps(2), + () => location.getWorld() === "Crimson Isle" && Settings.mythicLavaDetect +); diff --git a/features/crimsonIsle/TrophyCounter.js b/features/crimsonIsle/TrophyCounter.js index 7c5193a..d0f8c8e 100644 --- a/features/crimsonIsle/TrophyCounter.js +++ b/features/crimsonIsle/TrophyCounter.js @@ -1,18 +1,28 @@ +import { + AQUA, + BLUE, + BOLD, + DARK_AQUA, + DARK_GRAY, + DARK_PURPLE, + GOLD, + GRAY, + GREEN, + LOGO, + WHITE, +} from "../../utils/Constants"; +import { data } from "../../utils/Data"; import location from "../../utils/Location"; -import Settings from "../../utils/Settings"; -import { AQUA, BLUE, BOLD, DARK_AQUA, DARK_GRAY, DARK_PURPLE, GOLD, GRAY, GREEN, LOGO, WHITE } from "../../utils/Constants"; -import { convertToTitleCase } from "../../utils/functions/format"; -import { registerWhen } from "../../utils/RegisterTils"; import { Overlay } from "../../utils/Overlay"; +import { registerWhen } from "../../utils/RegisterTils"; +import Settings from "../../utils/Settings"; import { getPaused } from "../../utils/Stat"; -import { data } from "../../utils/Data"; - +import { convertToTitleCase } from "../../utils/functions/format"; /** * Variables used to format and display trophy fishes. */ -const trophyExample = -`&6&lTrophy Fishing: +const trophyExample = `&6&lTrophy Fishing: &fBlobfish&f: &323477 &816649 &76249 &6518 &b61 &fGusher&f: &39627 &86838 &72556 &6210 &b23 &9Lava Horse&f: &36831 &84939 &71733 &6143 &b16 @@ -38,38 +48,38 @@ trophyOverlay.setMessage(""); * Variables used for formatting */ const TROPHY_COLORS = { - blobfish: WHITE, - gusher: WHITE, - skeleton_fish: DARK_PURPLE, - lava_horse: BLUE, - golden_fish: GOLD, - mana_ray: BLUE, - flyfish: GREEN, - obfuscated_fish_1: WHITE, - obfuscated_fish_2: GREEN, - volcanic_stonefish: BLUE, - steaming_hot_flounder: WHITE, - sulphur_skitter: WHITE, - moldfin: DARK_PURPLE, - soul_fish: DARK_PURPLE, - vanille: BLUE, - obfuscated_fish_3: BLUE, - karate_fish: DARK_PURPLE, - slugfish: GREEN -} + blobfish: WHITE, + gusher: WHITE, + skeleton_fish: DARK_PURPLE, + lava_horse: BLUE, + golden_fish: GOLD, + mana_ray: BLUE, + flyfish: GREEN, + obfuscated_fish_1: WHITE, + obfuscated_fish_2: GREEN, + volcanic_stonefish: BLUE, + steaming_hot_flounder: WHITE, + sulphur_skitter: WHITE, + moldfin: DARK_PURPLE, + soul_fish: DARK_PURPLE, + vanille: BLUE, + obfuscated_fish_3: BLUE, + karate_fish: DARK_PURPLE, + slugfish: GREEN, +}; const TROPHY_ID = { - "lavahorse": "lava_horse", - "obfuscated_1": "obfuscated_fish_1", - "obfuscated_2": "obfuscated_fish_2", - "obfuscated_3": "obfuscated_fish_3", - "steaming-hot_flounder": "steaming_hot_flounder" -} + lavahorse: "lava_horse", + obfuscated_1: "obfuscated_fish_1", + obfuscated_2: "obfuscated_fish_2", + obfuscated_3: "obfuscated_fish_3", + "steaming-hot_flounder": "steaming_hot_flounder", +}; const TIER_INDEX = { - "bronze": 1, - "silver": 2, - "gold": 3, - "diamond": 4 -} + bronze: 1, + silver: 2, + gold: 3, + diamond: 4, +}; /** * Variables used to track trophy fishes/ @@ -77,63 +87,78 @@ const TIER_INDEX = { let sessionTrophy = {}; let timePassed = 0; register("command", () => { - sessionTrophy = {}; - timePassed = 0; - trophyOverlay.setMessage(""); - ChatLib.chat(`${LOGO + GREEN}Successfully reset trophy fish counter!`); + sessionTrophy = {}; + timePassed = 0; + trophyOverlay.setMessage(""); + ChatLib.chat(`${LOGO + GREEN}Successfully reset trophy fish counter!`); }).setName("resetTrophy"); /** * Update trophyOverlay message using inputted trophy data. */ function updateMessage() { - const sortedTrophy = Object.entries(sessionTrophy).sort((a, b) => b[1][0] - a[1][0]).reduce((sorted, [fish, fishData]) => { - if (fishData[0] !== 0) { - const title = TROPHY_COLORS[fish] + convertToTitleCase(fish); - const [total, bronze, silver, gold, diamond] = fishData; - const rate = `${GRAY}- ${WHITE + (total * 3600 / timePassed).toFixed(0)}/hr`; - sorted.push(`${title + WHITE}: ${DARK_AQUA + total} ${DARK_GRAY + bronze} ${GRAY + silver} ${GOLD + gold} ${AQUA + diamond} ${rate}`); - } - return sorted; + const sortedTrophy = Object.entries(sessionTrophy) + .sort((a, b) => b[1][0] - a[1][0]) + .reduce((sorted, [fish, fishData]) => { + if (fishData[0] !== 0) { + const title = TROPHY_COLORS[fish] + convertToTitleCase(fish); + const [total, bronze, silver, gold, diamond] = fishData; + const rate = `${GRAY}- ${WHITE + ((total * 3600) / timePassed).toFixed(0)}/hr`; + sorted.push( + `${title + WHITE}: ${DARK_AQUA + total} ${DARK_GRAY + bronze} ${GRAY + silver} ${GOLD + gold} ${ + AQUA + diamond + } ${rate}` + ); + } + return sorted; }, []); - - if (sortedTrophy.length != 0) trophyOverlay.setMessage(`${GOLD + BOLD}Trophy Fishing:\n${sortedTrophy.join("\n")}`); + + if (sortedTrophy.length != 0) trophyOverlay.setMessage(`${GOLD + BOLD}Trophy Fishing:\n${sortedTrophy.join("\n")}`); } /** * Update counter variables. - * + * * @param {String} fish - Fish type and tier. */ function updateCounter(fish) { - const args = fish.toLowerCase().split(' '); - const tier = args.pop(); - let type = args.join('_'); - if (type in TROPHY_ID) type = TROPHY_ID[type]; + const args = fish.toLowerCase().split(" "); + const tier = args.pop(); + let type = args.join("_"); + if (type in TROPHY_ID) type = TROPHY_ID[type]; - // Update Session - if (!(type in sessionTrophy)) sessionTrophy[type] = [0, 0, 0, 0, 0]; - sessionTrophy[type][0]++; - sessionTrophy[type][TIER_INDEX[tier]]++; - updateMessage(); + // Update Session + if (!(type in sessionTrophy)) sessionTrophy[type] = [0, 0, 0, 0, 0]; + sessionTrophy[type][0]++; + sessionTrophy[type][TIER_INDEX[tier]]++; + updateMessage(); } /** * Track fishing messages to update counter. */ -registerWhen(register("chat", (fish) => { +registerWhen( + register("chat", (fish) => { updateCounter(fish); -}).setCriteria("TROPHY FISH! You caught a ${fish}."), () => location.getWorld() === "Crimson Isle" && Settings.trophyCounter); + }).setCriteria("TROPHY FISH! You caught a ${fish}."), + () => location.getWorld() === "Crimson Isle" && Settings.trophyCounter +); -registerWhen(register("chat", (fish) => { +registerWhen( + register("chat", (fish) => { updateCounter(fish); -}).setCriteria("NEW DISCOVERY: ${fish}"), () => location.getWorld() === "Crimson Isle" && Settings.trophyCounter); + }).setCriteria("NEW DISCOVERY: ${fish}"), + () => location.getWorld() === "Crimson Isle" && Settings.trophyCounter +); /** * Update time for session view */ -registerWhen(register("step", () => { +registerWhen( + register("step", () => { if (getPaused()) return; if (Object.keys(sessionTrophy).length !== 0) timePassed++; updateMessage(); -}).setFps(1), () => location.getWorld() === "Crimson Isle" && Settings.trophyCounter); + }).setFps(1), + () => location.getWorld() === "Crimson Isle" && Settings.trophyCounter +); diff --git a/features/crimsonIsle/VanqFeatures.js b/features/crimsonIsle/VanqFeatures.js index d3645f2..bfbbfc6 100644 --- a/features/crimsonIsle/VanqFeatures.js +++ b/features/crimsonIsle/VanqFeatures.js @@ -1,27 +1,25 @@ +import { AMOGUS, BOLD, DARK_PURPLE, GREEN, LOGO, RED, RESET, WHITE, WITHER_CLASS } from "../../utils/Constants"; +import { data } from "../../utils/Data"; import location from "../../utils/Location"; +import { Overlay } from "../../utils/Overlay"; import party from "../../utils/Party"; -import Settings from "../../utils/Settings"; -import { AMOGUS, BOLD, DARK_PURPLE, GREEN, LOGO, RED, RESET, WHITE, WITHER_CLASS } from "../../utils/Constants"; -import { announceMob, playSound } from "../../utils/functions/misc"; import { registerWhen } from "../../utils/RegisterTils"; +import Settings from "../../utils/Settings"; import { delay } from "../../utils/ThreadTils"; -import { Overlay } from "../../utils/Overlay"; -import { data } from "../../utils/Data"; import Waypoint from "../../utils/Waypoint"; - +import { announceMob, playSound } from "../../utils/functions/misc"; /** * Variables used to track and display item and vanquisher kill counts. */ let items = {}; let session = { - "vanqs": 0, - "kills": 0, - "last": 0, - "average": 0, + vanqs: 0, + kills: 0, + last: 0, + average: 0, }; -const counterExample = -`${RED + BOLD}Total Vanqs: ${RESET}Xue +const counterExample = `${RED + BOLD}Total Vanqs: ${RESET}Xue ${RED + BOLD}Total Kills: ${RESET}Hua ${RED + BOLD}Kills Since: ${RESET}Piao ${RED + BOLD}Average Kills: ${RESET}Piao`; @@ -31,103 +29,116 @@ counterOverlay.setMessage(""); /** * Uses the "Book of Stats" to track whenever player kills an entity and updates the Vanquisher Overlay. */ -registerWhen(register("entityDeath", (death) => { +registerWhen( + register("entityDeath", (death) => { if (Player.getHeldItem() === null) return; const registry = Player.getHeldItem().getRegistryName(); if (!registry.endsWith("hoe") && !registry.endsWith("bow") && Player.asPlayerMP().distanceTo(death) > 16) return; Client.scheduleTask(1, () => { - const ExtraAttributes = Player.getHeldItem().getNBT().getCompoundTag("tag").getCompoundTag("ExtraAttributes"); - const heldItem = ExtraAttributes.getString("id"); - const newKills = ExtraAttributes.getInteger("stats_book"); - - if (!(heldItem in items)) { - items[heldItem] = newKills; - return; - } - - killsDiff = Math.abs(newKills - items[heldItem]); - - if (killsDiff > 10) { // In order for mobs in other islands to not count - items[heldItem] = newKills; - return; - } - - // Overall - data.vanqSession.kills += killsDiff; - data.vanqSession.last += killsDiff; - if (data.vanqSession.vanqs) data.vanqSession.average = Math.round(data.vanqSession.kills / data.vanqSession.vanqs); - - // Session - session.kills += killsDiff; - session.last += killsDiff; - if (session.vanqs) session.average = Math.round(session.kills / session.vanqs); + const ExtraAttributes = Player.getHeldItem().getNBT().getCompoundTag("tag").getCompoundTag("ExtraAttributes"); + const heldItem = ExtraAttributes.getString("id"); + const newKills = ExtraAttributes.getInteger("stats_book"); + + if (!(heldItem in items)) { items[heldItem] = newKills; + return; + } + + killsDiff = Math.abs(newKills - items[heldItem]); - // Update HUD - counterOverlay.setMessage(Settings.vanqCounter === 1 ? -`${RED + BOLD}Total Vanqs: ${RESET + data.vanqSession.vanqs} + if (killsDiff > 10) { + // In order for mobs in other islands to not count + items[heldItem] = newKills; + return; + } + + // Overall + data.vanqSession.kills += killsDiff; + data.vanqSession.last += killsDiff; + if (data.vanqSession.vanqs) + data.vanqSession.average = Math.round(data.vanqSession.kills / data.vanqSession.vanqs); + + // Session + session.kills += killsDiff; + session.last += killsDiff; + if (session.vanqs) session.average = Math.round(session.kills / session.vanqs); + items[heldItem] = newKills; + + // Update HUD + counterOverlay.setMessage( + Settings.vanqCounter === 1 + ? `${RED + BOLD}Total Vanqs: ${RESET + data.vanqSession.vanqs} ${RED + BOLD}Total Kills: ${RESET + data.vanqSession.kills} ${RED + BOLD}Kills Since: ${RESET + data.vanqSession.last} ${RED + BOLD}Average Kills: ${RESET + data.vanqSession.average}` -: -`${RED + BOLD}Total Vanqs: ${RESET + session.vanqs} + : `${RED + BOLD}Total Vanqs: ${RESET + session.vanqs} ${RED + BOLD}Total Kills: ${RESET + session.kills} ${RED + BOLD}Kills Since: ${RESET + session.last} -${RED + BOLD}Average Kills: ${RESET + session.average}`); +${RED + BOLD}Average Kills: ${RESET + session.average}` + ); }); -}), () => location.getWorld() === "Crimson Isle" && Settings.vanqCounter !== 0); + }), + () => location.getWorld() === "Crimson Isle" && Settings.vanqCounter !== 0 +); /** * Tracks whenever the player spawns a Vanquisher and updates the counter. */ -registerWhen(register("chat", () => { +registerWhen( + register("chat", () => { // Overall data.vanqSession.vanqs++; - data.vanqSession.average = (data.vanqSession.kills / data.vanqSession.vanqs); + data.vanqSession.average = data.vanqSession.kills / data.vanqSession.vanqs; data.vanqSession.last = 0; - + // Session session.vanqs++; - session.average = (session.kills / session.vanqs); + session.average = session.kills / session.vanqs; session.last = 0; -}).setCriteria("A Vanquisher is spawning nearby!"), () => location.getWorld() === "Crimson Isle" && Settings.vanqCounter !== 0); + }).setCriteria("A Vanquisher is spawning nearby!"), + () => location.getWorld() === "Crimson Isle" && Settings.vanqCounter !== 0 +); /** * Command to reset the stats for the overall counter. */ register("command", () => { - session = { - "vanqs": 0, - "kills": 0, - "last": 0, - "average": 0, - }; - data.vanqSession = { - "vanqs": 0, - "kills": 0, - "last": 0, - "average": 0, - }; - ChatLib.chat(`${LOGO + GREEN}Successfully reset Vanq Counter!`) + session = { + vanqs: 0, + kills: 0, + last: 0, + average: 0, + }; + data.vanqSession = { + vanqs: 0, + kills: 0, + last: 0, + average: 0, + }; + ChatLib.chat(`${LOGO + GREEN}Successfully reset Vanq Counter!`); }).setName("resetCounter"); - /** * --- Vanquisher Detect --- * Announce vanquisher spawn on chat message appears. */ -registerWhen(register("chat", () => { +registerWhen( + register("chat", () => { announceMob(Settings.vanqAlert, "Vanquisher", Player.getX(), Player.getY(), Player.getZ()); -}).setCriteria("A Vanquisher is spawning nearby!"), () => location.getWorld() === "Crimson Isle" && Settings.vanqAlert !== 0); + }).setCriteria("A Vanquisher is spawning nearby!"), + () => location.getWorld() === "Crimson Isle" && Settings.vanqAlert !== 0 +); /** * Alerts player when another VA user posts coords. */ -registerWhen(register("chat", () => { +registerWhen( + register("chat", () => { playSound(AMOGUS, 10000); -}).setCriteria("${player}: ${coords} | Vanquisher Spawned at [${location}]!"), -() => location.getWorld() === "Crimson Isle" && Settings.vanqSound); + }).setCriteria("${player}: ${coords} | Vanquisher Spawned at [${location}]!"), + () => location.getWorld() === "Crimson Isle" && Settings.vanqSound +); /** * Tracks world for any vanquishers near player. @@ -137,24 +148,28 @@ const vanqExample = `${DARK_PURPLE + BOLD}Vanquisher ${WHITE}Detected`; const vanqOverlay = new Overlay("vanqDetect", data.QL, "moveVanq", vanqExample, ["Crimson Isle"]); vanqOverlay.setMessage(""); -registerWhen(register("step", () => { +registerWhen( + register("step", () => { vanqWaypoints.clear(); - const vanquishers = World.getAllEntitiesOfType(WITHER_CLASS).filter(entity => entity.getEntity().func_110138_aP() === 1024); + const vanquishers = World.getAllEntitiesOfType(WITHER_CLASS).filter( + (entity) => entity.getEntity().func_110138_aP() === 1024 + ); if (vanquishers.length > 0) { - // Check if vanquisher is dead - let foundDead = false; - vanquishers.forEach(vanq => { - if (data.moblist.includes("vanquisher")) vanqWaypoints.push([DARK_PURPLE + "Vanquisher", vanq]); - if (vanq.getEntity().func_110143_aJ() === 0) foundDead = true; - }); - - // Update HUD - if (foundDead) vanqOverlay.setMessage(`${DARK_PURPLE + BOLD}Vanquisher ${RED}Dead!`); - else vanqOverlay.setMessage(vanqExample); + // Check if vanquisher is dead + let foundDead = false; + vanquishers.forEach((vanq) => { + if (data.moblist.includes("vanquisher")) vanqWaypoints.push([DARK_PURPLE + "Vanquisher", vanq]); + if (vanq.getEntity().func_110143_aJ() === 0) foundDead = true; + }); + + // Update HUD + if (foundDead) vanqOverlay.setMessage(`${DARK_PURPLE + BOLD}Vanquisher ${RED}Dead!`); + else vanqOverlay.setMessage(vanqExample); } else vanqOverlay.setMessage(""); -}).setFps(2), () => location.getWorld() === "Crimson Isle" && Settings.vanqDetect); - + }).setFps(2), + () => location.getWorld() === "Crimson Isle" && Settings.vanqDetect +); /** * --- Vanquisher Warp --- @@ -167,7 +182,8 @@ let vanqMessage = ""; /** * Saves location data and invites every player in settings whenever player spawns a Vanquisher. */ -registerWhen(register("chat", () => { +registerWhen( + register("chat", () => { // Set message to copy and post const x = Math.round(Player.getX()); const y = Math.round(Player.getY()); @@ -178,78 +194,109 @@ registerWhen(register("chat", () => { // Return if previous vanq still alive if (vanqSpawned) { - ChatLib.command("pc " + vanqMessage); - return; + ChatLib.command("pc " + vanqMessage); + return; } vanqSpawned = true; notInParty = 0; // INVITE PARTY - delay(() => { if (party.getIn()) ChatLib.command("p leave") }, 500); - - let timeout = 1000 - Settings.vanqParty.split(", ").forEach(ign => { - delay(() => { ChatLib.command(`p ${ign}`) }, timeout); - notInParty++; - timeout += 500; + delay(() => { + if (party.getIn()) ChatLib.command("p leave"); + }, 500); + + let timeout = 1000; + Settings.vanqParty.split(", ").forEach((ign) => { + delay(() => { + ChatLib.command(`p ${ign}`); + }, timeout); + notInParty++; + timeout += 500; }); -}).setCriteria("A Vanquisher is spawning nearby!"), () => location.getWorld() === "Crimson Isle" && Settings.vanqParty !== ""); -registerWhen(register("chat", () => { + }).setCriteria("A Vanquisher is spawning nearby!"), + () => location.getWorld() === "Crimson Isle" && Settings.vanqParty !== "" +); +registerWhen( + register("chat", () => { vanqSpawned = false; -}).setCriteria("RARE DROP! Nether Star"), () => location.getWorld() === "Crimson Isle" && Settings.vanqParty !== ""); + }).setCriteria("RARE DROP! Nether Star"), + () => location.getWorld() === "Crimson Isle" && Settings.vanqParty !== "" +); /** * Tracks whenever a player joins/fails to join the party and warps party to lobby whenever all players have joined. */ function warpParty() { - if (!vanqSpawned) return; + if (!vanqSpawned) return; - notInParty--; - if (notInParty <= 0 && party.getIn()) { - notInParty = 0; + notInParty--; + if (notInParty <= 0 && party.getIn()) { + notInParty = 0; - delay(() => { ChatLib.command("p warp") }, 500); - delay(() => { ChatLib.command("pc " + vanqMessage) }, 1000); - delay(() => { ChatLib.command("p disband") }, 1500); - } + delay(() => { + ChatLib.command("p warp"); + }, 500); + delay(() => { + ChatLib.command("pc " + vanqMessage); + }, 1000); + delay(() => { + ChatLib.command("p disband"); + }, 1500); + } } -registerWhen(register("chat", () => { +registerWhen( + register("chat", () => { delay(warpParty(), 500); -}).setCriteria("${player} joined the party."), () => location.getWorld() === "Crimson Isle" && Settings.vanqParty !== ""); -registerWhen(register("chat", () => { + }).setCriteria("${player} joined the party."), + () => location.getWorld() === "Crimson Isle" && Settings.vanqParty !== "" +); +registerWhen( + register("chat", () => { delay(warpParty(), 500); -}).setCriteria("The party invite to ${player} has expired"), () => location.getWorld() === "Crimson Isle" && Settings.vanqParty !== ""); + }).setCriteria("The party invite to ${player} has expired"), + () => location.getWorld() === "Crimson Isle" && Settings.vanqParty !== "" +); /** * Fail safe for whenever a player goes offline or player inputs invalid username. */ function noInvite() { - if (!vanqSpawned) return; + if (!vanqSpawned) return; - notInParty--; - if (notInParty <= 0) { - notInParty = 0; - vanqSpawned = false; - } + notInParty--; + if (notInParty <= 0) { + notInParty = 0; + vanqSpawned = false; + } } -registerWhen(register("chat", () => { +registerWhen( + register("chat", () => { delay(noInvite(), 500); -}).setCriteria("Couldn't find a player with that name!"), () => location.getWorld() === "Crimson Isle" && Settings.vanqParty !== ""); -registerWhen(register("chat", () => { + }).setCriteria("Couldn't find a player with that name!"), + () => location.getWorld() === "Crimson Isle" && Settings.vanqParty !== "" +); +registerWhen( + register("chat", () => { delay(noInvite(), 500); -}).setCriteria("You cannot invite that player since they're not online."), -() => location.getWorld() === "Crimson Isle" && Settings.vanqParty !== ""); + }).setCriteria("You cannot invite that player since they're not online."), + () => location.getWorld() === "Crimson Isle" && Settings.vanqParty !== "" +); /** * Fail safe in the event that two players spawn at same time or the Vanquisher you spawn dies before warp. */ -registerWhen(register("chat", () => { +registerWhen( + register("chat", () => { vanqSpawned = false; notInParty = 0; -}).setCriteria("You have joined ${player} party!"), () => location.getWorld() === "Crimson Isle" && Settings.vanqParty !== ""); -registerWhen(register("chat", () => { + }).setCriteria("You have joined ${player} party!"), + () => location.getWorld() === "Crimson Isle" && Settings.vanqParty !== "" +); +registerWhen( + register("chat", () => { vanqSpawned = false; notInParty = 0; -}).setCriteria("RARE DROP! Nether Star"), () => location.getWorld() === "Crimson Isle" && Settings.vanqParty !== ""); - + }).setCriteria("RARE DROP! Nether Star"), + () => location.getWorld() === "Crimson Isle" && Settings.vanqParty !== "" +); diff --git a/features/dungeon/CroesusHighlight.js b/features/dungeon/CroesusHighlight.js index bcefd3f..d21ac07 100644 --- a/features/dungeon/CroesusHighlight.js +++ b/features/dungeon/CroesusHighlight.js @@ -1,48 +1,49 @@ -import Settings from "../../utils/Settings"; import { registerWhen } from "../../utils/RegisterTils"; +import Settings from "../../utils/Settings"; import { getSlotCoords } from "../../utils/functions/find"; - let unopened = {}; const highlight = register("guiRender", () => { - Object.keys(unopened).forEach(index => { - const [x, y] = getSlotCoords(index); - Renderer.translate(0, 0, 100); - Renderer.drawRect(unopened[index], x, y, 16, 16); - }); + Object.keys(unopened).forEach((index) => { + const [x, y] = getSlotCoords(index); + Renderer.translate(0, 0, 100); + Renderer.drawRect(unopened[index], x, y, 16, 16); + }); }).unregister(); const close = register("guiClosed", () => { - close.unregister(); - highlight.unregister(); + close.unregister(); + highlight.unregister(); }).unregister(); -registerWhen(register("guiOpened", () => { +registerWhen( + register("guiOpened", () => { Client.scheduleTask(1, () => { - if (Player.getContainer().getName() !== "Croesus") return; - const items = Player.getContainer().getItems(); - unopened = {}; - - // Iterate through the chest slots - for (let i = 1; i < 5; i++) { - for (let j = 1; j < 8; j++) { - // Calculate the index of the item in the container - let index = i * 9 + j; - let lore = items[index]?.getLore(); - if (lore === undefined) continue; - - // Check if the chest is unopened, opened, or empty - if (lore.find(line => line === "§5§o§8No Chests Opened!") !== undefined) - unopened[index] = Renderer.GREEN; - else if (lore.find(line => line.startsWith("§5§o§8Opened Chest:")) !== undefined) - unopened[index] = Renderer.YELLOW; - } + if (Player.getContainer().getName() !== "Croesus") return; + const items = Player.getContainer().getItems(); + unopened = {}; + + // Iterate through the chest slots + for (let i = 1; i < 5; i++) { + for (let j = 1; j < 8; j++) { + // Calculate the index of the item in the container + let index = i * 9 + j; + let lore = items[index]?.getLore(); + if (lore === undefined) continue; + + // Check if the chest is unopened, opened, or empty + if (lore.find((line) => line === "§5§o§8No Chests Opened!") !== undefined) unopened[index] = Renderer.GREEN; + else if (lore.find((line) => line.startsWith("§5§o§8Opened Chest:")) !== undefined) + unopened[index] = Renderer.YELLOW; } + } - if (Object.keys(unopened).length > 0) { - highlight.register(); - close.register(); - } - }) -}), () => Settings.croesusHighlight); + if (Object.keys(unopened).length > 0) { + highlight.register(); + close.register(); + } + }); + }), + () => Settings.croesusHighlight +); diff --git a/features/dungeon/DungeonProfit.js b/features/dungeon/DungeonProfit.js index bc2a471..82cdf1c 100644 --- a/features/dungeon/DungeonProfit.js +++ b/features/dungeon/DungeonProfit.js @@ -1,88 +1,94 @@ -import Settings from "../../utils/Settings"; import { GREEN, RED, YELLOW } from "../../utils/Constants"; import { data } from "../../utils/Data"; import { Overlay } from "../../utils/Overlay"; +import Settings from "../../utils/Settings"; import { formatNumber, unformatNumber } from "../../utils/functions/format"; import { getBazaar } from "../economy/Economy"; import { getItemValue } from "../economy/ItemPrice"; - -const profitExample = -`§eProfit: §c-123.77k +const profitExample = `§eProfit: §c-123.77k §9Enchanted Book: §a0 §fEnchanted Book: §a0 §dWither Essence §8x9: §a26.82k §dUndead Essence §8x10: §a5.99k §eCost: §c156.59k`; -const profitOverlay = new Overlay("dungeonProfit", data.DL, "moveDP", profitExample, ["Catacombs", "Dungeon Hub"], "guiRender"); +const profitOverlay = new Overlay( + "dungeonProfit", + data.DL, + "moveDP", + profitExample, + ["Catacombs", "Dungeon Hub"], + "guiRender" +); const close = register("guiClosed", () => { - profitOverlay.setMessage(''); - close.unregister(); + profitOverlay.setMessage(""); + close.unregister(); }).unregister(); /** * Sets the profit overlay message for the chest. */ function setChest() { - const items = Player.getContainer().getItems(); - const bazaar = getBazaar(); + const items = Player.getContainer().getItems(); + const bazaar = getBazaar(); - // Get the cost of the chest - const costLore = items[31].getLore(); - let costIndex = costLore.findIndex(line => line.endsWith(" Coins")); - let cost = unformatNumber(costLore[costIndex]?.split(' ')?.[0]?.removeFormatting()); - cost += costLore[costIndex + 1] !== "§5§o§9Dungeon Chest Key" ? 0 : - bazaar["DUNGEON_CHEST_KEY"]?.[Settings.priceType]; + // Get the cost of the chest + const costLore = items[31].getLore(); + let costIndex = costLore.findIndex((line) => line.endsWith(" Coins")); + let cost = unformatNumber(costLore[costIndex]?.split(" ")?.[0]?.removeFormatting()); + cost += costLore[costIndex + 1] !== "§5§o§9Dungeon Chest Key" ? 0 : bazaar["DUNGEON_CHEST_KEY"]?.[Settings.priceType]; - let profit = -cost; - let message = ''; + let profit = -cost; + let message = ""; - // Iterate through the items in the chest - for (let i = 0; i < 9; i++) { - // Get the item and its value - let item = items[9 + i]; - let unlocalName = item.getUnlocalizedName(); - if (unlocalName === "tile.thinStainedGlass") continue; - - // Calculate the value of the item - let value = getItemValue(item, false); - if (unlocalName !== "item.enchantedBook" && value === 0) { - let split = item.getName().removeFormatting().split(' '); - let amount = parseInt(split.pop().substring(1)); - let id = (split[1] + '_' + split[0]).toUpperCase(); - value = (bazaar[id]?.[Settings.priceType] ?? 0) * amount; - } + // Iterate through the items in the chest + for (let i = 0; i < 9; i++) { + // Get the item and its value + let item = items[9 + i]; + let unlocalName = item.getUnlocalizedName(); + if (unlocalName === "tile.thinStainedGlass") continue; - profit += value; - message += `\n${item.getName()}: ${GREEN + formatNumber(value)}`; + // Calculate the value of the item + let value = getItemValue(item, false); + if (unlocalName !== "item.enchantedBook" && value === 0) { + let split = item.getName().removeFormatting().split(" "); + let amount = parseInt(split.pop().substring(1)); + let id = (split[1] + "_" + split[0]).toUpperCase(); + value = (bazaar[id]?.[Settings.priceType] ?? 0) * amount; } - // Set the profit overlay message - let profitColor = profit < 0 ? RED : GREEN; - message = `${YELLOW}Profit: ${profitColor + formatNumber(profit)}\n${message}\n${YELLOW}Cost: ${RED + formatNumber(cost)}`; - profitOverlay.setMessage(message); + profit += value; + message += `\n${item.getName()}: ${GREEN + formatNumber(value)}`; + } + + // Set the profit overlay message + let profitColor = profit < 0 ? RED : GREEN; + message = `${YELLOW}Profit: ${profitColor + formatNumber(profit)}\n${message}\n${YELLOW}Cost: ${ + RED + formatNumber(cost) + }`; + profitOverlay.setMessage(message); } /** * TBD at a later date if I feel like it :). */ function setCroesus() { - const bazaar = getBazaar(); - const items = Player.getContainer().getItems(); + const bazaar = getBazaar(); + const items = Player.getContainer().getItems(); - for (let i = 0; i < 9; i++) { - let chest = items[9 + i]; - if (chest.getUnlocalizedName() === "tile.thinStainedGlass") continue; - } + for (let i = 0; i < 9; i++) { + let chest = items[9 + i]; + if (chest.getUnlocalizedName() === "tile.thinStainedGlass") continue; + } } register("guiOpened", () => { - Client.scheduleTask(1, () => { - if (Player.getContainer().getItems()[31]?.getName() !== "§aOpen Reward Chest") return; + Client.scheduleTask(1, () => { + if (Player.getContainer().getItems()[31]?.getName() !== "§aOpen Reward Chest") return; - setChest(); - close.register(); - }); + setChest(); + close.register(); + }); }); diff --git a/features/dungeon/StarDetect.js b/features/dungeon/StarDetect.js index 4ef9b5a..7fa031f 100644 --- a/features/dungeon/StarDetect.js +++ b/features/dungeon/StarDetect.js @@ -1,10 +1,9 @@ -import location from "../../utils/Location"; -import Settings from "../../utils/Settings"; import { EntityArmorStand, EntityWither } from "../../utils/Constants"; +import location from "../../utils/Location"; import { registerWhen } from "../../utils/RegisterTils"; +import Settings from "../../utils/Settings"; import Waypoint from "../../utils/Waypoint"; - /** * Variables used to detect and store star mob data. */ @@ -15,54 +14,66 @@ const starHighlight = new Set(); * Sets the color and box of the star mobs. */ function setStar() { - const c = Settings.starColor; - starMobs.setBox(Settings.starDetect === 2); - starMobs.setColor([c.getRed()/255, c.getGreen()/255, c.getBlue()/255]); + const c = Settings.starColor; + starMobs.setBox(Settings.starDetect === 2); + starMobs.setColor([c.getRed() / 255, c.getGreen() / 255, c.getBlue() / 255]); } setStar(); register("guiClosed", (event) => { - if (!event.toString().startsWith("gg.essential.vigilance.gui.SettingsGui")) return; - setStar(); + if (!event.toString().startsWith("gg.essential.vigilance.gui.SettingsGui")) return; + setStar(); }); /** * Scans and stores all starred mob in world every half second. */ -registerWhen(register("step", () => { +registerWhen( + register("step", () => { starMobs.clear(); starHighlight.clear(); const stands = World.getAllEntitiesOfType(EntityArmorStand.class); - stands.forEach(stand => { - if (!stand.getName().startsWith("§6✯")) return; - const standEntity = stand.getEntity(); + stands.forEach((stand) => { + if (!stand.getName().startsWith("§6✯")) return; + const standEntity = stand.getEntity(); - // Find closest mob - const closestEntity = World.getWorld().func_72839_b(standEntity, standEntity.func_174813_aQ().func_72314_b(1, 5, 1)) - .filter(entity => { - return entity && - !(entity instanceof EntityArmorStand) && - !(entity instanceof EntityWither) && - entity !== Player.getPlayer(); - }) - .reduce((closest, entity) => { - const distance = stand.distanceTo(entity); - return distance < closest.distance ? { entity, distance } : closest; - }, { entity: standEntity, distance: 20 }); + // Find closest mob + const closestEntity = World.getWorld() + .func_72839_b(standEntity, standEntity.func_174813_aQ().func_72314_b(1, 5, 1)) + .filter((entity) => { + return ( + entity && + !(entity instanceof EntityArmorStand) && + !(entity instanceof EntityWither) && + entity !== Player.getPlayer() + ); + }) + .reduce( + (closest, entity) => { + const distance = stand.distanceTo(entity); + return distance < closest.distance ? { entity, distance } : closest; + }, + { entity: standEntity, distance: 20 } + ); - if (closestEntity.entity) { - starMobs.push([stand.getName(), closestEntity.entity]); - starHighlight.add(closestEntity.entity?.func_145782_y()); - } + if (closestEntity.entity) { + starMobs.push([stand.getName(), closestEntity.entity]); + starHighlight.add(closestEntity.entity?.func_145782_y()); + } }); -}).setFps(2), () => location.getWorld() === "Catacombs" && Settings.starDetect !== 0); + }).setFps(2), + () => location.getWorld() === "Catacombs" && Settings.starDetect !== 0 +); /** * Rendering for colored star mobs. */ -registerWhen(register("renderEntity", (entity) => { +registerWhen( + register("renderEntity", (entity) => { if (!starHighlight.has(entity.getEntity().func_145782_y())) return; const c = Settings.starColor; - Tessellator.colorize(c.getRed()/255, c.getGreen()/255, c.getBlue()/255, 1); -}), () => location.getWorld() === "Catacombs" && Settings.starDetect === 1); + Tessellator.colorize(c.getRed() / 255, c.getGreen() / 255, c.getBlue() / 255, 1); + }), + () => location.getWorld() === "Catacombs" && Settings.starDetect === 1 +); diff --git a/features/economy/AttributePricing.js b/features/economy/AttributePricing.js index 607128b..0ba9860 100644 --- a/features/economy/AttributePricing.js +++ b/features/economy/AttributePricing.js @@ -1,71 +1,89 @@ import request from "../../../requestV2"; -import { AQUA, BOLD, DARK_AQUA, DARK_GRAY, DARK_RED, GOLD, GRAY, GREEN, ITALIC, LOGO, RED, WHITE } from "../../utils/Constants"; +import { + AQUA, + BOLD, + DARK_AQUA, + DARK_GRAY, + DARK_RED, + GOLD, + GRAY, + GREEN, + ITALIC, + LOGO, + RED, + WHITE, +} from "../../utils/Constants"; +import { data } from "../../utils/Data"; import { commafy, convertToTitleCase, formatNumber } from "../../utils/functions/format"; import { decode } from "../../utils/functions/misc"; -import { data } from "../../utils/Data"; import { getAuction } from "./Economy"; - let attributesBin = {}; /** * Loops through Auction api for any item with attributes and then recalls getAttributes - * + * * @param {Number} page - Auction api page number, * @param {String[]} command - User inputted command arguments */ function findAttributes(page, command) { - request({ - url: `https://api.hypixel.net/v2/skyblock/auctions?page=${page}`, - json: true - }).then(response => { - const KUUDRA_PIECES = new Set(["FERVOR", "AURORA", "TERROR", "CRIMSON", "HOLLOW", "MOLTEN"]); - ChatLib.clearChat(444); - new Message(`${LOGO + RED}Auction Looping (${page + 1}/${response.totalPages})`).setChatLineId(444).chat(); - - response.auctions.forEach(auction => { - // Get item data - const { uuid, bin, starting_bid, item_bytes } = auction; - if (!bin) return; - const item_data = new NBTTagCompound(decode(item_bytes).func_150305_b(0)).getCompoundTag("tag").getCompoundTag("ExtraAttributes"); - let id = item_data.getString("id"); - const attributes = item_data.getCompoundTag("attributes").toObject(); - const keys = Object.keys(attributes); - if (keys.length === 0) return; - - // Set item in map (kuudra set differently) - const ids = id.split('_'); - id = KUUDRA_PIECES.has(ids[0]) ? ids[1] : id; - if (!(id in attributesBin)) attributesBin[id] = {}; - const category = attributesBin[id]; - - // Add attribute costs - keys.forEach(key => { - const tier = attributes[key]; - const value = starting_bid / (2 ** (tier - 1)); - if (key in category) category[key].push([uuid, value, tier]); - else category[key] = [[uuid, value, tier]]; - }); + request({ + url: `https://api.hypixel.net/v2/skyblock/auctions?page=${page}`, + json: true, + }) + .then((response) => { + const KUUDRA_PIECES = new Set(["FERVOR", "AURORA", "TERROR", "CRIMSON", "HOLLOW", "MOLTEN"]); + ChatLib.clearChat(444); + new Message(`${LOGO + RED}Auction Looping (${page + 1}/${response.totalPages})`).setChatLineId(444).chat(); + + response.auctions.forEach((auction) => { + // Get item data + const { uuid, bin, starting_bid, item_bytes } = auction; + if (!bin) return; + const item_data = new NBTTagCompound(decode(item_bytes).func_150305_b(0)) + .getCompoundTag("tag") + .getCompoundTag("ExtraAttributes"); + let id = item_data.getString("id"); + const attributes = item_data.getCompoundTag("attributes").toObject(); + const keys = Object.keys(attributes); + if (keys.length === 0) return; + + // Set item in map (kuudra set differently) + const ids = id.split("_"); + id = KUUDRA_PIECES.has(ids[0]) ? ids[1] : id; + if (!(id in attributesBin)) attributesBin[id] = {}; + const category = attributesBin[id]; + + // Add attribute costs + keys.forEach((key) => { + const tier = attributes[key]; + const value = starting_bid / Math.pow(2, tier - 1); + if (key in category) category[key].push([uuid, value, tier]); + else category[key] = [[uuid, value, tier]]; + }); + }); + + if (page + 1 < response.totalPages) findAttributes(page + 1, command); + else { + // Sort values + Object.keys(attributesBin).forEach((id) => { + Object.keys(attributesBin[id]).forEach((attribute) => { + attributesBin[id][attribute].sort((a, b) => a[1] - b[1]); + }); }); - if (page + 1 < response.totalPages) findAttributes(page + 1, command); - else { - // Sort values - Object.keys(attributesBin).forEach(id => { - Object.keys(attributesBin[id]).forEach(attribute => { - attributesBin[id][attribute].sort((a, b) => a[1] - b[1]); - }); - }); - - ChatLib.chat(`${LOGO + GREEN}Auction loop complete!`); - if (command !== undefined) getAttributes(command); - ChatLib.chat(`${DARK_GRAY}Attribute values saved, use '/refreshAttr' to refresh auction data!`); - } - }).catch(err => ChatLib.chat(LOGO + DARK_RED + (err.cause ?? err))); + ChatLib.chat(`${LOGO + GREEN}Auction loop complete!`); + if (command !== undefined) getAttributes(command); + ChatLib.chat(`${DARK_GRAY}Attribute values saved, use '/refreshAttr' to refresh auction data!`); + } + }) + .catch((err) => ChatLib.chat(LOGO + DARK_RED + (err.cause ?? err))); } register("command", () => { - attributesBin = {}; - findAttributes(0, undefined); -}).setName("refreshAttr", true).setAliases("refreshAttributes", "refreshAttribute"); + attributesBin = {}; + findAttributes(0, undefined); +}) + .setName("refreshAttr", true) + .setAliases("refreshAttributes", "refreshAttribute"); const worthless = []; /** @@ -74,118 +92,140 @@ const worthless = []; * @param {String[]} args - Arguments from player input values. */ export function getAttributes(args) { - const validCategories = new Set([ - "shard", "shards", - "helmet", "chestplate", "leggings", "boots", - "necklace", "cloak", "belt", "bracelet" - ]); - - // args - const category = args[1]?.toLowerCase(); - let piece = args[2]?.toLowerCase(); - const attribute = args[3]?.toLowerCase(); - const auction = getAuction(); - - if (validCategories.has(category)) { // Attribute - const item = args[1].includes("shard") ? "ATTRIBUTE_SHARD" : category.toUpperCase(); - const tier = args[2] !== undefined && !isNaN(args[2]) ? parseInt(args[2]) : 1; - const attributes = auction[item]?.attributes || {}; - - ChatLib.chat(`${LOGO + DARK_AQUA + BOLD + convertToTitleCase(item)} Attribute Prices (t${tier})`); - Object.entries(attributes).forEach(([attributeName, attributeValue]) => { - if (!data.attributelist.includes(attributeName)) return; - const adjustedValue = attributeValue * (2 ** (tier - 1)); - if (adjustedValue !== 0) { - ChatLib.chat(`-${AQUA + convertToTitleCase(attributeName)}: ${WHITE + commafy(adjustedValue)}`); - } - }); - } else if (category === "lbin" || category === "upgrade") { - // Load attribute values on first run - if (Object.keys(attributesBin).length === 0) { - findAttributes(0, args); - return; - } - - // Check if valid attribute - piece = piece?.toUpperCase(); - const attributeBin = attributesBin?.[piece]?.[attribute]; - if (attributeBin === undefined) { - ChatLib.chat(`\n${LOGO + RED}Error: Invalid argument "${attribute}"!`); - ChatLib.chat(`${LOGO + RED}Please input as: ${WHITE}/va attribute lbin [item] [attribute] *[min tier] *[amount]`); - return; - } - - // Set correct bin and values - const min = isNaN(args[4]) ? 1 : parseInt(args[4]); - const bin = attributeBin.filter(piece => piece[2] >= min); - const amount = isNaN(args[5]) ? Math.min(10, bin.length) : Math.min(parseInt(args[5]), bin.length); - - // Clear chat - let chatID = 8008; - ChatLib.clearChat([8008]); - new Message(`\n${LOGO + DARK_AQUA + BOLD}Top ${amount} t${min}+ ${attribute} ${piece}s:`).setChatLineId(chatID++).chat(); - - // And print new values - for (let i = 0; i < amount; i++) { - new Message(`${i+1}. `, new TextComponent(`${AQUA + bin[i][0]}`) - .setClick("run_command", `/viewauction ${bin[i][0]}`) - .setHoverValue(`Click to open auction #${i+1}!`), - `${GRAY} [t${bin[i][2]}: ${formatNumber(bin[i][1])}]`) - .setChatLineId(chatID++).chat(); - } - } else if (piece !== undefined) { - const KUUDRA_PIECES = ["CRIMSON", "AURORA", "TERROR", "FERVOR", "HOLLOW"]; - const ARMOR_TYPES = ["HELMET", "CHESTPLATE", "LEGGINGS", "BOOTS"]; - const EQUIPMENT_TYPES = ["NECKLACE", "CLOAK", "BELT", "BRACELET"]; - - const combo = category < piece ? `${category} ${piece}` : `${piece} ${category}`; - ChatLib.chat(`${LOGO + GOLD}Attribute Combo ${GRAY}(${combo})${GOLD}:`); - let chatID = 10946; - - // Armor pieces - KUUDRA_PIECES.forEach(piece => { - ChatLib.chat(`${DARK_AQUA + convertToTitleCase(piece)} Pieces:`); - ARMOR_TYPES.forEach(armor => { - const price = auction[`${piece}_${armor}`]?.attribute_combos?.[combo] ?? 0; - if (price < 10_000_000) worthless.push(chatID); - new Message(`${DARK_GRAY}- ${AQUA + convertToTitleCase(armor)}: ${WHITE + formatNumber(price)}`) - .setChatLineId(chatID).chat(); - chatID++; - }); - }); + const validCategories = new Set([ + "shard", + "shards", + "helmet", + "chestplate", + "leggings", + "boots", + "necklace", + "cloak", + "belt", + "bracelet", + ]); + + // args + const category = args[1]?.toLowerCase(); + let piece = args[2]?.toLowerCase(); + const attribute = args[3]?.toLowerCase(); + const auction = getAuction(); + + if (validCategories.has(category)) { + // Attribute + const item = args[1].includes("shard") ? "ATTRIBUTE_SHARD" : category.toUpperCase(); + const tier = args[2] !== undefined && !isNaN(args[2]) ? parseInt(args[2]) : 1; + const attributes = auction[item]?.attributes || {}; + + ChatLib.chat(`${LOGO + DARK_AQUA + BOLD + convertToTitleCase(item)} Attribute Prices (t${tier})`); + Object.entries(attributes).forEach(([attributeName, attributeValue]) => { + if (!data.attributelist.includes(attributeName)) return; + const adjustedValue = attributeValue * Math.pow(2, tier - 1); + if (adjustedValue !== 0) { + ChatLib.chat(`-${AQUA + convertToTitleCase(attributeName)}: ${WHITE + commafy(adjustedValue)}`); + } + }); + } else if (category === "lbin" || category === "upgrade") { + // Load attribute values on first run + if (Object.keys(attributesBin).length === 0) { + findAttributes(0, args); + return; + } - // Equipment - ChatLib.chat(`${DARK_AQUA}Molten Pieces:`); - EQUIPMENT_TYPES.forEach(equip => { - const price = auction["MOLTEN_" + equip]?.attribute_combos?.[combo] ?? 0; - if (price < 10_000_000) worthless.push(chatID); - new Message(`${DARK_GRAY}- ${AQUA + convertToTitleCase(equip)}: ${WHITE + formatNumber(price)}`) - .setChatLineId(chatID).chat(); - chatID++; - }); + // Check if valid attribute + piece = piece?.toUpperCase(); + const attributeBin = attributesBin?.[piece]?.[attribute]; + if (attributeBin === undefined) { + ChatLib.chat(`\n${LOGO + RED}Error: Invalid argument "${attribute}"!`); + ChatLib.chat(`${LOGO + RED}Please input as: ${WHITE}/va attribute lbin [item] [attribute] *[min tier] *[amount]`); + return; + } + + // Set correct bin and values + const min = isNaN(args[4]) ? 1 : parseInt(args[4]); + const bin = attributeBin.filter((piece) => piece[2] >= min); + const amount = isNaN(args[5]) ? Math.min(10, bin.length) : Math.min(parseInt(args[5]), bin.length); + + // Clear chat + let chatID = 8008; + ChatLib.clearChat([8008]); + new Message(`\n${LOGO + DARK_AQUA + BOLD}Top ${amount} t${min}+ ${attribute} ${piece}s:`) + .setChatLineId(chatID++) + .chat(); + + // And print new values + for (let i = 0; i < amount; i++) { + new Message( + `${i + 1}. `, + new TextComponent(`${AQUA + bin[i][0]}`) + .setClick("run_command", `/viewauction ${bin[i][0]}`) + .setHoverValue(`Click to open auction #${i + 1}!`), + `${GRAY} [t${bin[i][2]}: ${formatNumber(bin[i][1])}]` + ) + .setChatLineId(chatID++) + .chat(); + } + } else if (piece !== undefined) { + const KUUDRA_PIECES = ["CRIMSON", "AURORA", "TERROR", "FERVOR", "HOLLOW"]; + const ARMOR_TYPES = ["HELMET", "CHESTPLATE", "LEGGINGS", "BOOTS"]; + const EQUIPMENT_TYPES = ["NECKLACE", "CLOAK", "BELT", "BRACELET"]; + + const combo = category < piece ? `${category} ${piece}` : `${piece} ${category}`; + ChatLib.chat(`${LOGO + GOLD}Attribute Combo ${GRAY}(${combo})${GOLD}:`); + let chatID = 10946; + + // Armor pieces + KUUDRA_PIECES.forEach((piece) => { + ChatLib.chat(`${DARK_AQUA + convertToTitleCase(piece)} Pieces:`); + ARMOR_TYPES.forEach((armor) => { + const price = auction[`${piece}_${armor}`]?.attribute_combos?.[combo] ?? 0; + if (price < 10_000_000) worthless.push(chatID); + new Message(`${DARK_GRAY}- ${AQUA + convertToTitleCase(armor)}: ${WHITE + formatNumber(price)}`) + .setChatLineId(chatID) + .chat(); + chatID++; + }); + }); - // Remove broke pieces - new Message(new TextComponent(`${LOGO + GRAY}Click here to remove pieces worth less than 10m!`) - .setClick("run_command", "/clearWorthlessAttributes") - .setHoverValue("Click me!") - ).chat(); - } else { - ChatLib.chat( -`\n${LOGO + RED}Error: Invalid argument "${args[1]}"! + // Equipment + ChatLib.chat(`${DARK_AQUA}Molten Pieces:`); + EQUIPMENT_TYPES.forEach((equip) => { + const price = auction["MOLTEN_" + equip]?.attribute_combos?.[combo] ?? 0; + if (price < 10_000_000) worthless.push(chatID); + new Message(`${DARK_GRAY}- ${AQUA + convertToTitleCase(equip)}: ${WHITE + formatNumber(price)}`) + .setChatLineId(chatID) + .chat(); + chatID++; + }); + + // Remove broke pieces + new Message( + new TextComponent(`${LOGO + GRAY}Click here to remove pieces worth less than 10m!`) + .setClick("run_command", "/clearWorthlessAttributes") + .setHoverValue("Click me!") + ).chat(); + } else { + ChatLib.chat( + `\n${LOGO + RED}Error: Invalid argument "${args[1]}"! ${LOGO + RED}Please input as: ${WHITE}/va attribute ${GRAY}<${WHITE}shard, [armor], [equipment]${GRAY}> ${WHITE}[tier] ${LOGO + RED}To check combo price, please input as: ${WHITE}/va attribute [attribute_1] [attribute_2] -${LOGO + RED}To fetch lbin attributes, please input as: ${WHITE}/va attribute lbin [item] [attribute] *[min tier] *[amount] +${ + LOGO + RED +}To fetch lbin attributes, please input as: ${WHITE}/va attribute lbin [item] [attribute] *[min tier] *[amount] ${LOGO + RED}To fetch piece upgrades, please input as: ${WHITE}/va attribute upgrade *[attribute] *[min tier] *[amount] -${DARK_GRAY + ITALIC}Please note that values with more than one word need to be seperated by an underscore. (* means optional)`); - } +${ + DARK_GRAY + ITALIC +}Please note that values with more than one word need to be seperated by an underscore. (* means optional)` + ); + } } /** * Remove worthless (< 10m) cost items from chat */ register("command", () => { - worthless.forEach(id => { - ChatLib.clearChat(id); - }); - worthless.length = 0; + worthless.forEach((id) => { + ChatLib.clearChat(id); + }); + worthless.length = 0; }).setName("clearWorthlessAttributes"); diff --git a/features/economy/BitsAlert.js b/features/economy/BitsAlert.js index e83b3d3..e858d2b 100644 --- a/features/economy/BitsAlert.js +++ b/features/economy/BitsAlert.js @@ -1,9 +1,8 @@ -import Settings from "../../utils/Settings"; import { BOLD, DARK_AQUA, LOGO } from "../../utils/Constants"; import { registerWhen } from "../../utils/RegisterTils"; +import Settings from "../../utils/Settings"; import { setTitle } from "../../utils/Title"; - /** * Variable to track last bits detected */ @@ -12,35 +11,46 @@ let last = -1; /** * Check the half-hourly bit generation */ -registerWhen(register("step", () => { - const bits = Scoreboard.getLines().find(line => line.getName().startsWith("Bits:")); +registerWhen( + register("step", () => { + const bits = Scoreboard.getLines().find((line) => line.getName().startsWith("Bits:")); if (bits === undefined) return; // Check if current amount matches last - const amount = bits.getName().removeFormatting().replace(/[^0-9]/g, ''); + const amount = bits + .getName() + .removeFormatting() + .replace(/[^0-9]/g, ""); if (amount === last) { - if (Settings.bitsAlert === 1 || Settings.bitsAlert === 3) setTitle(`${DARK_AQUA + BOLD}NO MO BITS!`, "", 10, 50, 10, 5); - if (Settings.bitsAlert === 2 || Settings.bitsAlert === 3) ChatLib.chat(`${LOGO + DARK_AQUA + BOLD}NO MO BITS!`); + if (Settings.bitsAlert === 1 || Settings.bitsAlert === 3) + setTitle(`${DARK_AQUA + BOLD}NO MO BITS!`, "", 10, 50, 10, 5); + if (Settings.bitsAlert === 2 || Settings.bitsAlert === 3) ChatLib.chat(`${LOGO + DARK_AQUA + BOLD}NO MO BITS!`); } last = amount; -}).setDelay(2700), () => Settings.bitsAlert !== 0); + }).setDelay(2700), + () => Settings.bitsAlert !== 0 +); /** * Check for inventory bits */ -registerWhen(register("guiOpened", () => { +registerWhen( + register("guiOpened", () => { Client.scheduleTask(1, () => { - const container = Player.getContainer(); - if (container.getName() === "SkyBlock Menu") { - const cookie = container.getStackInSlot(51).getLore(); - if (!cookie[4].endsWith("Not active!") && cookie[5] !== "§5§o§7Bits Available: §b0") return; - } else if (container.getName() === "Booster Cookie") { - const bits = !container.getStackInSlot(11).getLore()[7].startsWith("§5§o§7Bits Available: §b0"); - const active = !(container.getStackInSlot(13).getLore()[20] === "§5§o§7§cYou do not currently have a"); - if (bits && active) return; - } else return; + const container = Player.getContainer(); + if (container.getName() === "SkyBlock Menu") { + const cookie = container.getStackInSlot(51).getLore(); + if (!cookie[4].endsWith("Not active!") && cookie[5] !== "§5§o§7Bits Available: §b0") return; + } else if (container.getName() === "Booster Cookie") { + const bits = !container.getStackInSlot(11).getLore()[7].startsWith("§5§o§7Bits Available: §b0"); + const active = !(container.getStackInSlot(13).getLore()[20] === "§5§o§7§cYou do not currently have a"); + if (bits && active) return; + } else return; - if (Settings.bitsAlert === 1 || Settings.bitsAlert === 3) setTitle(`${DARK_AQUA + BOLD}NO MO BITS!`, "", 10, 50, 10, 5); - if (Settings.bitsAlert === 2 || Settings.bitsAlert === 3) ChatLib.chat(`${LOGO + DARK_AQUA + BOLD}NO MO BITS!`); + if (Settings.bitsAlert === 1 || Settings.bitsAlert === 3) + setTitle(`${DARK_AQUA + BOLD}NO MO BITS!`, "", 10, 50, 10, 5); + if (Settings.bitsAlert === 2 || Settings.bitsAlert === 3) ChatLib.chat(`${LOGO + DARK_AQUA + BOLD}NO MO BITS!`); }); -}), () => Settings.bitsAlert !== 0); + }), + () => Settings.bitsAlert !== 0 +); diff --git a/features/economy/CoinTracker.js b/features/economy/CoinTracker.js index 2945e3f..8976451 100644 --- a/features/economy/CoinTracker.js +++ b/features/economy/CoinTracker.js @@ -1,23 +1,20 @@ -import location from "../../utils/Location"; -import Settings from "../../utils/Settings"; import { BOLD, GOLD, GREEN, LOGO, RED, WHITE } from "../../utils/Constants"; -import { commafy, formatTime } from "../../utils/functions/format"; -import { registerWhen } from "../../utils/RegisterTils"; +import { data } from "../../utils/Data"; import { Overlay } from "../../utils/Overlay"; +import { registerWhen } from "../../utils/RegisterTils"; +import Settings from "../../utils/Settings"; import { Stat, getPaused } from "../../utils/Stat"; -import { data } from "../../utils/Data"; - +import { commafy, formatTime } from "../../utils/functions/format"; /** * Variables used to track and display coin tracker. */ const piggy = new Stat(); register("command", () => { - piggy.reset(); - ChatLib.chat(`${LOGO + GREEN}Successfully reset coin tracker!`); + piggy.reset(); + ChatLib.chat(`${LOGO + GREEN}Successfully reset coin tracker!`); }).setName("resetCoins"); -const coinExample = -`${GOLD + BOLD}Gained: ${WHITE}COUNTING +const coinExample = `${GOLD + BOLD}Gained: ${WHITE}COUNTING ${GOLD + BOLD}Time: ${WHITE}ME ${GOLD + BOLD}Rate: ${WHITE}MONEY`; const coinOverlay = new Overlay("coinTracker", data.ML, "moveCoins", coinExample); @@ -25,28 +22,32 @@ const coinOverlay = new Overlay("coinTracker", data.ML, "moveCoins", coinExample /** * Tracks Piggybank in Scoreboard for changes in coins and updates Coins Overlay every second. */ -registerWhen(register("step", () => { +registerWhen( + register("step", () => { if (!World.isLoaded()) return; // Get cha ching from purse - let purse = Scoreboard?.getLines()?.find(line => line.getName().includes("Purse:")); + let purse = Scoreboard?.getLines()?.find((line) => line.getName().includes("Purse:")); if (getPaused() || purse === undefined) return; - purse = parseInt(purse.getName().removeFormatting().split(" ")[1].replace(/\D/g,'')); + purse = parseInt(purse.getName().removeFormatting().split(" ")[1].replace(/\D/g, "")); // Get starting balance if (piggy.since >= Settings.coinTracker * 60) { - piggy.start = purse; - piggy.time = 1; + piggy.start = purse; + piggy.time = 1; } // Calculate changes if (purse !== piggy.now && piggy.now) piggy.since = 0; piggy.time++; piggy.now = purse; - + // Update GUI const timeDisplay = piggy.since < Settings.coinTracker * 60 ? formatTime(piggy.time) : `${RED}Inactive`; coinOverlay.setMessage( -`${GOLD + BOLD}Gained: ${WHITE + commafy(piggy.getGain())} ¢ + `${GOLD + BOLD}Gained: ${WHITE + commafy(piggy.getGain())} ¢ ${GOLD + BOLD}Time: ${WHITE + timeDisplay} -${GOLD + BOLD}Rate: ${WHITE + commafy(piggy.getRate())} ¢/hr`); -}).setFps(1), () => Settings.coinTracker !== 0); +${GOLD + BOLD}Rate: ${WHITE + commafy(piggy.getRate())} ¢/hr` + ); + }).setFps(1), + () => Settings.coinTracker !== 0 +); diff --git a/features/economy/ContainerValue.js b/features/economy/ContainerValue.js index df969bf..c210493 100644 --- a/features/economy/ContainerValue.js +++ b/features/economy/ContainerValue.js @@ -1,19 +1,30 @@ +import { + AQUA, + BLUE, + DARK_PURPLE, + DARK_RED, + GOLD, + GRAY, + GREEN, + ITALIC, + LIGHT_PURPLE, + RED, + WHITE, + YELLOW, +} from "../../utils/Constants"; +import { data } from "../../utils/Data"; +import { Overlay } from "../../utils/Overlay"; +import { registerWhen } from "../../utils/RegisterTils"; import Settings from "../../utils/Settings"; -import { AQUA, BLUE, GRAY, DARK_PURPLE, DARK_RED, GOLD, GREEN, LIGHT_PURPLE, RED, WHITE, ITALIC, YELLOW } from "../../utils/Constants"; import { formatNumber, unformatNumber } from "../../utils/functions/format"; -import { registerWhen } from "../../utils/RegisterTils"; -import { Overlay } from "../../utils/Overlay"; -import { data } from "../../utils/Data"; import { getBazaar } from "./Economy"; import { getItemValue } from "./ItemPrice"; - /** * Variables used to track and display container value. */ const VALID_CONTAINERS = new Set(["Chest", "Backpack", "Bag", "Wardrobe", "Pets", "Vault", "Museum"]); -const containerExample = -`${WHITE}Item 1${GRAY} - ${WHITE}Be +const containerExample = `${WHITE}Item 1${GRAY} - ${WHITE}Be ${GREEN}Item 2${GRAY} - ${WHITE}Extremely ${BLUE}Item 3${GRAY} - ${WHITE}Subtle ${DARK_PURPLE}Item 4${GRAY} - ${WHITE}Even @@ -23,180 +34,209 @@ ${AQUA}Item 7${GRAY} - ${WHITE}Point ${RED}Item 8${GRAY} - ${WHITE}Of ${RED}Item 9${GRAY} - ${WHITE}Formlessness ${DARK_RED}-Sun Tzu, The Art of War`; -const containerOverlay = new Overlay("containerValue", data.RL, "moveContainer", containerExample, ["all"], "guiRender"); +const containerOverlay = new Overlay( + "containerValue", + data.RL, + "moveContainer", + containerExample, + ["all"], + "guiRender" +); containerOverlay.setMessage(""); /** * Set the message of the overlay given the items object and value. - * + * * @param {Object} itemValues - itemName: [count, value] - * @returns + * @returns */ function setMessage(itemValues) { - // Convert itemValues object to an array of [itemName, [count, value]] - const sortedItems = Object.entries(itemValues).sort((a, b) => b[1][1] - a[1][1]); - - // Display the sorted items and total value - let overlayMessage = ''; - let displayedItems = 0; - let totalValue = 0; - - // Destructuring here for cleaner loop - for ([itemName, [itemCount, itemValue]] of sortedItems) { - totalValue += itemValue; - displayedItems++; - - // Display only the top containerValue items - if (displayedItems === Settings.containerValue) { - const remainingItems = sortedItems.length - Settings.containerValue; - if (remainingItems > 0) { - overlayMessage += `\n${GRAY + ITALIC}+ ${remainingItems} more items...`; - } - } else if (displayedItems < Settings.containerValue) { - overlayMessage += `\n${itemName} ${GRAY}x${formatNumber(itemCount)} ${WHITE}= ${GREEN + formatNumber(itemValue)}`; - } + // Convert itemValues object to an array of [itemName, [count, value]] + const sortedItems = Object.entries(itemValues).sort((a, b) => b[1][1] - a[1][1]); + + // Display the sorted items and total value + let overlayMessage = ""; + let displayedItems = 0; + let totalValue = 0; + + // Destructuring here for cleaner loop + for ([itemName, [itemCount, itemValue]] of sortedItems) { + totalValue += itemValue; + displayedItems++; + + // Display only the top containerValue items + if (displayedItems === Settings.containerValue) { + const remainingItems = sortedItems.length - Settings.containerValue; + if (remainingItems > 0) { + overlayMessage += `\n${GRAY + ITALIC}+ ${remainingItems} more items...`; + } + } else if (displayedItems < Settings.containerValue) { + overlayMessage += `\n${itemName} ${GRAY}x${formatNumber(itemCount)} ${WHITE}= ${GREEN + formatNumber(itemValue)}`; } + } - if (totalValue === 0) - containerOverlay.setMessage(""); - else - containerOverlay.setMessage(`${GOLD}Total Value: ${YELLOW + formatNumber(totalValue)}\n` + overlayMessage); + if (totalValue === 0) containerOverlay.setMessage(""); + else containerOverlay.setMessage(`${GOLD}Total Value: ${YELLOW + formatNumber(totalValue)}\n` + overlayMessage); } /** * Caclulate the value of a sack container (different stack size) - * + * * @param {Inventory} container - Player GUI container. */ function sackValue(container) { - const bazaar = getBazaar(); - const itemValues = {}; - - for (let i = 0; i < 45; i++) { - // Get item stack or detect no item - let stack = container.getStackInSlot(i); - let stackName = stack.getName(); - if (stackName === "§aGo Back") break; - if (stack.getName() === " ") continue; - - // Get value and count from item stack - let id = stack.getNBT().getCompoundTag("tag").getCompoundTag("ExtraAttributes").getString("id"); - let value = bazaar?.[id]?.[Settings.priceType] ?? 0; - let count = stack.getLore().find(line => line.startsWith("§5§o§7Stored:")) - ?.removeFormatting() - ?.match(/Stored: (\d+(?:,\d{3})*(?:\.\d+)?)/) - ?.[1]?.replace(/,/g, ''); - if (count === undefined || count == 0 || value == 0) continue; - - // Add value into container value message - let stackValue = value * count; - itemValues[stackName] = [count, stackValue]; - } - - setMessage(itemValues); + const bazaar = getBazaar(); + const itemValues = {}; + + for (let i = 0; i < 45; i++) { + // Get item stack or detect no item + let stack = container.getStackInSlot(i); + let stackName = stack.getName(); + if (stackName === "§aGo Back") break; + if (stack.getName() === " ") continue; + + // Get value and count from item stack + let id = stack.getNBT().getCompoundTag("tag").getCompoundTag("ExtraAttributes").getString("id"); + let value = bazaar?.[id]?.[Settings.priceType] ?? 0; + let count = stack + .getLore() + .find((line) => line.startsWith("§5§o§7Stored:")) + ?.removeFormatting() + ?.match(/Stored: (\d+(?:,\d{3})*(?:\.\d+)?)/)?.[1] + ?.replace(/,/g, ""); + if (count === undefined || count == 0 || value == 0) continue; + + // Add value into container value message + let stackValue = value * count; + itemValues[stackName] = [count, stackValue]; + } + + setMessage(itemValues); } /** * Calculate the value of a composter container. - * + * * @param {Inventory} container - Player GUI container. */ function composterValue(container) { - // Get composting values - const bazaar = getBazaar(); - const compost = container.getStackInSlot(13); - const available = parseInt(compost.getLore().find(line => line.startsWith("§5§o§7§7Compost Available:")).split(' ')[2].removeFormatting()); - - // Get composter upgrades - const crop = unformatNumber(container.getStackInSlot(1).getLore()[1].removeFormatting().trim().split('/')[0]); - const fuel = unformatNumber(container.getStackInSlot(7).getLore()[1].removeFormatting().trim().split('/')[0]); - const costUpgrade = data.composterUpgrades["Cost Reduction"]; - const noCrop = crop / (4000 * (1 - costUpgrade/100)); - const noFuel = fuel / (2000 * (1 - costUpgrade/100)); - const composting = Math.min(noCrop, noFuel) * (1 + 0.03 * data.composterUpgrades["Multi Drop"]); - const value = bazaar?.["COMPOST"]?.[Settings.priceType] ?? 0; - - // Set composting values - const itemValues = { - "Composted": [available, available * value], - "Composting": [composting, composting * value] - }; - setMessage(itemValues, value); + // Get composting values + const bazaar = getBazaar(); + const compost = container.getStackInSlot(13); + const available = parseInt( + compost + .getLore() + .find((line) => line.startsWith("§5§o§7§7Compost Available:")) + .split(" ")[2] + .removeFormatting() + ); + + // Get composter upgrades + const crop = unformatNumber(container.getStackInSlot(1).getLore()[1].removeFormatting().trim().split("/")[0]); + const fuel = unformatNumber(container.getStackInSlot(7).getLore()[1].removeFormatting().trim().split("/")[0]); + const costUpgrade = data.composterUpgrades["Cost Reduction"]; + const noCrop = crop / (4000 * (1 - costUpgrade / 100)); + const noFuel = fuel / (2000 * (1 - costUpgrade / 100)); + const composting = Math.min(noCrop, noFuel) * (1 + 0.03 * data.composterUpgrades["Multi Drop"]); + const value = bazaar?.["COMPOST"]?.[Settings.priceType] ?? 0; + + // Set composting values + const itemValues = { + Composted: [available, available * value], + Composting: [composting, composting * value], + }; + setMessage(itemValues, value); } /** * Calculate the value of a bazaar container. - * + * * @param {Inventory} container - Player GUI container. */ function bazaarValue(container) { - const itemValues = {}; - - for (let i = 1; i < 5; i++) { - const firstStack = container.getStackInSlot(i * 9 + 1); - if (firstStack !== null && firstStack.getUnlocalizedName() === "tile.thinStainedGlass") break; - - for (let j = 1; j < 9; j++) { - // Get item stack or detect no item - let stack = container.getStackInSlot(i * 9 + j); - if (stack === null) break; - const lore = stack.getLore(); - - // Get item name, capacity, and price - const name = stack.getName().split(' ').slice(1).join(' '); - let amount = 0; - let price = 0; - - // Get capacity and price from lore - const amountLine = lore.find(line => line.includes("amount:")); - if (amountLine) { - const regex = /(Offer|Order) amount: ([0-9,]+)x/; - const match = amountLine.removeFormatting().match(regex); - if (match) amount = unformatNumber(match[2]); - } - - // Get price from lore - const priceLine = lore.find(line => line.startsWith("§5§o§7Price per unit:")); - if (priceLine) { - const match = priceLine.removeFormatting().split(' ')[3]; - price = unformatNumber(match); - } - - itemValues[name] = [amount, amount * price]; - } + const itemValues = {}; + + for (let i = 1; i < 5; i++) { + const firstStack = container.getStackInSlot(i * 9 + 1); + if (firstStack !== null && firstStack.getUnlocalizedName() === "tile.thinStainedGlass") break; + + for (let j = 1; j < 9; j++) { + // Get item stack or detect no item + let stack = container.getStackInSlot(i * 9 + j); + if (stack === null) break; + const lore = stack.getLore(); + + // Get item name, capacity, and price + const name = stack.getName().split(" ").slice(1).join(" "); + let amount = 0; + let price = 0; + + // Get capacity and price from lore + const amountLine = lore.find((line) => line.includes("amount:")); + if (amountLine) { + const regex = /(Offer|Order) amount: ([0-9,]+)x/; + const match = amountLine.removeFormatting().match(regex); + if (match) amount = unformatNumber(match[2]); + } + + // Get price from lore + const priceLine = lore.find((line) => line.startsWith("§5§o§7Price per unit:")); + if (priceLine) { + const match = priceLine.removeFormatting().split(" ")[3]; + price = unformatNumber(match); + } + + itemValues[name] = [amount, amount * price]; } + } - setMessage(itemValues); + setMessage(itemValues); } /** * Calculate the value of an auction container. - * + * * @param {Inventory} container - Player GUI container. */ function auctionValue(container) { - const itemValues = {}; - - for (let i = 1; i < 5; i++) { - const firstStack = container.getStackInSlot(i * 9 + 1); - if (firstStack !== null && firstStack.getUnlocalizedName() === "tile.thinStainedGlass") break; - - for (let j = 1; j < 9; j++) { - // Get item stack or detect no item - let stack = container.getStackInSlot(i * 9 + j); - if (stack === null) break; - const lore = stack.getLore(); - - // Get item name, capacity, and price - const name = stack.getName(); - let value = unformatNumber(lore.find(line => line.startsWith("§5§o§7Buy it now:"))?.split(' ')?.[3]?.removeFormatting()) || - unformatNumber(lore.find(line => line.startsWith("§5§o§7Sold for:"))?.split(' ')?.[2]?.removeFormatting()); - unformatNumber(lore.find(line => line.startsWith("§5§o§7Top bid:"))?.split(' ')?.[2]?.removeFormatting()); - itemValues[name] = [1, value]; - } + const itemValues = {}; + + for (let i = 1; i < 5; i++) { + const firstStack = container.getStackInSlot(i * 9 + 1); + if (firstStack !== null && firstStack.getUnlocalizedName() === "tile.thinStainedGlass") break; + + for (let j = 1; j < 9; j++) { + // Get item stack or detect no item + let stack = container.getStackInSlot(i * 9 + j); + if (stack === null) break; + const lore = stack.getLore(); + + // Get item name, capacity, and price + const name = stack.getName(); + let value = + unformatNumber( + lore + .find((line) => line.startsWith("§5§o§7Buy it now:")) + ?.split(" ")?.[3] + ?.removeFormatting() + ) || + unformatNumber( + lore + .find((line) => line.startsWith("§5§o§7Sold for:")) + ?.split(" ")?.[2] + ?.removeFormatting() + ); + unformatNumber( + lore + .find((line) => line.startsWith("§5§o§7Top bid:")) + ?.split(" ")?.[2] + ?.removeFormatting() + ); + itemValues[name] = [1, value]; } + } - setMessage(itemValues); + setMessage(itemValues); } /** @@ -204,50 +244,53 @@ function auctionValue(container) { * Generates a formatted overlay message for the containerOverlay. */ function updateContainerValue(remove) { - Client.scheduleTask(3, () => { - // Check if container is valid - const container = Player.getContainer(); - const containerName = container.getName().removeFormatting(); - const items = container.getItems(); - const words = containerName.split(" "); - if (containerName.endsWith("Sack")) { - sackValue(container); - return; - } else if (containerName === "Composter") { - composterValue(container); - return; - } else if (containerName.endsWith("Bazaar Orders")) { - bazaarValue(container); - return; - } else if (containerName === "Manage Auctions") { - auctionValue(container); - return; - } else if ((!VALID_CONTAINERS.has(words[0]) && !VALID_CONTAINERS.has(words[1]) && remove !== 0) || - items[31]?.getName() === "§aOpen Reward Chest") return; - - const itemValues = {}; - for (let i = 0; i < items.length - remove; i++) { - let item = items[i]; - if (item === null) continue; - - // Change name if attribute shard or enchanted book - let itemName = item.getName(); - if (itemName === "§fAttribute Shard" || itemName === "§fEnchanted Book") - itemName = item.getNBT().getCompoundTag("tag").getCompoundTag("display").toObject().Lore[0]; - - // Get item value + item count - let value = getItemValue(item, false); - if (value !== 0) { - let itemCount = item.getStackSize(); - - // Destructuring here for cleaner loop - let [existingItemCount = 0, existingItemValue = 0] = itemValues[itemName] || []; - itemValues[itemName] = [existingItemCount + itemCount, existingItemValue + value]; - } - } - - setMessage(itemValues); - }); + Client.scheduleTask(3, () => { + // Check if container is valid + const container = Player.getContainer(); + const containerName = container.getName().removeFormatting(); + const items = container.getItems(); + const words = containerName.split(" "); + if (containerName.endsWith("Sack")) { + sackValue(container); + return; + } else if (containerName === "Composter") { + composterValue(container); + return; + } else if (containerName.endsWith("Bazaar Orders")) { + bazaarValue(container); + return; + } else if (containerName === "Manage Auctions") { + auctionValue(container); + return; + } else if ( + (!VALID_CONTAINERS.has(words[0]) && !VALID_CONTAINERS.has(words[1]) && remove !== 0) || + items[31]?.getName() === "§aOpen Reward Chest" + ) + return; + + const itemValues = {}; + for (let i = 0; i < items.length - remove; i++) { + let item = items[i]; + if (item === null) continue; + + // Change name if attribute shard or enchanted book + let itemName = item.getName(); + if (itemName === "§fAttribute Shard" || itemName === "§fEnchanted Book") + itemName = item.getNBT().getCompoundTag("tag").getCompoundTag("display").toObject().Lore[0]; + + // Get item value + item count + let value = getItemValue(item, false); + if (value !== 0) { + let itemCount = item.getStackSize(); + + // Destructuring here for cleaner loop + let [existingItemCount = 0, existingItemValue = 0] = itemValues[itemName] || []; + itemValues[itemName] = [existingItemCount + itemCount, existingItemValue + value]; + } + } + + setMessage(itemValues); + }); } /** @@ -255,23 +298,24 @@ function updateContainerValue(remove) { * Invokes `updateContainerValue` to update value overlay display. */ const mouse = register("guiMouseRelease", (_, __, ___, gui) => { - const guiName = gui.class.getName(); - if (guiName === "net.minecraft.client.gui.inventory.GuiInventory") updateContainerValue(0); - else if (guiName === "net.minecraft.client.gui.inventory.GuiChest") updateContainerValue(36); + const guiName = gui.class.getName(); + if (guiName === "net.minecraft.client.gui.inventory.GuiInventory") updateContainerValue(0); + else if (guiName === "net.minecraft.client.gui.inventory.GuiChest") updateContainerValue(36); }).unregister(); /** * This function clears the content of the container overlay message, effectively removing it from display. */ const close = register("guiClosed", () => { - mouse.unregister(); - close.unregister(); + mouse.unregister(); + close.unregister(); }).unregister(); /** * Handles GUI events in container GUIs, updating value overlay display if a chest GUI is involved. */ -registerWhen(register("guiOpened", (event) => { +registerWhen( + register("guiOpened", (event) => { containerOverlay.setMessage(""); const guiName = event.gui.class.getName(); if (guiName === "net.minecraft.client.gui.inventory.GuiInventory") updateContainerValue(0); @@ -280,4 +324,6 @@ registerWhen(register("guiOpened", (event) => { mouse.register(); close.register(); -}), () => Settings.containerValue !== 0); + }), + () => Settings.containerValue !== 0 +); diff --git a/features/economy/Economy.js b/features/economy/Economy.js index 29f3c61..785341c 100644 --- a/features/economy/Economy.js +++ b/features/economy/Economy.js @@ -1,37 +1,42 @@ import request from "../../../requestV2"; -import Settings from "../../utils/Settings"; import { GREEN, LOGO } from "../../utils/Constants"; -import { registerWhen } from "../../utils/RegisterTils"; import { data } from "../../utils/Data"; - /** * Variables used to generate and record Skyblock economy pricing. */ let items = {}; -export function getAuction() { return items }; +export function getAuction() { + return items; +} let products = {}; -export function getBazaar() { return products }; +export function getBazaar() { + return products; +} /** * Makes a PULL request to update economy data. */ export function updateAuction() { - request({ - url: "https://volcaronitee.pythonanywhere.com/auction", - json: true - }).then(response => { - items = response.items; - Object.keys(data.valuelist).forEach(key => items[key] = {lbin: data.valuelist[key]}); - }).catch(err => console.error(`VolcAddons: ${err.cause ?? err}`)); + request({ + url: "https://volcaronitee.pythonanywhere.com/auction", + json: true, + }) + .then((response) => { + items = response.items; + Object.keys(data.valuelist).forEach((key) => (items[key] = { lbin: data.valuelist[key] })); + }) + .catch((err) => console.error(`VolcAddons: ${err.cause ?? err}`)); } function updateBazaar() { - request({ - url: "https://volcaronitee.pythonanywhere.com/bazaar", - json: true - }).then(response => { - products = response.items; - }).catch(err => console.error(`$VolcAddons: ${err.cause ?? err}`)); + request({ + url: "https://volcaronitee.pythonanywhere.com/bazaar", + json: true, + }) + .then((response) => { + products = response.items; + }) + .catch((err) => console.error(`$VolcAddons: ${err.cause ?? err}`)); } updateAuction(); updateBazaar(); @@ -40,15 +45,15 @@ updateBazaar(); * Calls for an auction house reloop every X minutes. */ register("step", () => { - updateAuction(); - updateBazaar(); + updateAuction(); + updateBazaar(); }).setDelay(3600); /** * Updates auction and bazaar data and notifies the user upon successful update. */ register("command", () => { - updateAuction(); - updateBazaar(); - ChatLib.chat(`${LOGO + GREEN}Successfully updated Auction and Bazaar!`); + updateAuction(); + updateBazaar(); + ChatLib.chat(`${LOGO + GREEN}Successfully updated Auction and Bazaar!`); }).setName("updateEconomy"); diff --git a/features/economy/GdragCalc.js b/features/economy/GdragCalc.js index ba83043..c94ba99 100644 --- a/features/economy/GdragCalc.js +++ b/features/economy/GdragCalc.js @@ -3,78 +3,88 @@ import { BLUE, BOLD, DARK_GRAY, DARK_RED, GOLD, GRAY, GREEN, LOGO, RED, WHITE } import { formatNumber } from "../../utils/functions/format"; import { decode } from "../../utils/functions/misc"; - /** * Loops through Auction api for all golden dragons and then calls calcGdrag function. - * + * * @param {Number} page - Auction api page number, * @param {Number} minLvl - Minimum levle of Golden Dragon to calculate */ let gdrags = []; function findGdrag(page, minLvl) { - request({ - url: `https://api.hypixel.net/v2/skyblock/auctions?page=${page}`, - json: true - }).then(response => { - ChatLib.clearChat(888); - new Message(`${LOGO + RED}Auction Looping (${page + 1}/${response.totalPages})`).setChatLineId(888).chat(); - - response.auctions.forEach(auction => { - const { uuid, item_name, bin, starting_bid, item_bytes } = auction; - const level = parseInt(item_name.match(/\[Lvl (\d+)\] Golden Dragon/)?.[1]); - if (!bin || isNaN(level) || level < 100) return; // Skip non-bin auctions + request({ + url: `https://api.hypixel.net/v2/skyblock/auctions?page=${page}`, + json: true, + }) + .then((response) => { + ChatLib.clearChat(888); + new Message(`${LOGO + RED}Auction Looping (${page + 1}/${response.totalPages})`).setChatLineId(888).chat(); + + response.auctions.forEach((auction) => { + const { uuid, item_name, bin, starting_bid, item_bytes } = auction; + const level = parseInt(item_name.match(/\[Lvl (\d+)\] Golden Dragon/)?.[1]); + if (!bin || isNaN(level) || level < 100) return; // Skip non-bin auctions + + // Checks for pet candy. + const itemData = decode(item_bytes).func_150305_b(0); + const petInfo = new NBTTagCompound(itemData) + .getCompoundTag("tag") + .getCompoundTag("ExtraAttributes") + .getString("petInfo"); + if (JSON.parse(petInfo).candyUsed !== 0) return; - // Checks for pet candy. - const itemData = decode(item_bytes).func_150305_b(0); - const petInfo = new NBTTagCompound(itemData).getCompoundTag("tag").getCompoundTag("ExtraAttributes").getString("petInfo"); - if (JSON.parse(petInfo).candyUsed !== 0) return; - - // Add to list - const levelPrice = (starting_bid - 600_000_000) / (level - 100); - gdrags.push([uuid, level, levelPrice]); - }); + // Add to list + const levelPrice = (starting_bid - 600_000_000) / (level - 100); + gdrags.push([uuid, level, levelPrice]); + }); - if (page + 1 < response.totalPages) findGdrag(page + 1, minLvl); - else { - // Sort by level cost - gdrags.sort((a, b) => a[2] - b[2]); + if (page + 1 < response.totalPages) findGdrag(page + 1, minLvl); + else { + // Sort by level cost + gdrags.sort((a, b) => a[2] - b[2]); - ChatLib.chat(`${LOGO + GREEN}Auction loop complete!`); - if (minLvl != 0) calcGdrag(minLvl); - ChatLib.chat(`${DARK_GRAY}GDrag values saved, use '/refreshGdrag' to refresh auction data!`); - } - }).catch(err => ChatLib.chat(LOGO + DARK_RED + (err.cause ?? err))); + ChatLib.chat(`${LOGO + GREEN}Auction loop complete!`); + if (minLvl != 0) calcGdrag(minLvl); + ChatLib.chat(`${DARK_GRAY}GDrag values saved, use '/refreshGdrag' to refresh auction data!`); + } + }) + .catch((err) => ChatLib.chat(LOGO + DARK_RED + (err.cause ?? err))); } register("command", () => { - gdrags = []; - findGdrag(0, 0); + gdrags = []; + findGdrag(0, 0); }).setName("refreshGdrag", true); /** * Calculates best 5 gdrags in object using level costs. - * + * * @param {Number} minLvl - Minimum level of gdrag required to be calculated */ export function calcGdrag(minLvl) { - // Check for gdrags variable - if (gdrags.length === 0) { - findGdrag(0, minLvl); - return; - } - const fDrag = gdrags.filter(drag => drag[1] >= minLvl); - const amount = Math.min(fDrag.length, 10); + // Check for gdrags variable + if (gdrags.length === 0) { + findGdrag(0, minLvl); + return; + } + const fDrag = gdrags.filter((drag) => drag[1] >= minLvl); + const amount = Math.min(fDrag.length, 10); - // Clear chat - let chatID = 8008; - ChatLib.clearChat([8008]); - new Message(`\n${LOGO + GOLD + BOLD}Top ${amount} Golden Dragons [${WHITE}lvl ${minLvl}+${GOLD}]:`).setChatLineId(chatID++).chat(); + // Clear chat + let chatID = 8008; + ChatLib.clearChat([8008]); + new Message(`\n${LOGO + GOLD + BOLD}Top ${amount} Golden Dragons [${WHITE}lvl ${minLvl}+${GOLD}]:`) + .setChatLineId(chatID++) + .chat(); - // And print new values - for (let i = 0; i < amount; i++) { - new Message(`${i+1}. `, new TextComponent(`${BLUE + fDrag[i][0]}`) - .setClick("run_command", `/viewauction ${fDrag[i][0]}`) - .setHoverValue(`Click to open auction #${i+1}!`), - `${GRAY} [lvl ${fDrag[i][1]}: ${formatNumber(fDrag[i][2])}]`) - .setChatLineId(chatID++).chat(); - } + // And print new values + for (let i = 0; i < amount; i++) { + new Message( + `${i + 1}. `, + new TextComponent(`${BLUE + fDrag[i][0]}`) + .setClick("run_command", `/viewauction ${fDrag[i][0]}`) + .setHoverValue(`Click to open auction #${i + 1}!`), + `${GRAY} [lvl ${fDrag[i][1]}: ${formatNumber(fDrag[i][2])}]` + ) + .setChatLineId(chatID++) + .chat(); + } } diff --git a/features/economy/ItemPrice.js b/features/economy/ItemPrice.js index 5340ede..7635414 100644 --- a/features/economy/ItemPrice.js +++ b/features/economy/ItemPrice.js @@ -1,40 +1,30 @@ +import { + AQUA, + BLACK, + BOLD, + DARK_AQUA, + DARK_GRAY, + DARK_GREEN, + DARK_PURPLE, + DARK_RED, + GOLD, + GRAY, + GREEN, + LIGHT_PURPLE, + NBTTagString, + RED, + WHITE, + YELLOW, +} from "../../utils/Constants"; +import { data } from "../../utils/Data"; +import { Overlay } from "../../utils/Overlay"; +import { registerWhen } from "../../utils/RegisterTils"; import Settings from "../../utils/Settings"; -import { AQUA, BLACK, BOLD, DARK_AQUA, DARK_GRAY, DARK_GREEN, DARK_PURPLE, DARK_RED, GOLD, GRAY, GREEN, LIGHT_PURPLE, NBTTagString, RED, WHITE, YELLOW } from "../../utils/Constants"; +import { findClosest } from "../../utils/functions/find"; import { convertToTitleCase, formatNumber } from "../../utils/functions/format"; -import { registerWhen } from "../../utils/RegisterTils"; -import { Overlay } from "../../utils/Overlay"; -import { data } from "../../utils/Data"; import { getAuction, getBazaar } from "./Economy"; - -/** - * Variables used to represent item enchantment data. - * "ENCHANTMENT_NAME": Minimum max craft. - */ -const MAX_ENCHANTS = { - // Weapon Enchantments - "BANE_OF_ARTHROPODS": 7, "CHAMPION": 1, "CLEAVE": 6, "CRITICAL": 7, "CUBISM": 6, "DIVINE_GIFT": 1, "DRAGON_HUNTER": 1, "ENDER_SLAYER": 7, - "EXECUTE": 6, "EXPERIENCE": 5, "FIRE_ASPECT": 3, "FIRST_STRIKE": 5, "GIANT_KILLER": 7, "LETHALITY": 6, "LIFE_STEAL": 5, "LOOTING": 5, - "LUCK": 7, "MANA_STEAL": 1, "PROSECUTE": 6, "SCAVENGER": 5, "SHARPNESS": 7, "SMITE": 7, "SMOLDERING": 1, "SYPHON": 5, "TABASCO": 3, - "THUNDERBOLT": 6, "THUNDERBOLT": 7, "TITAN_KILLER": 7, "TRIPLE_STRIKE": 5, "VAMPIRISM": 6, "VENOMOUS": 6, "VICIOUS": 5, "ULTIMATE_CHIMERA": 1, - "ULTIMATE_COMBO": 1, "ULTIMATE_FATAL_TEMPO": 1, "ULTIMATE_INFERNO": 1, "ULTIMATE_ONE_FOR_ALL": 1, "ULTIMATE_SOUL_EATER": 1, - "ULTIMATE_SWARM": 1, "ULTIMATE_JERRY": 1, "ULTIMATE_WISE": 1, "CHANCE": 5, "INFINITE_QUIVER": 6, "OVERLOAD": 1, "POWER": 7, "SNIPE": 4, - "ULTIMATE_REITERATE": 1, "ULTIMATE_REND": 1, - // Armor Enchantments - "BIG_BRAIN": 3, "TRANSYLVANIAN": 4, "BLAST_PROTECTION": 7, "FEROCIOUS_MANA": 5, "FIRE_PROTECTION": 7, "GROWTH": 7, "HARDENED_MANA": 5, - "HECATOMB": 1, "MANA_VAMPIRE": 1, "PROJECTILE_PROTECTION": 6, "PROTECTION": 7, "REJUVENATE": 1, "RESPITE": 1, "STRONG_MANA": 5, - "ULTIMATE_BANK": 1, "ULTIMATE_HABANERO_TACTICS": 4, "ULTIMATE_LAST_STAND": 1, "ULTIMATE_LEGION": 1, "ULTIMATE_NO_PAIN_NO_GAIN": 1, - "ULTIMATE_WISDOM": 1, "REFLECTION": 1, "COUNTER_STRIKE": 5, "TRUE_PROTECTION": 1, "SMARTY_PANTS": 1, "FEATHER_FALLING": 6, "SUGAR_RUSH": 1, - "ICE_COLD": 1, - // Tool Enchantments - "COMPACT": 1, "EFFICIENCY": 1, "FORTUNE": 4, "PRISTINE": 1, "CULTIVATING": 1, "DEDICATION": 4, "DELICATE": 5, "HARVESTING": 6, "REPLENISH": 1, - "TURBO_CACTUS": 1, "TURBO_CANE": 1, "TURBO_CARROT": 1, "TURBO_MUSHROOMS": 1, "TURBO_POTATO": 1, "TURBO_WARTS": 1, "TURBO_WHEAT": 1, - "SUNDER": 1, "TURBO_COCO": 1, "TURBO_MELON": 1, "TURBO_PUMPKIN": 1, "EXPERTISE": 1, "ULTIMATE_BOBBIN_TIME": 3, "ULTIMATE_FLASH": 1, - "PALEONTOLOGIST": 1, - // Equipment Enchantments - "CAYENNE": 1, "GREEN_THUMB": 1, "PROSPERITY": 1, "QUANTUM": 3, "ULTIMATE_THE_ONE": 4, "PESTERMINATOR": 1 -}; -const STACKING_ENCHANTS = new Set(["EXPERTISE", "COMPACT", "CULTIVATING", "CHAMPION", "HECATOMB", "EFFICIENCY"]); +const STACKING_ENCHANTS = new Set(["EXPERTISE", "COMPACT", "CULTIVATING", "CHAMPION", "HECATOMB", "TOXOPHILITE"]); /** * This function calculates the value of enchantments on an item, taking into account @@ -46,24 +36,21 @@ const STACKING_ENCHANTS = new Set(["EXPERTISE", "COMPACT", "CULTIVATING", "CHAMP * @returns {Number} - The calculated value of the enchantments on the item. */ function getEnchantmentValue(enchantments, bazaar, type) { - value = 0; - Object.entries(enchantments ?? {}).forEach(([enchant, enchantlvl]) => { - enchant = enchant.toUpperCase(); - const maxEnchantLevel = MAX_ENCHANTS[enchant]; - if (!MAX_ENCHANTS.hasOwnProperty(enchant)) return; - - const enchantName = enchant === "EFFICIENCY" ? "SIL_EX" : `ENCHANTMENT_${enchant}_${enchantlvl}`; - const enchantKey = enchant === "EFFICIENCY" ? "SIL_EX" : `ENCHANTMENT_${enchant}_${maxEnchantLevel}`; - const base = bazaar?.[enchantKey] ?? 0; - let multiplier = enchant === "EFFICIENCY" ? enchantlvl - 5 : 1; - multiplier = STACKING_ENCHANTS.has(enchant) ? multiplier : 2 ** (enchantlvl - maxEnchantLevel); - value += Math.max( - 0, - (base?.[type] ?? 0) * multiplier, - bazaar?.[enchantName]?.[type] ?? bazaar?.[enchantKey]?.[type] ?? 0 - ); - }); - return value; + let value = 0; + Object.entries(enchantments ?? {}).forEach(([enchant, enchantlvl]) => { + enchant = enchant.toUpperCase(); + + if (enchant === "EFFICIENCY") { + const multipler = 2 ** (enchantlvl - 6); + value += multipler > 0 ? bazaar?.["SIL_EX"]?.[type] * multipler : 0; + return; + } + + if (STACKING_ENCHANTS.has(enchant)) enchantlvl = 1; + value += bazaar?.[`ENCHANTMENT_${enchant}_${enchantlvl}`]?.[type] ?? 0; + }); + + return value; } /** @@ -71,37 +58,98 @@ function getEnchantmentValue(enchantments, bazaar, type) { */ const STAR_PLACEMENT = ["FIRST", "SECOND", "THIRD", "FOURTH", "FIFTH"]; const GEMSTONE_SLOTS = { - "JADE": GREEN, "AMBER": GOLD, "TOPAZ": YELLOW, "SAPPHIRE": AQUA, "AMETHYST": DARK_PURPLE, "RUBY": RED, "JASPER": LIGHT_PURPLE, "OPAL": WHITE, "CITRINE": RED, - "AQUAMARINE": AQUA, "PERIDOT": DARK_GREEN, "ONYX": BLACK + JADE: GREEN, + AMBER: GOLD, + TOPAZ: YELLOW, + SAPPHIRE: AQUA, + AMETHYST: DARK_PURPLE, + RUBY: RED, + JASPER: LIGHT_PURPLE, + OPAL: WHITE, + CITRINE: RED, + AQUAMARINE: AQUA, + PERIDOT: DARK_GREEN, + ONYX: BLACK, }; const MULTIUSE_SLOTS = new Set(["COMBAT", "DEFENSIVE", "MINING", "UNIVERSAL", "CHISEL"]); const REFORGES = { - "coldfused": "ENTROPY_SUPPRESSOR", "dirty": "DIRT_BOTTLE", "fabled": "DRAGON_CLAW", "gilded": "MIDAS_JEWEL", "suspicious": "SUSPICIOUS_VIAL", - "aote_stone": "AOTE_STONE", "withered": "WITHER_BLOOD", "bulky": "BULKY_STONE", "jerry_stone": "JERRY_STONE", - "fanged": "FULL_JAW_FANGING_KIT", "precise": "OPTICAL_LENS", "spiritual": "SPIRIT_STONE", "headstrong": "SALMON_OPAL", "candied": "CANDY_CORN", - "submerged": "DEEP_SEA_ORB", "perfect": "DIAMOND_ATOM", "reinforced": "RARE_DIAMOND", "renowned": "DRAGON_HORN", "spike": "DRAGON_SCALE", - "hyper": "END_STONE_GEODE", "giant": "GIANT_TOOTH", "jaded": "JADERALD", "cubic": "MOLTEN_CUBE", "necrotic": "NECROMANCER_BROOCH", - "empowered": "SADAN_BROOCH", "ancient": "PRECURSOR_GEAR", "undead": "PREMIUM_FLESH", "loving": "RED_SCARF", "RIDICULOUS": "red_nose", - "bustling": "SKYMART_BROCHURE", "mossy": "OVERGROWN_GRASS", "festive": "FROZEN_BUBBLE", "glistening": "SHINY_PRISM", - "strengthened": "SEARING_STONE", "waxed": "BLAZE_WAX", "fortified": "METEOR_SHARD", "rooted": "BURROWING_SPORES", - "blooming": "FLOWERING_BOUQUET", "snowy": "TERRY_SNOWGLOBE", "blood_soaked": "PRESUMED_GALLON_OF_RED_PAINT", "greater_spook": "BOO_STONE", "salty": "SALT_CUBE", - "treacherous": "RUSTY_ANCHOR", "lucky": "LUCKY_DICE", "stiff": "HARDENED_WOOD", "chomp": "KUUDRA_MANDIBLE", "pitchin": "PITCHIN_KOI", - "ambered": "AMBERED_MATERIAL", "auspicious": "ROCK_GEMSTONE", "fleet": "DIAMONITE", "heated": "HOT_STUFF", "magnetic": "LAPIS_CRYSTAL", - "mithraic": "PURE_MITHRIL", "refined": "REFINED_AMBER", "stellar": "PETRIFIED_STARFALL", "fruitful": "ONYX", "moil": "MOIL_LOG", - "toil": "TOIL_LOG", "blessed": "BLESSED_FRUIT", "earthy": "LARGE_WALNUT", "blessed": "BLESSED_FRUIT", "bountiful": "GOLDEN_BALL" + coldfused: "ENTROPY_SUPPRESSOR", + dirty: "DIRT_BOTTLE", + fabled: "DRAGON_CLAW", + gilded: "MIDAS_JEWEL", + suspicious: "SUSPICIOUS_VIAL", + aote_stone: "AOTE_STONE", + withered: "WITHER_BLOOD", + bulky: "BULKY_STONE", + jerry_stone: "JERRY_STONE", + fanged: "FULL_JAW_FANGING_KIT", + precise: "OPTICAL_LENS", + spiritual: "SPIRIT_STONE", + headstrong: "SALMON_OPAL", + candied: "CANDY_CORN", + submerged: "DEEP_SEA_ORB", + perfect: "DIAMOND_ATOM", + reinforced: "RARE_DIAMOND", + renowned: "DRAGON_HORN", + spike: "DRAGON_SCALE", + hyper: "END_STONE_GEODE", + giant: "GIANT_TOOTH", + jaded: "JADERALD", + cubic: "MOLTEN_CUBE", + necrotic: "NECROMANCER_BROOCH", + empowered: "SADAN_BROOCH", + ancient: "PRECURSOR_GEAR", + undead: "PREMIUM_FLESH", + loving: "RED_SCARF", + RIDICULOUS: "red_nose", + bustling: "SKYMART_BROCHURE", + mossy: "OVERGROWN_GRASS", + festive: "FROZEN_BUBBLE", + glistening: "SHINY_PRISM", + strengthened: "SEARING_STONE", + waxed: "BLAZE_WAX", + fortified: "METEOR_SHARD", + rooted: "BURROWING_SPORES", + blooming: "FLOWERING_BOUQUET", + snowy: "TERRY_SNOWGLOBE", + blood_soaked: "PRESUMED_GALLON_OF_RED_PAINT", + greater_spook: "BOO_STONE", + salty: "SALT_CUBE", + treacherous: "RUSTY_ANCHOR", + lucky: "LUCKY_DICE", + stiff: "HARDENED_WOOD", + chomp: "KUUDRA_MANDIBLE", + pitchin: "PITCHIN_KOI", + ambered: "AMBERED_MATERIAL", + auspicious: "ROCK_GEMSTONE", + fleet: "DIAMONITE", + heated: "HOT_STUFF", + magnetic: "LAPIS_CRYSTAL", + mithraic: "PURE_MITHRIL", + refined: "REFINED_AMBER", + stellar: "PETRIFIED_STARFALL", + fruitful: "ONYX", + moil: "MOIL_LOG", + toil: "TOIL_LOG", + blessed: "BLESSED_FRUIT", + earthy: "LARGE_WALNUT", + blessed: "BLESSED_FRUIT", + bountiful: "GOLDEN_BALL", }; const KUUDRA_UPGRADES = { - "HOT": [150, 170, 190, 215, 240, 270, 300, 340, 390, 440, 500], - "BURNING": [800, 900, 1000, 1125, 1270, 1450, 1650, 1850, 2100, 2350, 2650], - "FIERY": [4500, 5000, 5600, 6300, 7000, 8000, 9000, 10200, 11500, 13000, 14500], - "INFERNAL": [25500, 30000, 35000, 41000, 48000, 56000, 65500, 76000, 89000, 105000, 120000, 140000, 165000, 192000, 225000, 265000] + HOT: [150, 170, 190, 215, 240, 270, 300, 340, 390, 440, 500], + BURNING: [800, 900, 1000, 1125, 1270, 1450, 1650, 1850, 2100, 2350, 2650], + FIERY: [4500, 5000, 5600, 6300, 7000, 8000, 9000, 10200, 11500, 13000, 14500], + INFERNAL: [ + 25500, 30000, 35000, 41000, 48000, 56000, 65500, 76000, 89000, 105000, 120000, 140000, 165000, 192000, 225000, + 265000, + ], }; /** * Variables used to represent and display advanced item value. */ -const valueExample = -`&3&lItem: §dSussy Baka §6✪§6✪§6✪§6✪§6✪§c➊ +const valueExample = `&3&lItem: §dSussy Baka §6✪§6✪§6✪§6✪§6✪§c➊ - &bBase: &a+O - &bMaster Stars: &a+Say - &bRecomb: &a+Can @@ -133,342 +181,383 @@ valueOverlay.setMessage(""); let savedValues = {}; /** * Calculates the complete value of given item. - * + * * @param {Object} item - Item Object. * @returns {Number} - Total value of item. */ -export function getItemValue(item, save=true) { - // Get Item statistics - if (item === null) return 0; - const nbt = item?.getNBT() ?? item; - const itemTag = nbt?.getCompoundTag("tag")?.toObject(); - const itemName = itemTag?.display?.Name; - const itemData = itemTag?.ExtraAttributes; - const itemID = itemData?.id; - - // Check for early return - if (itemName?.startsWith("§6") && itemName?.endsWith(" coins")) - return parseInt(tag?.display?.Lore[4]?.removeFormatting()?.replace(/[^0-9]/g, '')) ?? 0; - if (itemID === undefined) return 0; - - // Get remaining statistics - const amount = item?.getStackSize() ?? 1; - const itemUUID = (itemData?.uuid || item?.getName()) + amount; - - // Check if value is already calculated - if (itemUUID !== undefined) { - const saved = savedValues?.[itemUUID]?.[0]; - if (saved !== undefined) return saved; - } +export function getItemValue(item, save = true) { + // Get Item statistics + if (item === null) return 0; + const nbt = item?.getNBT() ?? item; + const itemTag = nbt?.getCompoundTag("tag")?.toObject(); + const itemName = itemTag?.display?.Name; + const itemData = itemTag?.ExtraAttributes; + const itemID = itemData?.id; + + // Check for early return + if (itemName?.startsWith("§6") && itemName?.endsWith(" coins")) + return parseInt(tag?.display?.Lore[4]?.removeFormatting()?.replace(/[^0-9]/g, "")) ?? 0; + if (itemID === undefined) return 0; + + // Get remaining statistics + const amount = item?.getStackSize() ?? 1; + const itemUUID = (itemData?.uuid || item?.getName()) + amount; + + // Check if value is already calculated + if (itemUUID !== undefined) { + const saved = savedValues?.[itemUUID]?.[0]; + if (saved !== undefined) return saved; + } + + // Start Price Checking + const auction = getAuction(); + const bazaar = getBazaar(); + let auctionItem = auction?.[itemID]; + let value = (auctionItem?.lbin ?? 0) * amount; + + // Base Value + let valueMessage = `${DARK_AQUA + BOLD}Item: ${itemName}`; + if (amount !== 1 && save) valueMessage += ` ${GRAY}x${amount}\n`; + else if (save) valueMessage += `\n`; + + // Check for Edge Cases + if (value === 0) { + const partsID = itemID.split("_"); + const pieceTier = partsID[0]; + if (pieceTier in KUUDRA_UPGRADES) { + // Kuudra Piece Upgrade Value + const formatID = partsID.slice(1).join("_"); + auctionItem = auction?.[formatID]; + value = (auctionItem?.lbin ?? 0) * amount; + if (save) valueMessage += `- ${AQUA}Base: ${GREEN}+${formatNumber(value)}\n`; + + let crimsonEssence = 0; + const upgrades = Object.keys(KUUDRA_UPGRADES); + const upgradeTier = upgrades.indexOf(pieceTier); + + for (let i = 0; i <= upgradeTier; i++) { + const upgradeArray = KUUDRA_UPGRADES[upgrades[i]]; + const crimsonStars = i === upgradeTier ? itemData?.upgrade_level || 0 : 10; + + crimsonEssence += upgradeArray.slice(0, crimsonStars + 1).reduce((acc, value) => acc + value, 0); + } + const crimsonValue = crimsonEssence * (bazaar?.ESSENCE_CRIMSON?.[Settings.priceType] ?? 1); + value += crimsonValue; + if (save) valueMessage += `- ${AQUA}Essence Upgrades: ${GREEN}+${formatNumber(crimsonValue)}\n`; + } else { + if (itemID === "PET") { + // Pet Value + const petInfo = JSON.parse(itemData?.petInfo); + const petName = `${petInfo?.tier}_${petInfo?.type}`; + const petAuction = auction?.[petName]; + if (petAuction === undefined) return 0; + + const petLevel = itemName.split("]")[0].split(" ")[1]; + const petLevels = Object.keys(auction?.[`${petName}`]?.levels); + const closestLevel = findClosest(petLevel, petLevels); + value = petAuction?.levels?.[closestLevel]?.lbin ?? petAuction?.lbin ?? 0; + + valueMessage += `- ${AQUA}Base: ${GREEN}+${formatNumber(value)}\n`; + const skinValue = auction["PET_SKIN_" + petInfo.skin]?.lbin ?? 0; + value += skinValue; - // Start Price Checking - const auction = getAuction(); - const bazaar = getBazaar(); - let auctionItem = auction?.[itemID]; - let value = (auctionItem?.lbin ?? 0) * amount; - - // Base Value - let valueMessage = `${DARK_AQUA + BOLD}Item: ${itemName}`; - if (amount !== 1 && save) valueMessage += ` ${GRAY}x${amount}\n`; - else if (save) valueMessage += `\n`; - - // Check for Edge Cases - if (value === 0) { - const partsID = itemID.split('_'); - const pieceTier = partsID[0]; - if (pieceTier in KUUDRA_UPGRADES) { // Kuudra Piece Upgrade Value - const formatID = partsID.slice(1).join('_'); - auctionItem = auction?.[formatID]; - value = (auctionItem?.lbin ?? 0) * amount; - if (save) valueMessage += `- ${AQUA}Base: ${GREEN}+${formatNumber(value)}\n`; - - let crimsonEssence = 0; - const upgrades = Object.keys(KUUDRA_UPGRADES); - const upgradeTier = upgrades.indexOf(pieceTier); - - for (let i = 0; i <= upgradeTier; i++) { - const upgradeArray = KUUDRA_UPGRADES[upgrades[i]]; - const crimsonStars = (i === upgradeTier) ? (itemData?.upgrade_level || 0) : 10; - - crimsonEssence += upgradeArray.slice(0, crimsonStars + 1).reduce((acc, value) => acc + value, 0); - } - const crimsonValue = crimsonEssence * (bazaar?.ESSENCE_CRIMSON?.[Settings.priceType] ?? 1); - value += crimsonValue; - if (save) valueMessage += `- ${AQUA}Essence Upgrades: ${GREEN}+${formatNumber(crimsonValue)}\n`; - } else { - if (itemID === "PET") { // Pet Value - const petInfo = JSON.parse(itemData?.petInfo); - value = auction?.[`${petInfo?.tier}_${petInfo?.type}`]?.lbin ?? 0; - valueMessage += `- ${AQUA}Base: ${GREEN}+${formatNumber(value)}\n`; - const skinValue = auction["PET_SKIN_" + petInfo.skin]?.lbin ?? 0; - value += skinValue; - if (skinValue !== 0 && save) valueMessage += `- ${AQUA}Skin: ${GREEN}+${formatNumber(skinValue)}\n`; - if (save) { - valueMessage += `\n${GOLD}Total Value: ${YELLOW + formatNumber(value)}`; - savedValues[itemUUID] = [value, valueMessage]; - } - } else if (itemID === "ENCHANTED_BOOK") { // Enchantment Value - value = getEnchantmentValue(itemData?.enchantments, bazaar, 0); - if (save) { - valueMessage += `- ${AQUA}Base: ${GREEN}+${formatNumber(value)}`; - savedValues[itemUUID] = [value, valueMessage]; - } - } else { // Bazaar Value - value = (bazaar?.[itemID]?.[Settings.priceType] ?? 0) * amount; - const order = (bazaar?.[itemID]?.[0] ?? 0) * amount; - const insta = (bazaar?.[itemID]?.[1] ?? 0) * amount; - if (order !== 0 || insta !== 0 && save) { - if (save) { - valueMessage += `- ${AQUA}Insta Sell: ${GREEN}+${formatNumber(order)}\n`; - valueMessage += `- ${AQUA}Sell Offer: ${GREEN}+${formatNumber(insta)}`; - savedValues[itemUUID] = [value, valueMessage]; - } - } - } - return value; + if (skinValue !== 0 && save) valueMessage += `- ${AQUA}Skin: ${GREEN}+${formatNumber(skinValue)}\n`; + if (save) { + valueMessage += `\n${GOLD}Total Value: ${YELLOW + formatNumber(value)}`; + savedValues[itemUUID] = [value, valueMessage]; + } + } else if (itemID === "ENCHANTED_BOOK") { + // Enchantment Value + value = getEnchantmentValue(itemData?.enchantments, bazaar, 0); + if (save) { + valueMessage += `- ${AQUA}Base: ${GREEN}+${formatNumber(value)}`; + savedValues[itemUUID] = [value, valueMessage]; } + } else { + // Bazaar Value + value = (bazaar?.[itemID]?.[Settings.priceType] ?? 0) * amount; + const order = (bazaar?.[itemID]?.[0] ?? 0) * amount; + const insta = (bazaar?.[itemID]?.[1] ?? 0) * amount; + if (order !== 0 || (insta !== 0 && save)) { + if (save) { + valueMessage += `- ${AQUA}Insta Sell: ${GREEN}+${formatNumber(order)}\n`; + valueMessage += `- ${AQUA}Sell Offer: ${GREEN}+${formatNumber(insta)}`; + savedValues[itemUUID] = [value, valueMessage]; + } + } + } + return value; } - if (save) valueMessage += `- ${AQUA}Base: ${GREEN}+${formatNumber(value)}\n`; - - // Skin Value - const skinValue = auction?.[itemData.skin]?.lbin ?? 0; - if (skinValue !== 0) { - value += skinValue; - if (save) valueMessage += `- ${AQUA}Skin: ${GREEN}+${formatNumber(skinValue)}\n`; + } + if (save) valueMessage += `- ${AQUA}Base: ${GREEN}+${formatNumber(value)}\n`; + + // Skin Value + const skinValue = auction?.[itemData.skin]?.lbin ?? 0; + if (skinValue !== 0) { + value += skinValue; + if (save) valueMessage += `- ${AQUA}Skin: ${GREEN}+${formatNumber(skinValue)}\n`; + } + + // Reforge Value + const reforgeValue = bazaar?.[REFORGES?.[itemData?.modifier]]?.[Settings.priceType] ?? 0; + if (reforgeValue !== 0) { + value += reforgeValue; + if (save) valueMessage += `- ${AQUA}Reforge: ${GREEN}+${formatNumber(reforgeValue)}\n`; + } + // Master Star Values + if (itemTag?.display?.Lore?.find((line) => line.includes("DUNGEON")) !== undefined) { + let starValue = 0; + const upgrade_level = itemData?.upgrade_level ?? itemData?.dungeon_item_level ?? 0; + for (let i = 0; i < Math.max(upgrade_level - 5, 0); i++) + starValue += bazaar?.[`${STAR_PLACEMENT[i]}_MASTER_STAR`]?.[Settings.priceType] ?? 0; + if (starValue !== 0) { + value += starValue; + if (save) valueMessage += `- ${AQUA}Master Stars: ${GREEN}+${formatNumber(starValue)}\n`; } - - // Reforge Value - const reforgeValue = bazaar?.[REFORGES?.[itemData?.modifier]]?.[Settings.priceType] ?? 0; - if (reforgeValue !== 0) { - value += reforgeValue; - if (save) valueMessage += `- ${AQUA}Reforge: ${GREEN}+${formatNumber(reforgeValue)}\n`; + } + // Recomb Value + const recombValue = + itemData?.rarity_upgrades === undefined ? 0 : bazaar?.RECOMBOBULATOR_3000?.[Settings.priceType] ?? 0; + if (recombValue !== 0) { + value += recombValue; + if (save) valueMessage += `- ${AQUA}Recomb: ${GREEN}+${formatNumber(recombValue)}\n`; + } + // Dye Value + const dyeValue = auction?.[itemData?.dye_item]?.lbin ?? 0; + if (dyeValue !== 0) { + value += dyeValue; + if (save) valueMessage += `- ${AQUA}Dye: ${GREEN}+${formatNumber(dyeValue)}\n`; + } + // Rune Value + const runes = itemData?.runes; + if (runes !== undefined) { + const [runeKey, runeLevel] = Object.entries(runes)[0]; + const runeValue = auction[`${runeKey}_${runeLevel}`]?.lbin ?? 0; + if (runeValue !== 0) { + if (save) valueMessage += `- ${AQUA}Rune: ${GREEN}+${formatNumber(runeValue)}\n`; + value += runeValue; } - // Master Star Values - if (itemTag?.display?.Lore?.find(line => line.includes("DUNGEON")) !== undefined) { - let starValue = 0; - const upgrade_level = itemData?.upgrade_level ?? itemData?.dungeon_item_level ?? 0; - for (let i = 0; i < Math.max(upgrade_level - 5, 0); i++)starValue += bazaar?.[`${STAR_PLACEMENT[i]}_MASTER_STAR`]?.[Settings.priceType] ?? 0; - if (starValue !== 0) { - value += starValue; - if (save) valueMessage += `- ${AQUA}Master Stars: ${GREEN}+${formatNumber(starValue)}\n`; - } + } + + // Potato Book Values + const potatoCount = itemData?.hot_potato_count ?? 0; + if (potatoCount !== 0) { + // Get hot value + const hotPotatoCount = Math.min(potatoCount, 10); + const hotPotatoValue = hotPotatoCount * (bazaar?.HOT_POTATO_BOOK?.[Settings.priceType] ?? 0); + if (save) valueMessage += `\n- ${GOLD + BOLD}Books:\n`; + if (save) valueMessage += ` - ${YELLOW}HPB (${hotPotatoCount}/10): ${GREEN}+${formatNumber(hotPotatoValue)}\n`; + + // Get fuming value + const fumingPotatoCount = Math.max(potatoCount - 10, 0); + const fumingPotatoValue = fumingPotatoCount * (bazaar?.FUMING_POTATO_BOOK?.[Settings.priceType] ?? 0); + if (fumingPotatoValue !== 0 && save) + if (save) + valueMessage += ` - ${YELLOW}FPB (${fumingPotatoCount}/5): ${GREEN}+${formatNumber(fumingPotatoValue)}\n`; + value += hotPotatoValue + fumingPotatoValue; + } + // Art of War Value + const tzuValue = itemData?.art_of_war_count === undefined ? 0 : bazaar?.THE_ART_OF_WAR?.[Settings.priceType]; + if (tzuValue !== 0) { + value += tzuValue; + if (save) valueMessage += ` - ${YELLOW}Sun Tzu: ${GREEN}+${formatNumber(tzuValue)}\n`; + } + // Art of Peace Value + const peaceValue = itemData?.artOfPeaceApplied === undefined ? 0 : bazaar?.THE_ART_OF_PEACE?.[Settings.priceType]; + if (peaceValue !== 0) { + value += peaceValue; + if (save) valueMessage += ` - ${YELLOW}Moon Tzu: ${GREEN}+${formatNumber(peaceValue)}\n`; + } + + // Drill Part Values + const tankPart = itemData?.drill_part_fuel_tank; + const enginePart = itemData?.drill_part_engine; + const modulePart = itemData?.drill_part_upgrade_module; + if (tankPart !== undefined || enginePart !== undefined || modulePart !== undefined) { + if (save) valueMessage += `\n- ${GOLD + BOLD}Drill Parts:\n`; + const tankValue = tankPart === undefined ? 0 : auction?.[tankPart.toUpperCase()]?.lbin ?? 0; + const engineValue = enginePart === undefined ? 0 : auction?.[enginePart.toUpperCase()]?.lbin ?? 0; + const moduleValue = modulePart === undefined ? 0 : auction?.[modulePart.toUpperCase()]?.lbin ?? 0; + + if (save) { + if (tankValue !== 0) + valueMessage += ` - ${AQUA + convertToTitleCase(tankPart)}: ${GREEN}+${formatNumber(tankValue)}\n`; + if (engineValue !== 0) + valueMessage += ` - ${AQUA + convertToTitleCase(enginePart)}: ${GREEN}+${formatNumber(engineValue)}\n`; + if (moduleValue !== 0) + valueMessage += ` - ${AQUA + convertToTitleCase(modulePart)}: ${GREEN}+${formatNumber(moduleValue)}\n`; } - // Recomb Value - const recombValue = itemData?.rarity_upgrades === undefined ? 0 : bazaar?.RECOMBOBULATOR_3000?.[Settings.priceType] ?? 0; - if (recombValue !== 0) { - value += recombValue; - if (save) valueMessage += `- ${AQUA}Recomb: ${GREEN}+${formatNumber(recombValue)}\n`; + } + + // Gem Values + const gemsKeys = Object.keys(itemData?.gems ?? {}); + const powerScroll = itemData?.power_ability_scroll; + if (gemsKeys.length !== 0 || powerScroll) { + if (save) valueMessage += `\n- ${GOLD + BOLD}Gemstones:\n`; + + if (powerScroll) { + const powerScrollValue = auction?.[powerScroll]?.lbin ?? 0; + const scrollColor = GEMSTONE_SLOTS?.[powerScroll.split("_")[0]] ?? WHITE; + if (save) + valueMessage += ` - ${scrollColor + convertToTitleCase(powerScroll)}: ${GREEN}+${formatNumber( + powerScrollValue + )}\n`; + value += powerScrollValue; } - // Dye Value - const dyeValue = auction?.[itemData?.dye_item]?.lbin ?? 0; - if (dyeValue !== 0) { - value += dyeValue; - if (save) valueMessage += `- ${AQUA}Dye: ${GREEN}+${formatNumber(dyeValue)}\n`; + } + + gemsKeys.forEach((gemstone) => { + const gemstoneData = itemData.gems[gemstone]; + const gemstoneTier = gemstoneData?.quality ?? gemstoneData; + const gemstoneType = gemstone.split("_"); + + let gemstoneValue = 0; + let gemstoneName = ""; + if (gemstoneType[0] in GEMSTONE_SLOTS) { + gemstoneName = `${gemstoneTier}_${gemstoneType?.[0]}_GEM`; + gemstoneColor = GEMSTONE_SLOTS[gemstoneType[0]]; + gemstoneValue = bazaar?.[gemstoneName]?.[Settings.priceType] ?? 0; + } else if (MULTIUSE_SLOTS.has(gemstoneType?.[0]) && gemstoneType?.[gemstoneType.length - 1] !== "gem") { + gemstoneName = `${gemstoneTier}_${itemData.gems?.[gemstone + "_gem"]}_GEM`; + gemstoneColor = GEMSTONE_SLOTS[itemData.gems?.[gemstone + "_gem"]]; + gemstoneValue = bazaar?.[gemstoneName]?.[Settings.priceType] ?? 0; } - // Rune Value - const runes = itemData?.runes; - if (runes !== undefined) { - const [runeKey, runeLevel] = Object.entries(runes)[0]; - const runeValue = auction[`${runeKey}_${runeLevel}`]?.lbin ?? 0; - if (runeValue !== 0) { - if (save) valueMessage += `- ${AQUA}Rune: ${GREEN}+${formatNumber(runeValue)}\n`; - value += runeValue - } + + if (gemstoneValue !== 0) { + value += gemstoneValue; + if (save) + valueMessage += ` - ${gemstoneColor + convertToTitleCase(gemstoneName)}: ${GREEN}+${formatNumber( + gemstoneValue + )}\n`; } - - // Potato Book Values - const potatoCount = itemData?.hot_potato_count ?? 0; - if (potatoCount !== 0) { - // Get hot value - const hotPotatoCount = Math.min(potatoCount, 10); - const hotPotatoValue = hotPotatoCount * (bazaar?.HOT_POTATO_BOOK?.[Settings.priceType] ?? 0); - if (save) valueMessage += `\n- ${GOLD + BOLD}Books:\n`; - if (save) valueMessage += ` - ${YELLOW}HPB (${hotPotatoCount}/10): ${GREEN}+${formatNumber(hotPotatoValue)}\n`; - - // Get fuming value - const fumingPotatoCount = Math.max(potatoCount - 10, 0); - const fumingPotatoValue = fumingPotatoCount * (bazaar?.FUMING_POTATO_BOOK?.[Settings.priceType] ?? 0); - if (fumingPotatoValue !== 0 && save) - if (save) valueMessage += ` - ${YELLOW}FPB (${fumingPotatoCount}/5): ${GREEN}+${formatNumber(fumingPotatoValue)}\n`; - value += hotPotatoValue + fumingPotatoValue; + }); + + // Enchantment Values + const enchantOrderValue = getEnchantmentValue(itemData?.enchantments, bazaar, 0); + const enchantInstaValue = getEnchantmentValue(itemData?.enchantments, bazaar, 1); + if (enchantOrderValue !== 0) { + value += enchantOrderValue; + if (save) { + valueMessage += `\n- ${GOLD + BOLD}Enchantments:\n`; + valueMessage += ` - ${DARK_GREEN}Buy Order Value: ${GREEN}+${formatNumber(enchantOrderValue)}\n`; + valueMessage += ` - ${DARK_GREEN}Insta Buy Value: ${GREEN}+${formatNumber(enchantInstaValue)}\n`; } - // Art of War Value - const tzuValue = itemData?.art_of_war_count === undefined ? 0 : bazaar?.THE_ART_OF_WAR?.[Settings.priceType]; - if (tzuValue !== 0) { - value += tzuValue; - if (save) valueMessage += ` - ${YELLOW}Sun Tzu: ${GREEN}+${formatNumber(tzuValue)}\n`; + } + + // Wither Impact Scroll Values + const witherScrolls = itemData?.ability_scroll ?? []; + if (witherScrolls.length !== 0 && save) valueMessage += `\n- ${GOLD + BOLD}Wither Scrolls:\n`; + witherScrolls.forEach((scroll) => { + const scrollValue = bazaar?.[scroll]?.[Settings.priceType]; + if (scrollValue !== 0) { + if (save) + valueMessage += ` - ${DARK_GRAY + convertToTitleCase(scroll)}: ${GREEN}+${formatNumber(scrollValue)}\n`; + value += scrollValue; } - // Art of Peace Value - const peaceValue = itemData?.artOfPeaceApplied === undefined ? 0 : bazaar?.THE_ART_OF_PEACE?.[Settings.priceType]; - if (peaceValue !== 0) { - value += peaceValue; - if (save) valueMessage += ` - ${YELLOW}Moon Tzu: ${GREEN}+${formatNumber(peaceValue)}\n`; + }); + + // Attribute Values + const attributes = Object.keys(itemData?.attributes ?? {}).sort(); + let attributesValue = 0; + let comboCalc = true; + let doubleCalc = false; + let attributeMessage = ""; + if (attributes.length && save) valueMessage += `\n- ${GOLD + BOLD}Attributes:\n`; + attributes.forEach((attribute) => { + // Get attribute data + const attributeLevel = itemData?.attributes[attribute]; + const attributeCount = 2 ** (attributeLevel - 1); + const attributePiece = auctionItem?.attributes?.[attribute]?.lbin ?? 0; + const attributeValue = + Math.min(attributePiece, auction?.ATTRIBUTE_SHARD?.attributes?.[attribute]?.lbin ?? attributePiece) * + attributeCount; + + // Check if valid attribute to calc + if (!data.attributelist.includes(attribute)) { + attributeMessage += ` - ${RED + convertToTitleCase(attribute)} ${attributeLevel}: ${DARK_RED}Nullified\n`; + comboCalc = false; + return; } - // Drill Part Values - const tankPart = itemData?.drill_part_fuel_tank; - const enginePart = itemData?.drill_part_engine; - const modulePart = itemData?.drill_part_upgrade_module; - if (tankPart !== undefined || enginePart !== undefined || modulePart !== undefined) { - if (save) valueMessage += `\n- ${GOLD + BOLD}Drill Parts:\n`; - const tankValue = tankPart === undefined ? 0 : auction?.[tankPart.toUpperCase()]?.lbin ?? 0; - const engineValue = enginePart === undefined ? 0 : auction?.[enginePart.toUpperCase()]?.lbin ?? 0; - const moduleValue = modulePart === undefined ? 0 : auction?.[modulePart.toUpperCase()]?.lbin ?? 0; - - if (save) { - if (tankValue !== 0) valueMessage += ` - ${AQUA + convertToTitleCase(tankPart)}: ${GREEN}+${formatNumber(tankValue)}\n`; - if (engineValue !== 0) valueMessage += ` - ${AQUA + convertToTitleCase(enginePart)}: ${GREEN}+${formatNumber(engineValue)}\n`; - if (moduleValue !== 0) valueMessage += ` - ${AQUA + convertToTitleCase(modulePart)}: ${GREEN}+${formatNumber(moduleValue)}\n`; - } - } - - // Gem Values - const gemsKeys = Object.keys(itemData?.gems ?? {}); - const powerScroll = itemData?.power_ability_scroll; - if (gemsKeys.length !== 0 || powerScroll) { - if (save) valueMessage += `\n- ${GOLD + BOLD}Gemstones:\n`; - - if (powerScroll) { - const powerScrollValue = auction?.[powerScroll]?.lbin ?? 0; - const scrollColor = GEMSTONE_SLOTS?.[powerScroll.split('_')[0]] ?? WHITE; - if (save) valueMessage += ` - ${scrollColor + convertToTitleCase(powerScroll)}: ${GREEN}+${formatNumber(powerScrollValue)}\n`; - value += powerScrollValue; - } + // Add value and message based on calced values + if (attributeLevel > 5 || !Settings.singleAttribute) { + attributesValue += attributeValue; + attributeMessage += ` - ${RED + convertToTitleCase(attribute)} ${attributeLevel}: ${GREEN}+${formatNumber( + attributeValue + )}\n`; + doubleCalc = true; + } else if (attributeValue > attributesValue && !doubleCalc) { + attributeMessage = attributeMessage.replace(/(\+[^\n]+)/, `${DARK_RED}Nullified`); + attributeMessage += ` - ${RED + convertToTitleCase(attribute)} ${attributeLevel}: ${GREEN}+${formatNumber( + attributeValue + )}\n`; + attributesValue = attributeValue; + } else attributeMessage += ` - ${RED + convertToTitleCase(attribute)} ${attributeLevel}: ${DARK_RED}Nullified\n`; + }); + + // Attribute combo value + const comboValue = auctionItem?.attribute_combos?.[attributes.join(" ")]?.lbin ?? 0; + if (comboCalc && comboValue >= Settings.minGR * 1_000_000 && Settings.minGR !== 0) { + if (doubleCalc) { + attributeMessage += ` - ${RED}Go(o)d Roll: ${GREEN}+${formatNumber(comboValue)}\n`; + attributesValue += comboValue; + } else if (comboValue > attributesValue) { + attributeMessage = ` - ${RED}Go(o)d Roll: ${GREEN}+${formatNumber(comboValue)}\n`; + attributesValue = comboValue; } + } + // Final values + if (save) valueMessage += attributeMessage; + value += attributesValue; - gemsKeys.forEach((gemstone) => { - const gemstoneData = itemData.gems[gemstone]; - const gemstoneTier = gemstoneData?.quality ?? gemstoneData; - const gemstoneType = gemstone.split('_'); - - let gemstoneValue = 0; - let gemstoneName = ""; - if (gemstoneType[0] in GEMSTONE_SLOTS) { - gemstoneName = `${gemstoneTier}_${gemstoneType?.[0]}_GEM`; - gemstoneColor = GEMSTONE_SLOTS[gemstoneType[0]]; - gemstoneValue = bazaar?.[gemstoneName]?.[Settings.priceType] ?? 0; - } else if (MULTIUSE_SLOTS.has(gemstoneType?.[0]) && gemstoneType?.[gemstoneType.length - 1] !== "gem") { - gemstoneName = `${gemstoneTier}_${itemData.gems?.[gemstone + "_gem"]}_GEM`; - gemstoneColor = GEMSTONE_SLOTS[itemData.gems?.[gemstone + "_gem"]]; - gemstoneValue = bazaar?.[gemstoneName]?.[Settings.priceType] ?? 0; - } - - if (gemstoneValue !== 0) { - value += gemstoneValue; - if (save) valueMessage += ` - ${gemstoneColor + convertToTitleCase(gemstoneName)}: ${GREEN}+${formatNumber(gemstoneValue)}\n`; - } - }); - - // Enchantment Values - const enchantOrderValue = getEnchantmentValue(itemData?.enchantments, bazaar, 0); - const enchantInstaValue = getEnchantmentValue(itemData?.enchantments, bazaar, 1); - if (enchantOrderValue !== 0) { - value += enchantOrderValue; - if (save) { - valueMessage += `\n- ${GOLD + BOLD}Enchantments:\n`; - valueMessage += ` - ${DARK_GREEN}Buy Order Value: ${GREEN}+${formatNumber(enchantOrderValue)}\n`; - valueMessage += ` - ${DARK_GREEN}Insta Buy Value: ${GREEN}+${formatNumber(enchantInstaValue)}\n`; - } - } - - // Wither Impact Scroll Values - const witherScrolls = itemData?.ability_scroll ?? []; - if (witherScrolls.length !== 0 && save) valueMessage += `\n- ${GOLD + BOLD}Wither Scrolls:\n`; - witherScrolls.forEach(scroll => { - const scrollValue = bazaar?.[scroll]?.[Settings.priceType]; - if (scrollValue !== 0) { - if (save) valueMessage += ` - ${DARK_GRAY + convertToTitleCase(scroll)}: ${GREEN}+${formatNumber(scrollValue)}\n`; - value += scrollValue; - } - }); - - // Attribute Values - const attributes = Object.keys(itemData?.attributes ?? {}).sort(); - let attributesValue = 0; - let comboCalc = true; - let doubleCalc = false; - let attributeMessage = ""; - if (attributes.length && save) valueMessage += `\n- ${GOLD + BOLD}Attributes:\n`; - attributes.forEach(attribute => { - // Get attribute data - const attributeLevel = itemData?.attributes[attribute]; - const attributeCount = 2 ** (attributeLevel - 1); - const attributePiece = auctionItem?.attributes?.[attribute] ?? 0; - const attributeValue = Math.min(attributePiece, auction?.ATTRIBUTE_SHARD?.attributes?.[attribute] ?? attributePiece) * attributeCount; - - // Check if valid attribute to calc - if (!data.attributelist.includes(attribute)) { - attributeMessage += ` - ${RED + convertToTitleCase(attribute)} ${attributeLevel}: ${DARK_RED}Nullified\n`; - comboCalc = false; - return; - } - - // Add value and message based on calced values - if (attributeLevel > 5 || !Settings.singleAttribute) { - attributesValue += attributeValue; - attributeMessage += ` - ${RED + convertToTitleCase(attribute)} ${attributeLevel}: ${GREEN}+${formatNumber(attributeValue)}\n`; - doubleCalc = true; - } else if (attributeValue > attributesValue && !doubleCalc) { - attributeMessage = attributeMessage.replace(/(\+[^\n]+)/, `${DARK_RED}Nullified`); - attributeMessage += ` - ${RED + convertToTitleCase(attribute)} ${attributeLevel}: ${GREEN}+${formatNumber(attributeValue)}\n`; - attributesValue = attributeValue; - } else attributeMessage += ` - ${RED + convertToTitleCase(attribute)} ${attributeLevel}: ${DARK_RED}Nullified\n`; - }); - // Attribute combo value - const comboValue = auctionItem?.attribute_combos?.[attributes.join(" ")] ?? 0; - if (comboCalc && comboValue >= Settings.minGR * 1_000_000 && Settings.minGR !== 0) { - if (doubleCalc) { - attributeMessage += ` - ${RED}Go(o)d Roll: ${GREEN}+${formatNumber(comboValue)}\n`; - attributesValue += comboValue; - } else if (comboValue > attributesValue) { - attributeMessage = ` - ${RED}Go(o)d Roll: ${GREEN}+${formatNumber(comboValue)}\n`; - attributesValue = comboValue; - } - } - // Final values - if (save) valueMessage += attributeMessage; - value += attributesValue; - - // Total Value - if (save) valueMessage += `\n${GOLD}Total Value: ${YELLOW + formatNumber(value)}`; - - if (save) savedValues[itemUUID] = [value, valueMessage]; - return value; + // Total Value + if (save) valueMessage += `\n${GOLD}Total Value: ${YELLOW + formatNumber(value)}`; + + if (save) savedValues[itemUUID] = [value, valueMessage]; + return value; } /** * Adds enchantment value tag onto item over all items hovered over. */ -registerWhen(register("preItemRender", (_, __, ___, gui) => { +registerWhen( + register("preItemRender", (_, __, ___, gui) => { // Check item data to cancel lore append. const item = Player.getContainer().getItems()[gui?.getSlotUnderMouse()?.field_75222_d]; if (!item) return; - + const itemTag = item.getNBT().getCompoundTag("tag"); const loreTag = itemTag.getCompoundTag("display").getTagMap().get("Lore"); - const itemUUID = (itemTag.getCompoundTag("ExtraAttributes").getString("uuid") || item.getName()) + item.getStackSize(); + const itemUUID = + (itemTag.getCompoundTag("ExtraAttributes").getString("uuid") || item.getName()) + item.getStackSize(); if (loreTag === null) return; // Check if value already in tooltip const list = new NBTTagList(loreTag); for (let i = 0; i < list.getTagCount(); i++) { - if (list.getStringTagAt(i).startsWith("§3§lItem Value:")) { - valueOverlay.setMessage(savedValues?.[itemUUID]?.[1] ?? ""); - return; - } + if (list.getStringTagAt(i).startsWith("§3§lItem Value:")) { + valueOverlay.setMessage(savedValues?.[itemUUID]?.[1] ?? ""); + return; + } } // Add to item lore. const value = getItemValue(item); valueOverlay.setMessage(savedValues?.[itemUUID]?.[1] ?? ""); if (value !== 0 && (Settings.itemPrice === 2 || Settings.itemPrice === 3)) { - list.appendTag(new NBTTagString('')); - list.appendTag(new NBTTagString(`§3§lItem Value: §6${formatNumber(value)}`)); + list.appendTag(new NBTTagString("")); + list.appendTag(new NBTTagString(`§3§lItem Value: §6${formatNumber(value)}`)); } -}), () => Settings.itemPrice !== 0); + }), + () => Settings.itemPrice !== 0 +); /** * Reset data on data transfers. */ -register("guiClosed", () => { valueOverlay.setMessage("") }); -register("worldUnload", () => { savedValues = {} }); +register("guiClosed", () => { + valueOverlay.setMessage(""); +}); +register("worldUnload", () => { + savedValues = {}; +}); diff --git a/features/economy/MinionCalc.js b/features/economy/MinionCalc.js index 1241cf9..d8c4242 100644 --- a/features/economy/MinionCalc.js +++ b/features/economy/MinionCalc.js @@ -1,8 +1,21 @@ -import Settings from '../../utils/Settings'; -import { AQUA, BOLD, DARK_AQUA, DARK_GRAY, GOLD, GRAY, GREEN, ITALIC, LOGO, RED, RESET, UNDERLINE, WHITE } from '../../utils/Constants'; -import { commafy, formatNumber } from '../../utils/functions/format'; -import { getBazaar } from './Economy'; - +import { + AQUA, + BOLD, + DARK_AQUA, + DARK_GRAY, + GOLD, + GRAY, + GREEN, + ITALIC, + LOGO, + RED, + RESET, + UNDERLINE, + WHITE, +} from "../../utils/Constants"; +import Settings from "../../utils/Settings"; +import { commafy, formatNumber } from "../../utils/functions/format"; +import { getBazaar } from "./Economy"; /** * Variables used to represent minion upgrades. @@ -14,7 +27,7 @@ const VAMPIRE_ACTIONS = [190, 175, 160, 140, 117, 95]; /** * Variables used to represent minion action speeds. * 1 (base) + 0.4 (2 flycatchers) + 0.11 (beacon) + 0.1 (mithril infusion) = 1.61 - * + * * Other potention upgrades: * +1.8 (inferno minions) * -0.2 (super compacter) @@ -28,7 +41,7 @@ const PSA = `${GRAY + ITALIC}Note that these calculations are done with max upgr /** * Hypergolic gabagool calculation. - * + * * HYPERGOLIC GABAGOOL: * 1202 Enchanted Coal * 31 = 37262 * 75.125 Enchanted Sulphur * 31 = 2328.875 @@ -40,8 +53,10 @@ const PSA = `${GRAY + ITALIC}Note that these calculations are done with max upgr * @param {String} type - Sound category. */ function calcHypergolic(type) { - const bazaar = getBazaar(); - return 1202 * bazaar.ENCHANTED_COAL[type] + 75.125 * bazaar.ENCHANTED_SULPHUR[type] + 6912 * bazaar.CRUDE_GABAGOOL[type]; + const bazaar = getBazaar(); + return ( + 1202 * bazaar.ENCHANTED_COAL[type] + 75.125 * bazaar.ENCHANTED_SULPHUR[type] + 6912 * bazaar.CRUDE_GABAGOOL[type] + ); } /** @@ -50,29 +65,29 @@ function calcHypergolic(type) { * @param {String[]} args - Array of player input values. */ export function calcMinions(args) { - // Update Prices - const bazaar = getBazaar(); + // Update Prices + const bazaar = getBazaar(); - // Universal variables - const minions = isNaN(args[2]) ? 31 : args[2]; - const tier = isNaN(args[3]) || args[3] > 11 ? 3 : args[3]; - let infernoAction = 1.1 * (INFERNO_ACTION_BASE - (tier * INFERNO_ACTION_UPGRADE)) / MAX_INFERNO; - const vampAction = 86400 / (VAMPIRE_ACTIONS[Math.ceil(tier/2) - 1]*2/MAX_CATALYST) * minions; + // Universal variables + const minions = isNaN(args[2]) ? 31 : args[2]; + const tier = isNaN(args[3]) || args[3] > 11 ? 3 : args[3]; + let infernoAction = (1.1 * (INFERNO_ACTION_BASE - tier * INFERNO_ACTION_UPGRADE)) / MAX_INFERNO; + const vampAction = (86400 / ((VAMPIRE_ACTIONS[Math.ceil(tier / 2) - 1] * 2) / MAX_CATALYST)) * minions; - // Different Calcs - switch (args[1]) { - case "hypergolic": - case "hg": - const hypergolic = bazaar.HYPERGOLIC_GABAGOOL; - const orderHypergolic = calcHypergolic(0); - const instaHypergolic = calcHypergolic(1); - const p1 = hypergolic[0] - orderHypergolic; - const p2 = hypergolic[0] - instaHypergolic; - const p3 = hypergolic[1] - orderHypergolic; - const p4 = hypergolic[1] - instaHypergolic; + // Different Calcs + switch (args[1]) { + case "hypergolic": + case "hg": + const hypergolic = bazaar.HYPERGOLIC_GABAGOOL; + const orderHypergolic = calcHypergolic(0); + const instaHypergolic = calcHypergolic(1); + const p1 = hypergolic[0] - orderHypergolic; + const p2 = hypergolic[0] - instaHypergolic; + const p3 = hypergolic[1] - orderHypergolic; + const p4 = hypergolic[1] - instaHypergolic; - ChatLib.chat( -`\n${LOGO + GREEN + BOLD}Hypergolic Craft Profits:\n + ChatLib.chat( + `\n${LOGO + GREEN + BOLD}Hypergolic Craft Profits:\n ${RED + BOLD + UNDERLINE}Hypergolic Gabagool Cost: ${AQUA}Insta Sell: ${WHITE + commafy(hypergolic[0])} ${AQUA}Sell Offer: ${WHITE + commafy(hypergolic[1])}\n @@ -83,9 +98,10 @@ ${DARK_AQUA + BOLD + UNDERLINE}Total Profit: ${AQUA}Insta Sell + Buy Order: ${WHITE + commafy(p1)} ${AQUA}Insta Sell + Insta Buy: ${WHITE + commafy(p2)} ${AQUA}Sell Offer + Buy Order: ${WHITE + commafy(p3)} -${AQUA}Sell Offer + Insta Buy: ${WHITE + commafy(p4)}\n`); - break; - /* INFERNO MINION LOOT TABLE: +${AQUA}Sell Offer + Insta Buy: ${WHITE + commafy(p4)}\n` + ); + break; + /* INFERNO MINION LOOT TABLE: Chili Pepper 1/156 Inferno Vertex 1/16,364 Inferno Apex 1/1,570,909 @@ -95,84 +111,90 @@ ${AQUA}Sell Offer + Insta Buy: ${WHITE + commafy(p4)}\n`); base / (21 * (1 + flyCatchers + minExpander + infusion + beacon + powerCrystal + risingCelsius)) base / 71.61 for MAX UPGRADES */ - case "inferno": // INFERNO MINION PROFIT - const eyedrop = 1.3; - infernoAction /= 21; + case "inferno": // INFERNO MINION PROFIT + const eyedrop = 1.3; + infernoAction /= 21; - // Drops - const actions = minions * 86400 / (2 * infernoAction); - const apexMinion = tier >= 10 ? 2 : 1; + // Drops + const actions = (minions * 86400) / (2 * infernoAction); + const apexMinion = tier >= 10 ? 2 : 1; - const drops = { - "GABAGOOL": actions, - "CHILI": (actions / (156 / eyedrop) * 1.15), - "VERTEX": (actions / (16364 / eyedrop) * 2.8).toFixed(2), - "APEX": (actions / (1570909 / eyedrop) * apexMinion * 1.2).toFixed(2), - "REAPER": (actions / (458182 / eyedrop)).toFixed(2) - } - const profit = { - "GABAGOOL": drops.GABAGOOL * bazaar.CRUDE_GABAGOOL[1 - Settings.priceType], - "CHILI": drops.CHILI * bazaar.CHILI_PEPPER[1 - Settings.priceType], - "VERTEX": drops.VERTEX * bazaar.INFERNO_VERTEX[1 - Settings.priceType], - "APEX": drops.APEX * bazaar.INFERNO_APEX[1 - Settings.priceType], - "REAPER": drops.REAPER * bazaar.REAPER_PEPPER[1 - Settings.priceType] - }; + const drops = { + GABAGOOL: actions, + CHILI: (actions / (156 / eyedrop)) * 1.15, + VERTEX: ((actions / (16364 / eyedrop)) * 2.8).toFixed(2), + APEX: ((actions / (1570909 / eyedrop)) * apexMinion * 1.2).toFixed(2), + REAPER: (actions / (458182 / eyedrop)).toFixed(2), + }; + const profit = { + GABAGOOL: drops.GABAGOOL * bazaar.CRUDE_GABAGOOL[1 - Settings.priceType], + CHILI: drops.CHILI * bazaar.CHILI_PEPPER[1 - Settings.priceType], + VERTEX: drops.VERTEX * bazaar.INFERNO_VERTEX[1 - Settings.priceType], + APEX: drops.APEX * bazaar.INFERNO_APEX[1 - Settings.priceType], + REAPER: drops.REAPER * bazaar.REAPER_PEPPER[1 - Settings.priceType], + }; - // Fuel + Net Gain - const fuel = minions * ( - bazaar.HYPERGOLIC_GABAGOOL[Settings.priceType] + - 6 * bazaar.CRUDE_GABAGOOL_DISTILLATE[Settings.priceType] + - 2 * bazaar.INFERNO_FUEL_BLOCK[Settings.priceType] + - bazaar.CAPSAICIN_EYEDROPS_NO_CHARGES[Settings.priceType] - ); - const net = Object.values(profit).reduce((a, c) => a + c, 0) - fuel; + // Fuel + Net Gain + const fuel = + minions * + (bazaar.HYPERGOLIC_GABAGOOL[Settings.priceType] + + 6 * bazaar.CRUDE_GABAGOOL_DISTILLATE[Settings.priceType] + + 2 * bazaar.INFERNO_FUEL_BLOCK[Settings.priceType] + + bazaar.CAPSAICIN_EYEDROPS_NO_CHARGES[Settings.priceType]); + const net = Object.values(profit).reduce((a, c) => a + c, 0) - fuel; - // ChatLib the values - ChatLib.chat( -`\n${GOLD + BOLD + minions} Inferno Minion(s) t${tier} ${DARK_GRAY + BOLD}[Hypergolic]: + // ChatLib the values + ChatLib.chat( + `\n${GOLD + BOLD + minions} Inferno Minion(s) t${tier} ${DARK_GRAY + BOLD}[Hypergolic]: ${AQUA}Crude Gabagool ${GRAY}[${formatNumber(drops.GABAGOOL)}]${AQUA}: ${RESET + commafy(profit.GABAGOOL)} ${AQUA}Chili Pepper ${GRAY}[${formatNumber(drops.CHILI)}]${AQUA}: ${RESET + commafy(profit.CHILI)} ${AQUA}Inferno Vertex ${GRAY}[${drops.VERTEX}]${AQUA}: ${RESET + commafy(profit.VERTEX)} ${AQUA}Inferno Apex ${GRAY}[${drops.APEX}]${AQUA}: ${RESET + commafy(profit.APEX)} ${AQUA}Reaper Pepper ${GRAY}[${drops.REAPER}]${AQUA}: ${RESET + commafy(profit.REAPER)}\n ${RED}Fuel Price: ${RESET + commafy(fuel)} -${GREEN}Total Profit: ${RESET + commafy(net)}\n${PSA}`); - break; - case "gabagool": // GABAGOOL!!! - case "gaba": - // Heavy 15x - infernoAction /= 16; - const gabagool = minions * 86400 / (2 * infernoAction); - const heavyGabagool = gabagool * bazaar.CRUDE_GABAGOOL[1 - Settings.priceType]; - const heavyPrice = minions * ( - bazaar.HEAVY_GABAGOOL[Settings.priceType] + - 6 * bazaar.CRUDE_GABAGOOL_DISTILLATE[Settings.priceType] + - 2 * bazaar.INFERNO_FUEL_BLOCK[Settings.priceType] - ); - const heavyProfit = heavyGabagool - heavyPrice; +${GREEN}Total Profit: ${RESET + commafy(net)}\n${PSA}` + ); + break; + case "gabagool": // GABAGOOL!!! + case "gaba": + // Heavy 15x + infernoAction /= 16; + const gabagool = (minions * 86400) / (2 * infernoAction); + const heavyGabagool = gabagool * bazaar.CRUDE_GABAGOOL[1 - Settings.priceType]; + const heavyPrice = + minions * + (bazaar.HEAVY_GABAGOOL[Settings.priceType] + + 6 * bazaar.CRUDE_GABAGOOL_DISTILLATE[Settings.priceType] + + 2 * bazaar.INFERNO_FUEL_BLOCK[Settings.priceType]); + const heavyProfit = heavyGabagool - heavyPrice; - // Format ChatLib.chat - ChatLib.chat( -`\n${GOLD + BOLD}${minions} Inferno Minion(s) t${tier} ${DARK_GRAY + BOLD}[Heavy]: + // Format ChatLib.chat + ChatLib.chat( + `\n${GOLD + BOLD}${minions} Inferno Minion(s) t${tier} ${DARK_GRAY + BOLD}[Heavy]: ${AQUA}Gabagool ${GRAY}[${commafy(gabagool)}]${AQUA}: ${RESET + commafy(heavyGabagool)}\n ${RED}Fuel Price: ${RESET + commafy(heavyPrice)} -${GREEN}Total Profit: ${RESET + commafy(heavyProfit)}\n${PSA}`); - break; - case "vampire": - case "vamp": - const hemovibe = [(vampAction).toFixed(4), vampAction*bazaar.HEMOVIBE[1 - Settings.priceType]]; - const hemoglass = [(hemovibe[0]/160).toFixed(4), hemovibe[0]/160*bazaar.HEMOGLASS[1 - Settings.priceType]]; - const hemobomb = [(hemoglass[0]/15).toFixed(4), hemoglass[0]/15*bazaar.HEMOBOMB[1 - Settings.priceType]]; - const vampCost = bazaar.HYPER_CATALYST[Settings.priceType] * 4 * minions; - const vampProfit = hemovibe[1] - vampCost; - - ChatLib.chat( -`\n${GOLD + BOLD}Drops for ${minions} Vampire Minion(s) t${tier} +${GREEN}Total Profit: ${RESET + commafy(heavyProfit)}\n${PSA}` + ); + break; + case "vampire": + case "vamp": + const hemovibe = [vampAction.toFixed(4), vampAction * bazaar.HEMOVIBE[1 - Settings.priceType]]; + const hemoglass = [ + (hemovibe[0] / 160).toFixed(4), + (hemovibe[0] / 160) * bazaar.HEMOGLASS[1 - Settings.priceType], + ]; + const hemobomb = [(hemoglass[0] / 15).toFixed(4), (hemoglass[0] / 15) * bazaar.HEMOBOMB[1 - Settings.priceType]]; + const vampCost = bazaar.HYPER_CATALYST[Settings.priceType] * 4 * minions; + const vampProfit = hemovibe[1] - vampCost; + + ChatLib.chat( + `\n${GOLD + BOLD}Drops for ${minions} Vampire Minion(s) t${tier} ${AQUA}Hemovibe ${GRAY}[${hemovibe[0]}]${AQUA}: ${RESET + commafy(hemovibe[1])} ${AQUA}Hemoglass ${GRAY}[${hemoglass[0]}]${AQUA}: ${RESET + commafy(hemoglass[1])} ${AQUA}Hemobomb ${GRAY}[${hemobomb[0]}]${AQUA}: ${RESET + commafy(hemobomb[1])}\n ${RED}Hyper Catalyst Cost: ${RESET + commafy(vampCost)} -${GREEN}Total Profit: ${RESET + commafy(vampProfit)}\n${PSA}`); - break; - } +${GREEN}Total Profit: ${RESET + commafy(vampProfit)}\n${PSA}` + ); + break; + } } diff --git a/features/economy/MissingSkins.js b/features/economy/MissingSkins.js index 8395358..62f7c33 100644 --- a/features/economy/MissingSkins.js +++ b/features/economy/MissingSkins.js @@ -3,105 +3,110 @@ import { AQUA, DARK_GRAY, GREEN, ITALIC, LOGO, RED } from "../../utils/Constants import { Json } from "../../utils/Json"; import { decode } from "../../utils/functions/misc"; - const skins = new Json("skins.json", false).getData(); register("guiOpened", () => { - Client.scheduleTask(2, () => { - if (!Player.getContainer().getName().startsWith("Previous Fire Sales")) return; - const items = Player.getContainer().getItems(); + Client.scheduleTask(2, () => { + if (!Player.getContainer().getName().startsWith("Previous Fire Sales")) return; + const items = Player.getContainer().getItems(); - for (let i = 1; i < 5; i++) { - for (let j = 1; j < 8; j++) { - let skin = items[i * 9 + j]; - if (skin === null) break; + for (let i = 1; i < 5; i++) { + for (let j = 1; j < 8; j++) { + let skin = items[i * 9 + j]; + if (skin === null) break; - let skinID = skin.getNBT().getCompoundTag("tag").getCompoundTag("ExtraAttributes").getString("id"); - if (skinID.endsWith("RUNE") || skinID.startsWith("DYE") || skinID.endsWith("BARN_SKIN")) continue; + let skinID = skin.getNBT().getCompoundTag("tag").getCompoundTag("ExtraAttributes").getString("id"); + if (skinID.endsWith("RUNE") || skinID.startsWith("DYE") || skinID.endsWith("BARN_SKIN")) continue; - if (!skins.includes(skinID)) skins.push(skinID); - } - } - }); + if (!skins.includes(skinID)) skins.push(skinID); + } + } + }); }); register("command", () => { - if (skins.length === 0) { - ChatLib.chat(`${LOGO + RED}Please open all Fire Sale menus first to register all valid skins!`) - return; - } + if (skins.length === 0) { + ChatLib.chat(`${LOGO + RED}Please open all Fire Sale menus first to register all valid skins!`); + return; + } - axios.get(`https://sky.shiiyu.moe/api/v2/profile/${Player.getName()}`).then(response => { - let missing = [...skins]; + axios + .get(`https://sky.shiiyu.moe/api/v2/profile/${Player.getName()}`) + .then((response) => { + let missing = [...skins]; - function parseSkins(contents) { - if (contents === undefined) return; + function parseSkins(contents) { + if (contents === undefined) return; - let items = decode(contents); - for (let i = 0; i < items.func_74745_c(); i++) { - let nbt = new NBTTagCompound(items.func_150305_b(i)).getCompoundTag("tag").getCompoundTag("ExtraAttributes"); + let items = decode(contents); + for (let i = 0; i < items.func_74745_c(); i++) { + let nbt = new NBTTagCompound(items.func_150305_b(i)).getCompoundTag("tag").getCompoundTag("ExtraAttributes"); - let skin = nbt.getString("skin"); - if (skin) { - let index = missing.indexOf(skin); - if (index !== -1) missing.splice(index, 1); - } - - let item = nbt.getString("id"); - if (item) { - let index = missing.indexOf(item); - if (index !== -1) missing.splice(index, 1); - } - } - } - - const profiles = response.data.profiles; - const player = profiles[Object.keys(profiles).find(prof => profiles[prof].current)]?.raw; - if (player === undefined) { - ChatLib.chat(`${LOGO + RED}Error fetching player profile!`); - return; - } - const inv = player.inventory; - if (inv === undefined) { - ChatLib.chat(`${LOGO + RED}Player inventory API is turned off!`); - return; - } - - // Inventroy values - parseSkins(inv.inv_contents?.data); - parseSkins(inv.inv_armor?.data); - parseSkins(inv.equipment_contents?.data); - - // Storage values - parseSkins(inv.wardrobe_contents?.data); - parseSkins(inv.ender_chest_contents?.data); - parseSkins(inv.personal_vault_contents?.data); - - // Backpack values - const backpacks = inv.backpack_contents; - const packs = backpacks === undefined ? 0 : Object.keys(backpacks).length; - for (let i = 0; i < packs; i++) { - let backpack = backpacks[i.toString()]; - parseSkins(backpack?.data) - } - - const icons = inv.backpack_icons; - const sacks = icons === undefined ? 0 : Object.keys(icons).length; - for (let i = 0; i < sacks; i++) { - let icon = icons[i.toString()]; - parseSkins(icon?.data) - } - - // Pets values - const pets = player.pets_data.pets; - pets.forEach(pet => { - const skin = "PET_SKIN_" + pet.skin; + let skin = nbt.getString("skin"); + if (skin) { let index = missing.indexOf(skin); if (index !== -1) missing.splice(index, 1); - }); + } - const miss = missing.join(`\n ${DARK_GRAY}- ${AQUA}`); - ChatLib.chat( -`${LOGO + GREEN}\n${DARK_GRAY} - ${AQUA + miss} -${DARK_GRAY + ITALIC}This takes a while between updates, also does not track Taylor, Museum, Barn, and Rune cosmetics...`); - }).catch(err => ChatLib.chat(err)); + let item = nbt.getString("id"); + if (item) { + let index = missing.indexOf(item); + if (index !== -1) missing.splice(index, 1); + } + } + } + + const profiles = response.data.profiles; + const player = profiles[Object.keys(profiles).find((prof) => profiles[prof].current)]?.raw; + if (player === undefined) { + ChatLib.chat(`${LOGO + RED}Error fetching player profile!`); + return; + } + const inv = player.inventory; + if (inv === undefined) { + ChatLib.chat(`${LOGO + RED}Player inventory API is turned off!`); + return; + } + + // Inventroy values + parseSkins(inv.inv_contents?.data); + parseSkins(inv.inv_armor?.data); + parseSkins(inv.equipment_contents?.data); + + // Storage values + parseSkins(inv.wardrobe_contents?.data); + parseSkins(inv.ender_chest_contents?.data); + parseSkins(inv.personal_vault_contents?.data); + + // Backpack values + const backpacks = inv.backpack_contents; + const packs = backpacks === undefined ? 0 : Object.keys(backpacks).length; + for (let i = 0; i < packs; i++) { + let backpack = backpacks[i.toString()]; + parseSkins(backpack?.data); + } + + const icons = inv.backpack_icons; + const sacks = icons === undefined ? 0 : Object.keys(icons).length; + for (let i = 0; i < sacks; i++) { + let icon = icons[i.toString()]; + parseSkins(icon?.data); + } + + // Pets values + const pets = player.pets_data.pets; + pets.forEach((pet) => { + const skin = "PET_SKIN_" + pet.skin; + let index = missing.indexOf(skin); + if (index !== -1) missing.splice(index, 1); + }); + + const miss = missing.join(`\n ${DARK_GRAY}- ${AQUA}`); + ChatLib.chat( + `${LOGO + GREEN}\n${DARK_GRAY} - ${AQUA + miss} +${ + DARK_GRAY + ITALIC +}This takes a while between updates, also does not track Taylor, Museum, Barn, and Rune cosmetics...` + ); + }) + .catch((err) => ChatLib.chat(err)); }).setName("missingSkins"); diff --git a/features/economy/Networth.js b/features/economy/Networth.js index 47fd538..cb97c04 100644 --- a/features/economy/Networth.js +++ b/features/economy/Networth.js @@ -1,197 +1,244 @@ import axios from "../../../axios"; -import { AQUA, BLUE, DARK_AQUA, DARK_GRAY, DARK_GREEN, DARK_PURPLE, DARK_RED, GOLD, GRAY, GREEN, LIGHT_PURPLE, LOGO, RED, WHITE, YELLOW } from "../../utils/Constants"; +import { + AQUA, + BLUE, + DARK_AQUA, + DARK_GRAY, + DARK_GREEN, + DARK_PURPLE, + DARK_RED, + GOLD, + GRAY, + GREEN, + LIGHT_PURPLE, + LOGO, + RED, + WHITE, + YELLOW, +} from "../../utils/Constants"; +import Settings from "../../utils/Settings"; import { convertToTitleCase, formatNumber } from "../../utils/functions/format"; import { decode } from "../../utils/functions/misc"; -import Settings from "../../utils/Settings"; -import { getItemValue } from "./ItemPrice"; import { getAuction, getBazaar } from "./Economy"; - +import { getItemValue } from "./ItemPrice"; /** * Decodes inventory NBT into an array of items and add their value up. - * + * * @param {String} inv - NBT string of the inventory data * @param {String} type - Name of the inventory * @returns {Type[]} [total value, value String] */ function getInvValue(inv, type) { - // Check if API is on - if (inv === undefined) return [0, ` ${DARK_GRAY}- ${RED + type} API is turned off!\n`]; - - // Decode inventory NBT - let items = decode(inv); - let total = 0; - - // Loop through inventory data - for (let i = 0; i < items.func_74745_c(); i++) { - let nbt = new NBTTagCompound(items.func_150305_b(i)); - total += getItemValue(nbt, false); - } - - // Return value and message - const color = total === 0 ? RED : GREEN; - return [total, ` ${DARK_GRAY}- ${AQUA + type} Value: ${color + formatNumber(total)}\n`]; + // Check if API is on + if (inv === undefined) return [0, ` ${DARK_GRAY}- ${RED + type} API is turned off!\n`]; + + // Decode inventory NBT + let items = decode(inv); + let total = 0; + + // Loop through inventory data + for (let i = 0; i < items.func_74745_c(); i++) { + let nbt = new NBTTagCompound(items.func_150305_b(i)); + total += getItemValue(nbt, false); + } + + // Return value and message + const color = total === 0 ? RED : GREEN; + return [total, ` ${DARK_GRAY}- ${AQUA + type} Value: ${color + formatNumber(total)}\n`]; } /** * Adds up values in a specific category to print out a message of total value with a hover value. Also returns total value. - * + * * @param {String} name - Name of the container * @param {Type[]} values - Array of the value totals and strings * @param {String} extra - Any extra details to add to the bottom of message hover value. * @returns {Number} - Total value of all containers added together. */ -function setInv(name, values, extra=undefined) { - let value = 0; - let msg = `${DARK_AQUA + name} Value:\n`; - - // Add up category values - if (values.length === 0) msg += ` ${DARK_GRAY}- ${RED}${name} API is turned off!`; - for (let i = 0; i < values.length; i++) { - value += values[i][0]; - if (i < 48) msg += values[i][1]; - else if (i === 48) msg += `${GRAY}and ${values.length - 48} more!`; - } - if (extra !== undefined) msg += extra; - - new TextComponent(` ${DARK_GRAY}- ${AQUA + name} Value: ${GREEN + formatNumber(value)}`).setHoverValue(msg.trim()).chat(); - values.length = 0; - return value; +function setInv(name, values, extra = undefined) { + let value = 0; + let msg = `${DARK_AQUA + name} Value:\n`; + + // Add up category values + if (values.length === 0) msg += ` ${DARK_GRAY}- ${RED}${name} API is turned off!`; + for (let i = 0; i < values.length; i++) { + value += values[i][0]; + if (i < 48) msg += values[i][1]; + else if (i === 48) msg += `${GRAY}and ${values.length - 48} more!`; + } + if (extra !== undefined) msg += extra; + + new TextComponent(` ${DARK_GRAY}- ${AQUA + name} Value: ${GREEN + formatNumber(value)}`) + .setHoverValue(msg.trim()) + .chat(); + values.length = 0; + return value; } let networthIDs = [3745]; /** * Fetches the networth of a player's Hypixel SkyBlock profile. - * + * * @param {String} username - Minecraft username to fetch networth for. * @param {String} fruit - Cute name (fruit) of the SkyBlock profile. */ export function getNetworth(username, fruit) { - // Get UUID of entered username - new Message(`${LOGO + YELLOW}Fetching API data...`).setChatLineId(3745).chat(); - axios.get(`https://sky.shiiyu.moe/api/v2/profile/${username}`).then(response => { - ChatLib.clearChat(networthIDs); - networthIDs = [3745]; - let networthID = 3746; - - // Check if user exists - if (response.data.error !== undefined) { - networthIDs.push(networthID); - new Message(`${LOGO + RED}Couldn't find any profile with name ${username}...`).setChatLineId(networthID++).chat(); - return; - } - - // Ask for desired profile - const profiles = response.data.profiles; - if (fruit === undefined) { - networthIDs.push(networthID); - new Message(`\n${LOGO + DARK_AQUA}Please select desired profile:`).setChatLineId(networthID++).chat(); - - let i = 1; - Object.keys(profiles).forEach(key => { - fruit = profiles[key].cute_name; - networthIDs.push(networthID); - new Message(` ${GRAY + (i++)}. `, new TextComponent((profiles[key].current ? GREEN : AQUA) + fruit) - .setClickAction("run_command") - .setClickValue(`/va nw ${username} ${fruit}`) - .setHoverValue(`${YELLOW}Click to calculate networth for ${fruit} profile.`) - ).setChatLineId(networthID++).chat(); - }); - return; - } - - // Check for correct profile and API - const selected = Object.keys(profiles).find(key => profiles[key].cute_name.toLowerCase() === fruit.toLowerCase()); - const player = profiles[selected]?.raw; - if (player === undefined) { - ChatLib.chat(`${LOGO + RED + fruit} profile was not found!`); - return; - } - const inv = player.inventory; - if (inv === undefined) { - ChatLib.chat(`${LOGO + RED + username}'s inventory API is turned off!`); - return; - } - - // Otherwise calculate networth for inputted profile - const auction = getAuction(); - const bazaar = getBazaar(); - ChatLib.chat(`\n${LOGO + DARK_AQUA}${username}'s ${fruit} Networth:\n`); - let total = 0; - let invValues = []; - - // Inventory value - invValues.push(getInvValue(inv.inv_contents?.data, "Inventory")); - invValues.push(getInvValue(inv.inv_armor?.data, "Armor")); - invValues.push(getInvValue(inv.equipment_contents?.data, "Equipment")); - total += setInv("Player", invValues); - - // Currency value - const currencies = player.currencies; - invValues.push([currencies?.coin_purse, ` ${DARK_GRAY}- ${AQUA}Purse Value: ${GREEN + formatNumber(currencies?.coin_purse)}\n`]); - - if (currencies?.bank === undefined) invValues.push([0, ` ${DARK_GRAY}- ${RED}Bank API is turned off!\n`]); - else invValues.push([currencies?.bank, ` ${DARK_GRAY}- ${AQUA}Bank Value: ${GREEN + formatNumber(currencies?.bank)}\n`]); - - const essences = currencies?.essence; - let essenceValue = 0; - if (essences !== undefined) Object.keys(essences).forEach(essence => { - essenceValue += bazaar["ESSENCE_" + essence][Settings.priceType] * essences[essence].current; + // Get UUID of entered username + new Message(`${LOGO + YELLOW}Fetching API data...`).setChatLineId(3745).chat(); + axios + .get(`https://sky.shiiyu.moe/api/v2/profile/${username}`) + .then((response) => { + ChatLib.clearChat(networthIDs); + networthIDs = [3745]; + let networthID = 3746; + + // Check if user exists + if (response.data.error !== undefined) { + networthIDs.push(networthID); + new Message(`${LOGO + RED}Couldn't find any profile with name ${username}...`) + .setChatLineId(networthID++) + .chat(); + return; + } + + // Ask for desired profile + const profiles = response.data.profiles; + if (fruit === undefined) { + networthIDs.push(networthID); + new Message(`\n${LOGO + DARK_AQUA}Please select desired profile:`).setChatLineId(networthID++).chat(); + + let i = 1; + Object.keys(profiles).forEach((key) => { + fruit = profiles[key].cute_name; + networthIDs.push(networthID); + new Message( + ` ${GRAY + i++}. `, + new TextComponent((profiles[key].current ? GREEN : AQUA) + fruit) + .setClickAction("run_command") + .setClickValue(`/va nw ${username} ${fruit}`) + .setHoverValue(`${YELLOW}Click to calculate networth for ${fruit} profile.`) + ) + .setChatLineId(networthID++) + .chat(); }); - invValues.push([essenceValue, ` ${DARK_GRAY}- ${AQUA}Essence Value: ${GREEN + formatNumber(essenceValue)}\n`]); - - total += setInv("Currency", invValues); - - // Storage value - invValues.push(getInvValue(inv.wardrobe_contents?.data, "Wardrobe")); - invValues.push(getInvValue(inv.ender_chest_contents?.data, "Ender Chest")); - invValues.push(getInvValue(inv.personal_vault_contents?.data, "Vault")); - total += setInv("Storage", invValues); - - // Backpack values - const backpacks = inv.backpack_contents; - const packs = backpacks === undefined ? 0 : Object.keys(backpacks).length; - - for (let i = 0; i < packs; i++) { - let backpack = backpacks[i.toString()]; - invValues.push(getInvValue(backpack?.data, `Backpack ${i + 1}`)); - } - invValues.sort((a, b) => b[0] - a[0]); - total += setInv("Backpack", invValues); - - // Bag values - const bags = inv.bag_contents; - if (bags !== undefined) Object.keys(bags).forEach(bag => invValues.push(getInvValue(bags[bag]?.data, convertToTitleCase(bag)))); - const sacks = inv.sacks_counts; - let sacksValue = 0; - if (sacks !== undefined) Object.keys(sacks).forEach(product => sacksValue += (bazaar[product]?.[Settings.priceType] ?? 0) * sacks[product]); - invValues.push([sacksValue, ` ${DARK_GRAY}- ${AQUA}Sacks Value: ${GREEN + formatNumber(sacksValue)}`]); - total += setInv("Bag", invValues); - - // Pets values - const pets = player.pets_data.pets; - let mia = 0; - pets.forEach(pet => { - const tier = pet.tier; - const petName = `${tier}_${pet.type}`; - let lbin = auction[petName]?.lbin ?? 0; - const color = tier === "MYTHIC" ? LIGHT_PURPLE : - tier === "LEGENDARY" ? GOLD : - tier === "EPIC" ? DARK_PURPLE : - tier === "RARE" ? BLUE : - tier === "UNCOMMON" ? GREEN : WHITE; - if (pet.skin !== null) { - const skinValue = auction["PET_SKIN_" + pet.skin]?.lbin ?? 0; - if (skinValue === 0) mia++; - lbin += skinValue; - } - invValues.push([lbin, `${color + convertToTitleCase(petName) + DARK_GRAY}: ${GREEN + formatNumber(lbin)}\n`]); + return; + } + + // Check for correct profile and API + const selected = Object.keys(profiles).find( + (key) => profiles[key].cute_name.toLowerCase() === fruit.toLowerCase() + ); + const player = profiles[selected]?.raw; + if (player === undefined) { + ChatLib.chat(`${LOGO + RED + fruit} profile was not found!`); + return; + } + const inv = player.inventory; + if (inv === undefined) { + ChatLib.chat(`${LOGO + RED + username}'s inventory API is turned off!`); + return; + } + + // Otherwise calculate networth for inputted profile + const auction = getAuction(); + const bazaar = getBazaar(); + ChatLib.chat(`\n${LOGO + DARK_AQUA}${username}'s ${fruit} Networth:\n`); + let total = 0; + let invValues = []; + + // Inventory value + invValues.push(getInvValue(inv.inv_contents?.data, "Inventory")); + invValues.push(getInvValue(inv.inv_armor?.data, "Armor")); + invValues.push(getInvValue(inv.equipment_contents?.data, "Equipment")); + total += setInv("Player", invValues); + + // Currency value + const currencies = player.currencies; + invValues.push([ + currencies?.coin_purse, + ` ${DARK_GRAY}- ${AQUA}Purse Value: ${GREEN + formatNumber(currencies?.coin_purse)}\n`, + ]); + + if (currencies?.bank === undefined) invValues.push([0, ` ${DARK_GRAY}- ${RED}Bank API is turned off!\n`]); + else + invValues.push([ + currencies?.bank, + ` ${DARK_GRAY}- ${AQUA}Bank Value: ${GREEN + formatNumber(currencies?.bank)}\n`, + ]); + + const essences = currencies?.essence; + let essenceValue = 0; + if (essences !== undefined) + Object.keys(essences).forEach((essence) => { + essenceValue += bazaar["ESSENCE_" + essence][Settings.priceType] * essences[essence].current; }); - invValues.sort((a, b) => b[0] - a[0]); - total += setInv("Pet", invValues, mia === 0 ? undefined : `\n${DARK_GRAY}also ${mia} unaccounted skins...`); - - // Total - ChatLib.chat(`${DARK_GRAY}Hover over values to see breakdown.`); - ChatLib.chat(`\n${LOGO + DARK_AQUA}Total Networth: ${DARK_GREEN + formatNumber(total)}`); - }).catch(err => ChatLib.chat(LOGO + DARK_RED + (err.cause ?? err))); + invValues.push([essenceValue, ` ${DARK_GRAY}- ${AQUA}Essence Value: ${GREEN + formatNumber(essenceValue)}\n`]); + + total += setInv("Currency", invValues); + + // Storage value + invValues.push(getInvValue(inv.wardrobe_contents?.data, "Wardrobe")); + invValues.push(getInvValue(inv.ender_chest_contents?.data, "Ender Chest")); + invValues.push(getInvValue(inv.personal_vault_contents?.data, "Vault")); + total += setInv("Storage", invValues); + + // Backpack values + const backpacks = inv.backpack_contents; + const packs = backpacks === undefined ? 0 : Object.keys(backpacks).length; + + for (let i = 0; i < packs; i++) { + let backpack = backpacks[i.toString()]; + invValues.push(getInvValue(backpack?.data, `Backpack ${i + 1}`)); + } + invValues.sort((a, b) => b[0] - a[0]); + total += setInv("Backpack", invValues); + + // Bag values + const bags = inv.bag_contents; + if (bags !== undefined) + Object.keys(bags).forEach((bag) => invValues.push(getInvValue(bags[bag]?.data, convertToTitleCase(bag)))); + const sacks = inv.sacks_counts; + let sacksValue = 0; + if (sacks !== undefined) + Object.keys(sacks).forEach( + (product) => (sacksValue += (bazaar[product]?.[Settings.priceType] ?? 0) * sacks[product]) + ); + invValues.push([sacksValue, ` ${DARK_GRAY}- ${AQUA}Sacks Value: ${GREEN + formatNumber(sacksValue)}`]); + total += setInv("Bag", invValues); + + // Pets values + const pets = player.pets_data.pets; + let mia = 0; + pets.forEach((pet) => { + const tier = pet.tier; + const petName = `${tier}_${pet.type}`; + let lbin = auction[petName]?.lbin ?? 0; + const color = + tier === "MYTHIC" + ? LIGHT_PURPLE + : tier === "LEGENDARY" + ? GOLD + : tier === "EPIC" + ? DARK_PURPLE + : tier === "RARE" + ? BLUE + : tier === "UNCOMMON" + ? GREEN + : WHITE; + if (pet.skin !== null) { + const skinValue = auction["PET_SKIN_" + pet.skin]?.lbin ?? 0; + if (skinValue === 0) mia++; + lbin += skinValue; + } + invValues.push([lbin, `${color + convertToTitleCase(petName) + DARK_GRAY}: ${GREEN + formatNumber(lbin)}\n`]); + }); + invValues.sort((a, b) => b[0] - a[0]); + total += setInv("Pet", invValues, mia === 0 ? undefined : `\n${DARK_GRAY}also ${mia} unaccounted skins...`); + + // Total + ChatLib.chat(`${DARK_GRAY}Hover over values to see breakdown.`); + ChatLib.chat(`\n${LOGO + DARK_AQUA}Total Networth: ${DARK_GREEN + formatNumber(total)}`); + }) + .catch((err) => ChatLib.chat(LOGO + DARK_RED + (err.cause ?? err))); } diff --git a/features/economy/TradeValue.js b/features/economy/TradeValue.js index ef00a66..334fd04 100644 --- a/features/economy/TradeValue.js +++ b/features/economy/TradeValue.js @@ -1,49 +1,49 @@ import { BOLD, DARK_GREEN, DARK_RED, GOLD, GREEN, RED } from "../../utils/Constants"; -import { formatNumber } from "../../utils/functions/format"; -import { Overlay } from "../../utils/Overlay"; import { data } from "../../utils/Data"; +import { Overlay } from "../../utils/Overlay"; +import { formatNumber } from "../../utils/functions/format"; import { getItemValue } from "./ItemPrice"; - -const tradeExample = -`${DARK_RED + BOLD}Giving: ${RED}Nah +const tradeExample = `${DARK_RED + BOLD}Giving: ${RED}Nah ${DARK_GREEN + BOLD}Receiving: ${GREEN}I'd ${GOLD + BOLD}Profit: ${GREEN}Win`; const tradeOverlay = new Overlay("tradeValue", data.TVL, "moveTrade", tradeExample, ["all"], "guiRender"); tradeOverlay.setMessage(""); const updateTrade = register("step", () => { - const container = Player.getContainer(); - let giving = 0; - let receiving = 0; - let index = 0; + const container = Player.getContainer(); + let giving = 0; + let receiving = 0; + let index = 0; - for (let i = 0; i < 4; i++) { - for (let j = 0; j < 4; j++) { - giving += getItemValue(container.getStackInSlot(index)); - receiving += getItemValue(container.getStackInSlot(index + 4)); - index += 1; - } - index += 5; + for (let i = 0; i < 4; i++) { + for (let j = 0; j < 4; j++) { + giving += getItemValue(container.getStackInSlot(index)); + receiving += getItemValue(container.getStackInSlot(index + 4)); + index += 1; } + index += 5; + } - const profit = receiving - giving; - tradeOverlay.setMessage(`${DARK_RED + BOLD}Giving: ${RED + formatNumber(giving)} + const profit = receiving - giving; + tradeOverlay.setMessage(`${DARK_RED + BOLD}Giving: ${RED + formatNumber(giving)} ${DARK_GREEN + BOLD}Receiving: ${GREEN + formatNumber(receiving)} ${GOLD + BOLD}Profit: ${(profit > 0 ? GREEN : RED) + formatNumber(profit)}`); -}).setFps(1).unregister(); +}) + .setFps(1) + .unregister(); const setTrade = register("guiClosed", () => { - updateTrade.unregister(); - tradeOverlay.setMessage(""); + updateTrade.unregister(); + tradeOverlay.setMessage(""); }).unregister(); register("guiOpened", () => { - Client.scheduleTask(3, () => { - const container = Player.getContainer(); - if (container.getName().startsWith("You ")) { - updateTrade.register(); - setTrade.register(); - } - }); + Client.scheduleTask(3, () => { + const container = Player.getContainer(); + if (container.getName().startsWith("You ")) { + updateTrade.register(); + setTrade.register(); + } + }); }); diff --git a/features/event/BingoCard.js b/features/event/BingoCard.js index 7c06a1a..c8ddf27 100644 --- a/features/event/BingoCard.js +++ b/features/event/BingoCard.js @@ -1,14 +1,12 @@ -import Settings from "../../utils/Settings"; import { BOLD, DARK_GREEN, GRAY, GREEN } from "../../utils/Constants"; -import { registerWhen } from "../../utils/RegisterTils"; -import { Overlay } from "../../utils/Overlay"; import { data } from "../../utils/Data"; - +import { Overlay } from "../../utils/Overlay"; +import { registerWhen } from "../../utils/RegisterTils"; +import Settings from "../../utils/Settings"; let community = {}; let personal = {}; -const bingoExample = -`${DARK_GREEN}Community Goals +const bingoExample = `${DARK_GREEN}Community Goals ${GRAY}Bingo${GREEN}! ${GRAY}Bingo${GREEN}! ${GRAY}Bingo${GREEN}! ${GRAY}Fun time Bingo${GREEN}! ${GRAY}Earth is a fine place to be${GREEN}, ${GRAY}yo${GREEN}! @@ -33,60 +31,69 @@ const bingoOverlay = new Overlay("bingoCard", data.BCL, "moveBingo", bingoExampl * Updates bingo overlay based on current uncompleted goals. */ function updateBingo() { - let bingoMessage = ""; + let bingoMessage = ""; - // Update community bingo goals - if (Settings.bingoCard === 1 || Settings.bingoCard === 3) { - bingoMessage += `${DARK_GREEN + BOLD}Community Goals\n`; - Object.keys(community).forEach(goal => { - bingoMessage += ` ${community[goal]}\n`; - }); - } + // Update community bingo goals + if (Settings.bingoCard === 1 || Settings.bingoCard === 3) { + bingoMessage += `${DARK_GREEN + BOLD}Community Goals\n`; + Object.keys(community).forEach((goal) => { + bingoMessage += ` ${community[goal]}\n`; + }); + } - bingoMessage += '\n'; + bingoMessage += "\n"; - // Update peersonal bingo goals - if (Settings.bingoCard === 1 || Settings.bingoCard === 2) { - bingoMessage += `${DARK_GREEN + BOLD}Bingo Goals\n`; - Object.keys(personal).forEach(goal => { - bingoMessage += ` ${personal[goal]}\n`; - }); - } + // Update peersonal bingo goals + if (Settings.bingoCard === 1 || Settings.bingoCard === 2) { + bingoMessage += `${DARK_GREEN + BOLD}Bingo Goals\n`; + Object.keys(personal).forEach((goal) => { + bingoMessage += ` ${personal[goal]}\n`; + }); + } - bingoOverlay.setMessage(bingoMessage); + bingoOverlay.setMessage(bingoMessage); } -registerWhen(register("guiOpened", () => { +registerWhen( + register("guiOpened", () => { Client.scheduleTask(3, () => { - const inv = Player.getContainer(); - if (inv.getName() !== "Bingo Card") return; + const inv = Player.getContainer(); + if (inv.getName() !== "Bingo Card") return; - const items = inv.getItems(); - community = {}; - personal = {}; + const items = inv.getItems(); + community = {}; + personal = {}; - // Loop through card and seperate community/personal goals - for (let i = 0; i < 5; i++) { - for (let j = 0; j < 5; j++) { - let item = items[(i * 9) + j + 2]; - let name = item.getName(); - let lore = item.getLore(); - let completed = lore[lore.length -1] === "§5§o§aGOAL REACHED"; - let goal = item.getLore()[3]; - if (i === j) community[name] = goal; - else if (!completed) personal[name] = goal; - } + // Loop through card and seperate community/personal goals + for (let i = 0; i < 5; i++) { + for (let j = 0; j < 5; j++) { + let item = items[i * 9 + j + 2]; + let name = item.getName(); + let lore = item.getLore(); + let completed = lore[lore.length - 1] === "§5§o§aGOAL REACHED"; + let goal = item.getLore()[3]; + if (i === j) community[name] = goal; + else if (!completed) personal[name] = goal; } + } - updateBingo(); + updateBingo(); }); -}), () => Settings.bingoCard !== 0); + }), + () => Settings.bingoCard !== 0 +); -registerWhen(register("chat", (goal) => { +registerWhen( + register("chat", (goal) => { if (goal in personal) delete personal[goal]; updateBingo(); -}).setCriteria("BINGO GOAL COMPLETE! ${goal}"), () => Settings.bingoCard !== 0); + }).setCriteria("BINGO GOAL COMPLETE! ${goal}"), + () => Settings.bingoCard !== 0 +); -registerWhen(register("chat", () => { +registerWhen( + register("chat", () => { bingoOverlay.setMessage(""); -}).setCriteria("Switching to profile ${profile}..."), () => Settings.bingoCard !== 0); + }).setCriteria("Switching to profile ${profile}..."), + () => Settings.bingoCard !== 0 +); diff --git a/features/event/BurrowDetect.js b/features/event/BurrowDetect.js index 4c1ace6..c508a5b 100644 --- a/features/event/BurrowDetect.js +++ b/features/event/BurrowDetect.js @@ -1,34 +1,45 @@ +import { AMOGUS, GRAY, LOGO, WHITE } from "../../utils/Constants"; import location from "../../utils/Location"; import mayor from "../../utils/Mayor"; -import Settings from "../../utils/Settings"; -import { AMOGUS, GRAY, LOGO, WHITE } from "../../utils/Constants"; -import { playSound } from "../../utils/functions/misc"; import { registerWhen } from "../../utils/RegisterTils"; +import Settings from "../../utils/Settings"; import { delay } from "../../utils/ThreadTils"; import Waypoint from "../../utils/Waypoint"; - +import { playSound } from "../../utils/functions/misc"; /** * Variables used for burrow tracking */ let echo = false; -const burrows = new Waypoint([0, 0.5, 0]); // Green Burrows -const lastBurrows = [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]; +const burrows = new Waypoint([0, 0.5, 0]); // Green Burrows +const lastBurrows = [ + [0, 0], + [0, 0], + [0, 0], + [0, 0], + [0, 0], + [0, 0], + [0, 0], +]; /** * Track when player uses ancestral spade */ -registerWhen(register("clicked", (_, __, button, isButtonDown) => { +registerWhen( + register("clicked", (_, __, button, isButtonDown) => { if (button !== 1 || !isButtonDown || echo || !Player.getHeldItem().getName().endsWith("Ancestral Spade")) return; echo = true; - delay(() => echo = false, 3000); -}), () => location.getWorld() === "Hub" && mayor.getPerks().has("Mythological Ritual") && Settings.burrowDetect !== 0); + delay(() => (echo = false), 3000); + }), + () => location.getWorld() === "Hub" && mayor.getPerks().has("Mythological Ritual") && Settings.burrowDetect !== 0 +); /** * Detect for mytholigical burrows */ -registerWhen(register("spawnParticle", (particle, type) => { +registerWhen( + register("spawnParticle", (particle, type) => { if (echo) return; const pos = particle.getPos(); @@ -38,44 +49,51 @@ registerWhen(register("spawnParticle", (particle, type) => { const waypoints = burrows.getWaypoints(); switch (type.toString()) { - case "FOOTSTEP": - // Detect last burrow particles to prevent off by 1 waypoints - if (lastBurrows.find(lastBurrow => lastBurrow[0] === x && lastBurrow[1] === z) === undefined) { - lastBurrows.shift(); - lastBurrows.push([x, z]); - return; - } - - // Detect for new burrows (> 3 distance away from current burrows) - if (closest[1] > 3 && World.getBlockAt(x, y, x).type?.getName()) { - // Add to burrows list - burrows.push(xyz); - - // Announce burrow depending on settings - if (Settings.burrowDetect === 2 || Settings.burrowDetect === 4) playSound(AMOGUS, 100); - if (Settings.burrowDetect === 3 || Settings.burrowDetect === 4) ChatLib.chat(`${LOGO + WHITE}Burrow Detected at ${GRAY}x: ${x}, y: ${y}, z: ${z}!`); - } - break; - case ("CRIT_MAGIC"): - if (closest[1] < 3) waypoints[waypoints.indexOf(closest[0])][0] = `§aStart`; - break; - case ("CRIT"): - if (closest[1] < 3) waypoints[waypoints.indexOf(closest[0])][0] = `§cMob`; - break; + case "FOOTSTEP": + // Detect last burrow particles to prevent off by 1 waypoints + if (lastBurrows.find((lastBurrow) => lastBurrow[0] === x && lastBurrow[1] === z) === undefined) { + lastBurrows.shift(); + lastBurrows.push([x, z]); + return; + } + + // Detect for new burrows (> 3 distance away from current burrows) + if (closest[1] > 3 && World.getBlockAt(x, y, x).type?.getName()) { + // Add to burrows list + burrows.push(xyz); + + // Announce burrow depending on settings + if (Settings.burrowDetect === 2 || Settings.burrowDetect === 4) playSound(AMOGUS, 100); + if (Settings.burrowDetect === 3 || Settings.burrowDetect === 4) + ChatLib.chat(`${LOGO + WHITE}Burrow Detected at ${GRAY}x: ${x}, y: ${y}, z: ${z}!`); + } + break; + case "CRIT_MAGIC": + if (closest[1] < 3) waypoints[waypoints.indexOf(closest[0])][0] = `§aStart`; + break; + case "CRIT": + if (closest[1] < 3) waypoints[waypoints.indexOf(closest[0])][0] = `§cMob`; + break; } -}), () => location.getWorld() === "Hub" && mayor.getPerks().has("Mythological Ritual") && Settings.burrowDetect !== 0); + }), + () => location.getWorld() === "Hub" && mayor.getPerks().has("Mythological Ritual") && Settings.burrowDetect !== 0 +); /** * Events to remove burrows from list */ -registerWhen(register("chat", () => { +registerWhen( + register("chat", () => { const closest = burrows.getClosest(["Player", Player.getX(), Player.getY(), Player.getZ()]); const waypoints = burrows.getWaypoints(); if (closest !== undefined) Client.scheduleTask(2, () => waypoints.splice(waypoints.indexOf(closest[0]), 1)); -}).setCriteria("You ${completed} Griffin ${burrow}! (${x}/4)"), -() => location.getWorld() === "Hub" && mayor.getPerks().has("Mythological Ritual") && Settings.burrowDetect !== 0); + }).setCriteria("You ${completed} Griffin ${burrow}! (${x}/4)"), + () => location.getWorld() === "Hub" && mayor.getPerks().has("Mythological Ritual") && Settings.burrowDetect !== 0 +); -registerWhen(register("chat", () => { +registerWhen( + register("chat", () => { burrows.clear(); -}).setCriteria(" ☠ You ${died}."), -() => location.getWorld() === "Hub" && mayor.getPerks().has("Mythological Ritual") && Settings.burrowDetect !== 0); + }).setCriteria(" ☠ You ${died}."), + () => location.getWorld() === "Hub" && mayor.getPerks().has("Mythological Ritual") && Settings.burrowDetect !== 0 +); diff --git a/features/event/CalendarTime.js b/features/event/CalendarTime.js index e546ce7..426db1e 100644 --- a/features/event/CalendarTime.js +++ b/features/event/CalendarTime.js @@ -1,101 +1,114 @@ -import Settings from "../../utils/Settings"; import { YELLOW } from "../../utils/Constants"; -import { unformatTime } from "../../utils/functions/format"; import { registerWhen } from "../../utils/RegisterTils"; - +import Settings from "../../utils/Settings"; +import { unformatTime } from "../../utils/functions/format"; const tooltip = register("preItemRender", (_, __, slot) => { - const item = Player.getContainer().getItems()[slot.getSlotIndex()]; - if (item === null) return; - const lore = item.getLore().join('\n').split('\n').slice(1).map(line => line.substring(4)); - const name = Player.getContainer().getName(); + const item = Player.getContainer().getItems()[slot.getSlotIndex()]; + if (item === null) return; + const lore = item + .getLore() + .join("\n") + .split("\n") + .slice(1) + .map((line) => line.substring(4)); + const name = Player.getContainer().getName(); + + // Calendar day date + if (item.getName().startsWith("§aDay") && !item.getName().endsWith("]")) { + const container = name.split(" "); + const diff = container[0] === "Early" ? -1 : container[0] === "Late" ? 1 : 0; + const month = + 3 * (["Spring", "Summer", "Autumn", "Winter"].indexOf(container[0 + Math.abs(diff)].replace(/,/g, "")) + 1) + + diff - + 2; + const day = item.getName().split(" ")[1] - 1; - // Calendar day date - if (item.getName().startsWith("§aDay") && !item.getName().endsWith("]")) { - const container = name.split(' '); - const diff = container[0] === "Early" ? -1 : - container[0] === "Late" ? 1 : 0; - const month = 3 * (["Spring", "Summer", "Autumn", "Winter"].indexOf(container[0 + Math.abs(diff)].replace(/,/g, '')) + 1) + diff - 2; - const day = item.getName().split(' ')[1] - 1; + const until = month * 37_200 + day * 1_200 - ((new Date().getTime() / 1_000 - 107_704) % 446_400); + const start = new Date(Date.now() + until * 1_000); + const date = start.toLocaleDateString(); + const time = start.toLocaleTimeString(); - const until = (month * 37_200 + day * 1_200) - (new Date().getTime() / 1_000 - 107_704) % 446_400; - const start = new Date(Date.now() + until * 1_000); + item.setName(`§aDay ${day + 1} §7[${YELLOW + date}, ${time.substring(0, time.length - 3).trim()}§7]`); + } else if (name === "Calendar and Events") { + // Calendar event date + let startDate = Date.now(); + for (let i = 0; i < lore.length + 1; i++) { + if (lore[i]?.startsWith("§7Starts in:") && !lore[i + 1]?.startsWith("§7Start Date")) { + startDate = unformatTime(lore[i].removeFormatting()) * 1_000 + Date.now(); + const start = new Date(Math.round(startDate / 60_000) * 60_000); const date = start.toLocaleDateString(); const time = start.toLocaleTimeString(); + lore.splice(i + 1, 0, `§7Start Date: ${YELLOW + date}, ${time.substring(0, time.length - 3)}`); + } else if (lore[i]?.startsWith("§7Event lasts for") && !lore[i + 1]?.startsWith("§7End Date")) { + const endDate = unformatTime(lore[i].removeFormatting()) * 1_000 + startDate; + const end = new Date(Math.round(endDate / 60_000) * 60_000); + const date = end.toLocaleDateString(); + const time = end.toLocaleTimeString(); + lore.splice(i + 1, 0, `§7End Date: ${YELLOW + date}, ${time.substring(0, time.length - 3)}`); + } + } + } else if (name === "SkyBlock Menu" && item.getName().startsWith("§aCalendar")) { + for (let i = 0; i < lore.length + 1; i++) { + if (lore[i]?.startsWith("§7Starting in:") && !lore[i + 1]?.startsWith("§7Start Date")) { + const startDate = unformatTime(lore[i].removeFormatting()) * 1_000 + Date.now(); + const start = new Date(Math.round(startDate / 60_000) * 60_000); + const date = start.toLocaleDateString(); + const time = start.toLocaleTimeString(); + lore.splice(i + 1, 0, `§7Start Date: ${YELLOW + date}, ${time.substring(0, time.length - 3)}`); + } else if (lore[i]?.startsWith("§7Ends in:") && !lore[i + 1]?.startsWith("§7End Date")) { + const endDate = unformatTime(lore[i].removeFormatting()) * 1_000 + Date.now(); + const end = new Date(Math.round(endDate / 60_000) * 60_000); + const date = end.toLocaleDateString(); + const time = end.toLocaleTimeString(); + lore.splice(i + 1, 0, `§7End Date: ${YELLOW + date}, ${time.substring(0, time.length - 3)}`); + } + } + } else return; - item.setName(`§aDay ${day + 1} §7[${YELLOW + date}, ${time.substring(0, time.length - 3).trim()}§7]`); - } else if (name === "Calendar and Events") { // Calendar event date - let startDate = Date.now(); - for (let i = 0; i < lore.length + 1; i++) { - if (lore[i]?.startsWith("§7Starts in:") && !lore[i + 1]?.startsWith("§7Start Date")) { - startDate = unformatTime(lore[i].removeFormatting()) * 1_000 + Date.now(); - const start = new Date(Math.round(startDate / 60_000) * 60_000); - const date = start.toLocaleDateString(); - const time = start.toLocaleTimeString(); - lore.splice(i + 1, 0, `§7Start Date: ${YELLOW + date}, ${time.substring(0, time.length - 3)}`); - } else if (lore[i]?.startsWith("§7Event lasts for") && !lore[i + 1]?.startsWith("§7End Date")) { - const endDate = unformatTime(lore[i].removeFormatting()) * 1_000 + startDate; - const end = new Date(Math.round(endDate / 60_000) * 60_000); - const date = end.toLocaleDateString(); - const time = end.toLocaleTimeString(); - lore.splice(i + 1, 0, `§7End Date: ${YELLOW + date}, ${time.substring(0, time.length - 3)}`); - } - } - } else if (name === "SkyBlock Menu" && item.getName().startsWith("§aCalendar")) { - for (let i = 0; i < lore.length + 1; i++) { - if (lore[i]?.startsWith("§7Starting in:") && !lore[i + 1]?.startsWith("§7Start Date")) { - const startDate = unformatTime(lore[i].removeFormatting()) * 1_000 + Date.now(); - const start = new Date(Math.round(startDate / 60_000) * 60_000); - const date = start.toLocaleDateString(); - const time = start.toLocaleTimeString(); - lore.splice(i + 1, 0, `§7Start Date: ${YELLOW + date}, ${time.substring(0, time.length - 3)}`); - } else if (lore[i]?.startsWith("§7Ends in:") && !lore[i + 1]?.startsWith("§7End Date")) { - const endDate = unformatTime(lore[i].removeFormatting()) * 1_000 + Date.now(); - const end = new Date(Math.round(endDate / 60_000) * 60_000); - const date = end.toLocaleDateString(); - const time = end.toLocaleTimeString(); - lore.splice(i + 1, 0, `§7End Date: ${YELLOW + date}, ${time.substring(0, time.length - 3)}`); - } - } - } else return; - - item.setLore(lore); + item.setLore(lore); }).unregister(); const forge = register("preItemRender", (_, __, slot) => { - const item = Player.getContainer().getItems()[slot.getSlotIndex()]; - if (item === null) return; - const lore = item.getLore().join('\n').split('\n').slice(1).map(line => line.substring(4)); + const item = Player.getContainer().getItems()[slot.getSlotIndex()]; + if (item === null) return; + const lore = item + .getLore() + .join("\n") + .split("\n") + .slice(1) + .map((line) => line.substring(4)); - for (let i = 0; i < lore.length + 1; i++) { - if (lore[i]?.startsWith("§7Duration:") && !lore[i + 1]?.startsWith("§7End Date")) { - const duration = lore[i].removeFormatting().split(' ').slice(1).join(''); - const startDate = unformatTime(duration) * 1_000 + Date.now(); - const start = new Date(Math.round(startDate / 60_000) * 60_000); - const date = start.toLocaleDateString(); - const time = start.toLocaleTimeString(); - lore.splice(i + 1, 0, `§7End Date: ${YELLOW + date}, ${time.substring(0, time.length - 3)}`); - } + for (let i = 0; i < lore.length + 1; i++) { + if (lore[i]?.startsWith("§7Duration:") && !lore[i + 1]?.startsWith("§7End Date")) { + const duration = lore[i].removeFormatting().split(" ").slice(1).join(""); + const startDate = unformatTime(duration) * 1_000 + Date.now(); + const start = new Date(Math.round(startDate / 60_000) * 60_000); + const date = start.toLocaleDateString(); + const time = start.toLocaleTimeString(); + lore.splice(i + 1, 0, `§7End Date: ${YELLOW + date}, ${time.substring(0, time.length - 3)}`); } + } - item.setLore(lore); + item.setLore(lore); }).unregister(); const close = register("guiClosed", () => { - forge.unregister(); - tooltip.unregister(); - close.unregister(); + forge.unregister(); + tooltip.unregister(); + close.unregister(); }).unregister(); -registerWhen(register("guiOpened", () => { +registerWhen( + register("guiOpened", () => { Client.scheduleTask(2, () => { - const name = Player.getContainer().getName(); - const split = Player.getContainer().getName().split(' '); - if (split[0] === "Calendar" || split[name.length - 2] === "Year" || name === "SkyBlock Menu") - tooltip.register(); - else if (name.startsWith("Refine (Slot #") || name.startsWith("Item Casting (Slot #")) - forge.register(); - else return; - close.register(); + const name = Player.getContainer().getName(); + const split = Player.getContainer().getName().split(" "); + if (split[0] === "Calendar" || split[name.length - 2] === "Year" || name === "SkyBlock Menu") tooltip.register(); + else if (name.startsWith("Refine (Slot #") || name.startsWith("Item Casting (Slot #")) forge.register(); + else return; + close.register(); }); -}), () => Settings.calendarTime); + }), + () => Settings.calendarTime +); diff --git a/features/event/ChocolateFactory.js b/features/event/ChocolateFactory.js index 8547dba..32298cb 100644 --- a/features/event/ChocolateFactory.js +++ b/features/event/ChocolateFactory.js @@ -1,80 +1,106 @@ +import { BOLD, GOLD, GRAY, GREEN, LIGHT_PURPLE, RED, WHITE, YELLOW } from "../../utils/Constants"; +import { data } from "../../utils/Data"; import Location from "../../utils/Location"; +import { Overlay } from "../../utils/Overlay"; +import { registerWhen } from "../../utils/RegisterTils"; import Settings from "../../utils/Settings"; -import { BOLD, GOLD, GRAY, GREEN, LIGHT_PURPLE, RED, WHITE, YELLOW } from "../../utils/Constants"; import { getSlotCoords } from "../../utils/functions/find"; import { formatNumber, formatTime, romanToNum, unformatNumber, unformatTime } from "../../utils/functions/format"; -import { registerWhen } from "../../utils/RegisterTils"; -import { Overlay } from "../../utils/Overlay"; -import { data } from "../../utils/Data"; - /** * Choco latte */ const updateChocolate = register("tick", () => { - if (Player?.getContainer()?.getName() !== "Chocolate Factory") return; - const items = Player.getContainer().getItems(); - const cf = data.cf; - - // Fetch the meaning of life - const chocoData = items[13]; - if (chocoData) { - cf.chocolate = parseInt(chocoData.getName().removeFormatting().replace(/\D/g, "")); - cf.production = parseFloat(chocoData.getLore().find(line => line.endsWith("§8per second"))?.removeFormatting()?.replace(/,/g, "") ?? 0); - cf.last = Math.floor(Date.now() / 1000); - - const allTime = chocoData.getLore().find(line => line.startsWith("§5§o§7All-time"))?.removeFormatting()?.split(' '); - cf.all = parseFloat(allTime?.[2]?.removeFormatting()?.replace(/,/g, "") ?? 0); - } - - // Fetch data related to prestiging - const prestigeData = items[27]?.getUnlocalizedName() === "tile.thinStainedGlass" ? items[28]?.getLore() : items[27]?.getLore(); - if (prestigeData !== undefined) { - const prestigeTotal = prestigeData.find(line => line.startsWith("§5§o§7Chocolate this Prestige"))?.removeFormatting()?.split(' '); - cf.total = parseFloat(prestigeTotal?.[prestigeTotal?.length - 1]?.replace(/,/g, "") ?? 0); - - const pestige = prestigeData.find(line => line.startsWith("§5§o§7§cRequires"))?.removeFormatting()?.split(' '); - cf.prestige = unformatNumber(pestige?.[1] ?? 0); - } - - // Fetch eggs - const eggData = items[35]?.getUnlocalizedName() === "tile.thinStainedGlass" ? items[34]?.getLore() : items[35]?.getLore(); - if (eggData !== undefined) { - const eggs = data.eggs; - const barnLine = eggData.find(line => line.startsWith("§5§o§7Your Barn:"))?.split(' ')?.[2]?.removeFormatting()?.split('/'); - eggs.total = parseInt(barnLine?.[0] ?? 0); - eggs.max = parseInt(barnLine?.[1] ?? 20); - } - - // Multiplier - const productionData = items[45]?.getLore(); - if (productionData !== undefined) { - const multiplier = productionData.find(line => line.startsWith("§5§o§7Total Multiplier:"))?.split(' ')?.[2]?.removeFormatting(); - cf.multiplier = parseFloat(multiplier ?? 1); - } - - // Time tower - const towerData = items[39]?.getLore(); - if (towerData !== undefined) { - const timeTower = data.timeTower; - timeTower.bonus = romanToNum(items[39]?.getName()?.removeFormatting()?.split(' ')?.[2]) / 10; - - const charges = towerData.find(line => line.startsWith("§5§o§7Charges:")); - timeTower.charges = parseInt(charges?.split(' ')?.[1]?.removeFormatting()?.split('/')?.[0] ?? 0); - - const chargeTime = towerData.find(line => line.startsWith("§5§o§7Next Charge:")); - timeTower.chargeTime = unformatTime(chargeTime?.split(' ')?.[2]?.removeFormatting() ?? 28_800); - - const status = towerData.find(line => line.startsWith("§5§o§7Status: §a§lACTIVE"))?.split(' ')?.[2]?.removeFormatting(); - timeTower.activeTime = status === undefined ? 0 : unformatTime(status); - } + if (Player?.getContainer()?.getName() !== "Chocolate Factory") return; + const items = Player.getContainer().getItems(); + const cf = data.cf; + + // Fetch the meaning of life + const chocoData = items[13]; + if (chocoData) { + cf.chocolate = parseInt(chocoData.getName().removeFormatting().replace(/\D/g, "")); + cf.production = parseFloat( + chocoData + .getLore() + .find((line) => line.endsWith("§8per second")) + ?.removeFormatting() + ?.replace(/,/g, "") ?? 0 + ); + cf.last = Math.floor(Date.now() / 1000); + + const allTime = chocoData + .getLore() + .find((line) => line.startsWith("§5§o§7All-time")) + ?.removeFormatting() + ?.split(" "); + cf.all = parseFloat(allTime?.[2]?.removeFormatting()?.replace(/,/g, "") ?? 0); + } + + // Fetch data related to prestiging + const prestigeData = + items[27]?.getUnlocalizedName() === "tile.thinStainedGlass" ? items[28]?.getLore() : items[27]?.getLore(); + if (prestigeData !== undefined) { + const prestigeTotal = prestigeData + .find((line) => line.startsWith("§5§o§7Chocolate this Prestige")) + ?.removeFormatting() + ?.split(" "); + cf.total = parseFloat(prestigeTotal?.[prestigeTotal?.length - 1]?.replace(/,/g, "") ?? 0); + + const pestige = prestigeData + .find((line) => line.startsWith("§5§o§7§cRequires")) + ?.removeFormatting() + ?.split(" "); + cf.prestige = unformatNumber(pestige?.[1] ?? 0); + } + + // Fetch eggs + const eggData = + items[35]?.getUnlocalizedName() === "tile.thinStainedGlass" ? items[34]?.getLore() : items[35]?.getLore(); + if (eggData !== undefined) { + const eggs = data.eggs; + const barnLine = eggData + .find((line) => line.startsWith("§5§o§7Your Barn:")) + ?.split(" ")?.[2] + ?.removeFormatting() + ?.split("/"); + eggs.total = parseInt(barnLine?.[0] ?? 0); + eggs.max = parseInt(barnLine?.[1] ?? 20); + } + + // Multiplier + const productionData = items[45]?.getLore(); + if (productionData !== undefined) { + const multiplier = productionData + .find((line) => line.startsWith("§5§o§7Total Multiplier:")) + ?.split(" ")?.[2] + ?.removeFormatting(); + cf.multiplier = parseFloat(multiplier ?? 1); + } + + // Time tower + const towerData = items[39]?.getLore(); + if (towerData !== undefined) { + const timeTower = data.timeTower; + timeTower.bonus = romanToNum(items[39]?.getName()?.removeFormatting()?.split(" ")?.[2]) / 10; + + const charges = towerData.find((line) => line.startsWith("§5§o§7Charges:")); + timeTower.charges = parseInt(charges?.split(" ")?.[1]?.removeFormatting()?.split("/")?.[0] ?? 0); + + const chargeTime = towerData.find((line) => line.startsWith("§5§o§7Next Charge:")); + timeTower.chargeTime = unformatTime(chargeTime?.split(" ")?.[2]?.removeFormatting() ?? 28_800); + + const status = towerData + .find((line) => line.startsWith("§5§o§7Status: §a§lACTIVE")) + ?.split(" ")?.[2] + ?.removeFormatting(); + timeTower.activeTime = status === undefined ? 0 : unformatTime(status); + } }).unregister(); /** * Chocolate overlay. */ -const chocoExample = -`§6§lChocolate: +const chocoExample = `§6§lChocolate: §eCurrent: §f12.72m §eProduction: §73.59k §eTotal: §f901.78m @@ -90,7 +116,8 @@ const chocoExample = §eDupes: §f0`; const chocoOverlay = new Overlay("chocoDisplay", data.CFL, "moveChoco", chocoExample); -registerWhen(register("step", () => { +registerWhen( + register("step", () => { const cf = data.cf; const eggs = data.eggs; const now = Math.floor(Date.now() / 1000); @@ -99,12 +126,17 @@ registerWhen(register("step", () => { // Time tower calc const towerData = data.timeTower; const timeLeft = towerData.activeTime - lastOpen; - const noTower = cf.production * (cf.multiplier - (towerData.activeTime > 0 ? towerData.bonus : 0)) / cf.multiplier; + const noTower = + (cf.production * (cf.multiplier - (towerData.activeTime > 0 ? towerData.bonus : 0))) / cf.multiplier; const production = timeLeft > 0 || towerData.activeTime <= 0 ? cf.production : noTower; const charges = parseInt(towerData.charges) + Math.max(0, Math.ceil((lastOpen - towerData.chargeTime) / 28_800)); - const towerStr = timeLeft > 0 ? formatTime(timeLeft) + GREEN + " ✔" : - charges > 0 ? `${Math.min(3, charges)}/3` : formatTime(Math.abs(lastOpen - towerData.chargeTime)) + RED + " ✘"; + const towerStr = + timeLeft > 0 + ? formatTime(timeLeft) + GREEN + " ✔" + : charges > 0 + ? `${Math.min(3, charges)}/3` + : formatTime(Math.abs(lastOpen - towerData.chargeTime)) + RED + " ✘"; // Chocolate calc const boostedCalc = timeLeft > 0 ? (cf.production - noTower) * timeLeft : 0; @@ -114,7 +146,7 @@ registerWhen(register("step", () => { const prestigeTime = (cf.prestige - chocoTotal) / noTower; chocoOverlay.setMessage( -`${GOLD + BOLD}Chocolate: + `${GOLD + BOLD}Chocolate: ${YELLOW}Current: ${WHITE + formatNumber(chocoCalc + cf.chocolate)} ${YELLOW}Production: ${GRAY + formatNumber(production)} ${YELLOW}Total: ${WHITE + formatNumber(chocoTotal)} @@ -130,163 +162,174 @@ ${GOLD + BOLD}Rabbits: ${YELLOW}Total: ${WHITE + eggs.total}/${eggs.max} ${YELLOW}Dupes: ${GRAY + eggs.dupe} ${YELLOW}Completion: ${WHITE + (eggs.total / 4.57).toFixed(2)}% - ${YELLOW}World: ${GRAY + Object.keys(data.eggs.found[Location.getWorld()] ?? {}).length}`); -}).setFps(1), () => Settings.chocoDisplay); - + ${YELLOW}World: ${GRAY + Object.keys(data.eggs.found[Location.getWorld()] ?? {}).length}` + ); + }).setFps(1), + () => Settings.chocoDisplay +); /** * Highlight best worker. */ -const workerLevels = [0, 0, 0, 0, 0, 0, 0]; +const workerLevels = [0, 0, 0, 0, 0, 0, 0, 0, 0]; let bestWorker = 0; let bestCost = 0; function findWorker() { - if (Player.getContainer().getName() !== "Chocolate Factory") return; - bestWorker = 0; - const cf = data.cf; - const items = Player.getContainer().getItems(); - const baseMultiplier = cf.multiplier - data.timeTower.bonus; - - // Worker calc - let maxValue = 0; - for (let i = 28; i < 35; i++) { - // Skip if not a worker - let worker = items[i]?.getLore(); - if (worker === undefined) { - Client.scheduleTask(1, findWorker); - return; - } - - let index = worker.findIndex(line => line === "§5§o§7Cost"); - if (index === -1) continue; - - // Get worker name - const name = items[i].getName(); - workerLevels[i - 28] = name.substring(0, 2) + name.split(' ')[3]?.removeFormatting()?.replace(/\[|\]/g, ''); - - // Calculate value - let cost = parseInt(worker[index + 1].removeFormatting().replace(/\D/g, "")); - let value = (i - 27) * baseMultiplier / cost; - - if (value > maxValue) { - bestWorker = i; - maxValue = value; - bestCost = cost; - } + if (Player.getContainer().getName() !== "Chocolate Factory") return; + bestWorker = 0; + const cf = data.cf; + const items = Player.getContainer().getItems(); + const baseMultiplier = cf.multiplier - data.timeTower.bonus; + + // Worker calc + let maxValue = 0; + for (let i = 28; i < 35; i++) { + // Skip if not a worker + let worker = items[i]?.getLore(); + if (worker === undefined) { + Client.scheduleTask(1, findWorker); + return; } - // Tower calc - const tower = items[39].getLore(); - if (tower === undefined) { - Client.scheduleTask(1, findWorker); - return; - } + let index = worker.findIndex((line) => line === "§5§o§7Cost"); + if (index === -1) continue; - const towerI = tower.findIndex(line => line === "§5§o§7Cost"); - if (Settings.rabbitHighlight === 1 && towerI !== -1) { - const towerCost = parseInt(tower[towerI + 1].removeFormatting().replace(/\D/g, "")); - const towerValue = cf.production / baseMultiplier * 0.0125 / towerCost; - - if (towerValue > maxValue) { - bestWorker = 39; - maxValue = towerValue; - bestCost = towerCost; - } - workerLevels[7] = LIGHT_PURPLE + romanToNum(items[39].getName().split(' ')[2]); - } + // Get worker name + let name = items[i].getName(); + let level = name.split(" ")[3]?.removeFormatting()?.replace(/\[|\]/g, ""); + workerLevels[i - 28] = name.substring(0, 2) + (isNaN(level) ? 0 : level); - // Jackrabbit calc - const jackrabbit = items[42].getLore(); - if (jackrabbit === undefined) { - Client.scheduleTask(1, findWorker); - return; - } - - const jackI = jackrabbit.findIndex(line => line === "§5§o§7Cost"); - if (Settings.rabbitHighlight !== 3 && jackI !== -1) { - const jackCost = parseInt(jackrabbit[jackI + 1].removeFormatting().replace(/\D/g, "")); - const jackValue = cf.production / baseMultiplier * 0.01 / jackCost; + // Calculate value + let cost = parseInt(worker[index + 1].removeFormatting().replace(/\D/g, "")); + let value = ((i - 27) * baseMultiplier) / cost; - if (jackValue > maxValue) { - bestWorker = 42; - bestCost = jackCost; - } - workerLevels[8] = LIGHT_PURPLE + romanToNum(items[42].getName().split(' ')[2]); + if (value > maxValue) { + bestWorker = i; + maxValue = value; + bestCost = cost; + } + } + + // Tower calc + const tower = items[39].getLore(); + if (tower === undefined) { + Client.scheduleTask(1, findWorker); + return; + } + + const towerI = tower.findIndex((line) => line === "§5§o§7Cost"); + if (Settings.rabbitHighlight === 1 && towerI !== -1) { + const towerCost = parseInt(tower[towerI + 1].removeFormatting().replace(/\D/g, "")); + const towerValue = ((cf.production / baseMultiplier) * 0.0125) / towerCost; + + if (towerValue > maxValue) { + bestWorker = 39; + maxValue = towerValue; + bestCost = towerCost; } + } + workerLevels[7] = LIGHT_PURPLE + (romanToNum(items[39].getName().split(" ")[2]) ?? 0); + + // Jackrabbit calc + const jackrabbit = items[42].getLore(); + if (jackrabbit === undefined) { + Client.scheduleTask(1, findWorker); + return; + } + + const jackI = jackrabbit.findIndex((line) => line === "§5§o§7Cost"); + if (Settings.rabbitHighlight !== 3 && jackI !== -1) { + const jackCost = parseInt(jackrabbit[jackI + 1].removeFormatting().replace(/\D/g, "")); + const jackValue = ((cf.production / baseMultiplier) * 0.01) / jackCost; + + if (jackValue > maxValue) { + bestWorker = 42; + bestCost = jackCost; + } + } + workerLevels[8] = LIGHT_PURPLE + (romanToNum(items[42].getName().split(" ")[2]) ?? 0); } const workerFind = register("chat", () => { - Client.scheduleTask(1, () => { - findWorker(); - }); -}).setCriteria("Rabbit ${rabbit} has been promoted to ${rank}!").unregister(); + Client.scheduleTask(1, () => { + findWorker(); + }); +}) + .setCriteria("Rabbit ${rabbit} has been promoted to ${rank}!") + .unregister(); const coachFind = register("chat", () => { - Client.scheduleTask(1, () => { - findWorker(); - }); -}).setCriteria("You upgraded to Coach Jackrabbit ${rank}!").unregister(); + Client.scheduleTask(1, () => { + findWorker(); + }); +}) + .setCriteria("You upgraded to Coach Jackrabbit ${rank}!") + .unregister(); const towerFind = register("chat", () => { - Client.scheduleTask(1, () => { - findWorker(); - }); -}).setCriteria("You upgraded to Time Tower ${rank}!").unregister(); + Client.scheduleTask(1, () => { + findWorker(); + }); +}) + .setCriteria("You upgraded to Time Tower ${rank}!") + .unregister(); const workerHighlight = register("guiRender", () => { - if (bestWorker === 0) return; - let [x, y] = getSlotCoords(bestWorker); - - Renderer.translate(0, 0, 100); - Renderer.drawRect(data.cf.chocolate > bestCost ? Renderer.GREEN : Renderer.RED, x, y, 16, 16); - - // Draw worker levels - const items = Player.getContainer().getItems(); - Renderer.retainTransforms(true); - Renderer.scale(0.9, 0.9); - Renderer.translate(0, 0, 275); - - // Draw worker levels - for (let i = 0; i < 7; i++) { - [x, y] = getSlotCoords(28 + i); - Renderer.drawString(workerLevels[i], x * 10/9, (y - 4) * 10/9, true); - } - - // Draw tower levels - Renderer.scale(10/9, 10/9); - [x, y] = getSlotCoords(39); - Renderer.drawString(workerLevels[7], x + 22 - Renderer.getStringWidth(workerLevels[7]), y + 12, true); - [x, y] = getSlotCoords(42); - Renderer.drawString(workerLevels[8], x + 22 - Renderer.getStringWidth(workerLevels[8]), y + 12, true); - - Renderer.retainTransforms(false); + if (bestWorker === 0) return; + let [x, y] = getSlotCoords(bestWorker); + + Renderer.translate(0, 0, 100); + Renderer.drawRect(data.cf.chocolate > bestCost ? Renderer.GREEN : Renderer.RED, x, y, 16, 16); + + // Draw worker levels + Renderer.retainTransforms(true); + Renderer.scale(0.9, 0.9); + Renderer.translate(0, 0, 275); + + // Draw worker levels + for (let i = 0; i < 7; i++) { + [x, y] = getSlotCoords(28 + i); + Renderer.drawString(workerLevels[i], (x * 10) / 9, ((y - 4) * 10) / 9, true); + } + + // Draw tower levels + Renderer.scale(10 / 9, 10 / 9); + [x, y] = getSlotCoords(39); + Renderer.drawString(workerLevels[7], x + 22 - Renderer.getStringWidth(workerLevels[7]), y + 12, true); + [x, y] = getSlotCoords(42); + Renderer.drawString(workerLevels[8], x + 22 - Renderer.getStringWidth(workerLevels[8]), y + 12, true); + + Renderer.retainTransforms(false); }).unregister(); /** * /cf controls. */ const chocomatte = register("guiClosed", () => { - chocomatte.unregister(); - updateChocolate.unregister(); - coachFind.unregister(); - towerFind.unregister(); - workerFind.unregister(); - workerHighlight.unregister(); + chocomatte.unregister(); + updateChocolate.unregister(); + coachFind.unregister(); + towerFind.unregister(); + workerFind.unregister(); + workerHighlight.unregister(); }).unregister(); -registerWhen(register("guiOpened", () => { +registerWhen( + register("guiOpened", () => { Client.scheduleTask(2, () => { - if (Player.getContainer().getName() !== "Chocolate Factory") return; - - updateChocolate.register(); - if (Settings.rabbitHighlight) { - findWorker(); - coachFind.register(); - towerFind.register(); - workerFind.register(); - workerHighlight.register(); - chocomatte.register(); - } + if (Player.getContainer().getName() !== "Chocolate Factory") return; + + updateChocolate.register(); + if (Settings.rabbitHighlight) { + findWorker(); + coachFind.register(); + towerFind.register(); + workerFind.register(); + workerHighlight.register(); + chocomatte.register(); + } }); -}), () => Settings.rabbitHighlight !== 0 || Settings.chocoDisplay); + }), + () => Settings.rabbitHighlight !== 0 || Settings.chocoDisplay +); diff --git a/features/event/GreatSpook.js b/features/event/GreatSpook.js index 9dfc492..06a248a 100644 --- a/features/event/GreatSpook.js +++ b/features/event/GreatSpook.js @@ -1,27 +1,33 @@ -import Settings from "../../utils/Settings"; import { AMOGUS, BOLD, GREEN, LOGO, RED, WHITE } from "../../utils/Constants"; -import { playSound } from "../../utils/functions/misc"; import { registerWhen } from "../../utils/RegisterTils"; +import Settings from "../../utils/Settings"; import { setTitle } from "../../utils/Title"; - +import { playSound } from "../../utils/functions/misc"; /** * Primal Fear Tracker */ -registerWhen(register("step", () => { +registerWhen( + register("step", () => { if (!World.isLoaded()) return; - if (TabList.getNames().find(name => name === "§r §r§cPrimal Fears§r§7: §r§61s§r") !== undefined) { - setTitle(`${RED + BOLD}FEAR UP`, '', 10, 50, 10, 20); - playSound(AMOGUS, 10000); + if (TabList.getNames().find((name) => name === "§r §r§cPrimal Fears§r§7: §r§61s§r") !== undefined) { + setTitle(`${RED + BOLD}FEAR UP`, "", 10, 50, 10, 20); + playSound(AMOGUS, 10000); } -}).setFps(2), () => Settings.fearAlert); + }).setFps(2), + () => Settings.fearAlert +); /** * Use eval to solve math teacher equation */ -registerWhen(register("chat", (equation, event) => { - ChatLib.chat(`${LOGO}&r&d&lQUICK MATHS! &r&7Solve: &r&e${equation + WHITE} = ${GREEN + eval(equation.replace(/[x]/g, '*'))}&r`); +registerWhen( + register("chat", (equation, event) => { + ChatLib.chat( + `${LOGO}&r&d&lQUICK MATHS! &r&7Solve: &r&e${equation + WHITE} = ${GREEN + eval(equation.replace(/[x]/g, "*"))}&r` + ); cancel(event); -}).setCriteria("QUICK MATHS! Solve: ${equation}"), () => Settings.mathSolver); - + }).setCriteria("QUICK MATHS! Solve: ${equation}"), + () => Settings.mathSolver +); diff --git a/features/event/InquisitorDetect.js b/features/event/InquisitorDetect.js index f3ac355..639f458 100644 --- a/features/event/InquisitorDetect.js +++ b/features/event/InquisitorDetect.js @@ -1,29 +1,27 @@ +import { BOLD, GOLD, GREEN, LOGO, PLAYER_CLASS, RED, RESET, WHITE } from "../../utils/Constants"; +import { data } from "../../utils/Data"; import location from "../../utils/Location"; import mayor from "../../utils/Mayor"; -import Settings from "../../utils/Settings"; -import { BOLD, GOLD, WHITE, RESET, RED, PLAYER_CLASS, GREEN, LOGO } from "../../utils/Constants"; -import { data } from "../../utils/Data"; -import { announceMob } from "../../utils/functions/misc"; import { Overlay } from "../../utils/Overlay"; import { registerWhen } from "../../utils/RegisterTils"; -import Waypoint from "../../utils/Waypoint"; +import Settings from "../../utils/Settings"; import { setTitle } from "../../utils/Title"; - +import Waypoint from "../../utils/Waypoint"; +import { announceMob } from "../../utils/functions/misc"; /** * Variables used to track and display Inquisitor counter. */ const session = { - "inqs": 0, - "burrows": 0, - "last": 0, - "average": 0, + inqs: 0, + burrows: 0, + last: 0, + average: 0, }; -const counterExample = -`${GOLD + BOLD}Total Inqs: ${RESET}Who. +const counterExample = `${GOLD + BOLD}Total Inqs: ${RESET}Who. ${GOLD + BOLD}Total Burrows: ${RESET}Let. ${GOLD + BOLD}Burrows Since: ${RESET}Him. -${GOLD + BOLD}Average Burrows: ${RESET}Cook.` +${GOLD + BOLD}Average Burrows: ${RESET}Cook.`; const counterOverlay = new Overlay("inqCounter", data.IL, "moveInq", counterExample, ["Hub"]); /** @@ -32,79 +30,89 @@ const counterOverlay = new Overlay("inqCounter", data.IL, "moveInq", counterExam * @param {Boolean} inqSpawned - True if inquisitor spawned, false otherwise. */ export function updateInqCounter(inqSpawned) { - // Overall - data.inqSession.burrows++; - data.inqSession.last++; - if (inqSpawned) { - data.inqSession.inqs++; - data.inqSession.last = 0; - } - if (data.inqSession.inqs) data.inqSession.average = Math.round(data.inqSession.burrows / data.inqSession.inqs); + // Overall + data.inqSession.burrows++; + data.inqSession.last++; + if (inqSpawned) { + data.inqSession.inqs++; + data.inqSession.last = 0; + } + if (data.inqSession.inqs) data.inqSession.average = Math.round(data.inqSession.burrows / data.inqSession.inqs); - // Session - session.burrows++; - session.last++; - if (inqSpawned) { - session.inqs++; - session.last = 0; - } - if (session.inqs) session.average = Math.round(session.burrows / session.inqs); + // Session + session.burrows++; + session.last++; + if (inqSpawned) { + session.inqs++; + session.last = 0; + } + if (session.inqs) session.average = Math.round(session.burrows / session.inqs); - // Update HUD - if (mayor.getPerks().has("Mythological Ritual")) counterOverlay.setMessage(Settings.inqCounter === 1 ? -`${GOLD + BOLD}Total Inqs: ${RESET + data.inqSession.inqs} + // Update HUD + if (mayor.getPerks().has("Mythological Ritual")) + counterOverlay.setMessage( + Settings.inqCounter === 1 + ? `${GOLD + BOLD}Total Inqs: ${RESET + data.inqSession.inqs} ${GOLD + BOLD}Total Burrows: ${RESET + data.inqSession.burrows} ${GOLD + BOLD}Burrows Since: ${RESET + data.inqSession.last} ${GOLD + BOLD}Average Burrows: ${RESET + data.inqSession.average}` -: -`${GOLD + BOLD}Total Inqs: ${RESET + session.inqs} + : `${GOLD + BOLD}Total Inqs: ${RESET + session.inqs} ${GOLD + BOLD}Total Burrows: ${RESET + session.burrows} ${GOLD + BOLD}Burrows Since: ${RESET + session.last} -${GOLD + BOLD}Average Burrows: ${RESET + session.average}`); +${GOLD + BOLD}Average Burrows: ${RESET + session.average}` + ); } /** * Command to reset the stats for the overall counter. */ register("command", () => { - data.inqSession = { - "inqs": 0, - "burrows": 0, - "last": 0, - "average": 0, - }; - if (mayor.getPerks().has("Mythological Ritual")) counterOverlay.setMessage(counterExample); - ChatLib.chat(`${LOGO + GREEN}Succesfully reset Inquisitor tracker!`); + data.inqSession = { + inqs: 0, + burrows: 0, + last: 0, + average: 0, + }; + if (mayor.getPerks().has("Mythological Ritual")) counterOverlay.setMessage(counterExample); + ChatLib.chat(`${LOGO + GREEN}Succesfully reset Inquisitor tracker!`); }).setName("resetInq"); /** * Announce inquisitor spawn on chat message appears. */ -registerWhen(register("chat", (_, mob) => { +registerWhen( + register("chat", (_, mob) => { if (mob === "Minos Inquisitor") { - announceMob(Settings.inqAlert, "Minos Inquisitor", Player.getX(), Player.getY(), Player.getZ()); - if (Settings.inqCounter !== 0) updateInqCounter(true); + announceMob(Settings.inqAlert, "Minos Inquisitor", Player.getX(), Player.getY(), Player.getZ()); + if (Settings.inqCounter !== 0) updateInqCounter(true); } else if (Settings.inqCounter !== 0) updateInqCounter(false); -}).setCriteria("${wow}! You dug out a ${mob}!"), () => location.getWorld() === "Hub" && mayor.getPerks().has("Mythological Ritual")); + }).setCriteria("${wow}! You dug out a ${mob}!"), + () => location.getWorld() === "Hub" && mayor.getPerks().has("Mythological Ritual") +); /** * Tracks world for any inquisitors near player. */ const inqWaypoints = new Waypoint([1, 0.84, 0], 2, true, true, false); -registerWhen(register("step", () => { +registerWhen( + register("step", () => { inqWaypoints.clear(); - const inquisitors = World.getAllEntitiesOfType(PLAYER_CLASS).filter(player => player.getName() === "Minos Inquisitor"); + const inquisitors = World.getAllEntitiesOfType(PLAYER_CLASS).filter( + (player) => player.getName() === "Minos Inquisitor" + ); if (inquisitors.length > 0) { - // Check if inquisitor is dead - let foundDead = false; - inquisitors.forEach(inq => { - if (data.moblist.includes("inquisitor")) inqWaypoints.push([GOLD + "Inquisitor", inq]); - if (inq.getEntity().func_110143_aJ() === 0) foundDead = true; - }); + // Check if inquisitor is dead + let foundDead = false; + inquisitors.forEach((inq) => { + if (data.moblist.includes("inquisitor")) inqWaypoints.push([GOLD + "Inquisitor", inq]); + if (inq.getEntity().func_110143_aJ() === 0) foundDead = true; + }); - // Update HUD - if (foundDead) setTitle(`${GOLD + BOLD}INQUISITOR ${RED}DEAD!`, "", 0, 50, 10, 59); - else setTitle(`${GOLD + BOLD}INQUISITOR ${WHITE}DETECTED!`, "", 0, 25, 5, 60); + // Update HUD + if (foundDead) setTitle(`${GOLD + BOLD}INQUISITOR ${RED}DEAD!`, "", 0, 50, 10, 59); + else setTitle(`${GOLD + BOLD}INQUISITOR ${WHITE}DETECTED!`, "", 0, 25, 5, 60); } -}).setFps(2), () => location.getWorld() === "Hub" && Settings.detectInq && mayor.getPerks().has("Mythological Ritual")); + }).setFps(2), + () => location.getWorld() === "Hub" && Settings.detectInq && mayor.getPerks().has("Mythological Ritual") +); diff --git a/features/event/MythRitual.js b/features/event/MythRitual.js index d69f3e1..cdadc89 100644 --- a/features/event/MythRitual.js +++ b/features/event/MythRitual.js @@ -1,26 +1,27 @@ -import Settings from "../../utils/Settings"; -import mayor from "../../utils/Mayor"; -import location from "../../utils/Location"; import { GREEN, LOGO } from "../../utils/Constants"; -import { getClosest } from "../../utils/functions/find"; +import { data } from "../../utils/Data"; +import location from "../../utils/Location"; +import mayor from "../../utils/Mayor"; import { registerWhen } from "../../utils/RegisterTils"; +import Settings from "../../utils/Settings"; import { delay } from "../../utils/ThreadTils"; -import { data } from "../../utils/Data"; import Waypoint from "../../utils/Waypoint"; - +import { getClosest } from "../../utils/functions/find"; /** * Key press to warp player to closest burrow. */ -const guessed = new Waypoint([1, 1, 0]); // Yellow Guess +const guessed = new Waypoint([1, 1, 0]); // Yellow Guess let warp = "player"; const dianaKey = new KeyBind("Diana Warp", data.dianaKey, "./VolcAddons.xdd"); -register("gameUnload", () => { data.dianaKey = dianaKey.getKeyCode() }).setPriority(Priority.HIGHEST); +register("gameUnload", () => { + data.dianaKey = dianaKey.getKeyCode(); +}).setPriority(Priority.HIGHEST); dianaKey.registerKeyPress(() => { - if (Settings.dianaWarp && warp !== "player") { - ChatLib.chat(`${LOGO + GREEN}Warping to "${warp}"...`); - ChatLib.command(`warp ${warp}`); - } + if (Settings.dianaWarp && warp !== "player") { + ChatLib.chat(`${LOGO + GREEN}Warping to "${warp}"...`); + ChatLib.command(`warp ${warp}`); + } }); /** @@ -28,64 +29,67 @@ dianaKey.registerKeyPress(() => { */ let warps = []; const WARPS = { - "hub": [-2.5, 70, -69.5], - "castle": [-250, 130, 45], - "da": [91.5, 75, 173.5], - "museum": [-75.5, 76, 80.5], - "crypt": [-161.5, 61, -99.5], - "wizard": [42.5, 122, 69] -} + hub: [-2.5, 70, -69.5], + castle: [-250, 130, 45], + da: [91.5, 75, 173.5], + museum: [-75.5, 76, 80.5], + crypt: [-161.5, 61, -99.5], + wizard: [42.5, 122, 69], +}; export function setWarps() { - warps = [["player", 0, 0, 0]]; + warps = [["player", 0, 0, 0]]; - data.dianalist.forEach(loc => { - if (loc in WARPS) warps.push([loc, WARPS[loc][0], WARPS[loc][1], WARPS[loc][2]]) - }); + data.dianalist.forEach((loc) => { + if (loc in WARPS) warps.push([loc, WARPS[loc][0], WARPS[loc][1], WARPS[loc][2]]); + }); } setWarps(); /** * Use linear regression to calculate a best-fit line using particle locations. - * + * * @param {Array[Number[]]} coordinates - xyz coordinates of Echo ability particles. * @param {Number} distance - Wanted distance to be calculated using best fit line. * @returns {[String, Number, Number, Number]} - [Title, X, Y, Z] */ function guessBurrow(coordinates, distance) { - const n = coordinates.length; - let sumX = 0, sumY = 0, sumZ = 0, sumXY = 0, sumXZ = 0, sumYZ = 0, sumX2 = 0, sumY2 = 0; - - for (const [x, y, z] of coordinates) { - sumX += x; - sumY += y; - sumZ += z; - sumXY += x * y; - sumXZ += x * z; - sumYZ += y * z; - sumX2 += x * x; - sumY2 += y * y; - } - - // Find the coordinate at the specified distance - const x1 = coordinates[0][0]; - const y1 = coordinates[0][1]; - const z1 = coordinates[0][2]; - - const x2 = coordinates[n - 1][0]; - const y2 = coordinates[n - 1][1]; - const z2 = coordinates[n - 1][2]; - - const guess = [ - x1 + (x2 - x1) * distance, - y1 + (y2 - y1) * distance - distance, - z1 + (z2 - z1) * distance - ]; - - // Get closest warp location - warps[0] = ["player", Player.getX(), Player.getY(), Player.getZ()]; - warp = getClosest(["Guess",...guess], warps)[0][0]; - - return [`/warp ${warp}`, ...guess]; + const n = coordinates.length; + let sumX = 0, + sumY = 0, + sumZ = 0, + sumXY = 0, + sumXZ = 0, + sumYZ = 0, + sumX2 = 0, + sumY2 = 0; + + for (const [x, y, z] of coordinates) { + sumX += x; + sumY += y; + sumZ += z; + sumXY += x * y; + sumXZ += x * z; + sumYZ += y * z; + sumX2 += x * x; + sumY2 += y * y; + } + + // Find the coordinate at the specified distance + const x1 = coordinates[0][0]; + const y1 = coordinates[0][1]; + const z1 = coordinates[0][2]; + + const x2 = coordinates[n - 1][0]; + const y2 = coordinates[n - 1][1]; + const z2 = coordinates[n - 1][2]; + + const guess = [x1 + (x2 - x1) * distance, y1 + (y2 - y1) * distance - distance, z1 + (z2 - z1) * distance]; + + // Get closest warp location + warps[0] = ["player", Player.getX(), Player.getY(), Player.getZ()]; + warp = getClosest(["Guess", ...guess], warps)[0][0]; + + return [`/warp ${warp}`, ...guess]; } /** @@ -98,18 +102,22 @@ let distance = 0; * Track when player uses ancestral spade */ let echo = false; -registerWhen(register("clicked", (_, __, button, isButtonDown) => { +registerWhen( + register("clicked", (_, __, button, isButtonDown) => { if (button !== 1 || !isButtonDown || echo || !Player.getHeldItem().getName().endsWith("Ancestral Spade")) return; echo = true; - delay(() => echo = false, 3000); + delay(() => (echo = false), 3000); path = [[Player.getX(), Player.getY(), Player.getZ()]]; -}), () => location.getWorld() === "Hub" && mayor.getPerks().has("Mythological Ritual") && Settings.dianaWaypoint); + }), + () => location.getWorld() === "Hub" && mayor.getPerks().has("Mythological Ritual") && Settings.dianaWaypoint +); /** * Use Ancestral Spade particles to guess a burrow location. */ -registerWhen(register("spawnParticle", (particle, type) => { +registerWhen( + register("spawnParticle", (particle, type) => { if (type.toString() !== "FIREWORKS_SPARK") return; // Check for distance disparity between particle spawns to ignore strays @@ -117,16 +125,22 @@ registerWhen(register("spawnParticle", (particle, type) => { const [x, y, z] = [particle.getX(), particle.getY(), particle.getZ()]; const span = Math.hypot(x - last[0], y - last[1], z - last[2]); if (span > 3) return; - + // Push to particles list and make a guess path.push([x, y, z]); guessed.clear(); guessed.push(guessBurrow(path, distance)); -}), () => location.getWorld() === "Hub" && mayor.getPerks().has("Mythological Ritual") && Settings.dianaWaypoint); + }), + () => location.getWorld() === "Hub" && mayor.getPerks().has("Mythological Ritual") && Settings.dianaWaypoint +); /** * Get distance using Echo note pitch. */ -registerWhen(register("soundPlay", (_, __, ___, pitch) => { - distance = (Math.E / pitch) ** (Math.E + (1 - 2 * pitch)) - Math.E ** (1 - pitch ** 2) + Math.E ** (0.8 - Math.E * pitch); -}).setCriteria("note.harp"), () => location.getWorld() === "Hub" && mayor.getPerks().has("Mythological Ritual") && Settings.dianaWaypoint); +registerWhen( + register("soundPlay", (_, __, ___, pitch) => { + distance = + (Math.E / pitch) ** (Math.E + (1 - 2 * pitch)) - Math.E ** (1 - pitch ** 2) + Math.E ** (0.8 - Math.E * pitch); + }).setCriteria("note.harp"), + () => location.getWorld() === "Hub" && mayor.getPerks().has("Mythological Ritual") && Settings.dianaWaypoint +); diff --git a/features/event/RabbitEggs.js b/features/event/RabbitEggs.js index d5b3d20..6c88fc0 100644 --- a/features/event/RabbitEggs.js +++ b/features/event/RabbitEggs.js @@ -1,138 +1,180 @@ +import { + AMOGUS, + BOLD, + DARK_GRAY, + GOLD, + GRAY, + GREEN, + LIGHT_PURPLE, + LOGO, + RED, + STAND_CLASS, + WHITE, + YELLOW, +} from "../../utils/Constants"; +import { data } from "../../utils/Data"; +import { Json } from "../../utils/Json"; +import { printList } from "../../utils/ListTils"; import location from "../../utils/Location"; +import { Overlay } from "../../utils/Overlay"; +import { registerWhen } from "../../utils/RegisterTils"; import Settings from "../../utils/Settings"; +import { setTitle } from "../../utils/Title"; import Waypoint from "../../utils/Waypoint"; -import { AMOGUS, BOLD, DARK_GRAY, GOLD, GRAY, GREEN, LIGHT_PURPLE, LOGO, RED, STAND_CLASS, WHITE, YELLOW } from "../../utils/Constants"; +import { getClosest } from "../../utils/functions/find"; import { convertToTitleCase, formatTime, unformatNumber } from "../../utils/functions/format"; -import { Json } from "../../utils/Json"; -import { printList } from "../../utils/ListTils"; -import { registerWhen } from "../../utils/RegisterTils"; -import { Overlay } from "../../utils/Overlay"; -import { data } from "../../utils/Data"; import { announceMob, playSound } from "../../utils/functions/misc"; -import { getClosest } from "../../utils/functions/find"; -import { setTitle } from "../../utils/Title"; - /** * Missing rabbits */ const missingRabbits = new Json("rabbits.json", true).getData(); register("guiOpened", () => { - Client.scheduleTask(1, () => { - if (!Player.getContainer().getName().endsWith("Hoppity's Collection")) return; - - const items = Player.getContainer().getItems(); - for (let i = 1; i < 5; i++) { - for (let j = 1; j < 8; j++) { - // Track rabbits with requirements - let item = items[i * 9 + j]; - let lore = item?.getLore(); - let index = lore?.findIndex(line => line.includes('Requirement')); - if (index === -1) continue; - - // Track duplicates - let dupeI = lore?.findIndex(line => line.endsWith("§7duplicate Rabbits.")); - if (dupeI !== -1) { - let dupes = unformatNumber(lore[dupeI - 1]?.removeFormatting()?.split(' ')?.[2]?.split('/')?.[0]); - if (dupes !== 0) data.eggs.dupe = dupes; - } - - // Get rabbit data - let complete = !lore[index].startsWith("§5§o§c✖"); - let name = item.getName(); - - // Get requirement - let requirement = lore[index] + '\n '; - while (lore[++index]?.length > 4) requirement += ' ' + lore[index]; - - // Update missing rabbits - if (!complete) missingRabbits[name] = requirement; - else if (missingRabbits.hasOwnProperty(name)) delete missingRabbits[name]; - } + Client.scheduleTask(1, () => { + if (!Player.getContainer().getName().endsWith("Hoppity's Collection")) return; + + const items = Player.getContainer().getItems(); + for (let i = 1; i < 5; i++) { + for (let j = 1; j < 8; j++) { + // Track rabbits with requirements + let item = items[i * 9 + j]; + let lore = item?.getLore(); + let index = lore?.findIndex((line) => line.includes("Requirement")); + if (index === -1) continue; + + // Track duplicates + let dupeI = lore?.findIndex((line) => line.endsWith("§7duplicate Rabbits.")); + if (dupeI !== -1) { + let dupes = unformatNumber(lore[dupeI - 1]?.removeFormatting()?.split(" ")?.[2]?.split("/")?.[0]); + if (dupes !== 0) data.eggs.dupe = dupes; } - }) + + // Get rabbit data + let complete = !lore[index].startsWith("§5§o§c✖"); + let name = item.getName(); + + // Get requirement + let requirement = lore[index] + "\n "; + while (lore[++index]?.length > 4) requirement += " " + lore[index]; + + // Update missing rabbits + if (!complete) missingRabbits[name] = requirement; + else if (missingRabbits.hasOwnProperty(name)) delete missingRabbits[name]; + } + } + }); }); /** * Print missing rabbits. - * + * * @param {Number} page - The page number to display. */ export function printRabbits(page, backup) { - printList(missingRabbits, "Rabbits", isNaN(page) ? backup : page, 6, false); - if (Object.keys(missingRabbits).length === 30) - ChatLib.chat(`${DARK_GRAY}Remember to go through rabbits menu to initialize tracking!`); + printList(missingRabbits, "Rabbits", isNaN(page) ? backup : page, 6, false); + if (Object.keys(missingRabbits).length === 30) + ChatLib.chat(`${DARK_GRAY}Remember to go through rabbits menu to initialize tracking!`); } - /** * Rabbit chat detection. */ register("chat", (x) => { - data.cf.chocolate += parseInt(x.replace(/,/g, '') || 0); - data.eggs.dupe++; + data.cf.chocolate += parseInt(x.replace(/,/g, "") || 0); + data.eggs.dupe++; }).setCriteria("DUPLICATE RABBIT! +${x} Chocolate"); -registerWhen(register("chat", (choco, mult) => { +registerWhen( + register("chat", (choco, mult) => { const cf = data.cf; cf.multiplier += parseFloat(mult); cf.production += parseInt(choco) * cf.multiplier; data.eggs.total++; -}).setCriteria("NEW RABBIT! +${choco} Chocolate and +${mult}x Chocolate per second!"), () => Settings.chocoDisplay); + }).setCriteria("NEW RABBIT! +${choco} Chocolate and +${mult}x Chocolate per second!"), + () => Settings.chocoDisplay +); -registerWhen(register("chat", (mult) => { +registerWhen( + register("chat", (mult) => { if (isNaN(mult)) return; data.cf.multiplier += parseFloat(mult); data.eggs.total++; -}).setCriteria("NEW RABBIT! +${mult}x Chocolate per second!"), () => Settings.chocoDisplay); + }).setCriteria("NEW RABBIT! +${mult}x Chocolate per second!"), + () => Settings.chocoDisplay +); /** * Egglocator */ const eggLocs = []; -const eggWaypoints = new Waypoint([0.25, 0.1, 0]); // Brown Eggs -const newWaypoints = new Waypoint([0.88, 0.75, 0.72]); // Rose Gold Eggs +const eggWaypoints = new Waypoint([0.25, 0.1, 0]); // Brown Eggs +const newWaypoints = new Waypoint([0.88, 0.75, 0.72]); // Rose Gold Eggs let updateUniques = false; const EGGS = { - "015adc61-0aba-3d4d-b3d1-ca47a68a154b": "Breakfast", - "55ae5624-c86b-359f-be54-e0ec7c175403": "Lunch", - "e67f7c89-3a19-3f30-ada2-43a3856e5028": "Dinner" + "015adc61-0aba-3d4d-b3d1-ca47a68a154b": "Breakfast", + "55ae5624-c86b-359f-be54-e0ec7c175403": "Lunch", + "e67f7c89-3a19-3f30-ada2-43a3856e5028": "Dinner", }; let looted = { - "Breakfast": false, - "Lunch": false, - "Dinner": false + Breakfast: false, + Lunch: false, + Dinner: false, }; let lastLooted = { - "Breakfast": 0, - "Lunch": 0, - "Dinner": 0 -} + Breakfast: 0, + Lunch: 0, + Dinner: 0, +}; // Track if egg was looted. -registerWhen(register("chat", (type) => { +registerWhen( + register("chat", (type) => { looted[type] = true; // Set last looted time if (lastLooted[type] === 0) { - const time = World.getTime() % 24_000 + 100; - const offset = type === "Breakfast" ? (time > 1_000 ? 1_000 - time : -23_000 - time) : - type === "Lunch" ? (time > 8_000 ? 8_000 - time : -16_000 - time) : - type === "Dinner" ? (time > 15_000 ? 15_000 - time : -9_000 - time) : 0; - lastLooted[type] = Date.now() + (offset * 50); + const time = (World.getTime() % 24_000) + 100; + const offset = + type === "Breakfast" + ? time > 1_000 + ? 1_000 - time + : -23_000 - time + : type === "Lunch" + ? time > 8_000 + ? 8_000 - time + : -16_000 - time + : type === "Dinner" + ? time > 15_000 + ? 15_000 - time + : -9_000 - time + : 0; + lastLooted[type] = Date.now() + offset * 50; } -}).setCriteria("You have already collected this Chocolate ${type} Egg! Try again when it respawns!"), -() => (Settings.chocoWaypoints || Settings.eggTimers) && location.getSeason() === "Spring"); + }).setCriteria("You have already collected this Chocolate ${type} Egg! Try again when it respawns!"), + () => (Settings.chocoWaypoints || Settings.eggTimers) && location.getSeason() === "Spring" +); -registerWhen(register("chat", (type) => { +registerWhen( + register("chat", (type) => { // Set looted status and last looted time looted[type] = true; - const time = World.getTime() % 24_000 + 100; - const offset = type === "Breakfast" ? (time > 1_000 ? 1_000 - time : -23_000 - time) : - type === "Lunch" ? (time > 8_000 ? 8_000 - time : -16_000 - time) : - type === "Dinner" ? (time > 15_000 ? 15_000 - time : -9_000 - time) : 0; - lastLooted[type] = Date.now() + (offset * 50) + 5_500; + const time = (World.getTime() % 24_000) + 100; + const offset = + type === "Breakfast" + ? time > 1_000 + ? 1_000 - time + : -23_000 - time + : type === "Lunch" + ? time > 8_000 + ? 8_000 - time + : -16_000 - time + : type === "Dinner" + ? time > 15_000 + ? 15_000 - time + : -9_000 - time + : 0; + lastLooted[type] = Date.now() + offset * 50 + 5_500; // Track egg location const found = data.eggs.found; @@ -142,67 +184,79 @@ registerWhen(register("chat", (type) => { if (!found.hasOwnProperty(world)) found[world] = {}; if (!found[world].hasOwnProperty(wpKey)) { - found[world][wpKey] = 1; - if (updateUniques) ChatLib.command("va eggs unique", true); + found[world][wpKey] = 1; + if (updateUniques) ChatLib.command("va eggs unique", true); } else found[world][wpKey]++; -}).setCriteria("HOPPITY'S HUNT You found a Chocolate ${type} Egg ${loc}!"), -() => (Settings.chocoWaypoints || Settings.eggTimers) && location.getSeason() === "Spring"); + }).setCriteria("HOPPITY'S HUNT You found a Chocolate ${type} Egg ${loc}!"), + () => (Settings.chocoWaypoints || Settings.eggTimers) && location.getSeason() === "Spring" +); -registerWhen(register("chat", (type) => { +registerWhen( + register("chat", (type) => { looted[type] = false; -}).setCriteria("HOPPITY'S HUNT A Chocolate ${type} Egg has appeared!"), -() => (Settings.chocoWaypoints || Settings.eggTimers) && location.getSeason() === "Spring"); + }).setCriteria("HOPPITY'S HUNT A Chocolate ${type} Egg has appeared!"), + () => (Settings.chocoWaypoints || Settings.eggTimers) && location.getSeason() === "Spring" +); -registerWhen(register("tick", () => { +registerWhen( + register("tick", () => { const time = World.getTime() % 24_000; if (Math.abs(time - 1_000) < 4 || Date.now() - lastLooted.Breakfast > 1_205_500) { - looted.Breakfast = false; - lastLooted.Breakfast = 0; + looted.Breakfast = false; + lastLooted.Breakfast = 0; } if (Math.abs(time - 8_000) < 4 || Date.now() - lastLooted.Lunch > 1_205_500) { - looted.Lunch = false; - lastLooted.Lunch = 0; + looted.Lunch = false; + lastLooted.Lunch = 0; } if (Math.abs(time - 15_000) < 4 || Date.now() - lastLooted.Dinner > 1_205_500) { - looted.Dinner = false; - lastLooted.Dinner = 0; + looted.Dinner = false; + lastLooted.Dinner = 0; } -}), () => (Settings.chocoWaypoints || Settings.eggTimers) && location.getSeason() === "Spring"); + }), + () => (Settings.chocoWaypoints || Settings.eggTimers) && location.getSeason() === "Spring" +); // ArmorStand ESP susge, UAYOR -registerWhen(register("step", () => { +registerWhen( + register("step", () => { const stands = World.getAllEntitiesOfType(STAND_CLASS); eggWaypoints.clear(); newWaypoints.clear(); const eggOld = [...eggLocs]; eggLocs.length = 0; - stands.forEach(stand => { - // Check if valid armor stand - const helmet = stand.getEntity()?.func_71124_b(4); // getEquipmentInSlot(0: Tool in Hand; 1-4: Armor) - if (helmet === null) return; - - // Check if valid egg ID - const id = helmet.func_77978_p()?.func_74775_l("SkullOwner")?.func_74779_i("Id"); // getNBT() +> getNBTTagCompound() => getString() - if (!(id in EGGS) || looted[EGGS[id]]) return; - - // Add waypoint - const wp = [EGGS[id], stand.getX(), stand.getY() + 1, stand.getZ()]; - const coords = wp.slice(1); - const dupe = data.eggs.found[location.getWorld()]?.hasOwnProperty(`${wp[1]},${wp[3]}`); - - eggLocs.push(coords); - if (dupe) eggWaypoints.push(wp); - else newWaypoints.push(wp); - - // Announce egg if new - const coordsStr = coords.toString(); - if (eggOld.find(egg => coordsStr === egg.toString()) === undefined) - ChatLib.chat(`${LOGO + YELLOW}Found a ${EGGS[id]} Egg: ${coords.map(c => Math.round(c)).join(', ')}! ${dupe ? GREEN + "✔" : RED + "✘"}`); + stands.forEach((stand) => { + // Check if valid armor stand + const helmet = stand.getEntity()?.func_71124_b(4); // getEquipmentInSlot(0: Tool in Hand; 1-4: Armor) + if (helmet === null) return; + + // Check if valid egg ID + const id = helmet.func_77978_p()?.func_74775_l("SkullOwner")?.func_74779_i("Id"); // getNBT() +> getNBTTagCompound() => getString() + if (!(id in EGGS) || looted[EGGS[id]]) return; + + // Add waypoint + const wp = [EGGS[id], stand.getX(), stand.getY() + 1, stand.getZ()]; + const coords = wp.slice(1); + const dupe = data.eggs.found[location.getWorld()]?.hasOwnProperty(`${wp[1]},${wp[3]}`); + + eggLocs.push(coords); + if (dupe) eggWaypoints.push(wp); + else newWaypoints.push(wp); + + // Announce egg if new + const coordsStr = coords.toString(); + if (eggOld.find((egg) => coordsStr === egg.toString()) === undefined) + ChatLib.chat( + `${LOGO + YELLOW}Found a ${EGGS[id]} Egg: ${coords.map((c) => Math.round(c)).join(", ")}! ${ + dupe ? GREEN + "✔" : RED + "✘" + }` + ); }); -}).setFps(1), () => Settings.chocoWaypoints); - + }).setFps(1), + () => Settings.chocoWaypoints +); /** * Announce egg location on roulette completion. @@ -211,161 +265,178 @@ let chocoType = ""; let chocoLoc = ""; const announceOnClose = register("guiClosed", () => { - Client.scheduleTask(5, () => announceMob(Settings.chocoAlert, chocoType, Player.getX(), Player.getY(), Player.getZ(), chocoLoc)); - announceOnClose.unregister(); + Client.scheduleTask(5, () => + announceMob(Settings.chocoAlert, chocoType, Player.getX(), Player.getY(), Player.getZ(), chocoLoc) + ); + announceOnClose.unregister(); }).unregister(); -registerWhen(register("chat", (type, loc) => { +registerWhen( + register("chat", (type, loc) => { chocoType = type + " Egg"; chocoLoc = convertToTitleCase(loc); announceOnClose.register(); -}).setCriteria("HOPPITY'S HUNT You found a Chocolate ${type} Egg ${loc}!"), () => Settings.chocoAlert !== 0); - + }).setCriteria("HOPPITY'S HUNT You found a Chocolate ${type} Egg ${loc}!"), + () => Settings.chocoAlert !== 0 +); /** * Egg timer overlay. - * + * * 18k = midnight * Breakfast- 7:00 am = 1_000 * Lunch- 2:00 pm = 8_000 * Dinner- 9:00 pm = 15_000 */ -const eggExample = -`${GOLD + BOLD}Egg Timers: +const eggExample = `${GOLD + BOLD}Egg Timers: ${YELLOW}Breakfast: ${WHITE}bling ${YELLOW}Lunch: ${WHITE}bang ${YELLOW}Dinner: ${WHITE}bang`; const eggOverlay = new Overlay("eggTimers", data.CGL, "moveEgg", eggExample); -registerWhen(register("step", () => { +registerWhen( + register("step", () => { const time = World.getTime() % 24_000; const breakfastTime = time > 1_000 ? 25_000 - time : 1_000 - time; const lunchTime = time > 8_000 ? 32_000 - time : 8_000 - time; const dinnerTime = time > 15_000 ? 39_000 - time : 15_000 - time; eggOverlay.setMessage( -`${GOLD + BOLD}Egg Timers: + `${GOLD + BOLD}Egg Timers: ${YELLOW}Breakfast: ${WHITE + formatTime(breakfastTime / 20)} ${looted.Breakfast ? GREEN + "✔" : RED + "✘"} ${YELLOW}Lunch: ${WHITE + formatTime(lunchTime / 20)} ${looted.Lunch ? GREEN + "✔" : RED + "✘"} - ${YELLOW}Dinner: ${WHITE + formatTime(dinnerTime / 20)} ${looted.Dinner ? GREEN + "✔" : RED + "✘"}`); -}).setFps(1), () => Settings.eggTimers && location.getSeason() === "Spring"); - -registerWhen(register("chat", (type) => { + ${YELLOW}Dinner: ${WHITE + formatTime(dinnerTime / 20)} ${looted.Dinner ? GREEN + "✔" : RED + "✘"}` + ); + }).setFps(1), + () => Settings.eggTimers && location.getSeason() === "Spring" +); + +registerWhen( + register("chat", (type) => { setTitle(`${LIGHT_PURPLE + BOLD}EGG SPAWNED!`, `${GOLD}A ${type} Egg ${GOLD}has spawned.`, 10, 50, 10, 40); -}).setCriteria("&r&d&lHOPPITY'S HUNT &r&dA &r${type} Egg &r&dhas appeared!&r"), () => Settings.eggTimers && location.getSeason() === "Spring"); - + }).setCriteria("&r&d&lHOPPITY'S HUNT &r&dA &r${type} Egg &r&dhas appeared!&r"), + () => Settings.eggTimers && location.getSeason() === "Spring" +); /** * Stray rabbit detection. */ const strayDetect = register("step", () => { - if (Player.getContainer().getName() !== "Chocolate Factory") return; - const items = Player.getContainer().getItems(); + if (Player.getContainer().getName() !== "Chocolate Factory") return; + const items = Player.getContainer().getItems(); - for (let i = 0; i < 27; i++) { - if (i === 13) continue; + for (let i = 0; i < 27; i++) { + if (i === 13) continue; - let item = items[i]; - if (item !== null && item.getRegistryName() !== "minecraft:stained_glass_pane") { - // Gold: 794465b5-3bd2-38fc-b02f-0b51d782e201 - // let skullId = item.getNBT().getCompoundTag("tag").getCompoundTag("SkullOwner").getString("Id"); - let name = item.getName(); + let item = items[i]; + if (item !== null && item.getRegistryName() !== "minecraft:stained_glass_pane") { + // Gold: 794465b5-3bd2-38fc-b02f-0b51d782e201 + // let skullId = item.getNBT().getCompoundTag("tag").getCompoundTag("SkullOwner").getString("Id"); + let name = item.getName(); - if (name.endsWith("§d§lCAUGHT!") || (!name.startsWith("§6§lGolden Rabbit") && Settings.strayAlert === 2)) return; - playSound(AMOGUS, 10_000); - } + if (name.endsWith("§d§lCAUGHT!") || (!name.startsWith("§6§lGolden Rabbit") && Settings.strayAlert === 2)) return; + playSound(AMOGUS, 10_000); } -}).setDelay(1).unregister(); + } +}) + .setDelay(1) + .unregister(); const strayClose = register("guiClosed", () => { - strayDetect.unregister(); - strayClose.unregister(); + strayDetect.unregister(); + strayClose.unregister(); }).unregister(); -registerWhen(register("guiOpened", () => { +registerWhen( + register("guiOpened", () => { Client.scheduleTask(2, () => { - if (Player.getContainer().getName() !== "Chocolate Factory") return; - strayClose.register(); - strayDetect.register(); - }) -}), () => Settings.strayAlert !== 0); - + if (Player.getContainer().getName() !== "Chocolate Factory") return; + strayClose.register(); + strayDetect.register(); + }); + }), + () => Settings.strayAlert !== 0 +); /** * Egg waypoints. */ const EGGPOINTS = new Json("eggs.json", false, false).getData(); -const eggPoints = new Waypoint([1, 1, 0]); // Yellow Eggs +const eggPoints = new Waypoint([1, 1, 0]); // Yellow Eggs /** * Update Skyblock Waypoints. - * + * * @param {String} command - add, clear, list, help. * @param {String} name - Name of the NPC or Zone. */ export function updateEggs(command, page) { - const world = location.getWorld(); - const base = EGGPOINTS[world]; - if (base === undefined) { - ChatLib.chat(`${LOGO + RED}Error: No eggs found in ${world}.`); - return; - } - - switch (command) { - case "all": - case "show": - // Show all waypoints - eggPoints.clear(); - base.forEach(wp => eggPoints.push([wp[0], ...wp.slice(2)])); - ChatLib.chat(`${LOGO + GREEN}Showing all egg waypoints.`); - break; - case "unique": - // Show all unique waypoints - eggPoints.clear(); - base.forEach(wp => { - const x = parseInt(wp[2]); - const z = parseInt(wp[4]); - if (!data.eggs.found[world]?.hasOwnProperty(`${x + (x < 0)}.5,${z + (z < 0)}.5`)) - eggPoints.push([wp[0], ...wp.slice(2)]); - }); - updateUniques = true; - ChatLib.chat(`${LOGO + GREEN}Showing all missing unique eggs.`); - break; - case "clear": - // Clear all waypoints - eggPoints.clear(); - updateUniques = false; - ChatLib.chat(`${LOGO + GREEN}Cleared egg waypoints.`); - break; - case "list": - // List all waypoints - const found = data.eggs.found[world]; - const unique = base.filter(wp => { - const x = parseInt(wp[2]); - const z = parseInt(wp[4]); - return !found.hasOwnProperty(`${x + (x < 0)}.5,${z + (z < 0)}.5`); - }); - const dupe = base.filter(wp => { - const x = parseInt(wp[2]); - const z = parseInt(wp[4]); - return found.hasOwnProperty(`${x + (x < 0)}.5,${z + (z < 0)}.5`); - }); - - const formatted = unique.map(wp => `${GOLD + wp[0]} ${RED}✘\n ${YELLOW + wp.slice(2).join(', ')} ${GRAY}(${wp[1]})`) - .concat(dupe.map(wp => `${GOLD + wp[0]} ${GREEN}✔\n ${YELLOW + wp.slice(2).join(', ')} ${GRAY}(${wp[1]})`)); - printList(formatted, "eggs", page, 6, false); - break; - case "help": - default: - if (command !== "help") ChatLib.chat(`${LOGO + RED}Error: Invalid argument "${command}"!\n`); - ChatLib.chat( -`${LOGO + GOLD + BOLD}Waypoint Commands: + const world = location.getWorld(); + const base = EGGPOINTS[world]; + if (base === undefined) { + ChatLib.chat(`${LOGO + RED}Error: No eggs found in ${world}.`); + return; + } + + switch (command) { + case "all": + case "show": + // Show all waypoints + eggPoints.clear(); + base.forEach((wp) => eggPoints.push([wp[0], ...wp.slice(2)])); + ChatLib.chat(`${LOGO + GREEN}Showing all egg waypoints.`); + break; + case "unique": + // Show all unique waypoints + eggPoints.clear(); + base.forEach((wp) => { + const x = parseInt(wp[2]); + const z = parseInt(wp[4]); + if (!data.eggs.found[world]?.hasOwnProperty(`${x + (x < 0)}.5,${z + (z < 0)}.5`)) + eggPoints.push([wp[0], ...wp.slice(2)]); + }); + updateUniques = true; + ChatLib.chat(`${LOGO + GREEN}Showing all missing unique eggs.`); + break; + case "clear": + // Clear all waypoints + eggPoints.clear(); + updateUniques = false; + ChatLib.chat(`${LOGO + GREEN}Cleared egg waypoints.`); + break; + case "list": + // List all waypoints + const found = data.eggs.found[world]; + const unique = base.filter((wp) => { + const x = parseInt(wp[2]); + const z = parseInt(wp[4]); + return !found.hasOwnProperty(`${x + (x < 0)}.5,${z + (z < 0)}.5`); + }); + const dupe = base.filter((wp) => { + const x = parseInt(wp[2]); + const z = parseInt(wp[4]); + return found.hasOwnProperty(`${x + (x < 0)}.5,${z + (z < 0)}.5`); + }); + + const formatted = unique + .map((wp) => `${GOLD + wp[0]} ${RED}✘\n ${YELLOW + wp.slice(2).join(", ")} ${GRAY}(${wp[1]})`) + .concat( + dupe.map((wp) => `${GOLD + wp[0]} ${GREEN}✔\n ${YELLOW + wp.slice(2).join(", ")} ${GRAY}(${wp[1]})`) + ); + printList(formatted, "eggs", page, 6, false); + break; + case "help": + default: + if (command !== "help") ChatLib.chat(`${LOGO + RED}Error: Invalid argument "${command}"!\n`); + ChatLib.chat( + `${LOGO + GOLD + BOLD}Waypoint Commands: ${DARK_GRAY}- ${GOLD}Base: ${YELLOW}/va [npc, zone] ${DARK_GRAY}- ${GOLD}show: ${YELLOW}Show all waypoints. ${DARK_GRAY}- ${GOLD}unique: ${YELLOW}Show all missing unique eggs. ${DARK_GRAY}- ${GOLD}clear: ${YELLOW}Delete all waypoints. ${DARK_GRAY}- ${GOLD}list: ${YELLOW}List all valid keys. - ${DARK_GRAY}- ${GOLD}help: ${YELLOW}Displays this help message.`); - break; - } + ${DARK_GRAY}- ${GOLD}help: ${YELLOW}Displays this help message.` + ); + break; + } } diff --git a/features/farming/Composter.js b/features/farming/Composter.js index ae6f7a1..16c3af2 100644 --- a/features/farming/Composter.js +++ b/features/farming/Composter.js @@ -1,19 +1,17 @@ -import location from "../../utils/Location"; -import Settings from "../../utils/Settings"; import { AQUA, BOLD, DARK_GRAY, DARK_GREEN, GREEN, LOGO, RED, WHITE } from "../../utils/Constants"; import { data } from "../../utils/Data"; -import { commafy, formatTime, romanToNum } from "../../utils/functions/format"; +import location from "../../utils/Location"; import { Overlay } from "../../utils/Overlay"; import { registerWhen } from "../../utils/RegisterTils"; -import { getBazaar } from "../economy/Economy"; +import Settings from "../../utils/Settings"; import { setTitle } from "../../utils/Title"; - +import { commafy, formatTime, romanToNum } from "../../utils/functions/format"; +import { getBazaar } from "../economy/Economy"; /** * Composter timers */ -const compostExample = -`${DARK_GREEN + BOLD}Composter: +const compostExample = `${DARK_GREEN + BOLD}Composter: ${GREEN}Empty: ${WHITE}Loading ${GREEN}Next: ${WHITE}...`; const compostOverlay = new Overlay("compostTab", data.OL, "moveCompost", compostExample, ["Garden"]); @@ -23,88 +21,118 @@ let emptyCompost = 0; * Tracks time until compost empty */ function updateCompost() { - const container = Player.getContainer(); - if (container.getName() !== "Composter") return; - const cropMeter = container.getStackInSlot(46)?.getLore(); - const fuelMeter = container.getStackInSlot(52)?.getLore(); - if (cropMeter === undefined || fuelMeter === undefined) return; - - // Composter Upgrades - const costUpgrade = data.composterUpgrades["Cost Reduction"]; - const speed = (600 / (1 + data.composterUpgrades["Composter Speed"] * 0.2)); - - // Run out calc - const crop = Object.entries(cropMeter)[1][1].removeFormatting().replace(/[\s,]/g, '').replace('/', ' ').split(' ')[0]; - const fuel = Object.entries(fuelMeter)[1][1].removeFormatting().replace(/[\s,]/g, '').replace('/', ' ').split(' ')[0]; - const noCrop = crop / (4000 * (1 - costUpgrade/100)) * speed; - const noFuel = fuel / (2000 * (1 - costUpgrade/100)) * speed; - emptyCompost = Math.min(noCrop, noFuel); + const container = Player.getContainer(); + if (container.getName() !== "Composter") return; + const cropMeter = container.getStackInSlot(46)?.getLore(); + const fuelMeter = container.getStackInSlot(52)?.getLore(); + if (cropMeter === undefined || fuelMeter === undefined) return; + + // Composter Upgrades + const costUpgrade = data.composterUpgrades["Cost Reduction"]; + const speed = 600 / (1 + data.composterUpgrades["Composter Speed"] * 0.2); + + // Run out calc + const crop = Object.entries(cropMeter)[1][1].removeFormatting().replace(/[\s,]/g, "").replace("/", " ").split(" ")[0]; + const fuel = Object.entries(fuelMeter)[1][1].removeFormatting().replace(/[\s,]/g, "").replace("/", " ").split(" ")[0]; + const noCrop = (crop / (4000 * (1 - costUpgrade / 100))) * speed; + const noFuel = (fuel / (2000 * (1 - costUpgrade / 100))) * speed; + emptyCompost = Math.min(noCrop, noFuel); } -registerWhen(register("guiOpened", () => { +registerWhen( + register("guiOpened", () => { Client.scheduleTask(1, updateCompost); -}), () => location.getWorld() === "Garden" && Settings.compostTab === 2); -registerWhen(register("guiMouseClick", () => { + }), + () => location.getWorld() === "Garden" && Settings.compostTab === 2 +); +registerWhen( + register("guiMouseClick", () => { Client.scheduleTask(1, updateCompost); -}), () => location.getWorld() === "Garden" && Settings.compostTab === 2); + }), + () => location.getWorld() === "Garden" && Settings.compostTab === 2 +); /** * Update compost overlay. */ -registerWhen(register("step", () => { +registerWhen( + register("step", () => { if (!World.isLoaded()) return; const tablist = TabList.getNames(); if (Settings.gardenTab === 1) { - if (tablist.find(tab => tab.includes("Time Left")) !== undefined) - setTitle(`${DARK_RED + BOLD} ${WHITE}COMPOSTER INACTIVE!`, "", 0, 25, 5, 3); - return; + if (tablist.find((tab) => tab.includes("Time Left")) !== undefined) + setTitle(`${DARK_RED + BOLD} ${WHITE}COMPOSTER INACTIVE!`, "", 0, 25, 5, 3); + return; } if (emptyCompost <= 0) { - // Composter Upgrades - const costUpgrade = data.composterUpgrades["Cost Reduction"]; - const speed = (600 / (1 + data.composterUpgrades["Composter Speed"] * 0.2)); - - // Run out calc - const organic = tablist.find(tab => tab.includes("Organic Matter"))?.removeFormatting() ?? "0"; - const crop = organic.replace(/\D/g, "") * (organic.includes('k') ? 1000 : 1); - const fuel = (tablist.find(tab => tab.includes("Fuel"))?.removeFormatting()?.replace(/\D/g, "") ?? 0) * 1000; - const noCrop = crop / (4000 * (1 - costUpgrade/100)) * speed; - const noFuel = fuel / (2000 * (1 - costUpgrade/100)) * speed; - - emptyCompost = Math.min(noCrop, noFuel); + // Composter Upgrades + const costUpgrade = data.composterUpgrades["Cost Reduction"]; + const speed = 600 / (1 + data.composterUpgrades["Composter Speed"] * 0.2); + + // Run out calc + const organic = tablist.find((tab) => tab.includes("Organic Matter"))?.removeFormatting() ?? "0"; + const crop = organic.replace(/\D/g, "") * (organic.includes("k") ? 1000 : 1); + const fuel = + (tablist + .find((tab) => tab.includes("Fuel")) + ?.removeFormatting() + ?.replace(/\D/g, "") ?? 0) * 1000; + const noCrop = (crop / (4000 * (1 - costUpgrade / 100))) * speed; + const noFuel = (fuel / (2000 * (1 - costUpgrade / 100))) * speed; + + emptyCompost = Math.min(noCrop, noFuel); } emptyCompost--; const message = emptyCompost <= 100 ? `${RED}Inactive` : `${WHITE + formatTime(emptyCompost)}`; - const time = (tablist.find(tab => tab.includes("Time Left"))?.removeFormatting()?.match(/(\d+)m (\d+)s|(\d+)s/) ?? 0); - const nextCompost = !time ? `${RED}Inactive` : - formatTime((time[1] ? parseInt(time[1], 10) : 0) * 60 + (time[2] ? parseInt(time[2], 10) : parseInt(time[3], 10))); + const time = + tablist + .find((tab) => tab.includes("Time Left")) + ?.removeFormatting() + ?.match(/(\d+)m (\d+)s|(\d+)s/) ?? 0; + const nextCompost = !time + ? `${RED}Inactive` + : formatTime( + (time[1] ? parseInt(time[1], 10) : 0) * 60 + (time[2] ? parseInt(time[2], 10) : parseInt(time[3], 10)) + ); compostOverlay.setMessage( -`${DARK_GREEN + BOLD}Composter: + `${DARK_GREEN + BOLD}Composter: ${GREEN}Empty: ${message} - ${GREEN}Next: ${WHITE + nextCompost}`); -}).setFps(1), () => location.getWorld() === "Garden" && Settings.gardenTab); + ${GREEN}Next: ${WHITE + nextCompost}` + ); + }).setFps(1), + () => location.getWorld() === "Garden" && Settings.gardenTab +); /** * Tracks whenever player is in the Composter Upgrades gui and saves their upgrade values. */ -registerWhen(register("guiOpened", () => { +registerWhen( + register("guiOpened", () => { Client.scheduleTask(1, () => { - // Get compsoter upgrades container - let container = Player.getContainer(); - if (container.getName() !== "Composter Upgrades") return; - - // Get composter levels - // Composter Speed => Sugar ID: 353 - // Multi Drop => Diamond Hoe ID: 293 - // Cost Reduction => Gold Ingot ID: 266 - const items = container.getItems(); - data.composterUpgrades["Composter Speed"] = romanToNum(items[container.indexOf(353)].getName().removeFormatting().split(" ").pop()); - data.composterUpgrades["Multi Drop"] = romanToNum(items[container.indexOf(293)].getName().removeFormatting().split(" ").pop()); - data.composterUpgrades["Cost Reduction"] = romanToNum(items[container.indexOf(266)].getName().removeFormatting().split(" ").pop()); + // Get compsoter upgrades container + let container = Player.getContainer(); + if (container.getName() !== "Composter Upgrades") return; + + // Get composter levels + // Composter Speed => Sugar ID: 353 + // Multi Drop => Diamond Hoe ID: 293 + // Cost Reduction => Gold Ingot ID: 266 + const items = container.getItems(); + data.composterUpgrades["Composter Speed"] = romanToNum( + items[container.indexOf(353)].getName().removeFormatting().split(" ").pop() + ); + data.composterUpgrades["Multi Drop"] = romanToNum( + items[container.indexOf(293)].getName().removeFormatting().split(" ").pop() + ); + data.composterUpgrades["Cost Reduction"] = romanToNum( + items[container.indexOf(266)].getName().removeFormatting().split(" ").pop() + ); }); -}), () => location.getWorld() === "Garden"); + }), + () => location.getWorld() === "Garden" +); /** * Calculates composting profit and cost considering composter upgrades and bazaar prices. @@ -112,42 +140,45 @@ registerWhen(register("guiOpened", () => { * @param {String[]} args - Array of composter upgrade levels. */ export function calcCompost(args) { - const bazaar = getBazaar(); - - // Upgrades - const testLevel = parseInt(args[2]); - if (isNaN(testLevel) && data.composterUpgrades["Cost Reduction"] === -1) { - ChatLib.chat(`${LOGO + RED}Please input as: ${WHITE}/va calc compost [level]!`); - ChatLib.chat(`${LOGO + DARK_GRAY}Please note that this means your composter upgrade menu has not yet been tracked!`); - return; - } - const speedUpgrade = !isNaN(testLevel) ? testLevel : data.composterUpgrades["Composter Speed"]; - const multiUpgrade = !isNaN(testLevel) ? testLevel : data.composterUpgrades["Multi Drop"]; - const costUpgrade = !isNaN(testLevel) ? testLevel : data.composterUpgrades["Cost Reduction"]; - - // Organic (4k) / Fuel (2k) Cost - // Box of Seeds give 25.6k organic - // Oil Barrel gives 10k fuel - const organicCost = bazaar["BOX_OF_SEEDS"][0] / (25600 / (4000 * (1 - costUpgrade/100))); - const fuelType = bazaar["OIL_BARREL"][0] > bazaar["VOLTA"][0] ? "Volta" : "Oil Barrel"; - const fuelCost = Math.min(bazaar["OIL_BARREL"][0], bazaar["VOLTA"][0]) / (10000 / (2000 * (1 - costUpgrade/100))); - const totalCost = Math.round(organicCost + fuelCost); - - // Profit - const compostPrice = Math.round(bazaar["COMPOST"][1] * (1 + multiUpgrade * 0.03)); // Multi Drop => +0.03% per level - const totalProfit = compostPrice - totalCost; - - // Daily Profit - const time = 600 / (1 + speedUpgrade * 0.2); - const hourlyProfit = commafy(3600 / time * totalProfit); - const dailyProfit = commafy(86400 / time * totalProfit); + const bazaar = getBazaar(); + // Upgrades + const testLevel = parseInt(args[2]); + if (isNaN(testLevel) && data.composterUpgrades["Cost Reduction"] === -1) { + ChatLib.chat(`${LOGO + RED}Please input as: ${WHITE}/va calc compost [level]!`); ChatLib.chat( -`\n${DARK_GREEN + BOLD}Composter Calculation: + `${LOGO + DARK_GRAY}Please note that this means your composter upgrade menu has not yet been tracked!` + ); + return; + } + const speedUpgrade = !isNaN(testLevel) ? testLevel : data.composterUpgrades["Composter Speed"]; + const multiUpgrade = !isNaN(testLevel) ? testLevel : data.composterUpgrades["Multi Drop"]; + const costUpgrade = !isNaN(testLevel) ? testLevel : data.composterUpgrades["Cost Reduction"]; + + // Organic (4k) / Fuel (2k) Cost + // Box of Seeds give 25.6k organic + // Oil Barrel gives 10k fuel + const organicCost = bazaar["BOX_OF_SEEDS"][0] / (25600 / (4000 * (1 - costUpgrade / 100))); + const fuelType = bazaar["OIL_BARREL"][0] > bazaar["VOLTA"][0] ? "Volta" : "Oil Barrel"; + const fuelCost = Math.min(bazaar["OIL_BARREL"][0], bazaar["VOLTA"][0]) / (10000 / (2000 * (1 - costUpgrade / 100))); + const totalCost = Math.round(organicCost + fuelCost); + + // Profit + const compostPrice = Math.round(bazaar["COMPOST"][1] * (1 + multiUpgrade * 0.03)); // Multi Drop => +0.03% per level + const totalProfit = compostPrice - totalCost; + + // Daily Profit + const time = 600 / (1 + speedUpgrade * 0.2); + const hourlyProfit = commafy((3600 / time) * totalProfit); + const dailyProfit = commafy((86400 / time) * totalProfit); + + ChatLib.chat( + `\n${DARK_GREEN + BOLD}Composter Calculation: ${AQUA}Organic Matter Cost [${WHITE}Box Of Seeds${AQUA}]: ${RED + commafy(organicCost)} ${AQUA}Fuel Cost [${WHITE + fuelType + AQUA}]: ${RED + commafy(fuelCost)} ${AQUA}Compost Profit: ${GREEN + commafy(compostPrice)} ${AQUA}Overall Profit: ${(totalProfit > 0 ? GREEN : RED) + commafy(totalProfit)}\n ${AQUA}Hourly Profit: ${(hourlyProfit > 0 ? GREEN : RED) + hourlyProfit} -${AQUA}Daily Profit: ${(dailyProfit > 0 ? GREEN : RED) + dailyProfit}\n`); +${AQUA}Daily Profit: ${(dailyProfit > 0 ? GREEN : RED) + dailyProfit}\n` + ); } diff --git a/features/farming/FarmingWebhook.js b/features/farming/FarmingWebhook.js index 279f7b1..1c82d5c 100644 --- a/features/farming/FarmingWebhook.js +++ b/features/farming/FarmingWebhook.js @@ -3,11 +3,10 @@ */ import request from "../../../requestV2"; -import Settings from "../../utils/Settings"; import location from "../../utils/Location"; -import Settings from "../../utils/Settings"; -import { formatNumber, formatTime, unformatNumber } from "../../utils/functions/format"; import { registerWhen } from "../../utils/RegisterTils"; +import Settings from "../../utils/Settings"; +import { formatNumber, unformatNumber } from "../../utils/functions/format"; import { getBazaar } from "../economy/Economy"; import { getWaifu } from "../party/PartyCommands"; diff --git a/features/farming/GardenTab.js b/features/farming/GardenTab.js index c16f846..8dc9d5a 100644 --- a/features/farming/GardenTab.js +++ b/features/farming/GardenTab.js @@ -1,17 +1,15 @@ +import { AQUA, BOLD, GREEN, RED, WHITE } from "../../utils/Constants"; +import { data } from "../../utils/Data"; import location from "../../utils/Location"; +import { Overlay } from "../../utils/Overlay"; +import { registerWhen } from "../../utils/RegisterTils"; import Settings from "../../utils/Settings"; -import { AQUA, BOLD, DARK_GREEN, DARK_RED, GREEN, RED, WHITE } from "../../utils/Constants"; import { formatTime } from "../../utils/functions/format"; -import { registerWhen } from "../../utils/RegisterTils"; -import { Overlay } from "../../utils/Overlay"; -import { data } from "../../utils/Data"; - /** * Variables used to represent and display visitors. */ -const gardenExample = -`${AQUA + BOLD}Visitors ${WHITE}(5): +const gardenExample = `${AQUA + BOLD}Visitors ${WHITE}(5): ${GREEN + BOLD} Never ${GREEN + BOLD} Gonna ${GREEN + BOLD} Give @@ -20,74 +18,95 @@ ${GREEN + BOLD} Up`; const gardenOverlay = new Overlay("gardenTab", data.VL, "moveVisitors", gardenExample); let nextVisitor = 0; let visitorCount = 5; -let visitors = [`${AQUA + BOLD}Visitors: ${WHITE}(5)`, ` ${RED}???`, ` ${RED}???`, ` ${RED}???`, ` ${RED}???`, ` ${RED}???`]; +let visitors = [ + `${AQUA + BOLD}Visitors: ${WHITE}(5)`, + ` ${RED}???`, + ` ${RED}???`, + ` ${RED}???`, + ` ${RED}???`, + ` ${RED}???`, +]; /** * Fetches the visitor data in tablist and updates the Visitors Overlay every second. */ -registerWhen(register("step", () => { +registerWhen( + register("step", () => { if (!World.isLoaded()) return; const tablist = TabList.getNames(); gardenOverlay.setMessage(""); let gardenMessage = ""; - let visitorIndex = tablist.findIndex(tab => tab.startsWith("§r§b§lVisitors:")); + let visitorIndex = tablist.findIndex((tab) => tab.startsWith("§r§b§lVisitors:")); if (visitorIndex === -1) return; // Get all visitors - visitorCount = parseInt(tablist[visitorIndex].split(' ')[1].substring(5, 6)); + visitorCount = parseInt(tablist[visitorIndex].split(" ")[1].substring(5, 6)); visitors = []; for (let i = 0; i <= visitorCount; i++) { - let visitor = tablist[visitorIndex + i]; - if (visitor.length > 34) visitor = visitor.split(' ').splice(0, 3).join(' '); - gardenMessage += visitor + '\n'; - visitors.push(visitor); + let visitor = tablist[visitorIndex + i]; + if (visitor.length > 34) visitor = visitor.split(" ").splice(0, 3).join(" "); + gardenMessage += visitor + "\n"; + visitors.push(visitor); } // Get next visitor timing let tabTime = 0; - const visitorTime = tablist[visitorIndex + visitorCount + 1].removeFormatting().replace(/[^0-9ms\s]/g, '').trim().split(' '); - if (visitorTime.length === 3) tabTime = 60 * visitorTime[1].replace('m', '') + parseInt(visitorTime[2].replace('s', '')); + const visitorTime = tablist[visitorIndex + visitorCount + 1] + .removeFormatting() + .replace(/[^0-9ms\s]/g, "") + .trim() + .split(" "); + if (visitorTime.length === 3) + tabTime = 60 * visitorTime[1].replace("m", "") + parseInt(visitorTime[2].replace("s", "")); else if (visitorTime.length === 2) { - if (visitorTime[1].endsWith('m')) tabTime = 60 * visitorTime[1].replace('m', ''); - else tabTime = parseInt(visitorTime[1].replace('s', '')); + if (visitorTime[1].endsWith("m")) tabTime = 60 * visitorTime[1].replace("m", ""); + else tabTime = parseInt(visitorTime[1].replace("s", "")); } // Update next display - if (tabTime !== 0 && tabTime < nextVisitor - 60 || tabTime > nextVisitor + 60 || nextVisitor === 0) nextVisitor = tabTime; + if ((tabTime !== 0 && tabTime < nextVisitor - 60) || tabTime > nextVisitor + 60 || nextVisitor === 0) + nextVisitor = tabTime; if (nextVisitor > 0) gardenMessage += ` Next Visitor: ${AQUA + formatTime(nextVisitor)}`; else gardenMessage += ` Next Visitor: ${RED + BOLD}Queue Full!`; gardenOverlay.setMessage(gardenMessage); -}).setFps(1), () => location.getWorld() === "Garden" && Settings.gardenTab); - + }).setFps(1), + () => location.getWorld() === "Garden" && Settings.gardenTab +); /** * Next Visitor stuff */ -registerWhen(register("step", () => { +registerWhen( + register("step", () => { // Decrement visitor timer nextVisitor--; if (location.getWorld() === "Garden") return; // Update visitor display outside Garden if (nextVisitor <= 0 && visitorCount < 5) { - visitorCount++; - visitors[0] = `${AQUA + BOLD}Visitors: ${WHITE}(${visitorCount})`; - visitors.push(` ${RED}???`); - nextVisitor = 720; + visitorCount++; + visitors[0] = `${AQUA + BOLD}Visitors: ${WHITE}(${visitorCount})`; + visitors.push(` ${RED}???`); + nextVisitor = 720; } let gardenMessage = ""; - visitors.forEach(visitor => { - gardenMessage += visitor + '\n'; + visitors.forEach((visitor) => { + gardenMessage += visitor + "\n"; }); if (nextVisitor > 0) gardenMessage += ` Next Visitor: ${AQUA + formatTime(nextVisitor)}`; else gardenMessage += ` Next Visitor: ${RED + BOLD}Queue Full!`; gardenOverlay.setMessage(gardenMessage); -}).setFps(1), () => Settings.gardenTab); + }).setFps(1), + () => Settings.gardenTab +); // Set next visitor time (assuming with 20% visitor reduction) -registerWhen(register("chat", () => { +registerWhen( + register("chat", () => { nextVisitor = 720; -}).setCriteria("${npc} has arrived on your Garden!"), () => Settings.gardenTab); + }).setCriteria("${npc} has arrived on your Garden!"), + () => Settings.gardenTab +); diff --git a/features/farming/JacobHighlight.js b/features/farming/JacobHighlight.js index beaaf77..2e0ba6d 100644 --- a/features/farming/JacobHighlight.js +++ b/features/farming/JacobHighlight.js @@ -2,58 +2,68 @@ * ARCHIVED */ -import Settings from "../../utils/Settings"; import location from "../../utils/Location"; +import { registerWhen } from "../../utils/RegisterTils"; import Settings from "../../utils/Settings"; import { getSlotCoords } from "../../utils/functions/find"; -import { registerWhen } from "../../utils/RegisterTils"; - /** * Track and reset all unclaimed rewards on Jacob reward menu open. */ let unclaimed = []; -registerWhen(register("guiOpened", () => { +registerWhen( + register("guiOpened", () => { Client.scheduleTask(2, () => { - const container = Player.getContainer(); - if (container.getName() !== "Your Contests") return; - - const COLORS = { - "BRONZE": [205, 127, 50], - "SILVER": [192, 192, 192], - "GOLD": [255, 215, 0], - "PLATINUM": [229, 228, 226], - "DIAMOND": [185, 242, 255] - } + const container = Player.getContainer(); + if (container.getName() !== "Your Contests") return; - for (let i = 10; i <= 43; i++) { - let lore = container - .getStackInSlot(i) - .getLore() - .filter(line => line.startsWith("§5§o§7You ") || line.startsWith("§5§o§7§7You") || line === "§5§o§eClick to claim reward!"); - - if (lore[1] !== undefined) { - let medal = lore[0].split(' ')[4]?.removeFormatting(); - unclaimed.push([i, COLORS[medal] ?? [0, 0, 0]]); - } - } - }) -}), () => location.getWorld() === "Garden" && Settings.jacobReward); -registerWhen(register("guiClosed", () => { + const COLORS = { + BRONZE: [205, 127, 50], + SILVER: [192, 192, 192], + GOLD: [255, 215, 0], + PLATINUM: [229, 228, 226], + DIAMOND: [185, 242, 255], + }; + + for (let i = 10; i <= 43; i++) { + let lore = container + .getStackInSlot(i) + .getLore() + .filter( + (line) => + line.startsWith("§5§o§7You ") || line.startsWith("§5§o§7§7You") || line === "§5§o§eClick to claim reward!" + ); + + if (lore[1] !== undefined) { + let medal = lore[0].split(" ")[4]?.removeFormatting(); + unclaimed.push([i, COLORS[medal] ?? [0, 0, 0]]); + } + } + }); + }), + () => location.getWorld() === "Garden" && Settings.jacobReward +); +registerWhen( + register("guiClosed", () => { unclaimed = []; -}), () => location.getWorld() === "Garden" && Settings.jacobReward); + }), + () => location.getWorld() === "Garden" && Settings.jacobReward +); /** * Renders neon green box over unclaimed rewards. */ -registerWhen(register("guiRender", () => { +registerWhen( + register("guiRender", () => { if (unclaimed.length === 0) return; - unclaimed.forEach(index => { - const [x, y] = getSlotCoords(index[0]); - - const color = index[1]; - Renderer.translate(0, 0, 100); - Renderer.drawRect(Renderer.color(...color, 255), x, y, 16, 16); + unclaimed.forEach((index) => { + const [x, y] = getSlotCoords(index[0]); + + const color = index[1]; + Renderer.translate(0, 0, 100); + Renderer.drawRect(Renderer.color(...color, 255), x, y, 16, 16); }); -}), () => location.getWorld() === "Garden" && Settings.jacobReward); + }), + () => location.getWorld() === "Garden" && Settings.jacobReward +); diff --git a/features/farming/PestTracking.js b/features/farming/PestTracking.js index af92779..bb15d24 100644 --- a/features/farming/PestTracking.js +++ b/features/farming/PestTracking.js @@ -1,14 +1,25 @@ import RenderLib from "../../../RenderLib"; +import { + AQUA, + BOLD, + DARK_GRAY, + DARK_GREEN, + DARK_RED, + GOLD, + GRAY, + GREEN, + RED, + RESET, + YELLOW, +} from "../../utils/Constants"; +import { data } from "../../utils/Data"; import location from "../../utils/Location"; +import { Overlay } from "../../utils/Overlay"; +import { registerWhen } from "../../utils/RegisterTils"; import Settings from "../../utils/Settings"; -import { AQUA, BOLD, DARK_GRAY, DARK_GREEN, DARK_RED, GOLD, GRAY, GREEN, RED, RESET, YELLOW } from "../../utils/Constants"; +import { setTitle } from "../../utils/Title"; import { getSlotCoords } from "../../utils/functions/find"; import { formatTime, removeNonNumeric } from "../../utils/functions/format"; -import { registerWhen } from "../../utils/RegisterTils"; -import { Overlay } from "../../utils/Overlay"; -import { data } from "../../utils/Data"; -import { setTitle } from "../../utils/Title"; - const sprays = {}; let pests = []; @@ -17,14 +28,14 @@ let pests = []; * Uses pests widget in TabList in order to track all plots with pests. */ function setPests() { - pests = []; - if (!World.isLoaded()) return; + pests = []; + if (!World.isLoaded()) return; - const pestLine = TabList.getNames().find(tab => tab.startsWith("§r Plots:")); - if (pestLine === undefined) return; + const pestLine = TabList.getNames().find((tab) => tab.startsWith("§r Plots:")); + if (pestLine === undefined) return; - pests = pestLine.removeFormatting().trim().split(', '); - if (pests.length > 0) pests[0] = pests[0].split(' ')[1]; + pests = pestLine.removeFormatting().trim().split(", "); + if (pests.length > 0) pests[0] = pests[0].split(" ")[1]; } registerWhen(register("step", setPests).setFps(1), () => location.getWorld() === "Garden"); @@ -32,140 +43,181 @@ registerWhen(register("step", setPests).setFps(1), () => location.getWorld() === * Command to teleport to plot with pests. */ register("command", () => { - setPests(); - if (pests.length === 0) { - setTitle(`${DARK_RED}Pests Controlled!`, "No plots have any pests!", 10, 50, 10, 70); - return; - } - - const zone = removeNonNumeric(Scoreboard.getLines().find(line => line.getName().startsWith(" §aPlot §7-")) - ?.getName() - ?.removeFormatting() - ?.trim() - ?.split(' ')?.[2] - ); - const plot = pests.find(plot => plot !== zone); - - if (plot !== undefined) { - setTitle(`${DARK_GREEN}Warping...`, `Teleporting to plot ${plot}!`, 10, 50, 10, 70); - ChatLib.command(`plottp ${plot}`); - } else - setTitle(`${DARK_RED}Please Remain Seated.`, "You are in the only plot with pests!", 10, 50, 10, 70); + setPests(); + if (pests.length === 0) { + setTitle(`${DARK_RED}Pests Controlled!`, "No plots have any pests!", 10, 50, 10, 70); + return; + } + + const zone = removeNonNumeric( + Scoreboard.getLines() + .find((line) => line.getName().startsWith(" §aPlot §7-")) + ?.getName() + ?.removeFormatting() + ?.trim() + ?.split(" ")?.[2] + ); + const plot = pests.find((plot) => plot !== zone); + + if (plot !== undefined) { + setTitle(`${DARK_GREEN}Warping...`, `Teleporting to plot ${plot}!`, 10, 50, 10, 70); + ChatLib.command(`plottp ${plot}`); + } else setTitle(`${DARK_RED}Please Remain Seated.`, "You are in the only plot with pests!", 10, 50, 10, 70); }).setName("pesttp"); - /** * Alerts for pest spawns */ -registerWhen(register("chat", (_, plot) => { +registerWhen( + register("chat", (_, plot) => { setTitle(`${GREEN}Plot ${GRAY}- ${AQUA + plot}`, `${GOLD}1 ${RED}Pest ${GRAY}has spawned...`, 10, 50, 10, 71); -}).setCriteria("${ew}! A Pest has appeared in Plot - ${plot}!"), () => location.getWorld() === "Garden" && Settings.pestAlert); -registerWhen(register("chat", (_, num, plot) => { - setTitle(`${GREEN}Plot ${GRAY}- ${AQUA + plot}`, `${GOLD + num} ${RED}Pests ${GRAY}have spawned...`, 10, 50, 10, 71); -}).setCriteria("${ew}! ${num} Pests have spawned in Plot - ${plot}!"), () => location.getWorld() === "Garden" && Settings.pestAlert); - + }).setCriteria("${ew}! A Pest has appeared in Plot - ${plot}!"), + () => location.getWorld() === "Garden" && Settings.pestAlert +); +registerWhen( + register("chat", (_, num, plot) => { + setTitle( + `${GREEN}Plot ${GRAY}- ${AQUA + plot}`, + `${GOLD + num} ${RED}Pests ${GRAY}have spawned...`, + 10, + 50, + 10, + 71 + ); + }).setCriteria("${ew}! ${num} Pests have spawned in Plot - ${plot}!"), + () => location.getWorld() === "Garden" && Settings.pestAlert +); /** * Pest swarm */ -registerWhen(register("step", () => { - const count = parseInt(TabList.getNames().find(name => name.startsWith("§r Alive:"))?.split(' ')?.[2]?.removeFormatting() ?? 0); +registerWhen( + register("step", () => { + const count = parseInt( + TabList.getNames() + .find((name) => name.startsWith("§r Alive:")) + ?.split(" ")?.[2] + ?.removeFormatting() ?? 0 + ); if (count < Settings.infestationAlert) return; - setTitle(`${DARK_GREEN + BOLD}SPREADING PLAGUE`, `${count} minions with ${BOLD}Taunt ${RESET}are in the way!`, 0, 25, 5, 69); -}).setFps(1).unregister(), () => Settings.infestationAlert !== 0 && location.getWorld() === "Garden"); - + setTitle( + `${DARK_GREEN + BOLD}SPREADING PLAGUE`, + `${count} minions with ${BOLD}Taunt ${RESET}are in the way!`, + 0, + 25, + 5, + 69 + ); + }) + .setFps(1) + .unregister(), + () => Settings.infestationAlert !== 0 && location.getWorld() === "Garden" +); /** * Desk highlighting for pests and sprays */ const render = register("guiRender", () => { - const items = Player.getContainer().getItems(); - Object.keys(highlights).forEach(index => { - const [x, y] = getSlotCoords(index); - - Renderer.translate(0, 0, 100); - Renderer.drawRect(highlights[index][0], x, y, 16, 16); - items[index].setStackSize(highlights[index][1]); - }); + const items = Player.getContainer().getItems(); + Object.keys(highlights).forEach((index) => { + const [x, y] = getSlotCoords(index); + + Renderer.translate(0, 0, 100); + Renderer.drawRect(highlights[index][0], x, y, 16, 16); + items[index].setStackSize(highlights[index][1]); + }); }).unregister(); const close = register("guiClosed", () => { - render.unregister(); - close.unregister(); + render.unregister(); + close.unregister(); }).unregister(); -registerWhen(register("guiOpened", () => { +registerWhen( + register("guiOpened", () => { Client.scheduleTask(3, () => { - if (Player.getContainer().getName() !== "Configure Plots") return; - - highlights = {}; - const items = Player.getContainer().getItems(); - for (let i = 0; i < 5; i++) { - for (let j = 2; j < 7; j++) { - let index = i * 9 + j; - let lore = items[index].getLore(); - - const pestLine = lore.find(line => line.startsWith("§5§o§4§lൠ"))?.split(' '); - const sprayLine = lore.find(line => line.startsWith("§5§o§7Sprayed")); - - if (sprayLine !== undefined) { - const splitSpray = sprayLine.split(' '); - highlights[index] = [Renderer.color(57, 255, 20, 128), splitSpray[splitSpray.length - 2].removeFormatting().replace(/\D/g, '')]; - - // Get time left on spray - let time = sprayLine.removeFormatting().match(/(?: (\d+)m)? (\d+)s/); - let minutes = time[1] ? parseInt(time[1], 10) : 0; - let seconds = parseInt(time[2], 10); - - // Set time left on spray (backup) - sprays[items[index].getName().removeFormatting().replace(/[^0-9]/g, '')] = minutes * 60 + parseInt(seconds); - } - if (pestLine !== undefined) - highlights[index] = [Renderer.color(139, 0, 0, 255), pestLine[pestLine.length - 2].removeFormatting()]; - } + if (Player.getContainer().getName() !== "Configure Plots") return; + + highlights = {}; + const items = Player.getContainer().getItems(); + for (let i = 0; i < 5; i++) { + for (let j = 2; j < 7; j++) { + let index = i * 9 + j; + let lore = items[index].getLore(); + + const pestLine = lore.find((line) => line.startsWith("§5§o§4§lൠ"))?.split(" "); + const sprayLine = lore.find((line) => line.startsWith("§5§o§7Sprayed")); + + if (sprayLine !== undefined) { + const splitSpray = sprayLine.split(" "); + highlights[index] = [ + Renderer.color(57, 255, 20, 128), + splitSpray[splitSpray.length - 2].removeFormatting().replace(/\D/g, ""), + ]; + + // Get time left on spray + let time = sprayLine.removeFormatting().match(/(?: (\d+)m)? (\d+)s/); + let minutes = time[1] ? parseInt(time[1], 10) : 0; + let seconds = parseInt(time[2], 10); + + // Set time left on spray (backup) + sprays[ + items[index] + .getName() + .removeFormatting() + .replace(/[^0-9]/g, "") + ] = minutes * 60 + parseInt(seconds); + } + if (pestLine !== undefined) + highlights[index] = [Renderer.color(139, 0, 0, 255), pestLine[pestLine.length - 2].removeFormatting()]; } + } - if (Settings.deskHighlight) { - render.register(); - close.register(); - } - }) -}), () => location.getWorld() === "Garden"); - + if (Settings.deskHighlight) { + render.register(); + close.register(); + } + }); + }), + () => location.getWorld() === "Garden" +); /** * Spray display. */ -const sprayExample = -`${GREEN + BOLD}Sprays: +const sprayExample = `${GREEN + BOLD}Sprays: ${AQUA}Plot 0 ${DARK_GRAY}(${GREEN}1m10s${DARK_GRAY}) - ${AQUA}Plot 0 ${DARK_GRAY}(${GREEN}1m01s${DARK_GRAY})` + ${AQUA}Plot 0 ${DARK_GRAY}(${GREEN}1m01s${DARK_GRAY})`; const sprayOverlay = new Overlay("sprayDisplay", data.SDL, "moveSpray", sprayExample, ["Garden"]); -registerWhen(register("step", () => { +registerWhen( + register("step", () => { setPests(); let sprayMessage = `${GREEN + BOLD}Sprays:`; - const keys = Object.keys(sprays) - keys.forEach(plot => { - if (--sprays[plot] <= 0) { - setTitle(`${RED + BOLD}SPRAY EXPIRED: ${GREEN}Plot ${plot + RED}!`, '', 10, 50, 10, 72); - delete sprays[plot]; - } + const keys = Object.keys(sprays); + keys.forEach((plot) => { + if (--sprays[plot] <= 0) { + setTitle(`${RED + BOLD}SPRAY EXPIRED: ${GREEN}Plot ${plot + RED}!`, "", 10, 50, 10, 72); + delete sprays[plot]; + } - const time = sprays[plot]; - const sprayColor = time > 1200 ? GREEN : - time > 600 ? YELLOW : RED; - sprayMessage += `\n ${AQUA}Plot ${plot + DARK_GRAY} (${sprayColor + formatTime(time) + DARK_GRAY})` + const time = sprays[plot]; + const sprayColor = time > 1200 ? GREEN : time > 600 ? YELLOW : RED; + sprayMessage += `\n ${AQUA}Plot ${plot + DARK_GRAY} (${sprayColor + formatTime(time) + DARK_GRAY})`; }); if (keys.length === 0) sprayMessage += `\n ${RED}None...`; sprayOverlay.setMessage(sprayMessage); -}).setFps(1), () => location.getWorld() === "Garden"); + }).setFps(1), + () => location.getWorld() === "Garden" +); -registerWhen(register("chat", (plot) => { +registerWhen( + register("chat", (plot) => { sprays[plot] = 1_800; -}).setCriteria("SPRAYONATOR! You sprayed Plot - ${plot} with Plant Matter!"), () => location.getWorld() === "Garden") - + }).setCriteria("SPRAYONATOR! You sprayed Plot - ${plot} with Plant Matter!"), + () => location.getWorld() === "Garden" +); /** * Pesthunter bonus tracking. @@ -173,14 +225,16 @@ registerWhen(register("chat", (plot) => { const bonuses = {}; let remain = 0; function addPests(pest) { - if (pest in bonuses) addPests(pest.toString() + String.fromCharCode(Math.floor(Math.random() * 26) + 97)); - else bonuses[pest] = 1800; + if (pest in bonuses) addPests(pest.toString() + String.fromCharCode(Math.floor(Math.random() * 26) + 97)); + else bonuses[pest] = 1800; } -registerWhen(register("chat", (_, fortune) => { +registerWhen( + register("chat", (_, fortune) => { remain = 1800; addPests(fortune); -}).setCriteria("[NPC] Phillip: In exchange for ${pests} Pests, I've given you +${fortune}☘ Farming Fortune for 30m!"), -() => location.getWorld() === "Garden" && Settings.pesthunterBonus); + }).setCriteria("[NPC] Phillip: In exchange for ${pests} Pests, I've given you +${fortune}☘ Farming Fortune for 30m!"), + () => location.getWorld() === "Garden" && Settings.pesthunterBonus +); /** * Track bonus timer @@ -188,43 +242,63 @@ registerWhen(register("chat", (_, fortune) => { const bonusExample = `${YELLOW + BOLD}Pest Bonus: ${GREEN}T1 FIGHTING`; const bonusOverlay = new Overlay("pesthunterBonus", data.PHL, "moveBonus", bonusExample, ["Garden"]); -registerWhen(register("step", () => { +registerWhen( + register("step", () => { let bonusMessage = `${YELLOW + BOLD}Pest Bonus: `; let fortune = 0; - Object.keys(bonuses).forEach(bonus => { - if (fortune === 0) time = bonuses[bonus]; - if (--bonuses[bonus] === 0) delete bonuses[bonus]; - else fortune += parseInt(bonus.replace(/[^0-9]/g, '')); + Object.keys(bonuses).forEach((bonus) => { + if (fortune === 0) time = bonuses[bonus]; + if (--bonuses[bonus] === 0) delete bonuses[bonus]; + else fortune += parseInt(bonus.replace(/[^0-9]/g, "")); }); if (fortune === 0) bonusMessage += `${RED + BOLD}Inactive!`; else { - const bonusColor = remain > 1200 ? GREEN : - remain > 600 ? YELLOW : RED; - bonusMessage += `${GOLD + fortune}☘ ${bonusColor}(${formattime(--remain)})` + const bonusColor = remain > 1200 ? GREEN : remain > 600 ? YELLOW : RED; + bonusMessage += `${GOLD + fortune}☘ ${bonusColor}(${formattime(--remain)})`; } bonusOverlay.setMessage(bonusMessage); -}).setFps(1), () => location.getWorld() === "Garden" && Settings.pesthunterBonus); + }).setFps(1), + () => location.getWorld() === "Garden" && Settings.pesthunterBonus +); /** * Garden box rendering. */ -registerWhen(register("renderWorld", () => { - const plotLine = Scoreboard.getLines().find(line => line.getName().startsWith(" §aPlot")); - const zone = plotLine?.getName()?.removeFormatting()?.trim()?.split(' ')?.[2]?.replace(/[^0-9]/g, '') ?? "0"; +registerWhen( + register("renderWorld", () => { + const plotLine = Scoreboard.getLines().find((line) => line.getName().startsWith(" §aPlot")); + const zone = + plotLine + ?.getName() + ?.removeFormatting() + ?.trim() + ?.split(" ")?.[2] + ?.replace(/[^0-9]/g, "") ?? "0"; const x = Math.floor((Player.getX() + 240) / 96); const z = Math.floor((Player.getZ() + 240) / 96); let color = [1, 1, 1]; if (pests.includes(zone)) { - const plotName = plotLine.getName(); - Tessellator.drawString(DARK_RED + plotName.substring(plotName.length - 1) + " Pests", x * 96 - 192, 77, z * 96 - 192, 0, true, 0.5, false); - color = [1, 0, 0]; + const plotName = plotLine.getName(); + Tessellator.drawString( + DARK_RED + plotName.substring(plotName.length - 1) + " Pests", + x * 96 - 192, + 77, + z * 96 - 192, + 0, + true, + 0.5, + false + ); + color = [1, 0, 0]; } else if (sprays.hasOwnProperty(zone)) { - Tessellator.drawString(GREEN + formatTime(sprays[zone]), x * 96 - 192, 77, z * 96 - 192, 0, true, 0.5, false); - color = [0, 1, 0]; + Tessellator.drawString(GREEN + formatTime(sprays[zone]), x * 96 - 192, 77, z * 96 - 192, 0, true, 0.5, false); + color = [0, 1, 0]; } RenderLib.drawEspBox(-192 + x * 96, 67, -192 + z * 96, 96, 10, ...color, 1, true); -}), () => location.getWorld() === "Garden" && Settings.gardenBox); + }), + () => location.getWorld() === "Garden" && Settings.gardenBox +); diff --git a/features/general/Autocorrect.js b/features/general/Autocorrect.js index a816962..1007f5d 100644 --- a/features/general/Autocorrect.js +++ b/features/general/Autocorrect.js @@ -1,128 +1,126 @@ -import Settings from "../../utils/Settings"; import { DARK_GRAY, GRAY, GREEN, LOGO, RED, WHITE } from "../../utils/Constants"; import { data } from "../../utils/Data"; +import Settings from "../../utils/Settings"; import { delay } from "../../utils/ThreadTils"; - /** * Finds the word with closest distance using Levensthein Distance formula. - * + * * @param {String} inputWord - Word to correct. * @param {Object} dictionary - Dictionary containing all the words and their counts. - * @returns + * @returns */ function autocorrect(inputWord, dictionary) { - if (dictionary.hasOwnProperty(inputWord)) return inputWord; + if (dictionary.hasOwnProperty(inputWord)) return inputWord; - // Function to calculate Levenshtein distance between two strings - function levenshteinDistance(a, b) { - if (a.length === 0) return b.length; - if (b.length === 0) return a.length; + // Function to calculate Levenshtein distance between two strings + function levenshteinDistance(a, b) { + if (a.length === 0) return b.length; + if (b.length === 0) return a.length; - const matrix = []; + const matrix = []; - // Initialize first column - for (let i = 0; i <= b.length; i++) { - matrix[i] = [i]; - } + // Initialize first column + for (let i = 0; i <= b.length; i++) { + matrix[i] = [i]; + } - // Initialize first row - for (let j = 0; j <= a.length; j++) { - matrix[0][j] = j; - } + // Initialize first row + for (let j = 0; j <= a.length; j++) { + matrix[0][j] = j; + } - // Fill in the rest of the matrix - for (let i = 1; i <= b.length; i++) { - for (let j = 1; j <= a.length; j++) { - if (b.charAt(i - 1) === a.charAt(j - 1)) { - matrix[i][j] = matrix[i - 1][j - 1]; - } else { - matrix[i][j] = Math.min( - matrix[i - 1][j - 1] + 1, // substitution - matrix[i][j - 1] + 1, // insertion - matrix[i - 1][j] + 1 // deletion - ); - } - } + // Fill in the rest of the matrix + for (let i = 1; i <= b.length; i++) { + for (let j = 1; j <= a.length; j++) { + if (b.charAt(i - 1) === a.charAt(j - 1)) { + matrix[i][j] = matrix[i - 1][j - 1]; + } else { + matrix[i][j] = Math.min( + matrix[i - 1][j - 1] + 1, // substitution + matrix[i][j - 1] + 1, // insertion + matrix[i - 1][j] + 1 // deletion + ); } - - return matrix[b.length][a.length]; + } } - // Autocorrect logic - let bestWord = inputWord; - let bestDistance = Infinity; + return matrix[b.length][a.length]; + } - Object.keys(dictionary).forEach(word => { - const distance = levenshteinDistance(inputWord, word); - if (distance <= 2 && distance < bestDistance) { - bestWord = word; - bestDistance = distance; - } - }); + // Autocorrect logic + let bestWord = inputWord; + let bestDistance = Infinity; - return bestWord; -} + Object.keys(dictionary).forEach((word) => { + const distance = levenshteinDistance(inputWord, word); + if (distance <= 2 && distance < bestDistance) { + bestWord = word; + bestDistance = distance; + } + }); + return bestWord; +} /** * Corrects a command by attempting to replace misspelled words with the most probable correct ones from a wordbank. - * + * * @param {String} command - Full command argument that was not processable. * @param {ForgeTClientChatReceivedEvent} event - Chat event. * @returns {String} - The corrected command if autocorrection is enabled; otherwise, returns null. -*/ + */ function correct(command, event) { - // Remove command from cache - data.commands[command] -= 2; - if (data.commands[command] <= 0) delete data.commands[command]; - - command.split(' ').forEach((word, index) => { - const wordbank = data.wordbanks[index]; - wordbank[word] -= 2; - if (wordbank[word] <= 0) delete wordbank[word]; - }); - if (cd || !Settings.autocorrect) return; - - // Seperate command into args and correct wordbank - const corrected = []; - command.split(' ').forEach((word, index) => { - corrected.push(autocorrect(word, data.wordbanks[index])); - }); - - // Attempt to run new command - const newCommand = corrected.join(' '); - if (newCommand !== command && data.commands.hasOwnProperty(newCommand)) { - // Run new command - cancel(event); - ChatLib.chat(`${LOGO + RED}Invalid command: "/${command}", attempting to run: "/${newCommand}"!`); - ChatLib.command(newCommand); - - // Put invalid command on CD for 3 seconds - cd = true; - delay(() => { - cd = false; - }, 3000); - } + // Remove command from cache + data.commands[command] -= 2; + if (data.commands[command] <= 0) delete data.commands[command]; + + command.split(" ").forEach((word, index) => { + const wordbank = data.wordbanks[index]; + wordbank[word] -= 2; + if (wordbank[word] <= 0) delete wordbank[word]; + }); + if (cd || !Settings.autocorrect) return; + + // Seperate command into args and correct wordbank + const corrected = []; + command.split(" ").forEach((word, index) => { + corrected.push(autocorrect(word, data.wordbanks[index])); + }); + + // Attempt to run new command + const newCommand = corrected.join(" "); + if (newCommand !== command && data.commands.hasOwnProperty(newCommand)) { + // Run new command + cancel(event); + ChatLib.chat(`${LOGO + RED}Invalid command: "/${command}", attempting to run: "/${newCommand}"!`); + ChatLib.command(newCommand); + + // Put invalid command on CD for 3 seconds + cd = true; + delay(() => { + cd = false; + }, 3000); + } } /** * Track invalid commands and attempt to correct them. */ let cd = false; -register("chat", (command, event) => correct(command, event)) -.setCriteria("Unknown command. Type \"/help\" for help. ('${command}')"); +register("chat", (command, event) => correct(command, event)).setCriteria( + "Unknown command. Type \"/help\" for help. ('${command}')" +); /** * Track invalid warp commands */ let lastMessage = ""; -register("chat", (event) => correct(lastMessage, event)) -.setCriteria("Unknown destination! Check the Fast Travel menu to view options!"); - -register("chat", (event) => correct(lastMessage, event)) -.setCriteria("You don't have permission to run that command!"); +register("chat", (event) => correct(lastMessage, event)).setCriteria( + "Unknown destination! Check the Fast Travel menu to view options!" +); +register("chat", (event) => correct(lastMessage, event)).setCriteria("You don't have permission to run that command!"); /** * Autocomplete @@ -132,123 +130,129 @@ let suggestions = []; let suggesting = false; const suggest = register("renderChat", () => { - const select = suggestions.length - selected - 1; - const strings = suggestions.map((command, index) => `${(index === select ? WHITE : GRAY) + command + DARK_GRAY} (${data.commands[command]})`); - - // Draw suggestions - const width = Renderer.getStringWidth(strings.reduce((longest, current) => current.length > longest.length ? current : longest, '')); - const height = strings.length * 9; - const y = Renderer.screen.getHeight() - 18 - height; - - Renderer.translate(0, 0, 300); - Renderer.drawRect(Renderer.color(0, 0, 0, 128), 7, y - 2, width + 4, height + 4); - - Renderer.translate(0, 0, 300); - Renderer.drawString(strings.join('\n'), 9, y, true); + const select = suggestions.length - selected - 1; + const strings = suggestions.map( + (command, index) => `${(index === select ? WHITE : GRAY) + command + DARK_GRAY} (${data.commands[command]})` + ); + + // Draw suggestions + const width = Renderer.getStringWidth( + strings.reduce((longest, current) => (current.length > longest.length ? current : longest), "") + ); + const height = strings.length * 9; + const y = Renderer.screen.getHeight() - 18 - height; + + Renderer.translate(0, 0, 300); + Renderer.drawRect(Renderer.color(0, 0, 0, 128), 7, y - 2, width + 4, height + 4); + + Renderer.translate(0, 0, 300); + Renderer.drawString(strings.join("\n"), 9, y, true); }).unregister(); const key = register("guiKey", (char, keyCode, __, event) => { - let chat = Client.getCurrentChatMessage() + (keyCode <= 57 && keyCode !== 15 ? char : ''); - if (keyCode === 14) chat = chat.substring(0, chat.length - 2); - suggestions = Object.keys(data.commands).filter(command => command.startsWith(chat.substring(1))); - suggestions.sort((a, b) => data.commands[a] - data.commands[b]); - selected = MathLib.clamp(selected, 0, Math.max(0, suggestions.length - 1)); + let chat = Client.getCurrentChatMessage() + (keyCode <= 57 && keyCode !== 15 ? char : ""); + if (keyCode === 14) chat = chat.substring(0, chat.length - 2); + suggestions = Object.keys(data.commands).filter((command) => command.startsWith(chat.substring(1))); + suggestions.sort((a, b) => data.commands[a] - data.commands[b]); + selected = MathLib.clamp(selected, 0, Math.max(0, suggestions.length - 1)); - if (!chat.startsWith('/') || chat.length < 3 || suggestions.length === 0) { - suggest.unregister(); - return; - } - - if (keyCode === 200) { // Up Key - selected = MathLib.clamp(selected + 1, 0, suggestions.length - 1); - if (suggesting) cancel(event); - } else if (keyCode === 208) { // Down Key - selected = MathLib.clamp(selected - 1, 0, suggestions.length - 1); - if (suggesting) cancel(event); - } else if (keyCode === 15) { // Tab Key - const fill = suggestions[suggestions.length - selected - 1]; - Client.setCurrentChatMessage('/' + fill); - suggestions = Object.keys(data.commands).filter(command => command.startsWith(fill)); - suggestions.sort((a, b) => data.commands[a] - data.commands[b]); - selected = suggestions.length - suggestions.indexOf(fill) - 1; - if (suggesting) cancel(event); - } else { - suggest.register(); - suggesting = true; - } + if (!chat.startsWith("/") || chat.length < 3 || suggestions.length === 0) { + suggest.unregister(); + return; + } + + if (keyCode === 200) { + // Up Key + selected = MathLib.clamp(selected + 1, 0, suggestions.length - 1); + if (suggesting) cancel(event); + } else if (keyCode === 208) { + // Down Key + selected = MathLib.clamp(selected - 1, 0, suggestions.length - 1); + if (suggesting) cancel(event); + } else if (keyCode === 15) { + // Tab Key + const fill = suggestions[suggestions.length - selected - 1]; + Client.setCurrentChatMessage("/" + fill); + suggestions = Object.keys(data.commands).filter((command) => command.startsWith(fill)); + suggestions.sort((a, b) => data.commands[a] - data.commands[b]); + selected = suggestions.length - suggestions.indexOf(fill) - 1; + if (suggesting) cancel(event); + } else { + suggest.register(); + suggesting = true; + } }).unregister(); const close = register("guiClosed", () => { - key.unregister(); - suggest.unregister(); - close.unregister(); - suggesting = false; + key.unregister(); + suggest.unregister(); + close.unregister(); + suggesting = false; }).unregister(); register("guiOpened", () => { - if (!Settings.autocomplete) return; + if (!Settings.autocomplete) return; - Client.scheduleTask(1, () => { - if (!Client.currentGui.get().toString().includes("GuiChat")) return; + Client.scheduleTask(1, () => { + if (!Client.currentGui.get().toString().includes("GuiChat")) return; - selected = 0; - key.register(); - close.register(); - }); + selected = 0; + key.register(); + close.register(); + }); }); - /** * Save words to wordbank. */ register("messageSent", (message) => { - if (!message.startsWith('/')) return; - lastMessage = message.substring(1); + if (!message.startsWith("/")) return; + lastMessage = message.substring(1); - // Add to wordbank count - lastMessage.split(' ').forEach((word, index) => { - if (data.wordbanks.length < index + 1) data.wordbanks.push({}); + // Add to wordbank count + lastMessage.split(" ").forEach((word, index) => { + if (data.wordbanks.length < index + 1) data.wordbanks.push({}); - const wordbank = data.wordbanks[index]; - if (wordbank.hasOwnProperty(word)) wordbank[word]++; - else wordbank[word] = 1; - }); + const wordbank = data.wordbanks[index]; + if (wordbank.hasOwnProperty(word)) wordbank[word]++; + else wordbank[word] = 1; + }); - // Add to command count - if (data.commands.hasOwnProperty(lastMessage)) data.commands[lastMessage]++; - else data.commands[lastMessage] = 1; + // Add to command count + if (data.commands.hasOwnProperty(lastMessage)) data.commands[lastMessage]++; + else data.commands[lastMessage] = 1; }).setPriority(Priority.HIGHEST); /** * Reset commands. */ register("command", () => { - data.wordbanks = []; - ChatLib.chat(`${LOGO + GREEN}Successfully reset wordbank cache!`); + data.wordbanks = []; + ChatLib.chat(`${LOGO + GREEN}Successfully reset wordbank cache!`); }).setName("resetWordbank"); register("command", () => { - data.commands = {}; - ChatLib.chat(`${LOGO + GREEN}Successfully reset commands cache!`); + data.commands = {}; + ChatLib.chat(`${LOGO + GREEN}Successfully reset commands cache!`); }).setName("resetCommands"); /** * Parse out uncommon commands/words */ try { - if (Date.now() - data.lastJoin > 3_600_000) { - data.wordbanks.forEach(wordbank => { - Object.keys(wordbank).forEach(word => { - wordbank[word]--; - if (wordbank[word] <= 0) delete wordbank[word]; - }); - }); - - Object.keys(data.commands).forEach(command => { - data.commands[command]--; - if (data.commands[command] <= 0) delete data.commands[command]; - }); - } -} catch(e) { - console.error(`[VolcAddons] Failed to parse data JSON!`); + if (Date.now() - data.lastJoin > 3_600_000) { + data.wordbanks.forEach((wordbank) => { + Object.keys(wordbank).forEach((word) => { + wordbank[word]--; + if (wordbank[word] <= 0) delete wordbank[word]; + }); + }); + + Object.keys(data.commands).forEach((command) => { + data.commands[command]--; + if (data.commands[command] <= 0) delete data.commands[command]; + }); + } +} catch (e) { + console.error(`[VolcAddons] Failed to parse data JSON!`); } diff --git a/features/general/BridgeFormatter.js b/features/general/BridgeFormatter.js index 9cfdc7e..f22499b 100644 --- a/features/general/BridgeFormatter.js +++ b/features/general/BridgeFormatter.js @@ -2,9 +2,11 @@ import { DARK_GREEN, GOLD, GRAY, WHITE, YELLOW } from "../../utils/Constants"; import { registerWhen } from "../../utils/RegisterTils"; import Settings from "../../utils/Settings"; - -registerWhen(register("chat", (_, player, msg, event) => { +registerWhen( + register("chat", (_, player, msg, event) => { cancel(event); player = player.replace("replying to", GRAY + "replying to" + GOLD); ChatLib.chat(`${DARK_GREEN}Guild > ${GOLD}[BDG${YELLOW}++${GOLD}] ${player + WHITE}: ${msg}`); -}).setCriteria("Guild > ${bridge}: ${player} » ${msg}"), () => Settings.bridgeFormat); + }).setCriteria("Guild > ${bridge}: ${player} » ${msg}"), + () => Settings.bridgeFormat +); diff --git a/features/general/ChangeMessage.js b/features/general/ChangeMessage.js index 125cf75..e62b985 100644 --- a/features/general/ChangeMessage.js +++ b/features/general/ChangeMessage.js @@ -1,97 +1,98 @@ -import Settings from "../../utils/Settings"; -import { registerWhen } from "../../utils/RegisterTils"; import { data } from "../../utils/Data"; - +import { registerWhen } from "../../utils/RegisterTils"; +import Settings from "../../utils/Settings"; /** * Variables used to represent MVP+ and MVP++ emotes. */ const MVP = { - "<3": "❤", - ":star:": "✮", - ":yes:": "✔", - ":no:": "✖", - ":java:": "☕", - ":arrow:": "➜", - ":shrug:": "¯\_(ツ)_/¯", - ":tableflip:": "(╯°□°)╯︵ ┻━┻", - "o/": "( ゚◡゚)/", - ":123:": "123", - ":totem:": "◎_◎", - ":typing:": "✎...", - ":maths:": "√(π+x)=L", - ":snail:": "@'-'", - ":thinking:": "(0.o?)", - ":gimme:": "༼つ ◕_◕ ༽つ", - ":wizard:": "('-')⊃━☆゚.*・。゚", - ":pvp:": "⚔", - ":peace:": "✌", - ":oof:": "OOF", - ":puffer:": "<('O')>", -} + "<3": "❤", + ":star:": "✮", + ":yes:": "✔", + ":no:": "✖", + ":java:": "☕", + ":arrow:": "➜", + ":shrug:": "¯_(ツ)_/¯", + ":tableflip:": "(╯°□°)╯︵ ┻━┻", + "o/": "( ゚◡゚)/", + ":123:": "123", + ":totem:": "◎_◎", + ":typing:": "✎...", + ":maths:": "√(π+x)=L", + ":snail:": "@'-'", + ":thinking:": "(0.o?)", + ":gimme:": "༼つ ◕_◕ ༽つ", + ":wizard:": "('-')⊃━☆゚.*・。゚", + ":pvp:": "⚔", + ":peace:": "✌", + ":oof:": "OOF", + ":puffer:": "<('O')>", +}; const GIFT = { - ":snow:": "☃", - ":dog:": "(ᵔᴥᵔ)", - ":sloth:": "( ⬩ ⊝ ⬩ )", - ":dab:": " { +registerWhen( + register("messageSent", (message, event) => { if (message === nextMessage) return; let contains = false; // MVP++ Emotes Object.keys(MVP).forEach((key) => { - if (message.includes(key)) { - const reg = new RegExp(key, 'g'); - message = message.replace(reg, MVP[key]); - contains = true; - } + if (message.includes(key)) { + const reg = new RegExp(key, "g"); + message = message.replace(reg, MVP[key]); + contains = true; + } }); // Rank Gifting Emotes Object.keys(GIFT).forEach((key) => { - if (message.includes(key)) { - const reg = new RegExp(key, 'g'); - message = message.replace(reg, GIFT[key]); - contains = true; - } + if (message.includes(key)) { + const reg = new RegExp(key, "g"); + message = message.replace(reg, GIFT[key]); + contains = true; + } }); // Custom Emotes Object.keys(data.emotelist).forEach((key) => { - if (message.includes(key)) { - const reg = new RegExp(key, 'g'); - message = message.replace(reg, data.emotelist[key]); - contains = true; - } + if (message.includes(key)) { + const reg = new RegExp(key, "g"); + message = message.replace(reg, data.emotelist[key]); + contains = true; + } }); if (contains) { - nextMessage = message; - ChatLib.say(message); - cancel(event); + nextMessage = message; + ChatLib.say(message); + cancel(event); } -}), () => Settings.enableEmotes); - + }), + () => Settings.enableEmotes +); /** * Message back last messaged player with `/b ${msg}` */ let lastMsg = ""; register("command", (...args) => { - ChatLib.command(`msg ${lastMsg} ${args.join(' ')}`); + ChatLib.command(`msg ${lastMsg} ${args.join(" ")}`); }).setName("b", true); register("messageSent", (message) => { - const args = message.split(' '); - if (!(args[0] === "/msg" || args[0] === "/message")) return; - lastMsg = args[1]; + const args = message.split(" "); + if (!(args[0] === "/msg" || args[0] === "/message")) return; + lastMsg = args[1]; }); diff --git a/features/general/ChatWebhook.js b/features/general/ChatWebhook.js index 35bb539..70d98ac 100644 --- a/features/general/ChatWebhook.js +++ b/features/general/ChatWebhook.js @@ -1,9 +1,8 @@ import { request } from "../../../axios"; +import { registerWhen } from "../../utils/RegisterTils"; import Settings from "../../utils/Settings"; import Toggles from "../../utils/Toggles"; import { getGuildName, getPlayerName } from "../../utils/functions/player"; -import { registerWhen } from "../../utils/RegisterTils"; - /** * Sends a webhook message to discord with message data and timestamp. @@ -12,54 +11,68 @@ import { registerWhen } from "../../utils/RegisterTils"; * @param {String} color - The color code for the embed. */ function sendWebhook(player, msg, color) { - request({ - url: Settings.chatWebhook, - method: "POST", - headers: { - "Content-type": "application/Json", - "User-Agent": "Mozilla/5.0" + request({ + url: Settings.chatWebhook, + method: "POST", + headers: { + "Content-type": "application/Json", + "User-Agent": "Mozilla/5.0", + }, + body: { + username: "VolcYapons", + avatar_url: `https://www.mc-heads.net/avatar/${player}`, + embeds: [ + { + author: { + name: player, + icon_url: `https://www.mc-heads.net/avatar/${player}`, + }, + color: color, + description: msg, + timestamp: new Date(), }, - body: { - "username": "VolcYapons", - "avatar_url": `https://www.mc-heads.net/avatar/${player}`, - "embeds": [{ - "author": { - "name": player, - "icon_url": `https://www.mc-heads.net/avatar/${player}` - }, - "color": color, - "description": msg, - "timestamp": new Date() - }] - } - }); + ], + }, + }); } /** * Check for public chat messages. */ -registerWhen(register("chat", (player, color, msg) => { +registerWhen( + register("chat", (player, color, msg) => { if (player.includes("Party") || player.includes("Guild") || !(color === "f" || color === "7")) return; sendWebhook(getPlayerName(player.removeFormatting()), msg, 0); -}).setCriteria("&r${player}&${color}: ${msg}&r"), () => Settings.chatWebhook !== "" && Toggles.publicChat); + }).setCriteria("&r${player}&${color}: ${msg}&r"), + () => Settings.chatWebhook !== "" && Toggles.publicChat +); /** * Check for party chat messages. */ -registerWhen(register("chat", (player, msg) => { +registerWhen( + register("chat", (player, msg) => { sendWebhook(getPlayerName(player), msg, 255); -}).setCriteria("Party > ${player}: ${msg}"), () => Settings.chatWebhook !== "" && Toggles.partyChat); + }).setCriteria("Party > ${player}: ${msg}"), + () => Settings.chatWebhook !== "" && Toggles.partyChat +); /** * Check for guild chat messages. */ -registerWhen(register("chat", (player, msg) => { +registerWhen( + register("chat", (player, msg) => { sendWebhook(getGuildName(player), msg, 32768); -}).setCriteria("Guild > ${player}: ${msg}"), () => Settings.chatWebhook !== "" && Toggles.guildChat); + }).setCriteria("Guild > ${player}: ${msg}"), + () => Settings.chatWebhook !== "" && Toggles.guildChat +); /** * Check for private chat messages. */ -registerWhen(register("chat", (player, msg) => { +registerWhen( + register("chat", (player, msg) => { sendWebhook(getPlayerName(player), msg, 16711935); -}).setCriteria("From ${player}: ${msg}"), () => Settings.chatWebhook !== "" && Toggles.privateChat); + }).setCriteria("From ${player}: ${msg}"), + () => Settings.chatWebhook !== "" && Toggles.privateChat +); diff --git a/features/general/ChunkBorders.js b/features/general/ChunkBorders.js index 2500c75..d75358c 100644 --- a/features/general/ChunkBorders.js +++ b/features/general/ChunkBorders.js @@ -1,23 +1,26 @@ import RenderLib from "../../../RenderLib/index"; import { data } from "../../utils/Data"; - /** * Draw bounding box on current plot. */ let rendering = false; const renderBorders = register("renderWorld", () => { - const x = Math.floor(Player.getX() / 16); - const z = Math.floor(Player.getZ() / 16); + const x = Math.floor(Player.getX() / 16); + const z = Math.floor(Player.getZ() / 16); - RenderLib.drawEspBox(x * 16 + 8, 0, z * 16 + 8, 16, 256, 1, 1, 0, 1, false); - RenderLib.drawInnerEspBox(x * 16 + 8, 0, z * 16 + 8, 16, 256, 1, 1, 0, 0.2, false); + RenderLib.drawEspBox(x * 16 + 8, 0, z * 16 + 8, 16, 256, 1, 1, 0, 1, false); + RenderLib.drawInnerEspBox(x * 16 + 8, 0, z * 16 + 8, 16, 256, 1, 1, 0, 0.2, false); }).unregister(); const chunkey = new KeyBind("Chunk Border", data.chunkey, "./VolcAddons.xdd"); -register("gameUnload", () => { data.chunkey = chunkey.getKeyCode() }); -chunkey.registerKeyPress(() => { +register("gameUnload", () => { + data.chunkey = chunkey.getKeyCode(); +}); +chunkey + .registerKeyPress(() => { if (rendering) renderBorders.unregister(); else renderBorders.register(); rendering = !rendering; -}).setPriority(Priority.HIGHEST); \ No newline at end of file + }) + .setPriority(Priority.HIGHEST); diff --git a/features/general/Cooldowns.js b/features/general/Cooldowns.js index 4cedd98..8d93604 100644 --- a/features/general/Cooldowns.js +++ b/features/general/Cooldowns.js @@ -1,15 +1,15 @@ -import Settings from "../../utils/Settings"; import { DARK_GREEN, DARK_RED, GREEN, RED, YELLOW } from "../../utils/Constants"; -import { registerWhen } from "../../utils/RegisterTils"; import { data } from "../../utils/Data"; +import { registerWhen } from "../../utils/RegisterTils"; +import Settings from "../../utils/Settings"; import { setTitle } from "../../utils/Title"; - /** * Callback for handling mouse button clicks and releases to track ability usage. */ let items = {}; -registerWhen(register("clicked", (x, y, button, down) => { +registerWhen( + register("clicked", (x, y, button, down) => { const held = Player.getHeldItem(); if (Client.isInGui() || !down || held === null) return; const heldName = held?.getItemNBT()?.getCompoundTag("tag")?.getCompoundTag("ExtraAttributes")?.getString("id"); @@ -17,98 +17,105 @@ registerWhen(register("clicked", (x, y, button, down) => { const cd = data.cdlist[heldName]; if (isNaN(cd)) { - const firstLetter = cd[0]; - const remaining = cd.substring(1); - if (isNaN(remaining)) return; + const firstLetter = cd[0]; + const remaining = cd.substring(1); + if (isNaN(remaining)) return; - if (firstLetter === 'a' || (firstLetter === 'l' && !(heldName in items) && button === 0)) { - items[heldName] = [remaining, held.getName()]; - } + if (firstLetter === "a" || (firstLetter === "l" && !(heldName in items) && button === 0)) { + items[heldName] = [remaining, held.getName()]; + } } else if (button === 1 && !(heldName in items)) { - items[heldName] = [cd, held.getName()]; + items[heldName] = [cd, held.getName()]; } -}), () => data.cdlist.length !== 0); + }), + () => data.cdlist.length !== 0 +); /** * Handles shift to track ability. */ const shiftKey = new KeyBind(Client.getMinecraft().field_71474_y.field_74311_E); shiftKey.registerKeyPress(() => { - // Get armor pieces - const armor = Player.armor; - const pieces = [ - armor.getHelmet(), - armor.getChestplate(), - armor.getLeggings(), - armor.getBoots() - ]; - - // Loop through to check in cd list - pieces.forEach(piece => { - const pieceName = piece?.getItemNBT()?.getCompoundTag("tag")?.getCompoundTag("ExtraAttributes")?.getString("id"); - const cd = data.cdlist[pieceName]; - if (cd === undefined) return; + // Get armor pieces + const armor = Player.armor; + const pieces = [armor.getHelmet(), armor.getChestplate(), armor.getLeggings(), armor.getBoots()]; - // cooldown checks - if (pieceName in items) return; - const firstLetter = cd[0]; - const remaining = cd.substring(1); + // Loop through to check in cd list + pieces.forEach((piece) => { + const pieceName = piece?.getItemNBT()?.getCompoundTag("tag")?.getCompoundTag("ExtraAttributes")?.getString("id"); + const cd = data.cdlist[pieceName]; + if (cd === undefined) return; - if (firstLetter === 's' && !isNaN(remaining)) { - items[pieceName] = [remaining, piece.getName()]; - } else if (!isNaN(cd)) { - items[pieceName] = [cd, piece.getName()]; - } - }); + // cooldown checks + if (pieceName in items) return; + const firstLetter = cd[0]; + const remaining = cd.substring(1); + + if (firstLetter === "s" && !isNaN(remaining)) { + items[pieceName] = [remaining, piece.getName()]; + } else if (!isNaN(cd)) { + items[pieceName] = [cd, piece.getName()]; + } + }); }); /** * Reset cooldowns on worldchange */ -registerWhen(register("worldUnload", () => { +registerWhen( + register("worldUnload", () => { items = {}; -}), () => data.cdlist.length !== 0); + }), + () => data.cdlist.length !== 0 +); /** * Update function for handling cooldowns and item stack sizes. */ let filteredItems = undefined; -registerWhen(register("tick", () => { +registerWhen( + register("tick", () => { const dupe = new Set(); - filteredItems = Player.getInventory().getItems().filter(item => - item !== null && item?.getItemNBT()?.getCompoundTag("tag")?.getCompoundTag("ExtraAttributes")?.getString("id") in items); - filteredItems.forEach(item => { - const itemID = item?.getItemNBT()?.getCompoundTag("tag")?.getCompoundTag("ExtraAttributes")?.getString("id"); - const itemName = items[itemID][1]; - if (!dupe.has(itemID)) { - items[itemID][0] -= 0.05; - dupe.add(itemID); - } + filteredItems = Player.getInventory() + .getItems() + .filter( + (item) => + item !== null && + item?.getItemNBT()?.getCompoundTag("tag")?.getCompoundTag("ExtraAttributes")?.getString("id") in items + ); + filteredItems.forEach((item) => { + const itemID = item?.getItemNBT()?.getCompoundTag("tag")?.getCompoundTag("ExtraAttributes")?.getString("id"); + const itemName = items[itemID][1]; + if (!dupe.has(itemID)) { + items[itemID][0] -= 0.05; + dupe.add(itemID); + } - const cd = Math.ceil(items[itemID][0]); - if (isNaN(cd)) delete items[itemID]; - else if (cd <= 0) { - if (Settings.cooldownAlert) - setTitle(`${GREEN + BOLD}${itemName}`, `${GREEN}is off cooldown!`, 5, 25, 5, 90); - delete items[itemID]; - } + const cd = Math.ceil(items[itemID][0]); + if (isNaN(cd)) delete items[itemID]; + else if (cd <= 0) { + if (Settings.cooldownAlert) setTitle(`${GREEN + BOLD}${itemName}`, `${GREEN}is off cooldown!`, 5, 25, 5, 90); + delete items[itemID]; + } }); -}), () => data.cdlist.length !== 0); + }), + () => data.cdlist.length !== 0 +); /** * Change stack size during renderHotbar for smoother countdown. */ -registerWhen(register("renderItemIntoGui", (item, x, y) => { +registerWhen( + register("renderItemIntoGui", (item, x, y) => { const id = item.getNBT()?.getCompoundTag("tag")?.getCompoundTag("ExtraAttributes")?.getString("id"); if (id in items) { - const cd = items[id][0]; - const color = cd < 5 ? GREEN : - cd < 15 ? DARK_GREEN : - cd < 30 ? YELLOW : - cd < 60 ? RED : DARK_RED; + const cd = items[id][0]; + const color = cd < 5 ? GREEN : cd < 15 ? DARK_GREEN : cd < 30 ? YELLOW : cd < 60 ? RED : DARK_RED; - Renderer.translate(0, 0, 999); - Renderer.drawString(color + parseFloat(cd).toFixed(cd >= 100 ? 0 : 1), x, y - 6, true); + Renderer.translate(0, 0, 999); + Renderer.drawString(color + parseFloat(cd).toFixed(cd >= 100 ? 0 : 1), x, y - 6, true); } -}), () => data.cdlist.length !== 0); + }), + () => data.cdlist.length !== 0 +); diff --git a/features/general/FairySouls.js b/features/general/FairySouls.js index 3ba2e16..6326ffc 100644 --- a/features/general/FairySouls.js +++ b/features/general/FairySouls.js @@ -1,125 +1,134 @@ -import location from "../../utils/Location"; -import Settings from "../../utils/Settings"; import { AQUA, BOLD, DARK_GRAY, GOLD, GREEN, LIGHT_PURPLE, LOGO, RED, YELLOW } from "../../utils/Constants"; -import { getClosest } from "../../utils/functions/find"; import { Json } from "../../utils/Json"; +import location from "../../utils/Location"; import { registerWhen } from "../../utils/RegisterTils"; +import Settings from "../../utils/Settings"; import Waypoint from "../../utils/Waypoint"; - +import { getClosest } from "../../utils/functions/find"; /** * Variables used to represent soul waypoints. */ -const soulWaypoints = new Waypoint([1, 0.75, 0.8], 1); // Pink Fairies +const soulWaypoints = new Waypoint([1, 0.75, 0.8], 1); // Pink Fairies const fairySouls = new Json("fairySouls.json", true); /** * Removes closest fairy soul to player once one is unlocked. */ -registerWhen(register("chat", () => { +registerWhen( + register("chat", () => { const souls = fairySouls.getData()[location.getWorld()]; if (souls.length === 0) return; // Delete closest soul const closest = getClosest([Player.getX(), Player.getY(), Player.getZ()], souls); if (closest !== undefined && closest[1] < 10) souls.splice(souls.indexOf(closest[0]), 1); -}).setCriteria("SOUL! You found a Fairy Soul!"), () => Settings.fairyWaypoint !== 0); + }).setCriteria("SOUL! You found a Fairy Soul!"), + () => Settings.fairyWaypoint !== 0 +); /** * Fail safe fairy soul remove in case player clicks on an unregistered soul. */ -registerWhen(register("chat", () => { +registerWhen( + register("chat", () => { const souls = fairySouls.getData()[location.getWorld()]; if (souls.length === 0) return; // Delete duplicate soul const closest = getClosest([Player.getX(), Player.getY(), Player.getZ()], souls); if (closest !== undefined && closest[1] < 10) souls.splice(souls.indexOf(closest[0]), 1); -}).setCriteria("You have already found that Fairy Soul!"), () => Settings.fairyWaypoint !== 0); + }).setCriteria("You have already found that Fairy Soul!"), + () => Settings.fairyWaypoint !== 0 +); /** * Updates fairy soul array closer than set threshold to player. */ -registerWhen(register("step", () => { +registerWhen( + register("step", () => { // Filters to closest souls soulWaypoints.clear(); - fairySouls.getData()[location.getWorld()]?.forEach(fairy => { - const x = parseFloat(fairy[1]) + 1; - const y = parseFloat(fairy[2]); - const z = parseFloat(fairy[3]) + 1; + fairySouls.getData()[location.getWorld()]?.forEach((fairy) => { + const x = parseFloat(fairy[1]) + 1; + const y = parseFloat(fairy[2]); + const z = parseFloat(fairy[3]) + 1; - if (Math.hypot(Player.getX() - x, Player.getZ() - z) < Settings.fairyWaypoint) { - soulWaypoints.push([x, y, z]); - } + if (Math.hypot(Player.getX() - x, Player.getZ() - z) < Settings.fairyWaypoint) { + soulWaypoints.push([x, y, z]); + } }); -}).setFps(1), () => Settings.fairyWaypoint !== 0); + }).setFps(1), + () => Settings.fairyWaypoint !== 0 +); /** * Updates the fairy soul array. - * + * * @param {String} command - The command to execute. * @param {Number} index - The index of the soul to remove. */ export function updateFairy(command, index) { - ChatLib.clearChat(5858); - const data = fairySouls.getData(); - const souls = data[location.getWorld()] ?? []; + ChatLib.clearChat(5858); + const data = fairySouls.getData(); + const souls = data[location.getWorld()] ?? []; - switch (command) { - case "clear": // Clear all waypoints - Object.keys(data).forEach(world => { - data[world].length = 0 - }); - ChatLib.chat(`${LOGO + GREEN}Successfully cleared out all Fairy Soul waypoints.`); - break; - case "delete": // Delete the specified soul - if (souls[index] === undefined) { - ChatLib.chat(`${LOGO + RED}Error: Invalid Fairy Soul index "${index}"!`); - return; - } - souls.splice(index, 1); - ChatLib.command(`va fairy list`, true); - ChatLib.chat(`${LOGO + GREEN}Successfully removed Fairy Soul.`); - break; - case "list": // List all missing fairy souls - const message = new Message(`\n${LOGO + GOLD + BOLD}Fairy Souls:`).setChatLineId(5858); - souls.forEach((soul, i) => { - // Get the soul data - const zone = soul[0]; - const x = soul[1]; - const y = soul[2]; - const z = soul[3]; + switch (command) { + case "clear": // Clear all waypoints + Object.keys(data).forEach((world) => { + data[world].length = 0; + }); + ChatLib.chat(`${LOGO + GREEN}Successfully cleared out all Fairy Soul waypoints.`); + break; + case "delete": // Delete the specified soul + if (souls[index] === undefined) { + ChatLib.chat(`${LOGO + RED}Error: Invalid Fairy Soul index "${index}"!`); + return; + } + souls.splice(index, 1); + ChatLib.command(`va fairy list`, true); + ChatLib.chat(`${LOGO + GREEN}Successfully removed Fairy Soul.`); + break; + case "list": // List all missing fairy souls + const message = new Message(`\n${LOGO + GOLD + BOLD}Fairy Souls:`).setChatLineId(5858); + souls.forEach((soul, i) => { + // Get the soul data + const zone = soul[0]; + const x = soul[1]; + const y = soul[2]; + const z = soul[3]; - // Add the text component - message.addTextComponent( - new TextComponent(`\n${DARK_GRAY}- ${AQUA}${zone}: ${YELLOW}(${x}, ${y}, ${z})`) - .setHoverValue(`${YELLOW}Click to remove ${LIGHT_PURPLE}Fairy Soul${YELLOW}.`) - .setClick("run_command", `/va fairy delete ${i}`) - ); - }); - message.chat(); - break; - case "pop": // Delete closest soul - const closest = getClosest([Player.getX(), Player.getY(), Player.getZ()], souls); - if (closest !== undefined) souls.splice(souls.indexOf(closest[0]), 1); - ChatLib.chat(`${LOGO + GREEN}Successfully removed closest Fairy Soul.`); - break; - case "reset": // Reset the array - fairySouls.reset(); - ChatLib.chat(`${LOGO + GREEN}Successfully reset Fairy Soul waypoints.`); - break; - case "help": // Display help message - default: - if (command !== "help") ChatLib.chat(`${LOGO + RED}Error: Invalid argument "${command}"!\n`); - ChatLib.chat( -`${LOGO + GOLD + BOLD}Fairy Soul Commands: + // Add the text component + message.addTextComponent( + new TextComponent(`\n${DARK_GRAY}- ${AQUA}${zone}: ${YELLOW}(${x}, ${y}, ${z})`) + .setHoverValue(`${YELLOW}Click to remove ${LIGHT_PURPLE}Fairy Soul${YELLOW}.`) + .setClick("run_command", `/va fairy delete ${i}`) + ); + }); + message.chat(); + break; + case "pop": // Delete closest soul + const closest = getClosest([Player.getX(), Player.getY(), Player.getZ()], souls); + if (closest !== undefined) souls.splice(souls.indexOf(closest[0]), 1); + ChatLib.chat(`${LOGO + GREEN}Successfully removed closest Fairy Soul.`); + break; + case "reset": // Reset the array + fairySouls.reset(); + ChatLib.chat(`${LOGO + GREEN}Successfully reset Fairy Soul waypoints.`); + break; + case "help": // Display help message + default: + if (command !== "help") ChatLib.chat(`${LOGO + RED}Error: Invalid argument "${command}"!\n`); + ChatLib.chat( + `${LOGO + GOLD + BOLD}Fairy Soul Commands: ${DARK_GRAY}- ${GOLD}Base: ${YELLOW}/va fairy ${DARK_GRAY}- ${GOLD}clear: ${YELLOW}Marks all Fairy Souls as complete. ${DARK_GRAY}- ${GOLD}list: ${YELLOW}List all missing Fairy Souls. ${DARK_GRAY}- ${GOLD}pop: ${YELLOW}Removes the closest Fairy Soul. ${DARK_GRAY}- ${GOLD}reset: ${YELLOW}Reset the Fairy Soul array. - ${DARK_GRAY}- ${GOLD}help: ${YELLOW}Displays this help message.`); - break; - } + ${DARK_GRAY}- ${GOLD}help: ${YELLOW}Displays this help message.` + ); + break; + } } diff --git a/features/general/ImageViewer.js b/features/general/ImageViewer.js index a5f49aa..ec3fbc5 100644 --- a/features/general/ImageViewer.js +++ b/features/general/ImageViewer.js @@ -2,9 +2,8 @@ * ARCHIVED */ -import Settings from "../../utils/Settings"; import { registerWhen } from "../../utils/RegisterTils"; - +import Settings from "../../utils/Settings"; /** * Variables used to determine image rendering. @@ -18,39 +17,55 @@ let imgUrl = undefined; * Sets size of screen. */ register("worldLoad", () => { - SCREEN_WIDTH = Renderer.screen.getWidth(); - SCREEN_HEIGHT = Renderer.screen.getHeight(); + SCREEN_WIDTH = Renderer.screen.getWidth(); + SCREEN_HEIGHT = Renderer.screen.getHeight(); }); /** * Renders the image on cursor location / lowest xy. */ -registerWhen(register("renderOverlay", () => { +registerWhen( + register("renderOverlay", () => { if (img === undefined) return; const imgWidth = img.getTextureWidth(); const imgHeight = img.getTextureHeight(); - const ratio = (imgWidth / SCREEN_WIDTH > imgHeight / SCREEN_HEIGHT ? imgWidth / SCREEN_WIDTH : imgHeight / SCREEN_HEIGHT) / Settings.imageRatio; + const ratio = + (imgWidth / SCREEN_WIDTH > imgHeight / SCREEN_HEIGHT ? imgWidth / SCREEN_WIDTH : imgHeight / SCREEN_HEIGHT) / + Settings.imageRatio; const width = imgWidth / ratio; const height = imgHeight / ratio; - img.draw(Math.min(Client.getMouseX(), SCREEN_WIDTH - width), Math.max(0, Client.getMouseY() - height), width, height); -}).setPriority(Priority.LOWEST), () => Settings.imageRatio !== 0); + img.draw( + Math.min(Client.getMouseX(), SCREEN_WIDTH - width), + Math.max(0, Client.getMouseY() - height), + width, + height + ); + }).setPriority(Priority.LOWEST), + () => Settings.imageRatio !== 0 +); /** * Gets image when hovering over Imgur/Discord link. */ -registerWhen(register("chatComponentHovered", (text) => { +registerWhen( + register("chatComponentHovered", (text) => { const hoverValue = text.getHoverValue().removeFormatting(); if (hoverValue === imgUrl || !(hoverValue.includes("imgur.com") || hoverValue.includes("cdn.discordapp"))) return; imgUrl = hoverValue; try { - img = Image.fromUrl(imgUrl); + img = Image.fromUrl(imgUrl); } catch (err) {} -}), () => Settings.imageRatio !== 0); + }), + () => Settings.imageRatio !== 0 +); /** * Resets image on gui close. */ -registerWhen(register("guiClosed", () => { +registerWhen( + register("guiClosed", () => { img = undefined; imgUrl = undefined; -}), () => Settings.imageRatio !== 0); + }), + () => Settings.imageRatio !== 0 +); diff --git a/features/general/LevelAlert.js b/features/general/LevelAlert.js index f7c1540..37e81de 100644 --- a/features/general/LevelAlert.js +++ b/features/general/LevelAlert.js @@ -1,16 +1,18 @@ -import Settings from "../../utils/Settings"; import { AQUA, BOLD, DARK_AQUA, GRAY, LOGO } from "../../utils/Constants"; import { registerWhen } from "../../utils/RegisterTils"; +import Settings from "../../utils/Settings"; import { setTitle } from "../../utils/Title"; - let lastMessage; -registerWhen(register("actionBar", (_, amount, category, now, __) => { +registerWhen( + register("actionBar", (_, amount, category, now, __) => { const message = `${LOGO + AQUA}+${amount} SkyBlock XP ${GRAY}(${category}) ${DARK_AQUA}(${now}/100)`; if (lastMessage === message) return; lastMessage = message; ChatLib.chat(message); setTitle(`${AQUA + BOLD}+${amount} SkyBlock XP`, `${GRAY}(${category}) ${DARK_AQUA}(${now}/100)`, 10, 50, 10, 98); -}).setCriteria("${hp}+${amount} SkyBlock XP (${category}) (${now}/100)${mana}"), () => Settings.levelAlert); + }).setCriteria("${hp}+${amount} SkyBlock XP (${category}) (${now}/100)${mana}"), + () => Settings.levelAlert +); diff --git a/features/general/Performance.js b/features/general/Performance.js index affe5ce..d3f2c7b 100644 --- a/features/general/Performance.js +++ b/features/general/Performance.js @@ -1,12 +1,24 @@ +import { + AQUA, + BOLD, + DARK_AQUA, + DARK_GRAY, + DARK_GREEN, + DARK_RED, + GOLD, + GRAY, + GREEN, + LOGO, + RED, + YELLOW, +} from "../../utils/Constants"; +import { data } from "../../utils/Data"; import location from "../../utils/Location"; +import { Overlay } from "../../utils/Overlay"; +import { registerWhen } from "../../utils/RegisterTils"; import Settings from "../../utils/Settings"; import Toggles from "../../utils/Toggles"; -import { AQUA, BOLD, DARK_AQUA, DARK_GRAY, DARK_GREEN, DARK_RED, GOLD, GRAY, GREEN, LOGO, RED, YELLOW } from "../../utils/Constants"; -import { Overlay } from "../../utils/Overlay"; import { isPlayer } from "../../utils/functions/player"; -import { registerWhen } from "../../utils/RegisterTils"; -import { data } from "../../utils/Data"; - /** * Java packet types. @@ -21,7 +33,9 @@ let S01PacketJoinGame = Java.type("net.minecraft.network.play.server.S01PacketJo * Variables used to represent TPS data. */ let tps = 20; -export function getTPS() { return tps }; +export function getTPS() { + return tps; +} const pastTps = [20, 20, 20]; let pastDate = 0; @@ -29,32 +43,34 @@ let pastDate = 0; * Calculates and updates the TPS (ticks per second) value. */ function calcTPS() { - if (pastDate !== null) { - const time = Date.now() - pastDate; - tps = Math.min(20000 / time, 20); - pastTps.shift(); - pastTps.push(tps); - tps = Math.min(...pastTps); - } - pastDate = Date.now(); + if (pastDate !== null) { + const time = Date.now() - pastDate; + tps = Math.min(20000 / time, 20); + pastTps.shift(); + pastTps.push(tps); + tps = Math.min(...pastTps); + } + pastDate = Date.now(); } // In case not CT 2.2.0 try { - register('packetReceived', () => { - calcTPS(); - }).setFilteredClass(S03PacketTimeUpdate); + register("packetReceived", () => { + calcTPS(); + }).setFilteredClass(S03PacketTimeUpdate); } catch (err) { - register('packetReceived', (packet) => { - if (packet !== S03PacketTimeUpdate) return; - calcTPS(); - }); + register("packetReceived", (packet) => { + if (packet !== S03PacketTimeUpdate) return; + calcTPS(); + }); } /** * Variables used to represent ping data. */ let ping = 0; -export function getPing() { return ping }; +export function getPing() { + return ping; +} let lastPing = 0; /** @@ -62,42 +78,44 @@ let lastPing = 0; * If `lastPing` is not set (0), it sends the request and updates `lastPing` with the current time. */ function sendPingRequest() { - if (lastPing === 0) { - Client.sendPacket(new C16PacketClientStatus(C16PacketClientStatus.EnumState.REQUEST_STATS)); - lastPing = Date.now(); - } -}; -registerWhen(register("step", () => { + if (lastPing === 0) { + Client.sendPacket(new C16PacketClientStatus(C16PacketClientStatus.EnumState.REQUEST_STATS)); + lastPing = Date.now(); + } +} +registerWhen( + register("step", () => { sendPingRequest(); -}).setDelay(3), () => Settings.serverStatus || Toggles.statusCommand); + }).setDelay(3), + () => Settings.serverStatus || Toggles.statusCommand +); /** * Calculates the ping (latency) based on the difference between the current time * and the time when the last ping request was sent. Updates the `ping` value and resets `lastPing`. */ function calculatePing() { - if (lastPing !== 0) { - ping = Math.abs(Date.now() - lastPing); - lastPing = 0; - } -}; + if (lastPing !== 0) { + ping = Math.abs(Date.now() - lastPing); + lastPing = 0; + } +} // In case not CT 2.2.0 try { - register('packetReceived', () => { - calculatePing(); - }).setFilteredClasses([S01PacketJoinGame, S37PacketStatistics]); + register("packetReceived", () => { + calculatePing(); + }).setFilteredClasses([S01PacketJoinGame, S37PacketStatistics]); } catch (err) { - register('packetReceived', (packet) => { - if (packet !== S01PacketJoinGame || packet !== S37PacketStatistics) return; - calculatePing(); - }); + register("packetReceived", (packet) => { + if (packet !== S01PacketJoinGame || packet !== S37PacketStatistics) return; + calculatePing(); + }); } /** * Variables used to represent and display player status. */ -const statusExample = -`${DARK_AQUA + BOLD}XYZ: ${GREEN}-195, 88, 58 +const statusExample = `${DARK_AQUA + BOLD}XYZ: ${GREEN}-195, 88, 58 ${DARK_AQUA + BOLD}Y/P: ${AQUA}-89.15 / 30.89 ${DARK_AQUA + BOLD}Dir: ${AQUA}East ${DARK_AQUA + BOLD}Ping: ${GREEN}58 ${AQUA}ms @@ -112,198 +130,271 @@ const statusOverlay = new Overlay("serverStatus", data.LL, "moveStatus", statusE * Constructs a formatted message containing the ping in milliseconds, TPS in ticks per second, * and FPS (frames per second) using the values from the `ping`, `tps`, and `Client.getFPS()` respectively. */ -registerWhen(register('tick', () => { +registerWhen( + register("tick", () => { let statusMessage = ""; // XYZ if (Toggles.xyzDisplay) { - const x = Math.round(Player.getX()); - const y = Math.round(Player.getY()); - const z = Math.round(Player.getZ()); - statusMessage += `\n${GRAY}[${GOLD}XYZ${GRAY}] ${DARK_GRAY + x}, ${y}, ${z}`; + const x = Math.round(Player.getX()); + const y = Math.round(Player.getY()); + const z = Math.round(Player.getZ()); + statusMessage += `\n${GRAY}[${GOLD}XYZ${GRAY}] ${DARK_GRAY + x}, ${y}, ${z}`; } // Yaw and Pitch if (Toggles.angleDisplay === 1 || Toggles.angleDisplay === 3) { - const yaw = Player.getYaw(); - const pitch = Player.getPitch(); - statusMessage += `\n${GRAY}[${GOLD}Y/P${GRAY}] ${DARK_GRAY + yaw.toFixed(2)} / ${pitch.toFixed(2)}`; + const yaw = Player.getYaw(); + const pitch = Player.getPitch(); + statusMessage += `\n${GRAY}[${GOLD}Y/P${GRAY}] ${DARK_GRAY + yaw.toFixed(2)} / ${pitch.toFixed(2)}`; } // Direction if (Toggles.dirDisplay) { - const yaw = (Player.getYaw() + 360) % 360; - const direction = yaw >= 45 && yaw < 135 ? "West" : - yaw >= 135 && yaw < 255 ? "North" : - yaw >= 225 && yaw < 315 ? "East" : "South"; - statusMessage += `\n${GRAY}[${GOLD}Dir${GRAY}] ${DARK_GRAY + direction}`; + const yaw = (Player.getYaw() + 360) % 360; + const direction = + yaw >= 45 && yaw < 135 + ? "West" + : yaw >= 135 && yaw < 255 + ? "North" + : yaw >= 225 && yaw < 315 + ? "East" + : "South"; + statusMessage += `\n${GRAY}[${GOLD}Dir${GRAY}] ${DARK_GRAY + direction}`; } // Ping if (Toggles.pingDisplay) { - const pingColor = ping < 100 ? GREEN : - ping < 200 ? DARK_GREEN : - ping < 300 ? YELLOW : - ping < 420 ? GOLD : - ping < 690 ? RED : DARK_RED; - statusMessage += `\n${GRAY}[${GOLD}Ping${GRAY}] ${pingColor + ping + DARK_GRAY} ms`; + const pingColor = + ping < 100 + ? GREEN + : ping < 200 + ? DARK_GREEN + : ping < 300 + ? YELLOW + : ping < 420 + ? GOLD + : ping < 690 + ? RED + : DARK_RED; + statusMessage += `\n${GRAY}[${GOLD}Ping${GRAY}] ${pingColor + ping + DARK_GRAY} ms`; } // FPS if (Toggles.fpsDisplay) { - const fps = Client.getFPS(); - const fpsMax = SETTINGS.field_74350_i; - const fpsRatio = fps / fpsMax; - const fpsColor = fpsMax >= 260 || fpsRatio > 0.9 ? GREEN : - fpsRatio > 0.8 ? DARK_GREEN : - fpsRatio > 0.7 ? YELLOW : - fpsRatio > 0.6 ? GOLD : - fpsRatio > 0.5 ? RED : DARK_RED; - statusMessage += `\n${GRAY}[${GOLD}FPS${GRAY}] ${fpsColor + fps + DARK_GRAY} fps`; + const fps = Client.getFPS(); + const fpsMax = SETTINGS.field_74350_i; + const fpsRatio = fps / fpsMax; + const fpsColor = + fpsMax >= 260 || fpsRatio > 0.9 + ? GREEN + : fpsRatio > 0.8 + ? DARK_GREEN + : fpsRatio > 0.7 + ? YELLOW + : fpsRatio > 0.6 + ? GOLD + : fpsRatio > 0.5 + ? RED + : DARK_RED; + statusMessage += `\n${GRAY}[${GOLD}FPS${GRAY}] ${fpsColor + fps + DARK_GRAY} fps`; } // TPS if (Toggles.tpsDisplay) { - const tpsColor = tps > 19 ? GREEN : - tps > 16 ? DARK_GREEN : - tps > 13 ? YELLOW : - tps > 10 ? GOLD : - tps > 7 ? RED : DARK_RED; - statusMessage += `\n${GRAY}[${GOLD}TPS${GRAY}] ${tpsColor + tps.toFixed(1) + DARK_GRAY} tps`; + const tpsColor = + tps > 19 ? GREEN : tps > 16 ? DARK_GREEN : tps > 13 ? YELLOW : tps > 10 ? GOLD : tps > 7 ? RED : DARK_RED; + statusMessage += `\n${GRAY}[${GOLD}TPS${GRAY}] ${tpsColor + tps.toFixed(1) + DARK_GRAY} tps`; } // CPS if (Toggles.cpsDisplay) { - const leftCPS = CPS.getLeftClicks(); - const leftColor = leftCPS < 3 ? GREEN : - leftCPS < 7 ? DARK_GREEN : - leftCPS < 13 ? YELLOW : - leftCPS < 21 ? GOLD : - leftCPS < 30 ? RED : DARK_RED; - const rightCPS = CPS.getRightClicks(); - const rightColor = rightCPS < 3 ? GREEN : - rightCPS < 7 ? DARK_GREEN : - rightCPS < 13 ? YELLOW : - rightCPS < 21 ? GOLD : - rightCPS < 30 ? RED : DARK_RED; - statusMessage += `\n${GRAY}[${GOLD}CPS${GRAY}] ${leftColor + leftCPS + DARK_GRAY} : ${rightColor + rightCPS}`; + const leftCPS = CPS.getLeftClicks(); + const leftColor = + leftCPS < 3 + ? GREEN + : leftCPS < 7 + ? DARK_GREEN + : leftCPS < 13 + ? YELLOW + : leftCPS < 21 + ? GOLD + : leftCPS < 30 + ? RED + : DARK_RED; + const rightCPS = CPS.getRightClicks(); + const rightColor = + rightCPS < 3 + ? GREEN + : rightCPS < 7 + ? DARK_GREEN + : rightCPS < 13 + ? YELLOW + : rightCPS < 21 + ? GOLD + : rightCPS < 30 + ? RED + : DARK_RED; + statusMessage += `\n${GRAY}[${GOLD}CPS${GRAY}] ${leftColor + leftCPS + DARK_GRAY} : ${rightColor + rightCPS}`; } // Day if (Toggles.dayDisplay) { - const daytime = (World.getTime() / 24000).toFixed(2); - statusMessage += `\n${GRAY}[${GOLD}Day${GRAY}] ${DARK_GRAY + daytime}`; + const daytime = (World.getTime() / 24000).toFixed(2); + statusMessage += `\n${GRAY}[${GOLD}Day${GRAY}] ${DARK_GRAY + daytime}`; } statusOverlay.setMessage(statusMessage.substring(1)); -}), () => Settings.serverStatus || Toggles.statusCommand); + }), + () => Settings.serverStatus || Toggles.statusCommand +); /** * Output status to user chat when user requests via command args. - * + * * @param {String} status - Status type to retrieve. */ export function getStatus(status) { - switch (status) { - case "ping": - const pingColor = ping < 100 ? GREEN : - ping < 200 ? DARK_GREEN : - ping < 300 ? YELLOW : - ping < 420 ? GOLD : - ping < 690 ? RED : DARK_RED; - ChatLib.chat(`${LOGO + DARK_AQUA + BOLD}Ping: ${pingColor + ping + AQUA} ms`); - break; - case "fps": - const fps = Client.getFPS(); - const fpsRatio = fps / SETTINGS.field_74350_i; - const fpsColor = fpsRatio > 0.9 ? GREEN : - fpsRatio > 0.8 ? DARK_GREEN : - fpsRatio > 0.7 ? YELLOW : - fpsRatio > 0.6 ? GOLD : - fpsRatio > 0.5 ? RED : DARK_RED; - ChatLib.chat(`${LOGO + DARK_AQUA + BOLD}FPS: ${fpsColor + fps + AQUA} fps`); - break; - case "tps": - const tpsColor = tps > 19 ? GREEN : - tps > 16 ? DARK_GREEN : - tps > 13 ? YELLOW : - tps > 10 ? GOLD : - tps > 7 ? RED : DARK_RED; - ChatLib.chat(`${LOGO + DARK_AQUA + BOLD}TPS: ${tpsColor + tps.toFixed(1) + AQUA} tps`); - break; - case "cps": - const leftCPS = CPS.getLeftClicks(); - const leftColor = leftCPS < 3 ? GREEN : - leftCPS < 7 ? DARK_GREEN : - leftCPS < 13 ? YELLOW : - leftCPS < 21 ? GOLD : - leftCPS < 30 ? RED : DARK_RED; - const rightCPS = CPS.getRightClicks(); - const rightColor = rightCPS < 3 ? GREEN : - rightCPS < 7 ? DARK_GREEN : - rightCPS < 13 ? YELLOW : - rightCPS < 21 ? GOLD : - rightCPS < 30 ? RED : DARK_RED; + switch (status) { + case "ping": + const pingColor = + ping < 100 + ? GREEN + : ping < 200 + ? DARK_GREEN + : ping < 300 + ? YELLOW + : ping < 420 + ? GOLD + : ping < 690 + ? RED + : DARK_RED; + ChatLib.chat(`${LOGO + DARK_AQUA + BOLD}Ping: ${pingColor + ping + AQUA} ms`); + break; + case "fps": + const fps = Client.getFPS(); + const fpsRatio = fps / SETTINGS.field_74350_i; + const fpsColor = + fpsRatio > 0.9 + ? GREEN + : fpsRatio > 0.8 + ? DARK_GREEN + : fpsRatio > 0.7 + ? YELLOW + : fpsRatio > 0.6 + ? GOLD + : fpsRatio > 0.5 + ? RED + : DARK_RED; + ChatLib.chat(`${LOGO + DARK_AQUA + BOLD}FPS: ${fpsColor + fps + AQUA} fps`); + break; + case "tps": + const tpsColor = + tps > 19 ? GREEN : tps > 16 ? DARK_GREEN : tps > 13 ? YELLOW : tps > 10 ? GOLD : tps > 7 ? RED : DARK_RED; + ChatLib.chat(`${LOGO + DARK_AQUA + BOLD}TPS: ${tpsColor + tps.toFixed(1) + AQUA} tps`); + break; + case "cps": + const leftCPS = CPS.getLeftClicks(); + const leftColor = + leftCPS < 3 + ? GREEN + : leftCPS < 7 + ? DARK_GREEN + : leftCPS < 13 + ? YELLOW + : leftCPS < 21 + ? GOLD + : leftCPS < 30 + ? RED + : DARK_RED; + const rightCPS = CPS.getRightClicks(); + const rightColor = + rightCPS < 3 + ? GREEN + : rightCPS < 7 + ? DARK_GREEN + : rightCPS < 13 + ? YELLOW + : rightCPS < 21 + ? GOLD + : rightCPS < 30 + ? RED + : DARK_RED; - ChatLib.chat(`${LOGO + DARK_AQUA + BOLD}CPS: ${leftColor + leftCPS + AQUA} : ${rightColor + rightCPS}`); - break; - case "yaw": - ChatLib.chat(`${LOGO + DARK_AQUA + BOLD}Yaw: ${AQUA + Player.getYaw()}°`); - break; - case "pitch": - ChatLib.chat(`${LOGO + DARK_AQUA + BOLD}Pitch: ${AQUA + Player.getPitch()}°`); - break; - case "dir": - case "direction": - const yaw = (Player.getYaw() + 360) % 360; - const direction = yaw >= 45 && yaw < 135 ? "West" : - yaw >= 135 && yaw < 255 ? "North" : - yaw >= 225 && yaw < 315 ? "East" : "South" - ChatLib.chat(`${LOGO + DARK_AQUA + BOLD}Dir: ${AQUA + direction}`) - break; - case "day": - const daytime = (World.getTime() / 24000).toFixed(2); - ChatLib.chat(`${DARK_AQUA + BOLD}Day: ${AQUA + daytime}`); - break; - } + ChatLib.chat(`${LOGO + DARK_AQUA + BOLD}CPS: ${leftColor + leftCPS + AQUA} : ${rightColor + rightCPS}`); + break; + case "yaw": + ChatLib.chat(`${LOGO + DARK_AQUA + BOLD}Yaw: ${AQUA + Player.getYaw()}°`); + break; + case "pitch": + ChatLib.chat(`${LOGO + DARK_AQUA + BOLD}Pitch: ${AQUA + Player.getPitch()}°`); + break; + case "dir": + case "direction": + const yaw = (Player.getYaw() + 360) % 360; + const direction = + yaw >= 45 && yaw < 135 + ? "West" + : yaw >= 135 && yaw < 255 + ? "North" + : yaw >= 225 && yaw < 315 + ? "East" + : "South"; + ChatLib.chat(`${LOGO + DARK_AQUA + BOLD}Dir: ${AQUA + direction}`); + break; + case "day": + const daytime = (World.getTime() / 24000).toFixed(2); + ChatLib.chat(`${DARK_AQUA + BOLD}Day: ${AQUA + daytime}`); + break; + } } - /** * Crosshair */ -registerWhen(register("renderCrosshair", () => { +registerWhen( + register("renderCrosshair", () => { const yaw = Player.getYaw().toFixed(2); const pitch = Player.getPitch().toFixed(2); const height = Renderer.screen.getHeight(); const width = Renderer.screen.getWidth(); - const offset = 9 * Renderer.screen.getScale() / 2 + Renderer.screen.getScale(); + const offset = (9 * Renderer.screen.getScale()) / 2 + Renderer.screen.getScale(); Renderer.drawString(GRAY + yaw, width / 2 + offset, height / 2 - 4.5, true); Renderer.drawString(GRAY + pitch, (width - Renderer.getStringWidth(pitch)) / 2, height / 2 + offset, true); -}), () => Toggles.angleDisplay > 1); - + }), + () => Toggles.angleDisplay > 1 +); /** * Check entity distance to player. Hide if too close or far. */ -registerWhen(register("renderEntity", (entity, _, __, event) => { +registerWhen( + register("renderEntity", (entity, _, __, event) => { const distance = entity.distanceTo(Player.asPlayerMP()); - if (Settings.hideFarEntity !== 0 && distance >= Settings.hideFarEntity) - cancel(event); - if (Settings.hideCloseEntity !== 0 && distance <= Settings.hideCloseEntity && entity.name !== Player.name && isPlayer(entity)) - cancel(event); -}).setPriority(Priority.LOWEST), () => { + if (Settings.hideFarEntity !== 0 && distance >= Settings.hideFarEntity) cancel(event); + if ( + Settings.hideCloseEntity !== 0 && + distance <= Settings.hideCloseEntity && + entity.name !== Player.name && + isPlayer(entity) + ) + cancel(event); + }).setPriority(Priority.LOWEST), + () => { if (Settings.hideFarEntity === 0 && Settings.hideCloseEntity === 0) return false; const world = location.getWorld()?.toLowerCase() ?? ""; const worlds = Settings.hideWorlds.toLowerCase().split(", "); return worlds[0] === "" || worlds.includes(world); -}); - + } +); /** * Prevents any particles from rendering. */ -registerWhen(register("spawnParticle", (particle, type, event) => { +registerWhen( + register("spawnParticle", (particle, type, event) => { cancel(event); -}).setPriority(Priority.LOWEST), () => Settings.hideParticles); + }).setPriority(Priority.LOWEST), + () => Settings.hideParticles +); diff --git a/features/general/ReminderTimer.js b/features/general/ReminderTimer.js index 8fde626..e8a3c70 100644 --- a/features/general/ReminderTimer.js +++ b/features/general/ReminderTimer.js @@ -1,9 +1,8 @@ -import Settings from "../../utils/Settings"; import { AMOGUS, BOLD, GOLD } from "../../utils/Constants"; -import { playSound } from "../../utils/functions/misc"; import { registerWhen } from "../../utils/RegisterTils"; +import Settings from "../../utils/Settings"; import { setTitle } from "../../utils/Title"; - +import { playSound } from "../../utils/functions/misc"; /** * Variable to represent minutes passed for timer. @@ -13,11 +12,14 @@ let minutes = 0; /** * Counts minutes until set timer to send an customizable alert. */ -registerWhen(register("step", () => { +registerWhen( + register("step", () => { minutes++; if (minutes >= Settings.reminderTime) { - setTitle(`${GOLD + BOLD + Settings.reminderText}`, "", 10, 50, 10, 99); - playSound(AMOGUS, 1000); - minutes = 0; + setTitle(`${GOLD + BOLD + Settings.reminderText}`, "", 10, 50, 10, 99); + playSound(AMOGUS, 1000); + minutes = 0; } -}).setDelay(60), () => Settings.reminderTime !== 0); + }).setDelay(60), + () => Settings.reminderTime !== 0 +); diff --git a/features/general/RemoveSelfie.js b/features/general/RemoveSelfie.js index 9c3f5d0..3dceb5d 100644 --- a/features/general/RemoveSelfie.js +++ b/features/general/RemoveSelfie.js @@ -1,6 +1,5 @@ -import Settings from "../../utils/Settings"; import { registerWhen } from "../../utils/RegisterTils"; - +import Settings from "../../utils/Settings"; /** * Variables used to represent F5 key bind. @@ -11,19 +10,17 @@ let keyPressed = false; /** * Removes first person view in f5. (ty boppeler21 qt) */ -registerWhen(register('tick', () => { +registerWhen( + register("tick", () => { try { - if (Client.settings.getSettings().field_74320_O === 2) - Client.settings.getSettings().field_74320_O = 0; - else if (Keyboard.isKeyDown(key.getKeyCode()) && !keyPressed) { - if (Client.settings.getSettings().field_74320_O === 1) - Client.settings.getSettings().field_74320_O = 2; - keyPressed = true; - } - else if (!Keyboard.isKeyDown(key.getKeyCode()) && keyPressed) - keyPressed = false; + if (Client.settings.getSettings().field_74320_O === 2) Client.settings.getSettings().field_74320_O = 0; + else if (Keyboard.isKeyDown(key.getKeyCode()) && !keyPressed) { + if (Client.settings.getSettings().field_74320_O === 1) Client.settings.getSettings().field_74320_O = 2; + keyPressed = true; + } else if (!Keyboard.isKeyDown(key.getKeyCode()) && keyPressed) keyPressed = false; } catch (err) { - if (Client.settings.getSettings().field_74320_O === 2) - Client.settings.getSettings().field_74320_O = 0; + if (Client.settings.getSettings().field_74320_O === 2) Client.settings.getSettings().field_74320_O = 0; } -}), () => Settings.removeSelfie === true); + }), + () => Settings.removeSelfie === true +); diff --git a/features/general/ServerAlert.js b/features/general/ServerAlert.js index a541231..04e549d 100644 --- a/features/general/ServerAlert.js +++ b/features/general/ServerAlert.js @@ -1,9 +1,8 @@ +import { BOLD, DARK_RED, WHITE } from "../../utils/Constants"; import location from "../../utils/Location"; +import { registerWhen } from "../../utils/RegisterTils"; import Settings from "../../utils/Settings"; -import { BOLD, DARK_RED, WHITE } from "../../utils/Constants"; import { formatTime } from "../../utils/functions/format"; -import { registerWhen } from "../../utils/RegisterTils"; - /** * Dictionary to track servers and their last join time. @@ -13,20 +12,26 @@ let servers = {}; /** * Replaces joining message if player has been in server in past X minutes. */ -registerWhen(register("chat", (server, event) => { +registerWhen( + register("chat", (server, event) => { if (servers?.[server] === undefined) return; const timeDiff = (Date.now() - servers[server]) / 1000; cancel(event); ChatLib.chat(`${DARK_RED + BOLD}Recent Server: ${WHITE + server + DARK_RED + BOLD}!`); ChatLib.chat(`${DARK_RED + BOLD}Last Joined: ${WHITE + formatTime(timeDiff, 2)} ago!`); -}).setCriteria("Sending to server ${server}..."), () => Settings.serverAlert); + }).setCriteria("Sending to server ${server}..."), + () => Settings.serverAlert +); /** * Clears server entry after X minutes when leaving it. */ -registerWhen(register("worldUnload", () => { +registerWhen( + register("worldUnload", () => { const server = location.getServer(); if (server === undefined) return; servers[server] = Date.now(); -}), () => Settings.serverAlert); + }), + () => Settings.serverAlert +); diff --git a/features/general/SkillTracker.js b/features/general/SkillTracker.js index 7569b51..5f6f724 100644 --- a/features/general/SkillTracker.js +++ b/features/general/SkillTracker.js @@ -1,34 +1,32 @@ -import Settings from "../../utils/Settings"; import { BOLD, DARK_AQUA, GREEN, RED, WHITE } from "../../utils/Constants"; -import { commafy, formatTime, romanToNum, unformatNumber } from "../../utils/functions/format"; -import { registerWhen } from "../../utils/RegisterTils"; +import { data } from "../../utils/Data"; import { Overlay } from "../../utils/Overlay"; +import { registerWhen } from "../../utils/RegisterTils"; +import Settings from "../../utils/Settings"; import { Stat, getPaused } from "../../utils/Stat"; -import { data } from "../../utils/Data"; import { setTitle } from "../../utils/Title"; - +import { commafy, formatTime, romanToNum, unformatNumber } from "../../utils/functions/format"; /** * Variables to track and display skill tracker overlay. */ const skills = { - "None": new Stat(), - "Fishing": new Stat(), - "Alchemy": new Stat(), - "Runecrafting": new Stat(), - "Mining": new Stat(), - "Farming": new Stat(), - "Enchanting": new Stat(), - "Taming": new Stat(), - "Foraging": new Stat(), - "Social": new Stat(), - "Carpentry": new Stat(), - "Combat": new Stat() -} + None: new Stat(), + Fishing: new Stat(), + Alchemy: new Stat(), + Runecrafting: new Stat(), + Mining: new Stat(), + Farming: new Stat(), + Enchanting: new Stat(), + Taming: new Stat(), + Foraging: new Stat(), + Social: new Stat(), + Carpentry: new Stat(), + Combat: new Stat(), +}; let skillsTracked = false; let current = "None"; -const skillExample = -`${DARK_AQUA + BOLD}Skill: ${WHITE}None +const skillExample = `${DARK_AQUA + BOLD}Skill: ${WHITE}None ${DARK_AQUA + BOLD}Gain: ${WHITE}0 ${DARK_AQUA + BOLD}Time: ${RED}Inactive ${DARK_AQUA + BOLD}Rate: ${WHITE}0 xp/hr @@ -36,69 +34,78 @@ ${DARK_AQUA + BOLD}Level Up: ${GREEN}MAXED`; const skillOverlay = new Overlay("skillTracker", data.AL, "moveSkills", skillExample); skillOverlay.setMessage(skillExample); -const xpTable = [0, 50, 175, 375, 675, 1175, 1925, 2925, 4425, 6425, 9925, 14925, 22425, 32425, 47425, 67425, 97425, 147425, 222425, 322425, 522425, 822425, 1222425, - 1722425, 2322425, 3022425, 3822425, 4722425, 5722425, 6822425, 8022425, 9322425, 10722425, 12222425, 13822425, 15522425, 17322425, 19222425, 21222425, 23322425, - 25522425, 27822425, 30222425, 32722425, 35322425, 38072425, 40972425, 44072425, 47472425, 51172425, 55172425, 59472425, 64072425, 68972425, 74172425, 79672425, - 85472425, 91572425, 97972425, 104672425, 111672425]; +const xpTable = [ + 0, 50, 175, 375, 675, 1175, 1925, 2925, 4425, 6425, 9925, 14925, 22425, 32425, 47425, 67425, 97425, 147425, 222425, + 322425, 522425, 822425, 1222425, 1722425, 2322425, 3022425, 3822425, 4722425, 5722425, 6822425, 8022425, 9322425, + 10722425, 12222425, 13822425, 15522425, 17322425, 19222425, 21222425, 23322425, 25522425, 27822425, 30222425, + 32722425, 35322425, 38072425, 40972425, 44072425, 47472425, 51172425, 55172425, 59472425, 64072425, 68972425, + 74172425, 79672425, 85472425, 91572425, 97972425, 104672425, 111672425, +]; const trackSkills = register("guiOpened", () => { - Client.scheduleTask(3, () => { - if (Player.getContainer().getName() !== "Your Skills") return; - - const items = Player.getContainer().getItems(); - items.forEach(item => { - if (item === null) return; - const names = item.getName().removeFormatting().split(' '); - const skill = names[0]; - const level = romanToNum(names[1]); - if (!(skill in skills) || isNaN(level)) return; - - const lore = item.getLore(); - const maxIndex = lore.findIndex(line => line === "§5§o§7§8Max Skill level reached!"); - const progressIndex = lore.findIndex(line => line.startsWith("§5§o§7Progress")); - const xp = parseFloat( - maxIndex !== -1 ? lore[maxIndex + 1].split('§6')[1].replace(/,/g, '') : - lore[progressIndex + 1].split('§e')[1].split('§6')[0].replace(/,/g, '') - ) + (maxIndex !== -1 ? xpTable[level - 1] : xpTable[level]); - - skills[skill].level = level; - skills[skill].start = xp; - skills[skill].now = xp; - skills[skill].since = 600; - }); - - skillsTracked = true; - trackSkills.unregister(); - if (Settings.skillTracker !== 0) setTitle(`${GREEN}Skills tracked!`, "Now begin the grind.", 10, 50, 10, 80); + Client.scheduleTask(3, () => { + if (Player.getContainer().getName() !== "Your Skills") return; + + const items = Player.getContainer().getItems(); + items.forEach((item) => { + if (item === null) return; + const names = item.getName().removeFormatting().split(" "); + const skill = names[0]; + const level = romanToNum(names[1]); + if (!(skill in skills) || isNaN(level)) return; + + const lore = item.getLore(); + const maxIndex = lore.findIndex((line) => line === "§5§o§7§8Max Skill level reached!"); + const progressIndex = lore.findIndex((line) => line.startsWith("§5§o§7Progress")); + const xp = + parseFloat( + maxIndex !== -1 + ? lore[maxIndex + 1].split("§6")[1].replace(/,/g, "") + : lore[progressIndex + 1].split("§e")[1].split("§6")[0].replace(/,/g, "") + ) + (maxIndex !== -1 ? xpTable[level - 1] : xpTable[level]); + + skills[skill].level = level; + skills[skill].start = xp; + skills[skill].now = xp; + skills[skill].since = 600; }); + + skillsTracked = true; + trackSkills.unregister(); + if (Settings.skillTracker !== 0) setTitle(`${GREEN}Skills tracked!`, "Now begin the grind.", 10, 50, 10, 80); + }); }); -registerWhen(register("chat", () => { +registerWhen( + register("chat", () => { skillsTracked = false; trackSkills.register(); -}).setCriteria("Switching to profile ${profile}..."), () => Settings.skillTracker !== 0); + }).setCriteria("Switching to profile ${profile}..."), + () => Settings.skillTracker !== 0 +); /** * Resets skill overlay to base state. */ register("command", () => { - skillsTracked = false; - trackSkills.register(); - - Object.keys(skills).forEach(skill => { - skills[skill].time = 0; - skills[skill].since = 600; - }); - setTitle(`${GREEN}Successfully reset skills!`, "Please open skills menu to retrack.", 10, 50, 10, 81); + skillsTracked = false; + trackSkills.register(); + + Object.keys(skills).forEach((skill) => { + skills[skill].time = 0; + skills[skill].since = 600; + }); + setTitle(`${GREEN}Successfully reset skills!`, "Please open skills menu to retrack.", 10, 50, 10, 81); }).setName("resetSkills"); /** * Uses action bar to detect skill xp gains for maxed out skills. */ -registerWhen(register("actionBar", (health, gain, type, amount, next, mana) => { +registerWhen( + register("actionBar", (health, gain, type, amount, next, mana) => { if (!skillsTracked) { - setTitle(`${RED}Skills not tracked!`, "Please open skills menu to track.", 0, 50, 10, 79); - return; + setTitle(`${RED}Skills not tracked!`, "Please open skills menu to track.", 0, 50, 10, 79); + return; } if (getPaused()) return; @@ -106,23 +113,26 @@ registerWhen(register("actionBar", (health, gain, type, amount, next, mana) => { current = type; const skill = skills[current]; if (skill === undefined) return; - + // Update info amount = unformatNumber(amount); skill.now = xpTable[skill.level] + amount; skill.since = 0; -}).setCriteria("${health}+${gain} ${type} (${amount}/${next})${mana}"), () => Settings.skillTracker !== 0); + }).setCriteria("${health}+${gain} ${type} (${amount}/${next})${mana}"), + () => Settings.skillTracker !== 0 +); /** * Uses action bar to detect skill xp gains for non maxed out skills. */ -registerWhen(register("actionBar", (health, gain, type, percent, mana) => { +registerWhen( + register("actionBar", (health, gain, type, percent, mana) => { if (!skillsTracked) { - setTitle(`${RED}Skills not tracked!`, "Please open skills menu to track.", 0, 50, 10, 79); - return; + setTitle(`${RED}Skills not tracked!`, "Please open skills menu to track.", 0, 50, 10, 79); + return; } if (getPaused()) return; - + // Update info; current = type; const skill = skills[current]; @@ -132,35 +142,40 @@ registerWhen(register("actionBar", (health, gain, type, percent, mana) => { percent = parseFloat(percent) / 100; skill.now = xpTable[skill.level + 1] - (xpTable[skill.level + 1] - xpTable[skill.level]) * (1 - percent); skill.since = 0; -}).setCriteria("${health}+${gain} ${type} (${percent}%)${mana}"), () => Settings.skillTracker !== 0); + }).setCriteria("${health}+${gain} ${type} (${percent}%)${mana}"), + () => Settings.skillTracker !== 0 +); /** * Tracks skill level ups */ -registerWhen(register("chat", (skill) => { +registerWhen( + register("chat", (skill) => { skills[skill].level += 1; -}).setCriteria(" SKILL LEVEL UP ${skill} ${from}➜${to}"), () => Settings.skillTracker !== 0); + }).setCriteria(" SKILL LEVEL UP ${skill} ${from}➜${to}"), + () => Settings.skillTracker !== 0 +); /** * Updates skill tracker data every second. */ -registerWhen(register("step", () => { +registerWhen( + register("step", () => { if (getPaused() || current === "None") return; - + // Update tracker let skill = skills[current]; if (skill === undefined) return; const rate = skill.getRate(); if (skill.since < Settings.skillTracker * 60) { - skill.since += 1; - skill.time += 1; + skill.since += 1; + skill.time += 1; } - + // Set HUD const timeDisplay = skill.since < Settings.skillTracker * 60 ? formatTime(skill.time) : `${RED}Inactive`; - let skillMessage = -`${DARK_AQUA + BOLD}Skill: ${WHITE + current} + let skillMessage = `${DARK_AQUA + BOLD}Skill: ${WHITE + current} ${DARK_AQUA + BOLD}Gain: ${WHITE + commafy(skill.getGain())} xp ${DARK_AQUA + BOLD}Time: ${WHITE + timeDisplay} ${DARK_AQUA + BOLD}Rate: ${WHITE + commafy(rate)} xp/hr @@ -168,10 +183,12 @@ ${DARK_AQUA + BOLD}Level Up: `; // Set time until next if (skill.level !== 60) { - const neededXP = xpTable[skill.level + 1] - skill.now; - if (neededXP > 0) skillMessage += `${WHITE + formatTime(neededXP / rate * 3600)}`; - else skillMessage += `${GREEN}MAXED`; + const neededXP = xpTable[skill.level + 1] - skill.now; + if (neededXP > 0) skillMessage += `${WHITE + formatTime((neededXP / rate) * 3600)}`; + else skillMessage += `${GREEN}MAXED`; } else skillMessage += `${GREEN}MAXED`; skillOverlay.setMessage(skillMessage); -}).setFps(1), () => Settings.skillTracker !== 0); + }).setFps(1), + () => Settings.skillTracker !== 0 +); diff --git a/features/general/SkyBlockWaypoints.js b/features/general/SkyBlockWaypoints.js index 8b8a604..aa2b4b2 100644 --- a/features/general/SkyBlockWaypoints.js +++ b/features/general/SkyBlockWaypoints.js @@ -1,85 +1,85 @@ -import location from "../../utils/Location"; -import Waypoint from "../../utils/Waypoint"; -import { AQUA, BOLD, DARK_GRAY, GOLD, GRAY, GREEN, ITALIC, LOGO, RED, WHITE, YELLOW } from "../../utils/Constants"; +import { AQUA, BOLD, DARK_GRAY, GOLD, GREEN, LOGO, RED, WHITE, YELLOW } from "../../utils/Constants"; import { Json } from "../../utils/Json"; import { printList } from "../../utils/ListTils"; - +import location from "../../utils/Location"; +import Waypoint from "../../utils/Waypoint"; const NPCS = new Json("npcs.json", false, false).getData(); -const npcWaypoints = new Waypoint([1, 1, 1]); // White NPCs +const npcWaypoints = new Waypoint([1, 1, 1]); // White NPCs const LOCATIONS = new Json("locations.json", false, false).getData(); -const locationWaypoints = new Waypoint([0, 1, 1]); // Aqua Locations +const locationWaypoints = new Waypoint([0, 1, 1]); // Aqua Locations /** * Update Skyblock Waypoints. - * + * * @param {String} type - NPC or Zone. * @param {String} command - add, clear, list, help. * @param {String} name - Name of the NPC or Zone. */ export function updateSBW(type, command, name) { - const world = location.getWorld(); - const base = type === "NPC" ? NPCS[world] : LOCATIONS[world]; - const waypoints = type === "NPC" ? npcWaypoints : locationWaypoints; - if (base === undefined) { - ChatLib.chat(`${LOGO + RED}Error: No ${type}s found in ${world}.`); - return; - } + const world = location.getWorld(); + const base = type === "NPC" ? NPCS[world] : LOCATIONS[world]; + const waypoints = type === "NPC" ? npcWaypoints : locationWaypoints; + if (base === undefined) { + ChatLib.chat(`${LOGO + RED}Error: No ${type}s found in ${world}.`); + return; + } - switch (command) { - case "add": - // Check if the name exists - const locs = base[name]; - if (locs === undefined) { - ChatLib.chat(`${LOGO + RED}Error: Could not find ${type} "${name}" in ${world}.`); - return; - } + switch (command) { + case "add": + // Check if the name exists + const locs = base[name]; + if (locs === undefined) { + ChatLib.chat(`${LOGO + RED}Error: Could not find ${type} "${name}" in ${world}.`); + return; + } - // Add all locations to waypoints - if (type === "NPC") - locs.forEach(loc => { - if (loc[1] === "") return; - const x = Math.floor(loc[1]) + 1; - const y = Math.floor(loc[2]); - const z = Math.floor(loc[3]) + 1; - waypoints.push([name, x, y, z]); - }); - else waypoints.push([name, ...locs]); + // Add all locations to waypoints + if (type === "NPC") + locs.forEach((loc) => { + if (loc[1] === "") return; + const x = Math.floor(loc[1]) + 1; + const y = Math.floor(loc[2]); + const z = Math.floor(loc[3]) + 1; + waypoints.push([name, x, y, z]); + }); + else waypoints.push([name, ...locs]); - ChatLib.chat(`${LOGO + GREEN}Added "${name}" to ${type} waypoints.`); - break; - case "clear": - // Clear all waypoints - waypoints.clear(); - ChatLib.chat(`${LOGO + GREEN}Cleared ${type} waypoints.`); - break; - case "list": - const formatted = {}; - Object.keys(base).forEach(key => { - let value = `${AQUA + key}:`; - if (type === "NPC") - base[key].forEach(loc => { - const x = loc[1] === "" ? RED + "?" : WHITE + loc[1]; - const y = loc[2] === "" ? "?" : loc[2]; - const z = loc[3] === "" ? "?" : loc[3]; - value += `\n${DARK_GRAY} - ${YELLOW + loc[0]}: ${x}, ${y}, ${z}`; - }); - else value += `\n${DARK_GRAY} - ${YELLOW}Origin: ${WHITE + base[key][0]}, ${base[key][1]}, ${base[key][2]}`; - formatted[key] = value + `\n\n${YELLOW}Click to add ${AQUA + key + YELLOW} to waypoints.`;; - }); - printList(formatted, type, name, 12, true, "add", true); - break; - case "help": - default: - if (command !== "help") ChatLib.chat(`${LOGO + RED}Error: Invalid argument "${command}"!\n`); - ChatLib.chat( -`${LOGO + GOLD + BOLD}Waypoint Commands: + ChatLib.chat(`${LOGO + GREEN}Added "${name}" to ${type} waypoints.`); + break; + case "clear": + // Clear all waypoints + waypoints.clear(); + ChatLib.chat(`${LOGO + GREEN}Cleared ${type} waypoints.`); + break; + case "list": + const formatted = {}; + Object.keys(base).forEach((key) => { + let value = `${AQUA + key}:`; + if (type === "NPC") + base[key].forEach((loc) => { + const x = loc[1] === "" ? RED + "?" : WHITE + loc[1]; + const y = loc[2] === "" ? "?" : loc[2]; + const z = loc[3] === "" ? "?" : loc[3]; + value += `\n${DARK_GRAY} - ${YELLOW + loc[0]}: ${x}, ${y}, ${z}`; + }); + else value += `\n${DARK_GRAY} - ${YELLOW}Origin: ${WHITE + base[key][0]}, ${base[key][1]}, ${base[key][2]}`; + formatted[key] = value + `\n\n${YELLOW}Click to add ${AQUA + key + YELLOW} to waypoints.`; + }); + printList(formatted, type, name, 12, true, "add", true); + break; + case "help": + default: + if (command !== "help") ChatLib.chat(`${LOGO + RED}Error: Invalid argument "${command}"!\n`); + ChatLib.chat( + `${LOGO + GOLD + BOLD}Waypoint Commands: ${DARK_GRAY}- ${GOLD}Base: ${YELLOW}/va [npc, zone] ${DARK_GRAY}- ${GOLD}add : ${YELLOW}Add waypoint using key name. ${DARK_GRAY}- ${GOLD}clear: ${YELLOW}Delete all waypoints. ${DARK_GRAY}- ${GOLD}list: ${YELLOW}List all valid keys. - ${DARK_GRAY}- ${GOLD}help: ${YELLOW}Displays this help message.`); - break; - } + ${DARK_GRAY}- ${GOLD}help: ${YELLOW}Displays this help message.` + ); + break; + } } diff --git a/features/general/SkyCrypt.js b/features/general/SkyCrypt.js index 1b2ca00..5556933 100644 --- a/features/general/SkyCrypt.js +++ b/features/general/SkyCrypt.js @@ -1,17 +1,18 @@ import { GREEN, LOGO, RED, WHITE } from "../../utils/Constants"; - /** * Opens the SkyCrypt page of inputted ign. */ register("command", (name) => { - if (name === undefined) { - ChatLib.chat(`${LOGO + RED}Please input as: ${WHITE}/sk [ign]`); - return; - } + if (name === undefined) { + ChatLib.chat(`${LOGO + RED}Please input as: ${WHITE}/sk [ign]`); + return; + } - const Desktop = Java.type('java.awt.Desktop'); - const URI = Java.type('java.net.URI'); - Desktop.getDesktop().browse(new URI(`https://sky.shiiyu.moe/stats/${name}`)); - ChatLib.chat(`${LOGO + GREEN}Opening ${name + (name[name.length - 1] === 's' ? "'" : "'s")} SkyCrypt profile!`); -}).setName("sk", true).setAliases("skycrypt"); \ No newline at end of file + const Desktop = Java.type("java.awt.Desktop"); + const URI = Java.type("java.net.URI"); + Desktop.getDesktop().browse(new URI(`https://sky.shiiyu.moe/stats/${name}`)); + ChatLib.chat(`${LOGO + GREEN}Opening ${name + (name[name.length - 1] === "s" ? "'" : "'s")} SkyCrypt profile!`); +}) + .setName("sk", true) + .setAliases("skycrypt"); diff --git a/features/general/SpamHider.js b/features/general/SpamHider.js index 670f6b6..f32741a 100644 --- a/features/general/SpamHider.js +++ b/features/general/SpamHider.js @@ -1,18 +1,20 @@ -import { registerWhen } from "../../utils/RegisterTils"; import { data } from "../../utils/Data"; - +import { registerWhen } from "../../utils/RegisterTils"; /** * Removes any identical chat messages in spamlist from chat. */ -registerWhen(register("chat", (text, event) => { +registerWhen( + register("chat", (text, event) => { for (let i in data.spamlist) { - let regexPattern = data.spamlist[i].replace(/\${\w+}/g, '(.*?)'); - let regex = new RegExp('^' + regexPattern + '$', 'i'); - - if (regex.test(text)) { - cancel(event); - break; - } + let regexPattern = data.spamlist[i].replace(/\${\w+}/g, "(.*?)"); + let regex = new RegExp("^" + regexPattern + "$", "i"); + + if (regex.test(text)) { + cancel(event); + break; + } } -}).setCriteria("${text}"), () => data.spamlist.length !== 0); + }).setCriteria("${text}"), + () => data.spamlist.length !== 0 +); diff --git a/features/general/Statistics.js b/features/general/Statistics.js index b864a92..6b702d0 100644 --- a/features/general/Statistics.js +++ b/features/general/Statistics.js @@ -1,18 +1,32 @@ +import { + AQUA, + BOLD, + DARK_AQUA, + DARK_BLUE, + DARK_GRAY, + DARK_GREEN, + DARK_PURPLE, + DARK_RED, + GOLD, + GRAY, + GREEN, + LOGO, + PLAYER_CLASS, + RED, + YELLOW, +} from "../../utils/Constants"; +import { data } from "../../utils/Data"; +import { Overlay } from "../../utils/Overlay"; +import { registerWhen } from "../../utils/RegisterTils"; import Settings from "../../utils/Settings"; import Toggles from "../../utils/Toggles"; -import { AQUA, BOLD, DARK_AQUA, DARK_BLUE, DARK_GRAY, DARK_GREEN, DARK_PURPLE, DARK_RED, GOLD, GRAY, GREEN, LOGO, PLAYER_CLASS, RED, WHITE, YELLOW } from "../../utils/Constants"; import { formatNumber, formatTime } from "../../utils/functions/format"; -import { Overlay } from "../../utils/Overlay"; import { isPlayer } from "../../utils/functions/player"; -import { registerWhen } from "../../utils/RegisterTils"; -import { data } from "../../utils/Data"; - /** * Stats display overlay variables */ -const statsExample = -`${GRAY}[${GOLD}Pet${GRAY}] ${GREEN}-..- +const statsExample = `${GRAY}[${GOLD}Pet${GRAY}] ${GREEN}-..- ${GRAY}[${GOLD}Legion${GRAY}] ${RED}0 ${DARK_GRAY}(0%) ${GRAY}[${GOLD}SF${GRAY}] ${GREEN}/ -.. ${AQUA}⸎ ${GRAY}[${GOLD}Daily PT${GRAY}] ${GREEN}/ -..`; @@ -22,57 +36,60 @@ const statsOverlay = new Overlay("statsDisplay", data.YL, "moveStats", statsExam * Get equipped pet through tab widget, menu, or chat. */ let petWidget = false; -registerWhen(register("step", () => { +registerWhen( + register("step", () => { if (!World.isLoaded()) return; const tabNames = TabList.getNames(); - const petIndex = tabNames.findIndex(tab => tab === "§r§e§lPet:§r"); + const petIndex = tabNames.findIndex((tab) => tab === "§r§e§lPet:§r"); if (petIndex !== -1) { - petWidget = true; - data.pet = tabNames[petIndex + 1].substring(3); + petWidget = true; + data.pet = tabNames[petIndex + 1].substring(3); - const petXP = tabNames[petIndex + 2].split(' ')[1]; - if (petXP != "§r§b§lMAX" && !data.pet.startsWith("§r§7No pet")) data.pet += `\n ${petXP} XP`; + const petXP = tabNames[petIndex + 2].split(" ")[1]; + if (petXP != "§r§b§lMAX" && !data.pet.startsWith("§r§7No pet")) data.pet += `\n ${petXP} XP`; } else petWidget = false; -}).setFps(1), () => Settings.statsDisplay && Toggles.petDisplay); + }).setFps(1), + () => Settings.statsDisplay && Toggles.petDisplay +); register("guiOpened", () => { - Client.scheduleTask(1, () => { - const container = Player.getContainer(); - if (!container.getName().startsWith("Pets (")) return; - - // Loop through pet menu - const pets = container.getItems(); - for (let i = 1; i <= 4; i++) { - for (let j = 1; j <= 7; j++) { - let pet = pets[i * 9 + j]; - if (pet.getLore().find(lore => lore === "§5§o§7§cClick to despawn!") !== undefined) { - let name = pet.getName(); - data.pet = name.substring(name.indexOf(']') + 2); - } - } + Client.scheduleTask(1, () => { + const container = Player.getContainer(); + if (!container.getName().startsWith("Pets (")) return; + + // Loop through pet menu + const pets = container.getItems(); + for (let i = 1; i <= 4; i++) { + for (let j = 1; j <= 7; j++) { + let pet = pets[i * 9 + j]; + if (pet.getLore().find((lore) => lore === "§5§o§7§cClick to despawn!") !== undefined) { + let name = pet.getName(); + data.pet = name.substring(name.indexOf("]") + 2); } - }); + } + } + }); }); register("chat", (pet) => { - if (!petWidget) data.pet = pet.substring(pet.indexOf(']') + 2); + if (!petWidget) data.pet = pet.substring(pet.indexOf("]") + 2); }).setCriteria("&cAutopet &eequipped your ${pet}&e! &a&lVIEW RULE&r"); register("chat", (pet) => { - if (!petWidget) data.pet = pet; + if (!petWidget) data.pet = pet; }).setCriteria("&r&aYou summoned your ${pet}&r&a!&r"); // Check for Montezuma let lastPet = data.pet; const revertPet = register("worldUnload", () => { - data.pet = lastPet; - revertPet.unregister(); + data.pet = lastPet; + revertPet.unregister(); }).unregister(); register("chat", () => { - const mont = TabList.getNames().find(name => name.startsWith("§r Montezuma:")); - if (mont !== undefined) { - lastPet = data.pet; - data.pet = (mont[18] >= 6 ? DARK_PURPLE : DARK_BLUE) + "Montezuma"; - revertPet.register(); - } + const mont = TabList.getNames().find((name) => name.startsWith("§r Montezuma:")); + if (mont !== undefined) { + lastPet = data.pet; + data.pet = (mont[18] >= 6 ? DARK_PURPLE : DARK_BLUE) + "Montezuma"; + revertPet.register(); + } }).setCriteria(" RIFT INSTABILITY WARNING"); /** @@ -80,123 +97,178 @@ register("chat", () => { */ let soulflow = 0; register("step", () => { - const container = Player.getContainer(); - if (container === null) return; + const container = Player.getContainer(); + if (container === null) return; - container.getItems().forEach(item => { - if (item !== null && item.getName().includes("Soulflow")) { - const internal = item.getLore()[1].removeFormatting(); - if (internal.startsWith("Internalized:")) soulflow = internal.replace(/[^0-9]/g, ''); - } - }); + container.getItems().forEach((item) => { + if (item !== null && item.getName().includes("Soulflow")) { + const internal = item.getLore()[1].removeFormatting(); + if (internal.startsWith("Internalized:")) soulflow = internal.replace(/[^0-9]/g, ""); + } + }); }).setDelay(5); /** * Count daily playtime */ register("step", () => { - if (!World.isLoaded()) return; + if (!World.isLoaded()) return; - const today = new Date().getDate(); - if (data.lastDay !== today) { - data.playtime = 0; - data.lastDay = today; - } + const today = new Date().getDate(); + if (data.lastDay !== today) { + data.playtime = 0; + data.lastDay = today; + } - const hours = Math.round(data.playtime / 3_600); - if(++data.playtime % 28_800 === 0 && data.playtime >= 28_800) - ChatLib.chat(`${LOGO + YELLOW}You have been playing for ${hours} hours. Excessive game playing may cause problems in your normal daily life.`); + const hours = Math.round(data.playtime / 3_600); + if (++data.playtime % 28_800 === 0 && data.playtime >= 28_800) + ChatLib.chat( + `${ + LOGO + YELLOW + }You have been playing for ${hours} hours. Excessive game playing may cause problems in your normal daily life.` + ); }).setFps(1); /** * Update statsOverlay message */ -registerWhen(register("tick", () => { +registerWhen( + register("tick", () => { let statsMessage = ""; // Pet if (Toggles.petDisplay) { - let pet = data.pet.length > 36 && !data.pet.startsWith("§r§7No pet") ? data.pet.split(' ').slice(2).join(' ') : data.pet; - statsMessage += `${GRAY}[${GOLD}Pet${GRAY}] ${pet}\n`; + let pet = + data.pet.length > 36 && !data.pet.startsWith("§r§7No pet") ? data.pet.split(" ").slice(2).join(" ") : data.pet; + statsMessage += `${GRAY}[${GOLD}Pet${GRAY}] ${pet}\n`; } // Legion if (Toggles.legionDisplay) { - const player = Player.asPlayerMP(); - const legionCount = World.getAllEntitiesOfType(PLAYER_CLASS).filter(other => isPlayer(other) && player.distanceTo(other) < 30).length; - const legionPercent = Math.round(Math.min(1, legionCount / 20) * 100); - const legionColor = legionCount > 16 ? GREEN : - legionCount > 12 ? DARK_GREEN : - legionCount > 8 ? YELLOW : - legionCount > 4 ? GOLD : - legionColor > 0 ? RED : DARK_RED; - statsMessage += `${GRAY}[${GOLD}Legion${GRAY}] ${legionColor + legionCount + DARK_GRAY} (${legionPercent}%)\n`; + const player = Player.asPlayerMP(); + const legionCount = World.getAllEntitiesOfType(PLAYER_CLASS).filter( + (other) => isPlayer(other) && player.distanceTo(other) < 30 + ).length; + const legionPercent = Math.round(Math.min(1, legionCount / 20) * 100); + const legionColor = + legionCount > 16 + ? GREEN + : legionCount > 12 + ? DARK_GREEN + : legionCount > 8 + ? YELLOW + : legionCount > 4 + ? GOLD + : legionColor > 0 + ? RED + : DARK_RED; + statsMessage += `${GRAY}[${GOLD}Legion${GRAY}] ${legionColor + legionCount + DARK_GRAY} (${legionPercent}%)\n`; } // Soulflow if (Toggles.soulflowDisplay) { - const soulflowColor = soulflow > 100_000 ? GREEN : - soulflow > 75_000 ? DARK_GREEN : - soulflow > 50_000 ? YELLOW : - soulflow > 25_000 ? GOLD : - soulflow > 10_000 ? RED : DARK_RED; - statsMessage += `${GRAY}[${GOLD}SF${GRAY}] ${soulflowColor + formatNumber(soulflow) + AQUA} ⸎\n`; + const soulflowColor = + soulflow > 100_000 + ? GREEN + : soulflow > 75_000 + ? DARK_GREEN + : soulflow > 50_000 + ? YELLOW + : soulflow > 25_000 + ? GOLD + : soulflow > 10_000 + ? RED + : DARK_RED; + statsMessage += `${GRAY}[${GOLD}SF${GRAY}] ${soulflowColor + formatNumber(soulflow) + AQUA} ⸎\n`; } // Playtime if (Toggles.trackPlaytime) { - const ptColor = data.playtime < 3_600 ? GREEN : - data.playtime < 7_200 ? DARK_GREEN : - data.playtime < 10_800 ? YELLOW : - data.playtime < 18_000 ? GOLD : - data.playtime < 28_800 ? RED : DARK_RED; - const formattedPlaytime = formatTime(data.playtime).replace(/\d+/g, (match) => `${ptColor}${match}${DARK_GRAY}`); - statsMessage += `${GRAY}[${GOLD}Daily PT${GRAY}] ${formattedPlaytime}`; + const ptColor = + data.playtime < 3_600 + ? GREEN + : data.playtime < 7_200 + ? DARK_GREEN + : data.playtime < 10_800 + ? YELLOW + : data.playtime < 18_000 + ? GOLD + : data.playtime < 28_800 + ? RED + : DARK_RED; + const formattedPlaytime = formatTime(data.playtime).replace(/\d+/g, (match) => `${ptColor}${match}${DARK_GRAY}`); + statsMessage += `${GRAY}[${GOLD}Daily PT${GRAY}] ${formattedPlaytime}`; } statsOverlay.setMessage(statsMessage); -}), () => Settings.statsDisplay); + }), + () => Settings.statsDisplay +); /** * Output Stats to user chat when user requests via command args. - * + * * @param {String} stat - Status type to retrieve. */ export function getStat(stat) { - switch (stat) { - case "pet": - ChatLib.chat(`${LOGO + DARK_AQUA + BOLD}Pet: ${data.pet}`); - break; - case "soulflow": - case "sf": - const soulflowColor = soulflow > 100_000 ? GREEN : - soulflow > 75_000 ? DARK_GREEN : - soulflow > 50_000 ? YELLOW : - soulflow > 25_000 ? GOLD : - soulflow > 10_000 ? RED : DARK_RED; - - ChatLib.chat(`${LOGO + DARK_AQUA + BOLD}SF: ${soulflowColor + formatNumber(soulflow) + AQUA} ⸎`); - break; - case "playtime": - case "pt": - const ptColor = data.playtime < 3_600 ? GREEN : - data.playtime < 7_200 ? DARK_GREEN : - data.playtime < 10_800 ? YELLOW : - data.playtime < 18_000 ? GOLD : - data.playtime < 28_800 ? RED : DARK_RED; - - ChatLib.chat(`${LOGO + DARK_AQUA + BOLD}Daily Playtime: ${ptColor + formatTime(data.playtime)}`); - break; - case "legion": - const player = Player.asPlayerMP(); - const legionCount = World.getAllEntitiesOfType(PLAYER_CLASS).filter(other => other.getEntity().func_110143_aJ() !== 20 && player.distanceTo(other) < 30).length - 1; - const legionPercent = Math.min(1, legionCount / 20).toFixed(2) * 100; - const legionColor = legionCount > 16 ? GREEN : - legionCount > 12 ? DARK_GREEN : - legionCount > 8 ? YELLOW : - legionCount > 4 ? GOLD : - legionColor > 0 ? RED : DARK_RED; - ChatLib.chat(`${DARK_AQUA + BOLD}Legion: ${legionColor + legionCount + DARK_GRAY} (${legionPercent}%)`); - break; - } + switch (stat) { + case "pet": + ChatLib.chat(`${LOGO + DARK_AQUA + BOLD}Pet: ${data.pet}`); + break; + case "soulflow": + case "sf": + const soulflowColor = + soulflow > 100_000 + ? GREEN + : soulflow > 75_000 + ? DARK_GREEN + : soulflow > 50_000 + ? YELLOW + : soulflow > 25_000 + ? GOLD + : soulflow > 10_000 + ? RED + : DARK_RED; + + ChatLib.chat(`${LOGO + DARK_AQUA + BOLD}SF: ${soulflowColor + formatNumber(soulflow) + AQUA} ⸎`); + break; + case "playtime": + case "pt": + const ptColor = + data.playtime < 3_600 + ? GREEN + : data.playtime < 7_200 + ? DARK_GREEN + : data.playtime < 10_800 + ? YELLOW + : data.playtime < 18_000 + ? GOLD + : data.playtime < 28_800 + ? RED + : DARK_RED; + + ChatLib.chat(`${LOGO + DARK_AQUA + BOLD}Daily Playtime: ${ptColor + formatTime(data.playtime)}`); + break; + case "legion": + const player = Player.asPlayerMP(); + const legionCount = + World.getAllEntitiesOfType(PLAYER_CLASS).filter( + (other) => other.getEntity().func_110143_aJ() !== 20 && player.distanceTo(other) < 30 + ).length - 1; + const legionPercent = Math.min(1, legionCount / 20).toFixed(2) * 100; + const legionColor = + legionCount > 16 + ? GREEN + : legionCount > 12 + ? DARK_GREEN + : legionCount > 8 + ? YELLOW + : legionCount > 4 + ? GOLD + : legionColor > 0 + ? RED + : DARK_RED; + ChatLib.chat(`${DARK_AQUA + BOLD}Legion: ${legionColor + legionCount + DARK_GRAY} (${legionPercent}%)`); + break; + } } diff --git a/features/general/UserWaypoints.js b/features/general/UserWaypoints.js index 52e6662..1fdea35 100644 --- a/features/general/UserWaypoints.js +++ b/features/general/UserWaypoints.js @@ -1,51 +1,51 @@ -import Settings from "../../utils/Settings"; import { GRAY, GREEN, LOGO, RED, WHITE } from "../../utils/Constants"; -import { getPlayerName } from "../../utils/functions/player"; +import { data } from "../../utils/Data"; import { registerWhen } from "../../utils/RegisterTils"; +import Settings from "../../utils/Settings"; import { delay } from "../../utils/ThreadTils"; -import { data } from "../../utils/Data"; import Waypoint from "../../utils/Waypoint"; - +import { getPlayerName } from "../../utils/functions/player"; /** * Variables used to represent user inputted waypoints. */ -const chatWaypoints = new Waypoint([0, 1, 1]); // Cyan Chat -const userWaypoints = new Waypoint([0, 1, 0]); // Green User +const chatWaypoints = new Waypoint([0, 1, 1]); // Cyan Chat +const userWaypoints = new Waypoint([0, 1, 0]); // Green User /** * Detects any patcher formatted coords sent in chat. */ -registerWhen(register("chat", (player, _, x, y, z) => { +registerWhen( + register("chat", (player, _, x, y, z) => { // Check blacklist if (data.blacklist.includes(getPlayerName(player).toLowerCase())) return; // Gets colors and titles in name - const bracketIndex = player.indexOf('[') - 2; - if (bracketIndex >= 0) - player = player.replaceAll('&', '§').substring(bracketIndex, player.length); - else - player = player.replaceAll('&', '§'); + const bracketIndex = player.indexOf("[") - 2; + if (bracketIndex >= 0) player = player.replaceAll("&", "§").substring(bracketIndex, player.length); + else player = player.replaceAll("&", "§"); // Remove anything after z coords - const spaceIndex = z.indexOf(' '); + const spaceIndex = z.indexOf(" "); let time = 999; if (spaceIndex !== -1) { - if (z.includes('|')) { - player = RED + z.split(' ')[2]; - time /= 3; - } - z = z.substring(0, spaceIndex); + if (z.includes("|")) { + player = RED + z.split(" ")[2]; + time /= 3; + } + z = z.substring(0, spaceIndex); } - + chatWaypoints.push([player, x, y, z]); // Delete waypoint after 'X' seconds delay(() => { - const waypoints = chatWaypoints.getWaypoints(); - if (waypoints.length) waypoints.shift(); + const waypoints = chatWaypoints.getWaypoints(); + if (waypoints.length) waypoints.shift(); }, Settings.drawWaypoint * time); -}).setCriteria("${player}:${spacing}x: ${x}, y: ${y}, z: ${z}&r"), () => Settings.drawWaypoint !== 0); + }).setCriteria("${player}:${spacing}x: ${x}, y: ${y}, z: ${z}&r"), + () => Settings.drawWaypoint !== 0 +); /** * Allows user to create waypoints via command. @@ -53,22 +53,24 @@ registerWhen(register("chat", (player, _, x, y, z) => { * @param {String[]} args - Array of user input needed for waypoint. */ export function createWaypoint(args) { - const name = args[1]; - const x = isNaN(args[2]) ? Math.round(Player.getX()) : parseFloat(args[2]); - const y = isNaN(args[3]) ? Math.round(Player.getY()) : parseFloat(args[3]); - const z = isNaN(args[4]) ? Math.round(Player.getZ()) : parseFloat(args[4]); + const name = args[1]; + const x = isNaN(args[2]) ? Math.round(Player.getX()) : parseFloat(args[2]); + const y = isNaN(args[3]) ? Math.round(Player.getY()) : parseFloat(args[3]); + const z = isNaN(args[4]) ? Math.round(Player.getZ()) : parseFloat(args[4]); - if (name === "clear") { - chatWaypoints.clear(); - userWaypoints.clear(); - NPCs = []; - zones = []; - ChatLib.chat(`${LOGO + GREEN}Successfully cleared waypoints!`); - } else if (args[2] && args[3] && args[4]) { - userWaypoints.push([name, x, y, z]); - ChatLib.chat(`${GREEN}Successfully added waypoint [${name}] at [x: ${x}, y: ${y}, z: ${z}]!`); - } else { - ChatLib.chat(`\n${LOGO + RED}Error: Invalid argument "${name}"!`); - ChatLib.chat(`${LOGO + RED}Please input as: ${WHITE}/va waypoint ${GRAY}<${WHITE}[name] [x] [y] [z], clear${GRAY}>`); - } + if (name === "clear") { + chatWaypoints.clear(); + userWaypoints.clear(); + NPCs = []; + zones = []; + ChatLib.chat(`${LOGO + GREEN}Successfully cleared waypoints!`); + } else if (args[2] && args[3] && args[4]) { + userWaypoints.push([name, x, y, z]); + ChatLib.chat(`${GREEN}Successfully added waypoint [${name}] at [x: ${x}, y: ${y}, z: ${z}]!`); + } else { + ChatLib.chat(`\n${LOGO + RED}Error: Invalid argument "${name}"!`); + ChatLib.chat( + `${LOGO + RED}Please input as: ${WHITE}/va waypoint ${GRAY}<${WHITE}[name] [x] [y] [z], clear${GRAY}>` + ); + } } diff --git a/features/general/WidgetDisplay.js b/features/general/WidgetDisplay.js index a95c8f2..905363d 100644 --- a/features/general/WidgetDisplay.js +++ b/features/general/WidgetDisplay.js @@ -1,9 +1,8 @@ -import Settings from "../../utils/Settings"; import { AQUA, BOLD, GREEN } from "../../utils/Constants"; +import { data } from "../../utils/Data"; import { Overlay } from "../../utils/Overlay"; import { registerWhen } from "../../utils/RegisterTils"; -import { data } from "../../utils/Data"; - +import Settings from "../../utils/Settings"; const widgetOverlays = {}; @@ -11,37 +10,45 @@ const widgetOverlays = {}; * Updates widget overlays list to correctly match widgetlist in data. */ export function updateWidgetList() { - data.widgetlist.forEach(widget => { - if (widget in widgetOverlays) return; - if (!(widget in data.WGL)) data.WGL[widget] = [100, 100, 1.2, false, false]; - widgetOverlays[widget] = new Overlay("widgetDisplay", data.WGL[widget], `move${widget.replace(/\s/g, "")}`, -`${AQUA + BOLD + widget}: + data.widgetlist.forEach((widget) => { + if (widget in widgetOverlays) return; + if (!(widget in data.WGL)) data.WGL[widget] = [100, 100, 1.2, false, false]; + widgetOverlays[widget] = new Overlay( + "widgetDisplay", + data.WGL[widget], + `move${widget.replace(/\s/g, "")}`, + `${AQUA + BOLD + widget}: Statistic ${GREEN}1 Statistic ${GREEN}2 Statistic ${GREEN}3 - Statistic ${GREEN}FOUR`); - }); + Statistic ${GREEN}FOUR` + ); + }); - Object.keys(widgetOverlays).forEach(widget => { - widgetOverlays[widget].setMessage(""); - if (!data.widgetlist.includes(widget)) delete widgetOverlays[widget]; - }) + Object.keys(widgetOverlays).forEach((widget) => { + widgetOverlays[widget].setMessage(""); + if (!data.widgetlist.includes(widget)) delete widgetOverlays[widget]; + }); } updateWidgetList(); -registerWhen(register("step", () => { +registerWhen( + register("step", () => { if (!World.isLoaded()) return; const tablist = TabList.getNames(); - Object.keys(widgetOverlays).forEach(widget => { - let index = tablist.findIndex(line => line.removeFormatting().toLowerCase() === `${widget.toLowerCase()}:`); - if (index === -1) { - widgetOverlays[widget].setMessage(""); - return; - } + Object.keys(widgetOverlays).forEach((widget) => { + let index = tablist.findIndex((line) => line.removeFormatting().toLowerCase() === `${widget.toLowerCase()}:`); + if (index === -1) { + widgetOverlays[widget].setMessage(""); + return; + } - let message = tablist[index++]; - while (tablist[index].startsWith("§r ") && !tablist[index].endsWith("§r§3§lInfo§r")) message += '\n' + tablist[index++]; - widgetOverlays[widget].setMessage(message); + let message = tablist[index++]; + while (tablist[index].startsWith("§r ") && !tablist[index].endsWith("§r§3§lInfo§r")) + message += "\n" + tablist[index++]; + widgetOverlays[widget].setMessage(message); }); -}).setFps(1), () => Settings.widgetDisplay); + }).setFps(1), + () => Settings.widgetDisplay +); diff --git a/features/kuudra/CrateEdit.js b/features/kuudra/CrateEdit.js index 3bfda04..8f6a66e 100644 --- a/features/kuudra/CrateEdit.js +++ b/features/kuudra/CrateEdit.js @@ -1,9 +1,8 @@ +import { data } from "../../utils/Data"; import location from "../../utils/Location"; -import Settings from "../../utils/Settings"; import { Overlay } from "../../utils/Overlay"; import { registerWhen } from "../../utils/RegisterTils"; -import { data } from "../../utils/Data"; - +import Settings from "../../utils/Settings"; /** * Crate overlay variables. @@ -14,19 +13,22 @@ const crateOverlay = new Overlay("crateEdit", data.CEL, "moveCrate", crateExampl /** * Cancel crate title render and replay with overlay render. */ -registerWhen(register("renderTitle", (title, _, event) => { +registerWhen( + register("renderTitle", (title, _, event) => { if (!title.startsWith("§8[") || !title.endsWith("%§r")) return; crateOverlay.setMessage(title); cancel(event); -}), () => Settings.crateEdit && location.getWorld() === "Kuudra"); + }), + () => Settings.crateEdit && location.getWorld() === "Kuudra" +); /** * Reset on crate pickup/cancel */ register("chat", () => { - Client.scheduleTask(20, () => crateOverlay.setMessage("")); + Client.scheduleTask(20, () => crateOverlay.setMessage("")); }).setCriteria("You retrieved some of Elle's supplies from the Lava!"); register("chat", () => { - Client.scheduleTask(20, () => crateOverlay.setMessage("")); + Client.scheduleTask(20, () => crateOverlay.setMessage("")); }).setCriteria("You moved and the Chest slipped out of your hands!"); diff --git a/features/kuudra/KuudraAlerts.js b/features/kuudra/KuudraAlerts.js index c467c0b..4930d98 100644 --- a/features/kuudra/KuudraAlerts.js +++ b/features/kuudra/KuudraAlerts.js @@ -1,148 +1,180 @@ +import { AQUA, BOLD, DARK_PURPLE, DARK_RED, GHAST_CLASS, GRAY, GREEN, MUSIC, RED, WHITE } from "../../utils/Constants"; import location from "../../utils/Location"; +import { registerWhen } from "../../utils/RegisterTils"; import Settings from "../../utils/Settings"; +import { setTitle } from "../../utils/Title"; import Toggles from "../../utils/Toggles"; -import {AQUA, BOLD, DARK_PURPLE, DARK_RED, GHAST_CLASS, GRAY, GREEN, MUSIC, RED, WHITE} from "../../utils/Constants"; import { playSound } from "../../utils/functions/misc"; -import { registerWhen } from "../../utils/RegisterTils"; -import { setTitle } from "../../utils/Title"; - /** * No key alert. */ -registerWhen(register("chat", () => { +registerWhen( + register("chat", () => { setTitle(`${BOLD}NO KUUDRA KEY!`, "", 10, 50, 10, 60); -}).setCriteria("WARNING: You do not have a key for this tier in your inventory, you will not be able to claim rewards."), -() => location.getWorld() === "Kuudra" && Settings.kuudraAlerts && Toggles.keyAlert); + }).setCriteria( + "WARNING: You do not have a key for this tier in your inventory, you will not be able to claim rewards." + ), + () => location.getWorld() === "Kuudra" && Settings.kuudraAlerts && Toggles.keyAlert +); /** * Unready alert. */ -registerWhen(register("chat", (player) => { +registerWhen( + register("chat", (player) => { const name = player.removeFormatting().toUpperCase(); playSound(MUSIC, 1000); setTitle(`${DARK_RED + BOLD + name} ${WHITE}IS NO LONGER READY!`, "", 10, 50, 10, 61); -}).setCriteria("${player} is no longer ready!"), () => location.getWorld() === "Kuudra" && Toggles.kuudraAlerts && Toggles.unreadyAlert); + }).setCriteria("${player} is no longer ready!"), + () => location.getWorld() === "Kuudra" && Toggles.kuudraAlerts && Toggles.unreadyAlert +); /** * Choose perk alert. */ -registerWhen(register("chat", () => { +registerWhen( + register("chat", () => { playSound(MUSIC, 1000); setTitle(`${AQUA + BOLD}BUY UPGRADE ROUTE!`, "", 10, 100, 10, 62); -}).setCriteria("[NPC] Elle: Okay adventurers, I will go and fish up Kuudra!"), -() => location.getWorld() === "Kuudra" && Settings.kuudraAlerts && Toggles.routeAlert); + }).setCriteria("[NPC] Elle: Okay adventurers, I will go and fish up Kuudra!"), + () => location.getWorld() === "Kuudra" && Settings.kuudraAlerts && Toggles.routeAlert +); /** * Supply spawned alert. */ -registerWhen(register("chat", () => { +registerWhen( + register("chat", () => { playSound(MUSIC, 1000); setTitle(`${AQUA + BOLD}PICKUP SUPPLY!`, "", 10, 100, 10, 63); -}).setCriteria("[NPC] Elle: Not again!"), () => location.getWorld() === "Kuudra" && Settings.kuudraAlerts && Toggles.supplyAlert); + }).setCriteria("[NPC] Elle: Not again!"), + () => location.getWorld() === "Kuudra" && Settings.kuudraAlerts && Toggles.supplyAlert +); /** * Building started alert. */ -registerWhen(register("chat", () => { +registerWhen( + register("chat", () => { playSound(MUSIC, 1000); setTitle(`${AQUA + BOLD}START BUILDING!`, "", 10, 50, 10, 64); -}).setCriteria("[NPC] Elle: It's time to build the Ballista again! Cover me!"), -() => location.getWorld() === "Kuudra" && Settings.kuudraAlerts && Toggles.buildingAlert); + }).setCriteria("[NPC] Elle: It's time to build the Ballista again! Cover me!"), + () => location.getWorld() === "Kuudra" && Settings.kuudraAlerts && Toggles.buildingAlert +); /** * Fresh tools alert. */ -registerWhen(register("chat", () => { +registerWhen( + register("chat", () => { playSound(MUSIC, 1000); setTitle(`${GREEN + BOLD}EAT FRESH!`, "", 10, 50, 10, 65); -}).setCriteria("Your Fresh Tools Perk bonus doubles your building speed for the next ${time} seconds!"), -() => location.getWorld() === "Kuudra" && Settings.kuudraAlerts && Toggles.freshAlert); + }).setCriteria("Your Fresh Tools Perk bonus doubles your building speed for the next ${time} seconds!"), + () => location.getWorld() === "Kuudra" && Settings.kuudraAlerts && Toggles.freshAlert +); /** * Fuel spawned alert. */ -registerWhen(register("chat", (player, percentage) => { // Ballista full alert +registerWhen( + register("chat", (player, percentage) => { + // Ballista full alert playSound(MUSIC, 1000); switch (percentage) { - case "100": - setTitle(`${AQUA + BOLD}100% ${GRAY}[${GREEN}||||${GRAY}]`, "", 10, 100, 10, 66); - break; - case "75": - setTitle(`${AQUA + BOLD}75% ${GRAY}[${GREEN}|||${RED}|${GRAY}]`, "", 10, 50, 10, 66); - break; - case "50": - setTitle(`${AQUA + BOLD}50% ${GRAY}[${GREEN}||${RED}||${GRAY}]`, "", 10, 50, 10, 66); - break; - case "25": - setTitle(`${AQUA + BOLD}25% ${GRAY}[${GREEN}|${RED}|||${GRAY}]`, "", 10, 50, 10, 66); - break; + case "100": + setTitle(`${AQUA + BOLD}100% ${GRAY}[${GREEN}||||${GRAY}]`, "", 10, 100, 10, 66); + break; + case "75": + setTitle(`${AQUA + BOLD}75% ${GRAY}[${GREEN}|||${RED}|${GRAY}]`, "", 10, 50, 10, 66); + break; + case "50": + setTitle(`${AQUA + BOLD}50% ${GRAY}[${GREEN}||${RED}||${GRAY}]`, "", 10, 50, 10, 66); + break; + case "25": + setTitle(`${AQUA + BOLD}25% ${GRAY}[${GREEN}|${RED}|||${GRAY}]`, "", 10, 50, 10, 66); + break; } -}).setCriteria("${player} recovered a Fuel Cell and charged the Ballista! (${percentage}%)"), -() => location.getWorld() === "Kuudra" && Settings.kuudraAlerts && Toggles.fuelAlert); + }).setCriteria("${player} recovered a Fuel Cell and charged the Ballista! (${percentage}%)"), + () => location.getWorld() === "Kuudra" && Settings.kuudraAlerts && Toggles.fuelAlert +); /** * Stunner eaten alert. */ -registerWhen(register("chat", (player) => { +registerWhen( + register("chat", (player) => { const ign = player.toUpperCase(); const stunner = Toggles.kuudraStunner.toUpperCase(); if (!ign.equals("ELLE") && (stunner === "ALL" || ign.equals(stunner))) { - playSound(MUSIC, 1000); - setTitle(`${GREEN + BOLD + ign} WAS EATEN!`, "", 10, 100, 10, 67); + playSound(MUSIC, 1000); + setTitle(`${GREEN + BOLD + ign} WAS EATEN!`, "", 10, 100, 10, 67); } -}).setCriteria("${player} has been eaten by Kuudra!"), -() => location.getWorld() === "Kuudra" && Settings.kuudraAlerts && Toggles.kuudraStunner !== ""); + }).setCriteria("${player} has been eaten by Kuudra!"), + () => location.getWorld() === "Kuudra" && Settings.kuudraAlerts && Toggles.kuudraStunner !== "" +); /** * Ballista mounted alert. */ -registerWhen(register("chat", (player) => { +registerWhen( + register("chat", (player) => { const ign = player.toUpperCase(); const cannonear = Toggles.kuudraCannonear.toUpperCase(); - if (cannonear === "" || (cannonear === "ALL" || ign.equals(cannonear))) { - playSound(MUSIC, 1000); - setTitle(`${AQUA + BOLD + ign} ASSUMED THE POSITION!`, "", 10, 100, 10, 68); + if (cannonear === "" || cannonear === "ALL" || ign.equals(cannonear)) { + playSound(MUSIC, 1000); + setTitle(`${AQUA + BOLD + ign} ASSUMED THE POSITION!`, "", 10, 100, 10, 68); } -}).setCriteria("${player} mounted a Cannon!"), -() => location.getWorld() === "Kuudra" && Settings.kuudraAlerts && Toggles.ballistaAlert !== ""); + }).setCriteria("${player} mounted a Cannon!"), + () => location.getWorld() === "Kuudra" && Settings.kuudraAlerts && Toggles.ballistaAlert !== "" +); /** * Kuudra stunned alert. */ -registerWhen(register("chat", () => { +registerWhen( + register("chat", () => { playSound(MUSIC, 1000); setTitle(`${GREEN + BOLD}KUUDRA STUNNED!`, "", 10, 100, 10, 69); -}).setCriteria("{player} destroyed one of Kuudra's pods!"), -() => location.getWorld() === "Kuudra" && Settings.kuudraAlerts && Toggles.stunAlert); + }).setCriteria("{player} destroyed one of Kuudra's pods!"), + () => location.getWorld() === "Kuudra" && Settings.kuudraAlerts && Toggles.stunAlert +); /** * Warns player about dropship as they get close to center. */ -let alerted = false -registerWhen(register("step", () => { - const dropships = World.getAllEntitiesOfType(GHAST_CLASS).find(ghast => { - distance = Math.hypot(ghast.getX() + 101, ghast.getZ() + 105); - return distance < 20 && distance > 10; +let alerted = false; +registerWhen( + register("step", () => { + const dropships = World.getAllEntitiesOfType(GHAST_CLASS).find((ghast) => { + distance = Math.hypot(ghast.getX() + 101, ghast.getZ() + 105); + return distance < 20 && distance > 10; }); if (dropships !== undefined) setTitle(`${RED + BOLD}ART IS AN EXPLOSION!`, "", 0, 50, 5, 71); -}).setFps(1), () => location.getWorld() === "Kuudra" && Settings.kuudraAlerts && Toggles.dropshipAlert); + }).setFps(1), + () => location.getWorld() === "Kuudra" && Settings.kuudraAlerts && Toggles.dropshipAlert +); /** * Alerts player when token gathered surpasses set threshhold. */ -registerWhen(register("step", () => { +registerWhen( + register("step", () => { if (alerted) return; tokens = Scoreboard?.getLines()?.find((line) => line.getName().includes("Tokens")); if (tokens === undefined) return; - tokens = tokens.getName().removeFormatting().replace(/\D/g,''); + tokens = tokens.getName().removeFormatting().replace(/\D/g, ""); if (tokens >= Math.round(Toggles.tokenAlert / 10) * 10) { - setTitle(`${BOLD + tokens} ${DARK_PURPLE + BOLD}TOKENS GATHERED!`, "", 0, 50, 5, 70); - alerted = true + setTitle(`${BOLD + tokens} ${DARK_PURPLE + BOLD}TOKENS GATHERED!`, "", 0, 50, 5, 70); + alerted = true; } -}).setFps(5), () => location.getWorld() === "Kuudra" && Settings.kuudraAlerts && Toggles.tokenAlert !== 0); -register("worldLoad", () => { alerted = false }); + }).setFps(5), + () => location.getWorld() === "Kuudra" && Settings.kuudraAlerts && Toggles.tokenAlert !== 0 +); +register("worldLoad", () => { + alerted = false; +}); diff --git a/features/kuudra/KuudraCrates.js b/features/kuudra/KuudraCrates.js index 7dca5cd..f624cbf 100644 --- a/features/kuudra/KuudraCrates.js +++ b/features/kuudra/KuudraCrates.js @@ -1,47 +1,52 @@ -import location from "../../utils/Location"; -import Settings from "../../utils/Settings"; import { GIANT_CLASS, STAND_CLASS } from "../../utils/Constants"; +import location from "../../utils/Location"; import { registerWhen } from "../../utils/RegisterTils"; -import { getPhase } from "./KuudraSplits"; +import Settings from "../../utils/Settings"; import Waypoint from "../../utils/Waypoint"; - +import { getPhase } from "./KuudraSplits"; /** * Variables used to track and display crate locations. */ -const crates = new Waypoint([1, 1, 1], 1, false, true, false); // White Crates -const builds = new Waypoint([1, 0, 0], 1, false, true, false); // Red Builds +const crates = new Waypoint([1, 1, 1], 1, false, true, false); // White Crates +const builds = new Waypoint([1, 0, 0], 1, false, true, false); // Red Builds /** * Tracks crates near player and colors them depending on how close they are. */ -registerWhen(register("tick", () => { +registerWhen( + register("tick", () => { if (getPhase() !== 1 && getPhase() !== 3) return; - + // Get all giant zombies and filter out the ones that are not on the ground const gzs = World.getAllEntitiesOfType(GIANT_CLASS); - const supplies = gzs.filter(gz => gz.getY() < 67); + const supplies = gzs.filter((gz) => gz.getY() < 67); const player = Player.asPlayerMP(); // Update waypoints crates.clear(); - supplies.forEach(supply => { - const yaw = supply.getYaw(); - const distance = player.distanceTo(supply); - const x = supply.getX() + 5 * Math.cos((yaw + 130) * (Math.PI / 180)) + 0.5; - const z = supply.getZ() + 5 * Math.sin((yaw + 130) * (Math.PI / 180)) + 0.5; - crates.push([distance > 32 ? 1 : 0, 1, distance > 32 ? 1 : 0, x, 75, z]); + supplies.forEach((supply) => { + const yaw = supply.getYaw(); + const distance = player.distanceTo(supply); + const x = supply.getX() + 5 * Math.cos((yaw + 130) * (Math.PI / 180)) + 0.5; + const z = supply.getZ() + 5 * Math.sin((yaw + 130) * (Math.PI / 180)) + 0.5; + crates.push([distance > 32 ? 1 : 0, 1, distance > 32 ? 1 : 0, x, 75, z]); }); -}), () => location.getWorld() === "Kuudra" && Settings.kuudraCrates); + }), + () => location.getWorld() === "Kuudra" && Settings.kuudraCrates +); /** * Marks build piles that are not completed. */ -registerWhen(register("step", () => { +registerWhen( + register("step", () => { if (getPhase() !== 2) return; builds.clear(); const stands = World.getAllEntitiesOfType(STAND_CLASS); - const piles = stands.filter(stand => stand.getName().includes('PUNCH')); - piles.forEach(pile => builds.push([pile.getX() + 0.5, pile.getY(), pile.getZ() + 0.5]) ); -}).setFps(2), () => location.getWorld() === "Kuudra" && Settings.kuudraBuild); + const piles = stands.filter((stand) => stand.getName().includes("PUNCH")); + piles.forEach((pile) => builds.push([pile.getX() + 0.5, pile.getY(), pile.getZ() + 0.5])); + }).setFps(2), + () => location.getWorld() === "Kuudra" && Settings.kuudraBuild +); diff --git a/features/kuudra/KuudraDetect.js b/features/kuudra/KuudraDetect.js index 8606e9a..fe3e996 100644 --- a/features/kuudra/KuudraDetect.js +++ b/features/kuudra/KuudraDetect.js @@ -1,76 +1,104 @@ -import location from "../../utils/Location"; -import Settings from "../../utils/Settings"; import { CUBE_CLASS } from "../../utils/Constants"; -import { formatNumber } from "../../utils/functions/format"; +import location from "../../utils/Location"; import { registerWhen } from "../../utils/RegisterTils"; +import Settings from "../../utils/Settings"; import { setTitle } from "../../utils/Title"; - +import { formatNumber } from "../../utils/functions/format"; /** * Variables used to track and display Kuudra HP and entity. */ let cubes = undefined; -let percentHP = new Text(`One Cycleable`, Renderer.screen.getWidth() / 2 - Renderer.getStringWidth(`One Cycleable`) / 2, 10); +let percentHP = new Text( + `One Cycleable`, + Renderer.screen.getWidth() / 2 - Renderer.getStringWidth(`One Cycleable`) / 2, + 10 +); let HPDisplay = ["100k/100k ❤", 0, 0, 0]; let currentHP = 0; -export function getKuudraHP() { return currentHP }; +export function getKuudraHP() { + return currentHP; +} /** * Tracks Kuudra's HP and spawn location if entering phase 4. */ -registerWhen(register("tick", () => { +registerWhen( + register("tick", () => { cubes = World.getAllEntitiesOfType(CUBE_CLASS); // Find Kuudra based off size and HP - const kuudra = cubes.find((cube) => cube.getWidth().toFixed(1) == 15.3 && cube.getEntity().func_110143_aJ() <= 100_000); + const kuudra = cubes.find( + (cube) => cube.getWidth().toFixed(1) == 15.3 && cube.getEntity().func_110143_aJ() <= 100_000 + ); if (kuudra !== undefined) { - currentHP = kuudra.getEntity().func_110143_aJ().toFixed(0); - - if (Settings.kuudraHP) { - // Tesselator Display - const color = currentHP > 99_000 ? "§a" : - currentHP > 75_000 ? "§2" : - currentHP > 50_000 ? "§e" : - currentHP > 25_000 ? "§6" : - currentHP > 10_000 ? "§c" : "§4"; - HPDisplay = [`${color + formatNumber(currentHP)}§7/§a100k §c❤`, kuudra.getX(), kuudra.getY(), kuudra.getZ()]; - - // Boss Health Bar Percentage - const percent = `${(currentHP / 100_000 * 100).toFixed(2)}%`; - percentHP = new Text(percent, Renderer.screen.getWidth() / 2 - Renderer.getStringWidth(percent) / 2, 10); - } + currentHP = kuudra.getEntity().func_110143_aJ().toFixed(0); + + if (Settings.kuudraHP) { + // Tesselator Display + const color = + currentHP > 99_000 + ? "§a" + : currentHP > 75_000 + ? "§2" + : currentHP > 50_000 + ? "§e" + : currentHP > 25_000 + ? "§6" + : currentHP > 10_000 + ? "§c" + : "§4"; + HPDisplay = [`${color + formatNumber(currentHP)}§7/§a100k §c❤`, kuudra.getX(), kuudra.getY(), kuudra.getZ()]; + + // Boss Health Bar Percentage + const percent = `${((currentHP / 100_000) * 100).toFixed(2)}%`; + percentHP = new Text(percent, Renderer.screen.getWidth() / 2 - Renderer.getStringWidth(percent) / 2, 10); + } - // KUUDRA SPAWN DETECT - if (Settings.kuudraSpawn && location.getTier() === 5 && currentHP <= 25_000 && currentHP > 24_900) { - x = kuudra.getX(); - z = kuudra.getZ(); + // KUUDRA SPAWN DETECT + if (Settings.kuudraSpawn && location.getTier() === 5 && currentHP <= 25_000 && currentHP > 24_900) { + x = kuudra.getX(); + z = kuudra.getZ(); - if (x < -128) setTitle("§c§lRIGHT!", "", 0, 25, 5, 81); - else if (z > -84) setTitle("§2§lFRONT!", "", 0, 25, 5, 81); - else if (x > -72) setTitle("§a§lLEFT!", "", 0, 25, 5, 81); - else if (z < -132) setTitle("§4§lBACK!", "", 0, 25, 5, 81); - } + if (x < -128) setTitle("§c§lRIGHT!", "", 0, 25, 5, 81); + else if (z > -84) setTitle("§2§lFRONT!", "", 0, 25, 5, 81); + else if (x > -72) setTitle("§a§lLEFT!", "", 0, 25, 5, 81); + else if (z < -132) setTitle("§4§lBACK!", "", 0, 25, 5, 81); + } } else HPDisplay = ["100k/100k ❤", 0, 0, 0]; -}), () => location.getWorld() === "Kuudra" && (Settings.kuudraHP || Settings.kuudraSpawn)); + }), + () => location.getWorld() === "Kuudra" && (Settings.kuudraHP || Settings.kuudraSpawn) +); /** * Renders Kuudra's percent HP. */ -registerWhen(register('renderOverlay', () => { +registerWhen( + register("renderOverlay", () => { percentHP.draw(); -}), () => location.getWorld() === "Kuudra" && Settings.kuudraHP); + }), + () => location.getWorld() === "Kuudra" && Settings.kuudraHP +); /** * Draws Kuudra HP onto its physical body. */ -registerWhen(register('renderWorld', () => { - if (HPDisplay[1]) Tessellator.drawString(HPDisplay[0], HPDisplay[1], HPDisplay[2] + 10, HPDisplay[3], 0xA7171A, true, 0.25, false); -}), () => location.getWorld() === "Kuudra" && Settings.kuudraHP); +registerWhen( + register("renderWorld", () => { + if (HPDisplay[1]) + Tessellator.drawString(HPDisplay[0], HPDisplay[1], HPDisplay[2] + 10, HPDisplay[3], 0xa7171a, true, 0.25, false); + }), + () => location.getWorld() === "Kuudra" && Settings.kuudraHP +); /** * Reset Kuudra's UUID on world exit. */ -register('worldUnload', () => { - percentHP = new Text(`One Cycleable`, Renderer.screen.getWidth() / 2 - Renderer.getStringWidth(`One Cycleable`) / 2, 10); - HPDisplay = ["100k/100k ❤", 0, 0, 0]; +register("worldUnload", () => { + percentHP = new Text( + `One Cycleable`, + Renderer.screen.getWidth() / 2 - Renderer.getStringWidth(`One Cycleable`) / 2, + 10 + ); + HPDisplay = ["100k/100k ❤", 0, 0, 0]; }); diff --git a/features/kuudra/KuudraProfit.js b/features/kuudra/KuudraProfit.js index 6ac48d1..79c2026 100644 --- a/features/kuudra/KuudraProfit.js +++ b/features/kuudra/KuudraProfit.js @@ -1,18 +1,23 @@ -import location from "../../utils/Location"; -import Settings from "../../utils/Settings"; import { AQUA, BOLD, DARK_AQUA, DARK_PURPLE, DARK_RED, GOLD, GREEN, RED, WHITE } from "../../utils/Constants"; -import { commafy, formatNumber, formatTime } from "../../utils/functions/format"; +import { data } from "../../utils/Data"; +import location from "../../utils/Location"; import { Overlay } from "../../utils/Overlay"; import { registerWhen } from "../../utils/RegisterTils"; -import { data } from "../../utils/Data"; -import { getItemValue } from "../economy/ItemPrice"; +import Settings from "../../utils/Settings"; +import { commafy, formatNumber, formatTime } from "../../utils/functions/format"; import { getBazaar } from "../economy/Economy"; - +import { getItemValue } from "../economy/ItemPrice"; /** * Variables used to calculate Kuudra chest profit. */ -const KEY_COST = [[200000, 2], [400000, 6], [750000, 20], [1500000, 60], [3000000, 120]]; +const KEY_COST = [ + [200000, 2], + [400000, 6], + [750000, 20], + [1500000, 60], + [3000000, 120], +]; /** * Variables used to track Kuudra session profit. @@ -21,37 +26,38 @@ let downtime = 300; let chestOpened = false; let chestProfit = 0; let kuudraSession = { - "profit": 0, - "chests": 0, - "average": 0, - "time": 0, - "rate": 0 + profit: 0, + chests: 0, + average: 0, + time: 0, + rate: 0, }; register("command", () => { - downtime = 300; - kuudraSession = { - "profit": 0, - "chests": 0, - "average": 0, - "time": 0, - "rate": 0 - }; - data.kuudraSession = { - "profit": 0, - "chests": 0, - "average": 0, - "time": 0, - "rate": 0 - }; - ChatLib.chat(`${GREEN}Successfully reset Kuudra Profit Tracker!`) + downtime = 300; + kuudraSession = { + profit: 0, + chests: 0, + average: 0, + time: 0, + rate: 0, + }; + data.kuudraSession = { + profit: 0, + chests: 0, + average: 0, + time: 0, + rate: 0, + }; + ChatLib.chat(`${GREEN}Successfully reset Kuudra Profit Tracker!`); }).setName("resetKPT"); -register("worldUnload", () => { chestOpened = false }); +register("worldUnload", () => { + chestOpened = false; +}); /** * Variables used to represent and render overlay. */ -const profitExample = -`${GOLD + BOLD}Profit/Loss: ${GREEN}Vaporean +const profitExample = `${GOLD + BOLD}Profit/Loss: ${GREEN}Vaporean ${AQUA + BOLD}Primary: ${GREEN}Is ${DARK_AQUA + BOLD}Secondary: ${GREEN}The @@ -60,13 +66,15 @@ ${RED + BOLD}Essence: ${GREEN}Compatible ${DARK_RED + BOLD}Key: ${RED}Pokemon...`; const profitOverlay = new Overlay("kuudraProfit", data.KL, "moveKP", profitExample, ["Kuudra"], "guiRender"); -const coinageExample = -`${DARK_RED + BOLD}Profit: ${WHITE}And +const coinageExample = `${DARK_RED + BOLD}Profit: ${WHITE}And ${DARK_RED + BOLD}Chests: ${WHITE}He ${DARK_RED + BOLD}Average: ${WHITE}Asked ${DARK_RED + BOLD}Time: ${WHITE}The ${DARK_RED + BOLD}Rate: ${WHITE}Man`; -const coinageOverlay = new Overlay("kuudraProfitTracker", data.ZL, "moveKPT", coinageExample, ["Kuudra", "Crimson Isle"]); +const coinageOverlay = new Overlay("kuudraProfitTracker", data.ZL, "moveKPT", coinageExample, [ + "Kuudra", + "Crimson Isle", +]); /** * Updates the profit tracker and metrics based on chest opening. @@ -74,80 +82,105 @@ const coinageOverlay = new Overlay("kuudraProfitTracker", data.ZL, "moveKPT", co * @param {Boolean} openedChest - Whether a chest was opened. */ function updateProfitTracker(openedChest) { - if (openedChest) { - kuudraSession.profit += chestProfit; - kuudraSession.chests++; - kuudraSession.average = kuudraSession.profit / kuudraSession.chests; - - data.kuudraSession.profit += chestProfit; - data.kuudraSession.chests++; - data.kuudraSession.average = data.kuudraSession.profit / data.kuudraSession.chests; - } else { - kuudraSession.time++; - data.kuudraSession.time++; - } - kuudraSession.rate = kuudraSession.profit / kuudraSession.time * 3600; - data.kuudraSession.rate = kuudraSession.profit / kuudraSession.time * 3600; + if (openedChest) { + kuudraSession.profit += chestProfit; + kuudraSession.chests++; + kuudraSession.average = kuudraSession.profit / kuudraSession.chests; + + data.kuudraSession.profit += chestProfit; + data.kuudraSession.chests++; + data.kuudraSession.average = data.kuudraSession.profit / data.kuudraSession.chests; + } else { + kuudraSession.time++; + data.kuudraSession.time++; + } + kuudraSession.rate = (kuudraSession.profit / kuudraSession.time) * 3600; + data.kuudraSession.rate = (kuudraSession.profit / kuudraSession.time) * 3600; - const profitView = Settings.kuudraProfitTracker === 1 ? data.kuudraSession : kuudraSession; - coinageOverlay.setMessage( -`${DARK_RED + BOLD}Profit: ${WHITE + formatNumber(profitView.profit.toFixed(0))} ¢ + const profitView = Settings.kuudraProfitTracker === 1 ? data.kuudraSession : kuudraSession; + coinageOverlay.setMessage( + `${DARK_RED + BOLD}Profit: ${WHITE + formatNumber(profitView.profit.toFixed(0))} ¢ ${DARK_RED + BOLD}Chests: ${WHITE + commafy(profitView.chests)} chests ${DARK_RED + BOLD}Average: ${WHITE + formatNumber(profitView.average.toFixed(0))} ¢/chest ${DARK_RED + BOLD}Time: ${WHITE + formatTime(profitView.time)} -${DARK_RED + BOLD}Rate: ${WHITE + formatNumber(profitView.rate.toFixed(0))} ¢/hr`); +${DARK_RED + BOLD}Rate: ${WHITE + formatNumber(profitView.rate.toFixed(0))} ¢/hr` + ); } /** * Handles interactions with the "Paid Chest" container and updates the profit tracker. */ -registerWhen(register("guiMouseClick", (x, y, button, gui) => { - if (Player.getContainer().getName() !== "Paid Chest" || gui?.getSlotUnderMouse()?.field_75222_d !== 31 || chestOpened) return; +registerWhen( + register("guiMouseClick", (x, y, button, gui) => { + if ( + Player.getContainer().getName() !== "Paid Chest" || + gui?.getSlotUnderMouse()?.field_75222_d !== 31 || + chestOpened + ) + return; downtime = 0; updateProfitTracker(true); chestOpened = true; -}), () => location.getWorld() === "Kuudra" && Settings.kuudraProfitTracker !== 0); + }), + () => location.getWorld() === "Kuudra" && Settings.kuudraProfitTracker !== 0 +); /** * Track time and downtime of runs. */ -registerWhen(register("step", () => { +registerWhen( + register("step", () => { downtime++; if (downtime >= 300) return; updateProfitTracker(false); -}).setFps(1), () => Settings.kuudraProfitTracker !== 0); -registerWhen(register("chat", () => { + }).setFps(1), + () => Settings.kuudraProfitTracker !== 0 +); +registerWhen( + register("chat", () => { downtime = 0; -}).setCriteria("[NPC] Elle: Talk with me to begin!"), () => Settings.kuudraProfitTracker !== 0); + }).setCriteria("[NPC] Elle: Talk with me to begin!"), + () => Settings.kuudraProfitTracker !== 0 +); /** * Updates Kuudra chest profit data and overlay on chest open. */ -registerWhen(register("guiOpened", () => { +registerWhen( + register("guiOpened", () => { Client.scheduleTask(1, () => { - const container = Player.getContainer(); - if (container.getName() !== "Paid Chest") return; - const bazaar = getBazaar(); - const tier = location.getTier(); + const container = Player.getContainer(); + if (container.getName() !== "Paid Chest") return; + const bazaar = getBazaar(); + const tier = location.getTier(); - const primary = getItemValue(container.getStackInSlot(11)); - let secondary = container.getStackInSlot(12); - secondary = bazaar[secondary.getNBT().getCompoundTag("tag").getCompoundTag("ExtraAttributes").getString("id")]?.[0] || getItemValue(secondary); - let essence = container.getStackInSlot(14).getNBT().toObject().tag.display.Name; - essence = parseInt(essence.slice(essence.indexOf('x') + 1)) * bazaar.ESSENCE_CRIMSON[0]; - const teeth = Settings.maxChili ? (bazaar.ENCHANTMENT_TABASCO_3[0] - 64*bazaar.CHILI_PEPPER[1])/6 * Math.ceil(tier / 2) : 0; - const cost = KEY_COST[tier - 1][0] + KEY_COST[tier - 1][1] * Math.min(bazaar.ENCHANTED_RED_SAND[1], bazaar.ENCHANTED_MYCELIUM[1]); - const value = primary + secondary + essence + teeth; - chestProfit = value - cost; + const primary = getItemValue(container.getStackInSlot(11)); + let secondary = container.getStackInSlot(12); + secondary = + bazaar[secondary.getNBT().getCompoundTag("tag").getCompoundTag("ExtraAttributes").getString("id")]?.[0] || + getItemValue(secondary); + let essence = container.getStackInSlot(14).getNBT().toObject().tag.display.Name; + essence = parseInt(essence.slice(essence.indexOf("x") + 1)) * bazaar.ESSENCE_CRIMSON[0]; + const teeth = Settings.maxChili + ? ((bazaar.ENCHANTMENT_TABASCO_3[0] - 64 * bazaar.CHILI_PEPPER[1]) / 6) * Math.ceil(tier / 2) + : 0; + const cost = + KEY_COST[tier - 1][0] + + KEY_COST[tier - 1][1] * Math.min(bazaar.ENCHANTED_RED_SAND[1], bazaar.ENCHANTED_MYCELIUM[1]); + const value = primary + secondary + essence + teeth; + chestProfit = value - cost; - const profitMessage = chestProfit >= 0 ? `${GREEN}+${commafy(chestProfit)}` : `${RED}-${commafy(chestProfit)}`; - profitOverlay.setMessage( -`${GOLD + BOLD}Profit/Loss: ${profitMessage} + const profitMessage = chestProfit >= 0 ? `${GREEN}+${commafy(chestProfit)}` : `${RED}-${commafy(chestProfit)}`; + profitOverlay.setMessage( + `${GOLD + BOLD}Profit/Loss: ${profitMessage} ${AQUA + BOLD}Primary: ${GREEN}+${commafy(primary)} ${DARK_AQUA + BOLD}Secondary: ${GREEN}+${commafy(secondary)} ${DARK_PURPLE + BOLD}Teeth: ${GREEN}+${commafy(teeth)} ${RED + BOLD}Essence: ${GREEN}+${commafy(essence)} -${DARK_RED + BOLD}Key: ${RED}-${commafy(cost)}`); +${DARK_RED + BOLD}Key: ${RED}-${commafy(cost)}` + ); }); -}), () => location.getWorld() === "Kuudra"); + }), + () => location.getWorld() === "Kuudra" +); diff --git a/features/kuudra/KuudraSplits.js b/features/kuudra/KuudraSplits.js index d851ecd..39527d0 100644 --- a/features/kuudra/KuudraSplits.js +++ b/features/kuudra/KuudraSplits.js @@ -1,25 +1,25 @@ -import location from "../../utils/Location"; -import Settings from "../../utils/Settings"; import { AQUA, BOLD, DARK_GREEN, GOLD, GRAY, GREEN, LOGO, RED, RESET } from "../../utils/Constants"; -import { formatTime, isValidDate } from "../../utils/functions/format"; +import { data } from "../../utils/Data"; +import location from "../../utils/Location"; import { Overlay } from "../../utils/Overlay"; -import { getPlayerName } from "../../utils/functions/player"; import { registerWhen } from "../../utils/RegisterTils"; +import Settings from "../../utils/Settings"; import { delay } from "../../utils/ThreadTils"; -import { data } from "../../utils/Data"; +import { formatTime, isValidDate } from "../../utils/functions/format"; +import { getPlayerName } from "../../utils/functions/player"; import { getKuudraHP } from "./KuudraDetect"; - /** * Variables used to track and display Kuudra split overlay. */ let kuudraSplit = [0, 0, 0, 0, 0]; -let times = ['0s', '0s', '0s', '0s']; +let times = ["0s", "0s", "0s", "0s"]; let phase = 0; -export function getPhase() { return phase }; +export function getPhase() { + return phase; +} let party = []; -const splitsExample = -`${AQUA + BOLD}Supplies: ${RESET}Bei +const splitsExample = `${AQUA + BOLD}Supplies: ${RESET}Bei ${AQUA + BOLD}Build: ${RESET}Feng ${AQUA + BOLD}Fuel/Stun: ${RESET}Xiao ${AQUA + BOLD}Kuudra: ${RESET}Xiao`; @@ -32,67 +32,84 @@ const now = new Date(); const yyyy = now.getFullYear(); let mm = now.getMonth() + 1; let dd = now.getDate(); -if (dd < 10) dd = '0' + dd; -if (mm < 10) mm = '0' + mm; +if (dd < 10) dd = "0" + dd; +if (mm < 10) mm = "0" + mm; /** * Resets Kuudra splits on run start. */ -registerWhen(register("worldLoad", () => { +registerWhen( + register("worldLoad", () => { kuudraSplit = [0, 0, 0, 0]; - times = ['0s', '0s', '0s', '0s']; + times = ["0s", "0s", "0s", "0s"]; phase = 0; -}), () => Settings.kuudraSplits); + }), + () => Settings.kuudraSplits +); /** * Tracks party on player ready. */ -registerWhen(register("chat", (player) => { +registerWhen( + register("chat", (player) => { player = player.toLowerCase(); if (!party.includes(player)) party.push(player); -}).setCriteria("${player} is now ready!"), () => Settings.kuudraSplits); + }).setCriteria("${player} is now ready!"), + () => Settings.kuudraSplits +); /** * First split. */ -registerWhen(register("chat", () => { +registerWhen( + register("chat", () => { kuudraSplit[0] = Date.now() / 1000; phase = 1; -}).setCriteria("[NPC] Elle: Okay adventurers, I will go and fish up Kuudra!"), -() => location.getWorld() === "Kuudra"); + }).setCriteria("[NPC] Elle: Okay adventurers, I will go and fish up Kuudra!"), + () => location.getWorld() === "Kuudra" +); /** * Second split. */ -registerWhen(register("chat", () => { +registerWhen( + register("chat", () => { kuudraSplit[1] = Date.now() / 1000; phase = 2; -}).setCriteria("[NPC] Elle: OMG! Great work collecting my supplies!"), -() => location.getWorld() === "Kuudra"); + }).setCriteria("[NPC] Elle: OMG! Great work collecting my supplies!"), + () => location.getWorld() === "Kuudra" +); /** * Third split. */ -registerWhen(register("chat", () => { +registerWhen( + register("chat", () => { kuudraSplit[2] = Date.now() / 1000; phase = 3; -}).setCriteria("[NPC] Elle: Phew! The Ballista is finally ready! It should be strong enough to tank Kuudra's blows now!"), -() => location.getWorld() === "Kuudra"); + }).setCriteria( + "[NPC] Elle: Phew! The Ballista is finally ready! It should be strong enough to tank Kuudra's blows now!" + ), + () => location.getWorld() === "Kuudra" +); /** * Fourth split. */ -registerWhen(register("chat", () => { +registerWhen( + register("chat", () => { kuudraSplit[3] = Date.now() / 1000; phase = 4; -}).setCriteria("[NPC] Elle: POW! SURELY THAT'S IT! I don't think he has any more in him!"), -() => location.getWorld() === "Kuudra"); + }).setCriteria("[NPC] Elle: POW! SURELY THAT'S IT! I don't think he has any more in him!"), + () => location.getWorld() === "Kuudra" +); /** * Fifth (final) split. * Records split to a full and party data file. */ -registerWhen(register("chat", () => { +registerWhen( + register("chat", () => { kuudraSplit[4] = Date.now() / 1000; phase = 5; if (!Settings.kuudraSplits) return; @@ -100,137 +117,160 @@ registerWhen(register("chat", () => { // Records last split and checks if no fucky wucky let broken = false; for (let i = 0; i < data.splits.last.length - 1; i++) { - data.splits.last[i] = parseFloat(Math.abs(kuudraSplit[i + 1] - kuudraSplit[i]).toFixed(2)); - if (data.splits.last[i] > 69420 || data.splits.last[i] === 0) broken = true; + data.splits.last[i] = parseFloat(Math.abs(kuudraSplit[i + 1] - kuudraSplit[i]).toFixed(2)); + if (data.splits.last[i] > 69420 || data.splits.last[i] === 0) broken = true; } // Record Total - data.splits.last[4] = parseFloat((data.splits.last[0] + data.splits.last[1] + data.splits.last[2] + data.splits.last[3]).toFixed(2)); - + data.splits.last[4] = parseFloat( + (data.splits.last[0] + data.splits.last[1] + data.splits.last[2] + data.splits.last[3]).toFixed(2) + ); + // Record splits let splitFormat = ""; if (location.getTier() === 5) { - // Check if new best split / run - for (let i = 0; i < data.splits.last.length; i++) { - if (!broken) - splitFormat += `${data.splits.last[i]}, `; - // Record best splits - if (data.splits.last[i] < data.splits.best[i] && data.splits.last[i] !== 0) - data.splits.best[i] = data.splits.last[i]; - // Record worst splits - if (data.splits.last[i] > data.splits.worst[i] && data.splits.last[i] < 999) - data.splits.worst[i] = data.splits.last[i]; - } - // Tracks when timer not infinite - if (!broken) { - splitFormat = splitFormat + mm+'/'+dd+'/'+yyyy + '\n'; - FileLib.append("./VolcAddons/Data", "splits.txt", splitFormat); - if (!data.files.includes("splits.txt")) - data.files.push("splits.txt"); + // Check if new best split / run + for (let i = 0; i < data.splits.last.length; i++) { + if (!broken) splitFormat += `${data.splits.last[i]}, `; + // Record best splits + if (data.splits.last[i] < data.splits.best[i] && data.splits.last[i] !== 0) + data.splits.best[i] = data.splits.last[i]; + // Record worst splits + if (data.splits.last[i] > data.splits.worst[i] && data.splits.last[i] < 999) + data.splits.worst[i] = data.splits.last[i]; + } + // Tracks when timer not infinite + if (!broken) { + splitFormat = splitFormat + mm + "/" + dd + "/" + yyyy + "\n"; + FileLib.append("./VolcAddons/Data", "splits.txt", splitFormat); + if (!data.files.includes("splits.txt")) data.files.push("splits.txt"); - // Tracks splits for unique parties - const fileMembers = party.sort().join("-") + ".txt"; - if (party.length === 4) { - FileLib.append("./VolcAddons/Data", fileMembers, splitFormat); - if (!data.files.includes(fileMembers)) - data.files.push(fileMembers); - } + // Tracks splits for unique parties + const fileMembers = party.sort().join("-") + ".txt"; + if (party.length === 4) { + FileLib.append("./VolcAddons/Data", fileMembers, splitFormat); + if (!data.files.includes(fileMembers)) data.files.push(fileMembers); } + } } // Resets party tracker party = []; -}).setCriteria("${before}KUUDRA DOWN${after}"), () => location.getWorld() === "Kuudra"); + }).setCriteria("${before}KUUDRA DOWN${after}"), + () => location.getWorld() === "Kuudra" +); /** * Fifth (final split) if you fail :skull:. */ -registerWhen(register("chat", () => { +registerWhen( + register("chat", () => { kuudraSplit[4] = Date.now() / 1000; phase = 5; -}).setCriteria("${before}DEFEAT${after}"), () => location.getWorld() === "Kuudra" && Settings.kuudraSplits); + }).setCriteria("${before}DEFEAT${after}"), + () => location.getWorld() === "Kuudra" && Settings.kuudraSplits +); /** * Updates time splits overlay. */ -registerWhen(register("step", () => { +registerWhen( + register("step", () => { // Phase 4 fail safe if (phase === 3 && getKuudraHP() < 25000 && location.getTier() === 5) { - kuudraSplit[3] = Date.now() / 1000; - phase = 4; + kuudraSplit[3] = Date.now() / 1000; + phase = 4; } if (phase === 4 && getKuudraHP() < 10) { - kuudraSplit[4] = Date.now() / 1000; - phase = 5; + kuudraSplit[4] = Date.now() / 1000; + phase = 5; } switch (phase) { - case 1: - times[0] = formatTime(Date.now() / 1000 - kuudraSplit[0], 2); - break; - case 2: - times[0] = formatTime(kuudraSplit[1] - kuudraSplit[0], 2); - times[1] = formatTime(Date.now() / 1000 - kuudraSplit[1], 2); - break; - case 3: - times[1] = formatTime(kuudraSplit[2] - kuudraSplit[1], 2); - times[2] = formatTime(Date.now() / 1000 - kuudraSplit[2], 2); - break; - case 4: - times[2] = formatTime(kuudraSplit[3] - kuudraSplit[2], 2); - times[3] = formatTime(Date.now() / 1000 - kuudraSplit[3], 2); - break; - case 5: - times[3] = formatTime(kuudraSplit[4] - kuudraSplit[3], 2); - break; + case 1: + times[0] = formatTime(Date.now() / 1000 - kuudraSplit[0], 2); + break; + case 2: + times[0] = formatTime(kuudraSplit[1] - kuudraSplit[0], 2); + times[1] = formatTime(Date.now() / 1000 - kuudraSplit[1], 2); + break; + case 3: + times[1] = formatTime(kuudraSplit[2] - kuudraSplit[1], 2); + times[2] = formatTime(Date.now() / 1000 - kuudraSplit[2], 2); + break; + case 4: + times[2] = formatTime(kuudraSplit[3] - kuudraSplit[2], 2); + times[3] = formatTime(Date.now() / 1000 - kuudraSplit[3], 2); + break; + case 5: + times[3] = formatTime(kuudraSplit[4] - kuudraSplit[3], 2); + break; } - + // Draw Splits splitsOverlay.setMessage( -`${AQUA + BOLD}Supplies: ${RESET + times[0]} + `${AQUA + BOLD}Supplies: ${RESET + times[0]} ${AQUA + BOLD}Build: ${RESET + times[1]} ${AQUA + BOLD}Fuel/Stun: ${RESET + times[2]} -${AQUA + BOLD}Kuudra: ${RESET + times[3]}`); -}).setFps(19), () => location.getWorld() === "Kuudra" && Settings.kuudraSplits); +${AQUA + BOLD}Kuudra: ${RESET + times[3]}` + ); + }).setFps(19), + () => location.getWorld() === "Kuudra" && Settings.kuudraSplits +); /** * Party commands for splits. */ let onCD = false; -registerWhen(register("chat", (player, message) => { +registerWhen( + register("chat", (player, message) => { const name = getPlayerName(player); if ((!Settings.partyCommands && !name.equals(Player.getName())) || onCD) return; const args = message.split(" "); switch (args[0]) { - case "splits": - case "split": - case "last": - last = [ - formatTime(data.splits.last[0], 2), - formatTime(data.splits.last[1], 2), - formatTime(data.splits.last[2], 2), - formatTime(data.splits.last[3], 2), - formatTime(data.splits.last[4], 2) - ]; - delay(() => ChatLib.command(`pc Supplies: ${last[0]} | Build: ${last[1]} | Fuel/Stun: ${last[2]} | Kuudra: ${last[3]} | Total: ${last[4]}`), 500); - break; - case "best": - best = [ - formatTime(data.splits.best[0], 2), - formatTime(data.splits.best[1], 2), - formatTime(data.splits.best[2], 2), - formatTime(data.splits.best[3], 2), - formatTime(data.splits.best[4], 2) - ]; - theory = formatTime(data.splits.best[0] + data.splits.best[1] + data.splits.best[2] + data.splits.best[3], 2); - delay(() => ChatLib.command(`pc Supplies: ${best[0]} | Build: ${best[1]} | Fuel/Stun: ${best[2]} | Kuudra: ${best[3]} | Total: ${best[4]} | Theoretical Best: ${theory}`), 500); - break; + case "splits": + case "split": + case "last": + last = [ + formatTime(data.splits.last[0], 2), + formatTime(data.splits.last[1], 2), + formatTime(data.splits.last[2], 2), + formatTime(data.splits.last[3], 2), + formatTime(data.splits.last[4], 2), + ]; + delay( + () => + ChatLib.command( + `pc Supplies: ${last[0]} | Build: ${last[1]} | Fuel/Stun: ${last[2]} | Kuudra: ${last[3]} | Total: ${last[4]}` + ), + 500 + ); + break; + case "best": + best = [ + formatTime(data.splits.best[0], 2), + formatTime(data.splits.best[1], 2), + formatTime(data.splits.best[2], 2), + formatTime(data.splits.best[3], 2), + formatTime(data.splits.best[4], 2), + ]; + theory = formatTime(data.splits.best[0] + data.splits.best[1] + data.splits.best[2] + data.splits.best[3], 2); + delay( + () => + ChatLib.command( + `pc Supplies: ${best[0]} | Build: ${best[1]} | Fuel/Stun: ${best[2]} | Kuudra: ${best[3]} | Total: ${best[4]} | Theoretical Best: ${theory}` + ), + 500 + ); + break; } onCD = true; - delay(() => onCD = false, 500); -}).setCriteria("Party > ${player}: ?${message}"), () => Settings.kuudraSplits); + delay(() => (onCD = false), 500); + }).setCriteria("Party > ${player}: ?${message}"), + () => Settings.kuudraSplits +); /** * Uses sound name and pitch to determine whenever Ragnarok Ability goes off. @@ -240,21 +280,27 @@ registerWhen(register("chat", (player, message) => { * @param {Number} runs - Amount of runs to average. */ function formatSplits(splits, color, runs) { - if (color === GREEN) ChatLib.chat(`${DARK_GREEN + BOLD}Average for last ${runs} runs:`); - ChatLib.chat( -`${color + BOLD}Supplies: ${RESET + formatTime(splits[0], 2)} + if (color === GREEN) ChatLib.chat(`${DARK_GREEN + BOLD}Average for last ${runs} runs:`); + ChatLib.chat( + `${color + BOLD}Supplies: ${RESET + formatTime(splits[0], 2)} ${color + BOLD}Build: ${RESET + formatTime(splits[1], 2)} ${color + BOLD}Fuel/Stun: ${RESET + formatTime(splits[2], 2)} ${color + BOLD}Kuudra: ${RESET + formatTime(splits[3], 2)} -${color + BOLD}Overall Run: ${RESET + formatTime(splits[4], 2)}`); - if (color === GOLD) { - const theory = (data.splits.best[0] + data.splits.best[1] + data.splits.best[2] + data.splits.best[3]).toFixed(2); - ChatLib.chat(`${color + BOLD}Theoretical Best: ${RESET + formatTime(theory, 2)}`); - } - if (color === RED) { - const conjecture = (data.splits.worst[0] + data.splits.worst[1] + data.splits.worst[2] + data.splits.worst[3]).toFixed(2); - ChatLib.chat(`${color + BOLD}Theoretical Worst: ${RESET + formatTime(conjecture, 2)}`); - } +${color + BOLD}Overall Run: ${RESET + formatTime(splits[4], 2)}` + ); + if (color === GOLD) { + const theory = (data.splits.best[0] + data.splits.best[1] + data.splits.best[2] + data.splits.best[3]).toFixed(2); + ChatLib.chat(`${color + BOLD}Theoretical Best: ${RESET + formatTime(theory, 2)}`); + } + if (color === RED) { + const conjecture = ( + data.splits.worst[0] + + data.splits.worst[1] + + data.splits.worst[2] + + data.splits.worst[3] + ).toFixed(2); + ChatLib.chat(`${color + BOLD}Theoretical Worst: ${RESET + formatTime(conjecture, 2)}`); + } } /** @@ -262,66 +308,79 @@ ${color + BOLD}Overall Run: ${RESET + formatTime(splits[4], 2)}`); * * @param {String[]} args - Array of player input values. */ -export function getSplits(args){ - switch (args[1]) { - case "last": - formatSplits(data.splits.last, AQUA, 0); - return; - case "best": - formatSplits(data.splits.best, GOLD, 0); - return; - case "worst": - formatSplits(data.splits.worst, RED, 0); - return; - case "today": - const today = true; - case "average": - // Gets file name - let fileName = "splits.txt"; - if (args[6] !== undefined) fileName = [args[3], args[4], args[5], args[6]].map(p => p.toLowerCase()).sort().join("-") + ".txt"; - else if (args[5] !== undefined) fileName = [args[2], args[3], args[4], args[5]].map(p => p.toLowerCase()).sort().join("-") + ".txt"; +export function getSplits(args) { + switch (args[1]) { + case "last": + formatSplits(data.splits.last, AQUA, 0); + return; + case "best": + formatSplits(data.splits.best, GOLD, 0); + return; + case "worst": + formatSplits(data.splits.worst, RED, 0); + return; + case "today": + const today = true; + case "average": + // Gets file name + let fileName = "splits.txt"; + if (args[6] !== undefined) + fileName = + [args[3], args[4], args[5], args[6]] + .map((p) => p.toLowerCase()) + .sort() + .join("-") + ".txt"; + else if (args[5] !== undefined) + fileName = + [args[2], args[3], args[4], args[5]] + .map((p) => p.toLowerCase()) + .sort() + .join("-") + ".txt"; - const fileSplits = FileLib.read("./VolcAddons/Data", fileName); + const fileSplits = FileLib.read("./VolcAddons/Data", fileName); - // Get runs from file - if (fileSplits) { - let runs = fileSplits.split("\n"); - runs.pop(); + // Get runs from file + if (fileSplits) { + let runs = fileSplits.split("\n"); + runs.pop(); - // Filter by date - if (isValidDate(args[2])) - runs = runs.filter((run) => run.split(", ")[5] === args[2]); - if (today || args[2] === "today") - runs = runs.filter((run) => run.split(", ")[5] === (mm+"/"+dd+"/"+yyyy)); - - // Get # of runs to average - let runsWanted = runs.length; - if (!isNaN(args[2])) if (args[2] < runsWanted) runsWanted = args[2]; + // Filter by date + if (isValidDate(args[2])) runs = runs.filter((run) => run.split(", ")[5] === args[2]); + if (today || args[2] === "today") + runs = runs.filter((run) => run.split(", ")[5] === mm + "/" + dd + "/" + yyyy); - // Average the runs - let average = [0, 0, 0, 0, 0]; - let run = undefined; - for (let i = runs.length - 1; i >= runs.length - runsWanted; i--) { - run = runs[i].split(", "); - if (run.length > 1) for (let j = 0; j < average.length; j++) average[j] += parseFloat(run[j]); - } - for (let i = 0; i < average.length; i++) average[i] = average[i] / runsWanted; + // Get # of runs to average + let runsWanted = runs.length; + if (!isNaN(args[2])) if (args[2] < runsWanted) runsWanted = args[2]; - formatSplits(average, GREEN, runsWanted); - } else ChatLib.chat(`${RED}File [${fileName + RED}] not found!`); - return; - case "clear": - // Clears every split - data.files.forEach(file => { - FileLib.delete("./VolcAddons/Data", file); - }); - data.files = []; - - ChatLib.chat(`${LOGO + GREEN}Succesfully cleared splits!`) - return; - } + // Average the runs + let average = [0, 0, 0, 0, 0]; + let run = undefined; + for (let i = runs.length - 1; i >= runs.length - runsWanted; i--) { + run = runs[i].split(", "); + if (run.length > 1) for (let j = 0; j < average.length; j++) average[j] += parseFloat(run[j]); + } + for (let i = 0; i < average.length; i++) average[i] = average[i] / runsWanted; + + formatSplits(average, GREEN, runsWanted); + } else ChatLib.chat(`${RED}File [${fileName + RED}] not found!`); + return; + case "clear": + // Clears every split + data.files.forEach((file) => { + FileLib.delete("./VolcAddons/Data", file); + }); + data.files = []; - // Invalid command - ChatLib.chat(`\n${LOGO + RED}Error: Invalid argument "${args[1]}"!`); - ChatLib.chat(`${LOGO + RED}Please input as: ${WHITE}/va splits ${GRAY}<${WHITE}clear, last, best, today, average ${GRAY}<${WHITE}[# of runs], [player members], [mm/dd/yyyy]${GRAY}>>`); -}; + ChatLib.chat(`${LOGO + GREEN}Succesfully cleared splits!`); + return; + } + + // Invalid command + ChatLib.chat(`\n${LOGO + RED}Error: Invalid argument "${args[1]}"!`); + ChatLib.chat( + `${ + LOGO + RED + }Please input as: ${WHITE}/va splits ${GRAY}<${WHITE}clear, last, best, today, average ${GRAY}<${WHITE}[# of runs], [player members], [mm/dd/yyyy]${GRAY}>>` + ); +} diff --git a/features/kuudra/KuudraView.js b/features/kuudra/KuudraView.js index e467db2..c2c97e4 100644 --- a/features/kuudra/KuudraView.js +++ b/features/kuudra/KuudraView.js @@ -1,228 +1,280 @@ import axios from "../../../axios"; +import { + AQUA, + BOLD, + DARK_AQUA, + DARK_GRAY, + DARK_PURPLE, + DARK_RED, + GOLD, + GRAY, + LOGO, + RED, + WHITE, + YELLOW, +} from "../../utils/Constants"; +import { registerWhen } from "../../utils/RegisterTils"; import Settings from "../../utils/Settings"; -import { AQUA, BOLD, DARK_AQUA, DARK_GRAY, DARK_PURPLE, DARK_RED, GOLD, GRAY, LOGO, RED, WHITE, YELLOW } from "../../utils/Constants"; import { convertToTitleCase, formatNumber } from "../../utils/functions/format"; import { decode } from "../../utils/functions/misc"; -import { registerWhen } from "../../utils/RegisterTils"; - /** * Checks if given inventory contains any required gear to print out. - * + * * @param {String} inv - Inventory NBT data. * @param {String} type - Inventory name. * @param {Array[]} aurora - Tracked aurora armor pieces [tier, stars, message] * @param {Array[]} terror - Tracked terror armor pieces [tier, stars, message] - * @returns + * @returns */ function containsGoods(inv, type, aurora, terror, dominance, lifeline) { - if (inv === undefined) { - ChatLib.chat(`${DARK_GRAY}- ${RED + type} API is OFF!`); - return; - } + if (inv === undefined) { + ChatLib.chat(`${DARK_GRAY}- ${RED + type} API is OFF!`); + return; + } + + // Goods to be contained + const GOODS = new Set(["NECRON_BLADE", "HYPERION", "VALKYRIE", "ASTRAEA", "SCYLLA", "TERMINATOR"]); + const TIERS = ["HOT", "BURNING", "FIERY", "INFERNAL"]; + const PIECE = ["HELMET", "CHESTPLATE", "LEGGINGS", "BOOTS"]; + const EQUIP = ["NECKLACE", "CLOAK", "BELT", "GAUNTLET", "GLOVES"]; - // Goods to be contained - const GOODS = new Set(["NECRON_BLADE", "HYPERION", "VALKYRIE", "ASTRAEA", "SCYLLA", "TERMINATOR"]); - const TIERS = ["HOT", "BURNING", "FIERY", "INFERNAL"]; - const PIECE = ["HELMET", "CHESTPLATE", "LEGGINGS", "BOOTS"]; - const EQUIP = ["NECKLACE", "CLOAK", "BELT", "GAUNTLET", "GLOVES"] - - - // Decode inventory NBT - let items = decode(inv); - - // Loop through inventory data - for (let i = 0; i < items.func_74745_c(); i++) { - // Get item data - let nbt = new NBTTagCompound(items.func_150305_b(i)); - let tag = nbt.getCompoundTag("tag"); - if (tag.hasNoTags()) continue; - let extraAttributes = tag.getCompoundTag("ExtraAttributes"); - let attributes = extraAttributes.getCompoundTag("attributes").toObject(); - let id = extraAttributes.getString("id"); - let args = id.split('_'); - let display = tag.getCompoundTag("display"); - let name = display.getString("Name"); - - // Check if item is a good one :) - if (GOODS.has(id) || (id === "RAGNAROCK_AXE" && extraAttributes.getInteger("rarity_upgrades") === 1)) { - let data = name; - let lore = display.toObject()["Lore"]; - lore.forEach(line => data += `\n${line}`); - new TextComponent(`${DARK_GRAY}- ${name}`).setHoverValue(data).chat(); - } else if (args[1] === "AURORA" || args[1] === "TERROR") { - let type = args[1] === "AURORA" ? aurora : terror; - let piece = type[PIECE.indexOf(args[2])]; - let tier = TIERS.indexOf(args[0]); - let level = extraAttributes.getInteger("upgrade_level"); - if (tier > piece[0] || (tier === piece[0] && level > piece[1])) { - piece[0] = tier; - piece[1] = level; - - // Get attribute and add to name - let attributeTitle = Object.entries(attributes) - .map(([key, value]) => { - const abbreviation = key.split('_').map(word => word.charAt(0).toUpperCase()).join(''); - return abbreviation + value; - }).join(', '); - piece[2] = `${GRAY}[${attributeTitle}] ${name}`; - } - - // Update dom/lifeline on terror - if (args[1] === "TERROR") { - piece[3] = Math.max(piece[3], attributes?.dominance ?? 0); - piece[4] = Math.max(piece[3], attributes?.lifeline ?? 0); - } - } else if (args.length !== 0) { - // Check for dominance/lifeline equip - let slot = Math.min(Math.max(EQUIP.indexOf(args[0]), EQUIP.indexOf(args[args.length - 1])), 3); - if (slot === -1) continue; - let dom = attributes?.dominance; - let ll = attributes?.lifeline; - - // Update equip - function setEquip(equip, type, newLevel) { - if (newLevel > equip[0]) { - equip[0] = newLevel; - equip[1] = `${GRAY}[${type + newLevel}] ${name}`; - } - } - if (dom !== undefined) setEquip(dominance[slot], "D", dom); - if (ll !== undefined) setEquip(lifeline[slot], "LL", ll); + // Decode inventory NBT + let items = decode(inv); + + // Loop through inventory data + for (let i = 0; i < items.func_74745_c(); i++) { + // Get item data + let nbt = new NBTTagCompound(items.func_150305_b(i)); + let tag = nbt.getCompoundTag("tag"); + if (tag.hasNoTags()) continue; + let extraAttributes = tag.getCompoundTag("ExtraAttributes"); + let attributes = extraAttributes.getCompoundTag("attributes").toObject(); + let id = extraAttributes.getString("id"); + let args = id.split("_"); + let display = tag.getCompoundTag("display"); + let name = display.getString("Name"); + + // Check if item is a good one :) + if (GOODS.has(id) || (id === "RAGNAROCK_AXE" && extraAttributes.getInteger("rarity_upgrades") === 1)) { + let data = name; + let lore = display.toObject()["Lore"]; + lore.forEach((line) => (data += `\n${line}`)); + new TextComponent(`${DARK_GRAY}- ${name}`).setHoverValue(data).chat(); + } else if (args[1] === "AURORA" || args[1] === "TERROR") { + let type = args[1] === "AURORA" ? aurora : terror; + let piece = type[PIECE.indexOf(args[2])]; + let tier = TIERS.indexOf(args[0]); + let level = extraAttributes.getInteger("upgrade_level"); + if (tier > piece[0] || (tier === piece[0] && level > piece[1])) { + piece[0] = tier; + piece[1] = level; + + // Get attribute and add to name + let attributeTitle = Object.entries(attributes) + .map(([key, value]) => { + const abbreviation = key + .split("_") + .map((word) => word.charAt(0).toUpperCase()) + .join(""); + return abbreviation + value; + }) + .join(", "); + piece[2] = `${GRAY}[${attributeTitle}] ${name}`; + } + + // Update dom/lifeline on terror + if (args[1] === "TERROR") { + piece[3] = Math.max(piece[3], attributes?.dominance ?? 0); + piece[4] = Math.max(piece[3], attributes?.lifeline ?? 0); + } + } else if (args.length !== 0) { + // Check for dominance/lifeline equip + let slot = Math.min(Math.max(EQUIP.indexOf(args[0]), EQUIP.indexOf(args[args.length - 1])), 3); + if (slot === -1) continue; + let dom = attributes?.dominance; + let ll = attributes?.lifeline; + + // Update equip + function setEquip(equip, type, newLevel) { + if (newLevel > equip[0]) { + equip[0] = newLevel; + equip[1] = `${GRAY}[${type + newLevel}] ${name}`; } + } + if (dom !== undefined) setEquip(dominance[slot], "D", dom); + if (ll !== undefined) setEquip(lifeline[slot], "LL", ll); } + } } /** * Parse user API data and search for relevant Kuudra items. - * + * * @param {String} name - Username ign to get Kuudra data of. */ function kuudraView(name) { - if (name === undefined) { - ChatLib.chat(`${LOGO + RED}Couldn't find any profile with name undefined...`); - ChatLib.chat(`${LOGO + RED}Please input as: ${WHITE}/kv [ign]`); + if (name === undefined) { + ChatLib.chat(`${LOGO + RED}Couldn't find any profile with name undefined...`); + ChatLib.chat(`${LOGO + RED}Please input as: ${WHITE}/kv [ign]`); + return; + } + + // Call Hypixel API + new Message(`${LOGO + YELLOW}Fetching API data...`).setChatLineId(3745).chat(); + axios + .get(`https://sky.shiiyu.moe/api/v2/profile/${name}`) + .then((response) => { + ChatLib.clearChat(3745); + + // Check if player profile exists + if (response.data.error !== undefined) { + ChatLib.chat(`${LOGO + RED}Couldn't find any profile with name ${name}...`); return; - } + } - // Call Hypixel API - new Message(`${LOGO + YELLOW}Fetching API data...`).setChatLineId(3745).chat(); - axios.get(`https://sky.shiiyu.moe/api/v2/profile/${name}`).then(response => { - ChatLib.clearChat(3745); + const profiles = response.data.profiles; + const selected = Object.keys(profiles).find((key) => profiles[key].current); + const data = profiles[selected].raw; + ChatLib.chat(`\n${LOGO + DARK_RED + BOLD + name}'s Kuudra View:\n`); - // Check if player profile exists - if (response.data.error !== undefined) { - ChatLib.chat(`${LOGO + RED}Couldn't find any profile with name ${name}...`); - return; - } + // Loop through inventory to check gear. + ChatLib.chat(`${DARK_AQUA + BOLD}Gear:`); + const inventory = data?.inventory; + // Armor pieces [tier, stars, name, dom, ll] + const aurora = [ + [-1, -1, `${RED}Headless`], + [-1, -1, `${RED}Heartless`], + [-1, -1, `${RED}Pantsgrab`], + [-1, -1, `${RED}Socksless`], + ]; + const terror = [ + [-1, -1, `${RED}Headless`, 0, 0], + [-1, -1, `${RED}Heartless`, 0, 0], + [-1, -1, `${RED}Pantsgrab`, 0, 0], + [-1, -1, `${RED}Socksless`, 0, 0], + ]; + // Equip pieces [attribute level, name] + const dominance = [ + [0, `${RED}Neckless`], + [0, `${RED}Cloakless`], + [0, `${RED}Fatherless`], + [0, `${RED}Handless`], + ]; + const lifeline = [ + [0, `${RED}Neckless`], + [0, `${RED}Cloakless`], + [0, `${RED}Fatherless`], + [0, `${RED}Handless`], + ]; + if (inventory === undefined) ChatLib.chat(`${DARK_GRAY}- ${RED}Inventory API is OFF!`); + else { + containsGoods(inventory?.inv_contents?.data, "Inventory", aurora, terror, dominance, lifeline); + containsGoods(inventory?.inv_armor?.data, "Armor", aurora, terror, dominance, lifeline); + containsGoods(inventory?.equipment_contents?.data, "Equipment", aurora, terror, dominance, lifeline); + containsGoods(inventory?.ender_chest_contents?.data, "Ender Chest", aurora, terror, dominance, lifeline); + containsGoods(inventory?.wardrobe_contents?.data, "Wardrobe", aurora, terror, dominance, lifeline); - const profiles = response.data.profiles; - const selected = Object.keys(profiles).find(key => profiles[key].current); - const data = profiles[selected].raw; - ChatLib.chat(`\n${LOGO + DARK_RED + BOLD + name}'s Kuudra View:\n`); - - // Loop through inventory to check gear. - ChatLib.chat(`${DARK_AQUA + BOLD}Gear:`); - const inventory = data?.inventory; - // Armor pieces [tier, stars, name, dom, ll] - const aurora = [[-1, -1, `${RED}Headless`], [-1, -1, `${RED}Heartless`], [-1, -1, `${RED}Pantsgrab`], [-1, -1, `${RED}Socksless`]]; - const terror = [[-1, -1, `${RED}Headless`, 0, 0], [-1, -1, `${RED}Heartless`, 0, 0], [-1, -1, `${RED}Pantsgrab`, 0, 0], [-1, -1, `${RED}Socksless`, 0, 0]]; - // Equip pieces [attribute level, name] - const dominance = [[0, `${RED}Neckless`], [0, `${RED}Cloakless`], [0, `${RED}Fatherless`], [0, `${RED}Handless`]]; - const lifeline = [[0, `${RED}Neckless`], [0, `${RED}Cloakless`], [0, `${RED}Fatherless`], [0, `${RED}Handless`]]; - if (inventory === undefined) ChatLib.chat(`${DARK_GRAY}- ${RED}Inventory API is OFF!`); - else { - containsGoods(inventory?.inv_contents?.data, "Inventory", aurora, terror, dominance, lifeline); - containsGoods(inventory?.inv_armor?.data, "Armor", aurora, terror, dominance, lifeline); - containsGoods(inventory?.equipment_contents?.data, "Equipment", aurora, terror, dominance, lifeline); - containsGoods(inventory?.ender_chest_contents?.data, "Ender Chest", aurora, terror, dominance, lifeline); - containsGoods(inventory?.wardrobe_contents?.data, "Wardrobe", aurora, terror, dominance, lifeline); - - // Loop over backpacks - const backpacks = inventory.backpack_contents; - const packs = backpacks === undefined ? 0 : Object.keys(backpacks).length; - for (let i = 0; i < packs; i++) { - containsGoods(backpacks[i.toString()]?.data, "Backpack", aurora, terror, dominance, lifeline); - } - if (packs === 0) ChatLib.chat(`${DARK_GRAY}- ${RED}Backpack API is OFF!`); - - // Chat out Aurora/Terror pieces in one message - ChatLib.chat(`${DARK_AQUA + BOLD}Armor:`); - new TextComponent(`${DARK_GRAY}- ${DARK_PURPLE}Aurora Pieces`) - .setHoverValue(`${DARK_PURPLE}Aurora Pieces\n${aurora.map(inner => inner[2]).join('\n')}`).chat(); - new TextComponent(`${DARK_GRAY}- ${DARK_PURPLE}Terror Pieces`) - .setHoverValue(`${DARK_PURPLE}Terror Pieces\n${terror.map(inner => inner[2]).join('\n')}`).chat(); - - // Equip pieces - const totalDom = dominance.reduce((sum, innerArray) => sum + (innerArray.length > 0 ? innerArray[0] : 0), 0) + - terror.reduce((sum, innerArray) => sum + (innerArray.length > 0 ? innerArray[3] : 0), 0); - const totolLL = lifeline.reduce((sum, innerArray) => sum + (innerArray.length > 0 ? innerArray[0] : 0), 0) + - terror.reduce((sum, innerArray) => sum + (innerArray.length > 0 ? innerArray[4] : 0), 0);; - new TextComponent(`${DARK_GRAY}- ${DARK_PURPLE}Dominance Equips ${GRAY}[Total: ${totalDom}]`) - .setHoverValue(`${DARK_PURPLE}Dominance Equips\n${dominance.map(inner => inner[1]).join('\n')}`).chat(); - new TextComponent(`${DARK_GRAY}- ${DARK_PURPLE}Lifeline Equips ${GRAY}[Total: ${totolLL}]`) - .setHoverValue(`${DARK_PURPLE}Lifeline Equips\n${lifeline.map(inner => inner[1]).join('\n')}`).chat(); + // Loop over backpacks + const backpacks = inventory.backpack_contents; + const packs = backpacks === undefined ? 0 : Object.keys(backpacks).length; + for (let i = 0; i < packs; i++) { + containsGoods(backpacks[i.toString()]?.data, "Backpack", aurora, terror, dominance, lifeline); } - - // Check for accessory power - ChatLib.chat(`${DARK_AQUA + BOLD}Misc:`); - ChatLib.chat(`${DARK_GRAY}- ${AQUA}Magical Power: ${WHITE + (data?.accessory_bag_storage?.highest_magical_power ?? RED + "I NEED MORE POWER.")}`); - - // Check for Gdrag - const pets = data?.pets_data?.pets; - let gdrag = [`${RED}Not found!`]; - pets.forEach(pet => { - if (pet.type !== "GOLDEN_DRAGON") return; - - if (pet.exp >= 210_255_385) { - gdrag[0] = `${GRAY}[Lvl 200] ${GOLD}Golden Dragon`; - gdrag.push(convertToTitleCase(pet.heldItem)); - } else if (gdrag[0].startsWith(RED)) - gdrag[0] = `${GRAY}[Lvl ${RED}< 200] ${GOLD}Golden Dragon`; + if (packs === 0) ChatLib.chat(`${DARK_GRAY}- ${RED}Backpack API is OFF!`); + + // Chat out Aurora/Terror pieces in one message + ChatLib.chat(`${DARK_AQUA + BOLD}Armor:`); + new TextComponent(`${DARK_GRAY}- ${DARK_PURPLE}Aurora Pieces`) + .setHoverValue(`${DARK_PURPLE}Aurora Pieces\n${aurora.map((inner) => inner[2]).join("\n")}`) + .chat(); + new TextComponent(`${DARK_GRAY}- ${DARK_PURPLE}Terror Pieces`) + .setHoverValue(`${DARK_PURPLE}Terror Pieces\n${terror.map((inner) => inner[2]).join("\n")}`) + .chat(); + + // Equip pieces + const totalDom = + dominance.reduce((sum, innerArray) => sum + (innerArray.length > 0 ? innerArray[0] : 0), 0) + + terror.reduce((sum, innerArray) => sum + (innerArray.length > 0 ? innerArray[3] : 0), 0); + const totolLL = + lifeline.reduce((sum, innerArray) => sum + (innerArray.length > 0 ? innerArray[0] : 0), 0) + + terror.reduce((sum, innerArray) => sum + (innerArray.length > 0 ? innerArray[4] : 0), 0); + new TextComponent(`${DARK_GRAY}- ${DARK_PURPLE}Dominance Equips ${GRAY}[Total: ${totalDom}]`) + .setHoverValue(`${DARK_PURPLE}Dominance Equips\n${dominance.map((inner) => inner[1]).join("\n")}`) + .chat(); + new TextComponent(`${DARK_GRAY}- ${DARK_PURPLE}Lifeline Equips ${GRAY}[Total: ${totolLL}]`) + .setHoverValue(`${DARK_PURPLE}Lifeline Equips\n${lifeline.map((inner) => inner[1]).join("\n")}`) + .chat(); + } + + // Check for accessory power + ChatLib.chat(`${DARK_AQUA + BOLD}Misc:`); + ChatLib.chat( + `${DARK_GRAY}- ${AQUA}Magical Power: ${ + WHITE + (data?.accessory_bag_storage?.highest_magical_power ?? RED + "I NEED MORE POWER.") + }` + ); + + // Check for Gdrag + const pets = data?.pets_data?.pets; + let gdrag = [`${RED}Not found!`]; + pets.forEach((pet) => { + if (pet.type !== "GOLDEN_DRAGON") return; + + if (pet.exp >= 210_255_385) { + gdrag[0] = `${GRAY}[Lvl 200] ${GOLD}Golden Dragon`; + gdrag.push(convertToTitleCase(pet.heldItem)); + } else if (gdrag[0].startsWith(RED)) gdrag[0] = `${GRAY}[Lvl ${RED}< 200] ${GOLD}Golden Dragon`; + }); + new TextComponent(`${DARK_GRAY}- ${AQUA}GDrag: ${gdrag[0]}`).setHoverValue(gdrag.join("\n")).chat(); + + // Bank + let money = data?.currencies?.bank ?? 0; + if (money === 0) ChatLib.chat(`${DARK_GRAY}- ${RED}Bank API is OFF!`); + else { + money += data?.currencies?.coin_purse ?? 0; + ChatLib.chat(`${DARK_GRAY}- ${AQUA}Bank: ${WHITE + formatNumber(money)}`); + } + + // Check completions + const tiers = data?.nether_island_player_data?.kuudra_completed_tiers; + if (tiers !== undefined) { + let completions = `${DARK_GRAY}- ${AQUA}Completions: `; + const tiers_key = Object.keys(tiers); + if (tiers_key.length === 0) completions += `${RED}None........`; + + tiers_key.forEach((tier) => { + if (tier.startsWith("highest")) return; + completions += `${WHITE + tiers[tier]} ${GRAY}| `; }); - new TextComponent(`${DARK_GRAY}- ${AQUA}GDrag: ${gdrag[0]}`).setHoverValue(gdrag.join('\n')).chat(); - - // Bank - let money = data?.currencies?.bank ?? 0; - if (money === 0) ChatLib.chat(`${DARK_GRAY}- ${RED}Bank API is OFF!`); - else { - money += data?.currencies?.coin_purse ?? 0; - ChatLib.chat(`${DARK_GRAY}- ${AQUA}Bank: ${WHITE + formatNumber(money)}`); - } + ChatLib.chat(completions.slice(0, -5)); + } else ChatLib.chat(`${DARK_GRAY}- ${AQUA}Completions: ${RED}None...`); - // Check completions - const tiers = data?.nether_island_player_data?.kuudra_completed_tiers; - if (tiers !== undefined) { - let completions = `${DARK_GRAY}- ${AQUA}Completions: `; - const tiers_key = Object.keys(tiers); - if (tiers_key.length === 0) completions += `${RED}None........` - - tiers_key.forEach(tier => { - if (tier.startsWith("highest")) return; - completions += `${WHITE + tiers[tier]} ${GRAY}| `; - }); - ChatLib.chat(completions.slice(0, -5)); - } else ChatLib.chat(`${DARK_GRAY}- ${AQUA}Completions: ${RED}None...`); - - // Reputation - const barb = data?.nether_island_player_data?.barbarians_reputation ?? 0; - const mage = data?.nether_island_player_data?.mages_reputation ?? 0; - ChatLib.chat(`${DARK_GRAY}- ${AQUA}Reputation: ${RED + barb} ${GRAY}| ${DARK_PURPLE + mage}`); - }).catch(err => ChatLib.chat(LOGO + DARK_RED + (err.cause ?? err))); + // Reputation + const barb = data?.nether_island_player_data?.barbarians_reputation ?? 0; + const mage = data?.nether_island_player_data?.mages_reputation ?? 0; + ChatLib.chat(`${DARK_GRAY}- ${AQUA}Reputation: ${RED + barb} ${GRAY}| ${DARK_PURPLE + mage}`); + }) + .catch((err) => ChatLib.chat(LOGO + DARK_RED + (err.cause ?? err))); } /** * /kv command to display useful information for Kuudar. */ register("command", (name) => { - kuudraView(name); -}).setName("kv", true).setAliases("kuudraView"); + kuudraView(name); +}) + .setName("kv", true) + .setAliases("kuudraView"); /** * Auto /kv on party finder join. */ -registerWhen(register("chat", (player) => { +registerWhen( + register("chat", (player) => { if (player === Player.getName()) return; kuudraView(player); -}).setCriteria("Party Finder > ${player} joined the group! (${combat})"), () => Settings.autoKV); + }).setCriteria("Party Finder > ${player} joined the group! (${combat})"), + () => Settings.autoKV +); diff --git a/features/kuudra/TabascoCalc.js b/features/kuudra/TabascoCalc.js index 49d84c0..0a7de09 100644 --- a/features/kuudra/TabascoCalc.js +++ b/features/kuudra/TabascoCalc.js @@ -2,23 +2,22 @@ import { AQUA, BOLD, DARK_AQUA, GRAY, GREEN, LOGO, RED, UNDERLINE, WHITE } from import { commafy } from "../../utils/functions/format"; import { getBazaar } from "../economy/Economy"; - /** * /va calc for tabasco book prices */ export function calcTabasco() { - const bazaar = getBazaar(); - const tabasco = bazaar.ENCHANTMENT_TABASCO_3; - const pepper = bazaar.CHILI_PEPPER; - const orderPepper = pepper[0] * 64; - const instaPepper = pepper[1] * 64; - const p1 = tabasco[0] - orderPepper; - const p2 = tabasco[0] - instaPepper; - const p3 = tabasco[1] - orderPepper; - const p4 = tabasco[1] - instaPepper; - - ChatLib.chat( -`\n${LOGO + GREEN + BOLD}Tabasco Craft Profits:\n + const bazaar = getBazaar(); + const tabasco = bazaar.ENCHANTMENT_TABASCO_3; + const pepper = bazaar.CHILI_PEPPER; + const orderPepper = pepper[0] * 64; + const instaPepper = pepper[1] * 64; + const p1 = tabasco[0] - orderPepper; + const p2 = tabasco[0] - instaPepper; + const p3 = tabasco[1] - orderPepper; + const p4 = tabasco[1] - instaPepper; + + ChatLib.chat( + `\n${LOGO + GREEN + BOLD}Tabasco Craft Profits:\n ${RED + BOLD + UNDERLINE}Tabasco III Cost: ${AQUA}Insta Sell: ${WHITE + commafy(tabasco[0])} ${AQUA}Sell Offer: ${WHITE + commafy(tabasco[1])}\n @@ -29,5 +28,6 @@ ${DARK_AQUA + BOLD + UNDERLINE}Total Profit: ${AQUA}Insta Sell + Buy Order: ${WHITE + commafy(p1)} ${GRAY}(${commafy(p1 / 6)} per teeth) ${AQUA}Insta Sell + Insta Buy: ${WHITE + commafy(p2)} ${GRAY}(${commafy(p2 / 6)} per teeth) ${AQUA}Sell Offer + Buy Order: ${WHITE + commafy(p3)} ${GRAY}(${commafy(p3 / 6)} per teeth) -${AQUA}Sell Offer + Insta Buy: ${WHITE + commafy(p4)} ${GRAY}(${commafy(p4 / 6)} per teeth)\n`); +${AQUA}Sell Offer + Insta Buy: ${WHITE + commafy(p4)} ${GRAY}(${commafy(p4 / 6)} per teeth)\n` + ); } diff --git a/features/mining/CommissionsDisplay.js b/features/mining/CommissionsDisplay.js index 7161ae8..1b3696f 100644 --- a/features/mining/CommissionsDisplay.js +++ b/features/mining/CommissionsDisplay.js @@ -1,126 +1,143 @@ -import location from "../../utils/Location"; -import Settings from "../../utils/Settings"; import { BOLD, GOLD, GREEN, UNDERLINE, YELLOW } from "../../utils/Constants"; -import { getClosest } from "../../utils/functions/find"; -import { isLookingAway } from "../../utils/functions/matrix"; +import { data } from "../../utils/Data"; +import location from "../../utils/Location"; import { Overlay } from "../../utils/Overlay"; import { registerWhen } from "../../utils/RegisterTils"; -import { data } from "../../utils/Data"; +import Settings from "../../utils/Settings"; import { setTitle } from "../../utils/Title"; - +import { getClosest } from "../../utils/functions/find"; +import { isLookingAway } from "../../utils/functions/matrix"; const GEMSTONE_WAYPOINTS = { - "Citrine": [ - ["Citrine", 0xe4d00a, -51.5, 129.5, 410.5], - ["Citrine", 0xe4d00a, -94.5, 146.5, 259.5], - ["Citrine", 0xe4d00a, 38, 121.5, 387], - ["Citrine", 0xe4d00a, -58, 146.5, 422]], - "Aquamarine": [ - ["Aquamarine", 0x7fffd4, -1.5, 141.5, 437.5], - ["Aquamarine", 0x7fffd4, 95.5, 155, 382.5], - ["Aquamarine", 0x7fffd4, 51.5, 119.5, 302.5]], - "Peridot": [ - ["Peridot", 0xb4c424, 91.5, 124.5, 397.5], - ["Peridot", 0xb4c424, -76.5, 122.5, 281.5], - ["Peridot", 0xb4c424, -62, 149.5, 300.5], - ["Peridot", 0xb4c424, -73, 124.5, 458.5]], - "Onyx": [ - ["Onyx", 0x000000, -68, 132.5, 407.5], - ["Onyx", 0x000000, 79.5, 121.5, 411.5], - ["Onyx", 0x000000, -6.5, 134.5, 386.5]], - "Tungsten": [ - ["Tungsten", 0x808080, 37.5, 155, 329.5]], - "Umber": [ - ["Umber", 0x917668, 32.5, 124.5, 359.5]], - "Glacite": [ - ["Glacite", 0xa5f2f3, 4.5, 134.5, 390.5]] + Citrine: [ + ["Citrine", 0xe4d00a, -51.5, 129.5, 410.5], + ["Citrine", 0xe4d00a, -94.5, 146.5, 259.5], + ["Citrine", 0xe4d00a, 38, 121.5, 387], + ["Citrine", 0xe4d00a, -58, 146.5, 422], + ], + Aquamarine: [ + ["Aquamarine", 0x7fffd4, -1.5, 141.5, 437.5], + ["Aquamarine", 0x7fffd4, 95.5, 155, 382.5], + ["Aquamarine", 0x7fffd4, 51.5, 119.5, 302.5], + ], + Peridot: [ + ["Peridot", 0xb4c424, 91.5, 124.5, 397.5], + ["Peridot", 0xb4c424, -76.5, 122.5, 281.5], + ["Peridot", 0xb4c424, -62, 149.5, 300.5], + ["Peridot", 0xb4c424, -73, 124.5, 458.5], + ], + Onyx: [ + ["Onyx", 0x000000, -68, 132.5, 407.5], + ["Onyx", 0x000000, 79.5, 121.5, 411.5], + ["Onyx", 0x000000, -6.5, 134.5, 386.5], + ], + Tungsten: [["Tungsten", 0x808080, 37.5, 155, 329.5]], + Umber: [["Umber", 0x917668, 32.5, 124.5, 359.5]], + Glacite: [["Glacite", 0xa5f2f3, 4.5, 134.5, 390.5]], }; let commissionWaypoints = []; let closestWaypoints = []; -const commissionExample = -`§r§9§lCommissions:§r +const commissionExample = `§r§9§lCommissions:§r §r §r§fUmber Collector: §r§c0%§r §r §r§fCorpse Looter: §r§c0%§r §r §r§fScrap Collector: §r§c0%§r §r §r§fCitrine Gemstone Collector: §r§c0%§r`; -const commissionOverlay = new Overlay("commissionsDisplay", data.CDL, "moveCommissions", commissionExample, ["Crystal Hollows", "Dwarven Mines", "Mineshaft"]); +const commissionOverlay = new Overlay("commissionsDisplay", data.CDL, "moveCommissions", commissionExample, [ + "Crystal Hollows", + "Dwarven Mines", + "Mineshaft", +]); -registerWhen(register("renderWorld", () => { - commissionWaypoints.forEach(gem => { - Tessellator.drawString(gem[0], gem[2], gem[3], gem[4], gem[1], true); +registerWhen( + register("renderWorld", () => { + commissionWaypoints.forEach((gem) => { + Tessellator.drawString(gem[0], gem[2], gem[3], gem[4], gem[1], true); }); -}), () => (location.getWorld() === "Crystal Hollows" || location.getWorld() === "Dwarven Mines") && Settings.commissionWaypoints !== 0); + }), + () => ["Crystal Hollows", "Dwarven Mines"].includes(location.getWorld()) && Settings.commissionWaypoints !== 0 +); -registerWhen(register("step", () => { +registerWhen( + register("step", () => { if (!World.isLoaded()) return; const tab = TabList.getNames(); - let index = tab.findIndex(name => name === "§r§9§lCommissions:§r"); + let index = tab.findIndex((name) => name === "§r§9§lCommissions:§r"); if (index === -1) return; let commissionMessage = tab[index++]; commissionWaypoints = [["Base Camp", 0xffd700, 0.5, 129, 200.5]]; closestWaypoints = []; while (tab[index].startsWith("§r §r§f")) { - // Set waypoints - let parsed = tab[index].removeFormatting().trim().split(' '); - let comm = parsed[0]; - if (comm in GEMSTONE_WAYPOINTS && parsed[1] !== "Walker") { - let commWaypoints = GEMSTONE_WAYPOINTS[comm]; - let closest = getClosest([Player.getX(), Player.getY(), Player.getZ()], commWaypoints)[0]; - commissionWaypoints = commissionWaypoints.concat(commWaypoints.filter(wp => wp != closest)); + // Set waypoints + let parsed = tab[index].removeFormatting().trim().split(" "); + let comm = parsed[0]; + if (comm in GEMSTONE_WAYPOINTS && parsed[1] !== "Walker") { + let commWaypoints = GEMSTONE_WAYPOINTS[comm]; + let closest = getClosest([Player.getX(), Player.getY(), Player.getZ()], commWaypoints)[0]; + commissionWaypoints = commissionWaypoints.concat(commWaypoints.filter((wp) => wp != closest)); - // Change style for closest waypoint - let closestCopy = [...closest]; - closestWaypoints.push(closestCopy); - closestCopy[0] = `${BOLD + UNDERLINE}${closestCopy[0]}`; - if (Settings.commissionWaypoints !== 2) commissionWaypoints.push(closestCopy); + // Change style for closest waypoint + let closestCopy = [...closest]; + closestWaypoints.push(closestCopy); + closestCopy[0] = `${BOLD + UNDERLINE}${closestCopy[0]}`; + if (Settings.commissionWaypoints !== 2) commissionWaypoints.push(closestCopy); - commissionMessage += `\n${tab[index].replace("§f", GOLD)}`; - } else commissionMessage += `\n${tab[index]}`; + commissionMessage += `\n${tab[index].replace("§f", GOLD)}`; + } else commissionMessage += `\n${tab[index]}`; - // Set commission message - index++; + // Set commission message + index++; } commissionOverlay.setMessage(commissionMessage); -}).setFps(4), () => (location.getWorld() === "Crystal Hollows" || location.getWorld() === "Dwarven Mines" || location.getWorld() === "Mineshaft") && - (Settings.commissionsDisplay || Settings.commissionWaypoints !== 0)); + }).setFps(4), + () => + ["Crystal Hollows", "Dwarven Mines", "Mineshaft"].includes(location.getWorld()) && + (Settings.commissionsDisplay || Settings.commissionWaypoints !== 0) +); /* Render closest lines */ const SKIP = new Set(["§l§nGlacite", "§l§nTungsten", "§l§nUmber"]); -registerWhen(register("renderWorld", (pt) => { +registerWhen( + register("renderWorld", (pt) => { const player = Player.asPlayerMP().getEntity(); const x = player.field_70165_t * pt - player.field_70142_S * (pt - 1); const y = player.field_70163_u * pt - player.field_70137_T * (pt - 1) + Player.asPlayerMP().getEyeHeight(); const z = player.field_70161_v * pt - player.field_70136_U * (pt - 1); - - closestWaypoints.forEach(close => { - if (isLookingAway(x, y, z, Player.getYaw(), close[2], close[3], close[4]) || SKIP.has(close[0])) return; - GL11.glBlendFunc(770, 771); - GL11.glEnable(GL11.GL_BLEND); - GL11.glLineWidth(5); - GL11.glDisable(GL11.GL_TEXTURE_2D); - GL11.glDepthMask(true); - GlStateManager.func_179094_E(); // pushMatrix() - - const hex = close[1]; - Tessellator.begin(3).colorize(((hex >> 16) & 0xff) / 255, ((hex >> 8) & 0xff) / 255, (hex & 0xff) / 255, 1); - Tessellator.pos(x, y, z); - Tessellator.pos(close[2], close[3], close[4]); - Tessellator.draw(); - - GlStateManager.func_179121_F(); // popMatrix() - GL11.glEnable(GL11.GL_TEXTURE_2D); - GL11.glDisable(GL11.GL_BLEND); - }) -}), () => location.getWorld() === "Dwarven Mines" && (Settings.commissionWaypoints === 2 || Settings.commissionWaypoints === 3)) + closestWaypoints.forEach((close) => { + if (isLookingAway(x, y, z, Player.getYaw(), close[2], close[3], close[4]) || SKIP.has(close[0])) return; + GL11.glBlendFunc(770, 771); + GL11.glEnable(GL11.GL_BLEND); + GL11.glLineWidth(5); + GL11.glDisable(GL11.GL_TEXTURE_2D); + GL11.glDepthMask(true); + GlStateManager.func_179094_E(); // pushMatrix() + + const hex = close[1]; + Tessellator.begin(3).colorize(((hex >> 16) & 0xff) / 255, ((hex >> 8) & 0xff) / 255, (hex & 0xff) / 255, 1); + Tessellator.pos(x, y, z); + Tessellator.pos(close[2], close[3], close[4]); + Tessellator.draw(); + + GlStateManager.func_179121_F(); // popMatrix() + GL11.glEnable(GL11.GL_TEXTURE_2D); + GL11.glDisable(GL11.GL_BLEND); + }); + }), + () => + location.getWorld() === "Dwarven Mines" && + (Settings.commissionWaypoints === 2 || Settings.commissionWaypoints === 3) +); /** * Commission Complete Annoucne */ -registerWhen(register("chat", (commission) => { +registerWhen( + register("chat", (commission) => { setTitle(GREEN + BOLD + commission, `${YELLOW}Commission Complete!`, 5, 25, 5, 51); -}).setCriteria("${commission} Commission Complete! Visit the King to claim your rewards!"), () => Settings.commissionAnnounce); + }).setCriteria("${commission} Commission Complete! Visit the King to claim your rewards!"), + () => Settings.commissionAnnounce +); diff --git a/features/mining/EventTracker.js b/features/mining/EventTracker.js index bc2918d..d02cd6c 100644 --- a/features/mining/EventTracker.js +++ b/features/mining/EventTracker.js @@ -1,137 +1,139 @@ -import location from "../../utils/Location"; -import Socket from "../../utils/Socket"; import { AQUA, DARK_GRAY, GRAY, LOGO, RED, WHITE, WITHER_CLASS, YELLOW } from "../../utils/Constants"; -import { formatTime } from "../../utils/functions/format"; +import location from "../../utils/Location"; import { registerWhen } from "../../utils/RegisterTils"; +import Socket from "../../utils/Socket"; import { delay } from "../../utils/ThreadTils"; - +import { formatTime } from "../../utils/functions/format"; /** * Track off of wither names. */ const findEvent = register("step", () => { - const loc = location.getWorld(); - const world = loc === "Dwarven Mines" ? "dm" : - loc === "Crystal Hollows" ? "ch" : undefined; - if (world === undefined) { - findEvent.unregister(); - return; + const loc = location.getWorld(); + const world = loc === "Dwarven Mines" ? "dm" : loc === "Crystal Hollows" ? "ch" : undefined; + if (world === undefined) { + findEvent.unregister(); + return; + } + + World.getAllEntitiesOfType(WITHER_CLASS).forEach((wither) => { + const name = wither.getName().toLowerCase().removeFormatting(); + let match = name.match(/(passive event|event) (.+) (running for|active in (.+)) (\d+):(\d+)/); + + if (match) { + const n = match.length; + const event = match[2]; + const time = parseInt(match[n - 2]) * 60 + parseInt(match[n - 1]) + (name.startsWith("passive") ? 300 : 600); + + // Capitalize the first and last word. + const words = event.split(" "); + words[0] = words[0][0].toUpperCase() + words[0].slice(1); + words[words.length - 1] = words[words.length - 1][0].toUpperCase() + words[words.length - 1].slice(1); + + // Send the event data to the server. + Socket.send({ + command: world, + request: "post", + event: words.join(" "), + time: time, + }); + findEvent.unregister(); } - - World.getAllEntitiesOfType(WITHER_CLASS).forEach(wither => { - const name = wither.getName().toLowerCase().removeFormatting(); - let match = name.match(/(passive event|event) (.+) (running for|active in (.+)) (\d+):(\d+)/); - - if (match) { - const n = match.length; - const event = match[2]; - const time = parseInt(match[n - 2]) * 60 + parseInt(match[n - 1]) + (name.startsWith("passive") ? 300 : 600); - - // Capitalize the first and last word. - const words = event.split(' '); - words[0] = words[0][0].toUpperCase() + words[0].slice(1); - words[words.length - 1] = words[words.length - 1][0].toUpperCase() + words[words.length - 1].slice(1); - - // Send the event data to the server. - Socket.send({ - "command": world, - "request": "post", - "event": words.join(' '), - "time": time - }); - findEvent.unregister(); - } - }); -}).setFps(1).unregister(); + }); +}) + .setFps(1) + .unregister(); /** * Track the event in the Dwarven Mines and Crystal Hollows. - * + * * @param {Number} attempt - The number of attempts to track the event. */ -function trackEvent(attempt=0) { - if (attempt > 5) return; +function trackEvent(attempt = 0) { + if (attempt > 5) return; - const world = location.getWorld(); - if (world === undefined) delay(() => trackEvent(attempt + 1), 1000); - else if (world === "Dwarven Mines" || world === "Crystal Hollows") findEvent.register(); - else findEvent.unregister(); + const world = location.getWorld(); + if (world === undefined) delay(() => trackEvent(attempt + 1), 1000); + else if (world === "Dwarven Mines" || world === "Crystal Hollows") findEvent.register(); + else findEvent.unregister(); } -register("worldLoad", trackEvent) +register("worldLoad", trackEvent); /** * Track the event start message. */ register("chat", (event) => { - const loc = location.getWorld(); - const world = loc === "Dwarven Mines" ? "dm" : - loc === "Crystal Hollows" ? "ch" : undefined; - if (world === undefined) return; - - Socket.send({ - "command": world, - "request": "post", - "event": event - }); + const loc = location.getWorld(); + const world = loc === "Dwarven Mines" ? "dm" : loc === "Crystal Hollows" ? "ch" : undefined; + if (world === undefined) return; + + Socket.send({ + command: world, + request: "post", + event: event, + }); }).setCriteria(" ⚑ The ${event} event starts in 20 seconds${_}"); /** * Process the event data received from the server. - * + * * @param {Object} data - The data received from the server. */ export function processEvent(data) { - const command = data.command; - const events = data.events; - const loc = command === "ch" ? "Crystal Hollows" : - command === "dm" ? "Dwarven Mines" : RED + "???"; - - ChatLib.chat(`${LOGO + YELLOW + loc} Events:`) - Object.keys(events).forEach(event => { - const time = events[event].time; - const percentage = parseFloat(events[event].percentage).toFixed(2); - - ChatLib.chat(`${DARK_GRAY}- ${AQUA + event + WHITE} ${formatTime(time) + GRAY} (${percentage}%)`); - }) + const command = data.command; + const events = data.events; + const loc = command === "ch" ? "Crystal Hollows" : command === "dm" ? "Dwarven Mines" : RED + "???"; + + ChatLib.chat(`${LOGO + YELLOW + loc} Events:`); + Object.keys(events).forEach((event) => { + const time = events[event].time; + const percentage = parseFloat(events[event].percentage).toFixed(2); + + ChatLib.chat(`${DARK_GRAY}- ${AQUA + event + WHITE} ${formatTime(time) + GRAY} (${percentage}%)`); + }); } register("command", () => { - Socket.send({ - "command": "ch", - "request": "get", - "event": "event" - }); + Socket.send({ + command: "ch", + request: "get", + event: "event", + }); }).setName("chevent"); register("command", () => { - Socket.send({ - "command": "dm", - "request": "get", - "event": "event" - }); + Socket.send({ + command: "dm", + request: "get", + event: "event", + }); }).setName("dmevent"); - /** * Alloy tracking. */ -registerWhen(register("chat", (username) => { +registerWhen( + register("chat", (username) => { Socket.send({ - "command": "alloy", - "request": "post", - "username": username + command: "alloy", + request: "post", + username: username, }); -}).setCriteria("ALLOY! ${username} just found a Divan's Alloy!"), () => location.getWorld() === "Crystal Hollows"); + }).setCriteria("ALLOY! ${username} just found a Divan's Alloy!"), + () => location.getWorld() === "Crystal Hollows" +); register("command", () => { - Socket.send({ - "command": "alloy", - "request": "get" - }); + Socket.send({ + command: "alloy", + request: "get", + }); }).setName("alloy"); export function processAlloy(data) { - const last_alloy = data.last_alloy; - const date = new Date(Date.now() - last_alloy * 1_000); - ChatLib.chat(`${LOGO + YELLOW}Alloy: ${WHITE + formatTime(last_alloy)} ago ${DARK_GRAY}(${date.toLocaleDateString()})`); + const last_alloy = data.last_alloy; + const date = new Date(Date.now() - last_alloy * 1_000); + ChatLib.chat( + `${LOGO + YELLOW}Alloy: ${WHITE + formatTime(last_alloy)} ago ${DARK_GRAY}(${date.toLocaleDateString()})` + ); } diff --git a/features/mining/FossilHelper.js b/features/mining/FossilHelper.js index 1c45938..c81e9c9 100644 --- a/features/mining/FossilHelper.js +++ b/features/mining/FossilHelper.js @@ -1,80 +1,79 @@ -import Settings from "../../utils/Settings"; import { AQUA, BOLD, DARK_AQUA, DARK_GRAY, GOLD, GRAY, RED, YELLOW } from "../../utils/Constants"; +import { data } from "../../utils/Data"; +import { Overlay } from "../../utils/Overlay"; +import { registerWhen } from "../../utils/RegisterTils"; +import Settings from "../../utils/Settings"; import { getSlotCoords } from "../../utils/functions/find"; import { formatNumber } from "../../utils/functions/format"; import { createMatrix, getAllFormations } from "../../utils/functions/matrix"; -import { Overlay } from "../../utils/Overlay"; -import { registerWhen } from "../../utils/RegisterTils"; -import { data } from "../../utils/Data"; import { getAuction } from "../economy/Economy"; - const FOSSILS = { - "Spine": [ - [1, -1, -1], - [1, 1, -1], - [1, 1, 1], - [1, 1, 1], - [1, 1, -1], - [1, -1, -1] - ], - "Helix": [ - [1, 1, 1, 1], - [-1, -1, -1, 1], - [1, 1, -1, 1], - [1, -1, -1, 1], - [1, 1, 1, 1] - ], - "Footprint": [ - [1, -1, 1, -1, 1], - [1, -1, 1, -1, 1], - [-1, 1, 1, 1, -1], - [-1, 1, 1, 1, -1], - [-1, -1, 1, -1, -1] - ], - "Webbed": [ - [-1, -1, -1, 1, -1, -1, -1], - [1, -1, -1, 1, -1, -1, 1], - [-1, 1, -1, 1, -1, 1, -1], - [-1, -1, 1, 1, 1, -1, -1] - ], - "Claw": [ - [-1, 1, -1, 1, -1, -1], - [1, -1, 1, -1, 1, -1], - [-1, 1, -1, 1, 1, -1], - [-1, -1, 1, 1, 1, 1], - [-1, -1, -1, -1, 1, -1] - ], - "Tusk": [ - [-1, -1, 1, -1, -1], - [-1, 1, -1, 1, -1], - [1, -1, -1, -1, -1], - [-1, 1, -1, -1, -1], - [-1, -1, 1, 1, 1] - ], - "Clubbed": [ - [-1, 1, 1, 1, 1, -1, -1, -1], - [1, -1, -1, -1, -1, 1, -1, -1], - [-1, 1, -1, -1, -1, -1, 1, 1], - [-1, -1, -1, -1, -1, -1, 1, 1] - ], - "Ugly": [ - [-1, 1, -1, -1], - [1, 1, 1, -1], - [1, 1, 1, 1], - [1, 1, 1, 1], - [1, 1, 1, -1], - [-1, 1, -1, -1] - ] -} + Spine: [ + [1, -1, -1], + [1, 1, -1], + [1, 1, 1], + [1, 1, 1], + [1, 1, -1], + [1, -1, -1], + ], + Helix: [ + [1, 1, 1, 1], + [-1, -1, -1, 1], + [1, 1, -1, 1], + [1, -1, -1, 1], + [1, 1, 1, 1], + ], + Footprint: [ + [1, -1, 1, -1, 1], + [1, -1, 1, -1, 1], + [-1, 1, 1, 1, -1], + [-1, 1, 1, 1, -1], + [-1, -1, 1, -1, -1], + ], + Webbed: [ + [-1, -1, -1, 1, -1, -1, -1], + [1, -1, -1, 1, -1, -1, 1], + [-1, 1, -1, 1, -1, 1, -1], + [-1, -1, 1, 1, 1, -1, -1], + ], + Claw: [ + [-1, 1, -1, 1, -1, -1], + [1, -1, 1, -1, 1, -1], + [-1, 1, -1, 1, 1, -1], + [-1, -1, 1, 1, 1, 1], + [-1, -1, -1, -1, 1, -1], + ], + Tusk: [ + [-1, -1, 1, -1, -1], + [-1, 1, -1, 1, -1], + [1, -1, -1, -1, -1], + [-1, 1, -1, -1, -1], + [-1, -1, 1, 1, 1], + ], + Clubbed: [ + [-1, 1, 1, 1, 1, -1, -1, -1], + [1, -1, -1, -1, -1, 1, -1, -1], + [-1, 1, -1, -1, -1, -1, 1, 1], + [-1, -1, -1, -1, -1, -1, 1, 1], + ], + Ugly: [ + [-1, 1, -1, -1], + [1, 1, 1, -1], + [1, 1, 1, 1], + [1, 1, 1, 1], + [1, 1, 1, -1], + [-1, 1, -1, -1], + ], +}; const FOSSIL_PERCENTS = { - "8.3%": ["Spine"], - "7.1%": ["Helix"], - "7.7%": ["Footprint", "Claw"], - "10%": ["Webbed"], - "12.5%": ["Tusk"], - "9.1%": ["Clubbed"], - "6.2%": ["Ugly"] + "8.3%": ["Spine"], + "7.1%": ["Helix"], + "7.7%": ["Footprint", "Claw"], + "10%": ["Webbed"], + "12.5%": ["Tusk"], + "9.1%": ["Clubbed"], + "6.2%": ["Ugly"], }; let patterns = FOSSILS; let board = []; @@ -84,200 +83,217 @@ const fossilExample = `${DARK_AQUA + BOLD}Fossil Probability: ${DARK_GRAY} - ${GOLD}Praise ${DARK_GRAY} - ${GOLD}Lord ${DARK_GRAY} - ${GOLD}Helix`; -const fossilOverlay = new Overlay("fossilHelper", data.FHL, "moveFossil", fossilExample, ["Dwarven Mines"], "guiRender"); +const fossilOverlay = new Overlay( + "fossilHelper", + data.FHL, + "moveFossil", + fossilExample, + ["Dwarven Mines"], + "guiRender" +); /** * Finds the excavator slot with the highest probability of being a fossil piece. * My algorithm might be wis :skull: - * + * * @returns [i, j] - Coord of most likely fossil slot or [-1, -1] if there is none. */ function findTile() { - const formations = {}; - Object.keys(patterns).forEach(pattern => { - formations[pattern] = getAllFormations(patterns[pattern]); - }); - const possibilityBoard = createMatrix(6, 9); - const fossilProbability = { - "Spine": 0, - "Helix": 0, - "Footprint": 0, - "Claw": 0, - "Webbed": 0, - "Tusk": 0, - "Clubbed": 0, - "Ugly": 0 - }; - let totalPossible = 0; + const formations = {}; + Object.keys(patterns).forEach((pattern) => { + formations[pattern] = getAllFormations(patterns[pattern]); + }); + const possibilityBoard = createMatrix(6, 9); + const fossilProbability = { + Spine: 0, + Helix: 0, + Footprint: 0, + Claw: 0, + Webbed: 0, + Tusk: 0, + Clubbed: 0, + Ugly: 0, + }; + let totalPossible = 0; - /** - * Adds a formation to the possibility board at the specified position. - * @param {number} i - The row index where the formation should be added. - * @param {number} j - The column index where the formation should be added. - * @param {Array} formation - The formation to be added to the possibility board. - */ - function addFormation(i, j, fossil, formation) { - fossilProbability[fossil]++; - totalPossible++; + /** + * Adds a formation to the possibility board at the specified position. + * @param {number} i - The row index where the formation should be added. + * @param {number} j - The column index where the formation should be added. + * @param {Array} formation - The formation to be added to the possibility board. + */ + function addFormation(i, j, fossil, formation) { + fossilProbability[fossil]++; + totalPossible++; - for (let m = 0; m < formation.length; m++) { - for (let n = 0; n < formation[m].length; n++) { - if (formation[m][n] === 1) { - possibilityBoard[i + m][j + n] += 1; - } - } + for (let m = 0; m < formation.length; m++) { + for (let n = 0; n < formation[m].length; n++) { + if (formation[m][n] === 1) { + possibilityBoard[i + m][j + n] += 1; } + } } + } - /** - * Checks if a formation can be placed on the board at the specified position. - * If possible, adds the formation to the possibility board. - * @param {number} i - The row index where the formation is to be checked. - * @param {number} j - The column index where the formation is to be checked. - * @param {Array} formation - The formation to be checked and potentially added to the possibility board. - */ - function checkFormation(i, j, fossil, formation) { - for (let m = 0; m < formation.length; m++) { - for (let n = 0; n < formation[0].length; n++) { - if (board[i + m][j + n] === -1 && formation[m][n] === 1) return; - if (board[i + m][j + n] === 1 && formation[m][n] === -1) return; - } - } - addFormation(i, j, fossil, formation); + /** + * Checks if a formation can be placed on the board at the specified position. + * If possible, adds the formation to the possibility board. + * @param {number} i - The row index where the formation is to be checked. + * @param {number} j - The column index where the formation is to be checked. + * @param {Array} formation - The formation to be checked and potentially added to the possibility board. + */ + function checkFormation(i, j, fossil, formation) { + for (let m = 0; m < formation.length; m++) { + for (let n = 0; n < formation[0].length; n++) { + if (board[i + m][j + n] === -1 && formation[m][n] === 1) return; + if (board[i + m][j + n] === 1 && formation[m][n] === -1) return; + } } + addFormation(i, j, fossil, formation); + } - // Find 1s box that must be checked - let topLeft = undefined; - let bottomRight = undefined; - for (let i = 0; i < board.length; i++) { - for (let j = 0; j < board[i].length; j++) { - if (board[i][j] === 1) { - if (topLeft === undefined) { - topLeft = [i, j]; - bottomRight = [i, j]; - } else { - // Update topLeft - topLeft[0] = Math.min(topLeft[0], i); - topLeft[1] = Math.min(topLeft[1], j); + // Find 1s box that must be checked + let topLeft = undefined; + let bottomRight = undefined; + for (let i = 0; i < board.length; i++) { + for (let j = 0; j < board[i].length; j++) { + if (board[i][j] === 1) { + if (topLeft === undefined) { + topLeft = [i, j]; + bottomRight = [i, j]; + } else { + // Update topLeft + topLeft[0] = Math.min(topLeft[0], i); + topLeft[1] = Math.min(topLeft[1], j); - // Update bottomRight - bottomRight[0] = Math.max(bottomRight[0], i); - bottomRight[1] = Math.max(bottomRight[1], j); - } - } + // Update bottomRight + bottomRight[0] = Math.max(bottomRight[0], i); + bottomRight[1] = Math.max(bottomRight[1], j); } + } } - - // 8 * 8 * 4 * 5 * 5 * 4 = O(25600) = constant agreege - Object.keys(formations).forEach(fossil => { - const pattern = formations[fossil]; - pattern.forEach(formation => { - for (let i = 0; i <= board.length - formation.length; i++) { - for (let j = 0; j <= board[0].length - formation[0].length; j++) { - if (topLeft === undefined || (topLeft[0] >= i && topLeft[1] >= j && bottomRight[0] < i + formation.length && bottomRight[1] < j + formation[0].length)) - checkFormation(i, j, fossil, formation); - } - } - }); - }); + } - // Determine best tile - let best = [-1, -1]; - let maxPos = 0; - for (let i = 0; i < 6; i++) { - for (let j = 0; j < 9; j++) { - if (board[i][j] !== 1 && possibilityBoard[i][j] > maxPos) { - best = [i, j]; - maxPos = possibilityBoard[i][j]; - } + // 8 * 8 * 4 * 5 * 5 * 4 = O(25600) = constant agreege + Object.keys(formations).forEach((fossil) => { + const pattern = formations[fossil]; + pattern.forEach((formation) => { + for (let i = 0; i <= board.length - formation.length; i++) { + for (let j = 0; j <= board[0].length - formation[0].length; j++) { + if ( + topLeft === undefined || + (topLeft[0] >= i && + topLeft[1] >= j && + bottomRight[0] < i + formation.length && + bottomRight[1] < j + formation[0].length) + ) + checkFormation(i, j, fossil, formation); } - } + } + }); + }); - // Update fossil display - const auction = getAuction(); - let fossilMessage = `${DARK_AQUA + BOLD}Fossil Probability:`; - if (maxPos === 0) fossilMessage += `\n ${RED}No possible fossils!`; - else { - let profit = 0; - Object.keys(fossilProbability).forEach(fossil => { - const possibility = fossilProbability[fossil] / totalPossible; - profit += possibility * (auction[fossil.toUpperCase() + "_FOSSIL"]?.lbin ?? 0); - if (possibility !== 0) fossilMessage += `\n ${DARK_GRAY}- ${GOLD + fossil} ${GRAY}(${(possibility * 100).toFixed(2)}%)`; - }); - fossilMessage += ` \n\n${AQUA + BOLD}Profit: ${YELLOW + formatNumber(profit)}` + // Determine best tile + let best = [-1, -1]; + let maxPos = 0; + for (let i = 0; i < 6; i++) { + for (let j = 0; j < 9; j++) { + if (board[i][j] !== 1 && possibilityBoard[i][j] > maxPos) { + best = [i, j]; + maxPos = possibilityBoard[i][j]; + } } - fossilOverlay.setMessage(fossilMessage); - - return best; + } + + // Update fossil display + const auction = getAuction(); + let fossilMessage = `${DARK_AQUA + BOLD}Fossil Probability:`; + if (maxPos === 0) fossilMessage += `\n ${RED}No possible fossils!`; + else { + let profit = 0; + Object.keys(fossilProbability).forEach((fossil) => { + const possibility = fossilProbability[fossil] / totalPossible; + profit += possibility * (auction[fossil.toUpperCase() + "_FOSSIL"]?.lbin ?? 0); + if (possibility !== 0) + fossilMessage += `\n ${DARK_GRAY}- ${GOLD + fossil} ${GRAY}(${(possibility * 100).toFixed(2)}%)`; + }); + fossilMessage += ` \n\n${AQUA + BOLD}Profit: ${YELLOW + formatNumber(profit)}`; + } + fossilOverlay.setMessage(fossilMessage); + + return best; } const highlightTile = register("guiRender", () => { - if (bestTile[0] === -1) return; - const [x, y] = getSlotCoords(bestTile[0] * 9 + bestTile[1]); + if (bestTile[0] === -1) return; + const [x, y] = getSlotCoords(bestTile[0] * 9 + bestTile[1]); - Renderer.translate(0, 0, 100); - Renderer.drawRect(Renderer.DARK_GREEN, x, y, 16, 16); + Renderer.translate(0, 0, 100); + Renderer.drawRect(Renderer.DARK_GREEN, x, y, 16, 16); }).unregister(); const trackClicks = register("guiMouseClick", () => { - Client.scheduleTask(2, () => { - const container = Player.getContainer().getItems(); - const fossil = container.find(item => item?.getName() === "§6Fossil"); - - // Determine possible fossil types - if (fossil !== undefined && Object.keys(patterns).length > 2) { - const progress = fossil.getLore()[6].split(' '); - const percent = progress[progress.length - 1].removeFormatting(); - if (percent in FOSSIL_PERCENTS) { - patterns = {}; - FOSSIL_PERCENTS[percent].forEach(fossilType => { - patterns[fossilType] = FOSSILS[fossilType]; - }); - } - } + Client.scheduleTask(2, () => { + const container = Player.getContainer().getItems(); + const fossil = container.find((item) => item?.getName() === "§6Fossil"); - // Set board - for (let i = 0; i < 6; i++) { - for (let j = 0; j < 9; j++) { - if (board[i][j] === 1) continue; + // Determine possible fossil types + if (fossil !== undefined && Object.keys(patterns).length > 2) { + const progress = fossil.getLore()[6].split(" "); + const percent = progress[progress.length - 1].removeFormatting(); + if (percent in FOSSIL_PERCENTS) { + patterns = {}; + FOSSIL_PERCENTS[percent].forEach((fossilType) => { + patterns[fossilType] = FOSSILS[fossilType]; + }); + } + } - let index = i * 9 + j; - let slot = container[index]?.getName(); - if (slot === undefined) board[i][j] = -1 - else if (slot === "§6Dirt") board[i][j] = 0; - else if (slot === "§6Fossil") board[i][j] = 1; - else board[i][j] = -1; - } - } + // Set board + for (let i = 0; i < 6; i++) { + for (let j = 0; j < 9; j++) { + if (board[i][j] === 1) continue; - bestTile = findTile(); - }); + let index = i * 9 + j; + let slot = container[index]?.getName(); + if (slot === undefined) board[i][j] = -1; + else if (slot === "§6Dirt") board[i][j] = 0; + else if (slot === "§6Fossil") board[i][j] = 1; + else board[i][j] = -1; + } + } + + bestTile = findTile(); + }); }).unregister(); const untrackFossils = register("guiClosed", () => { - trackClicks.unregister(); - untrackFossils.unregister(); - highlightTile.unregister(); - fossilOverlay.setMessage(""); - patterns = FOSSILS; -bestTile = [2, 4]; + trackClicks.unregister(); + untrackFossils.unregister(); + highlightTile.unregister(); + fossilOverlay.setMessage(""); + patterns = FOSSILS; + bestTile = [2, 4]; }).unregister(); -registerWhen(register("guiOpened", () => { +registerWhen( + register("guiOpened", () => { Client.scheduleTask(2, () => { - const container = Player.getContainer(); - if (container.getName() !== "Fossil Excavator" || container.getItems()[49].getName() === "§cClose") return; + const container = Player.getContainer(); + if (container.getName() !== "Fossil Excavator" || container.getItems()[49].getName() === "§cClose") return; - board = []; - for (let i = 0; i < 6; i++) { - board.push([]); - for (let j = 0; j < 9; j++) { - board[i].push(0); - } + board = []; + for (let i = 0; i < 6; i++) { + board.push([]); + for (let j = 0; j < 9; j++) { + board[i].push(0); } + } - highlightTile.register(); - trackClicks.register(); - untrackFossils.register(); - }) -}), () => Settings.fossilHelper); + highlightTile.register(); + trackClicks.register(); + untrackFossils.register(); + }); + }), + () => Settings.fossilHelper +); diff --git a/features/mining/PickDisplay.js b/features/mining/PickDisplay.js index 4481280..25691a1 100644 --- a/features/mining/PickDisplay.js +++ b/features/mining/PickDisplay.js @@ -1,13 +1,12 @@ import { DARK_GRAY, GOLD, GRAY, GREEN, RED, WHITE } from "../../utils/Constants"; import { data } from "../../utils/Data"; +import Location from "../../utils/Location"; import { Overlay } from "../../utils/Overlay"; import { registerWhen } from "../../utils/RegisterTils"; import Settings from "../../utils/Settings"; import { setTitle } from "../../utils/Title"; - -const drillExample = -`§bRefined Divan's Drill +const drillExample = `§bRefined Divan's Drill §7Fuel: §275,750§8/100k §7Abilities: §6Mining Speed Boost§f §8[§a104§8] @@ -15,23 +14,23 @@ const drillExample = const drillOverlay = new Overlay("pickDisplay", data.PDL, "movePick", drillExample); const cooldowns = {}; const ABILITY_COOLDOWNS = { - "Mining Speed Boost": 120, - "Pickobulus": 110, - "Vein Seeker": 60, - "Maniac Miner": 59, - "Gemstone Infusion": 140, - "Hazardous Miner": 140 -} + "Mining Speed Boost": 120, + Pickobulus: 110, + "Vein Seeker": 60, + "Maniac Miner": 59, + "Gemstone Infusion": 140, + "Hazardous Miner": 140, +}; -registerWhen(register("step", () => { +registerWhen( + register("step", () => { // Update overlay drillOverlay.setMessage(""); const abilities = Object.keys(cooldowns); - abilities.forEach(key => { - if (cooldowns[key] <= 0) return; + abilities.forEach((key) => { + if (cooldowns[key] <= 0) return; - if (--cooldowns[key] === 0) - setTitle(`${GOLD + key}`, `${GREEN}is ready to use!`, 10, 50, 10, 52); + if (--cooldowns[key] === 0) setTitle(`${GOLD + key}`, `${GREEN}is ready to use!`, 10, 50, 10, 52); }); // Check held data @@ -39,35 +38,42 @@ registerWhen(register("step", () => { if (held === null) return; const lore = held.getLore(); - if (lore.find(line => line.startsWith("§5§o§8Breaking Power")) === undefined) return; + if (lore.find((line) => line.startsWith("§5§o§8Breaking Power")) === undefined) return; // Begin drill message with fuel let drillMessage = held.getName(); - const fuel = lore.find(line => line.startsWith("§5§o§7Fuel:")); - if (fuel !== undefined) drillMessage += `\n ${GRAY}Fuel: ${fuel.split(' ')[1]}`; + const fuel = lore.find((line) => line.startsWith("§5§o§7Fuel:")); + if (fuel !== undefined) drillMessage += `\n ${GRAY}Fuel: ${fuel.split(" ")[1]}`; - // + // drillMessage += `\n ${GRAY}Abilities:`; if (abilities.length === 0) { - const ability = lore.find(line => line.startsWith("§5§o§6Ability:")); - if (ability !== undefined) - drillMessage += `\n ${GOLD + ability.substring(ability.indexOf(' '), ability.indexOf(" §e§lRIGHT CLICK"))} ${DARK_GRAY}[${RED}?${DARK_GRAY}]`; + const ability = lore.find((line) => line.startsWith("§5§o§6Ability:")); + if (ability !== undefined) + drillMessage += `\n ${ + GOLD + ability.substring(ability.indexOf(" "), ability.indexOf(" §e§lRIGHT CLICK")) + } ${DARK_GRAY}[${RED}?${DARK_GRAY}]`; } else { - abilities.forEach(key => { - const cd = cooldowns[key]; - drillMessage += `\n ${GOLD + key + WHITE} ${DARK_GRAY}[${(cd > 0 ? RED : GREEN) + cd + DARK_GRAY}]`; - }); + abilities.forEach((key) => { + const cd = cooldowns[key]; + drillMessage += `\n ${GOLD + key + WHITE} ${DARK_GRAY}[${(cd > 0 ? RED : GREEN) + cd + DARK_GRAY}]`; + }); } drillOverlay.setMessage(drillMessage); -}).setFps(1), () => Settings.pickDisplay); + }).setFps(1), + () => Settings.pickDisplay && ["Dwarven Mines", "Crystal Hollows", "Mineshaft"].includes(Location.getWorld()) +); -registerWhen(register("chat", (ability) => { +registerWhen( + register("chat", (ability) => { cooldowns[ability] = ABILITY_COOLDOWNS[ability]; -}).setCriteria("You used your ${ability} Pickaxe Ability!"), () => Settings.pickDisplay); + }).setCriteria("You used your ${ability} Pickaxe Ability!"), + () => Settings.pickDisplay +); register("worldLoad", () => { - Object.keys(cooldowns).forEach(ability => { - cooldowns[ability] = Math.round(ABILITY_COOLDOWNS[ability] / 2); - }) -}) + Object.keys(cooldowns).forEach((ability) => { + cooldowns[ability] = Math.round(ABILITY_COOLDOWNS[ability] / 2); + }); +}); diff --git a/features/mining/PowderChest.js b/features/mining/PowderChest.js index d5d4491..be3199d 100644 --- a/features/mining/PowderChest.js +++ b/features/mining/PowderChest.js @@ -1,35 +1,44 @@ -import location from "../../utils/Location"; -import Settings from "../../utils/Settings"; import { BOLD, CHEST_CLASS, DARK_AQUA, WHITE } from "../../utils/Constants"; +import { data } from "../../utils/Data"; +import location from "../../utils/Location"; import { Overlay } from "../../utils/Overlay"; import { registerWhen } from "../../utils/RegisterTils"; -import { data } from "../../utils/Data"; +import Settings from "../../utils/Settings"; import Waypoint from "../../utils/Waypoint"; - /** * Variables used to track and detect nearby chests. */ -const chests = new Waypoint([1, 0, 1], 1); // Purple Powder Chests +const chests = new Waypoint([1, 0, 1], 1); // Purple Powder Chests const powderExample = `${DARK_AQUA + BOLD}Nearby Chests: ${WHITE}dentge.`; const powderOverlay = new Overlay("powderChest", data.HL, "moveChest", powderExample, ["Crystal Hollows"]); - + /** * Detects nearby chests to create waypoints and update overlay. */ -registerWhen(register("tick", () => { +registerWhen( + register("tick", () => { chests.clear(); World.getAllTileEntitiesOfType(CHEST_CLASS) - .filter(chest => chest.tileEntity.field_145987_o === 0 && Player.asPlayerMP().distanceTo(chest.getBlockPos()) <= Settings.powderChest) - .forEach(chest => chests.push([chest.getX() + 1, chest.getY(), chest.getZ() + 1])); + .filter( + (chest) => + chest.tileEntity.field_145987_o === 0 && + Player.asPlayerMP().distanceTo(chest.getBlockPos()) <= Settings.powderChest + ) + .forEach((chest) => chests.push([chest.getX() + 1, chest.getY(), chest.getZ() + 1])); powderOverlay.setMessage(`${DARK_AQUA + BOLD}Nearby Chests: ${WHITE + chests.getWaypoints().length}`); -}), () => location.getWorld() === "Crystal Hollows" && Settings.powderChest !== 0); + }), + () => location.getWorld() === "Crystal Hollows" && Settings.powderChest !== 0 +); /** * Remove powder chest spam. */ -registerWhen(register("chat", (gain, event) => { +registerWhen( + register("chat", (gain, event) => { if (Settings.powderHider === 3) cancel(event); else if (Settings.powderHider === 1 && !gain.includes("Powder")) cancel(event); else if (Settings.powderHider === 2 && gain.includes("Powder")) cancel(event); -}).setCriteria("You received ${gain}"), () => Settings.powderHider !== 0) + }).setCriteria("You received ${gain}"), + () => Settings.powderHider !== 0 +); diff --git a/features/mining/PowderTracker.js b/features/mining/PowderTracker.js index c175d63..9a22c70 100644 --- a/features/mining/PowderTracker.js +++ b/features/mining/PowderTracker.js @@ -1,35 +1,35 @@ -import location from "../../utils/Location"; -import Settings from "../../utils/Settings"; import { AQUA, BLUE, BOLD, DARK_GREEN, GRAY, GREEN, LIGHT_PURPLE, LOGO, RED, WHITE } from "../../utils/Constants"; -import { commafy, formatNumber, formatTime } from "../../utils/functions/format"; +import { data } from "../../utils/Data"; +import location from "../../utils/Location"; import { Overlay } from "../../utils/Overlay"; import { registerWhen } from "../../utils/RegisterTils"; +import Settings from "../../utils/Settings"; import { Stat, getPaused } from "../../utils/Stat"; -import { data } from "../../utils/Data"; - +import { commafy, formatNumber, formatTime } from "../../utils/functions/format"; /** * Variables used to track and display current event and powder. */ const powders = { - "Mithril": new Stat(), - "Gemstone": new Stat(), - "Glacite": new Stat() -} -const powderExample = -`${DARK_GREEN + BOLD}Mithril: ${WHITE}I ${GRAY}(wake ᠅/hr) + Mithril: new Stat(), + Gemstone: new Stat(), + Glacite: new Stat(), +}; +const powderExample = `${DARK_GREEN + BOLD}Mithril: ${WHITE}I ${GRAY}(wake ᠅/hr) ${LIGHT_PURPLE + BOLD}Gemstone: ${WHITE}up ${GRAY}(to ᠅/hr) ${AQUA + BOLD}Glacite: ${WHITE}the ${GRAY}(sounds ᠅/hr) ${BLUE + BOLD}Time: ${RED}Inactive`; -const powderOverlay = new Overlay("powderTracker", data.PL, "movePowder", powderExample, ["Dwarven Mines", "Crystal Hollows"]); +const powderOverlay = new Overlay("powderTracker", data.PL, "movePowder", powderExample, [ + "Dwarven Mines", + "Crystal Hollows", +]); /** * Command to reset powder overlay. */ register("command", () => { - for (let key in powders) - powders[key].reset(); - ChatLib.chat(`${LOGO + GREEN}Successfully reset powder tracker!`); + for (let key in powders) powders[key].reset(); + ChatLib.chat(`${LOGO + GREEN}Successfully reset powder tracker!`); }).setName("resetPowder"); /** @@ -38,45 +38,65 @@ register("command", () => { * @param {Number} current - The current time. */ function updatePowder(powder, current) { - if (powder.now !== current && powder.now !== 0) powder.since = 0; - if (powder.start === 0) powder.start = current; - if (current < powder.now) powder.start -= powder.now - current; - powder.now = current; + if (powder.now !== current && powder.now !== 0) powder.since = 0; + if (powder.start === 0) powder.start = current; + if (current < powder.now) powder.start -= powder.now - current; + powder.now = current; - if (powder.since < Settings.powderTracker * 60) { - powder.since += 1; - powder.time += 1; - } + if (powder.since < Settings.powderTracker * 60) { + powder.since += 1; + powder.time += 1; + } } /** * Updates powder overlay every second. */ -registerWhen(register("step", () => { +registerWhen( + register("step", () => { if (getPaused() || !World.isLoaded()) return; const tablist = TabList.getNames(); - const powderIndex = tablist.findIndex(line => line === "§r§9§lPowders:§r"); + const powderIndex = tablist.findIndex((line) => line === "§r§9§lPowders:§r"); if (powderIndex === undefined || powderIndex === -1) return; - const currentMithril = parseInt(tablist[powderIndex + 1].removeFormatting().trim().split(' ')[1]?.replace(/\D/g, '')); - const currentGemstone = parseInt(tablist[powderIndex + 2].removeFormatting().trim().split(' ')[1]?.replace(/\D/g, '')); + const currentMithril = parseInt( + tablist[powderIndex + 1].removeFormatting().trim().split(" ")[1]?.replace(/\D/g, "") + ); + const currentGemstone = parseInt( + tablist[powderIndex + 2].removeFormatting().trim().split(" ")[1]?.replace(/\D/g, "") + ); if (currentMithril !== undefined) updatePowder(powders.Mithril, currentMithril); if (currentGemstone !== undefined) updatePowder(powders.Gemstone, currentGemstone); if (location.getWorld() === "Dwarven Mines") { - const currentGlacite = parseInt(tablist[powderIndex + 3].removeFormatting().trim().split(' ')[1]?.replace(/\D/g, '')); - if (currentGlacite !== undefined) updatePowder(powders.Glacite, currentGlacite); + const currentGlacite = parseInt( + tablist[powderIndex + 3].removeFormatting().trim().split(" ")[1]?.replace(/\D/g, "") + ); + if (currentGlacite !== undefined) updatePowder(powders.Glacite, currentGlacite); } // Get max valid time let displayTime = 0; - Object.keys(powders).forEach(powder => { - if (powders[powder].time > displayTime && powders[powder].since < Settings.powderTracker * 60) displayTime = powders[powder].time; + Object.keys(powders).forEach((powder) => { + if (powders[powder].time > displayTime && powders[powder].since < Settings.powderTracker * 60) + displayTime = powders[powder].time; }); // Set HUD const timeDisplay = displayTime !== 0 ? formatTime(displayTime) : `${RED}Inactive`; - powderOverlay.setMessage( -`${DARK_GREEN + BOLD}Mithril: ${WHITE + commafy(powders.Mithril.getGain()) + GRAY} (${formatNumber(powders.Mithril.getRate(), 3)} ᠅/hr) -${LIGHT_PURPLE + BOLD}Gemstone: ${WHITE + commafy(powders.Gemstone.getGain()) + GRAY} (${formatNumber(powders.Gemstone.getRate(), 3)} ᠅/hr) -${AQUA + BOLD}Glacite: ${WHITE + commafy(powders.Glacite.getGain()) + GRAY} (${formatNumber(powders.Glacite.getRate(), 3)} ᠅/hr) -${BLUE + BOLD}Time: ${WHITE + timeDisplay}`); -}).setFps(1), () => (location.getWorld() === "Crystal Hollows" || location.getWorld() === "Dwarven Mines") && Settings.powderTracker !== 0); + powderOverlay.setMessage( + `${DARK_GREEN + BOLD}Mithril: ${WHITE + commafy(powders.Mithril.getGain()) + GRAY} (${formatNumber( + powders.Mithril.getRate(), + 3 + )} ᠅/hr) +${LIGHT_PURPLE + BOLD}Gemstone: ${WHITE + commafy(powders.Gemstone.getGain()) + GRAY} (${formatNumber( + powders.Gemstone.getRate(), + 3 + )} ᠅/hr) +${AQUA + BOLD}Glacite: ${WHITE + commafy(powders.Glacite.getGain()) + GRAY} (${formatNumber( + powders.Glacite.getRate(), + 3 + )} ᠅/hr) +${BLUE + BOLD}Time: ${WHITE + timeDisplay}` + ); + }).setFps(1), + () => ["Crystal Hollows", "Dwarven Mines", "Mineshaft"].includes(location.getWorld()) && Settings.powderTracker !== 0 +); diff --git a/features/mining/ShaftAnnounce.js b/features/mining/ShaftAnnounce.js index a1fb6ee..563c4e4 100644 --- a/features/mining/ShaftAnnounce.js +++ b/features/mining/ShaftAnnounce.js @@ -1,13 +1,12 @@ +import { STAND_CLASS } from "../../utils/Constants"; import location from "../../utils/Location"; import party from "../../utils/Party"; -import Settings from "../../utils/Settings"; -import { STAND_CLASS } from "../../utils/Constants"; -import { getClosest } from "../../utils/functions/find"; -import { convertToTitleCase } from "../../utils/functions/format"; import { registerWhen } from "../../utils/RegisterTils"; +import Settings from "../../utils/Settings"; import { delay } from "../../utils/ThreadTils"; import Waypoint from "../../utils/Waypoint"; - +import { getClosest } from "../../utils/functions/find"; +import { convertToTitleCase } from "../../utils/functions/format"; /** * Shaft Transfer @@ -16,22 +15,24 @@ const TRANSFER_COMMANDS = ["?transfer", "!ptme", "!pt", ".transfer", "Mineshaft, /** * Recursively calls party chat transfer commands until player becomes leader. - * + * * @param {Number} index - Index in TRANSFER_COMMANDS. */ function attemptTransfer(index) { - delay(() => { - if (party.getLeader() || index >= TRANSFER_COMMANDS.length) return; - ChatLib.command(`pc ${TRANSFER_COMMANDS[index]}`); - attemptTransfer(index + 1); - }, 420); + delay(() => { + if (party.getLeader() || index >= TRANSFER_COMMANDS.length) return; + ChatLib.command(`pc ${TRANSFER_COMMANDS[index]}`); + attemptTransfer(index + 1); + }, 420); } -registerWhen(register("chat", () => { +registerWhen( + register("chat", () => { if (!party.getIn()) return; attemptTransfer(0); -}).setCriteria("WOW! You found a Glacite Mineshaft portal!"), () => Settings.shaftTransfer && location.getWorld() === "Dwarven Mines"); - + }).setCriteria("WOW! You found a Glacite Mineshaft portal!"), + () => Settings.shaftTransfer && location.getWorld() === "Dwarven Mines" +); /** * Corpse Announce @@ -39,75 +40,93 @@ registerWhen(register("chat", () => { let corpses = []; let looted = []; register("worldUnload", () => { - corpses = []; - looted = []; + corpses = []; + looted = []; }); register("chat", () => { - delay(updateKeys, 3000); + delay(updateKeys, 3000); }).setCriteria(" ⛏ ${player} entered the mineshaft!"); function announceCorpse(corpseType) { - if (!party.getIn()) return; - const x = Math.round(Player.getX()); - const y = Math.round(Player.getY()); - const z = Math.round(Player.getZ()); - - // Determine corpse type - if (getClosest([x, y, z], corpses)[1] < 10) return; - ChatLib.command(`pc x: ${x}, y: ${y}, z: ${z} | ${corpseType} Corpse!`); + if (!party.getIn()) return; + const x = Math.round(Player.getX()); + const y = Math.round(Player.getY()); + const z = Math.round(Player.getZ()); + + // Determine corpse type + if (getClosest([x, y, z], corpses)[1] < 10) return; + ChatLib.command(`pc x: ${x}, y: ${y}, z: ${z} | ${corpseType} Corpse!`); } -registerWhen(register("chat", (type) => { +registerWhen( + register("chat", (type) => { looted.push([Player.getX(), Player.getY(), Player.getZ()]); if (Settings.corpseAnnounce) announceCorpse(convertToTitleCase(type)); -}).setCriteria(" ${type} CORPSE LOOT! "), () => (Settings.corpseAnnounce || Settings.corpseWaypoints) && location.getWorld() === "Mineshaft"); -registerWhen(register("chat", () => { + }).setCriteria(" ${type} CORPSE LOOT! "), + () => (Settings.corpseAnnounce || Settings.corpseWaypoints) && location.getWorld() === "Mineshaft" +); +registerWhen( + register("chat", () => { announceCorpse("Tungsten"); -}).setCriteria("You need to be holding a Tungsten Key to unlock this corpse!"), () => Settings.corpseAnnounce && location.getWorld() === "Mineshaft"); -registerWhen(register("chat", () => { + }).setCriteria("You need to be holding a Tungsten Key to unlock this corpse!"), + () => Settings.corpseAnnounce && location.getWorld() === "Mineshaft" +); +registerWhen( + register("chat", () => { announceCorpse("Umber"); -}).setCriteria("You need to be holding an Umber Key to unlock this corpse!"), () => Settings.corpseAnnounce && location.getWorld() === "Mineshaft"); -registerWhen(register("chat", () => { + }).setCriteria("You need to be holding an Umber Key to unlock this corpse!"), + () => Settings.corpseAnnounce && location.getWorld() === "Mineshaft" +); +registerWhen( + register("chat", () => { announceCorpse("Vanguard"); -}).setCriteria("You need to be holding a Skeleton Key to unlock this corpse!"), () => Settings.corpseAnnounce && location.getWorld() === "Mineshaft"); + }).setCriteria("You need to be holding a Skeleton Key to unlock this corpse!"), + () => Settings.corpseAnnounce && location.getWorld() === "Mineshaft" +); -registerWhen(register("chat", (_, x, y, z) => { - corpses.push([x, y, z.split(' ')[0]]); -}).setCriteria("${player}: x: ${x}, y: ${y}, z: ${z}"), () => Settings.corpseAnnounce && location.getWorld() === "Mineshaft"); +registerWhen( + register("chat", (_, x, y, z) => { + corpses.push([x, y, z.split(" ")[0]]); + }).setCriteria("${player}: x: ${x}, y: ${y}, z: ${z}"), + () => Settings.corpseAnnounce && location.getWorld() === "Mineshaft" +); /** - * Corpse detection + * Corpse detection */ const ARMOR_MATCH = { - "Lapis": "Lapis", - "Mineral": "Tungsten", - "Yog": "Umber", - "Vanguard": "Vanguard" + Lapis: "Lapis", + Mineral: "Tungsten", + Yog: "Umber", + Vanguard: "Vanguard", }; const CORPSE_COLORS = { - "Lapis": [0.15, 0.38, 0.61], - "Mineral": [0.84, 0.82, 0.77], - "Yog": [1, 0.65, 0], - "Vanguard": [0, 1, 1] + Lapis: [0.15, 0.38, 0.61], + Mineral: [0.84, 0.82, 0.77], + Yog: [1, 0.65, 0], + Vanguard: [0, 1, 1], }; const corpseWaypoints = new Waypoint(); -registerWhen(register("step", () => { +registerWhen( + register("step", () => { const stands = World.getAllEntitiesOfType(STAND_CLASS); corpseWaypoints.clear(); - stands.forEach(stand => { - const helmet = stand.getEntity()?.func_71124_b(4); // getEquipmentInSlot(0: Tool in Hand; 1-4: Armor) - if (helmet !== null) { - const type = helmet.func_82833_r().removeFormatting().split(' ')[0]; // getDisplayName for ItemStack - if (!(type in CORPSE_COLORS)) return; + stands.forEach((stand) => { + const helmet = stand.getEntity()?.func_71124_b(4); // getEquipmentInSlot(0: Tool in Hand; 1-4: Armor) + if (helmet !== null) { + const type = helmet.func_82833_r().removeFormatting().split(" ")[0]; // getDisplayName for ItemStack + if (!(type in CORPSE_COLORS)) return; - const corpsePos = [ARMOR_MATCH[type], stand.getX(), stand.getY() + 1, stand.getZ()]; - if (getClosest(corpsePos, looted)[1] < 10) return; + const corpsePos = [ARMOR_MATCH[type], stand.getX(), stand.getY() + 1, stand.getZ()]; + if (getClosest(corpsePos, looted)[1] < 10) return; - const color = CORPSE_COLORS[type]; - corpseWaypoints.push([...color, ...corpsePos]); - } + const color = CORPSE_COLORS[type]; + corpseWaypoints.push([...color, ...corpsePos]); + } }); -}).setDelay(1), () => Settings.corpseWaypoints && location.getWorld() === "Mineshaft"); + }).setDelay(1), + () => Settings.corpseWaypoints && location.getWorld() === "Mineshaft" +); diff --git a/features/mining/WishingCompass.js b/features/mining/WishingCompass.js index ccbb175..c8712d9 100644 --- a/features/mining/WishingCompass.js +++ b/features/mining/WishingCompass.js @@ -1,83 +1,94 @@ -import location from "../../utils/Location"; -import Settings from "../../utils/Settings"; import { GRAY, GREEN, LOGO } from "../../utils/Constants"; +import location from "../../utils/Location"; import { registerWhen } from "../../utils/RegisterTils"; +import Settings from "../../utils/Settings"; import Waypoint from "../../utils/Waypoint"; - /** * Variables used to estimate compass location. */ -const compass = new Waypoint([0.75, 0.17, 0.41]); // Bright Purple Compass +const compass = new Waypoint([0.75, 0.17, 0.41]); // Bright Purple Compass let path = []; let lastPath = []; let zone = undefined; register("worldUnload", () => { - zone = undefined; + zone = undefined; }); /** * Finds the intercept location between the dx, dz of two lines. - * + * * @param {Number[]} line1 - Line numero uno * @param {Number[]} line2 - Line numero dos * @returns {Number[]} [x, z] of the intercept (or undefined if parallel) */ function findIntersection(line1, line2) { - const [[x1, , z1], [x2, , z2]] = line1; - const [[x3, , z3], [x4, , z4]] = line2; + const [[x1, , z1], [x2, , z2]] = line1; + const [[x3, , z3], [x4, , z4]] = line2; - const determinant = (x1 - x2) * (z3 - z4) - (z1 - z2) * (x3 - x4); - if (determinant === 0) return undefined; + const determinant = (x1 - x2) * (z3 - z4) - (z1 - z2) * (x3 - x4); + if (determinant === 0) return undefined; - // Calc intersection - const x = ((x1 * z2 - z1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * z4 - z3 * x4)) / determinant; - const z = ((x1 * z2 - z1 * x2) * (z3 - z4) - (z1 - z2) * (x3 * z4 - z3 * x4)) / determinant; + // Calc intersection + const x = ((x1 * z2 - z1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * z4 - z3 * x4)) / determinant; + const z = ((x1 * z2 - z1 * x2) * (z3 - z4) - (z1 - z2) * (x3 * z4 - z3 * x4)) / determinant; - return [x, z]; + return [x, z]; } /** * Uses wishing compass shattered chat message to update path logic. */ -registerWhen(register("chat", () => { +registerWhen( + register("chat", () => { if (zone === location.getZone()) lastPath = path; else lastPath = []; zone = location.getZone(); path = []; -}).setCriteria("Your Wishing Compass shattered into pieces!"), () => location.getWorld() === "Crystal Hollows" && Settings.compassLocator); + }).setCriteria("Your Wishing Compass shattered into pieces!"), + () => location.getWorld() === "Crystal Hollows" && Settings.compassLocator +); /** * Uses compass particles to track and estimate compass location. */ -registerWhen(register("spawnParticle", (particle, type) => { +registerWhen( + register("spawnParticle", (particle, type) => { if (path.length > 1 || type.toString() !== "VILLAGER_HAPPY") return; path.push([particle.getX(), particle.getY(), particle.getZ()]); if (path.length > 1 && lastPath.length > 1) { - // Find x and z of location - const intersect = findIntersection(path, lastPath); - if (intersect === undefined) return; + // Find x and z of location + const intersect = findIntersection(path, lastPath); + if (intersect === undefined) return; - // Find y of location - const origin = path[0]; - const distance = Math.hypot(intersect[0] - origin[0], intersect[1] - origin[2]); - const y = origin[1] + distance * (path[1][1] - origin[1]); + // Find y of location + const origin = path[0]; + const distance = Math.hypot(intersect[0] - origin[0], intersect[1] - origin[2]); + const y = origin[1] + distance * (path[1][1] - origin[1]); - // Get location name - const name = zone.startsWith(" ⏣ Mithril") ? "Meido in Abisu" : - zone.startsWith(" ⏣ Precursor") ? "Mirage Island" : - zone.startsWith(" ⏣ Goblin") ? "Beard Cutter" : - zone.startsWith(" ⏣ Jungle") ? "Tarzan's Jarven" : - zone.startsWith(" ⏣ Magma") ? "Heatran's Cave" : "Important Location"; + // Get location name + const name = zone.startsWith(" ⏣ Mithril") + ? "Meido in Abisu" + : zone.startsWith(" ⏣ Precursor") + ? "Mirage Island" + : zone.startsWith(" ⏣ Goblin") + ? "Beard Cutter" + : zone.startsWith(" ⏣ Jungle") + ? "Tarzan's Jarven" + : zone.startsWith(" ⏣ Magma") + ? "Heatran's Cave" + : "Important Location"; - compass.clear(); - compass.push([name, intersect[0], y, intersect[1]]); - ChatLib.chat(`${LOGO + GREEN}Compass location found!`); + compass.clear(); + compass.push([name, intersect[0], y, intersect[1]]); + ChatLib.chat(`${LOGO + GREEN}Compass location found!`); - // Check if paths are too close - const close = Math.hypot(origin[0] - lastPath[0][0], origin[2] - lastPath[0][2]); - if (close < 16) ChatLib.chat(`${LOGO + GRAY}Location may be incorrect due to proximity of compass uses...`); + // Check if paths are too close + const close = Math.hypot(origin[0] - lastPath[0][0], origin[2] - lastPath[0][2]); + if (close < 16) ChatLib.chat(`${LOGO + GRAY}Location may be incorrect due to proximity of compass uses...`); } -}), () => location.getWorld() === "Crystal Hollows" && Settings.compassLocator); + }), + () => location.getWorld() === "Crystal Hollows" && Settings.compassLocator +); diff --git a/features/party/AntiGhostParty.js b/features/party/AntiGhostParty.js index 21ee1f8..6cc7ced 100644 --- a/features/party/AntiGhostParty.js +++ b/features/party/AntiGhostParty.js @@ -1,23 +1,32 @@ -import party from "../../utils/Party"; -import Settings from "../../utils/Settings"; import { GREEN, LOGO } from "../../utils/Constants"; +import party from "../../utils/Party"; import { registerWhen } from "../../utils/RegisterTils"; +import Settings from "../../utils/Settings"; import { delay } from "../../utils/ThreadTils"; - /** * Handles redirection of party commands by intercepting and modifying chat messages. */ const partyCommands = new Set(["accept", "join", "kick", "promote", "demote"]); let cd = false; -registerWhen(register("messageSent", (message, event) => { - const args = message.split(' '); - if (cd || party.getIn() || args.length < 3 || (args[0] !== "/p" && args[0] !== "/Party") || partyCommands.has(args[1].toLowerCase())) return; +registerWhen( + register("messageSent", (message, event) => { + const args = message.split(" "); + if ( + cd || + party.getIn() || + args.length < 3 || + (args[0] !== "/p" && args[0] !== "/Party") || + partyCommands.has(args[1].toLowerCase()) + ) + return; cd = true; - delay(() => cd = false, 1000); + delay(() => (cd = false), 1000); - ChatLib.chat(`${LOGO + GREEN}Cancelling ghost party...`) + ChatLib.chat(`${LOGO + GREEN}Cancelling ghost party...`); cancel(event); ChatLib.command(`p ${args[1]}`); - delay(() => ChatLib.command(`p ${args.splice(2).join(' ')}`), 500); -}), () => Settings.antiGhostParty); + delay(() => ChatLib.command(`p ${args.splice(2).join(" ")}`), 500); + }), + () => Settings.antiGhostParty +); diff --git a/features/party/AutoKick.js b/features/party/AutoKick.js index abbaeb5..4a40e71 100644 --- a/features/party/AutoKick.js +++ b/features/party/AutoKick.js @@ -1,17 +1,18 @@ +import { data } from "../../utils/Data"; import party from "../../utils/Party"; -import { getPlayerName } from "../../utils/functions/player"; import { registerWhen } from "../../utils/RegisterTils"; import { delay } from "../../utils/ThreadTils"; -import { data } from "../../utils/Data"; - +import { getPlayerName } from "../../utils/functions/player"; /** * Kick users on blacklist. */ -registerWhen(register("chat", (player) => { +registerWhen( + register("chat", (player) => { if (!party.getLeader()) return; - + const name = getPlayerName(player).toLowerCase(); - if (data.blacklist.includes(name)) - delay(() => ChatLib.command(`p kick ${name}`)); -}).setCriteria("${player} joined the party."), () => data.blacklist.length !== 0); + if (data.blacklist.includes(name)) delay(() => ChatLib.command(`p kick ${name}`)); + }).setCriteria("${player} joined the party."), + () => data.blacklist.length !== 0 +); diff --git a/features/party/AutoTransfer.js b/features/party/AutoTransfer.js index a6a6363..9fe2906 100644 --- a/features/party/AutoTransfer.js +++ b/features/party/AutoTransfer.js @@ -1,50 +1,67 @@ import location from "../../utils/Location"; import party from "../../utils/Party"; -import Settings from "../../utils/Settings"; -import { getPlayerName } from "../../utils/functions/player"; import { registerWhen } from "../../utils/RegisterTils"; +import Settings from "../../utils/Settings"; import { delay } from "../../utils/ThreadTils"; - +import { getPlayerName } from "../../utils/functions/player"; /** * Transfers party back whenever you become leader. */ -registerWhen(register("chat", (player1, player2) => { +registerWhen( + register("chat", (player1, player2) => { const name1 = getPlayerName(player1).toLowerCase(); const name2 = getPlayerName(player2).toLowerCase(); - if (name1 === Player.getName().toLowerCase()) - delay(() => ChatLib.command("p transfer " + name2), 500); -}).setCriteria("The party was transferred to ${player1} by ${player2}"), () => Settings.autoTransfer === 1); - + if (name1 === Player.getName().toLowerCase()) delay(() => ChatLib.command("p transfer " + name2), 500); + }).setCriteria("The party was transferred to ${player1} by ${player2}"), + () => Settings.autoTransfer === 1 +); /** * Auto transfer if in lobby. */ let transferred = false; -registerWhen(register("chat", (player1, player2) => { +registerWhen( + register("chat", (player1, player2) => { if (location.getWorld() !== undefined && !transferred) return; const name1 = getPlayerName(player1).toLowerCase(); const name2 = getPlayerName(player2).toLowerCase(); if (name1 === Player.getName().toLowerCase()) delay(() => ChatLib.command("p transfer " + name2), 500); transferred = true; -}).setCriteria("The party was transferred to ${player1} by ${player2}"), () => Settings.autoTransfer === 2); -registerWhen(register("worldLoad", () => { transferred = false }), () => Settings.autoTransfer === 2); -registerWhen(register("chat", () => { + }).setCriteria("The party was transferred to ${player1} by ${player2}"), + () => Settings.autoTransfer === 2 +); +registerWhen( + register("worldLoad", () => { + transferred = false; + }), + () => Settings.autoTransfer === 2 +); +registerWhen( + register("chat", () => { if (!party.getLeader()) return; const members = Array.from(party.getMembers()); delay(() => ChatLib.command(`p transfer ${members[Math.floor(Math.random() * members.length)]}`), 500); -}).setCriteria("Oops! You are not on SkyBlock so we couldn't warp you!"), () => Settings.autoTransfer === 2); + }).setCriteria("Oops! You are not on SkyBlock so we couldn't warp you!"), + () => Settings.autoTransfer === 2 +); /** * Announce to party when kicked */ -registerWhen(register("chat", () => { +registerWhen( + register("chat", () => { if (!party.getIn()) return; delay(() => ChatLib.command(`pc ${Settings.kickAnnounce}`), 1000); -}).setCriteria("Oops! You are not on SkyBlock so we couldn't warp you!"), () => Settings.kickAnnounce !== ""); -registerWhen(register("chat", () => { + }).setCriteria("Oops! You are not on SkyBlock so we couldn't warp you!"), + () => Settings.kickAnnounce !== "" +); +registerWhen( + register("chat", () => { if (!party.getIn()) return; delay(() => ChatLib.command(`pc ${Settings.kickAnnounce}`), 1000); -}).setCriteria("You were kicked while joining that server!"), () => Settings.kickAnnounce !== ""); + }).setCriteria("You were kicked while joining that server!"), + () => Settings.kickAnnounce !== "" +); diff --git a/features/party/JoinMessage.js b/features/party/JoinMessage.js index 13c8352..e91516d 100644 --- a/features/party/JoinMessage.js +++ b/features/party/JoinMessage.js @@ -1,23 +1,28 @@ import party from "../../utils/Party"; -import Settings from "../../utils/Settings"; -import { getGuildName, getPlayerName } from "../../utils/functions/player"; import { registerWhen } from "../../utils/RegisterTils"; +import Settings from "../../utils/Settings"; import { delay } from "../../utils/ThreadTils"; - +import { getGuildName, getPlayerName } from "../../utils/functions/player"; /** * Sends a party chat message when someone joins. */ -registerWhen(register("chat", (player) => { +registerWhen( + register("chat", (player) => { if (Settings.partyMessageLeader && !party.getLeader()) return; - const regex = new RegExp("\\$\\{name\\}", 'g'); + const regex = new RegExp("\\$\\{name\\}", "g"); delay(() => ChatLib.command(`pc ${Settings.partyMessage.replace(regex, getPlayerName(player))}`), 250); -}).setCriteria("${player} joined the party."), () => Settings.partyMessage !== ""); + }).setCriteria("${player} joined the party."), + () => Settings.partyMessage !== "" +); /** * Sends a guild chat message when someone joins. */ -registerWhen(register("chat", (player) => { - const regex = new RegExp("\\$\\{name\\}", 'g'); +registerWhen( + register("chat", (player) => { + const regex = new RegExp("\\$\\{name\\}", "g"); delay(() => ChatLib.command(`gc ${Settings.guildMessage.replace(regex, getGuildName(player))}`), 250); -}).setCriteria("${player} joined the guild!"), () => Settings.guildMessage !== ""); + }).setCriteria("${player} joined the guild!"), + () => Settings.guildMessage !== "" +); diff --git a/features/party/JoinParty.js b/features/party/JoinParty.js index e32e800..868a05b 100644 --- a/features/party/JoinParty.js +++ b/features/party/JoinParty.js @@ -1,9 +1,8 @@ -import Settings from "../../utils/Settings"; -import { getPlayerName } from "../../utils/functions/player"; +import { data } from "../../utils/Data"; import { registerWhen } from "../../utils/RegisterTils"; +import Settings from "../../utils/Settings"; import { delay } from "../../utils/ThreadTils"; -import { data } from "../../utils/Data"; - +import { getPlayerName } from "../../utils/functions/player"; /** * Variables used to detect disbanded parties in past minute. @@ -13,30 +12,43 @@ let invites = []; /** * Records disbanded parties for 60 seconds for auto join reparty. */ -registerWhen(register("chat", (player) => { +registerWhen( + register("chat", (player) => { const name = getPlayerName(player).toLowerCase(); invites.push(name); delay(() => invites.shift(), 60000); -}).setCriteria("${player} has disbanded the party!"), () => Settings.joinRP); + }).setCriteria("${player} has disbanded the party!"), + () => Settings.joinRP +); /** * Acceots oarty invite if party was disbanded in last 60 seconds or part of user whitelist. */ -registerWhen(register("chat", (player) => { +registerWhen( + register("chat", (player) => { const name = getPlayerName(player).toLowerCase(); - if (Settings.joinWhitelist && data.whitelist.includes(name)) // Whitelist - delay(() => ChatLib.command("p accept " + name), 500); - else if (Settings.joinRP && invites.includes(name)) // Reparty - delay(() => ChatLib.command("p accept " + name), 500); -}).setCriteria("-----------------------------------------------------\n${player} has invited you to join their party!\nYou have 60 seconds to accept. Click here to join!\n-----------------------------------------------------"), -() => Settings.joinRP || Settings.joinWhitelist); -registerWhen(register("chat", (player1, player2) => { + if (Settings.joinWhitelist && data.whitelist.includes(name)) + // Whitelist + delay(() => ChatLib.command("p accept " + name), 500); + else if (Settings.joinRP && invites.includes(name)) + // Reparty + delay(() => ChatLib.command("p accept " + name), 500); + }).setCriteria( + "-----------------------------------------------------\n${player} has invited you to join their party!\nYou have 60 seconds to accept. Click here to join!\n-----------------------------------------------------" + ), + () => Settings.joinRP || Settings.joinWhitelist +); +registerWhen( + register("chat", (player1, player2) => { const name1 = getPlayerName(player1).toLowerCase(); const name2 = getPlayerName(player2).toLowerCase(); if (!data.whitelist.includes(name1) && !data.whitelist.includes(name2)) return; - + delay(() => ChatLib.command("p accept " + name1), 500); -}).setCriteria("-----------------------------------------------------\n${player1} has invited you to join ${player2}'s party!\nYou have 60 seconds to accept. Click here to join!\n-----------------------------------------------------"), -() => Settings.joinWhitelist); + }).setCriteria( + "-----------------------------------------------------\n${player1} has invited you to join ${player2}'s party!\nYou have 60 seconds to accept. Click here to join!\n-----------------------------------------------------" + ), + () => Settings.joinWhitelist +); diff --git a/features/party/PartyCommands.js b/features/party/PartyCommands.js index f474732..29afbf6 100644 --- a/features/party/PartyCommands.js +++ b/features/party/PartyCommands.js @@ -1,15 +1,14 @@ import axios from "../../../axios"; +import { AQUA, DARK_AQUA, DARK_GRAY, DARK_RED, LOGO, RED, WHITE, YELLOW } from "../../utils/Constants"; +import { data } from "../../utils/Data"; import party from "../../utils/Party"; +import { registerWhen } from "../../utils/RegisterTils"; import Settings from "../../utils/Settings"; +import { delay } from "../../utils/ThreadTils"; import Toggles from "../../utils/Toggles"; -import { AQUA, DARK_AQUA, DARK_GRAY, DARK_RED, LOGO, RED, WHITE, YELLOW } from "../../utils/Constants"; import { getGuildName, getPlayerName } from "../../utils/functions/player"; -import { registerWhen } from "../../utils/RegisterTils"; -import { delay } from "../../utils/ThreadTils"; -import { data } from "../../utils/Data"; import { getPing, getTPS } from "../general/Performance"; - /** * Variable used to represent /pc cooldown and possible responses. */ @@ -17,22 +16,53 @@ let onCD = false; const RESPONSES = JSON.parse(FileLib.read("VolcAddons", "json/8ball.json")); const RPS = ["rock", "paper", "scissors"]; const QUOTES = JSON.parse(FileLib.read("VolcAddons", "json/quotes.json")); -const W = ["waifu", "neko", "shinobu", "megumin", "bully", "cuddle", "cry", "hug", "awoo", "kiss", "lick", "pat", "smug", "bonk", "yeet", - "blush", "smile", "wave", "highfive", "handhold", "nom", "bite", "glomp", "slap", "kill", "kick", "happy", "wink", "poke", "dance", "cringe"]; +const W = [ + "waifu", + "neko", + "shinobu", + "megumin", + "bully", + "cuddle", + "cry", + "hug", + "awoo", + "kiss", + "lick", + "pat", + "smug", + "bonk", + "yeet", + "blush", + "smile", + "wave", + "highfive", + "handhold", + "nom", + "bite", + "glomp", + "slap", + "kill", + "kick", + "happy", + "wink", + "poke", + "dance", + "cringe", +]; register("command", (send, id, randID, command) => { - if (send !== "false") ChatLib.command(`${send} va-${id}-${command === "nsfw" ? "nw" : "w"} ${randID}`); - else { - const link = `https://i.waifu.pics/${id.replace('@', '.')}`; - new Message( - new TextComponent(LOGO + link).setHoverValue(link), - new TextComponent(` ${DARK_GRAY}[BOOP]`) - .setClickAction("run_command") - .setClickValue(`/va w ${command}`) - .setHoverValue(`${YELLOW}Click to regenerate image.`) - ).chat(); - } -}).setName("sendWaifu") + if (send !== "false") ChatLib.command(`${send} va-${id}-${command === "nsfw" ? "nw" : "w"} ${randID}`); + else { + const link = `https://i.waifu.pics/${id.replace("@", ".")}`; + new Message( + new TextComponent(LOGO + link).setHoverValue(link), + new TextComponent(` ${DARK_GRAY}[BOOP]`) + .setClickAction("run_command") + .setClickValue(`/va w ${command}`) + .setHoverValue(`${YELLOW}Click to regenerate image.`) + ).chat(); + } +}).setName("sendWaifu"); /** * Various party and leader commands. @@ -42,262 +72,292 @@ register("command", (send, id, randID, command) => { * @param {String} sendTo - Chat to send response to (/pc, /gc, /r) */ export function executeCommand(name, args, sendTo) { - if (data.ignorelist.includes(name.toLowerCase())) return; - const command = args[0].toLowerCase(); - - // PARTY COMMANDS - const randID = sendTo === "pc" ? '' : '@' + (Math.random() + 1).toString(36).substring(2); - - delay(() => { switch (command) { - case "cringe": // Slander - case "gay": - case "racist": - case "femboy": - case "trans": - case "transphobic": - if (!Toggles.slanderCommand) return; - - const slandering = args[1] ?? name; - const percentage = Math.floor(Math.random() * 100) + 1; - if (sendTo !== false) ChatLib.command(`${sendTo} ${slandering} is ${percentage}% ${command}! ${randID}`); - else ChatLib.chat(`${LOGO + DARK_AQUA}You are ${WHITE + percentage}% ${DARK_AQUA + command}!`); - break; - case "dice": // Dice roll - case "roll": - if (!Toggles.diceCommand) return; - - const roll = Math.floor(Math.random() * 6) + 1; - if (sendTo !== false) ChatLib.command(`${sendTo} ${name} rolled a ${roll}! ${randID}`); - else ChatLib.chat(`${LOGO + DARK_AQUA}You rolled a ${WHITE + roll + DARK_AQUA}!`); - break; - case "coin": // Coin flip - case "flip": - case "coinflip": - case "cf": - if (!Toggles.coinCommand) return; - - const flip = Math.floor(Math.random() * 2) ? "heads" : "tails"; - if (sendTo !== false) ChatLib.command(`${sendTo} ${name} flipped ${flip}! ${randID}`); - else ChatLib.chat(`${LOGO + DARK_AQUA}You flipped ${WHITE + flip + DARK_AQUA}!`); - break; - case "8ball": // 8ball - if (!Toggles.ballCommand) return; - - if (sendTo !== false) ChatLib.command(`${sendTo} ${RESPONSES[Math.floor(Math.random() * 20) + 1]}. ${randID}`); - else ChatLib.chat(`${LOGO + DARK_AQUA + RESPONSES[Math.floor(Math.random() * 20) + 1]}.`) - break; - case "rps": // Rock Paper Siccors - if (!Toggles.rpsCommand) return; - - const player = args[1] === undefined ? -1 : RPS.indexOf(args[1].toLowerCase()); - let reply = player === -1 ? `なんと、 ${args[1]}?` : "zzz..."; - // Plays game out if user inputs a correct symbol - if (player !== -1) { - const choice = Math.floor(Math.random() * 3); - if (sendTo !== false) ChatLib.command(`${sendTo} I choose ${RPS[choice]}! ${randID}`); - else ChatLib.chat(`${LOGO + DARK_AQUA}I choose ${WHITE + RPS[choice] + DARK_AQUA}!`); - const outcome = (player - choice); - - // Determine outcome of the game - switch (outcome) { - case -2: - case 1: - reply = "bor, this game is so bad."; - break; - case 2: - case -1: - const quote = QUOTES[Math.floor(Math.random() * QUOTES.length)]; - reply = `I believe you may need some advice: ${WHITE}"${quote}" ${AQUA}~Volcaronitee (i think)`; - break; - } - } - - // Output reponse depending if use wants party message or user message - if (sendTo !== false) delay(() => ChatLib.command(`${sendTo} ${reply} ${randID}`), 690); - else ChatLib.chat(`${LOGO + DARK_AQUA + reply}`); - break; - case "waifu": - case "women": - case "w": - const category = Toggles.womenCommand; - if (category === 0) return; - - const arg = W.includes(args[1]) ? args[1] : - category === 1 ? W[Math.floor(Math.random() * (W.length - 1))] : W[category - 2]; - const nsfw = args[1] === "nsfw"; - const link = nsfw ? `https://api.waifu.pics/nsfw/waifu` : `https://api.waifu.pics/sfw/${arg}`; - if (nsfw && !Toggles.r18) return; - - axios.get(link).then(w => { - const waifu = w.data.url.split('/')[3].replace('.', '@'); - - if (nsfw) { - new TextComponent(`${LOGO + RED}Click to send NSFW image.`) - .setClickAction("run_command") - .setClickValue(`/sendWaifu ${sendTo} ${waifu} ${randID} ${args[1]}`) - .setHoverValue(`${DARK_RED}WARNING: NSFW content!\nContinue at your own risk.`) - .chat(); - } else ChatLib.command(`sendWaifu ${sendTo} ${waifu} ${randID} ${args[1]}`, true); - }); - break; - case "coords": - case "waypoint": - case "xyz": - if (!Toggles.coordsCommand || Player.getName() === name) return; - - if (sendTo !== false) ChatLib.command(`${sendTo} x: ${Math.round(Player.getX())}, y: ${Math.round(Player.getY())}, z: ${Math.round(Player.getZ())} ${randID}`); - else ChatLib.command(`r x: ${Math.round(Player.getX())}, y: ${Math.round(Player.getY())}, z: ${Math.round(Player.getZ())}`); - break; - case "fps": - if (!Toggles.statusCommand) return; - - if (sendTo !== false) ChatLib.command(`${sendTo} ${Client.getFPS()}fps`); - break; - case "ping": - if (!Toggles.statusCommand) return; - - if (sendTo !== false) ChatLib.command(`${sendTo} ${getPing()}ms`); - break; - case "tps": - if (!Toggles.statusCommand) return; - - if (sendTo !== false) ChatLib.command(`${sendTo} ${getTPS().toFixed(2)}tps`); - break; - case "limbo": - case "lobby": - case "l": - if (!Toggles.limboCommand || party.getLeader() || Player.getName() === name) return; - - ChatLib.command("l"); - break; - case "leave": - if (!Toggles.limboCommand || party.getLeader() || Player.getName() === name) return; - - ChatLib.command("p leave"); - break; - case "help": - if (!Toggles.helpCommand || !sendTo) return; - - ChatLib.command(`${sendTo} Party Commands: ? ${randID}`); - if (party.getLeader() && Settings.leaderCommands) - delay(() => ChatLib.command(`${sendTo} Leader Commands: ? ${randID}`), 690); - break; - } }, 690); - - // LEADER COMMANDS - if (!sendTo || (sendTo === "pc" && party.getLeader() && Settings.leaderCommands && Player.getName() !== name)) { - switch (command) { - case "mute": - if (!Toggles.warpCommand) return; - ChatLib.command("p mute"); - break; - case "warp": - if (!Toggles.warpCommand) return; - ChatLib.command("p warp"); - break; - case "transfer": - case "ptme": - case "pt": - case "pm": - if (!Toggles.transferCommand) return; - ChatLib.command("p transfer " + name); - break; - case "promote": - if (!Toggles.promoteCommand) return; - ChatLib.command("p promote " + name); - break; - case "demote": - if (!Toggles.demoteCommand) return; - ChatLib.command("p demote " + name); - break; - case "allinvite": - case "allinv": - if (!Toggles.allinvCommand) return; - ChatLib.command("p settings allinvite"); - break; - case "streamopen": - case "stream": - if (!Toggles.streamCommand) return; - - num = isNaN(args[1]) ? 10 : args[1]; - ChatLib.command(`stream open ${args[1]}`); - break; - default: // Join instance commands - if (Toggles.instanceCommand == false) return; - const floors = { - 1: "one", - 2: "two", - 3: "three", - 4: "four", - 5: "five", - 6: "six", - 7: "seven" - } - const tiers = { - 1: "basic", - 2: "hot", - 3: "burning", - 4: "fiery", - 5: "infernal" - } - const l1 = args[0][0].toLowerCase(); - const l2 = args[0][1]; - args?.[0]; - - if (l1 === "m" && l2 in floors) ChatLib.command(`joininstance master_catacombs_floor_${floors[l2]}`); - else if (l1 === "f" && l2 in floors) ChatLib.command(`joininstance catacombs_floor_${floors[l2]}`); - else if (l1 === "t" && l2 in tiers) ChatLib.command(`joininstance kuudra_${tiers[l2]}`); - break; + if (data.ignorelist.includes(name.toLowerCase())) return; + const command = args[0].toLowerCase(); + + // PARTY COMMANDS + const randID = sendTo === "pc" ? "" : "@" + (Math.random() + 1).toString(36).substring(2); + + delay(() => { + switch (command) { + case "cringe": // Slander + case "gay": + case "racist": + case "femboy": + case "trans": + case "transphobic": + if (!Toggles.slanderCommand) return; + + const slandering = args[1] ?? name; + const percentage = Math.floor(Math.random() * 100) + 1; + if (sendTo !== false) ChatLib.command(`${sendTo} ${slandering} is ${percentage}% ${command}! ${randID}`); + else ChatLib.chat(`${LOGO + DARK_AQUA}You are ${WHITE + percentage}% ${DARK_AQUA + command}!`); + break; + case "dice": // Dice roll + case "roll": + if (!Toggles.diceCommand) return; + + const roll = Math.floor(Math.random() * 6) + 1; + if (sendTo !== false) ChatLib.command(`${sendTo} ${name} rolled a ${roll}! ${randID}`); + else ChatLib.chat(`${LOGO + DARK_AQUA}You rolled a ${WHITE + roll + DARK_AQUA}!`); + break; + case "coin": // Coin flip + case "flip": + case "coinflip": + case "cf": + if (!Toggles.coinCommand) return; + + const flip = Math.floor(Math.random() * 2) ? "heads" : "tails"; + if (sendTo !== false) ChatLib.command(`${sendTo} ${name} flipped ${flip}! ${randID}`); + else ChatLib.chat(`${LOGO + DARK_AQUA}You flipped ${WHITE + flip + DARK_AQUA}!`); + break; + case "8ball": // 8ball + if (!Toggles.ballCommand) return; + + if (sendTo !== false) ChatLib.command(`${sendTo} ${RESPONSES[Math.floor(Math.random() * 20) + 1]}. ${randID}`); + else ChatLib.chat(`${LOGO + DARK_AQUA + RESPONSES[Math.floor(Math.random() * 20) + 1]}.`); + break; + case "rps": // Rock Paper Siccors + if (!Toggles.rpsCommand) return; + + const player = args[1] === undefined ? -1 : RPS.indexOf(args[1].toLowerCase()); + let reply = player === -1 ? `なんと、 ${args[1]}?` : "zzz..."; + // Plays game out if user inputs a correct symbol + if (player !== -1) { + const choice = Math.floor(Math.random() * 3); + if (sendTo !== false) ChatLib.command(`${sendTo} I choose ${RPS[choice]}! ${randID}`); + else ChatLib.chat(`${LOGO + DARK_AQUA}I choose ${WHITE + RPS[choice] + DARK_AQUA}!`); + const outcome = player - choice; + + // Determine outcome of the game + switch (outcome) { + case -2: + case 1: + reply = "bor, this game is so bad."; + break; + case 2: + case -1: + const quote = QUOTES[Math.floor(Math.random() * QUOTES.length)]; + reply = `I believe you may need some advice: ${WHITE}"${quote}" ${AQUA}~Volcaronitee (i think)`; + break; + } } + + // Output reponse depending if use wants party message or user message + if (sendTo !== false) delay(() => ChatLib.command(`${sendTo} ${reply} ${randID}`), 690); + else ChatLib.chat(`${LOGO + DARK_AQUA + reply}`); + break; + case "waifu": + case "women": + case "w": + const category = Toggles.womenCommand; + if (category === 0) return; + + const arg = W.includes(args[1]) + ? args[1] + : category === 1 + ? W[Math.floor(Math.random() * (W.length - 1))] + : W[category - 2]; + const nsfw = args[1] === "nsfw"; + const link = nsfw ? `https://api.waifu.pics/nsfw/waifu` : `https://api.waifu.pics/sfw/${arg}`; + if (nsfw && !Toggles.r18) return; + + axios.get(link).then((w) => { + const waifu = w.data.url.split("/")[3].replace(".", "@"); + + if (nsfw) { + new TextComponent(`${LOGO + RED}Click to send NSFW image.`) + .setClickAction("run_command") + .setClickValue(`/sendWaifu ${sendTo} ${waifu} ${randID} ${args[1]}`) + .setHoverValue(`${DARK_RED}WARNING: NSFW content!\nContinue at your own risk.`) + .chat(); + } else ChatLib.command(`sendWaifu ${sendTo} ${waifu} ${randID} ${args[1]}`, true); + }); + break; + case "coords": + case "waypoint": + case "xyz": + if (!Toggles.coordsCommand || Player.getName() === name) return; + + if (sendTo !== false) + ChatLib.command( + `${sendTo} x: ${Math.round(Player.getX())}, y: ${Math.round(Player.getY())}, z: ${Math.round( + Player.getZ() + )} ${randID}` + ); + else + ChatLib.command( + `r x: ${Math.round(Player.getX())}, y: ${Math.round(Player.getY())}, z: ${Math.round(Player.getZ())}` + ); + break; + case "fps": + if (!Toggles.statusCommand) return; + + if (sendTo !== false) ChatLib.command(`${sendTo} ${Client.getFPS()}fps`); + break; + case "ping": + if (!Toggles.statusCommand) return; + + if (sendTo !== false) ChatLib.command(`${sendTo} ${getPing()}ms`); + break; + case "tps": + if (!Toggles.statusCommand) return; + + if (sendTo !== false) ChatLib.command(`${sendTo} ${getTPS().toFixed(2)}tps`); + break; + case "limbo": + case "lobby": + case "l": + if (!Toggles.limboCommand || party.getLeader() || Player.getName() === name) return; + + ChatLib.command("l"); + break; + case "leave": + if (!Toggles.limboCommand || party.getLeader() || Player.getName() === name) return; + + ChatLib.command("p leave"); + break; + case "help": + if (!Toggles.helpCommand || !sendTo) return; + + ChatLib.command(`${sendTo} Party Commands: ? ${randID}`); + if (party.getLeader() && Settings.leaderCommands) + delay( + () => + ChatLib.command( + `${sendTo} Leader Commands: ? ${randID}` + ), + 690 + ); + break; } - - // MODERATOR COMMANDS - if (Settings.leaderCommands && Toggles.inviteCommand && (command === "inv" || command === "invite")) { - if (data.whitelist.includes(name.toLowerCase())) ChatLib.command(`p ${name}`); - else ChatLib.command(`r You are not in the whitelist! ${randID}`); + }, 690); + + // LEADER COMMANDS + if (!sendTo || (sendTo === "pc" && party.getLeader() && Settings.leaderCommands && Player.getName() !== name)) { + switch (command) { + case "mute": + if (!Toggles.warpCommand) return; + ChatLib.command("p mute"); + break; + case "warp": + if (!Toggles.warpCommand) return; + ChatLib.command("p warp"); + break; + case "transfer": + case "ptme": + case "pt": + case "pm": + if (!Toggles.transferCommand) return; + ChatLib.command("p transfer " + name); + break; + case "promote": + if (!Toggles.promoteCommand) return; + ChatLib.command("p promote " + name); + break; + case "demote": + if (!Toggles.demoteCommand) return; + ChatLib.command("p demote " + name); + break; + case "allinvite": + case "allinv": + if (!Toggles.allinvCommand) return; + ChatLib.command("p settings allinvite"); + break; + case "streamopen": + case "stream": + if (!Toggles.streamCommand) return; + + num = isNaN(args[1]) ? 10 : args[1]; + ChatLib.command(`stream open ${args[1]}`); + break; + default: // Join instance commands + if (Toggles.instanceCommand == false) return; + const floors = { + 1: "one", + 2: "two", + 3: "three", + 4: "four", + 5: "five", + 6: "six", + 7: "seven", + }; + const tiers = { + 1: "basic", + 2: "hot", + 3: "burning", + 4: "fiery", + 5: "infernal", + }; + const l1 = args[0][0].toLowerCase(); + const l2 = args[0][1]; + args?.[0]; + + if (l1 === "m" && l2 in floors) ChatLib.command(`joininstance master_catacombs_floor_${floors[l2]}`); + else if (l1 === "f" && l2 in floors) ChatLib.command(`joininstance catacombs_floor_${floors[l2]}`); + else if (l1 === "t" && l2 in tiers) ChatLib.command(`joininstance kuudra_${tiers[l2]}`); + break; } + } + + // MODERATOR COMMANDS + if (Settings.leaderCommands && Toggles.inviteCommand && (command === "inv" || command === "invite")) { + if (data.whitelist.includes(name.toLowerCase())) ChatLib.command(`p ${name}`); + else ChatLib.command(`r You are not in the whitelist! ${randID}`); + } - onCD = true; - delay(() => onCD = false, 1000); + onCD = true; + delay(() => (onCD = false), 1000); } /** * Parse out rand ID of messages */ register("chat", (player, msg, id, event) => { - if (id.length !== 12 || id.includes(' ') || msg.endsWith('-w ')) return; + if (id.length !== 12 || id.includes(" ") || msg.endsWith("-w ")) return; - cancel(event); - ChatLib.chat(`&${player}:${msg}`); + cancel(event); + ChatLib.chat(`&${player}:${msg}`); }).setCriteria("&${player}:${msg}@${id}"); /** - * Detects when player inputs a ?command and set the chat + * Detects when player inputs a ?command and set the chat */ -data.prefixlist.forEach(prefix => { - const colorRegex = "&${color}"; - const playerRegex = "${player}"; - const messageRegex = "${message}"; - - registerWhen(register("chat", (player, _, message) => { - if (onCD) return; - executeCommand(getPlayerName(player), message.split(" "), "ac"); - }).setCriteria(`&r${playerRegex + colorRegex}: ${prefix + messageRegex}&r`), () => Settings.partyCommands && Toggles.allCommands); - registerWhen(register("chat", (player, message) => { - if (onCD) return; - executeCommand(getPlayerName(player), message.split(" "), "pc"); - }).setCriteria(`Party > ${playerRegex}: ${prefix + messageRegex}`), () => Settings.partyCommands && Toggles.partyCommands); - registerWhen(register("chat", (player, message) => { - if (onCD) return; - executeCommand(getGuildName(player), message.split(" "), "gc"); - }).setCriteria(`Guild > ${playerRegex}: ${prefix + messageRegex}`), () => Settings.partyCommands && Toggles.guildCommands); - registerWhen(register("chat", (player, message) => { - if (onCD) return; - executeCommand(getPlayerName(player), message.split(" "), "r"); - }).setCriteria(`From ${playerRegex}: ${prefix + messageRegex}`), () => Settings.partyCommands && Toggles.dmCommands); +data.prefixlist.forEach((prefix) => { + const colorRegex = "&${color}"; + const playerRegex = "${player}"; + const messageRegex = "${message}"; + + registerWhen( + register("chat", (player, _, message) => { + if (onCD) return; + executeCommand(getPlayerName(player), message.split(" "), "ac"); + }).setCriteria(`&r${playerRegex + colorRegex}: ${prefix + messageRegex}&r`), + () => Settings.partyCommands && Toggles.allCommands + ); + registerWhen( + register("chat", (player, message) => { + if (onCD) return; + executeCommand(getPlayerName(player), message.split(" "), "pc"); + }).setCriteria(`Party > ${playerRegex}: ${prefix + messageRegex}`), + () => Settings.partyCommands && Toggles.partyCommands + ); + registerWhen( + register("chat", (player, message) => { + if (onCD) return; + executeCommand(getGuildName(player), message.split(" "), "gc"); + }).setCriteria(`Guild > ${playerRegex}: ${prefix + messageRegex}`), + () => Settings.partyCommands && Toggles.guildCommands + ); + registerWhen( + register("chat", (player, message) => { + if (onCD) return; + executeCommand(getPlayerName(player), message.split(" "), "r"); + }).setCriteria(`From ${playerRegex}: ${prefix + messageRegex}`), + () => Settings.partyCommands && Toggles.dmCommands + ); }); - /** * ?w image rendering. */ @@ -305,58 +365,63 @@ let img = undefined; let imgUrl = undefined; const render = register("renderOverlay", () => { - if (img === undefined) { - Renderer.translate(0, 0, 999); - Renderer.drawString("Loading...", Client.getMouseX() + 9, Client.getMouseY() + 3, true); - return; - } - - const SCREEN_WIDTH = Renderer.screen.getWidth(); - const SCREEN_HEIGHT = Renderer.screen.getHeight(); - const imgWidth = img.getTextureWidth(); - const imgHeight = img.getTextureHeight(); - const ratio = (imgWidth / SCREEN_WIDTH > imgHeight / SCREEN_HEIGHT ? imgWidth / SCREEN_WIDTH : imgHeight / SCREEN_HEIGHT) / Toggles.wScale; - const width = imgWidth / ratio; - const height = imgHeight / ratio; - const x = Client.getMouseX() + width > SCREEN_WIDTH ? SCREEN_WIDTH - width : Client.getMouseX(); - const y = Client.getMouseY() - height < 0 ? 0 : Client.getMouseY() - height; - + if (img === undefined) { Renderer.translate(0, 0, 999); - img.draw(x, y, width, height); + Renderer.drawString("Loading...", Client.getMouseX() + 9, Client.getMouseY() + 3, true); + return; + } + + const SCREEN_WIDTH = Renderer.screen.getWidth(); + const SCREEN_HEIGHT = Renderer.screen.getHeight(); + const imgWidth = img.getTextureWidth(); + const imgHeight = img.getTextureHeight(); + const ratio = + (imgWidth / SCREEN_WIDTH > imgHeight / SCREEN_HEIGHT ? imgWidth / SCREEN_WIDTH : imgHeight / SCREEN_HEIGHT) / + Toggles.wScale; + const width = imgWidth / ratio; + const height = imgHeight / ratio; + const x = Client.getMouseX() + width > SCREEN_WIDTH ? SCREEN_WIDTH - width : Client.getMouseX(); + const y = Client.getMouseY() - height < 0 ? 0 : Client.getMouseY() - height; + + Renderer.translate(0, 0, 999); + img.draw(x, y, width, height); }).unregister(); const close = register("guiClosed", () => { - img = undefined; - imgUrl = undefined; - render.unregister(); - close.unregister() + img = undefined; + imgUrl = undefined; + render.unregister(); + close.unregister(); }).unregister(); -registerWhen(register("chatComponentHovered", (text) => { +registerWhen( + register("chatComponentHovered", (text) => { const hoverValue = text.getHoverValue().removeFormatting(); - + if (hoverValue === imgUrl || !hoverValue.startsWith("https://i.waifu.pics")) return; imgUrl = hoverValue; delay(() => { - try { - render.register(); - close.register(); - img = Image.fromUrl(hoverValue); - } catch (err) { - ChatLib.chat(`${LOGO + RED}Error: Unable to load image!`); - } + try { + render.register(); + close.register(); + img = Image.fromUrl(hoverValue); + } catch (err) { + ChatLib.chat(`${LOGO + RED}Error: Unable to load image!`); + } }, 1); -}), () => Toggles.wScale !== 0); + }), + () => Toggles.wScale !== 0 +); register("chat", (player, _, id, __, event) => { - cancel(event); - const link = `https://i.waifu.pics/${id.replace('@', '.')}`; - new TextComponent(`&${player}&f: ${link}`).setHoverValue(link).chat(); + cancel(event); + const link = `https://i.waifu.pics/${id.replace("@", ".")}`; + new TextComponent(`&${player}&f: ${link}`).setHoverValue(link).chat(); }).setCriteria("&${player}:${space}va-${id}-w${end}"); register("chat", (player, _, id, __, event) => { - cancel(event); - if (!Toggles.r18) return; - const link = `https://i.waifu.pics/${id.replace('@', '.')}`; - new TextComponent(`&${player}&f: ${link}`).setHoverValue(link).chat(); + cancel(event); + if (!Toggles.r18) return; + const link = `https://i.waifu.pics/${id.replace("@", ".")}`; + new TextComponent(`&${player}&f: ${link}`).setHoverValue(link).chat(); }).setCriteria("&${player}:${space}va-${id}-nw${end}"); diff --git a/features/rift/DDR.js b/features/rift/DDR.js index 15415be..01fe309 100644 --- a/features/rift/DDR.js +++ b/features/rift/DDR.js @@ -1,85 +1,85 @@ import location from "../../utils/Location"; -import Settings from "../../utils/Settings"; import { registerWhen } from "../../utils/RegisterTils"; - +import Settings from "../../utils/Settings"; /** * Variables used to represent complete dance and current dance move. */ last = ""; const DANCES = [ - "§bMove!§r", - "§cMove!§r", - "§dMove!§r", - "§aMove!§r", - "§5Sneak!§r", - "§eStand!§r", - "§5Sneak!§r", - "§eStand!§r", - "§5Sneak!§r", - "§eStand!§r", - "§5Sneak!§r", - "§eStand!§r", - "§5Sneak!§8 and §bJump!§r", - "§eStand!§8 and §bJump!§r", - "§5Sneak!§8 and §cDon't jump!§r", - "§eStand!§8 and §cDon't jump!§r", - "§5Sneak!§8 and §bJump!§r", - "§eStand!§8 and §bJump!§r", - "§5Sneak!§8 and §cDon't jump!§r", - "§eStand!§8 and §cDon't jump!§r", - "§5Sneak!§8 and §bJump!§r", - "§eStand!§8 and §bJump!§r", - "§5Sneak!§8 and §cDon't jump!§r", - "§eStand!§8 and §cDon't jump!§r", - "§5Sneak!§8 and §bJump!§r", - "§eStand!§8 and §bJump!§r", - "§5Sneak!§8 and §cDon't jump!§r", - "§eStand!§8 and §cDon't jump!§r", - "§5Sneak!§8 and §bJump!§r", - "§eStand!§8 and §bJump!§r", - "§5Sneak!§8 and §cDon't jump!§r", - "§eStand!§8 and §cDon't jump!§r", - "§5Sneak!§8 and §bJump!§8 and §aPunch!§r", - "§aPunch!§r", - "§eStand!§8 and §bJump!§8 and §aPunch!§r", - "§aPunch!§r", - "§5Sneak!§8 and §cDon't jump!§8 and §aPunch!§r", - "§aPunch!§r", - "§eStand!§8 and §cDon't jump!§8 and §aPunch!§r", - "§aPunch!§r", - "§5Sneak!§8 and §bJump!§8 and §aPunch!§r", - "§aPunch!§r", - "§eStand!§8 and §bJump!§8 and §aPunch!§r", - "§aPunch!§r", - "§5Sneak!§8 and §cDon't jump!§8 and §aPunch!§r", - "§aPunch!§r", - "§eStand!§8 and §cDon't jump!§8 and §aPunch!§r", - "§aPunch!§r", - "§5Sneak!§8 and §bJump!§8 and §aPunch!§r", - "§aPunch!§r", - "§eStand!§8 and §bJump!§8 and §aPunch!§r", - "§aPunch!§r", - "§5Sneak!§8 and §cDon't jump!§8 and §aPunch!§r", - "§aPunch!§r", - "§eStand!§8 and §cDon't jump!§8 and §aPunch!§r", - "§aPunch!§r", - "§5Sneak!§8 and §bJump!§8 and §aPunch!§r", - "§aPunch!§r", - "§eStand!§8 and §bJump!§8 and §aPunch!§r", - "§aPunch!§r", - "§5Sneak!§8 and §cDon't jump!§8 and §aPunch!§r", - "§aPunch!§r", - "§eStand!§8 and §cDon't jump!§8 and §aPunch!§r", - "§aPunch!§r", - "§6↑, ↑, ↓, ↓, ←, →, ←, →, B, A§r", + "§bMove!§r", + "§cMove!§r", + "§dMove!§r", + "§aMove!§r", + "§5Sneak!§r", + "§eStand!§r", + "§5Sneak!§r", + "§eStand!§r", + "§5Sneak!§r", + "§eStand!§r", + "§5Sneak!§r", + "§eStand!§r", + "§5Sneak!§8 and §bJump!§r", + "§eStand!§8 and §bJump!§r", + "§5Sneak!§8 and §cDon't jump!§r", + "§eStand!§8 and §cDon't jump!§r", + "§5Sneak!§8 and §bJump!§r", + "§eStand!§8 and §bJump!§r", + "§5Sneak!§8 and §cDon't jump!§r", + "§eStand!§8 and §cDon't jump!§r", + "§5Sneak!§8 and §bJump!§r", + "§eStand!§8 and §bJump!§r", + "§5Sneak!§8 and §cDon't jump!§r", + "§eStand!§8 and §cDon't jump!§r", + "§5Sneak!§8 and §bJump!§r", + "§eStand!§8 and §bJump!§r", + "§5Sneak!§8 and §cDon't jump!§r", + "§eStand!§8 and §cDon't jump!§r", + "§5Sneak!§8 and §bJump!§r", + "§eStand!§8 and §bJump!§r", + "§5Sneak!§8 and §cDon't jump!§r", + "§eStand!§8 and §cDon't jump!§r", + "§5Sneak!§8 and §bJump!§8 and §aPunch!§r", + "§aPunch!§r", + "§eStand!§8 and §bJump!§8 and §aPunch!§r", + "§aPunch!§r", + "§5Sneak!§8 and §cDon't jump!§8 and §aPunch!§r", + "§aPunch!§r", + "§eStand!§8 and §cDon't jump!§8 and §aPunch!§r", + "§aPunch!§r", + "§5Sneak!§8 and §bJump!§8 and §aPunch!§r", + "§aPunch!§r", + "§eStand!§8 and §bJump!§8 and §aPunch!§r", + "§aPunch!§r", + "§5Sneak!§8 and §cDon't jump!§8 and §aPunch!§r", + "§aPunch!§r", + "§eStand!§8 and §cDon't jump!§8 and §aPunch!§r", + "§aPunch!§r", + "§5Sneak!§8 and §bJump!§8 and §aPunch!§r", + "§aPunch!§r", + "§eStand!§8 and §bJump!§8 and §aPunch!§r", + "§aPunch!§r", + "§5Sneak!§8 and §cDon't jump!§8 and §aPunch!§r", + "§aPunch!§r", + "§eStand!§8 and §cDon't jump!§8 and §aPunch!§r", + "§aPunch!§r", + "§5Sneak!§8 and §bJump!§8 and §aPunch!§r", + "§aPunch!§r", + "§eStand!§8 and §bJump!§8 and §aPunch!§r", + "§aPunch!§r", + "§5Sneak!§8 and §cDon't jump!§8 and §aPunch!§r", + "§aPunch!§r", + "§eStand!§8 and §cDon't jump!§8 and §aPunch!§r", + "§aPunch!§r", + "§6↑, ↑, ↓, ↓, ←, →, ←, →, B, A§r", ]; let dancing = [...DANCES]; /** * Replaces Hypixel dance room subtitles with larger, permanent titles. */ -registerWhen(register("renderTitle", (title, subtitle, event) => { +registerWhen( + register("renderTitle", (title, subtitle, event) => { if (title === "§aIt's happening!§r" || title === "§aKeep it up!§r") cancel(event); if (!dancing.includes(subtitle) || subtitle === last) return; @@ -88,14 +88,28 @@ registerWhen(register("renderTitle", (title, subtitle, event) => { last = subtitle; print(subtitle); cancel(event); -}), () => location.getWorld() === "The Rift" && Settings.ddrHelper); + }), + () => location.getWorld() === "The Rift" && Settings.ddrHelper +); /** * Resets dance if player fails. */ -registerWhen(register("chat", () => { dancing = [...DANCES] }).setCriteria("You were${failure}!"), -() => location.getWorld() === "The Rift" && Settings.ddrHelper); -registerWhen(register("chat", () => { dancing = [...DANCES] }).setCriteria("You d${failure}!"), -() => location.getWorld() === "The Rift" && Settings.ddrHelper); -registerWhen(register("chat", () => { dancing = [...DANCES] }).setCriteria("You're ${failure}!"), -() => location.getWorld() === "The Rift" && Settings.ddrHelper); +registerWhen( + register("chat", () => { + dancing = [...DANCES]; + }).setCriteria("You were${failure}!"), + () => location.getWorld() === "The Rift" && Settings.ddrHelper +); +registerWhen( + register("chat", () => { + dancing = [...DANCES]; + }).setCriteria("You d${failure}!"), + () => location.getWorld() === "The Rift" && Settings.ddrHelper +); +registerWhen( + register("chat", () => { + dancing = [...DANCES]; + }).setCriteria("You're ${failure}!"), + () => location.getWorld() === "The Rift" && Settings.ddrHelper +); diff --git a/features/rift/EnigmaSouls.js b/features/rift/EnigmaSouls.js index 9abb724..e6ceb48 100644 --- a/features/rift/EnigmaSouls.js +++ b/features/rift/EnigmaSouls.js @@ -1,143 +1,150 @@ -import location from "../../utils/Location"; -import Settings from "../../utils/Settings"; import { AQUA, BOLD, DARK_GRAY, GOLD, GRAY, GREEN, LOGO, RED, YELLOW } from "../../utils/Constants"; -import { getClosest } from "../../utils/functions/find"; import { Json } from "../../utils/Json"; +import location from "../../utils/Location"; import { registerWhen } from "../../utils/RegisterTils"; +import Settings from "../../utils/Settings"; import Waypoint from "../../utils/Waypoint"; - +import { getClosest } from "../../utils/functions/find"; /** * Variables used to represent soul waypoints. */ -const soulWaypoints = new Waypoint([0.5, 0, 0.5], 1); // Purple Souls +const soulWaypoints = new Waypoint([0.5, 0, 0.5], 1); // Purple Souls const enigmaSouls = new Json("enigmaSouls.json", true); /** * Removes closest enigma soul to player once one is unlocked. */ -registerWhen(register("chat", () => { +registerWhen( + register("chat", () => { const souls = enigmaSouls.getData(); if (souls.length === 0) return; // Delete closest soul const closest = getClosest([Player.getX(), Player.getY(), Player.getZ()], souls); if (closest !== undefined && closest[1] < 10) souls.splice(souls.indexOf(closest[0]), 1); -}).setCriteria("SOUL! You unlocked an Enigma Soul!"), () => location.getWorld() === "The Rift"); + }).setCriteria("SOUL! You unlocked an Enigma Soul!"), + () => location.getWorld() === "The Rift" +); /** * Fail safe enigma soul remove in case player clicks on an unregistered soul. */ -registerWhen(register("chat", () => { +registerWhen( + register("chat", () => { const souls = enigmaSouls.getData(); if (souls.length === 0) return; // Delete duplicate soul const closest = getClosest([Player.getX(), Player.getY(), Player.getZ()], souls); if (closest !== undefined && closest[1] < 10) souls.splice(souls.indexOf(closest[0]), 1); -}).setCriteria("You have already found that Enigma Soul!"), () => location.getWorld() === "The Rift"); + }).setCriteria("You have already found that Enigma Soul!"), + () => location.getWorld() === "The Rift" +); /** * Updates enigma soul array closer than set threshold to player. */ -registerWhen(register("step", () => { +registerWhen( + register("step", () => { // Filters to closest souls soulWaypoints.clear(); - enigmaSouls.getData().forEach(enigma => { - const x = parseFloat(enigma[3]) + 1; - const y = parseFloat(enigma[4]); - const z = parseFloat(enigma[5]) + 1; - - if (Math.hypot(Player.getX() - x, Player.getZ() - z) < Settings.enigmaWaypoint) { - soulWaypoints.push([x, y, z]); - } + enigmaSouls.getData().forEach((enigma) => { + const x = parseFloat(enigma[3]) + 1; + const y = parseFloat(enigma[4]); + const z = parseFloat(enigma[5]) + 1; + + if (Math.hypot(Player.getX() - x, Player.getZ() - z) < Settings.enigmaWaypoint) { + soulWaypoints.push([x, y, z]); + } }); -}).setFps(1), () => location.getWorld() === "The Rift" && Settings.enigmaWaypoint !== 0); + }).setFps(1), + () => location.getWorld() === "The Rift" && Settings.enigmaWaypoint !== 0 +); /** * Updates the enigma soul array. - * + * * @param {String} command - The command to execute. * @param {String} name - The name of the enigma soul. */ export function updateEnigma(command, name) { - ChatLib.clearChat(5858); - - switch (command) { - case "clear": // Clear all waypoints - enigmaSouls.getData().length = 0; - ChatLib.chat(`${LOGO + GREEN}Successfully cleared out all Enigma Soul waypoints.`); - break; - case "delete": // Check if the name exists - const souls = enigmaSouls.getData(); - const soul = souls.find(soul => soul[0] === name); - if (soul === undefined) { - ChatLib.chat(`${LOGO + RED}Error: Could not find Enigma Soul "${name}".`); - return; - } - - // Delete the soul - souls.splice(souls.indexOf(soul), 1); - ChatLib.command("va enigma list", true); - ChatLib.chat(`${LOGO + GREEN}Successfully removed Enigma Soul "${name}".`); - break; - case "list": // List all souls - const message = new Message(`\n${LOGO + GOLD + BOLD}Enigma Souls:`).setChatLineId(5858); - enigmaSouls.getData().forEach(soul => { - // Get the soul data - const name = soul[0]; - const zone = soul[1]; - const x = soul[3]; - const y = soul[4]; - const z = soul[5]; - - // Split the description into rows - const words = soul[2].split(' '); - const rows = []; - let currentRow = ''; - words.forEach(word => { - if ((currentRow + word).length <= 50) { - currentRow += ' ' + word; - } else { - rows.push(currentRow); - currentRow = word; - } - }); - if (currentRow !== '') rows.push(currentRow); - - // Create the hover text - const desc = rows.join('\n ' + YELLOW); - const hover = -`${GOLD + name}: ${AQUA + zone} ${GRAY}(${x}, ${y}, ${z}) + ChatLib.clearChat(5858); + + switch (command) { + case "clear": // Clear all waypoints + enigmaSouls.getData().length = 0; + ChatLib.chat(`${LOGO + GREEN}Successfully cleared out all Enigma Soul waypoints.`); + break; + case "delete": // Check if the name exists + const souls = enigmaSouls.getData(); + const soul = souls.find((soul) => soul[0] === name); + if (soul === undefined) { + ChatLib.chat(`${LOGO + RED}Error: Could not find Enigma Soul "${name}".`); + return; + } + + // Delete the soul + souls.splice(souls.indexOf(soul), 1); + ChatLib.command("va enigma list", true); + ChatLib.chat(`${LOGO + GREEN}Successfully removed Enigma Soul "${name}".`); + break; + case "list": // List all souls + const message = new Message(`\n${LOGO + GOLD + BOLD}Enigma Souls:`).setChatLineId(5858); + enigmaSouls.getData().forEach((soul) => { + // Get the soul data + const name = soul[0]; + const zone = soul[1]; + const x = soul[3]; + const y = soul[4]; + const z = soul[5]; + + // Split the description into rows + const words = soul[2].split(" "); + const rows = []; + let currentRow = ""; + words.forEach((word) => { + if ((currentRow + word).length <= 50) { + currentRow += " " + word; + } else { + rows.push(currentRow); + currentRow = word; + } + }); + if (currentRow !== "") rows.push(currentRow); + + // Create the hover text + const desc = rows.join("\n " + YELLOW); + const hover = `${GOLD + name}: ${AQUA + zone} ${GRAY}(${x}, ${y}, ${z}) ${DARK_GRAY}Obtainment: ${YELLOW + desc} ${YELLOW}Click to remove ${AQUA + name + YELLOW} from the list.`; - // Add the text component - message.addTextComponent( - new TextComponent(`\n${DARK_GRAY}- ${AQUA}${name}: ${YELLOW + zone} ${GRAY}(${x}, ${y}, ${z})`) - .setHoverValue(hover) - .setClick("run_command", `/va enigma delete ${name}`) - ); - }); - message.chat(); - break; - case "pop": // Delete closest soul - const closest = getClosest([Player.getX(), Player.getY(), Player.getZ()], enigmaSouls.getData()); - if (closest !== undefined) enigmaSouls.getData().splice(enigmaSouls.getData().indexOf(closest[0]), 1); - ChatLib.chat(`${LOGO + GREEN}Successfully removed closest Enigma Soul.`); - break; - case "reset": // Reset the array - enigmaSouls.reset(); - ChatLib.chat(`${LOGO + GREEN}Successfully reset Enigma Soul waypoints.`); - break; - case "help": // Display help message - default: - if (command !== "help") ChatLib.chat(`${LOGO + RED}Error: Invalid argument "${command}"!\n`); - ChatLib.chat( -`${LOGO + GOLD + BOLD}Enigma Commands: + // Add the text component + message.addTextComponent( + new TextComponent(`\n${DARK_GRAY}- ${AQUA}${name}: ${YELLOW + zone} ${GRAY}(${x}, ${y}, ${z})`) + .setHoverValue(hover) + .setClick("run_command", `/va enigma delete ${name}`) + ); + }); + message.chat(); + break; + case "pop": // Delete closest soul + const closest = getClosest([Player.getX(), Player.getY(), Player.getZ()], enigmaSouls.getData()); + if (closest !== undefined) enigmaSouls.getData().splice(enigmaSouls.getData().indexOf(closest[0]), 1); + ChatLib.chat(`${LOGO + GREEN}Successfully removed closest Enigma Soul.`); + break; + case "reset": // Reset the array + enigmaSouls.reset(); + ChatLib.chat(`${LOGO + GREEN}Successfully reset Enigma Soul waypoints.`); + break; + case "help": // Display help message + default: + if (command !== "help") ChatLib.chat(`${LOGO + RED}Error: Invalid argument "${command}"!\n`); + ChatLib.chat( + `${LOGO + GOLD + BOLD}Enigma Commands: ${DARK_GRAY}- ${GOLD}Base: ${YELLOW}/va enigma ${DARK_GRAY}- ${GOLD}clear: ${YELLOW}Marks all Enigma Souls as complete. @@ -145,7 +152,8 @@ ${YELLOW}Click to remove ${AQUA + name + YELLOW} from the list.`; ${DARK_GRAY}- ${GOLD}list: ${YELLOW}List all missing Enigma Souls. ${DARK_GRAY}- ${GOLD}pop: ${YELLOW}Removes the closest Enigma Soul. ${DARK_GRAY}- ${GOLD}reset: ${YELLOW}Reset the Enigma Soul array. - ${DARK_GRAY}- ${GOLD}help: ${YELLOW}Displays this help message.`); - break; - } + ${DARK_GRAY}- ${GOLD}help: ${YELLOW}Displays this help message.` + ); + break; + } } diff --git a/features/rift/MontezumaSouls.js b/features/rift/MontezumaSouls.js index 55382c6..8de825d 100644 --- a/features/rift/MontezumaSouls.js +++ b/features/rift/MontezumaSouls.js @@ -1,128 +1,129 @@ -import location from "../../utils/Location"; -import Settings from "../../utils/Settings"; import { AQUA, BLUE, BOLD, DARK_GRAY, GOLD, GRAY, GREEN, LOGO, RED, YELLOW } from "../../utils/Constants"; -import { getClosest } from "../../utils/functions/find"; +import { Json } from "../../utils/Json"; +import location from "../../utils/Location"; import { registerWhen } from "../../utils/RegisterTils"; +import Settings from "../../utils/Settings"; import Waypoint from "../../utils/Waypoint"; -import { Json } from "../../utils/Json"; - +import { getClosest } from "../../utils/functions/find"; /** * Variables used to represent soul waypoints. */ -const soulWaypoints = new Waypoint([0, 0, 1]); // Blue Souls +const soulWaypoints = new Waypoint([0, 0, 1]); // Blue Souls const catSouls = new Json("catSouls.json", true); /** * Removes closest Montezuma soul piece when player finds one. */ -registerWhen(register("chat", () => { +registerWhen( + register("chat", () => { // Delete closest soul const souls = catSouls.getData(); const closest = getClosest([Player.getX(), Player.getY(), Player.getZ()], souls); - if (closest !== undefined) - souls.splice(souls.indexOf(closest[0]), 1); -}).setCriteria("You found a piece of Montezuma's soul!"), () => location.getWorld() === "The Rift"); + if (closest !== undefined) souls.splice(souls.indexOf(closest[0]), 1); + }).setCriteria("You found a piece of Montezuma's soul!"), + () => location.getWorld() === "The Rift" +); /** * Fail safe Montzuma soul piece remove in case player clicks on an unregistered soul. */ -registerWhen(register("chat", () => { +registerWhen( + register("chat", () => { // Delete duplicate soul const souls = catSouls.getData(); const closest = getClosest([Player.getX(), Player.getY(), Player.getZ()], souls); - if (closest[1] < 5) - souls.splice(souls.indexOf(closest[0]), 1); -}).setCriteria("You have already found this Montezuma soul piece!"), () => location.getWorld() === "The Rift"); + if (closest[1] < 5) souls.splice(souls.indexOf(closest[0]), 1); + }).setCriteria("You have already found this Montezuma soul piece!"), + () => location.getWorld() === "The Rift" +); /** * Updates enigma soul array closer than set threshold to player. */ -registerWhen(register("step", () => { +registerWhen( + register("step", () => { // Filters to closest souls soulWaypoints.clear(); const soul = catSouls.getData()[0]; if (soul === undefined) return; - soulWaypoints.push([ - soul[0], - parseFloat(soul[3]), - parseFloat(soul[4]), - parseFloat(soul[5]) - ]); -}).setFps(1), () => location.getWorld() === "The Rift" && Settings.catWaypoint); + soulWaypoints.push([soul[0], parseFloat(soul[3]), parseFloat(soul[4]), parseFloat(soul[5])]); + }).setFps(1), + () => location.getWorld() === "The Rift" && Settings.catWaypoint +); /** * Updates the fairy soul array. - * + * * @param {String} command - The command to execute. * @param {Number} index - The index of the soul to remove. */ export function updateCat(command, index) { - ChatLib.clearChat(5858); - const souls = catSouls.getData(); + ChatLib.clearChat(5858); + const souls = catSouls.getData(); - switch (command) { - case "clear": // Clear all waypoints - souls.length = 0; - ChatLib.chat(`${LOGO + GREEN}Successfully cleared out all Montezuma Soul waypoints.`); - break; - case "delete": // Delete the specified soul - if (souls[index] === undefined) { - ChatLib.chat(`${LOGO + RED}Error: Invalid Montezuma Soul index "${index}"!`); - return; - } - souls.splice(index, 1); - ChatLib.command(`va cat list`, true); - ChatLib.chat(`${LOGO + GREEN}Successfully removed Montezuma Soul.`); - break; - case "list": // List all missing fairy souls - const message = new Message(`\n${LOGO + GOLD + BOLD}Montezuma Souls:`).setChatLineId(5858); - souls.forEach((soul, i) => { - // Get the soul data - const name = soul[0]; - const zone = soul[1]; - const desc = soul[2]; - const x = soul[3]; - const y = soul[4]; - const z = soul[5]; - const hover = -`${GOLD + name}: ${AQUA + zone} ${GRAY}(${x}, ${y}, ${z}) + switch (command) { + case "clear": // Clear all waypoints + souls.length = 0; + ChatLib.chat(`${LOGO + GREEN}Successfully cleared out all Montezuma Soul waypoints.`); + break; + case "delete": // Delete the specified soul + if (souls[index] === undefined) { + ChatLib.chat(`${LOGO + RED}Error: Invalid Montezuma Soul index "${index}"!`); + return; + } + souls.splice(index, 1); + ChatLib.command(`va cat list`, true); + ChatLib.chat(`${LOGO + GREEN}Successfully removed Montezuma Soul.`); + break; + case "list": // List all missing fairy souls + const message = new Message(`\n${LOGO + GOLD + BOLD}Montezuma Souls:`).setChatLineId(5858); + souls.forEach((soul, i) => { + // Get the soul data + const name = soul[0]; + const zone = soul[1]; + const desc = soul[2]; + const x = soul[3]; + const y = soul[4]; + const z = soul[5]; + const hover = `${GOLD + name}: ${AQUA + zone} ${GRAY}(${x}, ${y}, ${z}) ${DARK_GRAY}Obtainment: ${YELLOW + desc} ${YELLOW}Click to remove ${BLUE}Montezuma Soul${YELLOW}.`; - // Add the text component - message.addTextComponent( - new TextComponent(`\n${DARK_GRAY}- ${AQUA}${zone}: ${YELLOW}(${x}, ${y}, ${z})`) - .setHoverValue(hover) - .setClick("run_command", `/va cat delete ${i}`) - ); - }); - message.chat(); - break; - case "shift": // Delete closest soul - souls.shift(); - ChatLib.chat(`${LOGO + GREEN}Successfully removed closest Montezuma Soul.`); - break; - case "reset": // Reset the array - catSouls.reset(); - ChatLib.chat(`${LOGO + GREEN}Successfully reset Montezuma Soul waypoints.`); - break; - case "help": // Display help message - default: - if (command !== "help") ChatLib.chat(`${LOGO + RED}Error: Invalid argument "${command}"!\n`); - ChatLib.chat( -`${LOGO + GOLD + BOLD}Fairy Soul Commands: + // Add the text component + message.addTextComponent( + new TextComponent(`\n${DARK_GRAY}- ${AQUA}${zone}: ${YELLOW}(${x}, ${y}, ${z})`) + .setHoverValue(hover) + .setClick("run_command", `/va cat delete ${i}`) + ); + }); + message.chat(); + break; + case "shift": // Delete closest soul + souls.shift(); + ChatLib.chat(`${LOGO + GREEN}Successfully removed closest Montezuma Soul.`); + break; + case "reset": // Reset the array + catSouls.reset(); + ChatLib.chat(`${LOGO + GREEN}Successfully reset Montezuma Soul waypoints.`); + break; + case "help": // Display help message + default: + if (command !== "help") ChatLib.chat(`${LOGO + RED}Error: Invalid argument "${command}"!\n`); + ChatLib.chat( + `${LOGO + GOLD + BOLD}Fairy Soul Commands: ${DARK_GRAY}- ${GOLD}Base: ${YELLOW}/va fairy ${DARK_GRAY}- ${GOLD}clear: ${YELLOW}Marks all Montezuma Souls as complete. ${DARK_GRAY}- ${GOLD}list: ${YELLOW}List all missing Montezuma Souls. ${DARK_GRAY}- ${GOLD}shift: ${YELLOW}Removes the next Montezuma Soul. ${DARK_GRAY}- ${GOLD}reset: ${YELLOW}Reset the Montezuma Soul array. - ${DARK_GRAY}- ${GOLD}help: ${YELLOW}Displays this help message.`); - break; - } + ${DARK_GRAY}- ${GOLD}help: ${YELLOW}Displays this help message.` + ); + break; + } } diff --git a/features/rift/VampireSlayer.js b/features/rift/VampireSlayer.js index a8d9541..3eff149 100644 --- a/features/rift/VampireSlayer.js +++ b/features/rift/VampireSlayer.js @@ -1,19 +1,17 @@ -import location from "../../utils/Location"; -import party from "../../utils/Party"; -import Settings from "../../utils/Settings"; import { AQUA, BOLD, DARK_AQUA, DARK_PURPLE, EntityArmorStand, GOLD, PLAYER_CLASS, SMA } from "../../utils/Constants"; +import { data } from "../../utils/Data"; +import location from "../../utils/Location"; import { Overlay } from "../../utils/Overlay"; +import party from "../../utils/Party"; import { registerWhen } from "../../utils/RegisterTils"; -import { data } from "../../utils/Data"; -import { getSlayerBoss } from "../combat/SlayerDetect"; +import Settings from "../../utils/Settings"; import Waypoint from "../../utils/Waypoint"; - +import { getSlayerBoss } from "../combat/SlayerDetect"; /** * Variables used to track and display vampire boss attacks. */ -const vampireExample = -`${DARK_PURPLE + BOLD}MANIA: ${AQUA}Dracule +const vampireExample = `${DARK_PURPLE + BOLD}MANIA: ${AQUA}Dracule ${GOLD + BOLD}TWINCLAWS: ${AQUA}Mihawk ${DARK_AQUA + BOLD}ICHOR: ${AQUA}3,590,000,000`; const vampireOverlay = new Overlay("vampireAttack", data.BL, "moveVamp", vampireExample, ["The Rift"]); @@ -26,77 +24,80 @@ let inMania = false; /** * Tracks player boss on spawn and updates vampire attack overlay every tick. */ -registerWhen(register("tick", () => { +registerWhen( + register("tick", () => { let vampireMessage = ""; vampireOverlay.setMessage(""); if (!getSlayerBoss()) { - bossUUID = 0; - mania = 0; - return; + bossUUID = 0; + mania = 0; + return; } const player = Player.asPlayerMP().getEntity(); const stands = World.getWorld() - .func_72839_b(player, player.func_174813_aQ().func_72314_b(16, 16, 16)) - .filter(entity => entity instanceof EntityArmorStand); + .func_72839_b(player, player.func_174813_aQ().func_72314_b(16, 16, 16)) + .filter((entity) => entity instanceof EntityArmorStand); // Boss Nametag Shit if (!bossUUID) { - const spawn = stands.find(stand => stand.func_95999_t().includes('03:59')); - if (spawn === undefined) return; - bossUUID = spawn.persistentID; + const spawn = stands.find((stand) => stand.func_95999_t().includes("03:59")); + if (spawn === undefined) return; + bossUUID = spawn.persistentID; } else { - const boss = stands.find(stand => stand.persistentID === bossUUID); - if (boss === undefined) return; - const name = boss.func_95999_t().split(" "); - - // Mania Detect - const maniaIndex = name.indexOf("§5§lMANIA"); - if (maniaIndex !== -1) { - vampireMessage += `${name[maniaIndex]}: ${name[maniaIndex + 1]}\n`; - if (!inMania) { - mania++; - inMania = true; - - const pX = Math.round(Player.getX()); - const PY = Math.round(Player.getY()); - const PZ = Math.round(Player.getZ()); - if (Settings.announceMania === 1) { - const id = (Math.random() + 1).toString(36).substring(6); - ChatLib.command(`ac x: ${pX}, y: ${PY}, z: ${PZ} | MANIA: ${mania}! @${id}`); - } else if (party.getIn() && Settings.announceMania === 2) - ChatLib.command(`pc x: ${pX}, y: ${PY}, z: ${PZ} | MANIA: ${mania}!`); - } - } else inMania = false; - - // Twinclaw Detect - const clawIndex = name.indexOf("§6§lTWINCLAWS"); - if (clawIndex !== -1) vampireMessage += `${name[clawIndex]}: ${name[clawIndex + 1]}\n`; - - // Ichor Detect - const ichorIndex = name.indexOf("§3§lICHOR"); - if (ichorIndex !== -1) ichorSpawn = true; - - vampireOverlay.setMessage(vampireMessage); + const boss = stands.find((stand) => stand.persistentID === bossUUID); + if (boss === undefined) return; + const name = boss.func_95999_t().split(" "); + + // Mania Detect + const maniaIndex = name.indexOf("§5§lMANIA"); + if (maniaIndex !== -1) { + vampireMessage += `${name[maniaIndex]}: ${name[maniaIndex + 1]}\n`; + if (!inMania) { + mania++; + inMania = true; + + const pX = Math.round(Player.getX()); + const PY = Math.round(Player.getY()); + const PZ = Math.round(Player.getZ()); + if (Settings.announceMania === 1) { + const id = (Math.random() + 1).toString(36).substring(6); + ChatLib.command(`ac x: ${pX}, y: ${PY}, z: ${PZ} | MANIA: ${mania}! @${id}`); + } else if (party.getIn() && Settings.announceMania === 2) + ChatLib.command(`pc x: ${pX}, y: ${PY}, z: ${PZ} | MANIA: ${mania}!`); + } + } else inMania = false; + + // Twinclaw Detect + const clawIndex = name.indexOf("§6§lTWINCLAWS"); + if (clawIndex !== -1) vampireMessage += `${name[clawIndex]}: ${name[clawIndex + 1]}\n`; + + // Ichor Detect + const ichorIndex = name.indexOf("§3§lICHOR"); + if (ichorIndex !== -1) ichorSpawn = true; + + vampireOverlay.setMessage(vampireMessage); } // Ichor Nametag Shit if (ichorSpawn) { - if (ichorUUID !== 0) { - const ichor = stands.find(stand => stand.persistentID === ichorUUID); - if (ichor === undefined) { - ichorSpawn = false; - ichorUUID = 0; - return; - } - vampireOverlay.setMessage(vampireMessage + `${DARK_AQUA + BOLD}ICHOR: ${ichor.func_95999_t()}\n`); - } else { - const ichor = stands.find(stand => stand.func_95999_t().includes('24.')); - if (ichor === undefined) return; - ichorUUID = ichor.persistentID; + if (ichorUUID !== 0) { + const ichor = stands.find((stand) => stand.persistentID === ichorUUID); + if (ichor === undefined) { + ichorSpawn = false; + ichorUUID = 0; + return; } + vampireOverlay.setMessage(vampireMessage + `${DARK_AQUA + BOLD}ICHOR: ${ichor.func_95999_t()}\n`); + } else { + const ichor = stands.find((stand) => stand.func_95999_t().includes("24.")); + if (ichor === undefined) return; + ichorUUID = ichor.persistentID; + } } -}), () => location.getWorld() === "The Rift" && (Settings.vampireAttack || Settings.announceMania !== 0)); + }), + () => location.getWorld() === "The Rift" && (Settings.vampireAttack || Settings.announceMania !== 0) +); /** * Highlights vampire bosses with steakable HP. @@ -105,40 +106,52 @@ const VAMP_HP = new Set([625, 1100, 1800, 2400, 3000]); const draculaWaypoints = new Waypoint([1, 0, 0], 2, true, true, false); const vampWaypoints = new Waypoint([1, 0, 0], 2, true, true, false); -registerWhen(register("step", () => { +registerWhen( + register("step", () => { draculaWaypoints.clear(); vampWaypoints.clear(); - World.getAllEntitiesOfType(PLAYER_CLASS).forEach(mob => { - const entity = mob.getEntity(); - const max = entity.func_110148_a(SMA.field_111267_a).func_111125_b(); + World.getAllEntitiesOfType(PLAYER_CLASS).forEach((mob) => { + const entity = mob.getEntity(); + const max = entity.func_110148_a(SMA.field_111267_a).func_111125_b(); - if (max > 210 && entity.func_110143_aJ() / max <= 0.2) vampWaypoints.push([RED + "Dracule", mob]); - else if (VAMP_HP.has(max)) draculaWaypoints.push([RED + "Mihawk", mob]); + if (max > 210 && entity.func_110143_aJ() / max <= 0.2) vampWaypoints.push([RED + "Dracule", mob]); + else if (VAMP_HP.has(max)) draculaWaypoints.push([RED + "Mihawk", mob]); }); -}).setFps(2), () => location.getWorld() === "The Rift" && Settings.vampireHitbox); - + }).setFps(2), + () => location.getWorld() === "The Rift" && Settings.vampireHitbox +); /** * Variables used to reprsent and track the 6 effigies. */ const EFFIGIES = [ - ["1st Effigy", 151, 73, 96], ["2nd Effigy", 194, 87, 120], ["3rd Effigy", 236, 104, 148], - ["4th Effigy", 294, 90, 135], ["5th Effigy", 263, 93, 95], ["6th Effigy", 241, 123, 119] + ["1st Effigy", 151, 73, 96], + ["2nd Effigy", 194, 87, 120], + ["3rd Effigy", 236, 104, 148], + ["4th Effigy", 294, 90, 135], + ["5th Effigy", 263, 93, 95], + ["6th Effigy", 241, 123, 119], ]; -const missingEffigies = new Waypoint([0.75, 0.75, 0.75]); // Silver effigies +const missingEffigies = new Waypoint([0.75, 0.75, 0.75]); // Silver effigies /** * Tracks missing effigies and makes a waypoint to them. */ -registerWhen(register("step", () => { +registerWhen( + register("step", () => { missingEffigies.clear(); let effigies = Scoreboard?.getLines()?.find((line) => line.getName().includes("Effigies")); if (effigies === undefined) return; - effigies = effigies.getName().replace(/[^§7⧯]/g,'').split("§"); + effigies = effigies + .getName() + .replace(/[^§7⧯]/g, "") + .split("§"); effigies.shift(); - effigies.forEach((effigy, i) => { - if (effigy.includes('7')) missingEffigies.push(EFFIGIES[i]); + effigies.forEach((effigy, i) => { + if (effigy.includes("7")) missingEffigies.push(EFFIGIES[i]); }); -}).setFps(1), () => location.getWorld() === "The Rift" && Settings.effigyWaypoint); + }).setFps(1), + () => location.getWorld() === "The Rift" && Settings.effigyWaypoint +); diff --git a/index.js b/index.js index e76f39d..498b976 100644 --- a/index.js +++ b/index.js @@ -1,21 +1,36 @@ register("command", () => { - ChatLib.chat( -`\n§6§lVolcDebug: + ChatLib.chat( + `\n§6§lVolcDebug: §eCT Version: §7v${ChatTriggers.MODVERSION} §eVolcAddons §7v${JSON.parse(FileLib.read("VolcAddons", "metadata.json")).version} -§8Please note that the mod may not work if CT is not v2.2.0`); +§8Please note that the mod may not work if CT is not v2.2.0` + ); }).setName("VATest"); - // Utility Modules -import "./utils/Launch"; -import "./utils/DevTils"; -import Settings from "./utils/Settings"; -import Toggles from "./utils/Toggles"; -import { AQUA, BOLD, DARK_AQUA, DARK_GRAY, DARK_RED, GOLD, GRAY, GREEN, LOGO, RED, RESET, UNDERLINE, WHITE, YELLOW } from "./utils/Constants"; +import { + AQUA, + BOLD, + DARK_AQUA, + DARK_GRAY, + DARK_RED, + GOLD, + GRAY, + GREEN, + LOGO, + RED, + RESET, + UNDERLINE, + WHITE, + YELLOW, +} from "./utils/Constants"; import { data, resetGUI } from "./utils/Data"; +import "./utils/DevTils"; +import "./utils/Launch"; import { updateList } from "./utils/ListTils"; import { openGUI } from "./utils/Overlay"; +import Settings from "./utils/Settings"; +import Toggles from "./utils/Toggles"; import { getLatestReleaseVersion } from "./utils/UpdateTils"; // General Features @@ -122,11 +137,10 @@ import { updateEnigma } from "./features/rift/EnigmaSouls"; import { updateCat } from "./features/rift/MontezumaSouls"; import "./features/rift/VampireSlayer"; - // HELP - Display help message for available commands function getHelp() { - ChatLib.chat( -`\n${GOLD + BOLD + UNDERLINE}VolcAddons v${JSON.parse(FileLib.read("VolcAddons", "metadata.json")).version + RESET} + ChatLib.chat( + `\n${GOLD + BOLD + UNDERLINE}VolcAddons v${JSON.parse(FileLib.read("VolcAddons", "metadata.json")).version + RESET} ${DARK_AQUA + BOLD}GENERAL COMMANDS:${RESET} ${AQUA + BOLD}Settings: ${WHITE}/va ${GRAY}<${WHITE}gui, settings, toggles, version, help${GRAY}> @@ -139,78 +153,81 @@ ${DARK_AQUA + BOLD}GENERAL COMMANDS:${RESET} ${DARK_AQUA + BOLD}GENERAL FEATURES:${RESET} ${AQUA + BOLD}Status Commands: ${WHITE}/va ${GRAY}<${WHITE}ping, fps, tps, yaw, pitch, xyz${GRAY}> ${AQUA + BOLD}Stats Commands: ${WHITE}/va ${GRAY}<${WHITE}pet, stats, pt, sf${GRAY}> - ${AQUA + BOLD}Party Commands: ${WHITE}Refer to '/va toggles'`); + ${AQUA + BOLD}Party Commands: ${WHITE}Refer to '/va toggles'` + ); } // `viewrecipe` GUI Button const recipeKey = new KeyBind("View Recipe", data.recipeKey, "./VolcAddons.xdd"); -register("gameUnload", () => { data.recipeKey = recipeKey.getKeyCode() }).setPriority(Priority.HIGHEST); +register("gameUnload", () => { + data.recipeKey = recipeKey.getKeyCode(); +}).setPriority(Priority.HIGHEST); register("guiKey", (_, keyCode, gui) => { - if (keyCode === recipeKey.getKeyCode()) { - // Check if hovering valid slot - const slot = gui?.getSlotUnderMouse()?.field_75222_d; - if (slot === undefined) return; - - // Check if item is null - const item = Player.getContainer().getItems()[slot]; - if (item === null) { - ChatLib.chat(`${LOGO + RED}Cannot viewrecipe of nothing.`); - return; - } + if (keyCode === recipeKey.getKeyCode()) { + // Check if hovering valid slot + const slot = gui?.getSlotUnderMouse()?.field_75222_d; + if (slot === undefined) return; - // Viewrecipe using item NBT ID - const id = item.getNBT().getCompoundTag("tag").getCompoundTag("ExtraAttributes").getString("id"); - ChatLib.command(`viewrecipe ${id}`); + // Check if item is null + const item = Player.getContainer().getItems()[slot]; + if (item === null) { + ChatLib.chat(`${LOGO + RED}Cannot viewrecipe of nothing.`); + return; } + + // Viewrecipe using item NBT ID + const id = item.getNBT().getCompoundTag("tag").getCompoundTag("ExtraAttributes").getString("id"); + ChatLib.command(`viewrecipe ${id}`); + } }); /** * Open settings GUI. */ function openSettings() { - try { - Settings.openGUI(); - } catch (err) { - ChatLib.chat(`${LOGO + RED}Error opening Settings... Please run '/ct reload' to fix!`); - register("gameUnload", () => { - FileLib.delete("VolcAddons", "config.toml"); - }).setPriority(Priority.LOWEST); - } + try { + Settings.openGUI(); + } catch (err) { + ChatLib.chat(`${LOGO + RED}Error opening Settings... Please run '/ct reload' to fix!`); + register("gameUnload", () => { + FileLib.delete("VolcAddons", "config.toml"); + }).setPriority(Priority.LOWEST); + } } // /va ...args -register ("command", (...args) => { - if (args === undefined) { - openSettings(); - return; - } +register("command", (...args) => { + if (args === undefined) { + openSettings(); + return; + } - // Parsing command and executing appropriate actions - const command = args[0] === undefined ? undefined : args[0].toLowerCase(); - const soleCommand = command.replace(/s$/, ''); - switch (soleCommand) { - // Settings - case undefined: - case "setting": - openSettings(); - break; - case "toggle": - case "control": - Toggles.openGUI(); - break; - case "dev": - data.devMode = !data.devMode; - const color = data.devMode ? GREEN : RED; - ChatLib.chat(`${LOGO + color}Developer mode is now ${data.devMode ? "enabled" : "disabled"}!`); - break; - // Help - case "help": - getHelp(); - break; - case "list": - ChatLib.chat( -`\n${LOGO + GOLD + BOLD} Lists: + // Parsing command and executing appropriate actions + const command = args[0] === undefined ? undefined : args[0].toLowerCase(); + const soleCommand = command.replace(/s$/, ""); + switch (soleCommand) { + // Settings + case undefined: + case "setting": + openSettings(); + break; + case "toggle": + case "control": + Toggles.openGUI(); + break; + case "dev": + data.devMode = !data.devMode; + const color = data.devMode ? GREEN : RED; + ChatLib.chat(`${LOGO + color}Developer mode is now ${data.devMode ? "enabled" : "disabled"}!`); + break; + // Help + case "help": + getHelp(); + break; + case "list": + ChatLib.chat( + `\n${LOGO + GOLD + BOLD} Lists: ${DARK_GRAY}- ${GOLD + BOLD}cd: ${YELLOW}cooldown-list ${DARK_GRAY}- ${GOLD + BOLD}wl: ${YELLOW}white-list ${DARK_GRAY}- ${GOLD + BOLD}bl: ${YELLOW}black-list @@ -221,225 +238,284 @@ ${DARK_GRAY}- ${GOLD + BOLD}sl: ${YELLOW}spam-list ${DARK_GRAY}- ${GOLD + BOLD}il: ${YELLOW}ignore-list ${DARK_GRAY}- ${GOLD + BOLD}wgl: ${YELLOW}widget-list ${DARK_GRAY}- ${GOLD + BOLD}pl: ${YELLOW}prefix-list -${DARK_GRAY}- ${GOLD + BOLD}rl: ${YELLOW}rabbit-list`); - break; - // Update - case "update": - case "version": - getLatestReleaseVersion(); - break; - // Contract - case "contract": - const Desktop = Java.type('java.awt.Desktop'); - const File = Java.type("java.io.File"); - Desktop.getDesktop().open(new File(Config.modulesFolder + "/VolcAddons/data/contract.txt")); - ChatLib.chat(`${LOGO + RED}My wealth and treasure? If you want it, I'll let you have it! Look for it! I left it all at that place!`); - break; - case "wdr": - case "sin": - if (!FileLib.read("./VolcAddons/Data", "contract.txt").split("\n")[51]?.includes(Player.getName())) { - ChatLib.chat(`${LOGO + RED}The contract, signed it must be. Access granted, for you to see. ${DARK_GRAY}/va contract`); - break; - } - - data.vision = !data.vision; - if (data.vision) ChatLib.chat(`${LOGO + GREEN}The white eye has been activated.`); - else ChatLib.chat(`${LOGO + RED}See no evil, hear no evil, speak no evil...`); - break; - // Move GUI - case "gui": - if (args[1] === "reset") { - resetGUI(); - ChatLib.chat(`${GREEN}Successfully reset ALL gui location settings!`); - ChatLib.command("ct load", true); - } else openGUI(); - break; - // Buttons - case "button": - buttonCommands(args); - break; - // Container Preview - case "preview": - previewCommands(args); - break; - // Slot Binding - case "slot": - case "bind": - slotCommands(args); - break; - // Send coords - case "coord": - case "xyz": - const randID = '@' + (Math.random() + 1).toString(36).substring(5); - ChatLib.say(`x: ${Math.round(Player.getX())}, y: ${Math.round(Player.getY())}, z: ${Math.round(Player.getZ())} ${randID}`); - break; - // Networth - case "networth": - case "nw": - getNetworth(args[1] || Player.getName(), args[2]); - break; - // Attribute Pricing - case "attribute": - getAttributes(args); - break; - // List Controls - case "whitelist": - case "white": - case "wl": - updateList(args, "whitelist"); - break; - case "blacklist": - case "black": - case "bl": - updateList(args, "blacklist"); - break; - case "emotelist": - case "emote": - case "el": - updateList(args, "emotelist"); - break; - case "cdlist": - case "cdl": - case "cooldown": - case "cd": - updateList(args, "cdlist"); - break; - case "moblist": - case "mob": - case "ml": - updateList(args, "moblist"); - break; - case "colorlist": - case "color": - case "cl": - updateList(args, "colorlist"); - break; - case "dianalist": - case "diana": - case "dl": - updateList(args, "dianalist"); - break; - case "valuelist": - case "value": - case "vl": - updateList(args, "valuelist"); - break; - case "spamlist": - case "spam": - case "sl": - updateList(args, "spamlist"); - break; - case "ignorelist": - case "ignore": - case "il": - updateList(args, "ignorelist"); - break; - case "attributelist": - case "al": - updateList(args, "attributelist"); - break; - case "widgetlist": - case "wgl": - updateList(args, "widgetlist"); - break; - case "prefixlist": - case "pl": - updateList(args, "prefixlist"); - break; - case "rabbitlist": - case "rabbit": - case "rl": - printRabbits(args[1], args[2]); - break; - // Kuudra Splits - case "split": - getSplits(args); - break; - // User Waypoints - case "waypoint": - case "wp": - createWaypoint(args); - break; - case "npc": - updateSBW("NPC", args[1], args.slice(2).join(' ')); - break; - case "location": - case "zone": - updateSBW(args[0], args[1], args.slice(2).join(' ')); - break; - // Bazaar Calculations - case "calculate": - case "calc": - try { - const MINION_ARGS = new Set(["hypergolic", "hg", "inferno", "gabagool", "gaba", "vampire", "vamp"]); - switch(args[1]) { - case "composter": - case "compost": - calcCompost(args); - break; - case "gdrag": - calcGdrag(isNaN(args[2]) ? 100 : args[2]); - break; - case "tabasco": - calcTabasco(args); - break; - default: - if (MINION_ARGS.has(args[1])) calcMinions(args); - else { - ChatLib.chat(`\n${LOGO + RED}Error: Invalid argument "${args[1]}"!`); - ChatLib.chat(`${LOGO + RED}Please input as: ${WHITE}/va calc ${GRAY}<${WHITE}gdrag, hg, inferno, gaba, tabasco, vampire, compost${GRAY}>`); - } - break; - } - } catch (err) { ChatLib.chat(LOGO + DARK_RED + (err.cause ?? err)) } - break; - // Configure Fairy souls - case "fairy": - updateFairy(args[1], args[2]); - break; - // Configure Enigma souls - case "enigma": - updateEnigma(args[1], args.splice(2).join(' ')); - break; - // Configure Montezuma souls - case "montezuma": - case "mont": - case "cat": - updateCat(args[1], args[2]); - break; - // Configure egg waypoints - case "egg": - updateEggs(args[1], args[2]); - break; - // Party Commands and Else Case - default: - args = args.map(w => w.toLowerCase()); - // Other args to check - const PARTY_COMMANDS = new Set([ - "cringe", "gay", "racist", "femboy", "trans", "transphobic", "dice", "roll", "coin", "flip", "coinflip", - "cf", "8ball", "rps", "waifu", "w", "women", "neko", "shinobu", "megumin", "bully", "cuddle", "cry", "hug", - "awoo", "kiss", "lick", "pat", "smug", "bonk", "yeet", "blush", "smile", "wave", "highfive", "handhold", "nom", - "bite", "glomp", "slap", "kill", "kick", "happy", "wink", "poke", "dance", "cringe" - ]); - const INSTANCES = new Set(["f", "m", "t"]); - const STATUS_ARGS = new Set(["ping", "tps", "fps", "cps", "yaw", "pitch", "dir", "direction", "day"]); - const STAT_ARGS = new Set(["pet", "stats", "soulflow", "sf", "playtime", "pt", "legion"]); +${DARK_GRAY}- ${GOLD + BOLD}rl: ${YELLOW}rabbit-list` + ); + break; + // Update + case "update": + case "version": + getLatestReleaseVersion(); + break; + // Contract + case "contract": + const Desktop = Java.type("java.awt.Desktop"); + const File = Java.type("java.io.File"); + Desktop.getDesktop().open(new File(Config.modulesFolder + "/VolcAddons/data/contract.txt")); + ChatLib.chat( + `${ + LOGO + RED + }My wealth and treasure? If you want it, I'll let you have it! Look for it! I left it all at that place!` + ); + break; + case "wdr": + case "sin": + if (!FileLib.read("./VolcAddons/Data", "contract.txt").split("\n")[51]?.includes(Player.getName())) { + ChatLib.chat( + `${LOGO + RED}The contract, signed it must be. Access granted, for you to see. ${DARK_GRAY}/va contract` + ); + break; + } - if (PARTY_COMMANDS.has(command) || (INSTANCES.has(command[0]) && !isNaN(command[1]))) executeCommand(Player.getName(), args, false); - else if (STATUS_ARGS.has(command)) getStatus(command); - else if (STAT_ARGS.has(command)) getStat(command); + data.vision = !data.vision; + if (data.vision) ChatLib.chat(`${LOGO + GREEN}The white eye has been activated.`); + else ChatLib.chat(`${LOGO + RED}See no evil, hear no evil, speak no evil...`); + break; + // Move GUI + case "gui": + if (args[1] === "reset") { + resetGUI(); + ChatLib.chat(`${GREEN}Successfully reset ALL gui location settings!`); + ChatLib.command("ct load", true); + } else openGUI(); + break; + // Buttons + case "button": + buttonCommands(args); + break; + // Container Preview + case "preview": + previewCommands(args); + break; + // Slot Binding + case "slot": + case "bind": + slotCommands(args); + break; + // Send coords + case "coord": + case "xyz": + const randID = "@" + (Math.random() + 1).toString(36).substring(5); + ChatLib.say( + `x: ${Math.round(Player.getX())}, y: ${Math.round(Player.getY())}, z: ${Math.round(Player.getZ())} ${randID}` + ); + break; + // Networth + case "networth": + case "nw": + getNetworth(args[1] || Player.getName(), args[2]); + break; + // Attribute Pricing + case "attribute": + getAttributes(args); + break; + // List Controls + case "whitelist": + case "white": + case "wl": + updateList(args, "whitelist"); + break; + case "blacklist": + case "black": + case "bl": + updateList(args, "blacklist"); + break; + case "emotelist": + case "emote": + case "el": + updateList(args, "emotelist"); + break; + case "cdlist": + case "cdl": + case "cooldown": + case "cd": + updateList(args, "cdlist"); + break; + case "moblist": + case "mob": + case "ml": + updateList(args, "moblist"); + break; + case "colorlist": + case "color": + case "cl": + updateList(args, "colorlist"); + break; + case "dianalist": + case "diana": + case "dl": + updateList(args, "dianalist"); + break; + case "valuelist": + case "value": + case "vl": + updateList(args, "valuelist"); + break; + case "spamlist": + case "spam": + case "sl": + updateList(args, "spamlist"); + break; + case "ignorelist": + case "ignore": + case "il": + updateList(args, "ignorelist"); + break; + case "attributelist": + case "al": + updateList(args, "attributelist"); + break; + case "widgetlist": + case "wgl": + updateList(args, "widgetlist"); + break; + case "prefixlist": + case "pl": + updateList(args, "prefixlist"); + break; + case "rabbitlist": + case "rabbit": + case "rl": + printRabbits(args[1], args[2]); + break; + // Kuudra Splits + case "split": + getSplits(args); + break; + // User Waypoints + case "waypoint": + case "wp": + createWaypoint(args); + break; + case "npc": + updateSBW("NPC", args[1], args.slice(2).join(" ")); + break; + case "location": + case "zone": + updateSBW(args[0], args[1], args.slice(2).join(" ")); + break; + // Bazaar Calculations + case "calculate": + case "calc": + try { + const MINION_ARGS = new Set(["hypergolic", "hg", "inferno", "gabagool", "gaba", "vampire", "vamp"]); + switch (args[1]) { + case "composter": + case "compost": + calcCompost(args); + break; + case "gdrag": + calcGdrag(isNaN(args[2]) ? 100 : args[2]); + break; + case "tabasco": + calcTabasco(args); + break; + default: + if (MINION_ARGS.has(args[1])) calcMinions(args); else { - ChatLib.chat(`${LOGO + RED}Unkown command: "${command}" was not found!`); - ChatLib.chat(`${LOGO + RED}Use '/va help' for a full list of commands.`); + ChatLib.chat(`\n${LOGO + RED}Error: Invalid argument "${args[1]}"!`); + ChatLib.chat( + `${ + LOGO + RED + }Please input as: ${WHITE}/va calc ${GRAY}<${WHITE}gdrag, hg, inferno, gaba, tabasco, vampire, compost${GRAY}>` + ); } break; - } -}).setName("va", true).setAliases("volcaddons", "volc", "itee"); + } + } catch (err) { + ChatLib.chat(LOGO + DARK_RED + (err.cause ?? err)); + } + break; + // Configure Fairy souls + case "fairy": + updateFairy(args[1], args[2]); + break; + // Configure Enigma souls + case "enigma": + updateEnigma(args[1], args.splice(2).join(" ")); + break; + // Configure Montezuma souls + case "montezuma": + case "mont": + case "cat": + updateCat(args[1], args[2]); + break; + // Configure egg waypoints + case "egg": + updateEggs(args[1], args[2]); + break; + // Party Commands and Else Case + default: + args = args.map((w) => w.toLowerCase()); + // Other args to check + const PARTY_COMMANDS = new Set([ + "cringe", + "gay", + "racist", + "femboy", + "trans", + "transphobic", + "dice", + "roll", + "coin", + "flip", + "coinflip", + "cf", + "8ball", + "rps", + "waifu", + "w", + "women", + "neko", + "shinobu", + "megumin", + "bully", + "cuddle", + "cry", + "hug", + "awoo", + "kiss", + "lick", + "pat", + "smug", + "bonk", + "yeet", + "blush", + "smile", + "wave", + "highfive", + "handhold", + "nom", + "bite", + "glomp", + "slap", + "kill", + "kick", + "happy", + "wink", + "poke", + "dance", + "cringe", + ]); + const INSTANCES = new Set(["f", "m", "t"]); + const STATUS_ARGS = new Set(["ping", "tps", "fps", "cps", "yaw", "pitch", "dir", "direction", "day"]); + const STAT_ARGS = new Set(["pet", "stats", "soulflow", "sf", "playtime", "pt", "legion"]); + if (PARTY_COMMANDS.has(command) || (INSTANCES.has(command[0]) && !isNaN(command[1]))) + executeCommand(Player.getName(), args, false); + else if (STATUS_ARGS.has(command)) getStatus(command); + else if (STAT_ARGS.has(command)) getStat(command); + else { + ChatLib.chat(`${LOGO + RED}Unkown command: "${command}" was not found!`); + ChatLib.chat(`${LOGO + RED}Use '/va help' for a full list of commands.`); + } + break; + } +}) + .setName("va", true) + .setAliases("volcaddons", "volc", "itee"); /** * Final imports */ import "./features/container/ArmorDisplay"; import { buttonCommands } from "./features/container/ContainerButtons"; - diff --git a/json/8ball.json b/json/8ball.json index 55b05d7..d5d3839 100644 --- a/json/8ball.json +++ b/json/8ball.json @@ -1,22 +1,22 @@ [ - "As I see it, yes", - "It is certain", - "It is decidedly so", - "Most likely", - "Outlook good", - "Signs point to yes", - "Without a doubt", - "Yes", - "Yes - definitely", - "You may rely on it", - "Reply hazy, try again", - "Ask again later", - "Better not tell you now", - "Cannot predict now", - "Concentrate and ask again", - "Don't count on it", - "My reply is no", - "My sources say no", - "Outlook not so good", - "Very doubtful" -] \ No newline at end of file + "As I see it, yes", + "It is certain", + "It is decidedly so", + "Most likely", + "Outlook good", + "Signs point to yes", + "Without a doubt", + "Yes", + "Yes - definitely", + "You may rely on it", + "Reply hazy, try again", + "Ask again later", + "Better not tell you now", + "Cannot predict now", + "Concentrate and ask again", + "Don't count on it", + "My reply is no", + "My sources say no", + "Outlook not so good", + "Very doubtful" +] diff --git a/json/catSouls.json b/json/catSouls.json index 70907d6..6f36d7a 100644 --- a/json/catSouls.json +++ b/json/catSouls.json @@ -1,75 +1,39 @@ [ - - [ - "Soul Piece #1", - "Wyld Woods", - "Found underneath a tree.", - -93, - 69, - 112 - ], - [ - "Soul Piece #2", - "Rift Gallery", - "Found behind a door to a small house, which can be opened by pressing the stone button next to it.", - 0, - 56, - 63 - ], - [ - "Soul Piece #3", - "Black Lagoon", - "Found behind the entry-point to the Leeches Lair.", - -139, - 45, - 15 - ], - [ - "Soul Piece #4", - "West Village", - "Found on top of the house containing Plumber Joe, which can be accessed by climbing the nearby water stream.", - -62, - 88, - -81 - ], - [ - "Soul Piece #5", - "Dreadfarm", - "Found behind a cracked stone brick wall, which can be destroyed with a Horsezooka.", - -24, - 72, - -198 - ], - [ - "Soul Piece #6", - "Otherside", - "Found adjacent to the Déjà Vu Alley.", - 38, - 65, - -118 - ], - [ - "Soul Piece #7", - "Book In A Book", - "Found in a hidden room, which can be accessed by clicking the bookshelf at 8, 72, -15.", - 8, - 72, - -15 - ], - [ - "Soul Piece #8", - "Living Stillness", - "Found underneath a destroyable snow block next to a Rock.", - 22, - 43, - -220 - ], - [ - "Soul Piece #9", - "Photon Pathway", - "Found in the second floor of the house at the end of the parkour.", - 152, - 80, - -14 - ] -] \ No newline at end of file + ["Soul Piece #1", "Wyld Woods", "Found underneath a tree.", -93, 69, 112], + [ + "Soul Piece #2", + "Rift Gallery", + "Found behind a door to a small house, which can be opened by pressing the stone button next to it.", + 0, + 56, + 63 + ], + ["Soul Piece #3", "Black Lagoon", "Found behind the entry-point to the Leeches Lair.", -139, 45, 15], + [ + "Soul Piece #4", + "West Village", + "Found on top of the house containing Plumber Joe, which can be accessed by climbing the nearby water stream.", + -62, + 88, + -81 + ], + [ + "Soul Piece #5", + "Dreadfarm", + "Found behind a cracked stone brick wall, which can be destroyed with a Horsezooka.", + -24, + 72, + -198 + ], + ["Soul Piece #6", "Otherside", "Found adjacent to the Déjà Vu Alley.", 38, 65, -118], + [ + "Soul Piece #7", + "Book In A Book", + "Found in a hidden room, which can be accessed by clicking the bookshelf at 8, 72, -15.", + 8, + 72, + -15 + ], + ["Soul Piece #8", "Living Stillness", "Found underneath a destroyable snow block next to a Rock.", 22, 43, -220], + ["Soul Piece #9", "Photon Pathway", "Found in the second floor of the house at the end of the parkour.", 152, 80, -14] +] diff --git a/json/eggs.json b/json/eggs.json index 8e2ba99..85599f9 100644 --- a/json/eggs.json +++ b/json/eggs.json @@ -1,1214 +1,194 @@ { - "Hub": [ - [ - "Bazaar Alley", - "in the Bazaar Alley", - "-40", - "70", - "-74" - ], - [ - "Blacksmith's House", - "behind the Blacksmith building", - "-38", - "70", - "-135" - ], - [ - "Colosseum", - "atop the Colosseum", - "160", - "97", - "-71" - ], - [ - "Farm", - "near the Wheat Minion", - "44", - "71", - "-137" - ], - [ - "Farmhouse", - "to the right of the Farmhouse building", - "18", - "70", - "-77" - ], - [ - "Fisherman's Hut", - "at the Fisherman's Hut", - "169", - "72", - "36" - ], - [ - "Forest", - "near the lumber piles", - "-153", - "74", - "-40" - ], - [ - "Graveyard", - "on the emerald altar", - "-127", - "73", - "-126" - ], - [ - "Mountain", - "on the Mountain path", - "-1", - "144", - "51" - ], - [ - "Mountain", - "on the second highest Mountain peak", - "-38", - "193", - "35" - ], - [ - "Ruins", - "within the Ruins", - "-186", - "87", - "81" - ], - [ - "Unincorporated", - "in Unincorporated Territory", - "-7", - "70", - "188" - ], - [ - "Village", - "on the crane", - "-61", - "80", - "-38" - ], - [ - "Wilderness", - "near the Dark Auction", - "125", - "74", - "168" - ], - [ - "Wilderness", - "within the emerald treehouse", - "147", - "113", - "117" - ], - [ - "Wilderness", - "within the wither cage", - "161", - "71", - "157" - ], - [ - "Wizard Tower", - "at the base of the Wizard Tower", - "35", - "72", - "79" - ] - ], - "The Farming Islands": [ - [ - "The Barn", - "in the pasture", - "152", - "72", - "-217" - ], - [ - "The Barn", - "on the second floor of The Barn", - "146", - "81", - "-242" - ], - [ - "Glowing Mushroom Cave", - "in Moby's attic", - "204", - "54", - "-503" - ], - [ - "Jake's House", - "overlooking the oasis from above", - "256", - "184", - "-564" - ], - [ - "Trapper's Den", - "outside the Trapper's Den", - "284", - "102", - "-573" - ], - [ - "Desert Settlement", - "beside the well", - "175", - "77", - "-373" - ], - [ - "Mushroom Desert", - "beside an animal skull", - "143", - "77", - "-404" - ], - [ - "Oasis", - "beneath an oasis bridge", - "142", - "71", - "-453" - ], - [ - "Windmill", - "within the windmill", - "95", - "83", - "-284" - ], - [ - "The Barn", - "behind the tallest hill", - "168", - "92", - "-319" - ], - [ - "The Barn", - "among the path through carrots", - "142", - "90", - "-300" - ], - [ - "The Barn", - "on top of a scarecrow", - "136", - "74", - "-215" - ], - [ - "Desert Settlement", - "in Mason's house", - "174", - "78", - "-357" - ], - [ - "Mushroom Desert", - "on Farmer Jon's table", - "163", - "92", - "-599" - ], - [ - "The Barn", - "at the end of the aqueduct", - "116", - "87", - "-222" - ] - ], - "Gold Mine": [ - [ - "Gold Mine", - "on top of the entrance", - "-15", - "104", - "-313" - ], - [ - "Gold Mine", - "next to the Lazy Miner's pickaxe", - "-19", - "25", - "-305" - ], - [ - "Gold Mine", - "on the top of the mountain", - "-19", - "143", - "-361" - ], - [ - "Gold Mine", - "near Rusty", - "-25", - "78", - "-328" - ], - [ - "Gold Mine", - "behind the Gold Forger", - "-30", - "74", - "-293" - ], - [ - "Gold Mine", - "in the Blacksmith's forge", - "-44", - "77", - "-303" - ], - [ - "Gold Mine", - "beside a pile of coal", - "15", - "75", - "-290" - ], - [ - "Gold Mine", - "[More Info Needed]", - "-1", - "78", - "-325" - ], - [ - "Gold Mine", - "[More Info Needed]", - "0", - "76", - "-307" - ], - [ - "Gold Mine", - "on top of platforms near a Fairy Soul", - "17", - "57", - "-340" - ], - [ - "Gold Mine", - "[More Info Needed]", - "-35", - "77", - "-313" - ], - [ - "Gold Mine", - "at the bottom of the stairs", - "-18", - "62", - "-353" - ], - [ - "Gold Mine", - "behind the Deep Caverns launch pad", - "-11", - "68", - "-397" - ], - [ - "Gold Mine", - "next to a hot fire underground", - "-22", - "37", - "-336" - ], - [ - "Gold Mine", - "on top of a giant crate", - "-25", - "79", - "-282" - ], - [ - "Gold Mine", - "looking towards the windmill", - "41", - "80", - "-311" - ], - [ - "Gold Mine", - "near a fork in the lava", - "-49", - "94", - "-335" - ], - [ - "Gold Mine", - "above the Iron Forger", - "-2", - "82", - "-309" - ] - ], - "Deep Caverns": [ - [ - "Deep Caverns", - "beneath the launch portal", - "4", - "148", - "88" - ], - [ - "Gunpowder Mines", - "beside Walter", - "24", - "156", - "-38" - ], - [ - "Gunpowder Mines", - "atop a platform", - "22", - "159", - "27" - ], - [ - "Gunpowder Mines", - "beside the Gunpowder Mines lift", - "56", - "151", - "23" - ], - [ - "Gunpowder Mines", - "beside a fire at the entrance to the mines", - "1", - "153", - "37" - ], - [ - "Deep Caverns", - "on top of the entrance", - "11", - "176", - "58" - ], - [ - "Deep Caverns", - "atop of the shortest mountain peak", - "-34", - "207", - "38" - ], - [ - "Diamond Reserve", - "beside the Diamond Reserve lift", - "46", - "38", - "22" - ], - [ - "Slimehill", - "next to a lily pad pond in Slimehill", - "20", - "68", - "43" - ], - [ - "Pigmen's Den", - "[More Info Needed]", - "21", - "104", - "17" - ], - [ - "Lapis Quarry", - "next to the lapis miner", - "-11", - "120", - "37" - ], - [ - "Pigmen's Den", - "in front of the Pigmen sign", - "1", - "110", - "45" - ], - [ - "Obsidian Sanctuary", - "adjacent to a fossil", - "38", - "12", - "20" - ], - [ - "Obsidian Sanctuary", - "next to Rhys", - "31", - "12", - "13" - ], - [ - "Deep Caverns", - "beside the entrance steps", - "7", - "157", - "63" - ] - ], - "Dwarven Mines": [ - [ - "Dwarven Village", - "behind Banker Broadjaw", - "13", - "201", - "-150" - ], - [ - "Cliffside Veins", - "overlooking The Mist", - "36", - "128", - "50" - ], - [ - "Divan's Gateway", - "atop the right massive dwarf statue", - "32", - "179", - "103" - ], - [ - "Great Ice Wall", - "under Divan's Gateway", - "-17", - "128", - "177" - ], - [ - "Dwarven Mines", - "underneath a massive crystal", - "-1", - "183", - "26" - ], - [ - "Dwarven Mines", - "underneath the gold gate", - "87", - "193", - "-44" - ], - [ - "Dwarven Mines", - "near Geo", - "91", - "199", - "-123" - ], - [ - "Miner's Guild", - "in the Miner's Guild", - "-73", - "222", - "-139" - ], - [ - "Royal Palace", - "on the Royal Palace table", - "126", - "197", - "187" - ], - [ - "Upper Mines", - "behind Emissary Fraiser", - "-134", - "174", - "-51" - ], - [ - "Goblin Burrows", - "in the Goblin Burrows gold room", - "-146", - "144", - "148" - ], - [ - "Hanging Court", - "looking off the Hanging Court", - "78", - "188", - "126" - ], - [ - "The Lift", - "behind the Lift", - "-93", - "200", - "-122" - ], - [ - "Aristocrat Passage", - "In the passage underneath the Royal Palace", - "104", - "151", - "142" - ], - [ - "The Forge", - "near lava in The Forge", - "-2", - "147", - "-52" - ] - ], - "Crystal Hollows": [ - [ - "Crystal Nucleus", - "near the Diamond Essence Shop", - "472", - "106", - "518" - ], - [ - "Crystal Nucleus", - "at an entrance to the Magma Fields near Amethyst", - "485", - "65", - "479" - ], - [ - "Crystal Nucleus", - "on a ledge next to the stairwell", - "510", - "93", - "470" - ], - [ - "Crystal Nucleus", - "behind Emissary Sisko", - "494", - "106", - "556" - ], - [ - "Crystal Nucleus", - "in between Jade and Sapphire crystals", - "553", - "106", - "513" - ], - [ - "Crystal Nucleus", - "on top of the Crystal Nucleus", - "508", - "115", - "564" - ], - [ - "Crystal Nucleus", - "on the arms of the Amethyst statue", - "463", - "127", - "505" - ], - [ - "Crystal Nucleus", - "at the base of the Sapphire pillar", - "532", - "69", - "528" - ], - [ - "Crystal Nucleus", - "next to a lava waterfall", - "530", - "71", - "465" - ], - [ - "Crystal Nucleus", - "underneath the stairwell", - "509", - "71", - "480" - ], - [ - "Crystal Nucleus", - "near Geonathan Greatforge", - "533", - "108", - "556" - ], - [ - "Crystal Nucleus", - "on a lower platform!", - "477", - "82", - "532" - ], - [ - "Crystal Nucleus", - "near the Mithril Deposits entrance", - "557", - "117", - "463" - ], - [ - "Crystal Nucleus", - "on top of some Gold Ore", - "553", - "100", - "489" - ], - [ - "Crystal Nucleus", - "in the middle of the Crystal Nucleus !", - "513", - "70", - "513" - ] - ], - "Spider's Den": [ - [ - "Arachne's Sanctuary", - "higher up in Arachne's Sanctuary", - "-303", - "65", - "-182" - ], - [ - "Arachne's Sanctuary", - "behind an egg in Arachne's Sanctuary", - "-266", - "72", - "-191" - ], - [ - "Archaeologist's Camp", - "within the Archaeologist's Camp", - "-355", - "111", - "-293" - ], - [ - "Spider Mound", - "behind the Spider's Den", - "-155", - "86", - "-351" - ], - [ - "Spider's Den", - "behind the Crimson Isle launch pad", - "-360", - "85", - "-356" - ], - [ - "Spider's Den", - "on top of the dragon skeleton's horn", - "-353", - "124", - "-250" - ], - [ - "Arachne's Burrows", - "near a Fairy Soul and Relic", - "-293", - "36", - "-271" - ], - [ - "Gravel Mines", - "within a Gravel Mines fossil", - "-181", - "61", - "-312" - ], - [ - "Gravel Mines", - "atop a mushroom in the Gravel Mines", - "-217", - "75", - "-345" - ], - [ - "Spider's Den", - "beside Haymitch", - "-202", - "83", - "-238" - ], - [ - "Spider's Den", - "on top of the pond boulder", - "-299", - "97", - "-167" - ], - [ - "Grandma's House", - "near slain spiders", - "-280", - "115", - "-181" - ], - [ - "Spider's Den", - "near the stray Fairy Soul", - "-398", - "139", - "-250" - ], - [ - "Grandma's House", - "on top of the house", - "-283", - "129", - "-185" - ], - [ - "Spider Mound", - "at the top of the Spider's Den", - "-200", - "174", - "-313" - ] - ], - "The End": [ - [ - "The End", - "atop a obsidian spike near the entrance", - "-451", - "50", - "-274" - ], - [ - "The End", - "overlooking the drop down", - "-539", - "102", - "-271" - ], - [ - "Zealot Bruiser Hideout", - "in the Bruiser Hideout chokepoint", - "-551", - "56", - "-239" - ], - [ - "The End", - "by the warp to Void Sepulture", - "-566", - "7", - "-316" - ], - [ - "The End", - "at the highest point", - "-584", - "211", - "-272" - ], - [ - "Void Slate", - "behind the Draconic Altar", - "-591", - "5", - "-276" - ], - [ - "Dragon's Nest", - "at the start of the parkour", - "-600", - "21", - "-255" - ], - [ - "Dragon's Nest", - "atop The Peak", - "-667", - "33", - "-210" - ], - [ - "Dragon's Nest", - "on top of the Dragon Egg", - "-672", - "77", - "-276" - ], - [ - "Dragon's Nest", - "at the Dragon's View", - "-714", - "41", - "-315" - ], - [ - "Dragon's Nest", - "on the End Spire", - "-722", - "56", - "-232" - ], - [ - "The End", - "behind the race checkpoint", - "-778", - "101", - "-300" - ], - [ - "The End", - "on the stairs up to the race", - "-510", - "110", - "-250" - ], - [ - "Dragon's Nest", - "near Zealots and Watchers", - "-661", - "5", - "-248" - ], - [ - "The End", - "in the Pearl Dealer's shop", - "-502", - "103", - "-299" - ] - ], - "Crimson Isle": [ - [ - "Stronghold", - "in the corner of Bladesoul's room", - "-270", - "83", - "-496" - ], - [ - "The Wasteland", - "beside the Desperate Engineer", - "-289", - "127", - "-985" - ], - [ - "Odger's Hut", - "in Odger's Hut", - "-376", - "207", - "-809" - ], - [ - "Cathedral", - "in front row seats of the Cathedral", - "-33", - "114", - "-937" - ], - [ - "Dojo", - "behind Master Tao", - "-237", - "108", - "-605" - ], - [ - "Dragontail", - "within the Dragontail colosseum", - "-694", - "98", - "-794" - ], - [ - "Minion Shop", - "in the Dragontail Minion Shop", - "-641", - "101", - "-822" - ], - [ - "Forgotten Skull", - "on the Forgotten Skull's tooth", - "-375", - "121", - "-1009" - ], - [ - "Mage Outpost", - "on the path through the Mage Outpost", - "-295", - "93", - "-689" - ], - [ - "Magma Chamber", - "the middle of the Magma Chamber", - "-376", - "63", - "-802" - ], - [ - "Ruins of Ashfang", - "in the Ruins of Ashfang", - "-469", - "135", - "-1020" - ], - [ - "Plhlegblast Pool", - "in the Plhlegblast Pool", - "-375", - "83", - "-709" - ], - [ - "Scarleton Plaza", - "in front of the Statue of the Hero", - "-62", - "107", - "-811" - ], - [ - "The Bastion", - "on the snout of The Bastion skeleton", - "-631", - "141", - "-926" - ], - [ - "Stronghold", - "on the entrance bridge", - "-358", - "80", - "-442" - ] - ], - "The Park": [ - [ - "Savanna Woodland", - "where the jungle meets the savannah", - "-440", - "110", - "-40" - ], - [ - "Savanna Woodland", - "behind Master Tactician Funk's bunks", - "-461", - "110", - "-9" - ], - [ - "Jungle Island", - "near mossy cobblestone", - "-406", - "121", - "-90" - ], - [ - "None", - "within the clay tunnel", - "-455", - "94", - "-1" - ], - [ - "Dark Thicket", - "within the cultist's tent", - "-327", - "104", - "-113" - ], - [ - "Viking Longhouse", - "in the back of Lonely Island", - "-362", - "91", - "76" - ], - [ - "Melody's Plateau", - "behind Melody", - "-400", - "110", - "35" - ], - [ - "Birch Park", - "behind the flower pot", - "-309", - "82", - "18" - ], - [ - "Birch Park", - "on the birch launch pad", - "-276", - "87", - "-17" - ], - [ - "Birch Park", - "next to the pond bridge", - "-285", - "82", - "-41" - ], - [ - "Jungle Island", - "at the race checkpoint", - "-407", - "129", - "-123" - ], - [ - "Jungle Island", - "between islands tethered by leaves", - "-441", - "124", - "-109" - ], - [ - "Birch Park", - "within the Rainmaker shrine", - "-312", - "83", - "-72" - ], - [ - "Howling Cave", - "in front of Old Shaman Nyko", - "-380", - "61", - "35" - ], - [ - "Jungle Island", - "on top of a stone sculpture", - "-454", - "138", - "-121" - ] - ], - "Dungeon Hub": [ - [ - "Dungeon Hub", - "at the Crystal Core", - "5", - "11", - "-1" - ], - [ - "Dungeon Hub", - "atop a tall statue", - "0", - "146", - "-78" - ], - [ - "Dungeon Hub", - "next to Croesus", - "-21", - "120", - "-16" - ], - [ - "Dungeon Hub", - "near the Hub Selector", - "-34", - "119", - "9" - ], - [ - "Dungeon Hub", - "by the race start", - "-23", - "86", - "-3" - ], - [ - "Dungeon Hub", - "atop the Catacombs gate", - "-52", - "156", - "0" - ], - [ - "Dungeon Hub", - "near Saul", - "-54", - "119", - "-13" - ], - [ - "Dungeon Hub", - "under the entrance bridge", - "0", - "108", - "0" - ], - [ - "Dungeon Hub", - "atop the Giant Mushroom", - "-5", - "23", - "98" - ], - [ - "Dungeon Hub", - "in the Precursor Ruins", - "-138", - "21", - "-7" - ], - [ - "Dungeon Hub", - "behind Ophelia's desk", - "-62", - "121", - "-7" - ], - [ - "Dungeon Hub", - "near something that will never arrive", - "-62", - "122", - "28" - ], - [ - "Dungeon Hub", - "beside the Hub Island portal", - "16", - "125", - "3" - ], - [ - "Dungeon Hub", - "behind leaderboards", - "-18", - "87", - "-27" - ], - [ - "Dungeon Hub", - "near Malik", - "-66", - "121", - "6" - ] - ] -} \ No newline at end of file + "Hub": [ + ["Bazaar Alley", "in the Bazaar Alley", "-40", "70", "-74"], + ["Blacksmith's House", "behind the Blacksmith building", "-38", "70", "-135"], + ["Colosseum", "atop the Colosseum", "160", "97", "-71"], + ["Farm", "near the Wheat Minion", "44", "71", "-137"], + ["Farmhouse", "to the right of the Farmhouse building", "18", "70", "-77"], + ["Fisherman's Hut", "at the Fisherman's Hut", "169", "72", "36"], + ["Forest", "near the lumber piles", "-153", "74", "-40"], + ["Graveyard", "on the emerald altar", "-127", "73", "-126"], + ["Mountain", "on the Mountain path", "-1", "144", "51"], + ["Mountain", "on the second highest Mountain peak", "-38", "193", "35"], + ["Ruins", "within the Ruins", "-186", "87", "81"], + ["Unincorporated", "in Unincorporated Territory", "-7", "70", "188"], + ["Village", "on the crane", "-61", "80", "-38"], + ["Wilderness", "near the Dark Auction", "125", "74", "168"], + ["Wilderness", "within the emerald treehouse", "147", "113", "117"], + ["Wilderness", "within the wither cage", "161", "71", "157"], + ["Wizard Tower", "at the base of the Wizard Tower", "35", "72", "79"] + ], + "The Farming Islands": [ + ["The Barn", "in the pasture", "152", "72", "-217"], + ["The Barn", "on the second floor of The Barn", "146", "81", "-242"], + ["Glowing Mushroom Cave", "in Moby's attic", "204", "54", "-503"], + ["Jake's House", "overlooking the oasis from above", "256", "184", "-564"], + ["Trapper's Den", "outside the Trapper's Den", "284", "102", "-573"], + ["Desert Settlement", "beside the well", "175", "77", "-373"], + ["Mushroom Desert", "beside an animal skull", "143", "77", "-404"], + ["Oasis", "beneath an oasis bridge", "142", "71", "-453"], + ["Windmill", "within the windmill", "95", "83", "-284"], + ["The Barn", "behind the tallest hill", "168", "92", "-319"], + ["The Barn", "among the path through carrots", "142", "90", "-300"], + ["The Barn", "on top of a scarecrow", "136", "74", "-215"], + ["Desert Settlement", "in Mason's house", "174", "78", "-357"], + ["Mushroom Desert", "on Farmer Jon's table", "163", "92", "-599"], + ["The Barn", "at the end of the aqueduct", "116", "87", "-222"] + ], + "Gold Mine": [ + ["Gold Mine", "on top of the entrance", "-15", "104", "-313"], + ["Gold Mine", "next to the Lazy Miner's pickaxe", "-19", "25", "-305"], + ["Gold Mine", "on the top of the mountain", "-19", "143", "-361"], + ["Gold Mine", "near Rusty", "-25", "78", "-328"], + ["Gold Mine", "behind the Gold Forger", "-30", "74", "-293"], + ["Gold Mine", "in the Blacksmith's forge", "-44", "77", "-303"], + ["Gold Mine", "beside a pile of coal", "15", "75", "-290"], + ["Gold Mine", "[More Info Needed]", "-1", "78", "-325"], + ["Gold Mine", "[More Info Needed]", "0", "76", "-307"], + ["Gold Mine", "on top of platforms near a Fairy Soul", "17", "57", "-340"], + ["Gold Mine", "[More Info Needed]", "-35", "77", "-313"], + ["Gold Mine", "at the bottom of the stairs", "-18", "62", "-353"], + ["Gold Mine", "behind the Deep Caverns launch pad", "-11", "68", "-397"], + ["Gold Mine", "next to a hot fire underground", "-22", "37", "-336"], + ["Gold Mine", "on top of a giant crate", "-25", "79", "-282"], + ["Gold Mine", "looking towards the windmill", "41", "80", "-311"], + ["Gold Mine", "near a fork in the lava", "-49", "94", "-335"], + ["Gold Mine", "above the Iron Forger", "-2", "82", "-309"] + ], + "Deep Caverns": [ + ["Deep Caverns", "beneath the launch portal", "4", "148", "88"], + ["Gunpowder Mines", "beside Walter", "24", "156", "-38"], + ["Gunpowder Mines", "atop a platform", "22", "159", "27"], + ["Gunpowder Mines", "beside the Gunpowder Mines lift", "56", "151", "23"], + ["Gunpowder Mines", "beside a fire at the entrance to the mines", "1", "153", "37"], + ["Deep Caverns", "on top of the entrance", "11", "176", "58"], + ["Deep Caverns", "atop of the shortest mountain peak", "-34", "207", "38"], + ["Diamond Reserve", "beside the Diamond Reserve lift", "46", "38", "22"], + ["Slimehill", "next to a lily pad pond in Slimehill", "20", "68", "43"], + ["Pigmen's Den", "[More Info Needed]", "21", "104", "17"], + ["Lapis Quarry", "next to the lapis miner", "-11", "120", "37"], + ["Pigmen's Den", "in front of the Pigmen sign", "1", "110", "45"], + ["Obsidian Sanctuary", "adjacent to a fossil", "38", "12", "20"], + ["Obsidian Sanctuary", "next to Rhys", "31", "12", "13"], + ["Deep Caverns", "beside the entrance steps", "7", "157", "63"] + ], + "Dwarven Mines": [ + ["Dwarven Village", "behind Banker Broadjaw", "13", "201", "-150"], + ["Cliffside Veins", "overlooking The Mist", "36", "128", "50"], + ["Divan's Gateway", "atop the right massive dwarf statue", "32", "179", "103"], + ["Great Ice Wall", "under Divan's Gateway", "-17", "128", "177"], + ["Dwarven Mines", "underneath a massive crystal", "-1", "183", "26"], + ["Dwarven Mines", "underneath the gold gate", "87", "193", "-44"], + ["Dwarven Mines", "near Geo", "91", "199", "-123"], + ["Miner's Guild", "in the Miner's Guild", "-73", "222", "-139"], + ["Royal Palace", "on the Royal Palace table", "126", "197", "187"], + ["Upper Mines", "behind Emissary Fraiser", "-134", "174", "-51"], + ["Goblin Burrows", "in the Goblin Burrows gold room", "-146", "144", "148"], + ["Hanging Court", "looking off the Hanging Court", "78", "188", "126"], + ["The Lift", "behind the Lift", "-93", "200", "-122"], + ["Aristocrat Passage", "In the passage underneath the Royal Palace", "104", "151", "142"], + ["The Forge", "near lava in The Forge", "-2", "147", "-52"] + ], + "Crystal Hollows": [ + ["Crystal Nucleus", "near the Diamond Essence Shop", "472", "106", "518"], + ["Crystal Nucleus", "at an entrance to the Magma Fields near Amethyst", "485", "65", "479"], + ["Crystal Nucleus", "on a ledge next to the stairwell", "510", "93", "470"], + ["Crystal Nucleus", "behind Emissary Sisko", "494", "106", "556"], + ["Crystal Nucleus", "in between Jade and Sapphire crystals", "553", "106", "513"], + ["Crystal Nucleus", "on top of the Crystal Nucleus", "508", "115", "564"], + ["Crystal Nucleus", "on the arms of the Amethyst statue", "463", "127", "505"], + ["Crystal Nucleus", "at the base of the Sapphire pillar", "532", "69", "528"], + ["Crystal Nucleus", "next to a lava waterfall", "530", "71", "465"], + ["Crystal Nucleus", "underneath the stairwell", "509", "71", "480"], + ["Crystal Nucleus", "near Geonathan Greatforge", "533", "108", "556"], + ["Crystal Nucleus", "on a lower platform!", "477", "82", "532"], + ["Crystal Nucleus", "near the Mithril Deposits entrance", "557", "117", "463"], + ["Crystal Nucleus", "on top of some Gold Ore", "553", "100", "489"], + ["Crystal Nucleus", "in the middle of the Crystal Nucleus !", "513", "70", "513"] + ], + "Spider's Den": [ + ["Arachne's Sanctuary", "higher up in Arachne's Sanctuary", "-303", "65", "-182"], + ["Arachne's Sanctuary", "behind an egg in Arachne's Sanctuary", "-266", "72", "-191"], + ["Archaeologist's Camp", "within the Archaeologist's Camp", "-355", "111", "-293"], + ["Spider Mound", "behind the Spider's Den", "-155", "86", "-351"], + ["Spider's Den", "behind the Crimson Isle launch pad", "-360", "85", "-356"], + ["Spider's Den", "on top of the dragon skeleton's horn", "-353", "124", "-250"], + ["Arachne's Burrows", "near a Fairy Soul and Relic", "-293", "36", "-271"], + ["Gravel Mines", "within a Gravel Mines fossil", "-181", "61", "-312"], + ["Gravel Mines", "atop a mushroom in the Gravel Mines", "-217", "75", "-345"], + ["Spider's Den", "beside Haymitch", "-202", "83", "-238"], + ["Spider's Den", "on top of the pond boulder", "-299", "97", "-167"], + ["Grandma's House", "near slain spiders", "-280", "115", "-181"], + ["Spider's Den", "near the stray Fairy Soul", "-398", "139", "-250"], + ["Grandma's House", "on top of the house", "-283", "129", "-185"], + ["Spider Mound", "at the top of the Spider's Den", "-200", "174", "-313"] + ], + "The End": [ + ["The End", "atop a obsidian spike near the entrance", "-451", "50", "-274"], + ["The End", "overlooking the drop down", "-539", "102", "-271"], + ["Zealot Bruiser Hideout", "in the Bruiser Hideout chokepoint", "-551", "56", "-239"], + ["The End", "by the warp to Void Sepulture", "-566", "7", "-316"], + ["The End", "at the highest point", "-584", "211", "-272"], + ["Void Slate", "behind the Draconic Altar", "-591", "5", "-276"], + ["Dragon's Nest", "at the start of the parkour", "-600", "21", "-255"], + ["Dragon's Nest", "atop The Peak", "-667", "33", "-210"], + ["Dragon's Nest", "on top of the Dragon Egg", "-672", "77", "-276"], + ["Dragon's Nest", "at the Dragon's View", "-714", "41", "-315"], + ["Dragon's Nest", "on the End Spire", "-722", "56", "-232"], + ["The End", "behind the race checkpoint", "-778", "101", "-300"], + ["The End", "on the stairs up to the race", "-510", "110", "-250"], + ["Dragon's Nest", "near Zealots and Watchers", "-661", "5", "-248"], + ["The End", "in the Pearl Dealer's shop", "-502", "103", "-299"] + ], + "Crimson Isle": [ + ["Stronghold", "in the corner of Bladesoul's room", "-270", "83", "-496"], + ["The Wasteland", "beside the Desperate Engineer", "-289", "127", "-985"], + ["Odger's Hut", "in Odger's Hut", "-376", "207", "-809"], + ["Cathedral", "in front row seats of the Cathedral", "-33", "114", "-937"], + ["Dojo", "behind Master Tao", "-237", "108", "-605"], + ["Dragontail", "within the Dragontail colosseum", "-694", "98", "-794"], + ["Minion Shop", "in the Dragontail Minion Shop", "-641", "101", "-822"], + ["Forgotten Skull", "on the Forgotten Skull's tooth", "-375", "121", "-1009"], + ["Mage Outpost", "on the path through the Mage Outpost", "-295", "93", "-689"], + ["Magma Chamber", "the middle of the Magma Chamber", "-376", "63", "-802"], + ["Ruins of Ashfang", "in the Ruins of Ashfang", "-469", "135", "-1020"], + ["Plhlegblast Pool", "in the Plhlegblast Pool", "-375", "83", "-709"], + ["Scarleton Plaza", "in front of the Statue of the Hero", "-62", "107", "-811"], + ["The Bastion", "on the snout of The Bastion skeleton", "-631", "141", "-926"], + ["Stronghold", "on the entrance bridge", "-358", "80", "-442"] + ], + "The Park": [ + ["Savanna Woodland", "where the jungle meets the savannah", "-440", "110", "-40"], + ["Savanna Woodland", "behind Master Tactician Funk's bunks", "-461", "110", "-9"], + ["Jungle Island", "near mossy cobblestone", "-406", "121", "-90"], + ["None", "within the clay tunnel", "-455", "94", "-1"], + ["Dark Thicket", "within the cultist's tent", "-327", "104", "-113"], + ["Viking Longhouse", "in the back of Lonely Island", "-362", "91", "76"], + ["Melody's Plateau", "behind Melody", "-400", "110", "35"], + ["Birch Park", "behind the flower pot", "-309", "82", "18"], + ["Birch Park", "on the birch launch pad", "-276", "87", "-17"], + ["Birch Park", "next to the pond bridge", "-285", "82", "-41"], + ["Jungle Island", "at the race checkpoint", "-407", "129", "-123"], + ["Jungle Island", "between islands tethered by leaves", "-441", "124", "-109"], + ["Birch Park", "within the Rainmaker shrine", "-312", "83", "-72"], + ["Howling Cave", "in front of Old Shaman Nyko", "-380", "61", "35"], + ["Jungle Island", "on top of a stone sculpture", "-454", "138", "-121"] + ], + "Dungeon Hub": [ + ["Dungeon Hub", "at the Crystal Core", "5", "11", "-1"], + ["Dungeon Hub", "atop a tall statue", "0", "146", "-78"], + ["Dungeon Hub", "next to Croesus", "-21", "120", "-16"], + ["Dungeon Hub", "near the Hub Selector", "-34", "119", "9"], + ["Dungeon Hub", "by the race start", "-23", "86", "-3"], + ["Dungeon Hub", "atop the Catacombs gate", "-52", "156", "0"], + ["Dungeon Hub", "near Saul", "-54", "119", "-13"], + ["Dungeon Hub", "under the entrance bridge", "0", "108", "0"], + ["Dungeon Hub", "atop the Giant Mushroom", "-5", "23", "98"], + ["Dungeon Hub", "in the Precursor Ruins", "-138", "21", "-7"], + ["Dungeon Hub", "behind Ophelia's desk", "-62", "121", "-7"], + ["Dungeon Hub", "near something that will never arrive", "-62", "122", "28"], + ["Dungeon Hub", "beside the Hub Island portal", "16", "125", "3"], + ["Dungeon Hub", "behind leaderboards", "-18", "87", "-27"], + ["Dungeon Hub", "near Malik", "-66", "121", "6"] + ] +} diff --git a/json/enigmaSouls.json b/json/enigmaSouls.json index 03bc00a..17262ef 100644 --- a/json/enigmaSouls.json +++ b/json/enigmaSouls.json @@ -1,338 +1,268 @@ [ - [ - "Tough Bark", - "The Bastion", - "Clicking on the tree with 20 Muted Bark .", - "-15", - "91", - "94" - ], - [ - "Next to Enigma", - "Enigma's Crib", - "Found in the lake in Enigma's Crib . It's mainly an example soul.", - "-27", - "71", - "90" - ], - [ - "Woods Flower Pot", - "Otherside", - "You must right click and following each of the flower pots in the Wyld Woods first.", - "-6", - "60", - "226" - ], - [ - "Wither Cage", - "Wyld Woods", - "Can be obtained by right-clicking on the nearby rotated wither skeleton skull.", - "-142", - "68", - "174" - ], - [ - "Pressurized", - "Pumpgrotto", - "Underneath a body of water that launches you upwards when you swim under it, but this can be bypassed by jumping into it from a high place.", - "-137", - "51", - "120" - ], - [ - "Two Plates", - "Wyld Woods", - "Requires two players standing on both adjacent stone pressure plates.", - "-129", - "72", - "77" - ], - [ - "Fleespook", - "The Bastion", - "Can be reached by following the Fleespook and climbing specific trees in the Wyld Woods .", - "-27", - "89", - "136" - ], - [ - "Up in the Sky", - "Wyld Woods", - "Reached by jumping on the launch pad.", - "-137", - "133", - "156" - ], - [ - "Between Branches", - "Wyld Woods", - "In a floating glass block, which can be reached by placing 2 Larva Silk and using a Silkwire Stick .", - "-108", - "117", - "123" - ], - [ - "Tel Kar", - "Wyld Woods", - "Unlocked by fully speaking to Tel Kar , which requires at least 2m30s Rift Time .", - "-115", - "69", - "61" - ], - [ - "Lagoon Cave", - "Lagoon Cave", - "Found at the end of the parkour course.", - "43", - "91", - "56" - ], - [ - "Mushroom Guy", - "Black Lagoon", - "Found on a tree near Mushroom Guy .", - "-168", - "81", - "12" - ], - [ - "Roy", - "Lagoon Hut", - "Can be purchased from Roy for 4,000 Motes and 8 Deadgehog Spines .", - "-204", - "75", - "49" - ], - [ - "Spinning Runes", - "Black Lagoon", - "Obtainable by making 5 spinning runes face the tree the soul is in. Clicking on a rune makes it and the 2 surrounding runes spin in the same direction.", - "-93", - "73", - "36" - ], - [ - "Hot Dog Contest", - "West Village", - "Can be obtained by feeding 50 Hot Dogs to Joey McPizza after speaking to Chef .", - "-102", - "72", - "-103" - ], - [ - "Fake Neuroscience", - "Infested House", - "Requires a Key To Infested House Soul from Kat .", - "-34", - "71", - "-88" - ], - [ - "2 Players Soul", - "West Village", - "Requires two players, where one player has to tell the other player which slots to click in a menu.", - "-106", - "78", - "-101" - ], - [ - "Cake House #1", - "Cake House", - "Requires you to eat through a lot cake.", - "-95", - "76", - "-82" - ], - [ - "Cake House #2", - "Cake House", - "Requires you to eat through a lot cake.", - "-94", - "70", - "-84" - ], - [ - "Dolphin Parkour", - "Dolphin Trainer", - "You must speak to Cosmo in the West Village and enter the Dolphin Trainer , then swim in the water like a dolphin.", - "-38", - "44", - "130" - ], - [ - "Between Roofs", - "West Village", - "In a floating glass block, which can be reached by placing 2 Larva Silk and using a Silkwire Stick .", - "-88", - "79", - "-102" - ], - [ - "Farm Flower Pot", - "Dreadfarm", - "Can be reached by clicking and following flower pots, with the last one launching you to the windmill.", - "-76", - "90", - "-149" - ], - [ - "Beanstalk", - "Great Beanstalk", - "Can be reached by fully growing the Great Beanstalk with the Plumber's Bucket .", - "-106", - "249", - "-149" - ], - [ - "Buttons", - "West Village", - "Obtainable by shooting all 56 wooden buttons in the West Village with the Berberis Blowgun and then going to Sorcerer Okron 's house.", - "-74", - "65", - "-119" - ], - [ - "Farm Balloons", - "Dreadfarm", - "Unlocked by shooting down balloons with the Berberis Blowgun .", - "-77", - "72", - "-176" - ], - [ - "Rabbit in the Hat!", - "Village Plaza", - "Unlocked by leading one of Cowboy Nick 's rabbits underneath the nearby floating yellow hat.", - "-21", - "72", - "-18" - ], - [ - "Back to Basics", - "\"Your\" Island", - "Can be found by bridging across the gap with Tree-like Wool .", - "-88", - "20", - "0" - ], - [ - "Buy BZT Today", - "Village Plaza", - "Can be purchased from Cryptosis for 15,000 Motes and 10 Scribe Cruxes .", - "27", - "71", - "-77" - ], - [ - "Plaza Fleespook", - "Village Plaza", - "Reached by following the Fleespook to the top of Barrier Street .", - "42", - "88", - "-91" - ], - [ - "Plaza Balloons", - "Otherside", - "Unlocked by shooting down balloons near the Village Plaza with the Berberis Blowgun .", - "47", - "68", - "-59" - ], - [ - "Lonely \u00c4vae\u00eckx", - "Lonely Terrace", - "Can be obtained by sitting in the chairs next to Lonely \u00c4vae\u00eckx for at least 3 minutes.", - "-34", - "66", - "-25" - ], - [ - "Between Cooler Roofs", - "Village Plaza", - "In a floating glass block, which can be reached by placing 2 Larva Silk and using a Silkwire Stick .", - "-23", - "84", - "-92" - ], - [ - "Horsing Around", - "Half-Eaten Cave", - "The surrounding Hay Bales can be destroyed with the Horsezooka .", - "40", - "70", - "27" - ], - [ - "Behind a Living Wall", - "Living Cave", - "Found behind a cracked stone brick wall, which can be destroyed with a Horsezooka .", - "38", - "63", - "-198" - ], - [ - "Flowery Message", - "Living Cave", - "Obtainable by saying \"Hi\" in chat after clicking and following a series of flower pots.", - "3", - "68", - "-204" - ], - [ - "Full Circle", - "Colosseum", - "Claimable by speaking to Mole and then jumping across all the Colosseum pillars.", - "-161", - "98", - "-72" - ], - [ - "Standing there", - "Stillgore Chateau", - "Found in a hidden cave around the cliffside.", - "255", - "74", - "160" - ], - [ - "Between Towers", - "Stillgore Chateau", - "In a floating glass block, which can be reached by placing 2 Larva Silk and using a Silkwire Stick .", - "262", - "118", - "94" - ], - [ - "Castle Fleespook", - "Stillgore Chateau", - "Can be obtained by following the Fleespook up the trees.", - "182", - "92", - "124" - ], - [ - "Castle Flower Pot", - "Stillgore Chateau", - "Claimable by following and right-clicking a trail of flower pots.", - "266", - "60", - "145" - ], - [ - "Castle Balloons", - "Stillgore Chateau", - "Unlocked by shooting down balloons with the Berberis Blowgun .", - "232", - "94", - "168" - ], - [ - "Fairylosopher", - "Fairylosopher Tower", - "Can be purchased from the highest Fairylosopher for 10,000 Motes and 32 Splatter Cruxes .", - "256", - "130", - "75" - ] -] \ No newline at end of file + ["Tough Bark", "The Bastion", "Clicking on the tree with 20 Muted Bark .", "-15", "91", "94"], + [ + "Next to Enigma", + "Enigma's Crib", + "Found in the lake in Enigma's Crib . It's mainly an example soul.", + "-27", + "71", + "90" + ], + [ + "Woods Flower Pot", + "Otherside", + "You must right click and following each of the flower pots in the Wyld Woods first.", + "-6", + "60", + "226" + ], + [ + "Wither Cage", + "Wyld Woods", + "Can be obtained by right-clicking on the nearby rotated wither skeleton skull.", + "-142", + "68", + "174" + ], + [ + "Pressurized", + "Pumpgrotto", + "Underneath a body of water that launches you upwards when you swim under it, but this can be bypassed by jumping into it from a high place.", + "-137", + "51", + "120" + ], + [ + "Two Plates", + "Wyld Woods", + "Requires two players standing on both adjacent stone pressure plates.", + "-129", + "72", + "77" + ], + [ + "Fleespook", + "The Bastion", + "Can be reached by following the Fleespook and climbing specific trees in the Wyld Woods .", + "-27", + "89", + "136" + ], + ["Up in the Sky", "Wyld Woods", "Reached by jumping on the launch pad.", "-137", "133", "156"], + [ + "Between Branches", + "Wyld Woods", + "In a floating glass block, which can be reached by placing 2 Larva Silk and using a Silkwire Stick .", + "-108", + "117", + "123" + ], + [ + "Tel Kar", + "Wyld Woods", + "Unlocked by fully speaking to Tel Kar , which requires at least 2m30s Rift Time .", + "-115", + "69", + "61" + ], + ["Lagoon Cave", "Lagoon Cave", "Found at the end of the parkour course.", "43", "91", "56"], + ["Mushroom Guy", "Black Lagoon", "Found on a tree near Mushroom Guy .", "-168", "81", "12"], + ["Roy", "Lagoon Hut", "Can be purchased from Roy for 4,000 Motes and 8 Deadgehog Spines .", "-204", "75", "49"], + [ + "Spinning Runes", + "Black Lagoon", + "Obtainable by making 5 spinning runes face the tree the soul is in. Clicking on a rune makes it and the 2 surrounding runes spin in the same direction.", + "-93", + "73", + "36" + ], + [ + "Hot Dog Contest", + "West Village", + "Can be obtained by feeding 50 Hot Dogs to Joey McPizza after speaking to Chef .", + "-102", + "72", + "-103" + ], + ["Fake Neuroscience", "Infested House", "Requires a Key To Infested House Soul from Kat .", "-34", "71", "-88"], + [ + "2 Players Soul", + "West Village", + "Requires two players, where one player has to tell the other player which slots to click in a menu.", + "-106", + "78", + "-101" + ], + ["Cake House #1", "Cake House", "Requires you to eat through a lot cake.", "-95", "76", "-82"], + ["Cake House #2", "Cake House", "Requires you to eat through a lot cake.", "-94", "70", "-84"], + [ + "Dolphin Parkour", + "Dolphin Trainer", + "You must speak to Cosmo in the West Village and enter the Dolphin Trainer , then swim in the water like a dolphin.", + "-38", + "44", + "130" + ], + [ + "Between Roofs", + "West Village", + "In a floating glass block, which can be reached by placing 2 Larva Silk and using a Silkwire Stick .", + "-88", + "79", + "-102" + ], + [ + "Farm Flower Pot", + "Dreadfarm", + "Can be reached by clicking and following flower pots, with the last one launching you to the windmill.", + "-76", + "90", + "-149" + ], + [ + "Beanstalk", + "Great Beanstalk", + "Can be reached by fully growing the Great Beanstalk with the Plumber's Bucket .", + "-106", + "249", + "-149" + ], + [ + "Buttons", + "West Village", + "Obtainable by shooting all 56 wooden buttons in the West Village with the Berberis Blowgun and then going to Sorcerer Okron 's house.", + "-74", + "65", + "-119" + ], + ["Farm Balloons", "Dreadfarm", "Unlocked by shooting down balloons with the Berberis Blowgun .", "-77", "72", "-176"], + [ + "Rabbit in the Hat!", + "Village Plaza", + "Unlocked by leading one of Cowboy Nick 's rabbits underneath the nearby floating yellow hat.", + "-21", + "72", + "-18" + ], + [ + "Back to Basics", + "\"Your\" Island", + "Can be found by bridging across the gap with Tree-like Wool .", + "-88", + "20", + "0" + ], + [ + "Buy BZT Today", + "Village Plaza", + "Can be purchased from Cryptosis for 15,000 Motes and 10 Scribe Cruxes .", + "27", + "71", + "-77" + ], + [ + "Plaza Fleespook", + "Village Plaza", + "Reached by following the Fleespook to the top of Barrier Street .", + "42", + "88", + "-91" + ], + [ + "Plaza Balloons", + "Otherside", + "Unlocked by shooting down balloons near the Village Plaza with the Berberis Blowgun .", + "47", + "68", + "-59" + ], + [ + "Lonely \u00c4vae\u00eckx", + "Lonely Terrace", + "Can be obtained by sitting in the chairs next to Lonely \u00c4vae\u00eckx for at least 3 minutes.", + "-34", + "66", + "-25" + ], + [ + "Between Cooler Roofs", + "Village Plaza", + "In a floating glass block, which can be reached by placing 2 Larva Silk and using a Silkwire Stick .", + "-23", + "84", + "-92" + ], + [ + "Horsing Around", + "Half-Eaten Cave", + "The surrounding Hay Bales can be destroyed with the Horsezooka .", + "40", + "70", + "27" + ], + [ + "Behind a Living Wall", + "Living Cave", + "Found behind a cracked stone brick wall, which can be destroyed with a Horsezooka .", + "38", + "63", + "-198" + ], + [ + "Flowery Message", + "Living Cave", + "Obtainable by saying \"Hi\" in chat after clicking and following a series of flower pots.", + "3", + "68", + "-204" + ], + [ + "Full Circle", + "Colosseum", + "Claimable by speaking to Mole and then jumping across all the Colosseum pillars.", + "-161", + "98", + "-72" + ], + ["Standing there", "Stillgore Chateau", "Found in a hidden cave around the cliffside.", "255", "74", "160"], + [ + "Between Towers", + "Stillgore Chateau", + "In a floating glass block, which can be reached by placing 2 Larva Silk and using a Silkwire Stick .", + "262", + "118", + "94" + ], + [ + "Castle Fleespook", + "Stillgore Chateau", + "Can be obtained by following the Fleespook up the trees.", + "182", + "92", + "124" + ], + [ + "Castle Flower Pot", + "Stillgore Chateau", + "Claimable by following and right-clicking a trail of flower pots.", + "266", + "60", + "145" + ], + [ + "Castle Balloons", + "Stillgore Chateau", + "Unlocked by shooting down balloons with the Berberis Blowgun .", + "232", + "94", + "168" + ], + [ + "Fairylosopher", + "Fairylosopher Tower", + "Can be purchased from the highest Fairylosopher for 10,000 Motes and 32 Splatter Cruxes .", + "256", + "130", + "75" + ] +] diff --git a/json/fairySouls.json b/json/fairySouls.json index 434fa3b..a98c691 100644 --- a/json/fairySouls.json +++ b/json/fairySouls.json @@ -1,1394 +1,252 @@ { - "Hub": [ - [ - "Village", - "-20", - "90", - "-12" - ], - [ - "Mountain", - "-60", - "108", - "3" - ], - [ - "Mountain", - "-50", - "132", - "32" - ], - [ - "Mountain", - "-52", - "161", - "43" - ], - [ - "Mountain", - "-56", - "115", - "28" - ], - [ - "Mountain", - "-39", - "191", - "34" - ], - [ - "Mountain", - "-3", - "193", - "32" - ], - [ - "Mountain", - "2", - "181", - "31" - ], - [ - "Mountain", - "10", - "179", - "22" - ], - [ - "Ruins", - "-207", - "100", - "66" - ], - [ - "Ruins", - "-214", - "103", - "89" - ], - [ - "Ruins", - "-252", - "132", - "51" - ], - [ - "Ruins", - "-261", - "56", - "115" - ], - [ - "Ruins", - "-229", - "123", - "84" - ], - [ - "Ruins", - "-191", - "102", - "109" - ], - [ - "Ruins", - "-245", - "75", - "149" - ], - [ - "Ruins", - "-259", - "114", - "85" - ], - [ - "Farmhouse", - "26", - "80", - "-65" - ], - [ - "Village", - "40", - "68", - "-65" - ], - [ - "Coal Mine", - "-34", - "67", - "-150" - ], - [ - "Coal Mine", - "-21", - "79", - "-166" - ], - [ - "Graveyard", - "-187", - "92", - "-104" - ], - [ - "Village", - "-16", - "66", - "-110" - ], - [ - "Village", - "23", - "79", - "-134" - ], - [ - "Ruins", - "-262", - "102", - "67" - ], - [ - "Ruins", - "-260", - "96", - "48" - ], - [ - "Ruins", - "-233", - "86", - "84" - ], - [ - "Mountain", - "-48", - "76", - "49" - ], - [ - "Wizard Tower", - "40", - "108", - "78" - ], - [ - "Wizard Tower", - "49", - "121", - "69" - ], - [ - "Wizard Tower", - "43", - "152", - "73" - ], - [ - "Mountain", - "57", - "90", - "79" - ], - [ - "Wilderness", - "110", - "67", - "58" - ], - [ - "Fisherman's Hut", - "155", - "62", - "28" - ], - [ - "Wilderness", - "132", - "144", - "114" - ], - [ - "Wilderness", - "82", - "61", - "196" - ], - [ - "Wilderness", - "113", - "102", - "106" - ], - [ - "Fisherman's Hut", - "148", - "112", - "88" - ], - [ - "Wilderness", - "149", - "116", - "115" - ], - [ - "Wilderness", - "111", - "120", - "127" - ], - [ - "Wilderness", - "96", - "106", - "121" - ], - [ - "Wilderness", - "86", - "89", - "66" - ], - [ - "Ruins", - "-195", - "55", - "153" - ], - [ - "Ruins", - "-152", - "67", - "123" - ], - [ - "Wilderness", - "87", - "77", - "43" - ], - [ - "Thaumaturgist", - "44", - "68", - "-34" - ], - [ - "Auction House", - "-53", - "70", - "-100" - ], - [ - "Coal Mine", - "-6", - "66", - "-179" - ], - [ - "Farm", - "34", - "72", - "-162" - ], - [ - "Forest", - "-142", - "77", - "-31" - ], - [ - "Forest", - "-208", - "70", - "-80" - ], - [ - "Farm", - "72", - "71", - "-190" - ], - [ - "Graveyard", - "-94", - "72", - "-128" - ], - [ - "Mountain", - "9", - "75", - "13" - ], - [ - "Fisherman's Hut", - "176", - "64", - "42" - ], - [ - "Colosseum", - "154", - "98", - "-71" - ], - [ - "Farm", - "104", - "77", - "-133" - ], - [ - "Bank", - "-24", - "88", - "-63" - ], - [ - "Ruins", - "-183", - "80", - "29" - ], - [ - "Ruins", - "-133", - "74", - "133" - ], - [ - "Wizard Tower", - "48", - "78", - "81" - ], - [ - "Wizard Tower", - "43", - "120", - "70" - ], - [ - "Village", - "-81", - "70", - "-88" - ], - [ - "Village", - "-49", - "90", - "-72" - ], - [ - "Mountain", - "22", - "132", - "25" - ], - [ - "Ruins", - "-166", - "79", - "93" - ], - [ - "Forest", - "-225", - "72", - "-21" - ], - [ - "Mountain", - "-32", - "71", - "21" - ], - [ - "Wilderness", - "180", - "63", - "-15" - ], - [ - "Fisherman's Hut", - "147", - "53", - "88" - ], - [ - "Colosseum", - "168", - "60", - "-36" - ], - [ - "None", - "162", - "46", - "69" - ], - [ - "Farm", - "70", - "90", - "-149" - ], - [ - "Wilderness", - "138", - "66", - "129" - ], - [ - "None", - "72", - "70", - "-99" - ], - [ - "Wilderness", - "169", - "60", - "129" - ], - [ - "Ruins", - "-248", - "74", - "125" - ], - [ - "Coal Mine", - "-33", - "76", - "-213" - ], - [ - "Catacombs Entrance", - "-92", - "59", - "-138" - ], - [ - "Pet Care", - "30.5", - "80", - "-93.5" - ] - ], - "Farming Islands": [ - [ - "Windmill", - "96", - "96", - "-287" - ], - [ - "The Barn", - "182", - "99", - "-235" - ], - [ - "The Barn", - "183", - "99", - "-305" - ], - [ - "The Barn", - "126", - "91", - "-304" - ], - [ - "Windmill", - "99", - "112", - "-275" - ], - [ - "The Barn", - "143", - "90", - "-243" - ], - [ - "None", - "155", - "23", - "-204" - ], - [ - "Oasis", - "111", - "63", - "-447" - ], - [ - "Mushroom Desert", - "263", - "177", - "-565" - ], - [ - "Mushroom Desert", - "138", - "72", - "-587" - ], - [ - "Mushroom Desert", - "273", - "141", - "-467" - ], - [ - "Shepherds Keep", - "387", - "78", - "-365" - ], - [ - "Mushroom Desert", - "261", - "133", - "-348" - ], - [ - "Desert Settlement", - "145", - "77", - "-374" - ], - [ - "Mushroom Gorge", - "254", - "70", - "-493" - ], - [ - "Mushroom Desert", - "193", - "66", - "-468" - ], - [ - "Trappers Den", - "279", - "112", - "-541" - ], - [ - "Overgrown Mushroom Cave", - "271", - "56", - "-361" - ], - [ - "Mushroom Desert", - "152", - "67", - "-361" - ], - [ - "Oasis", - "150", - "60", - "-448" - ] - ], - "Gold Mine": [ - [ - "Gold Mine", - "-11", - "76", - "-395" - ], - [ - "Gold Mine", - "-19", - "142", - "-364" - ], - [ - "Gold Mine", - "-37", - "78", - "-308" - ], - [ - "Gold Mine", - "-62", - "104", - "-289" - ], - [ - "Gold Mine", - "-26", - "94", - "-340" - ], - [ - "Gold Mine", - "17", - "86", - "-312" - ], - [ - "Gold Mine", - "21", - "36", - "-320" - ], - [ - "Gold Mine", - "-23", - "113", - "-353" - ], - [ - "Gold Mine", - "-44", - "100", - "-344" - ], - [ - "Gold Mine", - "-47", - "75", - "-298" - ], - [ - "Gold Mine", - "-1", - "80", - "-337" - ], - [ - "Gold Mine", - "19", - "57", - "-341" - ] - ], - "Deep Caverns": [ - [ - "Gunpowder Mines", - "22", - "156", - "-42" - ], - [ - "Deep Caverns", - "3", - "152", - "85" - ], - [ - "Deep Caverns", - "71", - "167", - "-12" - ], - [ - "Deep Caverns", - "-2", - "255", - "-1" - ], - [ - "Deep Caverns", - "3", - "182", - "50" - ], - [ - "Deep Caverns", - "9", - "170", - "44" - ], - [ - "Gunpowder Mines", - "57", - "161", - "19" - ], - [ - "Gunpowder Mines", - "-18", - "163", - "26" - ], - [ - "Gunpowder Mines", - "29", - "149", - "14" - ], - [ - "Lapis Quarry", - "-35", - "127", - "28" - ], - [ - "Pigmen's Den", - "44", - "98", - "23" - ], - [ - "Pigmen's Den", - "-11", - "102", - "-16" - ], - [ - "Slimehill", - "18", - "74", - "74" - ], - [ - "Slimehill", - "0", - "65", - "57" - ], - [ - "Slimehill", - "-40", - "72", - "0" - ], - [ - "Slimehill", - "-8", - "74", - "-44" - ], - [ - "Diamond Reserve", - "-60", - "37", - "52" - ], - [ - "Diamond Reserve", - "-21", - "25", - "72" - ], - [ - "Obsidian Sanctuary", - "3", - "14", - "51" - ], - [ - "Obsidian Sanctuary", - "-71", - "13", - "5" - ], - [ - "Obsidian Sanctuary", - "-76", - "13", - "24" - ] - ], - "Dwarven Mines": [ - [ - "Rampart's Quarry", - "-21", - "208", - "-59" - ], - [ - "Dwarven Mines", - "-139", - "220", - "-89" - ], - [ - "Dwarven Village", - "-9", - "230", - "-135" - ], - [ - "Royal Palace", - "155", - "189", - "123" - ], - [ - "Divan's Gateway", - "34", - "102", - "86" - ], - [ - "Dwarven Mines", - "133", - "104", - "104" - ], - [ - "Great Ice Wall", - "22", - "127", - "184" - ], - [ - "Goblin Burrows", - "-110", - "142", - "143" - ], - [ - "Goblin Burrows", - "-116", - "142", - "154" - ], - [ - "Goblin Burrows", - "-204", - "131", - "199" - ], - [ - "Dwarven Mines", - "-53", - "205", - "50" - ] - ], - "Spider's Den": [ - [ - "None", - "-422", - "106", - "-206" - ], - [ - "Spider's Den", - "-336", - "111", - "-253" - ], - [ - "Spider's Den", - "-222", - "74", - "-361" - ], - [ - "Spider's Den", - "-147", - "78", - "-299" - ], - [ - "Spider's Den", - "-169", - "62", - "-289" - ], - [ - "Spider's Den", - "-185", - "135", - "-290" - ], - [ - "Spider's Den", - "-322", - "95", - "-281" - ], - [ - "Spider's Den", - "-294", - "36", - "-274" - ], - [ - "Spider's Den", - "-160", - "62", - "-275" - ], - [ - "Spider's Den", - "-336", - "82", - "-153" - ], - [ - "Spider's Den", - "-297", - "90", - "-169" - ], - [ - "Spider's Den", - "-279", - "127", - "-177" - ], - [ - "Spider's Den", - "-309", - "63", - "-185" - ], - [ - "Spider's Den", - "-301", - "92", - "-171" - ], - [ - "Spider's Den", - "-140", - "85", - "-335" - ], - [ - "Spider's Den", - "-204", - "94", - "-241" - ], - [ - "Spider's Den", - "-203", - "169", - "-320" - ], - [ - "Spider's Den", - "-309", - "66", - "-245" - ], - [ - "Spider's Den", - "-198", - "160", - "-331" - ] - ], - "Crimson Isle": [ - [ - "Aura's Lab", - "-383", - "71", - "-883" - ], - [ - "Dragontail", - "-690", - "122", - "-752" - ], - [ - "Dragontail", - "-606", - "154", - "-800" - ], - [ - "Blazing Volcano", - "-343", - "235", - "-780" - ], - [ - "Cathedral", - "-31", - "178", - "-907" - ], - [ - "Stronghold", - "-352", - "191", - "-553" - ], - [ - "Crimson Isle", - "-480", - "104", - "-593" - ], - [ - "Stronghold", - "-346", - "75", - "-552" - ], - [ - "Stronghold", - "-342", - "101", - "-484" - ], - [ - "Crimson Isle", - "-462", - "78", - "-698" - ], - [ - "Crimson Isle", - "-361", - "69", - "-425" - ], - [ - "Blazing Volcano", - "-396", - "108", - "-764" - ], - [ - "The Wasteland", - "-310", - "156", - "-1008" - ], - [ - "Ruins of Ashfang", - "-445", - "110", - "-1026" - ], - [ - "Dragontail", - "-726", - "144", - "-891" - ], - [ - "Forgotten Skull", - "-380", - "141", - "-1020" - ], - [ - "Scarleton Bank", - "-79", - "139", - "-779" - ], - [ - "Scarleton", - "14", - "108", - "-769" - ], - [ - "Mystic Marsh", - "-106", - "89", - "-883" - ], - [ - "Crimson Isle", - "-247", - "44", - "-512" - ], - [ - "Stronghold", - "-361", - "133", - "-469" - ], - [ - "Dragontail", - "-721", - "125", - "-811" - ], - [ - "Burning Desert", - "-500", - "127", - "-795" - ], - [ - "The Bastion", - "-717", - "164", - "-981" - ], - [ - "Crimson Isle", - "-412", - "58", - "-935" - ], - [ - "Mystic Marsh", - "-297", - "81", - "-835" - ], - [ - "The Wasteland", - "-35", - "116", - "-1055" - ], - [ - "Ruins of Ashfang", - "-479", - "114", - "-972" - ], - [ - "Dragontail", - "-644", - "125", - "-689" - ] - ], - "End": [ - [ - "Dragon's Nest", - "-723", - "75", - "-222" - ], - [ - "Dragon's Nest", - "-609", - "84", - "-230" - ], - [ - "The End", - "-587", - "48", - "-293" - ], - [ - "The End", - "-545", - "92", - "-257" - ], - [ - "The End", - "-492", - "81", - "-275" - ], - [ - "The End", - "-583", - "208", - "-272" - ], - [ - "The End", - "-587", - "122", - "-276" - ], - [ - "The End", - "-517", - "100", - "-295" - ], - [ - "The End", - "-492", - "21", - "-175" - ], - [ - "Dragon's Nest", - "-657", - "36", - "-201" - ], - [ - "The End", - "-696", - "116", - "-256" - ], - [ - "Dragon's Nest", - "-748", - "106", - "-284" - ] - ], - "Park": [ - [ - "Birch Park", - "-315", - "89", - "-72" - ], - [ - "Birch Park", - "-294", - "85", - "31" - ], - [ - "Howling Cave", - "-390", - "61", - "-6" - ], - [ - "Spruce Woods", - "-357", - "99", - "79" - ], - [ - "None", - "-450", - "113", - "-87" - ], - [ - "Jungle Island", - "-471", - "132", - "-125" - ], - [ - "Jungle Island", - "-408", - "122", - "-92" - ], - [ - "Jungle Island", - "-454", - "120", - "-58" - ], - [ - "Savanna Woodland", - "-404", - "136", - "6" - ], - [ - "None", - "-386", - "108", - "-69" - ], - [ - "Dark Thicket", - "-370", - "112", - "-118" - ] - ], - "Jerry's Workshop": [ - [ - "Jerry Pond", - "-95", - "77", - "20" - ], - [ - "Jerry's Workshop", - "74", - "109", - "-18" - ], - [ - "Jerry's Workshop", - "-44", - "87", - "76" - ], - [ - "Jerry's Workshop", - "56", - "108", - "64" - ], - [ - "Jerry's Workshop", - "-7", - "108", - "107" - ] - ], - "Rift": [ - [ - "Fairylosopher Tower", - "253", - "123", - "90" - ] - ], - "Dungeon Hub": [ - [ - "Dungeon Hub", - "17", - "124", - "-58" - ], - [ - "Dungeon Hub", - "1", - "137", - "75" - ], - [ - "Dungeon Hub", - "-139", - "46", - "-1" - ], - [ - "Dungeon Hub", - "14", - "60", - "99" - ], - [ - "Dungeon Hub", - "-4", - "21", - "-17" - ], - [ - "Dungeon Hub", - "-55", - "82", - "-10" - ], - [ - "Dungeon Hub", - "10", - "164", - "-10" - ] - ] -} \ No newline at end of file + "Hub": [ + ["Village", "-20", "90", "-12"], + ["Mountain", "-60", "108", "3"], + ["Mountain", "-50", "132", "32"], + ["Mountain", "-52", "161", "43"], + ["Mountain", "-56", "115", "28"], + ["Mountain", "-39", "191", "34"], + ["Mountain", "-3", "193", "32"], + ["Mountain", "2", "181", "31"], + ["Mountain", "10", "179", "22"], + ["Ruins", "-207", "100", "66"], + ["Ruins", "-214", "103", "89"], + ["Ruins", "-252", "132", "51"], + ["Ruins", "-261", "56", "115"], + ["Ruins", "-229", "123", "84"], + ["Ruins", "-191", "102", "109"], + ["Ruins", "-245", "75", "149"], + ["Ruins", "-259", "114", "85"], + ["Farmhouse", "26", "80", "-65"], + ["Village", "40", "68", "-65"], + ["Coal Mine", "-34", "67", "-150"], + ["Coal Mine", "-21", "79", "-166"], + ["Graveyard", "-187", "92", "-104"], + ["Village", "-16", "66", "-110"], + ["Village", "23", "79", "-134"], + ["Ruins", "-262", "102", "67"], + ["Ruins", "-260", "96", "48"], + ["Ruins", "-233", "86", "84"], + ["Mountain", "-48", "76", "49"], + ["Wizard Tower", "40", "108", "78"], + ["Wizard Tower", "49", "121", "69"], + ["Wizard Tower", "43", "152", "73"], + ["Mountain", "57", "90", "79"], + ["Wilderness", "110", "67", "58"], + ["Fisherman's Hut", "155", "62", "28"], + ["Wilderness", "132", "144", "114"], + ["Wilderness", "82", "61", "196"], + ["Wilderness", "113", "102", "106"], + ["Fisherman's Hut", "148", "112", "88"], + ["Wilderness", "149", "116", "115"], + ["Wilderness", "111", "120", "127"], + ["Wilderness", "96", "106", "121"], + ["Wilderness", "86", "89", "66"], + ["Ruins", "-195", "55", "153"], + ["Ruins", "-152", "67", "123"], + ["Wilderness", "87", "77", "43"], + ["Thaumaturgist", "44", "68", "-34"], + ["Auction House", "-53", "70", "-100"], + ["Coal Mine", "-6", "66", "-179"], + ["Farm", "34", "72", "-162"], + ["Forest", "-142", "77", "-31"], + ["Forest", "-208", "70", "-80"], + ["Farm", "72", "71", "-190"], + ["Graveyard", "-94", "72", "-128"], + ["Mountain", "9", "75", "13"], + ["Fisherman's Hut", "176", "64", "42"], + ["Colosseum", "154", "98", "-71"], + ["Farm", "104", "77", "-133"], + ["Bank", "-24", "88", "-63"], + ["Ruins", "-183", "80", "29"], + ["Ruins", "-133", "74", "133"], + ["Wizard Tower", "48", "78", "81"], + ["Wizard Tower", "43", "120", "70"], + ["Village", "-81", "70", "-88"], + ["Village", "-49", "90", "-72"], + ["Mountain", "22", "132", "25"], + ["Ruins", "-166", "79", "93"], + ["Forest", "-225", "72", "-21"], + ["Mountain", "-32", "71", "21"], + ["Wilderness", "180", "63", "-15"], + ["Fisherman's Hut", "147", "53", "88"], + ["Colosseum", "168", "60", "-36"], + ["None", "162", "46", "69"], + ["Farm", "70", "90", "-149"], + ["Wilderness", "138", "66", "129"], + ["None", "72", "70", "-99"], + ["Wilderness", "169", "60", "129"], + ["Ruins", "-248", "74", "125"], + ["Coal Mine", "-33", "76", "-213"], + ["Catacombs Entrance", "-92", "59", "-138"], + ["Pet Care", "30.5", "80", "-93.5"] + ], + "Farming Islands": [ + ["Windmill", "96", "96", "-287"], + ["The Barn", "182", "99", "-235"], + ["The Barn", "183", "99", "-305"], + ["The Barn", "126", "91", "-304"], + ["Windmill", "99", "112", "-275"], + ["The Barn", "143", "90", "-243"], + ["None", "155", "23", "-204"], + ["Oasis", "111", "63", "-447"], + ["Mushroom Desert", "263", "177", "-565"], + ["Mushroom Desert", "138", "72", "-587"], + ["Mushroom Desert", "273", "141", "-467"], + ["Shepherds Keep", "387", "78", "-365"], + ["Mushroom Desert", "261", "133", "-348"], + ["Desert Settlement", "145", "77", "-374"], + ["Mushroom Gorge", "254", "70", "-493"], + ["Mushroom Desert", "193", "66", "-468"], + ["Trappers Den", "279", "112", "-541"], + ["Overgrown Mushroom Cave", "271", "56", "-361"], + ["Mushroom Desert", "152", "67", "-361"], + ["Oasis", "150", "60", "-448"] + ], + "Gold Mine": [ + ["Gold Mine", "-11", "76", "-395"], + ["Gold Mine", "-19", "142", "-364"], + ["Gold Mine", "-37", "78", "-308"], + ["Gold Mine", "-62", "104", "-289"], + ["Gold Mine", "-26", "94", "-340"], + ["Gold Mine", "17", "86", "-312"], + ["Gold Mine", "21", "36", "-320"], + ["Gold Mine", "-23", "113", "-353"], + ["Gold Mine", "-44", "100", "-344"], + ["Gold Mine", "-47", "75", "-298"], + ["Gold Mine", "-1", "80", "-337"], + ["Gold Mine", "19", "57", "-341"] + ], + "Deep Caverns": [ + ["Gunpowder Mines", "22", "156", "-42"], + ["Deep Caverns", "3", "152", "85"], + ["Deep Caverns", "71", "167", "-12"], + ["Deep Caverns", "-2", "255", "-1"], + ["Deep Caverns", "3", "182", "50"], + ["Deep Caverns", "9", "170", "44"], + ["Gunpowder Mines", "57", "161", "19"], + ["Gunpowder Mines", "-18", "163", "26"], + ["Gunpowder Mines", "29", "149", "14"], + ["Lapis Quarry", "-35", "127", "28"], + ["Pigmen's Den", "44", "98", "23"], + ["Pigmen's Den", "-11", "102", "-16"], + ["Slimehill", "18", "74", "74"], + ["Slimehill", "0", "65", "57"], + ["Slimehill", "-40", "72", "0"], + ["Slimehill", "-8", "74", "-44"], + ["Diamond Reserve", "-60", "37", "52"], + ["Diamond Reserve", "-21", "25", "72"], + ["Obsidian Sanctuary", "3", "14", "51"], + ["Obsidian Sanctuary", "-71", "13", "5"], + ["Obsidian Sanctuary", "-76", "13", "24"] + ], + "Dwarven Mines": [ + ["Rampart's Quarry", "-21", "208", "-59"], + ["Dwarven Mines", "-139", "220", "-89"], + ["Dwarven Village", "-9", "230", "-135"], + ["Royal Palace", "155", "189", "123"], + ["Divan's Gateway", "34", "102", "86"], + ["Dwarven Mines", "133", "104", "104"], + ["Great Ice Wall", "22", "127", "184"], + ["Goblin Burrows", "-110", "142", "143"], + ["Goblin Burrows", "-116", "142", "154"], + ["Goblin Burrows", "-204", "131", "199"], + ["Dwarven Mines", "-53", "205", "50"] + ], + "Spider's Den": [ + ["None", "-422", "106", "-206"], + ["Spider's Den", "-336", "111", "-253"], + ["Spider's Den", "-222", "74", "-361"], + ["Spider's Den", "-147", "78", "-299"], + ["Spider's Den", "-169", "62", "-289"], + ["Spider's Den", "-185", "135", "-290"], + ["Spider's Den", "-322", "95", "-281"], + ["Spider's Den", "-294", "36", "-274"], + ["Spider's Den", "-160", "62", "-275"], + ["Spider's Den", "-336", "82", "-153"], + ["Spider's Den", "-297", "90", "-169"], + ["Spider's Den", "-279", "127", "-177"], + ["Spider's Den", "-309", "63", "-185"], + ["Spider's Den", "-301", "92", "-171"], + ["Spider's Den", "-140", "85", "-335"], + ["Spider's Den", "-204", "94", "-241"], + ["Spider's Den", "-203", "169", "-320"], + ["Spider's Den", "-309", "66", "-245"], + ["Spider's Den", "-198", "160", "-331"] + ], + "Crimson Isle": [ + ["Aura's Lab", "-383", "71", "-883"], + ["Dragontail", "-690", "122", "-752"], + ["Dragontail", "-606", "154", "-800"], + ["Blazing Volcano", "-343", "235", "-780"], + ["Cathedral", "-31", "178", "-907"], + ["Stronghold", "-352", "191", "-553"], + ["Crimson Isle", "-480", "104", "-593"], + ["Stronghold", "-346", "75", "-552"], + ["Stronghold", "-342", "101", "-484"], + ["Crimson Isle", "-462", "78", "-698"], + ["Crimson Isle", "-361", "69", "-425"], + ["Blazing Volcano", "-396", "108", "-764"], + ["The Wasteland", "-310", "156", "-1008"], + ["Ruins of Ashfang", "-445", "110", "-1026"], + ["Dragontail", "-726", "144", "-891"], + ["Forgotten Skull", "-380", "141", "-1020"], + ["Scarleton Bank", "-79", "139", "-779"], + ["Scarleton", "14", "108", "-769"], + ["Mystic Marsh", "-106", "89", "-883"], + ["Crimson Isle", "-247", "44", "-512"], + ["Stronghold", "-361", "133", "-469"], + ["Dragontail", "-721", "125", "-811"], + ["Burning Desert", "-500", "127", "-795"], + ["The Bastion", "-717", "164", "-981"], + ["Crimson Isle", "-412", "58", "-935"], + ["Mystic Marsh", "-297", "81", "-835"], + ["The Wasteland", "-35", "116", "-1055"], + ["Ruins of Ashfang", "-479", "114", "-972"], + ["Dragontail", "-644", "125", "-689"] + ], + "End": [ + ["Dragon's Nest", "-723", "75", "-222"], + ["Dragon's Nest", "-609", "84", "-230"], + ["The End", "-587", "48", "-293"], + ["The End", "-545", "92", "-257"], + ["The End", "-492", "81", "-275"], + ["The End", "-583", "208", "-272"], + ["The End", "-587", "122", "-276"], + ["The End", "-517", "100", "-295"], + ["The End", "-492", "21", "-175"], + ["Dragon's Nest", "-657", "36", "-201"], + ["The End", "-696", "116", "-256"], + ["Dragon's Nest", "-748", "106", "-284"] + ], + "Park": [ + ["Birch Park", "-315", "89", "-72"], + ["Birch Park", "-294", "85", "31"], + ["Howling Cave", "-390", "61", "-6"], + ["Spruce Woods", "-357", "99", "79"], + ["None", "-450", "113", "-87"], + ["Jungle Island", "-471", "132", "-125"], + ["Jungle Island", "-408", "122", "-92"], + ["Jungle Island", "-454", "120", "-58"], + ["Savanna Woodland", "-404", "136", "6"], + ["None", "-386", "108", "-69"], + ["Dark Thicket", "-370", "112", "-118"] + ], + "Jerry's Workshop": [ + ["Jerry Pond", "-95", "77", "20"], + ["Jerry's Workshop", "74", "109", "-18"], + ["Jerry's Workshop", "-44", "87", "76"], + ["Jerry's Workshop", "56", "108", "64"], + ["Jerry's Workshop", "-7", "108", "107"] + ], + "Rift": [["Fairylosopher Tower", "253", "123", "90"]], + "Dungeon Hub": [ + ["Dungeon Hub", "17", "124", "-58"], + ["Dungeon Hub", "1", "137", "75"], + ["Dungeon Hub", "-139", "46", "-1"], + ["Dungeon Hub", "14", "60", "99"], + ["Dungeon Hub", "-4", "21", "-17"], + ["Dungeon Hub", "-55", "82", "-10"], + ["Dungeon Hub", "10", "164", "-10"] + ] +} diff --git a/json/locations.json b/json/locations.json index f60291e..4b4f5d1 100644 --- a/json/locations.json +++ b/json/locations.json @@ -1,710 +1,158 @@ { - "Hub": { - "Archery Range": [ - "5", - "61", - "-134" - ], - "Auction House": [ - "-24", - "71", - "-90" - ], - "Bank": [ - "-20", - "70", - "-65" - ], - "Bazaar Alley": [ - "-29", - "70", - "-75" - ], - "Blacksmith": [ - "-26", - "69", - "-124" - ], - "Builder's House": [ - "-51", - "71", - "-33" - ], - "Canvas Room": [ - "-10", - "19", - "-27" - ], - "Coal Mine": [ - "-20", - "71", - "-158" - ], - "Colosseum": [ - "82", - "70", - "-51" - ], - "Community Center": [ - "4", - "71", - "-99" - ], - "Election Room": [ - "-4", - "73", - "-114" - ], - "Farm": [ - "41", - "71", - "-138" - ], - "Farmhouse": [ - "18", - "71", - "-67" - ], - "Fashion Shop": [ - "27", - "71", - "-45" - ], - "Fisherman's Hut": [ - "155", - "68", - "46" - ], - "Flower House": [ - "-7", - "71", - "-26" - ], - "Forest": [ - "-98", - "72", - "-31" - ], - "Graveyard": [ - "-120", - "71", - "-83" - ], - "Catacombs Entrance": [ - "-80", - "56", - "-125" - ], - "Hexatorum": [ - "-64", - "70", - "-107" - ], - "Unincorporated": [ - "-25", - "74", - "142" - ], - "Library": [ - "-30", - "69", - "-110" - ], - "Mountain": [ - "-33", - "91", - "8" - ], - "Museum": [ - "-75", - "76", - "80" - ], - "Pet Care": [ - "24", - "70", - "-90" - ], - "Ruins": [ - "-208", - "91", - "47" - ], - "Tavern": [ - "-77", - "71", - "-51" - ], - "Thaumaturgist": [ - "46", - "69", - "-39" - ], - "Village": [ - "-2", - "70", - "-69" - ], - "Weaponsmith": [ - "-11", - "68", - "-127" - ], - "Wilderness": [ - "78", - "72", - "-12" - ], - "Wizard Tower": [ - "42", - "90", - "67" - ] - }, - "The Barn": { - "Windmill": [ - "107", - "82", - "-283" - ] - }, - "Mushroom Desert": { - "Desert Settlement": [ - "159", - "77", - "-368" - ], - "Glowing Mushroom Cave": [ - "231", - "54", - "-496" - ], - "Jake's House": [ - "258", - "184", - "-565" - ], - "Mushroom Gorge": [ - "276", - "47", - "-484" - ], - "Oasis": [ - "136", - "65", - "-474" - ], - "Overgrown Mushroom Cave": [ - "239", - "57", - "-398" - ], - "Shepherd's Keep": [ - "372", - "84", - "-373" - ], - "Trapper's Den": [ - "286", - "102", - "-570" - ], - "Treasure Hunter Camp": [ - "201", - "92", - "-429" - ] - }, - "The Park": { - "Birch Park": [ - "-276", - "82", - "-13" - ], - "Howling Cave": [ - "-335", - "89", - "-55" - ], - "Spruce Woods": [ - "-335", - "89", - "-15" - ], - "Lonely Island": [ - "-339", - "88", - "64" - ], - "Viking Longhouse": [ - "-350", - "91", - "75" - ], - "Dark Thicket": [ - "-353", - "103", - "-23" - ], - "Savanna Woodland": [ - "-379", - "110", - "-19" - ], - "Melody's Plateau": [ - "-397", - "110", - "28" - ], - "Jungle Island": [ - "-406", - "120", - "-39" - ] - }, - "The End": { - "Dragon's Nest": [ - "-600", - "22", - "-275" - ], - "Void Sepulture": [ - "-576", - "7", - "-317" - ], - "Void Slate": [ - "-611", - "5", - "-275" - ], - "Zealot Bruiser Hideout": [ - "-617", - "85", - "-203" - ] - }, - "Crimson Isle": { - "Stronghold": [ - "-360", - "86", - "-517" - ], - "Crimson Fields": [ - "-360", - "83", - "-604" - ], - "Blazing Volcano": [ - "-364", - "127", - "-756" - ], - "Odger's Hut": [ - "-373", - "206", - "-801" - ], - "Plhlegblast Pool": [ - "-383", - "81", - "-702" - ], - "Magma Chamber": [ - "-368", - "63", - "-779" - ], - "Aura's Lab": [ - "-395", - "69", - "-882" - ], - "Matriarch's Lair": [ - "-523", - "40", - "-887" - ], - "Belly of the Beast": [ - "-540", - "25", - "-888" - ], - "Dojo": [ - "-235", - "108", - "-597" - ], - "Burning Desert": [ - "-477", - "95", - "-659" - ], - "Mystic Marsh": [ - "-248", - "75", - "-678" - ], - "Barbarian Outpost": [ - "-425", - "104", - "-679" - ], - "Mage Outpost": [ - "-298", - "93", - "-690" - ], - "Dragontail": [ - "-556", - "98", - "-685" - ], - "Chief's Hut": [ - "-585", - "115", - "-687" - ], - "Dragontail Blacksmith": [ - "-550", - "98", - "-705" - ], - "Dragontail Townsquare": [ - "-631", - "100", - "-807" - ], - "Dragontail Auction House": [ - "-636", - "102", - "-789" - ], - "Dragontail Bazaar": [ - "-624", - "100", - "-797" - ], - "Dragontail Bank": [ - "-612", - "100", - "-786" - ], - "Minion Shop": [ - "-640", - "101", - "-825" - ], - "The Dukedom": [ - "-534", - "117", - "-903" - ], - "The Bastion": [ - "-635", - "111", - "-928" - ], - "Scarleton": [ - "-120", - "99", - "-805" - ], - "Community Center": [ - "-124", - "92", - "-754" - ], - "Throne Room": [ - "-124", - "103", - "-754" - ], - "Mage Council": [ - "-123", - "83", - "-754" - ], - "Scarleton Plaza": [ - "-60", - "107", - "-800" - ], - "Scarleton Minion Shop": [ - "-48", - "107", - "-783" - ], - "Scarleton Auction House": [ - "-60", - "107", - "-751" - ], - "Scarleton Bazaar": [ - "-69", - "107", - "-757" - ], - "Scarleton Bank": [ - "-79", - "107", - "-775" - ], - "Scarleton Blacksmith": [ - "-86", - "92", - "-734" - ], - "Igrupan's House": [ - "-23", - "92", - "-816" - ], - "Igrupan's Chicken Coop": [ - "-32", - "93", - "-818" - ], - "Cathedral": [ - "-37", - "114", - "-883" - ], - "Courtyard": [ - "-179", - "105", - "-857" - ], - "The Wasteland": [ - "-361", - "109", - "-889" - ], - "Ruins of Ashfang": [ - "-484", - "134", - "-996" - ], - "Forgotten Skull": [ - "-372", - "117", - "-991" - ], - "Smoldering Tomb": [ - "-270", - "97", - "-1,000" - ] - }, - "Deep Caverns": { - "Gunpowder Mines": [ - "-3", - "153", - "40" - ], - "Lapis Quarry": [ - "-14", - "129", - "-20" - ], - "Pigmen's Den": [ - "1", - "111", - "42" - ], - "Slimehill": [ - "-6", - "73", - "57" - ], - "Diamond Reserve": [ - "-40", - "46", - "-12" - ], - "Obsidian Sanctuary": [ - "-35", - "16", - "65" - ] - }, - "Dwarven Mines": { - "Aristocrat Passage": [ - "130", - "151", - "130" - ], - "Barracks Of Heroes": [ - "90", - "196", - "180" - ], - "C&C Minecarts Co.": [ - "-120", - "150", - "-20" - ], - "Cliffside Veins": [ - "40", - "128", - "40" - ], - "Divan's Gateway": [ - "0", - "128", - "100" - ], - "Dwarven Tavern": [ - "30", - "202", - "-150" - ], - "Dwarven Village": [ - "-41", - "200", - "-121" - ], - "Far Reserve": [ - "-140", - "149", - "50" - ], - "Forge Basin": [ - "0", - "146", - "-20" - ], - "Gates To The Mines": [ - "31", - "134", - "-54" - ], - "Goblin Burrows": [ - "-100", - "163", - "150" - ], - "Grand Library": [ - "180", - "196", - "180" - ], - "Great Ice Wall": [ - "0", - "128", - "150" - ], - "Hanging Court": [ - "80", - "188", - "140" - ], - "Lava Springs": [ - "60", - "197", - "-15" - ], - "Miner's Guild": [ - "-170", - "221", - "-110" - ], - "Palace Bridge": [ - "129", - "187", - "62" - ], - "Rampart's Quarry": [ - "-100", - "150", - "-20" - ], - "Royal Mines": [ - "130", - "154", - "30" - ], - "Royal Palace": [ - "130", - "195", - "170" - ], - "Royal Quarters": [ - "110", - "196", - "210" - ], - "The Forge": [ - "0", - "149", - "-70" - ], - "The Lift": [ - "-80", - "200", - "-120" - ], - "The Mist": [ - "150", - "76", - "50" - ], - "Upper Mines": [ - "-130", - "174", - "-50" - ] - }, - "Rift Dimension": { - "Wizard Tower": [ - "-47.5", - "122", - "72.5" - ], - "Black Lagoon": [ - "-150", - "67", - "59" - ], - "Rift Gallery Entrance": [ - "87", - "70", - "72" - ], - "West Village": [ - "-80", - "70", - "-40" - ], - "Village Plaza": [ - "-2", - "70", - "-50" - ], - "Living Cave": [ - "15", - "71", - "-158" - ], - "Living Stillness": [ - "15", - "71", - "-158" - ], - "Colosseum": [ - "-104.5", - "68", - "-51.5" - ], - "Barrier Street": [ - "31", - "72", - "-90" - ], - "Stillgore Ch\u00e2teau": [ - "197", - "76", - "42" - ] - } -} \ No newline at end of file + "Hub": { + "Archery Range": ["5", "61", "-134"], + "Auction House": ["-24", "71", "-90"], + "Bank": ["-20", "70", "-65"], + "Bazaar Alley": ["-29", "70", "-75"], + "Blacksmith": ["-26", "69", "-124"], + "Builder's House": ["-51", "71", "-33"], + "Canvas Room": ["-10", "19", "-27"], + "Coal Mine": ["-20", "71", "-158"], + "Colosseum": ["82", "70", "-51"], + "Community Center": ["4", "71", "-99"], + "Election Room": ["-4", "73", "-114"], + "Farm": ["41", "71", "-138"], + "Farmhouse": ["18", "71", "-67"], + "Fashion Shop": ["27", "71", "-45"], + "Fisherman's Hut": ["155", "68", "46"], + "Flower House": ["-7", "71", "-26"], + "Forest": ["-98", "72", "-31"], + "Graveyard": ["-120", "71", "-83"], + "Catacombs Entrance": ["-80", "56", "-125"], + "Hexatorum": ["-64", "70", "-107"], + "Unincorporated": ["-25", "74", "142"], + "Library": ["-30", "69", "-110"], + "Mountain": ["-33", "91", "8"], + "Museum": ["-75", "76", "80"], + "Pet Care": ["24", "70", "-90"], + "Ruins": ["-208", "91", "47"], + "Tavern": ["-77", "71", "-51"], + "Thaumaturgist": ["46", "69", "-39"], + "Village": ["-2", "70", "-69"], + "Weaponsmith": ["-11", "68", "-127"], + "Wilderness": ["78", "72", "-12"], + "Wizard Tower": ["42", "90", "67"] + }, + "The Barn": { + "Windmill": ["107", "82", "-283"] + }, + "Mushroom Desert": { + "Desert Settlement": ["159", "77", "-368"], + "Glowing Mushroom Cave": ["231", "54", "-496"], + "Jake's House": ["258", "184", "-565"], + "Mushroom Gorge": ["276", "47", "-484"], + "Oasis": ["136", "65", "-474"], + "Overgrown Mushroom Cave": ["239", "57", "-398"], + "Shepherd's Keep": ["372", "84", "-373"], + "Trapper's Den": ["286", "102", "-570"], + "Treasure Hunter Camp": ["201", "92", "-429"] + }, + "The Park": { + "Birch Park": ["-276", "82", "-13"], + "Howling Cave": ["-335", "89", "-55"], + "Spruce Woods": ["-335", "89", "-15"], + "Lonely Island": ["-339", "88", "64"], + "Viking Longhouse": ["-350", "91", "75"], + "Dark Thicket": ["-353", "103", "-23"], + "Savanna Woodland": ["-379", "110", "-19"], + "Melody's Plateau": ["-397", "110", "28"], + "Jungle Island": ["-406", "120", "-39"] + }, + "The End": { + "Dragon's Nest": ["-600", "22", "-275"], + "Void Sepulture": ["-576", "7", "-317"], + "Void Slate": ["-611", "5", "-275"], + "Zealot Bruiser Hideout": ["-617", "85", "-203"] + }, + "Crimson Isle": { + "Stronghold": ["-360", "86", "-517"], + "Crimson Fields": ["-360", "83", "-604"], + "Blazing Volcano": ["-364", "127", "-756"], + "Odger's Hut": ["-373", "206", "-801"], + "Plhlegblast Pool": ["-383", "81", "-702"], + "Magma Chamber": ["-368", "63", "-779"], + "Aura's Lab": ["-395", "69", "-882"], + "Matriarch's Lair": ["-523", "40", "-887"], + "Belly of the Beast": ["-540", "25", "-888"], + "Dojo": ["-235", "108", "-597"], + "Burning Desert": ["-477", "95", "-659"], + "Mystic Marsh": ["-248", "75", "-678"], + "Barbarian Outpost": ["-425", "104", "-679"], + "Mage Outpost": ["-298", "93", "-690"], + "Dragontail": ["-556", "98", "-685"], + "Chief's Hut": ["-585", "115", "-687"], + "Dragontail Blacksmith": ["-550", "98", "-705"], + "Dragontail Townsquare": ["-631", "100", "-807"], + "Dragontail Auction House": ["-636", "102", "-789"], + "Dragontail Bazaar": ["-624", "100", "-797"], + "Dragontail Bank": ["-612", "100", "-786"], + "Minion Shop": ["-640", "101", "-825"], + "The Dukedom": ["-534", "117", "-903"], + "The Bastion": ["-635", "111", "-928"], + "Scarleton": ["-120", "99", "-805"], + "Community Center": ["-124", "92", "-754"], + "Throne Room": ["-124", "103", "-754"], + "Mage Council": ["-123", "83", "-754"], + "Scarleton Plaza": ["-60", "107", "-800"], + "Scarleton Minion Shop": ["-48", "107", "-783"], + "Scarleton Auction House": ["-60", "107", "-751"], + "Scarleton Bazaar": ["-69", "107", "-757"], + "Scarleton Bank": ["-79", "107", "-775"], + "Scarleton Blacksmith": ["-86", "92", "-734"], + "Igrupan's House": ["-23", "92", "-816"], + "Igrupan's Chicken Coop": ["-32", "93", "-818"], + "Cathedral": ["-37", "114", "-883"], + "Courtyard": ["-179", "105", "-857"], + "The Wasteland": ["-361", "109", "-889"], + "Ruins of Ashfang": ["-484", "134", "-996"], + "Forgotten Skull": ["-372", "117", "-991"], + "Smoldering Tomb": ["-270", "97", "-1,000"] + }, + "Deep Caverns": { + "Gunpowder Mines": ["-3", "153", "40"], + "Lapis Quarry": ["-14", "129", "-20"], + "Pigmen's Den": ["1", "111", "42"], + "Slimehill": ["-6", "73", "57"], + "Diamond Reserve": ["-40", "46", "-12"], + "Obsidian Sanctuary": ["-35", "16", "65"] + }, + "Dwarven Mines": { + "Aristocrat Passage": ["130", "151", "130"], + "Barracks Of Heroes": ["90", "196", "180"], + "C&C Minecarts Co.": ["-120", "150", "-20"], + "Cliffside Veins": ["40", "128", "40"], + "Divan's Gateway": ["0", "128", "100"], + "Dwarven Tavern": ["30", "202", "-150"], + "Dwarven Village": ["-41", "200", "-121"], + "Far Reserve": ["-140", "149", "50"], + "Forge Basin": ["0", "146", "-20"], + "Gates To The Mines": ["31", "134", "-54"], + "Goblin Burrows": ["-100", "163", "150"], + "Grand Library": ["180", "196", "180"], + "Great Ice Wall": ["0", "128", "150"], + "Hanging Court": ["80", "188", "140"], + "Lava Springs": ["60", "197", "-15"], + "Miner's Guild": ["-170", "221", "-110"], + "Palace Bridge": ["129", "187", "62"], + "Rampart's Quarry": ["-100", "150", "-20"], + "Royal Mines": ["130", "154", "30"], + "Royal Palace": ["130", "195", "170"], + "Royal Quarters": ["110", "196", "210"], + "The Forge": ["0", "149", "-70"], + "The Lift": ["-80", "200", "-120"], + "The Mist": ["150", "76", "50"], + "Upper Mines": ["-130", "174", "-50"] + }, + "Rift Dimension": { + "Wizard Tower": ["-47.5", "122", "72.5"], + "Black Lagoon": ["-150", "67", "59"], + "Rift Gallery Entrance": ["87", "70", "72"], + "West Village": ["-80", "70", "-40"], + "Village Plaza": ["-2", "70", "-50"], + "Living Cave": ["15", "71", "-158"], + "Living Stillness": ["15", "71", "-158"], + "Colosseum": ["-104.5", "68", "-51.5"], + "Barrier Street": ["31", "72", "-90"], + "Stillgore Ch\u00e2teau": ["197", "76", "42"] + } +} diff --git a/json/npcs.json b/json/npcs.json index ba25d59..3cb8839 100644 --- a/json/npcs.json +++ b/json/npcs.json @@ -1,4946 +1,899 @@ { - "Hub": { - "Aatrox": [ - [ - "Community Center", - "6", - "73", - "-111" - ], - [ - "Election Room", - "1", - "32", - "-119" - ] - ], - "Adventurer": [ - [ - "Hub", - "-41", - "70", - "-64" - ] - ], - "Alchemist": [ - [ - "Village", - "41", - "70", - "-63" - ] - ], - "Alixer": [ - [ - "Village", - "0", - "70", - "-93" - ] - ], - "Amelia": [ - [ - "Village", - "-45", - "85", - "-5" - ] - ], - "Anita": [ - [ - "Farmhouse", - "23", - "77", - "-69" - ] - ], - "Andrew": [ - [ - "Village", - "38.5", - "68", - "-46.5" - ] - ], - "Apprentice": [ - [ - "Village", - "20", - "61", - "-9" - ] - ], - "Arthur": [ - [ - "Farm", - "51", - "71", - "-136" - ] - ], - "Auction Agents": [ - [ - "Auction House (Zone)", - "-36", - "73", - "-86" - ], - [ - "Auction House (Zone)", - "-36", - "73", - "-95" - ], - [ - "Auction House (Zone)", - "-31", - "73", - "-86" - ], - [ - "Auction House (Zone)", - "-31", - "73", - "-95" - ] - ], - "Auction Master": [ - [ - "Auction House (Zone)", - "-46", - "73", - "-90" - ] - ], - "Baker": [ - [ - "Village", - "-6", - "70", - "-47" - ] - ], - "Banker": [ - [ - "Bank", - "-24", - "71", - "-58" - ] - ], - "Bartender": [ - [ - "Forest", - "-83", - "71", - "-47" - ], - [ - "Tavern", - "-73", - "71", - "-55" - ] - ], - "Bazaar": [ - [ - "Bazaar Alley", - "-32", - "71", - "-76" - ] - ], - "Bea": [ - [ - "Pet Care", - "31.5", - "70", - "-94.5" - ] - ], - "Biblio": [ - [ - "Community Center", - "7.6", - "71", - "-99.8" - ] - ], - "Billy Joe": [ - [ - "Village", - "-78", - "70", - "-70" - ] - ], - "Bobby Joe": [ - [ - "Village", - "62.5", - "71", - "-115.5" - ] - ], - "Builder": [ - [ - "Builder's House", - "-51.5", - "71", - "-28" - ] - ], - "Carpenter": [ - [ - "Village", - "15", - "72", - "-21" - ] - ], - "Christopher": [ - [ - "Hub", - "-60", - "89", - "-17" - ] - ], - "Clerk Seraphine": [ - [ - "Community Center", - "11", - "71", - "-102" - ], - [ - "Election Room", - "9", - "32", - "-120" - ] - ], - "Cole": [ - [ - "Community Center", - "6", - "73", - "-111" - ], - [ - "Election Room", - "1", - "32", - "-119" - ] - ], - "Curator": [ - [ - "Museum", - "-63", - "77", - "84" - ] - ], - "Dusk": [ - [ - "Blacksmith (Zone)", - "-38", - "68", - "-126" - ] - ], - "Derpy": [ - [ - "Community Center", - "6", - "73", - "-110" - ], - [ - "Election Room", - "1", - "32", - "-119" - ] - ], - "Diana": [ - [ - "Community Center", - "6", - "73", - "-111" - ], - [ - "Election Room", - "1", - "32", - "-119" - ] - ], - "Diaz": [ - [ - "Community Center", - "6", - "73", - "-111" - ], - [ - "Election Room", - "1", - "32", - "-119" - ] - ], - "Duke": [ - [ - "Village", - "-6.5", - "70", - "-89.5" - ] - ], - "Elizabeth": [ - [ - "Community Center", - "0", - "71", - "-101" - ] - ], - "Erihann": [ - [ - "Wizard Tower", - "40", - "100", - "69.5" - ] - ], - "Fann": [ - [ - "Pet Care", - "38.5", - "70", - "-88.5" - ] - ], - "Farm Merchant": [ - [ - "Hub", - "15", - "70", - "-71" - ] - ], - "Farmer": [ - [ - "Farm", - "44", - "72", - "-162" - ] - ], - "Fear Mongerer": [ - [ - "Village", - "-2", - "70", - "-43" - ] - ], - "Felix": [ - [ - "Village", - "-25.75", - "68", - "-103" - ] - ], - "Finnegan": [ - [ - "Community Center", - "6", - "73", - "-111" - ], - [ - "Election Room", - "1", - "32", - "-119" - ] - ], - "Fish Merchant": [ - [ - "Village", - "52", - "68", - "-83" - ] - ], - "Fisherman": [ - [ - "Fisherman's Hut", - "155", - "68", - "47" - ] - ], - "Foxy": [ - [ - "Community Center", - "6", - "73", - "-111" - ], - [ - "Election Room", - "1", - "32", - "-119" - ] - ], - "George": [ - [ - "Pet Care", - "39.5", - "71", - "-100.5" - ] - ], - "Gladiator": [ - [ - "Wilderness", - "123", - "79", - "165" - ] - ], - "Guy": [ - [ - "Village", - "51", - "79", - "-14" - ] - ], - "Hootie": [ - [ - "Pet Care", - "38.5", - "70", - "-90.5" - ] - ], - "Hub Selector": [ - [ - "Village", - "-10", - "70", - "67" - ] - ], - "Jack": [ - [ - "Village", - "-0.5", - "70", - "-54.5" - ] - ], - "Jacob": [ - [ - "Farmhouse", - "23", - "71", - "-69" - ] - ], - "Jacobus": [ - [ - "Thaumaturgist", - "52", - "69", - "-43" - ] - ], - "Jamie": [ - [ - "Village", - "-36", - "68", - "-39" - ] - ], - "Jax": [ - [ - "Archery Range", - "5", - "61", - "-134" - ] - ], - "Mayor Jerry": [ - [ - "Community Center", - "6", - "73", - "-110" - ], - [ - "Election Room", - "1", - "32", - "-119" - ] - ], - "Jim Bob": [ - [ - "Village", - "65.5", - "71", - "-61.5" - ] - ], - "Kat": [ - [ - "Pet Care", - "31", - "70", - "-102" - ] - ], - "Leo": [ - [ - "Village", - "-7.5", - "70", - "-75.5" - ] - ], - "Liam": [ - [ - "Village", - "10.5", - "70", - "-41.5" - ] - ], - "Librarian": [ - [ - "Library", - "-36", - "69", - "-113" - ] - ], - "Lonely Philosopher": [ - [ - "Ruins", - "-250", - "130", - "41" - ] - ], - "Lucius": [ - [ - "Wilderness", - "124", - "73", - "165" - ] - ], - "Lumber Jack": [ - [ - "Forest", - "-112", - "74", - "-36" - ] - ], - "Lumber Merchant": [ - [ - "Village", - "-49", - "70", - "-68" - ] - ], - "Lynn": [ - [ - "Village", - "21.5", - "69", - "-124.5" - ] - ], - "Mad Redstone Engineer": [ - [ - "Builder's House", - "-52", - "65", - "-29" - ] - ], - "Madame Eleanor Q. Goldsworth III": [ - [ - "Museum", - "-50", - "77", - "76" - ] - ], - "Maddox the Slayer": [ - [ - "Tavern", - "-74", - "65", - "-51" - ] - ], - "Malik": [ - [ - "Dungeon Hub", - "-64", - "121", - "5" - ], - [ - "Catacombs Entrance (Zone)", - "-81", - "56", - "-120" - ] - ], - "Marco": [ - [ - "Flower House", - "-10", - "71", - "-14" - ] - ], - "Marina": [ - [ - "Community Center", - "6", - "73", - "-111" - ], - [ - "Election Room", - "1", - "32", - "-119" - ] - ], - "Maths Enjoyer": [ - [ - "Village", - "56", - "69", - "-40" - ] - ], - "Maxwell": [ - [ - "Thaumaturgist", - "46", - "69", - "-34" - ] - ], - "Mine Merchant": [ - [ - "Weaponsmith (Zone)", - "", - "68", - "-125" - ] - ], - "Minikloon": [ - [ - "Farm", - "83.5", - "72", - "-117.5" - ] - ], - "Mort": [ - [ - "Dungeon Hub", - "-69", - "123", - "0" - ], - [ - "Catacombs Entrance (Zone)", - "-89", - "55", - "-129" - ] - ], - "Nicole": [ - [ - "Wizard Tower", - "39.5", - "90", - "72.5" - ] - ], - "Ophelia": [ - [ - "Dungeon Hub", - "-64", - "121", - "-7" - ], - [ - "Catacombs Entrance (Zone)", - "-86", - "55", - "-140" - ] - ], - "Oringo": [ - [ - "Village", - "-4", - "70", - "-44" - ] - ], - "Ozanne": [ - [ - "Thaumaturgist", - "41", - "70", - "-39" - ] - ], - "Pat": [ - [ - "Graveyard", - "-134", - "73", - "-97" - ] - ], - "Paul": [ - [ - "Community Center", - "6", - "73", - "-111" - ], - [ - "Election Room", - "1", - "32", - "-119" - ] - ], - "Plumber Joe": [ - [ - "Village", - "56", - "70", - "-78" - ] - ], - "Romero": [ - [ - "The Park", - "-450", - "91", - "2" - ], - [ - "Flower House", - "-18", - "71", - "-28" - ], - [ - "Graveyard", - "-124", - "72", - "-123" - ], - [ - "Crimson Isle", - "-388", - "95", - "-478" - ], - [ - "Mountain", - "-55", - "169", - "43" - ], - [ - "Gold Mine", - "-22", - "36", - "-339" - ], - [ - "Wilderness", - "146", - "112", - "113" - ], - [ - "Colosseum", - "129", - "61", - "-103" - ], - [ - "Mushroom Desert", - "297", - "87", - "-563" - ], - [ - "The Park", - "-477", - "134", - "-117" - ] - ], - "Rosetta": [ - [ - "Weaponsmith (Zone)", - "-12", - "68", - "-141" - ] - ], - "Ryu": [ - [ - "Village", - "28", - "70", - "-116" - ] - ], - "Salesman": [ - [ - "Village", - "7", - "70", - "-85" - ], - [ - "Village", - "7", - "70", - "-85" - ], - [ - "Village", - "7", - "70", - "-85" - ], - [ - "Village", - "7", - "70", - "-85" - ], - [ - "Village", - "7", - "70", - "-85" - ] - ], - "Scoop": [ - [ - "Mountain", - "5", - "181", - "25" - ] - ], - "Scorpius": [ - [ - "Community Center", - "6", - "73", - "-110" - ], - [ - "Election Room", - "1", - "32", - "-119" - ], - [ - "Dark Auction", - "88", - "50", - "145" - ] - ], - "Security Sloth": [ - [ - "Village", - "2", - "70", - "-76" - ] - ], - "Seymour": [ - [ - "Fashion Shop", - "25", - "64", - "-41" - ] - ], - "Shania": [ - [ - "Farm", - "48", - "72", - "-160" - ] - ], - "Simon": [ - [ - "Village", - "6.5", - "70", - "-91.5" - ] - ], - "Sirius": [ - [ - "Wilderness", - "91", - "75", - "176" - ], - [ - "Dark Auction", - "88", - "50", - "145" - ] - ], - "SkyBlock Animation": [ - [ - "Village", - "-5.5", - "70", - "-91.5" - ] - ], - "Smithmonger": [ - [ - "Blacksmith (Zone)", - "-32", - "69", - "-135" - ] - ], - "Stella": [ - [ - "Village", - "17.5", - "70", - "-99.5" - ] - ], - "Taylor": [ - [ - "Fashion Shop", - "22", - "71", - "-45.5" - ] - ], - "The Handler": [ - [ - "Hexatorum", - "-65", - "70", - "-127" - ] - ], - "Tia the Fairy": [ - [ - "Wilderness", - "129", - "66", - "137" - ] - ], - "Tom": [ - [ - "Village", - "28.5", - "69", - "-57.5" - ] - ], - "Udium": [ - [ - "Wizard Tower", - "44.8", - "104", - "76" - ] - ], - "Vex": [ - [ - "Village", - "-16.5", - "70", - "-81.5" - ] - ], - "Weaponsmith": [ - [ - "Weaponsmith (Zone)", - "-9", - "68", - "-140" - ] - ], - "Wizard": [ - [ - "Wizard Tower", - "46", - "122", - "75" - ] - ], - "Wool Weaver": [ - [ - "Builder's House", - "-47", - "74", - "-30" - ] - ], - "Zog": [ - [ - "Pet Care", - "34", - "71", - "-99" - ] - ] - }, - "Dark Auction": { - "Bob": [ - [ - "Dark Auction", - "84", - "50", - "140" - ], - [ - "Dark Auction", - "92", - "50", - "140" - ] - ], - "Scorpius": [ - [ - "Community Center", - "6", - "73", - "-110" - ], - [ - "Election Room", - "1", - "32", - "-119" - ], - [ - "Dark Auction", - "88", - "50", - "145" - ] - ], - "Sirius": [ - [ - "Wilderness", - "91", - "75", - "176" - ], - [ - "Dark Auction", - "88", - "50", - "145" - ] - ] - }, - "The Park": { - "Campfire Adept": [ - [ - "Dark Thicket", - "-327", - "103", - "-105" - ] - ], - "Campfire Initiate": [ - [ - "Dark Thicket", - "-326", - "103", - "-108" - ], - [ - "Dark Thicket", - "-334", - "103", - "-109" - ] - ], - "Charlie": [ - [ - "Birch Park", - "-286", - "82", - "-17" - ] - ], - "Gustave": [ - [ - "The Park", - "-386", - "89", - "55" - ] - ], - "Juliette": [ - [ - "The Park", - "-477", - "134", - "-117" - ] - ], - "Master Tactician Funk": [ - [ - "Savanna Woodland", - "-462.5", - "110", - "-15.5" - ] - ], - "Melancholic Viking": [ - [ - "Viking Longhouse", - "-360", - "91", - "76" - ] - ], - "Melody": [ - [ - "Melody's Plateau", - "-399", - "110", - "34" - ] - ], - "Old Shaman Nyko": [ - [ - "Howling Cave", - "-379", - "60", - "36" - ] - ], - "Romero": [ - [ - "The Park", - "-450", - "91", - "2" - ], - [ - "Flower House", - "-18", - "71", - "-28" - ], - [ - "Graveyard", - "-124", - "72", - "-123" - ], - [ - "Crimson Isle", - "-388", - "95", - "-478" - ], - [ - "Mountain", - "-55", - "169", - "43" - ], - [ - "Gold Mine", - "-22", - "36", - "-339" - ], - [ - "Wilderness", - "146", - "112", - "113" - ], - [ - "Colosseum", - "129", - "61", - "-103" - ], - [ - "Mushroom Desert", - "297", - "87", - "-563" - ], - [ - "The Park", - "-477", - "134", - "-117" - ] - ], - "Ryan": [ - [ - "Dark Thicket", - "-330", - "103", - "-103" - ] - ], - "Vanessa": [ - [ - "Birch Park", - "-312", - "83", - "-70" - ] - ] - }, - "The Barn": { - "Farmhand": [ - [ - "The Barn", - "144", - "73", - "-240" - ] - ], - "Windmill Operator": [ - [ - "The Barn", - "97", - "89", - "-283" - ], - [ - "The Barn", - "102", - "82", - "-283" - ] - ] - }, - "Mushroom Desert": { - "Beth": [ - [ - "Mushroom Desert", - "163", - "77", - "-359" - ] - ], - "Friendly Hiker": [ - [ - "Desert Settlement", - "182", - "77", - "-379" - ] - ], - "Hungry Hiker": [ - [ - "Mushroom Gorge", - "269", - "48", - "-480" - ], - [ - "Desert Settlement", - "148", - "76", - "-375" - ] - ], - "Hunter Ava": [ - [ - "Mushroom Desert", - "321", - "102", - "-470" - ] - ], - "Jake": [ - [ - "Jake's House", - "261", - "184", - "-565" - ] - ], - "Mason": [ - [ - "Mushroom Desert", - "177", - "77", - "-356" - ] - ], - "Moby": [ - [ - "Glowing Mushroom Cave", - "206", - "43", - "-500" - ] - ], - "Romero": [ - [ - "The Park", - "-450", - "91", - "2" - ], - [ - "Flower House", - "-18", - "71", - "-28" - ], - [ - "Graveyard", - "-124", - "72", - "-123" - ], - [ - "Crimson Isle", - "-388", - "95", - "-478" - ], - [ - "Mountain", - "-55", - "169", - "43" - ], - [ - "Gold Mine", - "-22", - "36", - "-339" - ], - [ - "Wilderness", - "146", - "112", - "113" - ], - [ - "Colosseum", - "129", - "61", - "-103" - ], - [ - "Mushroom Desert", - "297", - "87", - "-563" - ], - [ - "The Park", - "-477", - "134", - "-117" - ] - ], - "Shepherd": [ - [ - "Shepherd's Keep", - "388", - "85", - "-373" - ] - ], - "Talbot": [ - [ - "Trapper's Den", - "285", - "104", - "-549" - ] - ], - "Tammy": [ - [ - "Trapper's Den", - "284", - "104", - "-545" - ] - ], - "Tony": [ - [ - "Trapper's Den", - "278", - "104", - "-545" - ] - ], - "Treasure Hunter": [ - [ - "Mushroom Desert", - "200", - "92", - "-437" - ] - ], - "Trevor The Trapper": [ - [ - "Trapper's Den", - "281", - "104", - "-543" - ] - ] - }, - "Spider's Den": { - "Archaeologist": [ - [ - "Spider's Den", - "-360", - "111", - "-290" - ] - ], - "Bramass Beastslayer": [ - [ - "Grandma's House", - "-271", - "113", - "-196" - ] - ], - "Grandma Wolf": [ - [ - "Grandma's House", - "-282", - "122", - "-191" - ] - ], - "Haymitch": [ - [ - "Spider's Den", - "-203", - "83", - "-237" - ] - ], - "Rick": [ - [ - "Gravel Mines", - "-251", - "70", - "-325" - ] - ], - "Shaggy": [ - [ - "Grandma's House", - "-278", - "121", - "-182" - ] - ], - "Spider Tamer": [ - [ - "Arachne's Sanctuary", - "-298.5", - "62", - "-194.5" - ] - ] - }, - "The End": { - "Gregory": [ - [ - "Dragon's Nest", - "-608", - "22", - "-285" - ] - ], - "Guber": [ - [ - "The End", - "-495", - "121", - "-242" - ] - ], - "Lone Adventurer": [ - [ - "The End", - "-524", - "101", - "-275" - ], - [ - "The End", - "-588", - "22", - "-270" - ] - ], - "Pearl Dealer": [ - [ - "The End", - "-504", - "101", - "-284" - ] - ], - "Tyzzo": [ - [ - "Void Slate", - "-597", - "5", - "-272" - ] - ] - }, - "Crimson Isle": { - "Aranya": [ - [ - "The Wasteland", - "-323", - "152", - "-1008" - ] - ], - "Aura": [ - [ - "Aura's Lab", - "-402.5", - "69", - "-883.5" - ] - ], - "Desperate Engineer": [ - [ - "The Wasteland", - "-289.5", - "127", - "-981.75" - ] - ], - "Kuudra Believer": [ - [ - "Plhlegblast Pool", - "-390.5", - "81", - "-701.5" - ] - ], - "Kuudra Gatekeeper": [ - [ - "Forgotten Skull", - "-371", - "114", - "-1039" - ] - ], - "Master Tao": [ - [ - "Dojo", - "-235", - "108", - "-602" - ] - ], - "Odger": [ - [ - "Odger's Hut", - "-373.5", - "207", - "-810.5" - ] - ], - "Romero": [ - [ - "The Park", - "-450", - "91", - "2" - ], - [ - "Flower House", - "-18", - "71", - "-28" - ], - [ - "Graveyard", - "-124", - "72", - "-123" - ], - [ - "Crimson Isle", - "-388", - "95", - "-478" - ], - [ - "Mountain", - "-55", - "169", - "43" - ], - [ - "Gold Mine", - "-22", - "36", - "-339" - ], - [ - "Wilderness", - "146", - "112", - "113" - ], - [ - "Colosseum", - "129", - "61", - "-103" - ], - [ - "Mushroom Desert", - "297", - "87", - "-563" - ], - [ - "The Park", - "-477", - "134", - "-117" - ] - ], - "Vulcan": [ - [ - "Forgotten Skull", - "-364", - "114", - "-1028" - ] - ], - "Alchemage": [ - [ - "Scarleton", - "-86", - "118", - "-771" - ] - ], - "Alwin": [ - [ - "Scarleton", - "-82", - "92", - "-734" - ] - ], - "Arba": [ - [ - "Scarleton", - "-3.5", - "93", - "-820.5" - ] - ], - "Arbadak": [ - [ - "Scarleton", - "-0.5", - "93", - "-824.5" - ] - ], - "Aviar": [ - [ - "The Wasteland", - "-360.5", - "117", - "-971.5" - ] - ], - "Avorius": [ - [ - "Scarleton", - "-152", - "128", - "-809" - ] - ], - "Belanor": [ - [ - "Scarleton Bank", - "-84", - "108", - "-776" - ] - ], - "Carrolyn": [ - [ - "Scarleton", - "1", - "103", - "-803" - ] - ], - "Colore": [ - [ - "Scarleton", - "-131", - "89", - "-711" - ] - ], - "Cyndarin": [ - [ - "The Wasteland", - "-205", - "112", - "-957" - ] - ], - "Dean": [ - [ - "Cathedral", - "-16", - "123", - "-882" - ] - ], - "Drakuu": [ - [ - "The Wasteland", - "-324.5", - "191", - "-1015.5" - ] - ], - "Edelis": [ - [ - "Scarleton", - "-61.5", - "107", - "-812.5" - ], - [ - "Scarleton", - "-92.5", - "104", - "-821.5" - ] - ], - "Elmar": [ - [ - "Scarleton Auction House", - "-60.5", - "108", - "-746.5" - ] - ], - "Eludore": [ - [ - "Scarleton", - "-101.5", - "91", - "-872.5" - ] - ], - "Ezekiel": [ - [ - "Scarleton", - "-27.5", - "107", - "-770.5" - ] - ], - "Grelius": [ - [ - "Scarleton", - "-131.5", - "101", - "-755.5" - ] - ], - "Gnyl": [ - [ - "The Wasteland", - "-376.5", - "117", - "-971.5" - ] - ], - "Hilda": [ - [ - "Scarleton Minion Shop", - "-46", - "107", - "-779" - ] - ], - "Igrupan": [ - [ - "Igrupan's Chicken Coop", - "-31", - "93", - "-824" - ] - ], - "Kheharad": [ - [ - "Scarleton", - "-111.5", - "99", - "-827.5" - ] - ], - "Kuudra Loremaster": [ - [ - "Scarleton", - "-106.5", - "126", - "-817.5" - ], - [ - "Deep Caverns", - "-256.5", - "146", - "120.5" - ] - ], - "Lys": [ - [ - "Scarleton", - "-108", - "99", - "-803" - ] - ], - "Mage Emissary": [ - [ - "Scarleton", - "-131.5", - "89", - "-722.5" - ] - ], - "Mazakala": [ - [ - "Scarleton", - "-3.5", - "93", - "-825.5" - ] - ], - "Mollim": [ - [ - "Scarleton", - "-132.5", - "100", - "-789.5" - ] - ], - "Nuvian": [ - [ - "Dragontail", - "-693.5", - "102", - "-735.5" - ] - ], - "Pablo": [ - [ - "Scarleton", - "-131", - "128", - "-816" - ] - ], - "Queen Nyx": [ - [ - "Scarleton", - "-117.5", - "105", - "-754.5" - ] - ], - "Rescue Recruiter": [ - [ - "Scarleton", - "-120.5", - "92", - "-751.5" - ], - [ - "Dragontail", - "-581.5", - "100", - "-690.5" - ] - ], - "Rhanora": [ - [ - "Scarleton", - "-144", - "100", - "-829" - ] - ], - "Rollim": [ - [ - "Scarleton", - "-147", - "106", - "-854" - ] - ], - "Rulenor": [ - [ - "Scarleton", - "-26.5", - "122", - "-769.5" - ] - ], - "Scholar Alluin": [ - [ - "Cathedral", - "-15.5", - "114", - "-915.5" - ] - ], - "Seffea": [ - [ - "Scarleton", - "-139", - "99", - "-816.5" - ] - ], - "Selenar": [ - [ - "Scarleton Bazaar", - "-71", - "107", - "-757" - ] - ], - "Udel": [ - [ - "Scarleton Bank", - "-78.5", - "107", - "-788.5" - ] - ], - "Ulyn": [ - [ - "Scarleton", - "-126", - "99", - "-784" - ] - ], - "Undercover Agent": [ - [ - "The Bastion", - "-624.5", - "120", - "-961.5" - ], - [ - "Cathedral", - "-15", - "93", - "-845.0" - ] - ], - "Velyna": [ - [ - "Scarleton", - "-86.5", - "104.5", - "-820.5" - ] - ], - "Ziri": [ - [ - "Scarleton Bank", - "-82", - "114", - "-793" - ] - ], - "An": [ - [ - "Dragontail", - "-621", - "108", - "-741" - ] - ], - "Baar": [ - [ - "Dragontail Auction House", - "-638", - "123", - "-791" - ] - ], - "Barbarian Emissary": [ - [ - "Dragontail", - "-580.5", - "99", - "-710.5" - ] - ], - "Barter": [ - [ - "Dragontail Bazaar", - "-624.5", - "101", - "-794.5" - ] - ], - "Bromm": [ - [ - "Dragontail Auction House", - "-636.5", - "103", - "-784.5" - ] - ], - "Bruto": [ - [ - "Dragontail Bank", - "-613", - "100", - "-787" - ] - ], - "Bruuh": [ - [ - "Dragontail", - "-635", - "124", - "-688" - ] - ], - "Chief Scorn": [ - [ - "Dragontail", - "-580.5", - "115.5", - "-687.5" - ] - ], - "Chihai": [ - [ - "Dragontail", - "-604", - "107", - "-861" - ] - ], - "Chak": [ - [ - "Dragontail", - "-602", - "107", - "-868" - ] - ], - "Chuk": [ - [ - "Dragontail", - "-604", - "107", - "-869" - ] - ], - "Corm": [ - [ - "The Bastion", - "-668", - "126", - "-924" - ] - ], - "Deng": [ - [ - "Dragontail", - "-686.5", - "102", - "-765.5" - ] - ], - "Etc": [ - [ - "Dragontail", - "-606", - "107", - "-868" - ] - ], - "Gris": [ - [ - "Dragontail", - "-684.5", - "124", - "-828.5" - ], - [ - "Dragontail", - "-686", - "116", - "-832" - ] - ], - "Grog": [ - [ - "Dragontail", - "-663.5", - "107", - "-736.5" - ] - ], - "Igor": [ - [ - "Dragontail", - "-549", - "98", - "-707" - ] - ], - "Jine": [ - [ - "Dragontail", - "-545.5", - "119", - "-854.5" - ] - ], - "Kaus": [ - [ - "Dragontail", - "-595", - "113", - "-640" - ] - ], - "Keran": [ - [ - "Dragontail", - "-561.5", - "98", - "-687.5" - ], - [ - "Dragontail", - "-612.5", - "108", - "-669.5" - ] - ], - "Kutral": [ - [ - "Dragontail", - "-658.5", - "107", - "-741.5" - ] - ], - "Kuudra Archaeologist": [ - [ - "Dragontail", - "-570.5", - "111", - "-642.5" - ] - ], - "Lasea": [ - [ - "Dragontail", - "-566", - "108", - "-657" - ] - ], - "Maggie": [ - [ - "Dragontail", - "-592.5", - "123", - "-846.5" - ] - ], - "Marthos": [ - [ - "Minion Shop", - "-644", - "101", - "-825" - ] - ], - "Nall": [ - [ - "Dragontail", - "-657.5", - "107", - "-738.5" - ] - ], - "Pam": [ - [ - "Dragontail", - "-673.5", - "102", - "-767.5" - ], - [ - "Dragontail", - "-662.5", - "102", - "-829.5" - ], - [ - "Dragontail", - "-598.5", - "109", - "-665.5" - ], - [ - "Dragontail", - "-592.5", - "107", - "-723.5" - ], - [ - "Dragontail", - "-575.5", - "111", - "-754.5" - ], - [ - "Dragontail", - "-631.5", - "100", - "-805.5" - ] - ], - "Piglin Guard": [ - [ - "The Wasteland", - "-367.5", - "117", - "-977.5" - ] - ], - "Plenk": [ - [ - "Dragontail", - "-572.5", - "118", - "-739.5" - ] - ], - "Pomtair": [ - [ - "Dragontail", - "-569.5", - "120", - "-775.5" - ] - ], - "Porc": [ - [ - "Dragontail", - "-577", - "118", - "-857" - ] - ], - "Prie": [ - [ - "Dragontail", - "-561.5", - "98", - "-689.5" - ], - [ - "Dragontail", - "-612.5", - "108", - "-661.5" - ] - ], - "Sirih": [ - [ - "Dragontail", - "-699.5", - "131", - "-887.5" - ], - [ - "Dragontail", - "-699.5", - "131", - "-887.5" - ] - ], - "Strux": [ - [ - "Dragontail", - "-596", - "123", - "-845" - ] - ], - "Suus": [ - [ - "Dragontail", - "-615", - "115", - "-706" - ] - ], - "Truu": [ - [ - "Dragontail", - "-613.5", - "122", - "-765.5" - ] - ], - "Turd": [ - [ - "Dragontail", - "-616", - "115", - "-708" - ] - ], - "Ugo": [ - [ - "Dragontail", - "-508.5", - "98", - "-633.5" - ] - ], - "Yoink": [ - [ - "Dragontail", - "-613.5", - "123", - "-786.5" - ] - ] - }, - "Kuudra's Hollow": { - "Weird Sailor": [ - [ - "Kuudra's Hollow", - "-131", - "92", - "-178" - ] - ] - }, - "Gold Mine": { - "Blacksmith": [ - [ - "Blacksmith (Zone)", - "-28", - "69", - "-125" - ], - [ - "Gold Mine", - "-39.5", - "77", - "-299.5" - ], - [ - "Dwarven Village", - "-6.5", - "201", - "-154.5" - ] - ], - "Gold Forger": [ - [ - "Gold Mine", - "-28", - "74", - "-295" - ] - ], - "Iron Forger": [ - [ - "Gold Mine", - "-2", - "75", - "-308" - ] - ], - "Lazy Miner": [ - [ - "Gold Mine", - "-12", - "78", - "-338" - ] - ], - "Romero": [ - [ - "The Park", - "-450", - "91", - "2" - ], - [ - "Flower House", - "-18", - "71", - "-28" - ], - [ - "Graveyard", - "-124", - "72", - "-123" - ], - [ - "Crimson Isle", - "-388", - "95", - "-478" - ], - [ - "Mountain", - "-55", - "169", - "43" - ], - [ - "Gold Mine", - "-22", - "36", - "-339" - ], - [ - "Wilderness", - "146", - "112", - "113" - ], - [ - "Colosseum", - "129", - "61", - "-103" - ], - [ - "Mushroom Desert", - "297", - "87", - "-563" - ], - [ - "The Park", - "-477", - "134", - "-117" - ] - ], - "Rusty": [ - [ - "Gold Mine", - "-21", - "78", - "-326" - ] - ] - }, - "Deep Caverns": { - "Lapis Miner": [ - [ - "Lapis Quarry", - "-10", - "120", - "35" - ] - ], - "Lift Operator": [ - [ - "Gunpowder Mines", - "45", - "150", - "15" - ], - [ - "Lapis Quarry", - "45", - "121", - "15" - ], - [ - "Pigmen's Den", - "45", - "101", - "17" - ], - [ - "Slimehill", - "45", - "66", - "15" - ], - [ - "Diamond Reserve", - "45", - "38", - "15" - ], - [ - "Obsidian Sanctuary", - "45", - "13", - "15" - ], - [ - "Dwarven Mines", - "-80", - "200", - "-124" - ] - ], - "Kuudra Loremaster": [ - [ - "Scarleton", - "-106.5", - "126", - "-817.5" - ], - [ - "Deep Caverns", - "-256.5", - "146", - "120.5" - ] - ], - "Redstone Miner": [ - [ - "Pigmen's Den", - "24", - "104", - "16" - ] - ], - "Rhys": [ - [ - "Deep Caverns", - "31", - "12", - "14" - ], - [ - "Dwarven Mines", - "-39", - "200", - "-120" - ] - ], - "Walter": [ - [ - "Deep Caverns", - "18", - "156", - "-37" - ] - ] - }, - "Dwarven Mines": { - "Banker Broadjaw": [ - [ - "Dwarven Village", - "13", - "201", - "-148" - ] - ], - "Bednom": [ - [ - "Dwarven Village", - "-30", - "214", - "-89" - ] - ], - "Blacksmith": [ - [ - "Blacksmith (Zone)", - "-28", - "69", - "-125" - ], - [ - "Gold Mine", - "-39.5", - "77", - "-299.5" - ], - [ - "Dwarven Village", - "-6.5", - "201", - "-154.5" - ] - ], - "Brammor": [ - [ - "Royal Palace", - "129", - "196", - "196" - ], - [ - "Royal Palace", - "129", - "196", - "196" - ], - [ - "Royal Palace", - "129", - "196", - "196" - ], - [ - "Royal Palace", - "129", - "196", - "196" - ], - [ - "Royal Palace", - "129", - "196", - "196" - ], - [ - "Royal Palace", - "129", - "196", - "196" - ], - [ - "Royal Palace", - "129", - "196", - "196" - ] - ], - "Brarnas": [ - [ - "Dwarven Mines", - "-45", - "192", - "47" - ], - [ - "Dwarven Mines", - "-48", - "192", - "46" - ], - [ - "Dwarven Mines", - "-47", - "192", - "42" - ] - ], - "Bubu": [ - [ - "Dwarven Mines", - "-10", - "201", - "-103" - ] - ], - "Bulvar": [ - [ - "Dwarven Village", - "-16", - "201", - "-99" - ] - ], - "Bylma": [ - [ - "Dwarven Mines", - "-9", - "128", - "59" - ] - ], - "Dalbrek": [ - [ - "Grand Library", - "190", - "216", - "154" - ] - ], - "Dalir": [ - [ - "Dwarven Mines", - "-45", - "192", - "47" - ], - [ - "Dwarven Mines", - "-48", - "192", - "46" - ], - [ - "Dwarven Mines", - "-47", - "192", - "42" - ] - ], - "Dirt Guy": [ - [ - "Dwarven Mines", - "43", - "108", - "175" - ] - ], - "Don Expresso": [ - [ - "Dwarven Mines", - "37", - "202", - "-123" - ] - ], - "Emissary Braum": [ - [ - "Dwarven Mines", - "89.5", - "198", - "-92.5" - ] - ], - "Emissary Carlton": [ - [ - "Rampart's Quarry", - "-72", - "153", - "-10" - ] - ], - "Emissary Ceanna": [ - [ - "Cliffside Veins", - "42", - "134", - "22" - ] - ], - "Emissary Eliza": [ - [ - "Dwarven Village", - "-37", - "200", - "-131" - ] - ], - "Emissary Fraiser": [ - [ - "Upper Mines", - "-132", - "174", - "-50" - ] - ], - "Emissary Lilith": [ - [ - "Lava Springs", - "58", - "198", - "-8" - ] - ], - "Emissary Wilson": [ - [ - "Royal Mines", - "171", - "150", - "31" - ] - ], - "Emkam": [ - [ - "Royal Palace", - "129", - "196", - "196" - ], - [ - "Royal Palace", - "129", - "196", - "196" - ], - [ - "Royal Palace", - "129", - "196", - "196" - ], - [ - "Royal Palace", - "129", - "196", - "196" - ], - [ - "Royal Palace", - "129", - "196", - "196" - ], - [ - "Royal Palace", - "129", - "196", - "196" - ], - [ - "Royal Palace", - "129", - "196", - "196" - ] - ], - "Emmor": [ - [ - "Royal Palace", - "129", - "196", - "196" - ], - [ - "Royal Palace", - "129", - "196", - "196" - ], - [ - "Royal Palace", - "129", - "196", - "196" - ], - [ - "Royal Palace", - "129", - "196", - "196" - ], - [ - "Royal Palace", - "129", - "196", - "196" - ], - [ - "Royal Palace", - "129", - "196", - "196" - ], - [ - "Royal Palace", - "129", - "196", - "196" - ] - ], - "Erren": [ - [ - "Royal Palace", - "129", - "196", - "196" - ], - [ - "Royal Palace", - "129", - "196", - "196" - ], - [ - "Royal Palace", - "129", - "196", - "196" - ], - [ - "Royal Palace", - "129", - "196", - "196" - ], - [ - "Royal Palace", - "129", - "196", - "196" - ], - [ - "Royal Palace", - "129", - "196", - "196" - ], - [ - "Royal Palace", - "129", - "196", - "196" - ] - ], - "Fetchur": [ - [ - "Dwarven Mines", - "84", - "224", - "-118" - ] - ], - "Forger": [ - [ - "The Forge", - "-23", - "151", - "-50" - ], - [ - "The Forge", - "-23", - "151", - "-80" - ], - [ - "The Forge", - "23", - "151", - "-58" - ], - [ - "The Forge", - "23", - "151", - "-88" - ] - ], - "Fragilis": [ - [ - "Dwarven Mines", - "88", - "199", - "-108" - ] - ], - "Fred": [ - [ - "The Forge", - "-2.5", - "149", - "-68.5" - ] - ], - "Geo": [ - [ - "Dwarven Mines", - "87", - "199", - "-116" - ] - ], - "Gimley": [ - [ - "Dwarven Tavern", - "29", - "202", - "-148" - ] - ], - "Grandan": [ - [ - "Royal Palace", - "129", - "196", - "196" - ], - [ - "Royal Palace", - "129", - "196", - "196" - ], - [ - "Royal Palace", - "129", - "196", - "196" - ], - [ - "Royal Palace", - "129", - "196", - "196" - ], - [ - "Royal Palace", - "129", - "196", - "196" - ], - [ - "Royal Palace", - "129", - "196", - "196" - ], - [ - "Royal Palace", - "129", - "196", - "196" - ] - ], - "Gwendolyn": [ - [ - "Dwarven Mines", - "89", - "198", - "-99" - ] - ], - "Hornum": [ - [ - "Dwarven Tavern", - "34", - "202", - "-145" - ] - ], - "Jotraeline Greatforge": [ - [ - "Forge Basin", - "-7", - "145", - "-19" - ] - ], - "Lift Operator": [ - [ - "Gunpowder Mines", - "45", - "150", - "15" - ], - [ - "Lapis Quarry", - "45", - "121", - "15" - ], - [ - "Pigmen's Den", - "45", - "101", - "17" - ], - [ - "Slimehill", - "45", - "66", - "15" - ], - [ - "Diamond Reserve", - "45", - "38", - "15" - ], - [ - "Obsidian Sanctuary", - "45", - "13", - "15" - ], - [ - "Dwarven Mines", - "-80", - "200", - "-124" - ] - ], - "Lumina": [ - [ - "Dwarven Mines", - "77", - "199", - "-110" - ] - ], - "Malmar": [ - [ - "Rampart's Quarry", - "-115", - "150", - "-25" - ], - [ - "Aristocrat Passage", - "105", - "151", - "141" - ], - [ - "Far Reserve", - "-96", - "155", - "88" - ] - ], - "Marigold": [ - [ - "Royal Mines", - "181.5", - "150", - "60.5" - ] - ], - "Old Man Garry": [ - [ - "Dwarven Village", - "5", - "200", - "-109" - ] - ], - "Puzzler": [ - [ - "Grand Library", - "181", - "196", - "135" - ] - ], - "Queen Mismyla": [ - [ - "Royal Palace", - "126", - "195", - "195" - ] - ], - "Redros": [ - [ - "Royal Palace", - "129", - "196", - "196" - ], - [ - "Royal Palace", - "129", - "196", - "196" - ], - [ - "Royal Palace", - "129", - "196", - "196" - ], - [ - "Royal Palace", - "129", - "196", - "196" - ], - [ - "Royal Palace", - "129", - "196", - "196" - ], - [ - "Royal Palace", - "129", - "196", - "196" - ], - [ - "Royal Palace", - "129", - "196", - "196" - ] - ], - "Rhys": [ - [ - "Deep Caverns", - "31", - "12", - "14" - ], - [ - "Dwarven Mines", - "-39", - "200", - "-120" - ] - ], - "Royal Guard": [ - [ - "The Lift", - "-46", - "200", - "-119" - ], - [ - "The Lift", - "-45", - "200", - "-124" - ] - ], - "Royal Resident": [ - [ - "Barracks Of Heroes", - "62", - "204", - "200" - ], - [ - "Barracks Of Heroes", - "65", - "211", - "198" - ] - ], - "Royal Residents": [ - [ - "Royal Quarters", - "164", - "202", - "278" - ], - [ - "Royal Quarters", - "92", - "202", - "276" - ] - ], - "Sargwyn": [ - [ - "Dwarven Tavern", - "34", - "202", - "-149" - ] - ], - "Silnar": [ - [ - "Cliffside Veins", - "53", - "141.5", - "19.5" - ] - ], - "Station Master": [ - [ - "Dwarven Village", - "38", - "201", - "-86" - ] - ], - "Tal Ker": [ - [ - "Grand Library", - "193", - "196", - "205" - ] - ], - "Tarwen": [ - [ - "Dwarven Tavern", - "33", - "202", - "-140" - ] - ], - "Thondin": [ - [ - "Dwarven Mines", - "-45", - "192", - "47" - ], - [ - "Dwarven Mines", - "-48", - "192", - "46" - ], - [ - "Dwarven Mines", - "-47", - "192", - "42" - ] - ], - "Thormyr": [ - [ - "Royal Palace", - "129", - "196", - "196" - ], - [ - "Royal Palace", - "129", - "196", - "196" - ], - [ - "Royal Palace", - "129", - "196", - "196" - ], - [ - "Royal Palace", - "129", - "196", - "196" - ], - [ - "Royal Palace", - "129", - "196", - "196" - ], - [ - "Royal Palace", - "129", - "196", - "196" - ], - [ - "Royal Palace", - "129", - "196", - "196" - ] - ], - "Ticket Master": [ - [ - "Aristocrat Passage", - "102", - "151", - "143" - ], - [ - "Hanging Court", - "85", - "187", - "115" - ], - [ - "Upper Mines", - "-149", - "169", - "-54" - ], - [ - "Forge Basin", - "0", - "145", - "23" - ], - [ - "Gates To The Mines", - "43", - "177", - "-42" - ], - [ - "Far Reserve", - "-96", - "142", - "111" - ] - ], - "Tornora": [ - [ - "Royal Palace", - "136", - "196", - "166" - ] - ] - }, - "Crystal Hollows": { - "Emissary Sisko": [ - [ - "Crystal Nucleus", - "495", - "105", - "556" - ] - ], - "Gemma": [ - [ - "Crystal Nucleus", - "475.5", - "106", - "513.5" - ] - ], - "Geonathan Greatforge": [ - [ - "Crystal Nucleus", - "530", - "106", - "556" - ] - ], - "Kalhuiki Door Guardian": [ - [ - "Jungle Temple", - "", - "", - "" - ], - [ - "Jungle Temple", - "", - "", - "" - ] - ], - "Professor Robot": [ - [ - "Lost Precursor City", - "", - "", - "" - ] - ], - "Queen's Guards": [ - [ - "Goblin Queen's Den", - "", - "", - "" - ], - [ - "Goblin Queen's Den", - "", - "", - "" - ], - [ - "Goblin Queen's Den", - "", - "", - "" - ] - ] - }, - "The Rift": { - "Alabaster": [ - [ - "Shifted Tavern", - "-130.3", - "73", - "166.5" - ] - ], - "Alatar": [ - [ - "Wizard Tower (Rift)", - "-46.5", - "116", - "70.5" - ] - ], - "Alchemist (Rift)": [ - [ - "West Village", - "-49.5", - "70", - "-65.5" - ] - ], - "Argofay Bughunter": [ - [ - "Wyld Woods", - "-85", - "108", - "97" - ] - ], - "Argofay Bugshopper": [ - [ - "Wyld Woods", - "-95", - "110", - "95.25" - ] - ], - "Argofay Serialbather": [ - [ - "Wyld Woods", - "-137.5", - "119.3", - "115.5" - ] - ], - "Argofay Sonfather": [ - [ - "Wyld Woods", - "-96", - "100", - "156.2" - ] - ], - "Argofay Threebrother": [ - [ - "Wyld Woods", - "-98.5", - "76.3", - "153.9" - ], - [ - "Wyld Woods", - "-92.8", - "76", - "108.6" - ], - [ - "Wyld Woods", - "-136", - "75", - "113" - ] - ], - "Argofay Trafficker": [ - [ - "Wyld Woods", - "-150.7", - "112", - "114.5" - ], - [ - "Wyld Woods", - "-137.5", - "97", - "112.5" - ], - [ - "Wyld Woods", - "-91.5", - "90", - "106.5" - ], - [ - "Wyld Woods", - "-136.4", - "88", - "114" - ] - ], - "Argofay Trailblazer": [ - [ - "Wyld Woods", - "-97.5", - "66", - "147.5" - ] - ], - "Argofay Tencounter": [ - [ - "Wyld Woods", - "-93", - "99", - "95" - ] - ], - "Argofay Treemerger": [ - [ - "Wyld Woods", - "-145", - "93", - "140" - ] - ], - "Arora": [ - [ - "Shifted Tavern", - "-128.5", - "73.3", - "168.6" - ] - ], - "Ashera": [ - [ - "Shifted Tavern", - "-124.3", - "73", - "172.5" - ] - ], - "Barry": [ - [ - "Barry HQ", - "-45.5", - "54", - "-142.5" - ] - ], - "Blacksmith (Rift)": [ - [ - "D\u00e9j\u00e0 Vu Alley", - "-1.5", - "68", - "-142" - ] - ], - "Chef": [ - [ - "West Village", - "-103", - "72", - "-104" - ] - ], - "Chester": [ - [ - "Shifted Tavern", - "-130.5", - "73.3", - "173.3" - ] - ], - "Chicken (Rift NPC)": [ - [ - "Dreadfarm", - "-53.9", - "72", - "-158" - ], - [ - "Dreadfarm", - "-57.1", - "72.2", - "-161.1" - ] - ], - "Cosmo": [ - [ - "West Village", - "-66.5", - "70", - "-92.3" - ] - ], - "Cow (Rift)": [ - [ - "Dreadfarm", - "-42.5", - "72", - "-167" - ], - [ - "Dreadfarm", - "-38.4", - "71.7", - "-164.3" - ] - ], - "Cowboy Nick": [ - [ - "Village Plaza", - "-13", - "70", - "-7.1" - ] - ], - "Creed": [ - [ - "Barry Center", - "-15.2", - "73", - "-112" - ], - [ - "Barry HQ", - "-51.5", - "54", - "-137.5" - ] - ], - "Cryptosis": [ - [ - "Village Plaza", - "27.5", - "71", - "-77" - ] - ], - "Dackinoru": [ - [ - "Shifted Tavern", - "-118.3", - "73.0", - "172.5" - ] - ], - "Damia (Rift)": [ - [ - "Barrier Street", - "30.5", - "72", - "-94.5" - ] - ], - "Deer": [ - [ - "Photon Pathway", - "139", - "70", - "-12" - ] - ], - "Detective Amog": [ - [ - "Murder House", - "-49.8", - "68", - "-37.2" - ], - [ - "Village Plaza", - "0.5", - "71", - "-13" - ], - [ - "Around Colosseum", - "-188", - "71", - "-56.7" - ], - [ - "D\u00e9j\u00e0 Vu Alley", - "1.5", - "68", - "-139.5" - ], - [ - "Murder House", - "-49.5", - "68", - "-38.75" - ], - [ - "Taylor's", - "-28.5", - "64", - "-40.5" - ], - [ - "Village Plaza", - "-53", - "68", - "-49.3" - ], - [ - "Murder House", - "-51.1", - "68", - "-40.7" - ] - ], - "Disinfestor": [ - [ - "Infested House", - "-38", - "71", - "-103" - ], - [ - "Infested House", - "-40", - "70", - "-88" - ], - [ - "Infested House", - "-37.5", - "77", - "-93.5" - ], - [ - "Infested House", - "-32.5", - "77", - "-88.5" - ] - ], - "Dr. Emmett": [ - [ - "Mirrorverse", - "", - "", - "To be added" - ], - [ - "Mirrorverse", - "", - "", - "To be added" - ], - [ - "Mirrorverse", - "", - "", - "To be added" - ], - [ - "Mirrorverse", - "", - "", - "To be added" - ], - [ - "Mirrorverse", - "", - "", - "To be added" - ], - [ - "Mirrorverse", - "", - "", - "To be added" - ], - [ - "Mirrorverse", - "", - "", - "To be added" - ], - [ - "Mirrorverse", - "", - "", - "To be added" - ] - ], - "Dr. Hibble": [ - [ - "Black Lagoon", - "-79.5", - "73", - "9.5" - ] - ], - "Dr. Phear": [ - [ - "Leeches Lair", - "-146.3", - "36", - "27.5" - ] - ], - "Dust": [ - [ - "Barry Center", - "-12", - "71", - "-100.2" - ], - [ - "Barry HQ", - "-54.5", - "54", - "-138.5" - ] - ], - "Elise (Rift)": [ - [ - "Wizard Tower (Rift)", - "-50.5", - "122", - "69.5" - ], - [ - "Rift Gallery", - "9.8", - "55", - "76" - ] - ], - "Enigma": [ - [ - "Enigma's Crib", - "", - "", - "Moves around" - ] - ], - "Fafnir": [ - [ - "Shifted Tavern", - "-127.5", - "73", - "172.6" - ] - ], - "Fairylosopher": [ - [ - "Fairylosopher Tower", - "252", - "106", - "76" - ], - [ - "Fairylosopher Tower", - "256", - "130", - "75" - ], - [ - "Fairylosopher Tower", - "256", - "123", - "84" - ], - [ - "Fairylosopher Tower", - "256", - "123", - "87" - ], - [ - "Fairylosopher Tower", - "253", - "123", - "90" - ], - [ - "Fairylosopher Tower", - "250", - "123", - "88" - ], - [ - "Fairylosopher Tower", - "250", - "123", - "85" - ] - ], - "Frankie": [ - [ - "Barry Center", - "-4", - "73", - "-111.4" - ], - [ - "Barry HQ", - "-44.5", - "54", - "-146.5" - ] - ], - "Finplex": [ - [ - "West Village", - "-57.5", - "68", - "-82.5" - ] - ], - "Garlacius": [ - [ - "Shifted Tavern", - "-128.5", - "79.3", - "170.6" - ] - ], - "Grandma": [ - [ - "West Village", - "-70.5", - "64.5", - "-60" - ] - ], - "Gunther": [ - [ - "West Village", - "-68.7", - "71", - "-61.5" - ] - ], - "Harriette": [ - [ - "Barry Center", - "-1.7", - "74", - "-108.9" - ], - [ - "Barry HQ", - "-39.5", - "54", - "-138.5" - ] - ], - "Hound": [ - [ - "Wyld Woods", - "-70", - "66", - "147.5" - ] - ], - "Inverted Sirius": [ - [ - "Wyld Woods", - "-96.5", - "75", - "190.5" - ] - ], - "Jerry (Rift)": [ - [ - "\"Your\" Island", - "-85.5", - "20", - "4.5" - ] - ], - "Joey McPizza": [ - [ - "West Village", - "-105.5", - "72.3", - "-102.5" - ] - ], - "Joliet": [ - [ - "Taylor's", - "-31.5", - "64.5", - "-43" - ] - ], - "Julian": [ - [ - "Oubliette", - "207", - "82", - "130.3" - ] - ], - "Kat (Rift)": [ - [ - "Infested House", - "-34.5", - "70", - "-90.5" - ] - ], - "Kay": [ - [ - "Lagoon Hut", - "-191", - "68", - "58.5" - ] - ], - "Kiermet": [ - [ - "Wyld Woods", - "-134.5", - "67", - "157.5" - ] - ], - "Lazarus": [ - [ - "Shifted Tavern", - "-127.6", - "79.3", - "166.3" - ] - ], - "Lonely \u00c4vae\u00eckx": [ - [ - "Lonely Terrace", - "", - "", - "Moves Between Chairs" - ] - ], - "Maddox The Slayer (Rift)": [ - [ - "Stillgore Ch\u00e2teau", - "204.5", - "77", - "45" - ] - ], - "Marcia": [ - [ - "Village Plaza", - "2.5", - "71", - "-10.5" - ] - ], - "Mole": [ - [ - "Colosseum (Rift)", - "-159.5", - "97", - "-75.5" - ] - ], - "Motes Grubber": [ - [ - "Wyld Woods", - "-92.5", - "65", - "156.5" - ], - [ - "West Village", - "-93", - "70.5", - "-73" - ], - [ - "Stillgore Ch\u00e2teau", - "164.5", - "77", - "125.5" - ] - ], - "Mushroom Guy": [ - [ - "Black Lagoon", - "-178.3125", - "75", - "12.5" - ] - ], - "Nene": [ - [ - "Village Plaza", - "23.5", - "88.5", - "-90.5" - ] - ], - "Phaser": [ - [ - "Photon Pathway", - "59.6", - "70", - "-88.5" - ] - ], - "Plumber Joe (Rift)": [ - [ - "West Village", - "-62", - "70", - "-78" - ] - ], - "Porhtal": [ - [ - "Broken Cage", - "-157.5", - "70.5", - "162" - ] - ], - "Ramero": [ - [ - "Murder House", - "-48.5", - "68", - "-38.5" - ] - ], - "Reed": [ - [ - "Lagoon Hut", - "-212", - "72", - "60.5" - ] - ], - "Ribbit": [ - [ - "Wyld Woods", - "-136.5", - "67", - "158.5" - ] - ], - "Rock": [ - [ - "Living Stillness", - "13.5", - "48.5", - "-186.5" - ], - [ - "Living Stillness", - "-15.5", - "45.6", - "-174.5" - ], - [ - "Living Stillness", - "-24.5", - "46.5", - "-203.5" - ], - [ - "Living Stillness", - "19.5", - "45.3", - "-221.5" - ], - [ - "Living Stillness", - "55.5", - "45.2", - "-199.5" - ], - [ - "Living Stillness", - "55.5", - "45.3", - "-198.5" - ], - [ - "Living Stillness", - "45.5", - "47.1", - "-180.5" - ], - [ - "Living Stillness", - "27.6", - "49", - "-191.5" - ] - ], - "Roger": [ - [ - "Village Plaza", - "-2.5", - "70", - "-43.5" - ] - ], - "Roy": [ - [ - "Lagoon Hut", - "-204.6", - "75", - "49.2" - ] - ], - "Seraphine (Rift)": [ - [ - "Barry Center", - "-16.5", - "71", - "-101" - ], - [ - "Barry Center", - "-16.5", - "71", - "-101" - ] - ], - "Seskel": [ - [ - "Shifted Tavern", - "-128.5", - "73.3", - "164.3" - ] - ], - "Seymour (Rift)": [ - [ - "Taylor's", - "-27", - "71", - "-44.5" - ] - ], - "Shania (Rift)": [ - [ - "Dreadfarm", - "-47.5", - "72", - "-161" - ] - ], - "Sheep (Rift)": [ - [ - "Dreadfarm", - "-58.5", - "72", - "-166.6" - ], - [ - "Dreadfarm", - "-55.1", - "73.4", - "-167.1" - ], - [ - "Dreadfarm", - "-55.1", - "73.4", - "-167.1" - ] - ], - "Shifted": [ - [ - "Shifted Tavern", - "-120.5", - "73", - "174.5" - ] - ], - "Sick Farmer": [ - [ - "Dreadfarm", - "-43", - "72", - "-160" - ] - ], - "Skylark": [ - [ - "West Village", - "-71.5", - "71.15", - "-115" - ] - ], - "Soma": [ - [ - "Barry Center", - "-17", - "74", - "-108" - ], - [ - "Barry HQ", - "-52", - "54", - "-141" - ] - ], - "Sorcerer Okron": [ - [ - "West Village", - "-70.5", - "81", - "-110.5" - ] - ], - "Stain": [ - [ - "Barry Center", - "-6.5", - "73.5", - "-100.2" - ], - [ - "Barry HQ", - "-57.6", - "54", - "-146.1" - ] - ], - "Taylor (Rift)": [ - [ - "Taylor's", - "-32.3", - "71", - "-38.5" - ], - [ - "Taylor's", - "-28.6", - "64", - "-44.9" - ] - ], - "Tel Kar": [ - [ - "Wyld Woods", - "-113", - "69", - "62" - ] - ], - "Tybalt": [ - [ - "Shifted Tavern", - "-129.3", - "79", - "168.5" - ] - ], - "Unbound Explorer": [ - [ - "Otherside", - "-28.5", - "72", - "315.5" - ] - ], - "Unhinged Kloon": [ - [ - "West Village", - "-56.5", - "79", - "-13.7" - ] - ], - "Violetta": [ - [ - "Barry Center", - "-4.2", - "73", - "-105.4" - ], - [ - "Barry HQ", - "-51.5", - "54", - "-147.5" - ] - ], - "Vreike": [ - [ - "Shifted Tavern", - "-124.3", - "73", - "172.5" - ] - ], - "Wizard (Rift)": [ - [ - "Wizard Tower (Rift)", - "-47.5", - "122.5", - "77.5" - ] - ], - "Wizardman": [ - [ - "Wizard Tower", - "-45", - "90", - "69.5" - ] - ], - "Yoshua": [ - [ - "West Village", - "-69", - "71.15", - "-117.5" - ] - ] - }, - "Dungeon Hub": { - "Adventurer Saul": [ - [ - "Dungeon Hub", - "-53", - "119", - "-9" - ] - ], - "Croesus": [ - [ - "Dungeon Hub", - "-21", - "120", - "-14" - ] - ], - "Dungeon Hub Selector": [ - [ - "Dungeon Hub", - "-37", - "119", - "10" - ] - ], - "Guildford": [ - [ - "Dungeon Hub", - "-19", - "86", - "4" - ] - ], - "Malik": [ - [ - "Dungeon Hub", - "-64", - "121", - "5" - ], - [ - "Catacombs Entrance (Zone)", - "-81", - "56", - "-120" - ] - ], - "Mort": [ - [ - "Dungeon Hub", - "-69", - "123", - "0" - ], - [ - "Catacombs Entrance (Zone)", - "-89", - "55", - "-129" - ] - ], - "Ophelia": [ - [ - "Dungeon Hub", - "-64", - "121", - "-7" - ], - [ - "Catacombs Entrance (Zone)", - "-86", - "55", - "-140" - ] - ] - }, - "Winter Island": { - "Banker Barry": [ - [ - "Jerry's Workshop", - "20", - "77", - "44" - ] - ], - "Daan": [ - [ - "Glacial Cave", - "80", - "78.5", - "110" - ] - ], - "Dirk": [ - [ - "Glacial Cave", - "79", - "80.5", - "55" - ] - ], - "Einary": [ - [ - "Einary's Emporium", - "-16.5", - "76", - "63.5" - ] - ], - "Frosty": [ - [ - "Jerry's Workshop", - "-2", - "76", - "92" - ] - ], - "Frozen Alex": [ - [ - "Reflective Pond", - "-74.5", - "74", - "-6.5" - ] - ], - "Gary": [ - [ - "Gary's Shack", - "53", - "103", - "56" - ] - ], - "Generow": [ - [ - "Jerry's Workshop", - "-10.5", - "76", - "93.5" - ] - ], - "Gulliver": [ - [ - "Jerry's Workshop", - "68", - "105", - "33" - ] - ], - "Helena": [ - [ - "Glacial Cave", - "36", - "79", - "75" - ] - ], - "Hendrik": [ - [ - "Hot Springs", - "16.5", - "62", - "-30.5" - ] - ], - "Jerry": [ - [ - "Private Island", - "9", - "100", - "33" - ], - [ - "Jerry's Workshop", - "-34", - "76", - "77" - ], - [ - "Jerry's Workshop", - "6", - "76", - "72" - ], - [ - "Jerry's Workshop", - "-15", - "76", - "49" - ], - [ - "Jerry's Workshop", - "18", - "76", - "31" - ], - [ - "Jerry's Workshop", - "-12", - "76", - "13" - ], - [ - "Village", - "-3", - "70", - "-47" - ] - ], - "Maaike": [ - [ - "Glacial Cave", - "49", - "81", - "50" - ] - ], - "Mees": [ - [ - "Glacial Cave", - "80", - "79", - "72" - ] - ], - "Sherry": [ - [ - "Jerry's Workshop", - "9", - "76", - "99" - ] - ], - "St. Jerry": [ - [ - "Jerry's Workshop", - "-23", - "76", - "92" - ] - ], - "Terry": [ - [ - "Jerry Pond", - "-92", - "78", - "25" - ] - ] - }, - "Private Island": { - "Jerry": [ - [ - "Private Island", - "9", - "100", - "33" - ], - [ - "Jerry's Workshop", - "-34", - "76", - "77" - ], - [ - "Jerry's Workshop", - "6", - "76", - "72" - ], - [ - "Jerry's Workshop", - "-15", - "76", - "49" - ], - [ - "Jerry's Workshop", - "18", - "76", - "31" - ], - [ - "Jerry's Workshop", - "-12", - "76", - "13" - ], - [ - "Village", - "-3", - "70", - "-47" - ] - ], - "Sam": [ - [ - "Private Island", - "8.5", - "100", - "41.5" - ], - [ - "Garden", - "-7.5", - "71", - "-6.5" - ], - [ - "Garden", - "-14.5", - "72", - "-27.5" - ], - [ - "Garden", - "4.5", - "72", - "-21.5" - ] - ] - }, - "Garden": { - "Anita": [ - [ - "Farmhouse", - "23", - "77", - "-69" - ] - ], - "Jacob": [ - [ - "Farmhouse", - "23", - "71", - "-69" - ] - ], - "Pesthunter Pamela": [ - [ - "Garden", - "-24", - "71", - "-14" - ] - ], - "Pesthunter Phillip": [ - [ - "Garden", - "-26", - "71", - "-14" - ] - ], - "Sam": [ - [ - "Private Island", - "8.5", - "100", - "41.5" - ], - [ - "Garden", - "-7.5", - "71", - "-6.5" - ], - [ - "Garden", - "-14.5", - "72", - "-27.5" - ], - [ - "Garden", - "4.5", - "72", - "-21.5" - ] - ] - } -} \ No newline at end of file + "Hub": { + "Aatrox": [ + ["Community Center", "6", "73", "-111"], + ["Election Room", "1", "32", "-119"] + ], + "Adventurer": [["Hub", "-41", "70", "-64"]], + "Alchemist": [["Village", "41", "70", "-63"]], + "Alixer": [["Village", "0", "70", "-93"]], + "Amelia": [["Village", "-45", "85", "-5"]], + "Anita": [["Farmhouse", "23", "77", "-69"]], + "Andrew": [["Village", "38.5", "68", "-46.5"]], + "Apprentice": [["Village", "20", "61", "-9"]], + "Arthur": [["Farm", "51", "71", "-136"]], + "Auction Agents": [ + ["Auction House (Zone)", "-36", "73", "-86"], + ["Auction House (Zone)", "-36", "73", "-95"], + ["Auction House (Zone)", "-31", "73", "-86"], + ["Auction House (Zone)", "-31", "73", "-95"] + ], + "Auction Master": [["Auction House (Zone)", "-46", "73", "-90"]], + "Baker": [["Village", "-6", "70", "-47"]], + "Banker": [["Bank", "-24", "71", "-58"]], + "Bartender": [ + ["Forest", "-83", "71", "-47"], + ["Tavern", "-73", "71", "-55"] + ], + "Bazaar": [["Bazaar Alley", "-32", "71", "-76"]], + "Bea": [["Pet Care", "31.5", "70", "-94.5"]], + "Biblio": [["Community Center", "7.6", "71", "-99.8"]], + "Billy Joe": [["Village", "-78", "70", "-70"]], + "Bobby Joe": [["Village", "62.5", "71", "-115.5"]], + "Builder": [["Builder's House", "-51.5", "71", "-28"]], + "Carpenter": [["Village", "15", "72", "-21"]], + "Christopher": [["Hub", "-60", "89", "-17"]], + "Clerk Seraphine": [ + ["Community Center", "11", "71", "-102"], + ["Election Room", "9", "32", "-120"] + ], + "Cole": [ + ["Community Center", "6", "73", "-111"], + ["Election Room", "1", "32", "-119"] + ], + "Curator": [["Museum", "-63", "77", "84"]], + "Dusk": [["Blacksmith (Zone)", "-38", "68", "-126"]], + "Derpy": [ + ["Community Center", "6", "73", "-110"], + ["Election Room", "1", "32", "-119"] + ], + "Diana": [ + ["Community Center", "6", "73", "-111"], + ["Election Room", "1", "32", "-119"] + ], + "Diaz": [ + ["Community Center", "6", "73", "-111"], + ["Election Room", "1", "32", "-119"] + ], + "Duke": [["Village", "-6.5", "70", "-89.5"]], + "Elizabeth": [["Community Center", "0", "71", "-101"]], + "Erihann": [["Wizard Tower", "40", "100", "69.5"]], + "Fann": [["Pet Care", "38.5", "70", "-88.5"]], + "Farm Merchant": [["Hub", "15", "70", "-71"]], + "Farmer": [["Farm", "44", "72", "-162"]], + "Fear Mongerer": [["Village", "-2", "70", "-43"]], + "Felix": [["Village", "-25.75", "68", "-103"]], + "Finnegan": [ + ["Community Center", "6", "73", "-111"], + ["Election Room", "1", "32", "-119"] + ], + "Fish Merchant": [["Village", "52", "68", "-83"]], + "Fisherman": [["Fisherman's Hut", "155", "68", "47"]], + "Foxy": [ + ["Community Center", "6", "73", "-111"], + ["Election Room", "1", "32", "-119"] + ], + "George": [["Pet Care", "39.5", "71", "-100.5"]], + "Gladiator": [["Wilderness", "123", "79", "165"]], + "Guy": [["Village", "51", "79", "-14"]], + "Hootie": [["Pet Care", "38.5", "70", "-90.5"]], + "Hub Selector": [["Village", "-10", "70", "67"]], + "Jack": [["Village", "-0.5", "70", "-54.5"]], + "Jacob": [["Farmhouse", "23", "71", "-69"]], + "Jacobus": [["Thaumaturgist", "52", "69", "-43"]], + "Jamie": [["Village", "-36", "68", "-39"]], + "Jax": [["Archery Range", "5", "61", "-134"]], + "Mayor Jerry": [ + ["Community Center", "6", "73", "-110"], + ["Election Room", "1", "32", "-119"] + ], + "Jim Bob": [["Village", "65.5", "71", "-61.5"]], + "Kat": [["Pet Care", "31", "70", "-102"]], + "Leo": [["Village", "-7.5", "70", "-75.5"]], + "Liam": [["Village", "10.5", "70", "-41.5"]], + "Librarian": [["Library", "-36", "69", "-113"]], + "Lonely Philosopher": [["Ruins", "-250", "130", "41"]], + "Lucius": [["Wilderness", "124", "73", "165"]], + "Lumber Jack": [["Forest", "-112", "74", "-36"]], + "Lumber Merchant": [["Village", "-49", "70", "-68"]], + "Lynn": [["Village", "21.5", "69", "-124.5"]], + "Mad Redstone Engineer": [["Builder's House", "-52", "65", "-29"]], + "Madame Eleanor Q. Goldsworth III": [["Museum", "-50", "77", "76"]], + "Maddox the Slayer": [["Tavern", "-74", "65", "-51"]], + "Malik": [ + ["Dungeon Hub", "-64", "121", "5"], + ["Catacombs Entrance (Zone)", "-81", "56", "-120"] + ], + "Marco": [["Flower House", "-10", "71", "-14"]], + "Marina": [ + ["Community Center", "6", "73", "-111"], + ["Election Room", "1", "32", "-119"] + ], + "Maths Enjoyer": [["Village", "56", "69", "-40"]], + "Maxwell": [["Thaumaturgist", "46", "69", "-34"]], + "Mine Merchant": [["Weaponsmith (Zone)", "", "68", "-125"]], + "Minikloon": [["Farm", "83.5", "72", "-117.5"]], + "Mort": [ + ["Dungeon Hub", "-69", "123", "0"], + ["Catacombs Entrance (Zone)", "-89", "55", "-129"] + ], + "Nicole": [["Wizard Tower", "39.5", "90", "72.5"]], + "Ophelia": [ + ["Dungeon Hub", "-64", "121", "-7"], + ["Catacombs Entrance (Zone)", "-86", "55", "-140"] + ], + "Oringo": [["Village", "-4", "70", "-44"]], + "Ozanne": [["Thaumaturgist", "41", "70", "-39"]], + "Pat": [["Graveyard", "-134", "73", "-97"]], + "Paul": [ + ["Community Center", "6", "73", "-111"], + ["Election Room", "1", "32", "-119"] + ], + "Plumber Joe": [["Village", "56", "70", "-78"]], + "Romero": [ + ["The Park", "-450", "91", "2"], + ["Flower House", "-18", "71", "-28"], + ["Graveyard", "-124", "72", "-123"], + ["Crimson Isle", "-388", "95", "-478"], + ["Mountain", "-55", "169", "43"], + ["Gold Mine", "-22", "36", "-339"], + ["Wilderness", "146", "112", "113"], + ["Colosseum", "129", "61", "-103"], + ["Mushroom Desert", "297", "87", "-563"], + ["The Park", "-477", "134", "-117"] + ], + "Rosetta": [["Weaponsmith (Zone)", "-12", "68", "-141"]], + "Ryu": [["Village", "28", "70", "-116"]], + "Salesman": [ + ["Village", "7", "70", "-85"], + ["Village", "7", "70", "-85"], + ["Village", "7", "70", "-85"], + ["Village", "7", "70", "-85"], + ["Village", "7", "70", "-85"] + ], + "Scoop": [["Mountain", "5", "181", "25"]], + "Scorpius": [ + ["Community Center", "6", "73", "-110"], + ["Election Room", "1", "32", "-119"], + ["Dark Auction", "88", "50", "145"] + ], + "Security Sloth": [["Village", "2", "70", "-76"]], + "Seymour": [["Fashion Shop", "25", "64", "-41"]], + "Shania": [["Farm", "48", "72", "-160"]], + "Simon": [["Village", "6.5", "70", "-91.5"]], + "Sirius": [ + ["Wilderness", "91", "75", "176"], + ["Dark Auction", "88", "50", "145"] + ], + "SkyBlock Animation": [["Village", "-5.5", "70", "-91.5"]], + "Smithmonger": [["Blacksmith (Zone)", "-32", "69", "-135"]], + "Stella": [["Village", "17.5", "70", "-99.5"]], + "Taylor": [["Fashion Shop", "22", "71", "-45.5"]], + "The Handler": [["Hexatorum", "-65", "70", "-127"]], + "Tia the Fairy": [["Wilderness", "129", "66", "137"]], + "Tom": [["Village", "28.5", "69", "-57.5"]], + "Udium": [["Wizard Tower", "44.8", "104", "76"]], + "Vex": [["Village", "-16.5", "70", "-81.5"]], + "Weaponsmith": [["Weaponsmith (Zone)", "-9", "68", "-140"]], + "Wizard": [["Wizard Tower", "46", "122", "75"]], + "Wool Weaver": [["Builder's House", "-47", "74", "-30"]], + "Zog": [["Pet Care", "34", "71", "-99"]] + }, + "Dark Auction": { + "Bob": [ + ["Dark Auction", "84", "50", "140"], + ["Dark Auction", "92", "50", "140"] + ], + "Scorpius": [ + ["Community Center", "6", "73", "-110"], + ["Election Room", "1", "32", "-119"], + ["Dark Auction", "88", "50", "145"] + ], + "Sirius": [ + ["Wilderness", "91", "75", "176"], + ["Dark Auction", "88", "50", "145"] + ] + }, + "The Park": { + "Campfire Adept": [["Dark Thicket", "-327", "103", "-105"]], + "Campfire Initiate": [ + ["Dark Thicket", "-326", "103", "-108"], + ["Dark Thicket", "-334", "103", "-109"] + ], + "Charlie": [["Birch Park", "-286", "82", "-17"]], + "Gustave": [["The Park", "-386", "89", "55"]], + "Juliette": [["The Park", "-477", "134", "-117"]], + "Master Tactician Funk": [["Savanna Woodland", "-462.5", "110", "-15.5"]], + "Melancholic Viking": [["Viking Longhouse", "-360", "91", "76"]], + "Melody": [["Melody's Plateau", "-399", "110", "34"]], + "Old Shaman Nyko": [["Howling Cave", "-379", "60", "36"]], + "Romero": [ + ["The Park", "-450", "91", "2"], + ["Flower House", "-18", "71", "-28"], + ["Graveyard", "-124", "72", "-123"], + ["Crimson Isle", "-388", "95", "-478"], + ["Mountain", "-55", "169", "43"], + ["Gold Mine", "-22", "36", "-339"], + ["Wilderness", "146", "112", "113"], + ["Colosseum", "129", "61", "-103"], + ["Mushroom Desert", "297", "87", "-563"], + ["The Park", "-477", "134", "-117"] + ], + "Ryan": [["Dark Thicket", "-330", "103", "-103"]], + "Vanessa": [["Birch Park", "-312", "83", "-70"]] + }, + "The Barn": { + "Farmhand": [["The Barn", "144", "73", "-240"]], + "Windmill Operator": [ + ["The Barn", "97", "89", "-283"], + ["The Barn", "102", "82", "-283"] + ] + }, + "Mushroom Desert": { + "Beth": [["Mushroom Desert", "163", "77", "-359"]], + "Friendly Hiker": [["Desert Settlement", "182", "77", "-379"]], + "Hungry Hiker": [ + ["Mushroom Gorge", "269", "48", "-480"], + ["Desert Settlement", "148", "76", "-375"] + ], + "Hunter Ava": [["Mushroom Desert", "321", "102", "-470"]], + "Jake": [["Jake's House", "261", "184", "-565"]], + "Mason": [["Mushroom Desert", "177", "77", "-356"]], + "Moby": [["Glowing Mushroom Cave", "206", "43", "-500"]], + "Romero": [ + ["The Park", "-450", "91", "2"], + ["Flower House", "-18", "71", "-28"], + ["Graveyard", "-124", "72", "-123"], + ["Crimson Isle", "-388", "95", "-478"], + ["Mountain", "-55", "169", "43"], + ["Gold Mine", "-22", "36", "-339"], + ["Wilderness", "146", "112", "113"], + ["Colosseum", "129", "61", "-103"], + ["Mushroom Desert", "297", "87", "-563"], + ["The Park", "-477", "134", "-117"] + ], + "Shepherd": [["Shepherd's Keep", "388", "85", "-373"]], + "Talbot": [["Trapper's Den", "285", "104", "-549"]], + "Tammy": [["Trapper's Den", "284", "104", "-545"]], + "Tony": [["Trapper's Den", "278", "104", "-545"]], + "Treasure Hunter": [["Mushroom Desert", "200", "92", "-437"]], + "Trevor The Trapper": [["Trapper's Den", "281", "104", "-543"]] + }, + "Spider's Den": { + "Archaeologist": [["Spider's Den", "-360", "111", "-290"]], + "Bramass Beastslayer": [["Grandma's House", "-271", "113", "-196"]], + "Grandma Wolf": [["Grandma's House", "-282", "122", "-191"]], + "Haymitch": [["Spider's Den", "-203", "83", "-237"]], + "Rick": [["Gravel Mines", "-251", "70", "-325"]], + "Shaggy": [["Grandma's House", "-278", "121", "-182"]], + "Spider Tamer": [["Arachne's Sanctuary", "-298.5", "62", "-194.5"]] + }, + "The End": { + "Gregory": [["Dragon's Nest", "-608", "22", "-285"]], + "Guber": [["The End", "-495", "121", "-242"]], + "Lone Adventurer": [ + ["The End", "-524", "101", "-275"], + ["The End", "-588", "22", "-270"] + ], + "Pearl Dealer": [["The End", "-504", "101", "-284"]], + "Tyzzo": [["Void Slate", "-597", "5", "-272"]] + }, + "Crimson Isle": { + "Aranya": [["The Wasteland", "-323", "152", "-1008"]], + "Aura": [["Aura's Lab", "-402.5", "69", "-883.5"]], + "Desperate Engineer": [["The Wasteland", "-289.5", "127", "-981.75"]], + "Kuudra Believer": [["Plhlegblast Pool", "-390.5", "81", "-701.5"]], + "Kuudra Gatekeeper": [["Forgotten Skull", "-371", "114", "-1039"]], + "Master Tao": [["Dojo", "-235", "108", "-602"]], + "Odger": [["Odger's Hut", "-373.5", "207", "-810.5"]], + "Romero": [ + ["The Park", "-450", "91", "2"], + ["Flower House", "-18", "71", "-28"], + ["Graveyard", "-124", "72", "-123"], + ["Crimson Isle", "-388", "95", "-478"], + ["Mountain", "-55", "169", "43"], + ["Gold Mine", "-22", "36", "-339"], + ["Wilderness", "146", "112", "113"], + ["Colosseum", "129", "61", "-103"], + ["Mushroom Desert", "297", "87", "-563"], + ["The Park", "-477", "134", "-117"] + ], + "Vulcan": [["Forgotten Skull", "-364", "114", "-1028"]], + "Alchemage": [["Scarleton", "-86", "118", "-771"]], + "Alwin": [["Scarleton", "-82", "92", "-734"]], + "Arba": [["Scarleton", "-3.5", "93", "-820.5"]], + "Arbadak": [["Scarleton", "-0.5", "93", "-824.5"]], + "Aviar": [["The Wasteland", "-360.5", "117", "-971.5"]], + "Avorius": [["Scarleton", "-152", "128", "-809"]], + "Belanor": [["Scarleton Bank", "-84", "108", "-776"]], + "Carrolyn": [["Scarleton", "1", "103", "-803"]], + "Colore": [["Scarleton", "-131", "89", "-711"]], + "Cyndarin": [["The Wasteland", "-205", "112", "-957"]], + "Dean": [["Cathedral", "-16", "123", "-882"]], + "Drakuu": [["The Wasteland", "-324.5", "191", "-1015.5"]], + "Edelis": [ + ["Scarleton", "-61.5", "107", "-812.5"], + ["Scarleton", "-92.5", "104", "-821.5"] + ], + "Elmar": [["Scarleton Auction House", "-60.5", "108", "-746.5"]], + "Eludore": [["Scarleton", "-101.5", "91", "-872.5"]], + "Ezekiel": [["Scarleton", "-27.5", "107", "-770.5"]], + "Grelius": [["Scarleton", "-131.5", "101", "-755.5"]], + "Gnyl": [["The Wasteland", "-376.5", "117", "-971.5"]], + "Hilda": [["Scarleton Minion Shop", "-46", "107", "-779"]], + "Igrupan": [["Igrupan's Chicken Coop", "-31", "93", "-824"]], + "Kheharad": [["Scarleton", "-111.5", "99", "-827.5"]], + "Kuudra Loremaster": [ + ["Scarleton", "-106.5", "126", "-817.5"], + ["Deep Caverns", "-256.5", "146", "120.5"] + ], + "Lys": [["Scarleton", "-108", "99", "-803"]], + "Mage Emissary": [["Scarleton", "-131.5", "89", "-722.5"]], + "Mazakala": [["Scarleton", "-3.5", "93", "-825.5"]], + "Mollim": [["Scarleton", "-132.5", "100", "-789.5"]], + "Nuvian": [["Dragontail", "-693.5", "102", "-735.5"]], + "Pablo": [["Scarleton", "-131", "128", "-816"]], + "Queen Nyx": [["Scarleton", "-117.5", "105", "-754.5"]], + "Rescue Recruiter": [ + ["Scarleton", "-120.5", "92", "-751.5"], + ["Dragontail", "-581.5", "100", "-690.5"] + ], + "Rhanora": [["Scarleton", "-144", "100", "-829"]], + "Rollim": [["Scarleton", "-147", "106", "-854"]], + "Rulenor": [["Scarleton", "-26.5", "122", "-769.5"]], + "Scholar Alluin": [["Cathedral", "-15.5", "114", "-915.5"]], + "Seffea": [["Scarleton", "-139", "99", "-816.5"]], + "Selenar": [["Scarleton Bazaar", "-71", "107", "-757"]], + "Udel": [["Scarleton Bank", "-78.5", "107", "-788.5"]], + "Ulyn": [["Scarleton", "-126", "99", "-784"]], + "Undercover Agent": [ + ["The Bastion", "-624.5", "120", "-961.5"], + ["Cathedral", "-15", "93", "-845.0"] + ], + "Velyna": [["Scarleton", "-86.5", "104.5", "-820.5"]], + "Ziri": [["Scarleton Bank", "-82", "114", "-793"]], + "An": [["Dragontail", "-621", "108", "-741"]], + "Baar": [["Dragontail Auction House", "-638", "123", "-791"]], + "Barbarian Emissary": [["Dragontail", "-580.5", "99", "-710.5"]], + "Barter": [["Dragontail Bazaar", "-624.5", "101", "-794.5"]], + "Bromm": [["Dragontail Auction House", "-636.5", "103", "-784.5"]], + "Bruto": [["Dragontail Bank", "-613", "100", "-787"]], + "Bruuh": [["Dragontail", "-635", "124", "-688"]], + "Chief Scorn": [["Dragontail", "-580.5", "115.5", "-687.5"]], + "Chihai": [["Dragontail", "-604", "107", "-861"]], + "Chak": [["Dragontail", "-602", "107", "-868"]], + "Chuk": [["Dragontail", "-604", "107", "-869"]], + "Corm": [["The Bastion", "-668", "126", "-924"]], + "Deng": [["Dragontail", "-686.5", "102", "-765.5"]], + "Etc": [["Dragontail", "-606", "107", "-868"]], + "Gris": [ + ["Dragontail", "-684.5", "124", "-828.5"], + ["Dragontail", "-686", "116", "-832"] + ], + "Grog": [["Dragontail", "-663.5", "107", "-736.5"]], + "Igor": [["Dragontail", "-549", "98", "-707"]], + "Jine": [["Dragontail", "-545.5", "119", "-854.5"]], + "Kaus": [["Dragontail", "-595", "113", "-640"]], + "Keran": [ + ["Dragontail", "-561.5", "98", "-687.5"], + ["Dragontail", "-612.5", "108", "-669.5"] + ], + "Kutral": [["Dragontail", "-658.5", "107", "-741.5"]], + "Kuudra Archaeologist": [["Dragontail", "-570.5", "111", "-642.5"]], + "Lasea": [["Dragontail", "-566", "108", "-657"]], + "Maggie": [["Dragontail", "-592.5", "123", "-846.5"]], + "Marthos": [["Minion Shop", "-644", "101", "-825"]], + "Nall": [["Dragontail", "-657.5", "107", "-738.5"]], + "Pam": [ + ["Dragontail", "-673.5", "102", "-767.5"], + ["Dragontail", "-662.5", "102", "-829.5"], + ["Dragontail", "-598.5", "109", "-665.5"], + ["Dragontail", "-592.5", "107", "-723.5"], + ["Dragontail", "-575.5", "111", "-754.5"], + ["Dragontail", "-631.5", "100", "-805.5"] + ], + "Piglin Guard": [["The Wasteland", "-367.5", "117", "-977.5"]], + "Plenk": [["Dragontail", "-572.5", "118", "-739.5"]], + "Pomtair": [["Dragontail", "-569.5", "120", "-775.5"]], + "Porc": [["Dragontail", "-577", "118", "-857"]], + "Prie": [ + ["Dragontail", "-561.5", "98", "-689.5"], + ["Dragontail", "-612.5", "108", "-661.5"] + ], + "Sirih": [ + ["Dragontail", "-699.5", "131", "-887.5"], + ["Dragontail", "-699.5", "131", "-887.5"] + ], + "Strux": [["Dragontail", "-596", "123", "-845"]], + "Suus": [["Dragontail", "-615", "115", "-706"]], + "Truu": [["Dragontail", "-613.5", "122", "-765.5"]], + "Turd": [["Dragontail", "-616", "115", "-708"]], + "Ugo": [["Dragontail", "-508.5", "98", "-633.5"]], + "Yoink": [["Dragontail", "-613.5", "123", "-786.5"]] + }, + "Kuudra's Hollow": { + "Weird Sailor": [["Kuudra's Hollow", "-131", "92", "-178"]] + }, + "Gold Mine": { + "Blacksmith": [ + ["Blacksmith (Zone)", "-28", "69", "-125"], + ["Gold Mine", "-39.5", "77", "-299.5"], + ["Dwarven Village", "-6.5", "201", "-154.5"] + ], + "Gold Forger": [["Gold Mine", "-28", "74", "-295"]], + "Iron Forger": [["Gold Mine", "-2", "75", "-308"]], + "Lazy Miner": [["Gold Mine", "-12", "78", "-338"]], + "Romero": [ + ["The Park", "-450", "91", "2"], + ["Flower House", "-18", "71", "-28"], + ["Graveyard", "-124", "72", "-123"], + ["Crimson Isle", "-388", "95", "-478"], + ["Mountain", "-55", "169", "43"], + ["Gold Mine", "-22", "36", "-339"], + ["Wilderness", "146", "112", "113"], + ["Colosseum", "129", "61", "-103"], + ["Mushroom Desert", "297", "87", "-563"], + ["The Park", "-477", "134", "-117"] + ], + "Rusty": [["Gold Mine", "-21", "78", "-326"]] + }, + "Deep Caverns": { + "Lapis Miner": [["Lapis Quarry", "-10", "120", "35"]], + "Lift Operator": [ + ["Gunpowder Mines", "45", "150", "15"], + ["Lapis Quarry", "45", "121", "15"], + ["Pigmen's Den", "45", "101", "17"], + ["Slimehill", "45", "66", "15"], + ["Diamond Reserve", "45", "38", "15"], + ["Obsidian Sanctuary", "45", "13", "15"], + ["Dwarven Mines", "-80", "200", "-124"] + ], + "Kuudra Loremaster": [ + ["Scarleton", "-106.5", "126", "-817.5"], + ["Deep Caverns", "-256.5", "146", "120.5"] + ], + "Redstone Miner": [["Pigmen's Den", "24", "104", "16"]], + "Rhys": [ + ["Deep Caverns", "31", "12", "14"], + ["Dwarven Mines", "-39", "200", "-120"] + ], + "Walter": [["Deep Caverns", "18", "156", "-37"]] + }, + "Dwarven Mines": { + "Banker Broadjaw": [["Dwarven Village", "13", "201", "-148"]], + "Bednom": [["Dwarven Village", "-30", "214", "-89"]], + "Blacksmith": [ + ["Blacksmith (Zone)", "-28", "69", "-125"], + ["Gold Mine", "-39.5", "77", "-299.5"], + ["Dwarven Village", "-6.5", "201", "-154.5"] + ], + "Brammor": [ + ["Royal Palace", "129", "196", "196"], + ["Royal Palace", "129", "196", "196"], + ["Royal Palace", "129", "196", "196"], + ["Royal Palace", "129", "196", "196"], + ["Royal Palace", "129", "196", "196"], + ["Royal Palace", "129", "196", "196"], + ["Royal Palace", "129", "196", "196"] + ], + "Brarnas": [ + ["Dwarven Mines", "-45", "192", "47"], + ["Dwarven Mines", "-48", "192", "46"], + ["Dwarven Mines", "-47", "192", "42"] + ], + "Bubu": [["Dwarven Mines", "-10", "201", "-103"]], + "Bulvar": [["Dwarven Village", "-16", "201", "-99"]], + "Bylma": [["Dwarven Mines", "-9", "128", "59"]], + "Dalbrek": [["Grand Library", "190", "216", "154"]], + "Dalir": [ + ["Dwarven Mines", "-45", "192", "47"], + ["Dwarven Mines", "-48", "192", "46"], + ["Dwarven Mines", "-47", "192", "42"] + ], + "Dirt Guy": [["Dwarven Mines", "43", "108", "175"]], + "Don Expresso": [["Dwarven Mines", "37", "202", "-123"]], + "Emissary Braum": [["Dwarven Mines", "89.5", "198", "-92.5"]], + "Emissary Carlton": [["Rampart's Quarry", "-72", "153", "-10"]], + "Emissary Ceanna": [["Cliffside Veins", "42", "134", "22"]], + "Emissary Eliza": [["Dwarven Village", "-37", "200", "-131"]], + "Emissary Fraiser": [["Upper Mines", "-132", "174", "-50"]], + "Emissary Lilith": [["Lava Springs", "58", "198", "-8"]], + "Emissary Wilson": [["Royal Mines", "171", "150", "31"]], + "Emkam": [ + ["Royal Palace", "129", "196", "196"], + ["Royal Palace", "129", "196", "196"], + ["Royal Palace", "129", "196", "196"], + ["Royal Palace", "129", "196", "196"], + ["Royal Palace", "129", "196", "196"], + ["Royal Palace", "129", "196", "196"], + ["Royal Palace", "129", "196", "196"] + ], + "Emmor": [ + ["Royal Palace", "129", "196", "196"], + ["Royal Palace", "129", "196", "196"], + ["Royal Palace", "129", "196", "196"], + ["Royal Palace", "129", "196", "196"], + ["Royal Palace", "129", "196", "196"], + ["Royal Palace", "129", "196", "196"], + ["Royal Palace", "129", "196", "196"] + ], + "Erren": [ + ["Royal Palace", "129", "196", "196"], + ["Royal Palace", "129", "196", "196"], + ["Royal Palace", "129", "196", "196"], + ["Royal Palace", "129", "196", "196"], + ["Royal Palace", "129", "196", "196"], + ["Royal Palace", "129", "196", "196"], + ["Royal Palace", "129", "196", "196"] + ], + "Fetchur": [["Dwarven Mines", "84", "224", "-118"]], + "Forger": [ + ["The Forge", "-23", "151", "-50"], + ["The Forge", "-23", "151", "-80"], + ["The Forge", "23", "151", "-58"], + ["The Forge", "23", "151", "-88"] + ], + "Fragilis": [["Dwarven Mines", "88", "199", "-108"]], + "Fred": [["The Forge", "-2.5", "149", "-68.5"]], + "Geo": [["Dwarven Mines", "87", "199", "-116"]], + "Gimley": [["Dwarven Tavern", "29", "202", "-148"]], + "Grandan": [ + ["Royal Palace", "129", "196", "196"], + ["Royal Palace", "129", "196", "196"], + ["Royal Palace", "129", "196", "196"], + ["Royal Palace", "129", "196", "196"], + ["Royal Palace", "129", "196", "196"], + ["Royal Palace", "129", "196", "196"], + ["Royal Palace", "129", "196", "196"] + ], + "Gwendolyn": [["Dwarven Mines", "89", "198", "-99"]], + "Hornum": [["Dwarven Tavern", "34", "202", "-145"]], + "Jotraeline Greatforge": [["Forge Basin", "-7", "145", "-19"]], + "Lift Operator": [ + ["Gunpowder Mines", "45", "150", "15"], + ["Lapis Quarry", "45", "121", "15"], + ["Pigmen's Den", "45", "101", "17"], + ["Slimehill", "45", "66", "15"], + ["Diamond Reserve", "45", "38", "15"], + ["Obsidian Sanctuary", "45", "13", "15"], + ["Dwarven Mines", "-80", "200", "-124"] + ], + "Lumina": [["Dwarven Mines", "77", "199", "-110"]], + "Malmar": [ + ["Rampart's Quarry", "-115", "150", "-25"], + ["Aristocrat Passage", "105", "151", "141"], + ["Far Reserve", "-96", "155", "88"] + ], + "Marigold": [["Royal Mines", "181.5", "150", "60.5"]], + "Old Man Garry": [["Dwarven Village", "5", "200", "-109"]], + "Puzzler": [["Grand Library", "181", "196", "135"]], + "Queen Mismyla": [["Royal Palace", "126", "195", "195"]], + "Redros": [ + ["Royal Palace", "129", "196", "196"], + ["Royal Palace", "129", "196", "196"], + ["Royal Palace", "129", "196", "196"], + ["Royal Palace", "129", "196", "196"], + ["Royal Palace", "129", "196", "196"], + ["Royal Palace", "129", "196", "196"], + ["Royal Palace", "129", "196", "196"] + ], + "Rhys": [ + ["Deep Caverns", "31", "12", "14"], + ["Dwarven Mines", "-39", "200", "-120"] + ], + "Royal Guard": [ + ["The Lift", "-46", "200", "-119"], + ["The Lift", "-45", "200", "-124"] + ], + "Royal Resident": [ + ["Barracks Of Heroes", "62", "204", "200"], + ["Barracks Of Heroes", "65", "211", "198"] + ], + "Royal Residents": [ + ["Royal Quarters", "164", "202", "278"], + ["Royal Quarters", "92", "202", "276"] + ], + "Sargwyn": [["Dwarven Tavern", "34", "202", "-149"]], + "Silnar": [["Cliffside Veins", "53", "141.5", "19.5"]], + "Station Master": [["Dwarven Village", "38", "201", "-86"]], + "Tal Ker": [["Grand Library", "193", "196", "205"]], + "Tarwen": [["Dwarven Tavern", "33", "202", "-140"]], + "Thondin": [ + ["Dwarven Mines", "-45", "192", "47"], + ["Dwarven Mines", "-48", "192", "46"], + ["Dwarven Mines", "-47", "192", "42"] + ], + "Thormyr": [ + ["Royal Palace", "129", "196", "196"], + ["Royal Palace", "129", "196", "196"], + ["Royal Palace", "129", "196", "196"], + ["Royal Palace", "129", "196", "196"], + ["Royal Palace", "129", "196", "196"], + ["Royal Palace", "129", "196", "196"], + ["Royal Palace", "129", "196", "196"] + ], + "Ticket Master": [ + ["Aristocrat Passage", "102", "151", "143"], + ["Hanging Court", "85", "187", "115"], + ["Upper Mines", "-149", "169", "-54"], + ["Forge Basin", "0", "145", "23"], + ["Gates To The Mines", "43", "177", "-42"], + ["Far Reserve", "-96", "142", "111"] + ], + "Tornora": [["Royal Palace", "136", "196", "166"]] + }, + "Crystal Hollows": { + "Emissary Sisko": [["Crystal Nucleus", "495", "105", "556"]], + "Gemma": [["Crystal Nucleus", "475.5", "106", "513.5"]], + "Geonathan Greatforge": [["Crystal Nucleus", "530", "106", "556"]], + "Kalhuiki Door Guardian": [ + ["Jungle Temple", "", "", ""], + ["Jungle Temple", "", "", ""] + ], + "Professor Robot": [["Lost Precursor City", "", "", ""]], + "Queen's Guards": [ + ["Goblin Queen's Den", "", "", ""], + ["Goblin Queen's Den", "", "", ""], + ["Goblin Queen's Den", "", "", ""] + ] + }, + "The Rift": { + "Alabaster": [["Shifted Tavern", "-130.3", "73", "166.5"]], + "Alatar": [["Wizard Tower (Rift)", "-46.5", "116", "70.5"]], + "Alchemist (Rift)": [["West Village", "-49.5", "70", "-65.5"]], + "Argofay Bughunter": [["Wyld Woods", "-85", "108", "97"]], + "Argofay Bugshopper": [["Wyld Woods", "-95", "110", "95.25"]], + "Argofay Serialbather": [["Wyld Woods", "-137.5", "119.3", "115.5"]], + "Argofay Sonfather": [["Wyld Woods", "-96", "100", "156.2"]], + "Argofay Threebrother": [ + ["Wyld Woods", "-98.5", "76.3", "153.9"], + ["Wyld Woods", "-92.8", "76", "108.6"], + ["Wyld Woods", "-136", "75", "113"] + ], + "Argofay Trafficker": [ + ["Wyld Woods", "-150.7", "112", "114.5"], + ["Wyld Woods", "-137.5", "97", "112.5"], + ["Wyld Woods", "-91.5", "90", "106.5"], + ["Wyld Woods", "-136.4", "88", "114"] + ], + "Argofay Trailblazer": [["Wyld Woods", "-97.5", "66", "147.5"]], + "Argofay Tencounter": [["Wyld Woods", "-93", "99", "95"]], + "Argofay Treemerger": [["Wyld Woods", "-145", "93", "140"]], + "Arora": [["Shifted Tavern", "-128.5", "73.3", "168.6"]], + "Ashera": [["Shifted Tavern", "-124.3", "73", "172.5"]], + "Barry": [["Barry HQ", "-45.5", "54", "-142.5"]], + "Blacksmith (Rift)": [["D\u00e9j\u00e0 Vu Alley", "-1.5", "68", "-142"]], + "Chef": [["West Village", "-103", "72", "-104"]], + "Chester": [["Shifted Tavern", "-130.5", "73.3", "173.3"]], + "Chicken (Rift NPC)": [ + ["Dreadfarm", "-53.9", "72", "-158"], + ["Dreadfarm", "-57.1", "72.2", "-161.1"] + ], + "Cosmo": [["West Village", "-66.5", "70", "-92.3"]], + "Cow (Rift)": [ + ["Dreadfarm", "-42.5", "72", "-167"], + ["Dreadfarm", "-38.4", "71.7", "-164.3"] + ], + "Cowboy Nick": [["Village Plaza", "-13", "70", "-7.1"]], + "Creed": [ + ["Barry Center", "-15.2", "73", "-112"], + ["Barry HQ", "-51.5", "54", "-137.5"] + ], + "Cryptosis": [["Village Plaza", "27.5", "71", "-77"]], + "Dackinoru": [["Shifted Tavern", "-118.3", "73.0", "172.5"]], + "Damia (Rift)": [["Barrier Street", "30.5", "72", "-94.5"]], + "Deer": [["Photon Pathway", "139", "70", "-12"]], + "Detective Amog": [ + ["Murder House", "-49.8", "68", "-37.2"], + ["Village Plaza", "0.5", "71", "-13"], + ["Around Colosseum", "-188", "71", "-56.7"], + ["D\u00e9j\u00e0 Vu Alley", "1.5", "68", "-139.5"], + ["Murder House", "-49.5", "68", "-38.75"], + ["Taylor's", "-28.5", "64", "-40.5"], + ["Village Plaza", "-53", "68", "-49.3"], + ["Murder House", "-51.1", "68", "-40.7"] + ], + "Disinfestor": [ + ["Infested House", "-38", "71", "-103"], + ["Infested House", "-40", "70", "-88"], + ["Infested House", "-37.5", "77", "-93.5"], + ["Infested House", "-32.5", "77", "-88.5"] + ], + "Dr. Emmett": [ + ["Mirrorverse", "", "", "To be added"], + ["Mirrorverse", "", "", "To be added"], + ["Mirrorverse", "", "", "To be added"], + ["Mirrorverse", "", "", "To be added"], + ["Mirrorverse", "", "", "To be added"], + ["Mirrorverse", "", "", "To be added"], + ["Mirrorverse", "", "", "To be added"], + ["Mirrorverse", "", "", "To be added"] + ], + "Dr. Hibble": [["Black Lagoon", "-79.5", "73", "9.5"]], + "Dr. Phear": [["Leeches Lair", "-146.3", "36", "27.5"]], + "Dust": [ + ["Barry Center", "-12", "71", "-100.2"], + ["Barry HQ", "-54.5", "54", "-138.5"] + ], + "Elise (Rift)": [ + ["Wizard Tower (Rift)", "-50.5", "122", "69.5"], + ["Rift Gallery", "9.8", "55", "76"] + ], + "Enigma": [["Enigma's Crib", "", "", "Moves around"]], + "Fafnir": [["Shifted Tavern", "-127.5", "73", "172.6"]], + "Fairylosopher": [ + ["Fairylosopher Tower", "252", "106", "76"], + ["Fairylosopher Tower", "256", "130", "75"], + ["Fairylosopher Tower", "256", "123", "84"], + ["Fairylosopher Tower", "256", "123", "87"], + ["Fairylosopher Tower", "253", "123", "90"], + ["Fairylosopher Tower", "250", "123", "88"], + ["Fairylosopher Tower", "250", "123", "85"] + ], + "Frankie": [ + ["Barry Center", "-4", "73", "-111.4"], + ["Barry HQ", "-44.5", "54", "-146.5"] + ], + "Finplex": [["West Village", "-57.5", "68", "-82.5"]], + "Garlacius": [["Shifted Tavern", "-128.5", "79.3", "170.6"]], + "Grandma": [["West Village", "-70.5", "64.5", "-60"]], + "Gunther": [["West Village", "-68.7", "71", "-61.5"]], + "Harriette": [ + ["Barry Center", "-1.7", "74", "-108.9"], + ["Barry HQ", "-39.5", "54", "-138.5"] + ], + "Hound": [["Wyld Woods", "-70", "66", "147.5"]], + "Inverted Sirius": [["Wyld Woods", "-96.5", "75", "190.5"]], + "Jerry (Rift)": [["\"Your\" Island", "-85.5", "20", "4.5"]], + "Joey McPizza": [["West Village", "-105.5", "72.3", "-102.5"]], + "Joliet": [["Taylor's", "-31.5", "64.5", "-43"]], + "Julian": [["Oubliette", "207", "82", "130.3"]], + "Kat (Rift)": [["Infested House", "-34.5", "70", "-90.5"]], + "Kay": [["Lagoon Hut", "-191", "68", "58.5"]], + "Kiermet": [["Wyld Woods", "-134.5", "67", "157.5"]], + "Lazarus": [["Shifted Tavern", "-127.6", "79.3", "166.3"]], + "Lonely \u00c4vae\u00eckx": [["Lonely Terrace", "", "", "Moves Between Chairs"]], + "Maddox The Slayer (Rift)": [["Stillgore Ch\u00e2teau", "204.5", "77", "45"]], + "Marcia": [["Village Plaza", "2.5", "71", "-10.5"]], + "Mole": [["Colosseum (Rift)", "-159.5", "97", "-75.5"]], + "Motes Grubber": [ + ["Wyld Woods", "-92.5", "65", "156.5"], + ["West Village", "-93", "70.5", "-73"], + ["Stillgore Ch\u00e2teau", "164.5", "77", "125.5"] + ], + "Mushroom Guy": [["Black Lagoon", "-178.3125", "75", "12.5"]], + "Nene": [["Village Plaza", "23.5", "88.5", "-90.5"]], + "Phaser": [["Photon Pathway", "59.6", "70", "-88.5"]], + "Plumber Joe (Rift)": [["West Village", "-62", "70", "-78"]], + "Porhtal": [["Broken Cage", "-157.5", "70.5", "162"]], + "Ramero": [["Murder House", "-48.5", "68", "-38.5"]], + "Reed": [["Lagoon Hut", "-212", "72", "60.5"]], + "Ribbit": [["Wyld Woods", "-136.5", "67", "158.5"]], + "Rock": [ + ["Living Stillness", "13.5", "48.5", "-186.5"], + ["Living Stillness", "-15.5", "45.6", "-174.5"], + ["Living Stillness", "-24.5", "46.5", "-203.5"], + ["Living Stillness", "19.5", "45.3", "-221.5"], + ["Living Stillness", "55.5", "45.2", "-199.5"], + ["Living Stillness", "55.5", "45.3", "-198.5"], + ["Living Stillness", "45.5", "47.1", "-180.5"], + ["Living Stillness", "27.6", "49", "-191.5"] + ], + "Roger": [["Village Plaza", "-2.5", "70", "-43.5"]], + "Roy": [["Lagoon Hut", "-204.6", "75", "49.2"]], + "Seraphine (Rift)": [ + ["Barry Center", "-16.5", "71", "-101"], + ["Barry Center", "-16.5", "71", "-101"] + ], + "Seskel": [["Shifted Tavern", "-128.5", "73.3", "164.3"]], + "Seymour (Rift)": [["Taylor's", "-27", "71", "-44.5"]], + "Shania (Rift)": [["Dreadfarm", "-47.5", "72", "-161"]], + "Sheep (Rift)": [ + ["Dreadfarm", "-58.5", "72", "-166.6"], + ["Dreadfarm", "-55.1", "73.4", "-167.1"], + ["Dreadfarm", "-55.1", "73.4", "-167.1"] + ], + "Shifted": [["Shifted Tavern", "-120.5", "73", "174.5"]], + "Sick Farmer": [["Dreadfarm", "-43", "72", "-160"]], + "Skylark": [["West Village", "-71.5", "71.15", "-115"]], + "Soma": [ + ["Barry Center", "-17", "74", "-108"], + ["Barry HQ", "-52", "54", "-141"] + ], + "Sorcerer Okron": [["West Village", "-70.5", "81", "-110.5"]], + "Stain": [ + ["Barry Center", "-6.5", "73.5", "-100.2"], + ["Barry HQ", "-57.6", "54", "-146.1"] + ], + "Taylor (Rift)": [ + ["Taylor's", "-32.3", "71", "-38.5"], + ["Taylor's", "-28.6", "64", "-44.9"] + ], + "Tel Kar": [["Wyld Woods", "-113", "69", "62"]], + "Tybalt": [["Shifted Tavern", "-129.3", "79", "168.5"]], + "Unbound Explorer": [["Otherside", "-28.5", "72", "315.5"]], + "Unhinged Kloon": [["West Village", "-56.5", "79", "-13.7"]], + "Violetta": [ + ["Barry Center", "-4.2", "73", "-105.4"], + ["Barry HQ", "-51.5", "54", "-147.5"] + ], + "Vreike": [["Shifted Tavern", "-124.3", "73", "172.5"]], + "Wizard (Rift)": [["Wizard Tower (Rift)", "-47.5", "122.5", "77.5"]], + "Wizardman": [["Wizard Tower", "-45", "90", "69.5"]], + "Yoshua": [["West Village", "-69", "71.15", "-117.5"]] + }, + "Dungeon Hub": { + "Adventurer Saul": [["Dungeon Hub", "-53", "119", "-9"]], + "Croesus": [["Dungeon Hub", "-21", "120", "-14"]], + "Dungeon Hub Selector": [["Dungeon Hub", "-37", "119", "10"]], + "Guildford": [["Dungeon Hub", "-19", "86", "4"]], + "Malik": [ + ["Dungeon Hub", "-64", "121", "5"], + ["Catacombs Entrance (Zone)", "-81", "56", "-120"] + ], + "Mort": [ + ["Dungeon Hub", "-69", "123", "0"], + ["Catacombs Entrance (Zone)", "-89", "55", "-129"] + ], + "Ophelia": [ + ["Dungeon Hub", "-64", "121", "-7"], + ["Catacombs Entrance (Zone)", "-86", "55", "-140"] + ] + }, + "Winter Island": { + "Banker Barry": [["Jerry's Workshop", "20", "77", "44"]], + "Daan": [["Glacial Cave", "80", "78.5", "110"]], + "Dirk": [["Glacial Cave", "79", "80.5", "55"]], + "Einary": [["Einary's Emporium", "-16.5", "76", "63.5"]], + "Frosty": [["Jerry's Workshop", "-2", "76", "92"]], + "Frozen Alex": [["Reflective Pond", "-74.5", "74", "-6.5"]], + "Gary": [["Gary's Shack", "53", "103", "56"]], + "Generow": [["Jerry's Workshop", "-10.5", "76", "93.5"]], + "Gulliver": [["Jerry's Workshop", "68", "105", "33"]], + "Helena": [["Glacial Cave", "36", "79", "75"]], + "Hendrik": [["Hot Springs", "16.5", "62", "-30.5"]], + "Jerry": [ + ["Private Island", "9", "100", "33"], + ["Jerry's Workshop", "-34", "76", "77"], + ["Jerry's Workshop", "6", "76", "72"], + ["Jerry's Workshop", "-15", "76", "49"], + ["Jerry's Workshop", "18", "76", "31"], + ["Jerry's Workshop", "-12", "76", "13"], + ["Village", "-3", "70", "-47"] + ], + "Maaike": [["Glacial Cave", "49", "81", "50"]], + "Mees": [["Glacial Cave", "80", "79", "72"]], + "Sherry": [["Jerry's Workshop", "9", "76", "99"]], + "St. Jerry": [["Jerry's Workshop", "-23", "76", "92"]], + "Terry": [["Jerry Pond", "-92", "78", "25"]] + }, + "Private Island": { + "Jerry": [ + ["Private Island", "9", "100", "33"], + ["Jerry's Workshop", "-34", "76", "77"], + ["Jerry's Workshop", "6", "76", "72"], + ["Jerry's Workshop", "-15", "76", "49"], + ["Jerry's Workshop", "18", "76", "31"], + ["Jerry's Workshop", "-12", "76", "13"], + ["Village", "-3", "70", "-47"] + ], + "Sam": [ + ["Private Island", "8.5", "100", "41.5"], + ["Garden", "-7.5", "71", "-6.5"], + ["Garden", "-14.5", "72", "-27.5"], + ["Garden", "4.5", "72", "-21.5"] + ] + }, + "Garden": { + "Anita": [["Farmhouse", "23", "77", "-69"]], + "Jacob": [["Farmhouse", "23", "71", "-69"]], + "Pesthunter Pamela": [["Garden", "-24", "71", "-14"]], + "Pesthunter Phillip": [["Garden", "-26", "71", "-14"]], + "Sam": [ + ["Private Island", "8.5", "100", "41.5"], + ["Garden", "-7.5", "71", "-6.5"], + ["Garden", "-14.5", "72", "-27.5"], + ["Garden", "4.5", "72", "-21.5"] + ] + } +} diff --git a/json/quotes.json b/json/quotes.json index 47f7879..321214c 100644 --- a/json/quotes.json +++ b/json/quotes.json @@ -1,46 +1,46 @@ [ - "The general who wins the battle makes many calculations in his temple before the battle is fought. The general who loses makes but few calculations beforehand.", - "A leader leads by example not by force.", - "The control of a large force is the same principle as the control of a few men: it is merely a question of dividing up their numbers.", - "The ultimate in disposing one's troops is to be without ascertainable shape. Then the most penetrating spies cannot pry in nor can the wise lay plans against you.", - "If words of command are not clear and distinct, if orders are not thoroughly understood, the general is to blame. But if his orders ARE clear, and the soldiers nevertheless disobey, then it is the fault of their officers.", - "Strategy without tactics is the slowest route to victory. Tactics without strategy is the noise before defeat.", - "All warfare is based on deception.", - "If fighting is sure to result in victory, then you must fight.", - "One defends when his strength is inadaquate, he attacks when it is abundant.", - "The quality of decision is like the well-timed swoop of a falcon which enables it to strike and destroy its victim.", - "When the enemy is at ease, be able to weary him; when well fed, to starve him; when at rest, to make him move. Appear at places to which he must hasten; move swiftly where he does not expect you.", - "If you know your enemy and you know yourself you need not fear the results of a hundred battles. If you know yourself but not the enemy for every victory gained you will also suffer a defeat. If you know neither the enemy nor yourself you will succumb in every battle.", - "The general who advances without coveting fame and retreats without fearing disgrace, whose only thought is to protect his country and do good service for his sovereign, is the jewel of the kingdom.", - "For to win one hundred victories in one hundred battles is not the acme of skill. To subdue the enemy without fighting is the acme of skill.", - "What the ancients called a clever fighter is one who not only wins, but excels in winning with ease.", - "To a surrounded enemy, you must leave a way of escape.", - "To know your Enemy, you must become your Enemy.", - "Thus, what is of supreme importance in war is to attack the enemy's strategy.", - "A leader leads by example, not force.", - "Too frequent rewards indicate that the general is at the end of his resources; too frequent punishments that he is in acute distress.", - "Pretend inferiority and encourage his arrogance.", - "All men can see these tactics whereby I conquer, but what none can see is the strategy out of which victory is evolved.", - "If we do not wish to fight, we can prevent the enemy from engaging us even though the lines of our encampment be merely traced out on the ground. All we need to do is to throw something odd and unaccountable in his way.", - "A military operation involves deception. Even though you are competent, appear to be incompetent. Though effective, appear to be ineffective.", - "Victorious warriors win first and then go to war, while defeated warriors go to war first and then seek to win.", - "The best victory is when the opponent surrenders of its own accord before there are any actual hostilities... It is best to win without fighting.", - "Opportunities multiply as they are seized.", - "Speed is the essence of war. Take advantage of the enemy's unpreparedness; travel by unexpected routes and strike him where he has taken no precautions.", - "If your opponent is of choleric temperament, seek to irritate him.", - "Management of many is the same as management of few. It is a matter of organization.", - "The good fighters of old first put themselves beyond the possibility of defeat, and then waited for an opportunity of defeating the enemy.", - "Build your opponent a golden bridge to retreat across.", - "Swift as the wind. Quiet as the forest. Conquer like the fire. Steady as the mountain.", - "It is essential to seek out enemy agents who have come to conduct espionage against you and to bribe them to serve you. Give them instructions and care for them. Thus doubled agents are recruited and used.", - "Now the reason the enlightened prince and the wise general conquer the enemy whenever they move and their achievements surpass those of ordinary men is foreknowledge.", - "And therefore those skilled in war bring the enemy to the field of battle and are not brought there by him.", - "There is no instance of a nation benefitting from prolonged warfare.", - "When able to attack, we must seem unable; when using our forces, we must seem inactive; when we are near, we must make the enemy believe we are far away; when far away, we must make him believe we are near.", - "When torrential water tosses boulders, it is because of its momentum. When the strike of a hawk breaks the body of its prey, it is because of timing.", - "Secret operations are essential in war; upon them the army relies to make its every move.", - "It is said that if you know your enemies and know yourself, you will not be imperilled in a hundred battles; if you do not know your enemies but do know yourself, you will win one and lose one; if you do not know your enemies nor yourself, you will be imperilled in every single battle.", - "He who knows when he can fight and when he cannot will be victorious.", - "Subtle and insubstantial, the expert leaves no trace; divinely mysterious, he is inaudible. Thus he is master of his enemy's fate.", - "A skilled commander seeks victory from the situation and does not demand it of his subordinates." -] \ No newline at end of file + "The general who wins the battle makes many calculations in his temple before the battle is fought. The general who loses makes but few calculations beforehand.", + "A leader leads by example not by force.", + "The control of a large force is the same principle as the control of a few men: it is merely a question of dividing up their numbers.", + "The ultimate in disposing one's troops is to be without ascertainable shape. Then the most penetrating spies cannot pry in nor can the wise lay plans against you.", + "If words of command are not clear and distinct, if orders are not thoroughly understood, the general is to blame. But if his orders ARE clear, and the soldiers nevertheless disobey, then it is the fault of their officers.", + "Strategy without tactics is the slowest route to victory. Tactics without strategy is the noise before defeat.", + "All warfare is based on deception.", + "If fighting is sure to result in victory, then you must fight.", + "One defends when his strength is inadaquate, he attacks when it is abundant.", + "The quality of decision is like the well-timed swoop of a falcon which enables it to strike and destroy its victim.", + "When the enemy is at ease, be able to weary him; when well fed, to starve him; when at rest, to make him move. Appear at places to which he must hasten; move swiftly where he does not expect you.", + "If you know your enemy and you know yourself you need not fear the results of a hundred battles. If you know yourself but not the enemy for every victory gained you will also suffer a defeat. If you know neither the enemy nor yourself you will succumb in every battle.", + "The general who advances without coveting fame and retreats without fearing disgrace, whose only thought is to protect his country and do good service for his sovereign, is the jewel of the kingdom.", + "For to win one hundred victories in one hundred battles is not the acme of skill. To subdue the enemy without fighting is the acme of skill.", + "What the ancients called a clever fighter is one who not only wins, but excels in winning with ease.", + "To a surrounded enemy, you must leave a way of escape.", + "To know your Enemy, you must become your Enemy.", + "Thus, what is of supreme importance in war is to attack the enemy's strategy.", + "A leader leads by example, not force.", + "Too frequent rewards indicate that the general is at the end of his resources; too frequent punishments that he is in acute distress.", + "Pretend inferiority and encourage his arrogance.", + "All men can see these tactics whereby I conquer, but what none can see is the strategy out of which victory is evolved.", + "If we do not wish to fight, we can prevent the enemy from engaging us even though the lines of our encampment be merely traced out on the ground. All we need to do is to throw something odd and unaccountable in his way.", + "A military operation involves deception. Even though you are competent, appear to be incompetent. Though effective, appear to be ineffective.", + "Victorious warriors win first and then go to war, while defeated warriors go to war first and then seek to win.", + "The best victory is when the opponent surrenders of its own accord before there are any actual hostilities... It is best to win without fighting.", + "Opportunities multiply as they are seized.", + "Speed is the essence of war. Take advantage of the enemy's unpreparedness; travel by unexpected routes and strike him where he has taken no precautions.", + "If your opponent is of choleric temperament, seek to irritate him.", + "Management of many is the same as management of few. It is a matter of organization.", + "The good fighters of old first put themselves beyond the possibility of defeat, and then waited for an opportunity of defeating the enemy.", + "Build your opponent a golden bridge to retreat across.", + "Swift as the wind. Quiet as the forest. Conquer like the fire. Steady as the mountain.", + "It is essential to seek out enemy agents who have come to conduct espionage against you and to bribe them to serve you. Give them instructions and care for them. Thus doubled agents are recruited and used.", + "Now the reason the enlightened prince and the wise general conquer the enemy whenever they move and their achievements surpass those of ordinary men is foreknowledge.", + "And therefore those skilled in war bring the enemy to the field of battle and are not brought there by him.", + "There is no instance of a nation benefitting from prolonged warfare.", + "When able to attack, we must seem unable; when using our forces, we must seem inactive; when we are near, we must make the enemy believe we are far away; when far away, we must make him believe we are near.", + "When torrential water tosses boulders, it is because of its momentum. When the strike of a hawk breaks the body of its prey, it is because of timing.", + "Secret operations are essential in war; upon them the army relies to make its every move.", + "It is said that if you know your enemies and know yourself, you will not be imperilled in a hundred battles; if you do not know your enemies but do know yourself, you will win one and lose one; if you do not know your enemies nor yourself, you will be imperilled in every single battle.", + "He who knows when he can fight and when he cannot will be victorious.", + "Subtle and insubstantial, the expert leaves no trace; divinely mysterious, he is inaudible. Thus he is master of his enemy's fate.", + "A skilled commander seeks victory from the situation and does not demand it of his subordinates." +] diff --git a/json/rabbits.json b/json/rabbits.json index a501f85..8c4e7f6 100644 --- a/json/rabbits.json +++ b/json/rabbits.json @@ -1,32 +1,32 @@ { - "§6Extra": "§5§o§c✖ §7Requirement §e0§7/§a20\n §5§o§7Collect §a20 §7unique §6§LLEGENDARY §5§o§6§7Rabbits.", - "§6Kaeman": "§5§o§c✖ §7Requirement\n §5§o§7§cMaster Mode Catacombs Floor VII §5§o§cCompletion", - "§5Implode": "§5§o§c✖ §7Requirement §e0§7/§a2,000\n §5§o§7Find §a2,000 §7duplicate Rabbits.", - "§5Necron": "§5§o§c✖ §7Requirement\n §5§o§7§cThe Catacombs Floor VII Completion", - "§5Saga": "§5§o§c✖ §7Requirement §e0§7/§a25\n §5§o§7Collect §a25 §7unique §5§LEPIC §7Rabbits.", - "§9Burst": "§5§o§c✖ §7Requirement §e0§7/§a1,000\n §5§o§7Find §a1,000 §7duplicate Rabbits.", - "§9Favor": "§5§o§c✖ §7Requirement §e0§7/§a50\n §5§o§7Collect §a50 §7unique §9§LRARE §7Rabbits.", - "§aArachno": "§5§o§c✖ §7Requirement\n §5§o§7Defeat §cTarantula Broodfather Tier §5§o§cIII§7.", - "§aAverage": "§5§o§c✖ §7Requirement\n §5§o§7Collect §a100 §7unique §F§LCOMMON §7Rabbits.", - "§aInferno": "§5§o§c✖ §7Requirement\n §5§o§7Defeat §cInferno Demonlord Tier III§7.", - "§aMediocrity": "§5§o§c✖ §7Requirement §e0§7/§a200\n §5§o§7Collect §a200 §7unique §F§LCOMMON §7Rabbits.", - "§aModest": "§5§o§c✖ §7Requirement\n §5§o§7Collect §a50 §7unique §A§LUNCOMMON §7Rabbits.", - "§aPrestige": "§5§o§c✖ §7Requirement\n §5§o§7Reach §6Chocolate Factory III§7.", - "§aPrudent": "§5§o§c✖ §7Requirement §e0§7/§a100\n §5§o§7Collect §a100 §7unique §A§LUNCOMMON §5§o§7§7Rabbits.", - "§aSquish": "§5§o§c✖ §7Requirement\n §5§o§7Find §a500 §7duplicate Rabbits.", - "§aSven": "§5§o§c✖ §7Requirement\n §5§o§7Defeat §cSven Packmaster Tier III§7.", - "§aVoid": "§5§o§c✖ §7Requirement\n §5§o§7Defeat §cVoidgloom Seraph Tier III§7.", - "§aZombie": "§5§o§c✖ §7Requirement\n §5§o§7Defeat §cRevenant Horror Tier III§7.", - "§fCave": "§5§o§c✖ §7Requirement §e0§7/§a15\n §5§o§7Find §a15 §7unique egg locations in the §5§o§7§bDeep Caverns§7.", - "§fCrush": "§5§o§c✖ §7Requirement\n §5§o§7Find §a100 §7duplicate Rabbits.", - "§fEmber": "§5§o§c✖ §7Requirement §e0§7/§a15\n §5§o§7Find §a15 §7unique egg locations in the §5§o§7§cCrimson Isle§7.", - "§fHubby": "§5§o§c✖ §7Requirement §e0§7/§a15\n §5§o§7Find §a15 §7unique egg locations in the §5§o§7§aHub§7.", - "§fJade": "§5§o§c✖ §7Requirement §e0§7/§a15\n §5§o§7Find §a15 §7unique egg locations in the §5§o§7§5Crystal Hollows§7.", - "§fJerry": "§5§o§c✖ §7Requirement\n §5§o§7Kill a §6Golden Jerry§7.", - "§fMimi": "§5§o§c✖ §7Requirement §e0§7/§a15\n §5§o§7Find §a15 §7unique egg locations in the §5§o§7§2Dwarven Mines§7.", - "§fPicky": "§5§o§c✖ §7Requirement §e0§7/§a15\n §5§o§7Find §a15 §7unique egg locations in the §5§o§7§6Gold Mine§7.", - "§fStinky": "§5§o§c✖ §7Requirement §e0§7/§a15\n §5§o§7Find §a15 §7unique egg locations in the §5§o§7§aFarming Islands§7.", - "§fWebb": "§5§o§c✖ §7Requirement §e0§7/§a15\n §5§o§7Find §a15 §7unique egg locations in the §5§o§7§cSpider's Den§7.", - "§fWoody": "§5§o§c✖ §7Requirement §e0§7/§a15\n §5§o§7Find §a15 §7unique egg locations in §aThe §5§o§aPark§7.", - "§fZea": "§5§o§c✖ §7Requirement §e0§7/§a15\n §5§o§7Find §a15 §7unique egg locations in §dThe §5§o§dEnd§7." -} \ No newline at end of file + "§6Extra": "§5§o§c✖ §7Requirement §e0§7/§a20\n §5§o§7Collect §a20 §7unique §6§LLEGENDARY §5§o§6§7Rabbits.", + "§6Kaeman": "§5§o§c✖ §7Requirement\n §5§o§7§cMaster Mode Catacombs Floor VII §5§o§cCompletion", + "§5Implode": "§5§o§c✖ §7Requirement §e0§7/§a2,000\n §5§o§7Find §a2,000 §7duplicate Rabbits.", + "§5Necron": "§5§o§c✖ §7Requirement\n §5§o§7§cThe Catacombs Floor VII Completion", + "§5Saga": "§5§o§c✖ §7Requirement §e0§7/§a25\n §5§o§7Collect §a25 §7unique §5§LEPIC §7Rabbits.", + "§9Burst": "§5§o§c✖ §7Requirement §e0§7/§a1,000\n §5§o§7Find §a1,000 §7duplicate Rabbits.", + "§9Favor": "§5§o§c✖ §7Requirement §e0§7/§a50\n §5§o§7Collect §a50 §7unique §9§LRARE §7Rabbits.", + "§aArachno": "§5§o§c✖ §7Requirement\n §5§o§7Defeat §cTarantula Broodfather Tier §5§o§cIII§7.", + "§aAverage": "§5§o§c✖ §7Requirement\n §5§o§7Collect §a100 §7unique §F§LCOMMON §7Rabbits.", + "§aInferno": "§5§o§c✖ §7Requirement\n §5§o§7Defeat §cInferno Demonlord Tier III§7.", + "§aMediocrity": "§5§o§c✖ §7Requirement §e0§7/§a200\n §5§o§7Collect §a200 §7unique §F§LCOMMON §7Rabbits.", + "§aModest": "§5§o§c✖ §7Requirement\n §5§o§7Collect §a50 §7unique §A§LUNCOMMON §7Rabbits.", + "§aPrestige": "§5§o§c✖ §7Requirement\n §5§o§7Reach §6Chocolate Factory III§7.", + "§aPrudent": "§5§o§c✖ §7Requirement §e0§7/§a100\n §5§o§7Collect §a100 §7unique §A§LUNCOMMON §5§o§7§7Rabbits.", + "§aSquish": "§5§o§c✖ §7Requirement\n §5§o§7Find §a500 §7duplicate Rabbits.", + "§aSven": "§5§o§c✖ §7Requirement\n §5§o§7Defeat §cSven Packmaster Tier III§7.", + "§aVoid": "§5§o§c✖ §7Requirement\n §5§o§7Defeat §cVoidgloom Seraph Tier III§7.", + "§aZombie": "§5§o§c✖ §7Requirement\n §5§o§7Defeat §cRevenant Horror Tier III§7.", + "§fCave": "§5§o§c✖ §7Requirement §e0§7/§a15\n §5§o§7Find §a15 §7unique egg locations in the §5§o§7§bDeep Caverns§7.", + "§fCrush": "§5§o§c✖ §7Requirement\n §5§o§7Find §a100 §7duplicate Rabbits.", + "§fEmber": "§5§o§c✖ §7Requirement §e0§7/§a15\n §5§o§7Find §a15 §7unique egg locations in the §5§o§7§cCrimson Isle§7.", + "§fHubby": "§5§o§c✖ §7Requirement §e0§7/§a15\n §5§o§7Find §a15 §7unique egg locations in the §5§o§7§aHub§7.", + "§fJade": "§5§o§c✖ §7Requirement §e0§7/§a15\n §5§o§7Find §a15 §7unique egg locations in the §5§o§7§5Crystal Hollows§7.", + "§fJerry": "§5§o§c✖ §7Requirement\n §5§o§7Kill a §6Golden Jerry§7.", + "§fMimi": "§5§o§c✖ §7Requirement §e0§7/§a15\n §5§o§7Find §a15 §7unique egg locations in the §5§o§7§2Dwarven Mines§7.", + "§fPicky": "§5§o§c✖ §7Requirement §e0§7/§a15\n §5§o§7Find §a15 §7unique egg locations in the §5§o§7§6Gold Mine§7.", + "§fStinky": "§5§o§c✖ §7Requirement §e0§7/§a15\n §5§o§7Find §a15 §7unique egg locations in the §5§o§7§aFarming Islands§7.", + "§fWebb": "§5§o§c✖ §7Requirement §e0§7/§a15\n §5§o§7Find §a15 §7unique egg locations in the §5§o§7§cSpider's Den§7.", + "§fWoody": "§5§o§c✖ §7Requirement §e0§7/§a15\n §5§o§7Find §a15 §7unique egg locations in §aThe §5§o§aPark§7.", + "§fZea": "§5§o§c✖ §7Requirement §e0§7/§a15\n §5§o§7Find §a15 §7unique egg locations in §dThe §5§o§dEnd§7." +} diff --git a/metadata.json b/metadata.json index a185f92..da087fa 100644 --- a/metadata.json +++ b/metadata.json @@ -4,13 +4,6 @@ "creator": "Volcaronitee", "description": "joe.", "entry": "index.js", - "version": "2.9.12", - "requires": [ - "axios", - "BeaconBeam", - "PogData", - "RenderLib", - "requestV2", - "Vigilance" - ] -} \ No newline at end of file + "version": "2.9.13", + "requires": ["axios", "BeaconBeam", "PogData", "RenderLib", "requestV2", "Vigilance"] +} diff --git a/utils/Constants.js b/utils/Constants.js index 03493d3..e4fa77e 100644 --- a/utils/Constants.js +++ b/utils/Constants.js @@ -1,57 +1,57 @@ /** * Color codes. */ -export const BLACK = '§0'; -export const DARK_BLUE = '§1'; -export const DARK_GREEN = '§2'; -export const DARK_AQUA = '§3'; -export const DARK_RED = '§4'; -export const DARK_PURPLE = '§5'; -export const GOLD = '§6'; -export const GRAY = '§7'; -export const DARK_GRAY = '§8'; -export const BLUE = '§9'; -export const GREEN = '§a'; -export const AQUA = '§b'; -export const RED = '§c'; -export const LIGHT_PURPLE = '§d'; -export const YELLOW = '§e'; -export const WHITE = '§f'; +export const BLACK = "§0"; +export const DARK_BLUE = "§1"; +export const DARK_GREEN = "§2"; +export const DARK_AQUA = "§3"; +export const DARK_RED = "§4"; +export const DARK_PURPLE = "§5"; +export const GOLD = "§6"; +export const GRAY = "§7"; +export const DARK_GRAY = "§8"; +export const BLUE = "§9"; +export const GREEN = "§a"; +export const AQUA = "§b"; +export const RED = "§c"; +export const LIGHT_PURPLE = "§d"; +export const YELLOW = "§e"; +export const WHITE = "§f"; export const COLOR_TABLE = { - '§0': Renderer.color(0, 0, 0, 200), - '§1': Renderer.color(0, 0, 139, 200), - '§2': Renderer.color(1, 50, 32, 200), - '§3': Renderer.color(2, 41, 42, 200), - '§4': Renderer.color(116, 57, 84, 200), - '§5': Renderer.color(108, 57, 142, 200), - '§6': Renderer.color(148, 122, 84, 200), - '§7': Renderer.color(128, 128, 128, 200), - '§8': Renderer.color(169, 169, 169, 200), - '§9': Renderer.color(83, 89, 181, 200), - '§a': Renderer.color(80, 145, 113, 200), - '§b': Renderer.color(83, 154, 181, 200), - '§c': Renderer.color(148, 89, 117, 200), - '§d': Renderer.color(148, 89, 181, 200), - '§e': Renderer.color(255, 255, 0, 200), - '§f': Renderer.color(148, 154, 181, 200), -} + "§0": Renderer.color(0, 0, 0, 200), + "§1": Renderer.color(0, 0, 139, 200), + "§2": Renderer.color(1, 50, 32, 200), + "§3": Renderer.color(2, 41, 42, 200), + "§4": Renderer.color(116, 57, 84, 200), + "§5": Renderer.color(108, 57, 142, 200), + "§6": Renderer.color(148, 122, 84, 200), + "§7": Renderer.color(128, 128, 128, 200), + "§8": Renderer.color(169, 169, 169, 200), + "§9": Renderer.color(83, 89, 181, 200), + "§a": Renderer.color(80, 145, 113, 200), + "§b": Renderer.color(83, 154, 181, 200), + "§c": Renderer.color(148, 89, 117, 200), + "§d": Renderer.color(148, 89, 181, 200), + "§e": Renderer.color(255, 255, 0, 200), + "§f": Renderer.color(148, 154, 181, 200), +}; /** * Formatting codes. */ -export const OBFUSCATED = '§k'; -export const BOLD = '§l'; -export const STRIKETHROUGH = '§m'; -export const UNDERLINE = '§n'; -export const ITALIC = '§o'; -export const RESET = '§r'; +export const OBFUSCATED = "§k"; +export const BOLD = "§l"; +export const STRIKETHROUGH = "§m"; +export const UNDERLINE = "§n"; +export const ITALIC = "§o"; +export const RESET = "§r"; /** * Sounds. */ -export const AMOGUS = new Sound({source: "amogus.ogg"}); -export const MUSIC = new Sound({source: "music.ogg"}); +export const AMOGUS = new Sound({ source: "amogus.ogg" }); +export const MUSIC = new Sound({ source: "music.ogg" }); /** * Minecraft Class Constants @@ -76,7 +76,7 @@ export const STAND_CLASS = EntityArmorStand.class; export const WITHER_CLASS = EntityWither.class; export const InventoryBasic = Java.type("net.minecraft.inventory.InventoryBasic"); -export const GuiInventory = Java.type("net.minecraft.client.gui.inventory.GuiInventory") +export const GuiInventory = Java.type("net.minecraft.client.gui.inventory.GuiInventory"); export const GuiChest = Java.type("net.minecraft.client.gui.inventory.GuiChest"); export const GuiTextField = Java.type("net.minecraft.client.gui.GuiTextField"); @@ -89,8 +89,9 @@ export const DataFlavor = Java.type("java.awt.datatransfer.DataFlavor"); /** * VolcAddons setting constants. */ -export const HEADER = -`${GRAY}[${GOLD}VolcAddons${GRAY}] ${YELLOW}v${JSON.parse(FileLib.read("VolcAddons", "metadata.json")).version} +export const HEADER = `${GRAY}[${GOLD}VolcAddons${GRAY}] ${YELLOW}v${ + JSON.parse(FileLib.read("VolcAddons", "metadata.json")).version +} ${WHITE}Made By Volcaronitee `; export const LOGO = `${GRAY}[${GOLD}VolcAddons${GRAY}] `; @@ -99,41 +100,133 @@ export const LOGO = `${GRAY}[${GOLD}VolcAddons${GRAY}] `; * Button presets. compact this please */ export const BUTTON_PRESETS = { - "inv": { - "inv13": ["inv1", 3, "auction", "golden_horse_armor"], - "inv40": ["inv4", 0, "sacks", "chest"], - "inv41": ["inv4", 1, "fishingbag", "fish"], - "inv42": ["inv4", 2, "potionbag", "potion"], - "inv43": ["inv4", 3, "quiver", "arrow"], - "inv44": ["inv4", 4, "accessorybag", "redstone_block"], - "inv14": ["inv1", 4, "bazaar", "gold_ingot"], - "inv10": ["inv1", 0, "hex", "book"], - "inv12": ["inv1", 2, "anvil", "anvil"], - "inv11": ["inv1", 1, "et", "enchanting_table"] - } -} + inv: { + inv13: ["inv1", 3, "auction", "golden_horse_armor"], + inv40: ["inv4", 0, "sacks", "chest"], + inv41: ["inv4", 1, "fishingbag", "fish"], + inv42: ["inv4", 2, "potionbag", "potion"], + inv43: ["inv4", 3, "quiver", "arrow"], + inv44: ["inv4", 4, "accessorybag", "redstone_block"], + inv14: ["inv1", 4, "bazaar", "gold_ingot"], + inv10: ["inv1", 0, "hex", "book"], + inv12: ["inv1", 2, "anvil", "anvil"], + inv11: ["inv1", 1, "et", "enchanting_table"], + }, +}; /** * Reforge names and categories. */ export const REFORGES = { - "weapon": new Set([ - "Epic", "Fair", "Fast", "Gentle", "Heroic", "Legendary", "odd", "Sharp", "Spicy", "Coldfused", "Dirty", "Fabled", "Gilded", "Suspicious", - "Warped", "Withered", "Bulky", "Fanged", - "Awkward", "Deadly", "Fine", "Grand", "Hasty", "Neat", "Rapid", "Rich", "Unreal", "Precise", "Spiritual", "Headstrong", - "Great", "Rugged", "Lush", "Lumberjacks", "Double-Bit", "Moil", "Toil", "Blessed", "Earthy" - ]), - "armor": new Set([ - "Clean", "Fierce", "Heavy", "Light", "Mythic", "Pure", "Titanic", "Smart", "Wise", "Candied", "Submerged", "Perfect", "Reinforced", - "Renowned", "Spiked", "Hyper", "Giant", "Jaded", "Cubic", "Necrotic", "Empowered", "Ancient", "Undead", "Loving", "Ridiculous", - "Bustling", "Mossy", "Festive", - "Very", "Highly", "Extremely", "Not so", "Thicc", "Absolutely", "Even More" - ]), - "misc": new Set([ - "Glistening", "Strengthened", "Waxed", "Fortified", "Rooted", "Blooming", "Snowy", "Blood-Soaked", - "Salty", "Treacherous", "Lucky", "Stiff", "Dirty", "Chomp", "Pitchin", - "Ambered", "Auspicious", "Fleet", "Heated", "Magnetic", "Mithraic", "Refined", "Stellar", "Fruitful", - "Robust", "Zooming", "Peasants", "Green Thumb", "Blessed", "Bountiful", - "Lvl" - ]) + weapon: new Set([ + "Epic", + "Fair", + "Fast", + "Gentle", + "Heroic", + "Legendary", + "odd", + "Sharp", + "Spicy", + "Coldfused", + "Dirty", + "Fabled", + "Gilded", + "Suspicious", + "Warped", + "Withered", + "Bulky", + "Fanged", + "Awkward", + "Deadly", + "Fine", + "Grand", + "Hasty", + "Neat", + "Rapid", + "Rich", + "Unreal", + "Precise", + "Spiritual", + "Headstrong", + "Great", + "Rugged", + "Lush", + "Lumberjacks", + "Double-Bit", + "Moil", + "Toil", + "Blessed", + "Earthy", + ]), + armor: new Set([ + "Clean", + "Fierce", + "Heavy", + "Light", + "Mythic", + "Pure", + "Titanic", + "Smart", + "Wise", + "Candied", + "Submerged", + "Perfect", + "Reinforced", + "Renowned", + "Spiked", + "Hyper", + "Giant", + "Jaded", + "Cubic", + "Necrotic", + "Empowered", + "Ancient", + "Undead", + "Loving", + "Ridiculous", + "Bustling", + "Mossy", + "Festive", + "Very", + "Highly", + "Extremely", + "Not so", + "Thicc", + "Absolutely", + "Even More", + ]), + misc: new Set([ + "Glistening", + "Strengthened", + "Waxed", + "Fortified", + "Rooted", + "Blooming", + "Snowy", + "Blood-Soaked", + "Salty", + "Treacherous", + "Lucky", + "Stiff", + "Dirty", + "Chomp", + "Pitchin", + "Ambered", + "Auspicious", + "Fleet", + "Heated", + "Magnetic", + "Mithraic", + "Refined", + "Stellar", + "Fruitful", + "Robust", + "Zooming", + "Peasants", + "Green Thumb", + "Blessed", + "Bountiful", + "Lvl", + ]), }; diff --git a/utils/Data.js b/utils/Data.js index 28375ed..3c5bec1 100644 --- a/utils/Data.js +++ b/utils/Data.js @@ -1,199 +1,224 @@ import PogObject from "../../PogData"; import { BUTTON_PRESETS } from "./Constants"; - // Relocate old data files (<2.9.5) if (FileLib.exists("./config/ChatTriggers/modules/VolcAddons/datitee.json")) { - const fileData = FileLib.read("VolcAddons", "datitee.json"); - FileLib.write("VolcAddons", "data/datitee.json", fileData); - FileLib.delete("VolcAddons", "datitee.json"); + const fileData = FileLib.read("VolcAddons", "datitee.json"); + FileLib.write("VolcAddons", "data/datitee.json", fileData); + FileLib.delete("VolcAddons", "datitee.json"); } const DEFAULT_GUI = { - "AL": [780, 430, 1.2, false, false, true], // Skill Tracker Location - "BL": [10, 120, 1.2, false, false, true], // Vampire Location - "CL": [10, 180, 1.2, false, false, true], // Counter Location - "DL": [575, 165, 1.2, false, false, true], // Dungeon Profit Location - "EL": [100, 150, 1.1, false, false, true], // Advanced Value Location - "FL": [220, 10, 1.2, false, false, true], // Trophy Fish Location - "GL": [10, 140, 1.2, false, false, true], // Gyro Location - "HL": [10, 240, 1.2, false, false, true], // Powder Chest Location - "IL": [10, 180, 1.2, false, false, true], // Inq Location - "JL": [150, 180, 1.2, false, false, true], // Kill Counter Location - "KL": [575, 165, 1.2, false, false, true], // Kuudra Profit Location - "LL": [770, 70, 1.2, false, false, true], // Server Status Location - "ML": [780, 390, 1.2, false, false, true], // Coins Location - "OL": [10, 130, 1.2, false, false, true], // Composter Location - "PL": [10, 85, 1.2, false, false, true], // Powder Location - "QL": [250, 225, 4, false, false, true], // Vanquisher Location - "RL": [600, 175, 1, false, false, true], // Container Value Location - "SL": [10, 180, 1.2, false, false, true], // Splits Location - "TL": [10, 130, 1.2, false, false, true], // Golden Fish Timer Location - "UL": [930, 65, 1.2, false, false, false], // Armor Display Location - "VL": [10, 170, 1.2, false, false, true], // Visitors Location - "WL": [730, 130, 1.2, false, false, true], // Wolf Combo Location - "XL": [Renderer.screen.getWidth()/2 - 96, Renderer.screen.getHeight()*6/7, 1, false, false, true], // Searchbox location - "YL": [770, 165, 1.2, false, false, true], // SkyBlock Stats Location - "ZL": [780, 330, 1.2, false, false, true], // Kuudra Profit Tracker Location - "BCL": [180, 10, 1, false, false, true], // Bingo Card Location - "BEL": [180, 10, 1.2, false, false, true], // Bestiary Counter Location - "CDL": [185, 65, 1.2, false, false, true], // Commission Display Location - "CEL": [375, 275, 3, false, false, true], // Crate edit location - "CFL": [625, 120, 1.2, false, false, true], // Chocolate factory location - "CGL": [625, 70, 1.2, false, false, true], // Chocolate egg location - "CPL": [575, 160, 1, false, false, false], // Container preview location - "EQL": [905, 65, 1.2, false, false, false], // Equipment Location - "FHL": [580, 160, 1.2, false, false, true], // Fossil Helper Location - "PDL": [580, 460, 1.2, false, false, true], // Pick display location - "PHL": [170, 160, 1.2, false, false, true], // Pesthunter Location - "SDL": [170, 180, 1.2, false, false, true], // Spray Display Location - "SPL": [392, 26, 1, false, false, true], // Storage Preview Location - "TVL": [600, 150, 1.2, false, false, true], // Trade Value Location -} + AL: [780, 430, 1.2, false, false, true], // Skill Tracker Location + BL: [10, 120, 1.2, false, false, true], // Vampire Location + CL: [10, 180, 1.2, false, false, true], // Counter Location + DL: [575, 165, 1.2, false, false, true], // Dungeon Profit Location + EL: [100, 150, 1.1, false, false, true], // Advanced Value Location + FL: [220, 10, 1.2, false, false, true], // Trophy Fish Location + GL: [10, 140, 1.2, false, false, true], // Gyro Location + HL: [10, 240, 1.2, false, false, true], // Powder Chest Location + IL: [10, 180, 1.2, false, false, true], // Inq Location + JL: [150, 180, 1.2, false, false, true], // Kill Counter Location + KL: [575, 165, 1.2, false, false, true], // Kuudra Profit Location + LL: [770, 70, 1.2, false, false, true], // Server Status Location + ML: [780, 390, 1.2, false, false, true], // Coins Location + OL: [10, 130, 1.2, false, false, true], // Composter Location + PL: [10, 85, 1.2, false, false, true], // Powder Location + QL: [250, 225, 4, false, false, true], // Vanquisher Location + RL: [600, 175, 1, false, false, true], // Container Value Location + SL: [10, 180, 1.2, false, false, true], // Splits Location + TL: [10, 130, 1.2, false, false, true], // Golden Fish Timer Location + UL: [930, 65, 1.2, false, false, false], // Armor Display Location + VL: [10, 170, 1.2, false, false, true], // Visitors Location + WL: [730, 130, 1.2, false, false, true], // Wolf Combo Location + XL: [Renderer.screen.getWidth() / 2 - 96, (Renderer.screen.getHeight() * 6) / 7, 1, false, false, true], // Searchbox location + YL: [770, 165, 1.2, false, false, true], // SkyBlock Stats Location + ZL: [780, 330, 1.2, false, false, true], // Kuudra Profit Tracker Location + BCL: [180, 10, 1, false, false, true], // Bingo Card Location + BEL: [180, 10, 1.2, false, false, true], // Bestiary Counter Location + CDL: [185, 65, 1.2, false, false, true], // Commission Display Location + CEL: [375, 275, 3, false, false, true], // Crate edit location + CFL: [625, 120, 1.2, false, false, true], // Chocolate factory location + CGL: [625, 70, 1.2, false, false, true], // Chocolate egg location + CPL: [575, 160, 1, false, false, false], // Container preview location + EQL: [905, 65, 1.2, false, false, false], // Equipment Location + FHL: [580, 160, 1.2, false, false, true], // Fossil Helper Location + PDL: [580, 460, 1.2, false, false, true], // Pick display location + PHL: [170, 160, 1.2, false, false, true], // Pesthunter Location + SDL: [170, 180, 1.2, false, false, true], // Spray Display Location + SPL: [392, 26, 1, false, false, true], // Storage Preview Location + TVL: [600, 150, 1.2, false, false, true], // Trade Value Location +}; // --- PERSISTENT DATA --- -export let data = new PogObject("VolcAddons", { +export let data = new PogObject( + "VolcAddons", + { // VolcAddons data tracking - "newUser": true, - "version": "2.3.1", - "vision": false, - "devMode": false, + newUser: true, + version: "2.3.1", + vision: false, + devMode: false, // Container bindings - "wardBinds": {}, - "slotBinds": { - "36": [], "37": [], "38": [], "39": [], "40": [], "41": [], "42": [], "43": [], "44": [] + wardBinds: {}, + slotBinds: { + 36: [], + 37: [], + 38: [], + 39: [], + 40: [], + 41: [], + 42: [], + 43: [], + 44: [], }, - "bindPresets": {}, - "buttons": {}, - "buttonPresets": BUTTON_PRESETS, + bindPresets: {}, + buttons: {}, + buttonPresets: BUTTON_PRESETS, // Control keys - "dianaKey": 0, - "pauseKey": 0, - "devKey": 0, - "recipeKey": 0, - "bindKey": 0, - "chunkey": 0, - "wardKey": 0, + dianaKey: 0, + pauseKey: 0, + devKey: 0, + recipeKey: 0, + bindKey: 0, + chunkey: 0, + wardKey: 0, // Hypixel persistant data - "commands": {}, - "equipmentLore": [[], [], [], []], - "party": { - "in": false, - "leader": false, - "members": [], - "updated": 0 + commands: {}, + equipmentLore: [[], [], [], []], + party: { + in: false, + leader: false, + members: [], + updated: 0, }, - "pet": undefined, - "wordbanks": [], + pet: undefined, + wordbanks: [], // playtime tracking - "playtime": 0, - "lastDay": 0, - "lastJoin": Date.now(), + playtime: 0, + lastDay: 0, + lastJoin: Date.now(), // lists - "whitelist": [], - "blacklist": [], - "dianalist": ["hub", "da", "castle", "museum", "wizard"], - "moblist": [], - "colorlist": {}, - "emotelist": {}, - "cdlist": {}, - "valuelist": {}, - "spamlist": [], - "ignorelist": [], - "prefixlist": ["?"], - "attributelist": ["breeze", "dominance", "fortitude", "lifeline", "magic_find", "mana_pool", - "mana_regeneration", "mending", "speed", "veteran", "blazing_fortune", "fishing_experience"], - "widgetlist": [], - "WGL": {}, + whitelist: [], + blacklist: [], + dianalist: ["hub", "da", "castle", "museum", "wizard"], + moblist: [], + colorlist: {}, + emotelist: {}, + cdlist: {}, + valuelist: {}, + spamlist: [], + ignorelist: [], + prefixlist: ["?"], + attributelist: [ + "breeze", + "dominance", + "fortitude", + "lifeline", + "magic_find", + "mana_pool", + "mana_regeneration", + "mending", + "speed", + "veteran", + "blazing_fortune", + "fishing_experience", + ], + widgetlist: [], + WGL: {}, // chocolate factory data - "cf": { - "chocolate": 0, - "production": 0, - "last": 0, - "total": 0, - "all": 0, - "prestige": 0, - "multiplier": 0 + cf: { + chocolate: 0, + production: 0, + last: 0, + total: 0, + all: 0, + prestige: 0, + multiplier: 0, }, - "eggs": { - "dupe": 0, - "total": 0, - "max": 20, - "found": {} + eggs: { + dupe: 0, + total: 0, + max: 20, + found: {}, }, - "timeTower": { - "charges": 0, - "chargeTime": 0, - "activeTime": 0, - "bonus": 0, + timeTower: { + charges: 0, + chargeTime: 0, + activeTime: 0, + bonus: 0, }, // Kuudra data - "files": [], - "splits": { - "last": [0, 0, 0, 0, 0], - "best": [999, 999, 999, 999, 9999], - "worst": [0, 0, 0, 0, 0], + files: [], + splits: { + last: [0, 0, 0, 0, 0], + best: [999, 999, 999, 999, 9999], + worst: [0, 0, 0, 0, 0], }, - "kuudraSession": { - "profit": 0, - "chests": 0, - "average": 0, - "time": 0, - "rate": 0 + kuudraSession: { + profit: 0, + chests: 0, + average: 0, + time: 0, + rate: 0, }, // Mob tracker data - "vanqSession": { - "vanqs": 0, - "kills": 0, - "last": 0, - "average": 0, + vanqSession: { + vanqs: 0, + kills: 0, + last: 0, + average: 0, }, - "inqSession": { - "inqs": 0, - "burrows": 0, - "last": 0, - "average": 0, + inqSession: { + inqs: 0, + burrows: 0, + last: 0, + average: 0, }, // Economy calculator data - "composterUpgrades": { - "Composter Speed": -1, - "Multi Drop": -1, - "Cost Reduction": -1 + composterUpgrades: { + "Composter Speed": -1, + "Multi Drop": -1, + "Cost Reduction": -1, }, // GUI locations - ...DEFAULT_GUI -}, "data/datitee.json"); + ...DEFAULT_GUI, + }, + "data/datitee.json" +); if (data.eggs.dupe === 0) data.eggs.dupe = data.dupeEggs ?? 0; -export const itemNBTs = new PogObject("VolcAddons", { - "armor": [null, null, null, null], - "equip": [null, null, null, null], - "backpacks": [], - "enderchests": [], - "storageCache": {} -}, "data/itemNBTs.json"); +export const itemNBTs = new PogObject( + "VolcAddons", + { + armor: [null, null, null, null], + equip: [null, null, null, null], + backpacks: [], + enderchests: [], + storageCache: {}, + }, + "data/itemNBTs.json" +); // Set up storage data if (itemNBTs.backpacks.length === 0) { - for (let i = 0; i < 18; i++) - itemNBTs.backpacks.push(new Array(54).fill(null)); + for (let i = 0; i < 18; i++) itemNBTs.backpacks.push(new Array(54).fill(null)); } if (itemNBTs.enderchests.length === 0) { - for (let i = 0; i < 18; i++) - itemNBTs.enderchests.push(new Array(54).fill(null)); + for (let i = 0; i < 18; i++) itemNBTs.enderchests.push(new Array(54).fill(null)); } data.autosave(30); // --- GUI CONTROL --- export function resetGUI() { - Object.keys(DEFAULT_GUI).forEach(overlay => { - data[overlay] = DEFAULT_GUI[overlay]; - }); + Object.keys(DEFAULT_GUI).forEach((overlay) => { + data[overlay] = DEFAULT_GUI[overlay]; + }); } // Saving data to persistent storage upon game unload register("gameUnload", () => { - data.lastJoin = Date.now(); - data.save(); - itemNBTs.save(); + data.lastJoin = Date.now(); + data.save(); + itemNBTs.save(); }).setPriority(Priority.LOWEST); diff --git a/utils/DevTils.js b/utils/DevTils.js index 7897fd7..74b8583 100644 --- a/utils/DevTils.js +++ b/utils/DevTils.js @@ -1,76 +1,75 @@ -import { GRAY, GREEN, LOGO, RED } from "./Constants"; +import { GRAY, GREEN, LOGO } from "./Constants"; import { data } from "./Data"; - /** * Dev mode */ const devKey = new KeyBind("Developer Mode", data.devKey, "./VolcAddons.xdd"); -register("gameUnload", () => { data.devKey = devKey.getKeyCode() }).setPriority(Priority.HIGHEST); +register("gameUnload", () => { + data.devKey = devKey.getKeyCode(); +}).setPriority(Priority.HIGHEST); devKey.registerKeyPress(() => { - if (devKey.getKeyCode() === 0 || !data.devMode) return; + if (devKey.getKeyCode() === 0 || !data.devMode) return; - const view = Player.lookingAt(); - if (view instanceof Entity) { - // Get entity data - const entity = view.entity; - const extraData = { - nbt: entity.getEntityData(), - persistantID: entity.persistentID, - entityClass: entity.class, - entityAttribute: entity.func_70668_bt(), - maxHP: entity.func_110148_a(SMA.field_111267_a).func_111125_b(), - pitch: entity.field_70125_A, - yaw: entity.field_70177_z, - ticksAlive: entity.field_70173_aa - }; - const textComponent = entity.func_145748_c_(); - let extraString = ""; - for (data in extraData) extraString += `${data}=${extraData[data]}, `; - ChatLib.command(`ct copy ${view.toString()} ⦿ ${textComponent} ⦿ ExtraData[${extraString}]`, true); - ChatLib.chat(`${LOGO + GREEN}Successfully copied entity data!`); - } else { - ChatLib.command(`ct copy ${view.toString()}`, true); - ChatLib.chat(`${LOGO + GREEN}Successfully copied block data!`); - } + const view = Player.lookingAt(); + if (view instanceof Entity) { + // Get entity data + const entity = view.entity; + const extraData = { + nbt: entity.getEntityData(), + persistantID: entity.persistentID, + entityClass: entity.class, + entityAttribute: entity.func_70668_bt(), + maxHP: entity.func_110148_a(SMA.field_111267_a).func_111125_b(), + pitch: entity.field_70125_A, + yaw: entity.field_70177_z, + ticksAlive: entity.field_70173_aa, + }; + const textComponent = entity.func_145748_c_(); + let extraString = ""; + for (data in extraData) extraString += `${data}=${extraData[data]}, `; + ChatLib.command(`ct copy ${view.toString()} ⦿ ${textComponent} ⦿ ExtraData[${extraString}]`, true); + ChatLib.chat(`${LOGO + GREEN}Successfully copied entity data!`); + } else { + ChatLib.command(`ct copy ${view.toString()}`, true); + ChatLib.chat(`${LOGO + GREEN}Successfully copied block data!`); + } }); register("guiKey", (_, keyCode, gui) => { - if (keyCode !== devKey.getKeyCode() || !data.devMode) return; - - const slot = gui?.getSlotUnderMouse()?.field_75222_d; - if (slot === undefined) return; - const item = Player.getContainer().getStackInSlot(slot); - if (item === null) return; - ChatLib.command(`ct copy ${JSON.stringify(item.getNBT().toObject(), null, 2)}`, true); - ChatLib.chat(`${LOGO + GREEN}Successfully copied ${GRAY}[${item.getName() + GRAY}] ${GREEN}NBT!`); -}); + if (keyCode !== devKey.getKeyCode() || !data.devMode) return; + const slot = gui?.getSlotUnderMouse()?.field_75222_d; + if (slot === undefined) return; + const item = Player.getContainer().getStackInSlot(slot); + if (item === null) return; + ChatLib.command(`ct copy ${JSON.stringify(item.getNBT().toObject(), null, 2)}`, true); + ChatLib.chat(`${LOGO + GREEN}Successfully copied ${GRAY}[${item.getName() + GRAY}] ${GREEN}NBT!`); +}); /** * Dev commands */ register("command", (...args) => { - ChatLib.command("ct copy " + args.join(' '), true); - ChatLib.chat(`${LOGO + GREEN}Successfully copied text to clipboard!`); + ChatLib.command("ct copy " + args.join(" "), true); + ChatLib.chat(`${LOGO + GREEN}Successfully copied text to clipboard!`); }).setName("vacopy"); register("command", () => { - TabList.getNames().forEach(name => { - print(name); - }); - ChatLib.chat(`${LOGO + GREEN}Succesfully printed TabList names to console!`); + TabList.getNames().forEach((name) => { + print(name); + }); + ChatLib.chat(`${LOGO + GREEN}Succesfully printed TabList names to console!`); }).setName("printTab"); register("command", () => { - Scoreboard.getLines().forEach(line => { - print(line.getName()); - }); - ChatLib.chat(`${LOGO + GREEN}Succesfully printed Scoreboard lines to console!`); + Scoreboard.getLines().forEach((line) => { + print(line.getName()); + }); + ChatLib.chat(`${LOGO + GREEN}Succesfully printed Scoreboard lines to console!`); }).setName("printScore"); - export function printKeys(object) { - Object.keys(object).forEach(key => print(key)); + Object.keys(object).forEach((key) => print(key)); } diff --git a/utils/Json.js b/utils/Json.js index 1f5a742..17cc7d4 100644 --- a/utils/Json.js +++ b/utils/Json.js @@ -1,58 +1,58 @@ const files = []; register("gameUnload", () => { - files.forEach(file => { - FileLib.write("VolcAddons", file.getPath(), JSON.stringify(file.getData(), null, 4)); - }); + files.forEach((file) => { + FileLib.write("VolcAddons", file.getPath(), JSON.stringify(file.getData(), null, 4)); + }); }).setPriority(Priority.HIGHEST); export class Json { - #data; - #file; - #path; + #data; + #file; + #path; - /** - * Create a new persistant single object JSON file. - * - * @param {String} file - The name of the JSON file - * @param {Boolean} save - Whether to save the file or not - */ - constructor(file, isData, save=true) { - if (save) files.push(this); - this.#file = file; - this.#path = (isData ? "data/" : "json/") + file; + /** + * Create a new persistant single object JSON file. + * + * @param {String} file - The name of the JSON file + * @param {Boolean} save - Whether to save the file or not + */ + constructor(file, isData, save = true) { + if (save) files.push(this); + this.#file = file; + this.#path = (isData ? "data/" : "json/") + file; - // Load the data from the file - if (isData && FileLib.exists("VolcAddons", "data/" + file)) { - this.#data = JSON.parse(FileLib.read("VolcAddons", "data/" + file)); - } else this.reset(); + // Load the data from the file + if (isData && FileLib.exists("VolcAddons", "data/" + file)) { + this.#data = JSON.parse(FileLib.read("VolcAddons", "data/" + file)); + } else this.reset(); - // Check if the file exists - if (this.#data === null) { - this.#data = {}; - FileLib.write("VolcAddons", this.#path, '{}'); - } + // Check if the file exists + if (this.#data === null) { + this.#data = {}; + FileLib.write("VolcAddons", this.#path, "{}"); } + } - /** - * Get the data of the JSON file. - * - * @returns {Object} The data of the JSON file. - */ - getData() { - return this.#data; - } + /** + * Get the data of the JSON file. + * + * @returns {Object} The data of the JSON file. + */ + getData() { + return this.#data; + } - /** - * Get the path of the JSON file. - */ - getPath() { - return this.#path; - } + /** + * Get the path of the JSON file. + */ + getPath() { + return this.#path; + } - /** - * Set the data of the JSON file. - */ - reset() { - this.#data = JSON.parse(FileLib.read("VolcAddons", "json/" + this.#file)); - } + /** + * Set the data of the JSON file. + */ + reset() { + this.#data = JSON.parse(FileLib.read("VolcAddons", "json/" + this.#file)); + } } diff --git a/utils/Launch.js b/utils/Launch.js index 1a01cbc..16829eb 100644 --- a/utils/Launch.js +++ b/utils/Launch.js @@ -1,13 +1,12 @@ +import { BOLD, GOLD, GRAY, LOGO, RESET, UNDERLINE, WHITE } from "./Constants"; import Settings from "./Settings"; import Socket from "./Socket"; -import { BOLD, GOLD, GRAY, LOGO, RESET, UNDERLINE, WHITE } from "./Constants"; import { delay } from "./ThreadTils"; - // Create the necessary directories and files for the module to work if (!FileLib.exists("VolcAddons", "data")) new java.io.File("config/ChatTriggers/modules/VolcAddons/Data").mkdir(); if (!FileLib.exists("VolcAddons", "data/contract.txt")) - FileLib.write("VolcAddons", "data/contract.txt", FileLib.read("VolcAddons", "assets/contract.txt")); + FileLib.write("VolcAddons", "data/contract.txt", FileLib.read("VolcAddons", "assets/contract.txt")); // Utility Variable Control const CHANGED_SETTINGS = new Set(["itemPrice", "bossAlert", "miniAlert", "vanqCounter"]); @@ -17,30 +16,36 @@ if (typeof Settings.partyCommands !== "boolean") Settings.partyCommands = false; // First Run const version = JSON.parse(FileLib.read("VolcAddons", "metadata.json")).version; const once = register("worldLoad", () => { - once.unregister(); - delay(() => { - // NEW UPDATE - Display update message when a new version is detected - if (version != data.version) { - data.version = JSON.parse(FileLib.read("VolcAddons", "metadata.json")).version; - ChatLib.chat(`\n${LOGO + WHITE + BOLD}LATEST UPDATE ${GRAY}[v${JSON.parse(FileLib.read("VolcAddons", "metadata.json")).version}]!`); - JSON.parse(FileLib.read("VolcAddons", "changelog.json")).forEach(change => ChatLib.chat(change)); - ChatLib.chat(""); - } + once.unregister(); + delay(() => { + // NEW UPDATE - Display update message when a new version is detected + if (version != data.version) { + data.version = JSON.parse(FileLib.read("VolcAddons", "metadata.json")).version; + ChatLib.chat( + `\n${LOGO + WHITE + BOLD}LATEST UPDATE ${GRAY}[v${ + JSON.parse(FileLib.read("VolcAddons", "metadata.json")).version + }]!` + ); + JSON.parse(FileLib.read("VolcAddons", "changelog.json")).forEach((change) => ChatLib.chat(change)); + ChatLib.chat(""); + } - // FIRST RUN - Display welcome message for new users - if (data.newUser) { - ChatLib.chat( -`\n${GOLD + BOLD + UNDERLINE}VolcAddons v${JSON.parse(FileLib.read("VolcAddons", "metadata.json")).version + RESET} -LF GRAPES! (P.S. do /volcaddons, /volc, /va, /itee) -Instruction manual (i think) => /va help\n`); - data.newUser = false; + // FIRST RUN - Display welcome message for new users + if (data.newUser) { + ChatLib.chat( + `\n${GOLD + BOLD + UNDERLINE}VolcAddons v${ + JSON.parse(FileLib.read("VolcAddons", "metadata.json")).version + RESET } - }, 1000); +LF GRAPES! (P.S. do /volcaddons, /volc, /va, /itee) +Instruction manual (i think) => /va help\n` + ); + data.newUser = false; + } + }, 1000); }); // Track unique users Socket.send({ - "command": "user", - "version": version, + command: "user", + version: version, }); - diff --git a/utils/ListTils.js b/utils/ListTils.js index d15840e..46f2c74 100644 --- a/utils/ListTils.js +++ b/utils/ListTils.js @@ -1,94 +1,98 @@ -import Settings from "./Settings"; -import { AQUA, DARK_AQUA, DARK_GRAY, GRAY, GREEN, LOGO, RED, WHITE, YELLOW } from "./Constants"; -import { convertToPascalCase, convertToTitleCase, unformatNumber } from "./functions/format"; -import { updateAuction } from "../features/economy/Economy"; import { updateEntityList } from "../features/combat/EntityDetect"; +import { updateAuction } from "../features/economy/Economy"; import { setWarps } from "../features/event/MythRitual"; import { updateWidgetList } from "../features/general/WidgetDisplay"; -import { setRegisters } from "./RegisterTils"; +import { AQUA, DARK_AQUA, DARK_GRAY, GRAY, GREEN, LOGO, RED, WHITE, YELLOW } from "./Constants"; import { data } from "./Data"; - +import { setRegisters } from "./RegisterTils"; +import Settings from "./Settings"; +import { convertToPascalCase, convertToTitleCase, unformatNumber } from "./functions/format"; /** * Prints a list of items to chat. - * + * * @param {Object} list - The list to be printed. * @param {String} listName - The name of the list for displaying messages. * @param {Number} page - The page number to display. * @param {Number} pagy - Number of items per page. */ -export function printList(list, listName, page, pagy=12, action=true, command="remove", hoverKey=false) { - if (isNaN(page)) page = 1; - - ChatLib.clearChat(5858); - const isArray = Array.isArray(list); - const length = isArray ? list.length : Object.keys(list).length; - const total = Math.ceil(length / pagy) || 1; - page = MathLib.clamp(page, 1, total); - - // Print out header - const message = new Message("\n&c&m-----------------------------------------------------&r").setChatLineId(5858); - const header = ChatLib.getCenteredText(`${listName} ${page > 1 ? "<< " : ""}(Page ${page} of ${total})${page < total ? " >>" : ""}`); - const whitespace = header.match(/^\s+/)[0]; - - const lArrow = new TextComponent("&r&e&l<<&r&9") - .setClickAction("run_command") - .setClickValue(`/va ${listName} list ${page - 1}`) - .setHoverValue(`${YELLOW}Click to view page ${page - 1}.`); - const rArrow = new TextComponent("&r&e&l>>") - .setClickAction("run_command") - .setClickValue(`/va ${listName} list ${page + 1}`) - .setHoverValue(`${YELLOW}Click to view page ${page + 1}.`); - message.addTextComponent(whitespace); - - if (page > 1) message.addTextComponent(lArrow); - message.addTextComponent(` §6${convertToTitleCase(listName)} §8(§fPage §7${page} §fof §7${total}§8) `); - if (page < total) message.addTextComponent(rArrow); - - // Loop through variables - const pageIndex = (page - 1) * pagy; - if (length === 0) message.addTextComponent(`\n` + ChatLib.getCenteredText(YELLOW + " 404, This list is empty!")); - else if (hoverKey) { - const keys = Object.keys(list); - for (let i = pageIndex; i < Math.min(pageIndex + pagy, length); i++) { - let key = keys[i]; - message.addTextComponent(`\n ${DARK_GRAY}⁍ `); - message.addTextComponent(new TextComponent(`${YELLOW + key}`) - .setClickAction("run_command") - .setClickValue(`/va ${listName} ${command} ${key}`) - .setHoverValue(list[key]) - ); - } - } else if (isArray) { - for (let i = pageIndex; i < Math.min(pageIndex + pagy, length); i++) { - if (action) { - message.addTextComponent(`\n ${DARK_GRAY}⁍ `); - message.addTextComponent(new TextComponent(`${YELLOW + list[i]}`) - .setClickAction("run_command") - .setClickValue(`/va ${listName} ${command} ${list[i]}`) - .setHoverValue(`${YELLOW}Click to ${command} ${AQUA + list[i] + YELLOW} from list.`) - ); - } else message.addTextComponent(`\n ${DARK_GRAY}⁍ ${YELLOW + list[i]}`); - } - } else { - const keys = Object.keys(list); - for (let i = pageIndex; i < Math.min(pageIndex + pagy, length); i++) { - let key = keys[i]; - if (action) { - message.addTextComponent(`\n ${DARK_GRAY}⁍ `); - message.addTextComponent(new TextComponent(`${YELLOW + key}`) - .setClickAction("run_command") - .setClickValue(`/va ${listName} remove ${key}`) - .setHoverValue(`${YELLOW}Click to remove ${YELLOW + key + YELLOW} from list.`) - ); - message.addTextComponent(new TextComponent(`${GRAY} => ${YELLOW + list[key]}`)); - } else message.addTextComponent(`\n ${DARK_GRAY}⁍ ${YELLOW + key + GRAY} => ${YELLOW + list[key]}`); - } +export function printList(list, listName, page, pagy = 12, action = true, command = "remove", hoverKey = false) { + if (isNaN(page)) page = 1; + + ChatLib.clearChat(5858); + const isArray = Array.isArray(list); + const length = isArray ? list.length : Object.keys(list).length; + const total = Math.ceil(length / pagy) || 1; + page = MathLib.clamp(page, 1, total); + + // Print out header + const message = new Message("\n&c&m-----------------------------------------------------&r").setChatLineId(5858); + const header = ChatLib.getCenteredText( + `${listName} ${page > 1 ? "<< " : ""}(Page ${page} of ${total})${page < total ? " >>" : ""}` + ); + const whitespace = header.match(/^\s+/)[0]; + + const lArrow = new TextComponent("&r&e&l<<&r&9") + .setClickAction("run_command") + .setClickValue(`/va ${listName} list ${page - 1}`) + .setHoverValue(`${YELLOW}Click to view page ${page - 1}.`); + const rArrow = new TextComponent("&r&e&l>>") + .setClickAction("run_command") + .setClickValue(`/va ${listName} list ${page + 1}`) + .setHoverValue(`${YELLOW}Click to view page ${page + 1}.`); + message.addTextComponent(whitespace); + + if (page > 1) message.addTextComponent(lArrow); + message.addTextComponent(` §6${convertToTitleCase(listName)} §8(§fPage §7${page} §fof §7${total}§8) `); + if (page < total) message.addTextComponent(rArrow); + + // Loop through variables + const pageIndex = (page - 1) * pagy; + if (length === 0) message.addTextComponent(`\n` + ChatLib.getCenteredText(YELLOW + " 404, This list is empty!")); + else if (hoverKey) { + const keys = Object.keys(list); + for (let i = pageIndex; i < Math.min(pageIndex + pagy, length); i++) { + let key = keys[i]; + message.addTextComponent(`\n ${DARK_GRAY}⁍ `); + message.addTextComponent( + new TextComponent(`${YELLOW + key}`) + .setClickAction("run_command") + .setClickValue(`/va ${listName} ${command} ${key}`) + .setHoverValue(list[key]) + ); + } + } else if (isArray) { + for (let i = pageIndex; i < Math.min(pageIndex + pagy, length); i++) { + if (action) { + message.addTextComponent(`\n ${DARK_GRAY}⁍ `); + message.addTextComponent( + new TextComponent(`${YELLOW + list[i]}`) + .setClickAction("run_command") + .setClickValue(`/va ${listName} ${command} ${list[i]}`) + .setHoverValue(`${YELLOW}Click to ${command} ${AQUA + list[i] + YELLOW} from list.`) + ); + } else message.addTextComponent(`\n ${DARK_GRAY}⁍ ${YELLOW + list[i]}`); + } + } else { + const keys = Object.keys(list); + for (let i = pageIndex; i < Math.min(pageIndex + pagy, length); i++) { + let key = keys[i]; + if (action) { + message.addTextComponent(`\n ${DARK_GRAY}⁍ `); + message.addTextComponent( + new TextComponent(`${YELLOW + key}`) + .setClickAction("run_command") + .setClickValue(`/va ${listName} remove ${key}`) + .setHoverValue(`${YELLOW}Click to remove ${YELLOW + key + YELLOW} from list.`) + ); + message.addTextComponent(new TextComponent(`${GRAY} => ${YELLOW + list[key]}`)); + } else message.addTextComponent(`\n ${DARK_GRAY}⁍ ${YELLOW + key + GRAY} => ${YELLOW + list[key]}`); } + } - // Footer - message.addTextComponent("&c&m-----------------------------------------------------&r"); - message.chat(); + // Footer + message.addTextComponent("&c&m-----------------------------------------------------&r"); + message.chat(); } /** @@ -100,68 +104,127 @@ export function printList(list, listName, page, pagy=12, action=true, command="r * @returns {String[]} - The updated list. */ export function updateList(args, listName) { - const list = data[listName]; - const isArray = Array.isArray(list); - const command = args[1] - const item = listName === "moblist" || listName === "spamlist" ? args.slice(2).join(' ') : args.slice(2).join(' ').toLowerCase(); - - // Object pairs - const held = Player?.getHeldItem()?.getItemNBT()?.getCompoundTag("tag")?.getCompoundTag("ExtraAttributes")?.getString("id"); - const value = listName === "valuelist" || listName === "cdlist" ? unformatNumber(args[2]) : args.slice(3).join(' '); - const key = listName === "colorlist" ? convertToPascalCase(args[2]) : - (listName === "cdlist" || listName === "valuelist") && held !== undefined ? held : args[2]; - - switch (command) { - case "add": // ADD TO LIST - if (isArray && !list.includes(item)) { - list.push(item); - ChatLib.chat(`${LOGO + GREEN}Successfully added "${WHITE + item + GREEN}" to the ${listName}!`); - } else if (!isArray && !(key in list)) { - list[key] = value; - ChatLib.chat(`${LOGO + GREEN}Successfully linked "${WHITE + value + GREEN}" to [${WHITE + key + GREEN}]!`); - } else ChatLib.chat(`${LOGO + RED}[${WHITE + (isArray ? item : key) + RED}] is already in the ${listName}!`); - break; - case "remove": // REMOVE FROM LIST - if (isArray && list.indexOf(item) > -1) { - list.splice(list.indexOf(item), 1); - ChatLib.chat(`${LOGO + GREEN}Successfully removed "${WHITE + item + GREEN}" from the ${listName}!`); - } else if (!isArray && key in list) { - delete list[key]; - ChatLib.chat(`${LOGO + GREEN}Successfully removed "${WHITE + key + GREEN}" from the ${listName}!`); - } else ChatLib.chat(`${LOGO + RED}[${WHITE + item + RED}] is not in the ${listName}!`); - break; - case "clear": // CLEAR LIST - if (isArray) list.length = 0; - else Object.keys(list).forEach(key => delete list[key]); - ChatLib.chat(`${LOGO + GREEN}Successfully cleared the ${listName}!`); - break; - case "view": // DISPLAY LIST - case "list": - printList(list, listName, parseInt(args[2] ?? 1)); - return; - case "reset": // RESET LIST TO DEFAULT - if (listName === "dianalist") data.dianalist = ["hub", "da", "castle", "museum", "wizard"]; - else if (listName === "attributelist") data.attributelist = ["arachno", "attack_speed", "blazing", "combo", "elite", "ender", "ignition", "life_recovery", - "mana_steal", "midas_touch", "undead", "warrior", "deadeye", "arachno_resistance", "blazing_resistance", "breeze", "dominance", "ender_resistance", - "experience", "fortitude", "life_regeneration", "lifeline", "magic_find", "mana_pool", "mana_regeneration", "mending", "speed", "undead_resistance", "veteran", - "blazing_fortune", "fishing_experience", "infection", "double_hook", "fisherman", "fishing_speed", "hunter", "trophy_hunter"]; - else if (listName === "moblist") if (listName === "moblist") data.moblist = ["vanquisher", "jawbus", "thunder", "inquisitor"]; - else if (isArray) list.length = 0; - else Object.keys(list).forEach(key => delete list[key]); - ChatLib.chat(`${LOGO + GREEN}Successfully reset the ${listName}!`); - break; - case "value": - if (listName === "attributelist") { - data.attributelist = ["breeze", "dominance", "fortitude", "lifeline", "magic_find", "mana_pool", "mana_regeneration", "mending", "speed", "veteran", - "blazing_fortune", "fishing_experience"]; - ChatLib.chat(`${LOGO + GREEN}Successfully limited to valuable attributes!`); - break; - } - default: - ChatLib.chat(`\n${LOGO + RED}Error: Invalid argument "${command}"!`); - let base = `${LOGO + RED}Please input as: ${WHITE}/va ${listName} ${GRAY}<${WHITE}view, clear, add, remove`; - - if (listName === "cdlist") base += `[cd] + const list = data[listName]; + const isArray = Array.isArray(list); + const command = args[1]; + const item = + listName === "moblist" || listName === "spamlist" ? args.slice(2).join(" ") : args.slice(2).join(" ").toLowerCase(); + + // Object pairs + const held = Player?.getHeldItem() + ?.getItemNBT() + ?.getCompoundTag("tag") + ?.getCompoundTag("ExtraAttributes") + ?.getString("id"); + const value = listName === "valuelist" || listName === "cdlist" ? unformatNumber(args[2]) : args.slice(3).join(" "); + const key = + listName === "colorlist" + ? convertToPascalCase(args[2]) + : (listName === "cdlist" || listName === "valuelist") && held !== undefined + ? held + : args[2]; + + switch (command) { + case "add": // ADD TO LIST + if (isArray && !list.includes(item)) { + list.push(item); + ChatLib.chat(`${LOGO + GREEN}Successfully added "${WHITE + item + GREEN}" to the ${listName}!`); + } else if (!isArray && !(key in list)) { + list[key] = value; + ChatLib.chat(`${LOGO + GREEN}Successfully linked "${WHITE + value + GREEN}" to [${WHITE + key + GREEN}]!`); + } else ChatLib.chat(`${LOGO + RED}[${WHITE + (isArray ? item : key) + RED}] is already in the ${listName}!`); + break; + case "remove": // REMOVE FROM LIST + if (isArray && list.indexOf(item) > -1) { + list.splice(list.indexOf(item), 1); + ChatLib.chat(`${LOGO + GREEN}Successfully removed "${WHITE + item + GREEN}" from the ${listName}!`); + } else if (!isArray && key in list) { + delete list[key]; + ChatLib.chat(`${LOGO + GREEN}Successfully removed "${WHITE + key + GREEN}" from the ${listName}!`); + } else ChatLib.chat(`${LOGO + RED}[${WHITE + item + RED}] is not in the ${listName}!`); + break; + case "clear": // CLEAR LIST + if (isArray) list.length = 0; + else Object.keys(list).forEach((key) => delete list[key]); + ChatLib.chat(`${LOGO + GREEN}Successfully cleared the ${listName}!`); + break; + case "view": // DISPLAY LIST + case "list": + printList(list, listName, parseInt(args[2] ?? 1)); + return; + case "reset": // RESET LIST TO DEFAULT + if (listName === "dianalist") data.dianalist = ["hub", "da", "castle", "museum", "wizard"]; + else if (listName === "attributelist") + data.attributelist = [ + "arachno", + "attack_speed", + "blazing", + "combo", + "elite", + "ender", + "ignition", + "life_recovery", + "mana_steal", + "midas_touch", + "undead", + "warrior", + "deadeye", + "arachno_resistance", + "blazing_resistance", + "breeze", + "dominance", + "ender_resistance", + "experience", + "fortitude", + "life_regeneration", + "lifeline", + "magic_find", + "mana_pool", + "mana_regeneration", + "mending", + "speed", + "undead_resistance", + "veteran", + "blazing_fortune", + "fishing_experience", + "infection", + "double_hook", + "fisherman", + "fishing_speed", + "hunter", + "trophy_hunter", + ]; + else if (listName === "moblist") + if (listName === "moblist") data.moblist = ["vanquisher", "jawbus", "thunder", "inquisitor"]; + else if (isArray) list.length = 0; + else Object.keys(list).forEach((key) => delete list[key]); + ChatLib.chat(`${LOGO + GREEN}Successfully reset the ${listName}!`); + break; + case "value": + if (listName === "attributelist") { + data.attributelist = [ + "breeze", + "dominance", + "fortitude", + "lifeline", + "magic_find", + "mana_pool", + "mana_regeneration", + "mending", + "speed", + "veteran", + "blazing_fortune", + "fishing_experience", + ]; + ChatLib.chat(`${LOGO + GREEN}Successfully limited to valuable attributes!`); + break; + } + default: + ChatLib.chat(`\n${LOGO + RED}Error: Invalid argument "${command}"!`); + let base = `${LOGO + RED}Please input as: ${WHITE}/va ${listName} ${GRAY}<${WHITE}view, clear, add, remove`; + + if (listName === "cdlist") + base += `[cd] ${DARK_GRAY}This will set the cooldown of your currently held item. ${DARK_AQUA}Special args (put in front, e.x 'a60'): @@ -169,33 +232,35 @@ ${DARK_AQUA}Special args (put in front, e.x 'a60'): - ${AQUA}l ${GRAY}=> ${AQUA}left click - ${AQUA}a ${GRAY}=> ${AQUA}no cd (e.x Plasmaflux) - ${AQUA}s ${GRAY}=> ${AQUA}shift`; - else if (listName === "emotelist") base += `${GRAY}> ${WHITE}[key] [value]`; - else if (listName === "dianalist") base += `${GRAY}> <${WHITE}hub, castle, da, museum, crypt, wizard${GRAY}>>`; - else if (listName === "moblist") base += `${GRAY}> <${WHITE}[MCEntityClass], [Stand Name]${GRAY}>>`; - else if (listName === "colorlist") base += `${GRAY}> ${WHITE}[mob] [r] [g] [b]`; - else if (listName === "valuelist") base += `${GRAY}> ${WHITE}*[item_id] [value]\n${DARK_GRAY}This will set the value of your currently held item.`; - else if (listName === "spamlist") - base += `${GRAY}> ${WHITE}[phrase]\n${DARK_GRAY}Remember to add variables with ${"${var}"}, for example:\n ${DARK_GRAY}va sl add Guild > ${"${player}"} left.`; - else if (listName === "attributelist") base += `, value> ${WHITE}[attribute_name]` - else base += "> [item]"; - - ChatLib.chat(base); - return; - } - - ChatLib.command(`va ${listName} list`, true); - - if (listName === "moblist" || listName === "colorlist") updateEntityList(); - else if (listName === "dianalist") setWarps(); - else if (listName === "valuelist") updateAuction(); - else if (listName === "widgetlist") updateWidgetList(); - else if (listName === "prefixlist") ChatLib.chat(`${LOGO + GREEN}Please use ${AQUA}/ct load ${GREEN}to reload registers!`); - setRegisters(off = Settings.skyblockToggle && !Scoreboard.getTitle().removeFormatting().includes("SKYBLOCK")); + else if (listName === "emotelist") base += `${GRAY}> ${WHITE}[key] [value]`; + else if (listName === "dianalist") base += `${GRAY}> <${WHITE}hub, castle, da, museum, crypt, wizard${GRAY}>>`; + else if (listName === "moblist") base += `${GRAY}> <${WHITE}[MCEntityClass], [Stand Name]${GRAY}>>`; + else if (listName === "colorlist") base += `${GRAY}> ${WHITE}[mob] [r] [g] [b]`; + else if (listName === "valuelist") + base += `${GRAY}> ${WHITE}*[item_id] [value]\n${DARK_GRAY}This will set the value of your currently held item.`; + else if (listName === "spamlist") + base += `${GRAY}> ${WHITE}[phrase]\n${DARK_GRAY}Remember to add variables with ${"${var}"}, for example:\n ${DARK_GRAY}va sl add Guild > ${"${player}"} left.`; + else if (listName === "attributelist") base += `, value> ${WHITE}[attribute_name]`; + else base += "> [item]"; + + ChatLib.chat(base); + return; + } + + ChatLib.command(`va ${listName} list`, true); + + if (listName === "moblist" || listName === "colorlist") updateEntityList(); + else if (listName === "dianalist") setWarps(); + else if (listName === "valuelist") updateAuction(); + else if (listName === "widgetlist") updateWidgetList(); + else if (listName === "prefixlist") + ChatLib.chat(`${LOGO + GREEN}Please use ${AQUA}/ct load ${GREEN}to reload registers!`); + setRegisters((off = Settings.skyblockToggle && !Scoreboard.getTitle().removeFormatting().includes("SKYBLOCK"))); } /** * /va edit command to directly change the waypoint arrays. - * + * * @param {String[]} args - Array of player input values. * @param {String} type - Type of soul (Enigma/Montezuma). * @param {String} soul - Name of the soul. @@ -203,29 +268,29 @@ ${DARK_AQUA}Special args (put in front, e.x 'a60'): * @param {String} world - Current world name */ export function soulEdit(args, type, soul, base, world) { - switch (args[1]) { - case "reset": - data[soul] = base; - ChatLib.chat(`${LOGO + GREEN}Succesfully reset ${type} waypoint!`); - break; - case "clear": - data[soul] = world === undefined ? [] : {}; - ChatLib.chat(`${LOGO + GREEN}Succesfully cleared ${type} waypoint!`); - break; - case "pop": - const souls = data[soul][world]; - if (souls === undefined || souls.length === 0) { - ChatLib.chat(`${LOGO + RED}There are no ${type} souls to pop!`); - return; - } - - const closest = getClosest([Player.getX(), Player.getY(), Player.getZ()], souls); - if (closest !== undefined) souls.splice(souls.indexOf(closest[0]), 1); - ChatLib.chat(`${LOGO + GREEN}Succesfully popped closest ${type} soul!`); - break; - default: - ChatLib.chat(`\n${LOGO + RED}Error: Invalid argument "${args[1]}"!`); - ChatLib.chat(`${LOGO + RED}Please input as: ${WHITE}/va ${type} ${GRAY}<${WHITE}reset, clear, pop${GRAY}>`); - break; - } + switch (args[1]) { + case "reset": + data[soul] = base; + ChatLib.chat(`${LOGO + GREEN}Succesfully reset ${type} waypoint!`); + break; + case "clear": + data[soul] = world === undefined ? [] : {}; + ChatLib.chat(`${LOGO + GREEN}Succesfully cleared ${type} waypoint!`); + break; + case "pop": + const souls = data[soul][world]; + if (souls === undefined || souls.length === 0) { + ChatLib.chat(`${LOGO + RED}There are no ${type} souls to pop!`); + return; + } + + const closest = getClosest([Player.getX(), Player.getY(), Player.getZ()], souls); + if (closest !== undefined) souls.splice(souls.indexOf(closest[0]), 1); + ChatLib.chat(`${LOGO + GREEN}Succesfully popped closest ${type} soul!`); + break; + default: + ChatLib.chat(`\n${LOGO + RED}Error: Invalid argument "${args[1]}"!`); + ChatLib.chat(`${LOGO + RED}Please input as: ${WHITE}/va ${type} ${GRAY}<${WHITE}reset, clear, pop${GRAY}>`); + break; + } } diff --git a/utils/Location.js b/utils/Location.js index 270edf7..c86fdf8 100644 --- a/utils/Location.js +++ b/utils/Location.js @@ -1,158 +1,155 @@ -import Settings from "./Settings"; import { BOLD, DARK_GRAY, GOLD, LOGO, YELLOW } from "./Constants"; import { setRegisters } from "./RegisterTils"; +import Settings from "./Settings"; import { delay } from "./ThreadTils"; - class Location { - #world = undefined; - #zone = undefined; - #tier = 0; - #server = undefined; - #season = undefined; - #time = 0; - - constructor() { - /** - * Set registers. - */ - register("chat", (id) => { - this.#server = id; - }).setCriteria("Sending to server ${id}..."); - - register("tick", () => { - if (!World.isLoaded()) return; - - let zoneLine = Scoreboard?.getLines()?.find((line) => line.getName().startsWith(" §7⏣")) ?? - Scoreboard?.getLines()?.find((line) => line.getName().startsWith(" §5ф")); - this.#zone = zoneLine === undefined ? "None" : - zoneLine.getName().removeFormatting().substring(3); - }); - - register("step", this.setSeason).setDelay(10); - this.setSeason(); - - register("worldLoad", () => { - this.findWorld(); - }).setPriority(Priority.LOWEST); - - register("worldUnload", () => { - this.#world = undefined; - setRegisters(off=true); - }); - - register("serverDisconnect", () => { - this.#world = undefined; - setRegisters(off=true); - }); - - register("command", () => { - this.test(); - }).setName("worldTest"); - } - - /** - * Returns Location.#world - * - * @returns {String} - Current world name i.e. "Gold Mine". - */ - getWorld() { - return this.#world; - } - - /** - * Returns Location.#zone - * - * @returns {String} - Current world name i.e. "Plot - 1". - */ - getZone() { - return this.#zone; - } - - /** - * Returns Location.#tier - * - * @returns {String} - Current tier of Kuudra or 0 if not in Kuudra. - */ - getTier() { - return this.#tier; - } - + #world = undefined; + #zone = undefined; + #tier = 0; + #server = undefined; + #season = undefined; + #time = 0; + + constructor() { /** - * Returns Location.#server - * - * @returns {String} - Current server id i.e. "m188AJ". + * Set registers. */ - getServer() { - return this.#server; - } - - /** - * Returns Location.#season - * - * @returns {String} - Current Skyblock season. - */ - getSeason() { - return this.#season; - } - - /** - * Sets Location.#season using epoch time and SB time offset. - */ - setSeason() { - const now = new Date().getTime() / 1_000; - this.#time = (now - 107_704) % 446_400; - // const sbMonth = this.#time / 37_200; - // const sbDay = (this.#time % 37_200) / 1_200; - const ratio = this.#time / 446_400; - - this.#season = ratio < 0.25 ? "Spring" : - ratio < 0.5 ? "Summer" : - ratio < 0.75 ? "Autumn" : "Winter"; - } - - /** - * Used to output current location data to chat for testing. - */ - test() { - ChatLib.chat( -`${LOGO + GOLD + BOLD}World Test: + register("chat", (id) => { + this.#server = id; + }).setCriteria("Sending to server ${id}..."); + + register("tick", () => { + if (!World.isLoaded()) return; + + let zoneLine = + Scoreboard?.getLines()?.find((line) => line.getName().startsWith(" §7⏣")) ?? + Scoreboard?.getLines()?.find((line) => line.getName().startsWith(" §5ф")); + this.#zone = zoneLine === undefined ? "None" : zoneLine.getName().removeFormatting().substring(3); + }); + + register("step", this.setSeason).setDelay(10); + this.setSeason(); + + register("worldLoad", () => { + this.findWorld(); + }).setPriority(Priority.LOWEST); + + register("worldUnload", () => { + this.#world = undefined; + setRegisters((off = true)); + }); + + register("serverDisconnect", () => { + this.#world = undefined; + setRegisters((off = true)); + }); + + register("command", () => { + this.test(); + }).setName("worldTest"); + } + + /** + * Returns Location.#world + * + * @returns {String} - Current world name i.e. "Gold Mine". + */ + getWorld() { + return this.#world; + } + + /** + * Returns Location.#zone + * + * @returns {String} - Current world name i.e. "Plot - 1". + */ + getZone() { + return this.#zone; + } + + /** + * Returns Location.#tier + * + * @returns {String} - Current tier of Kuudra or 0 if not in Kuudra. + */ + getTier() { + return this.#tier; + } + + /** + * Returns Location.#server + * + * @returns {String} - Current server id i.e. "m188AJ". + */ + getServer() { + return this.#server; + } + + /** + * Returns Location.#season + * + * @returns {String} - Current Skyblock season. + */ + getSeason() { + return this.#season; + } + + /** + * Sets Location.#season using epoch time and SB time offset. + */ + setSeason() { + const now = new Date().getTime() / 1_000; + this.#time = (now - 107_704) % 446_400; + // const sbMonth = this.#time / 37_200; + // const sbDay = (this.#time % 37_200) / 1_200; + const ratio = this.#time / 446_400; + + this.#season = ratio < 0.25 ? "Spring" : ratio < 0.5 ? "Summer" : ratio < 0.75 ? "Autumn" : "Winter"; + } + + /** + * Used to output current location data to chat for testing. + */ + test() { + ChatLib.chat( + `${LOGO + GOLD + BOLD}World Test: ${DARK_GRAY} - ${GOLD}World: ${YELLOW + this.#world} ${DARK_GRAY} - ${GOLD}Tier: ${YELLOW + this.#tier} ${DARK_GRAY} - ${GOLD}Season: ${YELLOW + this.#season} ${DARK_GRAY} - ${GOLD}Server: ${YELLOW + this.#server}` - ); - } - - /** - * Private. - */ - findWorld = (noFind = 0) => { - // Make sure Hypixel world is loaded) - if (noFind > 9) return; - else if (!World.isLoaded()) delay(() => this.findWorld(noFind + 1), 1000); - - // Get world from tab list - let world = TabList.getNames()?.find(tab => tab.startsWith("§r§b§lArea:") || tab.startsWith("§r§b§lDungeon:")); - if (world === undefined) delay(() => this.findWorld(noFind + 1), 1000); - else { - // Get world formatted - this.#world = world.removeFormatting().split(' ').splice(1).join(' '); - - // Get tier for Kuudra - if (this.#world === "Kuudra") { - delay(() => { - const zone = this.getZone(); - this.#tier = parseInt(zone.charAt(zone.length - 2)); - }, 1000); - } - - // Call functions when world is loaded - delay(() => { - setRegisters(off = Settings.skyblockToggle && !Scoreboard.getTitle().removeFormatting().includes("SKYBLOCK")); - Client.showTitle(" ", "", 0, 1, 0); // Fix first title not showing - }, 1000); - } + ); + } + + /** + * Private. + */ + findWorld = (noFind = 0) => { + // Make sure Hypixel world is loaded) + if (noFind > 9) return; + else if (!World.isLoaded()) delay(() => this.findWorld(noFind + 1), 1000); + + // Get world from tab list + let world = TabList.getNames()?.find((tab) => tab.startsWith("§r§b§lArea:") || tab.startsWith("§r§b§lDungeon:")); + if (world === undefined) delay(() => this.findWorld(noFind + 1), 1000); + else { + // Get world formatted + this.#world = world.removeFormatting().split(" ").splice(1).join(" "); + + // Get tier for Kuudra + if (this.#world === "Kuudra") { + delay(() => { + const zone = this.getZone(); + this.#tier = parseInt(zone.charAt(zone.length - 2)); + }, 1000); + } + + // Call functions when world is loaded + delay(() => { + setRegisters((off = Settings.skyblockToggle && !Scoreboard.getTitle().removeFormatting().includes("SKYBLOCK"))); + Client.showTitle(" ", "", 0, 1, 0); // Fix first title not showing + }, 1000); } + }; } -export default new Location; +export default new Location(); diff --git a/utils/Mayor.js b/utils/Mayor.js index c18bd7c..27c985d 100644 --- a/utils/Mayor.js +++ b/utils/Mayor.js @@ -1,28 +1,29 @@ import { request } from "../../requestV2"; - class Mayor { - #mayor = undefined; - #perks = new Set([]); + #mayor = undefined; + #perks = new Set([]); - constructor() { - register("worldLoad", () => { - request({ - url: "https://api.hypixel.net/v2/resources/skyblock/election", - json: true - }).then(response => { - this.#mayor = response.mayor.name; - this.#perks = new Set([...response.mayor.perks.map(perk => perk.name)]); - }).catch(err => console.error(`VolcAddons: ${err.cause ?? err}`)); - }); - } + constructor() { + register("worldLoad", () => { + request({ + url: "https://api.hypixel.net/v2/resources/skyblock/election", + json: true, + }) + .then((response) => { + this.#mayor = response.mayor.name; + this.#perks = new Set([...response.mayor.perks.map((perk) => perk.name)]); + }) + .catch((err) => console.error(`VolcAddons: ${err.cause ?? err}`)); + }); + } - getMayor() { - return this.#mayor; - } + getMayor() { + return this.#mayor; + } - getPerks() { - return this.#perks; - } + getPerks() { + return this.#perks; + } } -export default new Mayor; +export default new Mayor(); diff --git a/utils/Overlay.js b/utils/Overlay.js index 4ea311b..2aa42fa 100644 --- a/utils/Overlay.js +++ b/utils/Overlay.js @@ -1,9 +1,8 @@ +import { GREEN, ITALIC, LOGO } from "./Constants"; import location from "./Location"; import Settings from "./Settings"; -import { GREEN, ITALIC, LOGO } from "./Constants"; import { drawBox, renderScale } from "./functions/render"; - /** * Variables used to move all active GUIs. */ @@ -26,305 +25,328 @@ let guiView = 0; * Renders overlays on the GUI if it's open. */ const moving = register("renderOverlay", () => { - if (!Settings.vaToggle) return; - - // Hover detection - let hovered = false; - const mouseX = Client.getMouseX(); - const mouseY = Client.getMouseY(); - - overlays.forEach(o => { - if (!Settings[o.setting]) return; - - // Draw example text and box - const scale = o.loc[2]; - const editing = o === currentOverlay; - const x = o.loc[0] - (o.loc[3] ? o.ewidth : 0); - const y = o.loc[1]; - const highlight = !hovered && - mouseX > x - 3 * scale && mouseX < x + o.ewidth + 3 * scale && - mouseY > y - 3 * scale && mouseY < y + o.eheight + 3 * scale; - if (highlight) hovered = true; - - drawBox( - x - 3 * scale, y - 3 * scale, 0, - o.ewidth + 5 * scale, o.eheight + 5 * scale, - editing ? EDIT_BOX : highlight ? HOVER_BOX : o.loc[5] ? RECT_COLOR : EDIT_COLOR, - editing ? EDIT_BORDER : highlight ? HOVER_BORDER : BORDER_COLOR - ); - renderScale(o.loc[0], o.loc[1], o.example, o.loc[2], o.loc[3], o.loc[4], 0); - }); + if (!Settings.vaToggle) return; + + // Hover detection + let hovered = false; + const mouseX = Client.getMouseX(); + const mouseY = Client.getMouseY(); + + overlays.forEach((o) => { + if (!Settings[o.setting]) return; + + // Draw example text and box + const scale = o.loc[2]; + const editing = o === currentOverlay; + const x = o.loc[0] - (o.loc[3] ? o.ewidth : 0); + const y = o.loc[1]; + const highlight = + !hovered && + mouseX > x - 3 * scale && + mouseX < x + o.ewidth + 3 * scale && + mouseY > y - 3 * scale && + mouseY < y + o.eheight + 3 * scale; + if (highlight) hovered = true; + + drawBox( + x - 3 * scale, + y - 3 * scale, + 0, + o.ewidth + 5 * scale, + o.eheight + 5 * scale, + editing ? EDIT_BOX : highlight ? HOVER_BOX : o.loc[5] ? RECT_COLOR : EDIT_COLOR, + editing ? EDIT_BORDER : highlight ? HOVER_BORDER : BORDER_COLOR + ); + renderScale(o.loc[0], o.loc[1], o.example, o.loc[2], o.loc[3], o.loc[4], 0); + }); }).unregister(); /** * Handles overlay selection when clicking on the screen. */ const clicking = register("guiMouseClick", (x, y, button) => { - const clicked = overlays.find(o => { - const scale = o.loc[2]; - const oX = o.loc[0] - (o.loc[3] ? o.ewidth : 0); - const oY = o.loc[1]; - - return x > oX - 3 * scale && - x < oX + o.ewidth + 3 * scale && - y > oY - 3 * scale && - y < oY + o.eheight + 3 * scale; - }); - - if (button === 0) - currentOverlay = clicked; - else if (button === 1 && clicked !== undefined) - overlays.splice(overlays.indexOf(clicked), 1); + const clicked = overlays.find((o) => { + const scale = o.loc[2]; + const oX = o.loc[0] - (o.loc[3] ? o.ewidth : 0); + const oY = o.loc[1]; + + return x > oX - 3 * scale && x < oX + o.ewidth + 3 * scale && y > oY - 3 * scale && y < oY + o.eheight + 3 * scale; + }); + + if (button === 0) currentOverlay = clicked; + else if (button === 1 && clicked !== undefined) overlays.splice(overlays.indexOf(clicked), 1); }).unregister(); /** * Handles movement of the selected overlay. */ const dragging = register("dragged", (dx, dy) => { - if (currentOverlay === undefined || !gui.isOpen()) return; + if (currentOverlay === undefined || !gui.isOpen()) return; - currentOverlay.loc[0] += parseInt(dx); - currentOverlay.loc[1] += parseInt(dy); + currentOverlay.loc[0] += parseInt(dx); + currentOverlay.loc[1] += parseInt(dy); }).unregister(); /** * Handles scaling of the selected overlay using key presses. */ const keying = register("guiKey", (_, keyCode) => { - // View Change - if (keyCode === 17) { - guiView = (guiView + 1) % 5; - if (guiView === 0) { // All - overlays = [...overlaid]; - ChatLib.chat(`${LOGO + GREEN}Successfully changed to global view!`); - } else if (guiView === 1) { // World - overlays = overlaid.filter(overlay => overlay.requires.has("all") || overlay.requires.has(location.getWorld())); - ChatLib.chat(`${LOGO + GREEN}Successfully changed to world view!`); - } else if (guiView === 2) { // Visible - overlays = overlays.filter(overlay => overlay.message !== ""); - ChatLib.chat(`${LOGO + GREEN}Successfully changed to visible view!`); - } else if (guiView === 3) { // Overlay - overlays = overlaid.filter(overlay => overlay.trigger === "renderOverlay"); - ChatLib.chat(`${LOGO + GREEN}Successfully changed to overlay view!`); - } else if (guiView === 4) { // GUI - overlays = overlaid.filter(overlay => overlay.trigger === "guiRender"); - ChatLib.chat(`${LOGO + GREEN}Successfully changed to GUI view!`); - } - } else if (keyCode === 1) { - currentOverlay = undefined; - moving.unregister(); - clicking.unregister(); - dragging.unregister(); - keying.unregister(); - overlays = overlaid; - guiView = 0; - return; + // View Change + if (keyCode === 17) { + guiView = (guiView + 1) % 5; + if (guiView === 0) { + // All + overlays = [...overlaid]; + ChatLib.chat(`${LOGO + GREEN}Successfully changed to global view!`); + } else if (guiView === 1) { + // World + overlays = overlaid.filter((overlay) => overlay.requires.has("all") || overlay.requires.has(location.getWorld())); + ChatLib.chat(`${LOGO + GREEN}Successfully changed to world view!`); + } else if (guiView === 2) { + // Visible + overlays = overlays.filter((overlay) => overlay.message !== ""); + ChatLib.chat(`${LOGO + GREEN}Successfully changed to visible view!`); + } else if (guiView === 3) { + // Overlay + overlays = overlaid.filter((overlay) => overlay.trigger === "renderOverlay"); + ChatLib.chat(`${LOGO + GREEN}Successfully changed to overlay view!`); + } else if (guiView === 4) { + // GUI + overlays = overlaid.filter((overlay) => overlay.trigger === "guiRender"); + ChatLib.chat(`${LOGO + GREEN}Successfully changed to GUI view!`); } - - if (currentOverlay !== undefined) - currentOverlay.handleKey(keyCode); + } else if (keyCode === 1) { + currentOverlay = undefined; + moving.unregister(); + clicking.unregister(); + dragging.unregister(); + keying.unregister(); + overlays = overlaid; + guiView = 0; + return; + } + + if (currentOverlay !== undefined) currentOverlay.handleKey(keyCode); }).unregister(); /** * Opens gui to move all overlays */ export function openGUI() { - overlaid = [...overlays]; - gui.open(); - moving.register(); - clicking.register(); - dragging.register(); - keying.register(); -}; - + overlaid = [...overlays]; + gui.open(); + moving.register(); + clicking.register(); + dragging.register(); + keying.register(); +} /** * Overlay render in one register */ const renders = { - "guiRender": [], - "renderOverlay": [] -} -Object.keys(renders).forEach(key => { - register(key, () => { - if (!Settings.vaToggle) return; - - renders[key].forEach(render => { - if (Settings[render.setting] && !render.special() && !gui.isOpen() && !render.gui.isOpen() && render.message) { - if (!(render.requires.has(location.getWorld()) || render.requires.has("all"))) return; - - if (render.loc[5] && render.width !== 0) { - drawBox( - render.loc[0] - (render.loc[3] ? render.ewidth : 0) - 3 * render.loc[2], render.loc[1] - 3 * render.loc[2], 0, - render.width + 5 * render.loc[2], render.height + 5 * render.loc[2], - RECT_COLOR, BORDER_COLOR - ); - } - renderScale(render.loc[0], render.loc[1], render.message, render.loc[2], render.loc[3], render.loc[4], 0); - } - }); + guiRender: [], + renderOverlay: [], +}; +Object.keys(renders).forEach((key) => { + register(key, () => { + if (!Settings.vaToggle) return; + + renders[key].forEach((render) => { + if (Settings[render.setting] && !render.special() && !gui.isOpen() && !render.gui.isOpen() && render.message) { + if (!(render.requires.has(location.getWorld()) || render.requires.has("all"))) return; + + if (render.loc[5] && render.width !== 0) { + drawBox( + render.loc[0] - (render.loc[3] ? render.ewidth : 0) - 3 * render.loc[2], + render.loc[1] - 3 * render.loc[2], + 0, + render.width + 5 * render.loc[2], + render.height + 5 * render.loc[2], + RECT_COLOR, + BORDER_COLOR + ); + } + renderScale(render.loc[0], render.loc[1], render.message, render.loc[2], render.loc[3], render.loc[4], 0); + } }); + }); }); - export class Overlay { - /** - * Creates an overlay with HUD elements and GUI functionality. - * - * @param {String} setting - The setting key used to determine whether the overlay should be shown. - * @param {Type[]} loc - An array representing [x, y, scale, align, flex, background] of the overlay. - * @param {String} command - The command name that will open the GUI. - * @param {String} example - The example text to be displayed when editing the GUI. - * @param {String[]} requires - An array of world names where the overlay should be displayed or ["all"] if it should render everywhere. - * @param {String} trigger - Type of trigger to use for the rendering register. - * @param {Function} special - Special rendering function to optional be used in place of text rendering. Should return true if used. - */ - constructor(setting, loc, command, example = "Test", requires = ["all"], trigger = "renderOverlay", special = () => false) { - overlays.push(this); - - // Update private variables - this.setting = setting; - this.loc = loc; - this.message = ""; + /** + * Creates an overlay with HUD elements and GUI functionality. + * + * @param {String} setting - The setting key used to determine whether the overlay should be shown. + * @param {Type[]} loc - An array representing [x, y, scale, align, flex, background] of the overlay. + * @param {String} command - The command name that will open the GUI. + * @param {String} example - The example text to be displayed when editing the GUI. + * @param {String[]} requires - An array of world names where the overlay should be displayed or ["all"] if it should render everywhere. + * @param {String} trigger - Type of trigger to use for the rendering register. + * @param {Function} special - Special rendering function to optional be used in place of text rendering. Should return true if used. + */ + constructor( + setting, + loc, + command, + example = "Test", + requires = ["all"], + trigger = "renderOverlay", + special = () => false + ) { + overlays.push(this); + + // Update private variables + this.setting = setting; + this.loc = loc; + this.message = ""; + this.width = 0; + this.height = 0; + this.example = example; + this.setSize("example"); + this.requires = new Set(requires); + this.trigger = trigger; + this.special = special; + this.gui = new Gui(); + + // loc array changes for versions < 2.9.4 + if (this.loc[3] === undefined) this.loc.push(false); + if (this.loc[4] === undefined) this.loc.push(false); + if (this.loc[5] === undefined) this.loc.push(true); + + // The actual rendering register for the GUI. + renders[trigger]?.push(this); + + // Set registers for editing the GUI. + this.moving = register("renderOverlay", () => { + const width = Renderer.screen.getWidth(); + const height = Renderer.screen.getHeight(); + + // Coords and scale + const coords = `${ITALIC}x: ${Math.round(this.loc[0])}, y: ${Math.round(this.loc[1])}, s: ${this.loc[2].toFixed( + 2 + )}`; + const align = this.loc[0] + Renderer.getStringWidth(coords) > width; + renderScale(this.loc[0] + (align ? -2 : 2), this.loc[1] - 10, coords, 1, align, false, 300); + + Renderer.translate(0, 0, 300); + Renderer.drawLine(Renderer.WHITE, this.loc[0], 1, this.loc[0], height, 0.5); + Renderer.translate(0, 0, 300); + Renderer.drawLine(Renderer.WHITE, width, this.loc[1], 1, this.loc[1], 0.5); + + // Draw example text + if (this.loc[5]) { + drawBox( + this.loc[0] - (this.loc[3] ? this.ewidth : 0) - 3 * this.loc[2], + this.loc[1] - 3 * this.loc[2], + 0, + this.ewidth + 5 * this.loc[2], + this.eheight + 5 * this.loc[2], + this.loc[5] ? RECT_COLOR : EDIT_COLOR, + BORDER_COLOR + ); + } + renderScale(this.loc[0], this.loc[1], this.example, this.loc[2], this.loc[3], this.loc[4], 0); + }).unregister(); + + // Register editing stuff + this.dragging = register("dragged", (_, __, x, y) => { + this.loc[0] = parseInt(x); + this.loc[1] = parseInt(y); + }).unregister(); + + this.keying = register("guiKey", (_, keyCode) => { + if (keyCode === 1) { + this.moving.unregister(); + this.dragging.unregister(); + this.keying.unregister(); + } else this.handleKey(keyCode); + }).unregister(); + + // Register a command to open the GUI when executed. + register("command", () => { + this.gui.open(); + this.moving.register(); + this.dragging.register(); + this.keying.register(); + }).setName(command); + } + + handleKey(keyCode) { + if (keyCode === 13) this.loc[2] = Math.round((this.loc[2] + 0.05) * 100) / 100; + // Increase Scale (+ key) + else if (keyCode === 12) this.loc[2] = Math.round((this.loc[2] - 0.05) * 100) / 100; + // Decrease Scale (- key) + else if (keyCode === 38) this.loc[3] = !this.loc[3]; // Swap align (l key) + else if (keyCode === 35) this.loc[4] = !this.loc[4]; // Swap flex (h key) + else if (keyCode === 48) this.loc[5] = !this.loc[5]; // Swap flex (b key) + else return; + + this.setSize("message"); + this.setSize("example"); + } + + /** + * Replaces current overlay message with provided message. + * + * @param {String} message - Message to be updated to. + */ + setMessage(message) { + this.message = message; + this.setSize("message"); + } + + /** + * Sets width and height of overlay. + * Fixes getStringWidth not setting bolded size correctly. + * + * @param {String} type - "message" to set message height or "example" to set example height. + */ + setSize(type) { + // Set flex size + let message = type === "message" ? this.message : this.example; + message = this.loc[4] ? message.replace(/\n/g, " ") : message; + const lines = message?.split("\n"); + + // Check if message is not empty + if (!lines?.length) { + if (type === "message") { this.width = 0; this.height = 0; - this.example = example; - this.setSize("example"); - this.requires = new Set(requires); - this.trigger = trigger; - this.special = special; - this.gui = new Gui(); - - // loc array changes for versions < 2.9.4 - if (this.loc[3] === undefined) this.loc.push(false); - if (this.loc[4] === undefined) this.loc.push(false); - if (this.loc[5] === undefined) this.loc.push(true); - - // The actual rendering register for the GUI. - renders[trigger]?.push(this); - - // Set registers for editing the GUI. - this.moving = register("renderOverlay", () => { - const width = Renderer.screen.getWidth(); - const height = Renderer.screen.getHeight(); - - // Coords and scale - const coords = `${ITALIC}x: ${Math.round(this.loc[0])}, y: ${Math.round(this.loc[1])}, s: ${this.loc[2].toFixed(2)}`; - const align = this.loc[0] + Renderer.getStringWidth(coords) > width; - renderScale(this.loc[0] + (align ? -2 : 2), this.loc[1] - 10, coords, 1, align, false, 300); - - Renderer.translate(0, 0, 300); - Renderer.drawLine(Renderer.WHITE, this.loc[0], 1, this.loc[0], height, 0.5); - Renderer.translate(0, 0, 300); - Renderer.drawLine(Renderer.WHITE, width, this.loc[1], 1, this.loc[1], 0.5); - - // Draw example text - if (this.loc[5]) { - drawBox( - this.loc[0] - (this.loc[3] ? this.ewidth : 0) - 3 * this.loc[2], this.loc[1] - 3 * this.loc[2], 0, - this.ewidth + 5 * this.loc[2], this.eheight + 5 * this.loc[2], - this.loc[5] ? RECT_COLOR : EDIT_COLOR, BORDER_COLOR - ); - } - renderScale(this.loc[0], this.loc[1], this.example, this.loc[2], this.loc[3], this.loc[4], 0); - }).unregister(); - - // Register editing stuff - this.dragging = register("dragged", (_, __, x, y) => { - this.loc[0] = parseInt(x); - this.loc[1] = parseInt(y); - }).unregister(); - - this.keying = register("guiKey", (_, keyCode) => { - if (keyCode === 1) { - this.moving.unregister(); - this.dragging.unregister(); - this.keying.unregister(); - } else this.handleKey(keyCode); - }).unregister(); - - // Register a command to open the GUI when executed. - register("command", () => { - this.gui.open(); - this.moving.register(); - this.dragging.register(); - this.keying.register(); - }).setName(command); + } else { + this.ewidth = 0; + this.eheight = 0; + } + return; } - handleKey(keyCode) { - if (keyCode === 13) this.loc[2] = Math.round((this.loc[2] + 0.05) * 100) / 100; // Increase Scale (+ key) - else if (keyCode === 12) this.loc[2] = Math.round((this.loc[2] - 0.05) * 100) / 100; // Decrease Scale (- key) - else if (keyCode === 38) this.loc[3] = !this.loc[3]; // Swap align (l key) - else if (keyCode === 35) this.loc[4] = !this.loc[4]; // Swap flex (h key) - else if (keyCode === 48) this.loc[5] = !this.loc[5]; // Swap flex (b key) - else return; - - this.setSize("message"); - this.setSize("example"); - } - - /** - * Replaces current overlay message with provided message. - * - * @param {String} message - Message to be updated to. - */ - setMessage(message) { - this.message = message; - this.setSize("message"); - } - - /** - * Sets width and height of overlay. - * Fixes getStringWidth not setting bolded size correctly. - * - * @param {String} type - "message" to set message height or "example" to set example height. - */ - setSize(type) { - // Set flex size - let message = type === "message" ? this.message : this.example; - message = this.loc[4] ? message.replace(/\n/g, " ") : message; - const lines = message?.split("\n"); - - // Check if message is not empty - if (!(lines?.length)) { - if (type === "message") { - this.width = 0; - this.height = 0; - } else { - this.ewidth = 0; - this.eheight = 0; - } - return; + // Set message height + if (type === "message") this.height = lines.length * 9 * this.loc[2]; + else this.eheight = lines.length * 9 * this.loc[2]; + + // Find line with largest width + let maxWidth = 0; + lines.forEach((line) => { + if (line.includes("§l")) { + const splitLine = line.split("§l"); + let stringWidth = 0; + + for (let i = 0; i < splitLine.length; i++) { + if (i === 0) stringWidth += Renderer.getStringWidth(splitLine[i]); + else { + let clearIndex = splitLine[i].indexOf("§"); + let boldedString = clearIndex !== -1 ? splitLine[i].substring(0, clearIndex) : splitLine[i]; + let unboldedString = clearIndex !== -1 ? splitLine[i].substring(clearIndex, splitLine[i].length) : ""; + stringWidth += Renderer.getStringWidth(boldedString) * 1.2 + Renderer.getStringWidth(unboldedString); + } } - // Set message height - if (type === "message") this.height = lines.length * 9 * this.loc[2]; - else this.eheight = lines.length * 9 * this.loc[2]; - - // Find line with largest width - let maxWidth = 0; - lines.forEach(line => { - if (line.includes('§l')) { - const splitLine = line.split('§l'); - let stringWidth = 0; - - for (let i = 0; i < splitLine.length; i++) { - if (i === 0) stringWidth += Renderer.getStringWidth(splitLine[i]); - else { - let clearIndex = splitLine[i].indexOf("§"); - let boldedString = clearIndex !== -1 ? splitLine[i].substring(0, clearIndex) : splitLine[i]; - let unboldedString = clearIndex !== -1 ? splitLine[i].substring(clearIndex, splitLine[i].length) : ""; - stringWidth += Renderer.getStringWidth(boldedString) * 1.2 + Renderer.getStringWidth(unboldedString); - } - } - - maxWidth = Math.max(maxWidth, stringWidth); - } else maxWidth = Math.max(maxWidth, Renderer.getStringWidth(line)); - }); - - // Set message width - if (type === "message") this.width = maxWidth * this.loc[2]; - else this.ewidth = maxWidth * this.loc[2]; - } + maxWidth = Math.max(maxWidth, stringWidth); + } else maxWidth = Math.max(maxWidth, Renderer.getStringWidth(line)); + }); + + // Set message width + if (type === "message") this.width = maxWidth * this.loc[2]; + else this.ewidth = maxWidth * this.loc[2]; + } } diff --git a/utils/Party.js b/utils/Party.js index c363f1d..01cafeb 100644 --- a/utils/Party.js +++ b/utils/Party.js @@ -1,149 +1,145 @@ import { data } from "./Data"; import { getPlayerName } from "./functions/player"; - class Party { - #in = false; - #leader = false; - #members = new Set(); - - constructor() { - // --- PERSISTENT PARTY DATA --- - if (data.party.updated - Date.now() < 300_000) { - this.#in = data.party.in; - this.#leader = data.party.leader; - this.#members = new Set(data.party.members); - } - - register("gameUnload", () => { - data.party.in = this.#in; - data.party.leader = this.#leader; - data.party.members = [...this.#members]; - data.party.updated = Date.now(); - }).setPriority(Priority.HIGHEST); - - // --- TRACK EMPTY PARTY --- - register("chat", () => { - this.#in = false; - this.#leader = false; - this.#members.clear(); - }).setCriteria("${player} has disbanded the party!"); - - register("chat", () => { - this.#in = false; - this.#leader = false; - this.#members.clear(); - }).setCriteria("The party was disbanded because all invites expired and the party was empty."); - - register("chat", () => { - this.#in = false; - this.#leader = false; - this.#members.clear(); - }).setCriteria("You left the party."); - - register("chat", () => { - this.#in = false; - this.#leader = false; - this.#members.clear(); - }).setCriteria("You are not in a party right now."); - - - // --- TRACK PARTY LEADER --- - register("chat", (player1) => { - this.#leader = Player.getName() === getPlayerName(player1); - this.#in = true; - }).setCriteria("The party was transferred to ${player1} by ${player2}"); - - register("chat", (player1) => { - this.#leader = Player.getName() === getPlayerName(player1); - this.#in = true; - }).setCriteria("The party was transferred to ${player1} because ${player2} left"); - - register("chat", (_, player2) => { - this.#leader = Player.getName() === getPlayerName(player2); - this.#in = true; - }).setCriteria("${player1} has promoted ${player2} to Party Leader"); - - register("chat", (player1) => { - this.#leader = Player.getName() === getPlayerName(player1); - this.#in = true; - }).setCriteria("${player1} invited ${player2} to the party! They have 60 seconds to accept."); - - - // --- TRACK PARTY INTERACTIONS --- - register("chat", (player) => { - this.#leader = false; - this.#in = true; - }).setCriteria("You have joined ${player}'s party!"); - - register("chat", () => { - this.#leader = false; - this.#in = false; - this.#members.clear(); - }).setCriteria("You have been kicked from the party by ${player}"); - - - // --- CONTROL FOR GAME/CT RS --- - register("chat", (leader, event) => { - this.#in = true; - const player = getPlayerName(leader); - this.#leader = Player.getName() === player; - - if (player === Player.getName()) return; - this.#members.add(player); - }).setCriteria("Party Leader: ${leader} ●"); - - register("chat", (members) => { - this.#in = true; - members.split(" ● ").forEach(member => { - const name = getPlayerName(member); - if (name === Player.getName()) return; - this.#members.add(name); - }); - }).setCriteria("Party Moderators: ${members} ● "); - - register("chat", (members) => { - this.#in = true; - members.split(" ● ").forEach(member => { - const name = getPlayerName(member); - if (name === Player.getName()) return; - this.#members.add(name); - }); - }).setCriteria("Party Members: ${members} ● "); - - register("chat", (player) => { - this.#in = true; - const name = getPlayerName(player); - if (name === Player.getName()) return; - this.#members.delete(name); - }).setCriteria("${player} has been removed from the party."); - } - - /** - * Returns Party.#in - * - * @returns {Boolean} - True if in party, otherwise false. - */ - getIn() { - return this.#in; + #in = false; + #leader = false; + #members = new Set(); + + constructor() { + // --- PERSISTENT PARTY DATA --- + if (data.party.updated - Date.now() < 300_000) { + this.#in = data.party.in; + this.#leader = data.party.leader; + this.#members = new Set(data.party.members); } - /** - * Returns Party.#in && Party.#leader - * - * @returns {Boolean} - True if leader of party, otherwise false. - */ - getLeader() { - return this.#in && this.#leader; - } - - /** - * Returns Party.#members - * - * @returns {Set} - Set object of all party members. - */ - getMembers() { - return this.#members; - } + register("gameUnload", () => { + data.party.in = this.#in; + data.party.leader = this.#leader; + data.party.members = [...this.#members]; + data.party.updated = Date.now(); + }).setPriority(Priority.HIGHEST); + + // --- TRACK EMPTY PARTY --- + register("chat", () => { + this.#in = false; + this.#leader = false; + this.#members.clear(); + }).setCriteria("${player} has disbanded the party!"); + + register("chat", () => { + this.#in = false; + this.#leader = false; + this.#members.clear(); + }).setCriteria("The party was disbanded because all invites expired and the party was empty."); + + register("chat", () => { + this.#in = false; + this.#leader = false; + this.#members.clear(); + }).setCriteria("You left the party."); + + register("chat", () => { + this.#in = false; + this.#leader = false; + this.#members.clear(); + }).setCriteria("You are not in a party right now."); + + // --- TRACK PARTY LEADER --- + register("chat", (player1) => { + this.#leader = Player.getName() === getPlayerName(player1); + this.#in = true; + }).setCriteria("The party was transferred to ${player1} by ${player2}"); + + register("chat", (player1) => { + this.#leader = Player.getName() === getPlayerName(player1); + this.#in = true; + }).setCriteria("The party was transferred to ${player1} because ${player2} left"); + + register("chat", (_, player2) => { + this.#leader = Player.getName() === getPlayerName(player2); + this.#in = true; + }).setCriteria("${player1} has promoted ${player2} to Party Leader"); + + register("chat", (player1) => { + this.#leader = Player.getName() === getPlayerName(player1); + this.#in = true; + }).setCriteria("${player1} invited ${player2} to the party! They have 60 seconds to accept."); + + // --- TRACK PARTY INTERACTIONS --- + register("chat", (player) => { + this.#leader = false; + this.#in = true; + }).setCriteria("You have joined ${player}'s party!"); + + register("chat", () => { + this.#leader = false; + this.#in = false; + this.#members.clear(); + }).setCriteria("You have been kicked from the party by ${player}"); + + // --- CONTROL FOR GAME/CT RS --- + register("chat", (leader, event) => { + this.#in = true; + const player = getPlayerName(leader); + this.#leader = Player.getName() === player; + + if (player === Player.getName()) return; + this.#members.add(player); + }).setCriteria("Party Leader: ${leader} ●"); + + register("chat", (members) => { + this.#in = true; + members.split(" ● ").forEach((member) => { + const name = getPlayerName(member); + if (name === Player.getName()) return; + this.#members.add(name); + }); + }).setCriteria("Party Moderators: ${members} ● "); + + register("chat", (members) => { + this.#in = true; + members.split(" ● ").forEach((member) => { + const name = getPlayerName(member); + if (name === Player.getName()) return; + this.#members.add(name); + }); + }).setCriteria("Party Members: ${members} ● "); + + register("chat", (player) => { + this.#in = true; + const name = getPlayerName(player); + if (name === Player.getName()) return; + this.#members.delete(name); + }).setCriteria("${player} has been removed from the party."); + } + + /** + * Returns Party.#in + * + * @returns {Boolean} - True if in party, otherwise false. + */ + getIn() { + return this.#in; + } + + /** + * Returns Party.#in && Party.#leader + * + * @returns {Boolean} - True if leader of party, otherwise false. + */ + getLeader() { + return this.#in && this.#leader; + } + + /** + * Returns Party.#members + * + * @returns {Set} - Set object of all party members. + */ + getMembers() { + return this.#members; + } } export default new Party(); diff --git a/utils/RegisterTils.js b/utils/RegisterTils.js index 1f6c8b5..73de70c 100644 --- a/utils/RegisterTils.js +++ b/utils/RegisterTils.js @@ -1,6 +1,5 @@ import Settings from "./Settings"; - const registers = []; /** @@ -11,34 +10,34 @@ const registers = []; * @param {Function} dependency - The function representing the dependency of the trigger. */ export function registerWhen(trigger, callback) { - trigger.unregister(); - registers.push({ - "trigger": trigger, - "callback": callback, - "active": false - }); + trigger.unregister(); + registers.push({ + trigger: trigger, + callback: callback, + active: false, + }); } /** * Registers and unregisters all registers that need a world or setting to be active. - * + * * @param {Boolean} off - Unregisters all registers if true. */ export function setRegisters(off = false) { - registers.forEach(reg => { - if (!Settings.vaToggle || off || (reg.active && !reg.callback())) { - reg.trigger.unregister(); - reg.active = false; - } else if (!reg.active && reg.callback()) { - reg.trigger.register(); - reg.active = true; - } - }); + registers.forEach((reg) => { + if (!Settings.vaToggle || off || (reg.active && !reg.callback())) { + reg.trigger.unregister(); + reg.active = false; + } else if (!reg.active && reg.callback()) { + reg.trigger.register(); + reg.active = true; + } + }); } // Set registers on settings close register("guiClosed", (event) => { - if (!event.toString().startsWith("gg.essential.vigilance.gui.SettingsGui")) return; + if (!event.toString().startsWith("gg.essential.vigilance.gui.SettingsGui")) return; - setRegisters(off = Settings.skyblockToggle && !Scoreboard.getTitle().removeFormatting().includes("SKYBLOCK")); + setRegisters((off = Settings.skyblockToggle && !Scoreboard.getTitle().removeFormatting().includes("SKYBLOCK"))); }); diff --git a/utils/Settings.js b/utils/Settings.js index ab465e7..53b1d2c 100644 --- a/utils/Settings.js +++ b/utils/Settings.js @@ -1,140 +1,188 @@ -import { AQUA, BLUE, BOLD, DARK_RED, GOLD, GRAY, GREEN, HEADER, ITALIC, RED } from "./Constants"; import { - @TextProperty, - @PercentSliderProperty, - @SliderProperty, - @SwitchProperty, - @ButtonProperty, - @Vigilant, - @CheckboxProperty, - @SelectorProperty, - @ColorProperty, - Color + AQUA, + BLUE, + BOLD, + DARK_RED, + GOLD, + GRAY, + GREEN, + HEADER, + ITALIC, + RED, +} from "./Constants"; +import { + @TextProperty, + @PercentSliderProperty, + @SliderProperty, + @SwitchProperty, + @ButtonProperty, + @Vigilant, + @CheckboxProperty, + @SelectorProperty, + @ColorProperty, + Color } from '../../Vigilance/index'; - @Vigilant("VolcAddons", "VolcAddons", { - getCategoryComparator: () => (a, b) => { - const categories = ["General", "Container", "Party", "Economy", "Combat", "Mining", "Farming", "Event", "Crimson Isles", "Dungeon", "Kuudra", "Rift"]; - return categories.indexOf(a.name) - categories.indexOf(b.name); - } + getCategoryComparator: () => (a, b) => { + const categories = [ + "General", + "Container", + "Party", + "Economy", + "Combat", + "Mining", + "Farming", + "Event", + "Crimson Isles", + "Dungeon", + "Kuudra", + "Rift", + ]; + return categories.indexOf(a.name) - categories.indexOf(b.name); + }, }) class Settings { - constructor() { - this.initialize(this); + constructor() { + this.initialize(this); - // General Category - this.setCategoryDescription("General", - `${HEADER} + // General Category + this.setCategoryDescription( + "General", + `${HEADER} ${ITALIC}Related Commands: /va -${DARK_RED + BOLD}CAUTION: Some features are technically chat macros, so use at your own risk [UAYOR]!`); - - // Container Category - this.setCategoryDescription("Container", - `${HEADER} -${ITALIC}Related Commands: /va `); - - // Party Category - this.setCategoryDescription("Party", - `${HEADER} -${ITALIC}Related Commands: /va `); - - // Economy Category - this.setCategoryDescription("Economy", - `${HEADER} -${ITALIC}Related Commands: /va `); - - // Combat Category - this.setCategoryDescription("Combat", HEADER); - - // Mining Category - this.setCategoryDescription("Mining", - `${HEADER} -${ITALIC}Related Commands: /va `); - - // Farming Category - this.setCategoryDescription("Farming", - `${HEADER} -${ITALIC}Realted Commands: /pesttp`); - - // Event Category - this.setCategoryDescription("Event", - `${HEADER} -${ITALIC}Related Commands: /va `); - - // Crimson Isles Category - this.setCategoryDescription("Crimson Isles", - `${HEADER} -${ITALIC}Related Commands: /va `); - - // Dungeon Category - this.setCategoryDescription("Dungeon", HEADER); - - // Kuudra Category - this.setCategoryDescription("Kuudra", - `${HEADER} -${ITALIC}Related Commands: /va , /kv`); - - // Rift Category - this.setCategoryDescription("Rift", - `${HEADER} -${ITALIC}Related Commands: /va `); - } - - - // ████████████████████████████████████████████████████ GENERAL FEATURES ████████████████████████████████████████████████████ - - // --- Essential --- - @SwitchProperty({ - name: "VolcAddons Toggle", - description: `Toggle ${GREEN}ON ${GRAY}to enable or ${RED}OFF ${GRAY}to disable VolcAddons.`, - category: "General", - subcategory: "Essential" - }) - vaToggle = true; - - @SwitchProperty({ - name: "SkyBlock Toggle", - description: `Toggle ${GREEN}ON ${GRAY}for features to only function in Skyblock or ${RED}OFF ${GRAY}to function anywhere.`, - category: "General", - subcategory: "Essential" - }) - skyblockToggle = true; - - @SwitchProperty({ - name: "Socket Toggle", - description: `Toggle ${GREEN}ON ${GRAY}to send information to VolcSocket server or ${RED}OFF ${GRAY}to prevent. Requires a ${AQUA}/ct load ${GRAY}to take effect.`, - category: "General", - subcategory: "Essential" - }) - socketToggle = true; - - @ButtonProperty({ - name: "Discord", - description: `${RED}Please be mindful of Discord links in chat as they may pose a security risk! ${GRAY}Server very cool, much wow.`, - category: "General", - subcategory: "Essential", - placeholder: "Yamete Kudasai" - }) - discordLink() { - java.awt.Desktop.getDesktop().browse(new java.net.URI("https://discord.gg/ftxB4kG2tw")); - } - - @ButtonProperty({ - name: "GitHub Updater", - description: "Download the Forge.jar file for new release alerts and effortless updating!", - category: "General", - subcategory: "Essential", - placeholder: "Download" - }) - downloadForge() { - const url = "https://raw.githubusercontent.com/zhenga8533/VolcAddons/main/forge/VolcAddons-1.0.jar"; - java.awt.Desktop.getDesktop().browse(new java.net.URI(url)); - } - - @ButtonProperty({ - name: "Move GUI", - description: `Moves all current active GUIs. Runs ${AQUA}/va gui${GRAY}. +${ + DARK_RED + BOLD +}CAUTION: Some features are technically chat macros, so use at your own risk [UAYOR]!` + ); + + // Container Category + this.setCategoryDescription( + "Container", + `${HEADER} +${ITALIC}Related Commands: /va ` + ); + + // Party Category + this.setCategoryDescription( + "Party", + `${HEADER} +${ITALIC}Related Commands: /va ` + ); + + // Economy Category + this.setCategoryDescription( + "Economy", + `${HEADER} +${ITALIC}Related Commands: /va ` + ); + + // Combat Category + this.setCategoryDescription("Combat", HEADER); + + // Mining Category + this.setCategoryDescription( + "Mining", + `${HEADER} +${ITALIC}Related Commands: /va ` + ); + + // Farming Category + this.setCategoryDescription( + "Farming", + `${HEADER} +${ITALIC}Realted Commands: /pesttp` + ); + + // Event Category + this.setCategoryDescription( + "Event", + `${HEADER} +${ITALIC}Related Commands: /va ` + ); + + // Crimson Isles Category + this.setCategoryDescription( + "Crimson Isles", + `${HEADER} +${ITALIC}Related Commands: /va ` + ); + + // Dungeon Category + this.setCategoryDescription("Dungeon", HEADER); + + // Kuudra Category + this.setCategoryDescription( + "Kuudra", + `${HEADER} +${ITALIC}Related Commands: /va , /kv` + ); + + // Rift Category + this.setCategoryDescription( + "Rift", + `${HEADER} +${ITALIC}Related Commands: /va ` + ); + } + + // ████████████████████████████████████████████████████ GENERAL FEATURES ████████████████████████████████████████████████████ + + // --- Essential --- + @SwitchProperty({ + name: "VolcAddons Toggle", + description: `Toggle ${GREEN}ON ${GRAY}to enable or ${RED}OFF ${GRAY}to disable VolcAddons.`, + category: "General", + subcategory: "Essential", + }) + vaToggle = true; + + @SwitchProperty({ + name: "SkyBlock Toggle", + description: `Toggle ${GREEN}ON ${GRAY}for features to only function in Skyblock or ${RED}OFF ${GRAY}to function anywhere.`, + category: "General", + subcategory: "Essential", + }) + skyblockToggle = true; + + @SwitchProperty({ + name: "Socket Toggle", + description: `Toggle ${GREEN}ON ${GRAY}to send information to VolcSocket server or ${RED}OFF ${GRAY}to prevent. Requires a ${AQUA}/ct load ${GRAY}to take effect.`, + category: "General", + subcategory: "Essential", + }) + socketToggle = true; + + @ButtonProperty({ + name: "Discord", + description: `${RED}Please be mindful of Discord links in chat as they may pose a security risk! ${GRAY}Server very cool, much wow.`, + category: "General", + subcategory: "Essential", + placeholder: "Yamete Kudasai", + }) + discordLink() { + java.awt.Desktop.getDesktop().browse( + new java.net.URI("https://discord.gg/ftxB4kG2tw") + ); + } + + @ButtonProperty({ + name: "GitHub Updater", + description: + "Download the Forge.jar file for new release alerts and effortless updating!", + category: "General", + subcategory: "Essential", + placeholder: "Download", + }) + downloadForge() { + const url = + "https://raw.githubusercontent.com/zhenga8533/VolcAddons/main/forge/VolcAddons-1.0.jar"; + java.awt.Desktop.getDesktop().browse(new java.net.URI(url)); + } + + @ButtonProperty({ + name: "Move GUI", + description: `Moves all current active GUIs. Runs ${AQUA}/va gui${GRAY}. Controls: ${AQUA}+/- ${GRAY}to scale. ${AQUA}L ${GRAY}to swap align. @@ -142,1196 +190,1228 @@ Controls: ${AQUA}B ${GRAY}to show BG. ${AQUA}W ${GRAY}to change view. ${AQUA}RC ${GRAY}to hide in editor.`, - category: "General", - subcategory: "Essential", - placeholder: "Move GUI" - }) - moveGUI() { - ChatLib.command("va gui", true); - } - - // --- General --- - @SwitchProperty({ - name: "Remove Selfie Mode", - description: "Removes selfie mode from perspective toggle.", - category: "General", - subcategory: "General" - }) - removeSelfie = false; - - @SliderProperty({ - name: "Render Waypoint", - description: `Creates waypoints out of patcher formated coords. Set seconds until waypoints expire or as 0 to turn ${RED}OFF ${BLUE}(mob waypoints last 1/3 as long)${GRAY}.`, - category: "General", - subcategory: "General", - min: 0, - max: 120 - }) - drawWaypoint = 0; - - @SliderProperty({ - name: "Skill Tracker", - description: `Tracks and displays skill XP's rate of gain. Set minutes of inactivity required for tracker to reset or as 0 to turn ${RED}OFF${GRAY}. + category: "General", + subcategory: "Essential", + placeholder: "Move GUI", + }) + moveGUI() { + ChatLib.command("va gui", true); + } + + // --- General --- + @SwitchProperty({ + name: "Remove Selfie Mode", + description: "Removes selfie mode from perspective toggle.", + category: "General", + subcategory: "General", + }) + removeSelfie = false; + + @SliderProperty({ + name: "Render Waypoint", + description: `Creates waypoints out of patcher formated coords. Set seconds until waypoints expire or as 0 to turn ${RED}OFF ${BLUE}(mob waypoints last 1/3 as long)${GRAY}.`, + category: "General", + subcategory: "General", + min: 0, + max: 120, + }) + drawWaypoint = 0; + + @SliderProperty({ + name: "Skill Tracker", + description: `Tracks and displays skill XP's rate of gain. Set minutes of inactivity required for tracker to reset or as 0 to turn ${RED}OFF${GRAY}. Move GUI with ${AQUA}/moveSkills ${GRAY}or reset tracker with ${AQUA}/resetSkills${GRAY}.`, - category: "General", - subcategory: "General", - min: 0, - max: 10 - }) - skillTracker = 0; - - @SwitchProperty({ - name: "Text Shadow Toggle", - description: "Sets if text shadow renders on any GUI elements. Make sure Patcher's `Disable Text Shadow` is turned off!", - category: "General", - subcategory: "General" - }) - textShadow = true; - - @SwitchProperty({ - name: "Widget Display", - description: `Displays any widget in ${AQUA}/va wgl${GRAY}. Move GUI with ${AQUA}/move${GRAY}.`, - category: "General", - subcategory: "General" - }) - widgetDisplay = true; - - // --- Server --- - @SliderProperty({ - name: "Fairy Soul Waypoints", - description: `Set distance at which Fairy Soul waypoints will render or as 0 to turn ${RED}OFF${GRAY}.`, - category: "General", - subcategory: "Server", - min: 0, - max: 128 - }) - fairyWaypoint = 0; - - @SliderProperty({ - name: "Hide Far Entities", - description: `Set maximum distance away from player an entity can be or as 0 to turn ${RED}OFF${GRAY}.`, - category: "General", - subcategory: "Server", - min: 0, - max: 128 - }) - hideFarEntity = 0; - @SliderProperty({ - name: "Hide Close Players", - description: `Set minimum distance away from player a player can be or as 0 to turn ${RED}OFF${GRAY}.`, - category: "General", - subcategory: "Server", - min: 0, - max: 128 - }) - hideCloseEntity = 0; - @TextProperty({ - name: "Hide on bush", - description: `Enter world names as [${AQUA}world1, world2, ...${GRAY}] for entity hiders to work on or leave empty for all worlds.`, - category: "General", - subcategory: "Server", - placeholder: "world1, world2, ..." - }) - hideWorlds = ""; - - @SwitchProperty({ - name: "Hide All Particles", - description: "Prevents any particle from rendering, including those not in settings.", - category: "General", - subcategory: "Server" - }) - hideParticles = false; - - @SwitchProperty({ - name: "Server Alert", - description: "Alerts player when they rejoin a previously joined server.", - category: "General", - subcategory: "Server" - }) - serverAlert = true; - - @SwitchProperty({ - name: "Server Status", - description: `Tracks various server/player statistics.\nMove GUI with ${AQUA}/moveStatus${GRAY}.`, - category: "General", - subcategory: "Server" - }) - serverStatus = false; - - @SwitchProperty({ - name: "SkyBlock Stats Display", - description: `Tracks various SkyBlock statistics.\nMove GUI with ${AQUA}/moveStats ${GRAY}and set toggle with ${AQUA}/va toggles${GRAY}.`, - category: "General", - subcategory: "Server" - }) - statsDisplay = false; - - // --- Timer --- - @SwitchProperty({ - name: "Item Cooldown Alert", - description: `${GRAY}Alerts player once item cooldown timer expires.\nAdd cooldowns with ${AQUA}/va cd${GRAY}.`, - category: "General", - subcategory: "Timer" - }) - cooldownAlert = false; - - @TextProperty({ - name: "Reminder Text", - description: "Set the warning text that appears when timer expires.", - category: "General", - subcategory: "Timer", - placeholder: "konnichiwa." - }) - reminderText = ""; - @SliderProperty({ - name: "Reminder Time", - description: `Set minutes until timer expires or as 0 to turn ${RED}OFF${GRAY}.`, - category: "General", - subcategory: "Timer", - min: 0, - max: 120 - }) - reminderTime = 0; - - // --- Webhook --- - @TextProperty({ - name: "Discord Webhook", - description: `Input Discord Webhook link to send the chat messages to. Set toggle with ${AQUA}/va toggles${GRAY}.`, - category: "General", - subcategory: "Webhook", - protected: true - }) - chatWebhook = ""; - - // --- Yapping --- - @SwitchProperty({ - name: "Autocomplete Command", - description: "Attempts to autocomplete commands with the most used commands. Use arrow keys to navigate through suggestions and tab to select suggestion.", - category: "General", - subcategory: "Yapping" - }) - autocomplete = false; - - @SwitchProperty({ - name: "Autocorrect Command", - description: "Attempts to correct invalid commands with valid ones. It will take time to collect enough data to be accurate.", - category: "General", - subcategory: "Yapping" - }) - autocorrect = false; - - @SwitchProperty({ - name: "Bridge Formatter", - description: "Created for myself, so may or may not work for you. Detects '»' symbol in bridge messages to format them.", - category: "General", - subcategory: "Yapping" - }) - bridgeFormat = false; - - @SwitchProperty({ - name: "Custom Emotes", - description: `Replaces parts of chat messages containing emotes in ${AQUA}/emotes${GRAY}. + category: "General", + subcategory: "General", + min: 0, + max: 10, + }) + skillTracker = 0; + + @SwitchProperty({ + name: "Text Shadow Toggle", + description: + "Sets if text shadow renders on any GUI elements. Make sure Patcher's `Disable Text Shadow` is turned off!", + category: "General", + subcategory: "General", + }) + textShadow = true; + + @SwitchProperty({ + name: "Widget Display", + description: `Displays any widget in ${AQUA}/va wgl${GRAY}. Move GUI with ${AQUA}/move${GRAY}.`, + category: "General", + subcategory: "General", + }) + widgetDisplay = true; + + // --- Server --- + @SliderProperty({ + name: "Fairy Soul Waypoints", + description: `Set distance at which Fairy Soul waypoints will render or as 0 to turn ${RED}OFF${GRAY}.`, + category: "General", + subcategory: "Server", + min: 0, + max: 128, + }) + fairyWaypoint = 0; + + @SliderProperty({ + name: "Hide Far Entities", + description: `Set maximum distance away from player an entity can be or as 0 to turn ${RED}OFF${GRAY}.`, + category: "General", + subcategory: "Server", + min: 0, + max: 128, + }) + hideFarEntity = 0; + @SliderProperty({ + name: "Hide Close Players", + description: `Set minimum distance away from player a player can be or as 0 to turn ${RED}OFF${GRAY}.`, + category: "General", + subcategory: "Server", + min: 0, + max: 128, + }) + hideCloseEntity = 0; + @TextProperty({ + name: "Hide on bush", + description: `Enter world names as [${AQUA}world1, world2, ...${GRAY}] for entity hiders to work on or leave empty for all worlds.`, + category: "General", + subcategory: "Server", + placeholder: "world1, world2, ...", + }) + hideWorlds = ""; + + @SwitchProperty({ + name: "Hide All Particles", + description: + "Prevents any particle from rendering, including those not in settings.", + category: "General", + subcategory: "Server", + }) + hideParticles = false; + + @SwitchProperty({ + name: "Server Alert", + description: "Alerts player when they rejoin a previously joined server.", + category: "General", + subcategory: "Server", + }) + serverAlert = true; + + @SwitchProperty({ + name: "Server Status", + description: `Tracks various server/player statistics.\nMove GUI with ${AQUA}/moveStatus${GRAY}.`, + category: "General", + subcategory: "Server", + }) + serverStatus = false; + + @SwitchProperty({ + name: "SkyBlock Stats Display", + description: `Tracks various SkyBlock statistics.\nMove GUI with ${AQUA}/moveStats ${GRAY}and set toggle with ${AQUA}/va toggles${GRAY}.`, + category: "General", + subcategory: "Server", + }) + statsDisplay = false; + + // --- Timer --- + @SwitchProperty({ + name: "Item Cooldown Alert", + description: `${GRAY}Alerts player once item cooldown timer expires.\nAdd cooldowns with ${AQUA}/va cd${GRAY}.`, + category: "General", + subcategory: "Timer", + }) + cooldownAlert = false; + + @TextProperty({ + name: "Reminder Text", + description: "Set the warning text that appears when timer expires.", + category: "General", + subcategory: "Timer", + placeholder: "konnichiwa.", + }) + reminderText = ""; + @SliderProperty({ + name: "Reminder Time", + description: `Set minutes until timer expires or as 0 to turn ${RED}OFF${GRAY}.`, + category: "General", + subcategory: "Timer", + min: 0, + max: 120, + }) + reminderTime = 0; + + // --- Webhook --- + @TextProperty({ + name: "Discord Webhook", + description: `Input Discord Webhook link to send the chat messages to. Set toggle with ${AQUA}/va toggles${GRAY}.`, + category: "General", + subcategory: "Webhook", + protected: true, + }) + chatWebhook = ""; + + // --- Yapping --- + @SwitchProperty({ + name: "Autocomplete Command", + description: + "Attempts to autocomplete commands with the most used commands. Use arrow keys to navigate through suggestions and tab to select suggestion.", + category: "General", + subcategory: "Yapping", + }) + autocomplete = false; + + @SwitchProperty({ + name: "Autocorrect Command", + description: + "Attempts to correct invalid commands with valid ones. It will take time to collect enough data to be accurate.", + category: "General", + subcategory: "Yapping", + }) + autocorrect = false; + + @SwitchProperty({ + name: "Bridge Formatter", + description: + "Created for myself, so may or may not work for you. Detects '»' symbol in bridge messages to format them.", + category: "General", + subcategory: "Yapping", + }) + bridgeFormat = false; + + @SwitchProperty({ + name: "Custom Emotes", + description: `Replaces parts of chat messages containing emotes in ${AQUA}/emotes${GRAY}. Add custom emotes with ${AQUA}/va emote${GRAY}.`, - category: "General", - subcategory: "Yapping" - }) - enableEmotes = false; - - @SwitchProperty({ - name: "SkyBlock XP Alert", - description: "Displays a chat message and title when player gains SkyBlock XP in actionBar.", - category: "General", - subcategory: "General" - }) - levelAlert = true; - - - // ████████████████████████████████████████████████████ PARTY ████████████████████████████████████████████████████ - - // --- Container --- - @SwitchProperty({ - name: "Auction Highlight", - description: `Highlights all items in auction house that have been sold. Personal items are in ${GREEN}GREEN ${GRAY}and co-op items are in ${GOLD}GOLD${GRAY}.`, - category: "Container", - subcategory: "Container" - }) - auctionHighlight = false; - - @SelectorProperty({ - name: "Container Buttons ", - description: `Display buttons that runs a command when pressed. Use ${AQUA}/va buttons ${GRAY}to view related commands.`, - category: "Container", - subcategory: "Container", - options: ["OFF", "Default", "Transparent", "Semi-Transparent", "FurfSky"] - }) - containerButtons = 0; - - @SelectorProperty({ - name: "Container Preview ", - description: `Renders a preview of hovered container besides container GUI. Move GUI with ${AQUA}/movePreview${GRAY}. + category: "General", + subcategory: "Yapping", + }) + enableEmotes = false; + + @SwitchProperty({ + name: "SkyBlock XP Alert", + description: + "Displays a chat message and title when player gains SkyBlock XP in actionBar.", + category: "General", + subcategory: "General", + }) + levelAlert = true; + + // ████████████████████████████████████████████████████ PARTY ████████████████████████████████████████████████████ + + // --- Container --- + @SwitchProperty({ + name: "Auction Highlight", + description: `Highlights all items in auction house that have been sold. Personal items are in ${GREEN}GREEN ${GRAY}and co-op items are in ${GOLD}GOLD${GRAY}.`, + category: "Container", + subcategory: "Container", + }) + auctionHighlight = false; + + @SelectorProperty({ + name: "Container Buttons ", + description: `Display buttons that runs a command when pressed. Use ${AQUA}/va buttons ${GRAY}to view related commands.`, + category: "Container", + subcategory: "Container", + options: ["OFF", "Default", "Transparent", "Semi-Transparent", "FurfSky"], + }) + containerButtons = 0; + + @SelectorProperty({ + name: "Container Preview ", + description: `Renders a preview of hovered container besides container GUI. Move GUI with ${AQUA}/movePreview${GRAY}. Also ${AQUA}/va preview ${GRAY}can be used to lock previews and show replica. Move GUI with ${AQUA}/moveSP${GRAY}.`, - category: "Container", - subcategory: "Container", - options: ["OFF", "Default", "FurfSky"] - }) - containerPreview = 0; - - @SwitchProperty({ - name: "Searchbar", - description: `Highlights item with matching name/lore with search. Supports binary AND (&&) and OR (||). Also can be used as a basic calculator. + category: "Container", + subcategory: "Container", + options: ["OFF", "Default", "FurfSky"], + }) + containerPreview = 0; + + @SwitchProperty({ + name: "Searchbar", + description: `Highlights item with matching name/lore with search. Supports binary AND (&&) and OR (||). Also can be used as a basic calculator. Move GUI with ${AQUA}/moveSearch${GRAY}.`, - category: "Container", - subcategory: "Container" - }) - searchbar = false; - - @SwitchProperty({ - name: "Slot Binding", - description: `Scuffed version of NEU's slot binding feature. Use ${AQUA}/va slot ${GRAY}to view related commands.`, - category: "Container", - subcategory: "Container", - }) - slotBinding = true; - - @SwitchProperty({ - name: "Wardrobe Hotkey", - description: "Allows linking between wardrobe slots and keys. Set key in MC controls to use in wardrobe menu, and follow the instruction provided there.", - category: "Container", - subcategory: "Container", - }) - wardrobeBinding = true; - - // --- Items --- - @SwitchProperty({ - name: "Attribute Abbreviation", - description: `Renders abbreviation of attributes over any item that has them.`, - category: "Container", - subcategory: "Items" - }) - attributeAbbrev = false; - - @SwitchProperty({ - name: "Armor Display", - description: `Displays user's armor pieces as icons on an overlay. Move GUI with ${AQUA}/moveArmor${GRAY}.`, - category: "Container", - subcategory: "Items" - }) - armorDisplay = false; - - @SwitchProperty({ - name: "Equipment Display", - description: `Displays user's equipment pieces as icons on an overlay and besides inventory. Move GUI with ${AQUA}/moveEq${GRAY}.`, - category: "Container", - subcategory: "Items" - }) - equipDisplay = false; - - @SwitchProperty({ - name: "Jyrre Time Display", - description: "Adds time label to Bottle of Jyrre item description.", - category: "Container", - subcategory: "Items" - }) - jyrreTimer = true; - - @SwitchProperty({ - name: "Max Supercraft", - description: "Displays the maximum amount of items that can be crafted using supercraft.", - category: "Container", - subcategory: "Items" - }) - maxSupercraft = true; - - - // ████████████████████████████████████████████████████ PARTY ████████████████████████████████████████████████████ - - // --- Message --- - @TextProperty({ - name: "Guild Join Message", - description: `Set the message to be sent to the guild once a user joins. Use ${AQUA + "${name}"} ${GRAY}in order say player name.`, - category: "Party", - subcategory: "Message" - }) - guildMessage = ""; - - @TextProperty({ - name: "Party Join Message", - description: `Set the message to be sent to the party once a user joins. Use ${AQUA + "${name}"} ${GRAY}in order say player name.`, - category: "Party", - subcategory: "Message" - }) - partyMessage = ""; - - @SwitchProperty({ - name: "Party Leader Only", - description: "Only sends above message when you are party leader.", - category: "Party", - subcategory: "Message" - }) - partyMessageLeader = false; - - // --- Party --- - @SwitchProperty({ - name: "Anti Ghost Party", - description: "Prevents creating ghost parties when inviting multiple players.", - category: "Party", - subcategory: "Party" - }) - antiGhostParty = false; - - @SwitchProperty({ - name: "Auto Join Reparty", - description: "Accepts reparty invites sent within 60 seconds.", - category: "Party", - subcategory: "Party" - }) - joinRP = false; - - @SelectorProperty({ - name: "Auto Transfer", - description: "Transfers party when certain conditions are met.", - category: "Party", - subcategory: "Party", - options: ["OFF", "On Transfer", "On Kick"] - }) - autoTransfer = 0; - - @TextProperty({ - name: "Server Kick Announce", - description: `Set the message to be sent to the party if you get lobby kicked or as nothing to turn ${RED}OFF${GRAY}.`, - category: "Party", - subcategory: "Party" - }) - kickAnnounce = ""; - - @SwitchProperty({ - name: "Whitelist Rejoin", - description: `Accepts party invites from players on the whitelist. + category: "Container", + subcategory: "Container", + }) + searchbar = false; + + @SwitchProperty({ + name: "Slot Binding", + description: `Scuffed version of NEU's slot binding feature. Use ${AQUA}/va slot ${GRAY}to view related commands.`, + category: "Container", + subcategory: "Container", + }) + slotBinding = true; + + @SwitchProperty({ + name: "Wardrobe Hotkey", + description: + "Allows linking between wardrobe slots and keys. Set key in MC controls to use in wardrobe menu, and follow the instruction provided there.", + category: "Container", + subcategory: "Container", + }) + wardrobeBinding = true; + + // --- Items --- + @SwitchProperty({ + name: "Attribute Abbreviation", + description: `Renders abbreviation of attributes over any item that has them.`, + category: "Container", + subcategory: "Items", + }) + attributeAbbrev = false; + + @SwitchProperty({ + name: "Armor Display", + description: `Displays user's armor pieces as icons on an overlay. Move GUI with ${AQUA}/moveArmor${GRAY}.`, + category: "Container", + subcategory: "Items", + }) + armorDisplay = false; + + @SwitchProperty({ + name: "Equipment Display", + description: `Displays user's equipment pieces as icons on an overlay and besides inventory. Move GUI with ${AQUA}/moveEq${GRAY}.`, + category: "Container", + subcategory: "Items", + }) + equipDisplay = false; + + @SwitchProperty({ + name: "Jyrre Time Display", + description: "Adds time label to Bottle of Jyrre item description.", + category: "Container", + subcategory: "Items", + }) + jyrreTimer = true; + + @SwitchProperty({ + name: "Max Supercraft", + description: + "Displays the maximum amount of items that can be crafted using supercraft.", + category: "Container", + subcategory: "Items", + }) + maxSupercraft = true; + + // ████████████████████████████████████████████████████ PARTY ████████████████████████████████████████████████████ + + // --- Message --- + @TextProperty({ + name: "Guild Join Message", + description: `Set the message to be sent to the guild once a user joins. Use ${ + AQUA + "${name}" + } ${GRAY}in order say player name.`, + category: "Party", + subcategory: "Message", + }) + guildMessage = ""; + + @TextProperty({ + name: "Party Join Message", + description: `Set the message to be sent to the party once a user joins. Use ${ + AQUA + "${name}" + } ${GRAY}in order say player name.`, + category: "Party", + subcategory: "Message", + }) + partyMessage = ""; + + @SwitchProperty({ + name: "Party Leader Only", + description: "Only sends above message when you are party leader.", + category: "Party", + subcategory: "Message", + }) + partyMessageLeader = false; + + // --- Party --- + @SwitchProperty({ + name: "Anti Ghost Party", + description: + "Prevents creating ghost parties when inviting multiple players.", + category: "Party", + subcategory: "Party", + }) + antiGhostParty = false; + + @SwitchProperty({ + name: "Auto Join Reparty", + description: "Accepts reparty invites sent within 60 seconds.", + category: "Party", + subcategory: "Party", + }) + joinRP = false; + + @SelectorProperty({ + name: "Auto Transfer", + description: "Transfers party when certain conditions are met.", + category: "Party", + subcategory: "Party", + options: ["OFF", "On Transfer", "On Kick"], + }) + autoTransfer = 0; + + @TextProperty({ + name: "Server Kick Announce", + description: `Set the message to be sent to the party if you get lobby kicked or as nothing to turn ${RED}OFF${GRAY}.`, + category: "Party", + subcategory: "Party", + }) + kickAnnounce = ""; + + @SwitchProperty({ + name: "Whitelist Rejoin", + description: `Accepts party invites from players on the whitelist. Add players with ${AQUA}/va whitelist${GRAY}.`, - category: "Party", - subcategory: "Party" - }) - joinWhitelist = true; - - // --- Party Commands --- - @SwitchProperty({ - name: "Leader Chat Commands", - description: `Allows players in party to use leader commands.\nBanish players with ${AQUA}/va blacklist ${GRAY}and set toggle with ${AQUA}/va toggles${GRAY}.`, - category: "Party", - subcategory: "Party Commands" - }) - leaderCommands = false; - @SwitchProperty({ - name: "Party Chat Commands", - description: `Allows players to use party commands.\nBanish players with ${AQUA}/va blacklist ${GRAY}and set toggle with ${AQUA}/va toggles${GRAY}.`, - category: "Party", - subcategory: "Party Commands" - }) - partyCommands = false; - - - // ████████████████████████████████████████████████████ ECONOMY ████████████████████████████████████████████████████ - - // --- Economy --- - @SelectorProperty({ - name: "Bits Alert", - description: "Alerts player when they are no longer generating bits.", - category: "Economy", - subcategory: "Economy", - options: ["OFF", "Chat", "Title", "All"] - }) - bitsAlert = 0; - - @SliderProperty({ - name: "Coin Tracker", - description: `Tracks and displays purse's rate of gain. Set minutes until tracker resets or as 0 to turn ${RED}OFF${GRAY}. + category: "Party", + subcategory: "Party", + }) + joinWhitelist = true; + + // --- Party Commands --- + @SwitchProperty({ + name: "Leader Chat Commands", + description: `Allows players in party to use leader commands.\nBanish players with ${AQUA}/va blacklist ${GRAY}and set toggle with ${AQUA}/va toggles${GRAY}.`, + category: "Party", + subcategory: "Party Commands", + }) + leaderCommands = false; + @SwitchProperty({ + name: "Party Chat Commands", + description: `Allows players to use party commands.\nBanish players with ${AQUA}/va blacklist ${GRAY}and set toggle with ${AQUA}/va toggles${GRAY}.`, + category: "Party", + subcategory: "Party Commands", + }) + partyCommands = false; + + // ████████████████████████████████████████████████████ ECONOMY ████████████████████████████████████████████████████ + + // --- Economy --- + @SelectorProperty({ + name: "Bits Alert", + description: "Alerts player when they are no longer generating bits.", + category: "Economy", + subcategory: "Economy", + options: ["OFF", "Chat", "Title", "All"], + }) + bitsAlert = 0; + + @SliderProperty({ + name: "Coin Tracker", + description: `Tracks and displays purse's rate of gain. Set minutes until tracker resets or as 0 to turn ${RED}OFF${GRAY}. Move GUI with ${AQUA}/moveCoins ${GRAY}or reset tracker with ${AQUA}/resetCoins${GRAY}.`, - category: "Economy", - subcategory: "Economy", - min: 0, - max: 10 - }) - coinTracker = 0; - - // --- Item Cost --- - @SliderProperty({ - name: "Container Value", - description: `Displays item values in any inventory. Set number of item prices to display or as 0 to turn ${RED}OFF${GRAY}.`, - category: "Economy", - subcategory: "Pricing", - min: 0, - max: 54 - }) - containerValue = 0; - - @SelectorProperty({ - name: "Item Price", - description: "Displays complete item price including enchants, modifiers, and attributes.", - category: "Economy", - subcategory: "Pricing", - options: ["OFF", "Advanced View", "Tooltip View", "Omnipotent View"] - }) - itemPrice = 0; - - @SwitchProperty({ - name: "Single Attribute", - description: "Nullifies the value of the lower value attribute on items with T5 or lower attributes.", - category: "Economy", - subcategory: "Pricing", - }) - singleAttribute = true; - - @SelectorProperty({ - name: "Price Type", - description: "Choose the type of bazaar pricing used in item calculations.", - category: "Economy", - subcategory: "Pricing", - options: ["Order", "Insta"] - }) - priceType = 0; - - @SwitchProperty({ - name: "Trade Value", - description: "Determines the value difference between two trades.", - category: "Economy", - subcategory: "Pricing" - }) - tradeValue = false; - - - // ████████████████████████████████████████████████████ COMBAT ████████████████████████████████████████████████████ - - // --- Bestiary --- - @SelectorProperty({ - name: "Bestiary Counters", - description: `Tracks bestiary hourly progress using tablist widget. + category: "Economy", + subcategory: "Economy", + min: 0, + max: 10, + }) + coinTracker = 0; + + // --- Item Cost --- + @SliderProperty({ + name: "Container Value", + description: `Displays item values in any inventory. Set number of item prices to display or as 0 to turn ${RED}OFF${GRAY}.`, + category: "Economy", + subcategory: "Pricing", + min: 0, + max: 54, + }) + containerValue = 0; + + @SelectorProperty({ + name: "Item Price", + description: + "Displays complete item price including enchants, modifiers, and attributes.", + category: "Economy", + subcategory: "Pricing", + options: ["OFF", "Advanced View", "Tooltip View", "Omnipotent View"], + }) + itemPrice = 0; + + @SwitchProperty({ + name: "Single Attribute", + description: + "Nullifies the value of the lower value attribute on items with T5 or lower attributes.", + category: "Economy", + subcategory: "Pricing", + }) + singleAttribute = true; + + @SelectorProperty({ + name: "Price Type", + description: "Choose the type of bazaar pricing used in item calculations.", + category: "Economy", + subcategory: "Pricing", + options: ["Order", "Insta"], + }) + priceType = 0; + + @SwitchProperty({ + name: "Trade Value", + description: "Determines the value difference between two trades.", + category: "Economy", + subcategory: "Pricing", + }) + tradeValue = false; + + // ████████████████████████████████████████████████████ COMBAT ████████████████████████████████████████████████████ + + // --- Bestiary --- + @SelectorProperty({ + name: "Bestiary Counters", + description: `Tracks bestiary hourly progress using tablist widget. Move GUI with ${AQUA}/moveBe ${GRAY}or reset tracker with ${AQUA}/resetBe${GRAY}.`, - category: "Combat", - subcategory: "Bestiary", - options: ["OFF", "Cumulative", "World"] - }) - bestiaryCounter = 0; - - @SwitchProperty({ - name: "Bestiary GUI", - description: "Shows bestiary level as stack size and highlight uncompleted bestiary milestones.", - category: "Combat", - subcategory: "Bestiary" - }) - bestiaryGUI = false; - - @ColorProperty({ - name: "Hitbox Color", - description: `Set the seed and opacity used to randomize entity hitbox colors.`, - category: "Combat", - subcategory: "Bestiary", - hidden: !FileLib.read("./VolcAddons/Data", "contract.txt")?.split("\n")?.[51]?.includes(Player.getName()) ?? false - }) - hitboxColor = Color.BLACK; - - @SwitchProperty({ - name: "Kill Counter", - description: `Tracks average amount of specific mob kills an hour ${BLUE}(must have Book of Stats)${GRAY}. + category: "Combat", + subcategory: "Bestiary", + options: ["OFF", "Cumulative", "World"], + }) + bestiaryCounter = 0; + + @SwitchProperty({ + name: "Bestiary GUI", + description: + "Shows bestiary level as stack size and highlight uncompleted bestiary milestones.", + category: "Combat", + subcategory: "Bestiary", + }) + bestiaryGUI = false; + + @ColorProperty({ + name: "Hitbox Color", + description: `Set the seed and opacity used to randomize entity hitbox colors.`, + category: "Combat", + subcategory: "Bestiary", + hidden: + !FileLib.read("./VolcAddons/Data", "contract.txt") + ?.split("\n")?.[51] + ?.includes(Player.getName()) ?? false, + }) + hitboxColor = Color.BLACK; + + @SwitchProperty({ + name: "Kill Counter", + description: `Tracks average amount of specific mob kills an hour ${BLUE}(must have Book of Stats)${GRAY}. Move GUI with ${AQUA}/moveKills ${GRAY}or reset tracker with ${AQUA}/resetKills${GRAY}.`, - category: "Combat", - subcategory: "Bestiary" - }) - killCounter = false; - - // --- Combat --- - @SwitchProperty({ - name: "Combo Display", - description: "Replaces Grandma Wolf combo chat messages with a custom GUI.", - category: "Combat", - subcategory: "Combat" - }) - comboDisplay = false; - - @SelectorProperty({ - name: "Damage Tracer", - description: `Spams chat with unique damage ticks ${BLUE}(this is meant for training dummies)${GRAY}.`, - category: "Combat", - subcategory: "Combat", - options: ["OFF", "Simple", "Analytical"] - }) - damageTracker = 0; - - @PercentSliderProperty({ - name: "Low Health Alert", - description: `Set percent hp threshold until alert appears or as 0 to turn ${RED}OFF${GRAY}.`, - category: "Combat", - subcategory: "Combat" - }) - healthAlert = 0.0; - - @SwitchProperty({ - name: "Mana Drain Range", - description: "Highlights and displays number of players in mana drain range. Works only when holding End Stone Sword or fluxes.", - category: "Combat", - subcategory: "Combat" - }) - manaDrain = false; - - @SwitchProperty({ - name: "Ragnarok Detection", - description: "Displays an alert title when Ragnarock Axe finishes casting or is canceled.", - category: "Combat", - subcategory: "Combat" - }) - ragDetect = true; - - // --- Slayer --- - @SelectorProperty({ - name: "Announce Boss Chat", - description: "Sends coordinates of user slayer boss spawns to chat.", - category: "Combat", - subcategory: "Slayer", - options: ["OFF", "All Chat", "Party Chat", "Self"] - }) - bossAlert = 0; - @SelectorProperty({ - name: "Announce Miniboss Chat", - description: `Sends coordinates of user slayer miniboss spawns to chat ${BLUE}(sounds must be ${GREEN}ON${BLUE})${GRAY}.`, - category: "Combat", - subcategory: "Slayer", - options: ["OFF", "All Chat", "Party Chat", "Self"] - }) - miniAlert = 0; - - @SwitchProperty({ - name: "Boss Highlight", - description: "Shows colorful hitboxes around slayer bosses.", - category: "Combat", - subcategory: "Slayer" - }) - bossHighlight = false; - @SwitchProperty({ - name: "Miniboss Highlight", - description: "Shows colorful hitboxes around slayer minibosses.", - category: "Combat", - subcategory: "Slayer" - }) - miniHighlight = false; - - @SliderProperty({ - name: "Slayer Spawn Warning", - description: `Warns player when slayer boss is about to spawn. Set warning percentage or as 0 to turn ${RED}OFF${GRAY}.`, - category: "Combat", - subcategory: "Slayer", - min: 0, - max: 100 - }) - slayerSpawn = 0; - - - // ████████████████████████████████████████████████████ MINING ████████████████████████████████████████████████████ - - // --- Jinx --- - @SliderProperty({ - name: "Powder Chest Detect", - description: `Highlights and counts nearby powder chests. Set block range of detection radius or as 0 to turn ${RED}OFF${GRAY}. + category: "Combat", + subcategory: "Bestiary", + }) + killCounter = false; + + // --- Combat --- + @SwitchProperty({ + name: "Combo Display", + description: "Replaces Grandma Wolf combo chat messages with a custom GUI.", + category: "Combat", + subcategory: "Combat", + }) + comboDisplay = false; + + @SelectorProperty({ + name: "Damage Tracer", + description: `Spams chat with unique damage ticks ${BLUE}(this is meant for training dummies)${GRAY}.`, + category: "Combat", + subcategory: "Combat", + options: ["OFF", "Simple", "Analytical"], + }) + damageTracker = 0; + + @PercentSliderProperty({ + name: "Low Health Alert", + description: `Set percent hp threshold until alert appears or as 0 to turn ${RED}OFF${GRAY}.`, + category: "Combat", + subcategory: "Combat", + }) + healthAlert = 0.0; + + @SwitchProperty({ + name: "Mana Drain Range", + description: + "Highlights and displays number of players in mana drain range. Works only when holding End Stone Sword or fluxes.", + category: "Combat", + subcategory: "Combat", + }) + manaDrain = false; + + @SwitchProperty({ + name: "Ragnarok Detection", + description: + "Displays an alert title when Ragnarock Axe finishes casting or is canceled.", + category: "Combat", + subcategory: "Combat", + }) + ragDetect = true; + + // --- Slayer --- + @SelectorProperty({ + name: "Announce Boss Chat", + description: "Sends coordinates of user slayer boss spawns to chat.", + category: "Combat", + subcategory: "Slayer", + options: ["OFF", "All Chat", "Party Chat", "Self"], + }) + bossAlert = 0; + @SelectorProperty({ + name: "Announce Miniboss Chat", + description: `Sends coordinates of user slayer miniboss spawns to chat ${BLUE}(sounds must be ${GREEN}ON${BLUE})${GRAY}.`, + category: "Combat", + subcategory: "Slayer", + options: ["OFF", "All Chat", "Party Chat", "Self"], + }) + miniAlert = 0; + + @SwitchProperty({ + name: "Boss Highlight", + description: "Shows colorful hitboxes around slayer bosses.", + category: "Combat", + subcategory: "Slayer", + }) + bossHighlight = false; + @SwitchProperty({ + name: "Miniboss Highlight", + description: "Shows colorful hitboxes around slayer minibosses.", + category: "Combat", + subcategory: "Slayer", + }) + miniHighlight = false; + + @SliderProperty({ + name: "Slayer Spawn Warning", + description: `Warns player when slayer boss is about to spawn. Set warning percentage or as 0 to turn ${RED}OFF${GRAY}.`, + category: "Combat", + subcategory: "Slayer", + min: 0, + max: 100, + }) + slayerSpawn = 0; + + // ████████████████████████████████████████████████████ MINING ████████████████████████████████████████████████████ + + // --- Jinx --- + @SliderProperty({ + name: "Powder Chest Detect", + description: `Highlights and counts nearby powder chests. Set block range of detection radius or as 0 to turn ${RED}OFF${GRAY}. Move GUI with ${AQUA}/moveChest${GRAY}.`, - category: "Mining", - subcategory: "Jinx", - min: 0, - max: 128 - }) - powderChest = 0; - - @SelectorProperty({ - name: "Powder Chest Hider", - description: "Removes specified chat messages from powder chest openings.", - category: "Mining", - subcategory: "Jinx", - options: ["OFF", "Items", "Powder", "All"] - }) - powderHider = 0; - - @SliderProperty({ - name: "Powder Tracker", - description: `Displays powders' rate of gain. Set minutes of inactivity required for tracker to reset or as 0 to turn ${RED}OFF${GRAY}. + category: "Mining", + subcategory: "Jinx", + min: 0, + max: 128, + }) + powderChest = 0; + + @SelectorProperty({ + name: "Powder Chest Hider", + description: "Removes specified chat messages from powder chest openings.", + category: "Mining", + subcategory: "Jinx", + options: ["OFF", "Items", "Powder", "All"], + }) + powderHider = 0; + + @SliderProperty({ + name: "Powder Tracker", + description: `Displays powders' rate of gain. Set minutes of inactivity required for tracker to reset or as 0 to turn ${RED}OFF${GRAY}. Move GUI with ${AQUA}/movePowder ${GRAY}or reset tracker with ${AQUA}/resetPowder${GRAY}.`, - category: "Mining", - subcategory: "Jinx", - min: 0, - max: 10 - }) - powderTracker = 0; - - // --- Mining --- - @SwitchProperty({ - name: "Pick Display", - description: `Displays all pickaxe abilities as an overlay and alerts when they are off cooldown. Move GUI with ${AQUA}/movePick${GRAY}.`, - category: "Mining", - subcategory: "Mining" - }) - pickDisplay = false; - - @SwitchProperty({ - name: "Wishing Compass Locator", - description: `Attempts to use 2 wishing compass uses to estimate important locations ${BLUE}(more accurate when used further apart)${GRAY},`, - category: "Mining", - subcategory: "Mining" - }) - compassLocator = false; - - // --- Shaft --- - @SwitchProperty({ - name: "Commission Announce", - description: "Alerts user whenever a commission is completed.", - category: "Mining", - subcategory: "Shaft" - }) - commissionAnnounce = false; - - @SwitchProperty({ - name: "Commissions Display", - description: `Displays mining commissions from tab list onto screen as an overlay. Move GUI with ${AQUA}/moveCommissions${GRAY}.`, - category: "Mining", - subcategory: "Shaft" - }) - commissionsDisplay = false; - - @SelectorProperty({ - name: "Commission Waypoints", - description: "Renders a text waypoint to any gemstone location needed for a commission. Optionally draws line to closest waypoints.", - category: "Mining", - subcategory: "Shaft", - options: ["OFF", "Waypoint", "Line", "Both"] - }) - commissionWaypoints = 0; - - @SwitchProperty({ - name: "Corpse Announce", - description: "Announces corpse location to party chat if it has yet to be announced. Only announces corpse you loot.", - category: "Mining", - subcategory: "Shaft" - }) - corpseAnnounce = false; - - @SwitchProperty({ - name: "Corpse Waypoints", - description: `Display waypoints for nearby corpses. ${DARK_RED}Technically uses ESP so UAYOR!`, - category: "Mining", - subcategory: "Shaft" - }) - corpseWaypoints = false; - - - @SwitchProperty({ - name: "Fossil Helper", - description: "Highlights which slot has the highest probability of being a fossil piece. Sometimes bugs if server lags, but will update if you just click the side.", - category: "Mining", - subcategory: "Shaft" - }) - fossilHelper = false; - - @SwitchProperty({ - name: "Shaft Transfer", - description: "Attempts to use various party transfer chat commands if you discover a mineshaft.", - category: "Mining", - subcategory: "Shaft" - }) - shaftTransfer = false; - - - // ████████████████████████████████████████████████████ FARMING ████████████████████████████████████████████████████ - - // --- Garden --- - @SelectorProperty({ - name: "Composter Display", - description: "Alerts player when composter is inactive or display time until it is inactive.", - category: "Farming", - subcategory: "Garden", - options: ["OFF", "Inactive Title", "Time Overlay"] - }) - compostTab = 0; - - @SwitchProperty({ - name: "Garden Plot Box", - description: `Draws a bounding box on the current plot player is in. Also renders `, - category: "Farming", - subcategory: "Garden" - }) - gardenBox = false; - - @SwitchProperty({ - name: "Garden Tab Display", - description: `Displays garden visitors outside of tab menu.\nMove GUI with ${AQUA}/moveVisitors${GRAY}.`, - category: "Farming", - subcategory: "Garden" - }) - gardenTab = false; - - // --- Pests --- - @SwitchProperty({ - name: "Desk Highlight", - description: `Highlights plots in desk menu in ${RED}RED ${GRAY}if infested or in ${GREEN}GREEN ${GRAY}if sprayed. Also changes stack size of plot to count/time.`, - category: "Farming", - subcategory: "Pests" - }) - deskHighlight = false; - - @SliderProperty({ - name: "Infested Alert", - description: `Set minimum amount of pests must be on garden to display infestation alert or as 0 to turn ${RED}OFF${GRAY}.`, - category: "Farming", - subcategory: "Pests", - min: 0, - max: 8 - }) - infestationAlert = 0; - - @SwitchProperty({ - name: "Pest Alert", - description: "Displays a title on screen when any pests spawn.", - category: "Farming", - subcategory: "Pests" - }) - pestAlert = false; - - @SwitchProperty({ - name: "Pesthunter Display", - description: `Tracks and warns when the pesthunter bonus runs out.\nMove GUI with ${AQUA}/moveBonus${GRAY}.`, - category: "Farming", - subcategory: "Pests" - }) - pesthunterBonus = false; - - @SwitchProperty({ - name: "Spray Display", - description: `Tracks and warns when sprays on any plot expires.\nMove GUI with ${AQUA}/moveSpray${GRAY}.`, - category: "Farming", - subcategory: "Pests" - }) - sprayDisplay = false; - - - // ████████████████████████████████████████████████████ EVENT ████████████████████████████████████████████████████ - - // --- Chocolate Factory --- - @SwitchProperty({ - name: "Chocolate Overlay", - description: "Renders a GUI element that displays current chocolate production data.", - category: "Event", - subcategory: "Chocolate Factory" - }) - chocoDisplay = false; - - @SelectorProperty({ - name: "Egg Announce", - description: "Sends coordinates of opened chocolate eggs to chat.", - category: "Event", - subcategory: "Chocolate Factory", - options: ["OFF", "All Chat", "Party Chat", "Self"] - }) - chocoAlert = 0; - - @SwitchProperty({ - name: "Egg Timers", - description: "Displays overlay off how long until an egg spawns. Also shows title whenever an egg spawns.", - category: "Event", - subcategory: "Chocolate Factory" - }) - eggTimers = false; - - @SwitchProperty({ - name: "Egg Waypoints", - description: `Display waypoints for nearby eggs. ${DARK_RED}Technically uses ESP so UAYOR!`, - category: "Event", - subcategory: "Chocolate Factory" - }) - chocoWaypoints = false; - - @SelectorProperty({ - name: "Rabbit Highlight", - description: "Highlights the worker with the best cost to production ratio.", - category: "Event", - subcategory: "Chocolate Factory", - options: ["OFF", "All", "Only Workers", "No Tower"] - }) - rabbitHighlight = 0; - - @SelectorProperty({ - name: "Stray Alert", - description: `Calls an emergency meeting whenever a stray rabbit appears.`, - category: "Event", - subcategory: "Chocolate Factory", - options: ["OFF", "All", "Gold"] - }) - strayAlert = 0; - - // --- Diana --- - @SwitchProperty({ - name: "Diana Waypoint", - description: `Estimates theoretical burrow location using particles and pitch of Ancestral Spade cast ${BLUE}(POV Soopy servers are down)${GRAY}. + category: "Mining", + subcategory: "Jinx", + min: 0, + max: 10, + }) + powderTracker = 0; + + // --- Mining --- + @SwitchProperty({ + name: "Pick Display", + description: `Displays all pickaxe abilities as an overlay and alerts when they are off cooldown. Move GUI with ${AQUA}/movePick${GRAY}.`, + category: "Mining", + subcategory: "Mining", + }) + pickDisplay = false; + + @SwitchProperty({ + name: "Wishing Compass Locator", + description: `Attempts to use 2 wishing compass uses to estimate important locations ${BLUE}(more accurate when used further apart)${GRAY},`, + category: "Mining", + subcategory: "Mining", + }) + compassLocator = false; + + // --- Shaft --- + @SwitchProperty({ + name: "Commission Announce", + description: "Alerts user whenever a commission is completed.", + category: "Mining", + subcategory: "Shaft", + }) + commissionAnnounce = false; + + @SwitchProperty({ + name: "Commissions Display", + description: `Displays mining commissions from tab list onto screen as an overlay. Move GUI with ${AQUA}/moveCommissions${GRAY}.`, + category: "Mining", + subcategory: "Shaft", + }) + commissionsDisplay = false; + + @SelectorProperty({ + name: "Commission Waypoints", + description: + "Renders a text waypoint to any gemstone location needed for a commission. Optionally draws line to closest waypoints.", + category: "Mining", + subcategory: "Shaft", + options: ["OFF", "Waypoint", "Line", "Both"], + }) + commissionWaypoints = 0; + + @SwitchProperty({ + name: "Corpse Announce", + description: + "Announces corpse location to party chat if it has yet to be announced. Only announces corpse you loot.", + category: "Mining", + subcategory: "Shaft", + }) + corpseAnnounce = false; + + @SwitchProperty({ + name: "Corpse Waypoints", + description: `Display waypoints for nearby corpses. ${DARK_RED}Technically uses ESP so UAYOR!`, + category: "Mining", + subcategory: "Shaft", + }) + corpseWaypoints = false; + + @SwitchProperty({ + name: "Fossil Helper", + description: + "Highlights which slot has the highest probability of being a fossil piece. Sometimes bugs if server lags, but will update if you just click the side.", + category: "Mining", + subcategory: "Shaft", + }) + fossilHelper = false; + + @SwitchProperty({ + name: "Shaft Transfer", + description: + "Attempts to use various party transfer chat commands if you discover a mineshaft.", + category: "Mining", + subcategory: "Shaft", + }) + shaftTransfer = false; + + // ████████████████████████████████████████████████████ FARMING ████████████████████████████████████████████████████ + + // --- Garden --- + @SelectorProperty({ + name: "Composter Display", + description: + "Alerts player when composter is inactive or display time until it is inactive.", + category: "Farming", + subcategory: "Garden", + options: ["OFF", "Inactive Title", "Time Overlay"], + }) + compostTab = 0; + + @SwitchProperty({ + name: "Garden Plot Box", + description: `Draws a bounding box on the current plot player is in. Also renders `, + category: "Farming", + subcategory: "Garden", + }) + gardenBox = false; + + @SwitchProperty({ + name: "Garden Tab Display", + description: `Displays garden visitors outside of tab menu.\nMove GUI with ${AQUA}/moveVisitors${GRAY}.`, + category: "Farming", + subcategory: "Garden", + }) + gardenTab = false; + + // --- Pests --- + @SwitchProperty({ + name: "Desk Highlight", + description: `Highlights plots in desk menu in ${RED}RED ${GRAY}if infested or in ${GREEN}GREEN ${GRAY}if sprayed. Also changes stack size of plot to count/time.`, + category: "Farming", + subcategory: "Pests", + }) + deskHighlight = false; + + @SliderProperty({ + name: "Infested Alert", + description: `Set minimum amount of pests must be on garden to display infestation alert or as 0 to turn ${RED}OFF${GRAY}.`, + category: "Farming", + subcategory: "Pests", + min: 0, + max: 8, + }) + infestationAlert = 0; + + @SwitchProperty({ + name: "Pest Alert", + description: "Displays a title on screen when any pests spawn.", + category: "Farming", + subcategory: "Pests", + }) + pestAlert = false; + + @SwitchProperty({ + name: "Pesthunter Display", + description: `Tracks and warns when the pesthunter bonus runs out.\nMove GUI with ${AQUA}/moveBonus${GRAY}.`, + category: "Farming", + subcategory: "Pests", + }) + pesthunterBonus = false; + + @SwitchProperty({ + name: "Spray Display", + description: `Tracks and warns when sprays on any plot expires.\nMove GUI with ${AQUA}/moveSpray${GRAY}.`, + category: "Farming", + subcategory: "Pests", + }) + sprayDisplay = false; + + // ████████████████████████████████████████████████████ EVENT ████████████████████████████████████████████████████ + + // --- Chocolate Factory --- + @SwitchProperty({ + name: "Chocolate Overlay", + description: + "Renders a GUI element that displays current chocolate production data.", + category: "Event", + subcategory: "Chocolate Factory", + }) + chocoDisplay = false; + + @SelectorProperty({ + name: "Egg Announce", + description: "Sends coordinates of opened chocolate eggs to chat.", + category: "Event", + subcategory: "Chocolate Factory", + options: ["OFF", "All Chat", "Party Chat", "Self"], + }) + chocoAlert = 0; + + @SwitchProperty({ + name: "Egg Timers", + description: + "Displays overlay off how long until an egg spawns. Also shows title whenever an egg spawns.", + category: "Event", + subcategory: "Chocolate Factory", + }) + eggTimers = false; + + @SwitchProperty({ + name: "Egg Waypoints", + description: `Display waypoints for nearby eggs. ${DARK_RED}Technically uses ESP so UAYOR!`, + category: "Event", + subcategory: "Chocolate Factory", + }) + chocoWaypoints = false; + + @SelectorProperty({ + name: "Rabbit Highlight", + description: + "Highlights the worker with the best cost to production ratio.", + category: "Event", + subcategory: "Chocolate Factory", + options: ["OFF", "All", "Only Workers", "No Tower"], + }) + rabbitHighlight = 0; + + @SelectorProperty({ + name: "Stray Alert", + description: `Calls an emergency meeting whenever a stray rabbit appears.`, + category: "Event", + subcategory: "Chocolate Factory", + options: ["OFF", "All", "Gold"], + }) + strayAlert = 0; + + // --- Diana --- + @SwitchProperty({ + name: "Diana Waypoint", + description: `Estimates theoretical burrow location using particles and pitch of Ancestral Spade cast ${BLUE}(POV Soopy servers are down)${GRAY}. Particles must be ${GREEN}ON ${GRAY}and use ${AQUA}/togglemusic ${GRAY}to turn music ${RED}OFF${GRAY}.`, - category: "Event", - subcategory: "Mythological Ritual" - }) - dianaWaypoint = false; - @SwitchProperty({ - name: "Diana Warp", - description: `Set keybind in controls to warp to the location closest to estimation.\nSet wanted warps with ${AQUA}/va warplist${GRAY}.`, - category: "Event", - subcategory: "Mythological Ritual" - }) - dianaWarp = false; - - @SelectorProperty({ - name: "Burrow Detection", - description: "Detects, alerts, and creates waypoints to nearby burrow particles.", - category: "Event", - subcategory: "Mythological Ritual", - options: ["OFF", "ON", "w/ Amogus Alert", "w/ Chat Alert", "w/ Both Alerts"] - }) - burrowDetect = 0; - - // --- Great Spook --- - @SwitchProperty({ - name: "Math Teacher Solver", - description: "Solves the math equation for those with feeble minds.", - category: "Event", - subcategory: "Great Spook" - }) - mathSolver = false; - - @SwitchProperty({ - name: "Primal Fear Alert", - description: "Plays a sound and shows title when a primal fear is able to be spawned.", - category: "Event", - subcategory: "Great Spook" - }) - fearAlert = false; - - // --- Inquisitor --- - @SwitchProperty({ - name: "Detect Inquisitor", - description: "Alerts player of nearby Inquisitors.", - category: "Event", - subcategory: "Inquisitor" - }) - detectInq = false; - - @SelectorProperty({ - name: "Announce Inquisitor Chat", - description: "Sends coordinates of user Inquisitor spawns to chat.", - category: "Event", - subcategory: "Inquisitor", - options: ["OFF", "All Chat", "Party Chat", "Self"] - }) - inqAlert = 0; - - @SelectorProperty({ - name: "Inquisitor Counter", - description: `Tracks average kills of Inquisitor spawns.\nMove GUI with ${AQUA}/moveInq ${GRAY}or reset tracker with ${AQUA}/resetInq${GRAY}.`, - category: "Event", - subcategory: "Inquisitor", - options: ["OFF", "Overall View", "Session View"] - }) - inqCounter = 0; - - // --- SkyBlock --- - @SelectorProperty({ - name: "Bingo Card Overlay", - description: `Displays bingo card goals on screen as an overlay.\nMove GUI with ${AQUA}/moveBingo${GRAY}.`, - category: "Event", - subcategory: "SkyBlock", - options: ["OFF", "All", "Personal", "Community"] - }) - bingoCard = 0; - - @SwitchProperty({ - name: "Calendar Time", - description: "Adds the local start and end time to the tooltip of calendar events.", - category: "Event", - subcategory: "SkyBlock" - }) - calendarTime = false; - - // ████████████████████████████████████████████████████ CRIMSON ISLES ████████████████████████████████████████████████████ - - // --- Fishing --- - @SelectorProperty({ - name: "Announce Mythic Creature Spawn", - description: "Sends coordinates of user mythic lava creature spawns to chat.", - category: "Crimson Isles", - subcategory: "Fishing", - options: ["OFF", "All Chat", "Party Chat", "Self"] - }) - mythicLavaAnnounce = 0; - - @SwitchProperty({ - name: "Detect Mythic Lava Creature", - description: "Alerts player of nearby Thunders or Lord Jawbuses.", - category: "Crimson Isles", - subcategory: "Fishing" - }) - mythicLavaDetect = false; - - @SwitchProperty({ - name: "Golden Fish Timer", - description: `Sets a 4 minute timer on rod cast to track Golden Fish reset.\nMove GUI with ${AQUA}/moveTimer${GRAY}.`, - category: "Crimson Isles", - subcategory: "Fishing" - }) - goldenFishAlert = false; - - @SwitchProperty({ - name: "Trophy Fish Overlay", - description: `Tracks number of trophy fishes caught and displays rates per hour. + category: "Event", + subcategory: "Mythological Ritual", + }) + dianaWaypoint = false; + @SwitchProperty({ + name: "Diana Warp", + description: `Set keybind in controls to warp to the location closest to estimation.\nSet wanted warps with ${AQUA}/va warplist${GRAY}.`, + category: "Event", + subcategory: "Mythological Ritual", + }) + dianaWarp = false; + + @SelectorProperty({ + name: "Burrow Detection", + description: + "Detects, alerts, and creates waypoints to nearby burrow particles.", + category: "Event", + subcategory: "Mythological Ritual", + options: [ + "OFF", + "ON", + "w/ Amogus Alert", + "w/ Chat Alert", + "w/ Both Alerts", + ], + }) + burrowDetect = 0; + + // --- Great Spook --- + @SwitchProperty({ + name: "Math Teacher Solver", + description: "Solves the math equation for those with feeble minds.", + category: "Event", + subcategory: "Great Spook", + }) + mathSolver = false; + + @SwitchProperty({ + name: "Primal Fear Alert", + description: + "Plays a sound and shows title when a primal fear is able to be spawned.", + category: "Event", + subcategory: "Great Spook", + }) + fearAlert = false; + + // --- Inquisitor --- + @SwitchProperty({ + name: "Detect Inquisitor", + description: "Alerts player of nearby Inquisitors.", + category: "Event", + subcategory: "Inquisitor", + }) + detectInq = false; + + @SelectorProperty({ + name: "Announce Inquisitor Chat", + description: "Sends coordinates of user Inquisitor spawns to chat.", + category: "Event", + subcategory: "Inquisitor", + options: ["OFF", "All Chat", "Party Chat", "Self"], + }) + inqAlert = 0; + + @SelectorProperty({ + name: "Inquisitor Counter", + description: `Tracks average kills of Inquisitor spawns.\nMove GUI with ${AQUA}/moveInq ${GRAY}or reset tracker with ${AQUA}/resetInq${GRAY}.`, + category: "Event", + subcategory: "Inquisitor", + options: ["OFF", "Overall View", "Session View"], + }) + inqCounter = 0; + + // --- SkyBlock --- + @SelectorProperty({ + name: "Bingo Card Overlay", + description: `Displays bingo card goals on screen as an overlay.\nMove GUI with ${AQUA}/moveBingo${GRAY}.`, + category: "Event", + subcategory: "SkyBlock", + options: ["OFF", "All", "Personal", "Community"], + }) + bingoCard = 0; + + @SwitchProperty({ + name: "Calendar Time", + description: + "Adds the local start and end time to the tooltip of calendar events.", + category: "Event", + subcategory: "SkyBlock", + }) + calendarTime = false; + + // ████████████████████████████████████████████████████ CRIMSON ISLES ████████████████████████████████████████████████████ + + // --- Fishing --- + @SelectorProperty({ + name: "Announce Mythic Creature Spawn", + description: + "Sends coordinates of user mythic lava creature spawns to chat.", + category: "Crimson Isles", + subcategory: "Fishing", + options: ["OFF", "All Chat", "Party Chat", "Self"], + }) + mythicLavaAnnounce = 0; + + @SwitchProperty({ + name: "Detect Mythic Lava Creature", + description: "Alerts player of nearby Thunders or Lord Jawbuses.", + category: "Crimson Isles", + subcategory: "Fishing", + }) + mythicLavaDetect = false; + + @SwitchProperty({ + name: "Golden Fish Timer", + description: `Sets a 4 minute timer on rod cast to track Golden Fish reset.\nMove GUI with ${AQUA}/moveTimer${GRAY}.`, + category: "Crimson Isles", + subcategory: "Fishing", + }) + goldenFishAlert = false; + + @SwitchProperty({ + name: "Trophy Fish Overlay", + description: `Tracks number of trophy fishes caught and displays rates per hour. Move GUI with ${AQUA}/moveTrophy ${GRAY}or reset tracker with ${AQUA}/resetTrophy${GRAY}.`, - category: "Crimson Isles", - subcategory: "Fishing" - }) - trophyCounter = false; - - // --- Vanquisher --- - @SelectorProperty({ - name: "Announce Vanquisher Chat", - description: `Sends coordinates of user Vanquisher spawns ${BLUE}(only works if Vanquisher Auto-Warp is empty)${GRAY}.`, - category: "Crimson Isles", - subcategory: "Vanquisher", - options: ["OFF", "All Chat", "Party Chat", "Self"] - }) - vanqAlert = 0; - - @TextProperty({ - name: "Vanquisher Auto-Warp", - description: `Parties and warps players in list to lobby on user Vanquisher spawn.\nEnable by entering party as [${AQUA}ign1, ign2, ...${GRAY}].`, - category: "Crimson Isles", - subcategory: "Vanquisher", - placeholder: "ign1, ign2, ..." - }) - vanqParty = ""; - - @SelectorProperty({ - name: "Vanquisher Counter", - description: `Tracks average kills of Vanquisher spawns ${BLUE}(must have Book of Stats)${GRAY}. + category: "Crimson Isles", + subcategory: "Fishing", + }) + trophyCounter = false; + + // --- Vanquisher --- + @SelectorProperty({ + name: "Announce Vanquisher Chat", + description: `Sends coordinates of user Vanquisher spawns ${BLUE}(only works if Vanquisher Auto-Warp is empty)${GRAY}.`, + category: "Crimson Isles", + subcategory: "Vanquisher", + options: ["OFF", "All Chat", "Party Chat", "Self"], + }) + vanqAlert = 0; + + @TextProperty({ + name: "Vanquisher Auto-Warp", + description: `Parties and warps players in list to lobby on user Vanquisher spawn.\nEnable by entering party as [${AQUA}ign1, ign2, ...${GRAY}].`, + category: "Crimson Isles", + subcategory: "Vanquisher", + placeholder: "ign1, ign2, ...", + }) + vanqParty = ""; + + @SelectorProperty({ + name: "Vanquisher Counter", + description: `Tracks average kills of Vanquisher spawns ${BLUE}(must have Book of Stats)${GRAY}. Move GUI with ${AQUA}/moveCounter ${GRAY}or reset tracker with ${AQUA}/resetCounter${GRAY}.`, - category: "Crimson Isles", - subcategory: "Vanquisher", - options: ["OFF", "Overall View", "Session View"] - }) - vanqCounter = 0; - - @SwitchProperty({ - name: "Vanquisher Detection", - description: `Alerts player of nearby Vanquishers.\nMove GUI with ${AQUA}/moveVanq${GRAY}.`, - category: "Crimson Isles", - subcategory: "Vanquisher" - }) - vanqDetect = false; - - // ████████████████████████████████████████████████████ DUNGEON ████████████████████████████████████████████████████ - - // --- Chests --- - @SwitchProperty({ - name: "Croesus Highlight", - description: "Highlights not/partially opened chests in Croesus menu.", - category: "Dungeon", - subcategory: "Chests" - }) - croesusHighlight = false; - - @SwitchProperty({ - name: "Dungeon Profit", - description: `Display overall profit of Dungeon chests.\nMove GUI with ${AQUA}/moveDP${GRAY}.`, - category: "Dungeon", - subcategory: "Chests" - }) - dungeonProfit = false; - - // --- Star Detect --- - @SelectorProperty({ - name: "Star Mob Highlight", - description: "Detects star mobs and highlights them in the chosen method.", - category: "Dungeon", - subcategory: "Star Detect", - options: ["OFF", "Highlight", "Box", "Outline"] - }) - starDetect = 0; - @ColorProperty({ - name: "Star Highlight Color", - description: "Choose the highlight color.", - category: "Dungeon", - subcategory: "Star Detect", - }) - starColor = Color.GREEN; - - - // ████████████████████████████████████████████████████ KUUDRA ████████████████████████████████████████████████████ - - // --- Kuudra --- - @SwitchProperty({ - name: "Auto '/kv' PF", - description: "Automatically runs \`/kv\` command whenever a player joins using party finder.", - category: "Kuudra", - subcategory: "Kuudra" - }) - autoKV = false; - - @SwitchProperty({ - name: "Crate Edit", - description: `Changes the location and size of crate pickup rendering.\nMove GUI with ${AQUA}/moveCrate${GRAY}.`, - category: "Kuudra", - subcategory: "Kuudra" - }) - crateEdit = false; - - @SwitchProperty({ - name: "Kuudra HP Display", - description: "Displays Kuudra's HP as a percent and renders it on the boss.", - category: "Kuudra", - subcategory: "Kuudra" - }) - kuudraHP = false; - - @SwitchProperty({ - name: "Kuudra Spawn", - description: `Displays a title for where Kuudra spawns in p4 ${BLUE}(requires animation skip, so don't fail @BananaTheBot)${GRAY}. ${DARK_RED}Technically uses ESP so UAYOR!`, - category: "Kuudra", - subcategory: "Kuudra" - }) - kuudraSpawn = false; - - @SwitchProperty({ - name: "Kuudra Splits", - description: `Displays and records Kuudra splits.\nMove GUI with ${AQUA}/moveSplits ${GRAY}or view splits with ${AQUA}/va splits${GRAY}.`, - category: "Kuudra", - subcategory: "Kuudra" - }) - kuudraSplits = false; - - @SwitchProperty({ - name: "Show Crate Waypoints", - description: "Creates waypoints to nearby supplies and fuels.", - category: "Kuudra", - subcategory: "Kuudra" - }) - kuudraCrates = false; - - @SwitchProperty({ - name: "Show Supply Piles", - description: "Creates waypoints to uncompleted supply piles.", - category: "Kuudra", - subcategory: "Kuudra" - }) - kuudraBuild = false; - - // --- Kuudra Alert --- - @SwitchProperty({ - name: "Kuudra Alerts", - description: `Alerts player of important events Kuudra. Set toggle with ${AQUA}/va toggles${GRAY}.`, - category: "Kuudra", - subcategory: "Kuudra Alert" - }) - kuudraAlerts = false; - - // --- Kuudra Profit --- - @SliderProperty({ - name: "Minimum God Roll", - description: `Set the minimum amount a combo attribute may be to be tracked as a god roll or as 0 to turn ${RED}OFF ${BLUE}(in millions)${GRAY}.`, - category: "Kuudra", - subcategory: "Kuudra Profit", - min: 0, - max: 250 - }) - minGR = 50; - @SwitchProperty({ - name: "Kuudra Profit", - description: `Display overall profit of Kuudra chests.\nMove GUI with ${AQUA}/moveKP${GRAY}.`, - category: "Kuudra", - subcategory: "Kuudra Profit" - }) - kuudraProfit = false; - @SelectorProperty({ - name: "Kuudra Profit Tracker", - description: `Display Kuudra hourly rate of gain. Move GUI with ${AQUA}/moveKPT or reset tracker with ${AQUA}/resetKPT${GRAY}.`, - category: "Kuudra", - subcategory: "Kuudra Profit", - options: ["OFF", "Overall View", "Session View"] - }) - kuudraProfitTracker = 0; - @SwitchProperty({ - name: "Tabasco Enjoyer", - description: `Toggle ${RED}OFF ${GRAY}if you are a cringer without max chili pepper collection.`, - category: "Kuudra", - subcategory: "Kuudra Profit" - }) - maxChili = true; - - - // ████████████████████████████████████████████████████ RIFT ████████████████████████████████████████████████████ - - // --- Rift --- - @SwitchProperty({ - name: "DDR Helper", - description: "Replaces Dance Room titles with custom ones.", - category: "Rift", - subcategory: "Rift", - }) - ddrHelper = false; - - @SliderProperty({ - name: "Enigma Soul Waypoints", - description: `Set distance at which Enigma Soul waypoints will render or as 0 to turn ${RED}OFF${GRAY}.`, - category: "Rift", - subcategory: "Rift", - min: 0, - max: 128 - }) - enigmaWaypoint = 0; - - @SwitchProperty({ - name: "Montezuma Soul Waypoints", - description: "Displays waypoints of nearby discord kittens.", - category: "Rift", - subcategory: "Rift", - }) - catWaypoint = false; - - // --- Vampire --- - @SelectorProperty({ - name: "Announce Mania Phase", - description: "Sends coordinates when user's Vampire boss goes into mania.", - category: "Rift", - subcategory: "Vampire", - options: ["OFF", "All Chat", "Party Chat", "Self"] - }) - announceMania = 0; - - @SwitchProperty({ - name: "Effigy Waypoint", - description: "Displays waypoints of inactive Blood Effigies.", - category: "Rift", - subcategory: "Vampire", - }) - effigyWaypoint = true; - - @SwitchProperty({ - name: "Enlarge Impel Message", - description: "Converts Impel messages to be disturbingly noticable.", - category: "Rift", - subcategory: "Vampire", - }) - vampireImpel = false; - - @SwitchProperty({ - name: "Vampire Hitbox", - description: "Renders a small box around the vampire and colors it in when boss is steakable.", - category: "Rift", - subcategory: "Vampire", - }) - vampireHitbox = false; - - @SwitchProperty({ - name: "Vampire Attack Display", - description: `Displays time of Mania, Twinclaws, and Ichor attacks.\nMove GUI with ${AQUA}/moveVamp${GRAY}.`, - category: "Rift", - subcategory: "Vampire", - }) - vampireAttack = false; + category: "Crimson Isles", + subcategory: "Vanquisher", + options: ["OFF", "Overall View", "Session View"], + }) + vanqCounter = 0; + + @SwitchProperty({ + name: "Vanquisher Detection", + description: `Alerts player of nearby Vanquishers.\nMove GUI with ${AQUA}/moveVanq${GRAY}.`, + category: "Crimson Isles", + subcategory: "Vanquisher", + }) + vanqDetect = false; + + // ████████████████████████████████████████████████████ DUNGEON ████████████████████████████████████████████████████ + + // --- Chests --- + @SwitchProperty({ + name: "Croesus Highlight", + description: "Highlights not/partially opened chests in Croesus menu.", + category: "Dungeon", + subcategory: "Chests", + }) + croesusHighlight = false; + + @SwitchProperty({ + name: "Dungeon Profit", + description: `Display overall profit of Dungeon chests.\nMove GUI with ${AQUA}/moveDP${GRAY}.`, + category: "Dungeon", + subcategory: "Chests", + }) + dungeonProfit = false; + + // --- Star Detect --- + @SelectorProperty({ + name: "Star Mob Highlight", + description: "Detects star mobs and highlights them in the chosen method.", + category: "Dungeon", + subcategory: "Star Detect", + options: ["OFF", "Highlight", "Box", "Outline"], + }) + starDetect = 0; + @ColorProperty({ + name: "Star Highlight Color", + description: "Choose the highlight color.", + category: "Dungeon", + subcategory: "Star Detect", + }) + starColor = Color.GREEN; + + // ████████████████████████████████████████████████████ KUUDRA ████████████████████████████████████████████████████ + + // --- Kuudra --- + @SwitchProperty({ + name: "Auto '/kv' PF", + description: + "Automatically runs `/kv` command whenever a player joins using party finder.", + category: "Kuudra", + subcategory: "Kuudra", + }) + autoKV = false; + + @SwitchProperty({ + name: "Crate Edit", + description: `Changes the location and size of crate pickup rendering.\nMove GUI with ${AQUA}/moveCrate${GRAY}.`, + category: "Kuudra", + subcategory: "Kuudra", + }) + crateEdit = false; + + @SwitchProperty({ + name: "Kuudra HP Display", + description: + "Displays Kuudra's HP as a percent and renders it on the boss.", + category: "Kuudra", + subcategory: "Kuudra", + }) + kuudraHP = false; + + @SwitchProperty({ + name: "Kuudra Spawn", + description: `Displays a title for where Kuudra spawns in p4 ${BLUE}(requires animation skip, so don't fail @BananaTheBot)${GRAY}. ${DARK_RED}Technically uses ESP so UAYOR!`, + category: "Kuudra", + subcategory: "Kuudra", + }) + kuudraSpawn = false; + + @SwitchProperty({ + name: "Kuudra Splits", + description: `Displays and records Kuudra splits.\nMove GUI with ${AQUA}/moveSplits ${GRAY}or view splits with ${AQUA}/va splits${GRAY}.`, + category: "Kuudra", + subcategory: "Kuudra", + }) + kuudraSplits = false; + + @SwitchProperty({ + name: "Show Crate Waypoints", + description: "Creates waypoints to nearby supplies and fuels.", + category: "Kuudra", + subcategory: "Kuudra", + }) + kuudraCrates = false; + + @SwitchProperty({ + name: "Show Supply Piles", + description: "Creates waypoints to uncompleted supply piles.", + category: "Kuudra", + subcategory: "Kuudra", + }) + kuudraBuild = false; + + // --- Kuudra Alert --- + @SwitchProperty({ + name: "Kuudra Alerts", + description: `Alerts player of important events Kuudra. Set toggle with ${AQUA}/va toggles${GRAY}.`, + category: "Kuudra", + subcategory: "Kuudra Alert", + }) + kuudraAlerts = false; + + // --- Kuudra Profit --- + @SliderProperty({ + name: "Minimum God Roll", + description: `Set the minimum amount a combo attribute may be to be tracked as a god roll or as 0 to turn ${RED}OFF ${BLUE}(in millions)${GRAY}.`, + category: "Kuudra", + subcategory: "Kuudra Profit", + min: 0, + max: 250, + }) + minGR = 50; + @SwitchProperty({ + name: "Kuudra Profit", + description: `Display overall profit of Kuudra chests.\nMove GUI with ${AQUA}/moveKP${GRAY}.`, + category: "Kuudra", + subcategory: "Kuudra Profit", + }) + kuudraProfit = false; + @SelectorProperty({ + name: "Kuudra Profit Tracker", + description: `Display Kuudra hourly rate of gain. Move GUI with ${AQUA}/moveKPT or reset tracker with ${AQUA}/resetKPT${GRAY}.`, + category: "Kuudra", + subcategory: "Kuudra Profit", + options: ["OFF", "Overall View", "Session View"], + }) + kuudraProfitTracker = 0; + @SwitchProperty({ + name: "Tabasco Enjoyer", + description: `Toggle ${RED}OFF ${GRAY}if you are a cringer without max chili pepper collection.`, + category: "Kuudra", + subcategory: "Kuudra Profit", + }) + maxChili = true; + + // ████████████████████████████████████████████████████ RIFT ████████████████████████████████████████████████████ + + // --- Rift --- + @SwitchProperty({ + name: "DDR Helper", + description: "Replaces Dance Room titles with custom ones.", + category: "Rift", + subcategory: "Rift", + }) + ddrHelper = false; + + @SliderProperty({ + name: "Enigma Soul Waypoints", + description: `Set distance at which Enigma Soul waypoints will render or as 0 to turn ${RED}OFF${GRAY}.`, + category: "Rift", + subcategory: "Rift", + min: 0, + max: 128, + }) + enigmaWaypoint = 0; + + @SwitchProperty({ + name: "Montezuma Soul Waypoints", + description: "Displays waypoints of nearby discord kittens.", + category: "Rift", + subcategory: "Rift", + }) + catWaypoint = false; + + // --- Vampire --- + @SelectorProperty({ + name: "Announce Mania Phase", + description: "Sends coordinates when user's Vampire boss goes into mania.", + category: "Rift", + subcategory: "Vampire", + options: ["OFF", "All Chat", "Party Chat", "Self"], + }) + announceMania = 0; + + @SwitchProperty({ + name: "Effigy Waypoint", + description: "Displays waypoints of inactive Blood Effigies.", + category: "Rift", + subcategory: "Vampire", + }) + effigyWaypoint = true; + + @SwitchProperty({ + name: "Enlarge Impel Message", + description: "Converts Impel messages to be disturbingly noticable.", + category: "Rift", + subcategory: "Vampire", + }) + vampireImpel = false; + + @SwitchProperty({ + name: "Vampire Hitbox", + description: + "Renders a small box around the vampire and colors it in when boss is steakable.", + category: "Rift", + subcategory: "Vampire", + }) + vampireHitbox = false; + + @SwitchProperty({ + name: "Vampire Attack Display", + description: `Displays time of Mania, Twinclaws, and Ichor attacks.\nMove GUI with ${AQUA}/moveVamp${GRAY}.`, + category: "Rift", + subcategory: "Vampire", + }) + vampireAttack = false; } -export default new Settings; +export default new Settings(); diff --git a/utils/Socket.js b/utils/Socket.js index 46cb3d2..684ea27 100644 --- a/utils/Socket.js +++ b/utils/Socket.js @@ -1,225 +1,224 @@ -import Settings from "./Settings"; import { DARK_GRAY, GRAY, LOGO, RED } from "./Constants"; +import Settings from "./Settings"; import { NonPooledThread, delay } from "./ThreadTils"; - const Socket = Java.type("java.net.Socket"); const InputStreamReader = Java.type("java.io.InputStreamReader"); const BufferedReader = Java.type("java.io.BufferedReader"); const PrintWriter = Java.type("java.io.PrintWriter"); class WebSocket { - #socket = null; - #output; - #input; - #inputStream = []; - - // Condition variables - #connected = false; - #running = true; - - /** - * Creates a new WebSocket connection. - * - * Sugar, spice, and everything nice. - * These were the ingredients chosen to create the perfect little girl - * But Professor Utonium accidentally added an extra ingredient to the concoction-- - * https://github.com/Soopyboo32/soopyApis/tree/master - */ - constructor() { - if (!Settings.socketToggle) return; - - console.log("[VolcAddons] Connecting to socket server..."); - new NonPooledThread(() => { - this.connect(); - }).execute(); - this.expected = 0; - - // Send data to the server - new NonPooledThread(() => { - while (this.#running) { - if (this.#connected && this.#socket !== null) { - try { - if (this.#inputStream.length > 0) { - this.#inputStream.forEach(line => { - this.#input.println(line); - }); - this.#inputStream = []; - } else { - Thread.sleep(1_000); - } - } catch (e) { - this.disconnect(); - Thread.sleep(10_000); - } - } else { - this.disconnect(); - Thread.sleep(10_000); - } + #socket = null; + #output; + #input; + #inputStream = []; + + // Condition variables + #connected = false; + #running = true; + + /** + * Creates a new WebSocket connection. + * + * Sugar, spice, and everything nice. + * These were the ingredients chosen to create the perfect little girl + * But Professor Utonium accidentally added an extra ingredient to the concoction-- + * https://github.com/Soopyboo32/soopyApis/tree/master + */ + constructor() { + if (!Settings.socketToggle) return; + + console.log("[VolcAddons] Connecting to socket server..."); + new NonPooledThread(() => { + this.connect(); + }).execute(); + this.expected = 0; + + // Send data to the server + new NonPooledThread(() => { + while (this.#running) { + if (this.#connected && this.#socket !== null) { + try { + if (this.#inputStream.length > 0) { + this.#inputStream.forEach((line) => { + this.#input.println(line); + }); + this.#inputStream = []; + } else { + Thread.sleep(1_000); } - }).execute(); - - // Set registers - register("gameUnload", () => { - this.#running = false; + } catch (e) { this.disconnect(); - }).setPriority(Priority.HIGHEST); + Thread.sleep(10_000); + } + } else { + this.disconnect(); + Thread.sleep(10_000); + } + } + }).execute(); + + // Set registers + register("gameUnload", () => { + this.#running = false; + this.disconnect(); + }).setPriority(Priority.HIGHEST); + } + + /** + * Returns whether the socket is connected. + * + * @returns {Boolean} - Whether the socket is connected. + */ + getConnected() { + return this.#connected; + } + + /** + * Connects to the socket server. + * + * @param {Number} attempts - The number of attempts to connect to the server. + */ + connect(attempts = 0) { + if (!this.#running) return; + if (this.#connected || this.#socket !== null) { + console.error("[VolcAddons] Already connected to socket server."); + return; } - /** - * Returns whether the socket is connected. - * - * @returns {Boolean} - Whether the socket is connected. - */ - getConnected() { - return this.#connected; + if (attempts > 9) { + console.error("[VolcAddons] Failed to connect to socket server after 10 attempts."); + return; } - - /** - * Connects to the socket server. - * - * @param {Number} attempts - The number of attempts to connect to the server. - */ - connect(attempts = 0) { - if (!this.#running) return; - if (this.#connected || this.#socket !== null) { - console.error("[VolcAddons] Already connected to socket server."); - return; - } - - if (attempts > 9) { - console.error("[VolcAddons] Failed to connect to socket server after 10 attempts."); - return; - } - // Attempt to connect to the socket server - try { - this.#socket = new Socket("volca.dev", 3389); - } catch (e) { - const time = 10_000 * (1.5 ** attempts); - console.error(`[VolcAddons] Error connecting to socket server: ${e}. Retrying in ${(time / 1_000).toFixed(2)} seconds...`); - delay(() => { - this.connect(attempts + 1); - }, time); - return; - } - this.#connected = true; - this.expected = 0; - console.log("[VolcAddons] Connected to socket server."); - - // Initialize input and output streams - this.#output = this.#socket.getOutputStream(); - this.#input = new PrintWriter(this.#output, true); - - // Start the listener thread - new NonPooledThread(() => { - let input = this.#socket.getInputStream(); - let reader = new BufferedReader(new InputStreamReader(input)); - - while (this.#connected && this.#socket !== null && this.#running) { - try { - let data = reader.readLine(); - if (data !== null) { - this.receive(data); - } else { - this.disconnect(); - break; - } - } catch (e) { - console.error("[VolcAddons] Error reading data from socket server: " + e); - this.disconnect(); - break; - } - } - this.disconnect(); - }).execute(); + // Attempt to connect to the socket server + try { + this.#socket = new Socket("volca.dev", 3389); + } catch (e) { + const time = 10_000 * Math.pow(1.5, attempts); + console.error( + `[VolcAddons] Error connecting to socket server: ${e}. Retrying in ${(time / 1_000).toFixed(2)} seconds...` + ); + delay(() => { + this.connect(attempts + 1); + }, time); + return; } + this.#connected = true; + this.expected = 0; + console.log("[VolcAddons] Connected to socket server."); - /** - * Disconnects from the socket server. - */ - disconnect() { - new NonPooledThread(() => { - if (!this.#connected || this.#socket === null) return; - this.#connected = false; - this.expected = 0; - - try { - this.#input.println(`{ "command": "disconnect", "player": "${Player.getName()}" }`); - this.#input.close(); - this.#output.close(); - this.#socket.close(); - this.#socket = null; - console.log("[VolcAddons] Disconnected from socket server."); - } catch (e) { - console.error("[VolcAddons] Error disconnecting from socket server: " + e); - } + // Initialize input and output streams + this.#output = this.#socket.getOutputStream(); + this.#input = new PrintWriter(this.#output, true); - // Attempt reconnect - Thread.sleep(10_000); - if (this.#running) { - console.log("[VolcAddons] Attempting to reconnect to socket server..."); - this.connect(); - } - }).execute(); - } + // Start the listener thread + new NonPooledThread(() => { + let input = this.#socket.getInputStream(); + let reader = new BufferedReader(new InputStreamReader(input)); - /** - * Sends data to the server. - * - * @param {Object} data - The data to send to the server. - */ - send(data) { - data.player = Player.getName(); - if (data?.request === "get") { - if (this.#socket === null) { - ChatLib.chat(`${LOGO + RED}Socket server is not connected.`); - } - this.expected++; + while (this.#connected && this.#socket !== null && this.#running) { + try { + let data = reader.readLine(); + if (data !== null) { + this.receive(data); + } else { + this.disconnect(); + break; + } + } catch (e) { + console.error("[VolcAddons] Error reading data from socket server: " + e); + this.disconnect(); + break; } - - const json = JSON.stringify(data); - this.#inputStream.push(json); + } + this.disconnect(); + }).execute(); + } + + /** + * Disconnects from the socket server. + */ + disconnect() { + new NonPooledThread(() => { + if (!this.#connected || this.#socket === null) return; + this.#connected = false; + this.expected = 0; + + try { + this.#input.println(`{ "command": "disconnect", "player": "${Player.getName()}" }`); + this.#input.close(); + this.#output.close(); + this.#socket.close(); + this.#socket = null; + console.log("[VolcAddons] Disconnected from socket server."); + } catch (e) { + console.error("[VolcAddons] Error disconnecting from socket server: " + e); + } + + // Attempt reconnect + Thread.sleep(10_000); + if (this.#running) { + console.log("[VolcAddons] Attempting to reconnect to socket server..."); + this.connect(); + } + }).execute(); + } + + /** + * Sends data to the server. + * + * @param {Object} data - The data to send to the server. + */ + send(data) { + data.player = Player.getName(); + if (data?.request === "get") { + if (this.#socket === null) { + ChatLib.chat(`${LOGO + RED}Socket server is not connected.`); + } + this.expected++; } - /** - * Receives data from the server. - * - * @param {String} json - The data received from the server. - */ - receive(json) { - if (!json.startsWith("{") || !json.endsWith("}") || this.expected === 0) return; - - this.expected--; - callback(JSON.parse(json)); - } + const json = JSON.stringify(data); + this.#inputStream.push(json); + } + + /** + * Receives data from the server. + * + * @param {String} json - The data received from the server. + */ + receive(json) { + if (!json.startsWith("{") || !json.endsWith("}") || this.expected === 0) return; + + this.expected--; + callback(JSON.parse(json)); + } } export default new WebSocket(); - /** * Run callback and prevent circular dependency. */ import { processAlloy, processEvent } from "../features/mining/EventTracker"; -import Settings from "./Settings"; /** * Processes the event received from the server. - * + * * @param {Object} data - The data received from the server. */ function callback(data) { - const command = data.command; - - switch (command) { - case "alloy": - processAlloy(data); - break; - case "ch": - case "dm": - processEvent(data); - break; - default: - ChatLib.chat(`${LOGO + DARK_GRAY}Received unknown command: ${GRAY + command}`); - } + const command = data.command; + + switch (command) { + case "alloy": + processAlloy(data); + break; + case "ch": + case "dm": + processEvent(data); + break; + default: + ChatLib.chat(`${LOGO + DARK_GRAY}Received unknown command: ${GRAY + command}`); + } } diff --git a/utils/Stat.js b/utils/Stat.js index a1c4e7e..c5f3c0a 100644 --- a/utils/Stat.js +++ b/utils/Stat.js @@ -1,39 +1,38 @@ import { data } from "./Data"; - export class Stat { - constructor() { - this.reset(); - } - - /** - * Resets all stat tracking variables. - */ - reset() { - this.start = 0.00; // Starting amount - this.now = 0.00; // Current amount - this.time = 1; // Time passed - this.since = 600; // Time since last amount earn - this.level = 0; // Skill level - } - - /** - * Calculates current gain of current stat. - * - * @returns {Number} Gained stat number from start. - */ - getGain() { - return this.now - this.start; - } - - /** - * Calculates rate of gain an hour. - * - * @returns {Number} stat / hour. - */ - getRate() { - return this.getGain() / this.time * 3_600; - } + constructor() { + this.reset(); + } + + /** + * Resets all stat tracking variables. + */ + reset() { + this.start = 0.0; // Starting amount + this.now = 0.0; // Current amount + this.time = 1; // Time passed + this.since = 600; // Time since last amount earn + this.level = 0; // Skill level + } + + /** + * Calculates current gain of current stat. + * + * @returns {Number} Gained stat number from start. + */ + getGain() { + return this.now - this.start; + } + + /** + * Calculates rate of gain an hour. + * + * @returns {Number} stat / hour. + */ + getRate() { + return (this.getGain() / this.time) * 3_600; + } } /** @@ -43,16 +42,18 @@ export class Stat { */ let paused = false; export function getPaused() { - return paused; + return paused; } // Key binding for pausing or unpausing trackers const pauseKey = new KeyBind("Pause Trackers", data.pauseKey, "./VolcAddons.xdd"); pauseKey.registerKeyPress(() => { - paused = !paused; - const message = paused ? `${RED}Paused` : `${GREEN}Resumed`; - ChatLib.chat(`${LOGO + GOLD}Tracker ${message}!`); + paused = !paused; + const message = paused ? `${RED}Paused` : `${GREEN}Resumed`; + ChatLib.chat(`${LOGO + GOLD}Tracker ${message}!`); }); -register("gameUnload", () => { data.pauseKey = pauseKey.getKeyCode() }).setPriority(Priority.HIGHEST); +register("gameUnload", () => { + data.pauseKey = pauseKey.getKeyCode(); +}).setPriority(Priority.HIGHEST); diff --git a/utils/ThreadTils.js b/utils/ThreadTils.js index 5b3ba7c..355b483 100644 --- a/utils/ThreadTils.js +++ b/utils/ThreadTils.js @@ -3,43 +3,43 @@ const Threading = Java.type("gg.essential.api.utils.Multithreading"); const threads = []; export class NonPooledThread { - #callback; - #executor; + #callback; + #executor; - /** - * Creates a new non-pooled thread. - * Stolen from https://github.com/Soopyboo32/soopyApis/tree/master - * - * @param {Function} callback - The function to be executed. - */ - constructor(callback) { - threads.push(this); - this.#callback = callback; - this.#executor = Executors.newSingleThreadExecutor(); - } + /** + * Creates a new non-pooled thread. + * Stolen from https://github.com/Soopyboo32/soopyApis/tree/master + * + * @param {Function} callback - The function to be executed. + */ + constructor(callback) { + threads.push(this); + this.#callback = callback; + this.#executor = Executors.newSingleThreadExecutor(); + } - /** - * Executes the thread. - */ - execute() { - this.#executor.execute(this.#callback); - } + /** + * Executes the thread. + */ + execute() { + this.#executor.execute(this.#callback); + } - /** - * Shuts down the thread. - */ - shutdown() { - this.#executor.shutdown(); - } + /** + * Shuts down the thread. + */ + shutdown() { + this.#executor.shutdown(); + } } /** * Backup kill all threads. */ register("gameUnload", () => { - threads.forEach(thread => { - thread.shutdown(); - }); + threads.forEach((thread) => { + thread.shutdown(); + }); }).setPriority(Priority.LOWEST); /** @@ -49,12 +49,20 @@ register("gameUnload", () => { * @param {Number} time - The delay time in milliseconds (optional). If not provided, the function will run asynchronously. */ export function delay(func, time) { - if (time) { - // Schedule the function to be executed after the specified delay. - // The time value is converted to milliseconds using java.util.concurrent.TimeUnit.MILLISECONDS. - Threading.schedule(() => { func() }, time, java.util.concurrent.TimeUnit.MILLISECONDS); - } else { - // Run the function asynchronously without any delay. - Threading.runAsync(() => { func() }); - } + if (time) { + // Schedule the function to be executed after the specified delay. + // The time value is converted to milliseconds using java.util.concurrent.TimeUnit.MILLISECONDS. + Threading.schedule( + () => { + func(); + }, + time, + java.util.concurrent.TimeUnit.MILLISECONDS + ); + } else { + // Run the function asynchronously without any delay. + Threading.runAsync(() => { + func(); + }); + } } diff --git a/utils/Title.js b/utils/Title.js index 295504f..edee15d 100644 --- a/utils/Title.js +++ b/utils/Title.js @@ -4,7 +4,7 @@ let prio = 0; /** * Set the title of the player. - * + * * @param {String} title - Title of the message. * @param {String} subtitle - Subtitle of the message. * @param {Number} fadeIn - Fade in time in ticks. @@ -12,51 +12,49 @@ let prio = 0; * @param {Number} fadeOut - Fade out time in ticks. * @param {Number} priority - Higher number means higher priority. */ -export function setTitle(title, subtitle, fadeIn, time, fadeOut, priority=5) { - const ticks = fadeIn + time + fadeOut; - titles[title] = { - "subtitle": subtitle, - "fadeIn": fadeIn, - "time": time, - "fadeOut": fadeOut, - "ticks": ticks, - "priority": priority - } +export function setTitle(title, subtitle, fadeIn, time, fadeOut, priority = 5) { + const ticks = fadeIn + time + fadeOut; + titles[title] = { + subtitle: subtitle, + fadeIn: fadeIn, + time: time, + fadeOut: fadeOut, + ticks: ticks, + priority: priority, + }; - if (priority >= prio) { - Client.showTitle(title, subtitle, fadeIn, time, fadeOut); - current = title; - prio = priority; - } + if (priority >= prio) { + Client.showTitle(title, subtitle, fadeIn, time, fadeOut); + current = title; + prio = priority; + } } register("tick", () => { - Object.keys(titles).forEach(title => { - if (titles[title].ticks-- <= 0) - delete titles[title]; - }); + Object.keys(titles).forEach((title) => { + if (titles[title].ticks-- <= 0) delete titles[title]; + }); - // Find highest priority title - if (Object.keys(titles).length === 0) { - current = ""; - prio = 0; - return; - } else if (current === "" || !titles.hasOwnProperty(current)) { - current = Object.keys(titles).reduce((a, b) => { - if (titles[a] && titles[b]) { - return titles[a].priority > titles[b].priority ? a : b; - } - return a || b; - }, null); - prio = titles[current].priority; - const title = titles[current]; + // Find highest priority title + if (Object.keys(titles).length === 0) { + current = ""; + prio = 0; + return; + } else if (current === "" || !titles.hasOwnProperty(current)) { + current = Object.keys(titles).reduce((a, b) => { + if (titles[a] && titles[b]) { + return titles[a].priority > titles[b].priority ? a : b; + } + return a || b; + }, null); + prio = titles[current].priority; + const title = titles[current]; - // Draw title - Client.showTitle(current, title.subtitle, title.fadeIn, title.time, title.fadeOut); - } + // Draw title + Client.showTitle(current, title.subtitle, title.fadeIn, title.time, title.fadeOut); + } }); register("renderTitle", (title, _, event) => { - if (current !== "" && title !== current) - cancel(event); + if (current !== "" && title !== current) cancel(event); }).setPriority(Priority.LOWEST); diff --git a/utils/Toggles.js b/utils/Toggles.js index 8762141..e13a832 100644 --- a/utils/Toggles.js +++ b/utils/Toggles.js @@ -1,458 +1,533 @@ import { BLUE, DARK_AQUA, GRAY, HEADER, RED } from "./Constants"; import { - @Vigilant, - @TextProperty, - @CheckboxProperty, - @SliderProperty, - @SelectorProperty, - @PercentSliderProperty + @Vigilant, + @TextProperty, + @CheckboxProperty, + @SliderProperty, + @SelectorProperty, + @PercentSliderProperty } from "../../Vigilance/index"; @Vigilant("VolcAddons/Data", "VolcAddons' Toggles", { - // Function to compare categories for sorting settings - getCategoryComparator: () => (a, b) => { - const categories = ["Server Status", "Skyblock Stats", "Leader Commands", "Party Commands", "Kuudra Alerts", "Webhook Chats"]; - return categories.indexOf(a.name) - categories.indexOf(b.name); - } + // Function to compare categories for sorting settings + getCategoryComparator: () => (a, b) => { + const categories = [ + "Server Status", + "Skyblock Stats", + "Leader Commands", + "Party Commands", + "Kuudra Alerts", + "Webhook Chats", + ]; + return categories.indexOf(a.name) - categories.indexOf(b.name); + }, }) class Settings { - constructor() { - this.initialize(this); - - this.setCategoryDescription("Skyblock Stats", `${HEADER}\n\nSkyblock Stats Control Panel...`); - this.setCategoryDescription("Server Status", `${HEADER}\n\nServer Status Control Panel...`); - this.setCategoryDescription("Leader Commands", `${HEADER}\n\nLeader Commands Control Panel...`); - this.setCategoryDescription("Party Commands", `${HEADER}\n\nParty Commands Control Panel...`); - this.setCategoryDescription("Kuudra Alerts", `${HEADER}\n\nKuudra Alerts Control Panel...`); - this.setCategoryDescription("Webhook Chats", `${HEADER}\n\nWebhook Chats Control Panel...`); - } + constructor() { + this.initialize(this); - // --- SERVER STATUS --- - @CheckboxProperty({ - name: "Ping Display", - category: "Server Status", - subcategory: "Server Status", - description: "According to all known laws of aviation, there is no way a bee should be able to fly." - }) - pingDisplay = true; - @CheckboxProperty({ - name: "FPS Display", - category: "Server Status", - subcategory: "Server Status", - description: "Its wings are too small to get its fat little body off the ground." - }) - fpsDisplay = true; - @CheckboxProperty({ - name: "TPS Display", - category: "Server Status", - subcategory: "Server Status", - description: "The bee, of course, flies anyway because bees don't care what humans think is impossible." - }) - tpsDisplay = true; - @CheckboxProperty({ - name: "CPS Display", - category: "Server Status", - subcategory: "Server Status", - description: "Yellow, black. Yellow, black. Yellow, black. Yellow, black." - }) - cpsDisplay = true; - @CheckboxProperty({ - name: "XYZ Display", - category: "Server Status", - subcategory: "Server Status", - description: "Ooh, black and yellow! Let's shake it up a little." - }) - xyzDisplay = true; - @SelectorProperty({ - name: "Y/P Display", - category: "Server Status", - subcategory: "Server Status", - description: "JANET BENSON: Barry! Breakfast is ready!", - options: ["OFF", "GUI", "Crosshair", "All"] - }) - angleDisplay = 0; - @CheckboxProperty({ - name: "Direction Display", - category: "Server Status", - subcategory: "Server Status", - description: "BARRY: Coming!" - }) - dirDisplay = true; - @CheckboxProperty({ - name: "Day Display", - category: "Server Status", - subcategory: "Server Status", - description: "Hang on a second." - }) - dayDisplay = true; + this.setCategoryDescription( + "Skyblock Stats", + `${HEADER}\n\nSkyblock Stats Control Panel...` + ); + this.setCategoryDescription( + "Server Status", + `${HEADER}\n\nServer Status Control Panel...` + ); + this.setCategoryDescription( + "Leader Commands", + `${HEADER}\n\nLeader Commands Control Panel...` + ); + this.setCategoryDescription( + "Party Commands", + `${HEADER}\n\nParty Commands Control Panel...` + ); + this.setCategoryDescription( + "Kuudra Alerts", + `${HEADER}\n\nKuudra Alerts Control Panel...` + ); + this.setCategoryDescription( + "Webhook Chats", + `${HEADER}\n\nWebhook Chats Control Panel...` + ); + } - // --- SKYBLOCK STATS --- - @CheckboxProperty({ - name: "Equipped Pet Name", - category: "Skyblock Stats", - subcategory: "Skyblock Stats", - description: "Nice guys finish last that's why I'll treat you like trash" - }) - petDisplay = true; + // --- SERVER STATUS --- + @CheckboxProperty({ + name: "Ping Display", + category: "Server Status", + subcategory: "Server Status", + description: + "According to all known laws of aviation, there is no way a bee should be able to fly.", + }) + pingDisplay = true; + @CheckboxProperty({ + name: "FPS Display", + category: "Server Status", + subcategory: "Server Status", + description: + "Its wings are too small to get its fat little body off the ground.", + }) + fpsDisplay = true; + @CheckboxProperty({ + name: "TPS Display", + category: "Server Status", + subcategory: "Server Status", + description: + "The bee, of course, flies anyway because bees don't care what humans think is impossible.", + }) + tpsDisplay = true; + @CheckboxProperty({ + name: "CPS Display", + category: "Server Status", + subcategory: "Server Status", + description: "Yellow, black. Yellow, black. Yellow, black. Yellow, black.", + }) + cpsDisplay = true; + @CheckboxProperty({ + name: "XYZ Display", + category: "Server Status", + subcategory: "Server Status", + description: "Ooh, black and yellow! Let's shake it up a little.", + }) + xyzDisplay = true; + @SelectorProperty({ + name: "Y/P Display", + category: "Server Status", + subcategory: "Server Status", + description: "JANET BENSON: Barry! Breakfast is ready!", + options: ["OFF", "GUI", "Crosshair", "All"], + }) + angleDisplay = 0; + @CheckboxProperty({ + name: "Direction Display", + category: "Server Status", + subcategory: "Server Status", + description: "BARRY: Coming!", + }) + dirDisplay = true; + @CheckboxProperty({ + name: "Day Display", + category: "Server Status", + subcategory: "Server Status", + description: "Hang on a second.", + }) + dayDisplay = true; - @CheckboxProperty({ - name: "Legion Display", - category: "Skyblock Stats", - subcategory: "Skyblock Stats", - description: "It's not what I really want to do" - }) - legionDisplay = true; - - @CheckboxProperty({ - name: `Playtime Tracker`, - category: "Skyblock Stats", - subcategory: "Skyblock Stats", - description: "But you only date bad guys, so I'll give it my best try to treat you the way you want me to" - }) - trackPlaytime = true; + // --- SKYBLOCK STATS --- + @CheckboxProperty({ + name: "Equipped Pet Name", + category: "Skyblock Stats", + subcategory: "Skyblock Stats", + description: "Nice guys finish last that's why I'll treat you like trash", + }) + petDisplay = true; - @CheckboxProperty({ - name: `Soulflow Display ${BLUE}(Uses Inventory)`, - category: "Skyblock Stats", - subcategory: "Skyblock Stats", - description: "I'll never open a door or pull out a chair, you can tell me how your day was but I don't really care" - }) - soulflowDisplay = true; + @CheckboxProperty({ + name: "Legion Display", + category: "Skyblock Stats", + subcategory: "Skyblock Stats", + description: "It's not what I really want to do", + }) + legionDisplay = true; - // --- LEADER COMMANDS --- - @CheckboxProperty({ - name: `Allinvite Command ${DARK_AQUA}?`, - category: "Leader Commands", - subcategory: "Leader Commands", - description: "Once upon a time there was a lovely princess." - }) - allinvCommand = true; - @CheckboxProperty({ - name: `Demote Command ${DARK_AQUA}?demote`, - category: "Leader Commands", - subcategory: "Leader Commands", - description: "But she had an enchantment upon her of a fearful sort which could only be broken by love's first kiss." - }) - demoteCommand = true; - @CheckboxProperty({ - name: `Instance Command ${DARK_AQUA}?[1-7]`, - category: "Leader Commands", - subcategory: "Leader Commands", - description: "She was locked away in a castle guarded by a terrible fire-breathing dragon." - }) - instanceCommand = true; - @CheckboxProperty({ - name: `Invite Command ${DARK_AQUA}?invite`, - category: "Leader Commands", - subcategory: "Leader Commands", - description: "Many brave knigts had attempted to free her from this dreadful prison, but none prevailed." - }) - inviteCommand = true; - @CheckboxProperty({ - name: `Promote Command ${DARK_AQUA}?promote`, - category: "Leader Commands", - subcategory: "Leader Commands", - description: "She waited in the dragon's keep in the highest room of the tallest tower for her true love and true love's first kiss." - }) - promoteCommand = true; - @CheckboxProperty({ - name: `Stream Command ${DARK_AQUA}? [num]`, - category: "Leader Commands", - subcategory: "Leader Commands", - description: "Like that's ever gonna happen." - }) - streamCommand = true; - @CheckboxProperty({ - name: `Transfer Command ${DARK_AQUA}?transfer`, - category: "Leader Commands", - subcategory: "Leader Commands", - description: "{Paper Rusting, Toilet Flushes}" - }) - transferCommand = true; - @CheckboxProperty({ - name: `Warp Command ${DARK_AQUA}?warp`, - category: "Leader Commands", - subcategory: "Leader Commands", - description: "What a load of - " - }) - warpCommand = true; + @CheckboxProperty({ + name: `Playtime Tracker`, + category: "Skyblock Stats", + subcategory: "Skyblock Stats", + description: + "But you only date bad guys, so I'll give it my best try to treat you the way you want me to", + }) + trackPlaytime = true; - // --- PARTY COMMANDS --- - @CheckboxProperty({ - name: "All Chat", - category: "Party Commands", - subcategory: "Chat Options", - description: "Somebody once told me the world is gonna roll me" - }) - allCommands = true; - @CheckboxProperty({ - name: "Party Chat", - category: "Party Commands", - subcategory: "Chat Options", - description: "I ain't the sharpest tool in the shed" - }) - partyCommands = true; - @CheckboxProperty({ - name: "Guild Chat", - category: "Party Commands", - subcategory: "Chat Options", - description: "She was lookin' kind of dumb with her finger and her thumb" - }) - guildCommands = true; - @CheckboxProperty({ - name: "DM Chat", - category: "Party Commands", - subcategory: "Chat Options", - description: "In the shape of an \"L\" on her forehead" - }) - dmCommands = true; + @CheckboxProperty({ + name: `Soulflow Display ${BLUE}(Uses Inventory)`, + category: "Skyblock Stats", + subcategory: "Skyblock Stats", + description: + "I'll never open a door or pull out a chair, you can tell me how your day was but I don't really care", + }) + soulflowDisplay = true; - @CheckboxProperty({ - name: `8ball Command ${DARK_AQUA}?8ball`, - category: "Party Commands", - subcategory: "Party Commands", - description: "The years start comin' and they don't stop comin'" - }) - ballCommand = true; - @CheckboxProperty({ - name: `Coinflip Command ${DARK_AQUA}?`, - category: "Party Commands", - subcategory: "Party Commands", - description: "Fed to the rules and I hit the ground runnin'" - }) - coinCommand = true; - @CheckboxProperty({ - name: `Coords Command ${DARK_AQUA}?`, - category: "Party Commands", - subcategory: "Party Commands", - description: "Didn't make sense not to live for fun" - }) - coordsCommand = true; - @CheckboxProperty({ - name: `Dice Command ${DARK_AQUA}?`, - category: "Party Commands", - subcategory: "Party Commands", - description: "Your brain gets smart but your head gets dumb" - }) - diceCommand = true; - @CheckboxProperty({ - name: `Help Command ${DARK_AQUA}?help`, - category: "Party Commands", - subcategory: "Party Commands", - description: "So much to do, so much to see" - }) - helpCommand = true; - @CheckboxProperty({ - name: `Limbo Command ${DARK_AQUA}?`, - category: "Party Commands", - subcategory: "Party Commands", - description: "So what's wrong with taking the backstreets?" - }) - limboCommand = false; - @CheckboxProperty({ - name: `Leave Command ${DARK_AQUA}?leave`, - category: "Party Commands", - subcategory: "Party Commands", - description: "You'll never know if you don't go" - }) - leaveCommand = false; - @CheckboxProperty({ - name: `RPS Command ${DARK_AQUA}?rps`, - category: "Party Commands", - subcategory: "Party Commands", - description: "You'll never shine if you don't glow" - }) - rpsCommand = true; - @CheckboxProperty({ - name: `Slander Commands ${DARK_AQUA}(i plead the fifth.)`, - category: "Party Commands", - subcategory: "Party Commands", - description: "Hey now, you're an all-star, get your game on, go play" - }) - slanderCommand = true; - @CheckboxProperty({ - name: `Status Commands ${DARK_AQUA}?`, - category: "Party Commands", - subcategory: "Party Commands", - description: "Hey now, you're a rock star, get the show on, get paid" - }) - statusCommand = true; - @SelectorProperty({ - name: `Gyatt Command ${DARK_AQUA}?`, - category: "Party Commands", - subcategory: "Party Commands", - description: "This will only control '?w', you can access any of the other commands by using '?'.", - options: ["OFF", "random", "waifu", "neko", "shinobu", "megumin", "bully", "cuddle", "cry", "hug", "awoo", "kiss", "lick", "pat", "smug", "bonk", "yeet", "blush", - "smile", "wave", "highfive", "handhold", "nom", "bite", "glomp", "slap", "kill", "kick", "happy", "wink", "poke", "dance", "cringe"] - }) - womenCommand = 0; - @PercentSliderProperty({ - name: "W Scale", - description: `Set the scale of the rendered images or as 0 to turn ${RED}OFF${GRAY}.`, - category: "Party Commands", - subcategory: "Party Commands", - }) - wScale = 1; - @CheckboxProperty({ - name: "R18", - description: "Controls if NSFW content is rendered.", - category: "Party Commands", - subcategory: "Party Commands", - hidden: !FileLib.read("./VolcAddons/Data", "contract.txt")?.split("\n")?.[51]?.includes(Player.getName()) ?? false - }) - r18 = false; + // --- LEADER COMMANDS --- + @CheckboxProperty({ + name: `Allinvite Command ${DARK_AQUA}?`, + category: "Leader Commands", + subcategory: "Leader Commands", + description: "Once upon a time there was a lovely princess.", + }) + allinvCommand = true; + @CheckboxProperty({ + name: `Demote Command ${DARK_AQUA}?demote`, + category: "Leader Commands", + subcategory: "Leader Commands", + description: + "But she had an enchantment upon her of a fearful sort which could only be broken by love's first kiss.", + }) + demoteCommand = true; + @CheckboxProperty({ + name: `Instance Command ${DARK_AQUA}?[1-7]`, + category: "Leader Commands", + subcategory: "Leader Commands", + description: + "She was locked away in a castle guarded by a terrible fire-breathing dragon.", + }) + instanceCommand = true; + @CheckboxProperty({ + name: `Invite Command ${DARK_AQUA}?invite`, + category: "Leader Commands", + subcategory: "Leader Commands", + description: + "Many brave knigts had attempted to free her from this dreadful prison, but none prevailed.", + }) + inviteCommand = true; + @CheckboxProperty({ + name: `Promote Command ${DARK_AQUA}?promote`, + category: "Leader Commands", + subcategory: "Leader Commands", + description: + "She waited in the dragon's keep in the highest room of the tallest tower for her true love and true love's first kiss.", + }) + promoteCommand = true; + @CheckboxProperty({ + name: `Stream Command ${DARK_AQUA}? [num]`, + category: "Leader Commands", + subcategory: "Leader Commands", + description: "Like that's ever gonna happen.", + }) + streamCommand = true; + @CheckboxProperty({ + name: `Transfer Command ${DARK_AQUA}?transfer`, + category: "Leader Commands", + subcategory: "Leader Commands", + description: "{Paper Rusting, Toilet Flushes}", + }) + transferCommand = true; + @CheckboxProperty({ + name: `Warp Command ${DARK_AQUA}?warp`, + category: "Leader Commands", + subcategory: "Leader Commands", + description: "What a load of - ", + }) + warpCommand = true; - // --- KUUDRA ALERTS --- - - @CheckboxProperty({ - name: "No Key Alert", - category: "Kuudra Alerts", - subcategory: "Kuudra Alerts", - description: "Let's get down to business\nTo defeat the Huns" - }) - keyAlert = true; - @CheckboxProperty({ - name: "Unready Alert", - category: "Kuudra Alerts", - subcategory: "Kuudra Alerts", - description: "Did they send me daughters\nWhen I asked for sons?" - }) - unreadyAlert = true; - @CheckboxProperty({ - name: "Choose Route Alert", - category: "Kuudra Alerts", - subcategory: "Kuudra Alerts", - description: "You're the saddest bunch I ever met\nBut you can bet before we're through\nMister, I'll make a man out of you" - }) - routeAlert = true; - @CheckboxProperty({ - name: "Pickup Supply Alert", - category: "Kuudra Alerts", - subcategory: "Kuudra Alerts", - description: "Tranquil as a forest\nBut on fire within" - }) - supplyAlert = true; - @CheckboxProperty({ - name: "Building Alert", - category: "Kuudra Alerts", - subcategory: "Kuudra Alerts", - description: "Once you find your center\nYou are sure to win" - }) - buildingAlert = true; - @CheckboxProperty({ - name: "Fresh Tools Alert", - category: "Kuudra Alerts", - subcategory: "Kuudra Alerts", - description: "You're a spineless pale pathetic lot\nAnd you haven't got a clue" - }) - freshAlert = true; - @CheckboxProperty({ - name: "Fuel Percent Alert", - category: "Kuudra Alerts", - subcategory: "Kuudra Alerts", - description: "Somehow I'll make a man out of you" - }) - fuelAlert = true; - @CheckboxProperty({ - name: "Fresh Tools Alert", - category: "Kuudra Alerts", - subcategory: "Kuudra Alerts", - description: "I'm never gonna catch my breath\nSay goodbye to those who knew me" - }) - freshAlert = true; - @TextProperty({ - name: "Stunner Eaten Alert", - description: "Tracks who is stunning Kuudra. Enter 'all' to track everyone or empty to turn off.", - category: "Kuudra Alerts", - subcategory: "Kuudra Alerts", - description: "Why was I a fool in school for cutting gym?\nThis guy's got them scared to death!" - }) - kuudraStunner = "all"; - @TextProperty({ - name: "Mount Cannon Alert", - description: "Tracks who is shooting the ballista. Enter 'all' to track everyone or empty to turn off.", - category: "Kuudra Alerts", - subcategory: "Kuudra Alerts", - description: "Hope he doesn't see right through me\nNow I really wish that I knew how to swim" - }) - kuudraCannonear = "all"; - @CheckboxProperty({ - name: "Stun Alert", - category: "Kuudra Alerts", - subcategory: "Kuudra Alerts", - description: "We must be swift as the coursing river (Be a man)" - }) - stunAlert = true; - @CheckboxProperty({ - name: "Dropship Alert", - category: "Kuudra Alerts", - subcategory: "Kuudra Alerts", - description: "With all the force of a great typhoon (Be a man)" - }) - dropshipAlert = true; - @SliderProperty({ - name: "Token Alert", - description: `Set token threshold before alert appears or as 0 to turn ${RED}OFF${GRAY} (only alerts once per run).`, - category: "Kuudra Alerts", - subcategory: "Kuudra Alerts", - description: "With all the strength of a raging fire\nMysterious as the dark side of the moon", - min: 0, - max: 1000 - }) - tokenAlert = 0; + // --- PARTY COMMANDS --- + @CheckboxProperty({ + name: "All Chat", + category: "Party Commands", + subcategory: "Chat Options", + description: "Somebody once told me the world is gonna roll me", + }) + allCommands = true; + @CheckboxProperty({ + name: "Party Chat", + category: "Party Commands", + subcategory: "Chat Options", + description: "I ain't the sharpest tool in the shed", + }) + partyCommands = true; + @CheckboxProperty({ + name: "Guild Chat", + category: "Party Commands", + subcategory: "Chat Options", + description: "She was lookin' kind of dumb with her finger and her thumb", + }) + guildCommands = true; + @CheckboxProperty({ + name: "DM Chat", + category: "Party Commands", + subcategory: "Chat Options", + description: 'In the shape of an "L" on her forehead', + }) + dmCommands = true; - // --- WEBHOOK CHATS --- - @CheckboxProperty({ - name: "Public Chat", - category: "Webhook Chats", - subcategory: "Webhook Chats", - description: -`The snow glows white on the mountain tonight + @CheckboxProperty({ + name: `8ball Command ${DARK_AQUA}?8ball`, + category: "Party Commands", + subcategory: "Party Commands", + description: "The years start comin' and they don't stop comin'", + }) + ballCommand = true; + @CheckboxProperty({ + name: `Coinflip Command ${DARK_AQUA}?`, + category: "Party Commands", + subcategory: "Party Commands", + description: "Fed to the rules and I hit the ground runnin'", + }) + coinCommand = true; + @CheckboxProperty({ + name: `Coords Command ${DARK_AQUA}?`, + category: "Party Commands", + subcategory: "Party Commands", + description: "Didn't make sense not to live for fun", + }) + coordsCommand = true; + @CheckboxProperty({ + name: `Dice Command ${DARK_AQUA}?`, + category: "Party Commands", + subcategory: "Party Commands", + description: "Your brain gets smart but your head gets dumb", + }) + diceCommand = true; + @CheckboxProperty({ + name: `Help Command ${DARK_AQUA}?help`, + category: "Party Commands", + subcategory: "Party Commands", + description: "So much to do, so much to see", + }) + helpCommand = true; + @CheckboxProperty({ + name: `Limbo Command ${DARK_AQUA}?`, + category: "Party Commands", + subcategory: "Party Commands", + description: "So what's wrong with taking the backstreets?", + }) + limboCommand = false; + @CheckboxProperty({ + name: `Leave Command ${DARK_AQUA}?leave`, + category: "Party Commands", + subcategory: "Party Commands", + description: "You'll never know if you don't go", + }) + leaveCommand = false; + @CheckboxProperty({ + name: `RPS Command ${DARK_AQUA}?rps`, + category: "Party Commands", + subcategory: "Party Commands", + description: "You'll never shine if you don't glow", + }) + rpsCommand = true; + @CheckboxProperty({ + name: `Slander Commands ${DARK_AQUA}(i plead the fifth.)`, + category: "Party Commands", + subcategory: "Party Commands", + description: "Hey now, you're an all-star, get your game on, go play", + }) + slanderCommand = true; + @CheckboxProperty({ + name: `Status Commands ${DARK_AQUA}?`, + category: "Party Commands", + subcategory: "Party Commands", + description: "Hey now, you're a rock star, get the show on, get paid", + }) + statusCommand = true; + @SelectorProperty({ + name: `Gyatt Command ${DARK_AQUA}?`, + category: "Party Commands", + subcategory: "Party Commands", + description: + "This will only control '?w', you can access any of the other commands by using '?'.", + options: [ + "OFF", + "random", + "waifu", + "neko", + "shinobu", + "megumin", + "bully", + "cuddle", + "cry", + "hug", + "awoo", + "kiss", + "lick", + "pat", + "smug", + "bonk", + "yeet", + "blush", + "smile", + "wave", + "highfive", + "handhold", + "nom", + "bite", + "glomp", + "slap", + "kill", + "kick", + "happy", + "wink", + "poke", + "dance", + "cringe", + ], + }) + womenCommand = 0; + @PercentSliderProperty({ + name: "W Scale", + description: `Set the scale of the rendered images or as 0 to turn ${RED}OFF${GRAY}.`, + category: "Party Commands", + subcategory: "Party Commands", + }) + wScale = 1; + @CheckboxProperty({ + name: "R18", + description: "Controls if NSFW content is rendered.", + category: "Party Commands", + subcategory: "Party Commands", + hidden: + !FileLib.read("./VolcAddons/Data", "contract.txt") + ?.split("\n")?.[51] + ?.includes(Player.getName()) ?? false, + }) + r18 = false; + + // --- KUUDRA ALERTS --- + + @CheckboxProperty({ + name: "No Key Alert", + category: "Kuudra Alerts", + subcategory: "Kuudra Alerts", + description: "Let's get down to business\nTo defeat the Huns", + }) + keyAlert = true; + @CheckboxProperty({ + name: "Unready Alert", + category: "Kuudra Alerts", + subcategory: "Kuudra Alerts", + description: "Did they send me daughters\nWhen I asked for sons?", + }) + unreadyAlert = true; + @CheckboxProperty({ + name: "Choose Route Alert", + category: "Kuudra Alerts", + subcategory: "Kuudra Alerts", + description: + "You're the saddest bunch I ever met\nBut you can bet before we're through\nMister, I'll make a man out of you", + }) + routeAlert = true; + @CheckboxProperty({ + name: "Pickup Supply Alert", + category: "Kuudra Alerts", + subcategory: "Kuudra Alerts", + description: "Tranquil as a forest\nBut on fire within", + }) + supplyAlert = true; + @CheckboxProperty({ + name: "Building Alert", + category: "Kuudra Alerts", + subcategory: "Kuudra Alerts", + description: "Once you find your center\nYou are sure to win", + }) + buildingAlert = true; + @CheckboxProperty({ + name: "Fresh Tools Alert", + category: "Kuudra Alerts", + subcategory: "Kuudra Alerts", + description: + "You're a spineless pale pathetic lot\nAnd you haven't got a clue", + }) + freshAlert = true; + @CheckboxProperty({ + name: "Fuel Percent Alert", + category: "Kuudra Alerts", + subcategory: "Kuudra Alerts", + description: "Somehow I'll make a man out of you", + }) + fuelAlert = true; + @CheckboxProperty({ + name: "Fresh Tools Alert", + category: "Kuudra Alerts", + subcategory: "Kuudra Alerts", + description: + "I'm never gonna catch my breath\nSay goodbye to those who knew me", + }) + freshAlert = true; + @TextProperty({ + name: "Stunner Eaten Alert", + description: + "Tracks who is stunning Kuudra. Enter 'all' to track everyone or empty to turn off.", + category: "Kuudra Alerts", + subcategory: "Kuudra Alerts", + description: + "Why was I a fool in school for cutting gym?\nThis guy's got them scared to death!", + }) + kuudraStunner = "all"; + @TextProperty({ + name: "Mount Cannon Alert", + description: + "Tracks who is shooting the ballista. Enter 'all' to track everyone or empty to turn off.", + category: "Kuudra Alerts", + subcategory: "Kuudra Alerts", + description: + "Hope he doesn't see right through me\nNow I really wish that I knew how to swim", + }) + kuudraCannonear = "all"; + @CheckboxProperty({ + name: "Stun Alert", + category: "Kuudra Alerts", + subcategory: "Kuudra Alerts", + description: "We must be swift as the coursing river (Be a man)", + }) + stunAlert = true; + @CheckboxProperty({ + name: "Dropship Alert", + category: "Kuudra Alerts", + subcategory: "Kuudra Alerts", + description: "With all the force of a great typhoon (Be a man)", + }) + dropshipAlert = true; + @SliderProperty({ + name: "Token Alert", + description: `Set token threshold before alert appears or as 0 to turn ${RED}OFF${GRAY} (only alerts once per run).`, + category: "Kuudra Alerts", + subcategory: "Kuudra Alerts", + description: + "With all the strength of a raging fire\nMysterious as the dark side of the moon", + min: 0, + max: 1000, + }) + tokenAlert = 0; + + // --- WEBHOOK CHATS --- + @CheckboxProperty({ + name: "Public Chat", + category: "Webhook Chats", + subcategory: "Webhook Chats", + description: `The snow glows white on the mountain tonight Not a footprint to be seen A kingdom of isolation -And it looks like I'm the queen` - }) - publicChat = false; - @CheckboxProperty({ - name: "Party Chat", - category: "Webhook Chats", - subcategory: "Webhook Chats", - description: -`The wind is howling like this swirling storm inside +And it looks like I'm the queen`, + }) + publicChat = false; + @CheckboxProperty({ + name: "Party Chat", + category: "Webhook Chats", + subcategory: "Webhook Chats", + description: `The wind is howling like this swirling storm inside Couldn't keep it in, heaven knows I tried Don't let them in, don't let them see Be the good girl you always have to be Conceal, don't feel, don't let them know -Well, now they know` - }) - partyChat = false; - @CheckboxProperty({ - name: "Guild Chat", - category: "Webhook Chats", - subcategory: "Webhook Chats", - description: -`Let it go, let it go +Well, now they know`, + }) + partyChat = false; + @CheckboxProperty({ + name: "Guild Chat", + category: "Webhook Chats", + subcategory: "Webhook Chats", + description: `Let it go, let it go Can't hold it back anymore Let it go, let it go Turn away and slam the door I don't care what they're going to say Let the storm rage on -The cold never bothered me anyway` - }) - guildChat = false; - @CheckboxProperty({ - name: "Private Chat", - category: "Webhook Chats", - subcategory: "Webhook Chats", - description: -`It's funny how some distance makes everything seem small +The cold never bothered me anyway`, + }) + guildChat = false; + @CheckboxProperty({ + name: "Private Chat", + category: "Webhook Chats", + subcategory: "Webhook Chats", + description: `It's funny how some distance makes everything seem small And the fears that once controlled me can't get to me at all It's time to see what I can do To test the limits and break through No right, no wrong, no rules for me -I'm free` - }) - privateChat = false; +I'm free`, + }) + privateChat = false; } -export default new Settings; +export default new Settings(); diff --git a/utils/UpdateTils.js b/utils/UpdateTils.js index 60b9589..0d399f1 100644 --- a/utils/UpdateTils.js +++ b/utils/UpdateTils.js @@ -1,84 +1,90 @@ import axios from "../../axios"; -import { BOLD, GOLD, GREEN, LOGO, WHITE, GRAY, DARK_RED } from "./Constants"; - +import { BOLD, DARK_RED, GOLD, GRAY, GREEN, LOGO, WHITE } from "./Constants"; /** * Retrieves the current version of the addon from the metadata.json file. - * + * * @returns {string|null} The current version, or null if reading the version fails. */ function getCurrentVersion() { - try { - const metadataJson = FileLib.read("VolcAddons", "metadata.json"); - const metadata = JSON.parse(metadataJson); - return metadata.version; - } catch(err) { ChatLib.chat(LOGO + DARK_RED + (err.cause ?? err)) } + try { + const metadataJson = FileLib.read("VolcAddons", "metadata.json"); + const metadata = JSON.parse(metadataJson); + return metadata.version; + } catch (err) { + ChatLib.chat(LOGO + DARK_RED + (err.cause ?? err)); + } } /** * If version1 < version 2 return true else false. - * + * * @param {String} version1 - Version X.X.X numero 1 * @param {String} version2 - Version X.x.x numero 2 - * @returns + * @returns */ function compareVersions(version1, version2) { - const parts1 = version1.split('.').map(Number); - const parts2 = version2.split('.').map(Number); + const parts1 = version1.split(".").map(Number); + const parts2 = version2.split(".").map(Number); - for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) { - let part1 = parts1[i] || 0; - let part2 = parts2[i] || 0; + for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) { + let part1 = parts1[i] || 0; + let part2 = parts2[i] || 0; - if (part1 < part2) - return true; - else if (part1 > part2) - return false; - } + if (part1 < part2) return true; + else if (part1 > part2) return false; + } - return false; // Both versions are equal + return false; // Both versions are equal } /** * Fetches the latest release version from the GitHub repository and notifies the user if a new release is available. */ export function getLatestReleaseVersion() { - const releasesUrl = `https://api.github.com/repos/zhenga8533/VolcAddons/releases`; + const releasesUrl = `https://api.github.com/repos/zhenga8533/VolcAddons/releases`; - // Fetch the releases from the GitHub API using Axios - axios.get(releasesUrl).then(response => { - const releases = response.data; + // Fetch the releases from the GitHub API using Axios + axios + .get(releasesUrl) + .then((response) => { + const releases = response.data; - // Check if the response contains an array of releases - if (!Array.isArray(releases) || releases.length === 0) { - console.log("No releases found for the repository."); - return; - } + // Check if the response contains an array of releases + if (!Array.isArray(releases) || releases.length === 0) { + console.log("No releases found for the repository."); + return; + } - // Get the latest release from the array - const latestRelease = releases[0]; - if (!latestRelease.tag_name) { - console.log("Unable to find the latest release tag."); - return; - } + // Get the latest release from the array + const latestRelease = releases[0]; + if (!latestRelease.tag_name) { + console.log("Unable to find the latest release tag."); + return; + } - // Extract the version number from the latest release tag - const latestVersion = latestRelease.tag_name.replace('v', ''); - const currentVersion = getCurrentVersion(); + // Extract the version number from the latest release tag + const latestVersion = latestRelease.tag_name.replace("v", ""); + const currentVersion = getCurrentVersion(); - // Compare the current version with the latest version and notify the user if an update is available - if (currentVersion && compareVersions(currentVersion, latestVersion)) { - const downloadLink = latestRelease.html_url; - ChatLib.chat(`\n${LOGO + GOLD + BOLD}NEW RELEASE: ${WHITE + BOLD}v${latestVersion}`); - ChatLib.chat(`${GREEN}Download the new version here: ${downloadLink}`); - ChatLib.chat(`${GREEN}OR install using the forge installer: https://raw.githubusercontent.com/zhenga8533/VolcAddons/main/forge/VolcAddons-1.0.jar\n`); - } else { - ChatLib.chat(`\n${LOGO + GREEN + BOLD}You are on the latest version (${WHITE + BOLD}v${currentVersion + GREEN + BOLD})!\n`); - ChatLib.chat(`${GRAY + BOLD}Changelog:`); - JSON.parse(FileLib.read("VolcAddons", "changelog.json")).forEach(change => { - ChatLib.chat(change); - }); - ChatLib.chat(""); - } - }).catch(err => ChatLib.chat(LOGO + DARK_RED + (err.cause ?? err))); + // Compare the current version with the latest version and notify the user if an update is available + if (currentVersion && compareVersions(currentVersion, latestVersion)) { + const downloadLink = latestRelease.html_url; + ChatLib.chat(`\n${LOGO + GOLD + BOLD}NEW RELEASE: ${WHITE + BOLD}v${latestVersion}`); + ChatLib.chat(`${GREEN}Download the new version here: ${downloadLink}`); + ChatLib.chat( + `${GREEN}OR install using the forge installer: https://raw.githubusercontent.com/zhenga8533/VolcAddons/main/forge/VolcAddons-1.0.jar\n` + ); + } else { + ChatLib.chat( + `\n${LOGO + GREEN + BOLD}You are on the latest version (${WHITE + BOLD}v${currentVersion + GREEN + BOLD})!\n` + ); + ChatLib.chat(`${GRAY + BOLD}Changelog:`); + JSON.parse(FileLib.read("VolcAddons", "changelog.json")).forEach((change) => { + ChatLib.chat(change); + }); + ChatLib.chat(""); + } + }) + .catch((err) => ChatLib.chat(LOGO + DARK_RED + (err.cause ?? err))); } diff --git a/utils/Waypoint.js b/utils/Waypoint.js index 4e94331..fe1a293 100644 --- a/utils/Waypoint.js +++ b/utils/Waypoint.js @@ -4,216 +4,249 @@ import { DARK_GRAY, DARK_GREEN, DARK_RED, GOLD, GREEN, RED, YELLOW } from "./Con import { data } from "./Data"; import Settings from "./Settings"; - /** * General utility for rendering waypoints. */ const waypoints = []; register("renderWorld", (pt) => { - const pX = Player.getRenderX(); - const pY = Player.getRenderY(); - const pZ = Player.getRenderZ(); - const pEye = Player.asPlayerMP().getEyeHeight(); - - waypoints.forEach(waypoint => { - waypoint.draw(pX, pY, pZ, pEye, pt); - }); + const pX = Player.getRenderX(); + const pY = Player.getRenderY(); + const pZ = Player.getRenderZ(); + const pEye = Player.asPlayerMP().getEyeHeight(); + + waypoints.forEach((waypoint) => { + waypoint.draw(pX, pY, pZ, pEye, pt); + }); }); register("worldUnload", () => { - waypoints.forEach(waypoint => waypoint.clear()); + waypoints.forEach((waypoint) => waypoint.clear()); }); register("guiClosed", (event) => { - if (!event.toString().startsWith("gg.essential.vigilance.gui.SettingsGui")) return; - waypoints.forEach(waypoint => waypoint.clear()); + if (!event.toString().startsWith("gg.essential.vigilance.gui.SettingsGui")) return; + waypoints.forEach((waypoint) => waypoint.clear()); }); export default class Waypoint { - #color; - #waypoints; - #type; - #box; - #beam; - #rounded; - - /** - * Creates a new waypoint. - * - * @param {Number[]} color - RGB color of the waypoint. - * @param {Number} type - 0 for default, 1 for simple, 2 for entity, or 3 for simple entity. - * @param {Boolean} box - Whether or not to render the waypoint box. - * @param {Boolean} beam - Whether or not to render the beacon beam. - * @param {Boolean} rounded - Whether or not to round the waypoint coordinates. - * @returns {Waypoint} The new waypoint. - */ - constructor(color=[1, 1, 1], type=0, box=true, beam=true, rounded=true) { - waypoints.push(this); - this.#waypoints = []; - this.#color = color; - this.#type = type; - this.#box = box; - this.#beam = beam; - this.#rounded = rounded; + #color; + #waypoints; + #type; + #box; + #beam; + #rounded; + + /** + * Creates a new waypoint. + * + * @param {Number[]} color - RGB color of the waypoint. + * @param {Number} type - 0 for default, 1 for simple, 2 for entity, or 3 for simple entity. + * @param {Boolean} box - Whether or not to render the waypoint box. + * @param {Boolean} beam - Whether or not to render the beacon beam. + * @param {Boolean} rounded - Whether or not to round the waypoint coordinates. + * @returns {Waypoint} The new waypoint. + */ + constructor(color = [1, 1, 1], type = 0, box = true, beam = true, rounded = true) { + waypoints.push(this); + this.#waypoints = []; + this.#color = color; + this.#type = type; + this.#box = box; + this.#beam = beam; + this.#rounded = rounded; + } + + /** + * Draws the waypoints on the screen. + * + * @param {Number} pX - Player's x position. + * @param {Number} pY - Player's y position. + * @param {Number} pZ - Player's z position. + * @param {Number} pEye - Player's eye height. + * @param {Number} pt - Partial tick. + */ + draw(pX, pY, pZ, pEye, pt) { + if (this.#type > 1) { + this.highlight(pt); + return; } - /** - * Draws the waypoints on the screen. - * - * @param {Number} pX - Player's x position. - * @param {Number} pY - Player's y position. - * @param {Number} pZ - Player's z position. - * @param {Number} pEye - Player's eye height. - * @param {Number} pt - Partial tick. - */ - draw(pX, pY, pZ, pEye, pt){ - if (this.#type > 1) { - this.highlight(pt); - return; + this.#waypoints.forEach((waypoint) => { + const n = waypoint.length; + const title = waypoint[n - 4]; + + // Calculate the position of the waypoint + let x = parseFloat(this.#rounded ? Math.round(waypoint[n - 3]) : waypoint[n - 3]) - 0.5; + let y = parseFloat(this.#rounded ? Math.round(waypoint[n - 2]) : waypoint[n - 2]); + let z = parseFloat(this.#rounded ? Math.round(waypoint[n - 1]) : waypoint[n - 1]) - 0.5; + const color = waypoint.length < 6 && !isNaN(waypoint[2]) ? this.#color : waypoint.slice(0, 3); + + // Calculate the render distance of the waypoint + const distance = Math.hypot(pX - x, pY - y, pZ - z); + const renderDistance = Math.min(distance, 50); + const distanceColor = + distance < 10 + ? GREEN + : distance < 25 + ? DARK_GREEN + : distance < 50 + ? YELLOW + : distance < 100 + ? GOLD + : distance < 200 + ? RED + : DARK_RED; + + // Credit: https://github.com/Soopyboo32/SoopyV2/blob/master/src/utils/renderUtils.js + const rX = pX + (x - pX) / (distance / renderDistance); + const rY1 = pY + pEye + (y + 1 + (20 * distance) / 300 - (pY + pEye)) / (distance / renderDistance); + const rY2 = + pY + pEye + (y + 1 + (20 * distance) / 300 - (10 * distance) / 300 - (pY + pEye)) / (distance / renderDistance); + const rZ = pZ + (z - pZ) / (distance / renderDistance); + + // Render waypoint box + if (this.#box) { + RenderLib.drawEspBox(rX, distance > 50 ? rY1 : y, rZ, 1, 1, ...color, 1, data.vision || this.#type === 0); + RenderLib.drawInnerEspBox( + rX, + distance > 50 ? rY1 : y, + rZ, + 1, + 1, + ...color, + 0.25, + data.vision || this.#type === 0 + ); + } + + // Render waypoint text + if (this.#type === 0) { + Tessellator.drawString(GOLD + title, rX, rY1, rZ); + Tessellator.drawString(`${DARK_GRAY}[${distanceColor + Math.round(distance)}m${DARK_GRAY}]`, rX, rY2, rZ); + } + + // Render beacon beam + if (this.#beam) renderBeaconBeam(rX - 0.5, distance > 50 ? rY1 : y, rZ - 0.5, ...color, 0.5, false); + }); + } + + /** + * Highlights the waypoints on the screen. Typically used for entities. + * + * @param {Number} pt - Partial tick. + */ + highlight(pt) { + this.#waypoints.forEach((waypoint) => { + // Get the title and entity of the waypoint + const title = waypoint[0]; + const entity = waypoint[1]?.getEntity() ?? waypoint[1]; + const x = entity.field_70165_t * pt - entity.field_70142_S * (pt - 1); + const y = entity.field_70163_u * pt - entity.field_70137_T * (pt - 1); + const z = entity.field_70161_v * pt - entity.field_70136_U * (pt - 1); + const width = entity.field_70130_N; + const height = entity.field_70131_O; + + // Render the entity box + RenderLib.drawEspBox(x, y, z, width, height, ...this.#color, 1, data.vision); + if (this.#box) + RenderLib.drawInnerEspBox( + x, + y, + z, + width, + height, + ...this.#color, + Settings.hitboxColor.alpha / 510, + data.vision + ); + if (title !== undefined && data.vision) { + let text = ""; + if (this.#type === 2) { + const distance = Player.asPlayerMP().distanceTo(entity); + const distanceColor = + distance < 10 + ? GREEN + : distance < 25 + ? DARK_GREEN + : distance < 50 + ? YELLOW + : distance < 100 + ? GOLD + : distance < 200 + ? RED + : DARK_RED; + text = `${DARK_GRAY}[${distanceColor + Math.round(distance)}m${DARK_GRAY}]`; } - - this.#waypoints.forEach(waypoint => { - const n = waypoint.length; - const title = waypoint[n - 4]; - - // Calculate the position of the waypoint - let x = parseFloat(this.#rounded ? Math.round(waypoint[n - 3]) : waypoint[n - 3]) - 0.5; - let y = parseFloat(this.#rounded ? Math.round(waypoint[n - 2]) : waypoint[n - 2]); - let z = parseFloat(this.#rounded ? Math.round(waypoint[n - 1]) : waypoint[n - 1]) - 0.5; - const color = waypoint.length < 6 && !isNaN(waypoint[2]) ? this.#color : waypoint.slice(0, 3); - - // Calculate the render distance of the waypoint - const distance = Math.hypot(pX - x, pY - y, pZ - z); - const renderDistance = Math.min(distance, 50); - const distanceColor = distance < 10 ? GREEN : - distance < 25 ? DARK_GREEN : - distance < 50 ? YELLOW : - distance < 100 ? GOLD : - distance < 200 ? RED : DARK_RED; - - // Credit: https://github.com/Soopyboo32/SoopyV2/blob/master/src/utils/renderUtils.js - const rX = pX + (x - pX) / (distance / renderDistance); - const rY1 = pY + pEye + (y + 1 + (20 * distance / 300) - (pY + pEye)) / (distance / renderDistance); - const rY2 = pY + pEye + (y + 1 + (20 * distance / 300) - (10 * distance / 300) - (pY + pEye)) / (distance / renderDistance); - const rZ = pZ + (z - pZ) / (distance / renderDistance); - - // Render waypoint box - if (this.#box) { - RenderLib.drawEspBox(rX, distance > 50 ? rY1 : y, rZ, 1, 1, ...color, 1, data.vision || this.#type === 0); - RenderLib.drawInnerEspBox(rX, distance > 50 ? rY1 : y, rZ, 1, 1, ...color, 0.25, data.vision || this.#type === 0); - } - - // Render waypoint text - if (this.#type === 0) { - Tessellator.drawString(GOLD + title, rX, rY1, rZ); - Tessellator.drawString(`${DARK_GRAY}[${distanceColor + Math.round(distance)}m${DARK_GRAY}]`, rX, rY2, rZ); - } - - // Render beacon beam - if (this.#beam) renderBeaconBeam(rX - 0.5, distance > 50 ? rY1 : y, rZ - 0.5, ...color, 0.5, false); - }); - } - - /** - * Highlights the waypoints on the screen. Typically used for entities. - * - * @param {Number} pt - Partial tick. - */ - highlight(pt) { - this.#waypoints.forEach(waypoint => { - // Get the title and entity of the waypoint - const title = waypoint[0]; - const entity = waypoint[1]?.getEntity() ?? waypoint[1]; - const x = entity.field_70165_t * pt - entity.field_70142_S * (pt - 1); - const y = entity.field_70163_u * pt - entity.field_70137_T * (pt - 1); - const z = entity.field_70161_v * pt - entity.field_70136_U * (pt - 1); - const width = entity.field_70130_N; - const height = entity.field_70131_O; - - // Render the entity box - RenderLib.drawEspBox(x, y, z, width, height, ...this.#color, 1, data.vision); - if (this.#box) RenderLib.drawInnerEspBox(x, y, z, width, height, ...this.#color, Settings.hitboxColor.alpha/510, data.vision); - if (title !== undefined && data.vision) { - let text = ''; - if (this.#type === 2) { - const distance = Player.asPlayerMP().distanceTo(entity); - const distanceColor = distance < 10 ? GREEN : - distance < 25 ? DARK_GREEN : - distance < 50 ? YELLOW : - distance < 100 ? GOLD : - distance < 200 ? RED : DARK_RED; - text = `${DARK_GRAY}[${distanceColor + Math.round(distance)}m${DARK_GRAY}]`; - } - Tessellator.drawString(text, x, y + height + 1, z, 0xffffff, true); - } - }); - } - - /** - * Clears the list of waypoints. - */ - clear() { - this.#waypoints = []; - } - - /** - * Sets if the waypoint should render a box or not. - */ - setBox(box) { - this.#box = box; - } - - /** - * Sets the color of the waypoint. - */ - setColor(color) { - this.#color = color; - } - - /** - * Adds a waypoint to the list. - * - * @param {Number[]} waypoint - The waypoint to add. - */ - push(waypoint) { - this.#waypoints.push(waypoint); - } - - /** - * Finds the closest waypoint to the given coordinate. - * - * @param {Numer[]} coord - The coordinate to find the closest waypoint to. - * @returns {Number[]} The closest waypoint to the given coordinate. - */ - getClosest(coord) { - const n = coord.length; - let closest = [null, Infinity]; - - this.#waypoints.forEach(waypoint => { - const m = waypoint.length; - const dX = waypoint[m - 3] - coord[n - 3]; - const dY = waypoint[m - 2] - coord[n - 2]; - const dZ = waypoint[m - 1] - coord[n - 1]; - const distance = Math.hypot(dX, dY, dZ); - if (distance < closest[1]) closest = [waypoint, distance]; - }); - return closest; - } - - /** - * Gets the list of waypoints. - * - * @returns {Number[]} The list of waypoints. - */ - getWaypoints() { - return this.#waypoints; - } - - /** - * Deletes the waypoint. - */ - delete() { - waypoints.splice(waypoints.indexOf(this), 1); - } + Tessellator.drawString(text, x, y + height + 1, z, 0xffffff, true); + } + }); + } + + /** + * Clears the list of waypoints. + */ + clear() { + this.#waypoints = []; + } + + /** + * Sets if the waypoint should render a box or not. + */ + setBox(box) { + this.#box = box; + } + + /** + * Sets the color of the waypoint. + */ + setColor(color) { + this.#color = color; + } + + /** + * Adds a waypoint to the list. + * + * @param {Number[]} waypoint - The waypoint to add. + */ + push(waypoint) { + this.#waypoints.push(waypoint); + } + + /** + * Finds the closest waypoint to the given coordinate. + * + * @param {Numer[]} coord - The coordinate to find the closest waypoint to. + * @returns {Number[]} The closest waypoint to the given coordinate. + */ + getClosest(coord) { + const n = coord.length; + let closest = [null, Infinity]; + + this.#waypoints.forEach((waypoint) => { + const m = waypoint.length; + const dX = waypoint[m - 3] - coord[n - 3]; + const dY = waypoint[m - 2] - coord[n - 2]; + const dZ = waypoint[m - 1] - coord[n - 1]; + const distance = Math.hypot(dX, dY, dZ); + if (distance < closest[1]) closest = [waypoint, distance]; + }); + return closest; + } + + /** + * Gets the list of waypoints. + * + * @returns {Number[]} The list of waypoints. + */ + getWaypoints() { + return this.#waypoints; + } + + /** + * Deletes the waypoint. + */ + delete() { + waypoints.splice(waypoints.indexOf(this), 1); + } } diff --git a/utils/functions/find.js b/utils/functions/find.js index 559eace..a7db550 100644 --- a/utils/functions/find.js +++ b/utils/functions/find.js @@ -8,68 +8,79 @@ * the closest position. */ export function getClosest(origin, positions) { - if (positions.length === 0) return [[], 999]; + if (positions.length === 0) return [[], 999]; - const n = origin.length; - const oX = origin[n - 3]; - const oY = origin[n - 2]; - const oZ = origin[n - 1]; - let closestPosition = origin; - let closestDistance = 999; - let distance = 999; + const n = origin.length; + const oX = origin[n - 3]; + const oY = origin[n - 2]; + const oZ = origin[n - 1]; + let closestPosition = origin; + let closestDistance = 999; + let distance = 999; - positions.forEach(position => { - const m = position.length; - distance = Math.hypot(oX - position[m - 3], oY - position[m - 2], oZ - position[m - 1]); - if (distance < closestDistance) { - closestDistance = distance; - closestPosition = position; - } - }); + positions.forEach((position) => { + const m = position.length; + distance = Math.hypot(oX - position[m - 3], oY - position[m - 2], oZ - position[m - 1]); + if (distance < closestDistance) { + closestDistance = distance; + closestPosition = position; + } + }); - return [closestPosition, closestDistance]; + return [closestPosition, closestDistance]; } /** * Get x and y coords of top left of container index. - * + * * @param {Number} i - Slot index. * @returns {Number[]} - [x, y] coords. */ export function getSlotCoords(i) { - if (i >= Player.getContainer().getSize()) return [0, 0]; - const gui = Client.currentGui.get(); - const slot = gui.field_147002_h?.func_75139_a(i); - const x = slot.field_75223_e + gui?.getGuiLeft() ?? 0; - const y = slot.field_75221_f + gui?.getGuiTop() ?? 0; + if (i >= Player.getContainer().getSize()) return [0, 0]; + const gui = Client.currentGui.get(); + const slot = gui.field_147002_h?.func_75139_a(i); + const x = slot.field_75223_e + gui?.getGuiLeft() ?? 0; + const y = slot.field_75221_f + gui?.getGuiTop() ?? 0; + + return [x, y]; +} - return [x, y]; +/** + * Get the closest value from an array to a given value. + * + * @param {Type} value - Value to compare + * @param {Type[]} array - Array to compare against + * @returns {Type} - Closest value from array + */ +export function findClosest(value, array) { + return array.reduce((prev, curr) => (Math.abs(curr - value) < Math.abs(prev - value) ? curr : prev)); } /** * Recurse over an object to find the path of a key. - * + * * @param {Object} obj - JSON object to parse through. * @param {String} targetKey - Key to locate in object. * @returns {String} - Path string. */ export function findKeyPath(obj, targetKey) { - if (typeof obj !== "object") return undefined; + if (typeof obj !== "object") return undefined; - let result = undefined; + let result = undefined; - for (let key in obj) { - if (key !== targetKey) { - result = findKeyPath(obj[key], targetKey); - if (result !== undefined) { - result = key + '.' + result; - break; - } - } else { - result = key; - break; - } + for (let key in obj) { + if (key !== targetKey) { + result = findKeyPath(obj[key], targetKey); + if (result !== undefined) { + result = key + "." + result; + break; + } + } else { + result = key; + break; } + } - return result; + return result; } diff --git a/utils/functions/format.js b/utils/functions/format.js index d5b96a1..a300d99 100644 --- a/utils/functions/format.js +++ b/utils/functions/format.js @@ -1,98 +1,107 @@ import { REFORGES } from "../Constants"; - /** * Converts seconds to XXdXXhrXXmXXs format. - * + * * @param {Number} seconds - Total number of seconds to convert. * @returns {String} Formatted time in XXhrXXmXXs format. */ -export function formatTime(seconds, fixed=0, units=4) { - const days = Math.floor(seconds / 86400); // 86400 seconds in a day - const hours = Math.floor((seconds % 86400) / 3600); - const minutes = Math.floor((seconds % 3600) / 60); - const remainingSeconds = seconds % 60; - - const timeString = [ - days > 0 && units-- > 0 ? `${days}d` : '', - (hours > 0 || days > 0) && units-- > 0 ? `${(hours < 10 && days > 0 ? '0' : '') + hours}h` : '', - (minutes > 0 || hours > 0 || days > 0) && units-- > 0 ? `${(minutes < 10 && (days > 0 || hours > 0) ? '0' : '') + minutes}m` : '', - units-- > 0 ? `${(remainingSeconds < 9.5 && (days > 0 || hours > 0 || minutes > 0) ? '0' : '') + remainingSeconds.toFixed(fixed)}s` : '' - ].join(''); - - return timeString; +export function formatTime(seconds, fixed = 0, units = 4) { + const days = Math.floor(seconds / 86400); // 86400 seconds in a day + const hours = Math.floor((seconds % 86400) / 3600); + const minutes = Math.floor((seconds % 3600) / 60); + const remainingSeconds = seconds % 60; + + const timeString = [ + days > 0 && units-- > 0 ? `${days}d` : "", + (hours > 0 || days > 0) && units-- > 0 ? `${(hours < 10 && days > 0 ? "0" : "") + hours}h` : "", + (minutes > 0 || hours > 0 || days > 0) && units-- > 0 + ? `${(minutes < 10 && (days > 0 || hours > 0) ? "0" : "") + minutes}m` + : "", + units-- > 0 + ? `${ + (remainingSeconds < 9.5 && (days > 0 || hours > 0 || minutes > 0) ? "0" : "") + + remainingSeconds.toFixed(fixed) + }s` + : "", + ].join(""); + + return timeString; } /** * Convert a formatted time string into seconds. - * + * * @param {string} timeString - The formatted time string containing units (e.g., "2d3h45m12s"). * @returns {number} The total time in seconds. */ export function unformatTime(timeString) { - let seconds = 0; - const timeRegex = /(\d+)([dhms])/g; - let match; - - while ((match = timeRegex.exec(timeString)) !== null) { - let value = parseInt(match[1]); - let unit = match[2]; - switch (unit) { - case 'd': - seconds += value * 24 * 60 * 60; - break; - case 'hr': - case 'h': - seconds += value * 60 * 60; - break; - case 'm': - seconds += value * 60; - break; - case 's': - seconds += value; - break; - default: - break; - } + let seconds = 0; + const timeRegex = /(\d+)([dhms])/g; + let match; + + while ((match = timeRegex.exec(timeString)) !== null) { + let value = parseInt(match[1]); + let unit = match[2]; + switch (unit) { + case "d": + seconds += value * 24 * 60 * 60; + break; + case "hr": + case "h": + seconds += value * 60 * 60; + break; + case "m": + seconds += value * 60; + break; + case "s": + seconds += value; + break; + default: + break; } - return seconds; + } + return seconds; } /** * Rounds number and converts num to thousand seperator format. - * + * * @param {Number} num - Base number to convert. * @returns {String} Number converted to thousand seperator format. */ export function commafy(num) { - return num.toFixed(0).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); + return num + .toFixed(0) + .toString() + .replace(/\B(?=(\d{3})+(?!\d))/g, ","); } /** * Converts a number to a string in k, m, b notation - * + * * @param {Number} num - Base number to convert. * @param {Number} significantDigits - Number of significant digits to display. * @returns {String} Formatted number if k, m, b notation */ -export function formatNumber(num, significantDigits=5) { - if (isNaN(num) || num === 0) return "0"; - - const sign = Math.sign(num); - const absNum = Math.abs(num); - - if (absNum < 1) return (sign === -1 ? '-' : '') + absNum.toFixed(2); - - const abbrev = ["", "k", "m", "b", "t", "q", "Q"]; - const index = Math.floor(Math.log10(absNum) / 3); - - let formattedNumber = (sign === -1 ? -1 : 1) * absNum / Math.pow(10, index * 3); - const digits = formattedNumber.toFixed(0).length; - formattedNumber = formattedNumber.toFixed(MathLib.clamp(significantDigits - digits, 0, 2)) + abbrev[index]; - - // Check if the number is a whole number, and if so, remove the ".00" - if (Number.isInteger(absNum) && absNum < 1_000) return String(parseInt(formattedNumber)); - return formattedNumber; +export function formatNumber(num, significantDigits = 5) { + if (isNaN(num) || num === 0) return "0"; + + const sign = Math.sign(num); + const absNum = Math.abs(num); + + if (absNum < 1) return (sign === -1 ? "-" : "") + absNum.toFixed(2); + + const abbrev = ["", "k", "m", "b", "t", "q", "Q"]; + const index = Math.floor(Math.log10(absNum) / 3); + + let formattedNumber = ((sign === -1 ? -1 : 1) * absNum) / Math.pow(10, index * 3); + const digits = formattedNumber.toFixed(0).length; + formattedNumber = formattedNumber.toFixed(MathLib.clamp(significantDigits - digits, 0, 2)) + abbrev[index]; + + // Check if the number is a whole number, and if so, remove the ".00" + if (Number.isInteger(absNum) && absNum < 1_000) return String(parseInt(formattedNumber)); + return formattedNumber; } /** @@ -102,23 +111,23 @@ export function formatNumber(num, significantDigits=5) { * @returns {Number} - Numeric value represented by the input string, considering the notation. */ export function unformatNumber(str) { - if (str === undefined) return 0; - - const notationMap = { - k: 1_000, - m: 1_000_000, - b: 1_000_000_000 - }; - - const trimmedStr = str.trim(); // Remove leading and trailing whitespace - const numericPart = parseFloat(trimmedStr.replace(/[^\d.-]/g, '')); // Extract numeric part - const notation = trimmedStr.slice(-1).toLowerCase(); // Get the notation - - const multiplier = notationMap[notation] || 1; // Get the appropriate multiplier - - if (!isNaN(numericPart)) return numericPart * multiplier; - - return 0; // If conversion is not possible, return 0 + if (typeof str !== "string") return 0; + + const notationMap = { + k: 1_000, + m: 1_000_000, + b: 1_000_000_000, + }; + + const trimmedStr = str.trim(); // Remove leading and trailing whitespace + const numericPart = parseFloat(trimmedStr.replace(/[^\d.-]/g, "")); // Extract numeric part + const notation = trimmedStr.slice(-1).toLowerCase(); // Get the notation + + const multiplier = notationMap[notation] || 1; // Get the appropriate multiplier + + if (!isNaN(numericPart)) return numericPart * multiplier; + + return 0; // If conversion is not possible, return 0 } /** @@ -128,17 +137,17 @@ export function unformatNumber(str) { * @returns {Number} - The integer representation of the given Roman numeral. */ export function romanToNum(str) { - if (typeof str !== "string") return str; - const roman = { I: 1, V: 5, X: 10, L: 50, C: 100, D: 500, M: 1000 }; - - let num = 0.0; - for (let i = 0; i < str.length; i++) { - let curr = roman[str[i]]; - let next = roman[str[i + 1]]; - (curr < next) ? (num -= curr) : (num += curr); - } - return num; -}; + if (typeof str !== "string") return str; + const roman = { I: 1, V: 5, X: 10, L: 50, C: 100, D: 500, M: 1000 }; + + let num = 0.0; + for (let i = 0; i < str.length; i++) { + let curr = roman[str[i]]; + let next = roman[str[i + 1]]; + curr < next ? (num -= curr) : (num += curr); + } + return num; +} /** * Converts a number 1-10 to its roman numeral counterpart. @@ -147,33 +156,33 @@ export function romanToNum(str) { * @returns {String} - The roman numeral counterpart. */ export function numToRoman(num) { - return ["I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X"][num - 1]; -}; + return ["I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X"][num - 1]; +} /** * Converts a string with underscores to title case format. - * + * * @param {String} input - Input string with underscores. * @returns {String} String in title case format. */ export function convertToTitleCase(input) { - const args = input.includes('_') ? input.toLowerCase().split('_') : input.toLowerCase().split(' '); - return args.map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(' '); + const args = input.includes("_") ? input.toLowerCase().split("_") : input.toLowerCase().split(" "); + return args.map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" "); } /** * Converts a string of words to pascal case format. - * + * * @param {String} input - Input string with underscores. * @returns {String} String in pascal case format. */ export function convertToPascalCase(input) { - if (!input) return; - - return input - .split(" ") - .map(word => word.charAt(0).toUpperCase() + word.slice(1)) - .join(""); + if (!input) return; + + return input + .split(" ") + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + .join(""); } /** @@ -184,24 +193,20 @@ export function convertToPascalCase(input) { * @returns {String} - Item string with specified reforges removed. */ export function removeReforges(itemType, itemString) { - // Get the corresponding reforges Set based on the item type - const reforgesSet = itemType === "all" ? new Set([ - ...REFORGES.weapon, - ...REFORGES.armor, - ...REFORGES.misc - ]) : REFORGES[itemType]; + // Get the corresponding reforges Set based on the item type + const reforgesSet = + itemType === "all" ? new Set([...REFORGES.weapon, ...REFORGES.armor, ...REFORGES.misc]) : REFORGES[itemType]; - // If the item type is not valid or the reforges Set is empty, return the original item string - if (reforgesSet === undefined || !itemString) - return itemString; + // If the item type is not valid or the reforges Set is empty, return the original item string + if (reforgesSet === undefined || !itemString) return itemString; - // Split the item string into individual words - const words = itemString.replace(/[^a-zA-Z\s]/g, '').split(" "); + // Split the item string into individual words + const words = itemString.replace(/[^a-zA-Z\s]/g, "").split(" "); - // Filter out the words that match any of the reforges using Set.has() for faster lookup - const filteredWords = words.filter(word => !reforgesSet.has(word)); + // Filter out the words that match any of the reforges using Set.has() for faster lookup + const filteredWords = words.filter((word) => !reforgesSet.has(word)); - return filteredWords.join(" ").trim(); + return filteredWords.join(" ").trim(); } /** @@ -211,36 +216,33 @@ export function removeReforges(itemType, itemString) { * @returns {Boolean} - True if the date is valid, false otherwise. */ export function isValidDate(dateString) { - // First check for the pattern - if(!/^\d{1,2}\/\d{1,2}\/\d{4}$/.test(dateString)) - return false; + // First check for the pattern + if (!/^\d{1,2}\/\d{1,2}\/\d{4}$/.test(dateString)) return false; - // Parse the date parts to integers - var parts = dateString.split("/"); - var day = parseInt(parts[1], 10); - var month = parseInt(parts[0], 10); - var year = parseInt(parts[2], 10); + // Parse the date parts to integers + var parts = dateString.split("/"); + var day = parseInt(parts[1], 10); + var month = parseInt(parts[0], 10); + var year = parseInt(parts[2], 10); - // Check the ranges of month and year - if(year < 1000 || year > 3000 || month === 0 || month > 12) - return false; + // Check the ranges of month and year + if (year < 1000 || year > 3000 || month === 0 || month > 12) return false; - var monthLength = [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ]; + var monthLength = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; - // Adjust for leap years - if(year % 400 === 0 || (year % 100 !== 0 && year % 4 === 0)) - monthLength[1] = 29; + // Adjust for leap years + if (year % 400 === 0 || (year % 100 !== 0 && year % 4 === 0)) monthLength[1] = 29; - // Check the range of the day - return day > 0 && day <= monthLength[month - 1]; -}; + // Check the range of the day + return day > 0 && day <= monthLength[month - 1]; +} /** * Removes any non-numeric character from given string. - * + * * @param {String} str - String to remove non-numeric characters from. * @returns {String} String with non-numeric characters removed. */ export function removeNonNumeric(str) { - return !str ? "" : str.replace(/\D/g, ""); + return !str ? "" : str.replace(/\D/g, ""); } diff --git a/utils/functions/matrix.js b/utils/functions/matrix.js index 4e0967b..950ffa3 100644 --- a/utils/functions/matrix.js +++ b/utils/functions/matrix.js @@ -4,36 +4,36 @@ * @returns {Array} - An array containing all formations (including rotations and flips) of the input array. */ export function getAllFormations(array) { - const formations = []; + const formations = []; - // Function to rotate a 2D array by 90 degrees - function rotate90Degrees(arr) { - const rows = arr.length; - const cols = arr[0].length; - const rotated = []; - for (let i = 0; i < cols; i++) { - rotated.push([]); - for (let j = rows - 1; j >= 0; j--) { - rotated[i].push(arr[j][i]); - } - } - return rotated; + // Function to rotate a 2D array by 90 degrees + function rotate90Degrees(arr) { + const rows = arr.length; + const cols = arr[0].length; + const rotated = []; + for (let i = 0; i < cols; i++) { + rotated.push([]); + for (let j = rows - 1; j >= 0; j--) { + rotated[i].push(arr[j][i]); + } } + return rotated; + } - // Function to flip a 2D array horizontally - function flipHorizontally(arr) { - return arr.map(row => row.slice().reverse()); - } + // Function to flip a 2D array horizontally + function flipHorizontally(arr) { + return arr.map((row) => row.slice().reverse()); + } - // Generate all formations - let current = array; - for (let i = 0; i < 4; i++) { - formations.push(current); - formations.push(flipHorizontally(current)); - current = rotate90Degrees(current); - } + // Generate all formations + let current = array; + for (let i = 0; i < 4; i++) { + formations.push(current); + formations.push(flipHorizontally(current)); + current = rotate90Degrees(current); + } - return formations; + return formations; } /** @@ -43,11 +43,11 @@ export function getAllFormations(array) { * @returns {Array} - A 2D array representing the matrix. */ export function createMatrix(rows, cols) { - const array = []; - for (let i = 0; i < rows; i++) { - array.push(Array(cols).fill(0)); - } - return array; + const array = []; + for (let i = 0; i < rows; i++) { + array.push(Array(cols).fill(0)); + } + return array; } /** @@ -62,13 +62,13 @@ export function createMatrix(rows, cols) { * @returns {boolean} - True if the player is looking away from the target point, false otherwise. */ export function isLookingAway(x_p, y_p, z_p, yaw_p, x_target, y_target, z_target) { - const dir_x = -Math.sin(yaw_p * Math.PI / 180); - const dir_z = Math.cos(yaw_p * Math.PI / 180); + const dir_x = -Math.sin((yaw_p * Math.PI) / 180); + const dir_z = Math.cos((yaw_p * Math.PI) / 180); - const target_vec_x = x_target - x_p; - const target_vec_y = y_target - y_p; - const target_vec_z = z_target - z_p; - const dot_product = target_vec_x * dir_x + target_vec_y * 0 + target_vec_z * dir_z; + const target_vec_x = x_target - x_p; + const target_vec_y = y_target - y_p; + const target_vec_z = z_target - z_p; + const dot_product = target_vec_x * dir_x + target_vec_y * 0 + target_vec_z * dir_z; - return dot_product < 0; + return dot_product < 0; } diff --git a/utils/functions/misc.js b/utils/functions/misc.js index 893e41d..9515d2c 100644 --- a/utils/functions/misc.js +++ b/utils/functions/misc.js @@ -1,30 +1,29 @@ import party from "../Party"; import { delay } from "../ThreadTils"; - /** * Returns a random index in array. - * + * * @param {Type[]} arr - Array to find the random index of. * @returns {Number} Random number from 0 to arr.length - 1 */ export function randIndex(arr) { - return Math.floor(Math.random() * (arr.length - 1)); + return Math.floor(Math.random() * (arr.length - 1)); } let soundCD = false; /** * Plays a sound and sets cooldown - * - * @param {Sound} sound - A sound ogg file from constants.js + * + * @param {Sound} sound - A sound ogg file from constants.js * @param {Number} cd - Cooldown caused by sound play. */ export function playSound(sound, cd) { - if (soundCD) return; + if (soundCD) return; - sound?.play(); - soundCD = true; - delay(() => soundCD = false, cd); + sound?.play(); + soundCD = true; + delay(() => (soundCD = false), cd); } /** @@ -37,19 +36,20 @@ export function playSound(sound, cd) { * @param {Number} z - Z coordinate. */ export function announceMob(chat, mob, x, y, z, area) { - if ((chat === 2 && !party.getIn()) || chat === 0) return; - x = Math.round(x); - y = Math.round(y); - z = Math.round(z); - if (area === undefined) { - const zoneLine = Scoreboard?.getLines()?.find((line) => line.getName().includes("⏣")) ?? - Scoreboard?.getLines()?.find((line) => line.getName().includes("ф")); - area = zoneLine === undefined ? "None" : zoneLine.getName().removeFormatting(); - } - - const id = chat === 2 ? "" : ` @${(Math.random() + 1).toString(36).substring(2)}`; - const CHATS = ["OFF", "ac", "pc", `msg ${Player.getName()}`]; - ChatLib.command(`${CHATS[chat]} x: ${x}, y: ${y}, z: ${z} | ${mob} spawned at [${area} ]!${id}`); + if ((chat === 2 && !party.getIn()) || chat === 0) return; + x = Math.round(x); + y = Math.round(y); + z = Math.round(z); + if (area === undefined) { + const zoneLine = + Scoreboard?.getLines()?.find((line) => line.getName().includes("⏣")) ?? + Scoreboard?.getLines()?.find((line) => line.getName().includes("ф")); + area = zoneLine === undefined ? "None" : zoneLine.getName().removeFormatting(); + } + + const id = chat === 2 ? "" : ` @${(Math.random() + 1).toString(36).substring(2)}`; + const CHATS = ["OFF", "ac", "pc", `msg ${Player.getName()}`]; + ChatLib.command(`${CHATS[chat]} x: ${x}, y: ${y}, z: ${z} | ${mob} spawned at [${area} ]!${id}`); } const decoder = java.util.Base64.getDecoder(); @@ -57,15 +57,15 @@ const compressor = net.minecraft.nbt.CompressedStreamTools; /** * Decode Hypixel item NBT bytes * Credit to https://www.chattriggers.com/modules/v/SBInvSee for decoding! - * + * * @param {String} bytes - Encoded hypixel item data. * @returns {String} Decoded NBT data. */ export function decode(bytes) { - const bytearray = decoder.decode(bytes); - const inputstream = new java.io.ByteArrayInputStream(bytearray); - const nbt = compressor.func_74796_a(inputstream); - return nbt.func_150295_c("i", 10); + const bytearray = decoder.decode(bytes); + const inputstream = new java.io.ByteArrayInputStream(bytearray); + const nbt = compressor.func_74796_a(inputstream); + return nbt.func_150295_c("i", 10); } const GzipCompressorOutputStream = Java.type("org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream"); @@ -76,126 +76,131 @@ const IOUtils = Java.type("org.apache.commons.io.IOUtils"); /** * Compresses NBT object into a compressed string using Base64. - * + * * @param {Object} nbtObject - NBT data object of an item to compress. * @returns {String} Compressed form of the NBT data as a string. */ export function compressNBT(nbtObject) { - try { - // Convert NBT object to JSON string - const nbtString = JSON.stringify(nbtObject); - const nbtBytes = new java.lang.String(nbtString).getBytes("UTF-8"); - const byteArrayOutputStream = new ByteArrayOutputStream(); - const compressor = new GzipCompressorOutputStream(byteArrayOutputStream); - compressor.write(nbtBytes); - - // Close streams - compressor.close(); - byteArrayOutputStream.close(); - - // Get the compressed data as byte array - const compressedData = byteArrayOutputStream.toByteArray(); - - return java.util.Base64.getEncoder().encodeToString(compressedData); - } catch (e) { - console.error("Error compressing NBT: " + e); - return null; - } + try { + // Convert NBT object to JSON string + const nbtString = JSON.stringify(nbtObject); + const nbtBytes = new java.lang.String(nbtString).getBytes("UTF-8"); + const byteArrayOutputStream = new ByteArrayOutputStream(); + const compressor = new GzipCompressorOutputStream(byteArrayOutputStream); + compressor.write(nbtBytes); + + // Close streams + compressor.close(); + byteArrayOutputStream.close(); + + // Get the compressed data as byte array + const compressedData = byteArrayOutputStream.toByteArray(); + + return java.util.Base64.getEncoder().encodeToString(compressedData); + } catch (e) { + console.error("Error compressing NBT: " + e); + return null; + } } /** * Decompresses given compressed NBT string using Base64. - * + * * @param {String} compressedData - Compressed NBT string to decompress data from. * @returns {Object} Decompressed NBT data. */ export function decompressNBT(compressedData) { - try { - // Decode base64 string to byte array - const compressedBytes = java.util.Base64.getDecoder().decode(compressedData); - const byteArrayInputStream = new ByteArrayInputStream(compressedBytes); - const decompressor = new GzipCompressorInputStream(byteArrayInputStream); - const byteArrayOutputStream = new ByteArrayOutputStream(); - IOUtils.copy(decompressor, byteArrayOutputStream); - - // Close streams - decompressor.close(); - byteArrayInputStream.close(); - byteArrayOutputStream.close(); - - // Get the decompressed data as byte array - const decompressedBytes = byteArrayOutputStream.toByteArray(); - const decompressedString = new java.lang.String(decompressedBytes, "UTF-8"); - const nbtObject = JSON.parse(decompressedString); - - return nbtObject; - } catch (e) { - console.error("Error decompressing NBT: " + e); - return null; - } + try { + // Decode base64 string to byte array + const compressedBytes = java.util.Base64.getDecoder().decode(compressedData); + const byteArrayInputStream = new ByteArrayInputStream(compressedBytes); + const decompressor = new GzipCompressorInputStream(byteArrayInputStream); + const byteArrayOutputStream = new ByteArrayOutputStream(); + IOUtils.copy(decompressor, byteArrayOutputStream); + + // Close streams + decompressor.close(); + byteArrayInputStream.close(); + byteArrayOutputStream.close(); + + // Get the decompressed data as byte array + const decompressedBytes = byteArrayOutputStream.toByteArray(); + const decompressedString = new java.lang.String(decompressedBytes, "UTF-8"); + const nbtObject = JSON.parse(decompressedString); + + return nbtObject; + } catch (e) { + console.error("Error decompressing NBT: " + e); + return null; + } } /** * Parse a texture NBT string into an item. - * + * * @param {String} nbt - Base64 encoded NBT string. * @returns {Item} Item with the texture. */ export function parseTexture(nbt) { - const decoded = JSON.parse(FileLib.decodeBase64(nbt)); + const decoded = JSON.parse(FileLib.decodeBase64(nbt)); - // Stolen from Dalwyn ^_^ - const skullOwner = new NBTTagCompound(new net.minecraft.nbt.NBTTagCompound()); - const properties = new NBTTagCompound(new net.minecraft.nbt.NBTTagCompound()); - const textures = new NBTTagList(new net.minecraft.nbt.NBTTagList()); - const textureString = new NBTTagCompound(new net.minecraft.nbt.NBTTagCompound()); + // Stolen from Dalwyn ^_^ + const skullOwner = new NBTTagCompound(new net.minecraft.nbt.NBTTagCompound()); + const properties = new NBTTagCompound(new net.minecraft.nbt.NBTTagCompound()); + const textures = new NBTTagList(new net.minecraft.nbt.NBTTagList()); + const textureString = new NBTTagCompound(new net.minecraft.nbt.NBTTagCompound()); - const url = decoded.textures?.SKIN?.url?.split('/'); - const skin = url?.[url?.length - 1]; - const backup = Math.random().toString(36).substring(2, 12); - skullOwner.setString("Id", decoded.profileId ?? skin ?? backup); - skullOwner.setString("Name", ((decoded.profileName + decoded.timestamp) || skin) ?? backup); + const url = decoded.textures?.SKIN?.url?.split("/"); + const skin = url?.[url?.length - 1]; + const backup = Math.random().toString(36).substring(2, 12); + skullOwner.setString("Id", decoded.profileId ?? skin ?? backup); + skullOwner.setString("Name", (decoded.profileName + decoded.timestamp || skin) ?? backup); - textureString.setString("Value", nbt); - textures.appendTag(textureString); + textureString.setString("Value", nbt); + textures.appendTag(textureString); - properties.set("textures", textures); - skullOwner.set("Properties", properties); + properties.set("textures", textures); + skullOwner.set("Properties", properties); - return skullOwner; + return skullOwner; } /** * Parse a container cache into an array of items. - * + * * @param {String[]} cache - Cache of container items. * @returns {Item[]} Parsed items from the cache. */ export function parseContainerCache(cache) { - return cache.map(nbt => { - if (nbt === null) return null; - const parsedNBT = NBT.parse(decompressNBT(nbt)).rawNBT; - const item = new Item(net.minecraft.item.ItemStack.func_77949_a(parsedNBT)); - try { - const loreTag = parsedNBT.func_74775_l("tag").func_74775_l("display").field_74784_a.get("Lore"); - const lore = [item.getLore()[0]]; - if (loreTag !== null) { - for (let i = 0; i < loreTag.func_74745_c(); i++) { - lore.push(loreTag.func_150307_f(i)); - } - } - item.setLore(lore); - } catch(e) { - ChatLib.chat(e); + return cache.map((nbt) => { + if (nbt === null) return null; + const parsedNBT = NBT.parse(decompressNBT(nbt)).rawNBT; + const item = new Item(net.minecraft.item.ItemStack.func_77949_a(parsedNBT)); + try { + const loreTag = parsedNBT.func_74775_l("tag").func_74775_l("display").field_74784_a.get("Lore"); + const lore = [item.getLore()[0]]; + if (loreTag !== null) { + for (let i = 0; i < loreTag.func_74745_c(); i++) { + lore.push(loreTag.func_150307_f(i)); } + } + item.setLore(lore); + } catch (e) { + ChatLib.chat(e); + } - if (item.getUnlocalizedName() === "item.skull") { // Fix skull textures not rendering - const skullNBT = item.getNBT().getCompoundTag("tag").getCompoundTag("SkullOwner"); - const texture = skullNBT.getCompoundTag("Properties").getTagList("textures", 0).func_150305_b(0).func_74779_i("Value"); - const skull = parseTexture(texture); - item.getNBT().getCompoundTag("tag").set("SkullOwner", skull); - } + if (item.getUnlocalizedName() === "item.skull") { + // Fix skull textures not rendering + const skullNBT = item.getNBT().getCompoundTag("tag").getCompoundTag("SkullOwner"); + const texture = skullNBT + .getCompoundTag("Properties") + .getTagList("textures", 0) + .func_150305_b(0) + .func_74779_i("Value"); + const skull = parseTexture(texture); + item.getNBT().getCompoundTag("tag").set("SkullOwner", skull); + } - return item; - }); + return item; + }); } diff --git a/utils/functions/player.js b/utils/functions/player.js index 631fbfa..32b2adf 100644 --- a/utils/functions/player.js +++ b/utils/functions/player.js @@ -1,19 +1,19 @@ /** * Strips rank and tags off player name. - * + * * @param {String} player - Player name with rank and tags. * @returns {String} Base player ign. */ export function getPlayerName(player) { - let name = player.replace(/[^a-zA-Z0-9[\]_ ]/g, ''); - let nameIndex = name.indexOf(']'); + let name = player.replace(/[^a-zA-Z0-9[\]_ ]/g, ""); + let nameIndex = name.indexOf("]"); - while (nameIndex !== -1) { - name = name.substring(nameIndex + 2); - nameIndex = name.indexOf(']'); - } + while (nameIndex !== -1) { + name = name.substring(nameIndex + 2); + nameIndex = name.indexOf("]"); + } - return name.split(' ')[0]; + return name.split(" ")[0]; } /** @@ -23,21 +23,20 @@ export function getPlayerName(player) { * @returns {String} - Extracted guild name from the player's name. */ export function getGuildName(player) { - let name = player.replace(/[^a-zA-Z0-9[\]_ ]/g, ''); - let rankIndex = name.indexOf('] '); - if (rankIndex !== -1) - name = name.substring(name.indexOf('] ') + 2); - name = name.substring(0, name.indexOf('[') - 1); + let name = player.replace(/[^a-zA-Z0-9[\]_ ]/g, ""); + let rankIndex = name.indexOf("] "); + if (rankIndex !== -1) name = name.substring(name.indexOf("] ") + 2); + name = name.substring(0, name.indexOf("[") - 1); - return name; + return name; } /** * Returns True if entity is player otherwise False. - * + * * @param {Entity} entity - OtherPlayerMP Minecraft Entity. * @returns {Boolean} - Whether or not player is human. */ export function isPlayer(entity) { - return World.getPlayerByName(entity.getName())?.getPing() === 1; + return World.getPlayerByName(entity.getName())?.getPing() === 1; } diff --git a/utils/functions/render.js b/utils/functions/render.js index 2a90e10..1add4fd 100644 --- a/utils/functions/render.js +++ b/utils/functions/render.js @@ -1,10 +1,9 @@ import { COLOR_TABLE, DARK_GRAY } from "../Constants"; import Settings from "../Settings"; - /** * Function to draw a box on the screen. - * + * * @param {Number} x - X-Coordinate of the box. * @param {Number} y - Y-Coordinate of the box. * @param {Number} z - Z-Index of the box. @@ -14,44 +13,44 @@ import Settings from "../Settings"; * @param {Object} borderColor - Renderer.color of the border. */ export function drawBox(x, y, z, width, height, rectColor, borderColor) { - Renderer.translate(x, y, z); - Renderer.drawRect(rectColor, 0, 0, width, height); + Renderer.translate(x, y, z); + Renderer.drawRect(rectColor, 0, 0, width, height); - // Draw Outline - Renderer.retainTransforms(true); - Renderer.translate(x, y, z + 1); - Renderer.drawLine(borderColor, -1, -1, width + 1, -1, 1); - Renderer.drawLine(borderColor, -1, -1, -1, height + 1, 1); - Renderer.drawLine(borderColor, -1, height + 1, width + 1, height + 1, 1); - Renderer.drawLine(borderColor, width + 1, -1, width + 1, height + 1, 1); - Renderer.retainTransforms(false); + // Draw Outline + Renderer.retainTransforms(true); + Renderer.translate(x, y, z + 1); + Renderer.drawLine(borderColor, -1, -1, width + 1, -1, 1); + Renderer.drawLine(borderColor, -1, -1, -1, height + 1, 1); + Renderer.drawLine(borderColor, -1, height + 1, width + 1, height + 1, 1); + Renderer.drawLine(borderColor, width + 1, -1, width + 1, height + 1, 1); + Renderer.retainTransforms(false); } const BORDER_COLOR = Renderer.color(128, 128, 128, 128); /** * Function to draw a box on the screen. - * + * * @param {Number} hx - X-Coordinate of the box. * @param {Number} hy - Y-Coordinate of the box. * @param {String[]} lore - Array of strings to draw. */ export function drawLore(hx, hy, lore) { - lore = lore.slice(1); - if (lore.length === 0) return; + lore = lore.slice(1); + if (lore.length === 0) return; - const width = lore.reduce((max, line) => Math.max(max, Renderer.getStringWidth(line)), 0); - const height = lore.length * 9 + 4; - const x = hx + 18 + width > Renderer.screen.getWidth() ? Renderer.screen.getWidth() - width - 10 : hx + 8; - const y = hy - 14 + height > Renderer.screen.getHeight() ? Renderer.screen.getHeight() - height - 2 : hy - 16; + const width = lore.reduce((max, line) => Math.max(max, Renderer.getStringWidth(line)), 0); + const height = lore.length * 9 + 4; + const x = hx + 18 + width > Renderer.screen.getWidth() ? Renderer.screen.getWidth() - width - 10 : hx + 8; + const y = hy - 14 + height > Renderer.screen.getHeight() ? Renderer.screen.getHeight() - height - 2 : hy - 16; - drawBox(x + 2, y, 999, width + 6, height, Renderer.BLACK, BORDER_COLOR); - Renderer.translate(0, 0, 999); - Renderer.drawString(lore.join('\n'), x + 5, y + 2); + drawBox(x + 2, y, 999, width + 6, height, Renderer.BLACK, BORDER_COLOR); + Renderer.translate(0, 0, 999); + Renderer.drawString(lore.join("\n"), x + 5, y + 2); } /** * Function to draw a container on the screen. - * + * * @param {Number} bX - X-Coordinate of the container. * @param {Number} bY - Y-Coordinate of the container. * @param {String} title - Title of the container. @@ -61,38 +60,38 @@ export function drawLore(hx, hy, lore) { * @param {Number} mY - Y-Coordinate of the mouse. */ export function drawContainer(bX, bY, title, bg, items, mX, mY) { - bg.draw(bX, bY); - Renderer.drawString(DARK_GRAY + title, bX + 7, bY + 6); + bg.draw(bX, bY); + Renderer.drawString(DARK_GRAY + title, bX + 7, bY + 6); + + for (let i = 0; i < 6; i++) { + for (let j = 0; j < 9; j++) { + let index = i * 9 + j; + let item = items[index]; + if (!item) continue; - for (let i = 0; i < 6; i++) { - for (let j = 0; j < 9; j++) { - let index = i * 9 + j; - let item = items[index]; - if (!item) continue; + let x = bX + 7.5 + j * 18; + let y = bY + 17.5 + i * 18; - let x = bX + 7.5 + j * 18; - let y = bY + 17.5 + i * 18; - - // Draw rarity box - if (index >= 9) { - let color = COLOR_TABLE[item.getName().substring(0, 2)]; - if (color !== undefined) Renderer.drawRect(color, x, y, 16, 16); - } + // Draw rarity box + if (index >= 9) { + let color = COLOR_TABLE[item.getName().substring(0, 2)]; + if (color !== undefined) Renderer.drawRect(color, x, y, 16, 16); + } - // Draw item and size - item.draw(x, y, 1); - let size = item.getStackSize(); - if (size !== 1) { - Renderer.translate(0, 0, 500); - Renderer.drawString(size, x - Renderer.getStringWidth(size) + 17, y + 9, true); - } + // Draw item and size + item.draw(x, y, 1); + let size = item.getStackSize(); + if (size !== 1) { + Renderer.translate(0, 0, 500); + Renderer.drawString(size, x - Renderer.getStringWidth(size) + 17, y + 9, true); + } - // Draw lore if hovered - if (mX >= x && mX <= x + 16 && mY >= y && mY <= y + 16) { - drawLore(mX, mY, item.getLore()); - } - } + // Draw lore if hovered + if (mX >= x && mX <= x + 16 && mY >= y && mY <= y + 16) { + drawLore(mX, mY, item.getLore()); + } } + } } /** @@ -105,15 +104,15 @@ export function drawContainer(bX, bY, title, bg, items, mX, mY) { * @param {Boolean} align - True for right align, or false for left align. * @param {Boolean} flex - True for vertical flex, or false for horizontal flex. */ -export function renderScale(x, y, text, scale=1, align=false, flex=false, z=0) { - // Apply parameters - x /= scale; - y /= scale; - if (flex) text = text.replace(/\n/g, " "); +export function renderScale(x, y, text, scale = 1, align = false, flex = false, z = 0) { + // Apply parameters + x /= scale; + y /= scale; + if (flex) text = text.replace(/\n/g, " "); - // Scale and render - Renderer.scale(scale); - Renderer.translate(0, 0, z ?? 300); - if (align) new Text(text.replace(/&l/g, ''), x, y).setAlign("right").setShadow(Settings.textShadow).draw(); - else Renderer.drawString(text, x, y, Settings.textShadow); + // Scale and render + Renderer.scale(scale); + Renderer.translate(0, 0, z ?? 300); + if (align) new Text(text.replace(/&l/g, ""), x, y).setAlign("right").setShadow(Settings.textShadow).draw(); + else Renderer.drawString(text, x, y, Settings.textShadow); }